clang: lib/Basic/FileManager.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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/IOSandbox.h"
26#include "llvm/Support/MemoryBuffer.h"
27#include "llvm/Support/Path.h"
28#include "llvm/Support/raw_ostream.h"
29#include
30#include
31#include
32#include
33#include
34#include
35#include
36
37using namespace clang;
38
39#define DEBUG_TYPE "file-search"
40
41
42
43
44
47 : FS(std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),
48 SeenFileEntries(64), NextFileUID(0) {
49
50
51 if (!this->FS)
52 this->FS = llvm::vfs::getRealFileSystem();
53}
54
56
58 assert(statCache && "No stat cache provided?");
59 StatCache = std::move(statCache);
60}
61
63
64
65
68 bool CacheFailure) {
69 if (Filename.empty())
70 return llvm::errorCodeToError(
72
73 if (llvm::sys::path::is_separator(Filename[Filename.size() - 1]))
74 return llvm::errorCodeToError(make_error_code(std::errc::is_a_directory));
75
76 StringRef DirName = llvm::sys::path::parent_path(Filename);
77
78 if (DirName.empty())
79 DirName = ".";
80
81 return FileMgr.getDirectoryRef(DirName, CacheFailure);
82}
83
84DirectoryEntry *&FileManager::getRealDirEntry(const llvm::vfs::Status &Status) {
85 assert(Status.isDirectory() && "The directory should exist!");
86
87
88
89
90 DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()];
91
92 if (!UDE) {
93
94
95 UDE = new (DirsAlloc.Allocate()) DirectoryEntry();
96 }
97 return UDE;
98}
99
100
101
102void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
103 StringRef DirName = llvm::sys::path::parent_path(Path);
104 if (DirName.empty())
105 DirName = ".";
106
107 auto &NamedDirEnt = *SeenDirEntries.insert(
108 {DirName, std::errc::no_such_file_or_directory}).first;
109
110
111
112
113
114 if (NamedDirEnt.second)
115 return;
116
117
118 llvm::vfs::Status Status;
119 auto statError =
120 getStatValue(DirName, Status, false, nullptr );
121 if (statError) {
122
123
124 auto *UDE = new (DirsAlloc.Allocate()) DirectoryEntry();
125 NamedDirEnt.second = *UDE;
126 VirtualDirectoryEntries.push_back(UDE);
127 } else {
128
129 DirectoryEntry *&UDE = getRealDirEntry(Status);
130 NamedDirEnt.second = *UDE;
131 }
132
133
134 addAncestorsAsVirtualDirs(DirName);
135}
136
139
140
141
142 if (DirName.size() > 1 &&
143 DirName != llvm::sys::path::root_path(DirName) &&
144 llvm::sys::path::is_separator(DirName.back()))
145 DirName = DirName.drop_back();
146 std::optionalstd::string DirNameStr;
147 if (is_style_windows(llvm::sys::path::Style::native)) {
148
149
150 if (DirName.size() > 1 && DirName.back() == ':' &&
151 DirName.equals_insensitive(llvm::sys::path::root_name(DirName))) {
152 DirNameStr = DirName.str() + '.';
153 DirName = *DirNameStr;
154 }
155 }
156
157 ++NumDirLookups;
158
159
160
161 auto SeenDirInsertResult =
162 SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});
163 if (!SeenDirInsertResult.second) {
164 if (SeenDirInsertResult.first->second)
166 return llvm::errorCodeToError(SeenDirInsertResult.first->second.getError());
167 }
168
169
170 ++NumDirCacheMisses;
171 auto &NamedDirEnt = *SeenDirInsertResult.first;
172 assert(!NamedDirEnt.second && "should be newly-created");
173
174
175
176 StringRef InterndDirName = NamedDirEnt.first();
177
178
179 llvm::vfs::Status Status;
180 auto statError = getStatValue(InterndDirName, Status, false,
181 nullptr );
182 if (statError) {
183
184 if (CacheFailure)
185 NamedDirEnt.second = statError;
186 else
187 SeenDirEntries.erase(DirName);
188 return llvm::errorCodeToError(statError);
189 }
190
191
193 NamedDirEnt.second = *UDE;
194
196}
197
199 bool openFile,
200 bool CacheFailure,
201 bool IsText) {
202 ++NumFileLookups;
203
204
205 auto SeenFileInsertResult =
206 SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory});
207 if (!SeenFileInsertResult.second) {
208 if (!SeenFileInsertResult.first->second)
209 return llvm::errorCodeToError(
210 SeenFileInsertResult.first->second.getError());
211 return FileEntryRef(*SeenFileInsertResult.first);
212 }
213
214
215 ++NumFileCacheMisses;
216 auto *NamedFileEnt = &*SeenFileInsertResult.first;
217 assert(!NamedFileEnt->second && "should be newly-created");
218
219
220
221 StringRef InterndFileName = NamedFileEnt->first();
222
223
224
225
226
227
229 if (!DirInfoOrErr) {
230 std::error_code Err = errorToErrorCode(DirInfoOrErr.takeError());
231 if (CacheFailure)
232 NamedFileEnt->second = Err;
233 else
234 SeenFileEntries.erase(Filename);
235
236 return llvm::errorCodeToError(Err);
237 }
239
240
241
242
243
244 std::unique_ptrllvm::vfs::File F;
245 llvm::vfs::Status Status;
246 auto statError = getStatValue(InterndFileName, Status, true,
247 openFile ? &F : nullptr, IsText);
248 if (statError) {
249
250 if (CacheFailure)
251 NamedFileEnt->second = statError;
252 else
253 SeenFileEntries.erase(Filename);
254
255 return llvm::errorCodeToError(statError);
256 }
257
258 assert((openFile || !F) && "undesired open file");
259
260
261
262 FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
263 bool ReusingEntry = UFE != nullptr;
264 if (!UFE)
265 UFE = new (FilesAlloc.Allocate()) FileEntry();
266
267 if (!Status.ExposesExternalVFSPath || Status.getName() == Filename) {
268
270 } else {
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306 auto &Redirection =
307 *SeenFileEntries
309 .first;
311 "filename redirected to a non-canonical filename?");
313 "filename from getStatValue() refers to wrong file");
314
315
316
318 }
319
321 if (ReusingEntry) {
322 return ReturnedRef;
323 }
324
325
326 UFE->Size = Status.getSize();
327 UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
329 UFE->UID = NextFileUID++;
330 UFE->UniqueID = Status.getUniqueID();
331 UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
332 UFE->IsDeviceFile =
333 Status.getType() == llvm::sys::fs::file_type::character_file;
334 UFE->File = std::move(F);
335
336 if (UFE->File) {
337 if (auto PathName = UFE->File->getName())
338 fillRealPathName(UFE, *PathName);
339 } else if (!openFile) {
340
341 fillRealPathName(UFE, InterndFileName);
342 }
343 return ReturnedRef;
344}
345
347
348 if (STDIN)
349 return *STDIN;
350
351 auto ContentOrError = [] {
352 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
353 return llvm::MemoryBuffer::getSTDIN();
354 }();
355
356 if (!ContentOrError)
357 return llvm::errorCodeToError(ContentOrError.getError());
358
359 auto Content = std::move(*ContentOrError);
361 Content->getBufferSize(), 0);
363 FE.Content = std::move(Content);
364 FE.IsNamedPipe = true;
365 return *STDIN;
366}
367
369 FS->visit([Active](llvm::vfs::FileSystem &FileSys) {
370 if (auto *RFS = dyn_castllvm::vfs::RedirectingFileSystem(&FileSys))
371 RFS->setUsageTrackingActive(Active);
372 });
373}
374
376 time_t ModificationTime) {
377 ++NumFileLookups;
378
379
380 auto &NamedFileEnt = *SeenFileEntries.insert(
381 {Filename, std::errc::no_such_file_or_directory}).first;
382 if (NamedFileEnt.second) {
387 }
388
389
390 ++NumFileCacheMisses;
391 addAncestorsAsVirtualDirs(Filename);
393
394
395
396
397
398
399
401 *this, Filename.empty() ? "." : Filename, true));
402 assert(DirInfo &&
403 "The directory of a virtual file should already be in the cache.");
404
405
406 llvm::vfs::Status Status;
407 const char *InterndFileName = NamedFileEnt.first().data();
408 if (!getStatValue(InterndFileName, Status, true, nullptr)) {
409 Status = llvm::vfs::Status(
410 Status.getName(), Status.getUniqueID(),
411 llvm::sys::toTimePoint(ModificationTime),
412 Status.getUser(), Status.getGroup(), Size,
413 Status.getType(), Status.getPermissions());
414
415 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
416 if (RealFE) {
417
418
419
420 if (RealFE->File)
421 RealFE->closeFile();
422
423
424
425
428 }
429
430 RealFE = new (FilesAlloc.Allocate()) FileEntry();
431 RealFE->UniqueID = Status.getUniqueID();
432 RealFE->IsNamedPipe =
433 Status.getType() == llvm::sys::fs::file_type::fifo_file;
434 fillRealPathName(RealFE, Status.getName());
435
436 UFE = RealFE;
437 } else {
438
439 UFE = new (FilesAlloc.Allocate()) FileEntry();
440 VirtualFileEntries.push_back(UFE);
441 }
442
444 UFE->Size = Size;
445 UFE->ModTime = ModificationTime;
446 UFE->Dir = &DirInfo->getDirEntry();
447 UFE->UID = NextFileUID++;
448 UFE->File.reset();
450}
451
453
454 llvm::vfs::Status Status;
455 if (getStatValue(VF.getName(), Status, true, nullptr))
456 return std::nullopt;
457
458 if (!SeenBypassFileEntries)
459 SeenBypassFileEntries = std::make_unique<
460 llvm::StringMap<llvm::ErrorOrFileEntryRef::MapValue>>();
461
462
463 auto Insertion = SeenBypassFileEntries->insert(
464 {VF.getName(), std::errc::no_such_file_or_directory});
465 if (!Insertion.second)
467
468
470 BypassFileEntries.push_back(BFE);
472 BFE->Size = Status.getSize();
474 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
475 BFE->UID = NextFileUID++;
476
477
479}
480
483 StringRef pathRef(Path.data(), Path.size());
484
485 if (FileSystemOpts.WorkingDir.empty()
486 || llvm::sys::path::is_absolute(pathRef))
487 return false;
488
490 llvm::sys::path::append(NewPath, pathRef);
491 Path = NewPath;
492 return true;
493}
494
497
498 if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {
499 FS->makeAbsolute(Path);
500 Changed = true;
501 }
502
503 return Changed;
504}
505
506void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) {
508
509
510
511
513 llvm::sys::path::remove_dots(AbsPath, true);
514 UFE->RealPathName = std::string(AbsPath);
515}
516
517llvm::ErrorOr<std::unique_ptrllvm::MemoryBuffer>
519 bool RequiresNullTerminator,
520 std::optional<int64_t> MaybeLimit, bool IsText) {
522
523 if (Entry->Content)
524 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
525
526 uint64_t FileSize = Entry->getSize();
527
528 if (MaybeLimit)
529 FileSize = *MaybeLimit;
530
531
532
534 FileSize = -1;
535
536 StringRef Filename = FE.getName();
537
538 if (Entry->File) {
539 auto Result = Entry->File->getBuffer(Filename, FileSize,
540 RequiresNullTerminator, isVolatile);
543 }
544
545
546 return getBufferForFileImpl(Filename, FileSize, isVolatile,
547 RequiresNullTerminator, IsText);
548}
549
550llvm::ErrorOr<std::unique_ptrllvm::MemoryBuffer>
551FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,
552 bool isVolatile, bool RequiresNullTerminator,
553 bool IsText) const {
554 if (FileSystemOpts.WorkingDir.empty())
555 return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator,
556 isVolatile, IsText);
557
560 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
561 isVolatile, IsText);
562}
563
564
565
566
567
568
569std::error_code FileManager::getStatValue(StringRef Path,
570 llvm::vfs::Status &Status,
571 bool isFile,
572 std::unique_ptrllvm::vfs::File *F,
573 bool IsText) {
574
575
576 if (FileSystemOpts.WorkingDir.empty())
578 *FS, IsText);
579
582
584 StatCache.get(), *FS, IsText);
585}
586
587std::error_code
589 llvm::vfs::Status &Result) {
592
593 llvm::ErrorOrllvm::vfs::Status S = FS->status(FilePath.c_str());
594 if (!S)
595 return S.getError();
597 return std::error_code();
598}
599
602 UIDToFiles.clear();
603 UIDToFiles.resize(NextFileUID);
604
605 for (const auto &Entry : SeenFileEntries) {
606
607 if (!Entry.getValue() || (Entry.getValue()->V))
608 continue;
610
611
613 if (!ExistingFE || FE.getName() < ExistingFE->getName())
614 ExistingFE = FE;
615 }
616}
617
621
625
627 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =
628 CanonicalNames.find(Entry);
629 if (Known != CanonicalNames.end())
630 return Known->second;
631
632
633
634 StringRef CanonicalName(Name);
635
638 if (!FS->getRealPath(Name, RealPathBuf)) {
639 if (is_style_windows(llvm::sys::path::Style::native)) {
640
641
642 AbsPathBuf = Name;
643 if (!FS->makeAbsolute(AbsPathBuf)) {
644 if (llvm::sys::path::root_name(RealPathBuf) ==
645 llvm::sys::path::root_name(AbsPathBuf)) {
646 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
647 } else {
648
649
650
651 llvm::sys::path::remove_dots(AbsPathBuf, true);
652 CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);
653 }
654 }
655 } else {
656 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
657 }
658 }
659
660 CanonicalNames.insert({Entry, CanonicalName});
661 return CanonicalName;
662}
663
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
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
684 if (auto *T = dyn_cast_or_nullllvm::vfs::TracingFileSystem(&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
695}
static llvm::Expected< DirectoryEntryRef > getDirectoryFromFile(FileManager &FileMgr, StringRef Filename, bool CacheFailure)
Retrieve the directory that the given file name resides in.
Definition FileManager.cpp:67
Defines the clang::FileManager interface and associated types.
Defines the FileSystemStatCache interface.
A reference to a DirectoryEntry that includes the name of the directory as it was accessed by the Fil...
StringRef getName() const
const DirectoryEntry & getDirEntry() const
Cached information about one directory (either on disk or in the virtual file system).
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
const FileEntry & getFileEntry() const
StringRef getName() const
The name of this FileEntry.
DirectoryEntryRef getDir() const
Cached information about one file (either on disk or in the virtual file system).
bool isNamedPipe() const
Check whether the file is a named pipe (and thus can't be opened by the native FileManager methods).
Implements support for file system lookup, file system caching, and directory search management.
void AddStats(const FileManager &Other)
Import statistics from a child FileManager and add them to this current FileManager.
Definition FileManager.cpp:664
void trackVFSUsage(bool Active)
Enable or disable tracking of VFS usage.
Definition FileManager.cpp:368
void clearStatCache()
Removes the FileSystemStatCache object from the manager.
Definition FileManager.cpp:62
llvm::vfs::FileSystem & getVirtualFileSystem() const
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(FileEntryRef Entry, bool isVolatile=false, bool RequiresNullTerminator=true, std::optional< int64_t > MaybeLimit=std::nullopt, bool IsText=true)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
Definition FileManager.cpp:518
std::error_code getNoncachedStatValue(StringRef Path, llvm::vfs::Status &Result)
Get the 'stat' information for the given Path.
Definition FileManager.cpp:588
FileManager(const FileSystemOptions &FileSystemOpts, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS=nullptr)
Construct a file manager, optionally with a custom VFS.
Definition FileManager.cpp:45
llvm::Expected< FileEntryRef > getSTDIN()
Get the FileEntryRef for stdin, returning an error if stdin cannot be read.
Definition FileManager.cpp:346
StringRef getCanonicalName(DirectoryEntryRef Dir)
Retrieve the canonical name for a given directory.
Definition FileManager.cpp:618
void GetUniqueIDMapping(SmallVectorImpl< OptionalFileEntryRef > &UIDToFiles) const
Produce an array mapping from the unique IDs assigned to each file to the corresponding FileEntryRef.
Definition FileManager.cpp:600
bool makeAbsolutePath(SmallVectorImpl< char > &Path) const
Makes Path absolute taking into account FileSystemOptions and the working directory option.
Definition FileManager.cpp:495
llvm::Expected< DirectoryEntryRef > getDirectoryRef(StringRef DirName, bool CacheFailure=true)
Lookup, cache, and verify the specified directory (real or virtual).
Definition FileManager.cpp:138
void setStatCache(std::unique_ptr< FileSystemStatCache > statCache)
Installs the provided FileSystemStatCache object within the FileManager.
Definition FileManager.cpp:57
FileEntryRef getVirtualFileRef(StringRef Filename, off_t Size, time_t ModificationTime)
Retrieve a file entry for a "virtual" file that acts as if there were a file with the given name on d...
Definition FileManager.cpp:375
bool FixupRelativePath(SmallVectorImpl< char > &Path) const
If path is not absolute and FileSystemOptions set the working directory, the path is modified to be r...
void PrintStats() const
Definition FileManager.cpp:672
OptionalFileEntryRef getBypassFile(FileEntryRef VFE)
Retrieve a FileEntry that bypasses VFE, which is expected to be a virtual file entry,...
Definition FileManager.cpp:452
static bool fixupRelativePath(const FileSystemOptions &FileSystemOpts, SmallVectorImpl< char > &Path)
Definition FileManager.cpp:481
llvm::Expected< FileEntryRef > getFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true, bool IsText=true)
Lookup, cache, and verify the specified file (real or virtual).
Definition FileManager.cpp:198
Keeps track of options that affect how file operations are performed.
std::string WorkingDir
If set, paths are resolved as if the working directory was set to the value of WorkingDir.
static std::error_code get(StringRef Path, llvm::vfs::Status &Status, bool isFile, std::unique_ptr< llvm::vfs::File > *F, FileSystemStatCache *Cache, llvm::vfs::FileSystem &FS, bool IsText=true)
Get the 'stat' information for the specified path, using the cache to accelerate it if possible.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
std::error_code make_error_code(BuildPreambleError Error)
@ Result
The result type of a method or function.
const FunctionProtoType * T
U cast(CodeGen::Address addr)
@ Other
Other implicit parameter.
Type stored in the StringMap.