blob: c0a73ea298fef7c2ef97969b2fcb3a2a87473aee [file] [log] [blame]
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +00001#!/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
8from __future__ import print_function
9import argparse
10import os
11import sys
12from json import loads
13from math import ceil
14from subprocess import Popen, PIPE
15
Djordje Todorovica3ebc402020-01-13 12:31:28 +010016# Holds the debug location statistics.
17class 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.
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +000069def coverage_buckets():
70 yield '0%'
Kristina Bessonovad5655c42019-12-05 16:45:57 +030071 yield '(0%,10%)'
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +000072 for start in range(10, 91, 10):
Kristina Bessonova1cc4b602019-12-11 20:52:49 +030073 yield '[{0}%,{1}%)'.format(start, start + 10)
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +000074 yield '100%'
75
Djordje Todorovica3ebc402020-01-13 12:31:28 +010076# Parse the JSON representing the debug statistics, and create a
77# LocationStats object.
78def parse_locstats(opts, binary):
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +000079 # These will be different due to different options enabled.
80 variables_total = None
81 variables_total_locstats = None
82 variables_with_loc = None
83 variables_scope_bytes_covered = None
Kristina Bessonova68f464a2019-11-19 13:28:21 +030084 variables_scope_bytes = None
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +000085 variables_scope_bytes_entry_values = None
86 variables_coverage_map = {}
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +000087
88 # Get the directory of the LLVM tools.
89 llvm_dwarfdump_cmd = os.path.join(os.path.dirname(__file__), \
90 "llvm-dwarfdump")
91 # The statistics llvm-dwarfdump option.
92 llvm_dwarfdump_stats_opt = "--statistics"
93
Djordje Todorovica3ebc402020-01-13 12:31:28 +010094 # Generate the stats with the llvm-dwarfdump.
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +000095 subproc = Popen([llvm_dwarfdump_cmd, llvm_dwarfdump_stats_opt, binary], \
96 stdin=PIPE, stdout=PIPE, stderr=PIPE, \
97 universal_newlines = True)
98 cmd_stdout, cmd_stderr = subproc.communicate()
99
100 # Get the JSON and parse it.
101 json_parsed = None
102
103 try:
104 json_parsed = loads(cmd_stdout)
105 except:
106 print ('error: No valid llvm-dwarfdump statistics found.')
107 sys.exit(1)
108
Djordje Todorovica3ebc402020-01-13 12:31:28 +0100109 if opts.only_variables:
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +0000110 # Read the JSON only for local variables.
111 variables_total_locstats = \
112 json_parsed['total vars procesed by location statistics']
113 variables_scope_bytes_covered = \
114 json_parsed['vars scope bytes covered']
Kristina Bessonova68f464a2019-11-19 13:28:21 +0300115 variables_scope_bytes = \
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +0000116 json_parsed['vars scope bytes total']
Djordje Todorovica3ebc402020-01-13 12:31:28 +0100117 if not opts.ignore_debug_entry_values:
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +0000118 for cov_bucket in coverage_buckets():
119 cov_category = "vars with {} of its scope covered".format(cov_bucket)
120 variables_coverage_map[cov_bucket] = json_parsed[cov_category]
121 else:
122 variables_scope_bytes_entry_values = \
123 json_parsed['vars entry value scope bytes covered']
124 variables_scope_bytes_covered = variables_scope_bytes_covered \
125 - variables_scope_bytes_entry_values
126 for cov_bucket in coverage_buckets():
127 cov_category = \
128 "vars (excluding the debug entry values) " \
129 "with {} of its scope covered".format(cov_bucket)
130 variables_coverage_map[cov_bucket] = json_parsed[cov_category]
Djordje Todorovica3ebc402020-01-13 12:31:28 +0100131 elif opts.only_formal_parameters:
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +0000132 # Read the JSON only for formal parameters.
133 variables_total_locstats = \
134 json_parsed['total params procesed by location statistics']
135 variables_scope_bytes_covered = \
136 json_parsed['formal params scope bytes covered']
Kristina Bessonova68f464a2019-11-19 13:28:21 +0300137 variables_scope_bytes = \
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +0000138 json_parsed['formal params scope bytes total']
Djordje Todorovica3ebc402020-01-13 12:31:28 +0100139 if not opts.ignore_debug_entry_values:
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +0000140 for cov_bucket in coverage_buckets():
141 cov_category = "params with {} of its scope covered".format(cov_bucket)
142 variables_coverage_map[cov_bucket] = json_parsed[cov_category]
143 else:
144 variables_scope_bytes_entry_values = \
145 json_parsed['formal params entry value scope bytes covered']
146 variables_scope_bytes_covered = variables_scope_bytes_covered \
147 - variables_scope_bytes_entry_values
148 for cov_bucket in coverage_buckets():
149 cov_category = \
150 "params (excluding the debug entry values) " \
151 "with {} of its scope covered".format(cov_bucket)
Djordje Todorovic095531e2019-10-15 10:12:14 +0000152 variables_coverage_map[cov_bucket] = json_parsed[cov_category]
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +0000153 else:
154 # Read the JSON for both local variables and formal parameters.
155 variables_total = \
156 json_parsed['source variables']
157 variables_with_loc = json_parsed['variables with location']
158 variables_total_locstats = \
159 json_parsed['total variables procesed by location statistics']
160 variables_scope_bytes_covered = \
161 json_parsed['scope bytes covered']
Kristina Bessonova68f464a2019-11-19 13:28:21 +0300162 variables_scope_bytes = \
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +0000163 json_parsed['scope bytes total']
Djordje Todorovica3ebc402020-01-13 12:31:28 +0100164 if not opts.ignore_debug_entry_values:
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +0000165 for cov_bucket in coverage_buckets():
166 cov_category = "variables with {} of its scope covered". \
167 format(cov_bucket)
168 variables_coverage_map[cov_bucket] = json_parsed[cov_category]
169 else:
170 variables_scope_bytes_entry_values = \
171 json_parsed['entry value scope bytes covered']
172 variables_scope_bytes_covered = variables_scope_bytes_covered \
173 - variables_scope_bytes_entry_values
174 for cov_bucket in coverage_buckets():
175 cov_category = "variables (excluding the debug entry values) " \
176 "with {} of its scope covered". format(cov_bucket)
177 variables_coverage_map[cov_bucket] = json_parsed[cov_category]
178
Djordje Todorovica3ebc402020-01-13 12:31:28 +0100179 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.
184def 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.
199def 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
210def 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
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +0000221 # Pretty print collected info.
Djordje Todorovica3ebc402020-01-13 12:31:28 +0100222 if locstats.pretty_print() == -1:
223 sys.exit(0)
Djordje Todorovic2ef18fb2019-10-02 07:00:01 +0000224
225if __name__ == '__main__':
226 Main()
227 sys.exit(0)