Skip to content

Commit 93ed4cd

Browse files
committed
[clang][scan-deps] Add option to disable caching stat failures
While the source code isn't supposed to change during a build, in some environments it does. This adds an option that disables caching of stat failures, meaning that source files can be added to the build during scanning. This adds a `-no-cache-negative-stats` option to clang-scan-deps to enable this behavior. There are no tests for clang-scan-deps as there's no reliable way to do so from it. A unit test has been added that modifies the filesystem between scans to test it. Internally this uses an environment variable. `CLANG_SCAN_CACHE_NEGATIVE_STATS`: If this is set or set to 1 negative stat caching is enabled. If it's 0 then it's disabled. Negative stat caching is disabled by default.
1 parent b298d63 commit 93ed4cd

File tree

12 files changed

+307
-76
lines changed

12 files changed

+307
-76
lines changed

clang/include/clang/Tooling/DependencyScanning/DependencyScanningCASFilesystem.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@ class DependencyScanningCASFilesystem
3838
public:
3939
static const char ID;
4040

41-
DependencyScanningCASFilesystem(
42-
IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> WorkerFS,
43-
llvm::cas::ActionCache &Cache);
41+
DependencyScanningCASFilesystem(DependencyScanningService &Service,
42+
IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> WorkerFS);
4443

4544
~DependencyScanningCASFilesystem();
4645

@@ -109,6 +108,8 @@ class DependencyScanningCASFilesystem
109108

