10#include "llvm/Support/MemoryBuffer.h"
11#include "llvm/Support/Threading.h"
17llvm::ErrorOr<DependencyScanningWorkerFilesystem::TentativeEntry>
18DependencyScanningWorkerFilesystem::readFile(StringRef Filename) {
20 auto MaybeFile = getUnderlyingFS().openFileForRead(Filename);
22 return MaybeFile.getError();
23 auto File = std::move(*MaybeFile);
25 auto MaybeStat =
File->status();
27 return MaybeStat.getError();
28 auto Stat = std::move(*MaybeStat);
30 auto MaybeBuffer =
File->getBuffer(Stat.getName());
32 return MaybeBuffer.getError();
33 auto Buffer = std::move(*MaybeBuffer);
36 if (Stat.getSize() != Buffer->getBufferSize())
37 Stat = llvm::vfs::Status::copyWithNewSize(Stat, Buffer->getBufferSize());
39 return TentativeEntry(Stat, std::move(Buffer));
44 auto &Entry = Ref.Entry;
46 if (Entry.isError() || Entry.isDirectory())
50 assert(Contents &&
"contents not initialized");
56 std::lock_guard<std::mutex> GuardLock(Contents->
ValueLock);
70 Contents->
DepDirectives.store(
new std::optional<DependencyDirectivesTy>());
79 new std::optional<DependencyDirectivesTy>(std::move(Directives)));
91 std::max(2u, llvm::hardware_concurrency().compute_thread_count() / 4);
92 CacheShards = std::make_unique<CacheShard[]>(NumShards);
97 StringRef Filename)
const {
98 assert(llvm::sys::path::is_absolute_gnu(Filename));
104 llvm::sys::fs::UniqueID UID)
const {
105 auto Hash = llvm::hash_combine(UID.getDevice(), UID.getFile());
106 return CacheShards[Hash % NumShards];
109std::vector<DependencyScanningFilesystemSharedCache::OutOfDateEntry>
111 llvm::vfs::FileSystem &UnderlyingFS)
const {
113 std::vector<OutOfDateEntry> InvalidDiagInfo;
114 for (
unsigned i = 0; i < NumShards; i++) {
116 std::lock_guard<std::mutex> LockGuard(Shard.
CacheLock);
119 llvm::ErrorOr<llvm::vfs::Status> Status = UnderlyingFS.status(Path);
126 InvalidDiagInfo.emplace_back(Path.data());
128 llvm::vfs::Status CachedStatus = Entry->
getStatus();
129 if (Status->getType() == llvm::sys::fs::file_type::regular_file &&
130 Status->getType() == CachedStatus.getType()) {
138 uint64_t CachedSize = CachedStatus.getSize();
139 uint64_t ActualSize = Status->getSize();
140 if (CachedSize != ActualSize) {
143 InvalidDiagInfo.emplace_back(Path.data(), CachedSize, ActualSize);
150 return InvalidDiagInfo;
155 StringRef Filename)
const {
156 assert(llvm::sys::path::is_absolute_gnu(Filename));
157 std::lock_guard<std::mutex> LockGuard(
CacheLock);
164 llvm::sys::fs::UniqueID UID)
const {
165 std::lock_guard<std::mutex> LockGuard(
CacheLock);
173 llvm::ErrorOr<llvm::vfs::Status> Stat) {
174 std::lock_guard<std::mutex> LockGuard(
CacheLock);
175 auto [It, Inserted] =
CacheByFilename.insert({Filename, {
nullptr,
nullptr}});
180 assert((Inserted ||
CachedRealPath) &&
"existing file with empty pair");
189 llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat,
190 std::unique_ptr<llvm::MemoryBuffer> Contents) {
191 std::lock_guard<std::mutex> LockGuard(
CacheLock);
193 auto &CachedEntry = It->getSecond();
209 std::lock_guard<std::mutex> LockGuard(
CacheLock);
210 auto [It, Inserted] =
CacheByFilename.insert({Filename, {&Entry,
nullptr}});
212 if (!Inserted || !CachedEntry)
213 CachedEntry = &Entry;
219 StringRef Filename)
const {
220 assert(llvm::sys::path::is_absolute_gnu(Filename));
221 std::lock_guard<std::mutex> LockGuard(
CacheLock);
228 llvm::ErrorOr<llvm::StringRef> RealPath) {
229 std::lock_guard<std::mutex> LockGuard(
CacheLock);
232 if (!StoredRealPath) {
235 return RealPath.getError();
236 return RealPath->str();
243 return *StoredRealPath;
246bool DependencyScanningWorkerFilesystem::shouldBypass(StringRef Path)
const {
247 return BypassedPathPrefix && Path.starts_with(*BypassedPathPrefix);
255 SharedCache(SharedCache),
256 WorkingDirForCacheLookup(
llvm::errc::invalid_argument) {
257 updateWorkingDirForCacheLookup();
261DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID(
262 TentativeEntry TEntry) {
263 auto &Shard = SharedCache.
getShardForUID(TEntry.Status.getUniqueID());
265 std::move(TEntry.Status),
266 std::move(TEntry.Contents));
270DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough(
271 StringRef Filename) {
275 if (
const auto *Entry = Shard.findEntryByFilename(Filename))
280llvm::ErrorOr<const CachedFileSystemEntry &>
281DependencyScanningWorkerFilesystem::computeAndStoreResult(
282 StringRef OriginalFilename, StringRef FilenameForLookup) {
283 llvm::ErrorOr<llvm::vfs::Status> Stat =
284 getUnderlyingFS().status(OriginalFilename);
287 getOrEmplaceSharedEntryForFilename(FilenameForLookup, Stat.getError());
288 return insertLocalEntryForFilename(FilenameForLookup, Entry);
291 if (
const auto *Entry = findSharedEntryByUID(*Stat))
292 return insertLocalEntryForFilename(FilenameForLookup, *Entry);
295 Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(OriginalFilename);
297 const CachedFileSystemEntry *SharedEntry = [&]() {
299 const auto &UIDEntry = getOrEmplaceSharedEntryForUID(std::move(*TEntry));
300 return &getOrInsertSharedEntryForFilename(FilenameForLookup, UIDEntry);
302 return &getOrEmplaceSharedEntryForFilename(FilenameForLookup,
306 return insertLocalEntryForFilename(FilenameForLookup, *SharedEntry);
309llvm::ErrorOr<EntryRef>
311 StringRef OriginalFilename) {
313 auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf);
314 if (!FilenameForLookup)
315 return FilenameForLookup.getError();
317 if (
const auto *Entry =
318 findEntryByFilenameWithWriteThrough(*FilenameForLookup))
320 auto MaybeEntry = computeAndStoreResult(OriginalFilename, *FilenameForLookup);
322 return MaybeEntry.getError();
326llvm::ErrorOr<llvm::vfs::Status>
329 StringRef Filename = Path.toStringRef(OwnedFilename);
331 if (shouldBypass(Filename))
332 return getUnderlyingFS().status(Path);
337 return Result->getStatus();
347 llvm::ErrorOr<llvm::vfs::Status> Status =
status(Path);
348 return Status && Status->exists();
355class DepScanFile final :
public llvm::vfs::File {
357 DepScanFile(std::unique_ptr<llvm::MemoryBuffer> Buffer,
358 llvm::vfs::Status Stat)
359 : Buffer(
std::move(Buffer)), Stat(
std::move(Stat)) {}
361 static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> create(
EntryRef Entry);
363 llvm::ErrorOr<llvm::vfs::Status> status()
override {
return Stat; }
365 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
366 getBuffer(
const Twine &Name, int64_t FileSize,
bool RequiresNullTerminator,
367 bool IsVolatile)
override {
368 return llvm::MemoryBuffer::getMemBuffer(Buffer->getMemBufferRef(),
369 RequiresNullTerminator);
372 std::error_code close()
override {
return {}; }
375 std::unique_ptr<llvm::MemoryBuffer> Buffer;
376 llvm::vfs::Status Stat;
381llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
382DepScanFile::create(
EntryRef Entry) {
383 assert(!Entry.
isError() &&
"error");
386 return std::make_error_code(std::errc::is_a_directory);
388 auto Result = std::make_unique<DepScanFile>(
389 llvm::MemoryBuffer::getMemBuffer(Entry.
getContents(),
394 return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>(
395 std::unique_ptr<llvm::vfs::File>(std::move(
Result)));
398llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
401 StringRef Filename = Path.toStringRef(OwnedFilename);
403 if (shouldBypass(Filename))
404 return getUnderlyingFS().openFileForRead(Path);
409 return DepScanFile::create(
Result.get());
416 StringRef OriginalFilename = Path.toStringRef(OwnedFilename);
418 if (shouldBypass(OriginalFilename))
419 return getUnderlyingFS().getRealPath(Path, Output);
422 auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf);
423 if (!FilenameForLookup)
424 return FilenameForLookup.getError();
426 auto HandleCachedRealPath =
429 return RealPath.getError();
430 Output.assign(RealPath->begin(), RealPath->end());
435 if (
const auto *RealPath =
436 LocalCache.findRealPathByFilename(*FilenameForLookup))
437 return HandleCachedRealPath(*RealPath);
440 auto &Shard = SharedCache.getShardForFilename(*FilenameForLookup);
441 if (
const auto *ShardRealPath =
442 Shard.findRealPathByFilename(*FilenameForLookup)) {
443 const auto &RealPath = LocalCache.insertRealPathForFilename(
444 *FilenameForLookup, *ShardRealPath);
445 return HandleCachedRealPath(RealPath);
449 std::error_code EC = getUnderlyingFS().getRealPath(OriginalFilename, Output);
450 llvm::ErrorOr<llvm::StringRef> ComputedRealPath = EC;
452 ComputedRealPath = StringRef{Output.data(), Output.size()};
457 const auto &RealPath = Shard.getOrEmplaceRealPathForFilename(
458 *FilenameForLookup, ComputedRealPath);
459 return HandleCachedRealPath(
460 LocalCache.insertRealPathForFilename(*FilenameForLookup, RealPath));
465 std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path);
466 updateWorkingDirForCacheLookup();
470void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() {
471 llvm::ErrorOr<std::string> CWD =
472 getUnderlyingFS().getCurrentWorkingDirectory();
474 WorkingDirForCacheLookup = CWD.getError();
475 }
else if (!llvm::sys::path::is_absolute_gnu(*CWD)) {
476 WorkingDirForCacheLookup = llvm::errc::invalid_argument;
478 WorkingDirForCacheLookup = *CWD;
480 assert(!WorkingDirForCacheLookup ||
481 llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup));
484llvm::ErrorOr<StringRef>
485DependencyScanningWorkerFilesystem::tryGetFilenameForLookup(
486 StringRef OriginalFilename, llvm::SmallVectorImpl<char> &PathBuf)
const {
487 StringRef FilenameForLookup;
488 if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
489 FilenameForLookup = OriginalFilename;
490 }
else if (!WorkingDirForCacheLookup) {
491 return WorkingDirForCacheLookup.getError();
493 StringRef RelFilename = OriginalFilename;
494 RelFilename.consume_front(
"./");
495 PathBuf.assign(WorkingDirForCacheLookup->begin(),
496 WorkingDirForCacheLookup->end());
497 llvm::sys::path::append(PathBuf, RelFilename);
498 FilenameForLookup = StringRef{PathBuf.begin(), PathBuf.size()};
500 assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
501 return FilenameForLookup;
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
const CachedFileSystemEntry & insertEntryForFilename(StringRef Filename, const CachedFileSystemEntry &Entry)
Associates the given entry with the filename and returns the given entry pointer (for convenience).
const CachedFileSystemEntry * findEntryByFilename(StringRef Filename) const
Returns entry associated with the filename or nullptr if none is found.
This class is a shared cache, that caches the 'stat' and 'open' calls to the underlying real file sys...
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()
std::error_code getRealPath(const Twine &Path, SmallVectorImpl< char > &Output) override
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
DependencyScanningWorkerFilesystem(DependencyScanningFilesystemSharedCache &SharedCache, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS)
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
The JSON file list parser is used to communicate input to InstallAPI.
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)
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.
const CachedFileSystemEntry & getOrEmplaceEntryForUID(llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat, std::unique_ptr< llvm::MemoryBuffer > Contents)
Returns entry associated with the unique ID if there is some.
llvm::SpecificBumpPtrAllocator< CachedFileSystemEntry > EntryStorage
The backing storage for cached entries.
llvm::SpecificBumpPtrAllocator< CachedFileContents > ContentsStorage
The backing storage for cached contents.
llvm::SpecificBumpPtrAllocator< CachedRealPath > RealPathStorage
The backing storage for cached real paths.
const CachedFileSystemEntry * findEntryByUID(llvm::sys::fs::UniqueID UID) const
Returns entry associated with the unique ID or nullptr if none is found.
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 CachedFileSystemEntry & getOrInsertEntryForFilename(StringRef Filename, const CachedFileSystemEntry &Entry)
Returns entry associated with the filename if there is some.
const CachedFileSystemEntry * findEntryByFilename(StringRef Filename) const
Returns entry associated with the filename or nullptr if none is found.
const CachedFileSystemEntry & getOrEmplaceEntryForFilename(StringRef Filename, llvm::ErrorOr< llvm::vfs::Status > Stat)
Returns entry associated with the filename if there is some.
llvm::StringMap< std::pair< const CachedFileSystemEntry *, const CachedRealPath * >, llvm::BumpPtrAllocator > CacheByFilename
Map from filenames to cached entries and real paths.
const CachedRealPath & getOrEmplaceRealPathForFilename(StringRef Filename, llvm::ErrorOr< StringRef > RealPath)
Returns the real path associated with the filename if there is some.
llvm::DenseMap< llvm::sys::fs::UniqueID, const CachedFileSystemEntry * > EntriesByUID
Map from unique IDs to cached entries.