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