Beispiel #1
0
def test_to_yaml_sysctls():
    """
    Dump a snapshot with multiple sysctl groups into YAML.
    YAML string should contain the version of Diffkemp, source kernel
    directory, a simple list of function groups, each one containing a
    function list with the "name", "llvm", "glob_var" and "tag" fields set,
    and the kind of this list, which should be a group list.
    The LLVM paths in the YAML should be relative to the snapshot directory.
    """
    kernel_dir = "kernel/linux-3.10.0-957.el7"
    output_dir = "snapshots-sysctl/linux-3.10.0-957.el7"
    snap = Snapshot.create_from_source(kernel_dir, output_dir,
                                       "sysctl", False)

    snap.add_fun_group("kernel.sched_latency_ns")
    snap.add_fun_group("kernel.timer_migration")
    snap.add_fun("sched_proc_update_handler",
                 LlvmKernelModule(
                     "snapshots-sysctl/linux-3.10.0-957.el7/"
                     "kernel/sched/fair.ll"),
                 None, "proc handler", "kernel.sched_latency_ns")
    snap.add_fun("proc_dointvec_minmax",
                 LlvmKernelModule(
                     "snapshots-sysctl/linux-3.10.0-957.el7/kernel/sysctl.ll"),
                 None, "proc handler", "kernel.timer_migration")

    yaml_str = snap.to_yaml()
    yaml_snap = yaml.safe_load(yaml_str)
    assert len(yaml_snap) == 1
    yaml_dict = yaml_snap[0]
    assert len(yaml_dict) == 5
    assert isinstance(yaml_dict["created_time"], datetime.datetime)
    assert len(yaml_dict["list"]) == 2
    assert set([g["sysctl"] for g in yaml_dict["list"]]) == {
        "kernel.sched_latency_ns",
        "kernel.timer_migration"}

    for g in yaml_dict["list"]:
        assert len(g["functions"]) == 1
        if g["sysctl"] == "kernel.sched_latency_ns":
            assert g["functions"][0] == {
                "name": "sched_proc_update_handler",
                "llvm": "kernel/sched/fair.ll",
                "glob_var": None,
                "tag": "proc handler"
            }
        elif g["sysctl"] == "kernel.timer_migration":
            assert g["functions"][0] == {
                "name": "proc_dointvec_minmax",
                "llvm": "kernel/sysctl.ll",
                "glob_var": None,
                "tag": "proc handler"
            }
Beispiel #2
0
def test_filter():
    """Filter snapshot functions."""
    kernel_dir = "kernel/linux-3.10.0-957.el7"
    output_dir = "snapshots/linux-3.10.0-957.el7"
    snap = Snapshot.create_from_source(kernel_dir, output_dir, None, False)

    snap.add_fun("___pskb_trim", LlvmKernelModule("net/core/skbuff.ll"))
    snap.add_fun("__alloc_pages_nodemask",
                 LlvmKernelModule("mm/page_alloc.ll"))

    snap.filter(["__alloc_pages_nodemask"])
    assert len(snap.fun_groups[None].functions) == 1
    assert "___pskb_trim" not in snap.fun_groups[None].functions
    assert "__alloc_pages_nodemask" in snap.fun_groups[None].functions
Beispiel #3
0
def test_get_by_name_functions():
    """Get the module of inserted function by its name."""
    kernel_dir = "kernel/linux-3.10.0-957.el7"
    output_dir = "snapshots/linux-3.10.0-957.el7"
    snap = Snapshot.create_from_source(kernel_dir, output_dir, None, False)

    mod_buff = LlvmKernelModule("net/core/skbuff.ll")
    mod_alloc = LlvmKernelModule("mm/page_alloc.ll")
    snap.add_fun("___pskb_trim", mod_buff)
    snap.add_fun("__alloc_pages_nodemask", mod_alloc)

    fun = snap.get_by_name("___pskb_trim")
    assert fun.mod is mod_buff
    fun = snap.get_by_name("__alloc_pages_nodemask")
    assert fun.mod is mod_alloc
