Skip to content

Commit 0e490ae

Browse files
committed
Reland "[utils] Implement the llvm-locstats tool"
The tool reports verbose output for the DWARF debug location coverage. The llvm-locstats for each variable or formal parameter DIE computes what percentage from the code section bytes, where it is in scope, it has location description. The line 0 shows the number (and the percentage) of DIEs with no location information, but the line 100 shows the number (and the percentage) of DIEs where there is location information in all code section bytes (where the variable or parameter is in the scope). The line 50..59 shows the number (and the percentage) of DIEs where the location information is in between 50 and 59 percentage of its scope covered. Differential Revision: https://reviews.llvm.org/D66526 llvm-svn: 372554
1 parent c9e9024 commit 0e490ae

File tree

7 files changed

+319
-0
lines changed

7 files changed

+319
-0
lines changed

llvm/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,3 +1097,7 @@ if (LLVM_INCLUDE_BENCHMARKS)
10971097
add_subdirectory(utils/benchmark)
10981098
add_subdirectory(benchmarks)
10991099
endif()
1100+
1101+
if (LLVM_INCLUDE_UTILS)
1102+
add_subdirectory(utils/llvm-locstats)
1103+
endif()

llvm/docs/CommandGuide/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,4 @@ Developer Tools
7474
llvm-build
7575
llvm-exegesis
7676
llvm-pdbutil
77+
llvm-locstats
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
llvm-locstats - calculate statistics on DWARF debug location
2+
============================================================
3+
4+
.. program:: llvm-locstats
5+
6+
SYNOPSIS
7+
--------
8+
9+
:program:`llvm-locstats` [*options*] [*filename*]
10+
11+
DESCRIPTION
12+
-----------
13+
14+
:program:`llvm-locstats` works like a wrapper around :program:`llvm-dwarfdump`.
15+
It parses :program:`llvm-dwarfdump` statistics regarding debug location by
16+
pretty printing it in a more human readable way.
17+
18+
The line 0% shows the number and the percentage of DIEs with no location
19+
information, but the line 100% shows the information for DIEs where there is
20+
location information in all code section bytes (where the variable or parameter
21+
is in the scope). The line 50-59% shows the number and the percentage of DIEs
22+
where the location information is between 50 and 59 percentage of its scope
23+
covered.
24+
25+
OPTIONS
26+
-------
27+
28+
.. option:: -only-variables
29+
30+
Calculate the location statistics only for local variables.
31+
32+
.. option:: -only-formal-parameters
33+
34+
Calculate the location statistics only for formal parameters.
35+
36+
.. option:: -ignore-debug-entry-values
37+
38+
Ignore the location statistics on locations containing the
39+
debug entry values DWARF operation.
40+
41+
EXIT STATUS
42+
-----------
43+
44+
:program:`llvm-locstats` returns 0 if the input file were parsed
45+
successfully. Otherwise, it returns 1.
46+
47+
OUTPUT EXAMPLE
48+
--------------
49+
50+
.. code-block:: none
51+
52+
=================================================
53+
Debug Location Statistics
54+
=================================================
55+
cov% samples percentage(~)
56+
-------------------------------------------------
57+
0% 1 16%
58+
1-9% 0 0%
59+
10-19% 0 0%
60+
20-29% 0 0%
61+
30-39% 0 0%
62+
40-49% 0 0%
63+
50-99% 1 16%
64+
60-69% 0 0%
65+
70-79% 0 0%
66+
80-89% 1 16%
67+
90-99% 0 0%
68+
100% 3 50%
69+
=================================================
70+
-the number of debug variables processed: 6
71+
-PC ranges covered: 81%
72+
-------------------------------------------------
73+
-total availability: 83%
74+
=================================================
75+
76+
SEE ALSO
77+
--------
78+
79+
:manpage:`llvm-dwarfdump(1)`

llvm/test/lit.cfg.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ def get_asan_rtlib():
124124

125125
opt_viewer_cmd = '%s %s/tools/opt-viewer/opt-viewer.py' % (sys.executable, config.llvm_src_root)
126126

