From 7b063daced5407bded73c07953001c736dff4df5 Mon Sep 17 00:00:00 2001 From: bruntib Date: Mon, 22 Jul 2024 17:08:46 +0200 Subject: [PATCH] [feat] Print statistics after analysis through Makefile "CodeChecker analyze --makefile ..." is generating a Makefile that contains analyzer commands. With "make" command one can execute analysis using this generated Makefile. This commit is about printing some statistics about the analysis based on the Makefile. The statistics contain the number of successful and failed analysis. --- analyzer/codechecker_analyzer/makefile.py | 54 +++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/analyzer/codechecker_analyzer/makefile.py b/analyzer/codechecker_analyzer/makefile.py index 8c5b52196b..583c5babdc 100644 --- a/analyzer/codechecker_analyzer/makefile.py +++ b/analyzer/codechecker_analyzer/makefile.py @@ -56,6 +56,8 @@ def __init__(self, analyzers, output_path, config_map, self.__stats_dir = statistics_data['stats_out_dir'] self.__makefile = os.path.join(output_path, 'Makefile') + self.__stat_sh = os.path.join(output_path, 'print_stat.sh') + self.__exit_codes_dir = os.path.join(output_path, 'exit_codes') self.__config = None self.__func_map_cmd = None @@ -110,13 +112,51 @@ def __write_default_targets(self, mfile): analyzer. """ mfile.write("# Default target to run all analysis.\n" - "default: all\n\n") + "default: create_exit_code_folder all\n" + f"\t@bash {self.__stat_sh}\n" + f"\t@rm -rf {self.__exit_codes_dir}\n\n" + "# Folder for creating exit codes of analyses.\n" + "create_exit_code_folder:\n" + f"\t@rm -rf {self.__exit_codes_dir}\n" + f"\t@mkdir {self.__exit_codes_dir}\n\n") for analyzer in self.__analyzers: analyzer_name = self.__format_analyzer_type(analyzer) mfile.write(f"# Target to run only '{analyzer_name}' analysis.\n" f"all: all_{analyzer_name}\n\n") + def __write_print_stats(self, sfile): + """ Write target to print analyzer statistics. + + At the end of the analysis the Makefile should print statistics about + how many actions were analyzed by the specific analyzers. + """ + sfile.write(f'''#!/usr/bin/env bash +declare -A success +declare -A all +sum=0 + +for filename in $(ls {self.__exit_codes_dir}); do + success[$filename]=$(grep ^0$ {self.__exit_codes_dir}/$filename | wc -l) + all[$filename]=$(wc -l < {self.__exit_codes_dir}/$filename) + sum=$(($sum + ${{all[$filename]}})) +done + +echo "----==== Summary ====----" + +echo "Successfully analyzed" +for analyzer in "${{!success[@]}}"; do + echo $analyzer: ${{success[$analyzer]}} +done + +echo "Failed to analyze" +for analyzer in "${{!success[@]}}"; do + echo $analyzer: $((${{all[$analyzer]}} - ${{success[$analyzer]}})) +done + +echo "Total analyzed compilation commands: $sum" +echo "----=================----"''') + def __get_ctu_pre_analysis_cmds(self, action): """ Get CTU pre-analysis commands. """ cmds = [] @@ -238,6 +278,9 @@ def __write_analysis_targets(self, mfile, action, post_pre_all_target): target = self.__get_target_name(action) analyzer_name = self.__format_analyzer_type(action.analyzer_type) + save_exit_code = \ + f"; echo $$? >> {self.__exit_codes_dir}/{action.analyzer_type}" + if action.analyzer_type == ClangTidy.ANALYZER_NAME: analyzer_output_file = rh.analyzer_result_file + ".output" file_name = "{source_file}_{analyzer}_" + target @@ -247,11 +290,12 @@ def __write_analysis_targets(self, mfile, action, post_pre_all_target): "--filename", file_name, analyzer_output_file] - command = f"@{' '.join(analyzer_cmd)} > {analyzer_output_file}\n" \ + command = f"@{' '.join(analyzer_cmd)} > " \ + f"{analyzer_output_file}{save_exit_code}\n" \ f"\t@{' '.join(report_converter_cmd)} 1>/dev/null\n" \ f"\t@rm -rf {analyzer_output_file}\n" else: - command = f"@{' '.join(analyzer_cmd)} 1>/dev/null" + command = f"@{' '.join(analyzer_cmd)} 1>/dev/null{save_exit_code}" mfile.write( f'{target}: {post_pre_all_target}\n' @@ -265,6 +309,10 @@ def create(self, actions): LOG.info("Creating Makefile from the analyzer commands: '%s'...", self.__makefile) + with open(self.__stat_sh, 'w+', + encoding='utf-8', errors='ignore') as sfile: + self.__write_print_stats(sfile) + with open(self.__makefile, 'w+', encoding='utf-8', errors='ignore') as mfile: self.__write_header(mfile)