Since we're stuck with realpath for the header <-> module mapping,

factor the realpath calls into FileManager::getCanonicalName() so we
can cache the results of this epically slow operation. 5% speedup on
my modules test, and realpath drops out of the profile.

llvm-svn: 173542
This commit is contained in:
Douglas Gregor
2013-01-26 00:55:12 +00:00
parent d0c842d7a4
commit e00c8b205e
4 changed files with 67 additions and 43 deletions

View File

@@ -17,6 +17,7 @@
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallVector.h"
@@ -152,6 +153,12 @@ class FileManager : public RefCountedBase<FileManager> {
/// \see SeenDirEntries
llvm::StringMap<FileEntry*, llvm::BumpPtrAllocator> SeenFileEntries;
/// \brief The canonical names of directories.
llvm::DenseMap<const DirectoryEntry *, llvm::StringRef> CanonicalDirNames;
/// \brief Storage for canonical names that we have computed.
llvm::BumpPtrAllocator CanonicalNameStorage;
/// \brief Each FileEntry we create is assigned a unique ID #.
///
unsigned NextFileUID;
@@ -257,6 +264,13 @@ public:
static void modifyFileEntry(FileEntry *File, off_t Size,
time_t ModificationTime);
/// \brief Retrieve the canonical name for a given directory.
///
/// This is a very expensive operation, despite its results being cached,
/// and should only be used when the physical layout of the file system is
/// required, which is (almost) never.
StringRef getCanonicalName(const DirectoryEntry *Dir);
void PrintStats() const;
};

View File

