llvm-project/clang/unittests/Tooling/DependencyScanning/DependencyScanningFilesystemTest.cpp
Jan Svoboda a11a4324bb
[clang][deps] Cache VFS::getRealPath() (#68645)
This PR starts caching calls to
`DependencyScanningWorkerFilesystem::getRealPath()` that we use whenever
we canonicalize module map path. In the case of the real VFS, this
functions performs an expensive syscall that we'd like to do as rarely
as possible.

This PR keeps the real path out of `CachedFileSystemEntry`, since that's
**immutable**; populating the real path on creation of this data
structure (every stat/open) would be expensive.
2024-04-12 10:34:42 -07:00

153 lines
4.5 KiB
C++

//===- DependencyScanningFilesystemTest.cpp -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "gtest/gtest.h"
using namespace clang::tooling::dependencies;
namespace {
struct InstrumentingFilesystem
: llvm::RTTIExtends<InstrumentingFilesystem, llvm::vfs::ProxyFileSystem> {
unsigned NumStatusCalls = 0;
unsigned NumGetRealPathCalls = 0;
using llvm::RTTIExtends<InstrumentingFilesystem,
llvm::vfs::ProxyFileSystem>::RTTIExtends;
llvm::ErrorOr<llvm::vfs::Status> status(const llvm::Twine &Path) override {
++NumStatusCalls;
return ProxyFileSystem::status(Path);
}
std::error_code getRealPath(const llvm::Twine &Path,
llvm::SmallVectorImpl<char> &Output) override {
++NumGetRealPathCalls;
return ProxyFileSystem::getRealPath(Path, Output);
}
};
} // namespace
TEST(DependencyScanningWorkerFilesystem, CacheStatusFailures) {
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
auto InstrumentingFS =
llvm::makeIntrusiveRefCnt<InstrumentingFilesystem>(InMemoryFS);
DependencyScanningFilesystemSharedCache SharedCache;
DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS);
DependencyScanningWorkerFilesystem DepFS2(SharedCache, InstrumentingFS);
DepFS.status("/foo.c");
EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u);
DepFS.status("/foo.c");
EXPECT_EQ(InstrumentingFS->NumStatusCalls, 1u); // Cached, no increase.
DepFS.status("/bar.c");
EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u);
DepFS2.status("/foo.c");
EXPECT_EQ(InstrumentingFS->NumStatusCalls, 2u); // Shared cache.
}
TEST(DependencyScanningFilesystem, CacheGetRealPath) {
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
InMemoryFS->setCurrentWorkingDirectory("/");
InMemoryFS->addFile("/foo", 0, llvm::MemoryBuffer::getMemBuffer(""));
InMemoryFS->addFile("/bar", 0, llvm::MemoryBuffer::getMemBuffer(""));
auto InstrumentingFS =
llvm::makeIntrusiveRefCnt<InstrumentingFilesystem>(InMemoryFS);
DependencyScanningFilesystemSharedCache SharedCache;
DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS);
DependencyScanningWorkerFilesystem DepFS2(SharedCache, InstrumentingFS);
{
llvm::SmallString<128> Result;
DepFS.getRealPath("/foo", Result);
EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 1u);
}
{
llvm::SmallString<128> Result;
DepFS.getRealPath("/foo", Result);
EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 1u); // Cached, no increase.
}
{
llvm::SmallString<128> Result;
DepFS.getRealPath("/bar", Result);
EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 2u);
}
{
llvm::SmallString<128> Result;
DepFS2.getRealPath("/foo", Result);
EXPECT_EQ(InstrumentingFS->NumGetRealPathCalls, 2u); // Shared cache.
}
}
TEST(DependencyScanningFilesystem, RealPathAndStatusInvariants) {
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
InMemoryFS->setCurrentWorkingDirectory("/");
InMemoryFS->addFile("/foo.c", 0, llvm::MemoryBuffer::getMemBuffer(""));
InMemoryFS->addFile("/bar.c", 0, llvm::MemoryBuffer::getMemBuffer(""));
auto InstrumentingFS =
llvm::makeIntrusiveRefCnt<InstrumentingFilesystem>(InMemoryFS);
DependencyScanningFilesystemSharedCache SharedCache;
DependencyScanningWorkerFilesystem DepFS(SharedCache, InstrumentingFS);
// Success.
{
DepFS.status("/foo.c");
llvm::SmallString<128> Result;
DepFS.getRealPath("/foo.c", Result);
}
{
llvm::SmallString<128> Result;
DepFS.getRealPath("/bar.c", Result);
DepFS.status("/bar.c");
}
// Failure.
{
DepFS.status("/foo.m");
llvm::SmallString<128> Result;
DepFS.getRealPath("/foo.m", Result);
}
{
llvm::SmallString<128> Result;
DepFS.getRealPath("/bar.m", Result);
DepFS.status("/bar.m");
}
// Failure without caching.
{
DepFS.status("/foo");
llvm::SmallString<128> Result;
DepFS.getRealPath("/foo", Result);
}
{
llvm::SmallString<128> Result;
DepFS.getRealPath("/bar", Result);
DepFS.status("/bar");
}
}