diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/result_handler.py b/analyzer/codechecker_analyzer/analyzers/clangsa/result_handler.py index 4585e7c09a..36c6880b94 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/result_handler.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/result_handler.py @@ -14,7 +14,7 @@ from typing import Optional from codechecker_report_converter.report.parser.base import AnalyzerInfo -from codechecker_report_converter.report import report_file +from codechecker_report_converter.report import report_file, error_file from codechecker_report_converter.report.hash import get_report_hash, HashType from codechecker_common.logger import get_logger from codechecker_common.skiplist_handler import SkipListHandlers @@ -43,6 +43,11 @@ def postprocess_result( Generate analyzer result output file which can be parsed and stored into the database. """ + error_file.create( + self.analyzer_result_file, self.analyzer_returncode, + self.analyzer_info, self.analyzer_cmd, + self.analyzer_stdout, self.analyzer_stderr) + if os.path.exists(self.analyzer_result_file): reports = report_file.get_reports( self.analyzer_result_file, self.checker_labels, diff --git a/analyzer/codechecker_analyzer/analyzers/clangtidy/result_handler.py b/analyzer/codechecker_analyzer/analyzers/clangtidy/result_handler.py index 5bdb1dbc8c..83c5092325 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangtidy/result_handler.py +++ b/analyzer/codechecker_analyzer/analyzers/clangtidy/result_handler.py @@ -15,7 +15,7 @@ AnalyzerResult from codechecker_report_converter.analyzers.clang_tidy.parser import Parser from codechecker_report_converter.report.parser.base import AnalyzerInfo -from codechecker_report_converter.report import report_file +from codechecker_report_converter.report import report_file, error_file from codechecker_report_converter.report.hash import get_report_hash, HashType from codechecker_common.logger import get_logger @@ -76,3 +76,8 @@ def postprocess_result( report_file.create( self.analyzer_result_file, reports, self.checker_labels, self.analyzer_info) + + error_file.create( + self.analyzer_result_file, self.analyzer_returncode, + self.analyzer_info, self.analyzer_cmd, + self.analyzer_stdout, self.analyzer_stderr) diff --git a/analyzer/codechecker_analyzer/analyzers/cppcheck/result_handler.py b/analyzer/codechecker_analyzer/analyzers/cppcheck/result_handler.py index 68c28b02a9..5286c1fbd9 100644 --- a/analyzer/codechecker_analyzer/analyzers/cppcheck/result_handler.py +++ b/analyzer/codechecker_analyzer/analyzers/cppcheck/result_handler.py @@ -14,7 +14,7 @@ from codechecker_report_converter.analyzers.cppcheck.analyzer_result import \ AnalyzerResult from codechecker_report_converter.report import BugPathEvent, \ - Range, report_file + Range, report_file, error_file from codechecker_report_converter.report.hash import get_report_hash, HashType from codechecker_common.logger import get_logger @@ -84,3 +84,8 @@ def postprocess_result( report_file.create( self.analyzer_result_file, reports, self.checker_labels, self.analyzer_info) + + error_file.create( + self.analyzer_result_file, self.analyzer_returncode, + self.analyzer_info, self.analyzer_cmd, + self.analyzer_stdout, self.analyzer_stderr) diff --git a/analyzer/codechecker_analyzer/analyzers/infer/result_handler.py b/analyzer/codechecker_analyzer/analyzers/infer/result_handler.py index d0fd59877c..ae2a937a42 100644 --- a/analyzer/codechecker_analyzer/analyzers/infer/result_handler.py +++ b/analyzer/codechecker_analyzer/analyzers/infer/result_handler.py @@ -15,7 +15,7 @@ from codechecker_report_converter.report.parser.base import AnalyzerInfo from codechecker_report_converter.analyzers.infer.analyzer_result import \ AnalyzerResult -from codechecker_report_converter.report import report_file +from codechecker_report_converter.report import report_file, error_file from codechecker_report_converter.report.hash import get_report_hash, HashType from codechecker_common.logger import get_logger @@ -74,4 +74,9 @@ def postprocess_result( self.analyzer_result_file, reports, self.checker_labels, self.analyzer_info) + error_file.create( + self.analyzer_result_file, self.analyzer_returncode, + self.analyzer_info, self.analyzer_cmd, + self.analyzer_stdout, self.analyzer_stderr) + shutil.rmtree(Path(self.workspace, "infer", self.buildaction_hash)) diff --git a/analyzer/codechecker_analyzer/cli/parse.py b/analyzer/codechecker_analyzer/cli/parse.py index c0fb770817..c97c7aa428 100644 --- a/analyzer/codechecker_analyzer/cli/parse.py +++ b/analyzer/codechecker_analyzer/cli/parse.py @@ -14,6 +14,7 @@ import argparse import os import sys +import re from typing import Dict, Optional, Set import fnmatch @@ -28,6 +29,7 @@ from codechecker_analyzer import analyzer_context, suppress_handler +from codechecker_analyzer.analyzers.analyzer_types import supported_analyzers from codechecker_common import arg, logger, cmd_config from codechecker_common.review_status_handler import ReviewStatusHandler @@ -208,6 +210,14 @@ def add_arguments_to_parser(parser): help="Filter results by review statuses. Valid " f"values are: {', '.join(REVIEW_STATUS_VALUES)}") + parser.add_argument('--status', + dest="status", + action="store_true", + required=False, + default=argparse.SUPPRESS, + help="Print the number of successful and failed " + "analysis actions.") + group = parser.add_argument_group("file filter arguments") group.add_argument('-i', '--ignore', '--skip', @@ -263,6 +273,52 @@ def get_metadata(dir_path: str) -> Optional[Dict]: return None +def print_status(inputs): + if len(inputs) != 1: + LOG.error("Parse status can only be printed " + "for one directory.") + sys.exit(1) + + report_dir = inputs[0] + + if not os.path.isdir(report_dir): + LOG.error("Input path '%s' is not a directory.", + report_dir) + sys.exit(1) + + successful = {} + failed = {} + + report_dir_files = {os.fsdecode(e) for e in + os.listdir(os.fsencode(report_dir))} + + for analyzer in supported_analyzers: + plist_re = re.compile(fr'.*_{analyzer}_[0-9a-f]+\.plist$') + plist_err_re = re.compile(fr'.*_{analyzer}_[0-9a-f]+\.plist\.err$') + successful[analyzer] = 0 + failed[analyzer] = 0 + + for file in report_dir_files: + if (plist_re.match(file) and + f"{file}.err" not in report_dir_files): + successful[analyzer] += 1 + + if plist_err_re.match(file): + failed[analyzer] += 1 + + LOG.info("----==== Summary ====----") + LOG.info("Successfully analyzed") + for analyzer in supported_analyzers: + if successful[analyzer] > 0: + LOG.info(" %s: %s", analyzer, successful[analyzer]) + + LOG.info("Failed to analyze") + for analyzer in supported_analyzers: + if failed[analyzer] > 0: + LOG.info(" %s: %s", analyzer, failed[analyzer]) + LOG.info("----=================----") + + def main(args): """ Entry point for parsing some analysis results and printing them to the @@ -311,6 +367,10 @@ def main(args): if isinstance(args.input, str): args.input = [args.input] + if 'status' in args: + print_status(args.input) + return + src_comment_status_filter = args.review_status suppr_handler = None diff --git a/tools/report-converter/codechecker_report_converter/report/error_file.py b/tools/report-converter/codechecker_report_converter/report/error_file.py new file mode 100644 index 0000000000..d844a9b380 --- /dev/null +++ b/tools/report-converter/codechecker_report_converter/report/error_file.py @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- + +from codechecker_report_converter.report.parser import plist + + +def create(output_path, return_code, analyzer_info, + analyzer_cmd, stdout, stderr): + if return_code == 0: + return + + parser = plist.Parser() + + data = { + 'analyzer_name': analyzer_info.name, + 'analyzer_cmd': analyzer_cmd, + 'return_code': return_code, + 'stdout': stdout, + 'stderr': stderr + } + + parser.write(data, output_path + ".err")