diff --git a/llvm/include/llvm/DebugInfo/Symbolize/SymbolizableModule.h b/llvm/include/llvm/DebugInfo/Symbolize/SymbolizableModule.h --- a/llvm/include/llvm/DebugInfo/Symbolize/SymbolizableModule.h +++ b/llvm/include/llvm/DebugInfo/Symbolize/SymbolizableModule.h @@ -37,7 +37,7 @@ symbolizeFrame(object::SectionedAddress ModuleOffset) const = 0; virtual std::vector - findSymbol(StringRef Symbol) const = 0; + findSymbol(StringRef Symbol, uint64_t Offset) const = 0; // Return true if this is a 32-bit x86 PE COFF module. virtual bool isWin32Module() const = 0; diff --git a/llvm/include/llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h b/llvm/include/llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h --- a/llvm/include/llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h +++ b/llvm/include/llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h @@ -44,7 +44,7 @@ std::vector symbolizeFrame(object::SectionedAddress ModuleOffset) const override; std::vector - findSymbol(StringRef Symbol) const override; + findSymbol(StringRef Symbol, uint64_t Offset) const override; // Return true if this is a 32-bit x86 PE COFF module. bool isWin32Module() const override; diff --git a/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h b/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h --- a/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h +++ b/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h @@ -105,12 +105,12 @@ symbolizeFrame(ArrayRef BuildID, object::SectionedAddress ModuleOffset); - Expected> findSymbol(const ObjectFile &Obj, - StringRef Symbol); - Expected> findSymbol(StringRef ModuleName, - StringRef Symbol); - Expected> findSymbol(ArrayRef BuildID, - StringRef Symbol); + Expected> + findSymbol(const ObjectFile &Obj, StringRef Symbol, uint64_t Offset); + Expected> + findSymbol(const std::string &ModuleName, StringRef Symbol, uint64_t Offset); + Expected> + findSymbol(ArrayRef BuildID, StringRef Symbol, uint64_t Offset); void flush(); @@ -155,8 +155,8 @@ symbolizeFrameCommon(const T &ModuleSpecifier, object::SectionedAddress ModuleOffset); template - Expected> findSymbolCommon(const T &ModuleSpecifier, - StringRef Symbol); + Expected> + findSymbolCommon(const T &ModuleSpecifier, StringRef Symbol, uint64_t Offset); Expected getOrCreateModuleInfo(const ObjectFile &Obj); diff --git a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp --- a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp +++ b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp @@ -352,12 +352,14 @@ } std::vector -SymbolizableObjectFile::findSymbol(StringRef Symbol) const { +SymbolizableObjectFile::findSymbol(StringRef Symbol, uint64_t Offset) const { std::vector Result; for (const SymbolDesc &Sym : Symbols) { if (Sym.Name.equals(Symbol)) { - object::SectionedAddress A{Sym.Addr, - getModuleSectionIndexForAddress(Sym.Addr)}; + uint64_t Addr = Sym.Addr; + if (Offset < Sym.Size) + Addr += Offset; + object::SectionedAddress A{Addr, getModuleSectionIndexForAddress(Addr)}; Result.push_back(A); } } diff --git a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp --- a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp +++ b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -233,7 +233,8 @@ template Expected> -LLVMSymbolizer::findSymbolCommon(const T &ModuleSpecifier, StringRef Symbol) { +LLVMSymbolizer::findSymbolCommon(const T &ModuleSpecifier, StringRef Symbol, + uint64_t Offset) { auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier); if (!InfoOrErr) return InfoOrErr.takeError(); @@ -246,7 +247,7 @@ if (!Info) return Result; - for (object::SectionedAddress A : Info->findSymbol(Symbol)) { + for (object::SectionedAddress A : Info->findSymbol(Symbol, Offset)) { DILineInfo LineInfo = Info->symbolizeCode( A, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions), Opts.UseSymbolTable); @@ -261,18 +262,21 @@ } Expected> -LLVMSymbolizer::findSymbol(const ObjectFile &Obj, StringRef Symbol) { - return findSymbolCommon(Obj, Symbol); +LLVMSymbolizer::findSymbol(const ObjectFile &Obj, StringRef Symbol, + uint64_t Offset) { + return findSymbolCommon(Obj, Symbol, Offset); } Expected> -LLVMSymbolizer::findSymbol(StringRef ModuleName, StringRef Symbol) { - return findSymbolCommon(ModuleName.str(), Symbol); +LLVMSymbolizer::findSymbol(const std::string &ModuleName, StringRef Symbol, + uint64_t Offset) { + return findSymbolCommon(ModuleName, Symbol, Offset); } Expected> -LLVMSymbolizer::findSymbol(ArrayRef BuildID, StringRef Symbol) { - return findSymbolCommon(BuildID, Symbol); +LLVMSymbolizer::findSymbol(ArrayRef BuildID, StringRef Symbol, + uint64_t Offset) { + return findSymbolCommon(BuildID, Symbol, Offset); } void LLVMSymbolizer::flush() { diff --git a/llvm/test/tools/llvm-symbolizer/symbol-search.test b/llvm/test/tools/llvm-symbolizer/symbol-search.test --- a/llvm/test/tools/llvm-symbolizer/symbol-search.test +++ b/llvm/test/tools/llvm-symbolizer/symbol-search.test @@ -34,6 +34,40 @@ FUNCS: /tmp/dbginfo{{[/\]+}}symbols.part1.cpp:12 FUNCS: /tmp/dbginfo{{[/\]+}}symbols.part2.cpp:10 +# Symbol may be combined with offset. +RUN: llvm-symbolizer --obj=%p/Inputs/symbols.so func_01+0 | FileCheck --check-prefix=SYMLINE0 %s +RUN: llvm-addr2line -e %p/Inputs/symbols.so func_01+0 | FileCheck --check-prefix=SYMLINE0 %s +SYMLINE0: /tmp/dbginfo{{[/\]+}}symbols.part1.cpp:12 + +RUN: llvm-symbolizer --obj=%p/Inputs/symbols.so func_01+12 | FileCheck --check-prefix=SYMLINE1 %s +RUN: llvm-addr2line -e %p/Inputs/symbols.so func_01+12 | FileCheck --check-prefix=SYMLINE1 %s +SYMLINE1: /tmp/dbginfo{{[/\]+}}symbols.part1.cpp:13 + +RUN: llvm-symbolizer --obj=%p/Inputs/symbols.so func_01+19 | FileCheck --check-prefix=SYMLINE2 %s +RUN: llvm-addr2line -e %p/Inputs/symbols.so func_01+19 | FileCheck --check-prefix=SYMLINE2 %s +SYMLINE2: /tmp/dbginfo{{[/\]+}}symbols.part1.cpp:14 + +# Offset can be specified with various bases. +RUN: llvm-symbolizer --obj=%p/Inputs/symbols.so func_01+0x0C | FileCheck --check-prefix=SYMLINE1 %s +RUN: llvm-addr2line -e %p/Inputs/symbols.so func_01+0x0C | FileCheck --check-prefix=SYMLINE1 %s + +RUN: llvm-symbolizer --obj=%p/Inputs/symbols.so func_01+014 | FileCheck --check-prefix=SYMLINE1 %s +RUN: llvm-addr2line -e %p/Inputs/symbols.so func_01+014 | FileCheck --check-prefix=SYMLINE1 %s + +# If '+' is not followed by a number, it is a part of symbol, not an offset separator. +RUN: llvm-symbolizer --obj=%p/Inputs/symbols.so func_01+ | FileCheck --check-prefix=NONEXISTENT %s +RUN: llvm-addr2line --obj=%p/Inputs/symbols.so func_01+ | FileCheck --check-prefix=NONEXISTENT %s + +RUN: llvm-symbolizer --obj=%p/Inputs/symbols.so func_01+abc | FileCheck --check-prefix=NONEXISTENT %s +RUN: llvm-addr2line --obj=%p/Inputs/symbols.so func_01+abc | FileCheck --check-prefix=NONEXISTENT %s + +RUN: llvm-symbolizer --obj=%p/Inputs/symbols.so func_01+0A | FileCheck --check-prefix=NONEXISTENT %s +RUN: llvm-addr2line --obj=%p/Inputs/symbols.so func_01+0A | FileCheck --check-prefix=NONEXISTENT %s + +# If '+' is not preceded by a symbol, it is a part of symbol, not an offset separator. +RUN: llvm-symbolizer --obj=%p/Inputs/symbols.so +0x1138 | FileCheck --check-prefix=NONEXISTENT %s +RUN: llvm-addr2line --obj=%p/Inputs/symbols.so +0x1138 | FileCheck --check-prefix=NONEXISTENT %s + # Show that C++ mangled names may be specified. RUN: llvm-addr2line --obj=%p/Inputs/symbols.so _ZL14static_func_01i | FileCheck --check-prefix=MULTI-CXX %s RUN: llvm-symbolizer --obj=%p/Inputs/symbols.so _ZL14static_func_01i | FileCheck --check-prefix=MULTI-CXX %s 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 @@ -159,7 +159,7 @@ static Error parseCommand(StringRef BinaryName, bool IsAddr2Line, StringRef InputString, Command &Cmd, std::string &ModuleName, object::BuildID &BuildID, - StringRef &Symbol, uint64_t &ModuleOffset) { + StringRef &Symbol, uint64_t &Offset) { ModuleName = BinaryName; if (InputString.consume_front("CODE ")) { Cmd = Command::Code; @@ -224,25 +224,51 @@ return makeStringError("no input filename has been specified"); } - // Parse module offset, which can be specified as a number or as a symbol. - InputString = InputString.ltrim(); + // Parse address specification, which can be an offset in module or a + // symbol with optional offset. + InputString = InputString.trim(); if (InputString.empty()) return makeStringError("no module offset has been specified"); // If input string contains a space, ignore everything after it. This behavior // is consistent with GNU addr2line. - int OffsetLength = InputString.find_first_of(" \n\r"); - StringRef Offset = InputString.substr(0, OffsetLength); + int AddrSpecLength = InputString.find_first_of(" \n\r"); + StringRef AddrSpec = InputString.substr(0, AddrSpecLength); + bool StartsWithDigit = std::isdigit(AddrSpec.front()); - // GNU addr2line assumes the offset is hexadecimal and allows a redundant + // GNU addr2line assumes the address is hexadecimal and allows a redundant // "0x" or "0X" prefix; do the same for compatibility. if (IsAddr2Line) - Offset.consume_front("0x") || Offset.consume_front("0X"); + AddrSpec.consume_front("0x") || AddrSpec.consume_front("0X"); - // If the input is not a number, treat it is a symbol. - if (Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset)) { - Symbol = Offset; - ModuleOffset = 0; + // If address specification is a number, treat it as a module offset. + if (!AddrSpec.getAsInteger(IsAddr2Line ? 16 : 0, Offset)) { + // Module offset is an address. + Symbol = StringRef(); + return Error::success(); + } + + // If address specification starts with a digit, but is not a number, consider + // it as invalid. + if (StartsWithDigit || AddrSpec.empty()) + return makeStringError("expected a number as module offset"); + + // Otherwise it is a symbol name, potentially with an offset. + Symbol = AddrSpec; + Offset = 0; + + // If the address specification contains '+', try treating it as + // "symbol + offset". + size_t Plus = AddrSpec.rfind('+'); + if (Plus != StringRef::npos) { + StringRef SymbolStr = AddrSpec.take_front(Plus); + StringRef OffsetStr = AddrSpec.substr(Plus + 1); + if (!SymbolStr.empty() && !OffsetStr.empty() && + !OffsetStr.getAsInteger(0, Offset)) { + Symbol = SymbolStr; + return Error::success(); + } + // The found '+' is not an offset delimiter. } return Error::success(); @@ -268,7 +294,7 @@ print(SymRequest, ResOrErr, Printer); } else if (!Symbol.empty()) { Expected> ResOrErr = - Symbolizer.findSymbol(ModuleSpec, Symbol); + Symbolizer.findSymbol(ModuleSpec, Symbol, Offset); print(SymRequest, ResOrErr, Printer); } else if (ShouldInline) { Expected ResOrErr = diff --git a/llvm/unittests/ProfileData/MemProfTest.cpp b/llvm/unittests/ProfileData/MemProfTest.cpp --- a/llvm/unittests/ProfileData/MemProfTest.cpp +++ b/llvm/unittests/ProfileData/MemProfTest.cpp @@ -54,7 +54,8 @@ virtual std::vector symbolizeFrame(SectionedAddress) const { llvm_unreachable("unused"); } - virtual std::vector findSymbol(StringRef Symbol) const { + virtual std::vector findSymbol(StringRef Symbol, + uint64_t Offset) const { llvm_unreachable("unused"); } virtual bool isWin32Module() const { llvm_unreachable("unused"); }