| Ted Kremenek | 8fbc88e | 2007-12-04 22:42:20 +0000 | [diff] [blame] | 1 | ///===--- FileManager.cpp - File System Probing and Caching ----------------===// | 
| Reid Spencer | 5f016e2 | 2007-07-11 17:01:13 +0000 | [diff] [blame] | 2 | // | 
 | 3 | //                     The LLVM Compiler Infrastructure | 
 | 4 | // | 
 | 5 | // This file was developed by Chris Lattner and is distributed under | 
 | 6 | // the University of Illinois Open Source 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" | 
| Ted Kremenek | 9551a2c | 2007-12-04 18:21:35 +0000 | [diff] [blame] | 22 | #include "llvm/Bitcode/Serialize.h" | 
 | 23 | #include "llvm/Bitcode/Deserialize.h" | 
| Reid Spencer | 5f016e2 | 2007-07-11 17:01:13 +0000 | [diff] [blame] | 24 | #include <iostream> | 
 | 25 | using namespace clang; | 
 | 26 |  | 
 | 27 | // FIXME: Enhance libsystem to support inode and other fields. | 
 | 28 | #include <sys/stat.h> | 
 | 29 |  | 
| Chris Lattner | a8c11c6 | 2007-09-03 18:37:14 +0000 | [diff] [blame] | 30 | #if defined(_MSC_VER) | 
 | 31 | #define S_ISDIR(s) (_S_IFDIR & s) | 
 | 32 | #endif | 
