Skip to content

Commit 8be10d6

Browse files
committed
Adopt clang API for diagnosing invalid stat cache entries
This is sometimes useful to identify when missing dependencies may be introducing races in compilation rdar://150953611
1 parent 1356e5a commit 8be10d6

File tree

6 files changed

+73
-1
lines changed

6 files changed

+73
-1
lines changed

Sources/SWBBuildSystem/BuildOperation.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,6 +1563,8 @@ internal final class OperationSystemAdaptor: SWBLLBuild.BuildSystemDelegate, Act
15631563
// The build should be complete, validate the consistency of the target/task counts.
15641564
self.validateTargetCompletion(buildSucceeded: buildSucceeded)
15651565

1566+
self.diagnoseInvalidNegativeStatCacheEntries(buildSucceeded: buildSucceeded)
1567+
15661568
// If the build failed, make sure we flush any pending incremental build records.
15671569
// Usually, driver instances are cleaned up and write out their incremental build records when a target finishes building. However, this won't necessarily be the case if the build fails. Ensure we write out any pending records before tearing down the graph so we don't use a stale record on a subsequent build.
15681570
if !buildSucceeded {
@@ -1574,6 +1576,16 @@ internal final class OperationSystemAdaptor: SWBLLBuild.BuildSystemDelegate, Act
15741576
}
15751577
}
15761578

