Skip to content

Commit 68f464a

Browse files
committed
[llvm-dwarfdump][Statistics] Unify coverage statistic computation
Summary: The patch removes OffsetToFirstDefinition in the 'scope bytes total' statistic computation. Thus it unifies the way the scope and the coverage buckets are computed. The rationals behind that are the following: 1. OffsetToFirstDefinition was used to calculate the variable's life range. However, there is no simple way to do it accurately, so the scope calculated this way might be misleading. See D69027 for more details on the subject. 2. Both 'scope bytes total' and coverage buckets seem to be intended to represent the same data in different ways. Otherwise, the statistics might be controversial and confusing. Note that the approach gives up a thorough evaluation of debug information completeness (i.e. coverage buckets by themselves doesn't tell how good the debug information is). Only changes in coverage over time make a 'physical' sense. Reviewers: djtodoro, aprantl, vsk, dblaikie, avl Subscribers: llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D70548
1 parent 792fab3 commit 68f464a

File tree

7 files changed

+42
-76
lines changed

7 files changed

+42
-76
lines changed

llvm/test/tools/llvm-dwarfdump/X86/locstats.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
; CHECK: "formal params scope bytes total":20
66
; CHECK: "formal params scope bytes covered":20
77
; CHECK: "formal params entry value scope bytes covered":5
8-
; CHECK: "vars scope bytes total":78
8+
; CHECK: "vars scope bytes total":90
99
; CHECK: "vars scope bytes covered":60
1010
; CHECK: "vars entry value scope bytes covered":0
1111
; CHECK: "total variables procesed by location statistics":6

llvm/test/tools/llvm-dwarfdump/X86/statistics-base-address.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# RUN: llvm-mc -triple x86_64-pc-linux %s -filetype=obj -o %t
66
# RUN: llvm-dwarfdump --statistics %t | FileCheck %s
77

8-
# CHECK: "vars scope bytes total":8
8+
# CHECK: "vars scope bytes total":12
99
# CHECK: "vars scope bytes covered":8
1010

1111
.text

llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Test of the llmv-dwarfdump --statistics with split dwarf (dwo files)
2-
# (version 3).
2+
# (version >= 3).
33
#
44
# Create a directory in which to put all files, so .o file can find .dwo file.
55
RUN: rm -rf %t && mkdir -p %t
@@ -69,7 +69,7 @@ RUN: llvm-dwarfdump --statistics statistics-fib.split-dwarf.o | FileCheck %s
6969
# }
7070
#
7171

72-
CHECK: "version":3
72+
CHECK: "version":4
7373
CHECK: "source functions":3
7474
CHECK: "source functions with location":3
7575
CHECK: "inlined functions":7
@@ -80,7 +80,7 @@ CHECK: "source variables":30
8080
# Ideally the value below would be 33 but currently it's not.
8181
CHECK: "variables with location":22
8282
CHECK: "call site entries":7
83-
CHECK: "scope bytes total":2702
83+
CHECK: "scope bytes total":2817
8484
CHECK: "scope bytes covered":1160
8585
CHECK: "total function size":594
8686
CHECK: "total inlined function size":345

llvm/test/tools/llvm-dwarfdump/X86/statistics-v3.test

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Test of the llmv-dwarfdump --statistics newly added stats (version 3).
1+
# Test of the llmv-dwarfdump --statistics newly added stats (version >= 3).
22
#
33
RUN: llvm-mc -triple x86_64-unknown-linux-gnu %S/Inputs/statistics-fib.s -filetype=obj -o %t-statistics-fib.o
44
RUN: llvm-dwarfdump --statistics %t-statistics-fib.o | FileCheck %s
@@ -64,7 +64,7 @@ RUN: llvm-dwarfdump --statistics %t-statistics-fib.o | FileCheck %s
6464
# }
6565
#
6666

67-
CHECK: "version":3
67+
CHECK: "version":4
6868
CHECK: "source functions":3
6969
CHECK: "source functions with location":3
7070
CHECK: "inlined functions":8
@@ -75,7 +75,7 @@ CHECK: "source variables":33
7575
# Ideally the value below would be 33 but currently it's not.
7676
CHECK: "variables with location":24
7777
CHECK: "call site entries":8
78-
CHECK: "scope bytes total":2958
78+
CHECK: "scope bytes total":3072
7979
CHECK: "scope bytes covered":1188
8080
CHECK: "total function size":636
8181
CHECK: "total inlined function size":388

