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