xref: /llvm-project/clang/lib/Basic/FileManager.cpp (revision 26b5c190f86994c99e187fa2ee6c73d981dfd1ea)
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/FileSystemOptions.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 #if defined(_MSC_VER)
37 #define S_ISDIR(s) (_S_IFDIR & s)
38 #endif
39 
40 /// NON_EXISTENT_DIR - A special value distinct from null that is used to
41 /// represent a dir name that doesn't exist on the disk.
42 #define NON_EXISTENT_DIR reinterpret_cast<DirectoryEntry*>((intptr_t)-1)
43 
44 //===----------------------------------------------------------------------===//
45 // Windows.
46 //===----------------------------------------------------------------------===//
47 
48 #ifdef LLVM_ON_WIN32
49 
50 #define IS_DIR_SEPARATOR_CHAR(x) ((x) == '/' || (x) == '\\')
51 
52 namespace {
53   static std::string GetFullPath(const char *relPath) {
54     char *absPathStrPtr = _fullpath(NULL, relPath, 0);
55     assert(absPathStrPtr && "_fullpath() returned NULL!");
56 
57     std::string absPath(absPathStrPtr);
58 
59     free(absPathStrPtr);
60     return absPath;
61   }
62 }
63 
64 class FileManager::UniqueDirContainer {
65   /// UniqueDirs - Cache from full path to existing directories/files.
66   ///
67   llvm::StringMap<DirectoryEntry> UniqueDirs;
68 
69 public:
70   DirectoryEntry &getDirectory(const char *Name, struct stat &StatBuf) {
71     std::string FullPath(GetFullPath(Name));
72     return UniqueDirs.GetOrCreateValue(
73                               FullPath.c_str(),
74                               FullPath.c_str() + FullPath.size()
75                                                                 ).getValue();
76   }
77 
78   size_t size() { return UniqueDirs.size(); }
79 };
80 
81 class FileManager::UniqueFileContainer {
82   /// UniqueFiles - Cache from full path to existing directories/files.
83   ///
84   llvm::StringMap<FileEntry, llvm::BumpPtrAllocator> UniqueFiles;
85 
86 public:
87   FileEntry &getFile(const char *Name, struct stat &StatBuf) {
88     std::string FullPath(GetFullPath(Name));
89 
90     // LowercaseString because Windows filesystem is case insensitive.
91     FullPath = llvm::LowercaseString(FullPath);
92     return UniqueFiles.GetOrCreateValue(
93                                FullPath.c_str(),
94                                FullPath.c_str() + FullPath.size()
95                                                                  ).getValue();
96   }
97 
98   size_t size() { return UniqueFiles.size(); }
99 };
100 
101 //===----------------------------------------------------------------------===//
102 // Unix-like Systems.
103 //===----------------------------------------------------------------------===//
104 
105 #else
106 
107 #define IS_DIR_SEPARATOR_CHAR(x) ((x) == '/')
108 
109 class FileManager::UniqueDirContainer {
110   /// UniqueDirs - Cache from ID's to existing directories/files.
111   ///
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() { return UniqueDirs.size(); }
120 };
121 
122 class FileManager::UniqueFileContainer {
123   /// UniqueFiles - Cache from ID's to existing directories/files.
124   ///
125   std::set<FileEntry> UniqueFiles;
126 
127 public:
128   FileEntry &getFile(const char *Name, struct stat &StatBuf) {
129     return
130       const_cast<FileEntry&>(
131                     *UniqueFiles.insert(FileEntry(StatBuf.st_dev,
132                                                   StatBuf.st_ino,
133                                                   StatBuf.st_mode)).first);
134   }
135 
136   size_t size() { return UniqueFiles.size(); }
137 };
138 
139 #endif
140 
141 //===----------------------------------------------------------------------===//
142 // Common logic.
143 //===----------------------------------------------------------------------===//
144 
145 FileManager::FileManager(const FileSystemOptions &FSO)
146   : FileSystemOpts(FSO),
147     UniqueDirs(*new UniqueDirContainer),
148     UniqueFiles(*new UniqueFileContainer),
149     DirEntries(64), FileEntries(64), NextFileUID(0) {
150   NumDirLookups = NumFileLookups = 0;
151   NumDirCacheMisses = NumFileCacheMisses = 0;
152 }
153 
154 FileManager::~FileManager() {
155   delete &UniqueDirs;
156   delete &UniqueFiles;
157   for (llvm::SmallVectorImpl<FileEntry *>::iterator
158          V = VirtualFileEntries.begin(),
159          VEnd = VirtualFileEntries.end();
160        V != VEnd;
161        ++V)
162     delete *V;
163 }
164 
165 void FileManager::addStatCache(StatSysCallCache *statCache, bool AtBeginning) {
166   assert(statCache && "No stat cache provided?");
167   if (AtBeginning || StatCache.get() == 0) {
168     statCache->setNextStatCache(StatCache.take());
169     StatCache.reset(statCache);
170     return;
171   }
172 
173   StatSysCallCache *LastCache = StatCache.get();
174   while (LastCache->getNextStatCache())
175     LastCache = LastCache->getNextStatCache();
176 
177   LastCache->setNextStatCache(statCache);
178 }
179 
180 void FileManager::removeStatCache(StatSysCallCache *statCache) {
181   if (!statCache)
182     return;
183 
184   if (StatCache.get() == statCache) {
185     // This is the first stat cache.
186     StatCache.reset(StatCache->takeNextStatCache());
187     return;
188   }
189 
190   // Find the stat cache in the list.
191   StatSysCallCache *PrevCache = StatCache.get();
192   while (PrevCache && PrevCache->getNextStatCache() != statCache)
193     PrevCache = PrevCache->getNextStatCache();
194   if (PrevCache)
195     PrevCache->setNextStatCache(statCache->getNextStatCache());
196   else
197     assert(false && "Stat cache not found for removal");
198 }
199 
200 /// \brief Retrieve the directory that the given file name resides in.
201 static const DirectoryEntry *getDirectoryFromFile(FileManager &FileMgr,
202                                                   llvm::StringRef Filename) {
203   // Figure out what directory it is in.   If the string contains a / in it,
204   // strip off everything after it.
205   // FIXME: this logic should be in sys::Path.
206   size_t SlashPos = Filename.size();
207   while (SlashPos != 0 && !IS_DIR_SEPARATOR_CHAR(Filename[SlashPos-1]))
208     --SlashPos;
209 
210   // Use the current directory if file has no path component.
211   if (SlashPos == 0)
212     return FileMgr.getDirectory(".");
213 
214   if (SlashPos == Filename.size()-1)
215     return 0;       // If filename ends with a /, it's a directory.
216 
217   // Ignore repeated //'s.
218   while (SlashPos != 0 && IS_DIR_SEPARATOR_CHAR(Filename[SlashPos-1]))
219     --SlashPos;
220 
221   return FileMgr.getDirectory(Filename.substr(0, SlashPos));
222 }
223 
224 /// getDirectory - Lookup, cache, and verify the specified directory.  This
225 /// returns null if the directory doesn't exist.
226 ///
227 const DirectoryEntry *FileManager::getDirectory(llvm::StringRef Filename) {
228   // stat doesn't like trailing separators (at least on Windows).
229   if (Filename.size() > 1 && IS_DIR_SEPARATOR_CHAR(Filename.back()))
230     Filename = Filename.substr(0, Filename.size()-1);
231 
232   ++NumDirLookups;
233   llvm::StringMapEntry<DirectoryEntry *> &NamedDirEnt =
234     DirEntries.GetOrCreateValue(Filename);
235 
236   // See if there is already an entry in the map.
237   if (NamedDirEnt.getValue())
238     return NamedDirEnt.getValue() == NON_EXISTENT_DIR
239               ? 0 : NamedDirEnt.getValue();
240 
241   ++NumDirCacheMisses;
242 
243   // By default, initialize it to invalid.
244   NamedDirEnt.setValue(NON_EXISTENT_DIR);
245 
246   // Get the null-terminated directory name as stored as the key of the
247   // DirEntries map.
248   const char *InterndDirName = NamedDirEnt.getKeyData();
249 
250   // Check to see if the directory exists.
251   struct stat StatBuf;
252   if (stat_cached(InterndDirName, &StatBuf) ||   // Error stat'ing.
253       !S_ISDIR(StatBuf.st_mode))          // Not a directory?
254     return 0;
255 
256   // It exists.  See if we have already opened a directory with the same inode.
257   // This occurs when one dir is symlinked to another, for example.
258   DirectoryEntry &UDE = UniqueDirs.getDirectory(InterndDirName, StatBuf);
259 
260   NamedDirEnt.setValue(&UDE);
261   if (UDE.getName()) // Already have an entry with this inode, return it.
262     return &UDE;
263 
264   // Otherwise, we don't have this directory yet, add it.  We use the string
265   // key from the DirEntries map as the string.
266   UDE.Name  = InterndDirName;
267   return &UDE;
268 }
269 
270 /// NON_EXISTENT_FILE - A special value distinct from null that is used to
271 /// represent a filename that doesn't exist on the disk.
272 #define NON_EXISTENT_FILE reinterpret_cast<FileEntry*>((intptr_t)-1)
273 
274 /// getFile - Lookup, cache, and verify the specified file.  This returns null
275 /// if the file doesn't exist.
276 ///
277 const FileEntry *FileManager::getFile(llvm::StringRef Filename) {
278   ++NumFileLookups;
279 
280   // See if there is already an entry in the map.
281   llvm::StringMapEntry<FileEntry *> &NamedFileEnt =
282     FileEntries.GetOrCreateValue(Filename);
283 
284   // See if there is already an entry in the map.
285   if (NamedFileEnt.getValue())
286     return NamedFileEnt.getValue() == NON_EXISTENT_FILE
287                  ? 0 : NamedFileEnt.getValue();
288 
289   ++NumFileCacheMisses;
290 
291   // By default, initialize it to invalid.
292   NamedFileEnt.setValue(NON_EXISTENT_FILE);
293 
294 
295   // Get the null-terminated file name as stored as the key of the
296   // FileEntries map.
297   const char *InterndFileName = NamedFileEnt.getKeyData();
298 
299   const DirectoryEntry *DirInfo = getDirectoryFromFile(*this, Filename);
300   if (DirInfo == 0)  // Directory doesn't exist, file can't exist.
301     return 0;
302 
303   // FIXME: Use the directory info to prune this, before doing the stat syscall.
304   // FIXME: This will reduce the # syscalls.
305 
306   // Nope, there isn't.  Check to see if the file exists.
307   struct stat StatBuf;
308   //llvm::errs() << "STATING: " << Filename;
309   if (stat_cached(InterndFileName, &StatBuf) ||   // Error stat'ing.
310       S_ISDIR(StatBuf.st_mode)) {                 // A directory?
311     // If this file doesn't exist, we leave a null in FileEntries for this path.
312     //llvm::errs() << ": Not existing\n";
313     return 0;
314   }
315   //llvm::errs() << ": exists\n";
316 
317   // It exists.  See if we have already opened a file with the same inode.
318   // This occurs when one dir is symlinked to another, for example.
319   FileEntry &UFE = UniqueFiles.getFile(InterndFileName, StatBuf);
320 
321   NamedFileEnt.setValue(&UFE);
322   if (UFE.getName())  // Already have an entry with this inode, return it.
323     return &UFE;
324 
325   // Otherwise, we don't have this directory yet, add it.
326   // FIXME: Change the name to be a char* that points back to the 'FileEntries'
327   // key.
328   UFE.Name    = InterndFileName;
329   UFE.Size    = StatBuf.st_size;
330   UFE.ModTime = StatBuf.st_mtime;
331   UFE.Dir     = DirInfo;
332   UFE.UID     = NextFileUID++;
333   return &UFE;
334 }
335 
336 const FileEntry *
337 FileManager::getVirtualFile(llvm::StringRef Filename, off_t Size,
338                             time_t ModificationTime) {
339   ++NumFileLookups;
340 
341   // See if there is already an entry in the map.
342   llvm::StringMapEntry<FileEntry *> &NamedFileEnt =
343     FileEntries.GetOrCreateValue(Filename);
344 
345   // See if there is already an entry in the map.
346   if (NamedFileEnt.getValue())
347     return NamedFileEnt.getValue() == NON_EXISTENT_FILE
348                  ? 0 : NamedFileEnt.getValue();
349 
350   ++NumFileCacheMisses;
351 
352   // By default, initialize it to invalid.
353   NamedFileEnt.setValue(NON_EXISTENT_FILE);
354 
355   const DirectoryEntry *DirInfo = getDirectoryFromFile(*this, Filename);
356   if (DirInfo == 0)  // Directory doesn't exist, file can't exist.
357     return 0;
358 
359   FileEntry *UFE = new FileEntry();
360   VirtualFileEntries.push_back(UFE);
361   NamedFileEnt.setValue(UFE);
362 
363   UFE->Name    = NamedFileEnt.getKeyData();
364   UFE->Size    = Size;
365   UFE->ModTime = ModificationTime;
366   UFE->Dir     = DirInfo;
367   UFE->UID     = NextFileUID++;
368 
369   // If this virtual file resolves to a file, also map that file to the
370   // newly-created file entry.
371   const char *InterndFileName = NamedFileEnt.getKeyData();
372   struct stat StatBuf;
373   if (!stat_cached(InterndFileName, &StatBuf) &&
374       !S_ISDIR(StatBuf.st_mode)) {
375     llvm::sys::Path FilePath(InterndFileName);
376     FilePath.makeAbsolute();
377     FileEntries[FilePath.str()] = UFE;
378   }
379 
380   return UFE;
381 }
382 
383 void FileManager::FixupRelativePath(llvm::sys::Path &path,
384                                     const FileSystemOptions &FSOpts) {
385   if (FSOpts.WorkingDir.empty() || path.isAbsolute()) return;
386 
387   llvm::sys::Path NewPath(FSOpts.WorkingDir);
388   NewPath.appendComponent(path.str());
389   path = NewPath;
390 }
391 
392 llvm::MemoryBuffer *FileManager::
393 getBufferForFile(const FileEntry *Entry, std::string *ErrorStr) {
394   llvm::StringRef Filename = Entry->getName();
395   if (FileSystemOpts.WorkingDir.empty())
396     return llvm::MemoryBuffer::getFile(Filename, ErrorStr);
397 
398   llvm::sys::Path FilePath(Filename);
399   FixupRelativePath(FilePath, FileSystemOpts);
400   return llvm::MemoryBuffer::getFile(FilePath.c_str(), ErrorStr);
401 }
402 
403 llvm::MemoryBuffer *FileManager::
404 getBufferForFile(llvm::StringRef Filename, std::string *ErrorStr) {
405   if (FileSystemOpts.WorkingDir.empty())
406     return llvm::MemoryBuffer::getFile(Filename, ErrorStr);
407 
408   llvm::sys::Path FilePath(Filename);
409   FixupRelativePath(FilePath, FileSystemOpts);
410   return llvm::MemoryBuffer::getFile(FilePath.c_str(), ErrorStr);
411 }
412 
413 int FileManager::stat_cached(const char *path, struct stat *buf) {
414   if (FileSystemOpts.WorkingDir.empty())
415     return StatCache.get() ? StatCache->stat(path, buf) : stat(path, buf);
416 
417   llvm::sys::Path FilePath(path);
418   FixupRelativePath(FilePath, FileSystemOpts);
419 
420   return StatCache.get() ? StatCache->stat(FilePath.c_str(), buf)
421                          : stat(FilePath.c_str(), buf);
422 }
423 
424 void FileManager::PrintStats() const {
425   llvm::errs() << "\n*** File Manager Stats:\n";
426   llvm::errs() << UniqueFiles.size() << " files found, "
427                << UniqueDirs.size() << " dirs found.\n";
428   llvm::errs() << NumDirLookups << " dir lookups, "
429                << NumDirCacheMisses << " dir cache misses.\n";
430   llvm::errs() << NumFileLookups << " file lookups, "
431                << NumFileCacheMisses << " file cache misses.\n";
432 
433   //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups;
434 }
435 
436 int MemorizeStatCalls::stat(const char *path, struct stat *buf) {
437   int result = StatSysCallCache::stat(path, buf);
438 
439   // Do not cache failed stats, it is easy to construct common inconsistent
440   // situations if we do, and they are not important for PCH performance (which
441   // currently only needs the stats to construct the initial FileManager
442   // entries).
443   if (result != 0)
444     return result;
445 
446   // Cache file 'stat' results and directories with absolutely paths.
447   if (!S_ISDIR(buf->st_mode) || llvm::sys::Path(path).isAbsolute())
448     StatCalls[path] = StatResult(result, *buf);
449 
450   return result;
451 }
452