692 lines · cpp
1//===--- FileManager.cpp - File System Probing and Caching ----------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the FileManager interface.10//11//===----------------------------------------------------------------------===//12//13// TODO: This should index all interesting directories with dirent calls.14// getdirentries ?15// opendir/readdir_r/closedir ?16//17//===----------------------------------------------------------------------===//18 19#include "clang/Basic/FileManager.h"20#include "clang/Basic/FileSystemStatCache.h"21#include "llvm/ADT/SmallString.h"22#include "llvm/ADT/Statistic.h"23#include "llvm/Config/llvm-config.h"24#include "llvm/Support/FileSystem.h"25#include "llvm/Support/MemoryBuffer.h"26#include "llvm/Support/Path.h"27#include "llvm/Support/raw_ostream.h"28#include <cassert>29#include <climits>30#include <cstdint>31#include <cstdlib>32#include <optional>33#include <string>34#include <utility>35 36using namespace clang;37 38#define DEBUG_TYPE "file-search"39 40//===----------------------------------------------------------------------===//41// Common logic.42//===----------------------------------------------------------------------===//43 44FileManager::FileManager(const FileSystemOptions &FSO,45 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)46 : FS(std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),47 SeenFileEntries(64), NextFileUID(0) {48 // If the caller doesn't provide a virtual file system, just grab the real49 // file system.50 if (!this->FS)51 this->FS = llvm::vfs::getRealFileSystem();52}53 54FileManager::~FileManager() = default;55 56void FileManager::setStatCache(std::unique_ptr<FileSystemStatCache> statCache) {57 assert(statCache && "No stat cache provided?");58 StatCache = std::move(statCache);59}60 61void FileManager::clearStatCache() { StatCache.reset(); }62 63/// Retrieve the directory that the given file name resides in.64/// Filename can point to either a real file or a virtual file.65static llvm::Expected<DirectoryEntryRef>66getDirectoryFromFile(FileManager &FileMgr, StringRef Filename,67 bool CacheFailure) {68 if (Filename.empty())69 return llvm::errorCodeToError(70 make_error_code(std::errc::no_such_file_or_directory));71 72 if (llvm::sys::path::is_separator(Filename[Filename.size() - 1]))73 return llvm::errorCodeToError(make_error_code(std::errc::is_a_directory));74 75 StringRef DirName = llvm::sys::path::parent_path(Filename);76 // Use the current directory if file has no path component.77 if (DirName.empty())78 DirName = ".";79 80 return FileMgr.getDirectoryRef(DirName, CacheFailure);81}82 83DirectoryEntry *&FileManager::getRealDirEntry(const llvm::vfs::Status &Status) {84 assert(Status.isDirectory() && "The directory should exist!");85 // See if we have already opened a directory with the86 // same inode (this occurs on Unix-like systems when one dir is87 // symlinked to another, for example) or the same path (on88 // Windows).89 DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()];90 91 if (!UDE) {92 // We don't have this directory yet, add it. We use the string93 // key from the SeenDirEntries map as the string.94 UDE = new (DirsAlloc.Allocate()) DirectoryEntry();95 }96 return UDE;97}98 99/// Add all ancestors of the given path (pointing to either a file or100/// a directory) as virtual directories.101void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {102 StringRef DirName = llvm::sys::path::parent_path(Path);103 if (DirName.empty())104 DirName = ".";105 106 auto &NamedDirEnt = *SeenDirEntries.insert(107 {DirName, std::errc::no_such_file_or_directory}).first;108 109 // When caching a virtual directory, we always cache its ancestors110 // at the same time. Therefore, if DirName is already in the cache,111 // we don't need to recurse as its ancestors must also already be in112 // the cache (or it's a known non-virtual directory).113 if (NamedDirEnt.second)114 return;115 116 // Check to see if the directory exists.117 llvm::vfs::Status Status;118 auto statError =119 getStatValue(DirName, Status, false, nullptr /*directory lookup*/);120 if (statError) {121 // There's no real directory at the given path.122 // Add the virtual directory to the cache.123 auto *UDE = new (DirsAlloc.Allocate()) DirectoryEntry();124 NamedDirEnt.second = *UDE;125 VirtualDirectoryEntries.push_back(UDE);126 } else {127 // There is the real directory128 DirectoryEntry *&UDE = getRealDirEntry(Status);129 NamedDirEnt.second = *UDE;130 }131 132 // Recursively add the other ancestors.133 addAncestorsAsVirtualDirs(DirName);134}135 136llvm::Expected<DirectoryEntryRef>137FileManager::getDirectoryRef(StringRef DirName, bool CacheFailure) {138 // stat doesn't like trailing separators except for root directory.139 // At least, on Win32 MSVCRT, stat() cannot strip trailing '/'.140 // (though it can strip '\\')141 if (DirName.size() > 1 &&142 DirName != llvm::sys::path::root_path(DirName) &&143 llvm::sys::path::is_separator(DirName.back()))144 DirName = DirName.drop_back();145 std::optional<std::string> DirNameStr;146 if (is_style_windows(llvm::sys::path::Style::native)) {147 // Fixing a problem with "clang C:test.c" on Windows.148 // Stat("C:") does not recognize "C:" as a valid directory149 if (DirName.size() > 1 && DirName.back() == ':' &&150 DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) {151 DirNameStr = DirName.str() + '.';152 DirName = *DirNameStr;153 }154 }155 156 ++NumDirLookups;157 158 // See if there was already an entry in the map. Note that the map159 // contains both virtual and real directories.160 auto SeenDirInsertResult =161 SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});162 if (!SeenDirInsertResult.second) {163 if (SeenDirInsertResult.first->second)164 return DirectoryEntryRef(*SeenDirInsertResult.first);165 return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError());166 }167 168 // We've not seen this before. Fill it in.169 ++NumDirCacheMisses;170 auto &NamedDirEnt = *SeenDirInsertResult.first;171 assert(!NamedDirEnt.second && "should be newly-created");172 173 // Get the null-terminated directory name as stored as the key of the174 // SeenDirEntries map.175 StringRef InterndDirName = NamedDirEnt.first();176 177 // Check to see if the directory exists.178 llvm::vfs::Status Status;179 auto statError = getStatValue(InterndDirName, Status, false,180 nullptr /*directory lookup*/);181 if (statError) {182 // There's no real directory at the given path.183 if (CacheFailure)184 NamedDirEnt.second = statError;185 else186 SeenDirEntries.erase(DirName);187 return llvm::errorCodeToError(statError);188 }189 190 // It exists.191 DirectoryEntry *&UDE = getRealDirEntry(Status);192 NamedDirEnt.second = *UDE;193 194 return DirectoryEntryRef(NamedDirEnt);195}196 197llvm::Expected<FileEntryRef> FileManager::getFileRef(StringRef Filename,198 bool openFile,199 bool CacheFailure,200 bool IsText) {201 ++NumFileLookups;202 203 // See if there is already an entry in the map.204 auto SeenFileInsertResult =205 SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory});206 if (!SeenFileInsertResult.second) {207 if (!SeenFileInsertResult.first->second)208 return llvm::errorCodeToError(209 SeenFileInsertResult.first->second.getError());210 return FileEntryRef(*SeenFileInsertResult.first);211 }212 213 // We've not seen this before. Fill it in.214 ++NumFileCacheMisses;215 auto *NamedFileEnt = &*SeenFileInsertResult.first;216 assert(!NamedFileEnt->second && "should be newly-created");217 218 // Get the null-terminated file name as stored as the key of the219 // SeenFileEntries map.220 StringRef InterndFileName = NamedFileEnt->first();221 222 // Look up the directory for the file. When looking up something like223 // sys/foo.h we'll discover all of the search directories that have a 'sys'224 // subdirectory. This will let us avoid having to waste time on known-to-fail225 // searches when we go to find sys/bar.h, because all the search directories226 // without a 'sys' subdir will get a cached failure result.227 auto DirInfoOrErr = getDirectoryFromFile(*this, Filename, CacheFailure);228 if (!DirInfoOrErr) { // Directory doesn't exist, file can't exist.229 std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError());230 if (CacheFailure)231 NamedFileEnt->second = Err;232 else233 SeenFileEntries.erase(Filename);234 235 return llvm::errorCodeToError(Err);236 }237 DirectoryEntryRef DirInfo = *DirInfoOrErr;238 239 // FIXME: Use the directory info to prune this, before doing the stat syscall.240 // FIXME: This will reduce the # syscalls.241 242 // Check to see if the file exists.243 std::unique_ptr<llvm::vfs::File> F;244 llvm::vfs::Status Status;245 auto statError = getStatValue(InterndFileName, Status, true,246 openFile ? &F : nullptr, IsText);247 if (statError) {248 // There's no real file at the given path.249 if (CacheFailure)250 NamedFileEnt->second = statError;251 else252 SeenFileEntries.erase(Filename);253 254 return llvm::errorCodeToError(statError);255 }256 257 assert((openFile || !F) && "undesired open file");258 259 // It exists. See if we have already opened a file with the same inode.260 // This occurs when one dir is symlinked to another, for example.261 FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];262 bool ReusingEntry = UFE != nullptr;263 if (!UFE)264 UFE = new (FilesAlloc.Allocate()) FileEntry();265 266 if (!Status.ExposesExternalVFSPath || Status.getName() == Filename) {267 // Use the requested name. Set the FileEntry.268 NamedFileEnt->second = FileEntryRef::MapValue(*UFE, DirInfo);269 } else {270 // Name mismatch. We need a redirect. First grab the actual entry we want271 // to return.272 //273 // This redirection logic intentionally leaks the external name of a274 // redirected file that uses 'use-external-name' in \a275 // vfs::RedirectionFileSystem. This allows clang to report the external276 // name to users (in diagnostics) and to tools that don't have access to277 // the VFS (in debug info and dependency '.d' files).278 //279 // FIXME: This is pretty complex and has some very complicated interactions280 // with the rest of clang. It's also inconsistent with how "real"281 // filesystems behave and confuses parts of clang expect to see the282 // name-as-accessed on the \a FileEntryRef.283 //284 // A potential plan to remove this is as follows -285 // - Update callers such as `HeaderSearch::findUsableModuleForHeader()`286 // to explicitly use the `getNameAsRequested()` rather than just using287 // `getName()`.288 // - Add a `FileManager::getExternalPath` API for explicitly getting the289 // remapped external filename when there is one available. Adopt it in290 // callers like diagnostics/deps reporting instead of calling291 // `getName()` directly.292 // - Switch the meaning of `FileEntryRef::getName()` to get the requested293 // name, not the external name. Once that sticks, revert callers that294 // want the requested name back to calling `getName()`.295 // - Update the VFS to always return the requested name. This could also296 // return the external name, or just have an API to request it297 // lazily. The latter has the benefit of making accesses of the298 // external path easily tracked, but may also require extra work than299 // just returning up front.300 // - (Optionally) Add an API to VFS to get the external filename lazily301 // and update `FileManager::getExternalPath()` to use it instead. This302 // has the benefit of making such accesses easily tracked, though isn't303 // necessarily required (and could cause extra work than just adding to304 // eg. `vfs::Status` up front).305 auto &Redirection =306 *SeenFileEntries307 .insert({Status.getName(), FileEntryRef::MapValue(*UFE, DirInfo)})308 .first;309 assert(isa<FileEntry *>(Redirection.second->V) &&310 "filename redirected to a non-canonical filename?");311 assert(cast<FileEntry *>(Redirection.second->V) == UFE &&312 "filename from getStatValue() refers to wrong file");313 314 // Cache the redirection in the previously-inserted entry, still available315 // in the tentative return value.316 NamedFileEnt->second = FileEntryRef::MapValue(Redirection, DirInfo);317 }318 319 FileEntryRef ReturnedRef(*NamedFileEnt);320 if (ReusingEntry) { // Already have an entry with this inode, return it.321 return ReturnedRef;322 }323 324 // Otherwise, we don't have this file yet, add it.325 UFE->Size = Status.getSize();326 UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());327 UFE->Dir = &DirInfo.getDirEntry();328 UFE->UID = NextFileUID++;329 UFE->UniqueID = Status.getUniqueID();330 UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;331 UFE->IsDeviceFile =332 Status.getType() == llvm::sys::fs::file_type::character_file;333 UFE->File = std::move(F);334 335 if (UFE->File) {336 if (auto PathName = UFE->File->getName())337 fillRealPathName(UFE, *PathName);338 } else if (!openFile) {339 // We should still fill the path even if we aren't opening the file.340 fillRealPathName(UFE, InterndFileName);341 }342 return ReturnedRef;343}344 345llvm::Expected<FileEntryRef> FileManager::getSTDIN() {346 // Only read stdin once.347 if (STDIN)348 return *STDIN;349 350 std::unique_ptr<llvm::MemoryBuffer> Content;351 if (auto ContentOrError = llvm::MemoryBuffer::getSTDIN())352 Content = std::move(*ContentOrError);353 else354 return llvm::errorCodeToError(ContentOrError.getError());355 356 STDIN = getVirtualFileRef(Content->getBufferIdentifier(),357 Content->getBufferSize(), 0);358 FileEntry &FE = const_cast<FileEntry &>(STDIN->getFileEntry());359 FE.Content = std::move(Content);360 FE.IsNamedPipe = true;361 return *STDIN;362}363 364void FileManager::trackVFSUsage(bool Active) {365 FS->visit([Active](llvm::vfs::FileSystem &FileSys) {366 if (auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys))367 RFS->setUsageTrackingActive(Active);368 });369}370 371FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size,372 time_t ModificationTime) {373 ++NumFileLookups;374 375 // See if there is already an entry in the map for an existing file.376 auto &NamedFileEnt = *SeenFileEntries.insert(377 {Filename, std::errc::no_such_file_or_directory}).first;378 if (NamedFileEnt.second) {379 FileEntryRef::MapValue Value = *NamedFileEnt.second;380 if (LLVM_LIKELY(isa<FileEntry *>(Value.V)))381 return FileEntryRef(NamedFileEnt);382 return FileEntryRef(*cast<const FileEntryRef::MapEntry *>(Value.V));383 }384 385 // We've not seen this before, or the file is cached as non-existent.386 ++NumFileCacheMisses;387 addAncestorsAsVirtualDirs(Filename);388 FileEntry *UFE = nullptr;389 390 // Now that all ancestors of Filename are in the cache, the391 // following call is guaranteed to find the DirectoryEntry from the392 // cache. A virtual file can also have an empty filename, that could come393 // from a source location preprocessor directive with an empty filename as394 // an example, so we need to pretend it has a name to ensure a valid directory395 // entry can be returned.396 auto DirInfo = expectedToOptional(getDirectoryFromFile(397 *this, Filename.empty() ? "." : Filename, /*CacheFailure=*/true));398 assert(DirInfo &&399 "The directory of a virtual file should already be in the cache.");400 401 // Check to see if the file exists. If so, drop the virtual file402 llvm::vfs::Status Status;403 const char *InterndFileName = NamedFileEnt.first().data();404 if (!getStatValue(InterndFileName, Status, true, nullptr)) {405 Status = llvm::vfs::Status(406 Status.getName(), Status.getUniqueID(),407 llvm::sys::toTimePoint(ModificationTime),408 Status.getUser(), Status.getGroup(), Size,409 Status.getType(), Status.getPermissions());410 411 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];412 if (RealFE) {413 // If we had already opened this file, close it now so we don't414 // leak the descriptor. We're not going to use the file415 // descriptor anyway, since this is a virtual file.416 if (RealFE->File)417 RealFE->closeFile();418 // If we already have an entry with this inode, return it.419 //420 // FIXME: Surely this should add a reference by the new name, and return421 // it instead...422 NamedFileEnt.second = FileEntryRef::MapValue(*RealFE, *DirInfo);423 return FileEntryRef(NamedFileEnt);424 }425 // File exists, but no entry - create it.426 RealFE = new (FilesAlloc.Allocate()) FileEntry();427 RealFE->UniqueID = Status.getUniqueID();428 RealFE->IsNamedPipe =429 Status.getType() == llvm::sys::fs::file_type::fifo_file;430 fillRealPathName(RealFE, Status.getName());431 432 UFE = RealFE;433 } else {434 // File does not exist, create a virtual entry.435 UFE = new (FilesAlloc.Allocate()) FileEntry();436 VirtualFileEntries.push_back(UFE);437 }438 439 NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo);440 UFE->Size = Size;441 UFE->ModTime = ModificationTime;442 UFE->Dir = &DirInfo->getDirEntry();443 UFE->UID = NextFileUID++;444 UFE->File.reset();445 return FileEntryRef(NamedFileEnt);446}447 448OptionalFileEntryRef FileManager::getBypassFile(FileEntryRef VF) {449 // Stat of the file and return nullptr if it doesn't exist.450 llvm::vfs::Status Status;451 if (getStatValue(VF.getName(), Status, /*isFile=*/true, /*F=*/nullptr))452 return std::nullopt;453 454 if (!SeenBypassFileEntries)455 SeenBypassFileEntries = std::make_unique<456 llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>();457 458 // If we've already bypassed just use the existing one.459 auto Insertion = SeenBypassFileEntries->insert(460 {VF.getName(), std::errc::no_such_file_or_directory});461 if (!Insertion.second)462 return FileEntryRef(*Insertion.first);463 464 // Fill in the new entry from the stat.465 FileEntry *BFE = new (FilesAlloc.Allocate()) FileEntry();466 BypassFileEntries.push_back(BFE);467 Insertion.first->second = FileEntryRef::MapValue(*BFE, VF.getDir());468 BFE->Size = Status.getSize();469 BFE->Dir = VF.getFileEntry().Dir;470 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());471 BFE->UID = NextFileUID++;472 473 // Save the entry in the bypass table and return.474 return FileEntryRef(*Insertion.first);475}476 477bool FileManager::fixupRelativePath(const FileSystemOptions &FileSystemOpts,478 SmallVectorImpl<char> &Path) {479 StringRef pathRef(Path.data(), Path.size());480 481 if (FileSystemOpts.WorkingDir.empty()482 || llvm::sys::path::is_absolute(pathRef))483 return false;484 485 SmallString<128> NewPath(FileSystemOpts.WorkingDir);486 llvm::sys::path::append(NewPath, pathRef);487 Path = NewPath;488 return true;489}490 491bool FileManager::makeAbsolutePath(SmallVectorImpl<char> &Path) const {492 bool Changed = FixupRelativePath(Path);493 494 if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {495 FS->makeAbsolute(Path);496 Changed = true;497 }498 499 return Changed;500}501 502void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) {503 llvm::SmallString<128> AbsPath(FileName);504 // This is not the same as `VFS::getRealPath()`, which resolves symlinks505 // but can be very expensive on real file systems.506 // FIXME: the semantic of RealPathName is unclear, and the name might be507 // misleading. We need to clean up the interface here.508 makeAbsolutePath(AbsPath);509 llvm::sys::path::remove_dots(AbsPath, /*remove_dot_dot=*/true);510 UFE->RealPathName = std::string(AbsPath);511}512 513llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>514FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile,515 bool RequiresNullTerminator,516 std::optional<int64_t> MaybeLimit, bool IsText) {517 const FileEntry *Entry = &FE.getFileEntry();518 // If the content is living on the file entry, return a reference to it.519 if (Entry->Content)520 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());521 522 uint64_t FileSize = Entry->getSize();523 524 if (MaybeLimit)525 FileSize = *MaybeLimit;526 527 // If there's a high enough chance that the file have changed since we528 // got its size, force a stat before opening it.529 if (isVolatile || Entry->isNamedPipe())530 FileSize = -1;531 532 StringRef Filename = FE.getName();533 // If the file is already open, use the open file descriptor.534 if (Entry->File) {535 auto Result = Entry->File->getBuffer(Filename, FileSize,536 RequiresNullTerminator, isVolatile);537 Entry->closeFile();538 return Result;539 }540 541 // Otherwise, open the file.542 return getBufferForFileImpl(Filename, FileSize, isVolatile,543 RequiresNullTerminator, IsText);544}545 546llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>547FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,548 bool isVolatile, bool RequiresNullTerminator,549 bool IsText) const {550 if (FileSystemOpts.WorkingDir.empty())551 return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator,552 isVolatile, IsText);553 554 SmallString<128> FilePath(Filename);555 FixupRelativePath(FilePath);556 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,557 isVolatile, IsText);558}559 560/// getStatValue - Get the 'stat' information for the specified path,561/// using the cache to accelerate it if possible. This returns true562/// if the path points to a virtual file or does not exist, or returns563/// false if it's an existent real file. If FileDescriptor is NULL,564/// do directory look-up instead of file look-up.565std::error_code FileManager::getStatValue(StringRef Path,566 llvm::vfs::Status &Status,567 bool isFile,568 std::unique_ptr<llvm::vfs::File> *F,569 bool IsText) {570 // FIXME: FileSystemOpts shouldn't be passed in here, all paths should be571 // absolute!572 if (FileSystemOpts.WorkingDir.empty())573 return FileSystemStatCache::get(Path, Status, isFile, F, StatCache.get(),574 *FS, IsText);575 576 SmallString<128> FilePath(Path);577 FixupRelativePath(FilePath);578 579 return FileSystemStatCache::get(FilePath.c_str(), Status, isFile, F,580 StatCache.get(), *FS, IsText);581}582 583std::error_code584FileManager::getNoncachedStatValue(StringRef Path,585 llvm::vfs::Status &Result) {586 SmallString<128> FilePath(Path);587 FixupRelativePath(FilePath);588 589 llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str());590 if (!S)591 return S.getError();592 Result = *S;593 return std::error_code();594}595 596void FileManager::GetUniqueIDMapping(597 SmallVectorImpl<OptionalFileEntryRef> &UIDToFiles) const {598 UIDToFiles.clear();599 UIDToFiles.resize(NextFileUID);600 601 for (const auto &Entry : SeenFileEntries) {602 // Only return files that exist and are not redirected.603 if (!Entry.getValue() || !isa<FileEntry *>(Entry.getValue()->V))604 continue;605 FileEntryRef FE(Entry);606 // Add this file if it's the first one with the UID, or if its name is607 // better than the existing one.608 OptionalFileEntryRef &ExistingFE = UIDToFiles[FE.getUID()];609 if (!ExistingFE || FE.getName() < ExistingFE->getName())610 ExistingFE = FE;611 }612}613 614StringRef FileManager::getCanonicalName(DirectoryEntryRef Dir) {615 return getCanonicalName(Dir, Dir.getName());616}617 618StringRef FileManager::getCanonicalName(FileEntryRef File) {619 return getCanonicalName(File, File.getName());620}621 622StringRef FileManager::getCanonicalName(const void *Entry, StringRef Name) {623 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =624 CanonicalNames.find(Entry);625 if (Known != CanonicalNames.end())626 return Known->second;627 628 // Name comes from FileEntry/DirectoryEntry::getName(), so it is safe to629 // store it in the DenseMap below.630 StringRef CanonicalName(Name);631 632 SmallString<256> AbsPathBuf;633 SmallString<256> RealPathBuf;634 if (!FS->getRealPath(Name, RealPathBuf)) {635 if (is_style_windows(llvm::sys::path::Style::native)) {636 // For Windows paths, only use the real path if it doesn't resolve637 // a substitute drive, as those are used to avoid MAX_PATH issues.638 AbsPathBuf = Name;639 if (!FS->makeAbsolute(AbsPathBuf)) {640 if (llvm::sys::path::root_name(RealPathBuf) ==641 llvm::sys::path::root_name(AbsPathBuf)) {642 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);643 } else {644 // Fallback to using the absolute path.645 // Simplifying /../ is semantically valid on Windows even in the646 // presence of symbolic links.647 llvm::sys::path::remove_dots(AbsPathBuf, /*remove_dot_dot=*/true);648 CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);649 }650 }651 } else {652 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);653 }654 }655 656 CanonicalNames.insert({Entry, CanonicalName});657 return CanonicalName;658}659 660void FileManager::AddStats(const FileManager &Other) {661 assert(&Other != this && "Collecting stats into the same FileManager");662 NumDirLookups += Other.NumDirLookups;663 NumFileLookups += Other.NumFileLookups;664 NumDirCacheMisses += Other.NumDirCacheMisses;665 NumFileCacheMisses += Other.NumFileCacheMisses;666}667 668void FileManager::PrintStats() const {669 llvm::errs() << "\n*** File Manager Stats:\n";670 llvm::errs() << UniqueRealFiles.size() << " real files found, "671 << UniqueRealDirs.size() << " real dirs found.\n";672 llvm::errs() << VirtualFileEntries.size() << " virtual files found, "673 << VirtualDirectoryEntries.size() << " virtual dirs found.\n";674 llvm::errs() << NumDirLookups << " dir lookups, "675 << NumDirCacheMisses << " dir cache misses.\n";676 llvm::errs() << NumFileLookups << " file lookups, "677 << NumFileCacheMisses << " file cache misses.\n";678 679 getVirtualFileSystem().visit([](llvm::vfs::FileSystem &VFS) {680 if (auto *T = dyn_cast_or_null<llvm::vfs::TracingFileSystem>(&VFS))681 llvm::errs() << "\n*** Virtual File System Stats:\n"682 << T->NumStatusCalls << " status() calls\n"683 << T->NumOpenFileForReadCalls << " openFileForRead() calls\n"684 << T->NumDirBeginCalls << " dir_begin() calls\n"685 << T->NumGetRealPathCalls << " getRealPath() calls\n"686 << T->NumExistsCalls << " exists() calls\n"687 << T->NumIsLocalCalls << " isLocal() calls\n";688 });689 690 //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups;691}692