def diff_behavior(ctxfn1: FunctionInfo, ctxfn2: FunctionInfo,
                  options: AnalysisOptions) -> Union[str, List[BehaviorDiff]]:
    fn1, sig1 = ctxfn1.callable()
    fn2, sig2 = ctxfn2.callable()
    debug("Resolved signature:", sig1)
    all_diffs: List[BehaviorDiff] = []
    half1, half2 = options.split_limits(0.5)
    with condition_parser(options.analysis_kind), Patched():
        # We attempt both orderings of functions. This helps by:
        # (1) avoiding code path explosions in one of the functions
        # (2) using both signatures (in case they differ)
        all_diffs.extend(diff_behavior_with_signature(fn1, fn2, sig1, half1))
        all_diffs.extend(
            diff.reverse()
            for diff in diff_behavior_with_signature(fn2, fn1, sig2, half2))
    debug("diff candidates:", all_diffs)
    # greedily pick results:
    result_diffs = []
    opcodeset1 = set(i.offset for i in dis.get_instructions(fn1.__code__))
    opcodeset2 = set(i.offset for i in dis.get_instructions(fn2.__code__))
    while all_diffs:
        scorer = diff_scorer(opcodeset1, opcodeset2)
        selection = max(all_diffs, key=scorer)
        (num_opcodes, _) = scorer(selection)
        debug("Considering input", selection.args, " num opcodes=",
              num_opcodes)
        if num_opcodes == 0:
            break
        all_diffs.remove(selection)
        result_diffs.append(selection)
        coverage1, coverage2 = selection.coverage1, selection.coverage2
        if coverage1 is not None and coverage2 is not None:
            opcodeset1 -= coverage1.offsets_covered
            opcodeset2 -= coverage2.offsets_covered
    return result_diffs
Exemple #2
0
def path_cover(ctxfn: FunctionInfo, options: AnalysisOptions,
               coverage_type: CoverageType) -> List[PathSummary]:
    fn, sig = ctxfn.callable()
    search_root = SinglePathNode(True)
    condition_start = time.monotonic()
    paths: List[PathSummary] = []
    for i in range(1, options.max_iterations):
        debug("Iteration ", i)
        itr_start = time.monotonic()
        if itr_start > condition_start + options.per_condition_timeout:
            debug(
                "Stopping due to --per_condition_timeout=",
                options.per_condition_timeout,
            )
            break
        space = StateSpace(
            execution_deadline=itr_start + options.per_path_timeout,
            model_check_timeout=options.per_path_timeout / 2,
            search_root=search_root,
        )
        with condition_parser(options.analysis_kind), Patched(
        ), COMPOSITE_TRACER, StateSpaceContext(space):
            summary = None
            try:
                summary = run_iteration(fn, sig, space)
                verification_status = VerificationStatus.CONFIRMED
            except UnexploredPath:
                verification_status = VerificationStatus.UNKNOWN
            debug("Verification status:", verification_status)
            top_analysis, exhausted = space.bubble_status(
                CallAnalysis(verification_status))
            debug("Path tree stats", search_root.stats())
            if summary:
                paths.append(summary)
            if exhausted:
                debug("Stopping due to code path exhaustion. (yay!)")
                break
    opcodes_found: Set[int] = set()
    selected: List[PathSummary] = []
    while paths:
        next_best = max(
            paths,
            key=lambda p: len(p.coverage.offsets_covered - opcodes_found))
        cur_offsets = next_best.coverage.offsets_covered
        if coverage_type == CoverageType.OPCODE:
            if len(cur_offsets - opcodes_found) == 0:
                break
        selected.append(next_best)
        opcodes_found |= cur_offsets
        paths = [p for p in paths if p is not next_best]
    return selected