10b57cec5SDimitry Andric //===--- FileManager.cpp - File System Probing and Caching ----------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file implements the FileManager interface. 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric // 130b57cec5SDimitry Andric // TODO: This should index all interesting directories with dirent calls. 140b57cec5SDimitry Andric // getdirentries ? 150b57cec5SDimitry Andric // opendir/readdir_r/closedir ? 160b57cec5SDimitry Andric // 170b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 180b57cec5SDimitry Andric 190b57cec5SDimitry Andric #include "clang/Basic/FileManager.h" 200b57cec5SDimitry Andric #include "clang/Basic/FileSystemStatCache.h" 210b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 22a7dea167SDimitry Andric #include "llvm/ADT/SmallString.h" 23a7dea167SDimitry Andric #include "llvm/ADT/Statistic.h" 24a7dea167SDimitry Andric #include "llvm/Config/llvm-config.h" 250b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 260b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 270b57cec5SDimitry Andric #include "llvm/Support/Path.h" 280b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 290b57cec5SDimitry Andric #include <algorithm> 300b57cec5SDimitry Andric #include <cassert> 310b57cec5SDimitry Andric #include <climits> 320b57cec5SDimitry Andric #include <cstdint> 330b57cec5SDimitry Andric #include <cstdlib> 34bdd1243dSDimitry Andric #include <optional> 350b57cec5SDimitry Andric #include <string> 360b57cec5SDimitry Andric #include <utility> 370b57cec5SDimitry Andric 380b57cec5SDimitry Andric using namespace clang; 390b57cec5SDimitry Andric 40a7dea167SDimitry Andric #define DEBUG_TYPE "file-search" 41a7dea167SDimitry Andric 420b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 430b57cec5SDimitry Andric // Common logic. 440b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric FileManager::FileManager(const FileSystemOptions &FSO, 470b57cec5SDimitry Andric IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) 480b57cec5SDimitry Andric : FS(std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64), 490b57cec5SDimitry Andric SeenFileEntries(64), NextFileUID(0) { 500b57cec5SDimitry Andric // If the caller doesn't provide a virtual file system, just grab the real 510b57cec5SDimitry Andric // file system. 520b57cec5SDimitry Andric if (!this->FS) 530b57cec5SDimitry Andric this->FS = llvm::vfs::getRealFileSystem(); 540b57cec5SDimitry Andric } 550b57cec5SDimitry Andric 560b57cec5SDimitry Andric FileManager::~FileManager() = default; 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric void FileManager::setStatCache(std::unique_ptr<FileSystemStatCache> statCache) { 590b57cec5SDimitry Andric assert(statCache && "No stat cache provided?"); 600b57cec5SDimitry Andric StatCache = std::move(statCache); 610b57cec5SDimitry Andric } 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric void FileManager::clearStatCache() { StatCache.reset(); } 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric /// Retrieve the directory that the given file name resides in. 660b57cec5SDimitry Andric /// Filename can point to either a real file or a virtual file. 67e8d8bef9SDimitry Andric static llvm::Expected<DirectoryEntryRef> 68a7dea167SDimitry Andric getDirectoryFromFile(FileManager &FileMgr, StringRef Filename, 690b57cec5SDimitry Andric bool CacheFailure) { 700b57cec5SDimitry Andric if (Filename.empty()) 71e8d8bef9SDimitry Andric return llvm::errorCodeToError( 72e8d8bef9SDimitry Andric make_error_code(std::errc::no_such_file_or_directory)); 730b57cec5SDimitry Andric 740b57cec5SDimitry Andric if (llvm::sys::path::is_separator(Filename[Filename.size() - 1])) 75e8d8bef9SDimitry Andric return llvm::errorCodeToError(make_error_code(std::errc::is_a_directory)); 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric StringRef DirName = llvm::sys::path::parent_path(Filename); 780b57cec5SDimitry Andric // Use the current directory if file has no path component. 790b57cec5SDimitry Andric if (DirName.empty()) 800b57cec5SDimitry Andric DirName = "."; 810b57cec5SDimitry Andric 82e8d8bef9SDimitry Andric return FileMgr.getDirectoryRef(DirName, CacheFailure); 830b57cec5SDimitry Andric } 840b57cec5SDimitry Andric 85*0fca6ea1SDimitry Andric DirectoryEntry *&FileManager::getRealDirEntry(const llvm::vfs::Status &Status) { 86*0fca6ea1SDimitry Andric assert(Status.isDirectory() && "The directory should exist!"); 87*0fca6ea1SDimitry Andric // See if we have already opened a directory with the 88*0fca6ea1SDimitry Andric // same inode (this occurs on Unix-like systems when one dir is 89*0fca6ea1SDimitry Andric // symlinked to another, for example) or the same path (on 90*0fca6ea1SDimitry Andric // Windows). 91*0fca6ea1SDimitry Andric DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()]; 92*0fca6ea1SDimitry Andric 93*0fca6ea1SDimitry Andric if (!UDE) { 94*0fca6ea1SDimitry Andric // We don't have this directory yet, add it. We use the string 95*0fca6ea1SDimitry Andric // key from the SeenDirEntries map as the string. 96*0fca6ea1SDimitry Andric UDE = new (DirsAlloc.Allocate()) DirectoryEntry(); 97*0fca6ea1SDimitry Andric } 98*0fca6ea1SDimitry Andric return UDE; 99*0fca6ea1SDimitry Andric } 100*0fca6ea1SDimitry Andric 1010b57cec5SDimitry Andric /// Add all ancestors of the given path (pointing to either a file or 1020b57cec5SDimitry Andric /// a directory) as virtual directories. 1030b57cec5SDimitry Andric void FileManager::addAncestorsAsVirtualDirs(StringRef Path) { 1040b57cec5SDimitry Andric StringRef DirName = llvm::sys::path::parent_path(Path); 1050b57cec5SDimitry Andric if (DirName.empty()) 1060b57cec5SDimitry Andric DirName = "."; 1070b57cec5SDimitry Andric 108a7dea167SDimitry Andric auto &NamedDirEnt = *SeenDirEntries.insert( 109a7dea167SDimitry Andric {DirName, std::errc::no_such_file_or_directory}).first; 1100b57cec5SDimitry Andric 1110b57cec5SDimitry Andric // When caching a virtual directory, we always cache its ancestors 1120b57cec5SDimitry Andric // at the same time. Therefore, if DirName is already in the cache, 1130b57cec5SDimitry Andric // we don't need to recurse as its ancestors must also already be in 1140b57cec5SDimitry Andric // the cache (or it's a known non-virtual directory). 1150b57cec5SDimitry Andric if (NamedDirEnt.second) 1160b57cec5SDimitry Andric return; 1170b57cec5SDimitry Andric 118*0fca6ea1SDimitry Andric // Check to see if the directory exists. 119*0fca6ea1SDimitry Andric llvm::vfs::Status Status; 120*0fca6ea1SDimitry Andric auto statError = 121*0fca6ea1SDimitry Andric getStatValue(DirName, Status, false, nullptr /*directory lookup*/); 122*0fca6ea1SDimitry Andric if (statError) { 123*0fca6ea1SDimitry Andric // There's no real directory at the given path. 1240b57cec5SDimitry Andric // Add the virtual directory to the cache. 12581ad6265SDimitry Andric auto *UDE = new (DirsAlloc.Allocate()) DirectoryEntry(); 12681ad6265SDimitry Andric NamedDirEnt.second = *UDE; 12781ad6265SDimitry Andric VirtualDirectoryEntries.push_back(UDE); 128*0fca6ea1SDimitry Andric } else { 129*0fca6ea1SDimitry Andric // There is the real directory 130*0fca6ea1SDimitry Andric DirectoryEntry *&UDE = getRealDirEntry(Status); 131*0fca6ea1SDimitry Andric NamedDirEnt.second = *UDE; 132*0fca6ea1SDimitry Andric } 1330b57cec5SDimitry Andric 1340b57cec5SDimitry Andric // Recursively add the other ancestors. 1350b57cec5SDimitry Andric addAncestorsAsVirtualDirs(DirName); 1360b57cec5SDimitry Andric } 1370b57cec5SDimitry Andric 138a7dea167SDimitry Andric llvm::Expected<DirectoryEntryRef> 139a7dea167SDimitry Andric FileManager::getDirectoryRef(StringRef DirName, bool CacheFailure) { 1400b57cec5SDimitry Andric // stat doesn't like trailing separators except for root directory. 1410b57cec5SDimitry Andric // At least, on Win32 MSVCRT, stat() cannot strip trailing '/'. 1420b57cec5SDimitry Andric // (though it can strip '\\') 1430b57cec5SDimitry Andric if (DirName.size() > 1 && 1440b57cec5SDimitry Andric DirName != llvm::sys::path::root_path(DirName) && 1450b57cec5SDimitry Andric llvm::sys::path::is_separator(DirName.back())) 1460b57cec5SDimitry Andric DirName = DirName.substr(0, DirName.size()-1); 147bdd1243dSDimitry Andric std::optional<std::string> DirNameStr; 148349cc55cSDimitry Andric if (is_style_windows(llvm::sys::path::Style::native)) { 1490b57cec5SDimitry Andric // Fixing a problem with "clang C:test.c" on Windows. 1500b57cec5SDimitry Andric // Stat("C:") does not recognize "C:" as a valid directory 1510b57cec5SDimitry Andric if (DirName.size() > 1 && DirName.back() == ':' && 152fe6060f1SDimitry Andric DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) { 1530b57cec5SDimitry Andric DirNameStr = DirName.str() + '.'; 154349cc55cSDimitry Andric DirName = *DirNameStr; 1550b57cec5SDimitry Andric } 156349cc55cSDimitry Andric } 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric ++NumDirLookups; 1590b57cec5SDimitry Andric 1600b57cec5SDimitry Andric // See if there was already an entry in the map. Note that the map 1610b57cec5SDimitry Andric // contains both virtual and real directories. 162a7dea167SDimitry Andric auto SeenDirInsertResult = 163a7dea167SDimitry Andric SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory}); 164a7dea167SDimitry Andric if (!SeenDirInsertResult.second) { 165a7dea167SDimitry Andric if (SeenDirInsertResult.first->second) 166e8d8bef9SDimitry Andric return DirectoryEntryRef(*SeenDirInsertResult.first); 167a7dea167SDimitry Andric return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError()); 168a7dea167SDimitry Andric } 1690b57cec5SDimitry Andric 1700b57cec5SDimitry Andric // We've not seen this before. Fill it in. 1710b57cec5SDimitry Andric ++NumDirCacheMisses; 1720b57cec5SDimitry Andric auto &NamedDirEnt = *SeenDirInsertResult.first; 1730b57cec5SDimitry Andric assert(!NamedDirEnt.second && "should be newly-created"); 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric // Get the null-terminated directory name as stored as the key of the 1760b57cec5SDimitry Andric // SeenDirEntries map. 1770b57cec5SDimitry Andric StringRef InterndDirName = NamedDirEnt.first(); 1780b57cec5SDimitry Andric 1790b57cec5SDimitry Andric // Check to see if the directory exists. 1800b57cec5SDimitry Andric llvm::vfs::Status Status; 181a7dea167SDimitry Andric auto statError = getStatValue(InterndDirName, Status, false, 182a7dea167SDimitry Andric nullptr /*directory lookup*/); 183a7dea167SDimitry Andric if (statError) { 1840b57cec5SDimitry Andric // There's no real directory at the given path. 185a7dea167SDimitry Andric if (CacheFailure) 186a7dea167SDimitry Andric NamedDirEnt.second = statError; 187a7dea167SDimitry Andric else 1880b57cec5SDimitry Andric SeenDirEntries.erase(DirName); 189a7dea167SDimitry Andric return llvm::errorCodeToError(statError); 1900b57cec5SDimitry Andric } 1910b57cec5SDimitry Andric 192*0fca6ea1SDimitry Andric // It exists. 193*0fca6ea1SDimitry Andric DirectoryEntry *&UDE = getRealDirEntry(Status); 19481ad6265SDimitry Andric NamedDirEnt.second = *UDE; 1950b57cec5SDimitry Andric 196e8d8bef9SDimitry Andric return DirectoryEntryRef(NamedDirEnt); 1970b57cec5SDimitry Andric } 1980b57cec5SDimitry Andric 199a7dea167SDimitry Andric llvm::ErrorOr<const DirectoryEntry *> 200a7dea167SDimitry Andric FileManager::getDirectory(StringRef DirName, bool CacheFailure) { 201a7dea167SDimitry Andric auto Result = getDirectoryRef(DirName, CacheFailure); 202a7dea167SDimitry Andric if (Result) 203a7dea167SDimitry Andric return &Result->getDirEntry(); 204a7dea167SDimitry Andric return llvm::errorToErrorCode(Result.takeError()); 205a7dea167SDimitry Andric } 206a7dea167SDimitry Andric 207a7dea167SDimitry Andric llvm::ErrorOr<const FileEntry *> 208a7dea167SDimitry Andric FileManager::getFile(StringRef Filename, bool openFile, bool CacheFailure) { 209a7dea167SDimitry Andric auto Result = getFileRef(Filename, openFile, CacheFailure); 210a7dea167SDimitry Andric if (Result) 211a7dea167SDimitry Andric return &Result->getFileEntry(); 212a7dea167SDimitry Andric return llvm::errorToErrorCode(Result.takeError()); 213a7dea167SDimitry Andric } 214a7dea167SDimitry Andric 215a7dea167SDimitry Andric llvm::Expected<FileEntryRef> 216a7dea167SDimitry Andric FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) { 2170b57cec5SDimitry Andric ++NumFileLookups; 2180b57cec5SDimitry Andric 2190b57cec5SDimitry Andric // See if there is already an entry in the map. 220a7dea167SDimitry Andric auto SeenFileInsertResult = 221a7dea167SDimitry Andric SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory}); 222a7dea167SDimitry Andric if (!SeenFileInsertResult.second) { 223a7dea167SDimitry Andric if (!SeenFileInsertResult.first->second) 224a7dea167SDimitry Andric return llvm::errorCodeToError( 225a7dea167SDimitry Andric SeenFileInsertResult.first->second.getError()); 226e8d8bef9SDimitry Andric return FileEntryRef(*SeenFileInsertResult.first); 227a7dea167SDimitry Andric } 2280b57cec5SDimitry Andric 2290b57cec5SDimitry Andric // We've not seen this before. Fill it in. 2300b57cec5SDimitry Andric ++NumFileCacheMisses; 231480093f4SDimitry Andric auto *NamedFileEnt = &*SeenFileInsertResult.first; 232480093f4SDimitry Andric assert(!NamedFileEnt->second && "should be newly-created"); 2330b57cec5SDimitry Andric 2340b57cec5SDimitry Andric // Get the null-terminated file name as stored as the key of the 2350b57cec5SDimitry Andric // SeenFileEntries map. 236480093f4SDimitry Andric StringRef InterndFileName = NamedFileEnt->first(); 2370b57cec5SDimitry Andric 2380b57cec5SDimitry Andric // Look up the directory for the file. When looking up something like 2390b57cec5SDimitry Andric // sys/foo.h we'll discover all of the search directories that have a 'sys' 2400b57cec5SDimitry Andric // subdirectory. This will let us avoid having to waste time on known-to-fail 2410b57cec5SDimitry Andric // searches when we go to find sys/bar.h, because all the search directories 2420b57cec5SDimitry Andric // without a 'sys' subdir will get a cached failure result. 243a7dea167SDimitry Andric auto DirInfoOrErr = getDirectoryFromFile(*this, Filename, CacheFailure); 244a7dea167SDimitry Andric if (!DirInfoOrErr) { // Directory doesn't exist, file can't exist. 245e8d8bef9SDimitry Andric std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError()); 246a7dea167SDimitry Andric if (CacheFailure) 247e8d8bef9SDimitry Andric NamedFileEnt->second = Err; 248a7dea167SDimitry Andric else 2490b57cec5SDimitry Andric SeenFileEntries.erase(Filename); 2500b57cec5SDimitry Andric 251e8d8bef9SDimitry Andric return llvm::errorCodeToError(Err); 2520b57cec5SDimitry Andric } 253e8d8bef9SDimitry Andric DirectoryEntryRef DirInfo = *DirInfoOrErr; 2540b57cec5SDimitry Andric 2550b57cec5SDimitry Andric // FIXME: Use the directory info to prune this, before doing the stat syscall. 2560b57cec5SDimitry Andric // FIXME: This will reduce the # syscalls. 2570b57cec5SDimitry Andric 2580b57cec5SDimitry Andric // Check to see if the file exists. 2590b57cec5SDimitry Andric std::unique_ptr<llvm::vfs::File> F; 2600b57cec5SDimitry Andric llvm::vfs::Status Status; 261a7dea167SDimitry Andric auto statError = getStatValue(InterndFileName, Status, true, 262a7dea167SDimitry Andric openFile ? &F : nullptr); 263a7dea167SDimitry Andric if (statError) { 2640b57cec5SDimitry Andric // There's no real file at the given path. 265a7dea167SDimitry Andric if (CacheFailure) 266480093f4SDimitry Andric NamedFileEnt->second = statError; 267a7dea167SDimitry Andric else 2680b57cec5SDimitry Andric SeenFileEntries.erase(Filename); 2690b57cec5SDimitry Andric 270a7dea167SDimitry Andric return llvm::errorCodeToError(statError); 2710b57cec5SDimitry Andric } 2720b57cec5SDimitry Andric 2730b57cec5SDimitry Andric assert((openFile || !F) && "undesired open file"); 2740b57cec5SDimitry Andric 2750b57cec5SDimitry Andric // It exists. See if we have already opened a file with the same inode. 2760b57cec5SDimitry Andric // This occurs when one dir is symlinked to another, for example. 27781ad6265SDimitry Andric FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()]; 27881ad6265SDimitry Andric bool ReusingEntry = UFE != nullptr; 27981ad6265SDimitry Andric if (!UFE) 28081ad6265SDimitry Andric UFE = new (FilesAlloc.Allocate()) FileEntry(); 2810b57cec5SDimitry Andric 282bdd1243dSDimitry Andric if (!Status.ExposesExternalVFSPath || Status.getName() == Filename) { 283bdd1243dSDimitry Andric // Use the requested name. Set the FileEntry. 28481ad6265SDimitry Andric NamedFileEnt->second = FileEntryRef::MapValue(*UFE, DirInfo); 285e8d8bef9SDimitry Andric } else { 286e8d8bef9SDimitry Andric // Name mismatch. We need a redirect. First grab the actual entry we want 287e8d8bef9SDimitry Andric // to return. 288349cc55cSDimitry Andric // 289349cc55cSDimitry Andric // This redirection logic intentionally leaks the external name of a 290349cc55cSDimitry Andric // redirected file that uses 'use-external-name' in \a 291349cc55cSDimitry Andric // vfs::RedirectionFileSystem. This allows clang to report the external 292349cc55cSDimitry Andric // name to users (in diagnostics) and to tools that don't have access to 293349cc55cSDimitry Andric // the VFS (in debug info and dependency '.d' files). 294349cc55cSDimitry Andric // 29581ad6265SDimitry Andric // FIXME: This is pretty complex and has some very complicated interactions 29681ad6265SDimitry Andric // with the rest of clang. It's also inconsistent with how "real" 29781ad6265SDimitry Andric // filesystems behave and confuses parts of clang expect to see the 29881ad6265SDimitry Andric // name-as-accessed on the \a FileEntryRef. 29981ad6265SDimitry Andric // 30081ad6265SDimitry Andric // A potential plan to remove this is as follows - 30181ad6265SDimitry Andric // - Update callers such as `HeaderSearch::findUsableModuleForHeader()` 302bdd1243dSDimitry Andric // to explicitly use the `getNameAsRequested()` rather than just using 30381ad6265SDimitry Andric // `getName()`. 30481ad6265SDimitry Andric // - Add a `FileManager::getExternalPath` API for explicitly getting the 30581ad6265SDimitry Andric // remapped external filename when there is one available. Adopt it in 30681ad6265SDimitry Andric // callers like diagnostics/deps reporting instead of calling 30781ad6265SDimitry Andric // `getName()` directly. 30881ad6265SDimitry Andric // - Switch the meaning of `FileEntryRef::getName()` to get the requested 30981ad6265SDimitry Andric // name, not the external name. Once that sticks, revert callers that 31081ad6265SDimitry Andric // want the requested name back to calling `getName()`. 31181ad6265SDimitry Andric // - Update the VFS to always return the requested name. This could also 31281ad6265SDimitry Andric // return the external name, or just have an API to request it 31381ad6265SDimitry Andric // lazily. The latter has the benefit of making accesses of the 31481ad6265SDimitry Andric // external path easily tracked, but may also require extra work than 31581ad6265SDimitry Andric // just returning up front. 31681ad6265SDimitry Andric // - (Optionally) Add an API to VFS to get the external filename lazily 31781ad6265SDimitry Andric // and update `FileManager::getExternalPath()` to use it instead. This 31881ad6265SDimitry Andric // has the benefit of making such accesses easily tracked, though isn't 31981ad6265SDimitry Andric // necessarily required (and could cause extra work than just adding to 32081ad6265SDimitry Andric // eg. `vfs::Status` up front). 321e8d8bef9SDimitry Andric auto &Redirection = 322e8d8bef9SDimitry Andric *SeenFileEntries 32381ad6265SDimitry Andric .insert({Status.getName(), FileEntryRef::MapValue(*UFE, DirInfo)}) 324e8d8bef9SDimitry Andric .first; 325e8d8bef9SDimitry Andric assert(Redirection.second->V.is<FileEntry *>() && 326e8d8bef9SDimitry Andric "filename redirected to a non-canonical filename?"); 32781ad6265SDimitry Andric assert(Redirection.second->V.get<FileEntry *>() == UFE && 3280b57cec5SDimitry Andric "filename from getStatValue() refers to wrong file"); 329e8d8bef9SDimitry Andric 330e8d8bef9SDimitry Andric // Cache the redirection in the previously-inserted entry, still available 331e8d8bef9SDimitry Andric // in the tentative return value. 33206c3fb27SDimitry Andric NamedFileEnt->second = FileEntryRef::MapValue(Redirection, DirInfo); 3330b57cec5SDimitry Andric } 3340b57cec5SDimitry Andric 335e8d8bef9SDimitry Andric FileEntryRef ReturnedRef(*NamedFileEnt); 33681ad6265SDimitry Andric if (ReusingEntry) { // Already have an entry with this inode, return it. 337e8d8bef9SDimitry Andric return ReturnedRef; 3380b57cec5SDimitry Andric } 3390b57cec5SDimitry Andric 3400b57cec5SDimitry Andric // Otherwise, we don't have this file yet, add it. 34181ad6265SDimitry Andric UFE->Size = Status.getSize(); 34281ad6265SDimitry Andric UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime()); 34381ad6265SDimitry Andric UFE->Dir = &DirInfo.getDirEntry(); 34481ad6265SDimitry Andric UFE->UID = NextFileUID++; 34581ad6265SDimitry Andric UFE->UniqueID = Status.getUniqueID(); 34681ad6265SDimitry Andric UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file; 34781ad6265SDimitry Andric UFE->File = std::move(F); 3480b57cec5SDimitry Andric 34981ad6265SDimitry Andric if (UFE->File) { 35081ad6265SDimitry Andric if (auto PathName = UFE->File->getName()) 35181ad6265SDimitry Andric fillRealPathName(UFE, *PathName); 3520b57cec5SDimitry Andric } else if (!openFile) { 3530b57cec5SDimitry Andric // We should still fill the path even if we aren't opening the file. 35481ad6265SDimitry Andric fillRealPathName(UFE, InterndFileName); 3550b57cec5SDimitry Andric } 356e8d8bef9SDimitry Andric return ReturnedRef; 3570b57cec5SDimitry Andric } 3580b57cec5SDimitry Andric 359e8d8bef9SDimitry Andric llvm::Expected<FileEntryRef> FileManager::getSTDIN() { 360e8d8bef9SDimitry Andric // Only read stdin once. 361e8d8bef9SDimitry Andric if (STDIN) 362e8d8bef9SDimitry Andric return *STDIN; 363e8d8bef9SDimitry Andric 364e8d8bef9SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Content; 365e8d8bef9SDimitry Andric if (auto ContentOrError = llvm::MemoryBuffer::getSTDIN()) 366e8d8bef9SDimitry Andric Content = std::move(*ContentOrError); 367e8d8bef9SDimitry Andric else 368e8d8bef9SDimitry Andric return llvm::errorCodeToError(ContentOrError.getError()); 369e8d8bef9SDimitry Andric 370e8d8bef9SDimitry Andric STDIN = getVirtualFileRef(Content->getBufferIdentifier(), 371e8d8bef9SDimitry Andric Content->getBufferSize(), 0); 372e8d8bef9SDimitry Andric FileEntry &FE = const_cast<FileEntry &>(STDIN->getFileEntry()); 373e8d8bef9SDimitry Andric FE.Content = std::move(Content); 374e8d8bef9SDimitry Andric FE.IsNamedPipe = true; 375e8d8bef9SDimitry Andric return *STDIN; 376e8d8bef9SDimitry Andric } 377e8d8bef9SDimitry Andric 378*0fca6ea1SDimitry Andric void FileManager::trackVFSUsage(bool Active) { 379*0fca6ea1SDimitry Andric FS->visit([Active](llvm::vfs::FileSystem &FileSys) { 380*0fca6ea1SDimitry Andric if (auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys)) 381*0fca6ea1SDimitry Andric RFS->setUsageTrackingActive(Active); 382*0fca6ea1SDimitry Andric }); 383*0fca6ea1SDimitry Andric } 384*0fca6ea1SDimitry Andric 385e8d8bef9SDimitry Andric const FileEntry *FileManager::getVirtualFile(StringRef Filename, off_t Size, 386e8d8bef9SDimitry Andric time_t ModificationTime) { 387e8d8bef9SDimitry Andric return &getVirtualFileRef(Filename, Size, ModificationTime).getFileEntry(); 388e8d8bef9SDimitry Andric } 389e8d8bef9SDimitry Andric 390e8d8bef9SDimitry Andric FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size, 3910b57cec5SDimitry Andric time_t ModificationTime) { 3920b57cec5SDimitry Andric ++NumFileLookups; 3930b57cec5SDimitry Andric 3940b57cec5SDimitry Andric // See if there is already an entry in the map for an existing file. 395a7dea167SDimitry Andric auto &NamedFileEnt = *SeenFileEntries.insert( 396a7dea167SDimitry Andric {Filename, std::errc::no_such_file_or_directory}).first; 397a7dea167SDimitry Andric if (NamedFileEnt.second) { 398e8d8bef9SDimitry Andric FileEntryRef::MapValue Value = *NamedFileEnt.second; 399e8d8bef9SDimitry Andric if (LLVM_LIKELY(Value.V.is<FileEntry *>())) 400e8d8bef9SDimitry Andric return FileEntryRef(NamedFileEnt); 40106c3fb27SDimitry Andric return FileEntryRef(*Value.V.get<const FileEntryRef::MapEntry *>()); 402a7dea167SDimitry Andric } 4030b57cec5SDimitry Andric 4040b57cec5SDimitry Andric // We've not seen this before, or the file is cached as non-existent. 4050b57cec5SDimitry Andric ++NumFileCacheMisses; 4060b57cec5SDimitry Andric addAncestorsAsVirtualDirs(Filename); 4070b57cec5SDimitry Andric FileEntry *UFE = nullptr; 4080b57cec5SDimitry Andric 4090b57cec5SDimitry Andric // Now that all ancestors of Filename are in the cache, the 4100b57cec5SDimitry Andric // following call is guaranteed to find the DirectoryEntry from the 411fe6060f1SDimitry Andric // cache. A virtual file can also have an empty filename, that could come 412fe6060f1SDimitry Andric // from a source location preprocessor directive with an empty filename as 413fe6060f1SDimitry Andric // an example, so we need to pretend it has a name to ensure a valid directory 414fe6060f1SDimitry Andric // entry can be returned. 415fe6060f1SDimitry Andric auto DirInfo = expectedToOptional(getDirectoryFromFile( 416fe6060f1SDimitry Andric *this, Filename.empty() ? "." : Filename, /*CacheFailure=*/true)); 4170b57cec5SDimitry Andric assert(DirInfo && 4180b57cec5SDimitry Andric "The directory of a virtual file should already be in the cache."); 4190b57cec5SDimitry Andric 4200b57cec5SDimitry Andric // Check to see if the file exists. If so, drop the virtual file 4210b57cec5SDimitry Andric llvm::vfs::Status Status; 4220b57cec5SDimitry Andric const char *InterndFileName = NamedFileEnt.first().data(); 423a7dea167SDimitry Andric if (!getStatValue(InterndFileName, Status, true, nullptr)) { 4240b57cec5SDimitry Andric Status = llvm::vfs::Status( 4250b57cec5SDimitry Andric Status.getName(), Status.getUniqueID(), 4260b57cec5SDimitry Andric llvm::sys::toTimePoint(ModificationTime), 4270b57cec5SDimitry Andric Status.getUser(), Status.getGroup(), Size, 4280b57cec5SDimitry Andric Status.getType(), Status.getPermissions()); 4290b57cec5SDimitry Andric 43081ad6265SDimitry Andric auto &RealFE = UniqueRealFiles[Status.getUniqueID()]; 43181ad6265SDimitry Andric if (RealFE) { 4320b57cec5SDimitry Andric // If we had already opened this file, close it now so we don't 4330b57cec5SDimitry Andric // leak the descriptor. We're not going to use the file 4340b57cec5SDimitry Andric // descriptor anyway, since this is a virtual file. 43581ad6265SDimitry Andric if (RealFE->File) 43681ad6265SDimitry Andric RealFE->closeFile(); 4370b57cec5SDimitry Andric // If we already have an entry with this inode, return it. 438e8d8bef9SDimitry Andric // 439e8d8bef9SDimitry Andric // FIXME: Surely this should add a reference by the new name, and return 440e8d8bef9SDimitry Andric // it instead... 44181ad6265SDimitry Andric NamedFileEnt.second = FileEntryRef::MapValue(*RealFE, *DirInfo); 442e8d8bef9SDimitry Andric return FileEntryRef(NamedFileEnt); 44381ad6265SDimitry Andric } 44481ad6265SDimitry Andric // File exists, but no entry - create it. 44581ad6265SDimitry Andric RealFE = new (FilesAlloc.Allocate()) FileEntry(); 44681ad6265SDimitry Andric RealFE->UniqueID = Status.getUniqueID(); 44781ad6265SDimitry Andric RealFE->IsNamedPipe = 44881ad6265SDimitry Andric Status.getType() == llvm::sys::fs::file_type::fifo_file; 44981ad6265SDimitry Andric fillRealPathName(RealFE, Status.getName()); 4500b57cec5SDimitry Andric 45181ad6265SDimitry Andric UFE = RealFE; 4520b57cec5SDimitry Andric } else { 45381ad6265SDimitry Andric // File does not exist, create a virtual entry. 45481ad6265SDimitry Andric UFE = new (FilesAlloc.Allocate()) FileEntry(); 45581ad6265SDimitry Andric VirtualFileEntries.push_back(UFE); 4560b57cec5SDimitry Andric } 4570b57cec5SDimitry Andric 45881ad6265SDimitry Andric NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo); 4590b57cec5SDimitry Andric UFE->Size = Size; 4600b57cec5SDimitry Andric UFE->ModTime = ModificationTime; 461e8d8bef9SDimitry Andric UFE->Dir = &DirInfo->getDirEntry(); 4620b57cec5SDimitry Andric UFE->UID = NextFileUID++; 4630b57cec5SDimitry Andric UFE->File.reset(); 464e8d8bef9SDimitry Andric return FileEntryRef(NamedFileEnt); 4650b57cec5SDimitry Andric } 4660b57cec5SDimitry Andric 467bdd1243dSDimitry Andric OptionalFileEntryRef FileManager::getBypassFile(FileEntryRef VF) { 468a7dea167SDimitry Andric // Stat of the file and return nullptr if it doesn't exist. 469a7dea167SDimitry Andric llvm::vfs::Status Status; 470a7dea167SDimitry Andric if (getStatValue(VF.getName(), Status, /*isFile=*/true, /*F=*/nullptr)) 471bdd1243dSDimitry Andric return std::nullopt; 472a7dea167SDimitry Andric 473e8d8bef9SDimitry Andric if (!SeenBypassFileEntries) 474e8d8bef9SDimitry Andric SeenBypassFileEntries = std::make_unique< 475e8d8bef9SDimitry Andric llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>(); 476e8d8bef9SDimitry Andric 477e8d8bef9SDimitry Andric // If we've already bypassed just use the existing one. 478e8d8bef9SDimitry Andric auto Insertion = SeenBypassFileEntries->insert( 479e8d8bef9SDimitry Andric {VF.getName(), std::errc::no_such_file_or_directory}); 480e8d8bef9SDimitry Andric if (!Insertion.second) 481e8d8bef9SDimitry Andric return FileEntryRef(*Insertion.first); 482e8d8bef9SDimitry Andric 483e8d8bef9SDimitry Andric // Fill in the new entry from the stat. 48481ad6265SDimitry Andric FileEntry *BFE = new (FilesAlloc.Allocate()) FileEntry(); 48581ad6265SDimitry Andric BypassFileEntries.push_back(BFE); 48681ad6265SDimitry Andric Insertion.first->second = FileEntryRef::MapValue(*BFE, VF.getDir()); 48781ad6265SDimitry Andric BFE->Size = Status.getSize(); 48881ad6265SDimitry Andric BFE->Dir = VF.getFileEntry().Dir; 48981ad6265SDimitry Andric BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime()); 49081ad6265SDimitry Andric BFE->UID = NextFileUID++; 491e8d8bef9SDimitry Andric 492e8d8bef9SDimitry Andric // Save the entry in the bypass table and return. 493e8d8bef9SDimitry Andric return FileEntryRef(*Insertion.first); 494a7dea167SDimitry Andric } 495a7dea167SDimitry Andric 4960b57cec5SDimitry Andric bool FileManager::FixupRelativePath(SmallVectorImpl<char> &path) const { 4970b57cec5SDimitry Andric StringRef pathRef(path.data(), path.size()); 4980b57cec5SDimitry Andric 4990b57cec5SDimitry Andric if (FileSystemOpts.WorkingDir.empty() 5000b57cec5SDimitry Andric || llvm::sys::path::is_absolute(pathRef)) 5010b57cec5SDimitry Andric return false; 5020b57cec5SDimitry Andric 5030b57cec5SDimitry Andric SmallString<128> NewPath(FileSystemOpts.WorkingDir); 5040b57cec5SDimitry Andric llvm::sys::path::append(NewPath, pathRef); 5050b57cec5SDimitry Andric path = NewPath; 5060b57cec5SDimitry Andric return true; 5070b57cec5SDimitry Andric } 5080b57cec5SDimitry Andric 5090b57cec5SDimitry Andric bool FileManager::makeAbsolutePath(SmallVectorImpl<char> &Path) const { 5100b57cec5SDimitry Andric bool Changed = FixupRelativePath(Path); 5110b57cec5SDimitry Andric 5120b57cec5SDimitry Andric if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) { 5130b57cec5SDimitry Andric FS->makeAbsolute(Path); 5140b57cec5SDimitry Andric Changed = true; 5150b57cec5SDimitry Andric } 5160b57cec5SDimitry Andric 5170b57cec5SDimitry Andric return Changed; 5180b57cec5SDimitry Andric } 5190b57cec5SDimitry Andric 5200b57cec5SDimitry Andric void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) { 5210b57cec5SDimitry Andric llvm::SmallString<128> AbsPath(FileName); 5220b57cec5SDimitry Andric // This is not the same as `VFS::getRealPath()`, which resolves symlinks 5230b57cec5SDimitry Andric // but can be very expensive on real file systems. 5240b57cec5SDimitry Andric // FIXME: the semantic of RealPathName is unclear, and the name might be 5250b57cec5SDimitry Andric // misleading. We need to clean up the interface here. 5260b57cec5SDimitry Andric makeAbsolutePath(AbsPath); 5270b57cec5SDimitry Andric llvm::sys::path::remove_dots(AbsPath, /*remove_dot_dot=*/true); 5287a6dacacSDimitry Andric UFE->RealPathName = std::string(AbsPath); 5290b57cec5SDimitry Andric } 5300b57cec5SDimitry Andric 5310b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 5325f757f3fSDimitry Andric FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile, 533*0fca6ea1SDimitry Andric bool RequiresNullTerminator, 534*0fca6ea1SDimitry Andric std::optional<int64_t> MaybeLimit) { 5355f757f3fSDimitry Andric const FileEntry *Entry = &FE.getFileEntry(); 536e8d8bef9SDimitry Andric // If the content is living on the file entry, return a reference to it. 537e8d8bef9SDimitry Andric if (Entry->Content) 538e8d8bef9SDimitry Andric return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef()); 539e8d8bef9SDimitry Andric 5400b57cec5SDimitry Andric uint64_t FileSize = Entry->getSize(); 541*0fca6ea1SDimitry Andric 542*0fca6ea1SDimitry Andric if (MaybeLimit) 543*0fca6ea1SDimitry Andric FileSize = *MaybeLimit; 544*0fca6ea1SDimitry Andric 5450b57cec5SDimitry Andric // If there's a high enough chance that the file have changed since we 5460b57cec5SDimitry Andric // got its size, force a stat before opening it. 547e8d8bef9SDimitry Andric if (isVolatile || Entry->isNamedPipe()) 5480b57cec5SDimitry Andric FileSize = -1; 5490b57cec5SDimitry Andric 5505f757f3fSDimitry Andric StringRef Filename = FE.getName(); 5510b57cec5SDimitry Andric // If the file is already open, use the open file descriptor. 5520b57cec5SDimitry Andric if (Entry->File) { 5535ffd83dbSDimitry Andric auto Result = Entry->File->getBuffer(Filename, FileSize, 5545ffd83dbSDimitry Andric RequiresNullTerminator, isVolatile); 5550b57cec5SDimitry Andric Entry->closeFile(); 5560b57cec5SDimitry Andric return Result; 5570b57cec5SDimitry Andric } 5580b57cec5SDimitry Andric 5590b57cec5SDimitry Andric // Otherwise, open the file. 5605ffd83dbSDimitry Andric return getBufferForFileImpl(Filename, FileSize, isVolatile, 5615ffd83dbSDimitry Andric RequiresNullTerminator); 562a7dea167SDimitry Andric } 5630b57cec5SDimitry Andric 564a7dea167SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 565a7dea167SDimitry Andric FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize, 5665ffd83dbSDimitry Andric bool isVolatile, 567*0fca6ea1SDimitry Andric bool RequiresNullTerminator) const { 5680b57cec5SDimitry Andric if (FileSystemOpts.WorkingDir.empty()) 5695ffd83dbSDimitry Andric return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator, 5705ffd83dbSDimitry Andric isVolatile); 5710b57cec5SDimitry Andric 572a7dea167SDimitry Andric SmallString<128> FilePath(Filename); 5730b57cec5SDimitry Andric FixupRelativePath(FilePath); 5745ffd83dbSDimitry Andric return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator, 5755ffd83dbSDimitry Andric isVolatile); 5760b57cec5SDimitry Andric } 5770b57cec5SDimitry Andric 5780b57cec5SDimitry Andric /// getStatValue - Get the 'stat' information for the specified path, 5790b57cec5SDimitry Andric /// using the cache to accelerate it if possible. This returns true 5800b57cec5SDimitry Andric /// if the path points to a virtual file or does not exist, or returns 5810b57cec5SDimitry Andric /// false if it's an existent real file. If FileDescriptor is NULL, 5820b57cec5SDimitry Andric /// do directory look-up instead of file look-up. 583a7dea167SDimitry Andric std::error_code 584a7dea167SDimitry Andric FileManager::getStatValue(StringRef Path, llvm::vfs::Status &Status, 585a7dea167SDimitry Andric bool isFile, std::unique_ptr<llvm::vfs::File> *F) { 5860b57cec5SDimitry Andric // FIXME: FileSystemOpts shouldn't be passed in here, all paths should be 5870b57cec5SDimitry Andric // absolute! 5880b57cec5SDimitry Andric if (FileSystemOpts.WorkingDir.empty()) 589a7dea167SDimitry Andric return FileSystemStatCache::get(Path, Status, isFile, F, 590a7dea167SDimitry Andric StatCache.get(), *FS); 5910b57cec5SDimitry Andric 5920b57cec5SDimitry Andric SmallString<128> FilePath(Path); 5930b57cec5SDimitry Andric FixupRelativePath(FilePath); 5940b57cec5SDimitry Andric 595a7dea167SDimitry Andric return FileSystemStatCache::get(FilePath.c_str(), Status, isFile, F, 596a7dea167SDimitry Andric StatCache.get(), *FS); 5970b57cec5SDimitry Andric } 5980b57cec5SDimitry Andric 599a7dea167SDimitry Andric std::error_code 600a7dea167SDimitry Andric FileManager::getNoncachedStatValue(StringRef Path, 6010b57cec5SDimitry Andric llvm::vfs::Status &Result) { 6020b57cec5SDimitry Andric SmallString<128> FilePath(Path); 6030b57cec5SDimitry Andric FixupRelativePath(FilePath); 6040b57cec5SDimitry Andric 6050b57cec5SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str()); 6060b57cec5SDimitry Andric if (!S) 607a7dea167SDimitry Andric return S.getError(); 6080b57cec5SDimitry Andric Result = *S; 609a7dea167SDimitry Andric return std::error_code(); 6100b57cec5SDimitry Andric } 6110b57cec5SDimitry Andric 6120b57cec5SDimitry Andric void FileManager::GetUniqueIDMapping( 6135f757f3fSDimitry Andric SmallVectorImpl<OptionalFileEntryRef> &UIDToFiles) const { 6140b57cec5SDimitry Andric UIDToFiles.clear(); 6150b57cec5SDimitry Andric UIDToFiles.resize(NextFileUID); 6160b57cec5SDimitry Andric 6175f757f3fSDimitry Andric for (const auto &Entry : SeenFileEntries) { 6185f757f3fSDimitry Andric // Only return files that exist and are not redirected. 6195f757f3fSDimitry Andric if (!Entry.getValue() || !Entry.getValue()->V.is<FileEntry *>()) 6205f757f3fSDimitry Andric continue; 6215f757f3fSDimitry Andric FileEntryRef FE(Entry); 6225f757f3fSDimitry Andric // Add this file if it's the first one with the UID, or if its name is 6235f757f3fSDimitry Andric // better than the existing one. 6245f757f3fSDimitry Andric OptionalFileEntryRef &ExistingFE = UIDToFiles[FE.getUID()]; 6255f757f3fSDimitry Andric if (!ExistingFE || FE.getName() < ExistingFE->getName()) 6265f757f3fSDimitry Andric ExistingFE = FE; 627a7dea167SDimitry Andric } 6280b57cec5SDimitry Andric } 6290b57cec5SDimitry Andric 63006c3fb27SDimitry Andric StringRef FileManager::getCanonicalName(DirectoryEntryRef Dir) { 6315f757f3fSDimitry Andric return getCanonicalName(Dir, Dir.getName()); 632480093f4SDimitry Andric } 633480093f4SDimitry Andric 6345f757f3fSDimitry Andric StringRef FileManager::getCanonicalName(FileEntryRef File) { 6355f757f3fSDimitry Andric return getCanonicalName(File, File.getName()); 6365f757f3fSDimitry Andric } 6375f757f3fSDimitry Andric 6385f757f3fSDimitry Andric StringRef FileManager::getCanonicalName(const void *Entry, StringRef Name) { 6395f757f3fSDimitry Andric llvm::DenseMap<const void *, llvm::StringRef>::iterator Known = 6405f757f3fSDimitry Andric CanonicalNames.find(Entry); 641480093f4SDimitry Andric if (Known != CanonicalNames.end()) 642480093f4SDimitry Andric return Known->second; 643480093f4SDimitry Andric 6445f757f3fSDimitry Andric // Name comes from FileEntry/DirectoryEntry::getName(), so it is safe to 6455f757f3fSDimitry Andric // store it in the DenseMap below. 6465f757f3fSDimitry Andric StringRef CanonicalName(Name); 647480093f4SDimitry Andric 6485f757f3fSDimitry Andric SmallString<256> AbsPathBuf; 6495f757f3fSDimitry Andric SmallString<256> RealPathBuf; 6505f757f3fSDimitry Andric if (!FS->getRealPath(Name, RealPathBuf)) { 6515f757f3fSDimitry Andric if (is_style_windows(llvm::sys::path::Style::native)) { 6525f757f3fSDimitry Andric // For Windows paths, only use the real path if it doesn't resolve 6535f757f3fSDimitry Andric // a substitute drive, as those are used to avoid MAX_PATH issues. 6545f757f3fSDimitry Andric AbsPathBuf = Name; 6555f757f3fSDimitry Andric if (!FS->makeAbsolute(AbsPathBuf)) { 6565f757f3fSDimitry Andric if (llvm::sys::path::root_name(RealPathBuf) == 6575f757f3fSDimitry Andric llvm::sys::path::root_name(AbsPathBuf)) { 6585f757f3fSDimitry Andric CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage); 6595f757f3fSDimitry Andric } else { 6605f757f3fSDimitry Andric // Fallback to using the absolute path. 6615f757f3fSDimitry Andric // Simplifying /../ is semantically valid on Windows even in the 6625f757f3fSDimitry Andric // presence of symbolic links. 6635f757f3fSDimitry Andric llvm::sys::path::remove_dots(AbsPathBuf, /*remove_dot_dot=*/true); 6645f757f3fSDimitry Andric CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage); 6655f757f3fSDimitry Andric } 6665f757f3fSDimitry Andric } 6675f757f3fSDimitry Andric } else { 6685f757f3fSDimitry Andric CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage); 6695f757f3fSDimitry Andric } 6705f757f3fSDimitry Andric } 671480093f4SDimitry Andric 6725f757f3fSDimitry Andric CanonicalNames.insert({Entry, CanonicalName}); 6730b57cec5SDimitry Andric return CanonicalName; 6740b57cec5SDimitry Andric } 6750b57cec5SDimitry Andric 676*0fca6ea1SDimitry Andric void FileManager::AddStats(const FileManager &Other) { 677*0fca6ea1SDimitry Andric assert(&Other != this && "Collecting stats into the same FileManager"); 678*0fca6ea1SDimitry Andric NumDirLookups += Other.NumDirLookups; 679*0fca6ea1SDimitry Andric NumFileLookups += Other.NumFileLookups; 680*0fca6ea1SDimitry Andric NumDirCacheMisses += Other.NumDirCacheMisses; 681*0fca6ea1SDimitry Andric NumFileCacheMisses += Other.NumFileCacheMisses; 682*0fca6ea1SDimitry Andric } 683*0fca6ea1SDimitry Andric 6840b57cec5SDimitry Andric void FileManager::PrintStats() const { 6850b57cec5SDimitry Andric llvm::errs() << "\n*** File Manager Stats:\n"; 6860b57cec5SDimitry Andric llvm::errs() << UniqueRealFiles.size() << " real files found, " 6870b57cec5SDimitry Andric << UniqueRealDirs.size() << " real dirs found.\n"; 6880b57cec5SDimitry Andric llvm::errs() << VirtualFileEntries.size() << " virtual files found, " 6890b57cec5SDimitry Andric << VirtualDirectoryEntries.size() << " virtual dirs found.\n"; 6900b57cec5SDimitry Andric llvm::errs() << NumDirLookups << " dir lookups, " 6910b57cec5SDimitry Andric << NumDirCacheMisses << " dir cache misses.\n"; 6920b57cec5SDimitry Andric llvm::errs() << NumFileLookups << " file lookups, " 6930b57cec5SDimitry Andric << NumFileCacheMisses << " file cache misses.\n"; 6940b57cec5SDimitry Andric 6950b57cec5SDimitry Andric //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups; 6960b57cec5SDimitry Andric } 697