| #!/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) |