def do(self, g: graph.PyGraph, nodes=None, entry=None): if nodes is not None: self.nodes = nodes else: print("no abbs were commited to the dominance tree analysis") start_nodes, dom = self.find_dominators() #check if the entry argument is a real start abb check = None for start_node in start_nodes: if start_node.get_seed() == entry.get_seed(): check = start_node assert check != None self.immdom_tree = dict() for x in start_nodes: if x.get_seed() == check.get_seed(): self.immdom_tree[x.get_seed()] = None else: self.immdom_tree[x.get_seed()] = check self.immdom_tree_keys = [] for start_node in start_nodes: #print("start_node:",start_node.get_name()) self.immdom_tree[start_node.get_seed()] = None for abb_seed in dom: continue_flag = False abb = g.get_vertex(abb_seed) for tmp_abb in start_nodes: if abb.get_seed() == tmp_abb.get_seed(): continue_flag = True #print("abb node:",abb.get_name()) if continue_flag == True: #print("continued start node") continue visited = set() dominators = [] for element in dom[abb.get_seed()]: if element.get_seed() != abb.get_seed(): #print("dominator node:",element.get_name()) dominators.append(element) #print(dom[abb.get_seed()]) imdom = self.find_imdom(abb, dominators, visited, abb) #print("indom name:" , imdom.get_name()) assert abb.get_seed() != imdom.get_seed() and imdom != None self.immdom_tree[abb.get_seed()] = imdom self.immdom_tree_keys.append(abb.get_name())
def merge_loops(self,g: graph.PyGraph): anyChanges = True while anyChanges: anyChanges = False for abb in g.get_type_vertices("ABB"): mc = self.find_loops_to_merge(abb) if mc and self.can_be_merged(mc.entry_abb, mc.exit_abb, mc.inner_abbs): self.do_merge(g,mc.entry_abb, mc.exit_abb, mc.inner_abbs) anyChanges = True
def run(self, g: graph.PyGraph): # load the json outputstructure with json self._log.info(f"Reading oil file {self._config['oilfile']}") with open(self._config['oilfile']) as f: oil = json.load(f) assert("cpu" in oil) oil = oil["cpu"] # TODO validate json # prepare graph functions: funcs = g.get_type_vertices("Function") self.functions = dict([(x.get_name(), x) for x in funcs]) for f in self.functions: print(f, self.functions[f]) counter_m = {'maxallowedvalue': graph.Counter.set_max_allowed_value, 'ticksperbase': graph.Counter.set_ticks_per_base, 'mincycle': graph.Counter.set_min_cycle} task_m = {'priority': graph.Task.set_priority, 'autostart': graph.Task.set_autostart, 'schedule': graph.Task.set_scheduler, 'activation': graph.Task.set_activation} event_m = {'mask': OilStep.event_set_mask} resources_m = {'type': OilStep.resource_type} alarm_m = {'action': OilStep.alarm_action} isr_m = {'category': graph.ISR.set_category, 'priority': graph.ISR.set_priority} fill_graph = partial(self.fill_graph, g, oil) # fill the graph, according to the OIL specification some dependencies # must be met: # FOO -> BAR: FOO depends on BAR # alarm -> counter # alarm -> task # ISR -> resource fill_graph('events', graph.Event, attrs=event_m, inits=[self.event_init]) fill_graph('counters', graph.Counter, attrs=counter_m) fill_graph('tasks', graph.Task, attrs=task_m, inits=[self.task_init]) fill_graph('alarms', graph.Timer, attrs=alarm_m, inits=[self.alarm_init]) fill_graph('resources', graph.Mutex, attrs=resources_m, inits=[OilStep.resource_init]) fill_graph('isrs', graph.ISR, attrs=isr_m, inits=[self.isr_init])
def validate_osek_task_termination(g: graph.PyGraph): task_list = g.get_type_vertices("Task") #iterate about the tasks for task in task_list: #get last outgoing syscall edges = task.get_outgoing_edges() if not edges: continue last_syscall = edges[-1] #get abb reference from syscall abb_reference = last_syscall.get_abb_reference() if abb_reference is not None: #check if last outgoing syscall is a termination or chain task syscall syscall_type = abb_reference.get_syscall_type() if syscall_type is not graph.syscall_definition_type.destroy and syscall_type is not graph.syscall_definition_type.chain: print( "Warning: No termination or chain as last syscall in task", task.get_name())
def merge_branches(self,g: graph.PyGraph): """ Try to merge if - else branches with the following pattern: O O / \ |\ O O |O \/ |/ O O """ anyChanges = True while anyChanges: anyChanges = False for abb in g.get_type_vertices("ABB"): mc = self.find_branches_to_merge(abb) #if mc and mc.entry_abb and mc.exit_abb: # print("branch entry", mc.entry_abb.get_name(), "branch exit",mc.exit_abb.get_name()) if mc and self.can_be_merged(mc.entry_abb, mc.exit_abb, mc.inner_abbs): # print("can be merged") self.do_merge(g,mc.entry_abb, mc.exit_abb, mc.inner_abbs) anyChanges = True
def merge_linear_abbs(self,g: graph.PyGraph): anyChanges = True while anyChanges: anyChanges = False # copy original dict abb_list = g.get_type_vertices("ABB") for abb in abb_list: # Iterate over list of abbs dict KeysView #if abb.has_single_successor(E.function_level): # successor = abb.definite_after(E.function_level) #print("abb",abb.get_name()) for successor in abb.get_successors(): #print(successor.get_name()) #print("successor",successor.get_name()) #print("check merge",print(type(abb)),print(type(successor))) if successor and self.can_be_merged(abb, successor): self.do_merge(g,abb, successor) anyChanges = True
def run(self, g: graph.PyGraph): print("Run abb merge") function_list = g.get_type_vertices("Function") initial_abb_list = g.get_type_vertices("ABB") #iterate about the functions for function in function_list: if function.get_has_syscall() == False: #iterate about the abbs of the function already_visited = [] function_list = [] #DFS for function function_list.append(function) #do DFS until the function self and all called functions are analyzed while len(function_list) > 0: tmp_function = function_list[-1] del function_list[-1] success = False #check if the tmp function was not visited yet if not tmp_function.get_seed() in already_visited: already_visited.append(tmp_function.get_seed()) abb_list = tmp_function.get_atomic_basic_blocks() #iterate about the abbs for abb in abb_list: #check if abb has function call if abb.get_call_type() == graph.call_definition_type.func_call: #append function in functions to analyze list called_functions = abb.get_called_functions() for called_function in called_functions: function_list.append(called_function) elif abb.get_call_type() == graph.call_definition_type.sys_call: #syscall was detected-> set has syscall and break search for this function function.set_has_syscall(True) success = True break if success == True: #syscall was detected, so continue with next function break printer = DotFileParser(g) printer.print_bb_functions(g,"bbs_before_merge") printer.print_functions(g,"before_merge") current_size = None initial_abb_count = len(initial_abb_list) print(initial_abb_count) #merge dominance regions self.merge_dominance(g) initial_abb_list = g.get_type_vertices("ABB") initial_abb_count = len(initial_abb_list) print(initial_abb_count) printer.print_functions(g,"after_dominance_merge") while current_size != initial_abb_count: tmp_abb_list = g.get_type_vertices("ABB") initial_abb_count = len(tmp_abb_list) # linear merging: self.merge_linear_abbs(g) # try to merge if-else branches self.merge_branches(g) # merge loop regions self.merge_loops(g) tmp_abb_list = g.get_type_vertices("ABB") current_size = len(tmp_abb_list) print(len(g.get_type_vertices("ABB"))) printer.print_functions(g,"after_merge")
def merge_dominance(self,g: graph.PyGraph): #for func in g.get_type_vertices("Function"): ## Filter some functions #function_abbs = func.get_atomic_basic_blocks() ##TODO ##if func.get_has_syscall() == False: ## continue #if len(function_abbs) <= 3 or func.get_exit_abb() == None: #continue ## Forward analysis ##print("dom" , func.get_entry_abb()) #dom = DominanceAnalysis(forward = True) #dom.do(g,nodes=function_abbs,entry =func.get_entry_abb()) ## Backward analysis ##print("post_dom" , func.get_exit_abb()) #post_dom = DominanceAnalysis(forward = False) #post_dom.do(g,nodes=function_abbs,entry =func.get_exit_abb()) #removed = set() ##print("HELLO") ##print(dom.immdom_tree) ##print(post_dom.immdom_tree) #for abb in function_abbs: #if abb.get_seed() in removed: #continue ##if func.get_entry_abb().get_seed == abb.get_seed(): ## continue ##print( dom.immdom_tree) ##start = dom.immdom_tree[abb.get_seed()] ##end = post_dom.immdom_tree[abb.get_seed()] #start = abb.get_dominator(); #end = abb.get_postdominator(); ##print("abb", abb.get_name()) ##if not start: ##print("no dominator") ##else: ##print("dominator", start.get_name()) ##if not end: ##print("no postdominator") ##else: ##print("postdominator", end.get_name()) #if start and end and start != end: #region = self.find_region(start, end) #inner = set() #for element in region: #if element.get_seed() != start.get_seed() and element.get_seed() != end.get_seed(): #inner.add(element) ## Was there already some subset removed? #if start.get_seed() in removed or end.get_seed() in removed: #continue #tmp_inner = inner #inner = set() #for inner_element in tmp_inner: ##print("tmp inner" , inner_element.get_name()) #if not inner_element.get_seed() in removed: #inner.add(inner_element) ##print("start",start.get_name(),"end", end.get_name()) ##for element in inner: ##print("inner",element.get_name()) #if self.can_be_merged(start, end, inner): #self.do_merge(g,start, end, inner) ## Mark as removed #removed.add(end.get_seed()) ##print(end.get_name()) ##print(start.get_name()) #for element in inner: ##print(element.get_name()) #removed.add(element.get_seed()) ##self.merge_stats.after_dominance_merge = len(self.system_graph.abbs) #for abb in function_abbs: #start = abb.get_dominator(); #end = abb.get_postdominator(); #inner = set() #if start and end and start != end: #region = self.find_region(start, end) #for element in region: #if element.get_seed() != start.get_seed() and element.get_seed() != end.get_seed(): #inner.add(element) #elif start and not end: #end = abb #elif end and not start: #start = abb #if start and end and start != end: #if self.can_be_merged(start, end, inner): #self.do_merge(g,start, end, inner) #changes = True for func in g.get_type_vertices("Function"): # Filter some functions function_abbs = func.get_atomic_basic_blocks() #TODO #if func.get_has_syscall() == False: # continue if func.get_exit_abb() == None: continue # Forward analysis for abb in function_abbs: start = abb.get_dominator(); end = abb.get_postdominator(); inner = set() if start and end and start != end: region = self.find_region(start, end) for element in region: if element.get_seed() != start.get_seed() and element.get_seed() != end.get_seed(): inner.add(element) elif start and not end: end = abb elif end and not start: start = abb if start and end and start != end: if self.can_be_merged(start, end, inner): self.do_merge(g,start, end, inner) changes = True
def do_merge( self,g: graph.PyGraph,entry_abb, exit_abb, inner_abbs = set()): #print("merge entry abb" , entry_abb.get_name()) #print("merge exit abb" , exit_abb.get_name()) #entry_abb.print_information() #exit_abb.print_information() #if entry_abb.get_name() == "BB351": #print('Trying to merge:') #print("entry" ,entry_abb.get_name()) #for inner_abb in inner_abbs: #print("inner",inner_abb.get_name()) #print("exit",exit_abb.get_name()) assert not entry_abb.get_seed() == exit_abb.get_seed(), 'Entry ABB cannot merge itself into itself' #assert not entry_abb in inner_abbs #assert not entry_abb.relevant_callees and not exit_abb.relevant_callees, 'Mergeable ABBs may not call relevant functions' parent_function = entry_abb.get_parent_function() # adopt basic blocks and call sites for abb in (inner_abbs | {exit_abb}) - {entry_abb}: if abb.get_seed() != entry_abb.get_seed(): if entry_abb.append_basic_blocks(abb) == False: print("ERROR: abb to merge not in graph") # Collect all call sites entry_abb.expend_call_sites(abb) # We merge everything into the entry block of a region. # Therefore, we just update the exit node of the entry to # preserve a correct entry/exit region entry_abb.adapt_exit_bb(exit_abb) # adopt outgoing edges for successor in exit_abb.get_successors(): exit_abb.remove_successor(successor) seed = successor.get_seed() if not seed == entry_abb.get_seed(): # omit self loop entry_abb.set_successor(successor) successor.set_predecessor(entry_abb) successor.remove_predecessor(exit_abb) # Remove edges between entry and inner_abbs/exit for abb in inner_abbs | {entry_abb}: for successor in abb.get_successors(): seed = successor.get_seed() for element in inner_abbs | {exit_abb}: if element.get_seed() == seed: abb.remove_successor(successor) successor.remove_predecessor(abb) # remove conflict when entry abb has the exit abb as predecessor for predecessor in entry_abb.get_predecessors(): seed = predecessor.get_seed() if exit_abb.get_seed() == seed: entry_abb.remove_predecessor(exit_abb) for abb in (inner_abbs | {exit_abb}): # Adapt exit ABB in corresponding function function_exit_abb = parent_function.get_exit_abb() if function_exit_abb != None and function_exit_abb.get_seed() == abb.get_seed(): parent_function.set_exit_abb(entry_abb) # Remove merged successors from any existing list for abb in (inner_abbs | {exit_abb}) - {entry_abb}: parent_function.remove_abb(abb.get_seed()) #if not parent_function.remove_abb(abb.get_seed()): # print("Probem") #sys.exit("abb could not removed from function"+abb.get_name().decode("utf-8") ) g.remove_vertex(abb.get_seed())
def validate_osek_syscalls_in_different_abstractions(g: graph.PyGraph): isr_list = g.get_type_vertices("ISR") #iterate about the isrs for isr in isr_list: #different syscalls allwoend in isrs from different type if isr.get_category() == 1: #list of all valid syscalls valid_calls = [(graph.get_type_hash("RTOS"), graph.syscall_definition_type.enable), (graph.get_type_hash("RTOS"), graph.syscall_definition_type.disable), (graph.get_type_hash("RTOS"), graph.syscall_definition_type.suspend), (graph.get_type_hash("RTOS"), graph.syscall_definition_type.resume)] validate_syscalls(valid_calls, isr, False) elif isr.get_category() == 2: #list of all invalid syscalls valid_calls = [(graph.get_type_hash("RTOS"), graph.syscall_definition_type.start_scheduler), (graph.get_type_hash("Event"), graph.syscall_definition_type.receive), (graph.get_type_hash("Event"), graph.syscall_definition_type.destroy), (graph.get_type_hash("Task"), graph.syscall_definition_type.chain)( graph.get_type_hash("Task"), graph.syscall_definition_type.destroy), (graph.get_type_hash("RTOS"), graph.syscall_definition_type.schedule)] validate_syscalls(valid_calls, isr, True) task_list = g.get_type_vertices("Task") #iterate about the tasks for task in task_list: #list of all invalid syscalls valid_calls = [ (graph.get_type_hash("RTOS"), graph.syscall_definition_type.start_scheduler), ] validate_syscalls(valid_calls, task, True) hook_list = g.get_type_vertices("Hook") #iterate about the hooks for hook in hook_list: if hook.get_hook_type() != graph.hook_type.no_hook: #list of all valid syscalls valid_calls = [(graph.get_type_hash("RTOS"), graph.syscall_definition_type.start_scheduler), (graph.get_type_hash("Event"), graph.syscall_definition_type.receive), (graph.get_type_hash("Alarm"), graph.syscall_definition_type.receive)( graph.get_type_hash("RTOS"), graph.syscall_definition_type.suspend), (graph.get_type_hash("RTOS"), graph.syscall_definition_type.receive)] validate_syscalls(valid_calls, hook, False)
def run(self, g: graph.PyGraph): print("I'm an SyscallStep") #get information which os is used os = self._config["os"] if os == "osek": g.set_os_type(graph.os_type.OSEK) elif os == "freertos": g.set_os_type(graph.os_type.FreeRTOS) self.select_syscalls(os) #iterate about the functions of the graph function_list = g.get_type_vertices("Function") for function in function_list: #iterate about the abbs of the functions abb_list = function.get_atomic_basic_blocks() #iterate about the abbs of the function for abb in abb_list: #check if abb has a call if abb.get_call_type() == graph.call_definition_type.has_call: #get call name call_name = abb.get_call_name() #check if call is a function call or a sys call syscall = self.syscall_dict.get(call_name.decode('ascii'), "error") if syscall != "error": assert abb.convert_call_to_syscall(call_name) == True, "could not convert call to syscall" function.set_has_syscall(True) expected_argument_types = graph.cast_expected_syscall_argument_types(syscall[0]) #different_calles_argument_types = abb.get_call_argument_types() #assert len(different_calles_argument_types) <= 1, "more than one call in initial atomic basic block" #specific_call_argument_types = different_calles_argument_types[0] specific_call_argument_types = abb.get_call_argument_types() success = True counter = 0 #verify the typeid_hash_values of the syscall arguments if len(expected_argument_types) != len(specific_call_argument_types): success = False else: #iterate about the expected call types list for expected_type in expected_argument_types: #get argument types for this argument argument_types = specific_call_argument_types[counter] #iterate about the types for argument_type in argument_types: tmp_success = False #check if type is in expected types if isinstance(expected_type, Iterable): for tmp_expected_type in expected_type: #check if argument type is equal to expected type if tmp_expected_type == argument_type: tmp_success = True break else: if expected_type == argument_type: tmp_success = True success = tmp_success if success == False: break if success == False: break counter+=1 #check if lists dont match if success == False: print("TODO",call_name,counter) #sys.exit("unexpected argument type") #abb.print_information(); abb.set_call_type(graph.call_definition_type.sys_call) abb.set_syscall_type(syscall[1]) abb.set_call_target_instance(syscall[2]) abb.set_handler_argument_index(syscall[3]) #no syscall else: #set type to func call abb.set_call_type(graph.call_definition_type.func_call)