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