Skip to content

Commit 540321e

Browse files
committed
[llvm][clang] Trace VFS calls
1 parent e72c949 commit 540321e

File tree

18 files changed

+241
-13
lines changed

18 files changed

+241
-13
lines changed

clang/include/clang/Driver/Compilation.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ class Compilation {
115115
/// -ftime-trace result files.
116116
ArgStringMap TimeTraceFiles;
117117

118+
/// -fvfs-trace result files.
119+
ArgStringMap VFSTraceFiles;
120+
118121
/// Optional redirection for stdin, stdout, stderr.
119122
std::vector<std::optional<StringRef>> Redirects;
120123

@@ -280,6 +283,14 @@ class Compilation {
280283
TimeTraceFiles[JA] = Name;
281284
}
282285

286+
const char *getVFSTraceFile(const JobAction *JA) const {
287+
return VFSTraceFiles.lookup(JA);
288+
}
289+
void addVFSTraceFile(const char *Name, const JobAction *JA) {
290+
assert(!VFSTraceFiles.contains(JA));
291+
VFSTraceFiles[JA] = Name;
292+
}
293+
283294
/// CleanupFile - Delete a given file.
284295
///
285296
/// \param IssueErrors - Report failures as errors.

clang/include/clang/Driver/Options.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3904,6 +3904,13 @@ def ftime_trace_EQ : Joined<["-"], "ftime-trace=">, Group<f_Group>,
39043904
HelpText<"Similar to -ftime-trace. Specify the JSON file or a directory which will contain the JSON file">,
39053905
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
39063906
MarshallingInfoString<FrontendOpts<"TimeTracePath">>;
3907+
def fvfs_trace : Flag<["-"], "fvfs-trace">, Group<f_Group>,
3908+
HelpText<"Turn of virtual file system profiler. Generates text file based on output filename.">,
3909+
Visibility<[ClangOption, CLOption, DXCOption]>;
3910+
def fvfs_trace_EQ : Joined<["-"], "fvfs-trace=">, Group<f_Group>,
3911+
HelpText<"Similar to -fvfs-trace. Specify the text file or a directory that will contain the text file">,
3912+
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
3913+
MarshallingInfoString<FrontendOpts<"VFSTracePath">>;
39073914
def fproc_stat_report : Joined<["-"], "fproc-stat-report">, Group<f_Group>,
39083915
HelpText<"Print subprocess statistics">;
39093916
def fproc_stat_report_EQ : Joined<["-"], "fproc-stat-report=">, Group<f_Group>,

clang/include/clang/Frontend/CompilerInstance.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ namespace llvm {
3535
class raw_fd_ostream;
3636
class Timer;
3737
class TimerGroup;
38+
namespace vfs {
39+
struct InstrumentingFileSystem;
40+
}
3841
}
3942

4043
namespace clang {
@@ -89,6 +92,11 @@ class CompilerInstance : public ModuleLoader {
8992
/// Auxiliary Target info.
9093
IntrusiveRefCntPtr<TargetInfo> AuxTarget;
9194

95+
public:
96+
/// The instrumenting file system.
97+
IntrusiveRefCntPtr<llvm::vfs::InstrumentingFileSystem> IVFS;
98+
private:
99+
92100
/// The file manager.
93101
IntrusiveRefCntPtr<FileManager> FileMgr;
94102

clang/include/clang/Frontend/CompilerInvocation.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class ArgList;
3939
namespace vfs {
4040

4141
class FileSystem;
42+
struct InstrumentingFileSystem;
4243

4344
} // namespace vfs
4445

@@ -390,13 +391,14 @@ class CowCompilerInvocation : public CompilerInvocationBase {
390391
/// @}
391392
};
392393

393-
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
394-
createVFSFromCompilerInvocation(const CompilerInvocation &CI,
395-
DiagnosticsEngine &Diags);
394+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> createVFSFromCompilerInvocation(
395+
const CompilerInvocation &CI, DiagnosticsEngine &Diags,
396+
IntrusiveRefCntPtr<llvm::vfs::InstrumentingFileSystem> *TracingFS = {});
396397

397398
IntrusiveRefCntPtr<llvm::vfs::FileSystem> createVFSFromCompilerInvocation(
398399
const CompilerInvocation &CI, DiagnosticsEngine &Diags,
399-
IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS);
400+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
401+
IntrusiveRefCntPtr<llvm::vfs::InstrumentingFileSystem> *TracingFS = {});
400402

401403
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
402404
createVFSFromOverlayFiles(ArrayRef<std::string> VFSOverlayFiles,

clang/include/clang/Frontend/FrontendOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,9 @@ class FrontendOptions {
568568
/// Path which stores the output files for -ftime-trace
569569
std::string TimeTracePath;
570570

571+
/// Path which stores the output files for -fvfs-trace
572+
std::string VFSTracePath;
573+
571574
public:
572575
FrontendOptions()
573576
: DisableFree(false), RelocatablePCH(false), ShowHelp(false),

clang/lib/Driver/Driver.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5429,6 +5429,36 @@ static void handleTimeTrace(Compilation &C, const ArgList &Args,
54295429
C.addResultFile(ResultFile, JA);
54305430
}
54315431

5432+
static void handleVFSTrace(Compilation &C, const ArgList &Args,
5433+
const JobAction *JA, const char *BaseInput,
5434+
const InputInfo &Result) {
5435+
Arg *A = Args.getLastArg(options::OPT_fvfs_trace, options::OPT_fvfs_trace_EQ);
5436+
if (!A)
5437+
return;
5438+
SmallString<128> Path;
5439+
if (A->getOption().matches(options::OPT_fvfs_trace_EQ)) {
5440+
Path = A->getValue();
5441+
if (llvm::sys::fs::is_directory(Path)) {
5442+
SmallString<128> Tmp(Result.getFilename());
5443+
llvm::sys::path::replace_extension(Tmp, "vfs.txt");
5444+
llvm::sys::path::append(Path, llvm::sys::path::filename(Tmp));
5445+
}
5446+
} else {
5447+
if (Arg *DumpDir = Args.getLastArgNoClaim(options::OPT_dumpdir)) {
5448+
// The trace file is ${dumpdir}${basename}.vfs.txt. Note that dumpdir may
5449+
// not end with a path separator.
5450+
Path = DumpDir->getValue();
5451+
Path += llvm::sys::path::filename(BaseInput);
5452+
} else {
5453+
Path = Result.getFilename();
5454+
}
5455+
llvm::sys::path::replace_extension(Path, "vfs.txt");
5456+
}
5457+
const char *ResultFile = C.getArgs().MakeArgString(Path);
5458+
C.addVFSTraceFile(ResultFile, JA);
5459+
C.addResultFile(ResultFile, JA);
5460+
}
5461+
54325462
InputInfoList Driver::BuildJobsForActionNoCache(
54335463
Compilation &C, const Action *A, const ToolChain *TC, StringRef BoundArch,
54345464
bool AtTopLevel, bool MultipleArchs, const char *LinkingOutput,
@@ -5678,8 +5708,10 @@ InputInfoList Driver::BuildJobsForActionNoCache(
56785708
AtTopLevel, MultipleArchs,
56795709
OffloadingPrefix),
56805710
BaseInput);
5681-
if (T->canEmitIR() && OffloadingPrefix.empty())
5711+
if (T->canEmitIR() && OffloadingPrefix.empty()) {
56825712
handleTimeTrace(C, Args, JA, BaseInput, Result);
5713+
handleVFSTrace(C, Args, JA, BaseInput, Result);
5714+
}
56835715
}
56845716

56855717
if (CCCPrintBindings && !CCGenDiagnostics) {

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6759,6 +6759,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
67596759
Args.AddLastArg(CmdArgs, options::OPT_ftime_trace_granularity_EQ);
67606760
}
67616761

6762+
if (const char *Name = C.getVFSTraceFile(&JA))
6763+
CmdArgs.push_back(Args.MakeArgString("-fvfs-trace=" + Twine(Name)));
6764+
67626765
if (Arg *A = Args.getLastArg(options::OPT_ftrapv_handler_EQ)) {
67636766
CmdArgs.push_back("-ftrapv-handler");
67646767
CmdArgs.push_back(A->getValue());

clang/lib/Frontend/CompilerInstance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ FileManager *CompilerInstance::createFileManager(
379379
if (!VFS)
380380
VFS = FileMgr ? &FileMgr->getVirtualFileSystem()
381381
: createVFSFromCompilerInvocation(getInvocation(),
382-
getDiagnostics());
382+
getDiagnostics(), &IVFS);
383383
assert(VFS && "FileManager has no VFS?");
384384
FileMgr = new FileManager(getFileSystemOpts(), std::move(VFS));
385385
return FileMgr.get();

clang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4935,16 +4935,25 @@ void CompilerInvocation::clearImplicitModuleBuildOptions() {
49354935
}
49364936

49374937
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
4938-
clang::createVFSFromCompilerInvocation(const CompilerInvocation &CI,
4939-
DiagnosticsEngine &Diags) {
4940-
return createVFSFromCompilerInvocation(CI, Diags,
4941-
llvm::vfs::getRealFileSystem());
4938+
clang::createVFSFromCompilerInvocation(
4939+
const CompilerInvocation &CI, DiagnosticsEngine &Diags,
4940+
IntrusiveRefCntPtr<llvm::vfs::InstrumentingFileSystem> *TracingFS) {
4941+
return createVFSFromCompilerInvocation(
4942+
CI, Diags, llvm::vfs::getRealFileSystem(), TracingFS);
49424943
}
49434944

49444945
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
49454946
clang::createVFSFromCompilerInvocation(
49464947
const CompilerInvocation &CI, DiagnosticsEngine &Diags,
4947-
IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) {
4948+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
4949+
IntrusiveRefCntPtr<llvm::vfs::InstrumentingFileSystem> *TracingFS) {
4950+
if (!CI.getFrontendOpts().VFSTracePath.empty()) {
4951+
auto TFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InstrumentingFileSystem>(
4952+
std::move(BaseFS));
4953+
if (TracingFS)
4954+
*TracingFS = TFS;
4955+
BaseFS = std::move(TFS);
4956+
}
49484957
return createVFSFromOverlayFiles(CI.getHeaderSearchOpts().VFSOverlayFiles,
49494958
Diags, std::move(BaseFS));
49504959
}

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ class DependencyScanningAction : public tooling::ToolAction {
338338
ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
339339
ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
340340
ScanInstance.getFrontendOpts().ModulesShareFileManager = false;
341+
ScanInstance.getFrontendOpts().VFSTracePath.clear();
341342
ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
342343
ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage =
343344
any(OptimizeArgs & ScanningOptimizations::VFS);

clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ void ModuleDepCollector::addOutputPaths(CowCompilerInvocation &CI,
152152
CI.getMutDependencyOutputOpts().Targets.push_back(std::string(Target));
153153
}
154154
}
155+
if (!CI.getMutFrontendOpts().VFSTracePath.empty()) {
156+
SmallString<128> VFSTracePath{CI.getMutFrontendOpts().OutputFile};
157+
llvm::sys::path::replace_extension(VFSTracePath, "vfs.txt");
158+
CI.getMutFrontendOpts().VFSTracePath = VFSTracePath.str();
159+
}
155160
}
156161

157162
static CowCompilerInvocation
@@ -186,6 +191,8 @@ makeCommonInvocationForModuleBuild(CompilerInvocation CI) {
186191
CI.getDiagnosticOpts().DiagnosticSerializationFile = "-";
187192
if (!CI.getDependencyOutputOpts().OutputFile.empty())
188193
CI.getDependencyOutputOpts().OutputFile = "-";
194+
if (!CI.getFrontendOpts().VFSTracePath.empty())
195+
CI.getFrontendOpts().VFSTracePath = "-";
189196
CI.getDependencyOutputOpts().Targets.clear();
190197

191198
CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;

clang/test/ClangScanDeps/modules-inferred.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
[{
2222
"directory": "DIR",
2323
"file": "DIR/tu.m",
24-
"command": "clang -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -F DIR/frameworks -c DIR/tu.m -o DIR/tu.o"
24+
"command": "clang -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -F DIR/frameworks -c DIR/tu.m -o DIR/tu.o -fvfs-trace"
2525
}]
2626

2727
// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ static std::vector<std::string> ModuleDepTargets;
8484
static bool DeprecatedDriverCommand;
8585
static ResourceDirRecipeKind ResourceDirRecipe;
8686
static bool Verbose;
87+
static bool PrintVFSTrace;
8788
static bool PrintTiming;
8889
static std::vector<const char *> CommandLine;
8990

@@ -219,6 +220,8 @@ static void ParseArgs(int argc, char **argv) {
219220
ResourceDirRecipe = *Kind;
220221
}
221222

223+
PrintVFSTrace = Args.hasArg(OPT_vfs_trace);
224+
222225
PrintTiming = Args.hasArg(OPT_print_timing);
223226

224227
Verbose = Args.hasArg(OPT_verbose);
@@ -886,8 +889,16 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
886889
if (Format == ScanningOutputFormat::Full)
887890
FD.emplace(ModuleName.empty() ? Inputs.size() : 0);
888891

892+
std::atomic<std::size_t> NumStatusCalls = 0;
893+
std::atomic<std::size_t> NumOpenCalls = 0;
894+
std::atomic<std::size_t> NumDirBeginCalls = 0;
895+
std::atomic<std::size_t> NumRealPathCalls = 0;
896+
889897
auto ScanningTask = [&](DependencyScanningService &Service) {
890-
DependencyScanningTool WorkerTool(Service);
898+
auto TracingFS =
899+
llvm::makeIntrusiveRefCnt<llvm::vfs::InstrumentingFileSystem>(
900+
llvm::vfs::createPhysicalFileSystem());
901+
DependencyScanningTool WorkerTool(Service, TracingFS);
891902

892903
llvm::DenseSet<ModuleID> AlreadySeenModules;
893904
while (auto MaybeInputIndex = GetNextInputIndex()) {
@@ -970,6 +981,11 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
970981
HadErrors = true;
971982
}
972983
}
984+
985+
NumStatusCalls += TracingFS->NumStatusCalls;
986+
NumOpenCalls += TracingFS->NumOpenCalls;
987+
NumDirBeginCalls += TracingFS->NumDirBeginCalls;
988+
NumRealPathCalls += TracingFS->NumRealPathCalls;
973989
};
974990

975991
DependencyScanningService Service(ScanMode, Format, OptimizeArgs,
@@ -1001,6 +1017,13 @@ int clang_scan_deps_main(int argc, char **argv, const llvm::ToolContext &) {
10011017
"clang-scan-deps timing: %0.2fs wall, %0.2fs process\n",
10021018
T.getTotalTime().getWallTime(), T.getTotalTime().getProcessTime());
10031019

1020+
if (PrintVFSTrace)
1021+
llvm::errs() << llvm::format(
1022+
"clang-scan-deps VFS trace: %d status, %d openFileForRead, %d "
1023+
"dir_begin, %d getRealPath\n",
1024+
NumStatusCalls.load(), NumOpenCalls.load(), NumDirBeginCalls.load(),
1025+
NumRealPathCalls.load());
1026+
10041027
if (RoundTripArgs)
10051028
if (FD && FD->roundTripCommands(llvm::errs()))
10061029
HadErrors = true;

clang/tools/clang-scan-deps/Opts.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def deprecated_driver_command : F<"deprecated-driver-command", "use a single dri
3131

3232
defm resource_dir_recipe : Eq<"resource-dir-recipe", "How to produce missing '-resource-dir' argument">;
3333

34+
def vfs_trace: F<"fvfs-trace", "Profile virtual file system calls">;
3435
def print_timing : F<"print-timing", "Print timing information">;
3536

3637
def verbose : F<"v", "Use verbose output">;

clang/tools/driver/cc1_main.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,19 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
261261
}
262262
}
263263

264+
if (!Clang->getFrontendOpts().VFSTracePath.empty()) {
265+
assert(Clang->IVFS);
266+
if (auto VFSOutput = Clang->createOutputFile(
267+
Clang->getFrontendOpts().VFSTracePath, /*Binary=*/false,
268+
/*RemoveFileOnSignal=*/false,
269+
/*useTemporary=*/false)) {
270+
*VFSOutput << "status\t" << Clang->IVFS->NumStatusCalls << "\n"
271+
<< "openFileForRead\t" << Clang->IVFS->NumOpenCalls << "\n"
272+
<< "dir_begin\t" << Clang->IVFS->NumDirBeginCalls << "\n"
273+
<< "getRealPath\t" << Clang->IVFS->NumRealPathCalls << "\n";
274+
}
275+
}
276+
264277
// Our error handler depends on the Diagnostics object, which we're
265278
// potentially about to delete. Uninstall the handler now so that any
266279
// later errors use the default handling behavior instead.

llvm/include/llvm/Support/VirtualFileSystem.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/Support/FileSystem.h"
2727
#include "llvm/Support/Path.h"
2828
#include "llvm/Support/SourceMgr.h"
29+
#include <atomic>
2930
#include <cassert>
3031
#include <cstdint>
3132
#include <ctime>
@@ -1125,6 +1126,34 @@ class YAMLVFSWriter {
11251126
void write(llvm::raw_ostream &OS);
11261127
};
11271128

1129+
/// File system that tracks the number of calls to the underlying file system.
1130+
/// This is particularly useful when wrapped around \c RealFileSystem to add
1131+
/// lightweight tracking of expensive syscalls.
1132+
struct InstrumentingFileSystem
1133+
: llvm::RTTIExtends<InstrumentingFileSystem, OverlayFileSystem> {
1134+
static const char ID;
1135+
1136+
std::atomic<std::size_t> NumStatusCalls = 0;
1137+
std::atomic<std::size_t> NumOpenCalls = 0;
1138+
std::atomic<std::size_t> NumDirBeginCalls = 0;
1139+
mutable std::atomic<std::size_t> NumRealPathCalls = 0;
1140+
1141+
InstrumentingFileSystem(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS);
1142+
1143+
ErrorOr<Status> status(const Twine &Path) override;
1144+
1145+
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
1146+
1147+
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
1148+
1149+
std::error_code getRealPath(const Twine &Path,
1150+
SmallVectorImpl<char> &Output) const override;
1151+
1152+
protected:
1153+
void printImpl(raw_ostream &OS, PrintType Type,
1154+
unsigned IndentLevel) const override;
1155+
};
1156+
11281157
} // namespace vfs
11291158
} // namespace llvm
11301159

0 commit comments

Comments
 (0)