Beispiel #4
0
    def get_module_from_source(self, source_path):
        """
        Create an LLVM module from a source file.
        Builds the source into LLVM IR if needed.
        :param source_path: Relative path to the file
        :returns Instance of LlvmKernelModule
        """
        name = source_path[:-2] if source_path.endswith(".c") else source_path
        # If the module has already been created, return it
        if name in self.modules:
            return self.modules[name]

        llvm_file = os.path.join(self.kernel_dir, "{}.ll".format(name))
        source_file = os.path.join(self.kernel_dir, source_path)
        if self.builder:
            try:
                self.builder.build_source_to_llvm(source_file, llvm_file)
            except BuildException:
                pass

        if not os.path.isfile(llvm_file):
            return None

        mod = LlvmKernelModule(llvm_file, source_file)
        self.modules[name] = mod
        return mod
Beispiel #5
0
    def _from_yaml(self, yaml_file):
        """
        Load the snaphot from its YAML representation. Paths are assumed to be
        relative to the root directory.
        :param yaml_file: Contents of the YAML file.
        """
        yaml_file = yaml.safe_load(yaml_file)
        yaml_dict = yaml_file[0]

        self.created_time = yaml_dict["created_time"]

        if os.path.isdir(yaml_dict["source_kernel_dir"]):
            self.kernel_source = KernelSource(yaml_dict["source_kernel_dir"],
                                              True)

        for g in yaml_dict["list"]:
            if "sysctl" in g:
                self.kind = "sysctl"
                group = g["sysctl"]
                functions = g["functions"]
            else:
                group = None
                functions = yaml_dict["list"]
            self.fun_groups[group] = self.FunctionGroup()
            for f in functions:
                self.add_fun(
                    f["name"],
                    LlvmKernelModule(
                        os.path.join(
                            os.path.relpath(self.snapshot_source.kernel_dir),
                            f["llvm"])) if f["llvm"] else None, f["glob_var"],
                    f["tag"], group)
 def get_module_for_kernel_mod(self, mod_dir, mod_name):
     """
     Get LLVM module for a kernel module.
     :param mod_dir: Kernel module directory.
     :param mod_name: Kernel module name.
     :return: LlvmKernelModule containing the built LLVM file.
     """
     llvm_file = self.builder.build_kernel_mod_to_llvm(mod_dir, mod_name)
     return LlvmKernelModule(os.path.join(self.kernel_dir, llvm_file))
Beispiel #7
0
 def from_yaml(self, yaml_file):
     """
     Load the list from YAML file. Paths are assumed to be relative to the
     root directory.
     :param yaml_file: Contents of the YAML file.
     """
     funs = yaml.safe_load(yaml_file)
     for f in funs:
         self.add(f["name"],
                  LlvmKernelModule(os.path.join(self.root_dir, f["llvm"])))
Beispiel #8
0
def test_get_by_name_sysctls():
    """Get the module of inserted function by its name and sysctl group."""
    kernel_dir = "kernel/linux-3.10.0-957.el7"
    output_dir = "snapshots/linux-3.10.0-957.el7"
    snap = Snapshot.create_from_source(kernel_dir, output_dir, "sysctl", False)

    snap.add_fun_group("kernel.sched_latency_ns")
    snap.add_fun_group("kernel.timer_migration")
    mod_fair = LlvmKernelModule(
        "snapshots-sysctl/linux-3.10.0-957.el7/kernel/sched/fair.ll")
    mod_sysctl = LlvmKernelModule(
        "snapshots-sysctl/linux-3.10.0-957.el7/kernel/sysctl.ll")
    snap.add_fun("sched_proc_update_handler", mod_fair, None, "proc handler",
                 "kernel.sched_latency_ns")
    snap.add_fun("proc_dointvec_minmax", mod_sysctl, None, "proc handler",
                 "kernel.timer_migration")

    # Test that the function
    fun = snap.get_by_name("proc_dointvec_minmax", "kernel.sched_latency_ns")
    assert fun is None
    fun = snap.get_by_name("proc_dointvec_minmax", "kernel.timer_migration")
    assert fun.mod is mod_sysctl
