Don't use a potentially expensive shift if all we want is one set bit.
[oota-llvm.git] / tools / llvm-symbolizer / LLVMSymbolize.cpp
index 1b0ece5fc568467d1fd1c0c7fa3dc7dc4af8c0fc..1945d689dee4d8d7365093d8850d0c5f15c2e421 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "LLVMSymbolize.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/Object/MachO.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 
 #include <sstream>
 namespace llvm {
 namespace symbolize {
 
-static uint32_t getDILineInfoSpecifierFlags(
-    const LLVMSymbolizer::Options &Opts) {
+static bool error(error_code ec) {
+  if (!ec)
+    return false;
+  errs() << "LLVMSymbolizer: error reading file: " << ec.message() << ".\n";
+  return true;
+}
+
+static uint32_t
+getDILineInfoSpecifierFlags(const LLVMSymbolizer::Options &Opts) {
   uint32_t Flags = llvm::DILineInfoSpecifier::FileLineInfo |
                    llvm::DILineInfoSpecifier::AbsoluteFilePath;
   if (Opts.PrintFunctions)
@@ -37,8 +46,66 @@ static void patchFunctionNameInDILineInfo(const std::string &NewFunctionName,
                         LineInfo.getLine(), LineInfo.getColumn());
 }
 
-DILineInfo ModuleInfo::symbolizeCode(uint64_t ModuleOffset,
-    const LLVMSymbolizer::Options& Opts) const {
+ModuleInfo::ModuleInfo(ObjectFile *Obj, DIContext *DICtx)
+    : Module(Obj), DebugInfoContext(DICtx) {
+  error_code ec;
+  for (symbol_iterator si = Module->begin_symbols(), se = Module->end_symbols();
+       si != se; si.increment(ec)) {
+    if (error(ec))
+      return;
+    SymbolRef::Type SymbolType;
+    if (error(si->getType(SymbolType)))
+      continue;
+    if (SymbolType != SymbolRef::ST_Function &&
+        SymbolType != SymbolRef::ST_Data)
+      continue;
+    uint64_t SymbolAddress;
+    if (error(si->getAddress(SymbolAddress)) ||
+        SymbolAddress == UnknownAddressOrSize)
+      continue;
+    uint64_t SymbolSize;
+    // Getting symbol size is linear for Mach-O files, so assume that symbol
+    // occupies the memory range up to the following symbol.
+    if (isa<MachOObjectFile>(Obj))
+      SymbolSize = 0;
+    else if (error(si->getSize(SymbolSize)) ||
+             SymbolSize == UnknownAddressOrSize)
+      continue;
+    StringRef SymbolName;
+    if (error(si->getName(SymbolName)))
+      continue;
+    // Mach-O symbol table names have leading underscore, skip it.
+    if (Module->isMachO() && SymbolName.size() > 0 && SymbolName[0] == '_')
+      SymbolName = SymbolName.drop_front();
+    // FIXME: If a function has alias, there are two entries in symbol table
+    // with same address size. Make sure we choose the correct one.
+    SymbolMapTy &M = SymbolType == SymbolRef::ST_Function ? Functions : Objects;
+    SymbolDesc SD = { SymbolAddress, SymbolSize };
+    M.insert(std::make_pair(SD, SymbolName));
+  }
+}
+
+bool ModuleInfo::getNameFromSymbolTable(SymbolRef::Type Type, uint64_t Address,
+                                        std::string &Name, uint64_t &Addr,
+                                        uint64_t &Size) const {
+  const SymbolMapTy &M = Type == SymbolRef::ST_Function ? Functions : Objects;
+  if (M.empty())
+    return false;
+  SymbolDesc SD = { Address, Address };
+  SymbolMapTy::const_iterator it = M.upper_bound(SD);
+  if (it == M.begin())
+    return false;
+  --it;
+  if (it->first.Size != 0 && it->first.Addr + it->first.Size <= Address)
+    return false;
+  Name = it->second.str();
+  Addr = it->first.Addr;
+  Size = it->first.Size;
+  return true;
+}
+
+DILineInfo ModuleInfo::symbolizeCode(
+    uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const {
   DILineInfo LineInfo;
   if (DebugInfoContext) {
     LineInfo = DebugInfoContext->getLineInfoForAddress(
@@ -48,16 +115,16 @@ DILineInfo ModuleInfo::symbolizeCode(uint64_t ModuleOffset,
   if (Opts.PrintFunctions && Opts.UseSymbolTable) {
     std::string FunctionName;
     uint64_t Start, Size;
-    if (getNameFromSymbolTable(SymbolRef::ST_Function,
-                               ModuleOffset, FunctionName, Start, Size)) {
+    if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
+                               FunctionName, Start, Size)) {
       patchFunctionNameInDILineInfo(FunctionName, LineInfo);
     }
   }
   return LineInfo;
 }
 
-DIInliningInfo ModuleInfo::symbolizeInlinedCode(uint64_t ModuleOffset,
-    const LLVMSymbolizer::Options& Opts) const {
+DIInliningInfo ModuleInfo::symbolizeInlinedCode(
+    uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const {
   DIInliningInfo InlinedContext;
   if (DebugInfoContext) {
     InlinedContext = DebugInfoContext->getInliningInfoForAddress(
@@ -70,14 +137,13 @@ DIInliningInfo ModuleInfo::symbolizeInlinedCode(uint64_t ModuleOffset,
   // Override the function name in lower frame with name from symbol table.
   if (Opts.PrintFunctions && Opts.UseSymbolTable) {
     DIInliningInfo PatchedInlinedContext;
-    for (uint32_t i = 0, n = InlinedContext.getNumberOfFrames();
-         i < n; i++) {
+    for (uint32_t i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) {
       DILineInfo LineInfo = InlinedContext.getFrame(i);
       if (i == n - 1) {
         std::string FunctionName;
         uint64_t Start, Size;
-        if (getNameFromSymbolTable(SymbolRef::ST_Function,
-                                   ModuleOffset, FunctionName, Start, Size)) {
+        if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
+                                   FunctionName, Start, Size)) {
           patchFunctionNameInDILineInfo(FunctionName, LineInfo);
         }
       }
@@ -90,49 +156,11 @@ DIInliningInfo ModuleInfo::symbolizeInlinedCode(uint64_t ModuleOffset,
 
 bool ModuleInfo::symbolizeData(uint64_t ModuleOffset, std::string &Name,
                                uint64_t &Start, uint64_t &Size) const {
-  return getNameFromSymbolTable(SymbolRef::ST_Data,
-                                ModuleOffset, Name, Start, Size);
-}
-
-static bool error(error_code ec) {
-  if (!ec) return false;
-  errs() << "LLVMSymbolizer: error reading file: " << ec.message() << ".\n";
-  return true;
-}
-
-bool ModuleInfo::getNameFromSymbolTable(SymbolRef::Type Type, uint64_t Address,
-                                        std::string &Name, uint64_t &Addr,
-                                        uint64_t &Size) const {
-  assert(Module);
-  error_code ec;
-  for (symbol_iterator si = Module->begin_symbols(),
-                       se = Module->end_symbols();
-                       si != se; si.increment(ec)) {
-    if (error(ec)) return false;
-    uint64_t SymbolAddress;
-    uint64_t SymbolSize;
-    SymbolRef::Type SymbolType;
-    if (error(si->getAddress(SymbolAddress)) ||
-        SymbolAddress == UnknownAddressOrSize) continue;
-    if (error(si->getSize(SymbolSize)) ||
-        SymbolSize == UnknownAddressOrSize) continue;
-    if (error(si->getType(SymbolType))) continue;
-    // FIXME: If a function has alias, there are two entries in symbol table
-    // with same address size. Make sure we choose the correct one.
-    if (SymbolAddress <= Address && Address < SymbolAddress + SymbolSize &&
-        SymbolType == Type) {
-      StringRef SymbolName;
-      if (error(si->getName(SymbolName))) continue;
-      Name = SymbolName.str();
-      Addr = SymbolAddress;
-      Size = SymbolSize;
-      return true;
-    }
-  }
-  return false;
+  return getNameFromSymbolTable(SymbolRef::ST_Data, ModuleOffset, Name, Start,
+                                Size);
 }
 
-const std::string LLVMSymbolizer::kBadString = "??";
+const char LLVMSymbolizer::kBadString[] = "??";
 
 std::string LLVMSymbolizer::symbolizeCode(const std::string &ModuleName,
                                           uint64_t ModuleOffset) {
@@ -140,8 +168,8 @@ std::string LLVMSymbolizer::symbolizeCode(const std::string &ModuleName,
   if (Info == 0)
     return printDILineInfo(DILineInfo());
   if (Opts.PrintInlining) {
-    DIInliningInfo InlinedContext = Info->symbolizeInlinedCode(
-        ModuleOffset, Opts);
+    DIInliningInfo InlinedContext =
+        Info->symbolizeInlinedCode(ModuleOffset, Opts);
     uint32_t FramesNum = InlinedContext.getNumberOfFrames();
     assert(FramesNum > 0);
     std::string Result;
@@ -162,8 +190,8 @@ std::string LLVMSymbolizer::symbolizeData(const std::string &ModuleName,
   uint64_t Size = 0;
   if (Opts.UseSymbolTable) {
     if (ModuleInfo *Info = getOrCreateModuleInfo(ModuleName)) {
-      if (Info->symbolizeData(ModuleOffset, Name, Start, Size))
-        DemangleName(Name);
+      if (Info->symbolizeData(ModuleOffset, Name, Start, Size) && Opts.Demangle)
+        Name = DemangleName(Name);
     }
   }
   std::stringstream ss;
@@ -171,21 +199,14 @@ std::string LLVMSymbolizer::symbolizeData(const std::string &ModuleName,
   return ss.str();
 }
 
-// Returns true if the object endianness is known.
-static bool getObjectEndianness(const ObjectFile *Obj,
-                                bool &IsLittleEndian) {
-  // FIXME: Implement this when libLLVMObject allows to do it easily.
-  IsLittleEndian = true;
-  return true;
+void LLVMSymbolizer::flush() {
+  DeleteContainerSeconds(Modules);
+  DeleteContainerPointers(ParsedBinariesAndObjects);
+  BinaryForPath.clear();
+  ObjectFileForArch.clear();
 }
 
-static ObjectFile *getObjectFile(const std::string &Path) {
-  OwningPtr<MemoryBuffer> Buff;
-  MemoryBuffer::getFile(Path, Buff);
-  return ObjectFile::createObjectFile(Buff.take());
-}
-
-static std::string getDarwinDWARFResourceForModule(const std::string &Path) {
+static std::string getDarwinDWARFResourceForPath(const std::string &Path) {
   StringRef Basename = sys::path::filename(Path);
   const std::string &DSymDirectory = Path + ".dSYM";
   SmallString<16> ResourceName = StringRef(DSymDirectory);
@@ -194,36 +215,90 @@ static std::string getDarwinDWARFResourceForModule(const std::string &Path) {
   return ResourceName.str();
 }
 
-ModuleInfo *LLVMSymbolizer::getOrCreateModuleInfo(
-    const std::string &ModuleName) {
-  ModuleMapTy::iterator I = Modules.find(ModuleName);
-  if (I != Modules.end())
+LLVMSymbolizer::BinaryPair
+LLVMSymbolizer::getOrCreateBinary(const std::string &Path) {
+  BinaryMapTy::iterator I = BinaryForPath.find(Path);
+  if (I != BinaryForPath.end())
     return I->second;
+  Binary *Bin = 0;
+  Binary *DbgBin = 0;
+  OwningPtr<Binary> ParsedBinary;
+  OwningPtr<Binary> ParsedDbgBinary;
+  if (!error(createBinary(Path, ParsedBinary))) {
+    // Check if it's a universal binary.
+    Bin = ParsedBinary.take();
+    ParsedBinariesAndObjects.push_back(Bin);
+    if (Bin->isMachO() || Bin->isMachOUniversalBinary()) {
+      // On Darwin we may find DWARF in separate object file in
+      // resource directory.
+      const std::string &ResourcePath =
+          getDarwinDWARFResourceForPath(Path);
+      bool ResourceFileExists = false;
+      if (!sys::fs::exists(ResourcePath, ResourceFileExists) &&
+          ResourceFileExists &&
+          !error(createBinary(ResourcePath, ParsedDbgBinary))) {
+        DbgBin = ParsedDbgBinary.take();
+        ParsedBinariesAndObjects.push_back(DbgBin);
+      }
+    }
+  }
+  if (DbgBin == 0)
+    DbgBin = Bin;
+  BinaryPair Res = std::make_pair(Bin, DbgBin);
+  BinaryForPath[Path] = Res;
+  return Res;
+}
 
-  ObjectFile *Obj = getObjectFile(ModuleName);
-  ObjectFile *DbgObj = Obj;
-  if (Obj == 0) {
-    // Module name doesn't point to a valid object file.
-    Modules.insert(make_pair(ModuleName, (ModuleInfo*)0));
+ObjectFile *
+LLVMSymbolizer::getObjectFileFromBinary(Binary *Bin, const std::string &ArchName) {
+  if (Bin == 0)
     return 0;
+  ObjectFile *Res = 0;
+  if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(Bin)) {
+    ObjectFileForArchMapTy::iterator I = ObjectFileForArch.find(
+        std::make_pair(UB, ArchName));
+    if (I != ObjectFileForArch.end())
+      return I->second;
+    OwningPtr<ObjectFile> ParsedObj;
+    if (!UB->getObjectForArch(Triple(ArchName).getArch(), ParsedObj)) {
+      Res = ParsedObj.take();
+      ParsedBinariesAndObjects.push_back(Res);
+    }
+    ObjectFileForArch[std::make_pair(UB, ArchName)] = Res;
+  } else if (Bin->isObject()) {
+    Res = cast<ObjectFile>(Bin);
   }
+  return Res;
+}
 
-  DIContext *Context = 0;
-  bool IsLittleEndian;
-  if (getObjectEndianness(Obj, IsLittleEndian)) {
-    // On Darwin we may find DWARF in separate object file in
-    // resource directory.
-    if (isa<MachOObjectFile>(Obj)) {
-      const std::string &ResourceName = getDarwinDWARFResourceForModule(
-          ModuleName);
-      ObjectFile *ResourceObj = getObjectFile(ResourceName);
-      if (ResourceObj != 0)
-        DbgObj = ResourceObj;
-    }
-    Context = DIContext::getDWARFContext(DbgObj);
-    assert(Context);
+ModuleInfo *
+LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {
+  ModuleMapTy::iterator I = Modules.find(ModuleName);
+  if (I != Modules.end())
+    return I->second;
+  std::string BinaryName = ModuleName;
+  std::string ArchName = Opts.DefaultArch;
+  size_t ColonPos = ModuleName.find(':');
+#if defined(_WIN32)
+  // Recognize a drive letter on win32.
+  if (ColonPos == 1 && isalpha(ModuleName[0]))
+    ColonPos = ModuleName.find(':', 2);
+#endif
+  if (ColonPos != std::string::npos) {
+    BinaryName = ModuleName.substr(0, ColonPos);
+    ArchName = ModuleName.substr(ColonPos + 1);
   }
+  BinaryPair Binaries = getOrCreateBinary(BinaryName);
+  ObjectFile *Obj = getObjectFileFromBinary(Binaries.first, ArchName);
+  ObjectFile *DbgObj = getObjectFileFromBinary(Binaries.second, ArchName);
 
+  if (Obj == 0) {
+    // Failed to find valid object file.
+    Modules.insert(make_pair(ModuleName, (ModuleInfo *)0));
+    return 0;
+  }
+  DIContext *Context = DIContext::getDWARFContext(DbgObj);
+  assert(Context);
   ModuleInfo *Info = new ModuleInfo(Obj, Context);
   Modules.insert(make_pair(ModuleName, Info));
   return Info;
@@ -238,14 +313,15 @@ std::string LLVMSymbolizer::printDILineInfo(DILineInfo LineInfo) const {
     std::string FunctionName = LineInfo.getFunctionName();
     if (FunctionName == kDILineInfoBadString)
       FunctionName = kBadString;
-    DemangleName(FunctionName);
+    else if (Opts.Demangle)
+      FunctionName = DemangleName(FunctionName);
     Result << FunctionName << "\n";
   }
   std::string Filename = LineInfo.getFileName();
   if (Filename == kDILineInfoBadString)
     Filename = kBadString;
-  Result << Filename << ":" << LineInfo.getLine()
-                     << ":" << LineInfo.getColumn() << "\n";
+  Result << Filename << ":" << LineInfo.getLine() << ":" << LineInfo.getColumn()
+         << "\n";
   return Result.str();
 }
 
@@ -255,18 +331,19 @@ extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer,
                                 size_t *length, int *status);
 #endif
 
-void LLVMSymbolizer::DemangleName(std::string &Name) const {
+std::string LLVMSymbolizer::DemangleName(const std::string &Name) {
 #if !defined(_MSC_VER)
-  if (!Opts.Demangle)
-    return;
   int status = 0;
   char *DemangledName = __cxa_demangle(Name.c_str(), 0, 0, &status);
   if (status != 0)
-    return;
-  Name = DemangledName;
+    return Name;
+  std::string Result = DemangledName;
   free(DemangledName);
+  return Result;
+#else
+  return Name;
 #endif
 }
 
-}  // namespace symbolize
-}  // namespace llvm
+} // namespace symbolize
+} // namespace llvm