1 /* Copyright (c) 2016. The SimGrid Team.
2 * All rights reserved. */
4 /* This program is free software; you can redistribute it and/or modify it
5 * under the terms of the license (GNU LGPL) which comes with this package. */
15 #include <xbt/sysdep.h>
17 #include "src/mc/mc_state.h"
18 #include "src/mc/mc_request.h"
19 #include "src/mc/mc_safety.h"
20 #include "src/mc/mc_private.h"
21 #include "src/mc/mc_record.h"
22 #include "src/mc/mc_smx.h"
23 #include "src/mc/Client.hpp"
24 #include "src/mc/mc_exit.h"
25 #include "src/mc/Checker.hpp"
26 #include "src/mc/SafetyChecker.hpp"
27 #include "src/mc/VisitedState.hpp"
28 #include "src/mc/Transition.hpp"
30 #include "src/xbt/mmalloc/mmprivate.h"
32 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_safety, mc,
33 "Logging specific to MC safety verification ");
37 static void MC_show_non_termination(void)
39 XBT_INFO("******************************************");
40 XBT_INFO("*** NON-PROGRESSIVE CYCLE DETECTED ***");
41 XBT_INFO("******************************************");
42 XBT_INFO("Counter-example execution trace:");
43 for (auto& s : mc_model_checker->getChecker()->getTextualTrace())
44 XBT_INFO("%s", s.c_str());
45 MC_print_statistics(mc_stats);
48 static int snapshot_compare(simgrid::mc::State* state1, simgrid::mc::State* state2)
50 simgrid::mc::Snapshot* s1 = state1->system_state.get();
51 simgrid::mc::Snapshot* s2 = state2->system_state.get();
52 int num1 = state1->num;
53 int num2 = state2->num;
54 return snapshot_compare(num1, s1, num2, s2);
57 bool SafetyChecker::checkNonTermination(simgrid::mc::State* current_state)
59 for (auto i = stack_.rbegin(); i != stack_.rend(); ++i)
60 if (snapshot_compare(i->get(), current_state) == 0){
61 XBT_INFO("Non-progressive cycle : state %d -> state %d",
62 (*i)->num, current_state->num);
68 RecordTrace SafetyChecker::getRecordTrace() // override
71 for (auto const& state : stack_)
72 res.push_back(state->getRecordElement());
76 std::vector<std::string> SafetyChecker::getTextualTrace() // override
78 std::vector<std::string> trace;
79 for (auto const& state : stack_) {
80 int value = state->transition.argument;
81 smx_simcall_t req = &state->executed_req;
83 trace.push_back(simgrid::mc::request_to_string(
84 req, value, simgrid::mc::RequestType::executed));
89 int SafetyChecker::run()
93 while (!stack_.empty()) {
95 /* Get current state */
96 simgrid::mc::State* state = stack_.back().get();
98 XBT_DEBUG("**************************************************");
100 "Exploration depth=%zi (state=%p, num %d)(%zu interleave)",
101 stack_.size(), state, state->num,
102 state->interleaveSize());
104 mc_stats->visited_states++;
106 // The interleave set is empty or the maximum depth is reached,
108 smx_simcall_t req = nullptr;
109 if (stack_.size() > (std::size_t) _sg_mc_max_depth
110 || (req = MC_state_get_request(state)) == nullptr
111 || visitedState_ != nullptr) {
112 int res = this->backtrack();
119 int req_num = state->transition.argument;
121 // If there are processes to interleave and the maximum depth has not been
122 // reached then perform one step of the exploration algorithm.
123 XBT_DEBUG("Execute: %s",
124 simgrid::mc::request_to_string(
125 req, req_num, simgrid::mc::RequestType::simix).c_str());
128 if (dot_output != nullptr)
129 req_str = simgrid::mc::request_get_dot_output(req, req_num);
131 mc_stats->executed_transitions++;
133 // TODO, bundle both operations in a single message
134 // MC_execute_transition(req, req_num)
136 /* Answer the request */
137 mc_model_checker->handle_simcall(state->transition);
138 mc_model_checker->wait_for_requests();
140 /* Create the new expanded state */
141 std::unique_ptr<simgrid::mc::State> next_state =
142 std::unique_ptr<simgrid::mc::State>(MC_state_new());
144 if (_sg_mc_termination && this->checkNonTermination(next_state.get())) {
145 MC_show_non_termination();
146 return SIMGRID_MC_EXIT_NON_TERMINATION;
149 if (_sg_mc_visited == 0
150 || (visitedState_ = visitedStates_.addVisitedState(next_state.get(), true)) == nullptr) {
152 /* Get an enabled process and insert it in the interleave set of the next state */
153 for (auto& p : mc_model_checker->process().simix_processes())
154 if (simgrid::mc::process_is_enabled(&p.copy)) {
155 next_state->interleave(&p.copy);
156 if (reductionMode_ != simgrid::mc::ReductionMode::none)
160 if (dot_output != nullptr)
161 std::fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n",
162 state->num, next_state->num, req_str.c_str());
164 } else if (dot_output != nullptr)
165 std::fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n",
167 visitedState_->other_num == -1 ? visitedState_->num : visitedState_->other_num, req_str.c_str());
169 stack_.push_back(std::move(next_state));
172 XBT_INFO("No property violation found.");
173 MC_print_statistics(mc_stats);
174 initial_global_state = nullptr;
175 return SIMGRID_MC_EXIT_SUCCESS;
178 int SafetyChecker::backtrack()
180 if (stack_.size() > (std::size_t) _sg_mc_max_depth
181 || visitedState_ != nullptr) {
182 if (visitedState_ == nullptr)
183 XBT_WARN("/!\\ Max depth reached ! /!\\ ");
185 XBT_DEBUG("State already visited (equal to state %d),"
186 " exploration stopped on this path.",
187 visitedState_->other_num == -1 ? visitedState_->num : visitedState_->other_num);
189 XBT_DEBUG("There are no more processes to interleave. (depth %zi)",
194 visitedState_ = nullptr;
196 /* Check for deadlocks */
197 if (mc_model_checker->checkDeadlock()) {
199 return SIMGRID_MC_EXIT_DEADLOCK;
202 /* Traverse the stack backwards until a state with a non empty interleave
203 set is found, deleting all the states that have it empty in the way.
204 For each deleted state, check if the request that has generated it
205 (from it's predecesor state), depends on any other previous request
206 executed before it. If it does then add it to the interleave set of the
207 state that executed that previous request. */
209 while (!stack_.empty()) {
210 std::unique_ptr<simgrid::mc::State> state = std::move(stack_.back());
212 if (reductionMode_ == simgrid::mc::ReductionMode::dpor) {
213 smx_simcall_t req = &state->internal_req;
214 if (req->call == SIMCALL_MUTEX_LOCK || req->call == SIMCALL_MUTEX_TRYLOCK)
215 xbt_die("Mutex is currently not supported with DPOR, "
216 "use --cfg=model-check/reduction:none");
217 const smx_process_t issuer = MC_smx_simcall_get_issuer(req);
218 for (auto i = stack_.rbegin(); i != stack_.rend(); ++i) {
219 simgrid::mc::State* prev_state = i->get();
220 if (reductionMode_ != simgrid::mc::ReductionMode::none
221 && simgrid::mc::request_depend(req, &prev_state->internal_req)) {
222 if (XBT_LOG_ISENABLED(mc_safety, xbt_log_priority_debug)) {
223 XBT_DEBUG("Dependent Transitions:");
224 int value = prev_state->transition.argument;
225 smx_simcall_t prev_req = &prev_state->executed_req;
226 XBT_DEBUG("%s (state=%d)",
227 simgrid::mc::request_to_string(
228 prev_req, value, simgrid::mc::RequestType::internal).c_str(),
230 value = state->transition.argument;
231 prev_req = &state->executed_req;
232 XBT_DEBUG("%s (state=%d)",
233 simgrid::mc::request_to_string(
234 prev_req, value, simgrid::mc::RequestType::executed).c_str(),
238 if (!prev_state->processStates[issuer->pid].isDone())
239 prev_state->interleave(issuer);
241 XBT_DEBUG("Process %p is in done set", req->issuer);
245 } else if (req->issuer == prev_state->internal_req.issuer) {
247 XBT_DEBUG("Simcall %d and %d with same issuer", req->call, prev_state->internal_req.call);
252 const smx_process_t previous_issuer = MC_smx_simcall_get_issuer(&prev_state->internal_req);
253 XBT_DEBUG("Simcall %d, process %lu (state %d) and simcall %d, process %lu (state %d) are independant",
254 req->call, issuer->pid, state->num,
255 prev_state->internal_req.call,
256 previous_issuer->pid,
263 if (state->interleaveSize()
264 && stack_.size() < (std::size_t) _sg_mc_max_depth) {
265 /* We found a back-tracking point, let's loop */
266 XBT_DEBUG("Back-tracking to state %d at depth %zi",
267 state->num, stack_.size() + 1);
268 stack_.push_back(std::move(state));
269 simgrid::mc::replay(stack_);
270 XBT_DEBUG("Back-tracking to state %d at depth %zi done",
271 stack_.back()->num, stack_.size());
274 XBT_DEBUG("Delete state %d at depth %zi",
275 state->num, stack_.size() + 1);
278 return SIMGRID_MC_EXIT_SUCCESS;
281 void SafetyChecker::init()
283 reductionMode_ = simgrid::mc::reduction_mode;
284 if( _sg_mc_termination)
285 reductionMode_ = simgrid::mc::ReductionMode::none;
286 else if (reductionMode_ == simgrid::mc::ReductionMode::unset)
287 reductionMode_ = simgrid::mc::ReductionMode::dpor;
289 if (_sg_mc_termination)
290 XBT_INFO("Check non progressive cycles");
292 XBT_INFO("Check a safety property");
293 mc_model_checker->wait_for_requests();
295 XBT_DEBUG("Starting the safety algorithm");
297 std::unique_ptr<simgrid::mc::State> initial_state =
298 std::unique_ptr<simgrid::mc::State>(MC_state_new());
300 XBT_DEBUG("**************************************************");
301 XBT_DEBUG("Initial state");
303 /* Wait for requests (schedules processes) */
304 mc_model_checker->wait_for_requests();
306 /* Get an enabled process and insert it in the interleave set of the initial state */
307 for (auto& p : mc_model_checker->process().simix_processes())
308 if (simgrid::mc::process_is_enabled(&p.copy)) {
309 initial_state->interleave(&p.copy);
310 if (reductionMode_ != simgrid::mc::ReductionMode::none)
314 stack_.push_back(std::move(initial_state));
316 /* Save the initial state */
317 initial_global_state = std::unique_ptr<s_mc_global_t>(new s_mc_global_t());
318 initial_global_state->snapshot = simgrid::mc::take_snapshot(0);
321 SafetyChecker::SafetyChecker(Session& session) : Checker(session)
325 SafetyChecker::~SafetyChecker()
329 Checker* createSafetyChecker(Session& session)
331 return new SafetyChecker(session);