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));
44EntryRef DependencyScanningWorkerFilesystem::scanForDirectivesIfNecessary(
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);
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];
113 assert(llvm::sys::path::is_absolute_gnu(
Filename));
114 std::lock_guard<std::mutex> LockGuard(
CacheLock);
121 llvm::sys::fs::UniqueID UID)
const {
122 std::lock_guard<std::mutex> LockGuard(CacheLock);
123 auto It = EntriesByUID.find(UID);
124 return It == EntriesByUID.end() ? nullptr : It->getSecond();
130 llvm::ErrorOr<llvm::vfs::Status> Stat) {
131 std::lock_guard<std::mutex> LockGuard(CacheLock);
132 auto Insertion = EntriesByFilename.insert({
Filename,
nullptr});
133 if (Insertion.second)
134 Insertion.first->second =
136 return *Insertion.first->second;
141 llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat,
142 std::unique_ptr<llvm::MemoryBuffer> Contents) {
143 std::lock_guard<std::mutex> LockGuard(CacheLock);
144 auto Insertion = EntriesByUID.insert({UID,
nullptr});
145 if (Insertion.second) {
148 StoredContents =
new (ContentsStorage.Allocate())
150 Insertion.first->second =
new (EntryStorage.Allocate())
153 return *Insertion.first->second;
160 std::lock_guard<std::mutex> LockGuard(CacheLock);
161 return *EntriesByFilename.insert({
Filename, &Entry}).first->getValue();
170 StringRef Ext = llvm::sys::path::extension(
Filename);
173 return llvm::StringSwitch<bool>(Ext)
174 .CasesLower(
".c",
".cc",
".cpp",
".c++",
".cxx",
true)
175 .CasesLower(
".h",
".hh",
".hpp",
".h++",
".hxx",
true)
176 .CasesLower(
".m",
".mm",
true)
177 .CasesLower(
".i",
".ii",
".mi",
".mmi",
true)
178 .CasesLower(
".def",
".inc",
true)
183 StringRef Ext = llvm::sys::path::extension(
Filename);
188 StringRef FName = llvm::sys::path::filename(
Filename);
189 if (FName ==
"module.modulemap" || FName ==
"module.map")
197 : ProxyFileSystem(
std::move(FS)), SharedCache(SharedCache),
198 WorkingDirForCacheLookup(
llvm::errc::invalid_argument) {
199 updateWorkingDirForCacheLookup();
202bool DependencyScanningWorkerFilesystem::shouldScanForDirectives(
208DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID(
209 TentativeEntry TEntry) {
210 auto &Shard = SharedCache.
getShardForUID(TEntry.Status.getUniqueID());
212 std::move(TEntry.Status),
213 std::move(TEntry.Contents));
217DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough(
222 if (
const auto *Entry = Shard.findEntryByFilename(
Filename))
227llvm::ErrorOr<const CachedFileSystemEntry &>
228DependencyScanningWorkerFilesystem::computeAndStoreResult(
229 StringRef OriginalFilename, StringRef FilenameForLookup) {
230 llvm::ErrorOr<llvm::vfs::Status> Stat =
231 getUnderlyingFS().status(OriginalFilename);
236 getOrEmplaceSharedEntryForFilename(FilenameForLookup, Stat.getError());
237 return insertLocalEntryForFilename(FilenameForLookup, Entry);
240 if (
const auto *Entry = findSharedEntryByUID(*Stat))
241 return insertLocalEntryForFilename(FilenameForLookup, *Entry);
244 Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(OriginalFilename);
248 const auto &UIDEntry = getOrEmplaceSharedEntryForUID(std::move(*TEntry));
249 return &getOrInsertSharedEntryForFilename(FilenameForLookup, UIDEntry);
251 return &getOrEmplaceSharedEntryForFilename(FilenameForLookup,
255 return insertLocalEntryForFilename(FilenameForLookup, *SharedEntry);
258llvm::ErrorOr<EntryRef>
260 StringRef OriginalFilename,
bool DisableDirectivesScanning) {
261 StringRef FilenameForLookup;
263 if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
264 FilenameForLookup = OriginalFilename;
265 }
else if (!WorkingDirForCacheLookup) {
266 return WorkingDirForCacheLookup.getError();
268 StringRef RelFilename = OriginalFilename;
269 RelFilename.consume_front(
"./");
270 PathBuf = *WorkingDirForCacheLookup;
271 llvm::sys::path::append(PathBuf, RelFilename);
272 FilenameForLookup = PathBuf.str();
274 assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
275 if (
const auto *Entry =
276 findEntryByFilenameWithWriteThrough(FilenameForLookup))
277 return scanForDirectivesIfNecessary(*Entry, OriginalFilename,
278 DisableDirectivesScanning)
280 auto MaybeEntry = computeAndStoreResult(OriginalFilename, FilenameForLookup);
282 return MaybeEntry.getError();
283 return scanForDirectivesIfNecessary(*MaybeEntry, OriginalFilename,
284 DisableDirectivesScanning)
288llvm::ErrorOr<llvm::vfs::Status>
291 StringRef
Filename = Path.toStringRef(OwnedFilename);
294 return getUnderlyingFS().status(Path);
299 return Result->getStatus();
306class DepScanFile final :
public llvm::vfs::File {
308 DepScanFile(std::unique_ptr<llvm::MemoryBuffer> Buffer,
309 llvm::vfs::Status Stat)
310 : Buffer(
std::move(Buffer)), Stat(
std::move(Stat)) {}
312 static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> create(
EntryRef Entry);
314 llvm::ErrorOr<llvm::vfs::Status> status()
override {
return Stat; }
316 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
317 getBuffer(
const Twine &Name, int64_t FileSize,
bool RequiresNullTerminator,
318 bool IsVolatile)
override {
319 return std::move(Buffer);
322 std::error_code close()
override {
return {}; }
325 std::unique_ptr<llvm::MemoryBuffer> Buffer;
326 llvm::vfs::Status Stat;
331llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
332DepScanFile::create(
EntryRef Entry) {
333 assert(!Entry.
isError() &&
"error");
336 return std::make_error_code(std::errc::is_a_directory);
338 auto Result = std::make_unique<DepScanFile>(
339 llvm::MemoryBuffer::getMemBuffer(Entry.
getContents(),
344 return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>(
345 std::unique_ptr<llvm::vfs::File>(std::move(
Result)));
348llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
351 StringRef
Filename = Path.toStringRef(OwnedFilename);
354 return getUnderlyingFS().openFileForRead(Path);
359 return DepScanFile::create(
Result.get());
364 std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path);
365 updateWorkingDirForCacheLookup();
369void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() {
370 llvm::ErrorOr<std::string> CWD =
371 getUnderlyingFS().getCurrentWorkingDirectory();
373 WorkingDirForCacheLookup = CWD.getError();
374 }
else if (!llvm::sys::path::is_absolute_gnu(*CWD)) {
375 WorkingDirForCacheLookup = llvm::errc::invalid_argument;
377 WorkingDirForCacheLookup = *CWD;
379 assert(!WorkingDirForCacheLookup ||
380 llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup));
static bool shouldCacheStatFailures(StringRef Filename)
static bool shouldScanForDirectivesBasedOnExtension(StringRef Filename)
Whitelist file extensions that should be minimized, treating no extension as a source file that shoul...
An in-memory representation of a file system entity that is of interest to the dependency scanning fi...
std::error_code getError() const
CachedFileContents * getCachedContents() 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.
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.
YAML serialization mapping.
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