Skip to content

Commit f53ab61

Browse files
committed
[clang Dependency Scanning] Enhance File Caching Diagnostics (llvm#144105)
`DependencyScanningFileSystemSharedCache` can currently report out-of-date negatively stat cached paths. This PR enhances the reporting with two modifications. 1. The reported path are now null terminated char arrays instead of `StringRef`s. This way the API's user can avoid copying `StringRef`s to other containers because the char arrays can be used directly. 2. The API now reports out-of-date cache entry due to file size changes. Specifically, we check each file's cached size against the size of the same file on the underlying FS. If the sizes are different, diagnostics will be reported. rdar://152247357 (cherry picked from commit 51d1385)
1 parent 0b5ed8c commit f53ab61

File tree

3 files changed

+84
-23
lines changed

3 files changed

+84
-23
lines changed

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

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/Support/VirtualFileSystem.h"
2020
#include <mutex>
2121
#include <optional>
22+
#include <variant>
2223

2324
namespace clang {
2425
namespace tooling {
@@ -233,13 +234,34 @@ class DependencyScanningFilesystemSharedCache {
233234
CacheShard &getShardForFilename(StringRef Filename) const;
234235
CacheShard &getShardForUID(llvm::sys::fs::UniqueID UID) const;
235236

236-
/// Visits all cached entries and re-stat an entry using FS if
237-
/// it is negatively stat cached. If re-stat succeeds on a path,
238-
/// the path is added to InvalidPaths, indicating that the cache
239-
/// may have erroneously negatively cached it. The caller can then
240-
/// use InvalidPaths to issue diagnostics.
241-
std::vector<StringRef>
242-
getInvalidNegativeStatCachedPaths(llvm::vfs::FileSystem &UnderlyingFS) const;
237+
struct OutOfDateEntry {
238+
// A null terminated string that contains a path.
239+
const char *Path = nullptr;
240+
241+
struct NegativelyCachedInfo {};
242+
struct SizeChangedInfo {
243+
uint64_t CachedSize = 0;
244+
uint64_t ActualSize = 0;
245+
};
246+
247+
std::variant<NegativelyCachedInfo, SizeChangedInfo> Info;
248+
249+
OutOfDateEntry(const char *Path)
250+
: Path(Path), Info(NegativelyCachedInfo{}) {}
251+
252+
OutOfDateEntry(const char *Path, uint64_t CachedSize, uint64_t ActualSize)
253+
: Path(Path), Info(SizeChangedInfo{CachedSize, ActualSize}) {}
254+
};
255+
256+
/// Visits all cached entries and re-stat an entry using UnderlyingFS to check
257+
/// if the cache contains out-of-date entries. An entry can be out-of-date for
258+
/// two reasons:
259+
/// 1. The entry contains a stat error, indicating the file did not exist
260+
/// in the cache, but the file exists on the UnderlyingFS.
261+
/// 2. The entry is associated with a file whose size is different from the
262+
/// size of the file on the same path on the UnderlyingFS.
263+
std::vector<OutOfDateEntry>
264+
getOutOfDateEntries(llvm::vfs::FileSystem &UnderlyingFS) const;
243265

244266
private:
245267
std::unique_ptr<CacheShard[]> CacheShards;

clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,31 +113,39 @@ DependencyScanningFilesystemSharedCache::getShardForUID(
113113
return CacheShards[Hash % NumShards];
114114
}
115115

116-
std::vector<StringRef>
117-
DependencyScanningFilesystemSharedCache::getInvalidNegativeStatCachedPaths(
116+
std::vector<DependencyScanningFilesystemSharedCache::OutOfDateEntry>
117+
DependencyScanningFilesystemSharedCache::getOutOfDateEntries(
118118
llvm::vfs::FileSystem &UnderlyingFS) const {
119119
// Iterate through all shards and look for cached stat errors.
120-
std::vector<StringRef> InvalidPaths;
120+
std::vector<OutOfDateEntry> InvalidDiagInfo;
121121
for (unsigned i = 0; i < NumShards; i++) {
122122
const CacheShard &Shard = CacheShards[i];
123123
std::lock_guard<std::mutex> LockGuard(Shard.CacheLock);
124124
for (const auto &[Path, CachedPair] : Shard.CacheByFilename) {
125125
const CachedFileSystemEntry *Entry = CachedPair.first;
126126

127-
if (Entry->getError()) {
128-
// Only examine cached errors.
129-
llvm::ErrorOr<llvm::vfs::Status> Stat = UnderlyingFS.status(Path);
130-
if (Stat) {
127+
llvm::ErrorOr<llvm::vfs::Status> Status = UnderlyingFS.status(Path);
128+
if (Status) {
129+
if (Entry->getError()) {
131130
// This is the case where we have cached the non-existence
132-
// of the file at Path first, and a a file at the path is created
131+
// of the file at Path first, and a file at the path is created
133132
// later. The cache entry is not invalidated (as we have no good
134133
// way to do it now), which may lead to missing file build errors.
135-
InvalidPaths.push_back(Path);
134+
InvalidDiagInfo.emplace_back(Path.data());
135+
} else {
136+
llvm::vfs::Status CachedStatus = Entry->getStatus();
137+
uint64_t CachedSize = CachedStatus.getSize();
138+
uint64_t ActualSize = Status->getSize();
139+
if (CachedSize != ActualSize) {
140+
// This is the case where the cached file has a different size
141+
// from the actual file that comes from the underlying FS.
142+
InvalidDiagInfo.emplace_back(Path.data(), CachedSize, ActualSize);
143+
}
136144
}
137145
}
138146
}
139147
}
140-
return InvalidPaths;
148+
return InvalidDiagInfo;
141149
}
142150

143151
const CachedFileSystemEntry *

clang/unittests/Tooling/DependencyScanning/DependencyScanningFilesystemTest.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,18 +155,49 @@ TEST(DependencyScanningFilesystem, DiagnoseStaleStatFailures) {
155155
DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS);
156156

157157
bool Path1Exists = DepFS.exists("/path1.suffix");
158-
EXPECT_EQ(Path1Exists, false);
158+
ASSERT_EQ(Path1Exists, false);
159159

160160
// Adding a file that has been stat-ed,
161161
InMemoryFS->addFile("/path1.suffix", 0, llvm::MemoryBuffer::getMemBuffer(""));
162162
Path1Exists = DepFS.exists("/path1.suffix");
163163
// Due to caching in SharedCache, path1 should not exist in
164164
// DepFS's eyes.
165-
EXPECT_EQ(Path1Exists, false);
165+
ASSERT_EQ(Path1Exists, false);
166166

167-
std::vector<llvm::StringRef> InvalidPaths =
168-
SharedCache.getInvalidNegativeStatCachedPaths(*InMemoryFS.get());
167+
auto InvalidEntries = SharedCache.getOutOfDateEntries(*InMemoryFS);
169168

170-
EXPECT_EQ(InvalidPaths.size(), 1u);
171-
ASSERT_STREQ("/path1.suffix", InvalidPaths[0].str().c_str());
169+
EXPECT_EQ(InvalidEntries.size(), 1u);
170+
ASSERT_STREQ("/path1.suffix", InvalidEntries[0].Path);
171+
}
172+
173+
TEST(DependencyScanningFilesystem, DiagnoseCachedFileSizeChange) {
174+
auto InMemoryFS1 = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
175+
auto InMemoryFS2 = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
176+
InMemoryFS1->setCurrentWorkingDirectory("/");
177+
InMemoryFS2->setCurrentWorkingDirectory("/");
178+
179+
DependencyScanningFilesystemSharedCache SharedCache;
180+
DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS1);
181+
182+
InMemoryFS1->addFile("/path1.suffix", 0,
183+
llvm::MemoryBuffer::getMemBuffer(""));
184+
bool Path1Exists = DepFS.exists("/path1.suffix");
185+
ASSERT_EQ(Path1Exists, true);
186+
187+
// Add a file to a new FS that has the same path but different content.
188+
InMemoryFS2->addFile("/path1.suffix", 1,
189+
llvm::MemoryBuffer::getMemBuffer(" "));
190+
191+
// Check against the new file system. InMemoryFS2 could be the underlying
192+
// physical system in the real world.
193+
auto InvalidEntries = SharedCache.getOutOfDateEntries(*InMemoryFS2);
194+
195+
ASSERT_EQ(InvalidEntries.size(), 1u);
196+
ASSERT_STREQ("/path1.suffix", InvalidEntries[0].Path);
197+
auto SizeInfo = std::get_if<
198+
DependencyScanningFilesystemSharedCache::OutOfDateEntry::SizeChangedInfo>(
199+
&InvalidEntries[0].Info);
200+
ASSERT_TRUE(SizeInfo);
201+
ASSERT_EQ(SizeInfo->CachedSize, 0u);
202+
ASSERT_EQ(SizeInfo->ActualSize, 8u);
172203
}

0 commit comments

Comments
 (0)