[llvm-dwarfdump] Fix unsigned overflow when calculating stats

This fixes https://bugs.llvm.org/show_bug.cgi?id=51652.

The idea is to bump all the stat fields to 64-bit wide
unsigned integers. I've confirmed this resolves
the use case for chromium.

Differential Revision: https://reviews.llvm.org/D109217
This commit is contained in:
djtodoro 2021-10-15 11:20:36 +02:00
parent a4f42a33be
commit c450e47a8c
8 changed files with 382 additions and 139 deletions

View File

@ -0,0 +1,92 @@
# RUN: yaml2obj %s | llvm-dwarfdump --statistics - | FileCheck %s
## Check that we are covering the situation when
## sum of bytes in scope is a huge (uint64_t) number.
##
## The yaml represents this DWARF:
##
## DW_TAG_compile_unit
## DW_AT_low_pc (0x0000000000000000)
## DW_AT_high_pc (0x000000000000000b)
##
## DW_TAG_subprogram
## DW_AT_low_pc (0x0000000000000000)
## DW_AT_high_pc (0x00000000ffffffff)
## DW_TAG_variable
## DW_AT_location (0x00000023:
## [0x0000000000000003, 0x0000000000000005): DW_OP_reg2 RCX)
## DW_TAG_subprogram
## DW_AT_low_pc (0x0000000000000000)
## DW_AT_high_pc (0x00000000ffffffff)
## DW_TAG_variable
## DW_AT_location (0x00000023:
## [0x0000000000000003, 0x0000000000000005): DW_OP_reg2 RCX)
# CHECK: "version": 9,
# CHECK: "sum_all_variables(#bytes in parent scope)": 8589934590
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_X86_64
Sections:
- Name: .debug_loc
Type: SHT_PROGBITS
AddressAlign: 0x01
Content: '00000000000000000600000000000000010055000000000000000000000000000000000300000000000000050000000000000001005200000000000000000000000000000000'
- Name: .debug_ranges
Type: SHT_PROGBITS
AddressAlign: 0x01
Content: '000000000000000003000000000000000500000000000000080000000000000000000000000000000000000000000000'
DWARF:
debug_abbrev:
- Table:
- Code: 1
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_low_pc
Form: DW_FORM_addr
- Attribute: DW_AT_high_pc
Form: DW_FORM_data4
- Code: 2
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_low_pc
Form: DW_FORM_addr
- Attribute: DW_AT_high_pc
Form: DW_FORM_data4
- Code: 3
Tag: DW_TAG_variable
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_location
Form: DW_FORM_sec_offset
debug_info:
- Version: 4
AbbrOffset: 0x00
Entries:
- AbbrCode: 1 ## DW_TAG_compile_unit
Values:
- Value: 0x00 ## DW_AT_low_pc
- Value: 0x0b ## DW_AT_high_pc
- AbbrCode: 2 ## DW_TAG_subprogram
Values:
- Value: 0x00 ## DW_AT_low_pc
- Value: 0xFFFFFFFF ## DW_AT_high_pc
- AbbrCode: 3 ## DW_TAG_variable
Values:
- Value: 0x23 ## DW_AT_sec_offset
- AbbrCode: 0 ## NULL
- AbbrCode: 2 ## DW_TAG_subprogram
Values:
- Value: 0x00 ## DW_AT_low_pc
- Value: 0xFFFFFFFF ## DW_AT_high_pc
- AbbrCode: 3 ## DW_TAG_variable
Values:
- Value: 0x23 ## DW_AT_sec_offset
- AbbrCode: 0 ## NULL
- AbbrCode: 0 ## NULL

View File

