Пример #1
0
def vertices():
    yield [
        ComparisonGraph.Vertex(dup("f"), Result.Kind.EQUAL,
                               ("/test/f1/1.ll", "/test/f2/2.ll")),
        ComparisonGraph.Vertex(dup("h"), Result.Kind.NOT_EQUAL,
                               ("/test/f1/1.ll", "/test/f2/3.ll")),
        ComparisonGraph.Vertex(dup("g"), Result.Kind.NOT_EQUAL,
                               ("/test/f1/1.ll", "/test/f2/2.ll"))
    ]
Пример #2
0
def test_cachability_reset_after_absorb(graph_uncachable):
    """Tests whether the cachable attribute is reset to true after replacing
    the assumed equal vertex causing the uncachability."""
    graph_uncachable.normalize()
    graph_uncachable.populate_predecessor_lists()
    graph_uncachable.mark_uncachable_from_assumed_equal()
    assert not graph_uncachable["f2"].cachable
    graph_to_merge = ComparisonGraph()
    graph_to_merge["f3"] = ComparisonGraph.Vertex(dup("f3"),
                                                  Result.Kind.NOT_EQUAL,
                                                  dup("app/f2.c"), dup(20))
    graph_uncachable.absorb_graph(graph_to_merge)
    assert graph_uncachable["f2"].cachable
Пример #3
0
def test_add_vertex_strong(graph):
    """Tests adding a strong vertex to the graph."""
    graph["test"] = ComparisonGraph.Vertex(dup("test"), Result.Kind.EQUAL,
                                           dup("app/main.c"), (81, 82))
    assert "test" in graph.vertices
    assert graph["test"].names == dup("test")
    assert graph["test"].result == Result.Kind.EQUAL
    assert graph["test"].files == dup("app/main.c")
    assert graph["test"].lines == (81, 82)
    assert graph["test"] not in graph._weak_vertex_cache
Пример #4
0
def test_add_vertex_weak(graph):
    """Tests adding a weak vertex to the graph."""
    graph["test.void"] = ComparisonGraph.Vertex(("test", "test.void"),
                                                Result.Kind.EQUAL,
                                                dup("app/main.c"), (81, 82))
    assert "test.void" in graph.vertices
    assert graph["test.void"].names == ("test", "test.void")
    assert graph["test.void"].result == Result.Kind.EQUAL
    assert graph["test.void"].files == dup("app/main.c")
    assert graph["test.void"].lines == (81, 82)
    assert graph["test.void"] in graph._weak_vertex_cache
Пример #5
0
def test_add_edge_weak(graph):
    """Tests adding a weak edge to a graph."""
    graph.add_edge(graph["main_function"], ComparisonGraph.Side.LEFT,
                   ComparisonGraph.Edge("strength", "app/main.c", 61))
    graph.add_edge(graph["main_function"], ComparisonGraph.Side.RIGHT,
                   ComparisonGraph.Edge("strength.void", "app/main.c", 61))
    left_succesor_names = [
        edge.target_name for edge in graph["main_function"].successors[
            ComparisonGraph.Side.LEFT]
    ]
    right_successor_names = [
        edge.target_name for edge in graph["main_function"].successors[
            ComparisonGraph.Side.RIGHT]
    ]
    assert "strength" in left_succesor_names
    assert "strength.void" in right_successor_names
    assert "strength" not in right_successor_names
    assert "strength.void" not in left_succesor_names
    assert "strength.void" in [
        edge.target_name for edge in graph._normalize_edge_cache
    ]
Пример #6
0
def test_add_edge_strong(graph):
    """Tests adding a strong edge to a graph."""
    for side in ComparisonGraph.Side:
        graph.add_edge(graph["main_function"], side,
                       ComparisonGraph.Edge("missing", "app/main.c", 61))
        assert "missing" in [
            edge.target_name
            for edge in graph["main_function"].successors[side]
        ]
        assert "missing" not in [
            edge.target_name for edge in graph._normalize_edge_cache
        ]
