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;
}

File diff suppressed because it is too large Load Diff

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);