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-exception |
6 | // |
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 | |
36 | using namespace clang; |
37 | |
38 | #define DEBUG_TYPE "file-search" |
39 | |
40 | //===----------------------------------------------------------------------===// |
41 | // Common logic. |
42 | //===----------------------------------------------------------------------===// |
43 | |
44 | FileManager::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 real |
49 | // file system. |
50 | if (!this->FS) |
51 | this->FS = llvm::vfs::getRealFileSystem(); |
52 | } |
53 | |
54 | FileManager::~FileManager() = default; |
55 | |
56 | void FileManager::setStatCache(std::unique_ptr<FileSystemStatCache> statCache) { |
57 | assert(statCache && "No stat cache provided?"); |
58 | StatCache = std::move(statCache); |
59 | } |
60 | |
61 | void 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. |
65 | static llvm::Expected<DirectoryEntryRef> |
66 | getDirectoryFromFile(FileManager &FileMgr, StringRef Filename, |
67 | bool CacheFailure) { |
68 | if (Filename.empty()) |
69 | return llvm::errorCodeToError( |
70 | EC: make_error_code(e: std::errc::no_such_file_or_directory)); |
71 | |
72 | if (llvm::sys::path::is_separator(value: Filename[Filename.size() - 1])) |
73 | return llvm::errorCodeToError(EC: make_error_code(e: std::errc::is_a_directory)); |
74 | |
75 | StringRef DirName = llvm::sys::path::parent_path(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 | |
83 | DirectoryEntry *&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 the |
86 | // same inode (this occurs on Unix-like systems when one dir is |
87 | // symlinked to another, for example) or the same path (on |
88 | // Windows). |
89 | DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()]; |
90 | |
91 | if (!UDE) { |
92 | // We don't have this directory yet, add it. We use the string |
93 | // 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 or |
100 | /// a directory) as virtual directories. |
101 | void FileManager::addAncestorsAsVirtualDirs(StringRef Path) { |
102 | StringRef DirName = llvm::sys::path::parent_path(path: Path); |
103 | if (DirName.empty()) |
104 | DirName = "."; |
105 | |
106 | auto &NamedDirEnt = *SeenDirEntries.insert( |
107 | KV: {DirName, std::errc::no_such_file_or_directory}).first; |
108 | |
109 | // When caching a virtual directory, we always cache its ancestors |
110 | // 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 in |
112 | // 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(Path: DirName, Status, isFile: false, F: 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(Elt: UDE); |
126 | } else { |
127 | // There is the real directory |
128 | DirectoryEntry *&UDE = getRealDirEntry(Status); |
129 | NamedDirEnt.second = *UDE; |
130 | } |
131 | |
132 | // Recursively add the other ancestors. |
133 | addAncestorsAsVirtualDirs(Path: DirName); |
134 | } |
135 | |
136 | llvm::Expected<DirectoryEntryRef> |
137 | FileManager::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(path: DirName) && |
143 | llvm::sys::path::is_separator(value: DirName.back())) |
144 | DirName = DirName.substr(Start: 0, N: DirName.size()-1); |
145 | std::optional<std::string> DirNameStr; |
146 | if (is_style_windows(S: 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 directory |
149 | if (DirName.size() > 1 && DirName.back() == ':' && |
150 | DirName.equals_insensitive(RHS: llvm::sys::path::root_name(path: 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 map |
159 | // contains both virtual and real directories. |
160 | auto SeenDirInsertResult = |
161 | SeenDirEntries.insert(KV: {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(EC: 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 the |
174 | // 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(Path: InterndDirName, Status, isFile: false, |
180 | F: nullptr /*directory lookup*/); |
181 | if (statError) { |
182 | // There's no real directory at the given path. |
183 | if (CacheFailure) |
184 | NamedDirEnt.second = statError; |
185 | else |
186 | SeenDirEntries.erase(Key: DirName); |
187 | return llvm::errorCodeToError(EC: statError); |
188 | } |
189 | |
190 | // It exists. |
191 | DirectoryEntry *&UDE = getRealDirEntry(Status); |
192 | NamedDirEnt.second = *UDE; |
193 | |
194 | return DirectoryEntryRef(NamedDirEnt); |
195 | } |
196 | |
197 | llvm::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(KV: {Filename, std::errc::no_such_file_or_directory}); |
206 | if (!SeenFileInsertResult.second) { |
207 | if (!SeenFileInsertResult.first->second) |
208 | return llvm::errorCodeToError( |
209 | EC: 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 the |
219 | // SeenFileEntries map. |
220 | StringRef InterndFileName = NamedFileEnt->first(); |
221 | |
222 | // Look up the directory for the file. When looking up something like |
223 | // 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-fail |
225 | // searches when we go to find sys/bar.h, because all the search directories |
226 | // without a 'sys' subdir will get a cached failure result. |
227 | auto DirInfoOrErr = getDirectoryFromFile(FileMgr&: *this, Filename, CacheFailure); |
228 | if (!DirInfoOrErr) { // Directory doesn't exist, file can't exist. |
229 | std::error_code Err = errorToErrorCode(Err: DirInfoOrErr.takeError()); |
230 | if (CacheFailure) |
231 | NamedFileEnt->second = Err; |
232 | else |
233 | SeenFileEntries.erase(Key: Filename); |
234 | |
235 | return llvm::errorCodeToError(EC: 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(Path: InterndFileName, Status, isFile: true, |
246 | F: 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 | else |
252 | SeenFileEntries.erase(Key: Filename); |
253 | |
254 | return llvm::errorCodeToError(EC: 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 want |
271 | // to return. |
272 | // |
273 | // This redirection logic intentionally leaks the external name of a |
274 | // redirected file that uses 'use-external-name' in \a |
275 | // vfs::RedirectionFileSystem. This allows clang to report the external |
276 | // name to users (in diagnostics) and to tools that don't have access to |
277 | // the VFS (in debug info and dependency '.d' files). |
278 | // |
279 | // FIXME: This is pretty complex and has some very complicated interactions |
280 | // with the rest of clang. It's also inconsistent with how "real" |
281 | // filesystems behave and confuses parts of clang expect to see the |
282 | // 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 using |
287 | // `getName()`. |
288 | // - Add a `FileManager::getExternalPath` API for explicitly getting the |
289 | // remapped external filename when there is one available. Adopt it in |
290 | // callers like diagnostics/deps reporting instead of calling |
291 | // `getName()` directly. |
292 | // - Switch the meaning of `FileEntryRef::getName()` to get the requested |
293 | // name, not the external name. Once that sticks, revert callers that |
294 | // want the requested name back to calling `getName()`. |
295 | // - Update the VFS to always return the requested name. This could also |
296 | // return the external name, or just have an API to request it |
297 | // lazily. The latter has the benefit of making accesses of the |
298 | // external path easily tracked, but may also require extra work than |
299 | // just returning up front. |
300 | // - (Optionally) Add an API to VFS to get the external filename lazily |
301 | // and update `FileManager::getExternalPath()` to use it instead. This |
302 | // has the benefit of making such accesses easily tracked, though isn't |
303 | // necessarily required (and could cause extra work than just adding to |
304 | // eg. `vfs::Status` up front). |
305 | auto &Redirection = |
306 | *SeenFileEntries |
307 | .insert(KV: {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 available |
315 | // 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(TP: 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, FileName: *PathName); |
338 | } else if (!openFile) { |
339 | // We should still fill the path even if we aren't opening the file. |
340 | fillRealPathName(UFE, FileName: InterndFileName); |
341 | } |
342 | return ReturnedRef; |
343 | } |
344 | |
345 | llvm::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 | else |
354 | return llvm::errorCodeToError(EC: ContentOrError.getError()); |
355 | |
356 | STDIN = getVirtualFileRef(Filename: Content->getBufferIdentifier(), |
357 | Size: Content->getBufferSize(), ModificationTime: 0); |
358 | FileEntry &FE = const_cast<FileEntry &>(STDIN->getFileEntry()); |
359 | FE.Content = std::move(Content); |
360 | FE.IsNamedPipe = true; |
361 | return *STDIN; |
362 | } |
363 | |
364 | void FileManager::trackVFSUsage(bool Active) { |
365 | FS->visit(Callback: [Active](llvm::vfs::FileSystem &FileSys) { |
366 | if (auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(Val: &FileSys)) |
367 | RFS->setUsageTrackingActive(Active); |
368 | }); |
369 | } |
370 | |
371 | const FileEntry *FileManager::getVirtualFile(StringRef Filename, off_t Size, |
372 | time_t ModificationTime) { |
373 | return &getVirtualFileRef(Filename, Size, ModificationTime).getFileEntry(); |
374 | } |
375 | |
376 | FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size, |
377 | time_t ModificationTime) { |
378 | ++NumFileLookups; |
379 | |
380 | // See if there is already an entry in the map for an existing file. |
381 | auto &NamedFileEnt = *SeenFileEntries.insert( |
382 | KV: {Filename, std::errc::no_such_file_or_directory}).first; |
383 | if (NamedFileEnt.second) { |
384 | FileEntryRef::MapValue Value = *NamedFileEnt.second; |
385 | if (LLVM_LIKELY(isa<FileEntry *>(Value.V))) |
386 | return FileEntryRef(NamedFileEnt); |
387 | return FileEntryRef(*cast<const FileEntryRef::MapEntry *>(Val&: Value.V)); |
388 | } |
389 | |
390 | // We've not seen this before, or the file is cached as non-existent. |
391 | ++NumFileCacheMisses; |
392 | addAncestorsAsVirtualDirs(Path: Filename); |
393 | FileEntry *UFE = nullptr; |
394 | |
395 | // Now that all ancestors of Filename are in the cache, the |
396 | // following call is guaranteed to find the DirectoryEntry from the |
397 | // cache. A virtual file can also have an empty filename, that could come |
398 | // from a source location preprocessor directive with an empty filename as |
399 | // an example, so we need to pretend it has a name to ensure a valid directory |
400 | // entry can be returned. |
401 | auto DirInfo = expectedToOptional(E: getDirectoryFromFile( |
402 | FileMgr&: *this, Filename: Filename.empty() ? ".": Filename, /*CacheFailure=*/true)); |
403 | assert(DirInfo && |
404 | "The directory of a virtual file should already be in the cache."); |
405 | |
406 | // Check to see if the file exists. If so, drop the virtual file |
407 | llvm::vfs::Status Status; |
408 | const char *InterndFileName = NamedFileEnt.first().data(); |
409 | if (!getStatValue(Path: InterndFileName, Status, isFile: true, F: nullptr)) { |
410 | Status = llvm::vfs::Status( |
411 | Status.getName(), Status.getUniqueID(), |
412 | llvm::sys::toTimePoint(T: ModificationTime), |
413 | Status.getUser(), Status.getGroup(), Size, |
414 | Status.getType(), Status.getPermissions()); |
415 | |
416 | auto &RealFE = UniqueRealFiles[Status.getUniqueID()]; |
417 | if (RealFE) { |
418 | // If we had already opened this file, close it now so we don't |
419 | // leak the descriptor. We're not going to use the file |
420 | // descriptor anyway, since this is a virtual file. |
421 | if (RealFE->File) |
422 | RealFE->closeFile(); |
423 | // If we already have an entry with this inode, return it. |
424 | // |
425 | // FIXME: Surely this should add a reference by the new name, and return |
426 | // it instead... |
427 | NamedFileEnt.second = FileEntryRef::MapValue(*RealFE, *DirInfo); |
428 | return FileEntryRef(NamedFileEnt); |
429 | } |
430 | // File exists, but no entry - create it. |
431 | RealFE = new (FilesAlloc.Allocate()) FileEntry(); |
432 | RealFE->UniqueID = Status.getUniqueID(); |
433 | RealFE->IsNamedPipe = |
434 | Status.getType() == llvm::sys::fs::file_type::fifo_file; |
435 | fillRealPathName(UFE: RealFE, FileName: Status.getName()); |
436 | |
437 | UFE = RealFE; |
438 | } else { |
439 | // File does not exist, create a virtual entry. |
440 | UFE = new (FilesAlloc.Allocate()) FileEntry(); |
441 | VirtualFileEntries.push_back(Elt: UFE); |
442 | } |
443 | |
444 | NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo); |
445 | UFE->Size = Size; |
446 | UFE->ModTime = ModificationTime; |
447 | UFE->Dir = &DirInfo->getDirEntry(); |
448 | UFE->UID = NextFileUID++; |
449 | UFE->File.reset(); |
450 | return FileEntryRef(NamedFileEnt); |
451 | } |
452 | |
453 | OptionalFileEntryRef FileManager::getBypassFile(FileEntryRef VF) { |
454 | // Stat of the file and return nullptr if it doesn't exist. |
455 | llvm::vfs::Status Status; |
456 | if (getStatValue(Path: VF.getName(), Status, /*isFile=*/true, /*F=*/nullptr)) |
457 | return std::nullopt; |
458 | |
459 | if (!SeenBypassFileEntries) |
460 | SeenBypassFileEntries = std::make_unique< |
461 | llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>(); |
462 | |
463 | // If we've already bypassed just use the existing one. |
464 | auto Insertion = SeenBypassFileEntries->insert( |
465 | KV: {VF.getName(), std::errc::no_such_file_or_directory}); |
466 | if (!Insertion.second) |
467 | return FileEntryRef(*Insertion.first); |
468 | |
469 | // Fill in the new entry from the stat. |
470 | FileEntry *BFE = new (FilesAlloc.Allocate()) FileEntry(); |
471 | BypassFileEntries.push_back(Elt: BFE); |
472 | Insertion.first->second = FileEntryRef::MapValue(*BFE, VF.getDir()); |
473 | BFE->Size = Status.getSize(); |
474 | BFE->Dir = VF.getFileEntry().Dir; |
475 | BFE->ModTime = llvm::sys::toTimeT(TP: Status.getLastModificationTime()); |
476 | BFE->UID = NextFileUID++; |
477 | |
478 | // Save the entry in the bypass table and return. |
479 | return FileEntryRef(*Insertion.first); |
480 | } |
481 | |
482 | bool FileManager::FixupRelativePath(SmallVectorImpl<char> &path) const { |
483 | StringRef pathRef(path.data(), path.size()); |
484 | |
485 | if (FileSystemOpts.WorkingDir.empty() |
486 | || llvm::sys::path::is_absolute(path: pathRef)) |
487 | return false; |
488 | |
489 | SmallString<128> NewPath(FileSystemOpts.WorkingDir); |
490 | llvm::sys::path::append(path&: NewPath, a: pathRef); |
491 | path = NewPath; |
492 | return true; |
493 | } |
494 | |
495 | bool FileManager::makeAbsolutePath(SmallVectorImpl<char> &Path) const { |
496 | bool Changed = FixupRelativePath(path&: Path); |
497 | |
498 | if (!llvm::sys::path::is_absolute(path: StringRef(Path.data(), Path.size()))) { |
499 | FS->makeAbsolute(Path); |
500 | Changed = true; |
501 | } |
502 | |
503 | return Changed; |
504 | } |
505 | |
506 | void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) { |
507 | llvm::SmallString<128> AbsPath(FileName); |
508 | // This is not the same as `VFS::getRealPath()`, which resolves symlinks |
509 | // but can be very expensive on real file systems. |
510 | // FIXME: the semantic of RealPathName is unclear, and the name might be |
511 | // misleading. We need to clean up the interface here. |
512 | makeAbsolutePath(Path&: AbsPath); |
513 | llvm::sys::path::remove_dots(path&: AbsPath, /*remove_dot_dot=*/true); |
514 | UFE->RealPathName = std::string(AbsPath); |
515 | } |
516 | |
517 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> |
518 | FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile, |
519 | bool RequiresNullTerminator, |
520 | std::optional<int64_t> MaybeLimit, bool IsText) { |
521 | const FileEntry *Entry = &FE.getFileEntry(); |
522 | // If the content is living on the file entry, return a reference to it. |
523 | if (Entry->Content) |
524 | return llvm::MemoryBuffer::getMemBuffer(Ref: Entry->Content->getMemBufferRef()); |
525 | |
526 | uint64_t FileSize = Entry->getSize(); |
527 | |
528 | if (MaybeLimit) |
529 | FileSize = *MaybeLimit; |
530 | |
531 | // If there's a high enough chance that the file have changed since we |
532 | // got its size, force a stat before opening it. |
533 | if (isVolatile || Entry->isNamedPipe()) |
534 | FileSize = -1; |
535 | |
536 | StringRef Filename = FE.getName(); |
537 | // If the file is already open, use the open file descriptor. |
538 | if (Entry->File) { |
539 | auto Result = Entry->File->getBuffer(Name: Filename, FileSize, |
540 | RequiresNullTerminator, IsVolatile: isVolatile); |
541 | Entry->closeFile(); |
542 | return Result; |
543 | } |
544 | |
545 | // Otherwise, open the file. |
546 | return getBufferForFileImpl(Filename, FileSize, isVolatile, |
547 | RequiresNullTerminator, IsText); |
548 | } |
549 | |
550 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> |
551 | FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize, |
552 | bool isVolatile, bool RequiresNullTerminator, |
553 | bool IsText) const { |
554 | if (FileSystemOpts.WorkingDir.empty()) |
555 | return FS->getBufferForFile(Name: Filename, FileSize, RequiresNullTerminator, |
556 | IsVolatile: isVolatile, IsText); |
557 | |
558 | SmallString<128> FilePath(Filename); |
559 | FixupRelativePath(path&: FilePath); |
560 | return FS->getBufferForFile(Name: FilePath, FileSize, RequiresNullTerminator, |
561 | IsVolatile: isVolatile, IsText); |
562 | } |
563 | |
564 | /// getStatValue - Get the 'stat' information for the specified path, |
565 | /// using the cache to accelerate it if possible. This returns true |
566 | /// if the path points to a virtual file or does not exist, or returns |
567 | /// false if it's an existent real file. If FileDescriptor is NULL, |
568 | /// do directory look-up instead of file look-up. |
569 | std::error_code FileManager::getStatValue(StringRef Path, |
570 | llvm::vfs::Status &Status, |
571 | bool isFile, |
572 | std::unique_ptr<llvm::vfs::File> *F, |
573 | bool IsText) { |
574 | // FIXME: FileSystemOpts shouldn't be passed in here, all paths should be |
575 | // absolute! |
576 | if (FileSystemOpts.WorkingDir.empty()) |
577 | return FileSystemStatCache::get(Path, Status, isFile, F, Cache: StatCache.get(), |
578 | FS&: *FS, IsText); |
579 | |
580 | SmallString<128> FilePath(Path); |
581 | FixupRelativePath(path&: FilePath); |
582 | |
583 | return FileSystemStatCache::get(Path: FilePath.c_str(), Status, isFile, F, |
584 | Cache: StatCache.get(), FS&: *FS, IsText); |
585 | } |
586 | |
587 | std::error_code |
588 | FileManager::getNoncachedStatValue(StringRef Path, |
589 | llvm::vfs::Status &Result) { |
590 | SmallString<128> FilePath(Path); |
591 | FixupRelativePath(path&: FilePath); |
592 | |
593 | llvm::ErrorOr<llvm::vfs::Status> S = FS->status(Path: FilePath.c_str()); |
594 | if (!S) |
595 | return S.getError(); |
596 | Result = *S; |
597 | return std::error_code(); |
598 | } |
599 | |
600 | void FileManager::GetUniqueIDMapping( |
601 | SmallVectorImpl<OptionalFileEntryRef> &UIDToFiles) const { |
602 | UIDToFiles.clear(); |
603 | UIDToFiles.resize(N: NextFileUID); |
604 | |
605 | for (const auto &Entry : SeenFileEntries) { |
606 | // Only return files that exist and are not redirected. |
607 | if (!Entry.getValue() || !isa<FileEntry *>(Val: Entry.getValue()->V)) |
608 | continue; |
609 | FileEntryRef FE(Entry); |
610 | // Add this file if it's the first one with the UID, or if its name is |
611 | // better than the existing one. |
612 | OptionalFileEntryRef &ExistingFE = UIDToFiles[FE.getUID()]; |
613 | if (!ExistingFE || FE.getName() < ExistingFE->getName()) |
614 | ExistingFE = FE; |
615 | } |
616 | } |
617 | |
618 | StringRef FileManager::getCanonicalName(DirectoryEntryRef Dir) { |
619 | return getCanonicalName(Entry: Dir, Name: Dir.getName()); |
620 | } |
621 | |
622 | StringRef FileManager::getCanonicalName(FileEntryRef File) { |
623 | return getCanonicalName(Entry: File, Name: File.getName()); |
624 | } |
625 | |
626 | StringRef FileManager::getCanonicalName(const void *Entry, StringRef Name) { |
627 | llvm::DenseMap<const void *, llvm::StringRef>::iterator Known = |
628 | CanonicalNames.find(Val: Entry); |
629 | if (Known != CanonicalNames.end()) |
630 | return Known->second; |
631 | |
632 | // Name comes from FileEntry/DirectoryEntry::getName(), so it is safe to |
633 | // store it in the DenseMap below. |
634 | StringRef CanonicalName(Name); |
635 | |
636 | SmallString<256> AbsPathBuf; |
637 | SmallString<256> RealPathBuf; |
638 | if (!FS->getRealPath(Path: Name, Output&: RealPathBuf)) { |
639 | if (is_style_windows(S: llvm::sys::path::Style::native)) { |
640 | // For Windows paths, only use the real path if it doesn't resolve |
641 | // a substitute drive, as those are used to avoid MAX_PATH issues. |
642 | AbsPathBuf = Name; |
643 | if (!FS->makeAbsolute(Path&: AbsPathBuf)) { |
644 | if (llvm::sys::path::root_name(path: RealPathBuf) == |
645 | llvm::sys::path::root_name(path: AbsPathBuf)) { |
646 | CanonicalName = RealPathBuf.str().copy(A&: CanonicalNameStorage); |
647 | } else { |
648 | // Fallback to using the absolute path. |
649 | // Simplifying /../ is semantically valid on Windows even in the |
650 | // presence of symbolic links. |
651 | llvm::sys::path::remove_dots(path&: AbsPathBuf, /*remove_dot_dot=*/true); |
652 | CanonicalName = AbsPathBuf.str().copy(A&: CanonicalNameStorage); |
653 | } |
654 | } |
655 | } else { |
656 | CanonicalName = RealPathBuf.str().copy(A&: CanonicalNameStorage); |
657 | } |
658 | } |
659 | |
660 | CanonicalNames.insert(KV: {Entry, CanonicalName}); |
661 | return CanonicalName; |
662 | } |
663 | |
664 | void FileManager::AddStats(const FileManager &Other) { |
665 | assert(&Other != this && "Collecting stats into the same FileManager"); |
666 | NumDirLookups += Other.NumDirLookups; |
667 | NumFileLookups += Other.NumFileLookups; |
668 | NumDirCacheMisses += Other.NumDirCacheMisses; |
669 | NumFileCacheMisses += Other.NumFileCacheMisses; |
670 | } |
671 | |
672 | void FileManager::PrintStats() const { |
673 | llvm::errs() << "\n*** File Manager Stats:\n"; |
674 | llvm::errs() << UniqueRealFiles.size() << " real files found, " |
675 | << UniqueRealDirs.size() << " real dirs found.\n"; |
676 | llvm::errs() << VirtualFileEntries.size() << " virtual files found, " |
677 | << VirtualDirectoryEntries.size() << " virtual dirs found.\n"; |
678 | llvm::errs() << NumDirLookups << " dir lookups, " |
679 | << NumDirCacheMisses << " dir cache misses.\n"; |
680 | llvm::errs() << NumFileLookups << " file lookups, " |
681 | << NumFileCacheMisses << " file cache misses.\n"; |
682 | |
683 | getVirtualFileSystem().visit(Callback: [](llvm::vfs::FileSystem &VFS) { |
684 | if (auto *T = dyn_cast_or_null<llvm::vfs::TracingFileSystem>(Val: &VFS)) |
685 | llvm::errs() << "\n*** Virtual File System Stats:\n" |
686 | << T->NumStatusCalls << " status() calls\n" |
687 | << T->NumOpenFileForReadCalls << " openFileForRead() calls\n" |
688 | << T->NumDirBeginCalls << " dir_begin() calls\n" |
689 | << T->NumGetRealPathCalls << " getRealPath() calls\n" |
690 | << T->NumExistsCalls << " exists() calls\n" |
691 | << T->NumIsLocalCalls << " isLocal() calls\n"; |
692 | }); |
693 | |
694 | //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups; |
695 | } |
696 |
Definitions
- FileManager
- ~FileManager
- setStatCache
- clearStatCache
- getDirectoryFromFile
- getRealDirEntry
- addAncestorsAsVirtualDirs
- getDirectoryRef
- getFileRef
- getSTDIN
- trackVFSUsage
- getVirtualFile
- getVirtualFileRef
- getBypassFile
- FixupRelativePath
- makeAbsolutePath
- fillRealPathName
- getBufferForFile
- getBufferForFileImpl
- getStatValue
- getNoncachedStatValue
- GetUniqueIDMapping
- getCanonicalName
- getCanonicalName
- getCanonicalName
- AddStats
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more