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 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 DirectoryEntry &getDirectory(const char *Name, struct stat &StatBuf) { 110 return UniqueDirs[std::make_pair(StatBuf.st_dev, StatBuf.st_ino)]; 111 } 112 113 size_t size() { return UniqueDirs.size(); } 114 }; 115 116 class FileManager::UniqueFileContainer { 117 /// UniqueFiles - Cache from ID's to existing directories/files. 118 /// 119 std::set<FileEntry> UniqueFiles; 120 121 public: 122 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 StatBuf.st_mode)).first); 128 } 129 130 size_t size() { return UniqueFiles.size(); } 131 }; 132 133 #endif 134 135 //===----------------------------------------------------------------------===// 136 // Common logic. 137 //===----------------------------------------------------------------------===// 138 139 FileManager::FileManager() 140 : UniqueDirs(*new UniqueDirContainer), 141 UniqueFiles(*new UniqueFileContainer), 142 DirEntries(64), FileEntries(64), NextFileUID(0) { 143 NumDirLookups = NumFileLookups = 0; 144 NumDirCacheMisses = NumFileCacheMisses = 0; 145 } 146 147 FileManager::~FileManager() { 148 delete &UniqueDirs; 149 delete &UniqueFiles; 150 for (llvm::SmallVectorImpl<FileEntry *>::iterator 151 V = VirtualFileEntries.begin(), 152 VEnd = VirtualFileEntries.end(); 153 V != VEnd; 154 ++V) 155 delete *V; 156 } 157 158 void FileManager::addStatCache(StatSysCallCache *statCache, bool AtBeginning) { 159 assert(statCache && "No stat cache provided?"); 160 if (AtBeginning || StatCache.get() == 0) { 161 statCache->setNextStatCache(StatCache.take()); 162 StatCache.reset(statCache); 163 return; 164 } 165 166 StatSysCallCache *LastCache = StatCache.get(); 167 while (LastCache->getNextStatCache()) 168 LastCache = LastCache->getNextStatCache(); 169 170 LastCache->setNextStatCache(statCache); 171 } 172 173 void FileManager::removeStatCache(StatSysCallCache *statCache) { 174 if (!statCache) 175 return; 176 177 if (StatCache.get() == statCache) { 178 // This is the first stat cache. 179 StatCache.reset(StatCache->takeNextStatCache()); 180 return; 181 } 182 183 // Find the stat cache in the list. 184 StatSysCallCache *PrevCache = StatCache.get(); 185 while (PrevCache && PrevCache->getNextStatCache() != statCache) 186 PrevCache = PrevCache->getNextStatCache(); 187 if (PrevCache) 188 PrevCache->setNextStatCache(statCache->getNextStatCache()); 189 else 190 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 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 const char *SlashPos = NameEnd-1; 201 while (SlashPos >= NameStart && !IS_DIR_SEPARATOR_CHAR(SlashPos[0])) 202 --SlashPos; 203 // Ignore duplicate //'s. 204 while (SlashPos > NameStart && IS_DIR_SEPARATOR_CHAR(SlashPos[-1])) 205 --SlashPos; 206 207 if (SlashPos < NameStart) { 208 // Use the current directory if file has no path component. 209 const char *Name = "."; 210 return FileMgr.getDirectory(Name, Name+1); 211 } else if (SlashPos == NameEnd-1) 212 return 0; // If filename ends with a /, it's a directory. 213 else 214 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 const char *NameEnd) { 222 // stat doesn't like trailing separators (at least on Windows). 223 if (((NameEnd - NameStart) > 1) && 224 ((*(NameEnd - 1) == '/') || (*(NameEnd - 1) == '\\'))) 225 NameEnd--; 226 227 ++NumDirLookups; 228 llvm::StringMapEntry<DirectoryEntry *> &NamedDirEnt = 229 DirEntries.GetOrCreateValue(NameStart, NameEnd); 230 231 // See if there is already an entry in the map. 232 if (NamedDirEnt.getValue()) 233 return NamedDirEnt.getValue() == NON_EXISTENT_DIR 234 ? 0 : NamedDirEnt.getValue(); 235 236 ++NumDirCacheMisses; 237 238 // By default, initialize it to invalid. 239 NamedDirEnt.setValue(NON_EXISTENT_DIR); 240 241 // Get the null-terminated directory name as stored as the key of the 242 // DirEntries map. 243 const char *InterndDirName = NamedDirEnt.getKeyData(); 244 245 // Check to see if the directory exists. 246 struct stat StatBuf; 247 if (stat_cached(InterndDirName, &StatBuf) || // Error stat'ing. 248 !S_ISDIR(StatBuf.st_mode)) // Not a directory? 249 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 DirectoryEntry &UDE = UniqueDirs.getDirectory(InterndDirName, StatBuf); 254 255 NamedDirEnt.setValue(&UDE); 256 if (UDE.getName()) // Already have an entry with this inode, return it. 257 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 UDE.Name = InterndDirName; 262 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 const char *NameEnd) { 274 ++NumFileLookups; 275 276 // See if there is already an entry in the map. 277 llvm::StringMapEntry<FileEntry *> &NamedFileEnt = 278 FileEntries.GetOrCreateValue(NameStart, NameEnd); 279 280 // See if there is already an entry in the map. 281 if (NamedFileEnt.getValue()) 282 return NamedFileEnt.getValue() == NON_EXISTENT_FILE 283 ? 0 : NamedFileEnt.getValue(); 284 285 ++NumFileCacheMisses; 286 287 // By default, initialize it to invalid. 288 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 const char *InterndFileName = NamedFileEnt.getKeyData(); 294 295 const DirectoryEntry *DirInfo 296 = getDirectoryFromFile(*this, NameStart, NameEnd); 297 if (DirInfo == 0) // Directory doesn't exist, file can't exist. 298 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; 306 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 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 FileEntry &UFE = UniqueFiles.getFile(InterndFileName, StatBuf); 317 318 NamedFileEnt.setValue(&UFE); 319 if (UFE.getName()) // Already have an entry with this inode, return it. 320 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 UFE.Name = InterndFileName; 326 UFE.Size = StatBuf.st_size; 327 UFE.ModTime = StatBuf.st_mtime; 328 UFE.Dir = DirInfo; 329 UFE.UID = NextFileUID++; 330 return &UFE; 331 } 332 333 const FileEntry * 334 FileManager::getVirtualFile(llvm::StringRef Filename, off_t Size, 335 time_t ModificationTime) { 336 const char *NameStart = Filename.begin(), *NameEnd = Filename.end(); 337 338 ++NumFileLookups; 339 340 // See if there is already an entry in the map. 341 llvm::StringMapEntry<FileEntry *> &NamedFileEnt = 342 FileEntries.GetOrCreateValue(NameStart, NameEnd); 343 344 // See if there is already an entry in the map. 345 if (NamedFileEnt.getValue()) 346 return NamedFileEnt.getValue() == NON_EXISTENT_FILE 347 ? 0 : NamedFileEnt.getValue(); 348 349 ++NumFileCacheMisses; 350 351 // By default, initialize it to invalid. 352 NamedFileEnt.setValue(NON_EXISTENT_FILE); 353 354 const DirectoryEntry *DirInfo 355 = getDirectoryFromFile(*this, NameStart, NameEnd); 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::PrintStats() const { 384 llvm::errs() << "\n*** File Manager Stats:\n"; 385 llvm::errs() << UniqueFiles.size() << " files found, " 386 << UniqueDirs.size() << " dirs found.\n"; 387 llvm::errs() << NumDirLookups << " dir lookups, " 388 << NumDirCacheMisses << " dir cache misses.\n"; 389 llvm::errs() << NumFileLookups << " file lookups, " 390 << NumFileCacheMisses << " file cache misses.\n"; 391 392 //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups; 393 } 394 395 int MemorizeStatCalls::stat(const char *path, struct stat *buf) { 396 int result = StatSysCallCache::stat(path, buf); 397 398 // Do not cache failed stats, it is easy to construct common inconsistent 399 // situations if we do, and they are not important for PCH performance (which 400 // currently only needs the stats to construct the initial FileManager 401 // entries). 402 if (result != 0) 403 return result; 404 405 // Cache file 'stat' results and directories with absolutely paths. 406 if (!S_ISDIR(buf->st_mode) || llvm::sys::Path(path).isAbsolute()) 407 StatCalls[path] = StatResult(result, *buf); 408 409 return result; 410 } 411