Reapply "[dwarfdump] Add support for dumping accelerator tables."
authorFrederic Riss <friss@apple.com>
Fri, 14 Nov 2014 16:15:53 +0000 (16:15 +0000)
committerFrederic Riss <friss@apple.com>
Fri, 14 Nov 2014 16:15:53 +0000 (16:15 +0000)
This reverts commit r221842 which was a revert of r221836 and of the
test parts of r221837.

This new version fixes an UB bug pointed out by David (along with
addressing some other review comments), makes some dumping more
resilient to broken input data and forces the accelerator tables
to be dumped in the tests where we use them (this decision is
platform specific otherwise).

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@222003 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/DebugInfo/DIContext.h
lib/DebugInfo/CMakeLists.txt
lib/DebugInfo/DWARFAcceleratorTable.cpp [new file with mode: 0644]
lib/DebugInfo/DWARFAcceleratorTable.h [new file with mode: 0644]
lib/DebugInfo/DWARFContext.cpp
lib/DebugInfo/DWARFContext.h
test/DebugInfo/Inputs/gmlt.ll
test/DebugInfo/cross-cu-inlining.ll
test/DebugInfo/dwarfdump-accel.test [new file with mode: 0644]
tools/llvm-dwarfdump/llvm-dwarfdump.cpp

index 3ef541aa3035a2f2e8fe0b9e72034f2e80e4eb94..3aa098d7c30b2c895c1c67eb17a0ca525c1f31ba 100644 (file)
@@ -107,7 +107,11 @@ enum DIDumpType {
   DIDT_GnuPubtypes,
   DIDT_Str,
   DIDT_StrDwo,
-  DIDT_StrOffsetsDwo
+  DIDT_StrOffsetsDwo,
+  DIDT_AppleNames,
+  DIDT_AppleTypes,
+  DIDT_AppleNamespaces,
+  DIDT_AppleObjC
 };
 
 // In place of applying the relocations to the data we've read from disk we use
