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,432 @@ +#!/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 +from json import loads +from math import ceil +from tempfile import TemporaryFile +from subprocess import call + +def locstats_output( + variables_total, + variables_total_locstats, + variables_with_loc, + scope_bytes_covered, + scope_bytes_from_first_def, + variables_0_cov, + variables_1_9_cov, + variables_11_19_cov, + variables_21_29_cov, + variables_31_39_cov, + variables_41_49_cov, + variables_51_59_cov, + variables_61_69_cov, + variables_71_79_cov, + variables_81_89_cov, + variables_91_99_cov, + variables_100_cov + ): + + pc_ranges_covered = int(ceil(scope_bytes_covered * 100.0) + / scope_bytes_from_first_def) + variables_0_cov_per = int(ceil(variables_0_cov * 100.0) + / variables_total_locstats) + variables_1_9_cov_per = int(ceil(variables_1_9_cov * 100.0) + / variables_total_locstats) + variables_11_19_cov_per = int(ceil(variables_11_19_cov * 100.0) + / variables_total_locstats) + variables_21_29_cov_per = int(ceil(variables_21_29_cov * 100.0) + / variables_total_locstats) + variables_31_39_cov_per = int(ceil(variables_31_39_cov * 100.0) + / variables_total_locstats) + variables_41_49_cov_per = int(ceil(variables_41_49_cov * 100.0) + / variables_total_locstats) + variables_51_59_cov_per = int(ceil(variables_51_59_cov * 100.0) + / variables_total_locstats) + variables_61_69_cov_per = int(ceil(variables_61_69_cov * 100.0) + / variables_total_locstats) + variables_71_79_cov_per = int(ceil(variables_71_79_cov * 100.0) + / variables_total_locstats) + variables_81_89_cov_per = int(ceil(variables_81_89_cov * 100.0) + / variables_total_locstats) + variables_91_99_cov_per = int(ceil(variables_91_99_cov * 100.0) + / variables_total_locstats) + variables_100_cov_per = int(ceil(variables_100_cov * 100.0) + / variables_total_locstats) + + print (' =================================================') + print (' Debug Location Statistics ') + print (' =================================================') + print (' cov% samples percentage(~) ') + print (' -------------------------------------------------') + print (' 0 {0:8d} {1:3d}%'. \ + format(variables_0_cov, variables_0_cov_per)) + print (' 1..9 {0:8d} {1:3d}%'. \ + format(variables_1_9_cov, variables_1_9_cov_per)) + print (' 11..19 {0:8d} {1:3d}%'. \ + format(variables_11_19_cov, variables_11_19_cov_per)) + print (' 21..29 {0:8d} {1:3d}%'. \ + format(variables_21_29_cov, variables_21_29_cov_per)) + print (' 31..39 {0:8d} {1:3d}%'. \ + format(variables_31_39_cov, variables_31_39_cov_per)) + print (' 41..49 {0:8d} {1:3d}%'. \ + format(variables_41_49_cov, variables_41_49_cov_per)) + print (' 51..99 {0:8d} {1:3d}%'. \ + format(variables_51_59_cov, variables_51_59_cov_per)) + print (' 61..69 {0:8d} {1:3d}%'. \ + format(variables_61_69_cov, variables_61_69_cov_per)) + print (' 71..79 {0:8d} {1:3d}%'. \ + format(variables_71_79_cov, variables_71_79_cov_per)) + print (' 81..89 {0:8d} {1:3d}%'. \ + format(variables_81_89_cov, variables_81_89_cov_per)) + print (' 91..99 {0:8d} {1:3d}%'. \ + format(variables_91_99_cov, variables_91_99_cov_per)) + print (' 100 {0:8d} {1:3d}%'. \ + format(variables_100_cov, variables_100_cov_per)) + 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_0_cov = None + variables_1_9_cov = None + variables_11_19_cov = None + variables_21_29_cov = None + variables_31_39_cov = None + variables_41_49_cov = None + variables_51_59_cov = None + variables_61_69_cov = None + variables_71_79_cov = None + variables_81_89_cov = None + variables_91_99_cov = None + variables_100_cov = None + + binary = results.file_name + + # Get the directory of the LLVM tools. + llvm_dwarfdump_bin = os.path.join(os.path.dirname(__file__), + "llvm-dwarfdump") + + # Get the llvm-dwarfdump --statistics and write it into the temproary file. + tmp_stats_file = TemporaryFile(mode='w+b') + call([llvm_dwarfdump_bin, "--statistics", binary], stdout=tmp_stats_file) + + # Get the JSON and parse it. + tmp_stats_file.seek(0) + json_row_data = tmp_stats_file.readline() + json_parsed = None + + try: + json_parsed = 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_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: + variables_0_cov = \ + json_parsed['vars with 0% of its scope covered'] + variables_1_9_cov = \ + json_parsed['vars with 1-9% of its scope covered'] + variables_11_19_cov = \ + json_parsed['vars with 11-19% of its scope covered'] + variables_21_29_cov = \ + json_parsed['vars with 21-29% of its scope covered'] + variables_31_39_cov = \ + json_parsed['vars with 31-39% of its scope covered'] + variables_41_49_cov = \ + json_parsed['vars with 41-49% of its scope covered'] + variables_51_59_cov = \ + json_parsed['vars with 51-59% of its scope covered'] + variables_61_69_cov = \ + json_parsed['vars with 61-69% of its scope covered'] + variables_71_79_cov = \ + json_parsed['vars with 71-79% of its scope covered'] + variables_81_89_cov = \ + json_parsed['vars with 81-89% of its scope covered'] + variables_91_99_cov = \ + json_parsed['vars with 91-99% of its scope covered'] + variables_100_cov = \ + json_parsed['vars with 100% of its scope covered'] + 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 + variables_0_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 0% of its scope covered'] + variables_1_9_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 1-9% of its scope covered'] + variables_11_19_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 11-19% of its scope covered'] + variables_21_29_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 21-29% of its scope covered'] + variables_31_39_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 31-39% of its scope covered'] + variables_41_49_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 41-49% of its scope covered'] + variables_51_59_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 51-59% of its scope covered'] + variables_61_69_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 61-69% of its scope covered'] + variables_71_79_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 71-79% of its scope covered'] + variables_81_89_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 81-89% of its scope covered'] + variables_91_99_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 91-99% of its scope covered'] + variables_100_cov = \ + json_parsed['vars (excluding the debug entry values) ' + 'with 100% of its scope covered'] + 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: + variables_0_cov = \ + json_parsed['params with 0% of its scope covered'] + variables_1_9_cov = \ + json_parsed['params with 1-9% of its scope covered'] + variables_11_19_cov = \ + json_parsed['params with 11-19% of its scope covered'] + variables_21_29_cov = \ + json_parsed['params with 21-29% of its scope covered'] + variables_31_39_cov = \ + json_parsed['params with 31-39% of its scope covered'] + variables_41_49_cov = \ + json_parsed['params with 41-49% of its scope covered'] + variables_51_59_cov = \ + json_parsed['params with 51-59% of its scope covered'] + variables_61_69_cov = \ + json_parsed['params with 61-69% of its scope covered'] + variables_71_79_cov = \ + json_parsed['params with 71-79% of its scope covered'] + variables_81_89_cov = \ + json_parsed['params with 81-89% of its scope covered'] + variables_91_99_cov = \ + json_parsed['params with 91-99% of its scope covered'] + variables_100_cov = \ + json_parsed['params with 100% of its scope covered'] + 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 + variables_0_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 0% of its scope covered'] + variables_1_9_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 1-9% of its scope covered'] + variables_11_19_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 11-19% of its scope covered'] + variables_21_29_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 21-29% of its scope covered'] + variables_31_39_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 31-39% of its scope covered'] + variables_41_49_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 41-49% of its scope covered'] + variables_51_59_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 51-59% of its scope covered'] + variables_61_69_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 61-69% of its scope covered'] + variables_71_79_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 71-79% of its scope covered'] + variables_81_89_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 81-89% of its scope covered'] + variables_91_99_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 91-99% of its scope covered'] + variables_100_cov = \ + json_parsed['params (excluding the debug entry values) ' + 'with 100% of its scope covered'] + 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: + variables_0_cov = \ + json_parsed['variables with 0% of its scope covered'] + variables_1_9_cov = \ + json_parsed['variables with 1-9% of its scope covered'] + variables_11_19_cov = \ + json_parsed['variables with 11-19% of its scope covered'] + variables_21_29_cov = \ + json_parsed['variables with 21-29% of its scope covered'] + variables_31_39_cov = \ + json_parsed['variables with 31-39% of its scope covered'] + variables_41_49_cov = \ + json_parsed['variables with 41-49% of its scope covered'] + variables_51_59_cov = \ + json_parsed['variables with 51-59% of its scope covered'] + variables_61_69_cov = \ + json_parsed['variables with 61-69% of its scope covered'] + variables_71_79_cov = \ + json_parsed['variables with 71-79% of its scope covered'] + variables_81_89_cov = \ + json_parsed['variables with 81-89% of its scope covered'] + variables_91_99_cov = \ + json_parsed['variables with 91-99% of its scope covered'] + variables_100_cov = \ + json_parsed['variables with 100% of its scope covered'] + 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 + variables_0_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 0% of its scope covered'] + variables_1_9_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 1-9% of its scope covered'] + variables_11_19_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 11-19% of its scope covered'] + variables_21_29_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 21-29% of its scope covered'] + variables_31_39_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 31-39% of its scope covered'] + variables_41_49_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 41-49% of its scope covered'] + variables_51_59_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 51-59% of its scope covered'] + variables_61_69_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 61-69% of its scope covered'] + variables_71_79_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 71-79% of its scope covered'] + variables_81_89_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 81-89% of its scope covered'] + variables_91_99_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 91-99% of its scope covered'] + variables_100_cov = \ + json_parsed['variables (excluding the debug entry values) ' + 'with 100% of its scope covered'] + + # 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_0_cov, + variables_1_9_cov, + variables_11_19_cov, + variables_21_29_cov, + variables_31_39_cov, + variables_41_49_cov, + variables_51_59_cov, + variables_61_69_cov, + variables_71_79_cov, + variables_81_89_cov, + variables_91_99_cov, + variables_100_cov + ) + + # Clean up the temproary statistics file. + tmp_stats_file.close() + +if __name__ == '__main__': + Main()