diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h index e56d3781e824f..728eb869ff01b 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h @@ -30,6 +30,19 @@ class DWARFDebugAbbrev; class DataExtractor; struct DWARFSection; +class OutputCategoryAggregator { +private: + std::map Aggregation; + bool CallDetail; + +public: + OutputCategoryAggregator(bool callDetail = false) : CallDetail(callDetail) {} + void EnableDetail() { CallDetail = true; } + void DisableDetail() { CallDetail = false; } + void Report(StringRef s, std::function detailCallback); + void HandleAggregate(std::function handleCounts); +}; + /// A class that verifies DWARF debug information given a DWARF Context. class DWARFVerifier { public: @@ -76,9 +89,9 @@ class DWARFVerifier { bool intersects(const DieRangeInfo &RHS) const; }; -private: raw_ostream &OS; DWARFContext &DCtx; + OutputCategoryAggregator ErrorCategory; DIDumpOptions DumpOpts; uint32_t NumDebugLineErrors = 0; // Used to relax some checks that do not currently work portably @@ -348,6 +361,9 @@ class DWARFVerifier { bool verifyDebugStrOffsets( StringRef SectionName, const DWARFSection &Section, StringRef StrData, void (DWARFObject::*)(function_ref) const); + + /// Emits any aggregate information collection, depending on the dump options + void finish(bool Success); }; static inline bool operator<(const DWARFVerifier::DieRangeInfo &LHS, diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp index 792df53d304aa..13d9103a76ec2 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -1408,6 +1408,7 @@ bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) { if (DumpOpts.DumpType & DIDT_DebugStrOffsets) Success &= verifier.handleDebugStrOffsets(); Success &= verifier.handleAccelTables(); + verifier.finish(Success); return Success; } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp index c4c14f5e2c9d3..7ac47cf11ab9f 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -167,20 +167,24 @@ bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData, if (!ValidLength || !ValidVersion || !ValidAddrSize || !ValidAbbrevOffset || !ValidType) { Success = false; - error() << format("Units[%d] - start offset: 0x%08" PRIx64 " \n", UnitIndex, - OffsetStart); - if (!ValidLength) - note() << "The length for this unit is too " - "large for the .debug_info provided.\n"; - if (!ValidVersion) - note() << "The 16 bit unit header version is not valid.\n"; - if (!ValidType) - note() << "The unit type encoding is not valid.\n"; - if (!ValidAbbrevOffset) - note() << "The offset into the .debug_abbrev section is " - "not valid.\n"; - if (!ValidAddrSize) - note() << "The address size is unsupported.\n"; + ErrorCategory.Report("Invalid Length in Unit Header", [&]() { + error() << format("Units[%d] - start offset: 0x%08" PRIx64 " \n", + UnitIndex, OffsetStart); + if (!ValidLength) + note() << "The length for this unit is too " + "large for the .debug_info provided.\n"; + if (!ValidVersion) { + note() << "The 16 bit unit header version is not valid.\n"; + } + if (!ValidType) { + note() << "The unit type encoding is not valid.\n"; + } + if (!ValidAbbrevOffset) + note() << "The offset into the .debug_abbrev section is " + "not valid.\n"; + if (!ValidAddrSize) + note() << "The address size is unsupported.\n"; + }); } *Offset = OffsetStart + Length + (isUnitDWARF64 ? 12 : 4); return Success; @@ -198,12 +202,16 @@ bool DWARFVerifier::verifyName(const DWARFDie &Die) { if (OriginalFullName.empty() || OriginalFullName == ReconstructedName) return false; - error() << "Simplified template DW_AT_name could not be reconstituted:\n" - << formatv(" original: {0}\n" - " reconstituted: {1}\n", - OriginalFullName, ReconstructedName); - dump(Die) << '\n'; - dump(Die.getDwarfUnit()->getUnitDIE()) << '\n'; + ErrorCategory.Report( + "Simplified template DW_AT_name could not be reconstituted", [&]() { + error() + << "Simplified template DW_AT_name could not be reconstituted:\n" + << formatv(" original: {0}\n" + " reconstituted: {1}\n", + OriginalFullName, ReconstructedName); + dump(Die) << '\n'; + dump(Die.getDwarfUnit()->getUnitDIE()) << '\n'; + }); return true; } @@ -240,22 +248,28 @@ unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit, DWARFDie Die = Unit.getUnitDIE(/* ExtractUnitDIEOnly = */ false); if (!Die) { - error() << "Compilation unit without DIE.\n"; + ErrorCategory.Report("Compilation unit missing DIE", [&]() { + error() << "Compilation unit without DIE.\n"; + }); NumUnitErrors++; return NumUnitErrors; } if (!dwarf::isUnitType(Die.getTag())) { - error() << "Compilation unit root DIE is not a unit DIE: " - << dwarf::TagString(Die.getTag()) << ".\n"; + ErrorCategory.Report("Compilation unit root DIE is not a unit DIE", [&]() { + error() << "Compilation unit root DIE is not a unit DIE: " + << dwarf::TagString(Die.getTag()) << ".\n"; + }); NumUnitErrors++; } uint8_t UnitType = Unit.getUnitType(); if (!DWARFUnit::isMatchingUnitTypeAndTag(UnitType, Die.getTag())) { - error() << "Compilation unit type (" << dwarf::UnitTypeString(UnitType) - << ") and root DIE (" << dwarf::TagString(Die.getTag()) - << ") do not match.\n"; + ErrorCategory.Report("Mismatched unit type", [&]() { + error() << "Compilation unit type (" << dwarf::UnitTypeString(UnitType) + << ") and root DIE (" << dwarf::TagString(Die.getTag()) + << ") do not match.\n"; + }); NumUnitErrors++; } @@ -263,7 +277,9 @@ unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit, // 3.1.2 Skeleton Compilation Unit Entries: // "A skeleton compilation unit has no children." if (Die.getTag() == dwarf::DW_TAG_skeleton_unit && Die.hasChildren()) { - error() << "Skeleton compilation unit has children.\n"; + ErrorCategory.Report("Skeleton CU has children", [&]() { + error() << "Skeleton compilation unit has children.\n"; + }); NumUnitErrors++; } @@ -280,15 +296,21 @@ unsigned DWARFVerifier::verifyDebugInfoCallSite(const DWARFDie &Die) { DWARFDie Curr = Die.getParent(); for (; Curr.isValid() && !Curr.isSubprogramDIE(); Curr = Die.getParent()) { if (Curr.getTag() == DW_TAG_inlined_subroutine) { - error() << "Call site entry nested within inlined subroutine:"; - Curr.dump(OS); + ErrorCategory.Report( + "Call site nested entry within inlined subroutine", [&]() { + error() << "Call site entry nested within inlined subroutine:"; + Curr.dump(OS); + }); return 1; } } if (!Curr.isValid()) { - error() << "Call site entry not nested within a valid subprogram:"; - Die.dump(OS); + ErrorCategory.Report( + "Call site entry not nexted within valid subprogram", [&]() { + error() << "Call site entry not nested within a valid subprogram:"; + Die.dump(OS); + }); return 1; } @@ -297,9 +319,13 @@ unsigned DWARFVerifier::verifyDebugInfoCallSite(const DWARFDie &Die) { DW_AT_call_all_tail_calls, DW_AT_GNU_all_call_sites, DW_AT_GNU_all_source_call_sites, DW_AT_GNU_all_tail_call_sites}); if (!CallAttr) { - error() << "Subprogram with call site entry has no DW_AT_call attribute:"; - Curr.dump(OS); - Die.dump(OS, /*indent*/ 1); + ErrorCategory.Report( + "Subprogram with call site entry has no DW_AT_call attribute", [&]() { + error() + << "Subprogram with call site entry has no DW_AT_call attribute:"; + Curr.dump(OS); + Die.dump(OS, /*indent*/ 1); + }); return 1; } @@ -313,7 +339,9 @@ unsigned DWARFVerifier::verifyAbbrevSection(const DWARFDebugAbbrev *Abbrev) { Expected AbbrDeclsOrErr = Abbrev->getAbbreviationDeclarationSet(0); if (!AbbrDeclsOrErr) { - error() << toString(AbbrDeclsOrErr.takeError()) << "\n"; + ErrorCategory.Report("Abbreviation Declaration error", [&]() { + error() << toString(AbbrDeclsOrErr.takeError()) << "\n"; + }); return 1; } @@ -324,9 +352,12 @@ unsigned DWARFVerifier::verifyAbbrevSection(const DWARFDebugAbbrev *Abbrev) { for (auto Attribute : AbbrDecl.attributes()) { auto Result = AttributeSet.insert(Attribute.Attr); if (!Result.second) { - error() << "Abbreviation declaration contains multiple " - << AttributeString(Attribute.Attr) << " attributes.\n"; - AbbrDecl.dump(OS); + ErrorCategory.Report( + "Abbreviation declartion contains multiple attributes", [&]() { + error() << "Abbreviation declaration contains multiple " + << AttributeString(Attribute.Attr) << " attributes.\n"; + AbbrDecl.dump(OS); + }); ++NumErrors; } } @@ -440,10 +471,15 @@ unsigned DWARFVerifier::verifyIndex(StringRef Name, auto &M = *Sections[Col]; auto I = M.find(SC.getOffset()); if (I != M.end() && I.start() < (SC.getOffset() + SC.getLength())) { - error() << llvm::formatv( - "overlapping index entries for entries {0:x16} " - "and {1:x16} for column {2}\n", - *I, Sig, toString(Index.getColumnKinds()[Col])); + StringRef Category = InfoColumnKind == DWARFSectionKind::DW_SECT_INFO + ? "Overlapping CU index entries" + : "Overlapping TU index entries"; + ErrorCategory.Report(Category, [&]() { + error() << llvm::formatv( + "overlapping index entries for entries {0:x16} " + "and {1:x16} for column {2}\n", + *I, Sig, toString(Index.getColumnKinds()[Col])); + }); return 1; } M.insert(SC.getOffset(), SC.getOffset() + SC.getLength() - 1, Sig); @@ -532,7 +568,9 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, for (const auto &Range : Ranges) { if (!Range.valid()) { ++NumErrors; - error() << "Invalid address range " << Range << "\n"; + ErrorCategory.Report("Invalid address range", [&]() { + error() << "Invalid address range " << Range << "\n"; + }); DumpDieAfterError = true; continue; } @@ -545,9 +583,11 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, // address: 0 or -1. if (auto PrevRange = RI.insert(Range)) { ++NumErrors; - error() << "DIE has overlapping ranges in DW_AT_ranges attribute: " - << *PrevRange << " and " << Range << '\n'; - DumpDieAfterError = true; + ErrorCategory.Report("DIE has overlapping DW_AT_ranges", [&]() { + error() << "DIE has overlapping ranges in DW_AT_ranges attribute: " + << *PrevRange << " and " << Range << '\n'; + }); + DumpDieAfterError = DumpOpts.Verbose; } } if (DumpDieAfterError) @@ -558,9 +598,11 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, const auto IntersectingChild = ParentRI.insert(RI); if (IntersectingChild != ParentRI.Children.end()) { ++NumErrors; - error() << "DIEs have overlapping address ranges:"; - dump(Die); - dump(IntersectingChild->Die) << '\n'; + ErrorCategory.Report("DIEs have overlapping address ranges", [&]() { + error() << "DIEs have overlapping address ranges:"; + dump(Die); + dump(IntersectingChild->Die) << '\n'; + }); } // Verify that ranges are contained within their parent. @@ -569,9 +611,13 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, ParentRI.Die.getTag() == DW_TAG_subprogram); if (ShouldBeContained && !ParentRI.contains(RI)) { ++NumErrors; - error() << "DIE address ranges are not contained in its parent's ranges:"; - dump(ParentRI.Die); - dump(Die, 2) << '\n'; + ErrorCategory.Report( + "DIE address ranges are not contained by parent ranges", [&]() { + error() + << "DIE address ranges are not contained in its parent's ranges:"; + dump(ParentRI.Die); + dump(Die, 2) << '\n'; + }); } // Recursively check children. @@ -584,10 +630,12 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, DWARFAttribute &AttrValue) { unsigned NumErrors = 0; - auto ReportError = [&](const Twine &TitleMsg) { + auto ReportError = [&](StringRef category, const Twine &TitleMsg) { ++NumErrors; - error() << TitleMsg << '\n'; - dump(Die) << '\n'; + ErrorCategory.Report(category, [&]() { + error() << TitleMsg << '\n'; + dump(Die) << '\n'; + }); }; const DWARFObject &DObj = DCtx.getDWARFObj(); @@ -604,23 +652,27 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, if (U->isDWOUnit() && RangeSection.Data.empty()) break; if (*SectionOffset >= RangeSection.Data.size()) - ReportError( - "DW_AT_ranges offset is beyond " + - StringRef(DwarfVersion < 5 ? ".debug_ranges" : ".debug_rnglists") + - " bounds: " + llvm::formatv("{0:x8}", *SectionOffset)); + ReportError("DW_AT_ranges offset out of bounds", + "DW_AT_ranges offset is beyond " + + StringRef(DwarfVersion < 5 ? ".debug_ranges" + : ".debug_rnglists") + + " bounds: " + llvm::formatv("{0:x8}", *SectionOffset)); break; } - ReportError("DIE has invalid DW_AT_ranges encoding:"); + ReportError("Invalid DW_AT_ranges encoding", + "DIE has invalid DW_AT_ranges encoding:"); break; case DW_AT_stmt_list: // Make sure the offset in the DW_AT_stmt_list attribute is valid. if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { if (*SectionOffset >= U->getLineSection().Data.size()) - ReportError("DW_AT_stmt_list offset is beyond .debug_line bounds: " + - llvm::formatv("{0:x8}", *SectionOffset)); + ReportError("DW_AT_stmt_list offset out of bounds", + "DW_AT_stmt_list offset is beyond .debug_line bounds: " + + llvm::formatv("{0:x8}", *SectionOffset)); break; } - ReportError("DIE has invalid DW_AT_stmt_list encoding:"); + ReportError("Invalid DW_AT_stmt_list encoding", + "DIE has invalid DW_AT_stmt_list encoding:"); break; case DW_AT_location: { // FIXME: It might be nice if there's a way to walk location expressions @@ -644,14 +696,15 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, return Op.isError(); }); if (Error || !Expression.verify(U)) - ReportError("DIE contains invalid DWARF expression:"); + ReportError("Invalid DWARF expressions", + "DIE contains invalid DWARF expression:"); } } else if (Error Err = handleErrors( Loc.takeError(), [&](std::unique_ptr E) { return U->isDWOUnit() ? Error::success() : Error(std::move(E)); })) - ReportError(toString(std::move(Err))); + ReportError("Invalid DW_AT_location", toString(std::move(Err))); break; } case DW_AT_specification: @@ -668,19 +721,21 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, // This might be reference to a function declaration. if (DieTag == DW_TAG_GNU_call_site && RefTag == DW_TAG_subprogram) break; - ReportError("DIE with tag " + TagString(DieTag) + " has " + - AttributeString(Attr) + - " that points to DIE with " - "incompatible tag " + - TagString(RefTag)); + ReportError("Incompatible DW_AT_abstract_origin tag reference", + "DIE with tag " + TagString(DieTag) + " has " + + AttributeString(Attr) + + " that points to DIE with " + "incompatible tag " + + TagString(RefTag)); } break; } case DW_AT_type: { DWARFDie TypeDie = Die.getAttributeValueAsReferencedDie(DW_AT_type); if (TypeDie && !isType(TypeDie.getTag())) { - ReportError("DIE has " + AttributeString(Attr) + - " with incompatible tag " + TagString(TypeDie.getTag())); + ReportError("Incompatible DW_AT_type attribute tag", + "DIE has " + AttributeString(Attr) + + " with incompatible tag " + TagString(TypeDie.getTag())); } break; } @@ -695,35 +750,43 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, bool IsZeroIndexed = LT->Prologue.getVersion() >= 5; if (std::optional LastFileIdx = LT->getLastValidFileIndex()) { - ReportError("DIE has " + AttributeString(Attr) + - " with an invalid file index " + - llvm::formatv("{0}", *FileIdx) + - " (valid values are [" + (IsZeroIndexed ? "0-" : "1-") + - llvm::formatv("{0}", *LastFileIdx) + "])"); + ReportError("Invalid file index in DW_AT_decl_file", + "DIE has " + AttributeString(Attr) + + " with an invalid file index " + + llvm::formatv("{0}", *FileIdx) + + " (valid values are [" + + (IsZeroIndexed ? "0-" : "1-") + + llvm::formatv("{0}", *LastFileIdx) + "])"); } else { - ReportError("DIE has " + AttributeString(Attr) + - " with an invalid file index " + - llvm::formatv("{0}", *FileIdx) + - " (the file table in the prologue is empty)"); + ReportError("Invalid file index in DW_AT_decl_file", + "DIE has " + AttributeString(Attr) + + " with an invalid file index " + + llvm::formatv("{0}", *FileIdx) + + " (the file table in the prologue is empty)"); } } } else { - ReportError("DIE has " + AttributeString(Attr) + - " that references a file with index " + - llvm::formatv("{0}", *FileIdx) + - " and the compile unit has no line table"); + ReportError( + "File index in DW_AT_decl_file reference CU with no line table", + "DIE has " + AttributeString(Attr) + + " that references a file with index " + + llvm::formatv("{0}", *FileIdx) + + " and the compile unit has no line table"); } } else { - ReportError("DIE has " + AttributeString(Attr) + - " with invalid encoding"); + ReportError("Invalid encoding in DW_AT_decl_file", + "DIE has " + AttributeString(Attr) + + " with invalid encoding"); } break; } case DW_AT_call_line: case DW_AT_decl_line: { if (!AttrValue.Value.getAsUnsignedConstant()) { - ReportError("DIE has " + AttributeString(Attr) + - " with invalid encoding"); + ReportError( + Attr == DW_AT_call_line ? "Invalid file index in DW_AT_decl_line" + : "Invalid file index in DW_AT_call_line", + "DIE has " + AttributeString(Attr) + " with invalid encoding"); } break; } @@ -754,12 +817,14 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die, auto CUOffset = AttrValue.Value.getRawUValue(); if (CUOffset >= CUSize) { ++NumErrors; - error() << FormEncodingString(Form) << " CU offset " - << format("0x%08" PRIx64, CUOffset) - << " is invalid (must be less than CU size of " - << format("0x%08" PRIx64, CUSize) << "):\n"; - Die.dump(OS, 0, DumpOpts); - dump(Die) << '\n'; + ErrorCategory.Report("Invalid CU offset", [&]() { + error() << FormEncodingString(Form) << " CU offset " + << format("0x%08" PRIx64, CUOffset) + << " is invalid (must be less than CU size of " + << format("0x%08" PRIx64, CUSize) << "):\n"; + Die.dump(OS, 0, DumpOpts); + dump(Die) << '\n'; + }); } else { // Valid reference, but we will verify it points to an actual // DIE later. @@ -776,9 +841,11 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die, if (RefVal) { if (*RefVal >= DieCU->getInfoSection().Data.size()) { ++NumErrors; - error() << "DW_FORM_ref_addr offset beyond .debug_info " - "bounds:\n"; - dump(Die) << '\n'; + ErrorCategory.Report("DW_FORM_ref_addr offset out of bounds", [&]() { + error() << "DW_FORM_ref_addr offset beyond .debug_info " + "bounds:\n"; + dump(Die) << '\n'; + }); } else { // Valid reference, but we will verify it points to an actual // DIE later. @@ -796,8 +863,10 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die, case DW_FORM_line_strp: { if (Error E = AttrValue.Value.getAsCString().takeError()) { ++NumErrors; - error() << toString(std::move(E)) << ":\n"; - dump(Die) << '\n'; + ErrorCategory.Report("Invalid DW_FORM attribute", [&]() { + error() << toString(std::move(E)) << ":\n"; + dump(Die) << '\n'; + }); } break; } @@ -821,11 +890,13 @@ unsigned DWARFVerifier::verifyDebugInfoReferences( if (GetDIEForOffset(Pair.first)) continue; ++NumErrors; - error() << "invalid DIE reference " << format("0x%08" PRIx64, Pair.first) - << ". Offset is in between DIEs:\n"; - for (auto Offset : Pair.second) - dump(GetDIEForOffset(Offset)) << '\n'; - OS << "\n"; + ErrorCategory.Report("Invalid DIE reference", [&]() { + error() << "invalid DIE reference " << format("0x%08" PRIx64, Pair.first) + << ". Offset is in between DIEs:\n"; + for (auto Offset : Pair.second) + dump(GetDIEForOffset(Offset)) << '\n'; + OS << "\n"; + }); } return NumErrors; } @@ -845,9 +916,11 @@ void DWARFVerifier::verifyDebugLineStmtOffsets() { if (LineTableOffset < DCtx.getDWARFObj().getLineSection().Data.size()) { if (!LineTable) { ++NumDebugLineErrors; - error() << ".debug_line[" << format("0x%08" PRIx64, LineTableOffset) - << "] was not able to be parsed for CU:\n"; - dump(Die) << '\n'; + ErrorCategory.Report("Unparseable .debug_line entry", [&]() { + error() << ".debug_line[" << format("0x%08" PRIx64, LineTableOffset) + << "] was not able to be parsed for CU:\n"; + dump(Die) << '\n'; + }); continue; } } else { @@ -860,12 +933,14 @@ void DWARFVerifier::verifyDebugLineStmtOffsets() { auto Iter = StmtListToDie.find(LineTableOffset); if (Iter != StmtListToDie.end()) { ++NumDebugLineErrors; - error() << "two compile unit DIEs, " - << format("0x%08" PRIx64, Iter->second.getOffset()) << " and " - << format("0x%08" PRIx64, Die.getOffset()) - << ", have the same DW_AT_stmt_list section offset:\n"; - dump(Iter->second); - dump(Die) << '\n'; + ErrorCategory.Report("Identical DW_AT_stmt_list section offset", [&]() { + error() << "two compile unit DIEs, " + << format("0x%08" PRIx64, Iter->second.getOffset()) << " and " + << format("0x%08" PRIx64, Die.getOffset()) + << ", have the same DW_AT_stmt_list section offset:\n"; + dump(Iter->second); + dump(Die) << '\n'; + }); // Already verified this line table before, no need to do it again. continue; } @@ -892,12 +967,16 @@ void DWARFVerifier::verifyDebugLineRows() { // Verify directory index. if (FileName.DirIdx > MaxDirIndex) { ++NumDebugLineErrors; - error() << ".debug_line[" - << format("0x%08" PRIx64, - *toSectionOffset(Die.find(DW_AT_stmt_list))) - << "].prologue.file_names[" << FileIndex - << "].dir_idx contains an invalid index: " << FileName.DirIdx - << "\n"; + ErrorCategory.Report( + "Invalid index in .debug_line->prologue.file_names->dir_idx", + [&]() { + error() << ".debug_line[" + << format("0x%08" PRIx64, + *toSectionOffset(Die.find(DW_AT_stmt_list))) + << "].prologue.file_names[" << FileIndex + << "].dir_idx contains an invalid index: " + << FileName.DirIdx << "\n"; + }); } // Check file paths for duplicates. @@ -910,7 +989,7 @@ void DWARFVerifier::verifyDebugLineRows() { auto It = FullPathMap.find(FullPath); if (It == FullPathMap.end()) FullPathMap[FullPath] = FileIndex; - else if (It->second != FileIndex) { + else if (It->second != FileIndex && DumpOpts.Verbose) { warn() << ".debug_line[" << format("0x%08" PRIx64, *toSectionOffset(Die.find(DW_AT_stmt_list))) @@ -928,17 +1007,20 @@ void DWARFVerifier::verifyDebugLineRows() { // Verify row address. if (Row.Address.Address < PrevAddress) { ++NumDebugLineErrors; - error() << ".debug_line[" - << format("0x%08" PRIx64, - *toSectionOffset(Die.find(DW_AT_stmt_list))) - << "] row[" << RowIndex - << "] decreases in address from previous row:\n"; - - DWARFDebugLine::Row::dumpTableHeader(OS, 0); - if (RowIndex > 0) - LineTable->Rows[RowIndex - 1].dump(OS); - Row.dump(OS); - OS << '\n'; + ErrorCategory.Report( + "decreasing address between debug_line rows", [&]() { + error() << ".debug_line[" + << format("0x%08" PRIx64, + *toSectionOffset(Die.find(DW_AT_stmt_list))) + << "] row[" << RowIndex + << "] decreases in address from previous row:\n"; + + DWARFDebugLine::Row::dumpTableHeader(OS, 0); + if (RowIndex > 0) + LineTable->Rows[RowIndex - 1].dump(OS); + Row.dump(OS); + OS << '\n'; + }); } // If the prologue contains no file names and the line table has only one @@ -949,16 +1031,18 @@ void DWARFVerifier::verifyDebugLineRows() { LineTable->Rows.size() != 1) && !LineTable->hasFileAtIndex(Row.File)) { ++NumDebugLineErrors; - error() << ".debug_line[" - << format("0x%08" PRIx64, - *toSectionOffset(Die.find(DW_AT_stmt_list))) - << "][" << RowIndex << "] has invalid file index " << Row.File - << " (valid values are [" << MinFileIndex << ',' - << LineTable->Prologue.FileNames.size() - << (isDWARF5 ? ")" : "]") << "):\n"; - DWARFDebugLine::Row::dumpTableHeader(OS, 0); - Row.dump(OS); - OS << '\n'; + ErrorCategory.Report("Invalid file index in debug_line", [&]() { + error() << ".debug_line[" + << format("0x%08" PRIx64, + *toSectionOffset(Die.find(DW_AT_stmt_list))) + << "][" << RowIndex << "] has invalid file index " << Row.File + << " (valid values are [" << MinFileIndex << ',' + << LineTable->Prologue.FileNames.size() + << (isDWARF5 ? ")" : "]") << "):\n"; + DWARFDebugLine::Row::dumpTableHeader(OS, 0); + Row.dump(OS); + OS << '\n'; + }); } if (Row.EndSequence) PrevAddress = 0; @@ -973,6 +1057,9 @@ DWARFVerifier::DWARFVerifier(raw_ostream &S, DWARFContext &D, DIDumpOptions DumpOpts) : OS(S), DCtx(D), DumpOpts(std::move(DumpOpts)), IsObjectFile(false), IsMachOObject(false) { + if (DumpOpts.Verbose) { + ErrorCategory.EnableDetail(); + } if (const auto *F = DCtx.getDWARFObj().getFile()) { IsObjectFile = F->isRelocatableObject(); IsMachOObject = F->isMachO(); @@ -999,13 +1086,17 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection, // Verify that the fixed part of the header is not too short. if (!AccelSectionData.isValidOffset(AccelTable.getSizeHdr())) { - error() << "Section is too small to fit a section header.\n"; + ErrorCategory.Report("Section is too small to fit a section header", [&]() { + error() << "Section is too small to fit a section header.\n"; + }); return 1; } // Verify that the section is not too short. if (Error E = AccelTable.extract()) { - error() << toString(std::move(E)) << '\n'; + std::string Msg = toString(std::move(E)); + ErrorCategory.Report("Section is too small to fit a section header", + [&]() { error() << Msg << '\n'; }); return 1; } @@ -1020,18 +1111,24 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection, for (uint32_t BucketIdx = 0; BucketIdx < NumBuckets; ++BucketIdx) { uint32_t HashIdx = AccelSectionData.getU32(&BucketsOffset); if (HashIdx >= NumHashes && HashIdx != UINT32_MAX) { - error() << format("Bucket[%d] has invalid hash index: %u.\n", BucketIdx, - HashIdx); + ErrorCategory.Report("Invalid hash index", [&]() { + error() << format("Bucket[%d] has invalid hash index: %u.\n", BucketIdx, + HashIdx); + }); ++NumErrors; } } uint32_t NumAtoms = AccelTable.getAtomsDesc().size(); if (NumAtoms == 0) { - error() << "No atoms: failed to read HashData.\n"; + ErrorCategory.Report("No atoms", [&]() { + error() << "No atoms: failed to read HashData.\n"; + }); return 1; } if (!AccelTable.validateForms()) { - error() << "Unsupported form: failed to read HashData.\n"; + ErrorCategory.Report("Unsupported form", [&]() { + error() << "Unsupported form: failed to read HashData.\n"; + }); return 1; } @@ -1042,9 +1139,11 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection, uint64_t HashDataOffset = AccelSectionData.getU32(&DataOffset); if (!AccelSectionData.isValidOffsetForDataOfSize(HashDataOffset, sizeof(uint64_t))) { - error() << format("Hash[%d] has invalid HashData offset: " - "0x%08" PRIx64 ".\n", - HashIdx, HashDataOffset); + ErrorCategory.Report("Invalid HashData offset", [&]() { + error() << format("Hash[%d] has invalid HashData offset: " + "0x%08" PRIx64 ".\n", + HashIdx, HashDataOffset); + }); ++NumErrors; } @@ -1068,21 +1167,25 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection, if (!Name) Name = ""; - error() << format( - "%s Bucket[%d] Hash[%d] = 0x%08x " - "Str[%u] = 0x%08" PRIx64 " DIE[%d] = 0x%08" PRIx64 " " - "is not a valid DIE offset for \"%s\".\n", - SectionName, BucketIdx, HashIdx, Hash, StringCount, StrpOffset, - HashDataIdx, Offset, Name); + ErrorCategory.Report("Invalid DIE offset", [&]() { + error() << format( + "%s Bucket[%d] Hash[%d] = 0x%08x " + "Str[%u] = 0x%08" PRIx64 " DIE[%d] = 0x%08" PRIx64 " " + "is not a valid DIE offset for \"%s\".\n", + SectionName, BucketIdx, HashIdx, Hash, StringCount, StrpOffset, + HashDataIdx, Offset, Name); + }); ++NumErrors; continue; } if ((Tag != dwarf::DW_TAG_null) && (Die.getTag() != Tag)) { - error() << "Tag " << dwarf::TagString(Tag) - << " in accelerator table does not match Tag " - << dwarf::TagString(Die.getTag()) << " of DIE[" << HashDataIdx - << "].\n"; + ErrorCategory.Report("Mismatched Tag in accellerator table", [&]() { + error() << "Tag " << dwarf::TagString(Tag) + << " in accelerator table does not match Tag " + << dwarf::TagString(Die.getTag()) << " of DIE[" + << HashDataIdx << "].\n"; + }); ++NumErrors; } } @@ -1106,8 +1209,10 @@ DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) { unsigned NumErrors = 0; for (const DWARFDebugNames::NameIndex &NI : AccelTable) { if (NI.getCUCount() == 0) { - error() << formatv("Name Index @ {0:x} does not index any CU\n", - NI.getUnitOffset()); + ErrorCategory.Report("Name Index doesn't index any CU", [&]() { + error() << formatv("Name Index @ {0:x} does not index any CU\n", + NI.getUnitOffset()); + }); ++NumErrors; continue; } @@ -1116,17 +1221,22 @@ DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) { auto Iter = CUMap.find(Offset); if (Iter == CUMap.end()) { - error() << formatv( - "Name Index @ {0:x} references a non-existing CU @ {1:x}\n", - NI.getUnitOffset(), Offset); + ErrorCategory.Report("Name Index references non-existing CU", [&]() { + error() << formatv( + "Name Index @ {0:x} references a non-existing CU @ {1:x}\n", + NI.getUnitOffset(), Offset); + }); ++NumErrors; continue; } if (Iter->second != NotIndexed) { - error() << formatv("Name Index @ {0:x} references a CU @ {1:x}, but " - "this CU is already indexed by Name Index @ {2:x}\n", - NI.getUnitOffset(), Offset, Iter->second); + ErrorCategory.Report("Duplicate Name Index", [&]() { + error() << formatv( + "Name Index @ {0:x} references a CU @ {1:x}, but " + "this CU is already indexed by Name Index @ {2:x}\n", + NI.getUnitOffset(), Offset, Iter->second); + }); continue; } Iter->second = NI.getUnitOffset(); @@ -1167,9 +1277,12 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI, for (uint32_t Bucket = 0, End = NI.getBucketCount(); Bucket < End; ++Bucket) { uint32_t Index = NI.getBucketArrayEntry(Bucket); if (Index > NI.getNameCount()) { - error() << formatv("Bucket {0} of Name Index @ {1:x} contains invalid " - "value {2}. Valid range is [0, {3}].\n", - Bucket, NI.getUnitOffset(), Index, NI.getNameCount()); + ErrorCategory.Report("Name Index Bucket contains invalid value", [&]() { + error() << formatv("Bucket {0} of Name Index @ {1:x} contains invalid " + "value {2}. Valid range is [0, {3}].\n", + Bucket, NI.getUnitOffset(), Index, + NI.getNameCount()); + }); ++NumErrors; continue; } @@ -1202,9 +1315,11 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI, // will not match because we have already verified that the name's hash // puts it into the previous bucket.) if (B.Index > NextUncovered) { - error() << formatv("Name Index @ {0:x}: Name table entries [{1}, {2}] " - "are not covered by the hash table.\n", - NI.getUnitOffset(), NextUncovered, B.Index - 1); + ErrorCategory.Report("Name table entries uncovered by hash table", [&]() { + error() << formatv("Name Index @ {0:x}: Name table entries [{1}, {2}] " + "are not covered by the hash table.\n", + NI.getUnitOffset(), NextUncovered, B.Index - 1); + }); ++NumErrors; } uint32_t Idx = B.Index; @@ -1220,11 +1335,13 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI, // bucket as empty. uint32_t FirstHash = NI.getHashArrayEntry(Idx); if (FirstHash % NI.getBucketCount() != B.Bucket) { - error() << formatv( - "Name Index @ {0:x}: Bucket {1} is not empty but points to a " - "mismatched hash value {2:x} (belonging to bucket {3}).\n", - NI.getUnitOffset(), B.Bucket, FirstHash, - FirstHash % NI.getBucketCount()); + ErrorCategory.Report("Name Index point to mismatched hash value", [&]() { + error() << formatv( + "Name Index @ {0:x}: Bucket {1} is not empty but points to a " + "mismatched hash value {2:x} (belonging to bucket {3}).\n", + NI.getUnitOffset(), B.Bucket, FirstHash, + FirstHash % NI.getBucketCount()); + }); ++NumErrors; } @@ -1238,11 +1355,14 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI, const char *Str = NI.getNameTableEntry(Idx).getString(); if (caseFoldingDjbHash(Str) != Hash) { - error() << formatv("Name Index @ {0:x}: String ({1}) at index {2} " - "hashes to {3:x}, but " - "the Name Index hash is {4:x}\n", - NI.getUnitOffset(), Str, Idx, - caseFoldingDjbHash(Str), Hash); + ErrorCategory.Report( + "String hash doesn't match Name Index hash", [&]() { + error() << formatv( + "Name Index @ {0:x}: String ({1}) at index {2} " + "hashes to {3:x}, but " + "the Name Index hash is {4:x}\n", + NI.getUnitOffset(), Str, Idx, caseFoldingDjbHash(Str), Hash); + }); ++NumErrors; } @@ -1258,19 +1378,23 @@ unsigned DWARFVerifier::verifyNameIndexAttribute( DWARFDebugNames::AttributeEncoding AttrEnc) { StringRef FormName = dwarf::FormEncodingString(AttrEnc.Form); if (FormName.empty()) { - error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an " - "unknown form: {3}.\n", - NI.getUnitOffset(), Abbr.Code, AttrEnc.Index, - AttrEnc.Form); + ErrorCategory.Report("Unknown NameIndex Abbreviation", [&]() { + error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an " + "unknown form: {3}.\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Index, + AttrEnc.Form); + }); return 1; } if (AttrEnc.Index == DW_IDX_type_hash) { if (AttrEnc.Form != dwarf::DW_FORM_data8) { - error() << formatv( - "NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_type_hash " - "uses an unexpected form {2} (should be {3}).\n", - NI.getUnitOffset(), Abbr.Code, AttrEnc.Form, dwarf::DW_FORM_data8); + ErrorCategory.Report("Unexpected NameIndex Abbreviation", [&]() { + error() << formatv( + "NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_type_hash " + "uses an unexpected form {2} (should be {3}).\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Form, dwarf::DW_FORM_data8); + }); return 1; } return 0; @@ -1280,10 +1404,13 @@ unsigned DWARFVerifier::verifyNameIndexAttribute( constexpr static auto AllowedForms = {dwarf::Form::DW_FORM_flag_present, dwarf::Form::DW_FORM_ref4}; if (!is_contained(AllowedForms, AttrEnc.Form)) { - error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_parent " - "uses an unexpected form {2} (should be " - "DW_FORM_ref4 or DW_FORM_flag_present).\n", - NI.getUnitOffset(), Abbr.Code, AttrEnc.Form); + ErrorCategory.Report("Unexpected NameIndex Abbreviation", [&]() { + error() << formatv( + "NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_parent " + "uses an unexpected form {2} (should be " + "DW_FORM_ref4 or DW_FORM_flag_present).\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Form); + }); return 1; } return 0; @@ -1315,10 +1442,12 @@ unsigned DWARFVerifier::verifyNameIndexAttribute( } if (!DWARFFormValue(AttrEnc.Form).isFormClass(Iter->Class)) { - error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an " - "unexpected form {3} (expected form class {4}).\n", - NI.getUnitOffset(), Abbr.Code, AttrEnc.Index, - AttrEnc.Form, Iter->ClassName); + ErrorCategory.Report("Unexpected NameIndex Abbreviation", [&]() { + error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an " + "unexpected form {3} (expected form class {4}).\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Index, + AttrEnc.Form, Iter->ClassName); + }); return 1; } return 0; @@ -1344,9 +1473,13 @@ DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) { SmallSet Attributes; for (const auto &AttrEnc : Abbrev.Attributes) { if (!Attributes.insert(AttrEnc.Index).second) { - error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains " - "multiple {2} attributes.\n", - NI.getUnitOffset(), Abbrev.Code, AttrEnc.Index); + ErrorCategory.Report( + "NameIndex Abbreviateion contains multiple attributes", [&]() { + error() << formatv( + "NameIndex @ {0:x}: Abbreviation {1:x} contains " + "multiple {2} attributes.\n", + NI.getUnitOffset(), Abbrev.Code, AttrEnc.Index); + }); ++NumErrors; continue; } @@ -1354,16 +1487,20 @@ DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) { } if (NI.getCUCount() > 1 && !Attributes.count(dwarf::DW_IDX_compile_unit)) { - error() << formatv("NameIndex @ {0:x}: Indexing multiple compile units " - "and abbreviation {1:x} has no {2} attribute.\n", - NI.getUnitOffset(), Abbrev.Code, - dwarf::DW_IDX_compile_unit); + ErrorCategory.Report("Abbreviation contains no attribute", [&]() { + error() << formatv("NameIndex @ {0:x}: Indexing multiple compile units " + "and abbreviation {1:x} has no {2} attribute.\n", + NI.getUnitOffset(), Abbrev.Code, + dwarf::DW_IDX_compile_unit); + }); ++NumErrors; } if (!Attributes.count(dwarf::DW_IDX_die_offset)) { - error() << formatv( - "NameIndex @ {0:x}: Abbreviation {1:x} has no {2} attribute.\n", - NI.getUnitOffset(), Abbrev.Code, dwarf::DW_IDX_die_offset); + ErrorCategory.Report("Abbreviate in NameIndex missing attribute", [&]() { + error() << formatv( + "NameIndex @ {0:x}: Abbreviation {1:x} has no {2} attribute.\n", + NI.getUnitOffset(), Abbrev.Code, dwarf::DW_IDX_die_offset); + }); ++NumErrors; } } @@ -1417,9 +1554,11 @@ unsigned DWARFVerifier::verifyNameIndexEntries( const char *CStr = NTE.getString(); if (!CStr) { - error() << formatv( - "Name Index @ {0:x}: Unable to get string associated with name {1}.\n", - NI.getUnitOffset(), NTE.getIndex()); + ErrorCategory.Report("Unable to get string associated with name", [&]() { + error() << formatv("Name Index @ {0:x}: Unable to get string associated " + "with name {1}.\n", + NI.getUnitOffset(), NTE.getIndex()); + }); return 1; } StringRef Str(CStr); @@ -1433,9 +1572,11 @@ unsigned DWARFVerifier::verifyNameIndexEntries( EntryOr = NI.getEntry(&NextEntryID)) { uint32_t CUIndex = *EntryOr->getCUIndex(); if (CUIndex > NI.getCUCount()) { - error() << formatv("Name Index @ {0:x}: Entry @ {1:x} contains an " - "invalid CU index ({2}).\n", - NI.getUnitOffset(), EntryID, CUIndex); + ErrorCategory.Report("Name Index entry contains invalid CU index", [&]() { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x} contains an " + "invalid CU index ({2}).\n", + NI.getUnitOffset(), EntryID, CUIndex); + }); ++NumErrors; continue; } @@ -1443,24 +1584,32 @@ unsigned DWARFVerifier::verifyNameIndexEntries( uint64_t DIEOffset = CUOffset + *EntryOr->getDIEUnitOffset(); DWARFDie DIE = DCtx.getDIEForOffset(DIEOffset); if (!DIE) { - error() << formatv("Name Index @ {0:x}: Entry @ {1:x} references a " - "non-existing DIE @ {2:x}.\n", - NI.getUnitOffset(), EntryID, DIEOffset); + ErrorCategory.Report("NameIndex references nonexisten DIE", [&]() { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x} references a " + "non-existing DIE @ {2:x}.\n", + NI.getUnitOffset(), EntryID, DIEOffset); + }); ++NumErrors; continue; } if (DIE.getDwarfUnit()->getOffset() != CUOffset) { - error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched CU of " - "DIE @ {2:x}: index - {3:x}; debug_info - {4:x}.\n", - NI.getUnitOffset(), EntryID, DIEOffset, CUOffset, - DIE.getDwarfUnit()->getOffset()); + ErrorCategory.Report("Name index contains mismatched CU of DIE", [&]() { + error() << formatv( + "Name Index @ {0:x}: Entry @ {1:x}: mismatched CU of " + "DIE @ {2:x}: index - {3:x}; debug_info - {4:x}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, CUOffset, + DIE.getDwarfUnit()->getOffset()); + }); ++NumErrors; } if (DIE.getTag() != EntryOr->tag()) { - error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Tag of " - "DIE @ {2:x}: index - {3}; debug_info - {4}.\n", - NI.getUnitOffset(), EntryID, DIEOffset, EntryOr->tag(), - DIE.getTag()); + ErrorCategory.Report("Name Index contains mismatched Tag of DIE", [&]() { + error() << formatv( + "Name Index @ {0:x}: Entry @ {1:x}: mismatched Tag of " + "DIE @ {2:x}: index - {3}; debug_info - {4}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, EntryOr->tag(), + DIE.getTag()); + }); ++NumErrors; } @@ -1471,10 +1620,12 @@ unsigned DWARFVerifier::verifyNameIndexEntries( DIE.getTag() == DW_TAG_inlined_subroutine; auto EntryNames = getNames(DIE, IncludeStrippedTemplateNames); if (!is_contained(EntryNames, Str)) { - error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Name " - "of DIE @ {2:x}: index - {3}; debug_info - {4}.\n", - NI.getUnitOffset(), EntryID, DIEOffset, Str, - make_range(EntryNames.begin(), EntryNames.end())); + ErrorCategory.Report("Name Index contains mismatched name of DIE", [&]() { + error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Name " + "of DIE @ {2:x}: index - {3}; debug_info - {4}.\n", + NI.getUnitOffset(), EntryID, DIEOffset, Str, + make_range(EntryNames.begin(), EntryNames.end())); + }); ++NumErrors; } } @@ -1482,18 +1633,22 @@ unsigned DWARFVerifier::verifyNameIndexEntries( [&](const DWARFDebugNames::SentinelError &) { if (NumEntries > 0) return; - error() << formatv("Name Index @ {0:x}: Name {1} ({2}) is " - "not associated with any entries.\n", - NI.getUnitOffset(), NTE.getIndex(), Str); - ++NumErrors; - }, - [&](const ErrorInfoBase &Info) { - error() - << formatv("Name Index @ {0:x}: Name {1} ({2}): {3}\n", - NI.getUnitOffset(), NTE.getIndex(), Str, - Info.message()); - ++NumErrors; - }); + ErrorCategory.Report( + "NameIndex Name is not associated with any entries", [&]() { + error() << formatv("Name Index @ {0:x}: Name {1} ({2}) is " + "not associated with any entries.\n", + NI.getUnitOffset(), NTE.getIndex(), Str); + }); + ++NumErrors; + }, + [&](const ErrorInfoBase &Info) { + ErrorCategory.Report("Uncategorized NameIndex error", [&]() { + error() << formatv("Name Index @ {0:x}: Name {1} ({2}): {3}\n", + NI.getUnitOffset(), NTE.getIndex(), Str, + Info.message()); + }); + ++NumErrors; + }); return NumErrors; } @@ -1619,10 +1774,12 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness( if (none_of(NI.equal_range(Name), [&](const DWARFDebugNames::Entry &E) { return E.getDIEUnitOffset() == DieUnitOffset; })) { - error() << formatv("Name Index @ {0:x}: Entry for DIE @ {1:x} ({2}) with " - "name {3} missing.\n", - NI.getUnitOffset(), Die.getOffset(), Die.getTag(), - Name); + ErrorCategory.Report("Name Index DIE entry missing name", [&]() { + error() << formatv( + "Name Index @ {0:x}: Entry for DIE @ {1:x} ({2}) with " + "name {3} missing.\n", + NI.getUnitOffset(), Die.getOffset(), Die.getTag(), Name); + }); ++NumErrors; } } @@ -1641,7 +1798,9 @@ unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection, // This verifies that we can read individual name indices and their // abbreviation tables. if (Error E = AccelTable.extract()) { - error() << toString(std::move(E)) << '\n'; + std::string Msg = toString(std::move(E)); + ErrorCategory.Report("Accelerator Table Error", + [&]() { error() << Msg << '\n'; }); return 1; } @@ -1741,13 +1900,17 @@ bool DWARFVerifier::verifyDebugStrOffsets( if (!C) break; if (C.tell() + Length > DA.getData().size()) { - error() << formatv( - "{0}: contribution {1:X}: length exceeds available space " - "(contribution " - "offset ({1:X}) + length field space ({2:X}) + length ({3:X}) == " - "{4:X} > section size {5:X})\n", - SectionName, StartOffset, C.tell() - StartOffset, Length, - C.tell() + Length, DA.getData().size()); + ErrorCategory.Report( + "Section contribution length exceeds available space", [&]() { + error() << formatv( + "{0}: contribution {1:X}: length exceeds available space " + "(contribution " + "offset ({1:X}) + length field space ({2:X}) + length " + "({3:X}) == " + "{4:X} > section size {5:X})\n", + SectionName, StartOffset, C.tell() - StartOffset, Length, + C.tell() + Length, DA.getData().size()); + }); Success = false; // Nothing more to do - no other contributions to try. break; @@ -1755,8 +1918,10 @@ bool DWARFVerifier::verifyDebugStrOffsets( NextUnit = C.tell() + Length; uint8_t Version = DA.getU16(C); if (C && Version != 5) { - error() << formatv("{0}: contribution {1:X}: invalid version {2}\n", - SectionName, StartOffset, Version); + ErrorCategory.Report("Invalid Section version", [&]() { + error() << formatv("{0}: contribution {1:X}: invalid version {2}\n", + SectionName, StartOffset, Version); + }); Success = false; // Can't parse the rest of this contribution, since we don't know the // version, but we can pick up with the next contribution. @@ -1768,10 +1933,12 @@ bool DWARFVerifier::verifyDebugStrOffsets( DA.setAddressSize(OffsetByteSize); uint64_t Remainder = (Length - 4) % OffsetByteSize; if (Remainder != 0) { - error() << formatv( - "{0}: contribution {1:X}: invalid length ((length ({2:X}) " - "- header (0x4)) % offset size {3:X} == {4:X} != 0)\n", - SectionName, StartOffset, Length, OffsetByteSize, Remainder); + ErrorCategory.Report("Invalid section contribution length", [&]() { + error() << formatv( + "{0}: contribution {1:X}: invalid length ((length ({2:X}) " + "- header (0x4)) % offset size {3:X} == {4:X} != 0)\n", + SectionName, StartOffset, Length, OffsetByteSize, Remainder); + }); Success = false; } for (uint64_t Index = 0; C && C.tell() + OffsetByteSize <= NextUnit; ++Index) { @@ -1781,29 +1948,65 @@ bool DWARFVerifier::verifyDebugStrOffsets( if (StrOff == 0) continue; if (StrData.size() <= StrOff) { - error() << formatv( - "{0}: contribution {1:X}: index {2:X}: invalid string " - "offset *{3:X} == {4:X}, is beyond the bounds of the string section of length {5:X}\n", - SectionName, StartOffset, Index, OffOff, StrOff, StrData.size()); + ErrorCategory.Report( + "String offset out of bounds of string section", [&]() { + error() << formatv( + "{0}: contribution {1:X}: index {2:X}: invalid string " + "offset *{3:X} == {4:X}, is beyond the bounds of the string " + "section of length {5:X}\n", + SectionName, StartOffset, Index, OffOff, StrOff, + StrData.size()); + }); continue; } if (StrData[StrOff - 1] == '\0') continue; - error() << formatv("{0}: contribution {1:X}: index {2:X}: invalid string " - "offset *{3:X} == {4:X}, is neither zero nor " - "immediately following a null character\n", - SectionName, StartOffset, Index, OffOff, StrOff); + ErrorCategory.Report( + "Section contribution contains invalid string offset", [&]() { + error() << formatv( + "{0}: contribution {1:X}: index {2:X}: invalid string " + "offset *{3:X} == {4:X}, is neither zero nor " + "immediately following a null character\n", + SectionName, StartOffset, Index, OffOff, StrOff); + }); Success = false; } } if (Error E = C.takeError()) { - error() << SectionName << ": " << toString(std::move(E)) << '\n'; - return false; + std::string Msg = toString(std::move(E)); + ErrorCategory.Report("String offset error", [&]() { + error() << SectionName << ": " << Msg << '\n'; + return false; + }); } return Success; } +void OutputCategoryAggregator::Report( + StringRef s, std::function detailCallback) { + Aggregation[std::string(s)]++; + if (CallDetail) + detailCallback(); +} + +void OutputCategoryAggregator::HandleAggregate( + std::function handleCounts) { + for (auto &&[name, count] : Aggregation) { + handleCounts(name, count); + } +} + +void DWARFVerifier::finish(bool Success) { + if (!Success) { + error() << "Aggregated error category counts:\n"; + ErrorCategory.HandleAggregate([&](StringRef s, unsigned count) { + error() << "Error category '" << s << "' occurred " << count + << " time(s).\n"; + }); + } +} + raw_ostream &DWARFVerifier::error() const { return WithColor::error(OS); } raw_ostream &DWARFVerifier::warn() const { return WithColor::warning(OS); } diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index 941df4eb18445..4362ef0316905 100644 --- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -276,6 +276,10 @@ static cl::opt cat(DwarfDumpCategory)); static opt Verify("verify", desc("Verify the DWARF debug info."), cat(DwarfDumpCategory)); +static opt OnlyAggregateErrors( + "only-aggregate-errors", + desc("Only display the aggregate errors when verifying."), + cat(DwarfDumpCategory)); static opt Quiet("quiet", desc("Use with -verify to not emit to STDOUT."), cat(DwarfDumpCategory)); static opt DumpUUID("uuid", desc("Show the UUID for each architecture."), @@ -326,7 +330,7 @@ static DIDumpOptions getDumpOpts(DWARFContext &C) { DumpOpts.RecoverableErrorHandler = C.getRecoverableErrorHandler(); // In -verify mode, print DIEs without children in error messages. if (Verify) { - DumpOpts.Verbose = true; + DumpOpts.Verbose = !OnlyAggregateErrors; return DumpOpts.noImplicitRecursion(); } return DumpOpts;