Skip to content

Commit a3ebc40

Browse files
committed
[llvm-locstats][NFC] Support OOP concept
Making these changes, the code becomes more robust and easier for adding the new features. -Introduce the LocationStats class representing the statistics -Add the pretty_print() method in the LocationStats class -Add additional '-' for the program options -Add the verify_program_inputs() function -Add the parse_locstats() function -Rename 'results' => 'opts' -Add more comments Differential Revision: https://reviews.llvm.org/D71868
1 parent 109e4d1 commit a3ebc40

File tree

2 files changed

+113
-99
lines changed

2 files changed

+113
-99
lines changed

llvm/docs/CommandGuide/llvm-locstats.rst

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,18 @@ covered.
2525
OPTIONS
2626
-------
2727

28-
.. option:: -only-variables
28+
.. option:: --only-variables
2929

30-
Calculate the location statistics only for local variables.
30+
calculate the location statistics only for local variables
3131

32-
.. option:: -only-formal-parameters
32+
.. option:: --only-formal-parameters
3333

34-
Calculate the location statistics only for formal parameters.
34+
calculate the location statistics only for formal parameters
3535

36-
.. option:: -ignore-debug-entry-values
36+
.. option:: --ignore-debug-entry-values
3737

38-
Ignore the location statistics on locations containing the
39-
debug entry values DWARF operation.
38+
ignore the location statistics on locations containing the
39+
debug entry values DWARF operation
4040

4141
EXIT STATUS
4242
-----------

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

Lines changed: 106 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -13,91 +13,69 @@
1313
from math import ceil
1414
from subprocess import Popen, PIPE
1515

16+
# Holds the debug location statistics.
17+
class LocationStats:
18+
def __init__(self, file_name, variables_total, variables_total_locstats,
19+
variables_with_loc, variables_scope_bytes_covered, variables_scope_bytes,
20+
variables_coverage_map):
21+
self.file_name = file_name
22+
self.variables_total = variables_total
23+
self.variables_total_locstats = variables_total_locstats
24+
self.variables_with_loc = variables_with_loc
25+
self.scope_bytes_covered = variables_scope_bytes_covered
26+
self.scope_bytes = variables_scope_bytes
27+
self.variables_coverage_map = variables_coverage_map
28+
29+
# Pretty print the debug location buckets.
30+
def pretty_print(self):
31+
if self.scope_bytes == 0:
32+
print ('No scope bytes found.')
33+
return -1
34+
35+
pc_ranges_covered = int(ceil(self.scope_bytes_covered * 100.0) \
36+
/ self.scope_bytes)
37+
variables_coverage_per_map = {}
38+
for cov_bucket in coverage_buckets():
39+
variables_coverage_per_map[cov_bucket] = \
40+
int(ceil(self.variables_coverage_map[cov_bucket] * 100.0) \
41+
/ self.variables_total_locstats)
42+
43+
print (' =================================================')
44+
print (' Debug Location Statistics ')
45+
print (' =================================================')
46+
print (' cov% samples percentage(~) ')
47+
print (' -------------------------------------------------')
48+
for cov_bucket in coverage_buckets():
49+
print (' {0:10} {1:8d} {2:3d}%'. \
50+
format(cov_bucket, self.variables_coverage_map[cov_bucket], \
51+
variables_coverage_per_map[cov_bucket]))
52+
print (' =================================================')
53+
print (' -the number of debug variables processed: ' \
54+
+ str(self.variables_total_locstats))
55+
print (' -PC ranges covered: ' + str(pc_ranges_covered) + '%')
56+
57+
# Only if we are processing all the variables output the total
58+
# availability.
59+
if self.variables_total and self.variables_with_loc:
60+
total_availability = int(ceil(self.variables_with_loc * 100.0) \
61+
/ self.variables_total)
62+
print (' -------------------------------------------------')
63+
print (' -total availability: ' + str(total_availability) + '%')
64+
print (' =================================================')
65+
66+
return 0
67+
68+
# Define the location buckets.
1669
def coverage_buckets():
1770
yield '0%'
1871
yield '(0%,10%)'
1972
for start in range(10, 91, 10):
2073
yield '[{0}%,{1}%)'.format(start, start + 10)
2174
yield '100%'
2275

