Skip to content

Commit 2187de3

Browse files
committed
Enhancing DependencyScanningFilesystemSharedCache's API that reports invalid entries.
1 parent d1ca8d8 commit 2187de3

File tree

3 files changed

+88
-23
lines changed

3 files changed

+88
-23
lines changed

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

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,41 @@ 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;
223+
struct InvalidEntryDiagInfo {
224+
// A null terminated string that contains a path.
225+
const char *Path = nullptr;
226+
227+
enum class Type : unsigned char { NegativeCaching = 1, SizeChanged = 2 };
228+
229+
Type T;
230+
231+
struct SizeChangedInfo {
232+
uint64_t CachedSize = 0;
233+
uint64_t ActualSize = 0;
234+
};
235+
236+
std::optional<SizeChangedInfo> SizeInfo;
237+
238+
InvalidEntryDiagInfo(const char *Path) : Path(Path) {
239+
T = Type::NegativeCaching;
240+
}
241+
242+
InvalidEntryDiagInfo(const char *Path, uint64_t CachedSize,
243+
uint64_t ActualSize)
244+
: Path(Path), SizeInfo({CachedSize, ActualSize}) {
245+
T = Type::SizeChanged;
246+
}
247+
};
248+
249+
/// Visits all cached entries and re-stat an entry using UnderlyingFS to check
250+
/// if the cache contains invalid entries. An entry can be invalid for two
251+
/// reasons:
252+
/// 1. The entry contains a stat error, indicating the file did not exist
253+
/// in the cache, but the file exists on the UnderlyingFS.
254+
/// 2. The entry is associated with a file whose size is different from the
255+
/// actual size on the UnderlyingFS.
256+
std::vector<InvalidEntryDiagInfo>
257+
getInvalidEntryDiagInfo(llvm::vfs::FileSystem &UnderlyingFS) const;
230258

231259
private:
232260
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
@@ -107,31 +107,39 @@ DependencyScanningFilesystemSharedCache::getShardForUID(
107107
return CacheShards[Hash % NumShards];
108108
}
109109

110-
std::vector<StringRef>
111-
DependencyScanningFilesystemSharedCache::getInvalidNegativeStatCachedPaths(
110+
std::vector<DependencyScanningFilesystemSharedCache::InvalidEntryDiagInfo>
111+
DependencyScanningFilesystemSharedCache::getInvalidEntryDiagInfo(
112112
llvm::vfs::FileSystem &UnderlyingFS) const {
113113
// Iterate through all shards and look for cached stat errors.
114-
std::vector<StringRef> InvalidPaths;
114+
std::vector<InvalidEntryDiagInfo> InvalidDiagInfo;
115115
for (unsigned i = 0; i < NumShards; i++) {
116116
const CacheShard &Shard = CacheShards[i];
117117
std::lock_guard<std::mutex> LockGuard(Shard.CacheLock);
118118
for (const auto &[Path, CachedPair] : Shard.CacheByFilename) {
119119
const CachedFileSystemEntry *Entry = CachedPair.first;
120120

121-
if (Entry->getError()) {
122-
// Only examine cached errors.
123-
llvm::ErrorOr<llvm::vfs::Status> Stat = UnderlyingFS.status(Path);
124-
if (Stat) {
121+
llvm::ErrorOr<llvm::vfs::Status> Status = UnderlyingFS.status(Path);
122+
if (Status) {
123+
if (Entry->getError()) {
125124
// This is the case where we have cached the non-existence
126-
// of the file at Path first, and a a file at the path is created
125+
// of the file at Path first, and a file at the path is created
127126
// later. The cache entry is not invalidated (as we have no good
128127
// way to do it now), which may lead to missing file build errors.
129-
InvalidPaths.push_back(Path);
128+
InvalidDiagInfo.emplace_back(Path.data());
129+
} else {
130+
llvm::vfs::Status CachedStatus = Entry->getStatus();
131+
uint64_t CachedSize = CachedStatus.getSize();
132+
uint64_t ActualSize = Status->getSize();
133+
if (CachedSize != ActualSize) {
134+
// This is the case where the cached file has a different size
135+
// from the actual file that comes from the underlying FS.
136+
InvalidDiagInfo.emplace_back(Path.data(), CachedSize, ActualSize);
137+
}
130138
}
131139
}
132140
}
133141
}
134-
return InvalidPaths;
142+
return InvalidDiagInfo;
135143
}
136144

137145
const CachedFileSystemEntry *

clang/unittests/Tooling/DependencyScanning/DependencyScanningFilesystemTest.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,18 +187,47 @@ TEST(DependencyScanningFilesystem, DiagnoseStaleStatFailures) {
187187
DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS);
188188

189189
bool Path1Exists = DepFS.exists("/path1.suffix");
190-
EXPECT_EQ(Path1Exists, false);
190+
ASSERT_EQ(Path1Exists, false);
191191

192192
// Adding a file that has been stat-ed,
193193
InMemoryFS->addFile("/path1.suffix", 0, llvm::MemoryBuffer::getMemBuffer(""));
194194
Path1Exists = DepFS.exists("/path1.suffix");
195195
// Due to caching in SharedCache, path1 should not exist in
196196
// DepFS's eyes.
197-
EXPECT_EQ(Path1Exists, false);
197+
ASSERT_EQ(Path1Exists, false);
198198

199-
std::vector<llvm::StringRef> InvalidPaths =
200-
SharedCache.getInvalidNegativeStatCachedPaths(*InMemoryFS);
199+
auto InvalidEntries = SharedCache.getInvalidEntryDiagInfo(*InMemoryFS);
201200

202-
EXPECT_EQ(InvalidPaths.size(), 1u);
203-
ASSERT_STREQ("/path1.suffix", InvalidPaths[0].str().c_str());
201+
EXPECT_EQ(InvalidEntries.size(), 1u);
202+
ASSERT_STREQ("/path1.suffix", InvalidEntries[0].Path);
203+
}
204+
205+
TEST(DependencyScanningFilesystem, DiagnoseCachedFileSizeChange) {
206+
auto InMemoryFS1 = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
207+
auto InMemoryFS2 = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
208+
InMemoryFS1->setCurrentWorkingDirectory("/");
209+
InMemoryFS2->setCurrentWorkingDirectory("/");
210+
211+
DependencyScanningFilesystemSharedCache SharedCache;
212+
DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS1);
213+
214+
InMemoryFS1->addFile("/path1.suffix", 0,
215+
llvm::MemoryBuffer::getMemBuffer(""));
216+
bool Path1Exists = DepFS.exists("/path1.suffix");
217+
ASSERT_EQ(Path1Exists, true);
218+
219+
// Add a file to a new FS that has the same path but different content.
220+
InMemoryFS2->addFile("/path1.suffix", 1,
221+
llvm::MemoryBuffer::getMemBuffer(" "));
222+
223+
// Check against the new file system. InMemoryFS2 could be the underlying
224+
// physical system in the real world.
225+
auto InvalidEntries = SharedCache.getInvalidEntryDiagInfo(*InMemoryFS2);
226+
227+
ASSERT_EQ(InvalidEntries.size(), 1u);
228+
ASSERT_STREQ("/path1.suffix", InvalidEntries[0].Path);
229+
auto SizeInfo = InvalidEntries[0].SizeInfo;
230+
ASSERT_TRUE(SizeInfo);
231+
ASSERT_EQ(SizeInfo->CachedSize, 0u);
232+
ASSERT_EQ(SizeInfo->ActualSize, 8u);
204233
}

0 commit comments

Comments
 (0)