}
XBT_DEBUG("Replaced stack by %s", get_record_trace().to_string().c_str());
- if (reduction_mode_ == ReductionMode::sdpor) {
- execution_seq_ = sdpor::Execution();
+ if (reduction_mode_ == ReductionMode::sdpor || reduction_mode_ == ReductionMode::odpor) {
+ execution_seq_ = odpor::Execution();
// NOTE: The outgoing transition for the top-most
// state of the stack refers to that which was taken
// when reconstructing the Exploration for SDPOR.
for (auto iter = stack_.begin(); iter != stack_.end() - 1 and iter != stack_.end(); ++iter) {
const auto& state = *(iter);
- execution_seq_.push_transition(state->get_transition_out().get());
+ execution_seq_.push_transition(state->get_transition_out());
}
}
XBT_DEBUG("Additionally replaced corresponding SDPOR execution stack");
XBT_ERROR("/!\\ Max depth of %d reached! THIS WILL PROBABLY BREAK the dpor reduction /!\\",
_sg_mc_max_depth.get());
XBT_ERROR("/!\\ If bad things happen, disable dpor with --cfg=model-check/reduction:none /!\\");
- } else
+ } else if (reduction_mode_ == ReductionMode::sdpor || reduction_mode_ == ReductionMode::odpor) {
+ XBT_ERROR("/!\\ Max depth of %d reached! THIS **WILL** BREAK the reduction, which is not sound "
+ "when stopping at a fixed depth /!\\",
+ _sg_mc_max_depth.get());
+ XBT_ERROR("/!\\ If bad things happen, disable dpor with --cfg=model-check/reduction:none /!\\");
+ } else {
XBT_WARN("/!\\ Max depth reached ! /!\\ ");
+ }
this->backtrack();
continue;
}
}
#endif
+ if (reduction_mode_ == ReductionMode::odpor) {
+ // In the case of ODPOR, the wakeup tree for this
+ // state may be empty if we're exploring new territory
+ // (rather than following the partial execution of a
+ // wakeup tree). This corresponds to lines 9 to 13 of
+ // the ODPOR pseudocode
+ state->seed_wakeup_tree_if_needed(execution_seq_);
+ }
+
// Search for the next transition
- // next_transition returns a pair<aid_t, int> in case we want to consider multiple state (eg. during backtrack)
- auto [next, _] = state->next_transition_guided();
+ // next_transition returns a pair<aid_t, int>
+ // in case we want to consider multiple states (eg. during backtrack)
+ const aid_t next = reduction_mode_ == ReductionMode::odpor ? state->next_odpor_transition()
+ : std::get<0>(state->next_transition_guided());
if (next < 0) { // If there is no more transition in the current state, backtrack.
XBT_VERB("%lu actors remain, but none of them need to be interleaved (depth %zu).", state->get_actor_count(),
* done after next_state creation */
XBT_DEBUG("Marking Transition >>%s<< of process %ld done and adding it to the sleep set",
state->get_transition_out()->to_string().c_str(), state->get_transition_out()->aid_);
- state->add_sleep_set(state->get_transition_out()); // Actors are marked done when they are considerd in ActorState
+ state->add_sleep_set(state->get_transition_out()); // Actors are marked done when they are considered in ActorState
+
+ if (reduction_mode_ == ReductionMode::odpor) {
+ // With ODPOR, after taking a step forward, we must:
+ // 1. remove the subtree whose root is a single-process
+ // node of actor `next` (viz. the action we took) from
+ // the wakeup tree of `state`
+ //
+ // 2. assign a copy of that subtree to the next state
+ //
+ // The latter evidently must be done BEFORE the former
+ next_state->sprout_tree_from_parent_state();
+ state->remove_subtree_starting_with(next);
+
+ // TODO: Consider what we have to do to handle transitions
+ // with multiple possible executions. We probably have to re-insert
+ // something into `state` and make note of that for later (opened_states_)
+ }
/* DPOR persistent set procedure:
* for each new transition considered, check if it depends on any other previous transition executed before it
/**
* SDPOR Source Set Procedure:
*
- * Find "reversible races" in the current execution with respect
+ * Find "reversible races" in the current execution `E` with respect
* to the latest action `p`. For each such race, determine one thread
* not contained in the backtrack set at the "race point" `r` which
* "represents" the trace formed by first executing everything after
- * `r` and then `p` to flip the race
+ * `r` that doesn't depend on it (`v := notdep(r, E)`) and then `p` to
+ * flip the race.
+ *
+ * The intuition is that some subsequence of `v` may enable `p`, so
+ * we want to be sure that search "in that direction"
*/
- execution_seq_.push_transition(executed_transition.get());
-
+ const aid_t p = executed_transition->aid_;
+ execution_seq_.push_transition(std::move(executed_transition));
xbt_assert(execution_seq_.get_latest_event_handle().has_value(),
"No events are contained in the SDPOR/OPDPOR execution "
"even though one was just added");
- const aid_t p = executed_transition->aid_;
- const auto next_E_p = execution_seq_.get_latest_event_handle().value();
+ const auto next_E_p = execution_seq_.get_latest_event_handle().value();
for (const auto racing_event_handle : execution_seq_.get_racing_events_of(next_E_p)) {
// To determine if the race is reversible, we have to ensure
// that actor `p` running `next_E_p` (viz. the event such that
if (prev_state->is_actor_enabled(p)) {
// NOTE: To incorporate the idea of attempting to select the "best"
// backtrack point into SDPOR, instead of selecting the `first` initial,
- // we should instead compute all choices and decide which is bes
+ // we should instead compute all choices and decide which is best
const std::optional<aid_t> q =
- execution_seq_.get_first_ssdpor_initial_from(racing_event_handle, prev_state->get_backtrack_set());
+ execution_seq_.get_first_sdpor_initial_from(racing_event_handle, prev_state->get_backtrack_set());
if (q.has_value()) {
prev_state->consider_one(q.value());
opened_states_.emplace_back(std::move(prev_state));
}
}
}
+ } else if (reduction_mode_ == ReductionMode::odpor) {
+ // In the case of ODPOR, we simply observe the transition that was executed
+ // until we've reached a maximal trace
+ execution_seq_.push_transition(std::move(executed_transition));
}
// Before leaving that state, if the transition we just took can be taken multiple times, we
void DFSExplorer::backtrack()
{
+ if (const auto last_event = execution_seq_.get_latest_event_handle();
+ reduction_mode_ == ReductionMode::odpor and last_event.has_value()) {
+ /**
+ * ODPOR Race Detection Procedure:
+ *
+ * For each reversible race in the current execution, we
+ * note if there are any continuations `C` equivalent to that which
+ * would reverse the race that have already either a) been searched by ODPOR or
+ * b) been *noted* to be searched by the wakeup tree at the
+ * appropriate reversal point, either as `C` directly or
+ * an as equivalent to `C` ("eventually looks like C", viz. the `~_E`
+ * relation)
+ */
+ for (auto e_prime = static_cast<odpor::Execution::EventHandle>(0); e_prime <= last_event; e_prime++) {
+ for (const auto e : execution_seq_.get_racing_events_of(e_prime)) {
+ // To determine if the race is reversible, we have to ensure
+ // that actor `p` running `e_i` (viz. the event such that
+ // `racing_event -> (E_p) e_i` and no other event
+ // "happens-between" the two) is enabled in any equivalent
+ // execution where `racing_event` happens before `e_i`.
+ //
+ // Importantly, it is equivalent to checking if in ANY
+ // such equivalent execution sequence where `racing_event`
+ // happens-before `next_E_p` that `p` is enabled in `pre(racing_event, E.p)`.
+ // Thus it suffices to check THIS execution
+ //
+ // If the actor `p` is not enabled at s_[E'], it is not a *reversible* race
+ const aid_t p = execution_seq_.get_actor_with_handle(e_prime);
+ const std::shared_ptr<State> prev_state = stack_[e];
+ if (prev_state->is_actor_enabled(p)) {
+ const std::optional<odpor::PartialExecution> v = execution_seq_.get_odpor_extension_from(
+ e, e_prime, prev_state->get_sleeping_set(), prev_state->get_enabled_actors());
+ if (v.has_value()) {
+ prev_state->mark_path_interesting_for_odpor(v.value(), execution_seq_.get_prefix_before(e));
+ }
+ }
+ }
+ }
+ }
+
XBT_VERB("Backtracking from %s", get_record_trace().to_string().c_str());
XBT_DEBUG("%lu alternatives are yet to be explored:", opened_states_.size());
this->restore_stack(backtracking_point);
}
-DFSExplorer::DFSExplorer(const std::vector<char*>& args, bool with_dpor, bool need_memory_info)
+DFSExplorer::DFSExplorer(const std::vector<char*>& args, ReductionMode mode, bool need_memory_info)
: Exploration(args, need_memory_info || _sg_mc_termination
#if SIMGRID_HAVE_STATEFUL_MC
|| _sg_mc_checkpoint > 0
#endif
- )
+ )
+ , reduction_mode_(mode)
{
- if (with_dpor)
- reduction_mode_ = ReductionMode::dpor;
- else
- reduction_mode_ = ReductionMode::none;
-
if (_sg_mc_termination) {
- if (with_dpor) {
+ if (mode != ReductionMode::none) {
XBT_INFO("Check non progressive cycles (turning DPOR off)");
reduction_mode_ = ReductionMode::none;
} else {
opened_states_.emplace_back(stack_.back());
}
-Exploration* create_dfs_exploration(const std::vector<char*>& args, bool with_dpor)
+Exploration* create_dfs_exploration(const std::vector<char*>& args, ReductionMode mode)
{
- return new DFSExplorer(args, with_dpor);
+ return new DFSExplorer(args, mode);
}
} // namespace simgrid::mc