| Reid Spencer | 5f016e2 | 2007-07-11 17:01:13 +0000 | [diff] [blame] | 33 |  | 
 | 34 | /// NON_EXISTANT_DIR - A special value distinct from null that is used to | 
 | 35 | /// represent a dir name that doesn't exist on the disk. | 
 | 36 | #define NON_EXISTANT_DIR reinterpret_cast<DirectoryEntry*>((intptr_t)-1) | 
 | 37 |  | 
 | 38 | /// getDirectory - Lookup, cache, and verify the specified directory.  This | 
 | 39 | /// returns null if the directory doesn't exist. | 
 | 40 | ///  | 
 | 41 | const DirectoryEntry *FileManager::getDirectory(const char *NameStart, | 
 | 42 |                                                 const char *NameEnd) { | 
 | 43 |   ++NumDirLookups; | 
 | 44 |   llvm::StringMapEntry<DirectoryEntry *> &NamedDirEnt = | 
 | 45 |     DirEntries.GetOrCreateValue(NameStart, NameEnd); | 
 | 46 |    | 
 | 47 |   // See if there is already an entry in the map. | 
 | 48 |   if (NamedDirEnt.getValue()) | 
 | 49 |     return NamedDirEnt.getValue() == NON_EXISTANT_DIR | 
 | 50 |               ? 0 : NamedDirEnt.getValue(); | 
 | 51 |    | 
 | 52 |   ++NumDirCacheMisses; | 
 | 53 |    | 
 | 54 |   // By default, initialize it to invalid. | 
 | 55 |   NamedDirEnt.setValue(NON_EXISTANT_DIR); | 
 | 56 |    | 
 | 57 |   // Get the null-terminated directory name as stored as the key of the | 
 | 58 |   // DirEntries map. | 
 | 59 |   const char *InterndDirName = NamedDirEnt.getKeyData(); | 
 | 60 |    | 
 | 61 |   // Check to see if the directory exists. | 
 | 62 |   struct stat StatBuf; | 
 | 63 |   if (stat(InterndDirName, &StatBuf) ||   // Error stat'ing. | 
 | 64 |       !S_ISDIR(StatBuf.st_mode))          // Not a directory? | 
 | 65 |     return 0; | 
 | 66 |    | 
 | 67 |   // It exists.  See if we have already opened a directory with the same inode. | 
| Ted Kremenek | da99544 | 2007-12-18 20:45:25 +0000 | [diff] [blame] | 68 |   // This occurs when one dir is symlinked to another, for example.     | 
| Reid Spencer | 5f016e2 | 2007-07-11 17:01:13 +0000 | [diff] [blame] | 69 |   DirectoryEntry &UDE =  | 
 | 70 |     UniqueDirs[std::make_pair(StatBuf.st_dev, StatBuf.st_ino)]; | 
 | 71 |    | 
 | 72 |   NamedDirEnt.setValue(&UDE); | 
 | 73 |   if (UDE.getName()) // Already have an entry with this inode, return it. | 
 | 74 |     return &UDE; | 
 | 75 |    | 
 | 76 |   // Otherwise, we don't have this directory yet, add it.  We use the string | 
 | 77 |   // key from the DirEntries map as the string. | 
 | 78 |   UDE.Name  = InterndDirName; | 
 | 79 |   return &UDE; | 
 | 80 | } | 
 | 81 |  | 
 | 82 | /// NON_EXISTANT_FILE - A special value distinct from null that is used to | 
 | 83 | /// represent a filename that doesn't exist on the disk. | 
 | 84 | #define NON_EXISTANT_FILE reinterpret_cast<FileEntry*>((intptr_t)-1) | 
 | 85 |  | 
 | 86 | /// getFile - Lookup, cache, and verify the specified file.  This returns null | 
 | 87 | /// if the file doesn't exist. | 
 | 88 | ///  | 
 | 89 | const FileEntry *FileManager::getFile(const char *NameStart, | 
 | 90 |                                       const char *NameEnd) { | 
 | 91 |   ++NumFileLookups; | 
 | 92 |    | 
 | 93 |   // See if there is already an entry in the map. | 
 | 94 |   llvm::StringMapEntry<FileEntry *> &NamedFileEnt = | 
 | 95 |     FileEntries.GetOrCreateValue(NameStart, NameEnd); | 
 | 96 |  | 
 | 97 |   // See if there is already an entry in the map. | 
 | 98 |   if (NamedFileEnt.getValue()) | 
 | 99 |     return NamedFileEnt.getValue() == NON_EXISTANT_FILE | 
 | 100 |                  ? 0 : NamedFileEnt.getValue(); | 
 | 101 |    | 
 | 102 |   ++NumFileCacheMisses; | 
 | 103 |  | 
 | 104 |   // By default, initialize it to invalid. | 
 | 105 |   NamedFileEnt.setValue(NON_EXISTANT_FILE); | 
 | 106 |  | 
 | 107 |   // Figure out what directory it is in.   If the string contains a / in it, | 
 | 108 |   // strip off everything after it. | 
 | 109 |   // FIXME: this logic should be in sys::Path. | 
 | 110 |   const char *SlashPos = NameEnd-1; | 
 | 111 |   while (SlashPos >= NameStart && SlashPos[0] != '/') | 
 | 112 |     --SlashPos; | 
 | 113 |    | 
 | 114 |   const DirectoryEntry *DirInfo; | 
 | 115 |   if (SlashPos < NameStart) { | 
 | 116 |     // Use the current directory if file has no path component. | 
 | 117 |     const char *Name = "."; | 
 | 118 |     DirInfo = getDirectory(Name, Name+1); | 
 | 119 |   } else if (SlashPos == NameEnd-1) | 
 | 120 |     return 0;       // If filename ends with a /, it's a directory. | 
 | 121 |   else | 
 | 122 |     DirInfo = getDirectory(NameStart, SlashPos); | 
 | 123 |    | 
 | 124 |   if (DirInfo == 0)  // Directory doesn't exist, file can't exist. | 
 | 125 |     return 0; | 
 | 126 |    | 
 | 127 |   // Get the null-terminated file name as stored as the key of the | 
 | 128 |   // FileEntries map. | 
 | 129 |   const char *InterndFileName = NamedFileEnt.getKeyData(); | 
 | 130 |    | 
 | 131 |   // FIXME: Use the directory info to prune this, before doing the stat syscall. | 
 | 132 |   // FIXME: This will reduce the # syscalls. | 
 | 133 |    | 
 | 134 |   // Nope, there isn't.  Check to see if the file exists. | 
 | 135 |   struct stat StatBuf; | 
 | 136 |   //std::cerr << "STATING: " << Filename; | 
 | 137 |   if (stat(InterndFileName, &StatBuf) ||   // Error stat'ing. | 
 | 138 |       S_ISDIR(StatBuf.st_mode)) {           // A directory? | 
 | 139 |     // If this file doesn't exist, we leave a null in FileEntries for this path. | 
 | 140 |     //std::cerr << ": Not existing\n"; | 
 | 141 |     return 0; | 
 | 142 |   } | 
 | 143 |   //std::cerr << ": exists\n"; | 
 | 144 |    | 
| Ted Kremenek | bca6d12 | 2007-12-18 22:29:39 +0000 | [diff] [blame] | 145 |   // It exists.  See if we have already opened a file with the same inode. | 
| Reid Spencer | 5f016e2 | 2007-07-11 17:01:13 +0000 | [diff] [blame] | 146 |   // This occurs when one dir is symlinked to another, for example. | 
| Ted Kremenek | bca6d12 | 2007-12-18 22:29:39 +0000 | [diff] [blame] | 147 |   FileEntry &UFE =  | 
 | 148 |     const_cast<FileEntry&>(*UniqueFiles.insert(FileEntry(StatBuf.st_dev, | 
 | 149 |                                                          StatBuf.st_ino)).first); | 
 | 150 |  | 
| Reid Spencer | 5f016e2 | 2007-07-11 17:01:13 +0000 | [diff] [blame] | 151 |    | 
 | 152 |   NamedFileEnt.setValue(&UFE); | 
 | 153 |   if (UFE.getName())  // Already have an entry with this inode, return it. | 
 | 154 |     return &UFE; | 
 | 155 |  | 
 | 156 |   // Otherwise, we don't have this directory yet, add it. | 
 | 157 |   // FIXME: Change the name to be a char* that points back to the 'FileEntries' | 
 | 158 |   // key. | 
 | 159 |   UFE.Name    = InterndFileName; | 
 | 160 |   UFE.Size    = StatBuf.st_size; | 
 | 161 |   UFE.ModTime = StatBuf.st_mtime; | 
 | 162 |   UFE.Dir     = DirInfo; | 
 | 163 |   UFE.UID     = NextFileUID++; | 
 | 164 |   return &UFE; | 
 | 165 | } | 
 | 166 |  | 
 | 167 | void FileManager::PrintStats() const { | 
 | 168 |   std::cerr << "\n*** File Manager Stats:\n"; | 
 | 169 |   std::cerr << UniqueFiles.size() << " files found, " | 
 | 170 |             << UniqueDirs.size() << " dirs found.\n"; | 
 | 171 |   std::cerr << NumDirLookups << " dir lookups, " | 
 | 172 |             << NumDirCacheMisses << " dir cache misses.\n"; | 
 | 173 |   std::cerr << NumFileLookups << " file lookups, " | 
 | 174 |             << NumFileCacheMisses << " file cache misses.\n"; | 
 | 175 |    | 
 | 176 |   //std::cerr << PagesMapped << BytesOfPagesMapped << FSLookups; | 
 | 177 | } |