127+
config.substitutions.append(
128+
('%llvm-locstats', "'%s' %s" % (config.python_executable,
129+
os.path.join(config.llvm_tools_dir, 'llvm-locstats'))))
127130
tools = [
128131
ToolSubst('%lli', FindTool('lli'), post='.', extra_args=lli_args),
129132
ToolSubst('%llc_dwarf', FindTool('llc'), extra_args=llc_args),

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
; RUN: llc -debug-entry-values %s -o - -filetype=obj \
22
; RUN: | llvm-dwarfdump -statistics - | FileCheck %s
33
;
4+
; RUN: llc -debug-entry-values %s -o %t0.o -filetype=obj \
5+
; RUN: | %llvm-locstats %t0.o | FileCheck %s --check-prefix=LOCSTATS
6+
;
47
; CHECK: "entry value scope bytes covered":5
58
; CHECK: "formal params scope bytes total":20
69
; CHECK: "formal params scope bytes covered":20
@@ -84,6 +87,20 @@
8487
; CHECK: "vars (excluding the debug entry values) with 90-99% of its scope covered":0
8588
; CHECK: "vars (excluding the debug entry values) with 100% of its scope covered":1}
8689
;
90+
; Test the llvm-locstats output.
91+
; LOCSTATS: 0% 1 16%
92+
; LOCSTATS: 1-9% 0 0%
93+
; LOCSTATS: 10-19% 0 0%
94+
; LOCSTATS: 20-29% 0 0%
95+
; LOCSTATS: 30-39% 0 0%
96+
; LOCSTATS: 40-49% 0 0%
97+
; LOCSTATS: 50-59% 1 16%
98+
; LOCSTATS: 60-69% 0 0%
99+
; LOCSTATS: 70-79% 0 0%
100+
; LOCSTATS: 80-89% 1 16%
101+
; LOCSTATS: 90-99% 0 0%
102+
; LOCSTATS: 100% 3 50%
103+
;
87104
; The source code of the test case:
88105
; extern void fn3(int *);
89106
; extern void fn2 (int);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
if (LLVM_BUILD_UTILS AND LLVM_BUILD_TOOLS)
2+
add_custom_target(llvm-locstats ALL
3+
COMMAND ${CMAKE_COMMAND} -E copy ${LLVM_MAIN_SRC_DIR}/utils/llvm-locstats/llvm-locstats.py ${LLVM_TOOLS_BINARY_DIR}/llvm-locstats
4+
COMMENT "Copying llvm-locstats into ${LLVM_TOOLS_BINARY_DIR}"
5+
)
6+
set_target_properties(llvm-locstats PROPERTIES FOLDER "Tools")
7+
endif()
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
#!/usr/bin/env python
2+
#
3+
# This is a tool that works like debug location coverage calculator.
4+
# It parses the llvm-dwarfdump --statistics output by reporting it
5+
# in a more human readable way.
6+
#
7+
8+
from __future__ import print_function
9+
import argparse
10+
import os
11+
import sys
12+
from json import loads
13+
from math import ceil
14+
from subprocess import Popen, PIPE
15+
16+
def coverage_buckets():
17+
yield '0%'
18+
yield '1-9%'
19+
for start in range(10, 91, 10):
20+
yield '{0}-{1}%'.format(start, start + 9)
21+
yield '100%'
22+
23+
def locstats_output(
24+
variables_total,
25+
variables_total_locstats,
26+
variables_with_loc,
27+
scope_bytes_covered,
28+
scope_bytes_from_first_def,
29+
variables_coverage_map
30+
):
31+
32+
pc_ranges_covered = int(ceil(scope_bytes_covered * 100.0)
33+
/ scope_bytes_from_first_def)
34+
variables_coverage_per_map = {}
35+
for cov_bucket in coverage_buckets():
36+
variables_coverage_per_map[cov_bucket] = \
37+
int(ceil(variables_coverage_map[cov_bucket] * 100.0) \
38+
/ variables_total_locstats)
39+
40+
print (' =================================================')
41+
print (' Debug Location Statistics ')
42+
print (' =================================================')
43+
print (' cov% samples percentage(~) ')
44+
print (' -------------------------------------------------')
45+
for cov_bucket in coverage_buckets():
46+
print (' {0:6} {1:8d} {2:3d}%'. \
47+
format(cov_bucket, variables_coverage_map[cov_bucket], \
48+
variables_coverage_per_map[cov_bucket]))
49+
print (' =================================================')
50+
print (' -the number of debug variables processed: ' \
51+
+ str(variables_total_locstats))
52+
print (' -PC ranges covered: ' + str(pc_ranges_covered) + '%')
53+
54+
# Only if we are processing all the variables output the total
55+
# availability.
56+
if variables_total and variables_with_loc:
57+
total_availability = int(ceil(variables_with_loc * 100.0) \
58+
/ variables_total)
59+
print (' -------------------------------------------------')
60+
print (' -total availability: ' + str(total_availability) + '%')
61+
print (' =================================================')
62+
63+
def parse_program_args(parser):
64+
parser.add_argument('-only-variables', action='store_true',
65+
default=False,
66+
help='calculate the location statistics only for '
67+
'local variables'
68+
)
69+
parser.add_argument('-only-formal-parameters', action='store_true',
70+
default=False,
71+
help='calculate the location statistics only for '
72+
'formal parameters'
73+
)
74+
parser.add_argument('-ignore-debug-entry-values', action='store_true',
75+
default=False,
76+
help='ignore the location statistics on locations with '
77+
'entry values'
78+
)
79+
parser.add_argument('file_name', type=str, help='file to process')
80+
return parser.parse_args()
81+
82+
83+
def Main():
84+
parser = argparse.ArgumentParser()
85+
results = parse_program_args(parser)
86+
87+
if len(sys.argv) < 2:
88+
print ('error: Too few arguments.')
89+
parser.print_help()
90+
sys.exit(1)
91+
92+
if results.only_variables and results.only_formal_parameters:
93+
print ('error: Please use just one only* option.')
94+
parser.print_help()
95+
sys.exit(1)
96+
97+
# These will be different due to different options enabled.
98+
variables_total = None
99+
variables_total_locstats = None
100+
variables_with_loc = None
101+
variables_scope_bytes_covered = None
102+
variables_scope_bytes_from_first_def = None
103+
variables_scope_bytes_entry_values = None
104+
variables_coverage_map = {}
105+
binary = results.file_name
106+
107+
# Get the directory of the LLVM tools.
108+
llvm_dwarfdump_cmd = os.path.join(os.path.dirname(__file__), \
109+
"llvm-dwarfdump")
110+
# The statistics llvm-dwarfdump option.
111+
llvm_dwarfdump_stats_opt = "--statistics"
112+
113+
subproc = Popen([llvm_dwarfdump_cmd, llvm_dwarfdump_stats_opt, binary], \
114+
stdin=PIPE, stdout=PIPE, stderr=PIPE, \
115+
universal_newlines = True)
116+
cmd_stdout, cmd_stderr = subproc.communicate()
117+
118+
# Get the JSON and parse it.
119+
json_parsed = None
120+
121+
try:
122+
json_parsed = loads(cmd_stdout)
123+
except:
124+
print ('error: No valid llvm-dwarfdump statistics found.')
125+
sys.exit(1)
126+
127+
if results.only_variables:
128+
# Read the JSON only for local variables.
129+
variables_total_locstats = \
130+
json_parsed['total vars procesed by location statistics']
131+
variables_scope_bytes_covered = \
132+
json_parsed['vars scope bytes covered']
133+
variables_scope_bytes_from_first_def = \
134+
json_parsed['vars scope bytes total']
135+
if not results.ignore_debug_entry_values:
136+
for cov_bucket in coverage_buckets():
137+
cov_category = "vars with {} of its scope covered".format(cov_bucket)
138+
variables_coverage_map[cov_bucket] = json_parsed[cov_category]
139+
else:
140+
variables_scope_bytes_entry_values = \
141+
json_parsed['vars entry value scope bytes covered']
142+
variables_scope_bytes_covered = variables_scope_bytes_covered \
143+
- variables_scope_bytes_entry_values
144+
for cov_bucket in coverage_buckets():
145+
cov_category = \
146+
"vars (excluding the debug entry values) " \
147+
"with {} of its scope covered".format(cov_bucket)
148+
variables_coverage_map[cov_bucket] = json_parsed[cov_category]
149+
elif results.only_formal_parameters:
150+
# Read the JSON only for formal parameters.
151+
variables_total_locstats = \
152+
json_parsed['total params procesed by location statistics']
153+
variables_scope_bytes_covered = \
154+
json_parsed['formal params scope bytes covered']
155+
variables_scope_bytes_from_first_def = \
156+
json_parsed['formal params scope bytes total']
157+
if not results.ignore_debug_entry_values:
158+
for cov_bucket in coverage_buckets():
159+
cov_category = "params with {} of its scope covered".format(cov_bucket)
160+
variables_coverage_map[cov_bucket] = json_parsed[cov_category]
161+
else:
162+
variables_scope_bytes_entry_values = \
163+
json_parsed['formal params entry value scope bytes covered']
164+
variables_scope_bytes_covered = variables_scope_bytes_covered \
165+
- variables_scope_bytes_entry_values
166+
for cov_bucket in coverage_buckets():
167+
cov_category = \
168+
"params (excluding the debug entry values) " \
169+
"with {} of its scope covered".format(cov_bucket)
170+
else:
171+
# Read the JSON for both local variables and formal parameters.
172+
variables_total = \
173+
json_parsed['source variables']
174+
variables_with_loc = json_parsed['variables with location']
175+
variables_total_locstats = \
176+
json_parsed['total variables procesed by location statistics']
177+
variables_scope_bytes_covered = \
178+
json_parsed['scope bytes covered']
179+
variables_scope_bytes_from_first_def = \
180+
json_parsed['scope bytes total']
181+
if not results.ignore_debug_entry_values:
182+
for cov_bucket in coverage_buckets():
183+
cov_category = "variables with {} of its scope covered". \
184+
format(cov_bucket)
185+
variables_coverage_map[cov_bucket] = json_parsed[cov_category]
186+
else:
187+
variables_scope_bytes_entry_values = \
188+
json_parsed['entry value scope bytes covered']
189+
variables_scope_bytes_covered = variables_scope_bytes_covered \
190+
- variables_scope_bytes_entry_values
191+
for cov_bucket in coverage_buckets():
192+
cov_category = "variables (excluding the debug entry values) " \
193+
"with {} of its scope covered". format(cov_bucket)
194+
variables_coverage_map[cov_bucket] = json_parsed[cov_category]
195+
196+
# Pretty print collected info.
197+
locstats_output(
198+
variables_total,
199+
variables_total_locstats,
200+
variables_with_loc,
201+
variables_scope_bytes_covered,
202+
variables_scope_bytes_from_first_def,
203+
variables_coverage_map
204+
)
205+
206+
if __name__ == '__main__':
207+
Main()
208+
sys.exit(0)

0 commit comments

Comments
 (0)