Index: llvm/CMakeLists.txt =================================================================== --- llvm/CMakeLists.txt +++ llvm/CMakeLists.txt @@ -1104,3 +1104,7 @@ add_subdirectory(utils/benchmark) add_subdirectory(benchmarks) endif() + +if (LLVM_INCLUDE_UTILS) + add_subdirectory(utils/llvm-locstats) +endif() Index: llvm/tools/llvm-dwarfdump/CMakeLists.txt =================================================================== --- llvm/tools/llvm-dwarfdump/CMakeLists.txt +++ llvm/tools/llvm-dwarfdump/CMakeLists.txt @@ -13,3 +13,4 @@ ) add_subdirectory(fuzzer) +add_dependencies(llvm-dwarfdump llvm-locstats) Index: llvm/utils/llvm-locstats/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/utils/llvm-locstats/CMakeLists.txt @@ -0,0 +1,4 @@ +add_custom_target(llvm-locstats + COMMAND ${CMAKE_COMMAND} -E copy ${LLVM_MAIN_SRC_DIR}/utils/llvm-locstats/llvm-locstats.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/llvm-locstats + ) +set_target_properties(llvm-locstats PROPERTIES FOLDER "Utils") Index: llvm/utils/llvm-locstats/llvm-locstats.py =================================================================== --- /dev/null +++ llvm/utils/llvm-locstats/llvm-locstats.py @@ -0,0 +1,221 @@ +#!/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. +# + +import os +import argparse +import sys +import json + +def locstats_output( + variables_cumulative, + variables_with_loc, + scope_bytes_covered, + scope_bytes_from_first_def, + variables_fully_covered, + zero_covered_variables, + variables_with_more_than_half_scope_cov, + ): + + total_availability = int(round(variables_with_loc * 100.0) + / variables_cumulative) + pc_ranges_covered = int(round(scope_bytes_covered * 100.0) + / scope_bytes_from_first_def) + variables_fully_covered_per = int(round(variables_fully_covered + * 100.0) / variables_cumulative) + zero_covered_variables_per = int(round(zero_covered_variables + * 100.0) / variables_cumulative) + variables_with_more_than_half_scope_cov_per = \ + int(round(variables_with_more_than_half_scope_cov * 100.0) + / variables_cumulative) + + less_than_half_cov = variables_cumulative - (zero_covered_variables + + variables_fully_covered + + variables_with_more_than_half_scope_cov) + less_than_half_cov_per = int(round(less_than_half_cov * 100.0) + / variables_cumulative) + + print (' =================================================') + print (' Debug Location Statistics ') + print (' =================================================') + print (' cov% samples percentage(~) ') + print (' -------------------------------------------------') + print (' 0 {0:8d} {1:3d}%'. \ + format(zero_covered_variables, zero_covered_variables_per)) + print (' 1..50 {0:8d} {1:3d}%'. \ + format(less_than_half_cov, less_than_half_cov_per)) + print (' 51..99 {0:8d} {1:3d}%'. \ + format(variables_with_more_than_half_scope_cov, + variables_with_more_than_half_scope_cov_per)) + print (' 100 {0:8d} {1:3d}%'. \ + format(variables_fully_covered, variables_fully_covered_per)) + print (' =================================================') + print (' -the number of debug variables processed: ' \ + + str(variables_cumulative)) + print (' -total availability: ' + str(total_availability) + '%') + print (' -PC ranges covered: ' + str(pc_ranges_covered) + '%') + 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) + + # These will be different due to different options enabled. + variables_cumulative = None + variables_with_loc = None + variables_scope_bytes_covered = None + variables_scope_bytes_from_first_def = None + variables_scope_bytes_entry_values = None + variables_fully_covered = None + variables_fully_non_entry_value_covered = None + variables_with_more_than_half_scope_cov = None + variables_with_more_than_half_non_entry_val_scope_cov = None + zero_covered_variables = None + + binary = results.file_name + + # Get the directory of the LLVM tools. + wd = os.path.dirname(os.path.realpath(sys.argv[0])) + + # Get the llvm-dwarfdump --statistics and write it into the temproary file. + tmp_stats_file = 'stats.json' + run_command = \ + wd + '/llvm-dwarfdump --statistics ' \ + + binary + ' > ' + tmp_stats_file + os.system(run_command) + + # Get the JSON and parse it. + from_tmp_file = open(tmp_stats_file) + json_row_data = from_tmp_file.readline() + json_parsed = None + + try: + json_parsed = json.loads(json_row_data) + except: + print ('error: No valid llvm-dwarfdump statistics found.') + sys.exit(1) + + if results.only_variables: + # Read the JSON only for local variables. + variables_cumulative = json_parsed['total vars'] + variables_with_loc = \ + json_parsed['vars with binary location'] + variables_scope_bytes_covered = \ + json_parsed['vars scope bytes covered'] + variables_scope_bytes_from_first_def = \ + json_parsed['vars scope bytes total'] + variables_fully_covered = \ + json_parsed['vars with fully scope covered by debug locations' + ] + variables_with_more_than_half_scope_cov = \ + json_parsed['vars with more than half scope covered by debug locations' + ] + variables_scope_bytes_entry_values = \ + json_parsed['vars entry value scope bytes covered'] + variables_fully_non_entry_value_covered = \ + json_parsed['vars with fully scope covered by non entry value debug locations' + ] + variables_with_more_than_half_non_entry_val_scope_cov = \ + json_parsed['vars with more than half scope covered by non entry value debug locations' + ] + elif results.only_formal_parameters: + # Read the JSON only for formal parameters. + variables_cumulative = json_parsed['total formal params'] + variables_with_loc = \ + json_parsed['formal params with binary location'] + variables_scope_bytes_covered = \ + json_parsed['formal params scope bytes covered'] + variables_scope_bytes_from_first_def = \ + json_parsed['formal params scope bytes total'] + variables_fully_covered = \ + json_parsed['formal params with fully scope covered by debug locations' + ] + variables_with_more_than_half_scope_cov = \ + json_parsed['formal params with more than half scope covered by debug locations' + ] + variables_scope_bytes_entry_values = \ + json_parsed['formal params entry value scope bytes covered'] + variables_fully_non_entry_value_covered = \ + json_parsed['formal params with fully scope covered by non entry value debug locations' + ] + variables_with_more_than_half_non_entry_val_scope_cov = \ + json_parsed['formal params with more than half scope covered by non entry value debug locations' + ] + else: + # Read the JSON for both local variables and formal parameters. + total_vars = json_parsed['total vars'] + total_params = json_parsed['total formal params'] + constant_members = json_parsed['constant members'] + variables_cumulative = total_vars + total_params + constant_members + variables_with_loc = json_parsed['variables with location'] + variables_scope_bytes_covered = \ + json_parsed['scope bytes covered'] + variables_scope_bytes_from_first_def = \ + json_parsed['scope bytes total'] + variables_fully_covered = \ + json_parsed['variables with fully scope covered by debug locations' + ] + variables_with_more_than_half_scope_cov = \ + json_parsed['variables with more than half scope covered by debug locations' + ] + variables_scope_bytes_entry_values = \ + json_parsed['entry value scope bytes covered'] + variables_fully_non_entry_value_covered = \ + json_parsed['variables with fully scope covered by non entry value debug locations' + ] + variables_with_more_than_half_non_entry_val_scope_cov = \ + json_parsed['variables with more than half scope covered by non entry value debug locations' + ] + + zero_covered_variables = variables_cumulative - variables_with_loc + + if results.ignore_debug_entry_values: + variables_scope_bytes_covered = variables_scope_bytes_covered \ + - variables_scope_bytes_entry_values + variables_fully_covered = \ + variables_fully_non_entry_value_covered + variables_with_more_than_half_scope_cov = \ + variables_with_more_than_half_non_entry_val_scope_cov + + # Pretty print collected info. + locstats_output( + variables_cumulative, + variables_with_loc, + variables_scope_bytes_covered, + variables_scope_bytes_from_first_def, + variables_fully_covered, + zero_covered_variables, + variables_with_more_than_half_scope_cov, + ) + + # Clean up the temproary statistics file. + os.system('rm ' + tmp_stats_file) + +if __name__ == '__main__': + Main()