Skip to content

Commit 9ef9167

Browse files
authored
[clang][Dependency Scanning] Adding an API to Diagnose Invalid Negative Stat Cache Entries (#135703)
We have had numerous situations where the negatively stat cached paths are invalidated during the build, and such invalidations lead to build errors. This PR adds an API to diagnose such cases. `DependencyScanningFilesystemSharedCache::diagnoseNegativeStatCachedPaths` allows users of the cache to ask the cache to examine all negatively stat cached paths, and re-stat the paths using the passed-in file system. If any re-stat succeeds, the API emits diagnostics. rdar://149147920
1 parent 733c250 commit 9ef9167

File tree

3 files changed

+59
-0
lines changed

3 files changed

+59
-0
lines changed

clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,14 @@ class DependencyScanningFilesystemSharedCache {
220220
CacheShard &getShardForFilename(StringRef Filename) const;
221221
CacheShard &getShardForUID(llvm::sys::fs::UniqueID UID) const;
222222

223+
/// Visits all cached entries and re-stat an entry using FS if
224+
/// it is negatively stat cached. If re-stat succeeds on a path,
225+
/// the path is added to InvalidPaths, indicating that the cache
226+
/// may have erroneously negatively cached it. The caller can then
227+
/// use InvalidPaths to issue diagnostics.
228+
std::vector<StringRef>
229+
getInvalidNegativeStatCachedPaths(llvm::vfs::FileSystem &UnderlyingFS) const;
230+
223231
private:
224232
std::unique_ptr<CacheShard[]> CacheShards;
225233
unsigned NumShards;

clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,33 @@ DependencyScanningFilesystemSharedCache::getShardForUID(
108108
return CacheShards[Hash % NumShards];
109109
}
110110

111+
std::vector<StringRef>
112+
DependencyScanningFilesystemSharedCache::getInvalidNegativeStatCachedPaths(
113+
llvm::vfs::FileSystem &UnderlyingFS) const {
114+
// Iterate through all shards and look for cached stat errors.
115+
std::vector<StringRef> InvalidPaths;
116+
for (unsigned i = 0; i < NumShards; i++) {
117+
const CacheShard &Shard = CacheShards[i];
118+
std::lock_guard<std::mutex> LockGuard(Shard.CacheLock);
119+
for (const auto &[Path, CachedPair] : Shard.CacheByFilename) {
120+
const CachedFileSystemEntry *Entry = CachedPair.first;
121+
122+
if (Entry->getError()) {
123+
// Only examine cached errors.
124+
llvm::ErrorOr<llvm::vfs::Status> Stat = UnderlyingFS.status(Path);
125+
if (Stat) {
126+
// This is the case where we have cached the non-existence
127+
// of the file at Path first, and a a file at the path is created
128+
// later. The cache entry is not invalidated (as we have no good
129+
// way to do it now), which may lead to missing file build errors.
130+
InvalidPaths.push_back(Path);
131+
}
132+
}
133+
}
134+
}
135+
return InvalidPaths;
136+
}
137+
111138
const CachedFileSystemEntry *
112139
DependencyScanningFilesystemSharedCache::CacheShard::findEntryByFilename(
113140
StringRef Filename) const {

clang/unittests/Tooling/DependencyScanning/DependencyScanningFilesystemTest.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,27 @@ TEST(DependencyScanningFilesystem, CacheStatFailures) {
178178
DepFS.exists("/cache/a.pcm");
179179
EXPECT_EQ(InstrumentingFS->NumStatusCalls, 5u);
180180
}
181+
182+
TEST(DependencyScanningFilesystem, DiagnoseStaleStatFailures) {
183+
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
184+
InMemoryFS->setCurrentWorkingDirectory("/");
185+
186+
DependencyScanningFilesystemSharedCache SharedCache;
187+
DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS);
188+
189+
bool Path1Exists = DepFS.exists("/path1");
190+
EXPECT_EQ(Path1Exists, false);
191+
192+
// Adding a file that has been stat-ed,
193+
InMemoryFS->addFile("/path1", 0, llvm::MemoryBuffer::getMemBuffer(""));
194+
Path1Exists = DepFS.exists("/path1");
195+
// Due to caching in SharedCache, path1 should not exist in
196+
// DepFS's eyes.
197+
EXPECT_EQ(Path1Exists, false);
198+
199+
std::vector<llvm::StringRef> InvalidPaths =
200+
SharedCache.getInvalidNegativeStatCachedPaths(*InMemoryFS.get());
201+
202+
EXPECT_EQ(InvalidPaths.size(), 1u);
203+
ASSERT_STREQ("/path1", InvalidPaths[0].str().c_str());
204+
}

0 commit comments

Comments
 (0)