def create_control_flow_graphs(): for function_name, edge_list in edges_in_control_flow_graphs.items(): control_flow_graph = directed_graphs.ControlFlowGraph(function_name) # Add vertices to control flow graph representing basic blocks for an_edge in edge_list: pred_id = parse_int(an_edge[0]) succ_id = parse_int(an_edge[1]) if not control_flow_graph.has_vertex(pred_id): control_flow_graph.add_vertex(ProgramPointVertex(pred_id, pred_id)) if not control_flow_graph.has_vertex(succ_id): control_flow_graph.add_vertex(ProgramPointVertex(succ_id, succ_id)) # Add vertices to control flow graph representing transitions and # then link these to basic blocks for an_edge in edge_list: pred_id = parse_int(an_edge[0]) succ_id = parse_int(an_edge[1]) vertex = ProgramPointVertex(control_flow_graph.get_new_vertex_id(), (pred_id, succ_id)) control_flow_graph.add_vertex(vertex) control_flow_graph.add_edge(control_flow_graph.get_vertex(pred_id), vertex) control_flow_graph.add_edge(vertex, control_flow_graph.get_vertex(succ_id)) # Find entry and exit vertex, then add vertex representing an edge # from the exit vertex to the entry vertex control_flow_graph.set_entry_vertex() control_flow_graph.set_exit_vertex() control_flow_graph.add_exit_to_entry_edge() program[function_name] = control_flow_graph dot.make_file(control_flow_graph)
def create_call_graph(): for call_site_id, caller, callee in edges_in_call_graph: if program.call_graph.has_vertex_with_name(caller) and program.call_graph.has_vertex_with_name(callee): pred_call_vertex = program.call_graph.get_vertex_with_name(caller) succ_call_vertex = program.call_graph.get_vertex_with_name(callee) program.call_graph.add_edge(pred_call_vertex, succ_call_vertex, parse_int(call_site_id)) dot.make_file(program.call_graph)
def generate_program(): program = Program() for function_id in range(1, globals.args["subprograms"] + 1): function_name = "f{}".format(function_id) control_flow_graph = directed_graphs.create_control_flow_graph(function_name) program[function_name] = control_flow_graph dot.make_file(control_flow_graph) # For each control flow graph, work out which basic blocks can legitimately # make calls. call_site_candidates = {} for control_flow_graph in program: depth_first_search = directed_graphs.DepthFirstSearch( control_flow_graph, control_flow_graph.entry_vertex, False ) candidates_for_this_function = [] for vertex in control_flow_graph: if vertex.number_of_successors() == 1: succ_vertex = control_flow_graph.get_vertex(vertex.get_ith_successor_edge(0).vertex_id) # Check that the sole successor is not a loop header. if (vertex, succ_vertex) not in depth_first_search.backedges: candidates_for_this_function.append(vertex) call_site_candidates[control_flow_graph.name] = candidates_for_this_function # Sort the functions by the number of call sites. function_ordering = collections.OrderedDict( sorted(call_site_candidates.items(), key=lambda tup: len(tup[1]), reverse=True) ) # Assign each function a level in the call graph. levels_in_call_graph = {} level = 0 for function_name in function_ordering.keys(): levels_in_call_graph.setdefault(level, []).append(function_name) if level == 0: level += 1 elif bool(random.getrandbits(1)) and len(call_site_candidates[function_name]) > 0: level += 1 # Add acyclic call graph edges by finding a caller that is at a lower level # than the callee. for level in sorted(levels_in_call_graph.keys(), reverse=True): if level > 0: for callee in levels_in_call_graph[level]: if random.random() < 0.2: level_lower_than_current = random.randint(0, level - 1) else: level_lower_than_current = level - 1 candidate_callers = levels_in_call_graph[level_lower_than_current] caller = candidate_callers[random.randint(0, len(candidate_callers) - 1)] call_site_index = random.randint(0, len(call_site_candidates[caller]) - 1) call_site_vertex = call_site_candidates[caller][call_site_index] call_site_candidates[caller].remove(call_site_vertex) program.call_graph.add_edge( program.call_graph.get_vertex_with_name(caller), program.call_graph.get_vertex_with_name(callee), call_site_vertex.vertex_id, ) if len(call_site_candidates[caller]) == 0: candidate_callers.remove(caller) # Now that we have tried our best so that there is a path from a designated # root vertex to every function, add acyclic edges indiscriminately. for level in sorted(levels_in_call_graph.keys(), reverse=True): if level > 0: for callee in levels_in_call_graph[level]: if bool(random.getrandbits(1)): while True: level_lower_than_current = random.randint(0, level - 1) candidate_callers = levels_in_call_graph[level_lower_than_current] if candidate_callers: caller = candidate_callers[random.randint(0, len(candidate_callers) - 1)] call_site_index = random.randint(0, len(call_site_candidates[caller]) - 1) call_site_vertex = call_site_candidates[caller][call_site_index] call_site_candidates[caller].remove(call_site_vertex) program.call_graph.add_edge( program.call_graph.get_vertex_with_name(caller), program.call_graph.get_vertex_with_name(callee), call_site_vertex.vertex_id, ) if len(call_site_candidates[caller]) == 0: candidate_callers.remove(caller) else: break dot.make_file(program.call_graph) return program