Skip to content

Commit 6fbcea6

Browse files
authored
[clang Dependency Scanning] Out-of-Date Scanning File System Cache Entry Reporting C-APIs (#10927)
This PR implements the C-APIs to report a scanning file system cache's out-of-date entries. The C-APIs contains a function to return a set of file system cache out-of-date entries, functions to facilitate looping through all the entries, and reporting the relevant information from the entries. The APIs are based on llvm#144105. rdar://152247357
1 parent b71dff1 commit 6fbcea6

File tree

8 files changed

+384
-67
lines changed

8 files changed

+384
-67
lines changed

clang/include/clang-c/Dependencies.h

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -585,10 +585,98 @@ 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);
588+
/**
589+
* The kind of scanning file system cache out-of-date entries.
590+
*/
591+
typedef enum {
592+
/**
593+
* The entry is negatively stat cached (which indicates the file did not exist
594+
* the first time it was looked up during scanning), but the cached file
595+
* exists on the underlying file system.
596+
*/
597+
NegativelyCached,
598+
599+
/**
600+
* The entry indicates that for the cached file, its cached size
601+
* is different from its size reported by the underlying file system.
602+
*/
603+
SizeChanged
604+
} CXDepScanFSCacheOutOfDateKind;
605+
606+
/**
607+
* The opaque object that contains the scanning file system cache's out-of-date
608+
* entires.
609+
*/
610+
typedef struct CXOpaqueDepScanFSOutOfDateEntrySet *CXDepScanFSOutOfDateEntrySet;
611+
612+
/**
613+
* The opaque object that represents a single scanning file system cache's out-
614+
* of-date entry.
615+
*/
616+
typedef struct CXOpaqueDepScanFSOutOfDateEntry *CXDepScanFSOutOfDateEntry;
617+
618+
/**
619+
* Returns all the file system cache out-of-date entries given a
620+
* \c CXDependencyScannerService .
621+
*
622+
* This function is intended to be called when the build has finished,
623+
* and the \c CXDependencyScannerService instance is about to be disposed.
624+
*
625+
* The \c CXDependencyScannerService instance owns the strings used
626+
* by the out-of-date entries and should be disposed after the
627+
* out-of-date entries are used and disposed.
628+
*/
629+
CXDepScanFSOutOfDateEntrySet
630+
clang_experimental_DependencyScannerService_getFSCacheOutOfDateEntrySet(
631+
CXDependencyScannerService S);
632+
633+
/**
634+
* Returns the number of out-of-date entries contained in a
635+
* \c CXDepScanFSOutOfDateEntrySet .
636+
*/
637+
size_t clang_experimental_DepScanFSCacheOutOfDateEntrySet_getNumOfEntries(
638+
CXDepScanFSOutOfDateEntrySet Entries);
639+
640+
/**
641+
* Returns the out-of-date entry at offset \p Idx of the \c
642+
* CXDepScanFSOutOfDateEntrySet instance.
643+
*/
644+
CXDepScanFSOutOfDateEntry
645+
clang_experimental_DepScanFSCacheOutOfDateEntrySet_getEntry(
646+
CXDepScanFSOutOfDateEntrySet Entries, size_t Idx);
647+
648+
/**
649+
* Given an instance of \c CXDepScanFSOutOfDateEntry, returns its Kind.
650+
*/
651+
CXDepScanFSCacheOutOfDateKind
652+
clang_experimental_DepScanFSCacheOutOfDateEntry_getKind(
653+
CXDepScanFSOutOfDateEntry Entry);
654+
655+
/**
656+
* Given an instance of \c CXDepScanFSOutOfDateEntry, returns the path.
657+
*/
658+
CXString clang_experimental_DepScanFSCacheOutOfDateEntry_getPath(
659+
CXDepScanFSOutOfDateEntry Entry);
660+
661+
/**
662+
* Given an instance of \c CXDepScanFSOutOfDateEntry of kind SizeChanged,
663+
* returns the cached size.
664+
*/
665+
uint64_t clang_experimental_DepScanFSCacheOutOfDateEntry_getCachedSize(
666+
CXDepScanFSOutOfDateEntry Entry);
667+
668+
/**
669+
* Given an instance of \c CXDepScanFSOutOfDateEntry of kind SizeChanged,
670+
* returns the actual size on the underlying file system.
671+
*/
672+
uint64_t clang_experimental_DepScanFSCacheOutOfDateEntry_getActualSize(
673+
CXDepScanFSOutOfDateEntry Entry);
674+
675+
/**
676+
* Dispose the \c CXDepScanFSOutOfDateEntrySet instance.
677+
*/
678+
void clang_experimental_DepScanFSCacheOutOfDateEntrySet_disposeSet(
679+
CXDepScanFSOutOfDateEntrySet Entries);
592680

