Skip to content

Modify dwarfdump verification to allow sub-category counts #125062

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,29 @@ class DWARFDebugAbbrev;
class DataExtractor;
struct DWARFSection;

struct AggregationData {
unsigned OverallCount;
std::map<std::string, unsigned> DetailedCounts;
AggregationData() = default;
};

class OutputCategoryAggregator {
private:
std::map<std::string, unsigned> Aggregation;
std::map<std::string, AggregationData> 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 Report(StringRef category, std::function<void()> detailCallback);
void Report(StringRef category, StringRef sub_category,
std::function<void()> detailCallback);
void EnumerateResults(std::function<void(StringRef, unsigned)> handleCounts);
void EnumerateDetailedResultsFor(
StringRef category,
std::function<void(StringRef, unsigned)> handleCounts);
};

/// A class that verifies DWARF debug information given a DWARF Context.
Expand Down
46 changes: 37 additions & 9 deletions llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1942,12 +1942,14 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness(
if (none_of(NI.equal_range(Name), [&](const DWARFDebugNames::Entry &E) {
return E.getDIEUnitOffset() == DieUnitOffset;
})) {
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);
});
ErrorCategory.Report(
"Name Index DIE entry missing name",
llvm::dwarf::TagString(Die.getTag()), [&]() {
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;
}
}
Expand Down Expand Up @@ -2169,15 +2171,35 @@ bool DWARFVerifier::verifyDebugStrOffsets(

void OutputCategoryAggregator::Report(
StringRef s, std::function<void(void)> detailCallback) {
Aggregation[std::string(s)]++;
this->Report(s, "", detailCallback);
}

void OutputCategoryAggregator::Report(
StringRef category, StringRef sub_category,
std::function<void(void)> detailCallback) {
std::string category_str = std::string(category);
AggregationData *Agg = &Aggregation[category_str];
Agg->OverallCount++;
if (!sub_category.empty()) {
Agg->DetailedCounts[std::string(sub_category)]++;
}
if (IncludeDetail)
detailCallback();
}

void OutputCategoryAggregator::EnumerateResults(
std::function<void(StringRef, unsigned)> handleCounts) {
for (auto &&[name, count] : Aggregation) {
handleCounts(name, count);
for (auto &&[name, aggData] : Aggregation) {
handleCounts(name, aggData.OverallCount);
}
}
void OutputCategoryAggregator::EnumerateDetailedResultsFor(
StringRef category, std::function<void(StringRef, unsigned)> handleCounts) {
auto Agg = Aggregation.find(std::string(category));
if (Agg != Aggregation.end()) {
for (auto &&[name, count] : Agg->second.DetailedCounts) {
handleCounts(name, count);
}
}
}

Expand All @@ -2204,6 +2226,12 @@ void DWARFVerifier::summarize() {
ErrorCategory.EnumerateResults([&](StringRef Category, unsigned Count) {
llvm::json::Object Val;
Val.try_emplace("count", Count);
llvm::json::Object Details;
ErrorCategory.EnumerateDetailedResultsFor(
Category, [&](StringRef SubCategory, unsigned SubCount) {
Details.try_emplace(SubCategory, SubCount);
});
Val.try_emplace("details", std::move(Details));
Categories.try_emplace(Category, std::move(Val));
ErrorCount += Count;
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# RUN: llvm-mc -triple x86_64-pc-linux %s -filetype=obj -o - | not llvm-dwarfdump -verify --verify-json=%t.json -
# RUN: FileCheck %s --input-file %t.json

# CHECK: {"error-categories":{"Name Index DIE entry missing name":{"count":10,"details":{"DW_TAG_inlined_subroutine":1,"DW_TAG_label":1,"DW_TAG_namespace":2,"DW_TAG_subprogram":2,"DW_TAG_variable":4}}},"error-count":10}
# CHECK-NOT: error: Name Index @ 0x0: Entry for DIE @ {{.*}} (DW_TAG_variable) with name var_block_addr missing.

.section .debug_loc,"",@progbits
.Ldebug_loc0:
.quad 0
.quad 1
.short .Lloc0_end-.Lloc0_start # Loc expr size
.Lloc0_start:
.byte 3 # DW_OP_addr
.quad 0x47
.Lloc0_end:
.quad 0
.quad 0

.section .debug_abbrev,"",@progbits
.byte 1 # Abbreviation Code
.byte 17 # DW_TAG_compile_unit
.byte 1 # DW_CHILDREN_yes
.byte 37 # DW_AT_producer
.byte 8 # DW_FORM_string
.byte 17 # DW_AT_low_pc
.byte 1 # DW_FORM_addr
.byte 18 # DW_AT_high_pc
.byte 6 # DW_FORM_data4
.byte 0 # EOM(1)
.byte 0 # EOM(2)

.byte 2 # Abbreviation Code
.byte 52 # DW_TAG_variable
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.byte 8 # DW_FORM_string
.byte 2 # DW_AT_location
.byte 24 # DW_FORM_exprloc
.byte 0 # EOM(1)
.byte 0 # EOM(2)

.byte 3 # Abbreviation Code
.byte 46 # DW_TAG_subprogram
.byte 1 # DW_CHILDREN_yes
.byte 3 # DW_AT_name
.byte 8 # DW_FORM_string
.byte 110 # DW_AT_linkage_name
.byte 8 # DW_FORM_string
.byte 82 # DW_AT_entry_pc
.byte 1 # DW_FORM_addr
.byte 0 # EOM(1)
.byte 0 # EOM(2)

.byte 4 # Abbreviation Code
.byte 57 # DW_TAG_namespace
.byte 1 # DW_CHILDREN_yes
.byte 3 # DW_AT_name
.byte 8 # DW_FORM_string
.byte 0 # EOM(1)
.byte 0 # EOM(2)

.byte 5 # Abbreviation Code
.byte 52 # DW_TAG_variable
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.byte 8 # DW_FORM_string
.byte 2 # DW_AT_location
.byte 23 # DW_FORM_sec_offset
.byte 0 # EOM(1)
.byte 0 # EOM(2)

.byte 6 # Abbreviation Code
.byte 57 # DW_TAG_namespace
.byte 1 # DW_CHILDREN_yes
.byte 0 # EOM(1)
.byte 0 # EOM(2)

.byte 7 # Abbreviation Code
.byte 29 # DW_TAG_inlined_subroutine
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.byte 8 # DW_FORM_string
.byte 17 # DW_AT_low_pc
.byte 1 # DW_FORM_addr
.byte 18 # DW_AT_high_pc
.byte 1 # DW_FORM_addr
.byte 0 # EOM(1)
.byte 0 # EOM(2)

.byte 8 # Abbreviation Code
.byte 10 # DW_TAG_label
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.byte 8 # DW_FORM_string
.byte 82 # DW_AT_entry_pc
.byte 1 # DW_FORM_addr
.byte 0 # EOM(1)
.byte 0 # EOM(2)

.byte 0 # EOM(3)
.section .debug_info,"",@progbits

.Lcu_begin0:
.long .Lcu_end0-.Lcu_start0 # Length of Unit
.Lcu_start0:
.short 4 # DWARF version number
.long .debug_abbrev # Offset Into Abbrev. Section
.byte 8 # Address Size (in bytes)
.byte 1 # Abbrev [1] DW_TAG_compile_unit
.asciz "hand-written DWARF" # DW_AT_producer
.quad 0x0 # DW_AT_low_pc
.long 0x100 # DW_AT_high_pc

.byte 4 # Abbrev [4] DW_TAG_namespace
.asciz "namesp" # DW_AT_name
.byte 2 # Abbrev [2] DW_TAG_variable
.asciz "var_block_addr" # DW_AT_name
.byte 9 # DW_AT_location
.byte 3 # DW_OP_addr
.quad 0x47
.byte 0 # End Of Children Mark

.byte 6 # Abbrev [6] DW_TAG_namespace
.byte 5 # Abbrev [5] DW_TAG_variable
.asciz "var_loc_addr" # DW_AT_name
.long .Ldebug_loc0 # DW_AT_location
.byte 0 # End Of Children Mark

.byte 2 # Abbrev [2] DW_TAG_variable
.asciz "var_loc_tls" # DW_AT_name
.byte 1 # DW_AT_location
.byte 0x9b # DW_OP_form_tls_address

.byte 2 # Abbrev [2] DW_TAG_variable
.asciz "var_loc_gnu_tls" # DW_AT_name
.byte 1 # DW_AT_location
.byte 0xe0 # DW_OP_GNU_push_tls_address

.byte 3 # Abbrev [3] DW_TAG_subprogram
.asciz "fun_name" # DW_AT_name
.asciz "_Z8fun_name" # DW_AT_linkage_name
.quad 0x47 # DW_AT_entry_pc
.byte 7 # Abbrev [7] DW_TAG_inlined_subroutine
.asciz "fun_inline" # DW_AT_name
.quad 0x48 # DW_AT_low_pc
.quad 0x49 # DW_AT_high_pc
.byte 8 # Abbrev [8] DW_TAG_label
.asciz "label" # DW_AT_name
.quad 0x4a # DW_AT_entry_pc
.byte 0 # End Of Children Mark

.byte 0 # End Of Children Mark
.Lcu_end0:

.section .debug_names,"",@progbits
.long .Lnames_end0-.Lnames_start0 # Header: contribution length
.Lnames_start0:
.short 5 # Header: version
.short 0 # Header: padding
.long 1 # Header: compilation unit count
.long 0 # Header: local type unit count
.long 0 # Header: foreign type unit count
.long 0 # Header: bucket count
.long 0 # Header: name count
.long .Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
.long 0 # Header: augmentation length
.long .Lcu_begin0 # Compilation unit 0
.Lnames_abbrev_start0:
.byte 0 # End of abbrev list
.Lnames_abbrev_end0:
.Lnames_entries0:
.Lnames_end0:
32 changes: 32 additions & 0 deletions llvm/test/tools/llvm-dwarfdump/X86/dwarf-verify-good-json-output.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# RUN: llvm-mc %s -filetype obj -triple x86_64-apple-darwin -o - | llvm-dwarfdump -verify --verify-json=%t.json -
# RUN: FileCheck %s --input-file %t.json

# CHECK: {"error-categories":{},"error-count":0}

# This test is meant to verify that the -verify option
# in llvm-dwarfdump doesn't produce any .apple_names related
# output when there's no such section in the object.
# The test was manually modified to exclude the
# .apple_names section from the apple_names_verify_num_atoms.s
# test file in the same directory.

.section __TEXT,__text,regular,pure_instructions
.file 1 "basic.c"
.comm _i,4,2 ## @i
.comm _j,4,2 ## @j
.section __DWARF,__debug_str,regular,debug
Linfo_string:
.asciz "Apple LLVM version 8.1.0 (clang-802.0.35)" ## string offset=0
.asciz "basic.c" ## string offset=42
.asciz "/Users/sgravani/Development/tests" ## string offset=50
.asciz "i" ## string offset=84
.asciz "int" ## string offset=86
.asciz "j" ## string offset=90

.section __DWARF,__debug_info,regular,debug
Lsection_info:

.subsections_via_symbols
.section __DWARF,__debug_line,regular,debug
Lsection_line:
Lline_table_start0: