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/Support/raw_ostream.h" 23 #include "llvm/System/Path.h" 24 #include "llvm/Config/config.h" 25 #include <map> 26 #include <set> 27 #include <string> 28 using namespace clang; 29 30 // FIXME: Enhance libsystem to support inode and other fields. 31 #include <sys/stat.h> 32 33 #if defined(_MSC_VER) 34 #define S_ISDIR(s) (_S_IFDIR & s) 35 #endif 36 37 /// NON_EXISTENT_DIR - A special value distinct from null that is used to 38 /// represent a dir name that doesn't exist on the disk. 39 #define NON_EXISTENT_DIR reinterpret_cast<DirectoryEntry*>((intptr_t)-1) 40 41 //===----------------------------------------------------------------------===// 42 // Windows. 43 //===----------------------------------------------------------------------===// 44 45 #ifdef LLVM_ON_WIN32 46 47 #define IS_DIR_SEPARATOR_CHAR(x) ((x) == '/' || (x) == '\\') 48 49 namespace { 50 static std::string GetFullPath(const char *relPath) { 51 char *absPathStrPtr = _fullpath(NULL, relPath, 0); 52 assert(absPathStrPtr && "_fullpath() returned NULL!"); 53 54 std::string absPath(absPathStrPtr); 55 56 free(absPathStrPtr); 57 return absPath; 58 } 59 } 60 61 class FileManager::UniqueDirContainer { 62 /// UniqueDirs - Cache from full path to existing directories/files. 63 /// 64 llvm::StringMap<DirectoryEntry> UniqueDirs; 65 66 public: 67 DirectoryEntry &getDirectory(const char *Name, struct stat &StatBuf) { 68 std::string FullPath(GetFullPath(Name)); 69 return UniqueDirs.GetOrCreateValue( 70 FullPath.c_str(), 71 FullPath.c_str() + FullPath.size() 72 ).getValue(); 73 } 74 75 size_t size() { return UniqueDirs.size(); } 76 }; 77 78 class FileManager::UniqueFileContainer { 79 /// UniqueFiles - Cache from full path to existing directories/files. 80 /// 81 llvm::StringMap<FileEntry, llvm::BumpPtrAllocator> UniqueFiles; 82 83 public: 84 FileEntry &getFile(const char *Name, struct stat &StatBuf) { 85 std::string FullPath(GetFullPath(Name)); 86 return UniqueFiles.GetOrCreateValue( 87 FullPath.c_str(), 88 FullPath.c_str() + FullPath.size() 89 ).getValue(); 90 } 91 92 size_t size() { return UniqueFiles.size(); } 93 }; 94 95 //===----------------------------------------------------------------------===// 96 // Unix-like Systems. 97 //===----------------------------------------------------------------------===// 98 99 #else 100 101 #define IS_DIR_SEPARATOR_CHAR(x) ((x) == '/') 102 103 class FileManager::UniqueDirContainer { 104 /// UniqueDirs - Cache from ID's to existing directories/files. 105 /// 106 std::map<std::pair<dev_t, ino_t>, DirectoryEntry> UniqueDirs; 107 108 public: 109 DirectoryEntry &getDirectory(const char *Name, struct stat &StatBuf) { 110 return UniqueDirs[std::make_pair(StatBuf.st_dev, StatBuf.st_ino)]; 111 } 112 113 size_t size() { return UniqueDirs.size(); } 114 }; 115 116 class FileManager::UniqueFileContainer { 117 /// UniqueFiles - Cache from ID's to existing directories/files. 118 /// 119 std::set<FileEntry> UniqueFiles; 120 121 public: 122 FileEntry &getFile(const char *Name, struct stat &StatBuf) { 123 return 124 const_cast<FileEntry&>( 125 *UniqueFiles.insert(FileEntry(StatBuf.st_dev, 126 StatBuf.st_ino, 127 StatBuf.st_mode)).first); 128 } 129 130 size_t size() { return UniqueFiles.size(); } 131 }; 132 133 #endif 134 135 //===----------------------------------------------------------------------===// 136 // Common logic. 137 //===----------------------------------------------------------------------===// 138 139 FileManager::FileManager() 140 : UniqueDirs(*new UniqueDirContainer), 141 UniqueFiles(*new UniqueFileContainer), 142 DirEntries(64), FileEntries(64), NextFileUID(0) { 143 NumDirLookups = NumFileLookups = 0; 144 NumDirCacheMisses = NumFileCacheMisses = 0; 145 } 146 147 FileManager::~FileManager() { 148 delete &UniqueDirs; 149 delete &UniqueFiles; 150 } 151 152 /// getDirectory - Lookup, cache, and verify the specified directory. This 153 /// returns null if the directory doesn't exist. 154 /// 155 const DirectoryEntry *FileManager::getDirectory(const char *NameStart, 156 const char *NameEnd) { 157 ++NumDirLookups; 158 llvm::StringMapEntry<DirectoryEntry *> &NamedDirEnt = 159 DirEntries.GetOrCreateValue(NameStart, NameEnd); 160 161 // See if there is already an entry in the map. 162 if (NamedDirEnt.getValue()) 163 return NamedDirEnt.getValue() == NON_EXISTENT_DIR 164 ? 0 : NamedDirEnt.getValue(); 165 166 ++NumDirCacheMisses; 167 168 // By default, initialize it to invalid. 169 NamedDirEnt.setValue(NON_EXISTENT_DIR); 170 171 // Get the null-terminated directory name as stored as the key of the 172 // DirEntries map. 173 const char *InterndDirName = NamedDirEnt.getKeyData(); 174 175 // Check to see if the directory exists. 176 struct stat StatBuf; 177 if (stat_cached(InterndDirName, &StatBuf) || // Error stat'ing. 178 !S_ISDIR(StatBuf.st_mode)) // Not a directory? 179 return 0; 180 181 // It exists. See if we have already opened a directory with the same inode. 182 // This occurs when one dir is symlinked to another, for example. 183 DirectoryEntry &UDE = UniqueDirs.getDirectory(InterndDirName, StatBuf); 184 185 NamedDirEnt.setValue(&UDE); 186 if (UDE.getName()) // Already have an entry with this inode, return it. 187 return &UDE; 188 189 // Otherwise, we don't have this directory yet, add it. We use the string 190 // key from the DirEntries map as the string. 191 UDE.Name = InterndDirName; 192 return &UDE; 193 } 194 195 /// NON_EXISTENT_FILE - A special value distinct from null that is used to 196 /// represent a filename that doesn't exist on the disk. 197 #define NON_EXISTENT_FILE reinterpret_cast<FileEntry*>((intptr_t)-1) 198 199 /// getFile - Lookup, cache, and verify the specified file. This returns null 200 /// if the file doesn't exist. 201 /// 202 const FileEntry *FileManager::getFile(const char *NameStart, 203 const char *NameEnd) { 204 ++NumFileLookups; 205 206 // See if there is already an entry in the map. 207 llvm::StringMapEntry<FileEntry *> &NamedFileEnt = 208 FileEntries.GetOrCreateValue(NameStart, NameEnd); 209 210 // See if there is already an entry in the map. 211 if (NamedFileEnt.getValue()) 212 return NamedFileEnt.getValue() == NON_EXISTENT_FILE 213 ? 0 : NamedFileEnt.getValue(); 214 215 ++NumFileCacheMisses; 216 217 // By default, initialize it to invalid. 218 NamedFileEnt.setValue(NON_EXISTENT_FILE); 219 220 // Figure out what directory it is in. If the string contains a / in it, 221 // strip off everything after it. 222 // FIXME: this logic should be in sys::Path. 223 const char *SlashPos = NameEnd-1; 224 while (SlashPos >= NameStart && !IS_DIR_SEPARATOR_CHAR(SlashPos[0])) 225 --SlashPos; 226 // Ignore duplicate //'s. 227 while (SlashPos > NameStart && IS_DIR_SEPARATOR_CHAR(SlashPos[-1])) 228 --SlashPos; 229 230 const DirectoryEntry *DirInfo; 231 if (SlashPos < NameStart) { 232 // Use the current directory if file has no path component. 233 const char *Name = "."; 234 DirInfo = getDirectory(Name, Name+1); 235 } else if (SlashPos == NameEnd-1) 236 return 0; // If filename ends with a /, it's a directory. 237 else 238 DirInfo = getDirectory(NameStart, SlashPos); 239 240 if (DirInfo == 0) // Directory doesn't exist, file can't exist. 241 return 0; 242 243 // Get the null-terminated file name as stored as the key of the 244 // FileEntries map. 245 const char *InterndFileName = NamedFileEnt.getKeyData(); 246 247 // FIXME: Use the directory info to prune this, before doing the stat syscall. 248 // FIXME: This will reduce the # syscalls. 249 250 // Nope, there isn't. Check to see if the file exists. 251 struct stat StatBuf; 252 //llvm::errs() << "STATING: " << Filename; 253 if (stat_cached(InterndFileName, &StatBuf) || // Error stat'ing. 254 S_ISDIR(StatBuf.st_mode)) { // A directory? 255 // If this file doesn't exist, we leave a null in FileEntries for this path. 256 //llvm::errs() << ": Not existing\n"; 257 return 0; 258 } 259 //llvm::errs() << ": exists\n"; 260 261 // It exists. See if we have already opened a file with the same inode. 262 // This occurs when one dir is symlinked to another, for example. 263 FileEntry &UFE = UniqueFiles.getFile(InterndFileName, StatBuf); 264 265 NamedFileEnt.setValue(&UFE); 266 if (UFE.getName()) // Already have an entry with this inode, return it. 267 return &UFE; 268 269 // Otherwise, we don't have this directory yet, add it. 270 // FIXME: Change the name to be a char* that points back to the 'FileEntries' 271 // key. 272 UFE.Name = InterndFileName; 273 UFE.Size = StatBuf.st_size; 274 UFE.ModTime = StatBuf.st_mtime; 275 UFE.Dir = DirInfo; 276 UFE.UID = NextFileUID++; 277 return &UFE; 278 } 279 280 void FileManager::PrintStats() const { 281 llvm::errs() << "\n*** File Manager Stats:\n"; 282 llvm::errs() << UniqueFiles.size() << " files found, " 283 << UniqueDirs.size() << " dirs found.\n"; 284 llvm::errs() << NumDirLookups << " dir lookups, " 285 << NumDirCacheMisses << " dir cache misses.\n"; 286 llvm::errs() << NumFileLookups << " file lookups, " 287 << NumFileCacheMisses << " file cache misses.\n"; 288 289 //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups; 290 } 291 292 int MemorizeStatCalls::stat(const char *path, struct stat *buf) { 293 int result = ::stat(path, buf); 294 295 if (result != 0) { 296 // Cache failed 'stat' results. 297 struct stat empty; 298 StatCalls[path] = StatResult(result, empty); 299 } 300 else if (!S_ISDIR(buf->st_mode) || llvm::sys::Path(path).isAbsolute()) { 301 // Cache file 'stat' results and directories with absolutely 302 // paths. 303 StatCalls[path] = StatResult(result, *buf); 304 } 305 306 return result; 307 } 308