Пример #1
0
def test_cross_product_id():
    @timewinder.object
    class A:
        def __init__(self):
            self.a = 1
            self.b = 2

    s = StateController(MemoryCAS())
    s.mount("a", A())
    all_states = s.commit()
    states = list(all_states)
    assert len(states) == 1
Пример #2
0
def test_cross_product_b():
    @timewinder.object
    class A:
        def __init__(self):
            self.a = gens.InRange(0, 2)
            self.b = gens.InRange(0, 3)

    s = StateController(MemoryCAS())
    s.mount("a", A())
    s.mount("b", A())
    all_states = s.commit()
    states = list(all_states)
    assert len(states) == 144
Пример #3
0
 def __init__(self, *, objects=None, threads: List = None, specs: List = None):
     self.state_controller = StateController(MemoryCAS())
     if objects is not None:
         for m in objects:
             self.state_controller.mount(m._name, m)
     if threads is None or len(threads) == 0:
         # We're doing static state space analysis
         raise NotImplementedError("Static analysis TBD")
     else:
         self.threads = _prepare_threads(threads)
         for i, t in enumerate(self.threads):
             t.on_register_evaluator(i)
             self.state_controller.mount(f"_thread_{i}", t)
     self.specs = _prepare_specs(specs)
     self._evaled_states: Set[bytes] = set()
     self._stats: EvaluatorStats = EvaluatorStats()
Пример #4
0
class Evaluator:
    def __init__(self,
                 *,
                 objects=None,
                 threads: List = None,
                 specs: List = None):
        self.state_controller = StateController(MemoryCAS())
        if objects is not None:
            for m in objects:
                self.state_controller.mount(m._name, m)
        if threads is None or len(threads) == 0:
            # We're doing static state space analysis
            raise NotImplementedError("Static analysis TBD")
        else:
            self.threads = _prepare_threads(threads)
            for i, t in enumerate(self.threads):
                self.state_controller.mount(f"_thread_{i}", t)
        self.specs = _prepare_specs(specs)
        self._evaled_states: Set[bytes] = set()
        self._stats: EvaluatorStats = EvaluatorStats()

    def _initialize_evaluation(self):
        self._stats = EvaluatorStats()
        preds: List[List[Predicate]] = [s.get_predicates() for s in self.specs]
        # Flatten the list
        self.preds = [item for sub in preds for item in sub]
        for i, p in enumerate(self.preds):
            p.set_index(i)

    def evaluate(self, steps: Optional[int] = 5):
        self._initialize_evaluation()
        initial_hashes = self.state_controller.commit()
        next_queue = []
        for h in initial_hashes:
            pred_traces = [TTrace([]) for i in self.preds]
            next_queue.append(
                EvalThunk(trace=[], hashes=[h], predicate_traces=pred_traces))

        if steps is None:
            steps = 2**30
        for step in range(1, steps + 1):
            state_queue = next_queue
            next_queue = []
            if len(state_queue) == 0:
                print("No more states to evaluate")
                break
            print(f"Evaluating Step {step} ({len(state_queue)} states)...")
            self._stats.steps += 1
            for thunk in state_queue:
                new_runs = self._eval_state(thunk)
                next_queue.extend(new_runs)

    def _eval_preds(self, t: EvalThunk):
        for i, p in enumerate(self.preds):
            b = p.check(self.state_controller)
            t.predicate_traces[i].append(b)

    def _should_stutter(self) -> bool:
        # TODO: Fairness, etc.
        return True

    def _check_liveness(self, spec, t: EvalThunk):
        if self._should_stutter():
            trace = spec.eval_traces(t.predicate_traces)
            ok = trace[0]
            if not ok:
                err = StutterConstraintError(str(spec))
                err.thunk = t
                err.state = self.state_controller.tree
                raise err

    def _check_safety(self, spec, t: EvalThunk):
        trace = spec.eval_traces(t.predicate_traces)
        ok = trace[0]
        if not ok:
            err = ConstraintError(str(spec))
            err.thunk = t
            err.state = self.state_controller.tree
            raise err

    def _check_constraints(self, t: EvalThunk):
        for spec in self.specs:
            if spec.is_liveness():
                self._check_liveness(spec, t)
            else:
                self._check_safety(spec, t)

    def _eval_state(self, t: EvalThunk) -> List[EvalThunk]:
        if t.state_hash().bytes in self._evaled_states:
            return []
        self._stats.states += 1
        self._evaled_states.add(t.state_hash().bytes)
        self.state_controller.restore(t.state_hash())
        self._eval_preds(t)
        try:
            self._check_constraints(t)
        except ConstraintError as e:
            raise e
        if len(t.must_run) == 0:
            runnable_threads = [
                i for i, t in enumerate(self.threads) if t.can_execute()
            ]
        else:
            runnable_threads = t.must_run

        if len(runnable_threads) == 0:
            self._stats.final_states += 1
            # TODO: Check for deadlocking when awaiting
            return []
        return self._execute_threads(runnable_threads, t)

    def _execute_threads(self, thread_ids: List[int], t: EvalThunk):
        pre_restored = True
        out = []
        for thread_id in thread_ids:
            if pre_restored:
                pre_restored = False
            else:
                self.state_controller.restore(t.state_hash())
            new_thunk = t.clone()
            new_thunk.must_run = []
            new_thunk.trace.append(thread_id)
            thread = self.threads[thread_id]
            self._stats.thread_executions += 1
            cont = thread.execute(self.state_controller)
            next_hashes = self.state_controller.commit()
            for h in next_hashes:
                t_with_hash = new_thunk.clone()
                t_with_hash.hashes.append(h)
                if cont.fairness == Fairness.IMMEDIATE:
                    t_with_hash.must_run = [thread_id]
                out.append(t_with_hash)
        return out

    def replay_thunk(self, t: EvalThunk):
        print("Initial State:")
        self.state_controller.restore(t.initial_hash())
        print(self.state_controller.state_to_str())
        prev_hash = t.initial_hash()
        step = 0
        for tid, hash in zip(t.trace, t.hashes[1:]):
            step += 1
            print(f"Step {step}, thread {tid} executes")
            print(f"{prev_hash.hex()[:7]} -- {tid} --> {hash.hex()[:7]}")
            print("*" * 26)
            print("State: ")
            self.state_controller.restore(hash)
            print(self.state_controller.state_to_str())
            prev_hash = hash

    def _print_state_space(self):
        self.state_controller.cas.debug_print()

    @property
    def stats(self) -> EvaluatorStats:
        s = copy(self._stats)
        s.cas_objects = self.state_controller.cas.size()
        return s