[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
This commit is contained in:
Felipe de Azevedo Piovezan 2023-06-15 08:18:51 -04:00
parent ecb07f481b
commit 15a1f7f6f7
4 changed files with 159 additions and 4 deletions

View File

@ -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<StringRef> 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<Iterator, std::forward_iterator_tag,
EntryWithName> {
constexpr static auto EndMarker = std::numeric_limits<uint64_t>::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<SameNameIterator> 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

View File

@ -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<uint32_t> 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<uint32_t> 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::SameNameIterator>
AppleAcceleratorTable::equal_range(StringRef Key) const {
const auto EmptyRange =

View File

@ -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())

View File

@ -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<std::string>
value_desc("name"), cat(DwarfDumpCategory));
static alias FindAlias("f", desc("Alias for --find."), aliasopt(Find),
cl::NotHidden);
static opt<bool> FindAllApple(
"find-all-apple",
desc("Print every debug information entry in the accelerator tables."),
cat(DwarfDumpCategory));
static opt<bool> 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<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) {
StringMap<llvm::SmallSet<DWARFDie, 2>> NameToDies;
auto PushDIEs = [&](const AppleAcceleratorTable &Accel) {
for (const auto &Entry : Accel.entries()) {
if (std::optional<uint64_t> Off = Entry.BaseEntry.getDIESectionOffset()) {
std::optional<StringRef> 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=<offset>.
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.