1 /* Copyright (c) 2016-2023. The SimGrid Team. All rights reserved. */
3 /* This program is free software; you can redistribute it and/or modify it
4 * under the terms of the license (GNU LGPL) which comes with this package. */
6 #include "src/mc/explo/UdporChecker.hpp"
7 #include "src/mc/api/State.hpp"
8 #include <xbt/asserts.h>
11 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_udpor, mc, "Logging specific to verification using UDPOR");
13 namespace simgrid::mc::udpor {
15 UdporChecker::UdporChecker(const std::vector<char*>& args) : Exploration(args)
20 void UdporChecker::run()
22 XBT_INFO("Starting a UDPOR exploration");
23 // NOTE: `A`, `D`, and `C` are derived from the
24 // original UDPOR paper [1], while `prev_exC` arises
25 // from the incremental computation of ex(C) from [3]
28 // TODO: Move computing the root configuration into a method on the Unfolding
29 auto initial_state = get_current_state();
30 auto root_event = std::make_unique<UnfoldingEvent>(EventSet(), std::make_shared<Transition>());
31 auto* root_event_handle = root_event.get();
32 unfolding.insert(std::move(root_event));
33 C_root.add_event(root_event_handle);
35 explore(C_root, EventSet(), EventSet(), std::move(initial_state), EventSet());
37 XBT_INFO("UDPOR exploration terminated -- model checking completed");
40 void UdporChecker::explore(const Configuration& C, EventSet D, EventSet A, std::unique_ptr<State> stateC,
43 // Perform the incremental computation of exC
45 // TODO: This method will have side effects on
46 // the unfolding, but the naming of the method
47 // suggests it is doesn't have side effects. We should
48 // reconcile this in the future
49 auto [exC, enC] = compute_extension(C, *stateC, prev_exC);
51 // If enC is a subset of D, intuitively
52 // there aren't any enabled transitions
53 // which are "worth" exploring since their
54 // exploration would lead to a so-called
55 // "sleep-set blocked" trace.
56 if (enC.is_subset_of(D)) {
58 if (not C.get_events().empty()) {
59 // Report information...
62 // When `en(C)` is empty, intuitively this means that there
63 // are no enabled transitions that can be executed from the
64 // state reached by `C` (denoted `state(C)`), i.e. by some
65 // execution of the transitions in C obeying the causality
66 // relation. Here, then, we would be in a deadlock.
68 get_remote_app().check_deadlock();
74 // TODO: Add verbose logging about which event is being explored
76 UnfoldingEvent* e = select_next_unfolding_event(A, enC);
77 xbt_assert(e != nullptr, "\n\n****** INVARIANT VIOLATION ******\n"
78 "UDPOR guarantees that an event will be chosen at each point in\n"
79 "the search, yet no events were actually chosen\n"
80 "*********************************\n\n");
82 // Move the application into stateCe and actually make note of that state
83 move_to_stateCe(*stateC, *e);
84 auto stateCe = record_current_state();
93 // Explore(C + {e}, D, A \ {e})
94 explore(Ce, D, std::move(A), std::move(stateCe), std::move(exC));
99 // TODO: Determine a value of K to use or don't use it at all
100 constexpr unsigned K = 10;
101 if (auto J = compute_partial_alternative(D, C, K); !J.empty()) {
102 J.subtract(C.get_events());
104 // Before searching the "right half", we need to make
105 // sure the program actually reflects the fact
106 // that we are searching again from `stateC` (the recursive
107 // search moved the program into `stateCe`)
108 restore_program_state_to(*stateC);
110 // Explore(C, D + {e}, J \ C)
111 explore(C, D, std::move(J), std::move(stateC), std::move(prev_exC));
118 clean_up_explore(e, C, D);
121 std::tuple<EventSet, EventSet> UdporChecker::compute_extension(const Configuration& C, const State& stateC,
122 const EventSet& prev_exC) const
124 // See eqs. 5.7 of section 5.2 of [3]
125 // C = C' + {e_cur}, i.e. C' = C - {e_cur}
129 // ex(C) = ex(C' + {e_cur}) = ex(C') / {e_cur} +
130 // U{<a, K> : K is maximal, `a` depends on all of K, `a` enabled at K }
131 UnfoldingEvent* e_cur = C.get_latest_event();
132 EventSet exC = prev_exC;
135 for (const auto& [aid, actor_state] : stateC.get_actors_list()) {
136 for (const auto& transition : actor_state.get_enabled_transitions()) {
137 // First check for a specialized function that can compute the extension
138 // set "quickly" based on its type. Otherwise, fall back to computing
140 const auto specialized_extension_function = incremental_extension_functions.find(transition->type_);
141 if (specialized_extension_function != incremental_extension_functions.end()) {
142 exC.form_union((specialized_extension_function->second)(C, *transition));
144 exC.form_union(this->compute_extension_by_enumeration(C, *transition));
150 // TODO: Compute enC based on conflict relations
152 return std::tuple<EventSet, EventSet>(exC, enC);
155 EventSet UdporChecker::compute_extension_by_enumeration(const Configuration& C, const Transition& action) const
157 // Here we're computing the following:
159 // U{<a, K> : K is maximal, `a` depends on all of K, `a` enabled at K }
161 // where `a` is the `action` given to us.
162 EventSet incremental_exC;
164 // Compute the extension set here using the algorithm Martin described...
166 return incremental_exC;
169 void UdporChecker::move_to_stateCe(State& state, const UnfoldingEvent& e)
171 const aid_t next_actor = e.get_transition()->aid_;
173 // TODO: Add the trace if possible for reporting a bug
174 xbt_assert(next_actor >= 0, "\n\n****** INVARIANT VIOLATION ******\n"
175 "In reaching this execution path, UDPOR ensures that at least one\n"
176 "one transition of the state of an visited event is enabled, yet no\n"
177 "state was actually enabled. Please report this as a bug.\n"
178 "*********************************\n\n");
179 state.execute_next(next_actor);
182 void UdporChecker::restore_program_state_to(const State& stateC)
184 // TODO: Perform state regeneration in the same manner as is done
185 // in the DFSChecker.cpp
188 std::unique_ptr<State> UdporChecker::record_current_state()
190 auto next_state = this->get_current_state();
192 // In UDPOR, we care about all enabled transitions in a given state
193 next_state->mark_all_enabled_todo();
198 UnfoldingEvent* UdporChecker::select_next_unfolding_event(const EventSet& A, const EventSet& enC)
201 return *(enC.begin());
204 for (const auto& event : A) {
205 if (enC.contains(event)) {
212 EventSet UdporChecker::compute_partial_alternative(const EventSet& D, const Configuration& C, const unsigned k) const
214 // TODO: Compute k-partial alternatives using [2]
218 void UdporChecker::clean_up_explore(const UnfoldingEvent* e, const Configuration& C, const EventSet& D)
220 // TODO: Perform clean up here
223 RecordTrace UdporChecker::get_record_trace()
229 std::vector<std::string> UdporChecker::get_textual_trace()
231 // TODO: Topologically sort the events of the latest configuration
232 // and iterate through that topological sorting
233 std::vector<std::string> trace;
237 } // namespace simgrid::mc::udpor
239 namespace simgrid::mc {
241 Exploration* create_udpor_checker(const std::vector<char*>& args)
243 return new simgrid::mc::udpor::UdporChecker(args);
246 } // namespace simgrid::mc