10#include "llvm/Support/MemoryBuffer.h"
11#include "llvm/Support/SmallVectorMemoryBuffer.h"
12#include "llvm/Support/Threading.h"
16using namespace tooling;
17using namespace dependencies;
19llvm::ErrorOr<DependencyScanningWorkerFilesystem::TentativeEntry>
20DependencyScanningWorkerFilesystem::readFile(StringRef
Filename) {
22 auto MaybeFile = getUnderlyingFS().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);
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];
114 assert(llvm::sys::path::is_absolute_gnu(
Filename));
115 std::lock_guard<std::mutex> LockGuard(
CacheLock);
122 llvm::sys::fs::UniqueID UID)
const {
123 std::lock_guard<std::mutex> LockGuard(CacheLock);
124 auto It = EntriesByUID.find(UID);
125 return It == EntriesByUID.end() ? nullptr : It->getSecond();
131 llvm::ErrorOr<llvm::vfs::Status> Stat) {
132 std::lock_guard<std::mutex> LockGuard(CacheLock);
133 auto [It, Inserted] = CacheByFilename.insert({
Filename, {
nullptr,
nullptr}});
138 assert((Inserted ||
CachedRealPath) &&
"existing file with empty pair");
147 llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat,
148 std::unique_ptr<llvm::MemoryBuffer> Contents) {
149 std::lock_guard<std::mutex> LockGuard(CacheLock);
150 auto [It, Inserted] = EntriesByUID.insert({UID,
nullptr});
151 auto &CachedEntry = It->getSecond();
155 StoredContents =
new (ContentsStorage.Allocate())
157 CachedEntry =
new (EntryStorage.Allocate())
167 std::lock_guard<std::mutex> LockGuard(CacheLock);
168 auto [It, Inserted] = CacheByFilename.insert({
Filename, {&Entry,
nullptr}});
170 if (!Inserted || !CachedEntry)
171 CachedEntry = &Entry;
178 assert(llvm::sys::path::is_absolute_gnu(
Filename));
179 std::lock_guard<std::mutex> LockGuard(CacheLock);
180 auto It = CacheByFilename.find(
Filename);
181 return It == CacheByFilename.end() ? nullptr : It->getValue().second;
186 llvm::ErrorOr<llvm::StringRef> RealPath) {
187 std::lock_guard<std::mutex> LockGuard(CacheLock);
190 if (!StoredRealPath) {
193 return RealPath.getError();
194 return RealPath->str();
197 StoredRealPath =
new (RealPathStorage.Allocate())
201 return *StoredRealPath;
205 StringRef Ext = llvm::sys::path::extension(
Filename);
215 llvm::vfs::ProxyFileSystem>(
std::move(FS)),
216 SharedCache(SharedCache),
217 WorkingDirForCacheLookup(
llvm::errc::invalid_argument) {
218 updateWorkingDirForCacheLookup();
222DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID(
223 TentativeEntry TEntry) {
224 auto &Shard = SharedCache.
getShardForUID(TEntry.Status.getUniqueID());
226 std::move(TEntry.Status),
227 std::move(TEntry.Contents));
231DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough(
236 if (
const auto *Entry = Shard.findEntryByFilename(
Filename))
241llvm::ErrorOr<const CachedFileSystemEntry &>
242DependencyScanningWorkerFilesystem::computeAndStoreResult(
243 StringRef OriginalFilename, StringRef FilenameForLookup) {
244 llvm::ErrorOr<llvm::vfs::Status> Stat =
245 getUnderlyingFS().status(OriginalFilename);
250 getOrEmplaceSharedEntryForFilename(FilenameForLookup, Stat.getError());
251 return insertLocalEntryForFilename(FilenameForLookup, Entry);
254 if (
const auto *Entry = findSharedEntryByUID(*Stat))
255 return insertLocalEntryForFilename(FilenameForLookup, *Entry);
258 Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(OriginalFilename);
262 const auto &UIDEntry = getOrEmplaceSharedEntryForUID(std::move(*TEntry));
263 return &getOrInsertSharedEntryForFilename(FilenameForLookup, UIDEntry);
265 return &getOrEmplaceSharedEntryForFilename(FilenameForLookup,
269 return insertLocalEntryForFilename(FilenameForLookup, *SharedEntry);
272llvm::ErrorOr<EntryRef>
274 StringRef OriginalFilename) {
276 auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf);
277 if (!FilenameForLookup)
278 return FilenameForLookup.getError();
280 if (
const auto *Entry =
281 findEntryByFilenameWithWriteThrough(*FilenameForLookup))
283 auto MaybeEntry = computeAndStoreResult(OriginalFilename, *FilenameForLookup);
285 return MaybeEntry.getError();
289llvm::ErrorOr<llvm::vfs::Status>
292 StringRef
Filename = Path.toStringRef(OwnedFilename);
295 return getUnderlyingFS().status(Path);
300 return Result->getStatus();
310 llvm::ErrorOr<llvm::vfs::Status> Status =
status(Path);
311 return Status && Status->exists();
318class DepScanFile final :
public llvm::vfs::File {
320 DepScanFile(std::unique_ptr<llvm::MemoryBuffer> Buffer,
321 llvm::vfs::Status Stat)
322 : Buffer(
std::move(Buffer)), Stat(
std::move(Stat)) {}
324 static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> create(
EntryRef Entry);
326 llvm::ErrorOr<llvm::vfs::Status> status()
override {
return Stat; }
328 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
329 getBuffer(
const Twine &Name, int64_t FileSize,
bool RequiresNullTerminator,
330 bool IsVolatile)
override {
331 return std::move(Buffer);
334 std::error_code close()
override {
return {}; }
337 std::unique_ptr<llvm::MemoryBuffer> Buffer;
338 llvm::vfs::Status Stat;
343llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
344DepScanFile::create(
EntryRef Entry) {
345 assert(!Entry.
isError() &&
"error");
348 return std::make_error_code(std::errc::is_a_directory);
350 auto Result = std::make_unique<DepScanFile>(
351 llvm::MemoryBuffer::getMemBuffer(Entry.
getContents(),
356 return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>(
357 std::unique_ptr<llvm::vfs::File>(std::move(
Result)));
360llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
363 StringRef
Filename = Path.toStringRef(OwnedFilename);
366 return getUnderlyingFS().openFileForRead(Path);
371 return DepScanFile::create(
Result.get());
378 StringRef OriginalFilename = Path.toStringRef(OwnedFilename);
381 auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf);
382 if (!FilenameForLookup)
383 return FilenameForLookup.getError();
385 auto HandleCachedRealPath =
388 return RealPath.getError();
389 Output.assign(RealPath->begin(), RealPath->end());
394 if (
const auto *RealPath =
396 return HandleCachedRealPath(*RealPath);
400 if (
const auto *ShardRealPath =
401 Shard.findRealPathByFilename(*FilenameForLookup)) {
403 *FilenameForLookup, *ShardRealPath);
404 return HandleCachedRealPath(RealPath);
408 std::error_code EC = getUnderlyingFS().getRealPath(OriginalFilename, Output);
409 llvm::ErrorOr<llvm::StringRef> ComputedRealPath = EC;
411 ComputedRealPath = StringRef{Output.data(), Output.size()};
416 const auto &RealPath = Shard.getOrEmplaceRealPathForFilename(
417 *FilenameForLookup, ComputedRealPath);
418 return HandleCachedRealPath(
424 std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path);
425 updateWorkingDirForCacheLookup();
429void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() {
430 llvm::ErrorOr<std::string> CWD =
431 getUnderlyingFS().getCurrentWorkingDirectory();
433 WorkingDirForCacheLookup = CWD.getError();
434 }
else if (!llvm::sys::path::is_absolute_gnu(*CWD)) {
435 WorkingDirForCacheLookup = llvm::errc::invalid_argument;
437 WorkingDirForCacheLookup = *CWD;
439 assert(!WorkingDirForCacheLookup ||
440 llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup));
443llvm::ErrorOr<StringRef>
444DependencyScanningWorkerFilesystem::tryGetFilenameForLookup(
446 StringRef FilenameForLookup;
447 if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
448 FilenameForLookup = OriginalFilename;
449 }
else if (!WorkingDirForCacheLookup) {
450 return WorkingDirForCacheLookup.getError();
452 StringRef RelFilename = OriginalFilename;
453 RelFilename.consume_front(
"./");
454 PathBuf.assign(WorkingDirForCacheLookup->begin(),
455 WorkingDirForCacheLookup->end());
456 llvm::sys::path::append(PathBuf, RelFilename);
457 FilenameForLookup = StringRef{PathBuf.begin(), PathBuf.size()};
459 assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
460 return FilenameForLookup;
static bool shouldCacheStatFailures(StringRef Filename)
An in-memory representation of a file system entity that is of interest to the dependency scanning fi...
std::error_code getError() 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.
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