Skip to content

Commit b7aa45c

Browse files
authored
[clang][Modules] Adding C-API for Negative Stat Caching Diagnostics (#10524)
llvm#135703 added a C++ API to the shared cached to diagnose invalid negatively stat cached paths. This PR adds a C API so an external system can take advantage of the diagnostics. rdar://149147920
1 parent 005b675 commit b7aa45c

File tree

6 files changed

+106
-54
lines changed

6 files changed

+106
-54
lines changed

clang/include/clang-c/Dependencies.h

+5
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,11 @@ const char *clang_experimental_DepGraph_getTUContextHash(CXDepGraph);
585585
CINDEX_LINKAGE
586586
CXDiagnosticSet clang_experimental_DepGraph_getDiagnostics(CXDepGraph);
587587

588+
CINDEX_LINKAGE
589+
CXCStringArray
590+
clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths(
591+
CXDependencyScannerService);
592+
588593
/**
589594
* @}
590595
*/

clang/test/ClangScanDeps/error-c-api.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44

55
// CHECK: error: failed to get dependencies
66
// CHECK-NEXT: 'missing.h' file not found
7+
// CHECK-NEXT: number of invalid negatively stat cached paths: 0

clang/tools/c-index-test/core_main.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,14 @@ static int scanDeps(ArrayRef<const char *> Args, std::string WorkingDirectory,
907907
clang_disposeString(Spelling);
908908
clang_disposeDiagnostic(Diag);
909909
}
910+
911+
CXCStringArray InvalidNegativeStatCachedPaths =
912+
clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths(
913+
Service);
914+
915+
llvm::errs() << "note: number of invalid negatively stat cached paths: "
916+
<< InvalidNegativeStatCachedPaths.Count << "\n";
917+
910918
return 1;
911919
}
912920

clang/tools/libclang/CDependencies.cpp

+86-49
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,57 @@ struct DependencyScannerServiceOptions {
4141

4242
ScanningOutputFormat getFormat() const;
4343
};
44+
45+
struct CStringsManager {
46+
SmallVector<std::unique_ptr<std::vector<const char *>>> OwnedCStr;
47+
SmallVector<std::unique_ptr<std::vector<std::string>>> OwnedStdStr;
48+
49+
/// Doesn't own the string contents.
50+
CXCStringArray createCStringsRef(ArrayRef<std::string> Strings) {
51+
OwnedCStr.push_back(std::make_unique<std::vector<const char *>>());
52+
std::vector<const char *> &CStrings = *OwnedCStr.back();
53+
CStrings.reserve(Strings.size());
54+
for (const auto &String : Strings)
55+
CStrings.push_back(String.c_str());
56+
return {CStrings.data(), CStrings.size()};
57+
}
58+
59+
/// Doesn't own the string contents.
60+
CXCStringArray createCStringsRef(const llvm::StringSet<> &StringsUnordered) {
61+
std::vector<StringRef> Strings;
62+
63+
for (auto SI = StringsUnordered.begin(), SE = StringsUnordered.end();
64+
SI != SE; ++SI)
65+
Strings.push_back(SI->getKey());
66+
67+
llvm::sort(Strings);
68+
69+
OwnedCStr.push_back(std::make_unique<std::vector<const char *>>());
70+
std::vector<const char *> &CStrings = *OwnedCStr.back();
71+
CStrings.reserve(Strings.size());
72+
for (const auto &String : Strings)
73+
CStrings.push_back(String.data());
74+
return {CStrings.data(), CStrings.size()};
75+
}
76+
77+
/// Gets ownership of string contents.
78+
CXCStringArray createCStringsOwned(std::vector<std::string> &&Strings) {
79+
OwnedStdStr.push_back(
80+
std::make_unique<std::vector<std::string>>(std::move(Strings)));
81+
return createCStringsRef(*OwnedStdStr.back());
82+
}
83+
};
84+
85+
struct DependencyScannerService {
86+
DependencyScanningService Service;
87+
CStringsManager StrMgr{};
88+
};
4489
} // end anonymous namespace
4590

4691
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScannerServiceOptions,
4792
CXDependencyScannerServiceOptions)
4893

