Skip to content

Commit 6c77983

Browse files
authored
Merge pull request #786 from owenv/owenv/stat-cache-diags
Adopt clang API for diagnosing invalid stat cache entries
2 parents 49c22f9 + 8be10d6 commit 6c77983

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
@@ -1170,6 +1170,7 @@ public final class BuiltinMacros {
11701170
public static let VERSION_INFO_STRING = BuiltinMacros.declareStringMacro("VERSION_INFO_STRING")
11711171
public static let VERSION_INFO_SUFFIX = BuiltinMacros.declareStringMacro("VERSION_INFO_SUFFIX")
11721172
public static let ValidateForStore = BuiltinMacros.declareBooleanMacro("ValidateForStore")
1173+
public static let VERIFY_CLANG_SCANNER_NEGATIVE_STAT_CACHE = BuiltinMacros.declareBooleanMacro("VERIFY_CLANG_SCANNER_NEGATIVE_STAT_CACHE")
11731174
public static let WARNING_CFLAGS = BuiltinMacros.declareStringListMacro("WARNING_CFLAGS")
11741175
public static let WARNING_LDFLAGS = BuiltinMacros.declareStringListMacro("WARNING_LDFLAGS")
11751176
public static let WATCHKIT_2_SUPPORT_FOLDER_PATH = BuiltinMacros.declareStringMacro("WATCHKIT_2_SUPPORT_FOLDER_PATH")
@@ -2413,6 +2414,7 @@ public final class BuiltinMacros {
24132414
VERSION_INFO_STRING,
24142415
VERSION_INFO_SUFFIX,
24152416
ValidateForStore,
2417+
VERIFY_CLANG_SCANNER_NEGATIVE_STAT_CACHE,
24162418
WARNING_CFLAGS,
24172419
WARNING_LDFLAGS,
24182420
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)