index 61a3fb066d115a0c05f9fcc48c7ecfaaed146b16..81fc84d4a80522ba25c3c420c968ef8e6bc2fc2e 100644 (file)
@@ -1,6 +1,7 @@
 add_llvm_library(LLVMDebugInfo
   DIContext.cpp
   DWARFAbbreviationDeclaration.cpp
+  DWARFAcceleratorTable.cpp
   DWARFCompileUnit.cpp
   DWARFContext.cpp
   DWARFDebugAbbrev.cpp
diff --git a/lib/DebugInfo/DWARFAcceleratorTable.cpp b/lib/DebugInfo/DWARFAcceleratorTable.cpp
new file mode 100644 (file)
index 0000000..4b844e9
--- /dev/null
@@ -0,0 +1,116 @@
+#include "DWARFAcceleratorTable.h"
+
+#include "llvm/Support/Dwarf.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+
+bool DWARFAcceleratorTable::extract() {
+  uint32_t Offset = 0;
+
+  // Check that we can at least read the header.
+  if (!AccelSection.isValidOffset(offsetof(Header, HeaderDataLength)+4))
+    return false;
+
+  Hdr.Magic = AccelSection.getU32(&Offset);
+  Hdr.Version = AccelSection.getU16(&Offset);
+  Hdr.HashFunction = AccelSection.getU16(&Offset);
+  Hdr.NumBuckets = AccelSection.getU32(&Offset);
+  Hdr.NumHashes = AccelSection.getU32(&Offset);
+  Hdr.HeaderDataLength = AccelSection.getU32(&Offset);
+
+  // Check that we can read all the hashes and offsets from the
+  // section (see SourceLevelDebugging.rst for the structure of the index).
+  if (!AccelSection.isValidOffset(sizeof(Hdr) + Hdr.HeaderDataLength +
+                                  Hdr.NumBuckets*4 + Hdr.NumHashes*8))
+    return false;
+
+  HdrData.DIEOffsetBase = AccelSection.getU32(&Offset);
+  uint32_t NumAtoms = AccelSection.getU32(&Offset);
+
+  for (unsigned i = 0; i < NumAtoms; ++i) {
+    uint16_t AtomType = AccelSection.getU16(&Offset);
+    DWARFFormValue AtomForm(AccelSection.getU16(&Offset));
+    HdrData.Atoms.push_back(std::make_pair(AtomType, AtomForm));
+  }
+
+  return true;
+}
+
+void DWARFAcceleratorTable::dump(raw_ostream &OS) {
+  // Dump the header.
+  OS << "Magic = " << format("0x%08x", Hdr.Magic) << '\n'
+     << "Version = " << format("0x%04x", Hdr.Version) << '\n'
+     << "Hash function = " << format("0x%08x", Hdr.HashFunction) << '\n'
+     << "Bucket count = " << Hdr.NumBuckets << '\n'
+     << "Hashes count = " << Hdr.NumHashes << '\n'
+     << "HeaderData length = " << Hdr.HeaderDataLength << '\n'
+     << "DIE offset base = " << HdrData.DIEOffsetBase << '\n'
+     << "Number of atoms = " << HdrData.Atoms.size() << '\n';
+
+  unsigned i = 0;
+  for (const auto &Atom: HdrData.Atoms) {
+    OS << format("Atom[%d] Type: ", i++);
+    if (const char *TypeString = dwarf::AtomTypeString(Atom.first))
+      OS << TypeString;
+    else
+      OS << format("DW_ATOM_Unknown_0x%x", Atom.first);
+    OS << " Form: ";
+    if (const char *FormString = dwarf::FormEncodingString(Atom.second.getForm()))
+      OS << FormString;
+    else
+      OS << format("DW_FORM_Unknown_0x%x", Atom.second.getForm());
+    OS << '\n';
+  }
+
+  // Now go through the actual tables and dump them.
+  uint32_t Offset = sizeof(Hdr) + Hdr.HeaderDataLength;
+  unsigned HashesBase = Offset + Hdr.NumBuckets * 4;
+  unsigned OffsetsBase = HashesBase + Hdr.NumHashes * 4;
+
+  for (unsigned Bucket = 0; Bucket < Hdr.NumBuckets; ++Bucket) {
+    unsigned Index = AccelSection.getU32(&Offset);
+
+    OS << format("Bucket[%d]\n", Bucket);
+    if (Index == UINT32_MAX) {
+      OS << "  EMPTY\n";
+      continue;
+    }
+
+    for (unsigned HashIdx = Index; HashIdx < Hdr.NumHashes; ++HashIdx) {
+      unsigned HashOffset = HashesBase + HashIdx*4;
+      unsigned OffsetsOffset = OffsetsBase + HashIdx*4;
+      uint32_t Hash = AccelSection.getU32(&HashOffset);
+
+      if (Hash % Hdr.NumBuckets != Bucket)
+        break;
+
+      unsigned DataOffset = AccelSection.getU32(&OffsetsOffset);
+      OS << format("  Hash = 0x%08x Offset = 0x%08x\n", Hash, DataOffset);
+      if (!AccelSection.isValidOffset(DataOffset)) {
+        OS << "    Invalid section offset\n";
+        continue;
+      }
+      while (unsigned StringOffset = AccelSection.getU32(&DataOffset)) {
+        OS << format("    Name: %08x \"%s\"\n", StringOffset,
+                     StringSection.getCStr(&StringOffset));
+        unsigned NumData = AccelSection.getU32(&DataOffset);
+        for (unsigned Data = 0; Data < NumData; ++Data) {
+          OS << format("    Data[%d] => ", Data);
+          unsigned i = 0;
+          for (auto &Atom : HdrData.Atoms) {
+            OS << format("{Atom[%d]: ", i++);
+            if (Atom.second.extractValue(AccelSection, &DataOffset, nullptr))
+              Atom.second.dump(OS, nullptr);
+            else
+              OS << "Error extracting the value";
+            OS << "} ";
+          }
+          OS << '\n';
+        }
+      }
+    }
+  }
+}
+}
diff --git a/lib/DebugInfo/DWARFAcceleratorTable.h b/lib/DebugInfo/DWARFAcceleratorTable.h
new file mode 100644 (file)
index 0000000..bb25917
--- /dev/null
@@ -0,0 +1,38 @@
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/DebugInfo/DWARFFormValue.h"
+
+#include <cstdint>
+
+namespace llvm {
+
+class DWARFAcceleratorTable {
+
+  struct Header {
+    uint32_t Magic;
+    uint16_t Version;
+    uint16_t HashFunction;
+    uint32_t NumBuckets;
+    uint32_t NumHashes;
+    uint32_t HeaderDataLength;
+  };
+
+  struct HeaderData {
+    typedef uint16_t AtomType;
+    uint32_t DIEOffsetBase;
+    SmallVector<std::pair<AtomType, DWARFFormValue>, 1> Atoms;
+  };
+
+  struct Header Hdr;
+  struct HeaderData HdrData;
+  DataExtractor AccelSection;
+  DataExtractor StringSection;
+public:
+  DWARFAcceleratorTable(DataExtractor AccelSection, DataExtractor StringSection)
+    : AccelSection(AccelSection), StringSection(StringSection) {}
+
+  bool extract();
+  void dump(raw_ostream &OS);
+};
+
+}
index aa86f6aac0312b47bad723460d8d1dde69c177f5..845718d195d3c0f7af84bd9f59abad5a81ab3a5a 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "DWARFContext.h"
 #include "DWARFDebugArangeSet.h"
