diff --git a/include/llvm/DebugInfo/DIContext.h b/include/llvm/DebugInfo/DIContext.h index c7429069873..59f8e297296 100644 --- a/include/llvm/DebugInfo/DIContext.h +++ b/include/llvm/DebugInfo/DIContext.h @@ -114,7 +114,6 @@ struct DILineInfoSpecifier { /// This is just a helper to programmatically construct DIDumpType. enum DIDumpTypeCounter { - DIDT_ID_Null = 0, #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME) \ DIDT_ID_##ENUM_NAME, #include "llvm/BinaryFormat/Dwarf.def" @@ -129,10 +128,10 @@ enum DIDumpType : unsigned { DIDT_Null, DIDT_All = ~0U, #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME) \ - DIDT_##ENUM_NAME = 1U << (DIDT_ID_##ENUM_NAME - 1), + DIDT_##ENUM_NAME = 1U << DIDT_ID_##ENUM_NAME, #include "llvm/BinaryFormat/Dwarf.def" #undef HANDLE_DWARF_SECTION - DIDT_UUID = 1 << (DIDT_ID_UUID - 1), + DIDT_UUID = 1 << DIDT_ID_UUID, }; /// Container for dump options that control which debug information will be diff --git a/include/llvm/DebugInfo/DWARF/DWARFContext.h b/include/llvm/DebugInfo/DWARF/DWARFContext.h index a89d883ee0b..e43a9ba8adf 100644 --- a/include/llvm/DebugInfo/DWARF/DWARFContext.h +++ b/include/llvm/DebugInfo/DWARF/DWARFContext.h @@ -121,7 +121,15 @@ public: return DICtx->getKind() == CK_DWARF; } - void dump(raw_ostream &OS, DIDumpOptions DumpOpts) override; + /// Dump a textual representation to \p OS. If any \p DumpOffsets are present, + /// dump only the record at the specified offset. + void dump(raw_ostream &OS, DIDumpOptions DumpOpts, + std::array, DIDT_ID_Count> DumpOffsets); + + void dump(raw_ostream &OS, DIDumpOptions DumpOpts) override { + std::array, DIDT_ID_Count> DumpOffsets; + dump(OS, DumpOpts, DumpOffsets); + } bool verify(raw_ostream &OS, unsigned DumpType = DIDT_All, DIDumpOptions DumpOpts = {}) override; diff --git a/lib/DebugInfo/DWARF/DWARFContext.cpp b/lib/DebugInfo/DWARF/DWARFContext.cpp index c9832f43c24..3914b5a441a 100644 --- a/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -216,7 +216,11 @@ static void dumpStringOffsetsSection(raw_ostream &OS, StringRef SectionName, } } -void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { +void DWARFContext::dump( + raw_ostream &OS, DIDumpOptions DumpOpts, + std::array, DIDT_ID_Count> DumpOffsets) { + + Optional DumpOffset; uint64_t DumpType = DumpOpts.DumpType; bool DumpEH = DumpOpts.DumpEH; @@ -230,37 +234,41 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { // Print a header for each explicitly-requested section. // Otherwise just print one for non-empty sections. + // Only print empty .dwo section headers when dumping a .dwo file. bool Explicit = DumpType != DIDT_All && !IsDWO; - auto shouldDump = [&](bool IsExplicit, const char *Name, - unsigned DIDT_Section, StringRef Section) { - bool Should = (DumpType & DIDT_Section) && (IsExplicit || !Section.empty()); + bool ExplicitDWO = Explicit && IsDWO; + auto shouldDump = [&](bool Explicit, const char *Name, unsigned ID, + StringRef Section) { + DumpOffset = DumpOffsets[ID]; + unsigned Mask = 1U << ID; + bool Should = (DumpType & Mask) && (Explicit || !Section.empty()); if (Should) - OS << '\n' << Name << " contents:\n"; + OS << "\n" << Name << " contents:\n"; return Should; }; - // Only print empty .dwo section headers when dumping a .dwo file. - bool ExplicitDWO = Explicit && IsDWO; // Dump individual sections. - if (shouldDump(Explicit, ".debug_abbrev", DIDT_DebugAbbrev, - DObj->getAbbrevSection())) { + if (shouldDump(Explicit, ".debug_abbrev", DIDT_ID_DebugAbbrev, + DObj->getAbbrevSection())) getDebugAbbrev()->dump(OS); - } - if (shouldDump(ExplicitDWO, ".debug_abbrev.dwo", DIDT_DebugAbbrev, - DObj->getAbbrevDWOSection())) { + if (shouldDump(ExplicitDWO, ".debug_abbrev.dwo", DIDT_ID_DebugAbbrev, + DObj->getAbbrevDWOSection())) getDebugAbbrevDWO()->dump(OS); - } - if (shouldDump(Explicit, ".debug_info", DIDT_DebugInfo, - DObj->getInfoSection().Data)) { - for (const auto &CU : compile_units()) - CU->dump(OS, DumpOpts); - } - if (shouldDump(ExplicitDWO, ".debug_info.dwo", DIDT_DebugInfo, - DObj->getInfoDWOSection().Data)) { - for (const auto &DWOCU : dwo_compile_units()) - DWOCU->dump(OS, DumpOpts); - } + auto dumpDebugInfo = [&](bool IsExplicit, const char *Name, + DWARFSection Section, cu_iterator_range CUs) { + if (shouldDump(IsExplicit, Name, DIDT_ID_DebugInfo, Section.Data)) { + for (const auto &CU : CUs) + if (DumpOffset) + CU->getDIEForOffset(DumpOffset.getValue()).dump(OS, 0); + else + CU->dump(OS, DumpOpts); + } + }; + dumpDebugInfo(Explicit, ".debug_info", DObj->getInfoSection(), + compile_units()); + dumpDebugInfo(ExplicitDWO, ".debug_info.dwo", DObj->getInfoDWOSection(), + dwo_compile_units()); if ((DumpType & DIDT_DebugTypes)) { if (Explicit || getNumTypeUnits()) { @@ -277,16 +285,16 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { } } - if (shouldDump(Explicit, ".debug_loc", DIDT_DebugLoc, + if (shouldDump(Explicit, ".debug_loc", DIDT_ID_DebugLoc, DObj->getLocSection().Data)) { getDebugLoc()->dump(OS, getRegisterInfo()); } - if (shouldDump(ExplicitDWO, ".debug_loc.dwo", DIDT_DebugLoc, + if (shouldDump(ExplicitDWO, ".debug_loc.dwo", DIDT_ID_DebugLoc, DObj->getLocDWOSection().Data)) { getDebugLocDWO()->dump(OS, getRegisterInfo()); } - if (shouldDump(Explicit, ".debug_frame", DIDT_DebugFrames, + if (shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrames, DObj->getDebugFrameSection())) { getDebugFrame()->dump(OS); } @@ -302,7 +310,7 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { } } - if (shouldDump(Explicit, ".debug_aranges", DIDT_DebugAranges, + if (shouldDump(Explicit, ".debug_aranges", DIDT_ID_DebugAranges, DObj->getARangeSection())) { uint32_t offset = 0; DataExtractor arangesData(DObj->getARangeSection(), isLittleEndian(), 0); @@ -312,7 +320,7 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { } uint8_t savedAddressByteSize = 0; - if (shouldDump(Explicit, ".debug_line", DIDT_DebugLine, + if (shouldDump(Explicit, ".debug_line", DIDT_ID_DebugLine, DObj->getLineSection().Data)) { for (const auto &CU : compile_units()) { savedAddressByteSize = CU->getAddressByteSize(); @@ -335,7 +343,7 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { savedAddressByteSize = CU->getAddressByteSize(); break; } - if (shouldDump(ExplicitDWO, ".debug_line.dwo", DIDT_DebugLine, + if (shouldDump(ExplicitDWO, ".debug_line.dwo", DIDT_ID_DebugLine, DObj->getLineDWOSection().Data)) { unsigned stmtOffset = 0; DWARFDataExtractor lineData(*DObj, DObj->getLineDWOSection(), @@ -347,17 +355,17 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { } } - if (shouldDump(Explicit, ".debug_cu_index", DIDT_DebugCUIndex, + if (shouldDump(Explicit, ".debug_cu_index", DIDT_ID_DebugCUIndex, DObj->getCUIndexSection())) { getCUIndex().dump(OS); } - if (shouldDump(Explicit, ".debug_tu_index", DIDT_DebugTUIndex, + if (shouldDump(Explicit, ".debug_tu_index", DIDT_ID_DebugTUIndex, DObj->getTUIndexSection())) { getTUIndex().dump(OS); } - if (shouldDump(Explicit, ".debug_str", DIDT_DebugStr, + if (shouldDump(Explicit, ".debug_str", DIDT_ID_DebugStr, DObj->getStringSection())) { DataExtractor strData(DObj->getStringSection(), isLittleEndian(), 0); uint32_t offset = 0; @@ -367,7 +375,7 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { strOffset = offset; } } - if (shouldDump(ExplicitDWO, ".debug_str.dwo", DIDT_DebugStr, + if (shouldDump(ExplicitDWO, ".debug_str.dwo", DIDT_ID_DebugStr, DObj->getStringDWOSection())) { DataExtractor strDWOData(DObj->getStringDWOSection(), isLittleEndian(), 0); uint32_t offset = 0; @@ -378,7 +386,7 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { } } - if (shouldDump(Explicit, ".debug_ranges", DIDT_DebugRanges, + if (shouldDump(Explicit, ".debug_ranges", DIDT_ID_DebugRanges, DObj->getRangeSection().Data)) { // In fact, different compile units may have different address byte // sizes, but for simplicity we just use the address byte size of the @@ -393,60 +401,60 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { rangeList.dump(OS); } - if (shouldDump(Explicit, ".debug_pubnames", DIDT_DebugPubnames, + if (shouldDump(Explicit, ".debug_pubnames", DIDT_ID_DebugPubnames, DObj->getPubNamesSection())) DWARFDebugPubTable(DObj->getPubNamesSection(), isLittleEndian(), false) .dump(OS); - if (shouldDump(Explicit, ".debug_pubtypes", DIDT_DebugPubtypes, + if (shouldDump(Explicit, ".debug_pubtypes", DIDT_ID_DebugPubtypes, DObj->getPubTypesSection())) DWARFDebugPubTable(DObj->getPubTypesSection(), isLittleEndian(), false) .dump(OS); - if (shouldDump(Explicit, ".debug_gnu_pubnames", DIDT_DebugGnuPubnames, + if (shouldDump(Explicit, ".debug_gnu_pubnames", DIDT_ID_DebugGnuPubnames, DObj->getGnuPubNamesSection())) DWARFDebugPubTable(DObj->getGnuPubNamesSection(), isLittleEndian(), true /* GnuStyle */) .dump(OS); - if (shouldDump(Explicit, ".debug_gnu_pubtypes", DIDT_DebugGnuPubtypes, + if (shouldDump(Explicit, ".debug_gnu_pubtypes", DIDT_ID_DebugGnuPubtypes, DObj->getGnuPubTypesSection())) DWARFDebugPubTable(DObj->getGnuPubTypesSection(), isLittleEndian(), true /* GnuStyle */) .dump(OS); - if (shouldDump(Explicit, ".debug_str_offsets", DIDT_DebugStrOffsets, + if (shouldDump(Explicit, ".debug_str_offsets", DIDT_ID_DebugStrOffsets, DObj->getStringOffsetSection().Data)) dumpStringOffsetsSection( OS, "debug_str_offsets", *DObj, DObj->getStringOffsetSection(), DObj->getStringSection(), isLittleEndian(), getMaxVersion()); - if (shouldDump(ExplicitDWO, ".debug_str_offsets.dwo", DIDT_DebugStrOffsets, + if (shouldDump(ExplicitDWO, ".debug_str_offsets.dwo", DIDT_ID_DebugStrOffsets, DObj->getStringOffsetDWOSection().Data)) dumpStringOffsetsSection( OS, "debug_str_offsets.dwo", *DObj, DObj->getStringOffsetDWOSection(), DObj->getStringDWOSection(), isLittleEndian(), getMaxVersion()); - if (shouldDump(Explicit, ".gnu_index", DIDT_GdbIndex, + if (shouldDump(Explicit, ".gnu_index", DIDT_ID_GdbIndex, DObj->getGdbIndexSection())) { getGdbIndex().dump(OS); } - if (shouldDump(Explicit, ".apple_names", DIDT_AppleNames, + if (shouldDump(Explicit, ".apple_names", DIDT_ID_AppleNames, DObj->getAppleNamesSection().Data)) dumpAccelSection(OS, *DObj, DObj->getAppleNamesSection(), DObj->getStringSection(), isLittleEndian()); - if (shouldDump(Explicit, ".apple_types", DIDT_AppleTypes, + if (shouldDump(Explicit, ".apple_types", DIDT_ID_AppleTypes, DObj->getAppleTypesSection().Data)) dumpAccelSection(OS, *DObj, DObj->getAppleTypesSection(), DObj->getStringSection(), isLittleEndian()); - if (shouldDump(Explicit, ".apple_namespaces", DIDT_AppleNamespaces, + if (shouldDump(Explicit, ".apple_namespaces", DIDT_ID_AppleNamespaces, DObj->getAppleNamespacesSection().Data)) dumpAccelSection(OS, *DObj, DObj->getAppleNamespacesSection(), DObj->getStringSection(), isLittleEndian()); - if (shouldDump(Explicit, ".apple_objc", DIDT_AppleObjC, + if (shouldDump(Explicit, ".apple_objc", DIDT_ID_AppleObjC, DObj->getAppleObjCSection().Data)) dumpAccelSection(OS, *DObj, DObj->getAppleObjCSection(), DObj->getStringSection(), isLittleEndian()); diff --git a/test/tools/llvm-dwarfdump/X86/debug_info_offset.test b/test/tools/llvm-dwarfdump/X86/debug_info_offset.test new file mode 100644 index 00000000000..d3d1b6a94c6 --- /dev/null +++ b/test/tools/llvm-dwarfdump/X86/debug_info_offset.test @@ -0,0 +1,10 @@ +RUN: llvm-mc %S/brief.s -filetype obj -triple x86_64-apple-darwin -o - \ +RUN: | llvm-dwarfdump -debug-info=0x0000000b - | FileCheck %s +CHECK: .debug_info contents: +CHECK: 0x0000000b: DW_TAG_compile_unit +CHECK: DW_AT_name +CHECK-NOT: {{:}} + +RUN: llvm-mc %S/brief.s -filetype obj -triple x86_64-apple-darwin -o - \ +RUN: | llvm-dwarfdump -debug-info=0 - | FileCheck --allow-empty --check-prefix=EMPTY %s +EMPTY-NOT: DW_TAG diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index 823eb8e8133..7671bd8adaf 100644 --- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -37,8 +37,59 @@ using namespace llvm; using namespace object; +/// Parser for options that take an optional offest argument. +/// @{ +struct OffsetOption { + uint64_t Val = 0; + bool HasValue = false; + bool IsRequested = false; +}; + +template <> +class cl::parser final : public cl::basic_parser { +public: + parser(Option &O) : basic_parser(O) {} + + /// Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, OffsetOption &Val) { + if (Arg == "") { + Val.Val = 0; + Val.HasValue = false; + Val.IsRequested = true; + return false; + } + if (Arg.getAsInteger(0, Val.Val)) + return O.error("'" + Arg + "' value invalid for integer argument!"); + Val.HasValue = true; + Val.IsRequested = true; + return false; + } + + enum ValueExpected getValueExpectedFlagDefault() const { + return ValueOptional; + } + + void printOptionInfo(const Option &O, size_t GlobalWidth) const { + outs() << " -" << O.ArgStr; + Option::printHelpStr(O.HelpStr, GlobalWidth, getOptionWidth(O)); + } + + void printOptionDiff(const Option &O, OffsetOption V, OptVal Default, + size_t GlobalWidth) const { + printOptionName(O, GlobalWidth); + outs() << "[=offset]"; + } + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override {}; +}; + +/// @} +/// Command line options. +/// @{ + namespace { -using namespace llvm::cl; +using namespace cl; OptionCategory DwarfDumpCategory("Specific Options"); static opt Help("h", desc("Alias for -help"), Hidden, @@ -47,20 +98,26 @@ static list InputFilenames(Positional, desc(""), ZeroOrMore, cat(DwarfDumpCategory)); -cl::OptionCategory - SectionCategory("Section-specific Dump Options", - "These control which sections are dumped."); +cl::OptionCategory SectionCategory("Section-specific Dump Options", + "These control which sections are dumped. " + "Where applicable these parameters take an " + "optional = argument to dump only " + "the entry at the specified offset."); + static opt DumpAll("all", desc("Dump all debug info sections"), cat(SectionCategory)); static alias DumpAllAlias("a", desc("Alias for -all"), aliasopt(DumpAll)); +// Options for dumping specific sections. static unsigned DumpType = DIDT_Null; +static std::array, DIDT_ID_Count> DumpOffsets; #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME) \ - static opt Dump##ENUM_NAME(CMDLINE_NAME, \ - desc("Dump the " ELF_NAME " section"), \ - cat(SectionCategory)); + static opt Dump##ENUM_NAME( \ + CMDLINE_NAME, desc("Dump the " ELF_NAME " section"), \ + cat(SectionCategory)); #include "llvm/BinaryFormat/Dwarf.def" #undef HANDLE_DWARF_SECTION + static opt DumpUUID("uuid", desc("Show the UUID for each architecture"), cat(DwarfDumpCategory)); static alias DumpUUIDAlias("u", desc("Alias for -uuid"), aliasopt(DumpUUID)); @@ -78,6 +135,9 @@ static opt Verbose("verbose", static alias VerboseAlias("v", desc("Alias for -verbose"), aliasopt(Verbose), cat(DwarfDumpCategory)); } // namespace +/// @} +//===----------------------------------------------------------------------===// + static void error(StringRef Filename, std::error_code EC) { if (!EC) @@ -103,7 +163,7 @@ static bool dumpObjectFile(ObjectFile &Obj, Twine Filename) { outs() << Filename << ":\tfile format " << Obj.getFileFormatName() << '\n'; // Dump the complete DWARF structure. - DICtx->dump(outs(), getDumpOpts()); + DICtx->dump(outs(), getDumpOpts(), DumpOffsets); return true; } @@ -237,8 +297,11 @@ int main(int argc, char **argv) { // Defaults to dumping all sections, unless brief mode is specified in which // case only the .debug_info section in dumped. #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME) \ - if (Dump##ENUM_NAME) \ - DumpType |= DIDT_##ENUM_NAME; + if (Dump##ENUM_NAME.IsRequested) { \ + DumpType |= DIDT_##ENUM_NAME; \ + if (Dump##ENUM_NAME.HasValue) \ + DumpOffsets[DIDT_ID_##ENUM_NAME] = Dump##ENUM_NAME.Val; \ + } #include "llvm/BinaryFormat/Dwarf.def" #undef HANDLE_DWARF_SECTION if (DumpUUID)