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