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(std::move(C_root), EventSet(), EventSet(), std::move(initial_state), EventSet());
37 XBT_INFO("UDPOR exploration terminated -- model checking completed");
40 void UdporChecker::explore(Configuration C, EventSet D, EventSet A, std::unique_ptr<State> stateC, EventSet prev_exC)
42 // Perform the incremental computation of exC
44 // TODO: This method will have side effects on
45 // the unfolding, but the naming of the method
46 // suggests it is doesn't have side effects. We should
47 // reconcile this in the future
48 auto [exC, enC] = compute_extension(C, *stateC, prev_exC);
50 // If enC is a subset of D, intuitively
51 // there aren't any enabled transitions
52 // which are "worth" exploring since their
53 // exploration would lead to a so-called
54 // "sleep-set blocked" trace.
55 if (enC.is_subset_of(D)) {
57 if (C.get_events().size() > 0) {
59 // g_var::nb_traces++;
61 // TODO: Log here correctly
62 // XBT_DEBUG("\n Exploring executions: %d : \n", g_var::nb_traces);
67 // When `en(C)` is empty, intuitively this means that there
68 // are no enabled transitions that can be executed from the
69 // state reached by `C` (denoted `state(C)`), i.e. by some
70 // execution of the transitions in C obeying the causality
71 // relation. Here, then, we would be in a deadlock.
73 get_remote_app().check_deadlock();
79 // TODO: Add verbose logging about which event is being explored
81 UnfoldingEvent* e = select_next_unfolding_event(A, enC);
82 xbt_assert(e != nullptr, "\n\n****** INVARIANT VIOLATION ******\n"
83 "UDPOR guarantees that an event will be chosen at each point in\n"
84 "the search, yet no events were actually chosen\n"
85 "*********************************\n\n");
87 // Move the application into stateCe and actually make note of that state
88 move_to_stateCe(*stateC, *e);
89 auto stateCe = record_current_state();
98 // Explore(C + {e}, D, A \ {e})
99 explore(Ce, D, std::move(A), std::move(stateCe), std::move(exC));
104 // TODO: Determine a value of K to use or don't use it at all
105 constexpr unsigned K = 10;
106 auto J = compute_partial_alternative(D, C, K);
108 J.subtract(C.get_events());
110 // Before searching the "right half", we need to make
111 // sure the program actually reflects the fact
112 // that we are searching again from `stateC` (the recursive
113 // search moved the program into `stateCe`)
114 restore_program_state_to(*stateC);
116 // Explore(C, D + {e}, J \ C)
117 explore(C, D, std::move(J), std::move(stateC), std::move(prev_exC));
124 clean_up_explore(e, C, D);
127 std::tuple<EventSet, EventSet> UdporChecker::compute_extension(const Configuration& C, const State& stateC,
128 const EventSet& prev_exC) const
130 // See eqs. 5.7 of section 5.2 of [3]
131 // C = C' + {e_cur}, i.e. C' = C - {e_cur}
135 // ex(C) = ex(C' + {e_cur}) = ex(C') / {e_cur} + U{<a, K> : K is maximal, a depends on all of K, a enabled at K }
136 UnfoldingEvent* e_cur = C.get_latest_event();
137 EventSet exC = prev_exC;
140 for (const auto& [aid, actor_state] : stateC.get_actors_list()) {
141 for (const auto& transition : actor_state.get_enabled_transitions()) {
142 // First check for a specialized function that can compute the extension
143 // set "quickly" based on its type. Otherwise, fall back to computing
145 const auto specialized_extension_function = incremental_extension_functions.find(transition->type_);
146 if (specialized_extension_function != incremental_extension_functions.end()) {
147 exC.form_union((specialized_extension_function->second)(C, *transition));
149 exC.form_union(this->compute_extension_by_enumeration(C, *transition));
156 return std::tuple<EventSet, EventSet>(exC, enC);
159 EventSet UdporChecker::compute_extension_by_enumeration(const Configuration& C, const Transition& action) const
161 // TODO: Do something more interesting here
165 void UdporChecker::move_to_stateCe(State& state, const UnfoldingEvent& e)
167 const aid_t next_actor = e.get_transition()->aid_;
169 // TODO: Add the trace if possible for reporting a bug
170 xbt_assert(next_actor >= 0, "\n\n****** INVARIANT VIOLATION ******\n"
171 "In reaching this execution path, UDPOR ensures that at least one\n"
172 "one transition of the state of an visited event is enabled, yet no\n"
173 "state was actually enabled. Please report this as a bug.\n"
174 "*********************************\n\n");
175 state.execute_next(next_actor);
178 void UdporChecker::restore_program_state_to(const State& stateC)
180 // TODO: Perform state regeneration in the same manner as is done
181 // in the DFSChecker.cpp
184 std::unique_ptr<State> UdporChecker::record_current_state()
186 auto next_state = this->get_current_state();
188 // In UDPOR, we care about all enabled transitions in a given state
189 next_state->mark_all_enabled_todo();
194 UnfoldingEvent* UdporChecker::select_next_unfolding_event(const EventSet& A, const EventSet& enC)
197 return *(enC.begin());
200 for (const auto& event : A) {
201 if (enC.contains(event)) {
208 EventSet UdporChecker::compute_partial_alternative(const EventSet& D, const Configuration& C, const unsigned k) const
210 // TODO: Compute k-partial alternatives using [2]
214 void UdporChecker::clean_up_explore(const UnfoldingEvent* e, const Configuration& C, const EventSet& D)
216 // TODO: Perform clean up here
219 RecordTrace UdporChecker::get_record_trace()
225 std::vector<std::string> UdporChecker::get_textual_trace()
227 // TODO: Topologically sort the events of the latest configuration
228 // and iterate through that topological sorting
229 std::vector<std::string> trace;
233 } // namespace simgrid::mc::udpor
235 namespace simgrid::mc {
237 Exploration* create_udpor_checker(const std::vector<char*>& args)
239 return new simgrid::mc::udpor::UdporChecker(args);
242 } // namespace simgrid::mc