@ -0,0 +1,90 @@
# RUN: yaml2obj %s | llvm-dwarfdump --statistics - 2>&1 | FileCheck %s
## Check that we are covering the situation when a stat field overflows.
##
## The yaml represents this DWARF:
##
## DW_TAG_compile_unit
## DW_AT_low_pc (0x0000000000000000)
## DW_AT_high_pc (0x000000000000000b)
##
## DW_TAG_subprogram
## DW_AT_low_pc (0x0000000000000000)
## DW_AT_high_pc (0xffffffffffffffff)
## DW_TAG_variable
## DW_AT_location (0x00000023:
## [0x0000000000000003, 0x0000000000000005): DW_OP_reg2 RCX)
## DW_TAG_subprogram
## DW_AT_low_pc (0x0000000000000000)
## DW_AT_high_pc (0xffffffffffffffff)
## DW_TAG_variable
## DW_AT_location (0x00000023:
## [0x0000000000000003, 0x0000000000000005): DW_OP_reg2 RCX)
# CHECK: "sum_all_variables(#bytes in parent scope)": "overflowed"
--- !ELF
FileHeader:
Class: ELFCLASS[[BITS=64]]
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_X86_64
Sections:
- Name: .debug_loc
Type: SHT_PROGBITS
AddressAlign: 0x01
Content: '00000000000000000600000000000000010055000000000000000000000000000000000300000000000000050000000000000001005200000000000000000000000000000000'
- Name: .debug_ranges
Type: SHT_PROGBITS
AddressAlign: 0x01
Content: '000000000000000003000000000000000500000000000000080000000000000000000000000000000000000000000000'
DWARF:
debug_abbrev:
- Table:
- Code: 1
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_low_pc
Form: DW_FORM_addr
- Attribute: DW_AT_high_pc
Form: DW_FORM_data8
- Code: 2
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_low_pc
Form: DW_FORM_addr
- Attribute: DW_AT_high_pc
Form: DW_FORM_data8
- Code: 3
Tag: DW_TAG_variable
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_location
Form: DW_FORM_sec_offset
debug_info:
- Version: 4
AbbrOffset: 0x00
Entries:
- AbbrCode: 1 ## DW_TAG_compile_unit
Values:
- Value: 0x00 ## DW_AT_low_pc
- Value: 0x0b ## DW_AT_high_pc
- AbbrCode: 2 ## DW_TAG_subprogram
Values:
- Value: 0x00 ## DW_AT_low_pc
- Value: 0xFFFFFFFFFFFFFFFF ## DW_AT_high_pc
- AbbrCode: 3 ## DW_TAG_variable
Values:
- Value: 0x23 ## DW_AT_sec_offset
- AbbrCode: 0 ## NULL
- AbbrCode: 2 ## DW_TAG_subprogram
Values:
- Value: 0x00 ## DW_AT_low_pc
- Value: 0xFFFFFFFFFFFFFFFF ## DW_AT_high_pc
- AbbrCode: 3 ## DW_TAG_variable
Values:
- Value: 0x23 ## DW_AT_sec_offset
- AbbrCode: 0 ## NULL
- AbbrCode: 0 ## NULL

View File

@ -120,7 +120,7 @@
## DW_TAG_subprogram ## DW_TAG_subprogram
## DW_AT_abstract_origin (0x000000f0) ## DW_AT_abstract_origin (0x000000f0)
# CHECK: "version": 8, # CHECK: "version": 9,
# CHECK: "#variables processed by location statistics": 17, # CHECK: "#variables processed by location statistics": 17,
# CHECK: "#variables with 0% of parent scope covered by DW_AT_location": 13, # CHECK: "#variables with 0% of parent scope covered by DW_AT_location": 13,
# CHECK: "#variables with 100% of parent scope covered by DW_AT_location": 4, # CHECK: "#variables with 100% of parent scope covered by DW_AT_location": 4,

View File

@ -69,7 +69,7 @@ RUN: llvm-dwarfdump --statistics statistics-fib.split-dwarf.o | FileCheck %s
# } # }
# #
CHECK: "version": 8, CHECK: "version": 9,
CHECK: "#functions": 3, CHECK: "#functions": 3,
CHECK: "#functions with location": 3, CHECK: "#functions with location": 3,
CHECK: "#inlined functions": 7, CHECK: "#inlined functions": 7,

View File

@ -64,7 +64,7 @@ RUN: llvm-dwarfdump --statistics %t-statistics-fib.o | FileCheck %s
# } # }
# #
CHECK: "version": 8, CHECK: "version": 9,
CHECK: "#functions": 3, CHECK: "#functions": 3,
CHECK: "#functions with location": 3, CHECK: "#functions with location": 3,
CHECK: "#inlined functions": 8, CHECK: "#inlined functions": 8,

View File