110109
llvm::cas::ObjectStore &CAS;
111110
llvm::cas::ActionCache &Cache;
111+
/// The service associated with this VFS.
112+
DependencyScanningService &Service;
112113
std::optional<llvm::cas::ObjectRef> ClangFullVersionID;
113114
std::optional<llvm::cas::ObjectRef> DepDirectivesID;
114115
std::optional<llvm::cas::ObjectRef> EmptyBlobID;

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

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ namespace clang {
2525
namespace tooling {
2626
namespace dependencies {
2727

28+
class DependencyScanningService;
29+
2830
using DependencyDirectivesTy =
2931
SmallVector<dependency_directives_scan::Directive, 20>;
3032

@@ -387,7 +389,7 @@ class DependencyScanningWorkerFilesystem
387389
static const char ID;
388390

389391
DependencyScanningWorkerFilesystem(
390-
DependencyScanningFilesystemSharedCache &SharedCache,
392+
DependencyScanningService &Service,
391393
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS);
392394

393395
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override;
@@ -476,10 +478,7 @@ class DependencyScanningWorkerFilesystem
476478
/// Returns entry associated with the unique ID in the shared cache or nullptr
477479
/// if none is found.
478480
const CachedFileSystemEntry *
479-
findSharedEntryByUID(llvm::vfs::Status Stat) const {
480-
return SharedCache.getShardForUID(Stat.getUniqueID())
481-
.findEntryByUID(Stat.getUniqueID());
482-
}
481+
findSharedEntryByUID(llvm::vfs::Status Stat) const;
483482

484483
/// Associates the given entry with the filename in the local cache and
485484
/// returns it.
@@ -493,20 +492,14 @@ class DependencyScanningWorkerFilesystem
493492
/// some. Otherwise, constructs new one with the given error code, associates
494493
/// it with the filename and returns the result.
495494
const CachedFileSystemEntry &
496-
getOrEmplaceSharedEntryForFilename(StringRef Filename, std::error_code EC) {
497-
return SharedCache.getShardForFilename(Filename)
498-
.getOrEmplaceEntryForFilename(Filename, EC);
499-
}
495+
getOrEmplaceSharedEntryForFilename(StringRef Filename, std::error_code EC);
500496

501497
/// Returns entry associated with the filename in the shared cache if there is
502498
/// some. Otherwise, associates the given entry with the filename and returns
503499
/// it.
504500
const CachedFileSystemEntry &
505501
getOrInsertSharedEntryForFilename(StringRef Filename,
506-
const CachedFileSystemEntry &Entry) {
507-
return SharedCache.getShardForFilename(Filename)
508-
.getOrInsertEntryForFilename(Filename, Entry);
509-
}
502+
const CachedFileSystemEntry &Entry);
510503

511504
void printImpl(raw_ostream &OS, PrintType Type,
512505
unsigned IndentLevel) const override {
@@ -519,8 +512,9 @@ class DependencyScanningWorkerFilesystem
519512
/// VFS.
520513
bool shouldBypass(StringRef Path) const;
521514

522-
/// The global cache shared between worker threads.
523-
DependencyScanningFilesystemSharedCache &SharedCache;
515+
/// The service associated with this VFS.
516+
DependencyScanningService &Service;
517+
524518
/// The local cache is used by the worker thread to cache file system queries
525519
/// locally instead of querying the global cache every time.
526520
DependencyScanningFilesystemLocalCache LocalCache;

clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ enum class ScanningOptimizations {
9797

9898
#undef DSS_LAST_BITMASK_ENUM
9999

100+
bool shouldNegativeStatCacheDefault();
101+
100102
/// The dependency scanning service contains shared configuration and state that
101103
/// is used by the individual dependency scanning workers.
102104
class DependencyScanningService {
@@ -109,7 +111,8 @@ class DependencyScanningService {
109111
ScanningOptimizations OptimizeArgs = ScanningOptimizations::Default,
110112
bool EagerLoadModules = false, bool TraceVFS = false,
111113
std::time_t BuildSessionTimestamp =
112-
llvm::sys::toTimeT(std::chrono::system_clock::now()));
114+
llvm::sys::toTimeT(std::chrono::system_clock::now()),
115+
bool CacheNegativeStats = shouldNegativeStatCacheDefault());
113116

114117
ScanningMode getMode() const { return Mode; }
115118

@@ -121,6 +124,8 @@ class DependencyScanningService {
121124

122125
bool shouldTraceVFS() const { return TraceVFS; }
123126

127+
bool shouldCacheNegativeStats() const { return CacheNegativeStats; }
128+
124129
DependencyScanningFilesystemSharedCache &getSharedCache() {
125130
assert(!SharedFS && "Expected not to have a CASFS");
126131
assert(SharedCache && "Expected a shared cache");
@@ -152,6 +157,7 @@ class DependencyScanningService {
152157
const bool EagerLoadModules;
153158
/// Whether to trace VFS accesses.
154159
const bool TraceVFS;
160+
const bool CacheNegativeStats;
155161
/// Shared CachingOnDiskFileSystem. Set to nullptr to not use CAS dependency
156162
/// scanning.
157163
IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> SharedFS;

clang/lib/Tooling/DependencyScanning/DependencyScanningCASFilesystem.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "clang/Tooling/DependencyScanning/DependencyScanningCASFilesystem.h"
10+
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
1011
#include "clang/Basic/Version.h"
1112
#include "clang/Lex/DependencyDirectivesScanner.h"
1213
#include "llvm/CAS/ActionCache.h"
@@ -35,10 +36,10 @@ static void reportAsFatalIfError(llvm::Error E) {
3536
using llvm::Error;
3637

3738
DependencyScanningCASFilesystem::DependencyScanningCASFilesystem(
38-
IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> WorkerFS,
39-
llvm::cas::ActionCache &Cache)
40-
: FS(WorkerFS), Entries(EntryAlloc), CAS(WorkerFS->getCAS()), Cache(Cache) {
41-
}
39+
DependencyScanningService &Service,
40+
IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> WorkerFS)
41+
: FS(WorkerFS), Entries(EntryAlloc), CAS(WorkerFS->getCAS()),
42+
Cache(*Service.getCache()), Service(Service) {}
4243

4344
const char DependencyScanningCASFilesystem::ID = 0;
4445
DependencyScanningCASFilesystem::~DependencyScanningCASFilesystem() = default;
@@ -233,7 +234,7 @@ DependencyScanningCASFilesystem::lookupPath(const Twine &Path) {
233234
llvm::ErrorOr<llvm::vfs::Status> MaybeStatus =
234235
getCachingFS().statusAndFileID(PathRef, FileID);
235236
if (!MaybeStatus) {
236-
if (shouldCacheStatFailures(PathRef))
237+
if (Service.shouldCacheNegativeStats() && shouldCacheStatFailures(PathRef))
237238
Entries[PathRef].EC = MaybeStatus.getError();
238239
return LookupPathResult{nullptr, MaybeStatus.getError()};
239240
}

clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h"
10+
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
1011
#include "llvm/CAS/CASFileSystem.h"
1112
#include "llvm/Support/MemoryBuffer.h"
1213
#include "llvm/Support/Threading.h"
@@ -255,19 +256,19 @@ bool DependencyScanningWorkerFilesystem::shouldBypass(StringRef Path) const {
255256
}
256257

257258
DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem(
258-
DependencyScanningFilesystemSharedCache &SharedCache,
259+
DependencyScanningService &Service,
259260
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
260261
: llvm::RTTIExtends<DependencyScanningWorkerFilesystem,
261262
llvm::vfs::ProxyFileSystem>(std::move(FS)),
262-
SharedCache(SharedCache),
263-
WorkingDirForCacheLookup(llvm::errc::invalid_argument) {
263+
Service(Service), WorkingDirForCacheLookup(llvm::errc::invalid_argument) {
264264
updateWorkingDirForCacheLookup();
265265
}
266266

267267
const CachedFileSystemEntry &
268268
DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID(
269269
TentativeEntry TEntry) {
270-
auto &Shard = SharedCache.getShardForUID(TEntry.Status.getUniqueID());
270+
auto &Shard =
271+
Service.getSharedCache().getShardForUID(TEntry.Status.getUniqueID());
271272
return Shard.getOrEmplaceEntryForUID(
272273
TEntry.Status.getUniqueID(), std::move(TEntry.Status),
273274
std::move(TEntry.Contents), std::move(TEntry.CASContents));
@@ -278,18 +279,50 @@ DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough(
278279
StringRef Filename) {
279280
if (const auto *Entry = LocalCache.findEntryByFilename(Filename))
280281
return Entry;
281-
auto &Shard = SharedCache.getShardForFilename(Filename);
282+
auto &Shard = Service.getSharedCache().getShardForFilename(Filename);
282283
if (const auto *Entry = Shard.findEntryByFilename(Filename))
283284
return &LocalCache.insertEntryForFilename(Filename, *Entry);
284285
return nullptr;
285286
}
286287

288+
const CachedFileSystemEntry *
289+
DependencyScanningWorkerFilesystem::findSharedEntryByUID(
290+
llvm::vfs::Status Stat) const {
291+
return Service.getSharedCache()
292+
.getShardForUID(Stat.getUniqueID())
293+
.findEntryByUID(Stat.getUniqueID());
294+
}
295+
296+
const CachedFileSystemEntry &
297+
DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForFilename(
298+
StringRef Filename, std::error_code EC) {
299+
return Service.getSharedCache()
300+
.getShardForFilename(Filename)
301+
.getOrEmplaceEntryForFilename(Filename, EC);
302+
}
303+
304+
const CachedFileSystemEntry &
305+
DependencyScanningWorkerFilesystem::getOrInsertSharedEntryForFilename(
306+
StringRef Filename, const CachedFileSystemEntry &Entry) {
307+
return Service.getSharedCache()
308+
.getShardForFilename(Filename)
309+
.getOrInsertEntryForFilename(Filename, Entry);
310+
}
311+
287312
llvm::ErrorOr<const CachedFileSystemEntry &>
288313
DependencyScanningWorkerFilesystem::computeAndStoreResult(
289314
StringRef OriginalFilename, StringRef FilenameForLookup) {
290315
llvm::ErrorOr<llvm::vfs::Status> Stat =
291316
getUnderlyingFS().status(OriginalFilename);
292317
if (!Stat) {
318+
// rdar://148027982
319+
// rdar://127079541
320+
// Negative caching directories can cause build failures due to incorrectly
321+
// configured projects.
322+
StringRef Ext = llvm::sys::path::extension(OriginalFilename);
323+
if (!Service.shouldCacheNegativeStats() || Ext.empty() || Ext == ".framework")
324+
return Stat.getError();
325+
293326
const auto &Entry =
294327
getOrEmplaceSharedEntryForFilename(FilenameForLookup, Stat.getError());
295328
return insertLocalEntryForFilename(FilenameForLookup, Entry);
@@ -478,7 +511,8 @@ DependencyScanningWorkerFilesystem::getRealPath(const Twine &Path,
478511
return HandleCachedRealPath(*RealPath);
479512

480513
// If we have the result in the shared cache, cache it locally.
481-
auto &Shard = SharedCache.getShardForFilename(*FilenameForLookup);
514+
auto &Shard =
515+
Service.getSharedCache().getShardForFilename(*FilenameForLookup);
482516
if (const auto *ShardRealPath =
483517
Shard.findRealPathByFilename(*FilenameForLookup)) {
484518
const auto &RealPath = LocalCache.insertRealPathForFilename(

clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,36 @@
1111
#include "llvm/CAS/ActionCache.h"
1212
#include "llvm/CAS/CachingOnDiskFileSystem.h"
1313
#include "llvm/CAS/ObjectStore.h"
14+
#include "llvm/Support/Process.h"
1415

1516
using namespace clang;
1617
using namespace tooling;
1718
using namespace dependencies;
1819

20+
bool clang::tooling::dependencies::shouldNegativeStatCacheDefault() {
21+
if (std::optional<std::string> MaybeNegStats =
22+
llvm::sys::Process::GetEnv("CLANG_SCAN_CACHE_NEGATIVE_STATS")) {
23+
if (MaybeNegStats->empty())
24+
return true;
25+
return llvm::StringSwitch<bool>(*MaybeNegStats)
26+
.Case("1", true)
27+
.Case("0", false)
28+
.Default(false);
29+
}
30+
return false;
31+
}
32+
1933
DependencyScanningService::DependencyScanningService(
2034
ScanningMode Mode, ScanningOutputFormat Format, CASOptions CASOpts,
2135
std::shared_ptr<llvm::cas::ObjectStore> CAS,
2236
std::shared_ptr<llvm::cas::ActionCache> Cache,
2337
IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> SharedFS,
2438
ScanningOptimizations OptimizeArgs, bool EagerLoadModules, bool TraceVFS,
25-
std::time_t BuildSessionTimestamp)
39+
std::time_t BuildSessionTimestamp, bool CacheNegativeStats)
2640
: Mode(Mode), Format(Format), CASOpts(std::move(CASOpts)),
2741
CAS(std::move(CAS)), Cache(std::move(Cache)), OptimizeArgs(OptimizeArgs),
2842
EagerLoadModules(EagerLoadModules), TraceVFS(TraceVFS),
43+
CacheNegativeStats(CacheNegativeStats),
2944
SharedFS(std::move(SharedFS)),
3045
BuildSessionTimestamp(BuildSessionTimestamp) {
3146
if (!this->SharedFS)

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ DependencyScanningWorker::DependencyScanningWorker(
3434

3535
if (Service.useCASFS()) {
3636
CacheFS = Service.getSharedFS().createProxyFS();
37-
DepCASFS = new DependencyScanningCASFilesystem(CacheFS, *Service.getCache());
37+
DepCASFS = new DependencyScanningCASFilesystem(Service, CacheFS);
3838
BaseFS = DepCASFS;
3939
return;
4040
}
4141

4242
switch (Service.getMode()) {
4343
case ScanningMode::DependencyDirectivesScan:
4444
DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
45-
Service.getSharedCache(), FS);
45+
Service, FS);
4646
BaseFS = DepFS;
4747
break;
4848
case ScanningMode::CanonicalPreprocessing:

clang/tools/clang-scan-deps/ClangScanDeps.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ static ScanningOutputFormat Format = ScanningOutputFormat::Make;
9292
static ScanningOptimizations OptimizeArgs;
9393
static std::string ModuleFilesDir;
9494
static bool EagerLoadModules;
95+
static bool CacheNegativeStats = true;
9596
static unsigned NumThreads = 0;
9697
static std::string CompilationDB;
9798
static std::optional<std::string> ModuleNames;
@@ -212,6 +213,8 @@ static void ParseArgs(int argc, char **argv) {
212213

213214
EagerLoadModules = Args.hasArg(OPT_eager_load_pcm);
214215

216+
CacheNegativeStats = !Args.hasArg(OPT_no_cache_negative_stats);
217+
215218
if (const llvm::opt::Arg *A = Args.getLastArg(OPT_j)) {
216219
StringRef S{A->getValue()};
217220
if (!llvm::to_integer(S, NumThreads, 0)) {
@@ -1541,9 +1544,10 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
15411544
});
15421545
};
15431546

1544-
DependencyScanningService Service(ScanMode, Format, CASOpts, CAS, Cache, FS, OptimizeArgs,
1545-
EagerLoadModules,
1546-
/*TraceVFS=*/Verbose);
1547+
DependencyScanningService Service(
1548+
ScanMode, Format, CASOpts, CAS, Cache, FS, OptimizeArgs, EagerLoadModules,
1549+
/*TraceVFS=*/Verbose,
1550+
llvm::sys::toTimeT(std::chrono::system_clock::now()), CacheNegativeStats);
15471551

15481552
llvm::Timer T;
15491553
T.startTimer();

clang/tools/clang-scan-deps/Opts.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ defm module_files_dir : Eq<"module-files-dir",
2222

2323
def optimize_args_EQ : CommaJoined<["-", "--"], "optimize-args=">, HelpText<"Which command-line arguments of modules to optimize">;
2424
def eager_load_pcm : F<"eager-load-pcm", "Load PCM files eagerly (instead of lazily on import)">;
25+
def no_cache_negative_stats : F<"no-cache-negative-stats", "Don't cache stat failures">;
2526

2627
def j : Arg<"j", "Number of worker threads to use (default: use all concurrent threads)">;
2728

0 commit comments

Comments
 (0)