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 @@ -381,12 +381,14 @@ $ llvm-symbolizer --obj=inlined.elf --verbose 0x4004be baz() Filename: /tmp/test.cpp - Function start line: 9 + Function start line: 9 + Function start address: 0x4004b0 Line: 11 Column: 18 main Filename: /tmp/test.cpp - Function start line: 14 + Function start line: 14 + Function start address: 0x4004b0 Line: 15 Column: 0 diff --git a/llvm/include/llvm/DebugInfo/DIContext.h b/llvm/include/llvm/DebugInfo/DIContext.h --- a/llvm/include/llvm/DebugInfo/DIContext.h +++ b/llvm/include/llvm/DebugInfo/DIContext.h @@ -40,6 +40,7 @@ uint32_t Line = 0; uint32_t Column = 0; uint32_t StartLine = 0; + Optional StartAddress; // DWARF-specific. uint32_t Discriminator = 0; diff --git a/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h b/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h --- a/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h +++ b/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h @@ -78,6 +78,7 @@ const DILineInfo &Info) = 0; void printContext(SourceCode SourceCode); void printVerbose(StringRef Filename, const DILineInfo &Info); + virtual void printStartAddress(const DILineInfo &Info) {} virtual void printFooter() {} private: @@ -105,6 +106,7 @@ class LLVMPrinter : public PlainPrinterBase { private: void printSimpleLocation(StringRef Filename, const DILineInfo &Info) override; + void printStartAddress(const DILineInfo &Info) override; void printFooter() override; public: diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -1035,13 +1035,11 @@ /// TODO: change input parameter from "uint64_t Address" /// into "SectionedAddress Address" -static bool getFunctionNameAndStartLineForAddress(DWARFCompileUnit *CU, - uint64_t Address, - FunctionNameKind Kind, - DILineInfoSpecifier::FileLineInfoKind FileNameKind, - std::string &FunctionName, - std::string &StartFile, - uint32_t &StartLine) { +static bool getFunctionNameAndStartLineForAddress( + DWARFCompileUnit *CU, uint64_t Address, FunctionNameKind Kind, + DILineInfoSpecifier::FileLineInfoKind FileNameKind, + std::string &FunctionName, std::string &StartFile, uint32_t &StartLine, + Optional &StartAddress) { // The address may correspond to instruction in some inlined function, // so we have to build the chain of inlined functions and take the // name of the topmost function in it. @@ -1066,7 +1064,8 @@ StartLine = DeclLineResult; FoundResult = true; } - + if (auto LowPcAddr = toSectionedAddress(DIE.find(DW_AT_low_pc))) + StartAddress = LowPcAddr->Address; return FoundResult; } @@ -1233,9 +1232,9 @@ if (!CU) return Result; - getFunctionNameAndStartLineForAddress(CU, Address.Address, Spec.FNKind, Spec.FLIKind, - Result.FunctionName, - Result.StartFileName, Result.StartLine); + getFunctionNameAndStartLineForAddress( + CU, Address.Address, Spec.FNKind, Spec.FLIKind, Result.FunctionName, + Result.StartFileName, Result.StartLine, Result.StartAddress); if (Spec.FLIKind != FileLineInfoKind::None) { if (const DWARFLineTable *LineTable = getLineTableForUnit(CU)) { LineTable->getFileLineInfoForAddress( @@ -1248,7 +1247,7 @@ DILineInfoTable DWARFContext::getLineInfoForAddressRange( object::SectionedAddress Address, uint64_t Size, DILineInfoSpecifier Spec) { - DILineInfoTable Lines; + DILineInfoTable Lines; DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); if (!CU) return Lines; @@ -1256,8 +1255,10 @@ uint32_t StartLine = 0; std::string StartFileName; std::string FunctionName(DILineInfo::BadString); - getFunctionNameAndStartLineForAddress(CU, Address.Address, Spec.FNKind, Spec.FLIKind, - FunctionName, StartFileName, StartLine); + Optional StartAddress; + getFunctionNameAndStartLineForAddress(CU, Address.Address, Spec.FNKind, + Spec.FLIKind, FunctionName, + StartFileName, StartLine, StartAddress); // If the Specifier says we don't need FileLineInfo, just // return the top-most function at the starting address. @@ -1266,6 +1267,7 @@ Result.FunctionName = FunctionName; Result.StartFileName = StartFileName; Result.StartLine = StartLine; + Result.StartAddress = StartAddress; Lines.push_back(std::make_pair(Address.Address, Result)); return Lines; } @@ -1290,6 +1292,7 @@ Result.Column = Row.Column; Result.StartFileName = StartFileName; Result.StartLine = StartLine; + Result.StartAddress = StartAddress; Lines.push_back(std::make_pair(Row.Address.Address, Result)); } @@ -1332,6 +1335,8 @@ if (auto DeclLineResult = FunctionDIE.getDeclLine()) Frame.StartLine = DeclLineResult; Frame.StartFileName = FunctionDIE.getDeclFile(Spec.FLIKind); + if (auto LowPcAddr = toSectionedAddress(FunctionDIE.find(DW_AT_low_pc))) + Frame.StartAddress = LowPcAddr->Address; if (Spec.FLIKind != FileLineInfoKind::None) { if (i == 0) { // For the topmost frame, initialize the line table of this diff --git a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp --- a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp +++ b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp @@ -150,12 +150,21 @@ OS << " Function start filename: " << Info.StartFileName << '\n'; OS << " Function start line: " << Info.StartLine << '\n'; } + printStartAddress(Info); OS << " Line: " << Info.Line << '\n'; OS << " Column: " << Info.Column << '\n'; if (Info.Discriminator) OS << " Discriminator: " << Info.Discriminator << '\n'; } +void LLVMPrinter::printStartAddress(const DILineInfo &Info) { + if (Info.StartAddress) { + OS << " Function start address: 0x"; + OS.write_hex(*Info.StartAddress); + OS << '\n'; + } +} + void LLVMPrinter::printFooter() { OS << '\n'; } void PlainPrinterBase::print(const DILineInfo &Info, bool Inlined) { @@ -290,6 +299,8 @@ ? LineInfo.StartFileName : ""}, {"StartLine", LineInfo.StartLine}, + {"StartAddress", + LineInfo.StartAddress ? toHex(*LineInfo.StartAddress) : ""}, {"FileName", LineInfo.FileName != DILineInfo::BadString ? LineInfo.FileName : ""}, {"Line", LineInfo.Line}, 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 @@ -282,6 +282,7 @@ if (getNameFromSymbolTable(ModuleOffset.Address, FunctionName, Start, Size, FileName)) { LineInfo.FunctionName = FunctionName; + LineInfo.StartAddress = Start; if (LineInfo.FileName == DILineInfo::BadString && !FileName.empty()) LineInfo.FileName = FileName; } @@ -311,6 +312,7 @@ DILineInfo *LI = InlinedContext.getMutableFrame( InlinedContext.getNumberOfFrames() - 1); LI->FunctionName = FunctionName; + LI->StartAddress = Start; if (LI->FileName == DILineInfo::BadString && !FileName.empty()) LI->FileName = FileName; } diff --git a/llvm/test/tools/llvm-symbolizer/output-style-json-code.test b/llvm/test/tools/llvm-symbolizer/output-style-json-code.test --- a/llvm/test/tools/llvm-symbolizer/output-style-json-code.test +++ b/llvm/test/tools/llvm-symbolizer/output-style-json-code.test @@ -12,12 +12,12 @@ ## Expected a list with one empty object with default values. # RUN: llvm-symbolizer --output-style=JSON -e %p/Inputs/addr.exe 0x10000000 | \ # RUN: FileCheck %s --check-prefix=NOT-FOUND --strict-whitespace --match-full-lines --implicit-check-not={{.}} -# NOT-FOUND:[{"Address":"0x10000000","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartFileName":"","StartLine":0}]}] +# NOT-FOUND:[{"Address":"0x10000000","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]}] ## Check a non-zero discriminator. # RUN: llvm-symbolizer --output-style=JSON --obj=%p/Inputs/discrim 0x400575 | \ # RUN: FileCheck %s --check-prefix=DISCRIM --strict-whitespace --match-full-lines --implicit-check-not={{.}} -# DISCRIM:[{"Address":"0x400575","ModuleName":"{{.*}}/Inputs/discrim","Symbol":[{"Column":17,"Discriminator":2,"FileName":"/tmp{{/|\\\\}}discrim.c","FunctionName":"foo","Line":5,"StartFileName":"/tmp{{/|\\\\}}discrim.c","StartLine":4}]}] +# DISCRIM:[{"Address":"0x400575","ModuleName":"{{.*}}/Inputs/discrim","Symbol":[{"Column":17,"Discriminator":2,"FileName":"/tmp{{/|\\\\}}discrim.c","FunctionName":"foo","Line":5,"StartAddress":"0x400560","StartFileName":"/tmp{{/|\\\\}}discrim.c","StartLine":4}]}] ## In case of stdin input the output will contain a single JSON object for each input string. @@ -27,7 +27,7 @@ ## Invalid first argument before any valid one. # NO-INLINES:{"Error":{"Message":"unable to parse arguments: some text"},"ModuleName":"{{.*}}/Inputs/addr.exe"} ## Resolve valid address. -# NO-INLINES-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"main","Line":3,"StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2}]} +# NO-INLINES-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"main","Line":3,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2}]} ## Invalid argument after a valid one. # NO-INLINES-NEXT:{"Error":{"Message":"unable to parse arguments: some text2"},"ModuleName":"{{.*}}/Inputs/addr.exe"} @@ -37,7 +37,7 @@ ## Invalid first argument before any valid one. # INLINE:{"Error":{"Message":"unable to parse arguments: some text"},"ModuleName":"{{.*}}/Inputs/addr.exe"} ## Resolve valid address. -# INLINE-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inctwo","Line":3,"StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inc","Line":7,"StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":6},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"main","Line":14,"StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":12}]} +# INLINE-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inctwo","Line":3,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inc","Line":7,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":6},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"main","Line":14,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":12}]} ## Invalid argument after a valid one. # INLINE-NEXT:{"Error":{"Message":"unable to parse arguments: some text2"},"ModuleName":"{{.*}}/Inputs/addr.exe"} @@ -48,7 +48,7 @@ ## Invalid first argument before any valid one. # INLINE-A2L:{"Error":{"Message":"unable to parse arguments: some text"},"ModuleName":"{{.*}}/Inputs/addr.exe"} ## Resolve valid address. -# INLINE-A2L-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inctwo","Line":3,"StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inc","Line":7,"StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":6},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"main","Line":14,"StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":12}]} +# INLINE-A2L-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inctwo","Line":3,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inc","Line":7,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":6},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"main","Line":14,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":12}]} ## Invalid argument after a valid one. # INLINE-A2L-NEXT:{"Error":{"Message":"unable to parse arguments: some text2"},"ModuleName":"{{.*}}/Inputs/addr.exe"} @@ -58,6 +58,6 @@ ## Invalid first argument before any valid one. # NO-FUNC-A2L:{"Error":{"Message":"unable to parse arguments: some text"},"ModuleName":"{{.*}}/Inputs/addr.exe"} ## Resolve valid address. -# NO-FUNC-A2L-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"","Line":3,"StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"","Line":7,"StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":6},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"","Line":14,"StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":12}]} +# NO-FUNC-A2L-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"","Line":3,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"","Line":7,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":6},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"","Line":14,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":12}]} ## Invalid argument after a valid one. # NO-FUNC-A2L-NEXT:{"Error":{"Message":"unable to parse arguments: some text2"},"ModuleName":"{{.*}}/Inputs/addr.exe"} diff --git a/llvm/test/tools/llvm-symbolizer/sym-verbose.test b/llvm/test/tools/llvm-symbolizer/sym-verbose.test --- a/llvm/test/tools/llvm-symbolizer/sym-verbose.test +++ b/llvm/test/tools/llvm-symbolizer/sym-verbose.test @@ -20,12 +20,14 @@ #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 4 +#CHECK-NEXT: Function start address: 0x400590 #CHECK-NEXT: Line: 5 #CHECK-NEXT: Column: 7 #CHECK-NEXT: main #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 9 +#CHECK-NEXT: Function start address: 0x400590 #CHECK-NEXT: Line: 10 #CHECK-NEXT: Column: 0 @@ -34,6 +36,7 @@ #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 4 +#CHECK-NEXT: Function start address: 0x400590 #CHECK-NEXT: Line: 5 #CHECK-NEXT: Column: 17 #CHECK-NEXT: Discriminator: 2 @@ -41,6 +44,7 @@ #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 9 +#CHECK-NEXT: Function start address: 0x400590 #CHECK-NEXT: Line: 10 #CHECK-NEXT: Column: 0 @@ -49,6 +53,7 @@ #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 4 +#CHECK-NEXT: Function start address: 0x400590 #CHECK-NEXT: Line: 0 #CHECK-NEXT: Column: 30 #CHECK-NEXT: Discriminator: 4 @@ -56,6 +61,7 @@ #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 9 +#CHECK-NEXT: Function start address: 0x400590 #CHECK-NEXT: Line: 10 #CHECK-NEXT: Column: 0 @@ -64,12 +70,14 @@ #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 4 +#CHECK-NEXT: Function start address: 0x4005b9 #CHECK-NEXT: Line: 5 #CHECK-NEXT: Column: 7 #CHECK-NEXT: main #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 9 +#CHECK-NEXT: Function start address: 0x400590 #CHECK-NEXT: Line: 10 #CHECK-NEXT: Column: 0 #CHECK-NEXT: Discriminator: 2 @@ -79,6 +87,7 @@ #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 4 +#CHECK-NEXT: Function start address: 0x4005b9 #CHECK-NEXT: Line: 5 #CHECK-NEXT: Column: 17 #CHECK-NEXT: Discriminator: 2 @@ -86,6 +95,7 @@ #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 9 +#CHECK-NEXT: Function start address: 0x400590 #CHECK-NEXT: Line: 10 #CHECK-NEXT: Column: 0 #CHECK-NEXT: Discriminator: 2 @@ -95,6 +105,7 @@ #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 4 +#CHECK-NEXT: Function start address: 0x4005b9 #CHECK-NEXT: Line: 5 #CHECK-NEXT: Column: 30 #CHECK-NEXT: Discriminator: 4 @@ -102,6 +113,7 @@ #CHECK-NEXT: Filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c #CHECK-NEXT: Function start line: 9 +#CHECK-NEXT: Function start address: 0x400590 #CHECK-NEXT: Line: 10 #CHECK-NEXT: Column: 0 #CHECK-NEXT: Discriminator: 2