/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Basic/FileManager.cpp
Line | Count | Source (jump to first uncovered line) |
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/STLExtras.h" |
22 | | #include "llvm/ADT/SmallString.h" |
23 | | #include "llvm/ADT/Statistic.h" |
24 | | #include "llvm/Config/llvm-config.h" |
25 | | #include "llvm/Support/FileSystem.h" |
26 | | #include "llvm/Support/MemoryBuffer.h" |
27 | | #include "llvm/Support/Path.h" |
28 | | #include "llvm/Support/raw_ostream.h" |
29 | | #include <algorithm> |
30 | | #include <cassert> |
31 | | #include <climits> |
32 | | #include <cstdint> |
33 | | #include <cstdlib> |
34 | | #include <optional> |
35 | | #include <string> |
36 | | #include <utility> |
37 | | |
38 | | using namespace clang; |
39 | | |
40 | | #define DEBUG_TYPE "file-search" |
41 | | |
42 | | ALWAYS_ENABLED_STATISTIC(NumDirLookups, "Number of directory lookups."); |
43 | | ALWAYS_ENABLED_STATISTIC(NumFileLookups, "Number of file lookups."); |
44 | | ALWAYS_ENABLED_STATISTIC(NumDirCacheMisses, |
45 | | "Number of directory cache misses."); |
46 | | ALWAYS_ENABLED_STATISTIC(NumFileCacheMisses, "Number of file cache misses."); |
47 | | |
48 | | //===----------------------------------------------------------------------===// |
49 | | // Common logic. |
50 | | //===----------------------------------------------------------------------===// |
51 | | |
52 | | FileManager::FileManager(const FileSystemOptions &FSO, |
53 | | IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) |
54 | | : FS(std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64), |
55 | 250k | SeenFileEntries(64), NextFileUID(0) { |
56 | | // If the caller doesn't provide a virtual file system, just grab the real |
57 | | // file system. |
58 | 250k | if (!this->FS) |
59 | 313 | this->FS = llvm::vfs::getRealFileSystem(); |
60 | 250k | } |
61 | | |
62 | 244k | FileManager::~FileManager() = default; |
63 | | |
64 | 15 | void FileManager::setStatCache(std::unique_ptr<FileSystemStatCache> statCache) { |
65 | 15 | assert(statCache && "No stat cache provided?"); |
66 | 15 | StatCache = std::move(statCache); |
67 | 15 | } |
68 | | |
69 | 17.9k | void FileManager::clearStatCache() { StatCache.reset(); } |
70 | | |
71 | | /// Retrieve the directory that the given file name resides in. |
72 | | /// Filename can point to either a real file or a virtual file. |
73 | | static llvm::Expected<DirectoryEntryRef> |
74 | | getDirectoryFromFile(FileManager &FileMgr, StringRef Filename, |
75 | 4.10M | bool CacheFailure) { |
76 | 4.10M | if (Filename.empty()) |
77 | 11 | return llvm::errorCodeToError( |
78 | 11 | make_error_code(std::errc::no_such_file_or_directory)); |
79 | | |
80 | 4.10M | if (llvm::sys::path::is_separator(Filename[Filename.size() - 1])) |
81 | 26 | return llvm::errorCodeToError(make_error_code(std::errc::is_a_directory)); |
82 | | |
83 | 4.10M | StringRef DirName = llvm::sys::path::parent_path(Filename); |
84 | | // Use the current directory if file has no path component. |
85 | 4.10M | if (DirName.empty()) |
86 | 153k | DirName = "."; |
87 | | |
88 | 4.10M | return FileMgr.getDirectoryRef(DirName, CacheFailure); |
89 | 4.10M | } |
90 | | |
91 | | /// Add all ancestors of the given path (pointing to either a file or |
92 | | /// a directory) as virtual directories. |
93 | 13.8k | void FileManager::addAncestorsAsVirtualDirs(StringRef Path) { |
94 | 13.8k | StringRef DirName = llvm::sys::path::parent_path(Path); |
95 | 13.8k | if (DirName.empty()) |
96 | 6.97k | DirName = "."; |
97 | | |
98 | 13.8k | auto &NamedDirEnt = *SeenDirEntries.insert( |
99 | 13.8k | {DirName, std::errc::no_such_file_or_directory}).first; |
100 | | |
101 | | // When caching a virtual directory, we always cache its ancestors |
102 | | // at the same time. Therefore, if DirName is already in the cache, |
103 | | // we don't need to recurse as its ancestors must also already be in |
104 | | // the cache (or it's a known non-virtual directory). |
105 | 13.8k | if (NamedDirEnt.second) |
106 | 4.85k | return; |
107 | | |
108 | | // Add the virtual directory to the cache. |
109 | 9.03k | auto *UDE = new (DirsAlloc.Allocate()) DirectoryEntry(); |
110 | 9.03k | UDE->Name = NamedDirEnt.first(); |
111 | 9.03k | NamedDirEnt.second = *UDE; |
112 | 9.03k | VirtualDirectoryEntries.push_back(UDE); |
113 | | |
114 | | // Recursively add the other ancestors. |
115 | 9.03k | addAncestorsAsVirtualDirs(DirName); |
116 | 9.03k | } |
117 | | |
118 | | llvm::Expected<DirectoryEntryRef> |
119 | 10.3M | FileManager::getDirectoryRef(StringRef DirName, bool CacheFailure) { |
120 | | // stat doesn't like trailing separators except for root directory. |
121 | | // At least, on Win32 MSVCRT, stat() cannot strip trailing '/'. |
122 | | // (though it can strip '\\') |
123 | 10.3M | if (DirName.size() > 1 && |
124 | 10.3M | DirName != llvm::sys::path::root_path(DirName)9.80M && |
125 | 10.3M | llvm::sys::path::is_separator(DirName.back())9.80M ) |
126 | 14.0k | DirName = DirName.substr(0, DirName.size()-1); |
127 | 10.3M | std::optional<std::string> DirNameStr; |
128 | 10.3M | if (is_style_windows(llvm::sys::path::Style::native)) { |
129 | | // Fixing a problem with "clang C:test.c" on Windows. |
130 | | // Stat("C:") does not recognize "C:" as a valid directory |
131 | 0 | if (DirName.size() > 1 && DirName.back() == ':' && |
132 | 0 | DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) { |
133 | 0 | DirNameStr = DirName.str() + '.'; |
134 | 0 | DirName = *DirNameStr; |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | 10.3M | ++NumDirLookups; |
139 | | |
140 | | // See if there was already an entry in the map. Note that the map |
141 | | // contains both virtual and real directories. |
142 | 10.3M | auto SeenDirInsertResult = |
143 | 10.3M | SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory}); |
144 | 10.3M | if (!SeenDirInsertResult.second) { |
145 | 9.40M | if (SeenDirInsertResult.first->second) |
146 | 7.32M | return DirectoryEntryRef(*SeenDirInsertResult.first); |
147 | 2.08M | return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError()); |
148 | 9.40M | } |
149 | | |
150 | | // We've not seen this before. Fill it in. |
151 | 905k | ++NumDirCacheMisses; |
152 | 905k | auto &NamedDirEnt = *SeenDirInsertResult.first; |
153 | 905k | assert(!NamedDirEnt.second && "should be newly-created"); |
154 | | |
155 | | // Get the null-terminated directory name as stored as the key of the |
156 | | // SeenDirEntries map. |
157 | 905k | StringRef InterndDirName = NamedDirEnt.first(); |
158 | | |
159 | | // Check to see if the directory exists. |
160 | 905k | llvm::vfs::Status Status; |
161 | 905k | auto statError = getStatValue(InterndDirName, Status, false, |
162 | 905k | nullptr /*directory lookup*/); |
163 | 905k | if (statError) { |
164 | | // There's no real directory at the given path. |
165 | 446k | if (CacheFailure) |
166 | 446k | NamedDirEnt.second = statError; |
167 | 756 | else |
168 | 756 | SeenDirEntries.erase(DirName); |
169 | 446k | return llvm::errorCodeToError(statError); |
170 | 446k | } |
171 | | |
172 | | // It exists. See if we have already opened a directory with the |
173 | | // same inode (this occurs on Unix-like systems when one dir is |
174 | | // symlinked to another, for example) or the same path (on |
175 | | // Windows). |
176 | 458k | DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()]; |
177 | | |
178 | 458k | if (!UDE) { |
179 | | // We don't have this directory yet, add it. We use the string |
180 | | // key from the SeenDirEntries map as the string. |
181 | 457k | UDE = new (DirsAlloc.Allocate()) DirectoryEntry(); |
182 | 457k | UDE->Name = InterndDirName; |
183 | 457k | } |
184 | 458k | NamedDirEnt.second = *UDE; |
185 | | |
186 | 458k | return DirectoryEntryRef(NamedDirEnt); |
187 | 905k | } |
188 | | |
189 | | llvm::ErrorOr<const DirectoryEntry *> |
190 | 758k | FileManager::getDirectory(StringRef DirName, bool CacheFailure) { |
191 | 758k | auto Result = getDirectoryRef(DirName, CacheFailure); |
192 | 758k | if (Result) |
193 | 713k | return &Result->getDirEntry(); |
194 | 45.1k | return llvm::errorToErrorCode(Result.takeError()); |
195 | 758k | } |
196 | | |
197 | | llvm::ErrorOr<const FileEntry *> |
198 | 4.47M | FileManager::getFile(StringRef Filename, bool openFile, bool CacheFailure) { |
199 | 4.47M | auto Result = getFileRef(Filename, openFile, CacheFailure); |
200 | 4.47M | if (Result) |
201 | 4.40M | return &Result->getFileEntry(); |
202 | 73.1k | return llvm::errorToErrorCode(Result.takeError()); |
203 | 4.47M | } |
204 | | |
205 | | llvm::Expected<FileEntryRef> |
206 | 11.8M | FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) { |
207 | 11.8M | ++NumFileLookups; |
208 | | |
209 | | // See if there is already an entry in the map. |
210 | 11.8M | auto SeenFileInsertResult = |
211 | 11.8M | SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory}); |
212 | 11.8M | if (!SeenFileInsertResult.second) { |
213 | 7.69M | if (!SeenFileInsertResult.first->second) |
214 | 45.5k | return llvm::errorCodeToError( |
215 | 45.5k | SeenFileInsertResult.first->second.getError()); |
216 | 7.65M | return FileEntryRef(*SeenFileInsertResult.first); |
217 | 7.69M | } |
218 | | |
219 | | // We've not seen this before. Fill it in. |
220 | 4.10M | ++NumFileCacheMisses; |
221 | 4.10M | auto *NamedFileEnt = &*SeenFileInsertResult.first; |
222 | 4.10M | assert(!NamedFileEnt->second && "should be newly-created"); |
223 | | |
224 | | // Get the null-terminated file name as stored as the key of the |
225 | | // SeenFileEntries map. |
226 | 4.10M | StringRef InterndFileName = NamedFileEnt->first(); |
227 | | |
228 | | // Look up the directory for the file. When looking up something like |
229 | | // sys/foo.h we'll discover all of the search directories that have a 'sys' |
230 | | // subdirectory. This will let us avoid having to waste time on known-to-fail |
231 | | // searches when we go to find sys/bar.h, because all the search directories |
232 | | // without a 'sys' subdir will get a cached failure result. |
233 | 4.10M | auto DirInfoOrErr = getDirectoryFromFile(*this, Filename, CacheFailure); |
234 | 4.10M | if (!DirInfoOrErr) { // Directory doesn't exist, file can't exist. |
235 | 2.22M | std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError()); |
236 | 2.22M | if (CacheFailure) |
237 | 2.22M | NamedFileEnt->second = Err; |
238 | 756 | else |
239 | 756 | SeenFileEntries.erase(Filename); |
240 | | |
241 | 2.22M | return llvm::errorCodeToError(Err); |
242 | 2.22M | } |
243 | 1.88M | DirectoryEntryRef DirInfo = *DirInfoOrErr; |
244 | | |
245 | | // FIXME: Use the directory info to prune this, before doing the stat syscall. |
246 | | // FIXME: This will reduce the # syscalls. |
247 | | |
248 | | // Check to see if the file exists. |
249 | 1.88M | std::unique_ptr<llvm::vfs::File> F; |
250 | 1.88M | llvm::vfs::Status Status; |
251 | 1.88M | auto statError = getStatValue(InterndFileName, Status, true, |
252 | 1.88M | openFile ? &F939k : nullptr940k ); |
253 | 1.88M | if (statError) { |
254 | | // There's no real file at the given path. |
255 | 322k | if (CacheFailure) |
256 | 321k | NamedFileEnt->second = statError; |
257 | 904 | else |
258 | 904 | SeenFileEntries.erase(Filename); |
259 | | |
260 | 322k | return llvm::errorCodeToError(statError); |
261 | 322k | } |
262 | | |
263 | 1.55M | assert((openFile || !F) && "undesired open file"); |
264 | | |
265 | | // It exists. See if we have already opened a file with the same inode. |
266 | | // This occurs when one dir is symlinked to another, for example. |
267 | 1.55M | FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()]; |
268 | 1.55M | bool ReusingEntry = UFE != nullptr; |
269 | 1.55M | if (!UFE) |
270 | 1.55M | UFE = new (FilesAlloc.Allocate()) FileEntry(); |
271 | | |
272 | 1.55M | if (!Status.ExposesExternalVFSPath || Status.getName() == Filename175 ) { |
273 | | // Use the requested name. Set the FileEntry. |
274 | 1.55M | NamedFileEnt->second = FileEntryRef::MapValue(*UFE, DirInfo); |
275 | 1.55M | } else { |
276 | | // Name mismatch. We need a redirect. First grab the actual entry we want |
277 | | // to return. |
278 | | // |
279 | | // This redirection logic intentionally leaks the external name of a |
280 | | // redirected file that uses 'use-external-name' in \a |
281 | | // vfs::RedirectionFileSystem. This allows clang to report the external |
282 | | // name to users (in diagnostics) and to tools that don't have access to |
283 | | // the VFS (in debug info and dependency '.d' files). |
284 | | // |
285 | | // FIXME: This is pretty complex and has some very complicated interactions |
286 | | // with the rest of clang. It's also inconsistent with how "real" |
287 | | // filesystems behave and confuses parts of clang expect to see the |
288 | | // name-as-accessed on the \a FileEntryRef. |
289 | | // |
290 | | // A potential plan to remove this is as follows - |
291 | | // - Update callers such as `HeaderSearch::findUsableModuleForHeader()` |
292 | | // to explicitly use the `getNameAsRequested()` rather than just using |
293 | | // `getName()`. |
294 | | // - Add a `FileManager::getExternalPath` API for explicitly getting the |
295 | | // remapped external filename when there is one available. Adopt it in |
296 | | // callers like diagnostics/deps reporting instead of calling |
297 | | // `getName()` directly. |
298 | | // - Switch the meaning of `FileEntryRef::getName()` to get the requested |
299 | | // name, not the external name. Once that sticks, revert callers that |
300 | | // want the requested name back to calling `getName()`. |
301 | | // - Update the VFS to always return the requested name. This could also |
302 | | // return the external name, or just have an API to request it |
303 | | // lazily. The latter has the benefit of making accesses of the |
304 | | // external path easily tracked, but may also require extra work than |
305 | | // just returning up front. |
306 | | // - (Optionally) Add an API to VFS to get the external filename lazily |
307 | | // and update `FileManager::getExternalPath()` to use it instead. This |
308 | | // has the benefit of making such accesses easily tracked, though isn't |
309 | | // necessarily required (and could cause extra work than just adding to |
310 | | // eg. `vfs::Status` up front). |
311 | 153 | auto &Redirection = |
312 | 153 | *SeenFileEntries |
313 | 153 | .insert({Status.getName(), FileEntryRef::MapValue(*UFE, DirInfo)}) |
314 | 153 | .first; |
315 | 153 | assert(Redirection.second->V.is<FileEntry *>() && |
316 | 153 | "filename redirected to a non-canonical filename?"); |
317 | 153 | assert(Redirection.second->V.get<FileEntry *>() == UFE && |
318 | 153 | "filename from getStatValue() refers to wrong file"); |
319 | | |
320 | | // Cache the redirection in the previously-inserted entry, still available |
321 | | // in the tentative return value. |
322 | 153 | NamedFileEnt->second = FileEntryRef::MapValue(Redirection, DirInfo); |
323 | 153 | } |
324 | | |
325 | 1.55M | FileEntryRef ReturnedRef(*NamedFileEnt); |
326 | 1.55M | if (ReusingEntry) { // Already have an entry with this inode, return it. |
327 | | |
328 | | // FIXME: This hack ensures that `getDir()` will use the path that was |
329 | | // used to lookup this file, even if we found a file by different path |
330 | | // first. This is required in order to find a module's structure when its |
331 | | // headers/module map are mapped in the VFS. |
332 | | // |
333 | | // See above for how this will eventually be removed. `IsVFSMapped` |
334 | | // *cannot* be narrowed to `ExposesExternalVFSPath` as crash reproducers |
335 | | // also depend on this logic and they have `use-external-paths: false`. |
336 | 3.57k | if (&DirInfo.getDirEntry() != UFE->Dir && Status.IsVFSMapped1.34k ) |
337 | 5 | UFE->Dir = &DirInfo.getDirEntry(); |
338 | | |
339 | | // Always update LastRef to the last name by which a file was accessed. |
340 | | // FIXME: Neither this nor always using the first reference is correct; we |
341 | | // want to switch towards a design where we return a FileName object that |
342 | | // encapsulates both the name by which the file was accessed and the |
343 | | // corresponding FileEntry. |
344 | | // FIXME: LastRef should be removed from FileEntry once all clients adopt |
345 | | // FileEntryRef. |
346 | 3.57k | UFE->LastRef = ReturnedRef; |
347 | | |
348 | 3.57k | return ReturnedRef; |
349 | 3.57k | } |
350 | | |
351 | | // Otherwise, we don't have this file yet, add it. |
352 | 1.55M | UFE->LastRef = ReturnedRef; |
353 | 1.55M | UFE->Size = Status.getSize(); |
354 | 1.55M | UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime()); |
355 | 1.55M | UFE->Dir = &DirInfo.getDirEntry(); |
356 | 1.55M | UFE->UID = NextFileUID++; |
357 | 1.55M | UFE->UniqueID = Status.getUniqueID(); |
358 | 1.55M | UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file; |
359 | 1.55M | UFE->File = std::move(F); |
360 | | |
361 | 1.55M | if (UFE->File) { |
362 | 680k | if (auto PathName = UFE->File->getName()) |
363 | 680k | fillRealPathName(UFE, *PathName); |
364 | 873k | } else if (873k !openFile873k ) { |
365 | | // We should still fill the path even if we aren't opening the file. |
366 | 873k | fillRealPathName(UFE, InterndFileName); |
367 | 873k | } |
368 | 1.55M | return ReturnedRef; |
369 | 1.55M | } |
370 | | |
371 | 656 | llvm::Expected<FileEntryRef> FileManager::getSTDIN() { |
372 | | // Only read stdin once. |
373 | 656 | if (STDIN) |
374 | 0 | return *STDIN; |
375 | | |
376 | 656 | std::unique_ptr<llvm::MemoryBuffer> Content; |
377 | 656 | if (auto ContentOrError = llvm::MemoryBuffer::getSTDIN()) |
378 | 656 | Content = std::move(*ContentOrError); |
379 | 0 | else |
380 | 0 | return llvm::errorCodeToError(ContentOrError.getError()); |
381 | | |
382 | 656 | STDIN = getVirtualFileRef(Content->getBufferIdentifier(), |
383 | 656 | Content->getBufferSize(), 0); |
384 | 656 | FileEntry &FE = const_cast<FileEntry &>(STDIN->getFileEntry()); |
385 | 656 | FE.Content = std::move(Content); |
386 | 656 | FE.IsNamedPipe = true; |
387 | 656 | return *STDIN; |
388 | 656 | } |
389 | | |
390 | | const FileEntry *FileManager::getVirtualFile(StringRef Filename, off_t Size, |
391 | 4.30k | time_t ModificationTime) { |
392 | 4.30k | return &getVirtualFileRef(Filename, Size, ModificationTime).getFileEntry(); |
393 | 4.30k | } |
394 | | |
395 | | FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size, |
396 | 5.03k | time_t ModificationTime) { |
397 | 5.03k | ++NumFileLookups; |
398 | | |
399 | | // See if there is already an entry in the map for an existing file. |
400 | 5.03k | auto &NamedFileEnt = *SeenFileEntries.insert( |
401 | 5.03k | {Filename, std::errc::no_such_file_or_directory}).first; |
402 | 5.03k | if (NamedFileEnt.second) { |
403 | 178 | FileEntryRef::MapValue Value = *NamedFileEnt.second; |
404 | 178 | if (LLVM_LIKELY(Value.V.is<FileEntry *>())) |
405 | 178 | return FileEntryRef(NamedFileEnt); |
406 | 0 | return FileEntryRef(*Value.V.get<const FileEntryRef::MapEntry *>()); |
407 | 178 | } |
408 | | |
409 | | // We've not seen this before, or the file is cached as non-existent. |
410 | 4.85k | ++NumFileCacheMisses; |
411 | 4.85k | addAncestorsAsVirtualDirs(Filename); |
412 | 4.85k | FileEntry *UFE = nullptr; |
413 | | |
414 | | // Now that all ancestors of Filename are in the cache, the |
415 | | // following call is guaranteed to find the DirectoryEntry from the |
416 | | // cache. A virtual file can also have an empty filename, that could come |
417 | | // from a source location preprocessor directive with an empty filename as |
418 | | // an example, so we need to pretend it has a name to ensure a valid directory |
419 | | // entry can be returned. |
420 | 4.85k | auto DirInfo = expectedToOptional(getDirectoryFromFile( |
421 | 4.85k | *this, Filename.empty() ? "."1 : Filename4.85k , /*CacheFailure=*/true)); |
422 | 4.85k | assert(DirInfo && |
423 | 4.85k | "The directory of a virtual file should already be in the cache."); |
424 | | |
425 | | // Check to see if the file exists. If so, drop the virtual file |
426 | 4.85k | llvm::vfs::Status Status; |
427 | 4.85k | const char *InterndFileName = NamedFileEnt.first().data(); |
428 | 4.85k | if (!getStatValue(InterndFileName, Status, true, nullptr)) { |
429 | 2.65k | Status = llvm::vfs::Status( |
430 | 2.65k | Status.getName(), Status.getUniqueID(), |
431 | 2.65k | llvm::sys::toTimePoint(ModificationTime), |
432 | 2.65k | Status.getUser(), Status.getGroup(), Size, |
433 | 2.65k | Status.getType(), Status.getPermissions()); |
434 | | |
435 | 2.65k | auto &RealFE = UniqueRealFiles[Status.getUniqueID()]; |
436 | 2.65k | if (RealFE) { |
437 | | // If we had already opened this file, close it now so we don't |
438 | | // leak the descriptor. We're not going to use the file |
439 | | // descriptor anyway, since this is a virtual file. |
440 | 0 | if (RealFE->File) |
441 | 0 | RealFE->closeFile(); |
442 | | // If we already have an entry with this inode, return it. |
443 | | // |
444 | | // FIXME: Surely this should add a reference by the new name, and return |
445 | | // it instead... |
446 | 0 | NamedFileEnt.second = FileEntryRef::MapValue(*RealFE, *DirInfo); |
447 | 0 | return FileEntryRef(NamedFileEnt); |
448 | 0 | } |
449 | | // File exists, but no entry - create it. |
450 | 2.65k | RealFE = new (FilesAlloc.Allocate()) FileEntry(); |
451 | 2.65k | RealFE->UniqueID = Status.getUniqueID(); |
452 | 2.65k | RealFE->IsNamedPipe = |
453 | 2.65k | Status.getType() == llvm::sys::fs::file_type::fifo_file; |
454 | 2.65k | fillRealPathName(RealFE, Status.getName()); |
455 | | |
456 | 2.65k | UFE = RealFE; |
457 | 2.65k | } else { |
458 | | // File does not exist, create a virtual entry. |
459 | 2.19k | UFE = new (FilesAlloc.Allocate()) FileEntry(); |
460 | 2.19k | VirtualFileEntries.push_back(UFE); |
461 | 2.19k | } |
462 | | |
463 | 4.85k | NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo); |
464 | 4.85k | UFE->LastRef = FileEntryRef(NamedFileEnt); |
465 | 4.85k | UFE->Size = Size; |
466 | 4.85k | UFE->ModTime = ModificationTime; |
467 | 4.85k | UFE->Dir = &DirInfo->getDirEntry(); |
468 | 4.85k | UFE->UID = NextFileUID++; |
469 | 4.85k | UFE->File.reset(); |
470 | 4.85k | return FileEntryRef(NamedFileEnt); |
471 | 4.85k | } |
472 | | |
473 | 2 | OptionalFileEntryRef FileManager::getBypassFile(FileEntryRef VF) { |
474 | | // Stat of the file and return nullptr if it doesn't exist. |
475 | 2 | llvm::vfs::Status Status; |
476 | 2 | if (getStatValue(VF.getName(), Status, /*isFile=*/true, /*F=*/nullptr)) |
477 | 0 | return std::nullopt; |
478 | | |
479 | 2 | if (!SeenBypassFileEntries) |
480 | 2 | SeenBypassFileEntries = std::make_unique< |
481 | 2 | llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>(); |
482 | | |
483 | | // If we've already bypassed just use the existing one. |
484 | 2 | auto Insertion = SeenBypassFileEntries->insert( |
485 | 2 | {VF.getName(), std::errc::no_such_file_or_directory}); |
486 | 2 | if (!Insertion.second) |
487 | 0 | return FileEntryRef(*Insertion.first); |
488 | | |
489 | | // Fill in the new entry from the stat. |
490 | 2 | FileEntry *BFE = new (FilesAlloc.Allocate()) FileEntry(); |
491 | 2 | BypassFileEntries.push_back(BFE); |
492 | 2 | Insertion.first->second = FileEntryRef::MapValue(*BFE, VF.getDir()); |
493 | 2 | BFE->LastRef = FileEntryRef(*Insertion.first); |
494 | 2 | BFE->Size = Status.getSize(); |
495 | 2 | BFE->Dir = VF.getFileEntry().Dir; |
496 | 2 | BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime()); |
497 | 2 | BFE->UID = NextFileUID++; |
498 | | |
499 | | // Save the entry in the bypass table and return. |
500 | 2 | return FileEntryRef(*Insertion.first); |
501 | 2 | } |
502 | | |
503 | 1.64M | bool FileManager::FixupRelativePath(SmallVectorImpl<char> &path) const { |
504 | 1.64M | StringRef pathRef(path.data(), path.size()); |
505 | | |
506 | 1.64M | if (FileSystemOpts.WorkingDir.empty() |
507 | 1.64M | || llvm::sys::path::is_absolute(pathRef)19.3k ) |
508 | 1.64M | return false; |
509 | | |
510 | 1.54k | SmallString<128> NewPath(FileSystemOpts.WorkingDir); |
511 | 1.54k | llvm::sys::path::append(NewPath, pathRef); |
512 | 1.54k | path = NewPath; |
513 | 1.54k | return true; |
514 | 1.64M | } |
515 | | |
516 | 1.62M | bool FileManager::makeAbsolutePath(SmallVectorImpl<char> &Path) const { |
517 | 1.62M | bool Changed = FixupRelativePath(Path); |
518 | | |
519 | 1.62M | if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) { |
520 | 160k | FS->makeAbsolute(Path); |
521 | 160k | Changed = true; |
522 | 160k | } |
523 | | |
524 | 1.62M | return Changed; |
525 | 1.62M | } |
526 | | |
527 | 1.55M | void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) { |
528 | 1.55M | llvm::SmallString<128> AbsPath(FileName); |
529 | | // This is not the same as `VFS::getRealPath()`, which resolves symlinks |
530 | | // but can be very expensive on real file systems. |
531 | | // FIXME: the semantic of RealPathName is unclear, and the name might be |
532 | | // misleading. We need to clean up the interface here. |
533 | 1.55M | makeAbsolutePath(AbsPath); |
534 | 1.55M | llvm::sys::path::remove_dots(AbsPath, /*remove_dot_dot=*/true); |
535 | 1.55M | UFE->RealPathName = std::string(AbsPath.str()); |
536 | 1.55M | } |
537 | | |
538 | | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> |
539 | | FileManager::getBufferForFile(const FileEntry *Entry, bool isVolatile, |
540 | 950k | bool RequiresNullTerminator) { |
541 | | // If the content is living on the file entry, return a reference to it. |
542 | 950k | if (Entry->Content) |
543 | 656 | return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef()); |
544 | | |
545 | 949k | uint64_t FileSize = Entry->getSize(); |
546 | | // If there's a high enough chance that the file have changed since we |
547 | | // got its size, force a stat before opening it. |
548 | 949k | if (isVolatile || Entry->isNamedPipe()937k ) |
549 | 11.8k | FileSize = -1; |
550 | | |
551 | 949k | StringRef Filename = Entry->getName(); |
552 | | // If the file is already open, use the open file descriptor. |
553 | 949k | if (Entry->File) { |
554 | 678k | auto Result = Entry->File->getBuffer(Filename, FileSize, |
555 | 678k | RequiresNullTerminator, isVolatile); |
556 | 678k | Entry->closeFile(); |
557 | 678k | return Result; |
558 | 678k | } |
559 | | |
560 | | // Otherwise, open the file. |
561 | 271k | return getBufferForFileImpl(Filename, FileSize, isVolatile, |
562 | 271k | RequiresNullTerminator); |
563 | 949k | } |
564 | | |
565 | | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> |
566 | | FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize, |
567 | | bool isVolatile, |
568 | 275k | bool RequiresNullTerminator) { |
569 | 275k | if (FileSystemOpts.WorkingDir.empty()) |
570 | 275k | return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator, |
571 | 275k | isVolatile); |
572 | | |
573 | 407 | SmallString<128> FilePath(Filename); |
574 | 407 | FixupRelativePath(FilePath); |
575 | 407 | return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator, |
576 | 407 | isVolatile); |
577 | 275k | } |
578 | | |
579 | | /// getStatValue - Get the 'stat' information for the specified path, |
580 | | /// using the cache to accelerate it if possible. This returns true |
581 | | /// if the path points to a virtual file or does not exist, or returns |
582 | | /// false if it's an existent real file. If FileDescriptor is NULL, |
583 | | /// do directory look-up instead of file look-up. |
584 | | std::error_code |
585 | | FileManager::getStatValue(StringRef Path, llvm::vfs::Status &Status, |
586 | 2.79M | bool isFile, std::unique_ptr<llvm::vfs::File> *F) { |
587 | | // FIXME: FileSystemOpts shouldn't be passed in here, all paths should be |
588 | | // absolute! |
589 | 2.79M | if (FileSystemOpts.WorkingDir.empty()) |
590 | 2.77M | return FileSystemStatCache::get(Path, Status, isFile, F, |
591 | 2.77M | StatCache.get(), *FS); |
592 | | |
593 | 12.8k | SmallString<128> FilePath(Path); |
594 | 12.8k | FixupRelativePath(FilePath); |
595 | | |
596 | 12.8k | return FileSystemStatCache::get(FilePath.c_str(), Status, isFile, F, |
597 | 12.8k | StatCache.get(), *FS); |
598 | 2.79M | } |
599 | | |
600 | | std::error_code |
601 | | FileManager::getNoncachedStatValue(StringRef Path, |
602 | 5.77k | llvm::vfs::Status &Result) { |
603 | 5.77k | SmallString<128> FilePath(Path); |
604 | 5.77k | FixupRelativePath(FilePath); |
605 | | |
606 | 5.77k | llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str()); |
607 | 5.77k | if (!S) |
608 | 5.76k | return S.getError(); |
609 | 16 | Result = *S; |
610 | 16 | return std::error_code(); |
611 | 5.77k | } |
612 | | |
613 | | void FileManager::GetUniqueIDMapping( |
614 | 8.25k | SmallVectorImpl<const FileEntry *> &UIDToFiles) const { |
615 | 8.25k | UIDToFiles.clear(); |
616 | 8.25k | UIDToFiles.resize(NextFileUID); |
617 | | |
618 | | // Map file entries |
619 | 8.25k | for (llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>, |
620 | 8.25k | llvm::BumpPtrAllocator>::const_iterator |
621 | 8.25k | FE = SeenFileEntries.begin(), |
622 | 8.25k | FEEnd = SeenFileEntries.end(); |
623 | 1.00M | FE != FEEnd; ++FE1.00M ) |
624 | 1.00M | if (llvm::ErrorOr<FileEntryRef::MapValue> Entry = FE->getValue()) { |
625 | 495k | if (const auto *FE = Entry->V.dyn_cast<FileEntry *>()) |
626 | 495k | UIDToFiles[FE->getUID()] = FE; |
627 | 495k | } |
628 | | |
629 | | // Map virtual file entries |
630 | 8.25k | for (const auto &VFE : VirtualFileEntries) |
631 | 731 | UIDToFiles[VFE->getUID()] = VFE; |
632 | 8.25k | } |
633 | | |
634 | 352k | StringRef FileManager::getCanonicalName(const DirectoryEntry *Dir) { |
635 | 352k | llvm::DenseMap<const void *, llvm::StringRef>::iterator Known |
636 | 352k | = CanonicalNames.find(Dir); |
637 | 352k | if (Known != CanonicalNames.end()) |
638 | 343k | return Known->second; |
639 | | |
640 | 8.89k | StringRef CanonicalName(Dir->getName()); |
641 | | |
642 | 8.89k | SmallString<4096> CanonicalNameBuf; |
643 | 8.89k | if (!FS->getRealPath(Dir->getName(), CanonicalNameBuf)) |
644 | 8.86k | CanonicalName = CanonicalNameBuf.str().copy(CanonicalNameStorage); |
645 | | |
646 | 8.89k | CanonicalNames.insert({Dir, CanonicalName}); |
647 | 8.89k | return CanonicalName; |
648 | 352k | } |
649 | | |
650 | 3.31M | StringRef FileManager::getCanonicalName(const FileEntry *File) { |
651 | 3.31M | llvm::DenseMap<const void *, llvm::StringRef>::iterator Known |
652 | 3.31M | = CanonicalNames.find(File); |
653 | 3.31M | if (Known != CanonicalNames.end()) |
654 | 2.69M | return Known->second; |
655 | | |
656 | 623k | StringRef CanonicalName(File->getName()); |
657 | | |
658 | 623k | SmallString<4096> CanonicalNameBuf; |
659 | 623k | if (!FS->getRealPath(File->getName(), CanonicalNameBuf)) |
660 | 623k | CanonicalName = CanonicalNameBuf.str().copy(CanonicalNameStorage); |
661 | | |
662 | 623k | CanonicalNames.insert({File, CanonicalName}); |
663 | 623k | return CanonicalName; |
664 | 3.31M | } |
665 | | |
666 | 6 | void FileManager::PrintStats() const { |
667 | 6 | llvm::errs() << "\n*** File Manager Stats:\n"; |
668 | 6 | llvm::errs() << UniqueRealFiles.size() << " real files found, " |
669 | 6 | << UniqueRealDirs.size() << " real dirs found.\n"; |
670 | 6 | llvm::errs() << VirtualFileEntries.size() << " virtual files found, " |
671 | 6 | << VirtualDirectoryEntries.size() << " virtual dirs found.\n"; |
672 | 6 | llvm::errs() << NumDirLookups << " dir lookups, " |
673 | 6 | << NumDirCacheMisses << " dir cache misses.\n"; |
674 | 6 | llvm::errs() << NumFileLookups << " file lookups, " |
675 | 6 | << NumFileCacheMisses << " file cache misses.\n"; |
676 | | |
677 | | //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups; |
678 | 6 | } |