aboutsummaryrefslogtreecommitdiff
path: root/utils/llvm-locstats/llvm-locstats.py
blob: 4df525ed1a96bcc988459ecc55f6096686f03f44 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/env python
#
# This is a tool that works like debug location coverage calculator.
# It parses the llvm-dwarfdump --statistics output by reporting it
# in a more human readable way.
#

from __future__ import print_function
import argparse
import os
import sys
from json import loads
from math import ceil
from subprocess import Popen, PIPE

def coverage_buckets():
  yield '0%'
  yield '1-9%'
  for start in range(10, 91, 10):
    yield '{0}-{1}%'.format(start, start + 9)
  yield '100%'

def locstats_output(
  variables_total,
  variables_total_locstats,
  variables_with_loc,
  scope_bytes_covered,
  scope_bytes_from_first_def,
  variables_coverage_map
  ):

  pc_ranges_covered = int(ceil(scope_bytes_covered * 100.0)
              / scope_bytes_from_first_def)
  variables_coverage_per_map = {}
  for cov_bucket in coverage_buckets():
    variables_coverage_per_map[cov_bucket] = \
      int(ceil(variables_coverage_map[cov_bucket] * 100.0) \
               / variables_total_locstats)

  print (' =================================================')
  print ('            Debug Location Statistics       ')
  print (' =================================================')
  print ('     cov%          samples          percentage(~)  ')
  print (' -------------------------------------------------')
  for cov_bucket in coverage_buckets():
    print ('   {0:6}        {1:8d}             {2:3d}%'. \
      format(cov_bucket, variables_coverage_map[cov_bucket], \
             variables_coverage_per_map[cov_bucket]))
  print (' =================================================')
  print (' -the number of debug variables processed: ' \
    + str(variables_total_locstats))
  print (' -PC ranges covered: ' + str(pc_ranges_covered) + '%')

  # Only if we are processing all the variables output the total
  # availability.
  if variables_total and variables_with_loc:
    total_availability = int(ceil(variables_with_loc * 100.0) \
                                  / variables_total)
    print (' -------------------------------------------------')
    print (' -total availability: ' + str(total_availability) + '%')
  print (' =================================================')

def parse_program_args(parser):
  parser.add_argument('-only-variables', action='store_true',
            default=False,
            help='calculate the location statistics only for '
               'local variables'
            )
  parser.add_argument('-only-formal-parameters', action='store_true',
            default=False,
            help='calculate the location statistics only for '
               'formal parameters'
            )
  parser.add_argument('-ignore-debug-entry-values', action='store_true',
            default=False,
            help='ignore the location statistics on locations with '
               'entry values'
            )
  parser.add_argument('file_name', type=str, help='file to process')
  return parser.parse_args()


def Main():
  parser = argparse.ArgumentParser()
  results = parse_program_args(parser)

  if len(sys.argv) < 2:
    print ('error: Too few arguments.')
    parser.print_help()
    sys.exit(1)

  if results.only_variables and results.only_formal_parameters:
    print ('error: Please use just one only* option.')
    parser.print_help()
    sys.exit(1)

  # These will be different due to different options enabled.
  variables_total = None
  variables_total_locstats = None
  variables_with_loc = None
  variables_scope_bytes_covered = None
  variables_scope_bytes_from_first_def = None
  variables_scope_bytes_entry_values = None
  variables_coverage_map = {}
  binary = results.file_name

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

  subproc = Popen([llvm_dwarfdump_cmd, llvm_dwarfdump_stats_opt, binary], \
                  stdin=PIPE, stdout=PIPE, stderr=PIPE, \
                  universal_newlines = True)
  cmd_stdout, cmd_stderr = subproc.communicate()

  # Get the JSON and parse it.
  json_parsed = None

  try:
    json_parsed = loads(cmd_stdout)
  except:
    print ('error: No valid llvm-dwarfdump statistics found.')
    sys.exit(1)

  if results.only_variables:
    # Read the JSON only for local variables.
    variables_total_locstats = \
      json_parsed['total vars procesed by location statistics']
    variables_scope_bytes_covered = \
      json_parsed['vars scope bytes covered']
    variables_scope_bytes_from_first_def = \
      json_parsed['vars scope bytes total']
    if not results.ignore_debug_entry_values:
      for cov_bucket in coverage_buckets():
        cov_category = "vars with {} of its scope covered".format(cov_bucket)
        variables_coverage_map[cov_bucket] = json_parsed[cov_category]
    else:
      variables_scope_bytes_entry_values = \
        json_parsed['vars entry value scope bytes covered']
      variables_scope_bytes_covered = variables_scope_bytes_covered \
         - variables_scope_bytes_entry_values
      for cov_bucket in coverage_buckets():
        cov_category = \
          "vars (excluding the debug entry values) " \
          "with {} of its scope covered".format(cov_bucket)
        variables_coverage_map[cov_bucket] = json_parsed[cov_category]
  elif results.only_formal_parameters:
    # Read the JSON only for formal parameters.
    variables_total_locstats = \
      json_parsed['total params procesed by location statistics']
    variables_scope_bytes_covered = \
      json_parsed['formal params scope bytes covered']
    variables_scope_bytes_from_first_def = \
      json_parsed['formal params scope bytes total']
    if not results.ignore_debug_entry_values:
      for cov_bucket in coverage_buckets():
        cov_category = "params with {} of its scope covered".format(cov_bucket)
        variables_coverage_map[cov_bucket] = json_parsed[cov_category]
    else:
      variables_scope_bytes_entry_values = \
        json_parsed['formal params entry value scope bytes covered']
      variables_scope_bytes_covered = variables_scope_bytes_covered \
        - variables_scope_bytes_entry_values
      for cov_bucket in coverage_buckets():
        cov_category = \
          "params (excluding the debug entry values) " \
          "with {} of its scope covered".format(cov_bucket)
        variables_coverage_map[cov_bucket] = json_parsed[cov_category]
  else:
    # Read the JSON for both local variables and formal parameters.
    variables_total = \
      json_parsed['source variables']
    variables_with_loc = json_parsed['variables with location']
    variables_total_locstats = \
      json_parsed['total variables procesed by location statistics']
    variables_scope_bytes_covered = \
      json_parsed['scope bytes covered']
    variables_scope_bytes_from_first_def = \
      json_parsed['scope bytes total']
    if not results.ignore_debug_entry_values:
      for cov_bucket in coverage_buckets():
        cov_category = "variables with {} of its scope covered". \
                       format(cov_bucket)
        variables_coverage_map[cov_bucket] = json_parsed[cov_category]
    else:
      variables_scope_bytes_entry_values = \
        json_parsed['entry value scope bytes covered']
      variables_scope_bytes_covered = variables_scope_bytes_covered \
        - variables_scope_bytes_entry_values
      for cov_bucket in coverage_buckets():
        cov_category = "variables (excluding the debug entry values) " \
                       "with {} of its scope covered". format(cov_bucket)
        variables_coverage_map[cov_bucket] = json_parsed[cov_category]

  # Pretty print collected info.
  locstats_output(
    variables_total,
    variables_total_locstats,
    variables_with_loc,
    variables_scope_bytes_covered,
    variables_scope_bytes_from_first_def,
    variables_coverage_map
    )

if __name__ == '__main__':
  Main()
  sys.exit(0)