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)
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
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
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
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
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)
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)
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
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"]
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
def __group_functions_by_file(self): for func, file in traverse(self.funcs, 2): self.funcs_by_file[file][func] = self.funcs[func][file]