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