Beispiel #9
0
def test_get_modules():
    """
    Test getting all modules in the snapshot function lists.
    Check if the snapshot returns a list of all modules of all groups in case
    that multiple groups are present.
    """
    kernel_dir = "kernel/linux-3.10.0-957.el7"
    output_dir = "snapshots/linux-3.10.0-957.el7"
    snap = Snapshot.create_from_source(kernel_dir, output_dir, "sysctl", False)

    snap.add_fun_group("kernel.sched_latency_ns")
    snap.add_fun_group("kernel.timer_migration")
    snap.add_fun("sched_proc_update_handler",
                 LlvmKernelModule("kernel/sched/fair.ll"), None,
                 "proc_handler", "kernel.sched_latency_ns")
    snap.add_fun("proc_dointvec_minmax", LlvmKernelModule("kernel/sysctl.ll"),
                 None, "proc_handler", "kernel.timer_migration")

    modules = snap.modules()
    assert len(modules) == 2
    assert set([m.llvm for m in modules
                ]) == {"kernel/sched/fair.ll", "kernel/sysctl.ll"}
Beispiel #10
0
def test_add_fun_none_group():
    """Create a snapshot and try to add functions into a None group."""
    kernel_dir = "kernel/linux-3.10.0-957.el7"
    output_dir = "snapshots/linux-3.10.0-957.el7"
    snap = Snapshot.create_from_source(kernel_dir, output_dir, None, False)

    mod = LlvmKernelModule("net/core/skbuff.ll")
    snap.add_fun("___pskb_trim", mod)

    assert "___pskb_trim" in snap.fun_groups[None].functions
    fun_desc = snap.fun_groups[None].functions["___pskb_trim"]
    assert fun_desc.mod is mod
    assert fun_desc.glob_var is None
    assert fun_desc.tag is None
Beispiel #11
0
def test_to_yaml_functions():
    """
    Dump a snapshot with a single "None" group into YAML.
    YAML string should contain the version of Diffkemp, source kernel
    directory, a simple list of functions, each one having the "name",
    "llvm", "glob_var" and "tag" fields set, and the kind of this list,
    which should be a function list.
    The LLVM paths in the YAML should be relative to the snapshot directory.
    """
    kernel_dir = "kernel/linux-3.10.0-957.el7"
    output_dir = "snapshots/linux-3.10.0-957.el7"
    snap = Snapshot.create_from_source(kernel_dir, output_dir, None, False)

    snap.add_fun(
        "___pskb_trim",
        LlvmKernelModule("snapshots/linux-3.10.0-957.el7/net/core/skbuff.ll"))
    snap.add_fun(
        "__alloc_pages_nodemask",
        LlvmKernelModule("snapshots/linux-3.10.0-957.el7/mm/page_alloc.ll"))

    yaml_str = snap.to_yaml()
    yaml_snap = yaml.safe_load(yaml_str)

    assert len(yaml_snap) == 1
    yaml_dict = yaml_snap[0]
    assert len(yaml_dict) == 5
    assert isinstance(yaml_dict["created_time"], datetime.datetime)
    assert len(yaml_dict["list"]) == 2
    assert set([f["name"] for f in yaml_dict["list"]]) ==\
        {"___pskb_trim",
         "__alloc_pages_nodemask"}

    for f in yaml_dict["list"]:
        if f["name"] == "___pskb_trim":
            assert f["llvm"] == "net/core/skbuff.ll"
        elif f["name"] == "__alloc_pages_nodemask":
            assert f["llvm"] == "mm/page_alloc.ll"