Пример #7
0
def run_simpll(first, second, fun_first, fun_second, var, suffix=None,
               cache_dir=None, control_flow_only=False, output_llvm_ir=False,
               print_asm_diffs=False, verbose=False, use_ffi=False):
    """
    Simplify modules to ease their semantic difference. Uses the SimpLL tool.
    :return A tuple containing the two LLVM IR files generated by SimpLL
            followed by the result of the comparison in the form of a graph and
            a list of missing function definitions.
    """
    stderr = None
    if not verbose:
        stderr = open(os.devnull, "w")

    first_out_name = add_suffix(first, suffix) if suffix else first
    second_out_name = add_suffix(second, suffix) if suffix else second

    if use_ffi:
        output = ffi.new("char [1000000]")
        conf_struct = ffi.new("struct config *")

        cache_dir = ffi.new("char []", cache_dir.encode("ascii") if cache_dir
                            else b"")
        variable = ffi.new("char []", var.encode("ascii") if var else b"")
        conf_struct = ffi.new("struct config *")
        conf_struct.CacheDir = cache_dir
        conf_struct.ControlFlowOnly = control_flow_only
        conf_struct.OutputLlvmIR = output_llvm_ir
        conf_struct.PrintAsmDiffs = print_asm_diffs
        conf_struct.PrintCallStacks = True
        conf_struct.Variable = variable
        conf_struct.Verbose = verbose
        conf_struct.VerboseMacros = False

        module_left = ffi.new("char []", first.encode("ascii"))
        module_right = ffi.new("char []", second.encode("ascii"))
        module_left_out = ffi.new("char []", first_out_name.encode("ascii"))
        module_right_out = ffi.new("char []", second_out_name.encode("ascii"))
        fun_left = ffi.new("char []", fun_first.encode("ascii"))
        fun_right = ffi.new("char []", fun_second.encode("ascii"))

        try:
            lib.runSimpLL(module_left, module_right, module_left_out,
                          module_right_out, fun_left, fun_right,
                          conf_struct[0], output)
            simpll_out = ffi.string(output)
        except ffi.error:
            raise SimpLLException("Simplifying files failed")
    else:
        try:
            # Determine the SimpLL binary to use.
            # The manually built one has priority over the installed one.
            if os.path.isfile("build/diffkemp/simpll/diffkemp-simpll"):
                simpll_bin = "build/diffkemp/simpll/diffkemp-simpll"
            else:
                simpll_bin = "diffkemp-simpll"
            # SimpLL command
            simpll_command = list([simpll_bin, first, second,
                                   "--print-callstacks"])
            # Main (analysed) functions
            simpll_command.append("--fun")
            if fun_first != fun_second:
                simpll_command.append("{},{}".format(fun_first, fun_second))
            else:
                simpll_command.append(fun_first)
            # Analysed variable
            if var:
                simpll_command.extend(["--var", var])
            # Suffix for output files
            if suffix and output_llvm_ir:
                simpll_command.extend(["--suffix", suffix])
            # Cache directory with equal function pairs
            if cache_dir:
                simpll_command.extend(["--cache-dir", cache_dir])

            if control_flow_only:
                simpll_command.append("--control-flow")

            if output_llvm_ir:
                simpll_command.append("--output-llvm-ir")

            if print_asm_diffs:
                simpll_command.append("--print-asm-diffs")

            if verbose:
                simpll_command.append("--verbose")
                print(" ".join(simpll_command))

            simpll_out = check_output(simpll_command)
        except CalledProcessError:
            raise SimpLLException("Simplifying files failed")

    if output_llvm_ir:
        check_call(["opt", "-S", "-deadargelim", "-o", first_out_name,
                    first_out_name],
                   stderr=stderr)
        check_call(["opt", "-S", "-deadargelim", "-o", second_out_name,
                    second_out_name],
                   stderr=stderr)

    first_out = LlvmKernelModule(first_out_name)
    second_out = LlvmKernelModule(second_out_name)

    missing_defs = None
    try:
        result_graph = ComparisonGraph()
        simpll_result = yaml.safe_load(simpll_out)
        if simpll_result is not None:
            if "function-results" in simpll_result:
                for fun_result in simpll_result["function-results"]:
                    # Create the vertex from the result and insert it into
                    # the graph.
                    vertex = ComparisonGraph.Vertex.from_yaml(
                        fun_result, result_graph)
                    # Prefer pointed name to ensure that a difference
                    # contaning the variant function as either the left or
                    # the right side has its name in the key.
                    # This is useful because one can tell this is a weak
                    # vertex from its name.
                    if "." in vertex.names[ComparisonGraph.Side.LEFT]:
                        result_graph[vertex.names[
                            ComparisonGraph.Side.LEFT]] = vertex
                    else:
                        result_graph[vertex.names[
                            ComparisonGraph.Side.RIGHT]] = vertex
            result_graph.normalize()
            result_graph.populate_predecessor_lists()
            result_graph.mark_uncachable_from_assumed_equal()
            missing_defs = simpll_result["missing-defs"] \
                if "missing-defs" in simpll_result else None
    except yaml.YAMLError:
        pass

    return first_out, second_out, result_graph, missing_defs
