zcov: / lib/Basic/FileManager.cpp


Files: 1 Branches Taken: 74.5% 70 / 94
Generated: 2010-02-10 01:31 Branches Executed: 83.0% 78 / 94
Line Coverage: 91.5% 118 / 129


Programs: 2 Runs 3018


       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             5167: 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            14963:   DirectoryEntry &getDirectory(const char *Name, struct stat &StatBuf) {
     110            14963:     return UniqueDirs[std::make_pair(StatBuf.st_dev, StatBuf.st_ino)];
     111                 :   }
     112                 : 
     113                2:   size_t size() { return UniqueDirs.size(); }
     114                 : };
     115                 : 
     116             5167: class FileManager::UniqueFileContainer {
     117                 :   /// UniqueFiles - Cache from ID's to existing directories/files.
     118                 :   ///
     119                 :   std::set<FileEntry> UniqueFiles;
     120                 : 
     121                 : public:
     122             2998:   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             2998:                                                   StatBuf.st_mode)).first);
     128                 :   }
     129                 : 
     130                2:   size_t size() { return UniqueFiles.size(); }
     131                 : };
     132                 : 
     133                 : #endif
     134                 : 
     135                 : //===----------------------------------------------------------------------===//
     136                 : // Common logic.
     137                 : //===----------------------------------------------------------------------===//
     138                 : 
     139             2585: FileManager::FileManager()
     140                 :   : UniqueDirs(*new UniqueDirContainer),
     141                 :     UniqueFiles(*new UniqueFileContainer),
     142             2585:     DirEntries(64), FileEntries(64), NextFileUID(0) {
     143             2585:   NumDirLookups = NumFileLookups = 0;
     144             2585:   NumDirCacheMisses = NumFileCacheMisses = 0;
     145             2585: }
     146                 : 
     147             2582: FileManager::~FileManager() {
                     2582: branch 0 taken
                        0: branch 1 not taken
                     2582: branch 4 taken
                     2582: branch 5 taken
     148             2582:   delete &UniqueDirs;
                     2582: branch 0 taken
                        0: branch 1 not taken
                     2582: branch 4 taken
                     2582: branch 5 taken
     149             2582:   delete &UniqueFiles;
                       12: branch 0 taken
                     2582: branch 1 taken
                     2582: branch 2 taken
                     2582: branch 3 taken
     150             2594:   for (llvm::SmallVectorImpl<FileEntry *>::iterator
     151             2582:          V = VirtualFileEntries.begin(),
     152             2582:          VEnd = VirtualFileEntries.end();
     153                 :        V != VEnd; 
     154                 :        ++V)
     155               12:     delete *V;
     156             2582: }
     157                 : 
     158               92: void FileManager::addStatCache(StatSysCallCache *statCache, bool AtBeginning) {
                        0: branch 0 not taken
                       92: branch 1 taken
     159               92:   assert(statCache && "No stat cache provided?");
                       47: branch 0 taken
                       45: branch 1 taken
                       46: branch 3 taken
                        1: branch 4 taken
                       91: branch 5 taken
                        1: branch 6 taken
     160               92:   if (AtBeginning || StatCache.get() == 0) {
     161               91:     statCache->setNextStatCache(StatCache.take());
     162               91:     StatCache.reset(statCache);
     163               91:     return;
     164                 :   }
     165                 :   
     166                1:   StatSysCallCache *LastCache = StatCache.get();
                        0: branch 1 not taken
                        1: branch 2 taken
     167                2:   while (LastCache->getNextStatCache())
     168                0:     LastCache = LastCache->getNextStatCache();
     169                 :   
     170                1:   LastCache->setNextStatCache(statCache);
     171                 : }
     172                 : 
     173                1: void FileManager::removeStatCache(StatSysCallCache *statCache) {
                        1: branch 0 taken
                        0: branch 1 not taken
     174                1:   if (!statCache)
     175                0:     return;
     176                 :   
                        1: branch 1 taken
                        0: branch 2 not taken
     177                1:   if (StatCache.get() == statCache) {
     178                 :     // This is the first stat cache.
     179                1:     StatCache.reset(StatCache->takeNextStatCache());
     180                1:     return;
     181                 :   }
     182                 :   
     183                 :   // Find the stat cache in the list.
     184                0:   StatSysCallCache *PrevCache = StatCache.get();
                        0: branch 0 not taken
                        0: branch 1 not taken
                        0: branch 3 not taken
                        0: branch 4 not taken
                        0: branch 5 not taken
                        0: branch 6 not taken
     185                0:   while (PrevCache && PrevCache->getNextStatCache() != statCache)
     186                0:     PrevCache = PrevCache->getNextStatCache();
                        0: branch 0 not taken
                        0: branch 1 not taken
     187                0:   if (PrevCache)
     188                0:     PrevCache->setNextStatCache(statCache->getNextStatCache());
     189                 :   else
     190                0:     assert(false && "Stat cache not found for removal");
     191                 : }
     192                 : 
     193                 : /// \brief Retrieve the directory that the given file name resides in.
     194                 : static const DirectoryEntry *getDirectoryFromFile(FileManager &FileMgr,
     195                 :                                                   const char *NameStart,
     196            43150:                                                   const char *NameEnd) {
     197                 :   // Figure out what directory it is in.   If the string contains a / in it,
     198                 :   // strip off everything after it.
     199                 :   // FIXME: this logic should be in sys::Path.
     200            43150:   const char *SlashPos = NameEnd-1;
                   245910: branch 0 taken
                        1: branch 1 taken
                   202761: branch 2 taken
                    43149: branch 3 taken
     201           289061:   while (SlashPos >= NameStart && !IS_DIR_SEPARATOR_CHAR(SlashPos[0]))
     202           202761:     --SlashPos;
     203                 :   // Ignore duplicate //'s.
                    43149: branch 0 taken
                        1: branch 1 taken
                        0: branch 2 not taken
                    43149: branch 3 taken
     204            86300:   while (SlashPos > NameStart && IS_DIR_SEPARATOR_CHAR(SlashPos[-1]))
     205                0:     --SlashPos;
     206                 : 
                        1: branch 0 taken
                    43149: branch 1 taken
     207            43150:   if (SlashPos < NameStart) {
     208                 :     // Use the current directory if file has no path component.
     209                1:     const char *Name = ".";
     210                1:     return FileMgr.getDirectory(Name, Name+1);
                    13539: branch 0 taken
                    29610: branch 1 taken
     211            43149:   } else if (SlashPos == NameEnd-1)
     212            13539:     return 0;       // If filename ends with a /, it's a directory.
     213                 :   else
     214            29610:     return FileMgr.getDirectory(NameStart, SlashPos);
     215                 : }
     216                 : 
     217                 : /// getDirectory - Lookup, cache, and verify the specified directory.  This
     218                 : /// returns null if the directory doesn't exist.
     219                 : ///
     220                 : const DirectoryEntry *FileManager::getDirectory(const char *NameStart,
     221            95214:                                                 const char *NameEnd) {
     222                 :   // stat doesn't like trailing separators (at least on Windows).
                    95211: branch 0 taken
                        3: branch 1 taken
                    80882: branch 2 taken
                    14329: branch 3 taken
                        0: branch 4 not taken
                    80882: branch 5 taken
     223            95214:   if (((NameEnd - NameStart) > 1) &&
     224                 :       ((*(NameEnd - 1) == '/') || (*(NameEnd - 1) == '\\')))
     225            14329:     NameEnd--;
     226                 : 
     227            95214:   ++NumDirLookups;
     228                 :   llvm::StringMapEntry<DirectoryEntry *> &NamedDirEnt =
     229            95214:     DirEntries.GetOrCreateValue(NameStart, NameEnd);
     230                 : 
     231                 :   // See if there is already an entry in the map.
                    32175: branch 1 taken
                    63039: branch 2 taken
     232            95214:   if (NamedDirEnt.getValue())
     233                 :     return NamedDirEnt.getValue() == NON_EXISTENT_DIR
                    11905: branch 1 taken
                    20270: branch 2 taken
     234            32175:               ? 0 : NamedDirEnt.getValue();
     235                 : 
     236            63039:   ++NumDirCacheMisses;
     237                 : 
     238                 :   // By default, initialize it to invalid.
     239            63039:   NamedDirEnt.setValue(NON_EXISTENT_DIR);
     240                 : 
     241                 :   // Get the null-terminated directory name as stored as the key of the
     242                 :   // DirEntries map.
     243            63039:   const char *InterndDirName = NamedDirEnt.getKeyData();
     244                 : 
     245                 :   // Check to see if the directory exists.
     246                 :   struct stat StatBuf;
                    14963: branch 1 taken
                    48076: branch 2 taken
                        0: branch 3 not taken
                    14963: branch 4 taken
                    48076: branch 5 taken
                    14963: branch 6 taken
     247            63039:   if (stat_cached(InterndDirName, &StatBuf) ||   // Error stat'ing.
     248                 :       !S_ISDIR(StatBuf.st_mode))          // Not a directory?
     249            48076:     return 0;
     250                 : 
     251                 :   // It exists.  See if we have already opened a directory with the same inode.
     252                 :   // This occurs when one dir is symlinked to another, for example.
     253            14963:   DirectoryEntry &UDE = UniqueDirs.getDirectory(InterndDirName, StatBuf);
     254                 : 
     255            14963:   NamedDirEnt.setValue(&UDE);
                     1581: branch 1 taken
                    13382: branch 2 taken
     256            14963:   if (UDE.getName()) // Already have an entry with this inode, return it.
     257             1581:     return &UDE;
     258                 : 
     259                 :   // Otherwise, we don't have this directory yet, add it.  We use the string
     260                 :   // key from the DirEntries map as the string.
     261            13382:   UDE.Name  = InterndDirName;
     262            13382:   return &UDE;
     263                 : }
     264                 : 
     265                 : /// NON_EXISTENT_FILE - A special value distinct from null that is used to
     266                 : /// represent a filename that doesn't exist on the disk.
     267                 : #define NON_EXISTENT_FILE reinterpret_cast<FileEntry*>((intptr_t)-1)
     268                 : 
     269                 : /// getFile - Lookup, cache, and verify the specified file.  This returns null
     270                 : /// if the file doesn't exist.
     271                 : ///
     272                 : const FileEntry *FileManager::getFile(const char *NameStart,
     273            53007:                                       const char *NameEnd) {
     274            53007:   ++NumFileLookups;
     275                 : 
     276                 :   // See if there is already an entry in the map.
     277                 :   llvm::StringMapEntry<FileEntry *> &NamedFileEnt =
     278            53007:     FileEntries.GetOrCreateValue(NameStart, NameEnd);
     279                 : 
     280                 :   // See if there is already an entry in the map.
                     9869: branch 1 taken
                    43138: branch 2 taken
     281            53007:   if (NamedFileEnt.getValue())
     282                 :     return NamedFileEnt.getValue() == NON_EXISTENT_FILE
                      382: branch 1 taken
                     9487: branch 2 taken
     283             9869:                  ? 0 : NamedFileEnt.getValue();
     284                 : 
     285            43138:   ++NumFileCacheMisses;
     286                 : 
     287                 :   // By default, initialize it to invalid.
     288            43138:   NamedFileEnt.setValue(NON_EXISTENT_FILE);
     289                 : 
     290                 : 
     291                 :   // Get the null-terminated file name as stored as the key of the
     292                 :   // FileEntries map.
     293            43138:   const char *InterndFileName = NamedFileEnt.getKeyData();
     294                 : 
     295                 :   const DirectoryEntry *DirInfo
     296            43138:     = getDirectoryFromFile(*this, NameStart, NameEnd);
                    32535: branch 0 taken
                    10603: branch 1 taken
     297            43138:   if (DirInfo == 0)  // Directory doesn't exist, file can't exist.
     298            32535:     return 0;
     299                 : 
     300                 :   // FIXME: Use the directory info to prune this, before doing the stat syscall.
     301                 :   // FIXME: This will reduce the # syscalls.
     302                 : 
     303                 :   // Nope, there isn't.  Check to see if the file exists.
     304                 :   struct stat StatBuf;
     305                 :   //llvm::errs() << "STATING: " << Filename;
                     2998: branch 1 taken
                     7605: branch 2 taken
                        0: branch 3 not taken
                     2998: branch 4 taken
                     7605: branch 5 taken
                     2998: branch 6 taken
     306            10603:   if (stat_cached(InterndFileName, &StatBuf) ||   // Error stat'ing.
     307                 :         S_ISDIR(StatBuf.st_mode)) {           // A directory?
     308                 :     // If this file doesn't exist, we leave a null in FileEntries for this path.
     309                 :     //llvm::errs() << ": Not existing\n";
     310             7605:     return 0;
     311                 :   }
     312                 :   //llvm::errs() << ": exists\n";
     313                 : 
     314                 :   // It exists.  See if we have already opened a file with the same inode.
     315                 :   // This occurs when one dir is symlinked to another, for example.
     316             2998:   FileEntry &UFE = UniqueFiles.getFile(InterndFileName, StatBuf);
     317                 : 
     318             2998:   NamedFileEnt.setValue(&UFE);
                        1: branch 1 taken
                     2997: branch 2 taken
     319             2998:   if (UFE.getName())  // Already have an entry with this inode, return it.
     320                1:     return &UFE;
     321                 : 
     322                 :   // Otherwise, we don't have this directory yet, add it.
     323                 :   // FIXME: Change the name to be a char* that points back to the 'FileEntries'
     324                 :   // key.
     325             2997:   UFE.Name    = InterndFileName;
     326             2997:   UFE.Size    = StatBuf.st_size;
     327             2997:   UFE.ModTime = StatBuf.st_mtime;
     328             2997:   UFE.Dir     = DirInfo;
     329             2997:   UFE.UID     = NextFileUID++;
     330             2997:   return &UFE;
     331                 : }
     332                 : 
     333                 : const FileEntry *
     334                 : FileManager::getVirtualFile(const llvm::StringRef &Filename,
     335               12:                             off_t Size, time_t ModificationTime) {
     336               12:   const char *NameStart = Filename.begin(), *NameEnd = Filename.end();
     337                 : 
     338               12:   ++NumFileLookups;
     339                 : 
     340                 :   // See if there is already an entry in the map.
     341                 :   llvm::StringMapEntry<FileEntry *> &NamedFileEnt =
     342               12:     FileEntries.GetOrCreateValue(NameStart, NameEnd);
     343                 : 
     344                 :   // See if there is already an entry in the map.
                        0: branch 1 not taken
                       12: branch 2 taken
     345               12:   if (NamedFileEnt.getValue())
     346                 :     return NamedFileEnt.getValue() == NON_EXISTENT_FILE
                        0: branch 1 not taken
                        0: branch 2 not taken
     347                0:                  ? 0 : NamedFileEnt.getValue();
     348                 : 
     349               12:   ++NumFileCacheMisses;
     350                 : 
     351                 :   // By default, initialize it to invalid.
     352               12:   NamedFileEnt.setValue(NON_EXISTENT_FILE);
     353                 : 
     354                 :   const DirectoryEntry *DirInfo
     355               12:     = getDirectoryFromFile(*this, NameStart, NameEnd);
                        0: branch 0 not taken
                       12: branch 1 taken
     356               12:   if (DirInfo == 0)  // Directory doesn't exist, file can't exist.
     357                0:     return 0;
     358                 : 
     359               12:   FileEntry *UFE = new FileEntry();
     360               12:   VirtualFileEntries.push_back(UFE);
     361               12:   NamedFileEnt.setValue(UFE);
     362                 : 
     363               12:   UFE->Name    = NamedFileEnt.getKeyData();
     364               12:   UFE->Size    = Size;
     365               12:   UFE->ModTime = ModificationTime;
     366               12:   UFE->Dir     = DirInfo;
     367               12:   UFE->UID     = NextFileUID++;
     368               12:   return UFE;
     369                 : }
     370                 : 
     371                2: void FileManager::PrintStats() const {
     372                2:   llvm::errs() << "\n*** File Manager Stats:\n";
     373                 :   llvm::errs() << UniqueFiles.size() << " files found, "
     374                2:                << UniqueDirs.size() << " dirs found.\n";
     375                 :   llvm::errs() << NumDirLookups << " dir lookups, "
     376                2:                << NumDirCacheMisses << " dir cache misses.\n";
     377                 :   llvm::errs() << NumFileLookups << " file lookups, "
     378                2:                << NumFileCacheMisses << " file cache misses.\n";
     379                 : 
     380                 :   //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups;
     381                2: }
     382                 : 
     383               91: int MemorizeStatCalls::stat(const char *path, struct stat *buf) {
     384               91:   int result = StatSysCallCache::stat(path, buf);
     385                 :   
     386                 :   // Do not cache failed stats, it is easy to construct common inconsistent
     387                 :   // situations if we do, and they are not important for PCH performance (which
     388                 :   // currently only needs the stats to construct the initial FileManager
     389                 :   // entries).
                        1: branch 0 taken
                       90: branch 1 taken
     390               91:   if (result != 0)
     391                1:     return result;
     392                 : 
     393                 :   // Cache file 'stat' results and directories with absolutely paths.
                       43: branch 0 taken
                       47: branch 1 taken
                       43: branch 5 taken
                        0: branch 6 not taken
                       43: branch 7 taken
                       47: branch 8 taken
                       90: branch 10 taken
                        0: branch 11 not taken
     394               90:   if (!S_ISDIR(buf->st_mode) || llvm::sys::Path(path).isAbsolute())
     395               90:     StatCalls[path] = StatResult(result, *buf);
     396                 : 
     397               90:   return result;
     398                 : }

Generated: 2010-02-10 01:31 by zcov