From 3e25c4a1e3e58bc1d00d894854a29dd2e4e7e88a Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Mon, 2 Jul 2012 05:54:45 +0000 Subject: This patch extends the libLLVMDebugInfo which contains a minimalistic DWARF parser: 1) DIContext is now able to return function name for a given instruction address (besides file/line info). 2) llvm-dwarfdump accepts flag --functions that prints the function name (if address is specified by --address flag). 3) test case that checks the basic functionality of llvm-dwarfdump added git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@159512 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/DebugInfo/DIContext.h | 34 ++++++++++++--- lib/DebugInfo/DWARFCompileUnit.cpp | 18 ++++++-- lib/DebugInfo/DWARFCompileUnit.h | 5 +++ lib/DebugInfo/DWARFContext.cpp | 50 ++++++++++++++--------- lib/DebugInfo/DWARFContext.h | 3 +- lib/DebugInfo/DWARFDebugAranges.cpp | 2 +- lib/DebugInfo/DWARFDebugInfoEntry.cpp | 48 ++++++++++++++++++++++ lib/DebugInfo/DWARFDebugInfoEntry.h | 7 ++++ test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64 | Bin 0 -> 10246 bytes test/DebugInfo/Inputs/dwarfdump-test2.elf-x86-64 | Bin 0 -> 7774 bytes test/DebugInfo/dwarfdump-test.test | 25 ++++++++++++ tools/llvm-dwarfdump/llvm-dwarfdump.cpp | 13 +++++- 12 files changed, 174 insertions(+), 31 deletions(-) create mode 100755 test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64 create mode 100755 test/DebugInfo/Inputs/dwarfdump-test2.elf-x86-64 create mode 100644 test/DebugInfo/dwarfdump-test.test diff --git a/include/llvm/DebugInfo/DIContext.h b/include/llvm/DebugInfo/DIContext.h index 64f80c5065..6377acb634 100644 --- a/include/llvm/DebugInfo/DIContext.h +++ b/include/llvm/DebugInfo/DIContext.h @@ -26,26 +26,49 @@ class raw_ostream; /// DILineInfo - a format-neutral container for source line information. class DILineInfo { const char *FileName; + const char *FunctionName; uint32_t Line; uint32_t Column; public: - DILineInfo() : FileName(""), Line(0), Column(0) {} - DILineInfo(const char *fileName, uint32_t line, uint32_t column) - : FileName(fileName), Line(line), Column(column) {} + DILineInfo() + : FileName(""), FunctionName(""), + Line(0), Column(0) {} + DILineInfo(const char *fileName, const char *functionName, + uint32_t line, uint32_t column) + : FileName(fileName), FunctionName(functionName), + Line(line), Column(column) {} const char *getFileName() const { return FileName; } + const char *getFunctionName() const { return FunctionName; } uint32_t getLine() const { return Line; } uint32_t getColumn() const { return Column; } bool operator==(const DILineInfo &RHS) const { return Line == RHS.Line && Column == RHS.Column && - std::strcmp(FileName, RHS.FileName) == 0; + std::strcmp(FileName, RHS.FileName) == 0 && + std::strcmp(FunctionName, RHS.FunctionName) == 0; } bool operator!=(const DILineInfo &RHS) const { return !(*this == RHS); } }; +/// DILineInfoSpecifier - controls which fields of DILineInfo container +/// should be filled with data. +class DILineInfoSpecifier { + const uint32_t Flags; // Or'ed flags that set the info we want to fetch. +public: + enum Specification { + FileLineInfo = 1 << 0, + FunctionName = 1 << 1 + }; + // Use file/line info by default. + DILineInfoSpecifier(uint32_t flags = FileLineInfo) : Flags(flags) {} + bool needs(Specification spec) const { + return (Flags & spec) > 0; + } +}; + class DIContext { public: virtual ~DIContext(); @@ -60,7 +83,8 @@ public: virtual void dump(raw_ostream &OS) = 0; - virtual DILineInfo getLineInfoForAddress(uint64_t address) = 0; + virtual DILineInfo getLineInfoForAddress(uint64_t address, + DILineInfoSpecifier specifier = DILineInfoSpecifier()) = 0; }; } diff --git a/lib/DebugInfo/DWARFCompileUnit.cpp b/lib/DebugInfo/DWARFCompileUnit.cpp index 24bf97ff60..2683990e58 100644 --- a/lib/DebugInfo/DWARFCompileUnit.cpp +++ b/lib/DebugInfo/DWARFCompileUnit.cpp @@ -82,7 +82,7 @@ void DWARFCompileUnit::clear() { Abbrevs = 0; AddrSize = 0; BaseAddr = 0; - DieArray.clear(); + clearDIEs(false); } void DWARFCompileUnit::dump(raw_ostream &OS) { @@ -201,7 +201,7 @@ size_t DWARFCompileUnit::extractDIEsIfNeeded(bool cu_die_only) { } void DWARFCompileUnit::clearDIEs(bool keep_compile_unit_die) { - if (DieArray.size() > 1) { + if (DieArray.size() > (unsigned)keep_compile_unit_die) { // std::vectors never get any smaller when resized to a smaller size, // or when clear() or erase() are called, the size will report that it // is smaller, but the memory allocated remains intact (call capacity() @@ -227,8 +227,8 @@ DWARFCompileUnit::buildAddressRangeTable(DWARFDebugAranges *debug_aranges, // all compile units to stay loaded when they weren't needed. So we can end // up parsing the DWARF and then throwing them all away to keep memory usage // down. - const bool clear_dies = extractDIEsIfNeeded(false) > 1; - + const bool clear_dies = extractDIEsIfNeeded(false) > 1 && + clear_dies_if_already_not_parsed; DieArray[0].buildAddressRangeTable(this, debug_aranges); // Keep memory down by clearing DIEs if this generate function @@ -236,3 +236,13 @@ DWARFCompileUnit::buildAddressRangeTable(DWARFDebugAranges *debug_aranges, if (clear_dies) clearDIEs(true); } + +const DWARFDebugInfoEntryMinimal* +DWARFCompileUnit::getFunctionDIEForAddress(int64_t address) { + size_t n = extractDIEsIfNeeded(false); + for (size_t i = 0; i != n; i++) { + if (DieArray[i].addressRangeContainsAddress(this, address)) + return &DieArray[i]; + } + return 0; +} diff --git a/lib/DebugInfo/DWARFCompileUnit.h b/lib/DebugInfo/DWARFCompileUnit.h index d9167292a9..dc558da714 100644 --- a/lib/DebugInfo/DWARFCompileUnit.h +++ b/lib/DebugInfo/DWARFCompileUnit.h @@ -104,6 +104,11 @@ public: void buildAddressRangeTable(DWARFDebugAranges *debug_aranges, bool clear_dies_if_already_not_parsed); + /// getFunctionDIEForAddress - Returns pointer to parsed subprogram DIE, + /// address ranges of which contain the provided address, + /// or NULL if there is no such subprogram. The pointer + /// is valid until DWARFCompileUnit::clear() or clearDIEs() is called. + const DWARFDebugInfoEntryMinimal *getFunctionDIEForAddress(int64_t address); }; } diff --git a/lib/DebugInfo/DWARFContext.cpp b/lib/DebugInfo/DWARFContext.cpp index dccadc4ea4..6be230e73a 100644 --- a/lib/DebugInfo/DWARFContext.cpp +++ b/lib/DebugInfo/DWARFContext.cpp @@ -140,30 +140,42 @@ DWARFCompileUnit *DWARFContext::getCompileUnitForOffset(uint32_t offset) { return 0; } -DILineInfo DWARFContext::getLineInfoForAddress(uint64_t address) { +DILineInfo DWARFContext::getLineInfoForAddress(uint64_t address, + DILineInfoSpecifier specifier) { // First, get the offset of the compile unit. uint32_t cuOffset = getDebugAranges()->findAddress(address); // Retrieve the compile unit. DWARFCompileUnit *cu = getCompileUnitForOffset(cuOffset); if (!cu) - return DILineInfo("", 0, 0); - // Get the line table for this compile unit. - const DWARFDebugLine::LineTable *lineTable = getLineTableForCompileUnit(cu); - if (!lineTable) - return DILineInfo("", 0, 0); - // Get the index of the row we're looking for in the line table. - uint64_t hiPC = - cu->getCompileUnitDIE()->getAttributeValueAsUnsigned(cu, DW_AT_high_pc, - -1ULL); - uint32_t rowIndex = lineTable->lookupAddress(address, hiPC); - if (rowIndex == -1U) - return DILineInfo("", 0, 0); - - // From here, contruct the DILineInfo. - const DWARFDebugLine::Row &row = lineTable->Rows[rowIndex]; - const std::string &fileName = lineTable->Prologue.FileNames[row.File-1].Name; - - return DILineInfo(fileName.c_str(), row.Line, row.Column); + return DILineInfo(); + const char *fileName = ""; + const char *functionName = ""; + uint32_t line = 0; + uint32_t column = 0; + if (specifier.needs(DILineInfoSpecifier::FunctionName)) { + const DWARFDebugInfoEntryMinimal *function_die = + cu->getFunctionDIEForAddress(address); + if (function_die) + functionName = function_die->getSubprogramName(cu); + } + if (specifier.needs(DILineInfoSpecifier::FileLineInfo)) { + // Get the line table for this compile unit. + const DWARFDebugLine::LineTable *lineTable = getLineTableForCompileUnit(cu); + if (lineTable) { + // Get the index of the row we're looking for in the line table. + uint64_t hiPC = cu->getCompileUnitDIE()->getAttributeValueAsUnsigned( + cu, DW_AT_high_pc, -1ULL); + uint32_t rowIndex = lineTable->lookupAddress(address, hiPC); + if (rowIndex != -1U) { + const DWARFDebugLine::Row &row = lineTable->Rows[rowIndex]; + // Take file/line info from the line table. + fileName = lineTable->Prologue.FileNames[row.File - 1].Name.c_str(); + line = row.Line; + column = row.Column; + } + } + } + return DILineInfo(fileName, functionName, line, column); } void DWARFContextInMemory::anchor() { } diff --git a/lib/DebugInfo/DWARFContext.h b/lib/DebugInfo/DWARFContext.h index d2e763a87a..e55a27e698 100644 --- a/lib/DebugInfo/DWARFContext.h +++ b/lib/DebugInfo/DWARFContext.h @@ -66,7 +66,8 @@ public: const DWARFDebugLine::LineTable * getLineTableForCompileUnit(DWARFCompileUnit *cu); - virtual DILineInfo getLineInfoForAddress(uint64_t address); + virtual DILineInfo getLineInfoForAddress(uint64_t address, + DILineInfoSpecifier specifier = DILineInfoSpecifier()); bool isLittleEndian() const { return IsLittleEndian; } diff --git a/lib/DebugInfo/DWARFDebugAranges.cpp b/lib/DebugInfo/DWARFDebugAranges.cpp index 1788145356..ef470e5799 100644 --- a/lib/DebugInfo/DWARFDebugAranges.cpp +++ b/lib/DebugInfo/DWARFDebugAranges.cpp @@ -93,6 +93,7 @@ bool DWARFDebugAranges::generate(DWARFContext *ctx) { cu->buildAddressRangeTable(this, true); } } + sort(true, /* overlap size */ 0); return !isEmpty(); } @@ -221,4 +222,3 @@ bool DWARFDebugAranges::getMaxRange(uint64_t &LoPC, uint64_t &HiPC) const { HiPC = Aranges.back().HiPC(); return true; } - diff --git a/lib/DebugInfo/DWARFDebugInfoEntry.cpp b/lib/DebugInfo/DWARFDebugInfoEntry.cpp index 236db97c44..1024b45255 100644 --- a/lib/DebugInfo/DWARFDebugInfoEntry.cpp +++ b/lib/DebugInfo/DWARFDebugInfoEntry.cpp @@ -440,3 +440,51 @@ DWARFDebugInfoEntryMinimal::buildAddressRangeTable(const DWARFCompileUnit *cu, } } } + +bool +DWARFDebugInfoEntryMinimal::addressRangeContainsAddress( + const DWARFCompileUnit *cu, const uint64_t address) const { + if (!isNULL() && getTag() == DW_TAG_subprogram) { + uint64_t hi_pc = -1ULL; + uint64_t lo_pc = getAttributeValueAsUnsigned(cu, DW_AT_low_pc, -1ULL); + if (lo_pc != -1ULL) + hi_pc = getAttributeValueAsUnsigned(cu, DW_AT_high_pc, -1ULL); + if (hi_pc != -1ULL) { + return (lo_pc <= address && address < hi_pc); + } + } + return false; +} + +static inline const char* +getSubprogramNameFromDie(const DWARFCompileUnit *cu, + const DWARFDebugInfoEntryMinimal *die) { + const char *result = 0; + if (!die->isNULL() && die->getTag() == DW_TAG_subprogram) { + // Try to get mangled name if possible. + result = die->getAttributeValueAsString(cu, DW_AT_MIPS_linkage_name, 0); + if (result == 0) + result = die->getAttributeValueAsString(cu, DW_AT_linkage_name, 0); + if (result == 0) + result = die->getAttributeValueAsString(cu, DW_AT_name, 0); + } + return result; +} + +const char* +DWARFDebugInfoEntryMinimal::getSubprogramName( + const DWARFCompileUnit *cu) const { + if (isNULL() || getTag() != DW_TAG_subprogram) + return 0; + const char *name = getSubprogramNameFromDie(cu, this); + if (name == 0) { + // Try to get name from specification DIE. + uint32_t ref = getAttributeValueAsReference(cu, DW_AT_specification, -1U); + if (ref != -1U) { + DWARFDebugInfoEntryMinimal spec_die; + if (spec_die.extract(cu, &ref)) + name = getSubprogramNameFromDie(cu, &spec_die); + } + } + return name; +} diff --git a/lib/DebugInfo/DWARFDebugInfoEntry.h b/lib/DebugInfo/DWARFDebugInfoEntry.h index 37b3bcdd96..1a040a53a3 100644 --- a/lib/DebugInfo/DWARFDebugInfoEntry.h +++ b/lib/DebugInfo/DWARFDebugInfoEntry.h @@ -128,6 +128,13 @@ public: void buildAddressRangeTable(const DWARFCompileUnit *cu, DWARFDebugAranges *debug_aranges) const; + + bool addressRangeContainsAddress(const DWARFCompileUnit *cu, + const uint64_t address) const; + + // If a DIE represents a subroutine, returns its mangled name + // (or short name, if mangled is missing). Otherwise returns null. + const char* getSubprogramName(const DWARFCompileUnit *cu) const; }; } diff --git a/test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64 b/test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64 new file mode 100755 index 0000000000..7cee968072 Binary files /dev/null and b/test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64 differ diff --git a/test/DebugInfo/Inputs/dwarfdump-test2.elf-x86-64 b/test/DebugInfo/Inputs/dwarfdump-test2.elf-x86-64 new file mode 100755 index 0000000000..a226e79f3b Binary files /dev/null and b/test/DebugInfo/Inputs/dwarfdump-test2.elf-x86-64 differ diff --git a/test/DebugInfo/dwarfdump-test.test b/test/DebugInfo/dwarfdump-test.test new file mode 100644 index 0000000000..84fe7f3ec8 --- /dev/null +++ b/test/DebugInfo/dwarfdump-test.test @@ -0,0 +1,25 @@ +RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test.elf-x86-64 \ +RUN: --address=0x400589 --functions | FileCheck %s -check-prefix MAIN +RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test.elf-x86-64 \ +RUN: --address=0x400558 --functions | FileCheck %s -check-prefix FUNCTION +RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test.elf-x86-64 \ +RUN: --address=0x4005b6 --functions | FileCheck %s -check-prefix CTOR_WITH_SPEC +RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test2.elf-x86-64 \ +RUN: --address=0x4004b8 --functions | FileCheck %s -check-prefix MANY_CU_1 +RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test2.elf-x86-64 \ +RUN: --address=0x4004c4 --functions | FileCheck %s -check-prefix MANY_CU_2 + +MAIN: main +MAIN-NEXT: dwarfdump-test.cc:16:10 + +FUNCTION: _Z1fii +FUNCTION-NEXT: dwarfdump-test.cc:11:18 + +CTOR_WITH_SPEC: _ZN10DummyClassC1Ei +CTOR_WITH_SPEC-NEXT: dwarfdump-test.cc:4:30 + +MANY_CU_1: a +MANY_CU_1-NEXT: a.cc:2:0 + +MANY_CU_2: main +MANY_CU_2-NEXT: main.cc:4:0 diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index ca0493de5d..b6536fa1d7 100644 --- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -39,6 +39,11 @@ static cl::opt Address("address", cl::init(-1ULL), cl::desc("Print line information for a given address")); +static cl::opt +PrintFunctions("functions", cl::init(false), + cl::desc("Print function names as well as line information " + "for a given address")); + static void DumpInput(const StringRef &Filename) { OwningPtr Buff; @@ -92,7 +97,13 @@ static void DumpInput(const StringRef &Filename) { dictx->dump(outs()); } else { // Print line info for the specified address. - DILineInfo dli = dictx->getLineInfoForAddress(Address); + int spec_flags = DILineInfoSpecifier::FileLineInfo; + if (PrintFunctions) + spec_flags |= DILineInfoSpecifier::FunctionName; + DILineInfo dli = dictx->getLineInfoForAddress(Address, spec_flags); + if (PrintFunctions) + outs() << (dli.getFunctionName() ? dli.getFunctionName() : "") + << "\n"; outs() << (dli.getFileName() ? dli.getFileName() : "") << ':' << dli.getLine() << ':' << dli.getColumn() << '\n'; } -- cgit v1.2.3