Skip to content

Commit 4a906ad

Browse files
committed
[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. (cherry picked from commit 70fcdb3)
1 parent b82f083 commit 4a906ad

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
@@ -1196,6 +1196,60 @@ class YAMLVFSWriter {
11961196
void write(llvm::raw_ostream &OS);
11971197
};
11981198

1199+
/// File system that tracks the number of calls to the underlying file system.
1200+
/// This is particularly useful when wrapped around \c RealFileSystem to add
1201+
/// lightweight tracking of expensive syscalls.
1202+
class TracingFileSystem
1203+
: public llvm::RTTIExtends<TracingFileSystem, ProxyFileSystem> {
1204+
public:
1205+
static const char ID;
1206+
1207+
std::size_t NumStatusCalls = 0;
1208+
std::size_t NumOpenFileForReadCalls = 0;
1209+
std::size_t NumDirBeginCalls = 0;
1210+
std::size_t NumGetRealPathCalls = 0;
1211+
std::size_t NumExistsCalls = 0;
1212+
std::size_t NumIsLocalCalls = 0;
1213+
1214+
TracingFileSystem(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
1215+
: RTTIExtends(std::move(FS)) {}
1216+
1217+
ErrorOr<Status> status(const Twine &Path) override {
1218+
++NumStatusCalls;
1219+
return ProxyFileSystem::status(Path);
1220+
}
1221+
1222+
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override {
1223+
++NumOpenFileForReadCalls;
1224+
return ProxyFileSystem::openFileForRead(Path);
1225+
}
1226+
1227+
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override {
1228+
++NumDirBeginCalls;
1229+
return ProxyFileSystem::dir_begin(Dir, EC);
1230+
}
1231+
1232+
std::error_code getRealPath(const Twine &Path,
1233+
SmallVectorImpl<char> &Output) override {
1234+
++NumGetRealPathCalls;
1235+
return ProxyFileSystem::getRealPath(Path, Output);
1236+
}
1237+
1238+
bool exists(const Twine &Path) override {
1239+
++NumExistsCalls;
1240+
return ProxyFileSystem::exists(Path);
1241+
}
1242+
1243+
std::error_code isLocal(const Twine &Path, bool &Result) override {
1244+
++NumIsLocalCalls;
1245+
return ProxyFileSystem::isLocal(Path, Result);
1246+
}
1247+
1248+
protected:
1249+
void printImpl(raw_ostream &OS, PrintType Type,
1250+
unsigned IndentLevel) const override;
1251+
};
1252+
11991253
} // namespace vfs
12001254
} // namespace llvm
12011255

llvm/lib/Support/VirtualFileSystem.cpp

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

2968+
void TracingFileSystem::printImpl(raw_ostream &OS, PrintType Type,
2969+
unsigned IndentLevel) const {
2970+
printIndent(OS, IndentLevel);
2971+
OS << "TracingFileSystem\n";
2972+
if (Type == PrintType::Summary)
2973+
return;
2974+
2975+
printIndent(OS, IndentLevel);
2976+
OS << "NumStatusCalls=" << NumStatusCalls << "\n";
2977+
printIndent(OS, IndentLevel);
2978+
OS << "NumOpenFileForReadCalls=" << NumOpenFileForReadCalls << "\n";
2979+
printIndent(OS, IndentLevel);
2980+
OS << "NumDirBeginCalls=" << NumDirBeginCalls << "\n";
2981+
printIndent(OS, IndentLevel);
2982+
OS << "NumGetRealPathCalls=" << NumGetRealPathCalls << "\n";
2983+
printIndent(OS, IndentLevel);
2984+
OS << "NumExistsCalls=" << NumExistsCalls << "\n";
2985+
printIndent(OS, IndentLevel);
2986+
OS << "NumIsLocalCalls=" << NumIsLocalCalls << "\n";
2987+
2988+
if (Type == PrintType::Contents)
2989+
Type = PrintType::Summary;
2990+
getUnderlyingFS().print(OS, Type, IndentLevel + 1);
2991+
}
2992+
29682993
const char FileSystem::ID = 0;
29692994
const char OverlayFileSystem::ID = 0;
29702995
const char ProxyFileSystem::ID = 0;
29712996
const char InMemoryFileSystem::ID = 0;
29722997
const char RedirectingFileSystem::ID = 0;
2998+
const char TracingFileSystem::ID = 0;

llvm/unittests/Support/VirtualFileSystemTest.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3566,3 +3566,117 @@ TEST(RedirectingFileSystemTest, ExternalPaths) {
35663566

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

0 commit comments

Comments
 (0)