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