@ -1,6 +1,6 @@
; RUN: llc -O0 %s -o - -filetype=obj \ ; RUN: llc -O0 %s -o - -filetype=obj \
; RUN: | llvm-dwarfdump -statistics - | FileCheck %s ; RUN: | llvm-dwarfdump -statistics - | FileCheck %s
; CHECK: "version": 8, ; CHECK: "version": 9,
; namespace test { ; namespace test {
; extern int a; ; extern int a;

View File

@ -33,7 +33,7 @@
## DW_AT_location (0x00000023: ## DW_AT_location (0x00000023:
## [0x0000000000000003, 0x0000000000000005): DW_OP_reg2 RCX) ## [0x0000000000000003, 0x0000000000000005): DW_OP_reg2 RCX)
# CHECK: "version": 8, # CHECK: "version": 9,
# CHECK: "sum_all_variables(#bytes in parent scope)": 12, # CHECK: "sum_all_variables(#bytes in parent scope)": 12,
# CHECK: "sum_all_variables(#bytes in any scope covered by DW_AT_location)": 8 # CHECK: "sum_all_variables(#bytes in any scope covered by DW_AT_location)": 8
# CHECK: "sum_all_variables(#bytes in parent scope covered by DW_AT_location)": 4 # CHECK: "sum_all_variables(#bytes in parent scope covered by DW_AT_location)": 4

View File

@ -29,6 +29,9 @@ constexpr int NumOfCoverageCategories = 12;
/// This is used for zero location coverage bucket. /// This is used for zero location coverage bucket.
constexpr unsigned ZeroCoverageBucket = 0; constexpr unsigned ZeroCoverageBucket = 0;
/// The UINT64_MAX is used as an indication of the overflow.
constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
/// This represents variables DIE offsets. /// This represents variables DIE offsets.
using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>; using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
/// This maps function DIE offset to its variables. /// This maps function DIE offset to its variables.
@ -36,22 +39,43 @@ using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
/// This represents function DIE offsets containing an abstract_origin. /// This represents function DIE offsets containing an abstract_origin.
using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>; using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;
/// This represents a data type for the stats and it helps us to
/// detect an overflow.
/// NOTE: This can be implemented as a template if there is an another type
/// needing this.
struct SaturatingUINT64 {
/// Number that represents the stats.
uint64_t Value;
SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
void operator++(int) { return *this += 1; }
void operator+=(uint64_t Value_) {
if (Value != OverflowValue) {
if (Value < OverflowValue - Value_)
Value += Value_;
else
Value = OverflowValue;
}
}
};
/// Holds statistics for one function (or other entity that has a PC range and /// Holds statistics for one function (or other entity that has a PC range and
/// contains variables, such as a compile unit). /// contains variables, such as a compile unit).
struct PerFunctionStats { struct PerFunctionStats {
/// Number of inlined instances of this function. /// Number of inlined instances of this function.
unsigned NumFnInlined = 0; uint64_t NumFnInlined = 0;
/// Number of out-of-line instances of this function. /// Number of out-of-line instances of this function.
unsigned NumFnOutOfLine = 0; uint64_t NumFnOutOfLine = 0;
/// Number of inlined instances that have abstract origins. /// Number of inlined instances that have abstract origins.
unsigned NumAbstractOrigins = 0; uint64_t NumAbstractOrigins = 0;
/// Number of variables and parameters with location across all inlined /// Number of variables and parameters with location across all inlined
/// instances. /// instances.
unsigned TotalVarWithLoc = 0; uint64_t TotalVarWithLoc = 0;
/// Number of constants with location across all inlined instances. /// Number of constants with location across all inlined instances.
unsigned ConstantMembers = 0; uint64_t ConstantMembers = 0;
/// Number of arificial variables, parameters or members across all instances. /// Number of arificial variables, parameters or members across all instances.
unsigned NumArtificial = 0; uint64_t NumArtificial = 0;
/// List of all Variables and parameters in this function. /// List of all Variables and parameters in this function.
StringSet<> VarsInFunction; StringSet<> VarsInFunction;
/// Compile units also cover a PC range, but have this flag set to false. /// Compile units also cover a PC range, but have this flag set to false.
@ -59,63 +83,63 @@ struct PerFunctionStats {
/// Function has source location information. /// Function has source location information.
bool HasSourceLocation = false; bool HasSourceLocation = false;
/// Number of function parameters. /// Number of function parameters.
unsigned NumParams = 0; uint64_t NumParams = 0;
/// Number of function parameters with source location. /// Number of function parameters with source location.
unsigned NumParamSourceLocations = 0; uint64_t NumParamSourceLocations = 0;
/// Number of function parameters with type. /// Number of function parameters with type.
unsigned NumParamTypes = 0; uint64_t NumParamTypes = 0;
/// Number of function parameters with a DW_AT_location. /// Number of function parameters with a DW_AT_location.
unsigned NumParamLocations = 0; uint64_t NumParamLocations = 0;
/// Number of local variables. /// Number of local variables.
unsigned NumLocalVars = 0; uint64_t NumLocalVars = 0;
/// Number of local variables with source location. /// Number of local variables with source location.
unsigned NumLocalVarSourceLocations = 0; uint64_t NumLocalVarSourceLocations = 0;
/// Number of local variables with type. /// Number of local variables with type.
unsigned NumLocalVarTypes = 0; uint64_t NumLocalVarTypes = 0;
/// Number of local variables with DW_AT_location. /// Number of local variables with DW_AT_location.
unsigned NumLocalVarLocations = 0; uint64_t NumLocalVarLocations = 0;
}; };
/// Holds accumulated global statistics about DIEs. /// Holds accumulated global statistics about DIEs.
struct GlobalStats { struct GlobalStats {
/// Total number of PC range bytes covered by DW_AT_locations. /// Total number of PC range bytes covered by DW_AT_locations.
unsigned TotalBytesCovered = 0; SaturatingUINT64 TotalBytesCovered = 0;
/// Total number of parent DIE PC range bytes covered by DW_AT_Locations. /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
unsigned ScopeBytesCovered = 0; SaturatingUINT64 ScopeBytesCovered = 0;
/// Total number of PC range bytes in each variable's enclosing scope. /// Total number of PC range bytes in each variable's enclosing scope.
unsigned ScopeBytes = 0; SaturatingUINT64 ScopeBytes = 0;
/// Total number of PC range bytes covered by DW_AT_locations with /// Total number of PC range bytes covered by DW_AT_locations with
/// the debug entry values (DW_OP_entry_value). /// the debug entry values (DW_OP_entry_value).
unsigned ScopeEntryValueBytesCovered = 0; SaturatingUINT64 ScopeEntryValueBytesCovered = 0;
/// Total number of PC range bytes covered by DW_AT_locations of /// Total number of PC range bytes covered by DW_AT_locations of
/// formal parameters. /// formal parameters.
unsigned ParamScopeBytesCovered = 0; SaturatingUINT64 ParamScopeBytesCovered = 0;
/// Total number of PC range bytes in each parameter's enclosing scope. /// Total number of PC range bytes in each parameter's enclosing scope.
unsigned ParamScopeBytes = 0; SaturatingUINT64 ParamScopeBytes = 0;
/// Total number of PC range bytes covered by DW_AT_locations with /// Total number of PC range bytes covered by DW_AT_locations with
/// the debug entry values (DW_OP_entry_value) (only for parameters). /// the debug entry values (DW_OP_entry_value) (only for parameters).
unsigned ParamScopeEntryValueBytesCovered = 0; SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;
/// Total number of PC range bytes covered by DW_AT_locations (only for local /// Total number of PC range bytes covered by DW_AT_locations (only for local
/// variables). /// variables).
unsigned LocalVarScopeBytesCovered = 0; SaturatingUINT64 LocalVarScopeBytesCovered = 0;
/// Total number of PC range bytes in each local variable's enclosing scope. /// Total number of PC range bytes in each local variable's enclosing scope.
unsigned LocalVarScopeBytes = 0; SaturatingUINT64 LocalVarScopeBytes = 0;
/// Total number of PC range bytes covered by DW_AT_locations with /// Total number of PC range bytes covered by DW_AT_locations with
/// the debug entry values (DW_OP_entry_value) (only for local variables). /// the debug entry values (DW_OP_entry_value) (only for local variables).
unsigned LocalVarScopeEntryValueBytesCovered = 0; SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;
/// Total number of call site entries (DW_AT_call_file & DW_AT_call_line). /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
unsigned CallSiteEntries = 0; SaturatingUINT64 CallSiteEntries = 0;
/// Total number of call site DIEs (DW_TAG_call_site). /// Total number of call site DIEs (DW_TAG_call_site).
unsigned CallSiteDIEs = 0; SaturatingUINT64 CallSiteDIEs = 0;
/// Total number of call site parameter DIEs (DW_TAG_call_site_parameter). /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
unsigned CallSiteParamDIEs = 0; SaturatingUINT64 CallSiteParamDIEs = 0;
/// Total byte size of concrete functions. This byte size includes /// Total byte size of concrete functions. This byte size includes
/// inline functions contained in the concrete functions. /// inline functions contained in the concrete functions.
unsigned FunctionSize = 0; SaturatingUINT64 FunctionSize = 0;
/// Total byte size of inlined functions. This is the total number of bytes /// Total byte size of inlined functions. This is the total number of bytes
/// for the top inline functions within concrete functions. This can help /// for the top inline functions within concrete functions. This can help
/// tune the inline settings when compiling to match user expectations. /// tune the inline settings when compiling to match user expectations.
unsigned InlineFunctionSize = 0; SaturatingUINT64 InlineFunctionSize = 0;
}; };
/// Holds accumulated debug location statistics about local variables and /// Holds accumulated debug location statistics about local variables and
@ -126,37 +150,37 @@ struct LocationStats {
/// of variables with the no debug location at all, but the last element /// of variables with the no debug location at all, but the last element
/// in the vector represents the number of fully covered variables within /// in the vector represents the number of fully covered variables within
/// its scope. /// its scope.
std::vector<unsigned> VarParamLocStats{ std::vector<SaturatingUINT64> VarParamLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)}; std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
/// Map non debug entry values coverage. /// Map non debug entry values coverage.
std::vector<unsigned> VarParamNonEntryValLocStats{ std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)}; std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
/// The debug location statistics for formal parameters. /// The debug location statistics for formal parameters.
std::vector<unsigned> ParamLocStats{ std::vector<SaturatingUINT64> ParamLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)}; std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
/// Map non debug entry values coverage for formal parameters. /// Map non debug entry values coverage for formal parameters.
std::vector<unsigned> ParamNonEntryValLocStats{ std::vector<SaturatingUINT64> ParamNonEntryValLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)}; std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
/// The debug location statistics for local variables. /// The debug location statistics for local variables.
std::vector<unsigned> LocalVarLocStats{ std::vector<SaturatingUINT64> LocalVarLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)}; std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
/// Map non debug entry values coverage for local variables. /// Map non debug entry values coverage for local variables.
std::vector<unsigned> LocalVarNonEntryValLocStats{ std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)}; std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
/// Total number of local variables and function parameters processed. /// Total number of local variables and function parameters processed.
unsigned NumVarParam = 0; SaturatingUINT64 NumVarParam = 0;
/// Total number of formal parameters processed. /// Total number of formal parameters processed.
unsigned NumParam = 0; SaturatingUINT64 NumParam = 0;
/// Total number of local variables processed. /// Total number of local variables processed.
unsigned NumVar = 0; SaturatingUINT64 NumVar = 0;
}; };
} // namespace } // namespace
/// Collect debug location statistics for one DIE. /// Collect debug location statistics for one DIE.
static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope, static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
std::vector<unsigned> &VarParamLocStats, std::vector<SaturatingUINT64> &VarParamLocStats,
std::vector<unsigned> &ParamLocStats, std::vector<SaturatingUINT64> &ParamLocStats,
std::vector<unsigned> &LocalVarLocStats, std::vector<SaturatingUINT64> &LocalVarLocStats,
bool IsParam, bool IsLocalVar) { bool IsParam, bool IsLocalVar) {
auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned { auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
// No debug location at all for the variable. // No debug location at all for the variable.
@ -173,11 +197,11 @@ static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
unsigned CoverageBucket = getCoverageBucket(); unsigned CoverageBucket = getCoverageBucket();
VarParamLocStats[CoverageBucket]++; VarParamLocStats[CoverageBucket].Value++;
if (IsParam) if (IsParam)
ParamLocStats[CoverageBucket]++; ParamLocStats[CoverageBucket].Value++;
else if (IsLocalVar) else if (IsLocalVar)
LocalVarLocStats[CoverageBucket]++; LocalVarLocStats[CoverageBucket].Value++;
} }
/// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
@ -350,11 +374,11 @@ static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
// Calculate the debug location statistics. // Calculate the debug location statistics.
if (BytesInScope && !DeferLocStats) { if (BytesInScope && !DeferLocStats) {
LocStats.NumVarParam++; LocStats.NumVarParam.Value++;
if (IsParam) if (IsParam)
LocStats.NumParam++; LocStats.NumParam.Value++;
else if (IsLocalVar) else if (IsLocalVar)
LocStats.NumVar++; LocStats.NumVar.Value++;
collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats, collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam, LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
@ -389,7 +413,7 @@ static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
GlobalStats.LocalVarScopeEntryValueBytesCovered += GlobalStats.LocalVarScopeEntryValueBytesCovered +=
BytesEntryValuesCovered; BytesEntryValuesCovered;
} }
assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes); assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
} }
if (IsConstantMember) { if (IsConstantMember) {
@ -603,45 +627,78 @@ static void collectStatsRecursive(
/// Print human-readable output. /// Print human-readable output.
/// \{ /// \{
static void printDatum(json::OStream &J, const char *Key, json::Value Value) { static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
J.attribute(Key, Value); if (Value == OverflowValue)
J.attribute(Key, "overflowed");
else
J.attribute(Key, Value);
LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
} }
static void printLocationStats(json::OStream &J, const char *Key, static void printLocationStats(json::OStream &J, const char *Key,
std::vector<unsigned> &LocationStats) { std::vector<SaturatingUINT64> &LocationStats) {
J.attribute( if (LocationStats[0].Value == OverflowValue)
(Twine(Key) + " with 0% of parent scope covered by DW_AT_location").str(), J.attribute((Twine(Key) +
LocationStats[0]); " with (0%,10%) of parent scope covered by DW_AT_location")
.str(),
"overflowed");
else
J.attribute(
(Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
.str(),
LocationStats[0].Value);
LLVM_DEBUG( LLVM_DEBUG(
llvm::dbgs() << Key llvm::dbgs() << Key
<< " with 0% of parent scope covered by DW_AT_location: \\" << " with 0% of parent scope covered by DW_AT_location: \\"
<< LocationStats[0] << '\n'); << LocationStats[0].Value << '\n');
J.attribute(
(Twine(Key) + " with (0%,10%) of parent scope covered by DW_AT_location") if (LocationStats[1].Value == OverflowValue)
.str(), J.attribute((Twine(Key) +
LocationStats[1]); " with (0%,10%) of parent scope covered by DW_AT_location")
.str(),
"overflowed");
else
J.attribute((Twine(Key) +
" with (0%,10%) of parent scope covered by DW_AT_location")
.str(),
LocationStats[1].Value);
LLVM_DEBUG(llvm::dbgs() LLVM_DEBUG(llvm::dbgs()
<< Key << Key
<< " with (0%,10%) of parent scope covered by DW_AT_location: " << " with (0%,10%) of parent scope covered by DW_AT_location: "
<< LocationStats[1] << '\n'); << LocationStats[1].Value << '\n');
for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) { for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," + if (LocationStats[i].Value == OverflowValue)
Twine(i * 10) + "%) of parent scope covered by DW_AT_location") J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
.str(), Twine(i * 10) +
LocationStats[i]); "%) of parent scope covered by DW_AT_location")
.str(),
"overflowed");
else
J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
Twine(i * 10) +
"%) of parent scope covered by DW_AT_location")
.str(),
LocationStats[i].Value);
LLVM_DEBUG(llvm::dbgs() LLVM_DEBUG(llvm::dbgs()
<< Key << " with [" << (i - 1) * 10 << "%," << i * 10 << Key << " with [" << (i - 1) * 10 << "%," << i * 10
<< "%) of parent scope covered by DW_AT_location: " << "%) of parent scope covered by DW_AT_location: "
<< LocationStats[i]); << LocationStats[i].Value);
} }
J.attribute( if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
(Twine(Key) + " with 100% of parent scope covered by DW_AT_location") J.attribute(
.str(), (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
LocationStats[NumOfCoverageCategories - 1]); .str(),
"overflowed");
else
J.attribute(
(Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
.str(),
LocationStats[NumOfCoverageCategories - 1].Value);
LLVM_DEBUG( LLVM_DEBUG(
llvm::dbgs() << Key llvm::dbgs() << Key
<< " with 100% of parent scope covered by DW_AT_location: " << " with 100% of parent scope covered by DW_AT_location: "
<< LocationStats[NumOfCoverageCategories - 1]); << LocationStats[NumOfCoverageCategories - 1].Value);
} }
static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) { static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
@ -750,31 +807,31 @@ bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
/// The version number should be increased every time the algorithm is changed /// The version number should be increased every time the algorithm is changed
/// (including bug fixes). New metrics may be added without increasing the /// (including bug fixes). New metrics may be added without increasing the
/// version. /// version.
unsigned Version = 8; unsigned Version = 9;
unsigned VarParamTotal = 0; SaturatingUINT64 VarParamTotal = 0;
unsigned VarParamUnique = 0; SaturatingUINT64 VarParamUnique = 0;
unsigned VarParamWithLoc = 0; SaturatingUINT64 VarParamWithLoc = 0;
unsigned NumFunctions = 0; SaturatingUINT64 NumFunctions = 0;
unsigned NumInlinedFunctions = 0; SaturatingUINT64 NumInlinedFunctions = 0;
unsigned NumFuncsWithSrcLoc = 0; SaturatingUINT64 NumFuncsWithSrcLoc = 0;
unsigned NumAbstractOrigins = 0; SaturatingUINT64 NumAbstractOrigins = 0;
unsigned ParamTotal = 0; SaturatingUINT64 ParamTotal = 0;
unsigned ParamWithType = 0; SaturatingUINT64 ParamWithType = 0;
unsigned ParamWithLoc = 0; SaturatingUINT64 ParamWithLoc = 0;
unsigned ParamWithSrcLoc = 0; SaturatingUINT64 ParamWithSrcLoc = 0;
unsigned LocalVarTotal = 0; SaturatingUINT64 LocalVarTotal = 0;
unsigned LocalVarWithType = 0; SaturatingUINT64 LocalVarWithType = 0;
unsigned LocalVarWithSrcLoc = 0; SaturatingUINT64 LocalVarWithSrcLoc = 0;
unsigned LocalVarWithLoc = 0; SaturatingUINT64 LocalVarWithLoc = 0;
for (auto &Entry : Statistics) { for (auto &Entry : Statistics) {
PerFunctionStats &Stats = Entry.getValue(); PerFunctionStats &Stats = Entry.getValue();
unsigned TotalVars = Stats.VarsInFunction.size() * uint64_t TotalVars = Stats.VarsInFunction.size() *
(Stats.NumFnInlined + Stats.NumFnOutOfLine); (Stats.NumFnInlined + Stats.NumFnOutOfLine);
// Count variables in global scope. // Count variables in global scope.
if (!Stats.IsFunction) if (!Stats.IsFunction)
TotalVars = TotalVars =
Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial; Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
unsigned Constants = Stats.ConstantMembers; uint64_t Constants = Stats.ConstantMembers;
VarParamWithLoc += Stats.TotalVarWithLoc + Constants; VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
VarParamTotal += TotalVars; VarParamTotal += TotalVars;
VarParamUnique += Stats.VarsInFunction.size(); VarParamUnique += Stats.VarsInFunction.size();
@ -806,70 +863,72 @@ bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
printDatum(J, "file", Filename.str()); printDatum(J, "file", Filename.str());
printDatum(J, "format", FormatName); printDatum(J, "format", FormatName);
printDatum(J, "#functions", NumFunctions); printDatum(J, "#functions", NumFunctions.Value);
printDatum(J, "#functions with location", NumFuncsWithSrcLoc); printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);
printDatum(J, "#inlined functions", NumInlinedFunctions); printDatum(J, "#inlined functions", NumInlinedFunctions.Value);
printDatum(J, "#inlined functions with abstract origins", NumAbstractOrigins); printDatum(J, "#inlined functions with abstract origins",
NumAbstractOrigins.Value);
// This includes local variables and formal parameters. // This includes local variables and formal parameters.
printDatum(J, "#unique source variables", VarParamUnique); printDatum(J, "#unique source variables", VarParamUnique.Value);
printDatum(J, "#source variables", VarParamTotal); printDatum(J, "#source variables", VarParamTotal.Value);
printDatum(J, "#source variables with location", VarParamWithLoc); printDatum(J, "#source variables with location", VarParamWithLoc.Value);
printDatum(J, "#call site entries", GlobalStats.CallSiteEntries); printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);
printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs); printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);
printDatum(J, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs); printDatum(J, "#call site parameter DIEs",
GlobalStats.CallSiteParamDIEs.Value);
printDatum(J, "sum_all_variables(#bytes in parent scope)", printDatum(J, "sum_all_variables(#bytes in parent scope)",
GlobalStats.ScopeBytes); GlobalStats.ScopeBytes.Value);
printDatum(J, printDatum(J,
"sum_all_variables(#bytes in any scope covered by DW_AT_location)", "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
GlobalStats.TotalBytesCovered); GlobalStats.TotalBytesCovered.Value);
printDatum(J, printDatum(J,
"sum_all_variables(#bytes in parent scope covered by " "sum_all_variables(#bytes in parent scope covered by "
"DW_AT_location)", "DW_AT_location)",
GlobalStats.ScopeBytesCovered); GlobalStats.ScopeBytesCovered.Value);
printDatum(J, printDatum(J,
"sum_all_variables(#bytes in parent scope covered by " "sum_all_variables(#bytes in parent scope covered by "
"DW_OP_entry_value)", "DW_OP_entry_value)",
GlobalStats.ScopeEntryValueBytesCovered); GlobalStats.ScopeEntryValueBytesCovered.Value);
printDatum(J, "sum_all_params(#bytes in parent scope)", printDatum(J, "sum_all_params(#bytes in parent scope)",
GlobalStats.ParamScopeBytes); GlobalStats.ParamScopeBytes.Value);
printDatum(J, printDatum(J,
"sum_all_params(#bytes in parent scope covered by DW_AT_location)", "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
GlobalStats.ParamScopeBytesCovered); GlobalStats.ParamScopeBytesCovered.Value);
printDatum(J, printDatum(J,
"sum_all_params(#bytes in parent scope covered by " "sum_all_params(#bytes in parent scope covered by "
"DW_OP_entry_value)", "DW_OP_entry_value)",
GlobalStats.ParamScopeEntryValueBytesCovered); GlobalStats.ParamScopeEntryValueBytesCovered.Value);
printDatum(J, "sum_all_local_vars(#bytes in parent scope)", printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
GlobalStats.LocalVarScopeBytes); GlobalStats.LocalVarScopeBytes.Value);
printDatum(J, printDatum(J,
"sum_all_local_vars(#bytes in parent scope covered by " "sum_all_local_vars(#bytes in parent scope covered by "
"DW_AT_location)", "DW_AT_location)",
GlobalStats.LocalVarScopeBytesCovered); GlobalStats.LocalVarScopeBytesCovered.Value);
printDatum(J, printDatum(J,
"sum_all_local_vars(#bytes in parent scope covered by " "sum_all_local_vars(#bytes in parent scope covered by "
"DW_OP_entry_value)", "DW_OP_entry_value)",
GlobalStats.LocalVarScopeEntryValueBytesCovered); GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
printDatum(J, "#bytes within functions", GlobalStats.FunctionSize); printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
printDatum(J, "#bytes within inlined functions", printDatum(J, "#bytes within inlined functions",
GlobalStats.InlineFunctionSize); GlobalStats.InlineFunctionSize.Value);
// Print the summary for formal parameters. // Print the summary for formal parameters.
printDatum(J, "#params", ParamTotal); printDatum(J, "#params", ParamTotal.Value);
printDatum(J, "#params with source location", ParamWithSrcLoc); printDatum(J, "#params with source location", ParamWithSrcLoc.Value);
printDatum(J, "#params with type", ParamWithType); printDatum(J, "#params with type", ParamWithType.Value);
printDatum(J, "#params with binary location", ParamWithLoc); printDatum(J, "#params with binary location", ParamWithLoc.Value);
// Print the summary for local variables. // Print the summary for local variables.
printDatum(J, "#local vars", LocalVarTotal); printDatum(J, "#local vars", LocalVarTotal.Value);
printDatum(J, "#local vars with source location", LocalVarWithSrcLoc); printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);
printDatum(J, "#local vars with type", LocalVarWithType); printDatum(J, "#local vars with type", LocalVarWithType.Value);
printDatum(J, "#local vars with binary location", LocalVarWithLoc); printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);
// Print the debug section sizes. // Print the debug section sizes.
printSectionSizes(J, Sizes); printSectionSizes(J, Sizes);
@ -877,32 +936,34 @@ bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
// Print the location statistics for variables (includes local variables // Print the location statistics for variables (includes local variables
// and formal parameters). // and formal parameters).
printDatum(J, "#variables processed by location statistics", printDatum(J, "#variables processed by location statistics",
LocStats.NumVarParam); LocStats.NumVarParam.Value);
printLocationStats(J, "#variables", LocStats.VarParamLocStats); printLocationStats(J, "#variables", LocStats.VarParamLocStats);
printLocationStats(J, "#variables - entry values", printLocationStats(J, "#variables - entry values",
LocStats.VarParamNonEntryValLocStats); LocStats.VarParamNonEntryValLocStats);
// Print the location statistics for formal parameters. // Print the location statistics for formal parameters.
printDatum(J, "#params processed by location statistics", LocStats.NumParam); printDatum(J, "#params processed by location statistics",
LocStats.NumParam.Value);
printLocationStats(J, "#params", LocStats.ParamLocStats); printLocationStats(J, "#params", LocStats.ParamLocStats);
printLocationStats(J, "#params - entry values", printLocationStats(J, "#params - entry values",
LocStats.ParamNonEntryValLocStats); LocStats.ParamNonEntryValLocStats);
// Print the location statistics for local variables. // Print the location statistics for local variables.
printDatum(J, "#local vars processed by location statistics", printDatum(J, "#local vars processed by location statistics",
LocStats.NumVar); LocStats.NumVar.Value);
printLocationStats(J, "#local vars", LocStats.LocalVarLocStats); printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
printLocationStats(J, "#local vars - entry values", printLocationStats(J, "#local vars - entry values",
LocStats.LocalVarNonEntryValLocStats); LocStats.LocalVarNonEntryValLocStats);
J.objectEnd(); J.objectEnd();
OS << '\n'; OS << '\n';
LLVM_DEBUG( LLVM_DEBUG(llvm::dbgs() << "Total Availability: "
llvm::dbgs() << "Total Availability: " << (int)std::round((VarParamWithLoc.Value * 100.0) /
<< (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal) VarParamTotal.Value)
<< "%\n"; << "%\n";
llvm::dbgs() << "PC Ranges covered: " llvm::dbgs() << "PC Ranges covered: "
<< (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) / << (int)std::round(
GlobalStats.ScopeBytes) (GlobalStats.ScopeBytesCovered.Value * 100.0) /
<< "%\n"); GlobalStats.ScopeBytes.Value)
<< "%\n");
return true; return true;
} }