示例#1
0
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)
示例#2
0
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