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