Skip to content

Commit 6e4dcbb

Browse files
authored
[clang][deps] Print tracing VFS data (#108056)
Clang's `-cc1 -print-stats` shows lots of useful internal data including basic `FileManager` stats. Since this layer caches some results, it is unclear how that information translates to actual filesystem accesses. This PR uses `llvm::vfs::TracingFileSystem` to provide that missing information. Similar mechanism is implemented for `clang-scan-deps`'s verbose mode (`-v`). IO contention proved to be a real bottleneck a couple of times already and this new feature should make those easier to detect in the future. The tracing VFS is inserted below the caching FS and above the real FS.
1 parent d32982b commit 6e4dcbb

File tree

10 files changed

+102
-4
lines changed

10 files changed

+102
-4
lines changed

clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class DependencyScanningService {
7676
DependencyScanningService(
7777
ScanningMode Mode, ScanningOutputFormat Format,
7878
ScanningOptimizations OptimizeArgs = ScanningOptimizations::Default,
79-
bool EagerLoadModules = false);
79+
bool EagerLoadModules = false, bool TraceVFS = false);
8080

8181
ScanningMode getMode() const { return Mode; }
8282

@@ -86,6 +86,8 @@ class DependencyScanningService {
8686

8787
bool shouldEagerLoadModules() const { return EagerLoadModules; }
8888

89+
bool shouldTraceVFS() const { return TraceVFS; }
90+
8991
DependencyScanningFilesystemSharedCache &getSharedCache() {
9092
return SharedCache;
9193
}
@@ -97,6 +99,8 @@ class DependencyScanningService {
9799
const ScanningOptimizations OptimizeArgs;
98100
/// Whether to set up command-lines to load PCM files eagerly.
99101
const bool EagerLoadModules;
102+
/// Whether to trace VFS accesses.
103+
const bool TraceVFS;
100104
/// The global file system cache.
101105
DependencyScanningFilesystemSharedCache SharedCache;
102106
};

clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ class DependencyScanningTool {
144144
StringRef CWD, const llvm::DenseSet<ModuleID> &AlreadySeen,
145145
LookupModuleOutputCallback LookupModuleOutput);
146146

147+
llvm::vfs::FileSystem &getWorkerVFS() const { return Worker.getVFS(); }
148+
147149
private:
148150
DependencyScanningWorker Worker;
149151
};

clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ class DependencyScanningWorker {
104104

105105
bool shouldEagerLoadModules() const { return EagerLoadModules; }
106106

107+
llvm::vfs::FileSystem &getVFS() const { return *BaseFS; }
108+
107109
private:
108110
std::shared_ptr<PCHContainerOperations> PCHContainerOps;
109111
/// The file system to be used during the scan.

clang/lib/Basic/FileManager.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,5 +692,16 @@ void FileManager::PrintStats() const {
692692
llvm::errs() << NumFileLookups << " file lookups, "
693693
<< NumFileCacheMisses << " file cache misses.\n";
694694

695+
getVirtualFileSystem().visit([](llvm::vfs::FileSystem &VFS) {
696+
if (auto *T = dyn_cast_or_null<llvm::vfs::TracingFileSystem>(&VFS))
697+
llvm::errs() << "\n*** Virtual File System Stats:\n"
698+
<< T->NumStatusCalls << " status() calls\n"
699+
<< T->NumOpenFileForReadCalls << " openFileForRead() calls\n"
700+
<< T->NumDirBeginCalls << " dir_begin() calls\n"
701+
<< T->NumGetRealPathCalls << " getRealPath() calls\n"
702+
<< T->NumExistsCalls << " exists() calls\n"
703+
<< T->NumIsLocalCalls << " isLocal() calls\n";
704+
});
705+
695706
//llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups;
696707
}

clang/lib/Frontend/CompilerInstance.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,9 @@ FileManager *CompilerInstance::createFileManager(
381381
: createVFSFromCompilerInvocation(getInvocation(),
382382
getDiagnostics());
383383
assert(VFS && "FileManager has no VFS?");
384+
if (getFrontendOpts().ShowStats)
385+
VFS =
386+
llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(VFS));
384387
FileMgr = new FileManager(getFileSystemOpts(), std::move(VFS));
385388
return FileMgr.get();
386389
}

clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ using namespace dependencies;
1515

1616
DependencyScanningService::DependencyScanningService(
1717
ScanningMode Mode, ScanningOutputFormat Format,
18-
ScanningOptimizations OptimizeArgs, bool EagerLoadModules)
18+
ScanningOptimizations OptimizeArgs, bool EagerLoadModules, bool TraceVFS)
1919
: Mode(Mode), Format(Format), OptimizeArgs(OptimizeArgs),
20-
EagerLoadModules(EagerLoadModules) {
20+
EagerLoadModules(EagerLoadModules), TraceVFS(TraceVFS) {
2121
// Initialize targets for object file support.
2222
llvm::InitializeAllTargets();
2323
llvm::InitializeAllTargetMCs();

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,9 @@ DependencyScanningWorker::DependencyScanningWorker(
501501
// The scanner itself writes only raw ast files.
502502
PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
503503

504+
if (Service.shouldTraceVFS())
505+
FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS));
506+
504507
switch (Service.getMode()) {
505508
case ScanningMode::DependencyDirectivesScan:
506509
DepFS =

clang/test/ClangScanDeps/verbose.test

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.in > %t/cdb.json
4+
5+
// RUN: clang-scan-deps -compilation-database %t/cdb.json -v -o %t/result.json 2>&1 | FileCheck %s
6+
// CHECK: *** Virtual File System Stats:
7+
// CHECK-NEXT: {{[[:digit:]]+}} status() calls
8+
// CHECK-NEXT: {{[[:digit:]]+}} openFileForRead() calls
9+
// CHECK-NEXT: {{[[:digit:]]+}} dir_begin() calls
10+
// CHECK-NEXT: {{[[:digit:]]+}} getRealPath() calls
11+
// CHECK-NEXT: {{[[:digit:]]+}} exists() calls
12+
// CHECK-NEXT: {{[[:digit:]]+}} isLocal() calls
13+
14+
//--- tu.c
15+
16+
//--- cdb.json.in
17+
[
18+
{
19+
"file": "DIR/tu.c"
20+
"directory": "DIR",
21+
"command": "clang -c DIR/tu.c -o DIR/tu.o"
22+
},
23+
{
24+
"file": "DIR/tu.c"
25+
"directory": "DIR",
26+
"command": "clang -c DIR/tu.c -o DIR/tu.o"
27+
}
28+
]

clang/test/Misc/print-stats-vfs.test

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
4+
// RUN: %clang_cc1 -fsyntax-only %t/tu.c -I %t/dir1 -I %t/dir2 -print-stats 2>&1 | FileCheck %s
5+
6+
//--- tu.c
7+
#include "header.h"
8+
//--- dir1/other.h
9+
//--- dir2/header.h
10+
11+
// CHECK: *** Virtual File System Stats:
12+
// CHECK-NEXT: {{[[:digit:]]+}} status() calls
13+
// CHECK-NEXT: {{[[:digit:]]+}} openFileForRead() calls
14+
// CHECK-NEXT: {{[[:digit:]]+}} dir_begin() calls
15+
// CHECK-NEXT: {{[[:digit:]]+}} getRealPath() calls
16+
// CHECK-NEXT: {{[[:digit:]]+}} exists() calls
17+
// CHECK-NEXT: {{[[:digit:]]+}} isLocal() calls

clang/tools/clang-scan-deps/ClangScanDeps.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,13 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
915915
if (Format == ScanningOutputFormat::Full)
916916
FD.emplace(ModuleName.empty() ? Inputs.size() : 0);
917917

918+
std::atomic<size_t> NumStatusCalls = 0;
919+
std::atomic<size_t> NumOpenFileForReadCalls = 0;
920+
std::atomic<size_t> NumDirBeginCalls = 0;
921+
std::atomic<size_t> NumGetRealPathCalls = 0;
922+
std::atomic<size_t> NumExistsCalls = 0;
923+
std::atomic<size_t> NumIsLocalCalls = 0;
924+
918925
auto ScanningTask = [&](DependencyScanningService &Service) {
919926
DependencyScanningTool WorkerTool(Service);
920927

@@ -999,10 +1006,21 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
9991006
HadErrors = true;
10001007
}
10011008
}
1009+
1010+
WorkerTool.getWorkerVFS().visit([&](llvm::vfs::FileSystem &VFS) {
1011+
if (auto *T = dyn_cast_or_null<llvm::vfs::TracingFileSystem>(&VFS)) {
1012+
NumStatusCalls += T->NumStatusCalls;
1013+
NumOpenFileForReadCalls += T->NumOpenFileForReadCalls;
1014+
NumDirBeginCalls += T->NumDirBeginCalls;
1015+
NumGetRealPathCalls += T->NumGetRealPathCalls;
1016+
NumExistsCalls += T->NumExistsCalls;
1017+
NumIsLocalCalls += T->NumIsLocalCalls;
1018+
}
1019+
});
10021020
};
10031021

10041022
DependencyScanningService Service(ScanMode, Format, OptimizeArgs,
1005-
EagerLoadModules);
1023+
EagerLoadModules, /*TraceVFS=*/Verbose);
10061024

10071025
llvm::Timer T;
10081026
T.startTimer();
@@ -1025,6 +1043,16 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
10251043
}
10261044

10271045
T.stopTimer();
1046+
1047+
if (Verbose)
1048+
llvm::errs() << "\n*** Virtual File System Stats:\n"
1049+
<< NumStatusCalls << " status() calls\n"
1050+
<< NumOpenFileForReadCalls << " openFileForRead() calls\n"
1051+
<< NumDirBeginCalls << " dir_begin() calls\n"
1052+
<< NumGetRealPathCalls << " getRealPath() calls\n"
1053+
<< NumExistsCalls << " exists() calls\n"
1054+
<< NumIsLocalCalls << " isLocal() calls\n";
1055+
10281056
if (PrintTiming)
10291057
llvm::errs() << llvm::format(
10301058
"clang-scan-deps timing: %0.2fs wall, %0.2fs process\n",

0 commit comments

Comments
 (0)