Пример #8
0
def graph_uncachable():
    """Graph used to test the marking of uncachable vertices."""
    graph = ComparisonGraph()
    graph["f1"] = ComparisonGraph.Vertex(dup("f1"), Result.Kind.EQUAL,
                                         dup("app/f1.c"), dup(10))
    graph["f2"] = ComparisonGraph.Vertex(dup("f2"), Result.Kind.EQUAL,
                                         dup("include/h1.h"), dup(20))
    graph["f3"] = ComparisonGraph.Vertex(dup("f3"), Result.Kind.ASSUMED_EQUAL,
                                         dup("app/f2.c"), dup(20))
    for side in ComparisonGraph.Side:
        graph.add_edge(graph["f1"], side,
                       ComparisonGraph.Edge("f2", "app/f1.c", 11))
        graph.add_edge(graph["f2"], side,
                       ComparisonGraph.Edge("f3", "include/h1.c", 21))
    yield graph
Пример #9
0
def test_absort_graph(graph):
    """Tests the absorb graph function, especially whether vertex replacing
    works properly."""
    new_graph = ComparisonGraph()
    # This vertex should replace the old one (which is assumed equal).
    new_graph["missing"] = ComparisonGraph.Vertex(dup("missing"),
                                                  Result.Kind.NOT_EQUAL,
                                                  dup("app/mod.c"), dup(665))
    # This vertex should not replace the old one.
    new_graph["do_check"] = ComparisonGraph.Vertex(dup("do_check"),
                                                   Result.Kind.EQUAL,
                                                   dup("app/mod.c"), dup(665))
    # This vertex should replace the old one (it has more successors).
    new_graph["strength"] = ComparisonGraph.Vertex(dup("strength"),
                                                   Result.Kind.NOT_EQUAL,
                                                   dup("app/test.h"), (5, 5))
    for side in ComparisonGraph.Side:
        new_graph.add_edge(new_graph["strength"], side,
                           ComparisonGraph.Edge("missing", "app/w.c", 6))
        new_graph.add_edge(new_graph["strength"], side,
                           ComparisonGraph.Edge("main_function", "app/w.c", 7))
    graph.absorb_graph(new_graph)
    assert graph["missing"].result == Result.Kind.NOT_EQUAL
    assert graph["do_check"].result == Result.Kind.NOT_EQUAL
    assert graph["strength"].result == Result.Kind.NOT_EQUAL