llvm/test/tools/llvm-dwarfdump/X86/statistics.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
; RUN: llc -O0 %s -o - -filetype=obj \
22
; RUN: | llvm-dwarfdump -statistics - | FileCheck %s
3-
; CHECK: "version":3
3+
; CHECK: "version":4
44

55
; int GlobalConst = 42;
66
; int Global;

llvm/tools/llvm-dwarfdump/Statistics.cpp

Lines changed: 26 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -60,28 +60,26 @@ struct PerFunctionStats {
6060
struct GlobalStats {
6161
/// Total number of PC range bytes covered by DW_AT_locations.
6262
unsigned ScopeBytesCovered = 0;
63-
/// Total number of PC range bytes in each variable's enclosing scope,
64-
/// starting from the first definition of the variable.
65-
unsigned ScopeBytesFromFirstDefinition = 0;
63+
/// Total number of PC range bytes in each variable's enclosing scope.
64+
unsigned ScopeBytes = 0;
6665
/// Total number of PC range bytes covered by DW_AT_locations with
6766
/// the debug entry values (DW_OP_entry_value).
6867
unsigned ScopeEntryValueBytesCovered = 0;
6968
/// Total number of PC range bytes covered by DW_AT_locations of
7069
/// formal parameters.
7170
unsigned ParamScopeBytesCovered = 0;
72-
/// Total number of PC range bytes in each variable's enclosing scope,
73-
/// starting from the first definition of the variable (only for parameters).
74-
unsigned ParamScopeBytesFromFirstDefinition = 0;
71+
/// Total number of PC range bytes in each variable's enclosing scope
72+
/// (only for parameters).
73+
unsigned ParamScopeBytes = 0;
7574
/// Total number of PC range bytes covered by DW_AT_locations with
7675
/// the debug entry values (DW_OP_entry_value) (only for parameters).
7776
unsigned ParamScopeEntryValueBytesCovered = 0;
7877
/// Total number of PC range bytes covered by DW_AT_locations (only for local
7978
/// variables).
8079
unsigned VarScopeBytesCovered = 0;
81-
/// Total number of PC range bytes in each variable's enclosing scope,
82-
/// starting from the first definition of the variable (only for local
83-
/// variables).
84-
unsigned VarScopeBytesFromFirstDefinition = 0;
80+
/// Total number of PC range bytes in each variable's enclosing scope
81+
/// (only for local variables).
82+
unsigned VarScopeBytes = 0;
8583
/// Total number of PC range bytes covered by DW_AT_locations with
8684
/// the debug entry values (DW_OP_entry_value) (only for local variables).
8785
unsigned VarScopeEntryValueBytesCovered = 0;
@@ -133,19 +131,6 @@ struct LocationStats {
133131
unsigned NumVar = 0;
134132
};
135133

136-
/// Extract the low pc from a Die.
137-
static uint64_t getLowPC(DWARFDie Die) {
138-
auto RangesOrError = Die.getAddressRanges();
139-
DWARFAddressRangesVector Ranges;
140-
if (RangesOrError)
141-
Ranges = RangesOrError.get();
142-
else
143-
llvm::consumeError(RangesOrError.takeError());
144-
if (Ranges.size())
145-
return Ranges[0].LowPC;
146-
return dwarf::toAddress(Die.find(dwarf::DW_AT_low_pc), 0);
147-
}
148-
149134
/// Collect debug location statistics for one DIE.
150135
static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope,
151136
std::vector<unsigned> &VarParamLocStats,
@@ -177,8 +162,8 @@ static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope,
177162

178163
/// Collect debug info quality metrics for one DIE.
179164
static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
180-
std::string VarPrefix, uint64_t ScopeLowPC,
181-
uint64_t BytesInScope, uint32_t InlineDepth,
165+
std::string VarPrefix, uint64_t BytesInScope,
166+
uint32_t InlineDepth,
182167
StringMap<PerFunctionStats> &FnStatMap,
183168
GlobalStats &GlobalStats,
184169
LocationStats &LocStats) {
@@ -188,7 +173,6 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
188173
bool IsArtificial = false;
189174
uint64_t BytesCovered = 0;
190175
uint64_t BytesEntryValuesCovered = 0;
191-
uint64_t OffsetToFirstDefinition = 0;
192176
auto &FnStats = FnStatMap[FnPrefix];
193177
bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter;
194178
bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable;
@@ -262,16 +246,6 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
262246
if (IsEntryValue(Entry.Expr))
263247
BytesEntryValuesCovered += BytesEntryCovered;
264248
}
265-
if (!Loc->empty()) {
266-
uint64_t FirstDef = Loc->front().Range->LowPC;
267-
// Ranges sometimes start before the lexical scope.
268-
if (FirstDef >= ScopeLowPC)
269-
OffsetToFirstDefinition = FirstDef - ScopeLowPC;
270-
// Or even after it. Count that as a failure.
271-
if (OffsetToFirstDefinition > BytesInScope)
272-
OffsetToFirstDefinition = 0;
273-
}
274-
assert(BytesInScope);
275249
}
276250
}
277251
}
@@ -304,25 +278,21 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
304278
FnStats.VarsInFunction.insert(VarPrefix + VarName);
305279
if (BytesInScope) {
306280
FnStats.TotalVarWithLoc += (unsigned)HasLoc;
307-
// Adjust for the fact the variables often start their lifetime in the
308-
// middle of the scope.
309-
BytesInScope -= OffsetToFirstDefinition;
310281
// Turns out we have a lot of ranges that extend past the lexical scope.
311282
GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered);
312-
GlobalStats.ScopeBytesFromFirstDefinition += BytesInScope;
283+
GlobalStats.ScopeBytes += BytesInScope;
313284
GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
314285
if (IsParam) {
315286
GlobalStats.ParamScopeBytesCovered +=
316287
std::min(BytesInScope, BytesCovered);
317-
GlobalStats.ParamScopeBytesFromFirstDefinition += BytesInScope;
288+
GlobalStats.ParamScopeBytes += BytesInScope;
318289
GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
319290
} else if (IsLocalVar) {
320291
GlobalStats.VarScopeBytesCovered += std::min(BytesInScope, BytesCovered);
321-
GlobalStats.VarScopeBytesFromFirstDefinition += BytesInScope;
292+
GlobalStats.VarScopeBytes += BytesInScope;
322293
GlobalStats.VarScopeEntryValueBytesCovered += BytesEntryValuesCovered;
323294
}
324-
assert(GlobalStats.ScopeBytesCovered <=
325-
GlobalStats.ScopeBytesFromFirstDefinition);
295+
assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes);
326296
} else if (Die.getTag() == dwarf::DW_TAG_member) {
327297
FnStats.ConstantMembers++;
328298
} else {
@@ -351,8 +321,8 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
351321

352322
/// Recursively collect debug info quality metrics.
353323
static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
354-
std::string VarPrefix, uint64_t ScopeLowPC,
355-
uint64_t BytesInScope, uint32_t InlineDepth,
324+
std::string VarPrefix, uint64_t BytesInScope,
325+
uint32_t InlineDepth,
356326
StringMap<PerFunctionStats> &FnStatMap,
357327
GlobalStats &GlobalStats,
358328
LocationStats &LocStats) {
@@ -387,7 +357,6 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
387357
uint64_t BytesInThisScope = 0;
388358
for (auto Range : Ranges)
389359
BytesInThisScope += Range.HighPC - Range.LowPC;
390-
ScopeLowPC = getLowPC(Die);
391360

392361
// Count the function.
393362
if (!IsBlock) {
@@ -423,8 +392,8 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
423392
}
424393
} else {
425394
// Not a scope, visit the Die itself. It could be a variable.
426-
collectStatsForDie(Die, FnPrefix, VarPrefix, ScopeLowPC, BytesInScope,
427-
InlineDepth, FnStatMap, GlobalStats, LocStats);
395+
collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
396+
FnStatMap, GlobalStats, LocStats);
428397
}
429398

