xref: /llvm-project/clang/lib/Basic/FileManager.cpp (revision 3cf715d22ccc0012243ecf131b87858dbd26d55e)
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()
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 (llvm::SmallVectorImpl<FileEntry *>::iterator
157          V = VirtualFileEntries.begin(),
158          VEnd = VirtualFileEntries.end();
159        V != VEnd;
160        ++V)
161     delete *V;
162 }
163 
164 void FileManager::addStatCache(StatSysCallCache *statCache, bool AtBeginning) {
165   assert(statCache && "No stat cache provided?");
166   if (AtBeginning || StatCache.get() == 0) {
167     statCache->setNextStatCache(StatCache.take());
168     StatCache.reset(statCache);
169     return;
170   }
171 
172   StatSysCallCache *LastCache = StatCache.get();
173   while (LastCache->getNextStatCache())
174     LastCache = LastCache->getNextStatCache();
175 
176   LastCache->setNextStatCache(statCache);
177 }
178 
179 void FileManager::removeStatCache(StatSysCallCache *statCache) {
180   if (!statCache)
181     return;
182 
183   if (StatCache.get() == statCache) {
184     // This is the first stat cache.
185     StatCache.reset(StatCache->takeNextStatCache());
186     return;
187   }
188 
189   // Find the stat cache in the list.
190   StatSysCallCache *PrevCache = StatCache.get();
191   while (PrevCache && PrevCache->getNextStatCache() != statCache)
192     PrevCache = PrevCache->getNextStatCache();
193   if (PrevCache)
194     PrevCache->setNextStatCache(statCache->getNextStatCache());
195   else
196     assert(false && "Stat cache not found for removal");
197 }
198 
199 /// \brief Retrieve the directory that the given file name resides in.
200 static const DirectoryEntry *getDirectoryFromFile(FileManager &FileMgr,
201                                                   llvm::StringRef Filename,
202                                       const FileSystemOptions &FileSystemOpts) {
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(".", FileSystemOpts);
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), FileSystemOpts);
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                                       const FileSystemOptions &FileSystemOpts) {
229   // stat doesn't like trailing separators (at least on Windows).
230   if (Filename.size() > 1 && IS_DIR_SEPARATOR_CHAR(Filename.back()))
231     Filename = Filename.substr(0, Filename.size()-1);
232 
233   ++NumDirLookups;
234   llvm::StringMapEntry<DirectoryEntry *> &NamedDirEnt =
235     DirEntries.GetOrCreateValue(Filename);
236 
237   // See if there is already an entry in the map.
238   if (NamedDirEnt.getValue())
239     return NamedDirEnt.getValue() == NON_EXISTENT_DIR
240               ? 0 : NamedDirEnt.getValue();
241 
242   ++NumDirCacheMisses;
243 
244   // By default, initialize it to invalid.
245   NamedDirEnt.setValue(NON_EXISTENT_DIR);
246 
247   // Get the null-terminated directory name as stored as the key of the
248   // DirEntries map.
249   const char *InterndDirName = NamedDirEnt.getKeyData();
250 
251   // Check to see if the directory exists.
252   struct stat StatBuf;
253   if (stat_cached(InterndDirName, &StatBuf, FileSystemOpts) ||   // Error stat'ing.
254       !S_ISDIR(StatBuf.st_mode))          // Not a directory?
255     return 0;
256 
257   // It exists.  See if we have already opened a directory with the same inode.
258   // This occurs when one dir is symlinked to another, for example.
259   DirectoryEntry &UDE = UniqueDirs.getDirectory(InterndDirName, StatBuf);
260 
261   NamedDirEnt.setValue(&UDE);
262   if (UDE.getName()) // Already have an entry with this inode, return it.
263     return &UDE;
264 
265   // Otherwise, we don't have this directory yet, add it.  We use the string
266   // key from the DirEntries map as the string.
267   UDE.Name  = InterndDirName;
268   return &UDE;
269 }
270 
271 /// NON_EXISTENT_FILE - A special value distinct from null that is used to
272 /// represent a filename that doesn't exist on the disk.
273 #define NON_EXISTENT_FILE reinterpret_cast<FileEntry*>((intptr_t)-1)
274 
275 /// getFile - Lookup, cache, and verify the specified file.  This returns null
276 /// if the file doesn't exist.
277 ///
278 const FileEntry *FileManager::getFile(llvm::StringRef Filename,
279                                       const FileSystemOptions &FileSystemOpts) {
280   ++NumFileLookups;
281 
282   // See if there is already an entry in the map.
283   llvm::StringMapEntry<FileEntry *> &NamedFileEnt =
284     FileEntries.GetOrCreateValue(Filename);
285 
286   // See if there is already an entry in the map.
287   if (NamedFileEnt.getValue())
288     return NamedFileEnt.getValue() == NON_EXISTENT_FILE
289                  ? 0 : NamedFileEnt.getValue();
290 
291   ++NumFileCacheMisses;
292 
293   // By default, initialize it to invalid.
294   NamedFileEnt.setValue(NON_EXISTENT_FILE);
295 
296 
297   // Get the null-terminated file name as stored as the key of the
298   // FileEntries map.
299   const char *InterndFileName = NamedFileEnt.getKeyData();
300 
301   const DirectoryEntry *DirInfo
302     = getDirectoryFromFile(*this, Filename, FileSystemOpts);
303   if (DirInfo == 0)  // Directory doesn't exist, file can't exist.
304     return 0;
305 
306   // FIXME: Use the directory info to prune this, before doing the stat syscall.
307   // FIXME: This will reduce the # syscalls.
308 
309   // Nope, there isn't.  Check to see if the file exists.
310   struct stat StatBuf;
311   //llvm::errs() << "STATING: " << Filename;
312   if (stat_cached(InterndFileName, &StatBuf, FileSystemOpts) ||   // Error stat'ing.
313         S_ISDIR(StatBuf.st_mode)) {           // A directory?
314     // If this file doesn't exist, we leave a null in FileEntries for this path.
315     //llvm::errs() << ": Not existing\n";
316     return 0;
317   }
318   //llvm::errs() << ": exists\n";
319 
320   // It exists.  See if we have already opened a file with the same inode.
321   // This occurs when one dir is symlinked to another, for example.
322   FileEntry &UFE = UniqueFiles.getFile(InterndFileName, StatBuf);
323 
324   NamedFileEnt.setValue(&UFE);
325   if (UFE.getName())  // Already have an entry with this inode, return it.
326     return &UFE;
327 
328   // Otherwise, we don't have this directory yet, add it.
329   // FIXME: Change the name to be a char* that points back to the 'FileEntries'
330   // key.
331   UFE.Name    = InterndFileName;
332   UFE.Size    = StatBuf.st_size;
333   UFE.ModTime = StatBuf.st_mtime;
334   UFE.Dir     = DirInfo;
335   UFE.UID     = NextFileUID++;
336   return &UFE;
337 }
338 
339 const FileEntry *
340 FileManager::getVirtualFile(llvm::StringRef Filename, off_t Size,
341                             time_t ModificationTime,
342                             const FileSystemOptions &FileSystemOpts) {
343   ++NumFileLookups;
344 
345   // See if there is already an entry in the map.
346   llvm::StringMapEntry<FileEntry *> &NamedFileEnt =
347     FileEntries.GetOrCreateValue(Filename);
348 
349   // See if there is already an entry in the map.
350   if (NamedFileEnt.getValue())
351     return NamedFileEnt.getValue() == NON_EXISTENT_FILE
352                  ? 0 : NamedFileEnt.getValue();
353 
354   ++NumFileCacheMisses;
355 
356   // By default, initialize it to invalid.
357   NamedFileEnt.setValue(NON_EXISTENT_FILE);
358 
359   const DirectoryEntry *DirInfo
360     = getDirectoryFromFile(*this, Filename, FileSystemOpts);
361   if (DirInfo == 0)  // Directory doesn't exist, file can't exist.
362     return 0;
363 
364   FileEntry *UFE = new FileEntry();
365   VirtualFileEntries.push_back(UFE);
366   NamedFileEnt.setValue(UFE);
367 
368   UFE->Name    = NamedFileEnt.getKeyData();
369   UFE->Size    = Size;
370   UFE->ModTime = ModificationTime;
371   UFE->Dir     = DirInfo;
372   UFE->UID     = NextFileUID++;
373 
374   // If this virtual file resolves to a file, also map that file to the
375   // newly-created file entry.
376   const char *InterndFileName = NamedFileEnt.getKeyData();
377   struct stat StatBuf;
378   if (!stat_cached(InterndFileName, &StatBuf, FileSystemOpts) &&
379       !S_ISDIR(StatBuf.st_mode)) {
380     llvm::sys::Path FilePath(InterndFileName);
381     FilePath.makeAbsolute();
382     FileEntries[FilePath.str()] = UFE;
383   }
384 
385   return UFE;
386 }
387 
388 llvm::MemoryBuffer *FileManager::
389 getBufferForFile(const char *FilenameStart, const char *FilenameEnd,
390                  const FileSystemOptions &FileSystemOpts,
391                  std::string *ErrorStr,
392                  int64_t FileSize,
393                  struct stat *FileInfo) {
394   llvm::sys::Path FilePath(llvm::StringRef(FilenameStart,
395                                            FilenameEnd-FilenameStart));
396   FixupRelativePath(FilePath, FileSystemOpts);
397 
398   return llvm::MemoryBuffer::getFile(FilePath.c_str(), ErrorStr,
399                                      FileSize, FileInfo);
400 }
401 
402 int FileManager::stat_cached(const char* path, struct stat* buf,
403                              const FileSystemOptions &FileSystemOpts) {
404   llvm::sys::Path FilePath(path);
405   FixupRelativePath(FilePath, FileSystemOpts);
406 
407   return StatCache.get() ? StatCache->stat(FilePath.c_str(), buf)
408                          : stat(FilePath.c_str(), buf);
409 }
410 
411 void FileManager::FixupRelativePath(llvm::sys::Path &path,
412                                     const FileSystemOptions &FSOpts) {
413   if (!FSOpts.WorkingDir.empty() && !path.isAbsolute()) {
414     llvm::sys::Path NewPath(FSOpts.WorkingDir);
415     NewPath.appendComponent(path.str());
416     path = NewPath;
417   }
418 }
419 
420 void FileManager::PrintStats() const {
421   llvm::errs() << "\n*** File Manager Stats:\n";
422   llvm::errs() << UniqueFiles.size() << " files found, "
423                << UniqueDirs.size() << " dirs found.\n";
424   llvm::errs() << NumDirLookups << " dir lookups, "
425                << NumDirCacheMisses << " dir cache misses.\n";
426   llvm::errs() << NumFileLookups << " file lookups, "
427                << NumFileCacheMisses << " file cache misses.\n";
428 
429   //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups;
430 }
431 
432 int MemorizeStatCalls::stat(const char *path, struct stat *buf) {
433   int result = StatSysCallCache::stat(path, buf);
434 
435   // Do not cache failed stats, it is easy to construct common inconsistent
436   // situations if we do, and they are not important for PCH performance (which
437   // currently only needs the stats to construct the initial FileManager
438   // entries).
439   if (result != 0)
440     return result;
441 
442   // Cache file 'stat' results and directories with absolutely paths.
443   if (!S_ISDIR(buf->st_mode) || llvm::sys::Path(path).isAbsolute())
444     StatCalls[path] = StatResult(result, *buf);
445 
446   return result;
447 }
448