Index: LLVMSymbolize.h =================================================================== --- LLVMSymbolize.h +++ LLVMSymbolize.h @@ -34,18 +34,24 @@ class LLVMSymbolizer { public: struct Options { + bool Verbose : 1; bool UseSymbolTable : 1; FunctionNameKind PrintFunctions; bool PrintInlining : 1; bool Demangle : 1; std::string DefaultArch; - Options(bool UseSymbolTable = true, + std::vector DsymHints; + Options(bool Verbose = false, bool UseSymbolTable = true, FunctionNameKind PrintFunctions = FunctionNameKind::LinkageName, bool PrintInlining = true, bool Demangle = true, std::string DefaultArch = "") - : UseSymbolTable(UseSymbolTable), PrintFunctions(PrintFunctions), - PrintInlining(PrintInlining), Demangle(Demangle), - DefaultArch(DefaultArch) {} + : Verbose(Verbose), UseSymbolTable(UseSymbolTable), + PrintFunctions(PrintFunctions), PrintInlining(PrintInlining), + Demangle(Demangle), DefaultArch(DefaultArch) {} + void InitDsymHints(const std::vector::iterator &HintBegin, + const std::vector::iterator &HintEnd) { + DsymHints.assign(HintBegin, HintEnd); + } }; LLVMSymbolizer(const Options &Opts = Options()) : Opts(Opts) {} @@ -63,10 +69,15 @@ static std::string DemangleName(const std::string &Name); private: typedef std::pair BinaryPair; + typedef std::pair ObjectPair; ModuleInfo *getOrCreateModuleInfo(const std::string &ModuleName); - /// \brief Returns pair of pointers to binary and debug binary. - BinaryPair getOrCreateBinary(const std::string &Path); + void populateDsymPaths(std::vector &Paths, const std::string &ExePath); + ObjectFile *lookUpDsymFile(const std::string &Path, ObjectFile *ExeObj, const std::string &ArchName); + + /// \brief Returns pair of pointers to object and debug object. + ObjectPair getOrCreateObjects(const std::string &Path, + const std::string &ArchName); /// \brief Returns a parsed object file for a given architecture in a /// universal binary (or the binary itself if it is an object file). ObjectFile *getObjectFileFromBinary(Binary *Bin, const std::string &ArchName); @@ -84,11 +95,12 @@ // Owns module info objects. typedef std::map ModuleMapTy; ModuleMapTy Modules; - typedef std::map BinaryMapTy; - BinaryMapTy BinaryForPath; typedef std::map, ObjectFile *> ObjectFileForArchMapTy; ObjectFileForArchMapTy ObjectFileForArch; + typedef std::map, ObjectPair> + ObjectPairForPathArchTy; + ObjectPairForPathArchTy ObjectPairForPathArch; Options Opts; static const char kBadString[]; Index: LLVMSymbolize.cpp =================================================================== --- LLVMSymbolize.cpp +++ LLVMSymbolize.cpp @@ -206,16 +206,24 @@ void LLVMSymbolizer::flush() { DeleteContainerSeconds(Modules); - BinaryForPath.clear(); + ObjectPairForPathArch.clear(); ObjectFileForArch.clear(); } +// For /path/to/foo and /path/to/foo.dSYM assume that debug info is in +// /path/to/foo.dSYM/Contents/Resources/DWARF/foo. 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); + SmallString<16> DsymDirectory; + if (sys::path::extension(Path) == ".dSYM") { + DsymDirectory = Path; + } else { + DsymDirectory = Path + ".dSYM"; + } + StringRef Basename = sys::path::filename(DsymDirectory); + SmallString<16> ResourceName = StringRef(DsymDirectory); sys::path::append(ResourceName, "Contents", "Resources", "DWARF"); sys::path::append(ResourceName, Basename); + sys::path::replace_extension(ResourceName, ""); return ResourceName.str(); } @@ -293,31 +301,93 @@ return false; } -LLVMSymbolizer::BinaryPair -LLVMSymbolizer::getOrCreateBinary(const std::string &Path) { - BinaryMapTy::iterator I = BinaryForPath.find(Path); - if (I != BinaryForPath.end()) +ErrorOr getUuidFromObjectFile(const ObjectFile *Obj) { + const MachOObjectFile *file = dyn_cast(Obj); + MachOObjectFile::LoadCommandInfo Command = file->getFirstLoadCommandInfo(); + MachO::mach_header Header = file->getHeader(); + unsigned ncmds = Header.ncmds; + for (unsigned i = 0;; ++i) { + if (Command.C.cmd == MachO::LC_UUID) + return file->getUuidCommand(Command); + if (i == ncmds - 1) + break; + else + Command = file->getNextLoadCommandInfo(Command); + } + return object_error::parse_failed; +} + +bool darwinDsymMatchesBinary(const ObjectFile *DbgObj, const ObjectFile *Obj) { + ErrorOr dbg_uuid_or_error = getUuidFromObjectFile(DbgObj); + ErrorOr bin_uuid_or_error = getUuidFromObjectFile(Obj); + if (error(dbg_uuid_or_error.getError()) || + error(bin_uuid_or_error.getError())) + return false; + return !memcmp(&(dbg_uuid_or_error.get()), &(bin_uuid_or_error.get()), sizeof(MachO::uuid_command)); +} + +void LLVMSymbolizer::populateDsymPaths(std::vector &Paths, const std::string &ExePath) { + const std::string &ResourcePath = + getDarwinDWARFResourceForPath(ExePath); + Paths.push_back(ResourcePath); + for (const std::string &path : Opts.DsymHints) { + if (sys::path::extension(path) == ".dSYM") + Paths.push_back(getDarwinDWARFResourceForPath(path)); + } +} + +ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath, + ObjectFile *ExeObj, const std::string &ArchName) { + ObjectFile *result = nullptr; + // On Darwin we may find DWARF in separate object file in + // resource directory. + std::vector dsym_paths; + populateDsymPaths(dsym_paths, ExePath); + for (const auto &path : dsym_paths) { + ErrorOr> BinaryOrErr = createBinary(path); + if (Opts.Verbose) + errs() << "Trying dSYM path: " << path << "\n"; + std::error_code EC = BinaryOrErr.getError(); + if (EC != errc::no_such_file_or_directory && !error(EC)) { + OwningBinary B = std::move(BinaryOrErr.get()); + Binary *DbgBin = B.getBinary().get(); + ObjectFile *DbgObj = getObjectFileFromBinary(DbgBin, ArchName); + if (darwinDsymMatchesBinary(DbgObj, ExeObj)) { + addOwningBinary(std::move(B)); + if (Opts.Verbose) + errs() << "Ok!\n"; + return DbgObj; + } else { + if (Opts.Verbose) + errs() << "Error: UUID mismatch!\n"; + } + } else { + if (Opts.Verbose) + errs() << "Error: no such file\n"; + } + } + return result; +} + +LLVMSymbolizer::ObjectPair +LLVMSymbolizer::getOrCreateObjects(const std::string &Path, const std::string &ArchName) { + ObjectPairForPathArchTy::iterator I = + ObjectPairForPathArch.find(std::make_pair(Path, ArchName)); + if (I != ObjectPairForPathArch.end()) return I->second; Binary *Bin = nullptr; Binary *DbgBin = nullptr; + ObjectFile *Obj = nullptr; + ObjectFile *DbgObj = nullptr; ErrorOr> BinaryOrErr = createBinary(Path); if (!error(BinaryOrErr.getError())) { OwningBinary &ParsedBinary = BinaryOrErr.get(); // Check if it's a universal binary. Bin = ParsedBinary.getBinary().get(); + Obj = getObjectFileFromBinary(Bin, ArchName); addOwningBinary(std::move(ParsedBinary)); if (Bin->isMachO() || Bin->isMachOUniversalBinary()) { - // On Darwin we may find DWARF in separate object file in - // resource directory. - const std::string &ResourcePath = - getDarwinDWARFResourceForPath(Path); - BinaryOrErr = createBinary(ResourcePath); - std::error_code EC = BinaryOrErr.getError(); - if (EC != errc::no_such_file_or_directory && !error(EC)) { - OwningBinary B = std::move(BinaryOrErr.get()); - DbgBin = B.getBinary().get(); - addOwningBinary(std::move(B)); - } + DbgObj = lookUpDsymFile(Path, Obj, ArchName); } // Try to locate the debug binary using .gnu_debuglink section. if (!DbgBin) { @@ -330,15 +400,17 @@ if (!error(BinaryOrErr.getError())) { OwningBinary B = std::move(BinaryOrErr.get()); DbgBin = B.getBinary().get(); + DbgObj = getObjectFileFromBinary(DbgBin, ArchName); addOwningBinary(std::move(B)); } } } } - if (!DbgBin) - DbgBin = Bin; - BinaryPair Res = std::make_pair(Bin, DbgBin); - BinaryForPath[Path] = Res; + if (!DbgObj) + DbgObj = Obj; + + ObjectPair Res = std::make_pair(Obj, DbgObj); + ObjectPairForPathArch[std::make_pair(Path, ArchName)] = Res; return Res; } @@ -381,18 +453,16 @@ ArchName = ArchStr; } } - BinaryPair Binaries = getOrCreateBinary(BinaryName); - ObjectFile *Obj = getObjectFileFromBinary(Binaries.first, ArchName); - ObjectFile *DbgObj = getObjectFileFromBinary(Binaries.second, ArchName); + ObjectPair Objects = getOrCreateObjects(BinaryName, ArchName); - if (!Obj) { + if (!Objects.first) { // Failed to find valid object file. Modules.insert(make_pair(ModuleName, (ModuleInfo *)nullptr)); return nullptr; } - DIContext *Context = DIContext::getDWARFContext(*DbgObj); + DIContext *Context = DIContext::getDWARFContext(*Objects.second); assert(Context); - ModuleInfo *Info = new ModuleInfo(Obj, Context); + ModuleInfo *Info = new ModuleInfo(Objects.first, Context); Modules.insert(make_pair(ModuleName, Info)); return Info; } Index: llvm-symbolizer.cpp =================================================================== --- llvm-symbolizer.cpp +++ llvm-symbolizer.cpp @@ -31,6 +31,9 @@ using namespace symbolize; static cl::opt +ClVerbose("v", cl::init(false), cl::desc("Verbose mode")); + +static cl::opt ClUseSymbolTable("use-symbol-table", cl::init(true), cl::desc("Prefer names in symbol table to names " "in debug info")); @@ -61,6 +64,11 @@ cl::desc("Path to object file to be symbolized (if not provided, " "object file should be specified for each input line)")); +static cl::list +ClDsymHint("dsym-hint", cl::ZeroOrMore, + cl::desc("Path to .dSYM bundles to search for debug info for the " + "object files")); + static bool parseCommand(bool &IsData, std::string &ModuleName, uint64_t &ModuleOffset) { const char *kDataCmd = "DATA "; @@ -117,8 +125,9 @@ llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. cl::ParseCommandLineOptions(argc, argv, "llvm-symbolizer\n"); - LLVMSymbolizer::Options Opts(ClUseSymbolTable, ClPrintFunctions, + LLVMSymbolizer::Options Opts(ClVerbose, ClUseSymbolTable, ClPrintFunctions, ClPrintInlining, ClDemangle, ClDefaultArch); + Opts.InitDsymHints(ClDsymHint.begin(), ClDsymHint.end()); LLVMSymbolizer Symbolizer(Opts); bool IsData = false;