593681
/**
594682
* Options used to generate a reproducer.

clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,22 @@ DependencyScanningFilesystemSharedCache::getOutOfDateEntries(
133133
InvalidDiagInfo.emplace_back(Path.data());
134134
} else {
135135
llvm::vfs::Status CachedStatus = Entry->getStatus();
136-
uint64_t CachedSize = CachedStatus.getSize();
137-
uint64_t ActualSize = Status->getSize();
138-
if (CachedSize != ActualSize) {
139-
// This is the case where the cached file has a different size
140-
// from the actual file that comes from the underlying FS.
141-
InvalidDiagInfo.emplace_back(Path.data(), CachedSize, ActualSize);
136+
if (Status->getType() == llvm::sys::fs::file_type::regular_file &&
137+
Status->getType() == CachedStatus.getType()) {
138+
// We only check regular files. Directory files sizes could change
139+
// due to content changes, and reporting directory size changes can
140+
// lead to false positives.
141+
// TODO: At the moment, we do not detect symlinks to files whose
142+
// size may change. We need to decide if we want to detect cached
143+
// symlink size changes. We can also expand this to detect file
144+
// type changes.
145+
uint64_t CachedSize = CachedStatus.getSize();
146+
uint64_t ActualSize = Status->getSize();
147+
if (CachedSize != ActualSize) {
148+
// This is the case where the cached file has a different size
149+
// from the actual file that comes from the underlying FS.
150+
InvalidDiagInfo.emplace_back(Path.data(), CachedSize, ActualSize);
151+
}
142152
}
143153
}
144154
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +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
7+
// CHECK-NEXT: number of out of date file system cache entries: 0

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -916,12 +916,18 @@ static int scanDeps(ArrayRef<const char *> Args, std::string WorkingDirectory,
916916
clang_disposeDiagnostic(Diag);
917917
}
918918

919-
CXCStringArray InvalidNegativeStatCachedPaths =
920-
clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths(
919+
CXDepScanFSOutOfDateEntrySet OutOfDateEntrySet =
920+
clang_experimental_DependencyScannerService_getFSCacheOutOfDateEntrySet(
921921
Service);
922922

923-
llvm::errs() << "note: number of invalid negatively stat cached paths: "
924-
<< InvalidNegativeStatCachedPaths.Count << "\n";
923+
llvm::errs()
924+
<< "note: number of out of date file system cache entries: "
925+
<< clang_experimental_DepScanFSCacheOutOfDateEntrySet_getNumOfEntries(
926+
OutOfDateEntrySet)
927+
<< "\n";
928+
929+
clang_experimental_DepScanFSCacheOutOfDateEntrySet_disposeSet(
930+
OutOfDateEntrySet);
925931

926932
return 1;
927933
}

clang/tools/libclang/CDependencies.cpp

Lines changed: 100 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,11 @@ struct CStringsManager {
8181
return createCStringsRef(*OwnedStdStr.back());
8282
}
8383
};
84-
85-
struct DependencyScannerService {
86-
DependencyScanningService Service;
87-
CStringsManager StrMgr{};
88-
};
8984
} // end anonymous namespace
9085

9186
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScannerServiceOptions,
9287
CXDependencyScannerServiceOptions)
93-
94-
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScannerService,
88+
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScanningService,
9589
CXDependencyScannerService)
9690
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScanningWorker,
9791
CXDependencyScannerWorker)
@@ -172,9 +166,9 @@ clang_experimental_DependencyScannerService_create_v0(CXDependencyMode Format) {
172166
// FIXME: Pass default CASOpts and nullptr as CachingOnDiskFileSystem now.
173167
CASOptions CASOpts;
174168
IntrusiveRefCntPtr<llvm::cas::CachingOnDiskFileSystem> FS;
175-
return wrap(new DependencyScannerService{DependencyScanningService(
169+
return wrap(new DependencyScanningService(
176170
ScanningMode::DependencyDirectivesScan, unwrap(Format), CASOpts,
177-
/*CAS=*/nullptr, /*ActionCache=*/nullptr, FS)});
171+
/*CAS=*/nullptr, /*ActionCache=*/nullptr, FS));
178172
}
179173

180174
ScanningOutputFormat DependencyScannerServiceOptions::getFormat() const {
@@ -210,10 +204,10 @@ clang_experimental_DependencyScannerService_create_v1(
210204
FS = llvm::cantFail(
211205
llvm::cas::createCachingOnDiskFileSystem(CAS));
212206
}
213-
return wrap(new DependencyScannerService{DependencyScanningService(
207+
return wrap(new DependencyScanningService(
214208
ScanningMode::DependencyDirectivesScan, Format, unwrap(Opts)->CASOpts,
215209
std::move(CAS), std::move(Cache), std::move(FS),
216-
unwrap(Opts)->OptimizeArgs)});
210+
unwrap(Opts)->OptimizeArgs));
217211
}
218212

