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