Skip to content

Commit 70fcdb3

Browse files
authored
[llvm][support] Implement tracing virtual file system (llvm#88326)
LLVM-based tools often use the `llvm::vfs::FileSystem` instrastructure to access the file system. This patch adds new kind of a VFS that performs lightweight tracing of file system operations on an underlying VFS. This is supposed to aid in investigating file system traffic without resorting to instrumentation on the operating system level. There will be follow-up patches that integrate this into Clang and its dependency scanner.
1 parent 7815abe commit 70fcdb3

File tree

4 files changed

+197
-31
lines changed

4 files changed

+197
-31
lines changed

clang/unittests/Tooling/DependencyScanning/DependencyScanningFilesystemTest.cpp

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,11 @@
1313

1414
using namespace clang::tooling::dependencies;
1515

16-
namespace {
17-
struct InstrumentingFilesystem
18-
: llvm::RTTIExtends<InstrumentingFilesystem, llvm::vfs::ProxyFileSystem> {
19-
unsigned NumStatusCalls = 0;
20-
unsigned NumGetRealPathCalls = 0;
21-
unsigned NumExistsCalls = 0;
22-
23-
using llvm::RTTIExtends<InstrumentingFilesystem,
24-
llvm::vfs::ProxyFileSystem>::RTTIExtends;
25-
26-
llvm::ErrorOr<llvm::vfs::Status> status(const llvm::Twine &Path) override {
27-
++NumStatusCalls;
28-
return ProxyFileSystem::status(Path);
29-
}
30-
31-
std::error_code getRealPath(const llvm::Twine &Path,
32-
llvm::SmallVectorImpl<char> &Output) override {
33-
++NumGetRealPathCalls;
34-
return ProxyFileSystem::getRealPath(Path, Output);
35-
}
36-
37-
bool exists(const llvm::Twine &Path) override {
38-
++NumExistsCalls;
39-
return ProxyFileSystem::exists(Path);
40-
}
41-
};
42-
} // namespace
43-
4416
TEST(DependencyScanningWorkerFilesystem, CacheStatusFailures) {
4517
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
4618

4719
auto InstrumentingFS =
48-
llvm::makeIntrusiveRefCnt<InstrumentingFilesystem>(InMemoryFS);
20+
llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(InMemoryFS);
4921

5022
DependencyScanningFilesystemSharedCache SharedCache;
5123
DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS);
@@ -71,7 +43,7 @@ TEST(DependencyScanningFilesystem, CacheGetRealPath) {
7143
InMemoryFS->addFile("/bar", 0, llvm::MemoryBuffer::getMemBuffer(""));
7244

7345
auto InstrumentingFS =
74-
llvm::makeIntrusiveRefCnt<InstrumentingFilesystem>(InMemoryFS);
46+
llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(InMemoryFS);
7547

7648
DependencyScanningFilesystemSharedCache SharedCache;
7749
DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS);
@@ -157,7 +129,7 @@ TEST(DependencyScanningFilesystem, RealPathAndStatusInvariants) {
157129
TEST(DependencyScanningFilesystem, CacheStatOnExists) {
158130
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
159131
auto InstrumentingFS =
160-
llvm::makeIntrusiveRefCnt<InstrumentingFilesystem>(InMemoryFS);
132+
llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(InMemoryFS);
161133
InMemoryFS->setCurrentWorkingDirectory("/");
162134
InMemoryFS->addFile("/foo", 0, llvm::MemoryBuffer::getMemBuffer(""));
163135
InMemoryFS->addFile("/bar", 0, llvm::MemoryBuffer::getMemBuffer(""));

llvm/include/llvm/Support/VirtualFileSystem.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,60 @@ class YAMLVFSWriter {
11321132
void write(llvm::raw_ostream &OS);
11331133
};
11341134

1135+
/// File system that tracks the number of calls to the underlying file system.
1136+
/// This is particularly useful when wrapped around \c RealFileSystem to add
1137+
/// lightweight tracking of expensive syscalls.
1138+
class TracingFileSystem
1139+
: public llvm::RTTIExtends<TracingFileSystem, ProxyFileSystem> {
1140+
public:
1141+
static const char ID;
1142+
1143+
std::size_t NumStatusCalls = 0;
1144+
std::size_t NumOpenFileForReadCalls = 0;
1145+
std::size_t NumDirBeginCalls = 0;
1146+
std::size_t NumGetRealPathCalls = 0;
1147+
std::size_t NumExistsCalls = 0;
1148+
std::size_t NumIsLocalCalls = 0;
1149+
1150+
TracingFileSystem(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
1151+
: RTTIExtends(std::move(FS)) {}
1152+
1153+
ErrorOr<Status> status(const Twine &Path) override {
1154+
++NumStatusCalls;
1155+
return ProxyFileSystem::status(Path);
1156+
}
1157+
1158+
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override {
1159+
++NumOpenFileForReadCalls;
1160+
return ProxyFileSystem::openFileForRead(Path);
1161+
}
1162+
1163+
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override {
1164+
++NumDirBeginCalls;
1165+
return ProxyFileSystem::dir_begin(Dir, EC);
1166+
}
1167+
1168+
std::error_code getRealPath(const Twine &Path,
1169+
SmallVectorImpl<char> &Output) override {
1170+
++NumGetRealPathCalls;
1171+
return ProxyFileSystem::getRealPath(Path, Output);
1172+
}
1173+
1174+
bool exists(const Twine &Path) override {
1175+
++NumExistsCalls;
1176+
return ProxyFileSystem::exists(Path);
1177+
}
1178+
1179+
std::error_code isLocal(const Twine &Path, bool &Result) override {
1180+
++NumIsLocalCalls;
1181+
return ProxyFileSystem::isLocal(Path, Result);
1182+
}
1183+
1184+
protected:
1185+
void printImpl(raw_ostream &OS, PrintType Type,
1186+
unsigned IndentLevel) const override;
1187+
};
1188+
11351189
} // namespace vfs
11361190
} // namespace llvm
11371191

llvm/lib/Support/VirtualFileSystem.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2945,8 +2945,34 @@ recursive_directory_iterator::increment(std::error_code &EC) {
29452945
return *this;
29462946
}
29472947

2948+
void TracingFileSystem::printImpl(raw_ostream &OS, PrintType Type,
2949+
unsigned IndentLevel) const {
2950+
printIndent(OS, IndentLevel);
2951+
OS << "TracingFileSystem\n";
2952+
if (Type == PrintType::Summary)
2953+
return;
2954+
2955+
printIndent(OS, IndentLevel);
2956+
OS << "NumStatusCalls=" << NumStatusCalls << "\n";
2957+
printIndent(OS, IndentLevel);
2958+
OS << "NumOpenFileForReadCalls=" << NumOpenFileForReadCalls << "\n";
2959+
printIndent(OS, IndentLevel);
2960+
OS << "NumDirBeginCalls=" << NumDirBeginCalls << "\n";
2961+
printIndent(OS, IndentLevel);
2962+
OS << "NumGetRealPathCalls=" << NumGetRealPathCalls << "\n";
2963+
printIndent(OS, IndentLevel);
2964+
OS << "NumExistsCalls=" << NumExistsCalls << "\n";
2965+
printIndent(OS, IndentLevel);
2966+
OS << "NumIsLocalCalls=" << NumIsLocalCalls << "\n";
2967+
2968+
if (Type == PrintType::Contents)
2969+
Type = PrintType::Summary;
2970+
getUnderlyingFS().print(OS, Type, IndentLevel + 1);
2971+
}
2972+
29482973
const char FileSystem::ID = 0;
29492974
const char OverlayFileSystem::ID = 0;
29502975
const char ProxyFileSystem::ID = 0;
29512976
const char InMemoryFileSystem::ID = 0;
29522977
const char RedirectingFileSystem::ID = 0;
2978+
const char TracingFileSystem::ID = 0;

llvm/unittests/Support/VirtualFileSystemTest.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3565,3 +3565,117 @@ TEST(RedirectingFileSystemTest, ExistsRedirectOnly) {
35653565
EXPECT_FALSE(Redirecting->exists("/b"));
35663566
EXPECT_TRUE(Redirecting->exists("/vfile"));
35673567
}
3568+
3569+
TEST(TracingFileSystemTest, TracingWorks) {
3570+
auto InMemoryFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();
3571+
auto TracingFS =
3572+
makeIntrusiveRefCnt<vfs::TracingFileSystem>(std::move(InMemoryFS));
3573+
3574+
EXPECT_EQ(TracingFS->NumStatusCalls, 0u);
3575+
EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 0u);
3576+
EXPECT_EQ(TracingFS->NumDirBeginCalls, 0u);
3577+
EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u);
3578+
EXPECT_EQ(TracingFS->NumExistsCalls, 0u);
3579+
EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);
3580+
3581+
(void)TracingFS->status("/foo");
3582+
EXPECT_EQ(TracingFS->NumStatusCalls, 1u);
3583+
EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 0u);
3584+
EXPECT_EQ(TracingFS->NumDirBeginCalls, 0u);
3585+
EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u);
3586+
EXPECT_EQ(TracingFS->NumExistsCalls, 0u);
3587+
EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);
3588+
3589+
(void)TracingFS->openFileForRead("/foo");
3590+
EXPECT_EQ(TracingFS->NumStatusCalls, 1u);
3591+
EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u);
3592+
EXPECT_EQ(TracingFS->NumDirBeginCalls, 0u);
3593+
EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u);
3594+
EXPECT_EQ(TracingFS->NumExistsCalls, 0u);
3595+
EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);
3596+
3597+
std::error_code EC;
3598+
(void)TracingFS->dir_begin("/foo", EC);
3599+
EXPECT_EQ(TracingFS->NumStatusCalls, 1u);
3600+
EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u);
3601+
EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u);
3602+
EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u);
3603+
EXPECT_EQ(TracingFS->NumExistsCalls, 0u);
3604+
EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);
3605+
3606+
SmallString<128> RealPath;
3607+
(void)TracingFS->getRealPath("/foo", RealPath);
3608+
EXPECT_EQ(TracingFS->NumStatusCalls, 1u);
3609+
EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u);
3610+
EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u);
3611+
EXPECT_EQ(TracingFS->NumGetRealPathCalls, 1u);
3612+
EXPECT_EQ(TracingFS->NumExistsCalls, 0u);
3613+
EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);
3614+
3615+
(void)TracingFS->exists("/foo");
3616+
EXPECT_EQ(TracingFS->NumStatusCalls, 1u);
3617+
EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u);
3618+
EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u);
3619+
EXPECT_EQ(TracingFS->NumGetRealPathCalls, 1u);
3620+
EXPECT_EQ(TracingFS->NumExistsCalls, 1u);
3621+
EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);
3622+
3623+
bool IsLocal;
3624+
(void)TracingFS->isLocal("/foo", IsLocal);
3625+
EXPECT_EQ(TracingFS->NumStatusCalls, 1u);
3626+
EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u);
3627+
EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u);
3628+
EXPECT_EQ(TracingFS->NumGetRealPathCalls, 1u);
3629+
EXPECT_EQ(TracingFS->NumExistsCalls, 1u);
3630+
EXPECT_EQ(TracingFS->NumIsLocalCalls, 1u);
3631+
}
3632+
3633+
TEST(TracingFileSystemTest, PrintOutput) {
3634+
auto InMemoryFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();
3635+
auto TracingFS =
3636+
makeIntrusiveRefCnt<vfs::TracingFileSystem>(std::move(InMemoryFS));
3637+
3638+
(void)TracingFS->status("/foo");
3639+
3640+
(void)TracingFS->openFileForRead("/foo");
3641+
(void)TracingFS->openFileForRead("/foo");
3642+
3643+
std::error_code EC;
3644+
(void)TracingFS->dir_begin("/foo", EC);
3645+
(void)TracingFS->dir_begin("/foo", EC);
3646+
(void)TracingFS->dir_begin("/foo", EC);
3647+
3648+
llvm::SmallString<128> RealPath;
3649+
(void)TracingFS->getRealPath("/foo", RealPath);
3650+
(void)TracingFS->getRealPath("/foo", RealPath);
3651+
(void)TracingFS->getRealPath("/foo", RealPath);
3652+
(void)TracingFS->getRealPath("/foo", RealPath);
3653+
3654+
(void)TracingFS->exists("/foo");
3655+
(void)TracingFS->exists("/foo");
3656+
(void)TracingFS->exists("/foo");
3657+
(void)TracingFS->exists("/foo");
3658+
(void)TracingFS->exists("/foo");
3659+
3660+
bool IsLocal;
3661+
(void)TracingFS->isLocal("/foo", IsLocal);
3662+
(void)TracingFS->isLocal("/foo", IsLocal);
3663+
(void)TracingFS->isLocal("/foo", IsLocal);
3664+
(void)TracingFS->isLocal("/foo", IsLocal);
3665+
(void)TracingFS->isLocal("/foo", IsLocal);
3666+
(void)TracingFS->isLocal("/foo", IsLocal);
3667+
3668+
std::string Output;
3669+
llvm::raw_string_ostream OS(Output);
3670+
TracingFS->print(OS);
3671+
3672+
ASSERT_EQ("TracingFileSystem\n"
3673+
"NumStatusCalls=1\n"
3674+
"NumOpenFileForReadCalls=2\n"
3675+
"NumDirBeginCalls=3\n"
3676+
"NumGetRealPathCalls=4\n"
3677+
"NumExistsCalls=5\n"
3678+
"NumIsLocalCalls=6\n"
3679+
" InMemoryFileSystem\n",
3680+
Output);
3681+
}

0 commit comments

Comments
 (0)