def loadBoogieFile(fname: str) -> BoogieTraceLvl: """ Load a boogie file, asserting it contains a single function with a single loop. """ fun: Function = unique(list(Function.load(fname))) hdr: BB = unique(fun.loopHeaders()) # The variables to trace are all live variables at the loop header vs = list(livevars(fun)[(hdr, 0)]) # Make sure variable names are different modulo case assert len(vs) == len(set([var.lower() for var in vs])) # See if there is a .trace or a .hint file hint = None header_vals = None terminates = False try: (vs, header_vals) = readTrace(fname[:-4] + '.trace', fun.getTypeEnv()) hint = load(open(fname[:-4] + '.hint')) except Exception: #TODO (Dimo) IOError here instead? pass assert (header_vals is not None) return { 'variables': vs, 'data': [[[row[v] for v in vs] for row in header_vals], [], []], 'hint': hint, 'program': fun, 'loop': hdr }
def findLoopHeaderLabel(dotFile): g = unique(graph_from_dot_file(dotFile)) nodes = g.get_nodes() shapeList = [(n.get_shape(), n.get_label()) for n in nodes] doubleCircles = [t for t in shapeList if t[0] == '"doublecircle"'] # The loop header should be the unique node with a double circle shape # Its label will be a string of the form '"N[0-9]*\\n[0-9]"'. We care # just about the first [0-9]* part. return unique(doubleCircles)[1].split("\\n")[0][2:]
def tryAndVerify_impl( fun: Function, oldSoundInvs: Iterable[AstExpr], invs: Iterable[AstExpr], timeout: Optional[int] = None ) -> Tuple[List[Tuple[AstExpr, Violation]], List[Tuple[AstExpr, Violation]], Set[AstExpr], List[Violation]]: """ Wrapper around checkInvNetwork for the case of a function with a single loop and no pre- post- conditions. Returns a tuple (overfitted, nonind, sound, violations) where overfitted, nonind are each a list of tuples (inv, Violation) sound is a set of sound invariants violations is a (potentially empty) list of safety Violations for the sound invariants. """ loopHdr = unique(fun.loopHeaders()).label cps: InvNetwork = {loopHdr: set(oldSoundInvs).union(set(invs))} (overfitted, nonind, sound, violations) =\ filterCandidateInvariants(fun, AstTrue(), AstTrue(), cps, timeout) overfittedL = list(overfitted[loopHdr]) nonindInvsL = list(nonind[loopHdr]) soundL = sound[loopHdr] return overfittedL, nonindInvsL, soundL, violations
def loop_exit_bb(body_paths, bbs): loop_header_succ = set(bbs[body_paths[0][0]].successors) bbs_in_loop = set([p[1] for p in body_paths]) assert (len(loop_header_succ) == len(bbs_in_loop) + 1 ) # Singular exit node # TODO: Check that no other paths exit the loop (sanity) return unique(loop_header_succ.difference(bbs_in_loop))
def tryAndVerifyLvl( lvl: BoogieTraceLvl, userInvs: Iterable[AstExpr], otherInvs: Set[AstExpr], timeout: Optional[int] = None, \ useSplitters: bool = True, addSPs: bool = True, generalizeUserInvs: bool = False ) -> TryAndVerifyResult: """ Try and verify a given Lvl. lvl - level to verify userInvs - invariant candidate from the user otherInvs - invariant candidates from other sources (e.g. previous users). Note: We will keep userInvs and otherInvs separate in the results. timeout - if specified, the z3 timeout for each query useSplitters - if the level supports splitter predicates, whether to use them or not. addSPs - if true, try to propagte SP through the loop and add the results as invariants. For example if before the loop we have "assert n>0;" and n is not modified in the loop, we can determine that"n>0" holds through the loop and add that to our cnadidate invariants. generalizeUserInvs - if true, for any invariant I involving a constant C, where one of the variables V shown to the user was always equal to C in the traces, try substituting C with V. For example if in the level always n=4, and the user entered i<=4, the generalizaition would also try i<=n. """ fun: Function = lvl['program'] loopHdr: BB = unique(fun.loopHeaders()) partialInvs: List[AstExpr] = [ lvl['partialInv'] ] \ if ('partialInv' in lvl) and useSplitters else [] splitterPreds: List[AstExpr] = lvl['splitterPreds'] \ if ('splitterPreds' in lvl) and useSplitters else [ ] if (generalizeUserInvs): replMaps: List[ReplMap_T] = generalizeConstTraceVars(lvl) else: replMaps = [] # Push any SPs that are syntactically unmodified if (addSPs): sps = propagateUnmodifiedPreds(fun)[(loopHdr, 0)] if (sps is not None): otherInvs = otherInvs.union(set(sps)) return tryAndVerify(fun, splitterPreds, partialInvs, \ userInvs, otherInvs, replMaps, timeout)
def load(fname: str, lvlId: str) -> "FlowgameLevel": fun: Function = unique(list(Function.load(fname))) try: (vs, header_vals) = readTrace(fname[:-4] + '.trace', fun.getTypeEnv()) except: (vs, header_vals) = (None, None) if (header_vals is not None): assert len( fun.loopHeaders() ) == 1, "TODO: Not implemented trace format for multi-loop functions" return FlowgameLevel(lvlId, fun, header_vals, fname)
def getEnsamble( fun: Function, exec_limit: int, tryFind: int = 100, vargens: Optional[Iterator[Store]] = None ) -> Iterator[Tuple[List[Store], Set[str]]]: loopHdr: BB = unique(fun.loopHeaders()) traceVs: List[str] = list(livevars(fun)[(loopHdr, 0)]) # TODO: Actually check the types randF: RandF = lambda state, varName: randint(0, 100) choiceF: ChoiceF = lambda states: [choice(list(states))] if vargens is None: def candidatef() -> Iterator[Store]: # TODO: Make this typesafe w.r.t. Function type env while True: yield {v: randint(0, 30) for v in traceVs} vargens = candidatef() tried: Set[Tuple[int, ...]] = set() #TODO: Not a smart way for generating random start values. Fix. s = 0 print("Trying to find ", tryFind, " traces of length up to ", exec_limit) while s < tryFind: candidate = next(vargens) hashable = tuple(ccast(candidate[v], int) for v in traceVs) if hashable in tried: continue tried.add(hashable) found = False (active_traces, done_traces) = trace_n_from_start(fun, candidate, exec_limit, randF, choiceF) for trace in active_traces + done_traces: vals: List[Store] = [ store for ((bb, idx), store, status) in trace if bb == loopHdr and idx == 0 ] bbhit: Set[str] = set(bb.label for ((bb, idx), _, _) in trace) yield (vals, bbhit) found = True s += 1 if (s >= tryFind): break if (not found): s += 1
def loopInvSafetyCtrex( fun: Function, \ invs: Iterable[AstExpr], \ timeout: Optional[int] = None ) -> List[Store]: """ Given a candidate loop invariant inv find 'safety' counterexamples. I.e. find counterexamples to "inv ==> post" or "inv ==> assert". Returns a potentially empty set of environments (dicts) that the invariant should satisfy. """ loopHdr = unique(fun.loopHeaders()).label cps: InvNetwork = {loopHdr: set(invs)} violations = checkInvNetwork(fun, AstTrue(), AstTrue(), cps, timeout) return ([ x.endEnv() for x in violations if x.isSafety() and # Safety fail x.startBB() == loopHdr ]) # From Inv
def loopInvOverfittedCtrex( fun: Function, \ invs: Iterable[AstExpr], \ timeout: Optional[int] = None ) -> List[Store]: """ Given a candidate loop invariant inv find 'overfittedness' counterexamples. I.e. find counterexamples to "precondition ==> inv". Returns a potentially empty set of environments (dicts) that the invariant should satisfy. """ loopHdr = unique(fun.loopHeaders()).label cps: InvNetwork = {loopHdr: set(invs)} violations = checkInvNetwork(fun, AstTrue(), AstTrue(), cps, timeout) entryBB = fun.entry().label return ([ x.endEnv() for x in violations if x.isInductive() and # Implication fail x.startBB() == entryBB and # From entry x.endBB() == loopHdr ]) # To loop inv
def loadBoogieFile(fname, multiround): bbs = get_bbs(fname) ensureSingleExit(bbs) loop = unique(loops(bbs), "Cannot handle program with multiple loops:" + fname) # The variables to trace are all live variables at the loop header vs = list(livevars(bbs)[loop.loop_paths[0][0]]) # Make sure variable names are different modulo case assert len(vs) == len(set([var.lower() for var in vs])) # See if there is a .trace or a .hint file hint = None header_vals = None terminates = False try: (vs, header_vals) = readTrace(fname[:-4] + '.trace') hint = load(open(fname[:-4] + '.hint')) except Exception: #TODO (Dimo) IOError here instead? pass if (not header_vals): header_vals, terminates = _tryUnroll(loop, bbs, 0, 4, None, None) # Assume we have no tests with dead loops assert (header_vals != []) invTemplates = ["_sv_x<_sv_y", "_sv_x<=_sv_y", "_sv_x==_sc_c", \ "_sv_x==_sv_y", "_sv_x==0", "_sv_x<0"] invTemplates = [parseExprAst(inv) for inv in invTemplates] new_header_vals, new_terminates = \ getInitialData(loop, bbs, 4, invTemplates, [ "_sv_x", "_sv_y" ]) if (new_header_vals != None): header_vals = new_header_vals terminates = new_terminates writeTrace(fname[:-4] + ".trace", new_header_vals) return { 'variables': vs, 'data': [[[str(row[v]) for v in vs] for row in header_vals], [], []], 'exploration_state': [([str(header_vals[0][v]) for v in vs], len(header_vals), terminates)], 'hint': hint, 'goal': { "verify": True }, 'support_pos_ex': True, 'support_neg_ex': True, 'support_ind_ex': True, 'multiround': multiround, 'program': bbs, 'loop': loop }
print "Print split lvl ", lvl_name, "is missing splitterPreds" if (endsWithNumP.match(lvl_name) and not "partialInv" in lvl): print "Print split lvl ", lvl_name, "is missing partialInv" if (endsWithNumP.match(lvl_name)): origName = justEnd.split(lvl_name)[0] originalToSplitM[origName] = originalToSplitM.get(origName, []) +\ [ lvl_name ] splitToOriginal[lvl_name] = origName print "Checking splitter predicates non-ovrelapping" for origName in originalToSplitM.keys(): # For now we assume each split level has a single splitterPred preds = [ unique(lvls[x]["splitterPreds"]) for x in originalToSplitM[origName] ] for i in xrange(0, len(preds)): for j in xrange(i + 1, len(preds)): if (not unsatisfiable( expr_to_z3(bast.ast_and([preds[i], preds[j]]), AllIntTypeEnv()))): print "Predicates ", preds[i], "and", preds[j], "from split of ", \ origName, "are not independent" print "Checking conjunction of partial predicates is a solution" for origName in originalToSplitM.keys(): # For now we assume each split level has a single splitterPred preds = [lvls[x]["partialInv"] for x in originalToSplitM[origName]] if (len(preds) == 1):
def run_lvl(lvl_name: str) -> None: print(("== " + lvl_name)) lvl = lvls[lvl_name] spliter_pred = find_split_pred(find_original_boogie_file(lvl)) print(("Spliter pred: " + str(spliter_pred))) if spliter_pred: print("Skiping programs with spliter preds.") return # vs = lvl["variables"] # vs.sort() fun: Function = lvl["program"] loopHdr: BB = unique(fun.loopHeaders()) liveVars = list(livevars(fun)[(loopHdr, 0)]) liveVars.sort() allVars = list(fun.getTypeEnv().keys()) print(("live vars: " + str(liveVars))) store_gen = varproduct({v: gen(v, allVars) for v in allVars}) target_len = 7 tried: Set[Tuple[int, ...]] = set() #filt_f = lambda bbs, states: [choice(states)] filt_f = lambda states: states rand_f = lambda state, Id: randint(-1000, 1000) done = False while not done: try: starting_store = next(store_gen) except StopIteration: print("Exhausted candidate stores (not more in iteration)") break hashable = tuple(starting_store[v] for v in liveVars) if hashable in tried: print("Exhausted candidate stores (getting duplicate stores)") break tried.add(hashable) print(("Evaluating in starting store: " + str(starting_store))) (active, inactive) = trace_n_from_start(fun, starting_store, args.limit, rand_f, filt_f) traces = active + [t for t in inactive if finished(t[-1])] traces = [[st.store for st in tr if st.pc.bb == loopHdr] for tr in traces] for trace in traces: print(("Found trace of length " + str(len(trace)) + ": ")) print((trace_to_str(trace))) if spliter_pred: (t0, t1) = split_trace(trace, spliter_pred) if len(t0) >= target_len and len(t1) >= target_len: print(("Found split traces with lengths >= " + str(target_len) + ". Writing first " + str(target_len) + " elmts.")) write_trace_col_format(lvl, trace[0:target_len]) write_trace_col_format(lvl, t0[0:target_len], "0") write_trace_col_format(lvl, t1[0:target_len], "1") done = True break else: if len(trace) >= target_len: print(("Found trace with length >= " + str(target_len) + ". Writing first " + str(target_len) + " elmts.")) write_trace_col_format(lvl, trace[0:target_len]) done = True break print_comparison(lvl_name)
if (endsWithNumP.match(lvl_name) and not "splitterPreds" in lvl): print("Print split lvl ", lvl_name, "is missing splitterPreds") if (endsWithNumP.match(lvl_name) and not "partialInv" in lvl): print("Print split lvl ", lvl_name, "is missing partialInv") if (endsWithNumP.match(lvl_name)): origName = justEnd.split(lvl_name)[0] originalToSplitM[origName] = originalToSplitM.get(origName, []) +\ [ lvl_name ] splitToOriginal[lvl_name] = origName print("Checking splitter predicates non-ovrelapping") for origName in list(originalToSplitM.keys()): # For now we assume each split level has a single splitterPred preds = [ unique(lvls[x]["splitterPreds"]) for x in originalToSplitM[origName] ] for i in range(0, len(preds)): for j in range(i+1, len(preds)): if (not unsatisfiable(expr_to_z3(bast.ast_and([preds[i], preds[j]]), AllIntTypeEnv()))): print("Predicates ", preds[i], "and", preds[j], "from split of ", \ origName, "are not independent") print("Checking conjunction of partial predicates is a solution") for origName in list(originalToSplitM.keys()): # For now we assume each split level has a single splitterPred preds = [ lvls[x]["partialInv"] for x in originalToSplitM[origName] ] if (len(preds) == 1): print("Skipping ", origName, "- only 1 split level (no other side of split)")