49-
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScanningService,
94+
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScannerService,
5095
CXDependencyScannerService)
5196
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScanningWorker,
5297
CXDependencyScannerWorker)
@@ -127,9 +172,9 @@ clang_experimental_DependencyScannerService_create_v0(CXDependencyMode Format) {
127172
// FIXME: Pass default CASOpts and nullptr as CachingOnDiskFileSystem now.
128173
CASOptions CASOpts;
129174
IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> FS;
130-
return wrap(new DependencyScanningService(
175+
return wrap(new DependencyScannerService{DependencyScanningService(
131176
ScanningMode::DependencyDirectivesScan, unwrap(Format), CASOpts,
132-
/*CAS=*/nullptr, /*ActionCache=*/nullptr, FS));
177+
/*CAS=*/nullptr, /*ActionCache=*/nullptr, FS)});
133178
}
134179

135180
ScanningOutputFormat DependencyScannerServiceOptions::getFormat() const {
@@ -165,10 +210,10 @@ clang_experimental_DependencyScannerService_create_v1(
165210
FS = llvm::cantFail(
166211
llvm::cas::createCachingOnDiskFileSystem(CAS));
167212
}
168-
return wrap(new DependencyScanningService(
213+
return wrap(new DependencyScannerService{DependencyScanningService(
169214
ScanningMode::DependencyDirectivesScan, Format, unwrap(Opts)->CASOpts,
170215
std::move(CAS), std::move(Cache), std::move(FS),
171-
unwrap(Opts)->OptimizeArgs));
216+
unwrap(Opts)->OptimizeArgs)});
172217
}
173218

