diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h index 6439827ef70f0..18555bafdc1f0 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h @@ -62,6 +62,7 @@ class DWARFDebugAbbrev { mutable DWARFAbbreviationDeclarationSetMap AbbrDeclSets; mutable DWARFAbbreviationDeclarationSetMap::const_iterator PrevAbbrOffsetPos; mutable std::optional Data; + mutable std::map CUAbbrevs; public: DWARFDebugAbbrev(DataExtractor Data); @@ -69,6 +70,9 @@ class DWARFDebugAbbrev { Expected getAbbreviationDeclarationSet(uint64_t CUAbbrOffset) const; + Expected + tryExtractCUAbbrevFast(uint64_t CUAbbrOffset) const; + void dump(raw_ostream &OS) const; Error parse() const; diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h index 80c27aea89312..8c019841035a1 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h @@ -419,6 +419,10 @@ class DWARFUnit { uint64_t getAbbreviationsOffset() const { return Header.getAbbrOffset(); } + /// Extracts only the abbreviation declaration with code 1, which is + /// typically the compile unit DIE (DW_TAG_compile_unit). + Expected tryExtractCUAbbrevFast() const; + const DWARFAbbreviationDeclarationSet *getAbbreviations() const; static bool isMatchingUnitTypeAndTag(uint8_t UnitType, dwarf::Tag Tag) { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp index 85959ecc5e17f..70fd5c1a8ce33 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp @@ -168,3 +168,25 @@ DWARFDebugAbbrev::getAbbreviationDeclarationSet(uint64_t CUAbbrOffset) const { .first; return &PrevAbbrOffsetPos->second; } + +Expected +DWARFDebugAbbrev::tryExtractCUAbbrevFast(uint64_t CUAbbrOffset) const { + if (auto AbbrevDecl = CUAbbrevs.find(CUAbbrOffset); + AbbrevDecl != CUAbbrevs.end()) + return &AbbrevDecl->second; + + if (!Data || CUAbbrOffset >= Data->getData().size()) + return make_error( + "the abbreviation offset into the .debug_abbrev section is not valid"); + + DWARFAbbreviationDeclaration Decl; + uint64_t Offset = CUAbbrOffset; + Expected ES = + Decl.extract(*Data, &Offset); + if (!ES) + return ES.takeError(); + if (Decl.getCode() != 1) + return nullptr; + + return &(CUAbbrevs[CUAbbrOffset] = std::move(Decl)); +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp index 4f0a6d96ace9e..c5156e55393f2 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugInfoEntry.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" #include #include @@ -34,36 +35,55 @@ bool DWARFDebugInfoEntry::extractFast(const DWARFUnit &U, uint64_t *OffsetPtr, return false; } assert(DebugInfoData.isValidOffset(UEndOffset - 1)); + AbbrevDecl = nullptr; + uint64_t AbbrCode = DebugInfoData.getULEB128(OffsetPtr); if (0 == AbbrCode) { // NULL debug tag entry. - AbbrevDecl = nullptr; return true; } - const auto *AbbrevSet = U.getAbbreviations(); - if (!AbbrevSet) { - U.getContext().getWarningHandler()( - createStringError(errc::invalid_argument, - "DWARF unit at offset 0x%8.8" PRIx64 " " - "contains invalid abbreviation set offset 0x%" PRIx64, - U.getOffset(), U.getAbbreviationsOffset())); - // Restore the original offset. - *OffsetPtr = Offset; - return false; + + // Fast path: parsing the entire abbreviation table is wasteful if we only + // need the unit DIE (typically AbbrCode == 1). + if (AbbrCode == 1) { + Expected DeclOrError = + U.tryExtractCUAbbrevFast(); + if (DeclOrError) + AbbrevDecl = *DeclOrError; + else + consumeError(DeclOrError.takeError()); // Retry full parsing below. } - AbbrevDecl = AbbrevSet->getAbbreviationDeclaration(AbbrCode); + if (!AbbrevDecl) { - U.getContext().getWarningHandler()( - createStringError(errc::invalid_argument, - "DWARF unit at offset 0x%8.8" PRIx64 " " - "contains invalid abbreviation %" PRIu64 " at " - "offset 0x%8.8" PRIx64 ", valid abbreviations are %s", - U.getOffset(), AbbrCode, *OffsetPtr, - AbbrevSet->getCodeRange().c_str())); - // Restore the original offset. - *OffsetPtr = Offset; - return false; + const auto *AbbrevSet = U.getAbbreviations(); + if (!AbbrevSet) { + U.getContext().getWarningHandler()(createStringError( + errc::invalid_argument, + "DWARF unit at offset 0x%8.8" PRIx64 " " + "contains invalid abbreviation set offset 0x%" PRIx64, + U.getOffset(), U.getAbbreviationsOffset())); + // Restore the original offset. + *OffsetPtr = Offset; + return false; + } + AbbrevDecl = AbbrevSet->getAbbreviationDeclaration(AbbrCode); + + if (!AbbrevDecl) { + U.getContext().getWarningHandler()(createStringError( + errc::invalid_argument, + "DWARF unit at offset 0x%8.8" PRIx64 " " + "contains invalid abbreviation %" PRIu64 " at " + "offset 0x%8.8" PRIx64 ", valid abbreviations are %s", + U.getOffset(), AbbrCode, *OffsetPtr, + AbbrevSet->getCodeRange().c_str())); + // Restore the original offset. + *OffsetPtr = Offset; + return false; + } } + + assert(AbbrevDecl && AbbrevDecl->getCode() == AbbrCode); + // See if all attributes in this DIE have fixed byte sizes. If so, we can // just add this size to the offset to skip to the next DIE. if (std::optional FixedSize = diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index bdd04b00f557b..aab3f5f078304 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -1051,6 +1051,11 @@ DWARFUnit::getLastChildEntry(const DWARFDebugInfoEntry *Die) const { return nullptr; } +Expected +DWARFUnit::tryExtractCUAbbrevFast() const { + return Abbrev->tryExtractCUAbbrevFast(getAbbreviationsOffset()); +} + const DWARFAbbreviationDeclarationSet *DWARFUnit::getAbbreviations() const { if (!Abbrevs) { Expected AbbrevsOrError =