Пример #10
0
def graph():
    g = ComparisonGraph()
    # Vertices
    g["main_function"] = ComparisonGraph.Vertex(dup("main_function"),
                                                Result.Kind.EQUAL,
                                                dup("app/main.c"), dup(51))
    g["side_function"] = ComparisonGraph.Vertex(dup("side_function"),
                                                Result.Kind.EQUAL,
                                                dup("app/main.c"), dup(255))
    g["do_check"] = ComparisonGraph.Vertex(dup("do_check"),
                                           Result.Kind.NOT_EQUAL,
                                           dup("app/main.c"), dup(105))
    g["missing"] = ComparisonGraph.Vertex(dup("missing"),
                                          Result.Kind.ASSUMED_EQUAL,
                                          dup("app/mod.c"), dup(665))
    g["looping"] = ComparisonGraph.Vertex(dup("looping"), Result.Kind.EQUAL,
                                          dup("app/main.c"), (81, 82))
    # Weak variant of "strength" function vertex (e.g. void-returning on the
    # right side)
    g["strength.void"] = ComparisonGraph.Vertex(("strength", "strength.void"),
                                                Result.Kind.EQUAL,
                                                dup("app/main.c"), (5, 5))
    # Strong variant of "strength" functin vertex
    g["strength"] = ComparisonGraph.Vertex(
        ("strength", "strength"), Result.Kind.EQUAL, dup("app/test.h"), (5, 5))
    # Non-function differences
    g["do_check"].nonfun_diffs.append(
        ComparisonGraph.SyntaxDiff(
            "MACRO", "do_check",
            dup([
                {
                    "function": "_MACRO",
                    "file": "test.c",
                    "line": 1
                },
                {
                    "function": "__MACRO",
                    "file": "test.c",
                    "line": 2
                },
                {
                    "function": "___MACRO",
                    "file": "test.c",
                    "line": 3
                },
            ]), ("5", "5L")))
    g["do_check"].nonfun_diffs.append(
        ComparisonGraph.TypeDiff(
            "struct file", "do_check",
            dup([
                {
                    "function": "struct file (type)",
                    "file": "include/file.h",
                    "line": 121
                },
            ]), dup("include/file.h"), dup(121)))
    # Edges
    for side in ComparisonGraph.Side:
        g.add_edge(g["main_function"], side,
                   ComparisonGraph.Edge("do_check", "app/main.c", 58))
        g.add_edge(g["main_function"], side,
                   ComparisonGraph.Edge("side_function", "app/main.c", 59))
        g.add_edge(g["do_check"], side,
                   ComparisonGraph.Edge("missing", "app/main.c", 60))
        g.add_edge(g["do_check"], side,
                   ComparisonGraph.Edge("looping", "app/main.c", 74))
        g.add_edge(g["looping"], side,
                   ComparisonGraph.Edge("main_function", "app/main.c", 85))
        # Strong call of "strength"
        g.add_edge(g["looping"], side,
                   ComparisonGraph.Edge("strength", "app/main.c", 86))
        g.add_edge(g["strength"], side,
                   ComparisonGraph.Edge("missing", "app/w.c", 6))
    # Weak call of "strength"
    g.add_edge(g["side_function"], ComparisonGraph.Side.LEFT,
               ComparisonGraph.Edge("strength", "app/main.c", 260))
    g.add_edge(g["side_function"], ComparisonGraph.Side.RIGHT,
               ComparisonGraph.Edge("strength.void", "app/main.c", 260))
    yield g
