diff --git a/llvm/docs/CommandGuide/llvm-symbolizer.rst b/llvm/docs/CommandGuide/llvm-symbolizer.rst --- a/llvm/docs/CommandGuide/llvm-symbolizer.rst +++ b/llvm/docs/CommandGuide/llvm-symbolizer.rst @@ -28,6 +28,10 @@ input or as positional arguments on the command-line, following any "DATA" or "CODE" prefix. +Prefixing the above with "BUILD_ID" causes the object file to be interpreted as +a hex build ID rather than a path. This will look up the corresponding debug +binary. + :program:`llvm-symbolizer` parses options from the environment variable ``LLVM_SYMBOLIZER_OPTS`` after parsing options from the command line. ``LLVM_SYMBOLIZER_OPTS`` is primarily useful for supplementing the command-line @@ -147,7 +151,18 @@ bar 6295592 4 -Example 5 - path-style options: +Example 5 - BUILD_ID prefix: + +.. code-block:: console + + $ llvm-symbolizer "BUILD_ID 123456789abcdef 0x400490" "BUILD_ID DATA 123456789abcdef 0x601028" + baz() + /tmp/test.cpp:11:0 + + bar + 6295592 4 + +Example 6 - path-style options: This example uses the same source file as above, but the source file's full path is /tmp/foo/test.cpp and is compiled as follows. The first case diff --git a/llvm/test/tools/llvm-symbolizer/debuginfod.test b/llvm/test/tools/llvm-symbolizer/debuginfod.test --- a/llvm/test/tools/llvm-symbolizer/debuginfod.test +++ b/llvm/test/tools/llvm-symbolizer/debuginfod.test @@ -27,11 +27,24 @@ RUN: FileCheck %s --check-prefix=FOUND FOUND: {{[/\]+}}tmp{{[/\]+}}x.c:14:0 -# This should also work if the build ID is provided. +# This should also work if the build ID is provided via flag. RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-symbolizer \ RUN: --build-id=127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d | \ RUN: FileCheck %s --check-prefix=FOUND +# This should also work if the build ID is provided via stdin. +RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-symbolizer \ +RUN: "BUILD_ID 127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d" | \ +RUN: FileCheck %s --check-prefix=FOUND + # The symbolizer shouldn't call the debuginfod library by default with no URLs. RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-symbolizer --print-address \ RUN: --obj=%t/addr.exe 0x40054d | FileCheck %s --check-prefix=NOTFOUND + +# The symbolizer shouldn't call the debuginfod library if explicitly disabled. +RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-symbolizer \ +RUN: --no-debuginfod \ +RUN: "BUILD_ID 127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d" | \ +RUN: FileCheck %s --check-prefix=NOTHINGFOUND +NOTHINGFOUND: ?? +NOTHINGFOUND-NEXT: ??:0:0 diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp --- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -105,11 +105,34 @@ Frame, }; -static bool parseCommand(StringRef BinaryName, ArrayRef BuildID, - bool IsAddr2Line, StringRef InputString, Command &Cmd, - std::string &ModuleName, uint64_t &ModuleOffset) { +static void enableDebuginfod(LLVMSymbolizer &Symbolizer) { + static bool IsEnabled = false; + if (IsEnabled) + return; + IsEnabled = true; + // Look up symbols using the debuginfod client. + Symbolizer.addDIFetcher(std::make_unique()); + // The HTTPClient must be initialized for use by the debuginfod client. + HTTPClient::initialize(); +} + +static SmallVector parseBuildID(StringRef Str) { + std::string Bytes; + if (!tryGetFromHex(Str, Bytes)) + return {}; + ArrayRef BuildID(reinterpret_cast(Bytes.data()), + Bytes.size()); + return SmallVector(BuildID.begin(), BuildID.end()); +} + +static bool parseCommand(StringRef BinaryName, bool IsAddr2Line, + StringRef InputString, Command &Cmd, + std::string &ModuleName, + SmallVectorImpl &BuildID, + uint64_t &ModuleOffset) { const char kDelimiters[] = " \n\r"; ModuleName = ""; + bool NameIsBuildID = InputString.consume_front("BUILD_ID "); if (InputString.consume_front("CODE ")) { Cmd = Command::Code; } else if (InputString.consume_front("DATA ")) { @@ -137,6 +160,12 @@ ModuleName = std::string(Pos, NameLength); Pos += NameLength; } + if (NameIsBuildID) { + BuildID = parseBuildID(ModuleName); + if (BuildID.empty()) + return false; + ModuleName.clear(); + } } else { ModuleName = BinaryName.str(); } @@ -193,21 +222,24 @@ } static void symbolizeInput(const opt::InputArgList &Args, - ArrayRef BuildID, uint64_t AdjustVMA, - bool IsAddr2Line, OutputStyle Style, - StringRef InputString, LLVMSymbolizer &Symbolizer, - DIPrinter &Printer) { + ArrayRef IncomingBuildID, + uint64_t AdjustVMA, bool IsAddr2Line, + OutputStyle Style, StringRef InputString, + LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { Command Cmd; std::string ModuleName; + SmallVector BuildID(IncomingBuildID.begin(), IncomingBuildID.end()); uint64_t Offset = 0; - if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), BuildID, IsAddr2Line, - StringRef(InputString), Cmd, ModuleName, Offset)) { + if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line, + StringRef(InputString), Cmd, ModuleName, BuildID, Offset)) { Printer.printInvalidCommand({ModuleName, None}, InputString); return; } bool ShouldInline = Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line); if (!BuildID.empty()) { assert(ModuleName.empty()); + if (!Args.hasArg(OPT_no_debuginfod)) + enableDebuginfod(Symbolizer); std::string BuildIDStr = toHex(BuildID); executeCommand(BuildIDStr, BuildID, Cmd, Offset, AdjustVMA, ShouldInline, Style, Symbolizer, Printer); @@ -281,43 +313,23 @@ return IsAddr2Line ? FunctionNameKind::None : FunctionNameKind::LinkageName; } -SmallVector parseBuildIDArg(const opt::InputArgList &Args, int ID) { - if (const opt::Arg *A = Args.getLastArg(ID)) { - StringRef V(A->getValue()); - std::string Bytes; - if (!tryGetFromHex(V, Bytes)) { - errs() << A->getSpelling() + ": expected a build ID, but got '" + V + - "'\n"; - exit(1); - } - ArrayRef BuildID(reinterpret_cast(Bytes.data()), - Bytes.size()); - return SmallVector(BuildID.begin(), BuildID.end()); +static SmallVector parseBuildIDArg(const opt::InputArgList &Args, + int ID) { + const opt::Arg *A = Args.getLastArg(ID); + if (!A) + return {}; + + StringRef V(A->getValue()); + SmallVector BuildID = parseBuildID(V); + if (BuildID.empty()) { + errs() << A->getSpelling() + ": expected a build ID, but got '" + V + "'\n"; + exit(1); } - return {}; + return BuildID; } ExitOnError ExitOnErr; -static bool shouldUseDebuginfodByDefault(ArrayRef BuildID) { - // If the user explicitly specified a build ID, the usual way to find it is - // debuginfod. - if (!BuildID.empty()) - return true; - - // A debuginfod lookup could succeed if a HTTP client is available and at - // least one backing URL is configured. - if (HTTPClient::isAvailable() && - !ExitOnErr(getDefaultDebuginfodUrls()).empty()) - return true; - - // A debuginfod lookup could also succeed if something were present in the - // cache directory, but it would be surprising to enable debuginfod on this - // basis alone. To use existing caches in an "offline" fashion, the debuginfod - // flag must be set. - return false; -} - int main(int argc, char **argv) { InitLLVM X(argc, argv); sys::InitializeCOMRAII COM(sys::COMThreadingMode::MultiThreaded); @@ -393,13 +405,14 @@ LLVMSymbolizer Symbolizer(Opts); + // A debuginfod lookup could succeed if a HTTP client is available and at + // least one backing URL is configured. + bool ShouldUseDebuginfodByDefault = + HTTPClient::isAvailable() && + !ExitOnErr(getDefaultDebuginfodUrls()).empty(); if (Args.hasFlag(OPT_debuginfod, OPT_no_debuginfod, - shouldUseDebuginfodByDefault(BuildID))) { - // Look up symbols using the debuginfod client. - Symbolizer.addDIFetcher(std::make_unique()); - // The HTTPClient must be initialized for use by the debuginfod client. - HTTPClient::initialize(); - } + ShouldUseDebuginfodByDefault)) + enableDebuginfod(Symbolizer); std::unique_ptr Printer; if (Style == OutputStyle::GNU)