+#include "DWARFAcceleratorTable.h"
 
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringSwitch.h"
@@ -59,6 +60,17 @@ static void dumpPubSection(raw_ostream &OS, StringRef Name, StringRef Data,
   }
 }
 
+static void dumpAccelSection(raw_ostream &OS, StringRef Name, StringRef Data,
+                             StringRef StringSection, bool LittleEndian) {
+  DataExtractor AccelSection(Data, LittleEndian, 0);
+  DataExtractor StrData(StringSection, LittleEndian, 0);
+  OS << "\n." << Name << " contents:\n";
+  DWARFAcceleratorTable Accel(AccelSection, StrData);
+  if (!Accel.extract())
+    return;
+  Accel.dump(OS);
+}
+
 void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType) {
   if (DumpType == DIDT_All || DumpType == DIDT_Abbrev) {
     OS << ".debug_abbrev contents:\n";
@@ -218,6 +230,22 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType) {
       OS << format("%8.8x\n", strOffsetExt.getU32(&offset));
     }
   }
+
+  if (DumpType == DIDT_All || DumpType == DIDT_AppleNames)
+    dumpAccelSection(OS, "apple_names", getAppleNamesSection(),
+                     getStringSection(), isLittleEndian());
+
+  if (DumpType == DIDT_All || DumpType == DIDT_AppleTypes)
+    dumpAccelSection(OS, "apple_types", getAppleTypesSection(),
+                     getStringSection(), isLittleEndian());
+
+  if (DumpType == DIDT_All || DumpType == DIDT_AppleNamespaces)
+    dumpAccelSection(OS, "apple_namespaces", getAppleNamespacesSection(),
+                     getStringSection(), isLittleEndian());
+
+  if (DumpType == DIDT_All || DumpType == DIDT_AppleObjC)
+    dumpAccelSection(OS, "apple_objc", getAppleObjCSection(),
+                     getStringSection(), isLittleEndian());
 }
 
 const DWARFDebugAbbrev *DWARFContext::getDebugAbbrev() {
@@ -565,6 +593,11 @@ DWARFContextInMemory::DWARFContextInMemory(const object::ObjectFile &Obj)
             .Case("debug_str.dwo", &StringDWOSection)
             .Case("debug_str_offsets.dwo", &StringOffsetDWOSection)
             .Case("debug_addr", &AddrSection)
+            .Case("apple_names", &AppleNamesSection)
+            .Case("apple_types", &AppleTypesSection)
+            .Case("apple_namespaces", &AppleNamespacesSection)
+            .Case("apple_namespac", &AppleNamespacesSection)
+            .Case("apple_objc", &AppleObjCSection)
             // Any more debug info sections go here.
             .Default(nullptr);
     if (SectionData) {
index c4586b0d4c1f7a703b31f9bf41b95799aa2f2263..926f7c39bd36ff727557647041a68a2400f6bae7 100644 (file)
@@ -192,6 +192,10 @@ public:
   virtual StringRef getStringOffsetDWOSection() = 0;
   virtual StringRef getRangeDWOSection() = 0;
   virtual StringRef getAddrSection() = 0;
+  virtual StringRef getAppleNamesSection() = 0;
+  virtual StringRef getAppleTypesSection() = 0;
+  virtual StringRef getAppleNamespacesSection() = 0;
+  virtual StringRef getAppleObjCSection() = 0;
 
   static bool isSupportedVersion(unsigned version) {
     return version == 2 || version == 3 || version == 4;
@@ -236,6 +240,10 @@ class DWARFContextInMemory : public DWARFContext {
   StringRef StringOffsetDWOSection;
   StringRef RangeDWOSection;
   StringRef AddrSection;
+  StringRef AppleNamesSection;
+  StringRef AppleTypesSection;
+  StringRef AppleNamespacesSection;
+  StringRef AppleObjCSection;
 
   SmallVector<SmallString<32>, 4> UncompressedSections;
 
@@ -256,6 +264,10 @@ public:
   StringRef getPubTypesSection() override { return PubTypesSection; }
   StringRef getGnuPubNamesSection() override { return GnuPubNamesSection; }
   StringRef getGnuPubTypesSection() override { return GnuPubTypesSection; }
+  StringRef getAppleNamesSection() override { return AppleNamesSection; }
+  StringRef getAppleTypesSection() override { return AppleTypesSection; }
+  StringRef getAppleNamespacesSection() override { return AppleNamespacesSection; }
+  StringRef getAppleObjCSection() override { return AppleObjCSection; }
 
   // Sections for DWARF5 split dwarf proposal.
   const DWARFSection &getInfoDWOSection() override { return InfoDWOSection; }
index 8de03decd7646f865836be3ef49aed0cf49ee8a1..ba8d11342aeb5a679dda5fbff21e90ad823235b2 100644 (file)
@@ -95,6 +95,8 @@
 ; CHECK: .debug_pubtypes contents:
 ; CHECK-NOT: Offset
 
+; CHECK: .apple{{.*}} contents:
+
 ; Function Attrs: nounwind uwtable
 define void @_Z2f1v() #0 {
 entry:
index ea8d90d89e3ece1d0439af35821184b5a7885212..f262022eee9bd6eab34a04c0e1aa2ccccef9dfd8 100644 (file)
@@ -1,6 +1,7 @@
 ; REQUIRES: object-emission
 
 ; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-dump=info - | FileCheck -implicit-check-not=DW_TAG %s
+; RUN: %llc_dwarf -dwarf-accel-tables=Enable -O0 -filetype=obj < %s | llvm-dwarfdump - | FileCheck --check-prefix=CHECK-ACCEL --check-prefix=CHECK %s
 
 ; Build from source:
 ; $ clang++ a.cpp b.cpp -g -c -emit-llvm
 ; CHECK:     DW_AT_location
 ; CHECK:     DW_AT_abstract_origin {{.*}} {0x[[ABS_VAR]]} "x"
 
+; Check that both the inline and the non out of line version of func are
+; correctly referenced in the accelerator table. Before r221837, the one
+; in the second compilation unit had a wrong offset
+; CHECK-ACCEL: .apple_names contents:
+; CHECK-ACCEL: Name{{.*}}"func"
+; CHECK-ACCEL-NOT: Name
+; CHECK-ACCEL: Atom[0]{{.*}}[[INLINED]]
+; CHECK-ACCEL-NOT: Name
+; CHECK-ACCEL: Atom[0]{{.*}}[[FUNC]]
+
 @i = external global i32
 
 ; Function Attrs: uwtable
diff --git a/test/DebugInfo/dwarfdump-accel.test b/test/DebugInfo/dwarfdump-accel.test
new file mode 100644 (file)
index 0000000..c5c3b01
--- /dev/null
@@ -0,0 +1,63 @@
+RUN: llvm-dwarfdump %p/Inputs/dwarfdump-objc.x86_64.o | FileCheck %s
+
+Gather some DIE indexes to verify the accelerator table contents.
+CHECK: .debug_info contents
+CHECK: [[TESTINTERFACE:0x[0-9a-f]*]]:{{.*}}DW_TAG_structure_type
+CHECK-NOT: DW_TAG
+CHECK:     DW_AT_name{{.*}}"TestInterface"
+CHECK: [[READONLY:0x[0-9a-f]*]]:{{.*}}DW_TAG_subprogram
+CHECK-NOT: DW_TAG
+CHECK:     DW_AT_name{{.*}}"-[TestInterface ReadOnly]"
+CHECK: [[ASSIGN:0x[0-9a-f]*]]:{{.*}}DW_TAG_subprogram
+CHECK-NOT: DW_TAG
+CHECK:     DW_AT_name{{.*}}"-[TestInterface Assign]"
+CHECK: [[SETASSIGN:0x[0-9a-f]*]]:{{.*}}DW_TAG_subprogram
+CHECK-NOT: DW_TAG
+CHECK:     DW_AT_name{{.*}}"-[TestInterface setAssign:]"
+
+
+Check that the section header is printed correclty.
+CHECK: .apple_names contents:
+CHECK: Magic = 0x48415348
+CHECK: Version = 0x0001
+CHECK: Hash function = 0x00000000
+CHECK: Bucket count = 11
+CHECK: Hashes count = 22
+CHECK: HeaderData length = 12
+CHECK: DIE offset base = 0
+CHECK: Number of atoms = 1
+CHECK: Atom[0]  Type: DW_ATOM_die_offset Form: DW_FORM_data4
+
+Check that empty buckets are handled correctly.
+CHECK: Bucket[2]
+CHECK:   EMPTY
+CHECK: Bucket[3]
+
+Check that the accelerators point to the right DIEs.
+CHECK:     Name:{{.*}}"-[TestInterface ReadOnly]"
+CHECK-NOT: Name
+CHECK:     {Atom[0]: [[READONLY]]}
+CHECK:     Name:{{.*}}"-[TestInterface setAssign:]"
+CHECK-NOT: Name
+CHECK:     {Atom[0]: [[SETASSIGN]]}
+CHECK:     Name:{{.*}}"-[TestInterface Assign]"
+CHECK-NOT: Name
+CHECK:     {Atom[0]: [[ASSIGN]]}
+
+Check that types are referenced correctly.
+CHECK: .apple_types contents:
+CHECK:     Name{{.*}}"TestInterface"
+CHECK-NOT: Name
+CHECK:     {Atom[0]: [[TESTINTERFACE]]}
+
+Check that an empty ecceleratorsection is handled correctly.
+CHECK: .apple_namespaces contents:
+CHECK-NOT: Magic
+
+Check ObjC specific accelerators.
+CHECK: .apple_objc contents:
+CHECK:     Name{{.*}}"TestInterface"
+CHECK-NOT Name
+CHECK:     {Atom[0]: [[READONLY]]}
+CHECK:     {Atom[0]: [[ASSIGN]]}
+CHECK:     {Atom[0]: [[SETASSIGN]]}
index b4aeb6b8d4aa2ae48c808ee9cff680751283b01c..1c540c988421f6c783013d934e1ba38928e50a05 100644 (file)
@@ -45,6 +45,10 @@ DumpType("debug-dump", cl::init(DIDT_All),
         clEnumValN(DIDT_All, "all", "Dump all debug sections"),
         clEnumValN(DIDT_Abbrev, "abbrev", ".debug_abbrev"),
         clEnumValN(DIDT_AbbrevDwo, "abbrev.dwo", ".debug_abbrev.dwo"),
+        clEnumValN(DIDT_AppleNames, "apple_names", ".apple_names"),
+        clEnumValN(DIDT_AppleTypes, "apple_types", ".apple_types"),
+        clEnumValN(DIDT_AppleNamespaces, "apple_namespaces", ".apple_namespaces"),
+        clEnumValN(DIDT_AppleObjC, "apple_objc", ".apple_objc"),
         clEnumValN(DIDT_Aranges, "aranges", ".debug_aranges"),
         clEnumValN(DIDT_Info, "info", ".debug_info"),
         clEnumValN(DIDT_InfoDwo, "info.dwo", ".debug_info.dwo"),