219213
void clang_experimental_DependencyScannerService_dispose_v0(
@@ -223,16 +217,16 @@ void clang_experimental_DependencyScannerService_dispose_v0(
223217

224218
CXDependencyScannerWorker clang_experimental_DependencyScannerWorker_create_v0(
225219
CXDependencyScannerService S) {
226-
ScanningOutputFormat Format = unwrap(S)->Service.getFormat();
220+
ScanningOutputFormat Format = unwrap(S)->getFormat();
227221
bool IsIncludeTreeOutput = Format == ScanningOutputFormat::IncludeTree ||
228222
Format == ScanningOutputFormat::FullIncludeTree;
229223
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
230224
llvm::vfs::createPhysicalFileSystem();
231225
if (IsIncludeTreeOutput)
232-
FS = llvm::cas::createCASProvidingFileSystem(unwrap(S)->Service.getCAS(),
226+
FS = llvm::cas::createCASProvidingFileSystem(unwrap(S)->getCAS(),
233227
std::move(FS));
234228

235-
return wrap(new DependencyScanningWorker(unwrap(S)->Service, FS));
229+
return wrap(new DependencyScanningWorker(*unwrap(S), FS));
236230
}
237231

238232
void clang_experimental_DependencyScannerWorker_dispose_v0(
@@ -566,43 +560,6 @@ CXDiagnosticSet clang_experimental_DepGraph_getDiagnostics(CXDepGraph Graph) {
566560
return unwrap(Graph)->getDiagnosticSet();
567561
}
568562

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 OutOfDateEntries = SharedCache.getOutOfDateEntries(*FS);
589-
590-
// FIXME: replace this code with proper APIs that handles the
591-
// OutOfDateEntries.
592-
std::vector<const char *> OutOfDatePaths;
593-
for (const auto &E : OutOfDateEntries)
594-
OutOfDatePaths.emplace_back(E.Path);
595-
596-
// FIXME: This code here creates copies of strings from
597-
// InvaidNegStatCachedPaths. It is acceptable because this C-API is expected
598-
// to be called only at the end of a CXDependencyScannerService's lifetime.
599-
// In other words, it is called very infrequently. We can change
600-
// CStringsManager's interface to accommodate handling arbitrary StringRefs
601-
// (which may not be null terminated) if we want to avoid copying.
602-
return StrMgr.createCStringsOwned(
603-
{OutOfDatePaths.begin(), OutOfDatePaths.end()});
604-
}
605-
606563
static std::string
607564
lookupModuleOutput(const ModuleDeps &MD, ModuleOutputKind MOK, void *MLOContext,
608565
std::variant<CXModuleLookupOutputCallback *,
@@ -646,6 +603,98 @@ std::string OutputLookup::lookupModuleOutput(const ModuleDeps &MD,
646603
return PCMPath.first->second;
647604
}
648605

606+
namespace {
607+
typedef std::vector<DependencyScanningFilesystemSharedCache::OutOfDateEntry>
608+
DependencyScannerFSOutOfDateEntrySet;
609+
610+
typedef DependencyScanningFilesystemSharedCache::OutOfDateEntry
611+
DependencyScannerFSOutOfDateEntry;
612+
} // namespace
613+
614+
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScannerFSOutOfDateEntrySet,
615+
CXDepScanFSOutOfDateEntrySet)
616+
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DependencyScannerFSOutOfDateEntry,
617+
CXDepScanFSOutOfDateEntry)
618+
619+
CXDepScanFSOutOfDateEntrySet
620+
clang_experimental_DependencyScannerService_getFSCacheOutOfDateEntrySet(
621+
CXDependencyScannerService S) {
622+
DependencyScanningService &Service = *unwrap(S);
623+
624+
// FIXME: CAS FS currently does not use the shared cache, and cannot produce
625+
// the same diagnostics. We should add such a diagnostics to CAS as well.
626+
if (Service.useCASFS())
627+
return nullptr;
628+
629+
// Note that it is critical that this FS is the same as the default virtual
630+
// file system we pass to the DependencyScanningWorkers.
631+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
632+
llvm::vfs::createPhysicalFileSystem();
633+
634+
DependencyScannerFSOutOfDateEntrySet *OODEntrySet =
635+
new DependencyScannerFSOutOfDateEntrySet();
636+
*OODEntrySet = Service.getSharedCache().getOutOfDateEntries(*FS);
637+
638+
return wrap(OODEntrySet);
639+
}
640+
641+
size_t clang_experimental_DepScanFSCacheOutOfDateEntrySet_getNumOfEntries(
642+
CXDepScanFSOutOfDateEntrySet Entries) {
643+
return unwrap(Entries)->size();
644+
}
645+
646+
CXDepScanFSOutOfDateEntry
647+
clang_experimental_DepScanFSCacheOutOfDateEntrySet_getEntry(
648+
CXDepScanFSOutOfDateEntrySet Entries, size_t Idx) {
649+
DependencyScannerFSOutOfDateEntrySet *EntSet = unwrap(Entries);
650+
return wrap(&(*EntSet)[Idx]);
651+
}
652+
653+
CXDepScanFSCacheOutOfDateKind
654+
clang_experimental_DepScanFSCacheOutOfDateEntry_getKind(
655+
CXDepScanFSOutOfDateEntry Entry) {
656+
DependencyScannerFSOutOfDateEntry *E = unwrap(Entry);
657+
auto &Info = E->Info;
658+
return std::visit(
659+
llvm::makeVisitor(
660+
[](const DependencyScannerFSOutOfDateEntry::NegativelyCachedInfo
661+
&Info) { return NegativelyCached; },
662+
[](const DependencyScannerFSOutOfDateEntry::SizeChangedInfo &Info) {
663+
return SizeChanged;
664+
}),
665+
Info);
666+
}
667+
668+
CXString clang_experimental_DepScanFSCacheOutOfDateEntry_getPath(
669+
CXDepScanFSOutOfDateEntry Entry) {
670+
return cxstring::createRef(unwrap(Entry)->Path);
671+
}
672+
673+
static DependencyScannerFSOutOfDateEntry::SizeChangedInfo *
674+
getOutOfDateEntrySizeChangedInfo(DependencyScannerFSOutOfDateEntry *E) {
675+
auto *SizeInfo =
676+
std::get_if<DependencyScannerFSOutOfDateEntry::SizeChangedInfo>(&E->Info);
677+
assert(SizeInfo && "Wrong entry kind to get size changed info!");
678+
return SizeInfo;
679+
}
680+
681+
uint64_t clang_experimental_DepScanFSCacheOutOfDateEntry_getCachedSize(
682+
CXDepScanFSOutOfDateEntry Entry) {
683+
DependencyScannerFSOutOfDateEntry *E = unwrap(Entry);
684+
return getOutOfDateEntrySizeChangedInfo(E)->CachedSize;
685+
}
686+
687+
uint64_t clang_experimental_DepScanFSCacheOutOfDateEntry_getActualSize(
688+
CXDepScanFSOutOfDateEntry Entry) {
689+
DependencyScannerFSOutOfDateEntry *E = unwrap(Entry);
690+
return getOutOfDateEntrySizeChangedInfo(E)->ActualSize;
691+
}
692+
693+
void clang_experimental_DepScanFSCacheOutOfDateEntrySet_disposeSet(
694+
CXDepScanFSOutOfDateEntrySet Entries) {
695+
delete unwrap(Entries);
696+
}
697+
649698
namespace {
650699
struct DependencyScannerReproducerOptions {
651700
std::vector<std::string> BuildArgs;

clang/tools/libclang/libclang.map

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,6 @@ LLVM_21 {
577577
clang_experimental_DepGraphModule_isCWDIgnored;
578578
clang_experimental_DepGraphModule_isInStableDirs;
579579
clang_getFullyQualifiedName;
580-
clang_experimental_DependencyScannerService_getInvalidNegStatCachedPaths;
581580
clang_experimental_DependencyScannerReproducerOptions_create;
582581
clang_experimental_DependencyScannerReproducerOptions_dispose;
583582
clang_experimental_DependencyScanner_generateReproducer;
@@ -590,6 +589,14 @@ LLVM_21 {
590589
clang_Cursor_getGCCAssemblyNumClobbers;
591590
clang_Cursor_getGCCAssemblyClobber;
592591
clang_Cursor_isGCCAssemblyVolatile;
592+
clang_experimental_DependencyScannerService_getFSCacheOutOfDateEntrySet;
593+
clang_experimental_DepScanFSCacheOutOfDateEntrySet_getNumOfEntries;
594+
clang_experimental_DepScanFSCacheOutOfDateEntrySet_getEntry;
595+
clang_experimental_DepScanFSCacheOutOfDateEntry_getKind;
596+
clang_experimental_DepScanFSCacheOutOfDateEntry_getPath;
597+
clang_experimental_DepScanFSCacheOutOfDateEntry_getCachedSize;
598+
clang_experimental_DepScanFSCacheOutOfDateEntry_getActualSize;
599+
clang_experimental_DepScanFSCacheOutOfDateEntrySet_disposeSet;
593600
};
594601

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

clang/unittests/libclang/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
add_clang_unittest(libclangTests
22
LibclangTest.cpp
33
DriverTest.cpp
4+
DependencyScanningCAPITests.cpp
45
LINK_LIBS
56
libclang
67
)

0 commit comments

Comments
 (0)