def fitness_function(chromosome): try: if fitness_function.vectorProperties.base_type == TestVectorProperties.BaseType[2]: # Sometimes this conversion fails and I don't see why? # Just catch it and move on chromosome.genomeList = [chr(val) for val in chromosome.genomeList] except TypeError: pass fitness_function.run += 1 traceFile = "%s.%s.%d" % (os.path.basename(fitness_function.binary), "trace", fitness_function.run) cmd = '%s --debug-flags=Fetch --trace-file=%s %s --cpu-type=timing -c %s -o "%s"' % \ (config.Arguments.gem5_simulator, traceFile, config.Arguments.gem5_config, fitness_function.binary, ' '.join(map(str, chromosome.genomeList))) debug.debug_message("Running '%s' on gem5" % cmd, 1) proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode = proc.wait() if returncode: debug.exit_message("Running '%s' failed" % cmd) gem5_trace = os.path.abspath(os.getcwd()) + os.sep + config.Arguments.m5_trace_directory + os.sep + traceFile assert os.path.exists(gem5_trace), "Expected to find gem5 trace in '%s' but it is not there" % gem5_trace firstLines = os.popen("head -1 %s" % gem5_trace).readlines() lastLines = os.popen("tail -1 %s" % gem5_trace).readlines() assert len(firstLines) == 1 assert len(lastLines) == 1 firstLine = firstLines[0] lastLine = lastLines[0] time1 = shlex.split(firstLine)[0] time2 = shlex.split(lastLine)[0] time1 = time1[:-1] time2 = time2[:-1] score = int(time2) - int(time1) debug.debug_message("Score = %d" % score, 1) fitness_function.gem5traces.append(compress_trace(gem5_trace)) return score
def solve(self): with open(self._filename, 'w') as clpFile: for line in self._lines: clpFile.write(line) debug.debug_message("Solving CLP in %s" % self._filename, 10) command = 'jeclipse -b %s -e "%s."' % (self._filename, self.__goal) debug.verbose_message("Running command '%s'" % command, __name__) start = timeit.default_timer() proc = subprocess.Popen(command, shell=True, executable="/bin/bash") returnCode = proc.wait() self._solvingTime = (timeit.default_timer() - start) if returnCode != 0: debug.warning_message("Running '%s' failed" % command) return self._wcet, self._solvingTime else: with open(self._filename + '.res') as f: for line in f: if re.match(r'%s' % CLP.WCET, line): values = re.findall(r'[0-9]+', line) assert len( values ) == 1, "Unable to find WCET value in CLP output file" self._wcet = int(values[0]) assert self._wcet assert self._solvingTime return self._wcet, self._solvingTime
def generate_a_trace(self): if config.Arguments.add_timestamps: current_time = random.randint(1, 100) root_callv = self.program.callg.getVertex(self.program.callg.rootID) current_callv = root_callv current_CFG = self.program.cfgs[root_callv.name] currentv = current_CFG.getVertex(current_CFG.get_entryID()) call_stack = [] while True: if not config.Arguments.add_timestamps: self.the_file.write("(%d) " % currentv.vertexID) else: self.the_file.write("(%d, %d) " % (currentv.vertexID, current_time)) current_time += random.randint(1, 100) if currentv.vertexID == current_CFG.get_exitID(): if current_callv == root_callv: # End of the program reached break else: # End of function call debug.debug_message("Returning from %s" % current_callv.name, __name__, 10) current_callv, current_CFG, currentv = call_stack.pop() # Go past the call site currentv = self.choose_intra_procedural_successor(current_CFG, currentv) elif current_CFG.is_call_site(currentv.vertexID): call_stack.append((current_callv, current_CFG, currentv)) succID = current_callv.get_successor_with_call_site(currentv.vertexID) current_callv = self.program.callg.getVertex(succID) debug.debug_message("Calling %s" % current_callv.name, __name__, 10) current_CFG = self.program.cfgs[current_callv.name] currentv = current_CFG.getVertex(current_CFG.get_entryID()) else: currentv = self.choose_intra_procedural_successor(current_CFG, currentv)
def __parseAddress (self, time, address, runID): if address == self.__lastAddr: self.__analyseWCETOfBasicBlock(time - self.__time1) if not self.__currentBB.hasAddress(address): # Instructions in the current basic block have finished. Analyse its execution time self.__analyseWCETOfBasicBlock(time - self.__time1) # Move the time marker forward to the current time to reflect that we are transitioning # to a new basic block self.__time1 = time # Make the switch to the next basic block if self.__currentCFG.isCallSite(self.__currentBB.vertexID): self.__handleCall(address) elif self.__currentCFG.getExitID() == self.__currentBB.vertexID: self.__handleReturn() # Since we have switched basic blocks in the current CFG, analyse the super blocks self._analyseCFGEdge(self.__currentPathg, self.__currentLNT, self.__predBB.vertexID, self.__currentBB.vertexID) else: for succID in self.__currentBB.getSuccessorIDs(): succv = self.__currentCFG.getVertex(succID) if succv.hasAddress(address): self.__predBB = self.__currentBB self.__currentBB = succv break # Since we have switched basic blocks in the current CFG, analyse the super blocks self._analyseCFGEdge(self.__currentPathg, self.__currentLNT, self.__predBB.vertexID, self.__currentBB.vertexID) debug.debug_message("Now in CFG '%s' at basic block %d" % (self.__currentCFG.getName(), self.__currentBB.vertexID), 10) self._analyseCFGVertex(self.__currentPathg, self.__currentLNT, self.__currentBB.vertexID)
def construct_loop_info(self, enhanced_icfg, lnt, ipg): for v in ipg: for succID in v.successors.keys(): succe = v.get_successor_edge(succID) for program_point in succe.edge_label: if isinstance(program_point, vertices.CFGEdge): the_edge = program_point.edge if lnt.is_loop_back_edge(the_edge[0], the_edge[1]): self.loop_back_edges[the_edge[1]].add( (v.vertexID, succID)) for treev in self.enhanced_lnt: if isinstance(treev, vertices.HeaderVertex): debug.debug_message("Analysing header %d" % treev.headerID, __name__, 1) enhanced_icfg_of_loop = self.enhanced_lnt.induce_subgraph( treev) self.enhanced_icfgs_per_loop[ treev.headerID] = enhanced_icfg_of_loop self.loop_exit_regions[ treev. headerID] = self.compute_reachable_program_points_from_loop_exits( treev) udraw.make_file( enhanced_icfg_of_loop, "%s.header_%d.%s" % (enhanced_icfg.name, treev.headerID, "icfg")) self.ipgs_per_loop[treev.headerID] = ipgs.IPGLoopInformation( self, treev.headerID, enhanced_icfg_of_loop, self.lnt, self.enhanced_lnt, ipg)
def __initialise(self): self.__currentContextv = None self.__currentCFG = None self.__currentLNT = None self.__predBB = None self.__currentBB = None self.__currentHeaderID = None self.__currentPathg = None self.__time1 = None self.__stack = [] self.__contextg = self._program.getContextGraph() rootv = self.__contextg.getVertex(self.__contextg.getRootID()) self.__rootCFG = self._program.getCFG(rootv.getName()) self.__firstAddr = self.__rootCFG.getFirstInstruction().getAddress() lastbb = self.__rootCFG.getVertex(self.__rootCFG.getExitID()) for instruction in reversed(lastbb.getInstructions()): if instruction.getOp() not in arm.armInstructionSet.Nops: self.__lastAddr = instruction.getAddress() break assert self.__lastAddr, "Unable to find last address" debug.debug_message( "Start address of root function '%s' is %s" % (rootv.getName(), hex(self.__firstAddr)), 1) debug.debug_message( "End address of root function '%s' is %s" % (rootv.getName(), hex(self.__lastAddr)), 1)
def solve(self): assert self._filename, "ILP filename has not been set" debug.debug_message("Solving ILP for %s" % self._filename, 10) command = "lp_solve %s -S1 -time" % self._filename start = timeit.default_timer() proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, executable="/bin/bash") returnCode = proc.wait() self._solvingTime = (timeit.default_timer() - start) if returnCode != 0: debug.warning_message("Running '%s' failed" % command) return self._wcet, self._solvingTime for line in proc.stdout.readlines(): if line.startswith("Value of objective function"): lexemes = shlex.split(line) self._wcet = long(decimal.Decimal(lexemes[-1])) assert self._wcet assert self._solvingTime for line in proc.stderr.readlines(): if line.startswith("CPU Time for Parsing"): lexemes = shlex.split(line) time = lexemes[5][:-1] #self.solvingTime -= float(time) return self._wcet, self._solvingTime
def __parseAddress(self, time, address, runID): if address == self.__lastAddr: self.__analyseWCETOfBasicBlock(time - self.__time1) if not self.__currentBB.hasAddress(address): # Instructions in the current basic block have finished. Analyse its execution time self.__analyseWCETOfBasicBlock(time - self.__time1) # Move the time marker forward to the current time to reflect that we are transitioning # to a new basic block self.__time1 = time # Make the switch to the next basic block if self.__currentCFG.isCallSite(self.__currentBB.vertexID): self.__handleCall(address) elif self.__currentCFG.getExitID() == self.__currentBB.vertexID: self.__handleReturn() # Since we have switched basic blocks in the current CFG, analyse the super blocks self._analyseCFGEdge(self.__currentPathg, self.__currentLNT, self.__predBB.vertexID, self.__currentBB.vertexID) else: for succID in self.__currentBB.getSuccessorIDs(): succv = self.__currentCFG.getVertex(succID) if succv.hasAddress(address): self.__predBB = self.__currentBB self.__currentBB = succv break # Since we have switched basic blocks in the current CFG, analyse the super blocks self._analyseCFGEdge(self.__currentPathg, self.__currentLNT, self.__predBB.vertexID, self.__currentBB.vertexID) debug.debug_message( "Now in CFG '%s' at basic block %d" % (self.__currentCFG.getName(), self.__currentBB.vertexID), 10) self._analyseCFGVertex(self.__currentPathg, self.__currentLNT, self.__currentBB.vertexID)
def do_it(self): filename = config.Arguments.basepath + os.sep + config.Arguments.basename + ".traces.gz" with gzip.open(filename, 'wb') as self.the_file: for trace in xrange(1, config.Arguments.generate_traces+1): debug.debug_message("Generating trace #%d" % trace, __name__, 10) self.generate_a_trace() self.the_file.write("\n\n")
def identify_leaders(self): functionToBranchTargets = {} for function_name in self.functions: debug.debug_message("Identifying leaders in '%s'" % function_name, __name__, 10) self.function_to_leaders[function_name] = set() functionToBranchTargets[function_name] = set() newLeader = True noopAfterJumpTableBranch = False index = 0 for instruction, nextInstruction in utils.peekahead_iterator(self.function_to_instructions[function_name]): if newLeader: self.function_to_leaders[function_name].add(instruction) newLeader = False elif noopAfterJumpTableBranch: assert instruction.the_instruction[1] in InstructionSet.Nops, "Did not find an no-op after jump table branch. Instead found %s" % instruction noopAfterJumpTableBranch = False newLeader = True op = instruction.the_instruction[1] if op in InstructionSet.Branches: newLeader = True addressTarget = int(instruction.the_instruction[2], 16) functionToBranchTargets[function_name].add(addressTarget) elif self.is_jump_table_branch(instruction): # Look for instructions with an explicit load into the PC debug.debug_message("Instruction '%s' is loading a value into the PC" % instruction, __name__, 10) if nextInstruction and nextInstruction.the_instruction[0] in InstructionSet.Nops: noopAfterJumpTableBranch = True else: newLeader = True index += 1 for instruction in self.function_to_instructions[function_name]: if instruction.address in functionToBranchTargets[function_name]: self.function_to_leaders[function_name].add(instruction)
def add_jump_table_edges(self): for function_name in self.functions: debug.debug_message("Adding jump table edges in '%s'" % function_name, __name__, 10) cfg = self.program.cfgs[function_name] i = 0 hasJumpTablePredecessor = set() for instr in self.function_to_instructions[function_name]: # If the instruction loads into the PC... if self.is_jump_table_branch(instr): # Get the number of directives associated with this instruction to work out # how many arms it has assert instr in self.jump_table_to_directives numberOfBranchArms = len(self.jump_table_to_directives[instr]) if instr.the_instruction[0] == InstructionSet.LoadInstructions[3] \ or instr.the_instruction[0] == InstructionSet.LoadInstructions[4]: numberOfBranchArms = 3 predv = cfg.get_basic_block_with_address(instr.address) # Now go through each successive address, get the vertex associated with # that address, and add an edge if the address belongs to a newly discovered # basic block for j in range(i, len(self.function_to_instructions[function_name])): nextInstr = self.function_to_instructions[function_name][j] address = nextInstr.address if not predv.hasAddress(address): succv = cfg.get_basic_block_with_address(address) if not predv.hasSuccessor(succv.vertexID) and succv not in hasJumpTablePredecessor: cfg.addEdge(predv.vertexID, succv.vertexID) hasJumpTablePredecessor.add(succv) numberOfBranchArms -= 1 # We know how many arms to expect. As soon as the supply has been # exhausted, stop adding edges if not numberOfBranchArms or self.is_jump_table_branch(nextInstr): break i += 1
def identify_call_graph(self, root_function): assert root_function in self.function_to_instructions, "Unable to locate root function '%s' in disassembly" % root_function self.functions = set() stack = [root_function] while stack: function_name = stack.pop() self.functions.add(function_name) debug.debug_message("Analysing function '%s'" % function_name, __name__, 10) assert function_name in self.function_to_instructions, "No instructions for '%s' discovered" % function_name for instruction in self.function_to_instructions[function_name]: if InstructionSet.Call in instruction.the_instruction: assert len( instruction.the_instruction ) == 4, "Unable to handle call instruction '%s' since it does not have 3 fields exactly" % instruction.the_instruction startAddress = int(instruction.the_instruction[2], 16) assert startAddress in self.start_address_to_function, "Unable to find function with start address %s (it should be %s)" % ( hex(startAddress), instruction.the_instruction[3]) callee_name = self.start_address_to_function[startAddress] if callee_name not in self.functions: stack.append(callee_name) elif instruction.the_instruction[1] in InstructionSet.Branches: assert len( instruction.the_instruction ) == 4, "Unable to handle branch instruction '%s' since it does not have 3 fields exactly" % instruction.the_instruction startAddress = int(instruction.the_instruction[2], 16) if startAddress < self.function_to_start_address[ function_name] or startAddress > self.function_to_last_address[ function_name]: callee_name = self.get_callee_name( instruction.the_instruction) if callee_name not in self.functions: stack.append(callee_name)
def __generateTrace(self): # To keep track of loop tail iteration count self.__functionToTailCount = {} # To keep trace of the number of function calls self.__numberOfCalls = 0 callg = self.__program.getCallGraph() rootv = callg.getVertex(callg.getRootID()) self.__currentCallv = rootv self.__currentCFG = self.__program.getCFG(rootv.getName()) self.__currentLNT = self.__program.getLNT(rootv.getName()) self.__currentv = self.__currentCFG.getVertex( self.__currentCFG.getEntryID()) self.__vertexID = self.__currentv.vertexID self.__callStack = [] while True: self.__outfile.write("%d " % self.__vertexID) if self.__vertexID == self.__currentCFG.getExitID(): if callg.getVertexWithName( self.__currentCFG.getName()) == rootv: # End of the program reached break else: # End of function call debug.debug_message( "Returning from %s" % self.__currentCallv.getName(), 5) self.__currentCallv, self.__currentCFG, self.__currentLNT, self.__currentv = self.__callStack.pop( ) self.__vertexID = self.__currentv.vertexID # Go past the call site self.__chooseSuccessorInICFG() elif self.__currentLNT.isLoopTail(self.__vertexID): tupleIndex = self.__currentCFG.getName(), self.__vertexID if tupleIndex not in self.__functionToTailCount: self.__functionToTailCount[tupleIndex] = 1 self.__chooseSuccessorInICFG() elif self.__functionToTailCount[ tupleIndex] < Generatetraces.maxLoopIterations: self.__functionToTailCount[tupleIndex] += 1 self.__chooseSuccessorInICFG() else: self.__chooseNonLoopBackEdgeSuccessorInICFG() elif self.__currentCFG.isCallSite(self.__vertexID): # Make the call. First save state then move to the callee ICFG self.__callStack.append( (self.__currentCallv, self.__currentCFG, self.__currentLNT, self.__currentv)) succID = self.__currentCallv.getSuccessorWithCallSite( self.__vertexID) self.__currentCallv = callg.getVertex(succID) calleeName = self.__currentCallv.getName() debug.debug_message( "Calling %s" % self.__currentCallv.getName(), 5) self.__currentCFG = self.__program.getICFG(calleeName) self.__currentLNT = self.__program.getLNT(calleeName) self.__currentv = self.__currentCFG.getVertex( self.__currentCFG.getEntryID()) self.__vertexID = self.__currentv.vertexID else: self.__chooseSuccessorInICFG()
def do_it(self): filename = config.Arguments.basepath + os.sep + config.Arguments.basename + ".traces.gz" with gzip.open(filename, 'wb') as self.the_file: for trace in xrange(1, config.Arguments.generate_traces + 1): debug.debug_message("Generating trace #%d" % trace, __name__, 10) self.generate_a_trace() self.the_file.write("\n\n")
def assign_wcets_to_ipg_egdes_using_random_values(self, ipg): for v in ipg: for succID in v.successors.keys(): succ_edge = v.getSuccessorEdge(succID) if not succ_edge.isDummyEdge(): key = (v.vertexID, succID) self.ipg_edge_WCETs[key] = random.randint(1, 10) debug.debug_message("WCET(%s) = %d" % (key, self.ipg_edge_WCETs[key]), __name__, 1)
def assign_wcets_to_basic_blocks(self, icfg): for v in icfg: if isinstance(v, vertices.CFGVertex) and not v.is_ipoint: self.basic_block_WCETs[v.vertexID] = random.randint(1, 20) debug.debug_message( "WCET(%d) = %d" % (v.vertexID, self.basic_block_WCETs[v.vertexID]), __name__, 1)
def __init__(self, program, numberOftraces=1): self.__program = program filename = config.Arguments.basepath + os.sep + config.Arguments.basename + ".traces" with open(filename, 'w') as self.__outfile: for trace in xrange(1, numberOftraces + 1): debug.debug_message("Generating trace #%d" % trace, 1) self.__outfile.write("%s\n" % newTrace) self.__generateTrace() self.__outfile.write("\n%s\n" % endTrace)
def __init__ (self, program, numberOftraces=1): self.__program = program filename = config.Arguments.basepath + os.sep + config.Arguments.basename + ".traces" with open(filename, 'w') as self.__outfile: for trace in xrange(1, numberOftraces+1): debug.debug_message("Generating trace #%d" % trace, 1) self.__outfile.write("%s\n" % newTrace) self.__generateTrace() self.__outfile.write("\n%s\n" % endTrace)
def add_edges(self): for function_name in self.functions: debug.debug_message("Adding edges in '%s'" % function_name, __name__, 10) cfg = self.program.cfgs[function_name] predID = vertices.dummyID for instruction in self.function_to_instructions[function_name]: v = self.instruction_to_basic_block[instruction] if predID != vertices.dummyID: cfg.addEdge(predID, v.vertexID) predID = vertices.dummyID if v.instructions[-1] == instruction: if instruction.the_instruction[1] == InstructionSet.Call: callee_name = self.get_callee_name( instruction.the_instruction) cfg.add_call_site(v.vertexID, callee_name) self.program.callg.addEdge(function_name, callee_name, v.vertexID) predID = v.vertexID elif instruction.the_instruction[ 1] in InstructionSet.UnconditionalJumps: jump_address = int(instruction.the_instruction[2], 16) if jump_address >= self.function_to_start_address[ function_name] and jump_address <= self.function_to_last_address[ function_name]: succv = cfg.get_basic_block_with_address( jump_address) cfg.addEdge(v.vertexID, succv.vertexID) else: callee_name = self.start_address_to_function[ jump_address] cfg.add_call_site(v.vertexID, callee_name) self.program.callg.addEdge(function_name, callee_name, v.vertexID) predID = v.vertexID elif instruction.the_instruction[ 1] in InstructionSet.Branches: branch_address = int(instruction.the_instruction[2], 16) if branch_address >= self.function_to_start_address[ function_name] and branch_address <= self.function_to_last_address[ function_name]: succv = cfg.get_basic_block_with_address( branch_address) cfg.addEdge(v.vertexID, succv.vertexID) else: callee_name = self.get_callee_name( instruction.the_instruction) cfg.add_call_site(v.vertexID, callee_name) self.program.callg.addEdge(function_name, callee_name, v.vertexID) predID = v.vertexID elif v in self.function_to_jump_table_basic_blocks[ function_name]: pass else: predID = v.vertexID
def __handleReturn(self): debug.debug_message( "Returning because of basic block %d" % self.__currentBB.vertexID, 1) self._endOfFunction(self.__currentCFG, self.__currentLNT, self.__currentPathg) if self.__stack: (self.__currentContextv, self.__currentCFG, self.__currentLNT, self.__predBB, self.__currentBB, self.__currentPathg) = self.__stack.pop()
def assign_wcets_to_ipg_egdes_using_random_values(self, ipg): for v in ipg: for succID in v.successors.keys(): succ_edge = v.getSuccessorEdge(succID) if not succ_edge.isDummyEdge(): key = (v.vertexID, succID) self.ipg_edge_WCETs[key] = random.randint(1, 10) debug.debug_message( "WCET(%s) = %d" % (key, self.ipg_edge_WCETs[key]), __name__, 1)
def assign_wcets_to_ipg_egdes_using_basic_block_wcets(self, ipg, icfg): for v in ipg: for succID in v.successors.keys(): key = (v.vertexID, succID) wcet = 0 for program_point in v.get_successor_edge(succID).edge_label: if isinstance(program_point, vertices.CFGVertex) and not program_point.is_ipoint: wcet += self.basic_block_WCETs[program_point.vertexID] self.ipg_edge_WCETs[key] = wcet debug.debug_message("WCET(%s) = %d" % (key, self.ipg_edge_WCETs[key]), __name__, 1)
def __parse(self, traceFiles): runID = 0 for filename in traceFiles: parsing = False with gzip.open(filename, 'r') as f: runID += 1 self._allruns.add(runID) debug.debug_message( "Analysing gem5 trace file '%s'" % filename, 1) for line in f: lexemes = shlex.split(line) PCLexeme = lexemes[-1] assert len( PCLexeme ) == 11, "Unable to parse program counter %s" % PCLexeme try: time = int(lexemes[0][:-1]) PCLexeme = PCLexeme[5:] PC = int(PCLexeme, 16) if PC == self.__firstAddr: self.__time1 = time startTime = time parsing = True self.__currentContextv = self.__contextg.getVertex( self.__contextg.getRootID()) self.__currentCFG = self._program.getCFG( self.__currentContextv.getName()) self.__currentLNT = self._program.getLNT( self.__currentContextv.getName()) self.__currentPathg = self._program.getPathInfoGraph( self.__currentContextv.getName()) self.__predBB = None self.__currentBB = self.__currentCFG.getVertex( self.__currentCFG.getEntryID()) self._analyseCFGVertex(self.__currentPathg, self.__currentLNT, self.__currentBB.vertexID) if parsing: self.__parseAddress(time, PC, runID) if PC == self.__lastAddr: # Stop parsing parsing = False # Compute the HWMT totalTime = time - startTime self._longestTime = max(self._longestTime, totalTime) # Falsify conjectures self._endOfFunction(self.__currentCFG, self.__currentLNT, self.__currentPathg) except ValueError: debug.exit_message( "Cannot cast %s into an integer: it is not a hexadecimal string" % PCLexeme)
def __parse(self, tracefile): runID = 0 with open(tracefile, 'r') as f: for line in f: if line.startswith(newTrace): runID += 1 debug.debug_message("=====> Run %d" % runID, 1) self._allruns.add(runID) self.__reset() elif line.startswith(endTrace): self.__handleReturn() else: lexemes = shlex.split(line) for lex in lexemes: nextID = int(lex) if nextID == self.__rootCFG.getEntryID(): self.__currentBB = self.__currentCFG.getVertex( nextID) else: found = False for succID in self.__currentBB.getSuccessorIDs(): if succID == nextID: self.__predBB = self.__currentBB self.__currentBB = self.__currentCFG.getVertex( succID) # We have switched basic blocks in the current CFG self._analyseCFGEdge( self.__currentPathg, self.__currentLNT, self.__predBB.vertexID, self.__currentBB.vertexID) found = True break if not found: if self.__currentBB.vertexID == self.__currentCFG.getExitID( ): succIDs = self.__currentBB.getSuccessorIDs( ) assert len(succIDs) == 1 succv = self.__currentCFG.getVertex( succIDs[0]) self.__predBB = self.__currentBB self.__currentBB = succv # Since we have switched basic blocks in the current CFG, analyse the super blocks self._analyseCFGEdge( self.__currentPathg, self.__currentLNT, self.__predBB.vertexID, self.__currentBB.vertexID) else: self.__handleCall(nextID) # We have switched basic blocks in the current CFG self._analyseCFGVertex(self.__currentPathg, self.__currentLNT, self.__currentBB.vertexID)
def parse_file(): program = programs.Program() icfg = None bb = None with open(config.Arguments.program_file) as f: for line in f: line = line.lower() if line.startswith('cfg:'): lexemes = shlex.split(line) assert len(lexemes) == 2, "Unable to parse CFG line %s" % line icfg = cfgs.ICFG() function_name = lexemes[-1] icfg.name = function_name debug.debug_message("Found new ICFG '%s'" % function_name, __name__, 1) elif line.startswith('bb:'): assert icfg, "Found basic block but current ICFG is null" lexemes = shlex.split(line) assert len(lexemes) == 2, "Unable to parse basic block line %s" % line vertexID = lexemes[-1] assert vertexID.isdigit(), "Vertex identifier '%s' is not an integer" % vertexID bb = vertices.CFGVertex(int(vertexID), False) icfg.addVertex(bb) elif line.startswith('ipoint'): assert bb, "Trying to add an Ipoint to a basic block but current basic block is null" index = line.index(':') position = line[index+1:].replace(' ', '').strip().lower() icfg.ipoint_positions[bb.vertexID] = position elif line.startswith('succ:'): assert bb, "Found edge but current basic block is null" index = line.index(':') line = line[index+1:] splitter = shlex.shlex(line) splitter.whitespace += ')' splitter.whitespace += '(' splitter.whitespace_split = False lexemes = list(splitter) assert len(lexemes) % 3 == 0, "Unable to parse edge information '%s'" % line if len(lexemes) > 1: index = 0 for lex in lexemes: if index % 3 == 0: function_name = lexemes[index] assert function_name == icfg.name, "Call edge found which is currently not handled" elif index % 3 == 2: succID = lexemes[index] assert succID.isdigit(), "Successor identifier '%s' is not an integer" % succID bb.add_successor(int(succID)) index += 1 assert icfg, "Attempting to analyse ICFG but current ICFG is null" icfg.add_predecessor_edges() icfg.set_entry_and_exit() program.add_ICFG(icfg) return program
def extract_instructions(self, filename): with open(filename, 'r') as f: parse = False last_instruction = None last_jump_table_instruction = None current_function = None for line in f: if parse: if re.match(r'[0-9a-fA-F]+\s<.*>.*', line): if last_instruction: assert current_function, "No function detected yet" self.function_to_last_address[ current_function] = last_instruction.address self.last_address_to_function[ last_instruction.address] = current_function lexemes = shlex.split(line) assert len( lexemes ) == 2, "Unable to handle disassembly line %s" % line address = int(lexemes[0], 16) function_name = lexemes[1][1:-2] debug.debug_message( "Detected function '%s' @ start address %d" % (function_name, address), __name__, 10) self.start_address_to_function[address] = function_name current_function = function_name self.function_to_instructions[current_function] = [] self.function_to_directives[current_function] = [] self.function_to_start_address[ current_function] = address self.function_to_jump_table_basic_blocks[ current_function] = [] last_jump_table_instruction = None elif re.match(r'\s*[0-9a-fA-F]+:.*', line): # Ignore directives reserving space for data if '.word' not in line and '.short' not in line and '.byte' not in line: instruction = vertices.BasicBlock.Instruction.get_instruction( line) self.function_to_instructions[ current_function].append(instruction) last_instruction = instruction if self.is_jump_table_branch(instruction): last_jump_table_instruction = instruction self.jump_table_to_directives[instruction] = [] else: self.function_to_directives[ current_function].append(line) if last_jump_table_instruction: self.jump_table_to_directives[ last_jump_table_instruction].append(line) elif line.startswith('Disassembly of section'): parse = '.text' in line
def assign_wcets_to_ipg_egdes_using_basic_block_wcets(self, ipg, icfg): for v in ipg: for succID in v.successors.keys(): key = (v.vertexID, succID) wcet = 0 for program_point in v.get_successor_edge(succID).edge_label: if isinstance(program_point, vertices.CFGVertex ) and not program_point.is_ipoint: wcet += self.basic_block_WCETs[program_point.vertexID] self.ipg_edge_WCETs[key] = wcet debug.debug_message( "WCET(%s) = %d" % (key, self.ipg_edge_WCETs[key]), __name__, 1)
def find_loop_exits(self): for headerID in self.__headerVertices.keys(): self.__loop_exit_edges[headerID] = set() for vertexID in self.__loopBodies[headerID]: v = self.__directedg.getVertex(vertexID) for succID in v.successors.keys(): if succID not in self.__loopBodies[headerID]: if headerID != vertexID and self.is_loop_header(vertexID): if succID not in self.__loopBodies[vertexID]: self.__loop_exit_edges[headerID].add((vertexID, succID)) else: self.__loop_exit_edges[headerID].add((vertexID, succID)) debug.debug_message("Exits of %s = %s" % (headerID, self.__loop_exit_edges[headerID]), __name__, 4)
def add_loop_back_edges(self, headerID): for predID in self.iteration_edge_sources: for succID in self.iteration_edge_destinations: predv = self.__ipg.getVertex(predID) succv = self.__ipg.getVertex(succID) succe = predv.get_successor_edge(succID) prede = succv.get_predecessor_edge(predID) succe.iteration_edge = True prede.iteration_edge = True self.edges_added.add((predID, succID)) debug.debug_message( "(%d, %d) is a loop-back edge for loop with header %d" % (predID, succID, headerID), __name__, 1 )
def __computeInnerRelativeBounds (self, pathg, lnt, pathv, headerv): for succID in headerv.getSuccessorIDs(): succv = lnt.getVertex(succID) if isinstance(succv, vertices.HeaderVertex): innerHeaderID = succv.getHeaderID() innerProgramPoint = list(pathg.getLoopMonitoredProgramPoints(innerHeaderID))[0] innerPathv = pathg.getProgramPointVertex(innerProgramPoint) succe = innerPathv.getSuccessoredges(edges.PathInformationEdgeType.LOOP_BOUNDS)[0] executionCount = self._relativeExecutionCountsThisRun[pathg][innerHeaderID] if executionCount > succe.relative: debug.debug_message("Falsifying conjecture that %d executes at most %d times relative to its innermost enclosing loop. Found %d instead" % (innerHeaderID, succe.relative, executionCount), 1) succe.relative = executionCount self._relativeExecutionCountsThisRun[pathg][innerHeaderID] = 0
def add_loop_back_edges(self, headerID): for predID in self.iteration_edge_sources: for succID in self.iteration_edge_destinations: predv = self.__ipg.getVertex(predID) succv = self.__ipg.getVertex(succID) succe = predv.get_successor_edge(succID) prede = succv.get_predecessor_edge(predID) succe.iteration_edge = True prede.iteration_edge = True self.edges_added.add((predID, succID)) debug.debug_message( "(%d, %d) is a loop-back edge for loop with header %d" % (predID, succID, headerID), __name__, 1)
def first_pass(filename): # Partially build call graph program = programs.Program() with open(filename) as the_file: for line in the_file: line = line.lower() if line.startswith(cfg_lexeme): names = name_regex.findall(line) assert len(names) == 2, "Too many names found '%s'" % ' '.join(names) cfg = directed_graphs.CFG() cfg.name = names[1] debug.debug_message("Found new CFG '%s'" % cfg.name, __name__, 1) program.add_CFG(cfg) return program
def tenant(tenant): PrintingService.default_start("TENANT") debug.debug_message("name: " + tenant["name"]) debug.debug_message("description: " + tenant["description"]) debug.debug_message("id: " + tenant["id"]) debug.debug_message("enabled: " + str(tenant["enabled"])) PrintingService.default_end()
def __generateTrace (self): # To keep track of loop tail iteration count self.__functionToTailCount = {} # To keep trace of the number of function calls self.__numberOfCalls = 0 callg = self.__program.getCallGraph() rootv = callg.getVertex(callg.getRootID()) self.__currentCallv = rootv self.__currentCFG = self.__program.getCFG(rootv.getName()) self.__currentLNT = self.__program.getLNT(rootv.getName()) self.__currentv = self.__currentCFG.getVertex(self.__currentCFG.getEntryID()) self.__vertexID = self.__currentv.vertexID self.__callStack = [] while True: self.__outfile.write("%d " % self.__vertexID) if self.__vertexID == self.__currentCFG.getExitID(): if callg.getVertexWithName(self.__currentCFG.getName()) == rootv: # End of the program reached break else: # End of function call debug.debug_message("Returning from %s" % self.__currentCallv.getName(), 5) self.__currentCallv, self.__currentCFG, self.__currentLNT, self.__currentv = self.__callStack.pop() self.__vertexID = self.__currentv.vertexID # Go past the call site self.__chooseSuccessorInICFG() elif self.__currentLNT.isLoopTail(self.__vertexID): tupleIndex = self.__currentCFG.getName(), self.__vertexID if tupleIndex not in self.__functionToTailCount: self.__functionToTailCount[tupleIndex] = 1 self.__chooseSuccessorInICFG() elif self.__functionToTailCount[tupleIndex] < Generatetraces.maxLoopIterations: self.__functionToTailCount[tupleIndex] += 1 self.__chooseSuccessorInICFG() else: self.__chooseNonLoopBackEdgeSuccessorInICFG() elif self.__currentCFG.isCallSite(self.__vertexID): # Make the call. First save state then move to the callee ICFG self.__callStack.append((self.__currentCallv, self.__currentCFG, self.__currentLNT, self.__currentv)) succID = self.__currentCallv.getSuccessorWithCallSite(self.__vertexID) self.__currentCallv = callg.getVertex(succID) calleeName = self.__currentCallv.getName() debug.debug_message("Calling %s" % self.__currentCallv.getName(), 5) self.__currentCFG = self.__program.getICFG(calleeName) self.__currentLNT = self.__program.getLNT(calleeName) self.__currentv = self.__currentCFG.getVertex(self.__currentCFG.getEntryID()) self.__vertexID = self.__currentv.vertexID else: self.__chooseSuccessorInICFG()
def find_loops_of_states(self, rootID): for v in self.__transition_graph: self.current_parent[v.vertexID] = v.vertexID predom_tree = Dominators(self.__transition_graph) for vertexID in reversed(self.__dfs.pre_order): v = self.__transition_graph.get_vertex(vertexID) for predID in v.predecessors.keys(): if self.__dfs.isDFSBackedge(predID, vertexID): assert predom_tree.is_ancestor(vertexID, predID), "Non-reducible loop found with DFS backedge %d => %d" % (predID, vertexID) debug.debug_message("%s => %s is a loop-back edge of non-trivial loop" % (predID, vertexID), __name__, 15) self.loop_bodies_per_backedge[(predID, vertexID)] = set() self.loop_exit_edges_per_backedge[(predID, vertexID)] = set() self.create_header_information(vertexID) self.find_loop_body(predID, vertexID) self.loop_tails.add(predID)
def find_loops(self, rootID): self.__dfs = DepthFirstSearch (self.__directedg, rootID) for vertexID in reversed(self.__dfs.getPreorder()): v = self.__directedg.getVertex(vertexID) worklist = [] for predID in v.predecessors.keys(): if self.__dfs.isDFSBackedge(predID, vertexID): if predID == vertexID: debug.debug_message("%s => %s is a loop-back edge of trivial loop" % (predID, vertexID), __name__, 3) self.add_self_loop(vertexID) else: debug.debug_message("%s => %s is a loop-back edge of non-trivial loop" % (predID, vertexID), __name__, 3) worklist.append(self.__parent[predID]) if worklist: self.build_loop_body(worklist, vertexID)
def first_pass(filename): # Partially build call graph program = programs.Program() with open(filename) as the_file: for line in the_file: line = line.lower() if line.startswith(cfg_lexeme): names = name_regex.findall(line) assert len( names) == 2, "Too many names found '%s'" % ' '.join(names) cfg = directed_graphs.CFG() cfg.name = names[1] debug.debug_message("Found new CFG '%s'" % cfg.name, __name__, 1) program.add_CFG(cfg) return program
def add_acyclic_edges(self, headerID): # Compute a topological sort on the ICFG dfs = trees.DepthFirstSearch(self.__icfg, self.__icfg.get_entryID()) if self.__ipg.hasVertex(self.__icfg.get_entryID()): # If the header of the loop is an ipoint then it is the only # destination of an iteration edge self.iteration_edge_destinations.add(self.__icfg.get_entryID()) # Perform data-flow analysis changed = True while changed: changed = False for vertexID in reversed(dfs.getPostorder()): debug.debug_message("At vertex %d" % vertexID, __name__, 1) v = self.__icfg.getVertex(vertexID) if self.__enhanced_lnt.is_loop_header( vertexID) and vertexID != self.__icfg.get_entryID(): # Inner header detected self.add_loop_entry_edges(v) self.add_ipoints_to_abstract_vertex(v) else: for predID in v.predecessors.keys(): if self.__ipg.hasVertex(predID): if self.__ipg.hasVertex(vertexID): self.add_edge(predID, vertexID) else: self.vertex_to_reachable[vertexID].add(predID) else: for keyID in self.vertex_to_reachable[predID]: if self.__ipg.hasVertex( keyID) and self.__ipg.hasVertex( vertexID): self.add_edge(keyID, vertexID) elif not self.__ipg.hasVertex( keyID) and self.__ipg.hasVertex( vertexID): self.iteration_edge_destinations.add( vertexID) else: self.vertex_to_reachable[vertexID].add( keyID) if vertexID in self.__enhanced_lnt.get_loop_tails(headerID): if self.__ipg.hasVertex(vertexID): self.iteration_edge_sources.add(vertexID) else: for keyID in self.vertex_to_reachable[vertexID]: if self.__ipg.hasVertex(keyID): self.iteration_edge_sources.add(keyID)
def read_file(filename): program = programs.Program() cfg = None bb = None with open(filename) as the_file: for line in the_file: line = line.lower() if line.startswith(cfg_lexeme): names = name_regex.findall(line) assert len( names) == 2, "Too many names found '%s'" % ' '.join(names) cfg = directed_graphs.CFG() cfg.name = names[1] program.add_CFG(cfg) debug.debug_message("Found new CFG '%s'" % cfg.name, __name__, 1) elif line.startswith(basic_block_lexeme): assert cfg, "Found basic block but current CFG is null" ids = int_regex.findall(line) assert len( ids ) == 1, "Too many identifiers found '%s'" % ' '.join(ids) assert ids[0].isdigit( ), "Vertex identifier '%s' is not an integer" % ids[0] bb = vertices.Vertex(int(ids[0])) cfg.add_vertex(bb) elif line.startswith(successors_lexeme): assert bb, "Found edge but current basic block is null" edges = edges_regex.findall(line) for edge in edges: a_tuple = edge_tuple_regex.findall(edge) assert len( a_tuple ) == 2, "Too many components in edge tuple: %s" % edge assert a_tuple[ 0] == cfg.name, "Call edge found which is currently not handled" assert a_tuple[1].isdigit( ), "Successor identifier '%s' is not an integer" % a_tuple[ 1] bb.add_successor(int(a_tuple[1])) for cfg in program.cfgs.values(): cfg.add_predecessor_edges() cfg.set_entry_and_exit() cfg.add_dummy_loop_between_exit_and_entry() udraw.make_file(cfg, "%s.cfg" % (cfg.name)) return program
def compile_program(program): debug.verbose_message("Compiling program", __name__) optimisation = "" extraFlags = "" if config.Arguments.flags: for flag in config.Arguments.flags: extraFlags += "-%s " % flag if re.match(r"O[0-3]+", flag): optimisation = flag binary = program[:-2] + optimisation cmd = "%s -fno-stack-protector -static %s %s -o %s" % (config.Arguments.GCC, extraFlags, program, binary) debug.debug_message("Compiling with command '%s'" % cmd, 1) proc = subprocess.Popen(cmd, shell=True, stdout=sys.stdout, stderr=sys.stderr) returncode = proc.wait() if returncode: debug.exit_message("Compiling '%s' with '%s' failed" % (program, cmd)) return binary
def create_basic_blocks(self): for function_name in self.functions: debug.debug_message("Identifying basic blocks in '%s'" % function_name, __name__, 10) cfg = directed_graphs.CFG() cfg.name = function_name self.program.addCFG(cfg) bb = None for instruction in self.function_to_instructions[function_name]: if instruction in self.function_to_leaders[function_name]: debug.debug_message("Instruction @ %s is a leader" % hex(instruction.address), __name__, 10) bb = vertices.BasicBlock(self.next_vertexID, function_name) cfg.addVertex(bb) self.next_vertexID += 1 assert bb, "Basic block is currently null" bb.instructions.append(instruction) self.instruction_to_basic_block[instruction] = bb if self.is_jump_table_branch(instruction): self.function_to_jump_table_basic_blocks[function_name].append(bb)
def __computeInnerRelativeBounds(self, pathg, lnt, pathv, headerv): for succID in headerv.getSuccessorIDs(): succv = lnt.getVertex(succID) if isinstance(succv, vertices.HeaderVertex): innerHeaderID = succv.getHeaderID() innerProgramPoint = list( pathg.getLoopMonitoredProgramPoints(innerHeaderID))[0] innerPathv = pathg.getProgramPointVertex(innerProgramPoint) succe = innerPathv.getSuccessoredges( edges.PathInformationEdgeType.LOOP_BOUNDS)[0] executionCount = self._relativeExecutionCountsThisRun[pathg][ innerHeaderID] if executionCount > succe.relative: debug.debug_message( "Falsifying conjecture that %d executes at most %d times relative to its innermost enclosing loop. Found %d instead" % (innerHeaderID, succe.relative, executionCount), 1) succe.relative = executionCount self._relativeExecutionCountsThisRun[pathg][innerHeaderID] = 0
def add_loop_entry_edges(self, v): debug.debug_message("Inner header %d detected" % v.vertexID, __name__, 1) inner_loop_info = self.loop_by_loop_info.ipgs_per_loop[v.vertexID] for predID in v.predecessors.keys(): if self.__ipg.hasVertex(predID): for succID in inner_loop_info.iteration_edge_destinations: self.add_edge(predID, succID) else: for keyID in self.vertex_to_reachable[predID]: if self.__ipg.hasVertex(keyID): for succID in inner_loop_info.iteration_edge_destinations: self.add_edge(keyID, succID) else: # The key is a header vertex. # This means that all the destinations of iteration edges of # the inner loop are also destinations of iterations edges of # the outer loop self.iteration_edge_destinations.update(inner_loop_info.iteration_edge_destinations)
def run_gem5(binary): test_vector_properties = TestVectorProperties() run = get_next_trace_file_number(binary) + 1 # Now run the program n times random_test_vectors = RandomGeneration(test_vector_properties) gem5traces = [] for i in xrange(run, config.Arguments.tests + run): traceFile = "%s.%s.%d.gz" % (os.path.basename(binary), "trace", i) cmd = '%s --debug-flags=Fetch --trace-file=%s %s --cpu-type=timing -c %s -o "%s"' % \ (config.Arguments.gem5_simulator, traceFile, config.Arguments.gem5_config, binary, random_test_vectors.next_test_vector()) debug.debug_message("Running '%s' on gem5" % cmd, 1) proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode = proc.wait() if returncode: debug.exit_message("Running '%s' failed" % cmd) gem5_trace = os.path.abspath(os.getcwd()) + os.sep + config.Arguments.m5_trace_directory + os.sep + traceFile assert os.path.exists(gem5_trace), "Expected to find gem5 trace in '%s' but it is not there" % gem5_trace gem5traces.append(gem5_trace) return gem5traces
def add_jump_table_edges(self): for function_name in self.functions: debug.debug_message( "Adding jump table edges in '%s'" % function_name, __name__, 10) cfg = self.program.cfgs[function_name] i = 0 hasJumpTablePredecessor = set() for instr in self.function_to_instructions[function_name]: # If the instruction loads into the PC... if self.is_jump_table_branch(instr): # Get the number of directives associated with this instruction to work out # how many arms it has assert instr in self.jump_table_to_directives numberOfBranchArms = len( self.jump_table_to_directives[instr]) if instr.the_instruction[0] == InstructionSet.LoadInstructions[3] \ or instr.the_instruction[0] == InstructionSet.LoadInstructions[4]: numberOfBranchArms = 3 predv = cfg.get_basic_block_with_address(instr.address) # Now go through each successive address, get the vertex associated with # that address, and add an edge if the address belongs to a newly discovered # basic block for j in range( i, len(self.function_to_instructions[function_name])): nextInstr = self.function_to_instructions[ function_name][j] address = nextInstr.address if not predv.hasAddress(address): succv = cfg.get_basic_block_with_address(address) if not predv.hasSuccessor( succv.vertexID ) and succv not in hasJumpTablePredecessor: cfg.addEdge(predv.vertexID, succv.vertexID) hasJumpTablePredecessor.add(succv) numberOfBranchArms -= 1 # We know how many arms to expect. As soon as the supply has been # exhausted, stop adding edges if not numberOfBranchArms or self.is_jump_table_branch( nextInstr): break i += 1
def query(self, the_pair): debug.debug_message("Computing lca(%s)" % (the_pair,), __name__, 1) if isinstance(self.tree, LoopNests): the_pair = (self.tree.program_point_to_lnt_vertexID[the_pair[0]], self.tree.program_point_to_lnt_vertexID[the_pair[1]]) lowest_level = self.dummy_level level_index = 2 * self.tree.number_of_vertices() if self.representative[the_pair[0]] < self.representative[the_pair[1]]: startIndex = self.representative[the_pair[0]] endIndex = self.representative[the_pair[1]] else: startIndex = self.representative[the_pair[1]] endIndex = self.representative[the_pair[0]] for i in range(startIndex, endIndex+1): if self.level[i] < lowest_level: lowest_level = self.level[i] level_index = i debug.debug_message("lca(%s) = %d" % (the_pair, self.euler_tour[level_index]), __name__, 15) return self.euler_tour[level_index]
def inline_calls(self): debug.verbose_message("Inlining to create single CFG", __name__) rootv = self.callg.getVertex(self.callg.rootID) dfs = directed_graphs.DepthFirstSearch(self.callg, rootv.vertexID) for vertexID in dfs.post_order: succv = self.callg.getVertex(vertexID) for calle in succv.predecessors.values(): predv = self.callg.getVertex(calle.vertexID) for call_siteID in calle.getCallSites(): debug.debug_message("Inlining '%s' into '%s' at call site %d" % (succv.name, predv.name, call_siteID), 1) self.__doInline(self.cfgs[predv.name], self.cfgs[succv.name], call_siteID) self.cfgs[succv.name].remove_call_site(call_siteID) for vertexID in dfs.post_order: callv = self.callg.getVertex(vertexID) if callv != rootv: self.remove_function(callv.name) else: cfg = self.cfgs[rootv.name] cfg.addEdge(cfg.get_exitID(), cfg.get_entryID())
def add_loop_entry_edges(self, v): debug.debug_message("Inner header %d detected" % v.vertexID, __name__, 1) inner_loop_info = self.loop_by_loop_info.ipgs_per_loop[v.vertexID] for predID in v.predecessors.keys(): if self.__ipg.hasVertex(predID): for succID in inner_loop_info.iteration_edge_destinations: self.add_edge(predID, succID) else: for keyID in self.vertex_to_reachable[predID]: if self.__ipg.hasVertex(keyID): for succID in inner_loop_info.iteration_edge_destinations: self.add_edge(keyID, succID) else: # The key is a header vertex. # This means that all the destinations of iteration edges of # the inner loop are also destinations of iterations edges of # the outer loop self.iteration_edge_destinations.update( inner_loop_info.iteration_edge_destinations)
def identify_leaders(self): functionToBranchTargets = {} for function_name in self.functions: debug.debug_message("Identifying leaders in '%s'" % function_name, __name__, 10) self.function_to_leaders[function_name] = set() functionToBranchTargets[function_name] = set() newLeader = True noopAfterJumpTableBranch = False index = 0 for instruction, nextInstruction in utils.peekahead_iterator( self.function_to_instructions[function_name]): if newLeader: self.function_to_leaders[function_name].add(instruction) newLeader = False elif noopAfterJumpTableBranch: assert instruction.the_instruction[ 1] in InstructionSet.Nops, "Did not find an no-op after jump table branch. Instead found %s" % instruction noopAfterJumpTableBranch = False newLeader = True op = instruction.the_instruction[1] if op in InstructionSet.Branches: newLeader = True addressTarget = int(instruction.the_instruction[2], 16) functionToBranchTargets[function_name].add(addressTarget) elif self.is_jump_table_branch(instruction): # Look for instructions with an explicit load into the PC debug.debug_message( "Instruction '%s' is loading a value into the PC" % instruction, __name__, 10) if nextInstruction and nextInstruction.the_instruction[ 0] in InstructionSet.Nops: noopAfterJumpTableBranch = True else: newLeader = True index += 1 for instruction in self.function_to_instructions[function_name]: if instruction.address in functionToBranchTargets[ function_name]: self.function_to_leaders[function_name].add(instruction)
def compile_program(program): debug.verbose_message("Compiling program", __name__) optimisation = "" extraFlags = "" if config.Arguments.flags: for flag in config.Arguments.flags: extraFlags += "-%s " % flag if re.match(r'O[0-3]+', flag): optimisation = flag binary = program[:-2] + optimisation cmd = "%s -fno-stack-protector -static %s %s -o %s" % ( config.Arguments.GCC, extraFlags, program, binary) debug.debug_message("Compiling with command '%s'" % cmd, 1) proc = subprocess.Popen(cmd, shell=True, stdout=sys.stdout, stderr=sys.stderr) returncode = proc.wait() if returncode: debug.exit_message("Compiling '%s' with '%s' failed" % (program, cmd)) return binary
def __parse (self, tracefile): runID = 0 with open(tracefile, 'r') as f: for line in f: if line.startswith(newTrace): runID += 1 debug.debug_message("=====> Run %d" % runID, 1) self._allruns.add(runID) self.__reset() elif line.startswith(endTrace): self.__handleReturn() else: lexemes = shlex.split(line) for lex in lexemes: nextID = int(lex) if nextID == self.__rootCFG.getEntryID(): self.__currentBB = self.__currentCFG.getVertex(nextID) else: found = False for succID in self.__currentBB.getSuccessorIDs(): if succID == nextID: self.__predBB = self.__currentBB self.__currentBB = self.__currentCFG.getVertex(succID) # We have switched basic blocks in the current CFG self._analyseCFGEdge(self.__currentPathg, self.__currentLNT, self.__predBB.vertexID, self.__currentBB.vertexID) found = True break if not found: if self.__currentBB.vertexID == self.__currentCFG.getExitID(): succIDs = self.__currentBB.getSuccessorIDs() assert len(succIDs) == 1 succv = self.__currentCFG.getVertex(succIDs[0]) self.__predBB = self.__currentBB self.__currentBB = succv # Since we have switched basic blocks in the current CFG, analyse the super blocks self._analyseCFGEdge(self.__currentPathg, self.__currentLNT, self.__predBB.vertexID, self.__currentBB.vertexID) else: self.__handleCall(nextID) # We have switched basic blocks in the current CFG self._analyseCFGVertex(self.__currentPathg, self.__currentLNT, self.__currentBB.vertexID)
def add_acyclic_edges(self, headerID): # Compute a topological sort on the ICFG dfs = trees.DepthFirstSearch(self.__icfg, self.__icfg.get_entryID()) if self.__ipg.hasVertex(self.__icfg.get_entryID()): # If the header of the loop is an ipoint then it is the only # destination of an iteration edge self.iteration_edge_destinations.add(self.__icfg.get_entryID()) # Perform data-flow analysis changed = True while changed: changed = False for vertexID in reversed(dfs.getPostorder()): debug.debug_message("At vertex %d" % vertexID, __name__, 1) v = self.__icfg.getVertex(vertexID) if self.__enhanced_lnt.is_loop_header(vertexID) and vertexID != self.__icfg.get_entryID(): # Inner header detected self.add_loop_entry_edges(v) self.add_ipoints_to_abstract_vertex(v) else: for predID in v.predecessors.keys(): if self.__ipg.hasVertex(predID): if self.__ipg.hasVertex(vertexID): self.add_edge(predID, vertexID) else: self.vertex_to_reachable[vertexID].add(predID) else: for keyID in self.vertex_to_reachable[predID]: if self.__ipg.hasVertex(keyID) and self.__ipg.hasVertex(vertexID): self.add_edge(keyID, vertexID) elif not self.__ipg.hasVertex(keyID) and self.__ipg.hasVertex(vertexID): self.iteration_edge_destinations.add(vertexID) else: self.vertex_to_reachable[vertexID].add(keyID) if vertexID in self.__enhanced_lnt.get_loop_tails(headerID): if self.__ipg.hasVertex(vertexID): self.iteration_edge_sources.add(vertexID) else: for keyID in self.vertex_to_reachable[vertexID]: if self.__ipg.hasVertex(keyID): self.iteration_edge_sources.add(keyID)
def extract_instructions(self, filename): with open(filename, 'r') as f: parse = False last_instruction = None last_jump_table_instruction = None current_function = None for line in f: if parse: if re.match(r'[0-9a-fA-F]+\s<.*>.*', line): if last_instruction: assert current_function, "No function detected yet" self.function_to_last_address[current_function] = last_instruction.address self.last_address_to_function[last_instruction.address] = current_function lexemes = shlex.split(line) assert len(lexemes) == 2, "Unable to handle disassembly line %s" % line address = int(lexemes[0], 16) function_name = lexemes[1][1:-2] debug.debug_message("Detected function '%s' @ start address %d" % (function_name, address), __name__, 10) self.start_address_to_function[address] = function_name current_function = function_name self.function_to_instructions[current_function] = [] self.function_to_directives[current_function] = [] self.function_to_start_address[current_function] = address self.function_to_jump_table_basic_blocks[current_function] = [] last_jump_table_instruction = None elif re.match(r'\s*[0-9a-fA-F]+:.*', line): # Ignore directives reserving space for data if '.word' not in line and '.short' not in line and '.byte' not in line: instruction = vertices.BasicBlock.Instruction.get_instruction(line) self.function_to_instructions[current_function].append(instruction) last_instruction = instruction if self.is_jump_table_branch(instruction): last_jump_table_instruction = instruction self.jump_table_to_directives[instruction] = [] else: self.function_to_directives[current_function].append(line) if last_jump_table_instruction: self.jump_table_to_directives[last_jump_table_instruction].append(line) elif line.startswith('Disassembly of section'): parse = '.text' in line
def add_edges(self): for function_name in self.functions: debug.debug_message("Adding edges in '%s'" % function_name, __name__, 10) cfg = self.program.cfgs[function_name] predID = vertices.dummyID for instruction in self.function_to_instructions[function_name]: v = self.instruction_to_basic_block[instruction] if predID != vertices.dummyID: cfg.addEdge(predID, v.vertexID) predID = vertices.dummyID if v.instructions[-1] == instruction: if instruction.the_instruction[1] == InstructionSet.Call: callee_name = self.get_callee_name(instruction.the_instruction) cfg.add_call_site(v.vertexID, callee_name) self.program.callg.addEdge(function_name, callee_name, v.vertexID) predID = v.vertexID elif instruction.the_instruction[1] in InstructionSet.UnconditionalJumps: jump_address = int(instruction.the_instruction[2], 16) if jump_address >= self.function_to_start_address[function_name] and jump_address <= self.function_to_last_address[function_name]: succv = cfg.get_basic_block_with_address(jump_address) cfg.addEdge(v.vertexID, succv.vertexID) else: callee_name = self.start_address_to_function[jump_address] cfg.add_call_site(v.vertexID, callee_name) self.program.callg.addEdge(function_name, callee_name, v.vertexID) predID = v.vertexID elif instruction.the_instruction[1] in InstructionSet.Branches: branch_address = int(instruction.the_instruction[2], 16) if branch_address >= self.function_to_start_address[function_name] and branch_address <= self.function_to_last_address[function_name]: succv = cfg.get_basic_block_with_address(branch_address) cfg.addEdge(v.vertexID, succv.vertexID) else: callee_name = self.get_callee_name(instruction.the_instruction) cfg.add_call_site(v.vertexID, callee_name) self.program.callg.addEdge(function_name, callee_name, v.vertexID) predID = v.vertexID elif v in self.function_to_jump_table_basic_blocks[function_name]: pass else: predID = v.vertexID
def __initialise (self): self.__currentContextv = None self.__currentCFG = None self.__currentLNT = None self.__predBB = None self.__currentBB = None self.__currentHeaderID = None self.__currentPathg = None self.__time1 = None self.__stack = [] self.__contextg = self._program.getContextGraph() rootv = self.__contextg.getVertex(self.__contextg.getRootID()) self.__rootCFG = self._program.getCFG(rootv.getName()) self.__firstAddr = self.__rootCFG.getFirstInstruction().getAddress() lastbb = self.__rootCFG.getVertex(self.__rootCFG.getExitID()) for instruction in reversed(lastbb.getInstructions()): if instruction.getOp() not in arm.armInstructionSet.Nops: self.__lastAddr = instruction.getAddress() break assert self.__lastAddr, "Unable to find last address" debug.debug_message("Start address of root function '%s' is %s" % (rootv.getName(), hex(self.__firstAddr)), 1) debug.debug_message("End address of root function '%s' is %s" % (rootv.getName(), hex(self.__lastAddr)), 1)
def __parse (self, traceFiles): runID = 0 for filename in traceFiles: parsing = False with gzip.open(filename, 'r') as f: runID += 1 self._allruns.add(runID) debug.debug_message("Analysing gem5 trace file '%s'" % filename, 1) for line in f: lexemes = shlex.split(line) PCLexeme = lexemes[-1] assert len(PCLexeme) == 11, "Unable to parse program counter %s" % PCLexeme try: time = int(lexemes[0][:-1]) PCLexeme = PCLexeme[5:] PC = int(PCLexeme, 16) if PC == self.__firstAddr: self.__time1 = time startTime = time parsing = True self.__currentContextv = self.__contextg.getVertex(self.__contextg.getRootID()) self.__currentCFG = self._program.getCFG(self.__currentContextv.getName()) self.__currentLNT = self._program.getLNT(self.__currentContextv.getName()) self.__currentPathg = self._program.getPathInfoGraph(self.__currentContextv.getName()) self.__predBB = None self.__currentBB = self.__currentCFG.getVertex(self.__currentCFG.getEntryID()) self._analyseCFGVertex(self.__currentPathg, self.__currentLNT, self.__currentBB.vertexID) if parsing: self.__parseAddress (time, PC, runID) if PC == self.__lastAddr: # Stop parsing parsing = False # Compute the HWMT totalTime = time - startTime self._longestTime = max(self._longestTime, totalTime) # Falsify conjectures self._endOfFunction(self.__currentCFG, self.__currentLNT, self.__currentPathg) except ValueError: debug.exit_message("Cannot cast %s into an integer: it is not a hexadecimal string" % PCLexeme)