11#include "llvm/Support/MemoryBuffer.h"
12#include "llvm/Support/Threading.h"
18llvm::ErrorOr<DependencyScanningWorkerFilesystem::TentativeEntry>
19DependencyScanningWorkerFilesystem::readFile(StringRef Filename,
20 llvm::vfs::FileSystem &FS) {
22 auto MaybeFile = FS.openFileForRead(Filename);
24 return MaybeFile.getError();
25 auto File = std::move(*MaybeFile);
27 auto MaybeStat =
File->status();
29 return MaybeStat.getError();
30 auto Stat = std::move(*MaybeStat);
32 auto MaybeBuffer =
File->getBuffer(Stat.getName());
34 return MaybeBuffer.getError();
35 auto Buffer = std::move(*MaybeBuffer);
38 if (Stat.getSize() != Buffer->getBufferSize())
39 Stat = llvm::vfs::Status::copyWithNewSize(Stat, Buffer->getBufferSize());
41 return TentativeEntry(Stat, std::move(Buffer));
46 auto &Entry = Ref.Entry;
48 if (Entry.isError() || Entry.isDirectory())
52 assert(Contents &&
"contents not initialized");
58 std::lock_guard<std::mutex> GuardLock(Contents->
ValueLock);
72 Contents->
DepDirectives.store(
new std::optional<DependencyDirectivesTy>());
81 new std::optional<DependencyDirectivesTy>(std::move(Directives)));
93 std::max(2u, llvm::hardware_concurrency().compute_thread_count() / 4);
94 CacheShards = std::make_unique<CacheShard[]>(NumShards);
99 StringRef Filename)
const {
100 assert(llvm::sys::path::is_absolute_gnu(Filename));
106 llvm::sys::fs::UniqueID UID)
const {
107 auto Hash = llvm::hash_combine(UID.getDevice(), UID.getFile());
108 return CacheShards[Hash % NumShards];
111std::vector<DependencyScanningFilesystemSharedCache::OutOfDateEntry>
113 llvm::vfs::FileSystem &UnderlyingFS)
const {
115 std::vector<OutOfDateEntry> InvalidDiagInfo;
116 for (
unsigned i = 0; i < NumShards; i++) {
118 std::lock_guard<std::mutex> LockGuard(Shard.
CacheLock);
126 llvm::ErrorOr<llvm::vfs::Status> Status = UnderlyingFS.status(Path);
133 InvalidDiagInfo.emplace_back(Path.data());
135 llvm::vfs::Status CachedStatus = Entry->
getStatus();
136 if (Status->getType() == llvm::sys::fs::file_type::regular_file &&
137 Status->getType() == CachedStatus.getType()) {
145 uint64_t CachedSize = CachedStatus.getSize();
146 uint64_t ActualSize = Status->getSize();
147 if (CachedSize != ActualSize) {
150 InvalidDiagInfo.emplace_back(Path.data(), CachedSize, ActualSize);
157 return InvalidDiagInfo;
161using InProgressEntry =
163using SlotResolved = llvm::ErrorOr<const CachedFileSystemEntry *>;
164using SlotProducer = std::shared_ptr<InProgressEntry>;
165using SlotAcquisitionResult = std::variant<SlotResolved, SlotProducer>;
170template <
typename Map,
typename Key>
171SlotAcquisitionResult acquireSlot(std::mutex &CacheLock, Map &M,
const Key &K) {
172 std::shared_ptr<InProgressEntry> Pending;
174 std::lock_guard<std::mutex> ShardLock(CacheLock);
179 return SlotResolved{State.Entry};
181 if (!State.InProgress) {
182 State.InProgress = std::make_shared<InProgressEntry>();
183 return SlotProducer{State.InProgress};
188 Pending = State.InProgress;
192 std::unique_lock<std::mutex> EntryLock(Pending->Mutex);
193 Pending->CondVar.wait(EntryLock, [&] {
return Pending->Done; });
194 return SlotResolved{Pending->Result};
200 StringRef Filename)
const {
201 assert(llvm::sys::path::is_absolute_gnu(Filename));
202 std::lock_guard<std::mutex> LockGuard(
CacheLock);
209 llvm::ErrorOr<llvm::StringRef> RealPath) {
210 std::lock_guard<std::mutex> LockGuard(
CacheLock);
213 if (!StoredRealPath) {
216 return RealPath.getError();
217 return RealPath->str();
224 return *StoredRealPath;
231 auto OverlayVFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
233 OverlayVFS->pushOverlay(std::move(GlobalFS));
234 GlobalFS = std::move(OverlayVFS);
246 Service(Service), WorkingDirForCacheLookup(
llvm::errc::invalid_argument),
247 LocalFS(
std::move(LocalFS)), BaseFS(FS) {
248 updateWorkingDirForCacheLookup();
252 llvm::ErrorOr<TentativeEntry> &TEntry,
253 llvm::SpecificBumpPtrAllocator<CachedFileContents> &ContentsStorage,
254 llvm::SpecificBumpPtrAllocator<CachedFileSystemEntry> &EntryStorage) {
257 if (TEntry->Contents)
258 StoredContents =
new (ContentsStorage.Allocate())
260 return new (EntryStorage.Allocate())
263 return new (EntryStorage.Allocate())
269DependencyScanningWorkerFilesystem::resolveUIDThroughSharedCache(
270 StringRef OriginalFilename,
const llvm::vfs::Status &Stat) {
271 auto &UIDShard = Service.getSharedCache().getShardForUID(Stat.getUniqueID());
272 auto UIDSlot = acquireSlot(UIDShard.CacheLock, UIDShard.EntriesByUID,
274 if (
auto *Resolved = std::get_if<SlotResolved>(&UIDSlot)) {
275 assert(*Resolved && **Resolved &&
276 "in-progress UID slot fulfilled without an entry");
279 auto UIDProducer = std::move(std::get<SlotProducer>(UIDSlot));
281 auto TEntry = Stat.isDirectory() ? TentativeEntry(Stat)
282 : readFile(OriginalFilename, *BaseFS);
288 const CachedFileSystemEntry *SharedEntry;
290 std::lock_guard<std::mutex> ShardLock(UIDShard.CacheLock);
291 auto &State = UIDShard.EntriesByUID[Stat.getUniqueID()];
292 assert(!State.Entry &&
"UID slot already published an entry");
294 createEntry(TEntry, UIDShard.ContentsStorage, UIDShard.EntryStorage);
295 State.Entry = SharedEntry;
296 State.InProgress.reset();
298 UIDProducer->publish(SharedEntry);
303DependencyScanningWorkerFilesystem::createCachedFSEntryForLocal(
304 StringRef OriginalFilename,
const llvm::vfs::Status &Stat) {
306 auto TEntry = Stat.isDirectory() ? TentativeEntry(Stat)
307 : readFile(OriginalFilename, *LocalFS);
308 return createEntry(TEntry, LocalContentsStorage, LocalEntryStorage);
311llvm::ErrorOr<const CachedFileSystemEntry *>
312DependencyScanningWorkerFilesystem::resolveFilenameThroughSharedCache(
313 StringRef OriginalFilename, StringRef FilenameForLookup) {
315 if (
auto Stat = LocalFS->status(OriginalFilename)) {
316 return createCachedFSEntryForLocal(OriginalFilename, *Stat);
319 assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
320 auto &FilenameShard =
321 Service.getSharedCache().getShardForFilename(FilenameForLookup);
323 acquireSlot(FilenameShard.CacheLock, FilenameShard.CacheByFilename,
325 if (
auto *Resolved = std::get_if<SlotResolved>(&FilenameSlot))
327 auto FilenameProducer = std::move(std::get<SlotProducer>(FilenameSlot));
336 auto Stat = BaseFS->status(OriginalFilename);
337 const bool ShouldCacheNegativeStat =
338 !Stat && Service.getOpts().CacheNegativeStats &&
340 llvm::ErrorOr<const CachedFileSystemEntry *>
Result = std::error_code{};
342 Result = resolveUIDThroughSharedCache(OriginalFilename, *Stat);
343 else if (!ShouldCacheNegativeStat)
350 std::lock_guard<std::mutex> ShardLock(FilenameShard.CacheLock);
351 auto &State = FilenameShard.CacheByFilename[FilenameForLookup];
352 assert(!State.Entry &&
"filename slot already published an entry");
353 if (ShouldCacheNegativeStat) {
354 auto *Entry =
new (FilenameShard.EntryStorage.Allocate())
355 CachedFileSystemEntry(Stat.getError());
361 State.InProgress.reset();
363 FilenameProducer->publish(
Result);
367llvm::ErrorOr<EntryRef>
369 StringRef OriginalFilename) {
371 auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf);
372 if (!FilenameForLookup)
373 return FilenameForLookup.getError();
375 auto &
Local = LocalCache[*FilenameForLookup];
380 resolveFilenameThroughSharedCache(OriginalFilename, *FilenameForLookup);
382 return MaybeEntry.getError();
383 Local.File = *MaybeEntry;
387llvm::ErrorOr<llvm::vfs::Status>
390 StringRef Filename = Path.toStringRef(OwnedFilename);
395 return Result->getStatus();
405 llvm::ErrorOr<llvm::vfs::Status> Status =
status(Path);
406 return Status && Status->exists();
413class DepScanFile final :
public llvm::vfs::File {
415 DepScanFile(std::unique_ptr<llvm::MemoryBuffer> Buffer,
416 llvm::vfs::Status Stat)
417 : Buffer(
std::move(Buffer)), Stat(
std::move(Stat)) {}
419 static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> create(
EntryRef Entry);
421 llvm::ErrorOr<llvm::vfs::Status> status()
override {
return Stat; }
423 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
424 getBuffer(
const Twine &Name,
int64_t FileSize,
bool RequiresNullTerminator,
425 bool IsVolatile)
override {
426 return llvm::MemoryBuffer::getMemBuffer(Buffer->getMemBufferRef(),
427 RequiresNullTerminator);
430 std::error_code close()
override {
return {}; }
433 std::unique_ptr<llvm::MemoryBuffer> Buffer;
434 llvm::vfs::Status Stat;
439llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
440DepScanFile::create(
EntryRef Entry) {
441 assert(!Entry.
isError() &&
"error");
444 return std::make_error_code(std::errc::is_a_directory);
446 auto Result = std::make_unique<DepScanFile>(
447 llvm::MemoryBuffer::getMemBuffer(Entry.
getContents(),
452 return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>(
453 std::unique_ptr<llvm::vfs::File>(std::move(
Result)));
456llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
459 StringRef Filename = Path.toStringRef(OwnedFilename);
464 return DepScanFile::create(
Result.get());
471 StringRef OriginalFilename = Path.toStringRef(OwnedFilename);
474 auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf);
475 if (!FilenameForLookup)
476 return FilenameForLookup.getError();
478 auto HandleCachedRealPath =
481 return RealPath.getError();
482 Output.assign(RealPath->begin(), RealPath->end());
487 auto &
Local = LocalCache[*FilenameForLookup];
489 return HandleCachedRealPath(*
Local.RealPath);
493 Service.getSharedCache().getShardForFilename(*FilenameForLookup);
494 if (
const auto *ShardRealPath =
495 Shard.findRealPathByFilename(*FilenameForLookup)) {
496 Local.RealPath = ShardRealPath;
497 return HandleCachedRealPath(*
Local.RealPath);
501 std::error_code EC = getUnderlyingFS().getRealPath(OriginalFilename, Output);
502 llvm::ErrorOr<llvm::StringRef> ComputedRealPath = EC;
504 ComputedRealPath = StringRef{Output.data(), Output.size()};
509 const auto &RealPath = Shard.getOrEmplaceRealPathForFilename(
510 *FilenameForLookup, ComputedRealPath);
511 Local.RealPath = &RealPath;
512 return HandleCachedRealPath(*
Local.RealPath);
517 std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path);
518 updateWorkingDirForCacheLookup();
522void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() {
523 llvm::ErrorOr<std::string> CWD =
524 getUnderlyingFS().getCurrentWorkingDirectory();
526 WorkingDirForCacheLookup = CWD.getError();
527 }
else if (!llvm::sys::path::is_absolute_gnu(*CWD)) {
528 WorkingDirForCacheLookup = llvm::errc::invalid_argument;
530 WorkingDirForCacheLookup = *CWD;
532 assert(!WorkingDirForCacheLookup ||
533 llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup));
536llvm::ErrorOr<StringRef>
537DependencyScanningWorkerFilesystem::tryGetFilenameForLookup(
538 StringRef OriginalFilename, llvm::SmallVectorImpl<char> &PathBuf)
const {
539 StringRef FilenameForLookup;
540 if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
541 FilenameForLookup = OriginalFilename;
542 }
else if (!WorkingDirForCacheLookup) {
543 return WorkingDirForCacheLookup.getError();
545 StringRef RelFilename = OriginalFilename;
546 RelFilename.consume_front(
"./");
547 PathBuf.assign(WorkingDirForCacheLookup->begin(),
548 WorkingDirForCacheLookup->end());
549 llvm::sys::path::append(PathBuf, RelFilename);
550 FilenameForLookup = StringRef{PathBuf.begin(), PathBuf.size()};
552 assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
553 return FilenameForLookup;
static IntrusiveRefCntPtr< llvm::vfs::FileSystem > combineGlobalAndLocalFS(IntrusiveRefCntPtr< llvm::vfs::FileSystem > GlobalFS, IntrusiveRefCntPtr< llvm::vfs::FileSystem > LocalFS)
An in-memory representation of a file system entity that is of interest to the dependency scanning fi...
llvm::vfs::Status getStatus() const
std::error_code getError() const
CacheShard & getShardForUID(llvm::sys::fs::UniqueID UID) const
CacheShard & getShardForFilename(StringRef Filename) const
Returns shard for the given key.
std::vector< OutOfDateEntry > getOutOfDateEntries(llvm::vfs::FileSystem &UnderlyingFS) const
Visits all cached entries and re-stat an entry using UnderlyingFS to check if the cache contains out-...
DependencyScanningFilesystemSharedCache()
The dependency scanning service contains shared configuration and state that is used by the individua...
std::error_code getRealPath(const Twine &Path, SmallVectorImpl< char > &Output) override
DependencyScanningWorkerFilesystem(DependencyScanningService &Service, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, IntrusiveRefCntPtr< llvm::vfs::FileSystem > LocalFS=nullptr)
bool ensureDirectiveTokensArePopulated(EntryRef Entry)
Ensure the directive tokens are populated for this file entry.
bool exists(const Twine &Path) override
Check whether Path exists.
llvm::ErrorOr< EntryRef > getOrCreateFileSystemEntry(StringRef Filename)
Returns entry for the given filename.
llvm::ErrorOr< std::unique_ptr< llvm::vfs::File > > openFileForRead(const Twine &Path) override
std::error_code setCurrentWorkingDirectory(const Twine &Path) override
llvm::ErrorOr< llvm::vfs::Status > status(const Twine &Path) override
Reference to a CachedFileSystemEntry.
llvm::vfs::Status getStatus() const
StringRef getContents() const
llvm::ErrorOr< EntryRef > unwrapError() const
If the cached entry represents an error, promotes it into ErrorOr.
llvm::ErrorOr< std::string > CachedRealPath
bool shouldCacheNegativeStatsForPath(StringRef Path)
Top level wrappers for InstallAPI frontend operations.
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
bool scanSourceForDependencyDirectives(StringRef Input, SmallVectorImpl< dependency_directives_scan::Token > &Tokens, SmallVectorImpl< dependency_directives_scan::Directive > &Directives, DiagnosticsEngine *Diags=nullptr, SourceLocation InputSourceLoc=SourceLocation())
Scan the input for the preprocessor directives that might have an effect on the dependencies for a co...
@ Result
The result type of a method or function.
Diagnostic wrappers for TextAPI types for error reporting.
hash_code hash_value(const clang::dependencies::ModuleID &ID)
__builtin_bitreverse32 uint32_t __builtin_riscv_aaddu_u32 __builtin_riscv_asubu_u32 __builtin_elementwise_add_sat __builtin_elementwise_sub_sat int32_t uint32_t uint32_t __builtin_riscv_mhacc_i32 __builtin_riscv_mhaccu_u32 __builtin_riscv_mhaccsu_i32 __builtin_riscv_mulq_i32 int64_t
Contents and directive tokens of a cached file entry.
std::unique_ptr< llvm::MemoryBuffer > Original
Owning storage for the original contents.
SmallVector< dependency_directives_scan::Token, 10 > DepDirectiveTokens
std::atomic< const std::optional< DependencyDirectivesTy > * > DepDirectives
Accessor to the directive tokens that's atomic to avoid data races.
std::mutex ValueLock
The mutex that must be locked before mutating directive tokens.
llvm::StringMap< FilenameCacheState, llvm::BumpPtrAllocator > CacheByFilename
Map from filenames to their cached state.
llvm::SpecificBumpPtrAllocator< CachedRealPath > RealPathStorage
The backing storage for cached real paths.
const CachedRealPath * findRealPathByFilename(StringRef Filename) const
Returns the real path associated with the filename or nullptr if none is found.
std::mutex CacheLock
The mutex that needs to be locked before mutation of any member.
const CachedRealPath & getOrEmplaceRealPathForFilename(StringRef Filename, llvm::ErrorOr< StringRef > RealPath)
Returns the real path associated with the filename if there is some.
In-flight slot used to dedup concurrent producers for the same key.