Index: docs/CommandGuide/llvm-symbolizer.rst =================================================================== --- docs/CommandGuide/llvm-symbolizer.rst +++ docs/CommandGuide/llvm-symbolizer.rst @@ -15,6 +15,42 @@ only addresses from standard input. This program uses debug info sections and symbol table in the object files. +OPTIONS +------- + +.. option:: -default-arch= + + Default architecture (for multi-arch objects). + +.. option:: -demangle + + Demangle function names. + +.. option:: -dsym-hint= + + Path to .dSYM bundle to search for debug info for the object files. + +.. option:: -functions={none,short,linkage} + + Print function name for a given address. + +.. option:: -inlining + + Print all inlined frames for a given address. + +.. option:: -obj + + Path to object file to be symbolized (if not provided, object file should be + specified for each input line). + +.. option:: -use-symbol-table + + Prefer names in symbol table to names in debug info. + +.. option:: -v + + Verbose mode. + EXAMPLE -------- Index: tools/llvm-symbolizer/LLVMSymbolize.h =================================================================== --- tools/llvm-symbolizer/LLVMSymbolize.h +++ tools/llvm-symbolizer/LLVMSymbolize.h @@ -34,18 +34,20 @@ 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) {} }; LLVMSymbolizer(const Options &Opts = Options()) : Opts(Opts) {} @@ -63,10 +65,17 @@ 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); + ObjectFile *lookUpDsymFile(const std::string &Path, ObjectFile *ExeObj, + const std::string &ArchName); + + void populateDsymPaths(const std::string &ExePath, + std::vector &OutPaths); + /// \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 +93,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: tools/llvm-symbolizer/LLVMSymbolize.cpp =================================================================== --- tools/llvm-symbolizer/LLVMSymbolize.cpp +++ tools/llvm-symbolizer/LLVMSymbolize.cpp @@ -206,14 +206,21 @@ void LLVMSymbolizer::flush() { DeleteContainerSeconds(Modules); - BinaryForPath.clear(); + ObjectPairForPathArch.clear(); ObjectFileForArch.clear(); } -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); +// For /path/to/foo assume that debug info is in +// /path/to/foo.dSYM/Contents/Resources/DWARF/foo. +// +// If the .dSYM bundle name is different from the executable name +static +std::string getDarwinDWARFResourceForPath( + const std::string &Path, const std::string &Basename) { + SmallString<16> ResourceName = StringRef(Path); + if (sys::path::extension(Path) != ".dSYM") { + ResourceName += ".dSYM"; + } sys::path::append(ResourceName, "Contents", "Resources", "DWARF"); sys::path::append(ResourceName, Basename); return ResourceName.str(); @@ -293,31 +300,95 @@ 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(const std::string &ExePath, + std::vector &OutPaths) { + StringRef filename = sys::path::filename(ExePath); + const std::string &resource_path = + getDarwinDWARFResourceForPath(ExePath, filename); + OutPaths.push_back(resource_path); + for (auto &path : Opts.DsymHints) { + if (sys::path::extension(path) == ".dSYM") + OutPaths.push_back(getDarwinDWARFResourceForPath(path, filename)); + } +} + +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(ExePath, dsym_paths); + 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 +401,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 +454,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: tools/llvm-symbolizer/llvm-symbolizer.cpp =================================================================== --- tools/llvm-symbolizer/llvm-symbolizer.cpp +++ tools/llvm-symbolizer/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.DsymHints.assign(ClDsymHint.begin(), ClDsymHint.end()); LLVMSymbolizer Symbolizer(Opts); bool IsData = false;