1a7dea167SDimitry Andric //===- DependencyScanningFilesystem.cpp - clang-scan-deps fs --------------===// 2a7dea167SDimitry Andric // 3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a7dea167SDimitry Andric // 7a7dea167SDimitry Andric //===----------------------------------------------------------------------===// 8a7dea167SDimitry Andric 9a7dea167SDimitry Andric #include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h" 10a7dea167SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 110eae32dcSDimitry Andric #include "llvm/Support/SmallVectorMemoryBuffer.h" 12a7dea167SDimitry Andric #include "llvm/Support/Threading.h" 13bdd1243dSDimitry Andric #include <optional> 14a7dea167SDimitry Andric 15a7dea167SDimitry Andric using namespace clang; 16a7dea167SDimitry Andric using namespace tooling; 17a7dea167SDimitry Andric using namespace dependencies; 18a7dea167SDimitry Andric 1904eeddc0SDimitry Andric llvm::ErrorOr<DependencyScanningWorkerFilesystem::TentativeEntry> 2004eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::readFile(StringRef Filename) { 21a7dea167SDimitry Andric // Load the file and its content from the file system. 2204eeddc0SDimitry Andric auto MaybeFile = getUnderlyingFS().openFileForRead(Filename); 23a7dea167SDimitry Andric if (!MaybeFile) 24a7dea167SDimitry Andric return MaybeFile.getError(); 250eae32dcSDimitry Andric auto File = std::move(*MaybeFile); 26a7dea167SDimitry Andric 270eae32dcSDimitry Andric auto MaybeStat = File->status(); 280eae32dcSDimitry Andric if (!MaybeStat) 290eae32dcSDimitry Andric return MaybeStat.getError(); 300eae32dcSDimitry Andric auto Stat = std::move(*MaybeStat); 310eae32dcSDimitry Andric 320eae32dcSDimitry Andric auto MaybeBuffer = File->getBuffer(Stat.getName()); 33a7dea167SDimitry Andric if (!MaybeBuffer) 34a7dea167SDimitry Andric return MaybeBuffer.getError(); 350eae32dcSDimitry Andric auto Buffer = std::move(*MaybeBuffer); 360eae32dcSDimitry Andric 3704eeddc0SDimitry Andric // If the file size changed between read and stat, pretend it didn't. 3804eeddc0SDimitry Andric if (Stat.getSize() != Buffer->getBufferSize()) 3904eeddc0SDimitry Andric Stat = llvm::vfs::Status::copyWithNewSize(Stat, Buffer->getBufferSize()); 4004eeddc0SDimitry Andric 4104eeddc0SDimitry Andric return TentativeEntry(Stat, std::move(Buffer)); 420eae32dcSDimitry Andric } 430eae32dcSDimitry Andric 44*0fca6ea1SDimitry Andric bool DependencyScanningWorkerFilesystem::ensureDirectiveTokensArePopulated( 45*0fca6ea1SDimitry Andric EntryRef Ref) { 46*0fca6ea1SDimitry Andric auto &Entry = Ref.Entry; 47*0fca6ea1SDimitry Andric 48*0fca6ea1SDimitry Andric if (Entry.isError() || Entry.isDirectory()) 49*0fca6ea1SDimitry Andric return false; 5004eeddc0SDimitry Andric 5181ad6265SDimitry Andric CachedFileContents *Contents = Entry.getCachedContents(); 5204eeddc0SDimitry Andric assert(Contents && "contents not initialized"); 5304eeddc0SDimitry Andric 5404eeddc0SDimitry Andric // Double-checked locking. 5581ad6265SDimitry Andric if (Contents->DepDirectives.load()) 56*0fca6ea1SDimitry Andric return true; 5704eeddc0SDimitry Andric 5804eeddc0SDimitry Andric std::lock_guard<std::mutex> GuardLock(Contents->ValueLock); 5904eeddc0SDimitry Andric 6004eeddc0SDimitry Andric // Double-checked locking. 6181ad6265SDimitry Andric if (Contents->DepDirectives.load()) 62*0fca6ea1SDimitry Andric return true; 63a7dea167SDimitry Andric 6481ad6265SDimitry Andric SmallVector<dependency_directives_scan::Directive, 64> Directives; 6581ad6265SDimitry Andric // Scan the file for preprocessor directives that might affect the 6681ad6265SDimitry Andric // dependencies. 6781ad6265SDimitry Andric if (scanSourceForDependencyDirectives(Contents->Original->getBuffer(), 6881ad6265SDimitry Andric Contents->DepDirectiveTokens, 6981ad6265SDimitry Andric Directives)) { 7081ad6265SDimitry Andric Contents->DepDirectiveTokens.clear(); 710eae32dcSDimitry Andric // FIXME: Propagate the diagnostic if desired by the client. 72bdd1243dSDimitry Andric Contents->DepDirectives.store(new std::optional<DependencyDirectivesTy>()); 73*0fca6ea1SDimitry Andric return false; 74a7dea167SDimitry Andric } 75a7dea167SDimitry Andric 7681ad6265SDimitry Andric // This function performed double-checked locking using `DepDirectives`. 7781ad6265SDimitry Andric // Assigning it must be the last thing this function does, otherwise other 78*0fca6ea1SDimitry Andric // threads may skip the critical section (`DepDirectives != nullptr`), leading 79*0fca6ea1SDimitry Andric // to a data race. 8081ad6265SDimitry Andric Contents->DepDirectives.store( 81bdd1243dSDimitry Andric new std::optional<DependencyDirectivesTy>(std::move(Directives))); 82*0fca6ea1SDimitry Andric return true; 83a7dea167SDimitry Andric } 84a7dea167SDimitry Andric 850eae32dcSDimitry Andric DependencyScanningFilesystemSharedCache:: 860eae32dcSDimitry Andric DependencyScanningFilesystemSharedCache() { 87a7dea167SDimitry Andric // This heuristic was chosen using a empirical testing on a 88a7dea167SDimitry Andric // reasonably high core machine (iMacPro 18 cores / 36 threads). The cache 89a7dea167SDimitry Andric // sharding gives a performance edge by reducing the lock contention. 90a7dea167SDimitry Andric // FIXME: A better heuristic might also consider the OS to account for 91a7dea167SDimitry Andric // the different cost of lock contention on different OSes. 925ffd83dbSDimitry Andric NumShards = 935ffd83dbSDimitry Andric std::max(2u, llvm::hardware_concurrency().compute_thread_count() / 4); 94a7dea167SDimitry Andric CacheShards = std::make_unique<CacheShard[]>(NumShards); 95a7dea167SDimitry Andric } 96a7dea167SDimitry Andric 9704eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard & 9804eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::getShardForFilename( 9904eeddc0SDimitry Andric StringRef Filename) const { 1004542f901SDimitry Andric assert(llvm::sys::path::is_absolute_gnu(Filename)); 10104eeddc0SDimitry Andric return CacheShards[llvm::hash_value(Filename) % NumShards]; 10204eeddc0SDimitry Andric } 10304eeddc0SDimitry Andric 10404eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard & 10504eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::getShardForUID( 10604eeddc0SDimitry Andric llvm::sys::fs::UniqueID UID) const { 10704eeddc0SDimitry Andric auto Hash = llvm::hash_combine(UID.getDevice(), UID.getFile()); 10804eeddc0SDimitry Andric return CacheShards[Hash % NumShards]; 10904eeddc0SDimitry Andric } 11004eeddc0SDimitry Andric 11104eeddc0SDimitry Andric const CachedFileSystemEntry * 11204eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::findEntryByFilename( 11304eeddc0SDimitry Andric StringRef Filename) const { 1144542f901SDimitry Andric assert(llvm::sys::path::is_absolute_gnu(Filename)); 11504eeddc0SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 116*0fca6ea1SDimitry Andric auto It = CacheByFilename.find(Filename); 117*0fca6ea1SDimitry Andric return It == CacheByFilename.end() ? nullptr : It->getValue().first; 11804eeddc0SDimitry Andric } 11904eeddc0SDimitry Andric 12004eeddc0SDimitry Andric const CachedFileSystemEntry * 12104eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::findEntryByUID( 12204eeddc0SDimitry Andric llvm::sys::fs::UniqueID UID) const { 12304eeddc0SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 12404eeddc0SDimitry Andric auto It = EntriesByUID.find(UID); 12504eeddc0SDimitry Andric return It == EntriesByUID.end() ? nullptr : It->getSecond(); 12604eeddc0SDimitry Andric } 12704eeddc0SDimitry Andric 12804eeddc0SDimitry Andric const CachedFileSystemEntry & 12904eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard:: 13004eeddc0SDimitry Andric getOrEmplaceEntryForFilename(StringRef Filename, 13104eeddc0SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> Stat) { 13204eeddc0SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 133*0fca6ea1SDimitry Andric auto [It, Inserted] = CacheByFilename.insert({Filename, {nullptr, nullptr}}); 134*0fca6ea1SDimitry Andric auto &[CachedEntry, CachedRealPath] = It->getValue(); 135*0fca6ea1SDimitry Andric if (!CachedEntry) { 136*0fca6ea1SDimitry Andric // The entry is not present in the shared cache. Either the cache doesn't 137*0fca6ea1SDimitry Andric // know about the file at all, or it only knows about its real path. 138*0fca6ea1SDimitry Andric assert((Inserted || CachedRealPath) && "existing file with empty pair"); 139*0fca6ea1SDimitry Andric CachedEntry = 14004eeddc0SDimitry Andric new (EntryStorage.Allocate()) CachedFileSystemEntry(std::move(Stat)); 141*0fca6ea1SDimitry Andric } 142*0fca6ea1SDimitry Andric return *CachedEntry; 14304eeddc0SDimitry Andric } 14404eeddc0SDimitry Andric 14504eeddc0SDimitry Andric const CachedFileSystemEntry & 14604eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::getOrEmplaceEntryForUID( 14704eeddc0SDimitry Andric llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat, 14804eeddc0SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Contents) { 14904eeddc0SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 150*0fca6ea1SDimitry Andric auto [It, Inserted] = EntriesByUID.insert({UID, nullptr}); 151*0fca6ea1SDimitry Andric auto &CachedEntry = It->getSecond(); 152*0fca6ea1SDimitry Andric if (Inserted) { 15304eeddc0SDimitry Andric CachedFileContents *StoredContents = nullptr; 15404eeddc0SDimitry Andric if (Contents) 15504eeddc0SDimitry Andric StoredContents = new (ContentsStorage.Allocate()) 15604eeddc0SDimitry Andric CachedFileContents(std::move(Contents)); 157*0fca6ea1SDimitry Andric CachedEntry = new (EntryStorage.Allocate()) 15804eeddc0SDimitry Andric CachedFileSystemEntry(std::move(Stat), StoredContents); 15904eeddc0SDimitry Andric } 160*0fca6ea1SDimitry Andric return *CachedEntry; 16104eeddc0SDimitry Andric } 16204eeddc0SDimitry Andric 16304eeddc0SDimitry Andric const CachedFileSystemEntry & 16404eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard:: 16504eeddc0SDimitry Andric getOrInsertEntryForFilename(StringRef Filename, 16604eeddc0SDimitry Andric const CachedFileSystemEntry &Entry) { 16704eeddc0SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 168*0fca6ea1SDimitry Andric auto [It, Inserted] = CacheByFilename.insert({Filename, {&Entry, nullptr}}); 169*0fca6ea1SDimitry Andric auto &[CachedEntry, CachedRealPath] = It->getValue(); 170*0fca6ea1SDimitry Andric if (!Inserted || !CachedEntry) 171*0fca6ea1SDimitry Andric CachedEntry = &Entry; 172*0fca6ea1SDimitry Andric return *CachedEntry; 173a7dea167SDimitry Andric } 174a7dea167SDimitry Andric 175*0fca6ea1SDimitry Andric const CachedRealPath * 176*0fca6ea1SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::findRealPathByFilename( 177*0fca6ea1SDimitry Andric StringRef Filename) const { 178*0fca6ea1SDimitry Andric assert(llvm::sys::path::is_absolute_gnu(Filename)); 179*0fca6ea1SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 180*0fca6ea1SDimitry Andric auto It = CacheByFilename.find(Filename); 181*0fca6ea1SDimitry Andric return It == CacheByFilename.end() ? nullptr : It->getValue().second; 182*0fca6ea1SDimitry Andric } 183*0fca6ea1SDimitry Andric 184*0fca6ea1SDimitry Andric const CachedRealPath &DependencyScanningFilesystemSharedCache::CacheShard:: 185*0fca6ea1SDimitry Andric getOrEmplaceRealPathForFilename(StringRef Filename, 186*0fca6ea1SDimitry Andric llvm::ErrorOr<llvm::StringRef> RealPath) { 187*0fca6ea1SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 188*0fca6ea1SDimitry Andric 189*0fca6ea1SDimitry Andric const CachedRealPath *&StoredRealPath = CacheByFilename[Filename].second; 190*0fca6ea1SDimitry Andric if (!StoredRealPath) { 191*0fca6ea1SDimitry Andric auto OwnedRealPath = [&]() -> CachedRealPath { 192*0fca6ea1SDimitry Andric if (!RealPath) 193*0fca6ea1SDimitry Andric return RealPath.getError(); 194*0fca6ea1SDimitry Andric return RealPath->str(); 195*0fca6ea1SDimitry Andric }(); 196*0fca6ea1SDimitry Andric 197*0fca6ea1SDimitry Andric StoredRealPath = new (RealPathStorage.Allocate()) 198*0fca6ea1SDimitry Andric CachedRealPath(std::move(OwnedRealPath)); 199*0fca6ea1SDimitry Andric } 200*0fca6ea1SDimitry Andric 201*0fca6ea1SDimitry Andric return *StoredRealPath; 202480093f4SDimitry Andric } 203480093f4SDimitry Andric 204480093f4SDimitry Andric static bool shouldCacheStatFailures(StringRef Filename) { 205480093f4SDimitry Andric StringRef Ext = llvm::sys::path::extension(Filename); 206480093f4SDimitry Andric if (Ext.empty()) 207480093f4SDimitry Andric return false; // This may be the module cache directory. 20806c3fb27SDimitry Andric return true; 209480093f4SDimitry Andric } 210480093f4SDimitry Andric 2114542f901SDimitry Andric DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem( 2124542f901SDimitry Andric DependencyScanningFilesystemSharedCache &SharedCache, 2134542f901SDimitry Andric IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) 214*0fca6ea1SDimitry Andric : llvm::RTTIExtends<DependencyScanningWorkerFilesystem, 215*0fca6ea1SDimitry Andric llvm::vfs::ProxyFileSystem>(std::move(FS)), 216*0fca6ea1SDimitry Andric SharedCache(SharedCache), 2174542f901SDimitry Andric WorkingDirForCacheLookup(llvm::errc::invalid_argument) { 2184542f901SDimitry Andric updateWorkingDirForCacheLookup(); 2194542f901SDimitry Andric } 2204542f901SDimitry Andric 22104eeddc0SDimitry Andric const CachedFileSystemEntry & 22204eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID( 22304eeddc0SDimitry Andric TentativeEntry TEntry) { 22404eeddc0SDimitry Andric auto &Shard = SharedCache.getShardForUID(TEntry.Status.getUniqueID()); 22504eeddc0SDimitry Andric return Shard.getOrEmplaceEntryForUID(TEntry.Status.getUniqueID(), 22604eeddc0SDimitry Andric std::move(TEntry.Status), 22704eeddc0SDimitry Andric std::move(TEntry.Contents)); 22804eeddc0SDimitry Andric } 22904eeddc0SDimitry Andric 23004eeddc0SDimitry Andric const CachedFileSystemEntry * 23104eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough( 23204eeddc0SDimitry Andric StringRef Filename) { 23304eeddc0SDimitry Andric if (const auto *Entry = LocalCache.findEntryByFilename(Filename)) 23404eeddc0SDimitry Andric return Entry; 23504eeddc0SDimitry Andric auto &Shard = SharedCache.getShardForFilename(Filename); 23604eeddc0SDimitry Andric if (const auto *Entry = Shard.findEntryByFilename(Filename)) 23704eeddc0SDimitry Andric return &LocalCache.insertEntryForFilename(Filename, *Entry); 23804eeddc0SDimitry Andric return nullptr; 23904eeddc0SDimitry Andric } 24004eeddc0SDimitry Andric 24104eeddc0SDimitry Andric llvm::ErrorOr<const CachedFileSystemEntry &> 2424542f901SDimitry Andric DependencyScanningWorkerFilesystem::computeAndStoreResult( 2434542f901SDimitry Andric StringRef OriginalFilename, StringRef FilenameForLookup) { 2444542f901SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> Stat = 2454542f901SDimitry Andric getUnderlyingFS().status(OriginalFilename); 24604eeddc0SDimitry Andric if (!Stat) { 2474542f901SDimitry Andric if (!shouldCacheStatFailures(OriginalFilename)) 24804eeddc0SDimitry Andric return Stat.getError(); 24904eeddc0SDimitry Andric const auto &Entry = 2504542f901SDimitry Andric getOrEmplaceSharedEntryForFilename(FilenameForLookup, Stat.getError()); 2514542f901SDimitry Andric return insertLocalEntryForFilename(FilenameForLookup, Entry); 25204eeddc0SDimitry Andric } 25304eeddc0SDimitry Andric 25404eeddc0SDimitry Andric if (const auto *Entry = findSharedEntryByUID(*Stat)) 2554542f901SDimitry Andric return insertLocalEntryForFilename(FilenameForLookup, *Entry); 25604eeddc0SDimitry Andric 25704eeddc0SDimitry Andric auto TEntry = 2584542f901SDimitry Andric Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(OriginalFilename); 25904eeddc0SDimitry Andric 26004eeddc0SDimitry Andric const CachedFileSystemEntry *SharedEntry = [&]() { 26104eeddc0SDimitry Andric if (TEntry) { 26204eeddc0SDimitry Andric const auto &UIDEntry = getOrEmplaceSharedEntryForUID(std::move(*TEntry)); 2634542f901SDimitry Andric return &getOrInsertSharedEntryForFilename(FilenameForLookup, UIDEntry); 26404eeddc0SDimitry Andric } 2654542f901SDimitry Andric return &getOrEmplaceSharedEntryForFilename(FilenameForLookup, 2664542f901SDimitry Andric TEntry.getError()); 26704eeddc0SDimitry Andric }(); 26804eeddc0SDimitry Andric 2694542f901SDimitry Andric return insertLocalEntryForFilename(FilenameForLookup, *SharedEntry); 270fe6060f1SDimitry Andric } 271fe6060f1SDimitry Andric 2720eae32dcSDimitry Andric llvm::ErrorOr<EntryRef> 273a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry( 274*0fca6ea1SDimitry Andric StringRef OriginalFilename) { 2754542f901SDimitry Andric SmallString<256> PathBuf; 276*0fca6ea1SDimitry Andric auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf); 277*0fca6ea1SDimitry Andric if (!FilenameForLookup) 278*0fca6ea1SDimitry Andric return FilenameForLookup.getError(); 279*0fca6ea1SDimitry Andric 2804542f901SDimitry Andric if (const auto *Entry = 281*0fca6ea1SDimitry Andric findEntryByFilenameWithWriteThrough(*FilenameForLookup)) 282*0fca6ea1SDimitry Andric return EntryRef(OriginalFilename, *Entry).unwrapError(); 283*0fca6ea1SDimitry Andric auto MaybeEntry = computeAndStoreResult(OriginalFilename, *FilenameForLookup); 28404eeddc0SDimitry Andric if (!MaybeEntry) 28504eeddc0SDimitry Andric return MaybeEntry.getError(); 286*0fca6ea1SDimitry Andric return EntryRef(OriginalFilename, *MaybeEntry).unwrapError(); 287a7dea167SDimitry Andric } 288a7dea167SDimitry Andric 289a7dea167SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> 290a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::status(const Twine &Path) { 291a7dea167SDimitry Andric SmallString<256> OwnedFilename; 292a7dea167SDimitry Andric StringRef Filename = Path.toStringRef(OwnedFilename); 2930eae32dcSDimitry Andric 2945f757f3fSDimitry Andric if (Filename.ends_with(".pcm")) 29506c3fb27SDimitry Andric return getUnderlyingFS().status(Path); 29606c3fb27SDimitry Andric 2970eae32dcSDimitry Andric llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename); 298a7dea167SDimitry Andric if (!Result) 299a7dea167SDimitry Andric return Result.getError(); 3000eae32dcSDimitry Andric return Result->getStatus(); 301a7dea167SDimitry Andric } 302a7dea167SDimitry Andric 303*0fca6ea1SDimitry Andric bool DependencyScanningWorkerFilesystem::exists(const Twine &Path) { 304*0fca6ea1SDimitry Andric // While some VFS overlay filesystems may implement more-efficient 305*0fca6ea1SDimitry Andric // mechanisms for `exists` queries, `DependencyScanningWorkerFilesystem` 306*0fca6ea1SDimitry Andric // typically wraps `RealFileSystem` which does not specialize `exists`, 307*0fca6ea1SDimitry Andric // so it is not likely to benefit from such optimizations. Instead, 308*0fca6ea1SDimitry Andric // it is more-valuable to have this query go through the 309*0fca6ea1SDimitry Andric // cached-`status` code-path of the `DependencyScanningWorkerFilesystem`. 310*0fca6ea1SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> Status = status(Path); 311*0fca6ea1SDimitry Andric return Status && Status->exists(); 312*0fca6ea1SDimitry Andric } 313*0fca6ea1SDimitry Andric 314a7dea167SDimitry Andric namespace { 315a7dea167SDimitry Andric 316a7dea167SDimitry Andric /// The VFS that is used by clang consumes the \c CachedFileSystemEntry using 317a7dea167SDimitry Andric /// this subclass. 31881ad6265SDimitry Andric class DepScanFile final : public llvm::vfs::File { 319a7dea167SDimitry Andric public: 32081ad6265SDimitry Andric DepScanFile(std::unique_ptr<llvm::MemoryBuffer> Buffer, 321a7dea167SDimitry Andric llvm::vfs::Status Stat) 322a7dea167SDimitry Andric : Buffer(std::move(Buffer)), Stat(std::move(Stat)) {} 323a7dea167SDimitry Andric 32481ad6265SDimitry Andric static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> create(EntryRef Entry); 325a7dea167SDimitry Andric 326e8d8bef9SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> status() override { return Stat; } 327a7dea167SDimitry Andric 328a7dea167SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 329a7dea167SDimitry Andric getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 330a7dea167SDimitry Andric bool IsVolatile) override { 331a7dea167SDimitry Andric return std::move(Buffer); 332a7dea167SDimitry Andric } 333a7dea167SDimitry Andric 334a7dea167SDimitry Andric std::error_code close() override { return {}; } 335a7dea167SDimitry Andric 336a7dea167SDimitry Andric private: 337a7dea167SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Buffer; 338a7dea167SDimitry Andric llvm::vfs::Status Stat; 339a7dea167SDimitry Andric }; 340a7dea167SDimitry Andric 341e8d8bef9SDimitry Andric } // end anonymous namespace 342e8d8bef9SDimitry Andric 34381ad6265SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> 34481ad6265SDimitry Andric DepScanFile::create(EntryRef Entry) { 34504eeddc0SDimitry Andric assert(!Entry.isError() && "error"); 34604eeddc0SDimitry Andric 3470eae32dcSDimitry Andric if (Entry.isDirectory()) 3480eae32dcSDimitry Andric return std::make_error_code(std::errc::is_a_directory); 3490eae32dcSDimitry Andric 35081ad6265SDimitry Andric auto Result = std::make_unique<DepScanFile>( 35104eeddc0SDimitry Andric llvm::MemoryBuffer::getMemBuffer(Entry.getContents(), 35204eeddc0SDimitry Andric Entry.getStatus().getName(), 353a7dea167SDimitry Andric /*RequiresNullTerminator=*/false), 35404eeddc0SDimitry Andric Entry.getStatus()); 3550eae32dcSDimitry Andric 356a7dea167SDimitry Andric return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>( 357a7dea167SDimitry Andric std::unique_ptr<llvm::vfs::File>(std::move(Result))); 358a7dea167SDimitry Andric } 359a7dea167SDimitry Andric 360a7dea167SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> 361a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::openFileForRead(const Twine &Path) { 362a7dea167SDimitry Andric SmallString<256> OwnedFilename; 363a7dea167SDimitry Andric StringRef Filename = Path.toStringRef(OwnedFilename); 364a7dea167SDimitry Andric 3655f757f3fSDimitry Andric if (Filename.ends_with(".pcm")) 36606c3fb27SDimitry Andric return getUnderlyingFS().openFileForRead(Path); 36706c3fb27SDimitry Andric 3680eae32dcSDimitry Andric llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename); 369a7dea167SDimitry Andric if (!Result) 370a7dea167SDimitry Andric return Result.getError(); 37181ad6265SDimitry Andric return DepScanFile::create(Result.get()); 372a7dea167SDimitry Andric } 3734542f901SDimitry Andric 374*0fca6ea1SDimitry Andric std::error_code 375*0fca6ea1SDimitry Andric DependencyScanningWorkerFilesystem::getRealPath(const Twine &Path, 376*0fca6ea1SDimitry Andric SmallVectorImpl<char> &Output) { 377*0fca6ea1SDimitry Andric SmallString<256> OwnedFilename; 378*0fca6ea1SDimitry Andric StringRef OriginalFilename = Path.toStringRef(OwnedFilename); 379*0fca6ea1SDimitry Andric 380*0fca6ea1SDimitry Andric SmallString<256> PathBuf; 381*0fca6ea1SDimitry Andric auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf); 382*0fca6ea1SDimitry Andric if (!FilenameForLookup) 383*0fca6ea1SDimitry Andric return FilenameForLookup.getError(); 384*0fca6ea1SDimitry Andric 385*0fca6ea1SDimitry Andric auto HandleCachedRealPath = 386*0fca6ea1SDimitry Andric [&Output](const CachedRealPath &RealPath) -> std::error_code { 387*0fca6ea1SDimitry Andric if (!RealPath) 388*0fca6ea1SDimitry Andric return RealPath.getError(); 389*0fca6ea1SDimitry Andric Output.assign(RealPath->begin(), RealPath->end()); 390*0fca6ea1SDimitry Andric return {}; 391*0fca6ea1SDimitry Andric }; 392*0fca6ea1SDimitry Andric 393*0fca6ea1SDimitry Andric // If we already have the result in local cache, no work required. 394*0fca6ea1SDimitry Andric if (const auto *RealPath = 395*0fca6ea1SDimitry Andric LocalCache.findRealPathByFilename(*FilenameForLookup)) 396*0fca6ea1SDimitry Andric return HandleCachedRealPath(*RealPath); 397*0fca6ea1SDimitry Andric 398*0fca6ea1SDimitry Andric // If we have the result in the shared cache, cache it locally. 399*0fca6ea1SDimitry Andric auto &Shard = SharedCache.getShardForFilename(*FilenameForLookup); 400*0fca6ea1SDimitry Andric if (const auto *ShardRealPath = 401*0fca6ea1SDimitry Andric Shard.findRealPathByFilename(*FilenameForLookup)) { 402*0fca6ea1SDimitry Andric const auto &RealPath = LocalCache.insertRealPathForFilename( 403*0fca6ea1SDimitry Andric *FilenameForLookup, *ShardRealPath); 404*0fca6ea1SDimitry Andric return HandleCachedRealPath(RealPath); 405*0fca6ea1SDimitry Andric } 406*0fca6ea1SDimitry Andric 407*0fca6ea1SDimitry Andric // If we don't know the real path, compute it... 408*0fca6ea1SDimitry Andric std::error_code EC = getUnderlyingFS().getRealPath(OriginalFilename, Output); 409*0fca6ea1SDimitry Andric llvm::ErrorOr<llvm::StringRef> ComputedRealPath = EC; 410*0fca6ea1SDimitry Andric if (!EC) 411*0fca6ea1SDimitry Andric ComputedRealPath = StringRef{Output.data(), Output.size()}; 412*0fca6ea1SDimitry Andric 413*0fca6ea1SDimitry Andric // ...and try to write it into the shared cache. In case some other thread won 414*0fca6ea1SDimitry Andric // this race and already wrote its own result there, just adopt it. Write 415*0fca6ea1SDimitry Andric // whatever is in the shared cache into the local one. 416*0fca6ea1SDimitry Andric const auto &RealPath = Shard.getOrEmplaceRealPathForFilename( 417*0fca6ea1SDimitry Andric *FilenameForLookup, ComputedRealPath); 418*0fca6ea1SDimitry Andric return HandleCachedRealPath( 419*0fca6ea1SDimitry Andric LocalCache.insertRealPathForFilename(*FilenameForLookup, RealPath)); 420*0fca6ea1SDimitry Andric } 421*0fca6ea1SDimitry Andric 4224542f901SDimitry Andric std::error_code DependencyScanningWorkerFilesystem::setCurrentWorkingDirectory( 4234542f901SDimitry Andric const Twine &Path) { 4244542f901SDimitry Andric std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path); 4254542f901SDimitry Andric updateWorkingDirForCacheLookup(); 4264542f901SDimitry Andric return EC; 4274542f901SDimitry Andric } 4284542f901SDimitry Andric 4294542f901SDimitry Andric void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() { 4304542f901SDimitry Andric llvm::ErrorOr<std::string> CWD = 4314542f901SDimitry Andric getUnderlyingFS().getCurrentWorkingDirectory(); 4324542f901SDimitry Andric if (!CWD) { 4334542f901SDimitry Andric WorkingDirForCacheLookup = CWD.getError(); 4344542f901SDimitry Andric } else if (!llvm::sys::path::is_absolute_gnu(*CWD)) { 4354542f901SDimitry Andric WorkingDirForCacheLookup = llvm::errc::invalid_argument; 4364542f901SDimitry Andric } else { 4374542f901SDimitry Andric WorkingDirForCacheLookup = *CWD; 4384542f901SDimitry Andric } 4394542f901SDimitry Andric assert(!WorkingDirForCacheLookup || 4404542f901SDimitry Andric llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup)); 4414542f901SDimitry Andric } 442*0fca6ea1SDimitry Andric 443*0fca6ea1SDimitry Andric llvm::ErrorOr<StringRef> 444*0fca6ea1SDimitry Andric DependencyScanningWorkerFilesystem::tryGetFilenameForLookup( 445*0fca6ea1SDimitry Andric StringRef OriginalFilename, llvm::SmallVectorImpl<char> &PathBuf) const { 446*0fca6ea1SDimitry Andric StringRef FilenameForLookup; 447*0fca6ea1SDimitry Andric if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) { 448*0fca6ea1SDimitry Andric FilenameForLookup = OriginalFilename; 449*0fca6ea1SDimitry Andric } else if (!WorkingDirForCacheLookup) { 450*0fca6ea1SDimitry Andric return WorkingDirForCacheLookup.getError(); 451*0fca6ea1SDimitry Andric } else { 452*0fca6ea1SDimitry Andric StringRef RelFilename = OriginalFilename; 453*0fca6ea1SDimitry Andric RelFilename.consume_front("./"); 454*0fca6ea1SDimitry Andric PathBuf.assign(WorkingDirForCacheLookup->begin(), 455*0fca6ea1SDimitry Andric WorkingDirForCacheLookup->end()); 456*0fca6ea1SDimitry Andric llvm::sys::path::append(PathBuf, RelFilename); 457*0fca6ea1SDimitry Andric FilenameForLookup = StringRef{PathBuf.begin(), PathBuf.size()}; 458*0fca6ea1SDimitry Andric } 459*0fca6ea1SDimitry Andric assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup)); 460*0fca6ea1SDimitry Andric return FilenameForLookup; 461*0fca6ea1SDimitry Andric } 462*0fca6ea1SDimitry Andric 463*0fca6ea1SDimitry Andric const char DependencyScanningWorkerFilesystem::ID = 0; 464