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