FileManager.cpp revision 5830b773c03657c0a53f68dee9ea8985b884aa4d
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/Bitcode/Serialize.h"
23#include "llvm/Bitcode/Deserialize.h"
24#include "llvm/Support/Streams.h"
25#include "llvm/Config/config.h"
26using namespace clang;
27
28// FIXME: Enhance libsystem to support inode and other fields.
29#include <sys/stat.h>
30
31#if defined(_MSC_VER)
32#defisstne S_ISDIR(s) (_S_IFDIR & s)
33#endif
34
35/// NON_EXISTENT_DIR - A special value distinct from null that is used to
36/// represent a dir name that doesn't exist on the disk.
37#define NON_EXISTENT_DIR reinterpret_cast<DirectoryEntry*>((intptr_t)-1)
38
39//===----------------------------------------------------------------------===//
40// Windows.
41//===----------------------------------------------------------------------===//
42
43#ifdef LLVM_ON_WIN32
44
45#define IS_DIR_SEPARATOR_CHAR(x) ((x) == '/' || (x) == '\\')
46
47namespace {
48  static std::string GetFullPath(const char *relPath)
49  {
50    char *absPathStrPtr = _fullpath(NULL, relPath, 0);
51    assert(absPathStrPtr && "_fullpath() returned NULL!");
52
53    std::string absPath(absPathStrPtr);
54
55    free(absPathStrPtr);
56    return absPath;
57  }
58}
59
60class FileManager::UniqueDirContainer {
61  /// UniqueDirs - Cache from full path to existing directories/files.
62  ///
63  llvm::StringMap<DirectoryEntry> UniqueDirs;
64
65public:
66  DirectoryEntry &getDirectory(const char *Name, struct stat &StatBuf) {
67    std::string FullPath(GetFullPath(Name));
68    return UniqueDirs.GetOrCreateValue(
69                              FullPath.c_str(),
70                              FullPath.c_str() + FullPath.size()
71                                                                ).getValue();
72  }
73
74  size_t size() { return UniqueDirs.size(); }
75};
76
77class FileManager::UniqueFileContainer {
78  /// UniqueFiles - Cache from full path to existing directories/files.
79  ///
80  llvm::StringMap<FileEntry, llvm::BumpPtrAllocator> UniqueFiles;
81
82public:
83  FileEntry &getFile(const char *Name, struct stat &StatBuf) {
84    std::string FullPath(GetFullPath(Name));
85    return UniqueFiles.GetOrCreateValue(
86                               FullPath.c_str(),
87                               FullPath.c_str() + FullPath.size()
88                                                                 ).getValue();
89  }
90
91  size_t size() { return UniqueFiles.size(); }
92};
93
94//===----------------------------------------------------------------------===//
95// Unix-like Systems.
96//===----------------------------------------------------------------------===//
97
98#else
99
100#define IS_DIR_SEPARATOR_CHAR(x) ((x) == '/')
101
102class FileManager::UniqueDirContainer {
103  /// UniqueDirs - Cache from ID's to existing directories/files.
104  ///
105  std::map<std::pair<dev_t, ino_t>, DirectoryEntry> UniqueDirs;
106
107public:
108  DirectoryEntry &getDirectory(const char *Name, struct stat &StatBuf) {
109    return UniqueDirs[std::make_pair(StatBuf.st_dev, StatBuf.st_ino)];
110  }
111
112  size_t size() { return UniqueDirs.size(); }
113};
114
115class FileManager::UniqueFileContainer {
116  /// UniqueFiles - Cache from ID's to existing directories/files.
117  ///
118  std::set<FileEntry> UniqueFiles;
119
120public:
121  FileEntry &getFile(const char *Name, struct stat &StatBuf) {
122    return
123      const_cast<FileEntry&>(
124                    *UniqueFiles.insert(FileEntry(StatBuf.st_dev,
125                                                  StatBuf.st_ino)).first);
126  }
127
128  size_t size() { return UniqueFiles.size(); }
129};
130
131#endif
132
133//===----------------------------------------------------------------------===//
134// Common logic.
135//===----------------------------------------------------------------------===//
136
137FileManager::FileManager(StatSysCallCache* statCache)
138  : UniqueDirs(*new UniqueDirContainer),
139    UniqueFiles(*new UniqueFileContainer),
140    DirEntries(64), FileEntries(64), NextFileUID(0),
141    StatCache(statCache) {
142  NumDirLookups = NumFileLookups = 0;
143  NumDirCacheMisses = NumFileCacheMisses = 0;
144}
145
146FileManager::~FileManager() {
147  delete &UniqueDirs;
148  delete &UniqueFiles;
149  delete StatCache;
150}
151
152
153/// getDirectory - Lookup, cache, and verify the specified directory.  This
154/// returns null if the directory doesn't exist.
155///
156const DirectoryEntry *FileManager::getDirectory(const char *NameStart,
157                                                const char *NameEnd) {
158  ++NumDirLookups;
159  llvm::StringMapEntry<DirectoryEntry *> &NamedDirEnt =
160    DirEntries.GetOrCreateValue(NameStart, NameEnd);
161
162  // See if there is already an entry in the map.
163  if (NamedDirEnt.getValue())
164    return NamedDirEnt.getValue() == NON_EXISTENT_DIR
165              ? 0 : NamedDirEnt.getValue();
166
167  ++NumDirCacheMisses;
168
169  // By default, initialize it to invalid.
170  NamedDirEnt.setValue(NON_EXISTENT_DIR);
171
172  // Get the null-terminated directory name as stored as the key of the
173  // DirEntries map.
174  const char *InterndDirName = NamedDirEnt.getKeyData();
175
176  // Check to see if the directory exists.
177  struct stat StatBuf;
178  if (stat_cached(InterndDirName, &StatBuf) ||   // Error stat'ing.
179      !S_ISDIR(StatBuf.st_mode))          // Not a directory?
180    return 0;
181
182  // It exists.  See if we have already opened a directory with the same inode.
183  // This occurs when one dir is symlinked to another, for example.
184  DirectoryEntry &UDE = UniqueDirs.getDirectory(InterndDirName, StatBuf);
185
186  NamedDirEnt.setValue(&UDE);
187  if (UDE.getName()) // Already have an entry with this inode, return it.
188    return &UDE;
189
190  // Otherwise, we don't have this directory yet, add it.  We use the string
191  // key from the DirEntries map as the string.
192  UDE.Name  = InterndDirName;
193  return &UDE;
194}
195
196/// NON_EXISTENT_FILE - A special value distinct from null that is used to
197/// represent a filename that doesn't exist on the disk.
198#define NON_EXISTENT_FILE reinterpret_cast<FileEntry*>((intptr_t)-1)
199
200/// getFile - Lookup, cache, and verify the specified file.  This returns null
201/// if the file doesn't exist.
202///
203const FileEntry *FileManager::getFile(const char *NameStart,
204                                      const char *NameEnd) {
205  ++NumFileLookups;
206
207  // See if there is already an entry in the map.
208  llvm::StringMapEntry<FileEntry *> &NamedFileEnt =
209    FileEntries.GetOrCreateValue(NameStart, NameEnd);
210
211  // See if there is already an entry in the map.
212  if (NamedFileEnt.getValue())
213    return NamedFileEnt.getValue() == NON_EXISTENT_FILE
214                 ? 0 : NamedFileEnt.getValue();
215
216  ++NumFileCacheMisses;
217
218  // By default, initialize it to invalid.
219  NamedFileEnt.setValue(NON_EXISTENT_FILE);
220
221  // Figure out what directory it is in.   If the string contains a / in it,
222  // strip off everything after it.
223  // FIXME: this logic should be in sys::Path.
224  const char *SlashPos = NameEnd-1;
225  while (SlashPos >= NameStart && !IS_DIR_SEPARATOR_CHAR(SlashPos[0]))
226    --SlashPos;
227
228  const DirectoryEntry *DirInfo;
229  if (SlashPos < NameStart) {
230    // Use the current directory if file has no path component.
231    const char *Name = ".";
232    DirInfo = getDirectory(Name, Name+1);
233  } else if (SlashPos == NameEnd-1)
234    return 0;       // If filename ends with a /, it's a directory.
235  else
236    DirInfo = getDirectory(NameStart, SlashPos);
237
238  if (DirInfo == 0)  // Directory doesn't exist, file can't exist.
239    return 0;
240
241  // Get the null-terminated file name as stored as the key of the
242  // FileEntries map.
243  const char *InterndFileName = NamedFileEnt.getKeyData();
244
245  // FIXME: Use the directory info to prune this, before doing the stat syscall.
246  // FIXME: This will reduce the # syscalls.
247
248  // Nope, there isn't.  Check to see if the file exists.
249  struct stat StatBuf;
250  //llvm::cerr << "STATING: " << Filename;
251  if (stat_cached(InterndFileName, &StatBuf) ||   // Error stat'ing.
252        S_ISDIR(StatBuf.st_mode)) {           // A directory?
253    // If this file doesn't exist, we leave a null in FileEntries for this path.
254    //llvm::cerr << ": Not existing\n";
255    return 0;
256  }
257  //llvm::cerr << ": exists\n";
258
259  // It exists.  See if we have already opened a file with the same inode.
260  // This occurs when one dir is symlinked to another, for example.
261  FileEntry &UFE = UniqueFiles.getFile(InterndFileName, StatBuf);
262
263  NamedFileEnt.setValue(&UFE);
264  if (UFE.getName())  // Already have an entry with this inode, return it.
265    return &UFE;
266
267  // Otherwise, we don't have this directory yet, add it.
268  // FIXME: Change the name to be a char* that points back to the 'FileEntries'
269  // key.
270  UFE.Name    = InterndFileName;
271  UFE.Size    = StatBuf.st_size;
272  UFE.ModTime = StatBuf.st_mtime;
273  UFE.Dir     = DirInfo;
274  UFE.UID     = NextFileUID++;
275  return &UFE;
276}
277
278void FileManager::PrintStats() const {
279  llvm::cerr << "\n*** File Manager Stats:\n";
280  llvm::cerr << UniqueFiles.size() << " files found, "
281             << UniqueDirs.size() << " dirs found.\n";
282  llvm::cerr << NumDirLookups << " dir lookups, "
283             << NumDirCacheMisses << " dir cache misses.\n";
284  llvm::cerr << NumFileLookups << " file lookups, "
285             << NumFileCacheMisses << " file cache misses.\n";
286
287  //llvm::cerr << PagesMapped << BytesOfPagesMapped << FSLookups;
288}
289