diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h @@ -140,6 +140,10 @@ /// Return the offset into the section where the offset list begins. uint64_t getOffsetBase() const { return getHashBase() + getNumHashes() * 4; } + 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; @@ -244,6 +248,92 @@ } }; + template struct MaybeWithName {}; + template <> struct MaybeWithName { + StringRef Name; + }; + + /// A general iterator for all entries in the table. We offer two variants of + /// this iterator: one that also keeps track of the name of each entry, and + /// one that doesn't. The nameless version is substantially faster. + template class Iterator : MaybeWithName { + constexpr static auto EndMarker = std::numeric_limits::max(); + + Entry Current; + uint64_t Offset; + uint32_t NumEntriesToCome; + + void setToEnd() { Offset = EndMarker; } + bool isEnd() const { return Offset == EndMarker; } + + /// Reads the next Entry in the table, populating `Current`. + /// If not possible (e.g. end of the section), becomes the end iterator. + void prepareNextEntryOrEnd() { + if (NumEntriesToCome == 0) + prepareNextStringOrEnd(); + if (isEnd()) + return; + uint64_t OffsetCopy = Offset; + Current.extract(&OffsetCopy); + NumEntriesToCome--; + Offset += Current.Table.getHashDataEntryLength(); + } + + /// Reads the next string pointer and the entry count for that string, + /// populating `NumEntriesToCome` and `Name` (if enabled). + /// If not possible (e.g. end of the section), becomes the end iterator. + /// Assumes `Offset` points to a string reference. + void prepareNextStringOrEnd() { + std::optional StrOffset = + Current.Table.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(); + + if constexpr (StoreName) { + std::optional MaybeStr = + Current.Table.readStringFromStrSection(*StrOffset); + if (!MaybeStr) + return setToEnd(); + this->Name = *MaybeStr; + } + + std::optional MaybeNumEntries = + Current.Table.readU32FromAccel(Offset); + if (!MaybeNumEntries || *MaybeNumEntries == 0) + return setToEnd(); + NumEntriesToCome = *MaybeNumEntries; + } + + public: + Iterator(const AppleAcceleratorTable &Table, bool SetEnd = false) + : Current(Table), Offset(Table.getEntriesBase()), NumEntriesToCome(0) { + if (SetEnd) + setToEnd(); + else + prepareNextEntryOrEnd(); + } + + Iterator &operator++() { + prepareNextEntryOrEnd(); + return *this; + } + bool operator==(const Iterator &It) { return Offset == It.Offset; } + bool operator!=(const Iterator &It) { return Offset != It.Offset; } + + decltype(auto) operator*() const { + assert(!isEnd() && "dereferencing end iterator"); + if constexpr (StoreName) + return std::make_pair(Current, this->Name); + else + return (Current); + } + }; + AppleAcceleratorTable(const DWARFDataExtractor &AccelSection, DataExtractor StringSection) : DWARFAcceleratorTable(AccelSection, StringSection) {} @@ -275,6 +365,19 @@ /// 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 and the name of each entry. + auto all_entries_and_names() const { + using ItWithName = Iterator; + return make_range(ItWithName(*this), ItWithName(*this, /*SetEnd*/ true)); + } + + /// Lookup all entries in the accelerator table, without the name of each + /// entry. + auto all_entries() const { + using It = Iterator; + return make_range(It(*this), It(*this, /*SetEnd*/ true)); + } }; /// .debug_names section consists of one or more units. Each unit starts with a diff --git a/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll b/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll --- a/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll +++ b/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll @@ -1,6 +1,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 @@ -8,13 +9,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} @@ -24,7 +35,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) @@ -34,3 +45,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 --- 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 @@ 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,35 @@ 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, Name] : Accel.all_entries_and_names()) { + if (std::optional Off = Entry.getDIESectionOffset()) { + if (DWARFDie Die = DICtx.getDIEForOffset(*Off)) + NameToDies[Name].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 +660,12 @@ 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 +823,7 @@ // 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.