174219
void clang_experimental_DependencyScannerService_dispose_v0(
@@ -177,17 +222,17 @@ void clang_experimental_DependencyScannerService_dispose_v0(
177222
}
178223

179224
CXDependencyScannerWorker clang_experimental_DependencyScannerWorker_create_v0(
180-
CXDependencyScannerService Service) {
181-
ScanningOutputFormat Format = unwrap(Service)->getFormat();
225+
CXDependencyScannerService S) {
226+
ScanningOutputFormat Format = unwrap(S)->Service.getFormat();
182227
bool IsIncludeTreeOutput = Format == ScanningOutputFormat::IncludeTree ||
183228
Format == ScanningOutputFormat::FullIncludeTree;
184229
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
185230
llvm::vfs::createPhysicalFileSystem();
186231
if (IsIncludeTreeOutput)
187-
FS = llvm::cas::createCASProvidingFileSystem(unwrap(Service)->getCAS(),
232+
FS = llvm::cas::createCASProvidingFileSystem(unwrap(S)->Service.getCAS(),
188233
std::move(FS));
189234

190-
return wrap(new DependencyScanningWorker(*unwrap(Service), FS));
235+
return wrap(new DependencyScanningWorker(unwrap(S)->Service, FS));
191236
}
192237

193238
void clang_experimental_DependencyScannerWorker_dispose_v0(
@@ -223,46 +268,6 @@ struct DependencyScannerWorkerScanSettings {
223268
MLO;
224269
};
225270

226-
struct CStringsManager {
227-
SmallVector<std::unique_ptr<std::vector<const char *>>> OwnedCStr;
228-
SmallVector<std::unique_ptr<std::vector<std::string>>> OwnedStdStr;
229-
230-
/// Doesn't own the string contents.
231-
CXCStringArray createCStringsRef(ArrayRef<std::string> Strings) {
232-
OwnedCStr.push_back(std::make_unique<std::vector<const char *>>());
233-
std::vector<const char *> &CStrings = *OwnedCStr.back();
234-
CStrings.reserve(Strings.size());
235-
for (const auto &String : Strings)
236-
CStrings.push_back(String.c_str());
237-
return {CStrings.data(), CStrings.size()};
238-
}
239-
240-
/// Doesn't own the string contents.
241-
CXCStringArray createCStringsRef(const llvm::StringSet<> &StringsUnordered) {
242-
std::vector<StringRef> Strings;
243-
244-
for (auto SI = StringsUnordered.begin(), SE = StringsUnordered.end();
245-
SI != SE; ++SI)
246-
Strings.push_back(SI->getKey());
247-
248-
llvm::sort(Strings);
249-
250-
OwnedCStr.push_back(std::make_unique<std::vector<const char *>>());
251-
std::vector<const char *> &CStrings = *OwnedCStr.back();
252-
CStrings.reserve(Strings.size());
253-
for (const auto &String : Strings)
254-
CStrings.push_back(String.data());
255-
return {CStrings.data(), CStrings.size()};
256-
}
257-
258-
/// Gets ownership of string contents.
259-
CXCStringArray createCStringsOwned(std::vector<std::string> &&Strings) {
260-
OwnedStdStr.push_back(
261-
std::make_unique<std::vector<std::string>>(std::move(Strings)));
262-
return createCStringsRef(*OwnedStdStr.back());
263-
}
264-
};
265-
266271
struct DependencyGraph {
267272
TranslationUnitDeps TUDeps;
268273
SmallString<256> SerialDiagBuf;
@@ -561,6 +566,38 @@ CXDiagnosticSet clang_experimental_DepGraph_getDiagnostics(CXDepGraph Graph) {
561566
return unwrap(Graph)->getDiagnosticSet();
562567
}
563568

569+
CXCStringArray
570+
clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths(
571+
CXDependencyScannerService S) {
572+
DependencyScanningService &Service = unwrap(S)->Service;
573+
CStringsManager &StrMgr = unwrap(S)->StrMgr;
574+
575+
// FIXME: CAS currently does not use the shared cache, and cannot produce
576+
// the same diagnostics. We should add such a diagnostics to CAS as well.
577+
if (Service.useCASFS())
578+
return {nullptr, 0};
579+
580+
DependencyScanningFilesystemSharedCache &SharedCache =
581+
Service.getSharedCache();
582+
583+
// Note that it is critical that this FS is the same as the default virtual
584+
// file system we pass to the DependencyScanningWorkers.
585+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
586+
llvm::vfs::createPhysicalFileSystem();
587+
588+
auto InvaidNegStatCachedPaths =
589+
SharedCache.getInvalidNegativeStatCachedPaths(*FS);
590+
591+
// FIXME: This code here creates copies of strings from
592+
// InvaidNegStatCachedPaths. It is acceptable because this C-API is expected
593+
// to be called only at the end of a CXDependencyScannerService's lifetime.
594+
// In other words, it is called very infrequently. We can change
595+
// CStringsManager's interface to accommodate handling arbitrary StringRefs
596+
// (which may not be null terminated) if we want to avoid copying.
597+
return StrMgr.createCStringsOwned(
598+
{InvaidNegStatCachedPaths.begin(), InvaidNegStatCachedPaths.end()});
599+
}
600+
564601
static std::string
565602
lookupModuleOutput(const ModuleDeps &MD, ModuleOutputKind MOK, void *MLOContext,
566603
std::variant<CXModuleLookupOutputCallback *,

clang/tools/libclang/CXString.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,13 @@ CXStringSet *createSet(const std::vector<std::string> &Strings) {
118118

119119
CXStringSet *createSet(const llvm::StringSet<> &StringsUnordered) {
120120
std::vector<StringRef> Strings;
121-
122-
for (auto SI = StringsUnordered.begin(),
123-
SE = StringsUnordered.end(); SI != SE; ++SI)
121+
122+
for (auto SI = StringsUnordered.begin(), SE = StringsUnordered.end();
123+
SI != SE; ++SI)
124124
Strings.push_back(SI->getKey());
125-
125+
126126
llvm::sort(Strings);
127-
127+
128128
CXStringSet *Set = new CXStringSet;
129129
Set->Count = Strings.size();
130130
Set->Strings = new CXString[Set->Count];

clang/tools/libclang/libclang.map

+1
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ LLVM_21 {
577577
clang_experimental_DepGraphModule_isCWDIgnored;
578578
clang_experimental_DepGraphModule_isInStableDirs;
579579
clang_getFullyQualifiedName;
580+
clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths;
580581
};
581582

582583
# Example of how to add a new symbol version entry. If you do add a new symbol

0 commit comments

Comments
 (0)