430399
// Set InlineDepth correctly for child recursion
@@ -441,9 +410,8 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
441410
if (Child.getTag() == dwarf::DW_TAG_lexical_block)
442411
ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
443412

444-
collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, ScopeLowPC,
445-
BytesInScope, InlineDepth, FnStatMap, GlobalStats,
446-
LocStats);
413+
collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope,
414+
InlineDepth, FnStatMap, GlobalStats, LocStats);
447415
Child = Child.getSibling();
448416
}
449417
}
@@ -496,13 +464,13 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
496464
StringMap<PerFunctionStats> Statistics;
497465
for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
498466
if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false))
499-
collectStatsRecursive(CUDie, "/", "g", 0, 0, 0, Statistics, GlobalStats,
467+
collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats,
500468
LocStats);
501469

502470
/// The version number should be increased every time the algorithm is changed
503471
/// (including bug fixes). New metrics may be added without increasing the
504472
/// version.
505-
unsigned Version = 3;
473+
unsigned Version = 4;
506474
unsigned VarParamTotal = 0;
507475
unsigned VarParamUnique = 0;
508476
unsigned VarParamWithLoc = 0;
@@ -562,19 +530,17 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
562530
printDatum(OS, "call site entries", GlobalStats.CallSiteEntries);
563531
printDatum(OS, "call site DIEs", GlobalStats.CallSiteDIEs);
564532
printDatum(OS, "call site parameter DIEs", GlobalStats.CallSiteParamDIEs);
565-
printDatum(OS, "scope bytes total",
566-
GlobalStats.ScopeBytesFromFirstDefinition);
533+
printDatum(OS, "scope bytes total", GlobalStats.ScopeBytes);
567534
printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);
568535
printDatum(OS, "entry value scope bytes covered",
569536
GlobalStats.ScopeEntryValueBytesCovered);
570537
printDatum(OS, "formal params scope bytes total",
571-
GlobalStats.ParamScopeBytesFromFirstDefinition);
538+
GlobalStats.ParamScopeBytes);
572539
printDatum(OS, "formal params scope bytes covered",
573540
GlobalStats.ParamScopeBytesCovered);
574541
printDatum(OS, "formal params entry value scope bytes covered",
575542
GlobalStats.ParamScopeEntryValueBytesCovered);
576-
printDatum(OS, "vars scope bytes total",
577-
GlobalStats.VarScopeBytesFromFirstDefinition);
543+
printDatum(OS, "vars scope bytes total", GlobalStats.VarScopeBytes);
578544
printDatum(OS, "vars scope bytes covered", GlobalStats.VarScopeBytesCovered);
579545
printDatum(OS, "vars entry value scope bytes covered",
580546
GlobalStats.VarScopeEntryValueBytesCovered);
@@ -609,7 +575,7 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
609575
<< "%\n";
610576
llvm::dbgs() << "PC Ranges covered: "
611577
<< (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
612-
GlobalStats.ScopeBytesFromFirstDefinition)
578+
GlobalStats.ScopeBytes)
613579
<< "%\n");
614580
return true;
615581
}

