This patch extends the libLLVMDebugInfo which contains a minimalistic DWARF parser:
authorAlexey Samsonov <samsonov@google.com>
Mon, 2 Jul 2012 05:54:45 +0000 (05:54 +0000)
committerAlexey Samsonov <samsonov@google.com>
Mon, 2 Jul 2012 05:54:45 +0000 (05:54 +0000)
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

12 files changed:
include/llvm/DebugInfo/DIContext.h
lib/DebugInfo/DWARFCompileUnit.cpp
lib/DebugInfo/DWARFCompileUnit.h
lib/DebugInfo/DWARFContext.cpp
lib/DebugInfo/DWARFContext.h
lib/DebugInfo/DWARFDebugAranges.cpp
lib/DebugInfo/DWARFDebugInfoEntry.cpp
lib/DebugInfo/DWARFDebugInfoEntry.h
test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64 [new file with mode: 0755]
test/DebugInfo/Inputs/dwarfdump-test2.elf-x86-64 [new file with mode: 0755]
test/DebugInfo/dwarfdump-test.test [new file with mode: 0644]
tools/llvm-dwarfdump/llvm-dwarfdump.cpp

index 64f80c506504fa7564bbf0a5a70c6b95767c43fa..6377acb634b765ef3c562341add06b80b98fb11d 100644 (file)
@@ -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("<invalid>"), Line(0), Column(0) {}
-  DILineInfo(const char *fileName, uint32_t line, uint32_t column)
-    : FileName(fileName), Line(line), Column(column) {}
+  DILineInfo()
+    : FileName("<invalid>"), FunctionName("<invalid>"),
+      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;
 };
 
 }
index 24bf97ff608ab302de56e343e62592239edd5f7b..2683990e5890d0f4d651418574c15e6592b9e452 100644 (file)
@@ -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;
+}
index d9167292a9df2a9da748383c61ed1c71c5b3400e..dc558da7141cecaaced3d4839b2d73769b456a4c 100644 (file)
@@ -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);
 };
 
 }
index dccadc4ea4da987d774cb25bc52247f627c39f76..6be230e73a537fc792c52214afaa8c89658b2373 100644 (file)
@@ -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("<invalid>", 0, 0);
-  // Get the line table for this compile unit.
-  const DWARFDebugLine::LineTable *lineTable = getLineTableForCompileUnit(cu);
-  if (!lineTable)
-    return DILineInfo("<invalid>", 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("<invalid>", 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 = "<invalid>";
+  const char *functionName = "<invalid>";
+  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() { }
index d2e763a87a45469e2e1891c9dfc72cfd3f4cfd31..e55a27e6984087181307447363c4a7430ce5f314 100644 (file)
@@ -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; }
 
index 17881453561212efdce5fd13cb4a3ec2eec6692e..ef470e5799cdfddfadd2aef338b5224360c38b6a 100644 (file)
@@ -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;
 }
-
index 236db97c44af41d6ce86b6c7a915a38764305fb7..1024b452551d91b3f723fc23eb14f07944527f5a 100644 (file)
@@ -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;
+}
index 37b3bcdd96e65298afb41037b8b7d8d3309ae47c..1a040a53a386ffd869521e272cfd2d24f4f80c31 100644 (file)
@@ -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 (executable)
index 0000000..7cee968
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 (executable)
index 0000000..a226e79
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 (file)
index 0000000..84fe7f3
--- /dev/null
@@ -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
index ca0493de5ddbe5451a34222f6b262b3ca1e95ec5..b6536fa1d71190262fc9c142303acec0f0214c06 100644 (file)
@@ -39,6 +39,11 @@ static cl::opt<unsigned long long>
 Address("address", cl::init(-1ULL),
         cl::desc("Print line information for a given address"));
 
+static cl::opt<bool>
+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<MemoryBuffer> 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() : "<unknown>")
+             << "\n";
     outs() << (dli.getFileName() ? dli.getFileName() : "<unknown>") << ':'
            << dli.getLine() << ':' << dli.getColumn() << '\n';
   }