Beispiel #12
0
def test_add_fun_sysctl_group():
    """Create a snapshot and try to add functions into sysctl groups."""
    kernel_dir = "kernel/linux-3.10.0-957.el7"
    output_dir = "snapshots/linux-3.10.0-957.el7"
    snap = Snapshot.create_from_source(kernel_dir, output_dir, "sysctl", False)

    snap.add_fun_group("kernel.sched_latency_ns")
    mod = LlvmKernelModule("kernel/sched/debug.ll")
    snap.add_fun("sched_debug_header", mod, "sysctl_sched_latency",
                 "using_data_variable \"sysctl_sched_latency\"",
                 "kernel.sched_latency_ns")

    assert "sched_debug_header" in snap.fun_groups[
        "kernel.sched_latency_ns"].functions
    fun_desc = snap.fun_groups["kernel.sched_latency_ns"].functions[
        "sched_debug_header"]
    assert fun_desc.mod is mod
    assert fun_desc.glob_var == "sysctl_sched_latency"
    assert fun_desc.tag == "using_data_variable \"sysctl_sched_latency\""
    def get_module_from_source(self, source_path, created_before=None):
        """
        Create an LLVM module from a source file.
        Builds the source into LLVM IR if needed. No module is returned if the
        module is already present but its LLVM IR was generated or its source
        file modified after the given time constraint.
        :param source_path: Relative path to the file
        :param created_before: File creation time constraint.
        :returns Instance of LlvmKernelModule
        """
        name = source_path[:-2] if source_path.endswith(".c") else source_path
        llvm_file = os.path.join(self.kernel_dir, "{}.ll".format(name))
        source_file = os.path.join(self.kernel_dir, source_path)

        # If the LLVM IR file exits but was modified after the given timestamp,
        # do not return the module.
        if created_before:
            try:
                if (os.path.getmtime(source_file) > created_before
                        or os.path.getmtime(llvm_file) > created_before):
                    return None
            except OSError:
                pass

        # If the module has already been created, return it
        if name in self.modules:
            return self.modules[name]

        if self.builder:
            try:
                self.builder.build_source_to_llvm(source_file, llvm_file)
            except BuildException:
                pass

        if not os.path.isfile(llvm_file):
            return None

        mod = LlvmKernelModule(llvm_file, source_file)
        self.modules[name] = mod
        return mod
Beispiel #14
0
def compare(args):
    old_functions = FunctionList(args.snapshot_dir_old)
    with open(os.path.join(args.snapshot_dir_old, "functions.yaml"),
              "r") as fun_list_yaml:
        old_functions.from_yaml(fun_list_yaml.read())
    old_source = KernelSource(args.snapshot_dir_old)
    new_functions = FunctionList(args.snapshot_dir_new)
    with open(os.path.join(args.snapshot_dir_new, "functions.yaml"),
              "r") as fun_list_yaml:
        new_functions.from_yaml(fun_list_yaml.read())
    new_source = KernelSource(args.snapshot_dir_new)

    if args.function:
        old_functions.filter([args.function])
        new_functions.filter([args.function])

    config = Config(old_source, new_source, args.show_diff,
                    args.control_flow_only, args.verbose,
                    args.semdiff_tool)
    result = Result(Result.Kind.NONE, args.snapshot_dir_old,
                    args.snapshot_dir_old)

    for fun, old_mod in sorted(old_functions.functions.items()):
        new_mod = new_functions.get_by_name(fun)
        if not (old_mod.has_function(fun) and new_mod.has_function(fun)):
            continue

        fun_result = functions_diff(
            mod_first=old_mod, mod_second=new_mod,
            fun_first=fun, fun_second=fun,
            glob_var=None, config=config)

        if fun_result is not None:
            if args.regex_filter is not None:
                # Filter results by regex
                pattern = re.compile(args.regex_filter)
                for called_res in fun_result.inner.values():
                    if pattern.search(called_res.diff):
                        break
                else:
                    fun_result.kind = Result.Kind.EQUAL_SYNTAX

            result.add_inner(fun_result)
            if fun_result.kind in [Result.Kind.ERROR, Result.Kind.UNKNOWN]:
                print("{}: {}".format(fun, str(fun_result.kind)))
            elif fun_result.kind == Result.Kind.NOT_EQUAL:
                print_syntax_diff(args.snapshot_dir_old,
                                  args.snapshot_dir_new,
                                  fun, fun_result, False,
                                  args.show_diff)

        # Clean LLVM modules (allow GC to collect the occupied memory)
        old_mod.clean_module()
        new_mod.clean_module()
        LlvmKernelModule.clean_all()

    if args.report_stat:
        print("")
        print("Statistics")
        print("----------")
        result.report_stat(args.show_errors)
    return 0