23-
def locstats_output(
24-
variables_total,
25-
variables_total_locstats,
26-
variables_with_loc,
27-
scope_bytes_covered,
28-
scope_bytes,
29-
variables_coverage_map
30-
):
31-
32-
if scope_bytes == 0:
33-
print ('No scope bytes found.')
34-
sys.exit(0)
35-
36-
pc_ranges_covered = int(ceil(scope_bytes_covered * 100.0)
37-
/ scope_bytes)
38-
variables_coverage_per_map = {}
39-
for cov_bucket in coverage_buckets():
40-
variables_coverage_per_map[cov_bucket] = \
41-
int(ceil(variables_coverage_map[cov_bucket] * 100.0) \
42-
/ variables_total_locstats)
43-
44-
print (' =================================================')
45-
print (' Debug Location Statistics ')
46-
print (' =================================================')
47-
print (' cov% samples percentage(~) ')
48-
print (' -------------------------------------------------')
49-
for cov_bucket in coverage_buckets():
50-
print (' {0:10} {1:8d} {2:3d}%'. \
51-
format(cov_bucket, variables_coverage_map[cov_bucket], \
52-
variables_coverage_per_map[cov_bucket]))
53-
print (' =================================================')
54-
print (' -the number of debug variables processed: ' \
55-
+ str(variables_total_locstats))
56-
print (' -PC ranges covered: ' + str(pc_ranges_covered) + '%')
57-
58-
# Only if we are processing all the variables output the total
59-
# availability.
60-
if variables_total and variables_with_loc:
61-
total_availability = int(ceil(variables_with_loc * 100.0) \
62-
/ variables_total)
63-
print (' -------------------------------------------------')
64-
print (' -total availability: ' + str(total_availability) + '%')
65-
print (' =================================================')
66-
67-
def parse_program_args(parser):
68-
parser.add_argument('-only-variables', action='store_true',
69-
default=False,
70-
help='calculate the location statistics only for '
71-
'local variables'
72-
)
73-
parser.add_argument('-only-formal-parameters', action='store_true',
74-
default=False,
75-
help='calculate the location statistics only for '
76-
'formal parameters'
77-
)
78-
parser.add_argument('-ignore-debug-entry-values', action='store_true',
79-
default=False,
80-
help='ignore the location statistics on locations with '
81-
'entry values'
82-
)
83-
parser.add_argument('file_name', type=str, help='file to process')
84-
return parser.parse_args()
85-
86-
87-
def Main():
88-
parser = argparse.ArgumentParser()
89-
results = parse_program_args(parser)
90-
91-
if len(sys.argv) < 2:
92-
print ('error: Too few arguments.')
93-
parser.print_help()
94-
sys.exit(1)
95-
96-
if results.only_variables and results.only_formal_parameters:
97-
print ('error: Please use just one only* option.')
98-
parser.print_help()
99-
sys.exit(1)
100-
76+
# Parse the JSON representing the debug statistics, and create a
77+
# LocationStats object.
78+
def parse_locstats(opts, binary):
10179
# These will be different due to different options enabled.
10280
variables_total = None
10381
variables_total_locstats = None
@@ -106,14 +84,14 @@ def Main():
10684
variables_scope_bytes = None
10785
variables_scope_bytes_entry_values = None
10886
variables_coverage_map = {}
109-
binary = results.file_name
11087

11188
# Get the directory of the LLVM tools.
11289
llvm_dwarfdump_cmd = os.path.join(os.path.dirname(__file__), \
11390
"llvm-dwarfdump")
11491
# The statistics llvm-dwarfdump option.
11592
llvm_dwarfdump_stats_opt = "--statistics"
11693

94+
# Generate the stats with the llvm-dwarfdump.
11795
subproc = Popen([llvm_dwarfdump_cmd, llvm_dwarfdump_stats_opt, binary], \
11896
stdin=PIPE, stdout=PIPE, stderr=PIPE, \
11997
universal_newlines = True)
@@ -128,15 +106,15 @@ def Main():
128106
print ('error: No valid llvm-dwarfdump statistics found.')
129107
sys.exit(1)
130108