@@ -40,6 +40,11 @@
#define S_ISFIFO(x) (0)
#endif
#endif
#if defined(LLVM_ON_UNIX)
#if defined(__linux__)
#include <linux/limits.h>
#endif
#endif
using namespace clang;
// FIXME: Enhance libsystem to support inode and other fields.
@@ -620,6 +625,29 @@ void FileManager::modifyFileEntry(FileEntry *File,
File->ModTime = ModificationTime;
}
StringRef FileManager::getCanonicalName(const DirectoryEntry *Dir) {
// FIXME: use llvm::sys::fs::canonical() when it gets implemented
#ifdef LLVM_ON_UNIX
llvm::DenseMap<const DirectoryEntry *, llvm::StringRef>::iterator Known
= CanonicalDirNames.find(Dir);
if (Known != CanonicalDirNames.end())
return Known->second;
StringRef CanonicalName(Dir->getName());
char CanonicalNameBuf[PATH_MAX];
if (realpath(Dir->getName(), CanonicalNameBuf)) {
unsigned Len = strlen(CanonicalNameBuf);
char *Mem = static_cast<char *>(CanonicalNameStorage.Allocate(Len, 1));
memcpy(Mem, CanonicalNameBuf, Len);
CanonicalName = StringRef(Mem, Len);
}
CanonicalDirNames.insert(std::make_pair(Dir, CanonicalName));
return CanonicalName;
#else
return StringRef(Dir->getName());
#endif
}
void FileManager::PrintStats() const {
llvm::errs() << "\n*** File Manager Stats:\n";

View File

@@ -268,6 +268,10 @@ const FileEntry *DirectoryLookup::LookupFile(
return Result;
}
/// FIXME: HACK HACK HACK!
static llvm::DenseMap<const DirectoryEntry *, const DirectoryEntry *>
TopFrameworkDirs;
/// \brief Given a framework directory, find the top-most framework directory.
///
/// \param FileMgr The file manager to use for directory lookups.
@@ -280,7 +284,6 @@ getTopFrameworkDir(FileManager &FileMgr, StringRef DirName,
assert(llvm::sys::path::extension(DirName) == ".framework" &&
"Not a framework directory");
#ifdef LLVM_ON_UNIX
// Note: as an egregious but useful hack we use the real path here, because
// frameworks moving between top-level frameworks to embedded frameworks tend
// to be symlinked, and we base the logical structure of modules on the
@@ -295,12 +298,8 @@ getTopFrameworkDir(FileManager &FileMgr, StringRef DirName,
//
// Similar issues occur when a top-level framework has moved into an
// embedded framework.
char RealDirName[PATH_MAX];
if (realpath(DirName.str().c_str(), RealDirName))
DirName = RealDirName;
#endif
const DirectoryEntry *TopFrameworkDir = FileMgr.getDirectory(DirName);
DirName = FileMgr.getCanonicalName(TopFrameworkDir);
do {
// Get the parent directory name.
DirName = llvm::sys::path::parent_path(DirName);

View File

@@ -163,20 +163,12 @@ Module *ModuleMap::findModuleForHeader(const FileEntry *File) {
const DirectoryEntry *Dir = File->getDir();
SmallVector<const DirectoryEntry *, 2> SkippedDirs;
#ifdef LLVM_ON_UNIX
// Note: as an egregious but useful hack we use the real path here, because
// frameworks moving from top-level frameworks to embedded frameworks tend
// to be symlinked from the top-level location to the embedded location,
// and we need to resolve lookups as if we had found the embedded location.
char RealDirName[PATH_MAX];
StringRef DirName;
if (realpath(Dir->getName(), RealDirName))
DirName = RealDirName;
else
DirName = Dir->getName();
#else
StringRef DirName = Dir->getName();
#endif
StringRef DirName = SourceMgr->getFileManager().getCanonicalName(Dir);
// Keep walking up the directory hierarchy, looking for a directory with
// an umbrella header.
@@ -420,16 +412,13 @@ ModuleMap::inferFrameworkModule(StringRef ModuleName,
// a framework module, do so.
if (!Parent) {
// Determine whether we're allowed to infer a module map.
StringRef FrameworkDirName = FrameworkDir->getName();
#ifdef LLVM_ON_UNIX
// Note: as an egregious but useful hack we use the real path here, because
// we might be looking at an embedded framework that symlinks out to a
// top-level framework, and we need to infer as if we were naming the
// top-level framework.
char RealFrameworkDirName[PATH_MAX];
if (realpath(FrameworkDir->getName(), RealFrameworkDirName))
FrameworkDirName = RealFrameworkDirName;
#endif
StringRef FrameworkDirName
= SourceMgr->getFileManager().getCanonicalName(FrameworkDir);
bool canInfer = false;
if (llvm::sys::path::has_parent_path(FrameworkDirName)) {
@@ -527,29 +516,23 @@ ModuleMap::inferFrameworkModule(StringRef ModuleName,
// check whether it is actually a subdirectory of the parent directory.
// This will not be the case if the 'subframework' is actually a symlink
// out to a top-level framework.
#ifdef LLVM_ON_UNIX
char RealSubframeworkDirName[PATH_MAX];
if (realpath(Dir->path().c_str(), RealSubframeworkDirName)) {
StringRef SubframeworkDirName = RealSubframeworkDirName;
StringRef SubframeworkDirName = FileMgr.getCanonicalName(SubframeworkDir);
bool FoundParent = false;
do {
// Get the parent directory name.
SubframeworkDirName
= llvm::sys::path::parent_path(SubframeworkDirName);
if (SubframeworkDirName.empty())
break;
bool FoundParent = false;
do {
// Get the parent directory name.
SubframeworkDirName
= llvm::sys::path::parent_path(SubframeworkDirName);
if (SubframeworkDirName.empty())
break;
if (FileMgr.getDirectory(SubframeworkDirName) == FrameworkDir) {
FoundParent = true;
break;
}
} while (true);
if (FileMgr.getDirectory(SubframeworkDirName) == FrameworkDir) {
FoundParent = true;
break;
}
} while (true);
if (!FoundParent)
continue;
}
#endif
if (!FoundParent)
continue;
// FIXME: Do we want to warn about subframeworks without umbrella headers?
SmallString<32> NameBuf;