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/Bitcode/Serialize.h" 23 #include "llvm/Bitcode/Deserialize.h" 24 #include "llvm/Support/Streams.h" 25 #include "llvm/Support/Allocator.h" 26 #include "llvm/Config/config.h" 27 using namespace clang; 28 29 // FIXME: Enhance libsystem to support inode and other fields. 30 #include <sys/stat.h> 31 32 #if defined(_MSC_VER) 33 #define S_ISDIR(s) (_S_IFDIR & s) 34 #endif 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 //===----------------------------------------------------------------------===// 41 // Windows. 42 //===----------------------------------------------------------------------===// 43 44 #ifdef LLVM_ON_WIN32 45 46 #define IS_DIR_SEPARATOR_CHAR(x) ((x) == '/' || (x) == '\\') 47 48 namespace { 49 static std::string GetFullPath(const char *relPath) 50 { 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> 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)).first); 127 } 128 129 size_t size() { return UniqueFiles.size(); } 130 }; 131 132 #endif 133 134 //===----------------------------------------------------------------------===// 135 // Common logic. 136 //===----------------------------------------------------------------------===// 137 138 FileManager::FileManager() : UniqueDirs(*new UniqueDirContainer), 139 UniqueFiles(*new UniqueFileContainer), 140 DirEntries(64), FileEntries(64), NextFileUID(0) 141 { 142 NumDirLookups = NumFileLookups = 0; 143 NumDirCacheMisses = NumFileCacheMisses = 0; 144 } 145 146 FileManager::~FileManager() { 147 delete &UniqueDirs; 148 delete &UniqueFiles; 149 } 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(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 227 const DirectoryEntry *DirInfo; 228 if (SlashPos < NameStart) { 229 // Use the current directory if file has no path component. 230 const char *Name = "."; 231 DirInfo = getDirectory(Name, Name+1); 232 } else if (SlashPos == NameEnd-1) 233 return 0; // If filename ends with a /, it's a directory. 234 else 235 DirInfo = getDirectory(NameStart, SlashPos); 236 237 if (DirInfo == 0) // Directory doesn't exist, file can't exist. 238 return 0; 239 240 // Get the null-terminated file name as stored as the key of the 241 // FileEntries map. 242 const char *InterndFileName = NamedFileEnt.getKeyData(); 243 244 // FIXME: Use the directory info to prune this, before doing the stat syscall. 245 // FIXME: This will reduce the # syscalls. 246 247 // Nope, there isn't. Check to see if the file exists. 248 struct stat StatBuf; 249 //llvm::cerr << "STATING: " << Filename; 250 if (stat(InterndFileName, &StatBuf) || // Error stat'ing. 251 S_ISDIR(StatBuf.st_mode)) { // A directory? 252 // If this file doesn't exist, we leave a null in FileEntries for this path. 253 //llvm::cerr << ": Not existing\n"; 254 return 0; 255 } 256 //llvm::cerr << ": exists\n"; 257 258 // It exists. See if we have already opened a file with the same inode. 259 // This occurs when one dir is symlinked to another, for example. 260 FileEntry &UFE = UniqueFiles.getFile(InterndFileName, StatBuf); 261 262 NamedFileEnt.setValue(&UFE); 263 if (UFE.getName()) // Already have an entry with this inode, return it. 264 return &UFE; 265 266 // Otherwise, we don't have this directory yet, add it. 267 // FIXME: Change the name to be a char* that points back to the 'FileEntries' 268 // key. 269 UFE.Name = InterndFileName; 270 UFE.Size = StatBuf.st_size; 271 UFE.ModTime = StatBuf.st_mtime; 272 UFE.Dir = DirInfo; 273 UFE.UID = NextFileUID++; 274 return &UFE; 275 } 276 277 void FileManager::PrintStats() const { 278 llvm::cerr << "\n*** File Manager Stats:\n"; 279 llvm::cerr << UniqueFiles.size() << " files found, " 280 << UniqueDirs.size() << " dirs found.\n"; 281 llvm::cerr << NumDirLookups << " dir lookups, " 282 << NumDirCacheMisses << " dir cache misses.\n"; 283 llvm::cerr << NumFileLookups << " file lookups, " 284 << NumFileCacheMisses << " file cache misses.\n"; 285 286 //llvm::cerr << PagesMapped << BytesOfPagesMapped << FSLookups; 287 } 288