From 15a1f7f6f7be103820b6a0c4caea1ec58c01d906 Mon Sep 17 00:00:00 2001 From: Felipe de Azevedo Piovezan Date: Thu, 15 Jun 2023 08:18:51 -0400 Subject: [PATCH] [AppleTables] Implement iterator over all entries in table This commit adds functionality to the Apple Accelerator table allowing iteration over all elements in the table. Our iterators look like streaming iterators: when we increment the iterator we check if there is still enough data in the "stream" (in our case, the blob of data of the accelerator table) and extract the next entry. If any failures occur, we immediately set the iterator to be the end iterator. Since the ultimate user of this functionality is LLDB, there are roughly two iteration methods we want support: one that also loads the name of each entry, and one which does not. Loading names is measurably slower (one order the magnitude) than only loading DIEs, so we used some template metaprograming to implement both iteration methods. Depends on D153066 Differential Revision: https://reviews.llvm.org/D153066 --- .../DebugInfo/DWARF/DWARFAcceleratorTable.h | 62 +++++++++++++++++++ .../DebugInfo/DWARF/DWARFAcceleratorTable.cpp | 37 +++++++++++ .../Generic/apple-names-hash-collisions.ll | 19 +++++- llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp | 45 +++++++++++++- 4 files changed, 159 insertions(+), 4 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h index f9f9cc7bf4cf..3df8e2190a86 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h @@ -140,6 +140,11 @@ class AppleAcceleratorTable : public DWARFAcceleratorTable { /// Return the offset into the section where the offset list begins. uint64_t getOffsetBase() const { return getHashBase() + getNumHashes() * 4; } + /// Return the offset into the section where the table entries begin. + uint64_t getEntriesBase() const { + return getOffsetBase() + getNumHashes() * 4; + } + /// Return the offset into the section where the I-th offset is. uint64_t getIthOffsetBase(uint32_t I) const { return getOffsetBase() + I * 4; @@ -240,6 +245,58 @@ public: } }; + struct EntryWithName { + EntryWithName(const AppleAcceleratorTable &Table) + : BaseEntry(Table), StrOffset(0) {} + + std::optional readName() const { + return BaseEntry.Table.readStringFromStrSection(StrOffset); + } + + Entry BaseEntry; + uint32_t StrOffset; + }; + + /// An iterator for all entries in the table. + class Iterator + : public iterator_facade_base { + constexpr static auto EndMarker = std::numeric_limits::max(); + + EntryWithName Current; + uint64_t Offset = EndMarker; + uint32_t NumEntriesToCome = 0; + + void setToEnd() { Offset = EndMarker; } + bool isEnd() const { return Offset == EndMarker; } + const AppleAcceleratorTable &getTable() const { + return Current.BaseEntry.Table; + } + + /// Reads the next Entry in the table, populating `Current`. + /// If not possible (e.g. end of the section), becomes the end iterator. + void prepareNextEntryOrEnd(); + + /// Reads the next string pointer and the entry count for that string, + /// populating `NumEntriesToCome`. + /// If not possible (e.g. end of the section), becomes the end iterator. + /// Assumes `Offset` points to a string reference. + void prepareNextStringOrEnd(); + + public: + Iterator(const AppleAcceleratorTable &Table, bool SetEnd = false); + + Iterator &operator++() { + prepareNextEntryOrEnd(); + return *this; + } + bool operator==(const Iterator &It) const { return Offset == It.Offset; } + const EntryWithName &operator*() const { + assert(!isEnd() && "dereferencing end iterator"); + return Current; + } + }; + AppleAcceleratorTable(const DWARFDataExtractor &AccelSection, DataExtractor StringSection) : DWARFAcceleratorTable(AccelSection, StringSection) {} @@ -271,6 +328,11 @@ public: /// Look up all entries in the accelerator table matching \c Key. iterator_range equal_range(StringRef Key) const; + + /// Lookup all entries in the accelerator table. + auto entries() const { + return make_range(Iterator(*this), Iterator(*this, /*SetEnd*/ true)); + } }; /// .debug_names section consists of one or more units. Each unit starts with a diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp index 3e052f25f1da..14962cd36c23 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -309,6 +309,43 @@ AppleAcceleratorTable::SameNameIterator::SameNameIterator( const AppleAcceleratorTable &AccelTable, uint64_t DataOffset) : Current(AccelTable), Offset(DataOffset) {} +void AppleAcceleratorTable::Iterator::prepareNextEntryOrEnd() { + if (NumEntriesToCome == 0) + prepareNextStringOrEnd(); + if (isEnd()) + return; + uint64_t OffsetCopy = Offset; + Current.BaseEntry.extract(&OffsetCopy); + NumEntriesToCome--; + Offset += getTable().getHashDataEntryLength(); +} + +void AppleAcceleratorTable::Iterator::prepareNextStringOrEnd() { + std::optional StrOffset = getTable().readStringOffsetAt(Offset); + if (!StrOffset) + return setToEnd(); + + // A zero denotes the end of the collision list. Read the next string + // again. + if (*StrOffset == 0) + return prepareNextStringOrEnd(); + Current.StrOffset = *StrOffset; + + std::optional MaybeNumEntries = getTable().readU32FromAccel(Offset); + if (!MaybeNumEntries || *MaybeNumEntries == 0) + return setToEnd(); + NumEntriesToCome = *MaybeNumEntries; +} + +AppleAcceleratorTable::Iterator::Iterator(const AppleAcceleratorTable &Table, + bool SetEnd) + : Current(Table), Offset(Table.getEntriesBase()), NumEntriesToCome(0) { + if (SetEnd) + setToEnd(); + else + prepareNextEntryOrEnd(); +} + iterator_range AppleAcceleratorTable::equal_range(StringRef Key) const { const auto EmptyRange = diff --git a/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll b/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll index 4283055abb2f..5664ec91d895 100644 --- a/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll +++ b/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll @@ -2,6 +2,7 @@ ; RUN: %llc_dwarf -accel-tables=Apple -filetype=obj -o %t < %s ; RUN: llvm-dwarfdump -apple-names %t | FileCheck %s --check-prefix=NUM_HASHES ; RUN: llvm-dwarfdump --find=bb --find=cA %t | FileCheck %s --check-prefix=FOUND_VARS +; RUN: llvm-dwarfdump --find-all-apple %t | FileCheck %s --check-prefix=ALL_ENTRIES ; The strings 'bb' and 'cA' hash to the same value under the Apple accelerator @@ -9,13 +10,23 @@ ; We first test that there is exactly one bucket and one hash. ; Then we check that both values are found. -; NUM_HASHES: Bucket count: 1 -; NUM_HASHES-NEXT: Hashes count: 1 +; NUM_HASHES: Bucket count: 2 +; NUM_HASHES-NEXT: Hashes count: 2 ; FOUND_VARS: DW_AT_name ("bb") ; FOUND_VARS: DW_AT_name ("cA") +; ALL_ENTRIES: Apple accelerator entries with name = "cA": +; ALL_ENTRIES: DW_AT_name ("cA") +; ALL_ENTRIES: Apple accelerator entries with name = "some_other_hash": +; ALL_ENTRIES: DW_AT_name ("some_other_hash") +; ALL_ENTRIES: Apple accelerator entries with name = "int": +; ALL_ENTRIES: DW_AT_name ("int") +; ALL_ENTRIES: Apple accelerator entries with name = "bb": +; ALL_ENTRIES: DW_AT_name ("bb") + @bb = global i32 200, align 4, !dbg !0 @cA = global i32 10, align 4, !dbg !5 +@some_other_hash = global i32 10, !dbg !17 !llvm.module.flags = !{!9, !10, !11, !12, !13} !llvm.dbg.cu = !{!2} @@ -25,7 +36,7 @@ !1 = distinct !DIGlobalVariable(name: "bb", scope: !2, file: !3, line: 1, type: !7, isDefinition: true) !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "", emissionKind: FullDebug, globals: !4) !3 = !DIFile(filename: "test.cpp", directory: "blah") -!4 = !{!0, !5} +!4 = !{!0, !5, !17} !5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) !6 = distinct !DIGlobalVariable(name: "cA", scope: !2, file: !3, line: 2, type: !7, isLocal: false, isDefinition: true) !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) @@ -35,3 +46,5 @@ !12 = !{i32 8, !"PIC Level", i32 2} !13 = !{i32 7, !"uwtable", i32 1} !15 = !{!"blah"} +!16 = distinct !DIGlobalVariable(name: "some_other_hash", scope: !2, file: !3, line: 2, type: !7, isLocal: false, isDefinition: true) +!17 = !DIGlobalVariableExpression(var: !16, expr: !DIExpression()) diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index c49a32f95474..cdcb5bbd8ab9 100644 --- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -12,6 +12,7 @@ #include "llvm-dwarfdump.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringSet.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" @@ -25,6 +26,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" @@ -171,6 +173,10 @@ static list value_desc("name"), cat(DwarfDumpCategory)); static alias FindAlias("f", desc("Alias for --find."), aliasopt(Find), cl::NotHidden); +static opt FindAllApple( + "find-all-apple", + desc("Print every debug information entry in the accelerator tables."), + cat(DwarfDumpCategory)); static opt IgnoreCase("ignore-case", desc("Ignore case distinctions when using --name."), value_desc("i"), cat(DwarfDumpCategory)); @@ -453,6 +459,37 @@ static void filterByAccelName( Die.dump(OS, 0, DumpOpts); } +/// Print all DIEs in apple accelerator tables +static void findAllApple( + DWARFContext &DICtx, raw_ostream &OS, + std::function GetNameForDWARFReg) { + StringMap> NameToDies; + + auto PushDIEs = [&](const AppleAcceleratorTable &Accel) { + for (const auto &Entry : Accel.entries()) { + if (std::optional Off = Entry.BaseEntry.getDIESectionOffset()) { + std::optional MaybeName = Entry.readName(); + DWARFDie Die = DICtx.getDIEForOffset(*Off); + if (Die && MaybeName) + NameToDies[*MaybeName].insert(Die); + } + } + }; + + PushDIEs(DICtx.getAppleNames()); + PushDIEs(DICtx.getAppleNamespaces()); + PushDIEs(DICtx.getAppleTypes()); + + DIDumpOptions DumpOpts = getDumpOpts(DICtx); + DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg; + for (const auto &[Name, Dies] : NameToDies) { + OS << llvm::formatv("\nApple accelerator entries with name = \"{0}\":\n", + Name); + for (DWARFDie Die : Dies) + Die.dump(OS, 0, DumpOpts); + } +} + /// Handle the --lookup option and dump the DIEs and line info for the given /// address. /// TODO: specified Address for --lookup option could relate for several @@ -625,6 +662,12 @@ static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, return true; } + // Handle the --find-all-apple option and lower it to --debug-info=. + if (FindAllApple) { + findAllApple(DICtx, OS, GetRegName); + return true; + } + // Dump the complete DWARF structure. auto DumpOpts = getDumpOpts(DICtx); DumpOpts.GetNameForDWARFReg = GetRegName; @@ -782,7 +825,7 @@ int main(int argc, char **argv) { // Unless dumping a specific DIE, default to --show-children. if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() && - Find.empty()) + Find.empty() && !FindAllApple) ShowChildren = true; // Defaults to a.out if no filenames specified.