Path: blob/main/contrib/llvm-project/clang/lib/Basic/FileManager.cpp
35232 views
//===--- FileManager.cpp - File System Probing and Caching ----------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the FileManager interface.9//10//===----------------------------------------------------------------------===//11//12// TODO: This should index all interesting directories with dirent calls.13// getdirentries ?14// opendir/readdir_r/closedir ?15//16//===----------------------------------------------------------------------===//1718#include "clang/Basic/FileManager.h"19#include "clang/Basic/FileSystemStatCache.h"20#include "llvm/ADT/STLExtras.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 <algorithm>29#include <cassert>30#include <climits>31#include <cstdint>32#include <cstdlib>33#include <optional>34#include <string>35#include <utility>3637using namespace clang;3839#define DEBUG_TYPE "file-search"4041//===----------------------------------------------------------------------===//42// Common logic.43//===----------------------------------------------------------------------===//4445FileManager::FileManager(const FileSystemOptions &FSO,46IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)47: FS(std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),48SeenFileEntries(64), NextFileUID(0) {49// If the caller doesn't provide a virtual file system, just grab the real50// file system.51if (!this->FS)52this->FS = llvm::vfs::getRealFileSystem();53}5455FileManager::~FileManager() = default;5657void FileManager::setStatCache(std::unique_ptr<FileSystemStatCache> statCache) {58assert(statCache && "No stat cache provided?");59StatCache = std::move(statCache);60}6162void FileManager::clearStatCache() { StatCache.reset(); }6364/// Retrieve the directory that the given file name resides in.65/// Filename can point to either a real file or a virtual file.66static llvm::Expected<DirectoryEntryRef>67getDirectoryFromFile(FileManager &FileMgr, StringRef Filename,68bool CacheFailure) {69if (Filename.empty())70return llvm::errorCodeToError(71make_error_code(std::errc::no_such_file_or_directory));7273if (llvm::sys::path::is_separator(Filename[Filename.size() - 1]))74return llvm::errorCodeToError(make_error_code(std::errc::is_a_directory));7576StringRef DirName = llvm::sys::path::parent_path(Filename);77// Use the current directory if file has no path component.78if (DirName.empty())79DirName = ".";8081return FileMgr.getDirectoryRef(DirName, CacheFailure);82}8384DirectoryEntry *&FileManager::getRealDirEntry(const llvm::vfs::Status &Status) {85assert(Status.isDirectory() && "The directory should exist!");86// See if we have already opened a directory with the87// same inode (this occurs on Unix-like systems when one dir is88// symlinked to another, for example) or the same path (on89// Windows).90DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()];9192if (!UDE) {93// We don't have this directory yet, add it. We use the string94// key from the SeenDirEntries map as the string.95UDE = new (DirsAlloc.Allocate()) DirectoryEntry();96}97return UDE;98}99100/// Add all ancestors of the given path (pointing to either a file or101/// a directory) as virtual directories.102void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {103StringRef DirName = llvm::sys::path::parent_path(Path);104if (DirName.empty())105DirName = ".";106107auto &NamedDirEnt = *SeenDirEntries.insert(108{DirName, std::errc::no_such_file_or_directory}).first;109110// When caching a virtual directory, we always cache its ancestors111// at the same time. Therefore, if DirName is already in the cache,112// we don't need to recurse as its ancestors must also already be in113// the cache (or it's a known non-virtual directory).114if (NamedDirEnt.second)115return;116117// Check to see if the directory exists.118llvm::vfs::Status Status;119auto statError =120getStatValue(DirName, Status, false, nullptr /*directory lookup*/);121if (statError) {122// There's no real directory at the given path.123// Add the virtual directory to the cache.124auto *UDE = new (DirsAlloc.Allocate()) DirectoryEntry();125NamedDirEnt.second = *UDE;126VirtualDirectoryEntries.push_back(UDE);127} else {128// There is the real directory129DirectoryEntry *&UDE = getRealDirEntry(Status);130NamedDirEnt.second = *UDE;131}132133// Recursively add the other ancestors.134addAncestorsAsVirtualDirs(DirName);135}136137llvm::Expected<DirectoryEntryRef>138FileManager::getDirectoryRef(StringRef DirName, bool CacheFailure) {139// stat doesn't like trailing separators except for root directory.140// At least, on Win32 MSVCRT, stat() cannot strip trailing '/'.141// (though it can strip '\\')142if (DirName.size() > 1 &&143DirName != llvm::sys::path::root_path(DirName) &&144llvm::sys::path::is_separator(DirName.back()))145DirName = DirName.substr(0, DirName.size()-1);146std::optional<std::string> DirNameStr;147if (is_style_windows(llvm::sys::path::Style::native)) {148// Fixing a problem with "clang C:test.c" on Windows.149// Stat("C:") does not recognize "C:" as a valid directory150if (DirName.size() > 1 && DirName.back() == ':' &&151DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) {152DirNameStr = DirName.str() + '.';153DirName = *DirNameStr;154}155}156157++NumDirLookups;158159// See if there was already an entry in the map. Note that the map160// contains both virtual and real directories.161auto SeenDirInsertResult =162SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});163if (!SeenDirInsertResult.second) {164if (SeenDirInsertResult.first->second)165return DirectoryEntryRef(*SeenDirInsertResult.first);166return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError());167}168169// We've not seen this before. Fill it in.170++NumDirCacheMisses;171auto &NamedDirEnt = *SeenDirInsertResult.first;172assert(!NamedDirEnt.second && "should be newly-created");173174// Get the null-terminated directory name as stored as the key of the175// SeenDirEntries map.176StringRef InterndDirName = NamedDirEnt.first();177178// Check to see if the directory exists.179llvm::vfs::Status Status;180auto statError = getStatValue(InterndDirName, Status, false,181nullptr /*directory lookup*/);182if (statError) {183// There's no real directory at the given path.184if (CacheFailure)185NamedDirEnt.second = statError;186else187SeenDirEntries.erase(DirName);188return llvm::errorCodeToError(statError);189}190191// It exists.192DirectoryEntry *&UDE = getRealDirEntry(Status);193NamedDirEnt.second = *UDE;194195return DirectoryEntryRef(NamedDirEnt);196}197198llvm::ErrorOr<const DirectoryEntry *>199FileManager::getDirectory(StringRef DirName, bool CacheFailure) {200auto Result = getDirectoryRef(DirName, CacheFailure);201if (Result)202return &Result->getDirEntry();203return llvm::errorToErrorCode(Result.takeError());204}205206llvm::ErrorOr<const FileEntry *>207FileManager::getFile(StringRef Filename, bool openFile, bool CacheFailure) {208auto Result = getFileRef(Filename, openFile, CacheFailure);209if (Result)210return &Result->getFileEntry();211return llvm::errorToErrorCode(Result.takeError());212}213214llvm::Expected<FileEntryRef>215FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {216++NumFileLookups;217218// See if there is already an entry in the map.219auto SeenFileInsertResult =220SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory});221if (!SeenFileInsertResult.second) {222if (!SeenFileInsertResult.first->second)223return llvm::errorCodeToError(224SeenFileInsertResult.first->second.getError());225return FileEntryRef(*SeenFileInsertResult.first);226}227228// We've not seen this before. Fill it in.229++NumFileCacheMisses;230auto *NamedFileEnt = &*SeenFileInsertResult.first;231assert(!NamedFileEnt->second && "should be newly-created");232233// Get the null-terminated file name as stored as the key of the234// SeenFileEntries map.235StringRef InterndFileName = NamedFileEnt->first();236237// Look up the directory for the file. When looking up something like238// sys/foo.h we'll discover all of the search directories that have a 'sys'239// subdirectory. This will let us avoid having to waste time on known-to-fail240// searches when we go to find sys/bar.h, because all the search directories241// without a 'sys' subdir will get a cached failure result.242auto DirInfoOrErr = getDirectoryFromFile(*this, Filename, CacheFailure);243if (!DirInfoOrErr) { // Directory doesn't exist, file can't exist.244std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError());245if (CacheFailure)246NamedFileEnt->second = Err;247else248SeenFileEntries.erase(Filename);249250return llvm::errorCodeToError(Err);251}252DirectoryEntryRef DirInfo = *DirInfoOrErr;253254// FIXME: Use the directory info to prune this, before doing the stat syscall.255// FIXME: This will reduce the # syscalls.256257// Check to see if the file exists.258std::unique_ptr<llvm::vfs::File> F;259llvm::vfs::Status Status;260auto statError = getStatValue(InterndFileName, Status, true,261openFile ? &F : nullptr);262if (statError) {263// There's no real file at the given path.264if (CacheFailure)265NamedFileEnt->second = statError;266else267SeenFileEntries.erase(Filename);268269return llvm::errorCodeToError(statError);270}271272assert((openFile || !F) && "undesired open file");273274// It exists. See if we have already opened a file with the same inode.275// This occurs when one dir is symlinked to another, for example.276FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];277bool ReusingEntry = UFE != nullptr;278if (!UFE)279UFE = new (FilesAlloc.Allocate()) FileEntry();280281if (!Status.ExposesExternalVFSPath || Status.getName() == Filename) {282// Use the requested name. Set the FileEntry.283NamedFileEnt->second = FileEntryRef::MapValue(*UFE, DirInfo);284} else {285// Name mismatch. We need a redirect. First grab the actual entry we want286// to return.287//288// This redirection logic intentionally leaks the external name of a289// redirected file that uses 'use-external-name' in \a290// vfs::RedirectionFileSystem. This allows clang to report the external291// name to users (in diagnostics) and to tools that don't have access to292// the VFS (in debug info and dependency '.d' files).293//294// FIXME: This is pretty complex and has some very complicated interactions295// with the rest of clang. It's also inconsistent with how "real"296// filesystems behave and confuses parts of clang expect to see the297// name-as-accessed on the \a FileEntryRef.298//299// A potential plan to remove this is as follows -300// - Update callers such as `HeaderSearch::findUsableModuleForHeader()`301// to explicitly use the `getNameAsRequested()` rather than just using302// `getName()`.303// - Add a `FileManager::getExternalPath` API for explicitly getting the304// remapped external filename when there is one available. Adopt it in305// callers like diagnostics/deps reporting instead of calling306// `getName()` directly.307// - Switch the meaning of `FileEntryRef::getName()` to get the requested308// name, not the external name. Once that sticks, revert callers that309// want the requested name back to calling `getName()`.310// - Update the VFS to always return the requested name. This could also311// return the external name, or just have an API to request it312// lazily. The latter has the benefit of making accesses of the313// external path easily tracked, but may also require extra work than314// just returning up front.315// - (Optionally) Add an API to VFS to get the external filename lazily316// and update `FileManager::getExternalPath()` to use it instead. This317// has the benefit of making such accesses easily tracked, though isn't318// necessarily required (and could cause extra work than just adding to319// eg. `vfs::Status` up front).320auto &Redirection =321*SeenFileEntries322.insert({Status.getName(), FileEntryRef::MapValue(*UFE, DirInfo)})323.first;324assert(Redirection.second->V.is<FileEntry *>() &&325"filename redirected to a non-canonical filename?");326assert(Redirection.second->V.get<FileEntry *>() == UFE &&327"filename from getStatValue() refers to wrong file");328329// Cache the redirection in the previously-inserted entry, still available330// in the tentative return value.331NamedFileEnt->second = FileEntryRef::MapValue(Redirection, DirInfo);332}333334FileEntryRef ReturnedRef(*NamedFileEnt);335if (ReusingEntry) { // Already have an entry with this inode, return it.336return ReturnedRef;337}338339// Otherwise, we don't have this file yet, add it.340UFE->Size = Status.getSize();341UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());342UFE->Dir = &DirInfo.getDirEntry();343UFE->UID = NextFileUID++;344UFE->UniqueID = Status.getUniqueID();345UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;346UFE->File = std::move(F);347348if (UFE->File) {349if (auto PathName = UFE->File->getName())350fillRealPathName(UFE, *PathName);351} else if (!openFile) {352// We should still fill the path even if we aren't opening the file.353fillRealPathName(UFE, InterndFileName);354}355return ReturnedRef;356}357358llvm::Expected<FileEntryRef> FileManager::getSTDIN() {359// Only read stdin once.360if (STDIN)361return *STDIN;362363std::unique_ptr<llvm::MemoryBuffer> Content;364if (auto ContentOrError = llvm::MemoryBuffer::getSTDIN())365Content = std::move(*ContentOrError);366else367return llvm::errorCodeToError(ContentOrError.getError());368369STDIN = getVirtualFileRef(Content->getBufferIdentifier(),370Content->getBufferSize(), 0);371FileEntry &FE = const_cast<FileEntry &>(STDIN->getFileEntry());372FE.Content = std::move(Content);373FE.IsNamedPipe = true;374return *STDIN;375}376377void FileManager::trackVFSUsage(bool Active) {378FS->visit([Active](llvm::vfs::FileSystem &FileSys) {379if (auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys))380RFS->setUsageTrackingActive(Active);381});382}383384const FileEntry *FileManager::getVirtualFile(StringRef Filename, off_t Size,385time_t ModificationTime) {386return &getVirtualFileRef(Filename, Size, ModificationTime).getFileEntry();387}388389FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size,390time_t ModificationTime) {391++NumFileLookups;392393// See if there is already an entry in the map for an existing file.394auto &NamedFileEnt = *SeenFileEntries.insert(395{Filename, std::errc::no_such_file_or_directory}).first;396if (NamedFileEnt.second) {397FileEntryRef::MapValue Value = *NamedFileEnt.second;398if (LLVM_LIKELY(Value.V.is<FileEntry *>()))399return FileEntryRef(NamedFileEnt);400return FileEntryRef(*Value.V.get<const FileEntryRef::MapEntry *>());401}402403// We've not seen this before, or the file is cached as non-existent.404++NumFileCacheMisses;405addAncestorsAsVirtualDirs(Filename);406FileEntry *UFE = nullptr;407408// Now that all ancestors of Filename are in the cache, the409// following call is guaranteed to find the DirectoryEntry from the410// cache. A virtual file can also have an empty filename, that could come411// from a source location preprocessor directive with an empty filename as412// an example, so we need to pretend it has a name to ensure a valid directory413// entry can be returned.414auto DirInfo = expectedToOptional(getDirectoryFromFile(415*this, Filename.empty() ? "." : Filename, /*CacheFailure=*/true));416assert(DirInfo &&417"The directory of a virtual file should already be in the cache.");418419// Check to see if the file exists. If so, drop the virtual file420llvm::vfs::Status Status;421const char *InterndFileName = NamedFileEnt.first().data();422if (!getStatValue(InterndFileName, Status, true, nullptr)) {423Status = llvm::vfs::Status(424Status.getName(), Status.getUniqueID(),425llvm::sys::toTimePoint(ModificationTime),426Status.getUser(), Status.getGroup(), Size,427Status.getType(), Status.getPermissions());428429auto &RealFE = UniqueRealFiles[Status.getUniqueID()];430if (RealFE) {431// If we had already opened this file, close it now so we don't432// leak the descriptor. We're not going to use the file433// descriptor anyway, since this is a virtual file.434if (RealFE->File)435RealFE->closeFile();436// If we already have an entry with this inode, return it.437//438// FIXME: Surely this should add a reference by the new name, and return439// it instead...440NamedFileEnt.second = FileEntryRef::MapValue(*RealFE, *DirInfo);441return FileEntryRef(NamedFileEnt);442}443// File exists, but no entry - create it.444RealFE = new (FilesAlloc.Allocate()) FileEntry();445RealFE->UniqueID = Status.getUniqueID();446RealFE->IsNamedPipe =447Status.getType() == llvm::sys::fs::file_type::fifo_file;448fillRealPathName(RealFE, Status.getName());449450UFE = RealFE;451} else {452// File does not exist, create a virtual entry.453UFE = new (FilesAlloc.Allocate()) FileEntry();454VirtualFileEntries.push_back(UFE);455}456457NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo);458UFE->Size = Size;459UFE->ModTime = ModificationTime;460UFE->Dir = &DirInfo->getDirEntry();461UFE->UID = NextFileUID++;462UFE->File.reset();463return FileEntryRef(NamedFileEnt);464}465466OptionalFileEntryRef FileManager::getBypassFile(FileEntryRef VF) {467// Stat of the file and return nullptr if it doesn't exist.468llvm::vfs::Status Status;469if (getStatValue(VF.getName(), Status, /*isFile=*/true, /*F=*/nullptr))470return std::nullopt;471472if (!SeenBypassFileEntries)473SeenBypassFileEntries = std::make_unique<474llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>();475476// If we've already bypassed just use the existing one.477auto Insertion = SeenBypassFileEntries->insert(478{VF.getName(), std::errc::no_such_file_or_directory});479if (!Insertion.second)480return FileEntryRef(*Insertion.first);481482// Fill in the new entry from the stat.483FileEntry *BFE = new (FilesAlloc.Allocate()) FileEntry();484BypassFileEntries.push_back(BFE);485Insertion.first->second = FileEntryRef::MapValue(*BFE, VF.getDir());486BFE->Size = Status.getSize();487BFE->Dir = VF.getFileEntry().Dir;488BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());489BFE->UID = NextFileUID++;490491// Save the entry in the bypass table and return.492return FileEntryRef(*Insertion.first);493}494495bool FileManager::FixupRelativePath(SmallVectorImpl<char> &path) const {496StringRef pathRef(path.data(), path.size());497498if (FileSystemOpts.WorkingDir.empty()499|| llvm::sys::path::is_absolute(pathRef))500return false;501502SmallString<128> NewPath(FileSystemOpts.WorkingDir);503llvm::sys::path::append(NewPath, pathRef);504path = NewPath;505return true;506}507508bool FileManager::makeAbsolutePath(SmallVectorImpl<char> &Path) const {509bool Changed = FixupRelativePath(Path);510511if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {512FS->makeAbsolute(Path);513Changed = true;514}515516return Changed;517}518519void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) {520llvm::SmallString<128> AbsPath(FileName);521// This is not the same as `VFS::getRealPath()`, which resolves symlinks522// but can be very expensive on real file systems.523// FIXME: the semantic of RealPathName is unclear, and the name might be524// misleading. We need to clean up the interface here.525makeAbsolutePath(AbsPath);526llvm::sys::path::remove_dots(AbsPath, /*remove_dot_dot=*/true);527UFE->RealPathName = std::string(AbsPath);528}529530llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>531FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile,532bool RequiresNullTerminator,533std::optional<int64_t> MaybeLimit) {534const FileEntry *Entry = &FE.getFileEntry();535// If the content is living on the file entry, return a reference to it.536if (Entry->Content)537return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());538539uint64_t FileSize = Entry->getSize();540541if (MaybeLimit)542FileSize = *MaybeLimit;543544// If there's a high enough chance that the file have changed since we545// got its size, force a stat before opening it.546if (isVolatile || Entry->isNamedPipe())547FileSize = -1;548549StringRef Filename = FE.getName();550// If the file is already open, use the open file descriptor.551if (Entry->File) {552auto Result = Entry->File->getBuffer(Filename, FileSize,553RequiresNullTerminator, isVolatile);554Entry->closeFile();555return Result;556}557558// Otherwise, open the file.559return getBufferForFileImpl(Filename, FileSize, isVolatile,560RequiresNullTerminator);561}562563llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>564FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,565bool isVolatile,566bool RequiresNullTerminator) const {567if (FileSystemOpts.WorkingDir.empty())568return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator,569isVolatile);570571SmallString<128> FilePath(Filename);572FixupRelativePath(FilePath);573return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,574isVolatile);575}576577/// getStatValue - Get the 'stat' information for the specified path,578/// using the cache to accelerate it if possible. This returns true579/// if the path points to a virtual file or does not exist, or returns580/// false if it's an existent real file. If FileDescriptor is NULL,581/// do directory look-up instead of file look-up.582std::error_code583FileManager::getStatValue(StringRef Path, llvm::vfs::Status &Status,584bool isFile, std::unique_ptr<llvm::vfs::File> *F) {585// FIXME: FileSystemOpts shouldn't be passed in here, all paths should be586// absolute!587if (FileSystemOpts.WorkingDir.empty())588return FileSystemStatCache::get(Path, Status, isFile, F,589StatCache.get(), *FS);590591SmallString<128> FilePath(Path);592FixupRelativePath(FilePath);593594return FileSystemStatCache::get(FilePath.c_str(), Status, isFile, F,595StatCache.get(), *FS);596}597598std::error_code599FileManager::getNoncachedStatValue(StringRef Path,600llvm::vfs::Status &Result) {601SmallString<128> FilePath(Path);602FixupRelativePath(FilePath);603604llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str());605if (!S)606return S.getError();607Result = *S;608return std::error_code();609}610611void FileManager::GetUniqueIDMapping(612SmallVectorImpl<OptionalFileEntryRef> &UIDToFiles) const {613UIDToFiles.clear();614UIDToFiles.resize(NextFileUID);615616for (const auto &Entry : SeenFileEntries) {617// Only return files that exist and are not redirected.618if (!Entry.getValue() || !Entry.getValue()->V.is<FileEntry *>())619continue;620FileEntryRef FE(Entry);621// Add this file if it's the first one with the UID, or if its name is622// better than the existing one.623OptionalFileEntryRef &ExistingFE = UIDToFiles[FE.getUID()];624if (!ExistingFE || FE.getName() < ExistingFE->getName())625ExistingFE = FE;626}627}628629StringRef FileManager::getCanonicalName(DirectoryEntryRef Dir) {630return getCanonicalName(Dir, Dir.getName());631}632633StringRef FileManager::getCanonicalName(FileEntryRef File) {634return getCanonicalName(File, File.getName());635}636637StringRef FileManager::getCanonicalName(const void *Entry, StringRef Name) {638llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =639CanonicalNames.find(Entry);640if (Known != CanonicalNames.end())641return Known->second;642643// Name comes from FileEntry/DirectoryEntry::getName(), so it is safe to644// store it in the DenseMap below.645StringRef CanonicalName(Name);646647SmallString<256> AbsPathBuf;648SmallString<256> RealPathBuf;649if (!FS->getRealPath(Name, RealPathBuf)) {650if (is_style_windows(llvm::sys::path::Style::native)) {651// For Windows paths, only use the real path if it doesn't resolve652// a substitute drive, as those are used to avoid MAX_PATH issues.653AbsPathBuf = Name;654if (!FS->makeAbsolute(AbsPathBuf)) {655if (llvm::sys::path::root_name(RealPathBuf) ==656llvm::sys::path::root_name(AbsPathBuf)) {657CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);658} else {659// Fallback to using the absolute path.660// Simplifying /../ is semantically valid on Windows even in the661// presence of symbolic links.662llvm::sys::path::remove_dots(AbsPathBuf, /*remove_dot_dot=*/true);663CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);664}665}666} else {667CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);668}669}670671CanonicalNames.insert({Entry, CanonicalName});672return CanonicalName;673}674675void FileManager::AddStats(const FileManager &Other) {676assert(&Other != this && "Collecting stats into the same FileManager");677NumDirLookups += Other.NumDirLookups;678NumFileLookups += Other.NumFileLookups;679NumDirCacheMisses += Other.NumDirCacheMisses;680NumFileCacheMisses += Other.NumFileCacheMisses;681}682683void FileManager::PrintStats() const {684llvm::errs() << "\n*** File Manager Stats:\n";685llvm::errs() << UniqueRealFiles.size() << " real files found, "686<< UniqueRealDirs.size() << " real dirs found.\n";687llvm::errs() << VirtualFileEntries.size() << " virtual files found, "688<< VirtualDirectoryEntries.size() << " virtual dirs found.\n";689llvm::errs() << NumDirLookups << " dir lookups, "690<< NumDirCacheMisses << " dir cache misses.\n";691llvm::errs() << NumFileLookups << " file lookups, "692<< NumFileCacheMisses << " file cache misses.\n";693694//llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups;695}696697698