Beispiel #15
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
Beispiel #16
0
def compare(args):
    """
    Compare snapshots of linux kernels. Runs the semantic comparison and shows
    information about the compared functions that are semantically different.
    """
    # Parse both the new and the old snapshot.
    old_snapshot = Snapshot.load_from_dir(args.snapshot_dir_old)
    new_snapshot = Snapshot.load_from_dir(args.snapshot_dir_new)

    # Set the output directory
    if not args.stdout:
        if args.output_dir:
            output_dir = args.output_dir
            if os.path.isdir(output_dir):
                sys.stderr.write("Error: output directory exists\n")
                sys.exit(errno.EEXIST)
        else:
            output_dir = default_output_dir(args.snapshot_dir_old,
                                            args.snapshot_dir_new)
    else:
        output_dir = None

    if args.function:
        old_snapshot.filter([args.function])
        new_snapshot.filter([args.function])

    config = Config(old_snapshot, new_snapshot, args.show_diff,
                    args.output_llvm_ir, args.control_flow_only,
                    args.print_asm_diffs, args.verbose, args.enable_simpll_ffi,
                    args.semdiff_tool)
    result = Result(Result.Kind.NONE,
                    args.snapshot_dir_old,
                    args.snapshot_dir_old,
                    start_time=default_timer())

    for group_name, group in sorted(old_snapshot.fun_groups.items()):
        group_printed = False

        # Set the group directory
        if output_dir is not None and group_name is not None:
            group_dir = os.path.join(output_dir, group_name)
        else:
            group_dir = None

        result_graph = None
        cache = SimpLLCache(mkdtemp())

        if args.enable_module_cache:
            module_cache = _generate_module_cache(group.functions.items(),
                                                  group_name, new_snapshot, 3)
        else:
            module_cache = None

        for fun, old_fun_desc in sorted(group.functions.items()):
            # Check if the function exists in the other snapshot
            new_fun_desc = new_snapshot.get_by_name(fun, group_name)
            if not new_fun_desc:
                continue

            # Check if the module exists in both snapshots
            if old_fun_desc.mod is None or new_fun_desc.mod is None:
                result.add_inner(Result(Result.Kind.UNKNOWN, fun, fun))
                if group_name is not None and not group_printed:
                    print("{}:".format(group_name))
                    group_printed = True
                print("{}: unknown".format(fun))
                continue

            # If function has a global variable, set it
            glob_var = KernelParam(old_fun_desc.glob_var) \
                if old_fun_desc.glob_var else None

            # Run the semantic diff
            fun_result = functions_diff(mod_first=old_fun_desc.mod,
                                        mod_second=new_fun_desc.mod,
                                        fun_first=fun,
                                        fun_second=fun,
                                        glob_var=glob_var,
                                        config=config,
                                        prev_result_graph=result_graph,
                                        function_cache=cache,
                                        module_cache=module_cache)
            result_graph = fun_result.graph

            if fun_result is not None:
                if args.regex_filter is not None:
                    # Filter results by regex
                    pattern = re.compile(args.regex_filter)
                    for called_res in fun_result.inner.values():
                        if pattern.search(called_res.diff):
                            break
                    else:
                        fun_result.kind = Result.Kind.EQUAL_SYNTAX

                result.add_inner(fun_result)

                # Printing information about failures and non-equal functions.
                if fun_result.kind in [
                        Result.Kind.NOT_EQUAL, Result.Kind.ERROR,
                        Result.Kind.UNKNOWN
                ]:
                    if fun_result.kind == Result.Kind.NOT_EQUAL:
                        # Create the output directory if needed
                        if output_dir is not None:
                            if not os.path.isdir(output_dir):
                                os.mkdir(output_dir)
                        # Create the group directory or print the group name
                        # if needed
                        if group_dir is not None:
                            if not os.path.isdir(group_dir):
                                os.mkdir(group_dir)
                        elif group_name is not None and not group_printed:
                            print("{}:".format(group_name))
                            group_printed = True
                        print_syntax_diff(
                            snapshot_dir_old=args.snapshot_dir_old,
                            snapshot_dir_new=args.snapshot_dir_new,
                            fun=fun,
                            fun_result=fun_result,
                            fun_tag=old_fun_desc.tag,
                            output_dir=group_dir if group_dir else output_dir,
                            show_diff=args.show_diff,
                            initial_indent=2 if (group_name is not None
                                                 and group_dir is None) else 0)
                    else:
                        # Print the group name if needed
                        if group_name is not None and not group_printed:
                            print("{}:".format(group_name))
                            group_printed = True
                        print("{}: {}".format(fun, str(fun_result.kind)))

            # Clean LLVM modules (allow GC to collect the occupied memory)
            old_fun_desc.mod.clean_module()
            new_fun_desc.mod.clean_module()
            LlvmKernelModule.clean_all()

    old_snapshot.finalize()
    new_snapshot.finalize()

    if output_dir is not None and os.path.isdir(output_dir):
        print("Differences stored in {}/".format(output_dir))

    if args.report_stat:
        print("")
        print("Statistics")
        print("----------")
        result.stop_time = default_timer()
        result.report_stat(args.show_errors)
    return 0
