def handle_success(rh, result_file, result_base, skip_handler, capture_analysis_output, success_dir): """ Result postprocessing is required if the analysis was successful (mainly clang tidy output conversion is done). Skipping reports for header files is done here too. """ if capture_analysis_output: save_output(os.path.join(success_dir, result_base), rh.analyzer_stdout, rh.analyzer_stderr) rh.postprocess_result() # Generated reports will be handled separately at store. save_metadata(result_file, rh.analyzer_result_file, rh.analyzed_source_file) if skip_handler: # We need to check the plist content because skipping # reports in headers can be done only this way. plist_parser.skip_report_from_plist(result_file, skip_handler)
def check(check_data): """ Invoke clang with an action which called by processes. Different analyzer object belongs to for each build action. skiplist handler is None if no skip file was configured. """ actions_map, action, context, analyzer_config, \ output_dir, skip_handler, quiet_output_on_stdout, \ capture_analysis_output, analysis_timeout, \ analyzer_environment, ctu_reanalyze_on_failure, \ output_dirs, statistics_data = check_data failed_dir = output_dirs["failed"] success_dir = output_dirs["success"] try: # If one analysis fails the check fails. return_codes = 0 reanalyzed = False result_file = '' if analyzer_config is None: raise Exception("Analyzer configuration is missing.") source_analyzer, rh = prepare_check(action, analyzer_config, output_dir, context.severity_map, skip_handler, statistics_data) reanalyzed = os.path.exists(rh.analyzer_result_file) # Construct the analyzer cmd. analyzer_cmd = source_analyzer.construct_analyzer_cmd(rh) # The analyzer invocation calls __create_timeout as a callback # when the analyzer starts. This callback creates the timeout # watcher over the analyzer process, which in turn returns a # function, that can later be used to check if the analyzer quit # because we killed it due to a timeout. # # We need to capture the "function pointer" returned by # setup_process_timeout as reference, so that we may call it # later. To work around scoping issues, we use a list here so the # "function pointer" is captured by reference. timeout_cleanup = [lambda: False] if analysis_timeout and analysis_timeout > 0: def __create_timeout(analyzer_process): """ Once the analyzer process is started, this method is called. Set up a timeout for the analysis. """ timeout_cleanup[0] = setup_process_timeout( analyzer_process, analysis_timeout) else: def __create_timeout(analyzer_process): # If no timeout is given by the client, this callback # shouldn't do anything. pass result_file_exists = os.path.exists(rh.analyzer_result_file) # Fills up the result handler with the analyzer information. source_analyzer.analyze(analyzer_cmd, rh, analyzer_environment, __create_timeout) # If execution reaches this line, the analyzer process has quit. if timeout_cleanup[0](): LOG.warning( "Analyzer ran too long, exceeding time limit " "of %d seconds.", analysis_timeout) LOG.warning("Considering this analysis as failed...") rh.analyzer_returncode = -1 rh.analyzer_stderr = (">>> CodeChecker: Analysis timed out " "after {0} seconds. <<<\n{1}") \ .format(analysis_timeout, rh.analyzer_stderr) # If source file contains escaped spaces ("\ " tokens), then # clangSA writes the plist file with removing this escape # sequence, whereas clang-tidy does not. We rewrite the file # names to contain no escape sequences for every analyzer. result_file = rh.analyzer_result_file.replace(r'\ ', ' ') result_base = os.path.basename(result_file) ctu_active = is_ctu_active(source_analyzer) ctu_suffix = '_CTU' zip_suffix = ctu_suffix if ctu_active else '' failure_type = "_unknown" if rh.analyzer_returncode == 1: failure_type = "_compile_error" elif rh.analyzer_returncode == 254: failure_type = "_crash" zip_file = result_base + zip_suffix + failure_type + '.zip' zip_file = os.path.join(failed_dir, zip_file) ctu_zip_file = result_base + ctu_suffix + failure_type + '.zip' ctu_zip_file = os.path.join(failed_dir, ctu_zip_file) return_codes = rh.analyzer_returncode source_file_name = os.path.basename(action.source) # Remove the previously generated error file. if os.path.exists(zip_file): os.remove(zip_file) # Remove the previously generated CTU error file. if os.path.exists(ctu_zip_file): os.remove(ctu_zip_file) if rh.analyzer_returncode == 0: handle_success(rh, result_file, result_base, skip_handler, capture_analysis_output, success_dir) LOG.info("[%d/%d] %s analyzed %s successfully.", progress_checked_num.value, progress_actions.value, action.analyzer_type, source_file_name) if result_file_exists: LOG.warning( "Previous analysis results in '%s' has been " "overwritten.", rh.analyzer_result_file) else: LOG.error("Analyzing %s with %s %s failed!", source_file_name, action.analyzer_type, "CTU" if ctu_active else "") if not quiet_output_on_stdout: LOG.error("\n%s", rh.analyzer_stdout) LOG.error("\n%s", rh.analyzer_stderr) handle_failure(source_analyzer, rh, zip_file, result_base, actions_map) if ctu_active and ctu_reanalyze_on_failure: LOG.error("Try to reanalyze without CTU") # Try to reanalyze with CTU disabled. source_analyzer, rh = \ prepare_check(action, analyzer_config, output_dir, context.severity_map, skip_handler, statistics_data, True) reanalyzed = os.path.exists(rh.analyzer_result_file) # Construct the analyzer cmd. analyzer_cmd = source_analyzer.construct_analyzer_cmd(rh) # Fills up the result handler with # the analyzer information. source_analyzer.analyze(analyzer_cmd, rh, analyzer_environment) return_codes = rh.analyzer_returncode if rh.analyzer_returncode == 0: handle_success(rh, result_file, result_base, skip_handler, capture_analysis_output, success_dir) LOG.info( "[%d/%d] %s analyzed %s without" " CTU successfully.", progress_checked_num.value, progress_actions.value, action.analyzer_type, source_file_name) if result_file_exists: LOG.warning( "Previous analysis results in '%s' has " "been overwritten.", rh.analyzer_result_file) else: LOG.error("Analyzing '%s' with %s without CTU failed.", source_file_name, action.analyzer_type) zip_file = result_base + '.zip' zip_file = os.path.join(failed_dir, zip_file) handle_failure(source_analyzer, rh, zip_file, result_base, actions_map) if skip_handler and os.path.exists(result_file): # We need to check the plist content because skipping # reports in headers can be done only this way. plist_parser.skip_report_from_plist(result_file, skip_handler) if not quiet_output_on_stdout: if rh.analyzer_returncode: LOG.error('\n%s', rh.analyzer_stdout) LOG.error('\n%s', rh.analyzer_stderr) else: LOG.debug_analyzer('\n%s', rh.analyzer_stdout) LOG.debug_analyzer('\n%s', rh.analyzer_stderr) progress_checked_num.value += 1 return return_codes, False, reanalyzed, action.analyzer_type, \ result_file, action.source except Exception as e: LOG.debug_analyzer(str(e)) traceback.print_exc(file=sys.stdout) return 1, False, reanalyzed, action.analyzer_type, None, \ action.source