Skip to content

Commit 08f0acd

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 08f0acd

16 files changed

+322
-75
lines changed

clang/include/clang-c/Dependencies.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,17 @@ CINDEX_DEPRECATED CINDEX_LINKAGE void
161161
clang_experimental_DependencyScannerServiceOptions_setActionCache(
162162
CXDependencyScannerServiceOptions Opts, CXCASActionCache Cache);
163163

164+
/**
165+
* Set if negative stat caching is enabled or disabled. If this is not called
166+
* the default value is used.
167+
*
168+
* The default is false unless the environment variable
169+
* `CLANG_SCAN_CACHE_NEGATIVE_STATS` is set to empty or "1".
170+
*/
171+
CINDEX_LINKAGE void
172+
clang_experimental_DependencyScannerServiceOptions_setCacheNegativeStats(
173+
CXDependencyScannerServiceOptions Opts, bool CacheNegativeStats);
174+
164175
/**
165176
* Create a \c CXDependencyScannerService object.
166177
* Must be disposed with \c

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: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ enum class ScanningOptimizations {
9797

9898
#undef DSS_LAST_BITMASK_ENUM
9999

100+
bool shouldCacheNegativeStatsDefault();
101+
102+
/// \return true if failed stats for files with this name should be cached,
103+
/// false otherwise.
104+
bool shouldCacheNegativeStatsForPath(StringRef Path);
105+
100106
/// The dependency scanning service contains shared configuration and state that
101107
/// is used by the individual dependency scanning workers.
102108
class DependencyScanningService {
@@ -109,7 +115,8 @@ class DependencyScanningService {
109115
ScanningOptimizations OptimizeArgs = ScanningOptimizations::Default,
110116
bool EagerLoadModules = false, bool TraceVFS = false,
111117
std::time_t BuildSessionTimestamp =
112-
llvm::sys::toTimeT(std::chrono::system_clock::now()));
118+
llvm::sys::toTimeT(std::chrono::system_clock::now()),
119+
bool CacheNegativeStats = shouldCacheNegativeStatsDefault());
113120

114121
ScanningMode getMode() const { return Mode; }
115122

@@ -121,6 +128,8 @@ class DependencyScanningService {
121128

122129
bool shouldTraceVFS() const { return TraceVFS; }
123130

131+
bool shouldCacheNegativeStats() const { return CacheNegativeStats; }
132+
124133
DependencyScanningFilesystemSharedCache &getSharedCache() {
125134
assert(!SharedFS && "Expected not to have a CASFS");
126135
assert(SharedCache && "Expected a shared cache");
@@ -152,6 +161,7 @@ class DependencyScanningService {
152161
const bool EagerLoadModules;
153162
/// Whether to trace VFS accesses.
154163
const bool TraceVFS;
164+
const bool CacheNegativeStats;
155165
/// Shared CachingOnDiskFileSystem. Set to nullptr to not use CAS dependency
156166
/// scanning.
157167
IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> SharedFS;

clang/lib/Tooling/DependencyScanning/DependencyScanningCASFilesystem.cpp

Lines changed: 7 additions & 12 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;
@@ -201,13 +202,6 @@ DependencyScanningCASFilesystem::getOriginal(cas::CASID InputDataID) {
201202
return Blob.takeError();
202203
}
203204

204-
static bool shouldCacheStatFailures(StringRef Filename) {
205-
StringRef Ext = llvm::sys::path::extension(Filename);
206-
if (Ext.empty())
207-
return false; // This may be the module cache directory.
208-
return true;
209-
}
210-
211205
llvm::cas::CachingOnDiskFileSystem &
212206
DependencyScanningCASFilesystem::getCachingFS() {
213207
return static_cast<llvm::cas::CachingOnDiskFileSystem &>(*FS);
@@ -233,7 +227,8 @@ DependencyScanningCASFilesystem::lookupPath(const Twine &Path) {
233227
llvm::ErrorOr<llvm::vfs::Status> MaybeStatus =
234228
getCachingFS().statusAndFileID(PathRef, FileID);
235229
if (!MaybeStatus) {
236-
if (shouldCacheStatFailures(PathRef))
230+
if (Service.shouldCacheNegativeStats() &&
231+
shouldCacheNegativeStatsForPath(PathRef))
237232
Entries[PathRef].EC = MaybeStatus.getError();
238233
return LookupPathResult{nullptr, MaybeStatus.getError()};
239234
}

clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp

Lines changed: 36 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,46 @@ 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+
if (!Service.shouldCacheNegativeStats() ||
319+
!shouldCacheNegativeStatsForPath(OriginalFilename))
320+
return Stat.getError();
321+
293322
const auto &Entry =
294323
getOrEmplaceSharedEntryForFilename(FilenameForLookup, Stat.getError());
295324
return insertLocalEntryForFilename(FilenameForLookup, Entry);
@@ -478,7 +507,8 @@ DependencyScanningWorkerFilesystem::getRealPath(const Twine &Path,
478507
return HandleCachedRealPath(*RealPath);
479508

480509
// If we have the result in the shared cache, cache it locally.
481-
auto &Shard = SharedCache.getShardForFilename(*FilenameForLookup);
510+
auto &Shard =
511+
Service.getSharedCache().getShardForFilename(*FilenameForLookup);
482512
if (const auto *ShardRealPath =
483513
Shard.findRealPathByFilename(*FilenameForLookup)) {
484514
const auto &RealPath = LocalCache.insertRealPathForFilename(

clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,49 @@
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::shouldCacheNegativeStatsDefault() {
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+
33+
// rdar://148027982
34+
// rdar://127079541
35+
// Negative caching directories can cause build failures due to incorrectly
36+
// configured projects.
37+
bool dependencies::shouldCacheNegativeStatsForPath(StringRef Path) {
38+
StringRef Ext = llvm::sys::path::extension(Path);
39+
if (Ext.empty())
40+
return false;
41+
if (Ext == ".framework")
42+
return false;
43+
return true;
44+
}
45+
1946
DependencyScanningService::DependencyScanningService(
2047
ScanningMode Mode, ScanningOutputFormat Format, CASOptions CASOpts,
2148
std::shared_ptr<llvm::cas::ObjectStore> CAS,
2249
std::shared_ptr<llvm::cas::ActionCache> Cache,
2350
IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> SharedFS,
2451
ScanningOptimizations OptimizeArgs, bool EagerLoadModules, bool TraceVFS,
25-
std::time_t BuildSessionTimestamp)
52+
std::time_t BuildSessionTimestamp, bool CacheNegativeStats)
2653
: Mode(Mode), Format(Format), CASOpts(std::move(CASOpts)),
2754
CAS(std::move(CAS)), Cache(std::move(Cache)), OptimizeArgs(OptimizeArgs),
2855
EagerLoadModules(EagerLoadModules), TraceVFS(TraceVFS),
56+
CacheNegativeStats(CacheNegativeStats),
2957
SharedFS(std::move(SharedFS)),
3058
BuildSessionTimestamp(BuildSessionTimestamp) {
3159
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;
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_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 cache_negative_stats : F<"cache-negative-stats", "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)