Beispiel #17
0
def simplify_modules_diff(first,
                          second,
                          fun_first,
                          fun_second,
                          var,
                          suffix=None,
                          control_flow_only=False,
                          verbose=False):
    """
    Simplify modules to ease their semantic difference. Uses the SimpLL tool.
    """
    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

    try:
        simpll_command = [
            "build/diffkemp/simpll/simpll", 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:
            simpll_command.extend(["--suffix", suffix])

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

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

        simpll_out = check_output(simpll_command)
        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)

        objects_to_compare = []
        missing_defs = None
        syndiff_defs = None
        try:
            simpll_result = yaml.safe_load(simpll_out)
            if simpll_result is not None:
                if "diff-functions" in simpll_result:
                    for fun_pair_yaml in simpll_result["diff-functions"]:
                        fun_pair = [
                            Result.Entity(
                                fun["function"],
                                fun["file"] if "file" in fun else "",
                                fun["line"] if "line" in fun else None,
                                "\n".join([
                                    "{} at {}:{}".format(
                                        call["function"], call["file"],
                                        call["line"])
                                    for call in fun["callstack"]
                                ]) if "callstack" in fun else "",
                                fun["is-syn-diff"], fun["covered-by-syn-diff"])
                            for fun in
                            [fun_pair_yaml["first"], fun_pair_yaml["second"]]
                        ]

                        objects_to_compare.append(tuple(fun_pair))
                missing_defs = simpll_result["missing-defs"] \
                    if "missing-defs" in simpll_result else None
                syndiff_defs = simpll_result["syndiff-defs"] \
                    if "syndiff-defs" in simpll_result else None
        except yaml.YAMLError:
            pass

        return first_out, second_out, objects_to_compare, missing_defs, \
            syndiff_defs
    except CalledProcessError:
        raise SimpLLException("Simplifying files failed")