xref: /llvm-project/clang/lib/Basic/FileManager.cpp (revision dd278430a387f1dd916618c3a1b90c14c0064973)
1 //===--- FileManager.cpp - File System Probing and Caching ----------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This file implements the FileManager interface.
11 //
12 //===----------------------------------------------------------------------===//
13 //
14 // TODO: This should index all interesting directories with dirent calls.
15 //  getdirentries ?
16 //  opendir/readdir_r/closedir ?
17 //
18 //===----------------------------------------------------------------------===//
19 
20 #include "clang/Basic/FileManager.h"
21 #include "clang/Basic/FileSystemStatCache.h"
22 #include "llvm/ADT/SmallString.h"
23 #include "llvm/ADT/StringExtras.h"
24 #include "llvm/Support/MemoryBuffer.h"
25 #include "llvm/Support/raw_ostream.h"
26 #include "llvm/System/Path.h"
27 #include "llvm/Config/config.h"
28 #include <map>
29 #include <set>
30 #include <string>
31 using namespace clang;
32 
33 // FIXME: Enhance libsystem to support inode and other fields.
34 #include <sys/stat.h>
35 
36 /// NON_EXISTENT_DIR - A special value distinct from null that is used to
37 /// represent a dir name that doesn't exist on the disk.
38 #define NON_EXISTENT_DIR reinterpret_cast<DirectoryEntry*>((intptr_t)-1)
39 
40 /// NON_EXISTENT_FILE - A special value distinct from null that is used to
41 /// represent a filename that doesn't exist on the disk.
42 #define NON_EXISTENT_FILE reinterpret_cast<FileEntry*>((intptr_t)-1)
43 
44 
45 FileEntry::~FileEntry() {
46   // If this FileEntry owns an open file descriptor that never got used, close
47   // it.
48   if (FD != -1) ::close(FD);
49 }
50 
51 //===----------------------------------------------------------------------===//
52 // Windows.
53 //===----------------------------------------------------------------------===//
54 
55 #ifdef LLVM_ON_WIN32
56 
57 #define IS_DIR_SEPARATOR_CHAR(x) ((x) == '/' || (x) == '\\')
58 
59 namespace {
60   static std::string GetFullPath(const char *relPath) {
61     char *absPathStrPtr = _fullpath(NULL, relPath, 0);
62     assert(absPathStrPtr && "_fullpath() returned NULL!");
63 
64     std::string absPath(absPathStrPtr);
65 
66     free(absPathStrPtr);
67     return absPath;
68   }
69 }
70 
71 class FileManager::UniqueDirContainer {
72   /// UniqueDirs - Cache from full path to existing directories/files.
73   ///
74   llvm::StringMap<DirectoryEntry> UniqueDirs;
75 
76 public:
77   DirectoryEntry &getDirectory(const char *Name, struct stat &StatBuf) {
78     std::string FullPath(GetFullPath(Name));
79     return UniqueDirs.GetOrCreateValue(FullPath).getValue();
80   }
81 
82   size_t size() const { return UniqueDirs.size(); }
83 };
84 
85 class FileManager::UniqueFileContainer {
86   /// UniqueFiles - Cache from full path to existing directories/files.
87   ///
88   llvm::StringMap<FileEntry, llvm::BumpPtrAllocator> UniqueFiles;
89 
90 public:
91   FileEntry &getFile(const char *Name, struct stat &StatBuf) {
92     std::string FullPath(GetFullPath(Name));
93 
94     // LowercaseString because Windows filesystem is case insensitive.
95     FullPath = llvm::LowercaseString(FullPath);
96     return UniqueFiles.GetOrCreateValue(FullPath).getValue();
97   }
98 
99   size_t size() const { return UniqueFiles.size(); }
100 };
101 
102 //===----------------------------------------------------------------------===//
103 // Unix-like Systems.
104 //===----------------------------------------------------------------------===//
105 
106 #else
107 
108 #define IS_DIR_SEPARATOR_CHAR(x) ((x) == '/')
109 
110 class FileManager::UniqueDirContainer {
111   /// UniqueDirs - Cache from ID's to existing directories/files.
112   std::map<std::pair<dev_t, ino_t>, DirectoryEntry> UniqueDirs;
113 
114 public:
115   DirectoryEntry &getDirectory(const char *Name, struct stat &StatBuf) {
116     return UniqueDirs[std::make_pair(StatBuf.st_dev, StatBuf.st_ino)];
117   }
118 
119   size_t size() const { return UniqueDirs.size(); }
120 };
121 
122 class FileManager::UniqueFileContainer {
123   /// UniqueFiles - Cache from ID's to existing directories/files.
124   std::set<FileEntry> UniqueFiles;
125 
126 public:
127   FileEntry &getFile(const char *Name, struct stat &StatBuf) {
128     return
129       const_cast<FileEntry&>(
130                     *UniqueFiles.insert(FileEntry(StatBuf.st_dev,
131                                                   StatBuf.st_ino,
132                                                   StatBuf.st_mode)).first);
133   }
134 
135   size_t size() const { return UniqueFiles.size(); }
136 };
137 
138 #endif
139 
140 //===----------------------------------------------------------------------===//
141 // Common logic.
142 //===----------------------------------------------------------------------===//
143 
144 FileManager::FileManager(const FileSystemOptions &FSO)
145   : FileSystemOpts(FSO),
146     UniqueDirs(*new UniqueDirContainer()),
147     UniqueFiles(*new UniqueFileContainer()),
148     DirEntries(64), FileEntries(64), NextFileUID(0) {
149   NumDirLookups = NumFileLookups = 0;
150   NumDirCacheMisses = NumFileCacheMisses = 0;
151 }
152 
153 FileManager::~FileManager() {
154   delete &UniqueDirs;
155   delete &UniqueFiles;
156   for (unsigned i = 0, e = VirtualFileEntries.size(); i != e; ++i)
157     delete VirtualFileEntries[i];
158 }
159 
160 void FileManager::addStatCache(FileSystemStatCache *statCache,
161                                bool AtBeginning) {
162   assert(statCache && "No stat cache provided?");
163   if (AtBeginning || StatCache.get() == 0) {
164     statCache->setNextStatCache(StatCache.take());
165     StatCache.reset(statCache);
166     return;
167   }
168 
169   FileSystemStatCache *LastCache = StatCache.get();
170   while (LastCache->getNextStatCache())
171     LastCache = LastCache->getNextStatCache();
172 
173   LastCache->setNextStatCache(statCache);
174 }
175 
176 void FileManager::removeStatCache(FileSystemStatCache *statCache) {
177   if (!statCache)
178     return;
179 
180   if (StatCache.get() == statCache) {
181     // This is the first stat cache.
182     StatCache.reset(StatCache->takeNextStatCache());
183     return;
184   }
185 
186   // Find the stat cache in the list.
187   FileSystemStatCache *PrevCache = StatCache.get();
188   while (PrevCache && PrevCache->getNextStatCache() != statCache)
189     PrevCache = PrevCache->getNextStatCache();
190 
191   assert(PrevCache && "Stat cache not found for removal");
192   PrevCache->setNextStatCache(statCache->getNextStatCache());
193 }
194 
195 /// \brief Retrieve the directory that the given file name resides in.
196 static const DirectoryEntry *getDirectoryFromFile(FileManager &FileMgr,
197                                                   llvm::StringRef Filename) {
198   // Figure out what directory it is in.   If the string contains a / in it,
199   // strip off everything after it.
200   // FIXME: this logic should be in sys::Path.
201   size_t SlashPos = Filename.size();
202   while (SlashPos != 0 && !IS_DIR_SEPARATOR_CHAR(Filename[SlashPos-1]))
203     --SlashPos;
204 
205   // Use the current directory if file has no path component.
206   if (SlashPos == 0)
207     return FileMgr.getDirectory(".");
208 
209   if (SlashPos == Filename.size()-1)
210     return 0;       // If filename ends with a /, it's a directory.
211 
212   // Ignore repeated //'s.
213   while (SlashPos != 0 && IS_DIR_SEPARATOR_CHAR(Filename[SlashPos-1]))
214     --SlashPos;
215 
216   return FileMgr.getDirectory(Filename.substr(0, SlashPos));
217 }
218 
219 /// getDirectory - Lookup, cache, and verify the specified directory.  This
220 /// returns null if the directory doesn't exist.
221 ///
222 const DirectoryEntry *FileManager::getDirectory(llvm::StringRef Filename) {
223   // stat doesn't like trailing separators (at least on Windows).
224   if (Filename.size() > 1 && IS_DIR_SEPARATOR_CHAR(Filename.back()))
225     Filename = Filename.substr(0, Filename.size()-1);
226 
227   ++NumDirLookups;
228   llvm::StringMapEntry<DirectoryEntry *> &NamedDirEnt =
229     DirEntries.GetOrCreateValue(Filename);
230 
231   // See if there is already an entry in the map.
232   if (NamedDirEnt.getValue())
233     return NamedDirEnt.getValue() == NON_EXISTENT_DIR
234               ? 0 : NamedDirEnt.getValue();
235 
236   ++NumDirCacheMisses;
237 
238   // By default, initialize it to invalid.
239   NamedDirEnt.setValue(NON_EXISTENT_DIR);
240 
241   // Get the null-terminated directory name as stored as the key of the
242   // DirEntries map.
243   const char *InterndDirName = NamedDirEnt.getKeyData();
244 
245   // Check to see if the directory exists.
246   struct stat StatBuf;
247   if (getStatValue(InterndDirName, StatBuf, 0/*directory lookup*/))
248     return 0;
249 
250   // It exists.  See if we have already opened a directory with the same inode.
251   // This occurs when one dir is symlinked to another, for example.
252   DirectoryEntry &UDE = UniqueDirs.getDirectory(InterndDirName, StatBuf);
253 
254   NamedDirEnt.setValue(&UDE);
255   if (UDE.getName()) // Already have an entry with this inode, return it.
256     return &UDE;
257 
258   // Otherwise, we don't have this directory yet, add it.  We use the string
259   // key from the DirEntries map as the string.
260   UDE.Name  = InterndDirName;
261   return &UDE;
262 }
263 
264 /// getFile - Lookup, cache, and verify the specified file.  This returns null
265 /// if the file doesn't exist.
266 ///
267 const FileEntry *FileManager::getFile(llvm::StringRef Filename) {
268   ++NumFileLookups;
269 
270   // See if there is already an entry in the map.
271   llvm::StringMapEntry<FileEntry *> &NamedFileEnt =
272     FileEntries.GetOrCreateValue(Filename);
273 
274   // See if there is already an entry in the map.
275   if (NamedFileEnt.getValue())
276     return NamedFileEnt.getValue() == NON_EXISTENT_FILE
277                  ? 0 : NamedFileEnt.getValue();
278 
279   ++NumFileCacheMisses;
280 
281   // By default, initialize it to invalid.
282   NamedFileEnt.setValue(NON_EXISTENT_FILE);
283 
284 
285   // Get the null-terminated file name as stored as the key of the
286   // FileEntries map.
287   const char *InterndFileName = NamedFileEnt.getKeyData();
288 
289 
290   // Look up the directory for the file.  When looking up something like
291   // sys/foo.h we'll discover all of the search directories that have a 'sys'
292   // subdirectory.  This will let us avoid having to waste time on known-to-fail
293   // searches when we go to find sys/bar.h, because all the search directories
294   // without a 'sys' subdir will get a cached failure result.
295   const DirectoryEntry *DirInfo = getDirectoryFromFile(*this, Filename);
296   if (DirInfo == 0)  // Directory doesn't exist, file can't exist.
297     return 0;
298 
299   // FIXME: Use the directory info to prune this, before doing the stat syscall.
300   // FIXME: This will reduce the # syscalls.
301 
302   // Nope, there isn't.  Check to see if the file exists.
303   int FileDescriptor = -1;
304   struct stat StatBuf;
305   if (getStatValue(InterndFileName, StatBuf, &FileDescriptor))
306     return 0;
307 
308   // It exists.  See if we have already opened a file with the same inode.
309   // This occurs when one dir is symlinked to another, for example.
310   FileEntry &UFE = UniqueFiles.getFile(InterndFileName, StatBuf);
311 
312   NamedFileEnt.setValue(&UFE);
313   if (UFE.getName()) { // Already have an entry with this inode, return it.
314     // If the stat process opened the file, close it to avoid a FD leak.
315     if (FileDescriptor != -1)
316       close(FileDescriptor);
317 
318     return &UFE;
319   }
320 
321   // Otherwise, we don't have this directory yet, add it.
322   // FIXME: Change the name to be a char* that points back to the 'FileEntries'
323   // key.
324   UFE.Name    = InterndFileName;
325   UFE.Size    = StatBuf.st_size;
326   UFE.ModTime = StatBuf.st_mtime;
327   UFE.Dir     = DirInfo;
328   UFE.UID     = NextFileUID++;
329   UFE.FD      = FileDescriptor;
330   return &UFE;
331 }
332 
333 const FileEntry *
334 FileManager::getVirtualFile(llvm::StringRef Filename, off_t Size,
335                             time_t ModificationTime) {
336   ++NumFileLookups;
337 
338   // See if there is already an entry in the map.
339   llvm::StringMapEntry<FileEntry *> &NamedFileEnt =
340     FileEntries.GetOrCreateValue(Filename);
341 
342   // See if there is already an entry in the map.
343   if (NamedFileEnt.getValue())
344     return NamedFileEnt.getValue() == NON_EXISTENT_FILE
345                  ? 0 : NamedFileEnt.getValue();
346 
347   ++NumFileCacheMisses;
348 
349   // By default, initialize it to invalid.
350   NamedFileEnt.setValue(NON_EXISTENT_FILE);
351 
352   const DirectoryEntry *DirInfo = getDirectoryFromFile(*this, Filename);
353   if (DirInfo == 0)  // Directory doesn't exist, file can't exist.
354     return 0;
355 
356   FileEntry *UFE = new FileEntry();
357   VirtualFileEntries.push_back(UFE);
358   NamedFileEnt.setValue(UFE);
359 
360   // Get the null-terminated file name as stored as the key of the
361   // FileEntries map.
362   const char *InterndFileName = NamedFileEnt.getKeyData();
363 
364   UFE->Name    = InterndFileName;
365   UFE->Size    = Size;
366   UFE->ModTime = ModificationTime;
367   UFE->Dir     = DirInfo;
368   UFE->UID     = NextFileUID++;
369 
370   // If this virtual file resolves to a file, also map that file to the
371   // newly-created file entry.
372   int FileDescriptor = -1;
373   struct stat StatBuf;
374   if (getStatValue(InterndFileName, StatBuf, &FileDescriptor))
375     return UFE;
376 
377   UFE->FD = FileDescriptor;
378   llvm::sys::Path FilePath(UFE->Name);
379   FilePath.makeAbsolute();
380   FileEntries[FilePath.str()] = UFE;
381   return UFE;
382 }
383 
384 void FileManager::FixupRelativePath(llvm::sys::Path &path,
385                                     const FileSystemOptions &FSOpts) {
386   if (FSOpts.WorkingDir.empty() || path.isAbsolute()) return;
387 
388   llvm::sys::Path NewPath(FSOpts.WorkingDir);
389   NewPath.appendComponent(path.str());
390   path = NewPath;
391 }
392 
393 llvm::MemoryBuffer *FileManager::
394 getBufferForFile(const FileEntry *Entry, std::string *ErrorStr) {
395   llvm::StringRef Filename = Entry->getName();
396   if (FileSystemOpts.WorkingDir.empty())
397     return llvm::MemoryBuffer::getFile(Filename, ErrorStr, Entry->getSize());
398 
399   llvm::sys::Path FilePath(Filename);
400   FixupRelativePath(FilePath, FileSystemOpts);
401   return llvm::MemoryBuffer::getFile(FilePath.c_str(), ErrorStr,
402                                      Entry->getSize());
403 }
404 
405 llvm::MemoryBuffer *FileManager::
406 getBufferForFile(llvm::StringRef Filename, std::string *ErrorStr) {
407   if (FileSystemOpts.WorkingDir.empty())
408     return llvm::MemoryBuffer::getFile(Filename, ErrorStr);
409 
410   llvm::sys::Path FilePath(Filename);
411   FixupRelativePath(FilePath, FileSystemOpts);
412   return llvm::MemoryBuffer::getFile(FilePath.c_str(), ErrorStr);
413 }
414 
415 /// getStatValue - Get the 'stat' information for the specified path, using the
416 /// cache to accellerate it if possible.  This returns true if the path does not
417 /// exist or false if it exists.
418 ///
419 /// The isForDir member indicates whether this is a directory lookup or not.
420 /// This will return failure if the lookup isn't the expected kind.
421 bool FileManager::getStatValue(const char *Path, struct stat &StatBuf,
422                                int *FileDescriptor) {
423   // FIXME: FileSystemOpts shouldn't be passed in here, all paths should be
424   // absolute!
425   if (FileSystemOpts.WorkingDir.empty())
426     return FileSystemStatCache::get(Path, StatBuf, FileDescriptor,
427                                     StatCache.get());
428 
429   llvm::sys::Path FilePath(Path);
430   FixupRelativePath(FilePath, FileSystemOpts);
431 
432   return FileSystemStatCache::get(FilePath.c_str(), StatBuf, FileDescriptor,
433                                   StatCache.get());
434 }
435 
436 
437 
438 void FileManager::PrintStats() const {
439   llvm::errs() << "\n*** File Manager Stats:\n";
440   llvm::errs() << UniqueFiles.size() << " files found, "
441                << UniqueDirs.size() << " dirs found.\n";
442   llvm::errs() << NumDirLookups << " dir lookups, "
443                << NumDirCacheMisses << " dir cache misses.\n";
444   llvm::errs() << NumFileLookups << " file lookups, "
445                << NumFileCacheMisses << " file cache misses.\n";
446 
447   //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups;
448 }
449 
450