brintos

brintos / llvm-project-archived public Read only

0
0
Text · 26.6 KiB · e744cc0 Raw
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