def run_iteration( fn1: Callable, fn2: Callable, sig: inspect.Signature, space: StateSpace ) -> Tuple[Optional[VerificationStatus], Optional[BehaviorDiff]]: with NoTracing(): original_args = gen_args(sig) args1 = copy.deepcopy(original_args) args2 = copy.deepcopy(original_args) coverage_manager = measure_fn_coverage(fn1, fn2) with ExceptionFilter() as efilter, coverage_manager as coverage: result1 = describe_behavior(fn1, args1) result2 = describe_behavior(fn2, args2) space.detach_path() if result1 == result2 and args1 == args2: debug("Functions equivalent") return (VerificationStatus.CONFIRMED, None) debug("Functions differ") realized_args = {k: repr(v) for (k, v) in original_args.arguments.items()} post_execution_args1 = {k: repr(v) for k, v in args1.arguments.items()} post_execution_args2 = {k: repr(v) for k, v in args2.arguments.items()} diff = BehaviorDiff( realized_args, Result(repr(result1[0]), result1[1], post_execution_args1), Result(repr(result2[0]), result2[1], post_execution_args2), coverage(fn1), coverage(fn2), ) return (VerificationStatus.REFUTED, diff) if efilter.user_exc: debug( "User-level exception found", repr(efilter.user_exc[0]), efilter.user_exc[1] ) return (None, None)
def run_iteration(fn: Callable, sig: Signature, space: StateSpace) -> Optional[PathSummary]: with NoTracing(): args = gen_args(sig) pre_args = copy.deepcopy(args) ret = None with measure_fn_coverage(fn) as coverage, ExceptionFilter() as efilter: # coverage = lambda _: CoverageResult(set(), set(), 1.0) # with ExceptionFilter() as efilter: ret = fn(*args.args, **args.kwargs) space.detach_path() if efilter.user_exc is not None: exc = efilter.user_exc[0] debug("user-level exception found", repr(exc), *efilter.user_exc[1]) return PathSummary(pre_args, ret, type(exc), args, coverage(fn)) elif efilter.ignore: return None else: return PathSummary( deep_realize(pre_args), deep_realize(ret), None, deep_realize(args), coverage(fn), )
def test_isfinite(self): with standalone_statespace: with NoTracing(): x = SymbolicFloat("symfloat") self.assertTrue(math.isfinite(x)) self.assertTrue(math.isfinite(2.3)) self.assertFalse(math.isfinite(float("nan")))
def test_concrete_proxy_with_bad_hash(): class Penguin: def __hash__(self): return 42 / 0 with standalone_statespace as space: with NoTracing(): # (because this function resumes tracing) p = proxy_class_as_concrete(Penguin, "p") assert type(p) is object
def tiny_stack(tb: TracebackLike = None, **kw) -> str: with NoTracing(): if tb is None: frames: Iterable[ traceback.FrameSummary] = traceback.extract_stack()[:-1] elif isinstance(tb, TracebackType): frames = traceback.extract_tb(tb) else: frames = tb return _tiny_stack_frames(frames, **kw)
def debug(*a): if not _DEBUG: return with NoTracing(): stack = traceback.extract_stack() frame = stack[-2] indent = len(stack) - 3 print( "{:06.3f}|{}|{}() {}".format(time.monotonic(), " " * indent, frame.name, " ".join(map(str, a))), file=sys.stderr, )
def test_class_proxies_are_created_through_constructor(): class Penguin: can_swim: bool def __init__(self): self.can_swim = True with standalone_statespace as space: with NoTracing(): # (because following function resumes tracing) p = proxy_for_class(Penguin, "p") # `can_swim` is locked to True assert p.can_swim is True
def compare_results(fn: Callable, *a: object, **kw: object) -> ResultComparison: original_a = deepcopy(a) original_kw = deepcopy(kw) symbolic_result = summarize_execution(fn, a, kw) concrete_a = deep_realize(original_a) concrete_kw = deep_realize(original_kw) with NoTracing(): concrete_result = summarize_execution(fn, concrete_a, concrete_kw) ret = ResultComparison(symbolic_result, concrete_result) bool(ret) return ret
def _match(self, string, pos=0, endpos=None) -> Union[None, re.Match, _Match]: with NoTracing(): if type(string) is SymbolicStr: try: return _match_pattern(self, self.pattern, string, pos, endpos) except ReUnhandled as e: debug( "Unable to symbolically analyze regular expression:", self.pattern, e, ) if endpos is None: return _orig_match(self, realize(string), pos) else: return _orig_match(self, realize(string), pos, endpos)
def _fullmatch(self, string, pos=0, endpos=None): with NoTracing(): if type(string) is SymbolicStr: try: return _match_pattern(self, self.pattern + r"\Z", string, pos, endpos) except ReUnhandled as e: debug( "Unable to symbolically analyze regular expression:", self.pattern, e, ) if endpos is None: return re.Pattern.fullmatch(self, realize(string), pos) else: return re.Pattern.fullmatch(self, realize(string), pos, endpos)
def choose_possible(self, expr: z3.ExprRef, favor_true=False) -> bool: with NoTracing(): if time.monotonic() > self.execution_deadline: debug( "Path execution timeout after making ", len(self.choices_made), " choices.", ) raise PathTimeout notexpr = z3.Not(expr) if self.search_position.is_stem(): self.search_position = self.search_position.grow_into( WorstResultNode(self._random, expr, self.solver)) self.search_position = self.search_position.simplify() node = self.search_position # NOTE: format_stack() is more human readable, but it pulls source file contents, # so it is (1) slow, and (2) unstable when source code changes while we are checking. statedesc = "\n".join(map(str, traceback.extract_stack(limit=8))) assert isinstance(node, SearchTreeNode) if node.statehash is None: node.statehash = statedesc else: if node.statehash != statedesc: debug(" *** Begin Not Deterministic Debug *** ") debug(" First state: ", len(node.statehash)) debug(node.statehash) debug(" Current state: ", len(statedesc)) debug(statedesc) debug(" Decision points prior to this:") for choice in self.choices_made: debug(" ", choice) debug(" Stack Diff: ") import difflib debug("\n".join( difflib.context_diff(node.statehash.split("\n"), statedesc.split("\n")))) debug(" *** End Not Deterministic Debug *** ") raise NotDeterministic() choose_true, stem = node.choose(favor_true=favor_true) assert isinstance(self.search_position, SearchTreeNode) self.choices_made.append(self.search_position) self.search_position = stem expr = expr if choose_true else notexpr debug("SMT chose:", expr) self.add(expr) return choose_true
def find_model_value(self, expr: z3.ExprRef) -> object: with NoTracing(): while True: if self.search_position.is_stem(): self.search_position = self.search_position.grow_into( ModelValueNode(self._random, expr, self.solver)) node = self.search_position.simplify() assert isinstance(node, ModelValueNode) (chosen, next_node) = node.choose(favor_true=True) self.choices_made.append(node) self.search_position = next_node if chosen: self.solver.add(expr == node.condition_value) ret = model_value_to_python(node.condition_value) if in_debug(): debug("SMT realized symbolic:", expr, "==", repr(ret)) debug("Realized at", test_stack()) return ret else: self.solver.add(expr != node.condition_value)
def test_immutable_concrete_proxy(): class Penguin: _can_swim: bool def __init__(self): self._can_swim = True class ImmutablePenguin(Penguin): # This hash definition signals immutability to CrossHair def __hash__(self): return self._can_swim with standalone_statespace as space: with NoTracing(): # (because this function resumes tracing) mut = proxy_class_as_concrete(Penguin, "mut") immut = proxy_class_as_concrete(ImmutablePenguin, "immut") # `can_swim` is locked to True in the immutable version, but # can be either in the mutable case. assert space.is_possible((mut._can_swim == False).var) assert space.is_possible((mut._can_swim == True).var) assert immut._can_swim is True
def detach_path(self, exc: Optional[Exception] = None) -> None: """ Mark the current path exhausted. Also verifies all deferred assumptions. After detaching, the space may continue to be used (for example, to print realized symbolics). """ if isinstance(exc, NotDeterministic): # NotDeterministic is a user-reportable error, but it isn't valid to try # and use the statespace after it's detected return for description, checker in self._deferred_assumptions: if not prefer_true(checker()): raise IgnoreAttempt("deferred assumption failed: " + description) with NoTracing(): assert self._search_position.is_stem() node = self._search_position.grow_into(DeatchedPathNode()) assert node.child.is_stem() self.choices_made.append(node) self._search_position = node.child
def debug(*a): """ Print debugging information in CrossHair's nested log output. Arguments are serialized with ``str()`` and printed when running in CrossHair's verbose mode. Avoid passing symbolic values, as taking the string of a symbolic will change the path exploration that CrossHair normally takes, leading to different outcomes in verbose and non-verbose mode. """ if not _DEBUG: return with NoTracing(): stack = traceback.extract_stack() frame = stack[-2] indent = len(stack) - 3 print( "{:06.3f}|{}|{}() {}".format(time.monotonic(), " " * indent, frame.name, " ".join(map(str, a))), file=sys.stderr, )
def find_key_in_heap( self, ref: z3.ExprRef, typ: Type, proxy_generator: Callable[[Type], object], snapshot: SnapshotRef = SnapshotRef(-1), ) -> object: with NoTracing(): for (curref, curtyp, curval) in itertools.chain(*self.heaps[snapshot:]): could_match = dynamic_typing.unify(curtyp, typ) if not could_match: continue if self.smt_fork(curref == ref): debug( "HEAP key lookup ", ref, ": Found existing. ", "type:", name_of_type(type(curval)), "id:", id(curval) % 1000, ) return curval ret = proxy_generator(typ) debug( "HEAP key lookup ", ref, ": Created new. ", "type:", name_of_type(type(ret)), "id:", id(ret) % 1000, ) self.add_value_to_heaps(ref, typ, ret) return ret
def _compile(*a): # Symbolic regexes aren't supported, and it's expensive to perform compilation # with tracing enabled. with NoTracing(): return re.compile(*deep_realize(a))