Пример #11
0
def run_simpll(first,
               second,
               fun_first,
               fun_second,
               var,
               suffix=None,
               cache_dir=None,
               pattern_config=None,
               control_flow_only=False,
               output_llvm_ir=False,
               print_asm_diffs=False,
               verbosity=0,
               use_ffi=False,
               module_cache=None,
               modules_to_cache=None):
    """
    Simplify modules to ease their semantic difference. Uses the SimpLL tool.
    :return A tuple containing the two LLVM IR files generated by SimpLL
            followed by the result of the comparison in the form of a graph and
            a list of missing function definitions.
    """
    stderr = None
    if verbosity == 0:
        stderr = open(os.devnull, "w")

    first_out_name = add_suffix(first, suffix) if suffix else first
    second_out_name = add_suffix(second, suffix) if suffix else second

    if use_ffi:
        # Preload module if in modules_to_cache
        if modules_to_cache is None:
            modules_to_cache = []

        for module in [first, second]:
            if module not in module_cache and module in modules_to_cache:
                module_cache[module] = SimpLLModule(module)
                module_cache[module].preprocess(control_flow_only)

        use_cached_modules = (module_cache and first in module_cache
                              and second in module_cache)

        output = ffi.new("char [1000000]")
        cache_dir = ffi.new("char []",
                            cache_dir.encode("ascii") if cache_dir else b"")
        patterns = ffi.new(
            "char []",
            pattern_config.path.encode("ascii") if pattern_config else b"")
        variable = ffi.new("char []", var.encode("ascii") if var else b"")

        conf_struct = ffi.new("struct config *")
        conf_struct.CacheDir = cache_dir
        conf_struct.Patterns = patterns
        conf_struct.ControlFlowOnly = control_flow_only
        conf_struct.OutputLlvmIR = output_llvm_ir
        conf_struct.PrintAsmDiffs = print_asm_diffs
        conf_struct.PrintCallStacks = True
        conf_struct.Variable = variable
        conf_struct.Verbosity = verbosity

        if use_cached_modules:
            module_left = module_cache[first].pointer
            module_right = module_cache[second].pointer
        else:
            module_left = ffi.new("char []", first.encode("ascii"))
            module_right = ffi.new("char []", second.encode("ascii"))

        module_left_out = ffi.new("char []", first_out_name.encode("ascii"))
        module_right_out = ffi.new("char []", second_out_name.encode("ascii"))
        fun_left = ffi.new("char []", fun_first.encode("ascii"))
        fun_right = ffi.new("char []", fun_second.encode("ascii"))

        try:
            if use_cached_modules:
                r, w = os.pipe()
                pid = os.fork()
                if pid == 0:
                    # Child process - run SimpLL and send result through pipe
                    os.close(r)
                    lib.runSimpLL(module_left, module_right, module_left_out,
                                  module_right_out, fun_left, fun_right,
                                  conf_struct[0], output)
                    os.write(w, ffi.string(output))
                    os.close(w)
                    os._exit(0)
                else:
                    # Parent process - collect result from pipe
                    os.close(w)
                    r = os.fdopen(r)
                    simpll_out = r.read()
                    _, status = os.waitpid(pid, 0)
                    if status != 0:
                        raise SimpLLException("Simplifying files failed")
            else:
                lib.parseAndRunSimpLL(module_left, module_right,
                                      module_left_out, module_right_out,
                                      fun_left, fun_right, conf_struct[0],
                                      output)
                simpll_out = ffi.string(output)
                lib.shutdownSimpLL()
        except ffi.error:
            raise SimpLLException("Simplifying files failed")
    else:
        try:
            # Determine the SimpLL binary to use.
            # The manually built one has priority over the installed one.
            simpll_bin = "{}/diffkemp/simpll/diffkemp-simpll".format(
                get_simpll_build_dir())
            if not os.path.isfile(simpll_bin):
                simpll_bin = "diffkemp-simpll"

            # SimpLL command
            simpll_command = list(
                [simpll_bin, first, second, "--print-callstacks"])
            # Main (analysed) functions
            simpll_command.append("--fun")
            if fun_first != fun_second:
                simpll_command.append("{},{}".format(fun_first, fun_second))
            else:
                simpll_command.append(fun_first)
            # Analysed variable
            if var:
                simpll_command.extend(["--var", var])
            # Suffix for output files
            if suffix and output_llvm_ir:
                simpll_command.extend(["--suffix", suffix])
            # Cache directory with equal function pairs
            if cache_dir:
                simpll_command.extend(["--cache-dir", cache_dir])
            # Difference pattern configuration path
            if pattern_config:
                simpll_command.extend(
                    ["--pattern-config", pattern_config.path])

            if control_flow_only:
                simpll_command.append("--control-flow")

            if output_llvm_ir:
                simpll_command.append("--output-llvm-ir")

            if print_asm_diffs:
                simpll_command.append("--print-asm-diffs")

            if verbosity > 0:
                simpll_command.extend(["--verbosity", str(verbosity)])
                print(" ".join(simpll_command))

            simpll_out = check_output(simpll_command)
        except CalledProcessError:
            raise SimpLLException("Simplifying files failed")

    if output_llvm_ir:
        check_call([
            "opt", "-S", "-deadargelim", "-o", first_out_name, first_out_name
        ],
                   stderr=stderr)
        check_call([
            "opt", "-S", "-deadargelim", "-o", second_out_name, second_out_name
        ],
                   stderr=stderr)

    first_out = LlvmModule(first_out_name)
    second_out = LlvmModule(second_out_name)

    missing_defs = None
    try:
        result_graph = ComparisonGraph()
        simpll_result = yaml.safe_load(simpll_out)
        if simpll_result is not None:
            if "function-results" in simpll_result:
                for fun_result in simpll_result["function-results"]:
                    # Create the vertex from the result and insert it into
                    # the graph.
                    vertex = ComparisonGraph.Vertex.from_yaml(
                        fun_result, result_graph)
                    # Prefer pointed name to ensure that a difference
                    # contaning the variant function as either the left or
                    # the right side has its name in the key.
                    # This is useful because one can tell this is a weak
                    # vertex from its name.
                    if "." in vertex.names[ComparisonGraph.Side.LEFT]:
                        result_graph[vertex.names[
                            ComparisonGraph.Side.LEFT]] = vertex
                    else:
                        result_graph[vertex.names[
                            ComparisonGraph.Side.RIGHT]] = vertex
            result_graph.normalize()
            result_graph.populate_predecessor_lists()
            result_graph.mark_uncachable_from_assumed_equal()
            missing_defs = simpll_result["missing-defs"] \
                if "missing-defs" in simpll_result else None
    except yaml.YAMLError:
        pass

    return first_out, second_out, result_graph, missing_defs