1579+
func diagnoseInvalidNegativeStatCacheEntries(buildSucceeded: Bool) {
1580+
let settings = operation.requestContext.getCachedSettings(operation.request.parameters)
1581+
guard settings.globalScope.evaluate(BuiltinMacros.VERIFY_CLANG_SCANNER_NEGATIVE_STAT_CACHE) || !buildSucceeded else {
1582+
return
1583+
}
1584+
for entry in dynamicOperationContext.clangModuleDependencyGraph.diagnoseInvalidNegativeStatCacheEntries() {
1585+
buildOutputDelegate.warning(Path(entry), "Clang reported an invalid negative stat cache entry for '\(entry)'; this may indicate a missing dependency which caused the file to be modified after being read by a dependent")
1586+
}
1587+
}
1588+
15771589
/// Cleanup the compilation cache to reduce resource usage in environments not configured to preserve it.
15781590
func cleanupCompilationCache() {
15791591
let settings = operation.requestContext.getCachedSettings(operation.request.parameters)

Sources/SWBCSupport/CLibclang.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,11 @@ extern "C" {
12591259
*/
12601260
CXDiagnosticSet (*clang_experimental_DepGraph_getDiagnostics)(CXDepGraph);
12611261

1262+
/**
1263+
* Checks negatively cached paths in the stat cache against the current state of the filesystem and returns a list of discrepancies.
1264+
*/
1265+
CXCStringArray (*clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths)(CXDependencyScannerService);
1266+
12621267
// MARK: Driver API
12631268

12641269
/**
@@ -1337,6 +1342,7 @@ struct LibclangWrapper {
13371342
bool hasDependencyScanner;
13381343
bool hasStructuredScanningDiagnostics;
13391344
bool hasCAS;
1345+
bool hasNegativeStatCacheDiagnostics;
13401346

13411347
LibclangWrapper(std::string path)
13421348
: path(path),
@@ -1348,7 +1354,7 @@ struct LibclangWrapper {
13481354
#else
13491355
handle(dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL)),
13501356
#endif
1351-
isLeaked(false), hasRequiredAPI(true), hasDependencyScanner(true), hasStructuredScanningDiagnostics(true), hasCAS(true) {
1357+
isLeaked(false), hasRequiredAPI(true), hasDependencyScanner(true), hasStructuredScanningDiagnostics(true), hasCAS(true), hasNegativeStatCacheDiagnostics(false) {
13521358
#if defined(_WIN32)
13531359
DWORD cchLength = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path.c_str(), -1, nullptr, 0);
13541360
std::unique_ptr<wchar_t[]> wszPath(new wchar_t[cchLength]);
@@ -1488,6 +1494,11 @@ struct LibclangWrapper {
14881494
hasCAS = false;
14891495
}
14901496

1497+
LOOKUP_OPTIONAL(clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths);
1498+
if (fns.clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths) {
1499+
hasNegativeStatCacheDiagnostics = true;
1500+
}
1501+
14911502
LOOKUP_OPTIONAL(clang_Driver_getExternalActionsForCommand_v0);
14921503
LOOKUP_OPTIONAL(clang_Driver_ExternalActionList_dispose);
14931504
LOOKUP_OPTIONAL(clang_install_aborting_llvm_fatal_error_handler);
@@ -1760,6 +1771,10 @@ extern "C" {
17601771
return lib->wrapper->hasStructuredScanningDiagnostics;
17611772
}
17621773

1774+
bool libclang_has_negative_stat_cache_diagnostics(libclang_t lib) {
1775+
return lib->wrapper->hasNegativeStatCacheDiagnostics;
1776+
}
1777+
17631778
libclang_scanner_t libclang_scanner_create(libclang_t lib, libclang_casdatabases_t casdbs, libclang_casoptions_t casOpts) {
17641779
return new libclang_scanner_t_{new LibclangScanner(
17651780
lib->wrapper, LibclangFunctions::CXDependencyMode_Full,
@@ -2043,6 +2058,17 @@ extern "C" {
20432058
return diagnostic_set;
20442059
}
20452060

2061+
void libclang_scanner_diagnose_invalid_negative_stat_cache_entries(libclang_scanner_t scanner, void (^path_callback)(const char *)) {
2062+
auto lib = scanner->scanner->lib;
2063+
if (!lib->fns.clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths) {
2064+
return;
2065+
}
2066+
LibclangFunctions::CXCStringArray paths = lib->fns.clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths(scanner->scanner->service);
2067+
for (size_t i = 0; i < paths.Count; ++i) {
2068+
path_callback(paths.Strings[i]);
2069+
}
2070+
}
2071+
20462072
bool libclang_scanner_scan_dependencies(
20472073
libclang_scanner_t scanner, int argc, char *const *argv, const char *workingDirectory,
20482074
__attribute__((noescape)) module_lookup_output_t module_lookup_output,

Sources/SWBCSupport/CLibclang.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ CSUPPORT_EXPORT bool libclang_has_scanner(libclang_t lib);
107107
/// Whether libclang supports reporting structured scanning diagnostics.
108108
CSUPPORT_EXPORT bool libclang_has_structured_scanner_diagnostics(libclang_t lib);
109109

110+
/// Whether libclang supports reporting negative stat caching diagnostics.
111+
CSUPPORT_EXPORT bool libclang_has_negative_stat_cache_diagnostics(libclang_t lib);
112+
110113
/// Create a new scanner instance with optional CAS databases.
111114
CSUPPORT_EXPORT libclang_scanner_t libclang_scanner_create(libclang_t lib, libclang_casdatabases_t, libclang_casoptions_t);
112115

@@ -184,6 +187,9 @@ typedef size_t (^module_lookup_output_t)(
184187
const char *module_name, const char *context_hash,
185188
clang_output_kind_t kind, char *output, size_t max_len);
186189

190+
/// Reports invalid entries in the scanner's negative stat cache.
191+
CSUPPORT_EXPORT void libclang_scanner_diagnose_invalid_negative_stat_cache_entries(libclang_scanner_t scanner, void (^path_callback)(const char *));
192+
187193
/// Scan the given Clang "cc1" invocation, looking for dependencies.
188194
///
189195
/// NOTE: This function is thread-safe.

Sources/SWBCore/LibclangVendored/Libclang.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ public final class Libclang {
9191
libclang_has_structured_scanner_diagnostics(lib)
9292
}
9393

94+
public var supportsNegativeStatCacheDiagnostics: Bool {
95+
libclang_has_negative_stat_cache_diagnostics(lib)
96+
}
97+
9498
public var supportsCASPruning: Bool {
9599
libclang_has_cas_pruning_feature(lib)
96100
}
@@ -274,6 +278,17 @@ public final class DependencyScanner {
274278
return fileDeps
275279
}
276280

281+
public func diagnoseInvalidNegativeStatCacheEntries() -> [String] {
282+
var entries: [String] = []
283+
libclang_scanner_diagnose_invalid_negative_stat_cache_entries(scanner, { cString in
284+
guard let cString else {
285+
return
286+
}
287+
entries.append(String(cString: cString))
288+
})
289+
return entries
290+
}
291+
277292
public func generateReproducer(
278293
commandLine: [String],
279294
workingDirectory: String

Sources/SWBCore/Settings/BuiltinMacros.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,7 @@ public final class BuiltinMacros {
11651165
public static let VERSION_INFO_STRING = BuiltinMacros.declareStringMacro("VERSION_INFO_STRING")
11661166
public static let VERSION_INFO_SUFFIX = BuiltinMacros.declareStringMacro("VERSION_INFO_SUFFIX")
11671167
public static let ValidateForStore = BuiltinMacros.declareBooleanMacro("ValidateForStore")
1168+
public static let VERIFY_CLANG_SCANNER_NEGATIVE_STAT_CACHE = BuiltinMacros.declareBooleanMacro("VERIFY_CLANG_SCANNER_NEGATIVE_STAT_CACHE")
11681169
public static let WARNING_CFLAGS = BuiltinMacros.declareStringListMacro("WARNING_CFLAGS")
11691170
public static let WARNING_LDFLAGS = BuiltinMacros.declareStringListMacro("WARNING_LDFLAGS")
11701171
public static let WATCHKIT_2_SUPPORT_FOLDER_PATH = BuiltinMacros.declareStringMacro("WATCHKIT_2_SUPPORT_FOLDER_PATH")
@@ -2402,6 +2403,7 @@ public final class BuiltinMacros {
24022403
VERSION_INFO_STRING,
24032404
VERSION_INFO_SUFFIX,
24042405
ValidateForStore,
2406+
VERIFY_CLANG_SCANNER_NEGATIVE_STAT_CACHE,
24052407
WARNING_CFLAGS,
24062408
WARNING_LDFLAGS,
24072409
WATCHKIT_2_SUPPORT_FOLDER_PATH,

Sources/SWBTaskExecution/DynamicTaskSpecs/ClangModuleDependencyGraph.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,17 @@ package final class ClangModuleDependencyGraph {
560560
return clangWithScanner.casDBs
561561
}
562562

563+
package func diagnoseInvalidNegativeStatCacheEntries() -> [String] {
564+
registryQueue.blocking_sync {
565+
self.scannerRegistry.values.flatMap { libClangWithScanner in
566+
guard libClangWithScanner.scanner.libclang.supportsNegativeStatCacheDiagnostics else {
567+
return Array<String>()
568+
}
569+
return libClangWithScanner.scanner.diagnoseInvalidNegativeStatCacheEntries()
570+
}
571+
}
572+
}
573+
563574
package func generateReproducer(forFailedDependency dependency: DependencyInfo,
564575
libclangPath: Path, casOptions: CASOptions?) throws -> String? {
565576
let clangWithScanner = try libclangWithScanner(

0 commit comments

Comments
 (0)