Пример #1
0
    def __gen_ref_to_macro(self, locations):
        for exp_file, expansions in self.extensions["Macros"].yield_expansions(
        ):
            if exp_file == "unknown" or "expand" not in locations[exp_file]:
                continue

            ref_to = nested_dict()

            for macro, loc_list in traverse(locations[exp_file]["expand"], 2):
                for loc_el in loc_list:
                    exp_line = str(loc_el[0])

                    for def_file, def_line in traverse(
                            expansions[exp_file][macro][exp_line], 2):
                        if def_file == "unknown":
                            continue

                        val = (loc_el, (def_file, int(def_line)))

                        if ref_to[exp_file]["def_macro"]:
                            ref_to[exp_file]["def_macro"].append(val)
                        else:
                            ref_to[exp_file]["def_macro"] = [val]

            self.__dump_ref_to(ref_to)
Пример #2
0
    def __get_raw_func_locations(self, raw_locations):
        for file, func in traverse(self.funcs, 2):
            def_line = self.funcs[file][func]["line"]

            if def_line:
                val = (def_line, func, "def_func")

                if file in raw_locations:
                    raw_locations[file].append(val)
                else:
                    raw_locations[file] = [val]

            for decl_file in self.funcs[file][func]["declarations"]:
                decl_line = self.funcs[file][func]["declarations"][decl_file][
                    "line"]

                val = (decl_line, func, "decl_func")

                if decl_file in raw_locations:
                    raw_locations[decl_file].append(val)
                else:
                    raw_locations[decl_file] = [val]

        for context_file, callgraph in self.extensions[
                "Callgraph"].yield_callgraph():
            for _, _, file, func, line in traverse(callgraph[context_file], 5,
                                                   {2: "calls"}):
                val = (line, func, "call")

                if context_file in raw_locations:
                    raw_locations[context_file].append(val)
                else:
                    raw_locations[context_file] = [val]

        return raw_locations
Пример #3
0
    def __get_raw_macro_locations(self, raw_locations):
        for exp_file, expansions in self.extensions["Macros"].yield_expansions(
        ):
            for macro, exp_line in traverse(expansions[exp_file], 2):
                val = (exp_line, macro, "expand")

                if exp_file in raw_locations:
                    raw_locations[exp_file].append(val)
                else:
                    raw_locations[exp_file] = [val]

        # Load macros dictionary independetly for each file
        for def_file, macros in self.extensions["Macros"].yield_macros():
            for macro, def_line in traverse(macros[def_file], 2):
                if def_file == "unknown":
                    continue

                val = (def_line, macro, "def_macro")

                if def_file in raw_locations:
                    raw_locations[def_file].append(val)
                else:
                    raw_locations[def_file] = [val]

        return raw_locations
Пример #4
0
    def get_macros_definitions(self, files=None, macros_names=None):
        """Get dictionary with macros definitions (C only).

        DEPRECATED: use get_macros() instead.

        Args:
            files: A list of files to narrow down returned dictionary
            macros_names: A list of macros names to find and return
        """

        macros = self.Macros.load_macros(files)

        definitions = nested_dict()

        # Map new format of macros to the old one
        for file, macro, line in traverse(macros, 3):
            if definitions[file][macro]:
                definitions[file][macro].append(line)
            else:
                definitions[file][macro] = [line]

        # Filter macro definitions by names
        if macros_names:
            filtered_definitions = nested_dict()

            for file, macros in traverse(definitions, 2):
                if macros in macros_names:
                    filtered_definitions[file][macros] = definitions[file][macros]

            return filtered_definitions

        return definitions
Пример #5
0
    def get_macros_expansions(self, files=None, macros_names=None):
        """Get dictionary with macros expansions (C only).

        DEPRECATED: use get_expansions() instead.

        Args:
            files: A list of files to narrow down returned dictionary
            macros_names: A list of macros names to find and return
        """

        exps = self.Macros.load_expansions(files)
        expansions = nested_dict()

        # Map new format of macros to the old one
        for exp_file, macro, _, _, _, args in traverse(exps, 6):
            if expansions[exp_file][macro]["args"]:
                expansions[exp_file][macro]["args"].extend(args)
            else:
                expansions[exp_file][macro]["args"] = args

        if macros_names:
            filtered_expansions = nested_dict()

            for file, macros in traverse(expansions, 2):
                if macros in macros_names:
                    filtered_expansions[file][macros] = expansions[file][macros]

            return filtered_expansions

        return expansions
Пример #6
0
    def __gen_ref_from_macro(self, locations):
        for def_file, macros in self.extensions["Macros"].yield_macros():
            if def_file == "unknown" or "def_macro" not in locations[def_file]:
                continue

            ref_from = nested_dict()

            for macro, loc_list in traverse(locations[def_file]["def_macro"],
                                            2):
                for loc_el in loc_list:
                    def_line = str(loc_el[0])

                    for exp_file in macros[def_file][macro][def_line]:
                        exp_lines = [
                            int(l) for l in macros[def_file][macro][def_line]
                            [exp_file]
                        ]
                        val = (loc_el, (exp_file, exp_lines))

                        if ref_from[def_file]["expand"]:
                            ref_from[def_file]["expand"].append(val)
                        else:
                            ref_from[def_file]["expand"] = [val]

            self.__dump_ref_from(ref_from)