llvm/utils/llvm-locstats/llvm-locstats.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ def locstats_output(
2525
variables_total_locstats,
2626
variables_with_loc,
2727
scope_bytes_covered,
28-
scope_bytes_from_first_def,
28+
scope_bytes,
2929
variables_coverage_map
3030
):
3131

3232
pc_ranges_covered = int(ceil(scope_bytes_covered * 100.0)
33-
/ scope_bytes_from_first_def)
33+
/ scope_bytes)
3434
variables_coverage_per_map = {}
3535
for cov_bucket in coverage_buckets():
3636
variables_coverage_per_map[cov_bucket] = \
@@ -99,7 +99,7 @@ def Main():
9999
variables_total_locstats = None
100100
variables_with_loc = None
101101
variables_scope_bytes_covered = None
102-
variables_scope_bytes_from_first_def = None
102+
variables_scope_bytes = None
103103
variables_scope_bytes_entry_values = None
104104
variables_coverage_map = {}
105105
binary = results.file_name
@@ -130,7 +130,7 @@ def Main():
130130
json_parsed['total vars procesed by location statistics']
131131
variables_scope_bytes_covered = \
132132
json_parsed['vars scope bytes covered']
133-
variables_scope_bytes_from_first_def = \
133+
variables_scope_bytes = \
134134
json_parsed['vars scope bytes total']
135135
if not results.ignore_debug_entry_values:
136136
for cov_bucket in coverage_buckets():
@@ -152,7 +152,7 @@ def Main():
152152
json_parsed['total params procesed by location statistics']
153153
variables_scope_bytes_covered = \
154154
json_parsed['formal params scope bytes covered']
155-
variables_scope_bytes_from_first_def = \
155+
variables_scope_bytes = \
156156
json_parsed['formal params scope bytes total']
157157
if not results.ignore_debug_entry_values:
158158
for cov_bucket in coverage_buckets():
@@ -177,7 +177,7 @@ def Main():
177177
json_parsed['total variables procesed by location statistics']
178178
variables_scope_bytes_covered = \
179179
json_parsed['scope bytes covered']
180-
variables_scope_bytes_from_first_def = \
180+
variables_scope_bytes = \
181181
json_parsed['scope bytes total']
182182
if not results.ignore_debug_entry_values:
183183
for cov_bucket in coverage_buckets():
@@ -200,7 +200,7 @@ def Main():
200200
variables_total_locstats,
201201
variables_with_loc,
202202
variables_scope_bytes_covered,
203-
variables_scope_bytes_from_first_def,
203+
variables_scope_bytes,
204204
variables_coverage_map
205205
)
206206

0 commit comments

Comments
 (0)