10#include "llvm/Support/MemoryBuffer.h"
11#include "llvm/Support/Threading.h"
18llvm::ErrorOr<DependencyScanningWorkerFilesystem::TentativeEntry>
19DependencyScanningWorkerFilesystem::readFile(StringRef Filename) {
21 auto MaybeFile = getUnderlyingFS().openFileForRead(Filename);
23 return MaybeFile.getError();
24 auto File = std::move(*MaybeFile);
26 auto MaybeStat =
File->status();
28 return MaybeStat.getError();
29 auto Stat = std::move(*MaybeStat);
31 auto MaybeBuffer =
File->getBuffer(Stat.getName());
33 return MaybeBuffer.getError();
34 auto Buffer = std::move(*MaybeBuffer);
37 if (Stat.getSize() != Buffer->getBufferSize())
38 Stat = llvm::vfs::Status::copyWithNewSize(Stat, Buffer->getBufferSize());
40 return TentativeEntry(Stat, std::move(Buffer));
45 auto &Entry = Ref.Entry;
47 if (Entry.isError() || Entry.isDirectory())
51 assert(Contents &&
"contents not initialized");
57 std::lock_guard<std::mutex> GuardLock(Contents->
ValueLock);
71 Contents->
DepDirectives.store(
new std::optional<DependencyDirectivesTy>());
80 new std::optional<DependencyDirectivesTy>(std::move(Directives)));
92 std::max(2u, llvm::hardware_concurrency().compute_thread_count() / 4);
93 CacheShards = std::make_unique<CacheShard[]>(NumShards);
98 StringRef Filename)
const {
99 assert(llvm::sys::path::is_absolute_gnu(Filename));
105 llvm::sys::fs::UniqueID UID)
const {
106 auto Hash = llvm::hash_combine(UID.getDevice(), UID.getFile());
107 return CacheShards[Hash % NumShards];
110std::vector<DependencyScanningFilesystemSharedCache::OutOfDateEntry>
112 llvm::vfs::FileSystem &UnderlyingFS)
const {
114 std::vector<OutOfDateEntry> InvalidDiagInfo;
115 for (
unsigned i = 0; i < NumShards; i++) {
117 std::lock_guard<std::mutex> LockGuard(Shard.
CacheLock);
120 llvm::ErrorOr<llvm::vfs::Status> Status = UnderlyingFS.status(Path);
127 InvalidDiagInfo.emplace_back(Path.data());
129 llvm::vfs::Status CachedStatus = Entry->
getStatus();
130 if (Status->getType() == llvm::sys::fs::file_type::regular_file &&
131 Status->getType() == CachedStatus.getType()) {
139 uint64_t CachedSize = CachedStatus.getSize();
140 uint64_t ActualSize = Status->getSize();
141 if (CachedSize != ActualSize) {
144 InvalidDiagInfo.emplace_back(Path.data(), CachedSize, ActualSize);
151 return InvalidDiagInfo;
156 StringRef Filename)
const {
157 assert(llvm::sys::path::is_absolute_gnu(Filename));
158 std::lock_guard<std::mutex> LockGuard(
CacheLock);
165 llvm::sys::fs::UniqueID UID)
const {
166 std::lock_guard<std::mutex> LockGuard(
CacheLock);
174 llvm::ErrorOr<llvm::vfs::Status> Stat) {
175 std::lock_guard<std::mutex> LockGuard(
CacheLock);
176 auto [It, Inserted] =
CacheByFilename.insert({Filename, {
nullptr,
nullptr}});
181 assert((Inserted ||
CachedRealPath) &&
"existing file with empty pair");
190 llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat,
191 std::unique_ptr<llvm::MemoryBuffer> Contents) {
192 std::lock_guard<std::mutex> LockGuard(
CacheLock);
194 auto &CachedEntry = It->getSecond();
210 std::lock_guard<std::mutex> LockGuard(
CacheLock);
211 auto [It, Inserted] =
CacheByFilename.insert({Filename, {&Entry,
nullptr}});
213 if (!Inserted || !CachedEntry)
214 CachedEntry = &Entry;
220 StringRef Filename)
const {
221 assert(llvm::sys::path::is_absolute_gnu(Filename));
222 std::lock_guard<std::mutex> LockGuard(
CacheLock);
229 llvm::ErrorOr<llvm::StringRef> RealPath) {
230 std::lock_guard<std::mutex> LockGuard(
CacheLock);
233 if (!StoredRealPath) {
236 return RealPath.getError();
237 return RealPath->str();
244 return *StoredRealPath;
247bool DependencyScanningWorkerFilesystem::shouldBypass(StringRef Path)
const {
248 return BypassedPathPrefix && Path.starts_with(*BypassedPathPrefix);
256 SharedCache(SharedCache),
257 WorkingDirForCacheLookup(
llvm::errc::invalid_argument) {
258 updateWorkingDirForCacheLookup();
262DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID(
263 TentativeEntry TEntry) {
264 auto &Shard = SharedCache.
getShardForUID(TEntry.Status.getUniqueID());
266 std::move(TEntry.Status),
267 std::move(TEntry.Contents));
271DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough(
272 StringRef Filename) {
276 if (
const auto *Entry = Shard.findEntryByFilename(Filename))
281llvm::ErrorOr<const CachedFileSystemEntry &>
282DependencyScanningWorkerFilesystem::computeAndStoreResult(
283 StringRef OriginalFilename, StringRef FilenameForLookup) {
284 llvm::ErrorOr<llvm::vfs::Status> Stat =
285 getUnderlyingFS().status(OriginalFilename);
288 getOrEmplaceSharedEntryForFilename(FilenameForLookup, Stat.getError());
289 return insertLocalEntryForFilename(FilenameForLookup, Entry);
292 if (
const auto *Entry = findSharedEntryByUID(*Stat))
293 return insertLocalEntryForFilename(FilenameForLookup, *Entry);
296 Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(OriginalFilename);
300 const auto &UIDEntry = getOrEmplaceSharedEntryForUID(std::move(*TEntry));
301 return &getOrInsertSharedEntryForFilename(FilenameForLookup, UIDEntry);
303 return &getOrEmplaceSharedEntryForFilename(FilenameForLookup,
307 return insertLocalEntryForFilename(FilenameForLookup, *SharedEntry);
310llvm::ErrorOr<EntryRef>
312 StringRef OriginalFilename) {
314 auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf);
315 if (!FilenameForLookup)
316 return FilenameForLookup.getError();
318 if (
const auto *Entry =
319 findEntryByFilenameWithWriteThrough(*FilenameForLookup))
321 auto MaybeEntry = computeAndStoreResult(OriginalFilename, *FilenameForLookup);
323 return MaybeEntry.getError();
327llvm::ErrorOr<llvm::vfs::Status>
330 StringRef Filename = Path.toStringRef(OwnedFilename);
332 if (shouldBypass(Filename))
333 return getUnderlyingFS().status(Path);
338 return Result->getStatus();
348 llvm::ErrorOr<llvm::vfs::Status> Status =
status(Path);
349 return Status && Status->exists();
356class DepScanFile final :
public llvm::vfs::File {
358 DepScanFile(std::unique_ptr<llvm::MemoryBuffer> Buffer,
359 llvm::vfs::Status Stat)
360 : Buffer(
std::move(Buffer)), Stat(
std::move(Stat)) {}
362 static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> create(
EntryRef Entry);
364 llvm::ErrorOr<llvm::vfs::Status> status()
override {
return Stat; }
366 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
367 getBuffer(
const Twine &Name, int64_t FileSize,
bool RequiresNullTerminator,
368 bool IsVolatile)
override {
369 return llvm::MemoryBuffer::getMemBuffer(Buffer->getMemBufferRef(),
370 RequiresNullTerminator);
373 std::error_code close()
override {
return {}; }
376 std::unique_ptr<llvm::MemoryBuffer> Buffer;
377 llvm::vfs::Status Stat;
382llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
383DepScanFile::create(
EntryRef Entry) {
384 assert(!Entry.
isError() &&
"error");
387 return std::make_error_code(std::errc::is_a_directory);
389 auto Result = std::make_unique<DepScanFile>(
390 llvm::MemoryBuffer::getMemBuffer(Entry.
getContents(),
395 return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>(
396 std::unique_ptr<llvm::vfs::File>(std::move(
Result)));
399llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
402 StringRef Filename = Path.toStringRef(OwnedFilename);
404 if (shouldBypass(Filename))
405 return getUnderlyingFS().openFileForRead(Path);
410 return DepScanFile::create(
Result.get());
417 StringRef OriginalFilename = Path.toStringRef(OwnedFilename);
419 if (shouldBypass(OriginalFilename))
420 return getUnderlyingFS().getRealPath(Path, Output);
423 auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf);
424 if (!FilenameForLookup)
425 return FilenameForLookup.getError();
427 auto HandleCachedRealPath =
430 return RealPath.getError();
431 Output.assign(RealPath->begin(), RealPath->end());
436 if (
const auto *RealPath =
437 LocalCache.findRealPathByFilename(*FilenameForLookup))
438 return HandleCachedRealPath(*RealPath);
441 auto &Shard = SharedCache.getShardForFilename(*FilenameForLookup);
442 if (
const auto *ShardRealPath =
443 Shard.findRealPathByFilename(*FilenameForLookup)) {
444 const auto &RealPath = LocalCache.insertRealPathForFilename(
445 *FilenameForLookup, *ShardRealPath);
446 return HandleCachedRealPath(RealPath);
450 std::error_code EC = getUnderlyingFS().getRealPath(OriginalFilename, Output);
451 llvm::ErrorOr<llvm::StringRef> ComputedRealPath = EC;
453 ComputedRealPath = StringRef{Output.data(), Output.size()};
458 const auto &RealPath = Shard.getOrEmplaceRealPathForFilename(
459 *FilenameForLookup, ComputedRealPath);
460 return HandleCachedRealPath(
461 LocalCache.insertRealPathForFilename(*FilenameForLookup, RealPath));
466 std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path);
467 updateWorkingDirForCacheLookup();
471void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() {
472 llvm::ErrorOr<std::string> CWD =
473 getUnderlyingFS().getCurrentWorkingDirectory();
475 WorkingDirForCacheLookup = CWD.getError();
476 }
else if (!llvm::sys::path::is_absolute_gnu(*CWD)) {
477 WorkingDirForCacheLookup = llvm::errc::invalid_argument;
479 WorkingDirForCacheLookup = *CWD;
481 assert(!WorkingDirForCacheLookup ||
482 llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup));
485llvm::ErrorOr<StringRef>
486DependencyScanningWorkerFilesystem::tryGetFilenameForLookup(
487 StringRef OriginalFilename, llvm::SmallVectorImpl<char> &PathBuf)
const {
488 StringRef FilenameForLookup;
489 if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
490 FilenameForLookup = OriginalFilename;
491 }
else if (!WorkingDirForCacheLookup) {
492 return WorkingDirForCacheLookup.getError();
494 StringRef RelFilename = OriginalFilename;
495 RelFilename.consume_front(
"./");
496 PathBuf.assign(WorkingDirForCacheLookup->begin(),
497 WorkingDirForCacheLookup->end());
498 llvm::sys::path::append(PathBuf, RelFilename);
499 FilenameForLookup = StringRef{PathBuf.begin(), PathBuf.size()};
501 assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
502 return FilenameForLookup;
An in-memory representation of a file system entity that is of interest to the dependency scanning fi...
std::error_code getError() const
llvm::vfs::Status getStatus() const
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.
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::tooling::dependencies::ModuleID &ID)
Contents and directive tokens of a cached file entry.
std::mutex ValueLock
The mutex that must be locked before mutating directive tokens.
std::atomic< const std::optional< DependencyDirectivesTy > * > DepDirectives
Accessor to the directive tokens that's atomic to avoid data races.
std::unique_ptr< llvm::MemoryBuffer > Original
Owning storage for the original contents.
SmallVector< dependency_directives_scan::Token, 10 > DepDirectiveTokens