[clang] [llvm] [llvm][clang] Trace VFS calls (PR #88326)
Jan Svoboda via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 10 16:04:09 PDT 2024
https://github.com/jansvoboda11 created https://github.com/llvm/llvm-project/pull/88326
None
>From 540321e84dbd3c5687cfcc60e9deec79d790896e Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Wed, 10 Apr 2024 16:03:19 -0700
Subject: [PATCH] [llvm][clang] Trace VFS calls
---
clang/include/clang/Driver/Compilation.h | 11 +++++
clang/include/clang/Driver/Options.td | 7 ++++
.../include/clang/Frontend/CompilerInstance.h | 8 ++++
.../clang/Frontend/CompilerInvocation.h | 10 +++--
.../include/clang/Frontend/FrontendOptions.h | 3 ++
clang/lib/Driver/Driver.cpp | 34 ++++++++++++++-
clang/lib/Driver/ToolChains/Clang.cpp | 3 ++
clang/lib/Frontend/CompilerInstance.cpp | 2 +-
clang/lib/Frontend/CompilerInvocation.cpp | 19 ++++++---
.../DependencyScanningWorker.cpp | 1 +
.../DependencyScanning/ModuleDepCollector.cpp | 7 ++++
clang/test/ClangScanDeps/modules-inferred.m | 2 +-
clang/tools/clang-scan-deps/ClangScanDeps.cpp | 25 ++++++++++-
clang/tools/clang-scan-deps/Opts.td | 1 +
clang/tools/driver/cc1_main.cpp | 13 ++++++
llvm/include/llvm/Support/VirtualFileSystem.h | 29 +++++++++++++
llvm/lib/Support/VirtualFileSystem.cpp | 42 +++++++++++++++++++
.../Support/VirtualFileSystemTest.cpp | 37 ++++++++++++++++
18 files changed, 241 insertions(+), 13 deletions(-)
diff --git a/clang/include/clang/Driver/Compilation.h b/clang/include/clang/Driver/Compilation.h
index 36ae85c4245143..baf55d6b0f6061 100644
--- a/clang/include/clang/Driver/Compilation.h
+++ b/clang/include/clang/Driver/Compilation.h
@@ -115,6 +115,9 @@ class Compilation {
/// -ftime-trace result files.
ArgStringMap TimeTraceFiles;
+ /// -fvfs-trace result files.
+ ArgStringMap VFSTraceFiles;
+
/// Optional redirection for stdin, stdout, stderr.
std::vector<std::optional<StringRef>> Redirects;
@@ -280,6 +283,14 @@ class Compilation {
TimeTraceFiles[JA] = Name;
}
+ const char *getVFSTraceFile(const JobAction *JA) const {
+ return VFSTraceFiles.lookup(JA);
+ }
+ void addVFSTraceFile(const char *Name, const JobAction *JA) {
+ assert(!VFSTraceFiles.contains(JA));
+ VFSTraceFiles[JA] = Name;
+ }
+
/// CleanupFile - Delete a given file.
///
/// \param IssueErrors - Report failures as errors.
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 0a74e6c75f95bb..630c1c763b5180 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3904,6 +3904,13 @@ def ftime_trace_EQ : Joined<["-"], "ftime-trace=">, Group<f_Group>,
HelpText<"Similar to -ftime-trace. Specify the JSON file or a directory which will contain the JSON file">,
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
MarshallingInfoString<FrontendOpts<"TimeTracePath">>;
+def fvfs_trace : Flag<["-"], "fvfs-trace">, Group<f_Group>,
+ HelpText<"Turn of virtual file system profiler. Generates text file based on output filename.">,
+ Visibility<[ClangOption, CLOption, DXCOption]>;
+def fvfs_trace_EQ : Joined<["-"], "fvfs-trace=">, Group<f_Group>,
+ HelpText<"Similar to -fvfs-trace. Specify the text file or a directory that will contain the text file">,
+ Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
+ MarshallingInfoString<FrontendOpts<"VFSTracePath">>;
def fproc_stat_report : Joined<["-"], "fproc-stat-report">, Group<f_Group>,
HelpText<"Print subprocess statistics">;
def fproc_stat_report_EQ : Joined<["-"], "fproc-stat-report=">, Group<f_Group>,
diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index 3464654284f199..2135002366a33a 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -35,6 +35,9 @@ namespace llvm {
class raw_fd_ostream;
class Timer;
class TimerGroup;
+namespace vfs {
+struct InstrumentingFileSystem;
+}
}
namespace clang {
@@ -89,6 +92,11 @@ class CompilerInstance : public ModuleLoader {
/// Auxiliary Target info.
IntrusiveRefCntPtr<TargetInfo> AuxTarget;
+public:
+ /// The instrumenting file system.
+ IntrusiveRefCntPtr<llvm::vfs::InstrumentingFileSystem> IVFS;
+private:
+
/// The file manager.
IntrusiveRefCntPtr<FileManager> FileMgr;
diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h
index 1a2a39411e58d8..b50e7c7da636ec 100644
--- a/clang/include/clang/Frontend/CompilerInvocation.h
+++ b/clang/include/clang/Frontend/CompilerInvocation.h
@@ -39,6 +39,7 @@ class ArgList;
namespace vfs {
class FileSystem;
+struct InstrumentingFileSystem;
} // namespace vfs
@@ -390,13 +391,14 @@ class CowCompilerInvocation : public CompilerInvocationBase {
/// @}
};
-IntrusiveRefCntPtr<llvm::vfs::FileSystem>
-createVFSFromCompilerInvocation(const CompilerInvocation &CI,
- DiagnosticsEngine &Diags);
+IntrusiveRefCntPtr<llvm::vfs::FileSystem> createVFSFromCompilerInvocation(
+ const CompilerInvocation &CI, DiagnosticsEngine &Diags,
+ IntrusiveRefCntPtr<llvm::vfs::InstrumentingFileSystem> *TracingFS = {});
IntrusiveRefCntPtr<llvm::vfs::FileSystem> createVFSFromCompilerInvocation(
const CompilerInvocation &CI, DiagnosticsEngine &Diags,
- IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS);
+ IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
+ IntrusiveRefCntPtr<llvm::vfs::InstrumentingFileSystem> *TracingFS = {});
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
createVFSFromOverlayFiles(ArrayRef<std::string> VFSOverlayFiles,
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 5ee4d471670f48..2cbf8bd7735888 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -568,6 +568,9 @@ class FrontendOptions {
/// Path which stores the output files for -ftime-trace
std::string TimeTracePath;
+ /// Path which stores the output files for -fvfs-trace
+ std::string VFSTracePath;
+
public:
FrontendOptions()
: DisableFree(false), RelocatablePCH(false), ShowHelp(false),
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index e7335a61b10c53..9bd13a14f48b6e 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -5429,6 +5429,36 @@ static void handleTimeTrace(Compilation &C, const ArgList &Args,
C.addResultFile(ResultFile, JA);
}
+static void handleVFSTrace(Compilation &C, const ArgList &Args,
+ const JobAction *JA, const char *BaseInput,
+ const InputInfo &Result) {
+ Arg *A = Args.getLastArg(options::OPT_fvfs_trace, options::OPT_fvfs_trace_EQ);
+ if (!A)
+ return;
+ SmallString<128> Path;
+ if (A->getOption().matches(options::OPT_fvfs_trace_EQ)) {
+ Path = A->getValue();
+ if (llvm::sys::fs::is_directory(Path)) {
+ SmallString<128> Tmp(Result.getFilename());
+ llvm::sys::path::replace_extension(Tmp, "vfs.txt");
+ llvm::sys::path::append(Path, llvm::sys::path::filename(Tmp));
+ }
+ } else {
+ if (Arg *DumpDir = Args.getLastArgNoClaim(options::OPT_dumpdir)) {
+ // The trace file is ${dumpdir}${basename}.vfs.txt. Note that dumpdir may
+ // not end with a path separator.
+ Path = DumpDir->getValue();
+ Path += llvm::sys::path::filename(BaseInput);
+ } else {
+ Path = Result.getFilename();
+ }
+ llvm::sys::path::replace_extension(Path, "vfs.txt");
+ }
+ const char *ResultFile = C.getArgs().MakeArgString(Path);
+ C.addVFSTraceFile(ResultFile, JA);
+ C.addResultFile(ResultFile, JA);
+}
+
InputInfoList Driver::BuildJobsForActionNoCache(
Compilation &C, const Action *A, const ToolChain *TC, StringRef BoundArch,
bool AtTopLevel, bool MultipleArchs, const char *LinkingOutput,
@@ -5678,8 +5708,10 @@ InputInfoList Driver::BuildJobsForActionNoCache(
AtTopLevel, MultipleArchs,
OffloadingPrefix),
BaseInput);
- if (T->canEmitIR() && OffloadingPrefix.empty())
+ if (T->canEmitIR() && OffloadingPrefix.empty()) {
handleTimeTrace(C, Args, JA, BaseInput, Result);
+ handleVFSTrace(C, Args, JA, BaseInput, Result);
+ }
}
if (CCCPrintBindings && !CCGenDiagnostics) {
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 766a9b91e3c0ad..5e179d0497fab0 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6759,6 +6759,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_granularity_EQ);
}
+ if (const char *Name = C.getVFSTraceFile(&JA))
+ CmdArgs.push_back(Args.MakeArgString("-fvfs-trace=" + Twine(Name)));
+
if (Arg *A = Args.getLastArg(options::OPT_ftrapv_handler_EQ)) {
CmdArgs.push_back("-ftrapv-handler");
CmdArgs.push_back(A->getValue());
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 6e3baf83864415..4620184a5f6b06 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -379,7 +379,7 @@ FileManager *CompilerInstance::createFileManager(
if (!VFS)
VFS = FileMgr ? &FileMgr->getVirtualFileSystem()
: createVFSFromCompilerInvocation(getInvocation(),
- getDiagnostics());
+ getDiagnostics(), &IVFS);
assert(VFS && "FileManager has no VFS?");
FileMgr = new FileManager(getFileSystemOpts(), std::move(VFS));
return FileMgr.get();
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 1f1f5440ddd75f..f6cae9d40591d4 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -4935,16 +4935,25 @@ void CompilerInvocation::clearImplicitModuleBuildOptions() {
}
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
-clang::createVFSFromCompilerInvocation(const CompilerInvocation &CI,
- DiagnosticsEngine &Diags) {
- return createVFSFromCompilerInvocation(CI, Diags,
- llvm::vfs::getRealFileSystem());
+clang::createVFSFromCompilerInvocation(
+ const CompilerInvocation &CI, DiagnosticsEngine &Diags,
+ IntrusiveRefCntPtr<llvm::vfs::InstrumentingFileSystem> *TracingFS) {
+ return createVFSFromCompilerInvocation(
+ CI, Diags, llvm::vfs::getRealFileSystem(), TracingFS);
}
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
clang::createVFSFromCompilerInvocation(
const CompilerInvocation &CI, DiagnosticsEngine &Diags,
- IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) {
+ IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
+ IntrusiveRefCntPtr<llvm::vfs::InstrumentingFileSystem> *TracingFS) {
+ if (!CI.getFrontendOpts().VFSTracePath.empty()) {
+ auto TFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InstrumentingFileSystem>(
+ std::move(BaseFS));
+ if (TracingFS)
+ *TracingFS = TFS;
+ BaseFS = std::move(TFS);
+ }
return createVFSFromOverlayFiles(CI.getHeaderSearchOpts().VFSOverlayFiles,
Diags, std::move(BaseFS));
}
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
index 32850f5eea92a9..39f8eb4bbb1e2e 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -338,6 +338,7 @@ class DependencyScanningAction : public tooling::ToolAction {
ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
ScanInstance.getFrontendOpts().ModulesShareFileManager = false;
+ ScanInstance.getFrontendOpts().VFSTracePath.clear();
ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage =
any(OptimizeArgs & ScanningOptimizations::VFS);
diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index 94ccbd3351b09d..f8b43c682d5e52 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -152,6 +152,11 @@ void ModuleDepCollector::addOutputPaths(CowCompilerInvocation &CI,
CI.getMutDependencyOutputOpts().Targets.push_back(std::string(Target));
}
}
+ if (!CI.getMutFrontendOpts().VFSTracePath.empty()) {
+ SmallString<128> VFSTracePath{CI.getMutFrontendOpts().OutputFile};
+ llvm::sys::path::replace_extension(VFSTracePath, "vfs.txt");
+ CI.getMutFrontendOpts().VFSTracePath = VFSTracePath.str();
+ }
}
static CowCompilerInvocation
@@ -186,6 +191,8 @@ makeCommonInvocationForModuleBuild(CompilerInvocation CI) {
CI.getDiagnosticOpts().DiagnosticSerializationFile = "-";
if (!CI.getDependencyOutputOpts().OutputFile.empty())
CI.getDependencyOutputOpts().OutputFile = "-";
+ if (!CI.getFrontendOpts().VFSTracePath.empty())
+ CI.getFrontendOpts().VFSTracePath = "-";
CI.getDependencyOutputOpts().Targets.clear();
CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
diff --git a/clang/test/ClangScanDeps/modules-inferred.m b/clang/test/ClangScanDeps/modules-inferred.m
index 4d18a20949205f..a10c7e4d9bde66 100644
--- a/clang/test/ClangScanDeps/modules-inferred.m
+++ b/clang/test/ClangScanDeps/modules-inferred.m
@@ -21,7 +21,7 @@
[{
"directory": "DIR",
"file": "DIR/tu.m",
- "command": "clang -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -F DIR/frameworks -c DIR/tu.m -o DIR/tu.o"
+ "command": "clang -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -F DIR/frameworks -c DIR/tu.m -o DIR/tu.o -fvfs-trace"
}]
// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json
diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index eaa76dd43e41dd..a8f0e0cf5a7f18 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -84,6 +84,7 @@ static std::vector<std::string> ModuleDepTargets;
static bool DeprecatedDriverCommand;
static ResourceDirRecipeKind ResourceDirRecipe;
static bool Verbose;
+static bool PrintVFSTrace;
static bool PrintTiming;
static std::vector<const char *> CommandLine;
@@ -219,6 +220,8 @@ static void ParseArgs(int argc, char **argv) {
ResourceDirRecipe = *Kind;
}
+ PrintVFSTrace = Args.hasArg(OPT_vfs_trace);
+
PrintTiming = Args.hasArg(OPT_print_timing);
Verbose = Args.hasArg(OPT_verbose);
@@ -886,8 +889,16 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
if (Format == ScanningOutputFormat::Full)
FD.emplace(ModuleName.empty() ? Inputs.size() : 0);
+ std::atomic<std::size_t> NumStatusCalls = 0;
+ std::atomic<std::size_t> NumOpenCalls = 0;
+ std::atomic<std::size_t> NumDirBeginCalls = 0;
+ std::atomic<std::size_t> NumRealPathCalls = 0;
+
auto ScanningTask = [&](DependencyScanningService &Service) {
- DependencyScanningTool WorkerTool(Service);
+ auto TracingFS =
+ llvm::makeIntrusiveRefCnt<llvm::vfs::InstrumentingFileSystem>(
+ llvm::vfs::createPhysicalFileSystem());
+ DependencyScanningTool WorkerTool(Service, TracingFS);
llvm::DenseSet<ModuleID> AlreadySeenModules;
while (auto MaybeInputIndex = GetNextInputIndex()) {
@@ -970,6 +981,11 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
HadErrors = true;
}
}
+
+ NumStatusCalls += TracingFS->NumStatusCalls;
+ NumOpenCalls += TracingFS->NumOpenCalls;
+ NumDirBeginCalls += TracingFS->NumDirBeginCalls;
+ NumRealPathCalls += TracingFS->NumRealPathCalls;
};
DependencyScanningService Service(ScanMode, Format, OptimizeArgs,
@@ -1001,6 +1017,13 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
"clang-scan-deps timing: %0.2fs wall, %0.2fs process\n",
T.getTotalTime().getWallTime(), T.getTotalTime().getProcessTime());
+ if (PrintVFSTrace)
+ llvm::errs() << llvm::format(
+ "clang-scan-deps VFS trace: %d status, %d openFileForRead, %d "
+ "dir_begin, %d getRealPath\n",
+ NumStatusCalls.load(), NumOpenCalls.load(), NumDirBeginCalls.load(),
+ NumRealPathCalls.load());
+
if (RoundTripArgs)
if (FD && FD->roundTripCommands(llvm::errs()))
HadErrors = true;
diff --git a/clang/tools/clang-scan-deps/Opts.td b/clang/tools/clang-scan-deps/Opts.td
index 5cd5d1a9fb37bc..07bc72c60d27db 100644
--- a/clang/tools/clang-scan-deps/Opts.td
+++ b/clang/tools/clang-scan-deps/Opts.td
@@ -31,6 +31,7 @@ def deprecated_driver_command : F<"deprecated-driver-command", "use a single dri
defm resource_dir_recipe : Eq<"resource-dir-recipe", "How to produce missing '-resource-dir' argument">;
+def vfs_trace: F<"fvfs-trace", "Profile virtual file system calls">;
def print_timing : F<"print-timing", "Print timing information">;
def verbose : F<"v", "Use verbose output">;
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index b5c6be3c557bb3..408a942ff1f9b7 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -261,6 +261,19 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
}
}
+ if (!Clang->getFrontendOpts().VFSTracePath.empty()) {
+ assert(Clang->IVFS);
+ if (auto VFSOutput = Clang->createOutputFile(
+ Clang->getFrontendOpts().VFSTracePath, /*Binary=*/false,
+ /*RemoveFileOnSignal=*/false,
+ /*useTemporary=*/false)) {
+ *VFSOutput << "status\t" << Clang->IVFS->NumStatusCalls << "\n"
+ << "openFileForRead\t" << Clang->IVFS->NumOpenCalls << "\n"
+ << "dir_begin\t" << Clang->IVFS->NumDirBeginCalls << "\n"
+ << "getRealPath\t" << Clang->IVFS->NumRealPathCalls << "\n";
+ }
+ }
+
// Our error handler depends on the Diagnostics object, which we're
// potentially about to delete. Uninstall the handler now so that any
// later errors use the default handling behavior instead.
diff --git a/llvm/include/llvm/Support/VirtualFileSystem.h b/llvm/include/llvm/Support/VirtualFileSystem.h
index 770ca8764426a4..e175996ee1c651 100644
--- a/llvm/include/llvm/Support/VirtualFileSystem.h
+++ b/llvm/include/llvm/Support/VirtualFileSystem.h
@@ -26,6 +26,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
+#include <atomic>
#include <cassert>
#include <cstdint>
#include <ctime>
@@ -1125,6 +1126,34 @@ class YAMLVFSWriter {
void write(llvm::raw_ostream &OS);
};
+/// File system that tracks the number of calls to the underlying file system.
+/// This is particularly useful when wrapped around \c RealFileSystem to add
+/// lightweight tracking of expensive syscalls.
+struct InstrumentingFileSystem
+ : llvm::RTTIExtends<InstrumentingFileSystem, OverlayFileSystem> {
+ static const char ID;
+
+ std::atomic<std::size_t> NumStatusCalls = 0;
+ std::atomic<std::size_t> NumOpenCalls = 0;
+ std::atomic<std::size_t> NumDirBeginCalls = 0;
+ mutable std::atomic<std::size_t> NumRealPathCalls = 0;
+
+ InstrumentingFileSystem(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS);
+
+ ErrorOr<Status> status(const Twine &Path) override;
+
+ ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
+
+ directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
+
+ std::error_code getRealPath(const Twine &Path,
+ SmallVectorImpl<char> &Output) const override;
+
+protected:
+ void printImpl(raw_ostream &OS, PrintType Type,
+ unsigned IndentLevel) const override;
+};
+
} // namespace vfs
} // namespace llvm
diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp
index 057f8eae0552c6..70bc8c28a18ae4 100644
--- a/llvm/lib/Support/VirtualFileSystem.cpp
+++ b/llvm/lib/Support/VirtualFileSystem.cpp
@@ -2880,8 +2880,50 @@ recursive_directory_iterator::increment(std::error_code &EC) {
return *this;
}
+InstrumentingFileSystem::InstrumentingFileSystem(
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
+ : RTTIExtends(std::move(FS)) {}
+
+ErrorOr<Status> InstrumentingFileSystem::status(const Twine &Path) {
+ ++NumStatusCalls;
+ return OverlayFileSystem::status(Path);
+}
+
+ErrorOr<std::unique_ptr<File>>
+InstrumentingFileSystem::openFileForRead(const Twine &Path) {
+ ++NumOpenCalls;
+ return OverlayFileSystem::openFileForRead(Path);
+}
+
+directory_iterator InstrumentingFileSystem::dir_begin(const Twine &Dir,
+ std::error_code &EC) {
+ ++NumDirBeginCalls;
+ return OverlayFileSystem::dir_begin(Dir, EC);
+}
+
+std::error_code
+InstrumentingFileSystem::getRealPath(const Twine &Path,
+ SmallVectorImpl<char> &Output) const {
+ ++NumRealPathCalls;
+ return OverlayFileSystem::getRealPath(Path, Output);
+}
+
+void InstrumentingFileSystem::printImpl(raw_ostream &OS, PrintType Type,
+ unsigned IndentLevel) const {
+ printIndent(OS, IndentLevel);
+ OS << "InstrumentingFileSystem\n";
+ if (Type == PrintType::Summary)
+ return;
+
+ if (Type == PrintType::Contents)
+ Type = PrintType::Summary;
+ for (const auto &FS : overlays_range())
+ FS->print(OS, Type, IndentLevel + 1);
+}
+
const char FileSystem::ID = 0;
const char OverlayFileSystem::ID = 0;
const char ProxyFileSystem::ID = 0;
const char InMemoryFileSystem::ID = 0;
const char RedirectingFileSystem::ID = 0;
+const char InstrumentingFileSystem::ID = 0;
diff --git a/llvm/unittests/Support/VirtualFileSystemTest.cpp b/llvm/unittests/Support/VirtualFileSystemTest.cpp
index 695b09343257f1..604d00b70757e0 100644
--- a/llvm/unittests/Support/VirtualFileSystemTest.cpp
+++ b/llvm/unittests/Support/VirtualFileSystemTest.cpp
@@ -3395,3 +3395,40 @@ TEST(RedirectingFileSystemTest, ExternalPaths) {
EXPECT_EQ(CheckFS->SeenPaths, Expected);
}
+
+TEST(InstrumentingFileSystemTest, Instrumentation) {
+ auto InMemoryFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();
+ auto InstrumentingFS =
+ makeIntrusiveRefCnt<vfs::InstrumentingFileSystem>(std::move(InMemoryFS));
+
+ EXPECT_EQ(InstrumentingFS->NumStatusCalls, 0u);
+ EXPECT_EQ(InstrumentingFS->NumOpenCalls, 0u);
+ EXPECT_EQ(InstrumentingFS->NumDirBeginCalls, 0u);
+ EXPECT_EQ(InstrumentingFS->NumRealPathCalls, 0u);
+
+ (void)InstrumentingFS->status("/foo");
+ EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u);
+ EXPECT_EQ(InstrumentingFS->NumOpenCalls, 0u);
+ EXPECT_EQ(InstrumentingFS->NumDirBeginCalls, 0u);
+ EXPECT_EQ(InstrumentingFS->NumRealPathCalls, 0u);
+
+ (void)InstrumentingFS->openFileForRead("/foo");
+ EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u);
+ EXPECT_EQ(InstrumentingFS->NumOpenCalls, 1u);
+ EXPECT_EQ(InstrumentingFS->NumDirBeginCalls, 0u);
+ EXPECT_EQ(InstrumentingFS->NumRealPathCalls, 0u);
+
+ std::error_code EC;
+ (void)InstrumentingFS->dir_begin("/foo", EC);
+ EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u);
+ EXPECT_EQ(InstrumentingFS->NumOpenCalls, 1u);
+ EXPECT_EQ(InstrumentingFS->NumDirBeginCalls, 1u);
+ EXPECT_EQ(InstrumentingFS->NumRealPathCalls, 0u);
+
+ SmallString<128> RealPath;
+ (void)InstrumentingFS->getRealPath("/foo", RealPath);
+ EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u);
+ EXPECT_EQ(InstrumentingFS->NumOpenCalls, 1u);
+ EXPECT_EQ(InstrumentingFS->NumDirBeginCalls, 1u);
+ EXPECT_EQ(InstrumentingFS->NumRealPathCalls, 1u);
+}
More information about the cfe-commits
mailing list