Пример #7
0
    def __gen_ref_to_func(self, locations):
        for context_file, callgraph in self.extensions[
                "Callgraph"].yield_callgraph():
            calls = set()

            for context_func, _, file in traverse(callgraph[context_file], 3,
                                                  {2: "calls"}):
                if file not in self.funcs:
                    self._error("Can't find file: {!r}".format(file))
                    continue

                for func in callgraph[context_file][context_func]["calls"][
                        file]:
                    if func not in locations[context_file]["call"]:
                        # Probably because of macro expansion
                        continue

                    if func not in self.funcs[file]:
                        self._error("Can't find function: {!r} {!r}".format(
                            func, file))
                        continue

                    calls.add((file, func))

            ref_to = nested_dict()

            for file, func in calls:
                loc_list = locations[context_file]["call"][func]

                if self.funcs[file][func]["line"]:
                    def_line = int(self.funcs[file][func]["line"])

                    for loc_el in loc_list:
                        val = (loc_el, (file, def_line))

                        if ref_to[context_file]["def_func"]:
                            ref_to[context_file]["def_func"].append(val)
                        else:
                            ref_to[context_file]["def_func"] = [val]

                for decl_file in self.funcs[file][func]["declarations"]:
                    decl_line = int(self.funcs[file][func]["declarations"]
                                    [decl_file]["line"])

                    for loc_el in loc_list:
                        val = (loc_el, (decl_file, decl_line))

                        if ref_to[context_file]["decl_func"]:
                            ref_to[context_file]["decl_func"].append(val)
                        else:
                            ref_to[context_file]["decl_func"] = [val]

            self.__dump_ref_to(ref_to)
Пример #8
0
    def __get_context_locs(self, file, func, callgraph):
        locs = []

        if func not in callgraph[file] or "called_in" not in callgraph[file][
                func]:
            return locs

        called_in = callgraph[file][func]["called_in"]

        for context_file in called_in:
            lines = []

            for _, line in traverse(called_in[context_file], 2):
                lines.append(int(line))

            locs.append((context_file, lines))

        return locs
Пример #9
0
    def __process_calls(self):
        is_bad = re.compile(r'__bad')

        for context_file, context_func, func, call_line, call_type, args in self.extensions[
                "Info"].iter_calls():
            # args are excluded from the debug log
            self.debug("Processing function calls: " + " ".join(
                [context_file, context_func, func, call_line, call_type]))

            if self.is_builtin.match(func) or (is_bad.match(func)
                                               and func not in self.callgraph):
                self.debug("Function {} is bad".format(func))
                continue

            # For each function call there can be many definitions with the same name, defined in different
            # files. Possible_files is a list of them.
            if func in self.funcs:
                possible_files = tuple(
                    f for f in self.funcs[func]
                    if f != "unknown" and self.funcs[func][f]["type"] in (
                        call_type, "exported"))
            else:
                self._error("Can't find '{}' in Functions".format(func))
                possible_files = []

            # Assign priority number for each possible definition. Examples:
            # 5 means that definition is located in the same file as the call
            # 4 - in the same translation unit
            # 3 - reserved for exported functions (Linux kernel only)
            # 2 - in the object file that is linked with the object file that contains the call
            # 1 - TODO: investigate this case
            # 0 - definition is not found
            index = 5
            for files in ((f for f in possible_files if f == context_file),
                          (f for f in possible_files
                           if self._t_unit_is_common(f, context_file)),
                          (f for f in possible_files
                           if self.funcs[func][f]["type"] == "exported")
                          if call_type == "extern" else tuple(),
                          (f for f in possible_files
                           if self._files_are_linked(f, context_file) and any(
                               self._t_unit_is_common(cf, context_file)
                               for cf in self.funcs[func][f]["declarations"]))
                          if call_type == "extern" else tuple(),
                          (f for f in possible_files if any(
                              self._t_unit_is_common(cf, context_file)
                              for cf in self.funcs[func][f]["declarations"]))
                          if call_type == "extern" else tuple(), ['unknown']):
                matched_files = tuple(files)
                if matched_files:
                    break
                index -= 1

            if len(matched_files) > 1:
                self._error("Multiple matches: {} {}".format(
                    func, context_func))

            for possible_file in matched_files:
                call_val = {
                    'match_type': index,
                }

                if args:
                    call_val["args"] = args

                self.debug(
                    "Fuction {} from {} is called in {}:{} in {}".format(
                        func, possible_file, context_file, call_line,
                        context_func))

                self.callgraph[possible_file][func]['called_in'][context_file][
                    context_func][call_line] = call_val

                # Create reversed callgraph
                self.callgraph[context_file][context_func]["calls"][
                    possible_file][func][call_line] = call_val

                if possible_file == "unknown":
                    self._error("Can't match definition: {} {}".format(
                        func, context_file))

        # Keep function types in the callgraph
        for path, func in traverse(self.callgraph, 2):
            if path == "unknown" and (func not in self.funcs
                                      or not self.funcs[func].get(path)):
                continue

            self.callgraph[path][func]["type"] = self.funcs[func][path]["type"]
Пример #10
0
 def __reverse_expansions(self):
     for def_file, macros in self.yield_macros():
         for macro, def_line, exp_file, exp_line, args in traverse(macros[def_file], 5):
             self.exps[exp_file][macro][exp_line][def_file][def_line] = args
Пример #11
0
 def __group_functions_by_file(self):
     for func, file in traverse(self.funcs, 2):
         self.funcs_by_file[file][func] = self.funcs[func][file]