Aggregate errors from llvm-dwarfdump --verify (#79648)

The amount and format of output from `llvm-dwarfdump --verify` makes it
quite difficult to know if a change to a tool that produces or modifies
DWARF is causing new problems, or is fixing existing problems. This diff
adds a categorized summary of issues found by the DWARF verifier, on by
default, at the bottom of the error output.

The change includes a new `--error-display` option with 4 settings:

* `--error-display=quiet`: Only display if errors occurred, but no
details or summary are printed.
* `--error-display=summary`: Only display the aggregated summary of
errors with no error detail.
* `--error-display=details`: Only display the detailed error messages
with no summary (previous behavior)
* `--error-display=full`: Display both the detailed error messages and
the aggregated summary of errors (the default)

I changed a handful of tests that were failing due to new output, adding
the flag to use the old behavior for all but a couple. For those two I
added the new aggregated output to the expected output of the test.

The `OutputCategoryAggregator` is a pretty simple little class that
@clayborg suggested to allow code to only be run to dump detail if it's
enabled, while still collating counts of the category. Knowing that the
lambda passed in is only conditionally executed is pretty important
(handling errors has to be done *outside* the lambda). I'm happy to move
this somewhere else (and change/improve it) to be more broadly useful if
folks would like.

---------

Co-authored-by: Kevin Frei <freik@meta.com>
This commit is contained in:
Kevin Frei 2024-02-01 08:47:11 -08:00 committed by GitHub
parent 09b4649ea5
commit bfdd78233f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 559 additions and 282 deletions

View File

@ -205,6 +205,7 @@ struct DIDumpOptions {
bool DisplayRawContents = false;
bool IsEH = false;
bool DumpNonSkeleton = false;
bool ShowAggregateErrors = false;
std::function<llvm::StringRef(uint64_t DwarfRegNum, bool IsEH)>
GetNameForDWARFReg;

View File

@ -30,6 +30,20 @@ class DWARFDebugAbbrev;
class DataExtractor;
struct DWARFSection;
class OutputCategoryAggregator {
private:
std::map<std::string, unsigned> Aggregation;
bool IncludeDetail;
public:
OutputCategoryAggregator(bool includeDetail = false)
: IncludeDetail(includeDetail) {}
void ShowDetail(bool showDetail) { IncludeDetail = showDetail; }
size_t GetNumCategories() const { return Aggregation.size(); }
void Report(StringRef s, std::function<void()> detailCallback);
void EnumerateResults(std::function<void(StringRef, unsigned)> handleCounts);
};
/// A class that verifies DWARF debug information given a DWARF Context.
class DWARFVerifier {
public:
@ -81,6 +95,7 @@ private:
DWARFContext &DCtx;
DIDumpOptions DumpOpts;
uint32_t NumDebugLineErrors = 0;
OutputCategoryAggregator ErrorCategory;
// Used to relax some checks that do not currently work portably
bool IsObjectFile;
bool IsMachOObject;
@ -348,6 +363,9 @@ public:
bool verifyDebugStrOffsets(
StringRef SectionName, const DWARFSection &Section, StringRef StrData,
void (DWARFObject::*)(function_ref<void(const DWARFSection &)>) const);
/// Emits any aggregate information collected, depending on the dump options
void summarize();
};
static inline bool operator<(const DWARFVerifier::DieRangeInfo &LHS,

View File

@ -1408,6 +1408,7 @@ bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) {
if (DumpOpts.DumpType & DIDT_DebugStrOffsets)
Success &= verifier.handleDebugStrOffsets();
Success &= verifier.handleAccelTables();
verifier.summarize();
return Success;
}

View File

@ -167,20 +167,48 @@ 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);
bool HeaderShown = false;
auto ShowHeaderOnce = [&]() {
if (!HeaderShown) {
error() << format("Units[%d] - start offset: 0x%08" PRIx64 " \n",
UnitIndex, OffsetStart);
HeaderShown = true;
}
};
if (!ValidLength)
ErrorCategory.Report(
"Unit Header Length: Unit too large for .debug_info provided", [&]() {
ShowHeaderOnce();
note() << "The length for this unit is too "
"large for the .debug_info provided.\n";
});
if (!ValidVersion)
ErrorCategory.Report(
"Unit Header Length: 16 bit unit header version is not valid", [&]() {
ShowHeaderOnce();
note() << "The 16 bit unit header version is not valid.\n";
});
if (!ValidType)
ErrorCategory.Report(
"Unit Header Length: Unit type encoding is not valid", [&]() {
ShowHeaderOnce();
note() << "The unit type encoding is not valid.\n";
});
if (!ValidAbbrevOffset)
ErrorCategory.Report(
"Unit Header Length: Offset into the .debug_abbrev section is not "
"valid",
[&]() {
ShowHeaderOnce();
note() << "The offset into the .debug_abbrev section is "
"not valid.\n";
});
if (!ValidAddrSize)
ErrorCategory.Report("Unit Header Length: Address size is unsupported",
[&]() {
ShowHeaderOnce();
note() << "The address size is unsupported.\n";
});
}
*Offset = OffsetStart + Length + (isUnitDWARF64 ? 12 : 4);
return Success;
@ -198,12 +226,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"
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 +272,28 @@ unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit,
DWARFDie Die = Unit.getUnitDIE(/* ExtractUnitDIEOnly = */ false);
if (!Die) {
ErrorCategory.Report("Compilation unit missing DIE", [&]() {
error() << "Compilation unit without DIE.\n";
});
NumUnitErrors++;
return NumUnitErrors;
}
if (!dwarf::isUnitType(Die.getTag())) {
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())) {
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 +301,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()) {
ErrorCategory.Report("Skeleton CU has children", [&]() {
error() << "Skeleton compilation unit has children.\n";
});
NumUnitErrors++;
}
@ -280,15 +320,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) {
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()) {
ErrorCategory.Report(
"Call site entry not nested within valid subprogram", [&]() {
error() << "Call site entry not nested within a valid subprogram:";
Die.dump(OS);
});
return 1;
}
@ -297,9 +343,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:";
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 +363,9 @@ unsigned DWARFVerifier::verifyAbbrevSection(const DWARFDebugAbbrev *Abbrev) {
Expected<const DWARFAbbreviationDeclarationSet *> AbbrDeclsOrErr =
Abbrev->getAbbreviationDeclarationSet(0);
if (!AbbrDeclsOrErr) {
error() << toString(AbbrDeclsOrErr.takeError()) << "\n";
std::string ErrMsg = toString(AbbrDeclsOrErr.takeError());
ErrorCategory.Report("Abbreviation Declaration error",
[&]() { error() << ErrMsg << "\n"; });
return 1;
}
@ -324,9 +376,12 @@ unsigned DWARFVerifier::verifyAbbrevSection(const DWARFDebugAbbrev *Abbrev) {
for (auto Attribute : AbbrDecl.attributes()) {
auto Result = AttributeSet.insert(Attribute.Attr);
if (!Result.second) {
ErrorCategory.Report(
"Abbreviation declartion contains multiple attributes", [&]() {
error() << "Abbreviation declaration contains multiple "
<< AttributeString(Attribute.Attr) << " attributes.\n";
AbbrDecl.dump(OS);
});
++NumErrors;
}
}
@ -440,10 +495,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())) {
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,8 +592,10 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die,
for (const auto &Range : Ranges) {
if (!Range.valid()) {
++NumErrors;
ErrorCategory.Report("Invalid address range", [&]() {
error() << "Invalid address range " << Range << "\n";
DumpDieAfterError = true;
});
continue;
}
@ -545,9 +607,11 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die,
// address: 0 or -1.
if (auto PrevRange = RI.insert(Range)) {
++NumErrors;
ErrorCategory.Report("DIE has overlapping DW_AT_ranges", [&]() {
error() << "DIE has overlapping ranges in DW_AT_ranges attribute: "
<< *PrevRange << " and " << Range << '\n';
DumpDieAfterError = true;
});
}
}
if (DumpDieAfterError)
@ -558,9 +622,11 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die,
const auto IntersectingChild = ParentRI.insert(RI);
if (IntersectingChild != ParentRI.Children.end()) {
++NumErrors;
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 +635,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:";
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 +654,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;
ErrorCategory.Report(category, [&]() {
error() << TitleMsg << '\n';
dump(Die) << '\n';
});
};
const DWARFObject &DObj = DCtx.getDWARFObj();
@ -604,23 +676,27 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
if (U->isDWOUnit() && RangeSection.Data.empty())
break;
if (*SectionOffset >= RangeSection.Data.size())
ReportError(
ReportError("DW_AT_ranges offset out of bounds",
"DW_AT_ranges offset is beyond " +
StringRef(DwarfVersion < 5 ? ".debug_ranges" : ".debug_rnglists") +
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: " +
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 +720,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<ResolverError> 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,7 +745,8 @@ 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 " +
ReportError("Incompatible DW_AT_abstract_origin tag reference",
"DIE with tag " + TagString(DieTag) + " has " +
AttributeString(Attr) +
" that points to DIE with "
"incompatible tag " +
@ -679,7 +757,8 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
case DW_AT_type: {
DWARFDie TypeDie = Die.getAttributeValueAsReferencedDie(DW_AT_type);
if (TypeDie && !isType(TypeDie.getTag())) {
ReportError("DIE has " + AttributeString(Attr) +
ReportError("Incompatible DW_AT_type attribute tag",
"DIE has " + AttributeString(Attr) +
" with incompatible tag " + TagString(TypeDie.getTag()));
}
break;
@ -695,26 +774,32 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
bool IsZeroIndexed = LT->Prologue.getVersion() >= 5;
if (std::optional<uint64_t> LastFileIdx =
LT->getLastValidFileIndex()) {
ReportError("DIE has " + AttributeString(Attr) +
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-") +
" (valid values are [" +
(IsZeroIndexed ? "0-" : "1-") +
llvm::formatv("{0}", *LastFileIdx) + "])");
} else {
ReportError("DIE has " + AttributeString(Attr) +
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) +
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) +
ReportError("Invalid encoding in DW_AT_decl_file",
"DIE has " + AttributeString(Attr) +
" with invalid encoding");
}
break;
@ -722,8 +807,10 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
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 +841,14 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
auto CUOffset = AttrValue.Value.getRawUValue();
if (CUOffset >= CUSize) {
++NumErrors;
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 +865,11 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
if (RefVal) {
if (*RefVal >= DieCU->getInfoSection().Data.size()) {
++NumErrors;
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 +887,11 @@ 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";
std::string ErrMsg = toString(std::move(E));
ErrorCategory.Report("Invalid DW_FORM attribute", [&]() {
error() << ErrMsg << ":\n";
dump(Die) << '\n';
});
}
break;
}
@ -821,11 +915,13 @@ unsigned DWARFVerifier::verifyDebugInfoReferences(
if (GetDIEForOffset(Pair.first))
continue;
++NumErrors;
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 +941,11 @@ void DWARFVerifier::verifyDebugLineStmtOffsets() {
if (LineTableOffset < DCtx.getDWARFObj().getLineSection().Data.size()) {
if (!LineTable) {
++NumDebugLineErrors;
ErrorCategory.Report("Unparsable .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 +958,14 @@ void DWARFVerifier::verifyDebugLineStmtOffsets() {
auto Iter = StmtListToDie.find(LineTableOffset);
if (Iter != StmtListToDie.end()) {
++NumDebugLineErrors;
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 +992,16 @@ void DWARFVerifier::verifyDebugLineRows() {
// Verify directory index.
if (FileName.DirIdx > MaxDirIndex) {
++NumDebugLineErrors;
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";
<< "].dir_idx contains an invalid index: "
<< FileName.DirIdx << "\n";
});
}
// Check file paths for duplicates.
@ -910,7 +1014,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,6 +1032,8 @@ void DWARFVerifier::verifyDebugLineRows() {
// Verify row address.
if (Row.Address.Address < PrevAddress) {
++NumDebugLineErrors;
ErrorCategory.Report(
"decreasing address between debug_line rows", [&]() {
error() << ".debug_line["
<< format("0x%08" PRIx64,
*toSectionOffset(Die.find(DW_AT_stmt_list)))
@ -939,6 +1045,7 @@ void DWARFVerifier::verifyDebugLineRows() {
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,6 +1056,7 @@ void DWARFVerifier::verifyDebugLineRows() {
LineTable->Rows.size() != 1) &&
!LineTable->hasFileAtIndex(Row.File)) {
++NumDebugLineErrors;
ErrorCategory.Report("Invalid file index in debug_line", [&]() {
error() << ".debug_line["
<< format("0x%08" PRIx64,
*toSectionOffset(Die.find(DW_AT_stmt_list)))
@ -959,6 +1067,7 @@ void DWARFVerifier::verifyDebugLineRows() {
DWARFDebugLine::Row::dumpTableHeader(OS, 0);
Row.dump(OS);
OS << '\n';
});
}
if (Row.EndSequence)
PrevAddress = 0;
@ -973,6 +1082,7 @@ DWARFVerifier::DWARFVerifier(raw_ostream &S, DWARFContext &D,
DIDumpOptions DumpOpts)
: OS(S), DCtx(D), DumpOpts(std::move(DumpOpts)), IsObjectFile(false),
IsMachOObject(false) {
ErrorCategory.ShowDetail(DumpOpts.Verbose || !DumpOpts.ShowAggregateErrors);
if (const auto *F = DCtx.getDWARFObj().getFile()) {
IsObjectFile = F->isRelocatableObject();
IsMachOObject = F->isMachO();
@ -999,13 +1109,17 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
// Verify that the fixed part of the header is not too short.
if (!AccelSectionData.isValidOffset(AccelTable.getSizeHdr())) {
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 +1134,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) {
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) {
ErrorCategory.Report("No atoms", [&]() {
error() << "No atoms: failed to read HashData.\n";
});
return 1;
}
if (!AccelTable.validateForms()) {
ErrorCategory.Report("Unsupported form", [&]() {
error() << "Unsupported form: failed to read HashData.\n";
});
return 1;
}
@ -1042,9 +1162,11 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
uint64_t HashDataOffset = AccelSectionData.getU32(&DataOffset);
if (!AccelSectionData.isValidOffsetForDataOfSize(HashDataOffset,
sizeof(uint64_t))) {
ErrorCategory.Report("Invalid HashData offset", [&]() {
error() << format("Hash[%d] has invalid HashData offset: "
"0x%08" PRIx64 ".\n",
HashIdx, HashDataOffset);
});
++NumErrors;
}
@ -1068,21 +1190,25 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
if (!Name)
Name = "<NULL>";
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)) {
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";
<< dwarf::TagString(Die.getTag()) << " of DIE["
<< HashDataIdx << "].\n";
});
++NumErrors;
}
}
@ -1106,8 +1232,10 @@ DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) {
unsigned NumErrors = 0;
for (const DWARFDebugNames::NameIndex &NI : AccelTable) {
if (NI.getCUCount() == 0) {
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 +1244,22 @@ DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) {
auto Iter = CUMap.find(Offset);
if (Iter == CUMap.end()) {
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 "
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 +1300,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()) {
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());
Bucket, NI.getUnitOffset(), Index,
NI.getNameCount());
});
++NumErrors;
continue;
}
@ -1202,9 +1338,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) {
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 +1358,13 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
// bucket as empty.
uint32_t FirstHash = NI.getHashArrayEntry(Idx);
if (FirstHash % NI.getBucketCount() != B.Bucket) {
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 +1378,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} "
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);
NI.getUnitOffset(), Str, Idx, caseFoldingDjbHash(Str), Hash);
});
++NumErrors;
}
@ -1258,19 +1401,23 @@ unsigned DWARFVerifier::verifyNameIndexAttribute(
DWARFDebugNames::AttributeEncoding AttrEnc) {
StringRef FormName = dwarf::FormEncodingString(AttrEnc.Form);
if (FormName.empty()) {
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) {
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 +1427,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 "
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 +1465,12 @@ unsigned DWARFVerifier::verifyNameIndexAttribute(
}
if (!DWARFFormValue(AttrEnc.Form).isFormClass(Iter->Class)) {
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 +1496,13 @@ DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) {
SmallSet<unsigned, 5> Attributes;
for (const auto &AttrEnc : Abbrev.Attributes) {
if (!Attributes.insert(AttrEnc.Index).second) {
error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains "
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 +1510,20 @@ DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) {
}
if (NI.getCUCount() > 1 && !Attributes.count(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)) {
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 +1577,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",
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 +1595,11 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
EntryOr = NI.getEntry(&NextEntryID)) {
uint32_t CUIndex = *EntryOr->getCUIndex();
if (CUIndex > NI.getCUCount()) {
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 +1607,32 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
uint64_t DIEOffset = CUOffset + *EntryOr->getDIEUnitOffset();
DWARFDie DIE = DCtx.getDIEForOffset(DIEOffset);
if (!DIE) {
ErrorCategory.Report("NameIndex references nonexistent 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 "
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 "
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,27 +1643,34 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
DIE.getTag() == DW_TAG_inlined_subroutine;
auto EntryNames = getNames(DIE, IncludeStrippedTemplateNames);
if (!is_contained(EntryNames, Str)) {
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;
}
}
handleAllErrors(EntryOr.takeError(),
handleAllErrors(
EntryOr.takeError(),
[&](const DWARFDebugNames::SentinelError &) {
if (NumEntries > 0)
return;
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) {
error()
<< formatv("Name Index @ {0:x}: Name {1} ({2}): {3}\n",
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 +1798,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 "
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);
NI.getUnitOffset(), Die.getOffset(), Die.getTag(), Name);
});
++NumErrors;
}
}
@ -1641,7 +1822,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 +1924,17 @@ bool DWARFVerifier::verifyDebugStrOffsets(
if (!C)
break;
if (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}) == "
"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 +1942,10 @@ bool DWARFVerifier::verifyDebugStrOffsets(
NextUnit = C.tell() + Length;
uint8_t Version = DA.getU16(C);
if (C && Version != 5) {
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 +1957,12 @@ bool DWARFVerifier::verifyDebugStrOffsets(
DA.setAddressSize(OffsetByteSize);
uint64_t Remainder = (Length - 4) % OffsetByteSize;
if (Remainder != 0) {
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 +1972,64 @@ bool DWARFVerifier::verifyDebugStrOffsets(
if (StrOff == 0)
continue;
if (StrData.size() <= StrOff) {
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());
"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 "
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';
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<void(void)> detailCallback) {
Aggregation[std::string(s)]++;
if (IncludeDetail)
detailCallback();
}
void OutputCategoryAggregator::EnumerateResults(
std::function<void(StringRef, unsigned)> handleCounts) {
for (auto &&[name, count] : Aggregation) {
handleCounts(name, count);
}
}
void DWARFVerifier::summarize() {
if (ErrorCategory.GetNumCategories() && DumpOpts.ShowAggregateErrors) {
error() << "Aggregated error counts:\n";
ErrorCategory.EnumerateResults([&](StringRef s, unsigned count) {
error() << s << " occurred " << count << " time(s).\n";
});
}
}
raw_ostream &DWARFVerifier::error() const { return WithColor::error(OS); }
raw_ostream &DWARFVerifier::warn() const { return WithColor::warning(OS); }

View File

@ -1,5 +1,5 @@
# RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.o
# RUN: not llvm-dwarfdump --verify %t.o | FileCheck %s
# RUN: not llvm-dwarfdump --error-display=details --verify %t.o | FileCheck %s
# CHECK: Verifying .debug_abbrev...
# CHECK-NEXT: Verifying .debug_info Unit Header Chain...
@ -51,5 +51,3 @@
.byte 2 # Abbrev [2]
.byte 0
.Lcu_end1:

View File

@ -1,5 +1,5 @@
RUN: llvm-dwarfdump -v %p/Inputs/dwarfdump-objc.x86_64.o | FileCheck %s
RUN: not llvm-dwarfdump -verify %p/Inputs/dwarfdump-objc.x86_64.o | FileCheck %s --check-prefix=VERIFY
RUN: not llvm-dwarfdump -error-display=details -verify %p/Inputs/dwarfdump-objc.x86_64.o | FileCheck %s --check-prefix=VERIFY
Gather some DIE indexes to verify the accelerator table contents.
CHECK: .debug_info contents

View File

@ -1,4 +1,4 @@
# RUN: yaml2obj %s | not llvm-dwarfdump --verify - | FileCheck %s --implicit-check-not=error:
# RUN: yaml2obj %s | not llvm-dwarfdump --error-display=details --verify - | FileCheck %s --implicit-check-not=error:
# CHECK: error: DIE has DW_AT_decl_file with an invalid file index 2 (valid values are [1-1]){{[[:space:]]}}
# CHECK-NEXT: 0x0000001e: DW_TAG_subprogram

View File

@ -1,4 +1,4 @@
# RUN: yaml2obj %s | not llvm-dwarfdump --verify - | FileCheck %s --implicit-check-not=error:
# RUN: yaml2obj %s | not llvm-dwarfdump --error-display=details --verify - | FileCheck %s --implicit-check-not=error:
# CHECK: error: DIE has DW_AT_decl_file with an invalid file index 2 (the file table in the prologue is empty){{[[:space:]]}}
# CHECK-NEXT: 0x0000001e: DW_TAG_subprogram

View File

@ -50,7 +50,11 @@
# CHECK-NEXT: DW_AT_decl_line [DW_FORM_sdata] (3)
# CHECK-NEXT: DW_AT_call_file [DW_FORM_sdata] (4)
# CHECK-NEXT: DW_AT_call_line [DW_FORM_sdata] (5){{[[:space:]]}}
# CHECK-NEXT: Verifying dwo Units...
# CHECK-NEXT: error: Aggregated error counts:
# CHECK-NEXT: error: Invalid encoding in DW_AT_decl_file occurred 4 time(s).
# CHECK-NEXT: error: Invalid file index in DW_AT_call_line occurred 1 time(s).
# CHECK-NEXT: error: Invalid file index in DW_AT_decl_line occurred 1 time(s).
--- !ELF
FileHeader:
Class: ELFCLASS64

View File

@ -39,7 +39,7 @@
#
# 0x00000066: NULL
# RUN: yaml2obj %s | not llvm-dwarfdump --verify - | FileCheck %s --implicit-check-not=error:
# RUN: yaml2obj %s | not llvm-dwarfdump --error-display=details --verify - | FileCheck %s --implicit-check-not=error:
# CHECK: error: DIE has overlapping ranges in DW_AT_ranges attribute: [0x0000000000000000, 0x0000000000000020) and [0x0000000000000000, 0x0000000000000030)

View File

@ -30,7 +30,7 @@
# 0x00000056: NULL
# RUN: yaml2obj %s | not llvm-dwarfdump --verify - | FileCheck %s --implicit-check-not=error:
# RUN: yaml2obj %s | not llvm-dwarfdump --error-display=details --verify - | FileCheck %s --implicit-check-not=error:
# CHECK: Verifying -: file format Mach-O 64-bit x86-64
# CHECK: Verifying .debug_abbrev...

View File

@ -8,7 +8,12 @@
# CHECK: error: Unsupported DW_AT_location encoding: DW_FORM_data1
# FIXME: This should read "type unit" or just "unit" to be correct for this case/in general
# CHECK: error: DIE has DW_AT_decl_file that references a file with index 1 and the compile unit has no line table
# CHECK: Errors detected
# CHECK: error: Aggregated error counts:
# CHECK: error: Compilation unit root DIE is not a unit DIE occurred 1 time(s).
# CHECK: error: File index in DW_AT_decl_file reference CU with no line table occurred 1 time(s).
# CHECK: error: Invalid DW_AT_location occurred 1 time(s).
# CHECK: error: Mismatched unit type occurred 1 time(s).
# CHECK: Errors detected.
.section .debug_info.dwo,"e",@progbits
.long .Ldebug_info_dwo_end1-.Ldebug_info_dwo_start1 # Length of Unit
.Ldebug_info_dwo_start1:

View File

@ -124,6 +124,14 @@ public:
namespace {
using namespace cl;
enum ErrorDetailLevel {
OnlyDetailsNoSummary,
NoDetailsOnlySummary,
NoDetailsOrSummary,
BothDetailsAndSummary,
Unspecified
};
OptionCategory DwarfDumpCategory("Specific Options");
static list<std::string>
InputFilenames(Positional, desc("<input object files or .dSYM bundles>"),
@ -276,6 +284,17 @@ static cl::opt<bool>
cat(DwarfDumpCategory));
static opt<bool> Verify("verify", desc("Verify the DWARF debug info."),
cat(DwarfDumpCategory));
static opt<ErrorDetailLevel> ErrorDetails(
"error-display", init(Unspecified),
values(clEnumValN(NoDetailsOrSummary, "quiet",
"Only display whether errors occurred."),
clEnumValN(NoDetailsOnlySummary, "summary",
"Display only a summary of the errors found."),
clEnumValN(OnlyDetailsNoSummary, "details",
"Display each error in detail but no summary."),
clEnumValN(BothDetailsAndSummary, "full",
"Display each error as well as a summary. [default]")),
cat(DwarfDumpCategory));
static opt<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."),
cat(DwarfDumpCategory));
static opt<bool> DumpUUID("uuid", desc("Show the UUID for each architecture."),
@ -326,7 +345,10 @@ 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 = ErrorDetails != NoDetailsOnlySummary &&
ErrorDetails != NoDetailsOrSummary;
DumpOpts.ShowAggregateErrors = ErrorDetails != OnlyDetailsNoSummary &&
ErrorDetails != NoDetailsOnlySummary;
return DumpOpts.noImplicitRecursion();
}
return DumpOpts;
@ -812,6 +834,8 @@ int main(int argc, char **argv) {
"-verbose is currently not supported";
return 1;
}
if (!Verify && ErrorDetails != Unspecified)
WithColor::warning() << "-error-detail has no affect without -verify";
std::error_code EC;
ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_TextWithCRLF);