131-
if results.only_variables:
109+
if opts.only_variables:
132110
# Read the JSON only for local variables.
133111
variables_total_locstats = \
134112
json_parsed['total vars procesed by location statistics']
135113
variables_scope_bytes_covered = \
136114
json_parsed['vars scope bytes covered']
137115
variables_scope_bytes = \
138116
json_parsed['vars scope bytes total']
139-
if not results.ignore_debug_entry_values:
117+
if not opts.ignore_debug_entry_values:
140118
for cov_bucket in coverage_buckets():
141119
cov_category = "vars with {} of its scope covered".format(cov_bucket)
142120
variables_coverage_map[cov_bucket] = json_parsed[cov_category]
@@ -150,15 +128,15 @@ def Main():
150128
"vars (excluding the debug entry values) " \
151129
"with {} of its scope covered".format(cov_bucket)
152130
variables_coverage_map[cov_bucket] = json_parsed[cov_category]
153-
elif results.only_formal_parameters:
131+
elif opts.only_formal_parameters:
154132
# Read the JSON only for formal parameters.
155133
variables_total_locstats = \
156134
json_parsed['total params procesed by location statistics']
157135
variables_scope_bytes_covered = \
158136
json_parsed['formal params scope bytes covered']
159137
variables_scope_bytes = \
160138
json_parsed['formal params scope bytes total']
161-
if not results.ignore_debug_entry_values:
139+
if not opts.ignore_debug_entry_values:
162140
for cov_bucket in coverage_buckets():
163141
cov_category = "params with {} of its scope covered".format(cov_bucket)
164142
variables_coverage_map[cov_bucket] = json_parsed[cov_category]
@@ -183,7 +161,7 @@ def Main():
183161
json_parsed['scope bytes covered']
184162
variables_scope_bytes = \
185163
json_parsed['scope bytes total']
186-
if not results.ignore_debug_entry_values:
164+
if not opts.ignore_debug_entry_values:
187165
for cov_bucket in coverage_buckets():
188166
cov_category = "variables with {} of its scope covered". \
189167
format(cov_bucket)
@@ -198,15 +176,51 @@ def Main():
198176
"with {} of its scope covered". format(cov_bucket)
199177
variables_coverage_map[cov_bucket] = json_parsed[cov_category]
200178

179+
return LocationStats(binary, variables_total, variables_total_locstats,
180+
variables_with_loc, variables_scope_bytes_covered,
181+
variables_scope_bytes, variables_coverage_map)
182+
183+
# Parse the program arguments.
184+
def parse_program_args(parser):
185+
parser.add_argument('--only-variables', action='store_true', default=False,
186+
help='calculate the location statistics only for local variables')
187+
parser.add_argument('--only-formal-parameters', action='store_true',
188+
default=False,
189+
help='calculate the location statistics only for formal parameters')
190+
parser.add_argument('--ignore-debug-entry-values', action='store_true',
191+
default=False,
192+
help='ignore the location statistics on locations with '
193+
'entry values')
194+
parser.add_argument('file_name', type=str, help='file to process')
195+
196+
return parser.parse_args()
197+
198+
# Verify that the program inputs meet the requirements.
199+
def verify_program_inputs(opts):
200+
if len(sys.argv) < 2:
201+
print ('error: Too few arguments.')
202+
return False
203+
204+
if opts.only_variables and opts.only_formal_parameters:
205+
print ('error: Please use just one --only* option.')
206+
return False
207+
208+
return True
209+
210+
def Main():
211+
parser = argparse.ArgumentParser()
212+
opts = parse_program_args(parser)
213+
214+
if not verify_program_inputs(opts):
215+
parser.print_help()
216+
sys.exit(1)
217+
218+
binary = opts.file_name
219+
locstats = parse_locstats(opts, binary)
220+
201221
# Pretty print collected info.
202-
locstats_output(
203-
variables_total,
204-
variables_total_locstats,
205-
variables_with_loc,
206-
variables_scope_bytes_covered,
207-
variables_scope_bytes,
208-
variables_coverage_map
209-
)
222+
if locstats.pretty_print() == -1:
223+
sys.exit(0)
210224

211225
if __name__ == '__main__':
212226
Main()

0 commit comments

Comments
 (0)