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. */
12 #include <xbt/dynar.h>
13 #include <xbt/dynar.hpp>
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"
28 #include "src/xbt/mmalloc/mmprivate.h"
30 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_safety, mc,
31 "Logging specific to MC safety verification ");
36 static int is_exploration_stack_state(mc_state_t current_state){
39 mc_state_t stack_state;
40 for(item = xbt_fifo_get_first_item(mc_stack); item != nullptr; item = xbt_fifo_get_next_item(item)) {
41 stack_state = (mc_state_t) xbt_fifo_get_item_content(item);
42 if(snapshot_compare(stack_state, current_state) == 0){
43 XBT_INFO("Non-progressive cycle : state %d -> state %d", stack_state->num, current_state->num);
50 int SafetyChecker::run()
54 char *req_str = nullptr;
56 smx_simcall_t req = nullptr;
57 mc_state_t state = nullptr, prev_state = NULL, next_state = NULL;
58 xbt_fifo_item_t item = nullptr;
59 std::unique_ptr<simgrid::mc::VisitedState> visited_state = nullptr;
61 while (xbt_fifo_size(mc_stack) > 0) {
63 /* Get current state */
64 state = (mc_state_t)xbt_fifo_get_item_content(xbt_fifo_get_first_item(mc_stack));
66 XBT_DEBUG("**************************************************");
68 ("Exploration depth=%d (state=%p, num %d)(%u interleave, user_max_depth %d)",
69 xbt_fifo_size(mc_stack), state, state->num,
70 MC_state_interleave_size(state), user_max_depth_reached);
72 /* Update statistics */
73 mc_stats->visited_states++;
75 /* If there are processes to interleave and the maximum depth has not been reached
76 then perform one step of the exploration algorithm */
77 if (xbt_fifo_size(mc_stack) <= _sg_mc_max_depth && !user_max_depth_reached
78 && (req = MC_state_get_request(state, &value)) && visited_state == nullptr) {
80 req_str = simgrid::mc::request_to_string(req, value, simgrid::mc::RequestType::simix);
81 XBT_DEBUG("Execute: %s", req_str);
84 if (dot_output != nullptr)
85 req_str = simgrid::mc::request_get_dot_output(req, value);
87 MC_state_set_executed_request(state, req, value);
88 mc_stats->executed_transitions++;
90 // TODO, bundle both operations in a single message
91 // MC_execute_transition(req, value)
93 /* Answer the request */
94 simgrid::mc::handle_simcall(req, value);
95 mc_model_checker->wait_for_requests();
97 /* Create the new expanded state */
98 next_state = MC_state_new();
100 if(_sg_mc_termination && is_exploration_stack_state(next_state)){
101 MC_show_non_termination();
102 return SIMGRID_MC_EXIT_NON_TERMINATION;
105 if (_sg_mc_visited == 0 || (visited_state = simgrid::mc::is_visited_state(next_state, true)) == nullptr) {
107 /* Get an enabled process and insert it in the interleave set of the next state */
108 for (auto& p : mc_model_checker->process().simix_processes())
109 if (simgrid::mc::process_is_enabled(&p.copy)) {
110 MC_state_interleave_process(next_state, &p.copy);
111 if (reductionMode_ != simgrid::mc::ReductionMode::none)
115 if (dot_output != nullptr)
116 std::fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n", state->num, next_state->num, req_str);
118 } else if (dot_output != nullptr)
119 std::fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n", state->num, visited_state->other_num == -1 ? visited_state->num : visited_state->other_num, req_str);
122 xbt_fifo_unshift(mc_stack, next_state);
124 if (dot_output != nullptr)
127 /* Let's loop again */
129 /* The interleave set is empty or the maximum depth is reached, let's back-track */
132 if ((xbt_fifo_size(mc_stack) > _sg_mc_max_depth) || user_max_depth_reached || visited_state != nullptr) {
134 if (user_max_depth_reached && visited_state == nullptr)
135 XBT_DEBUG("User max depth reached !");
136 else if (visited_state == nullptr)
137 XBT_WARN("/!\\ Max depth reached ! /!\\ ");
139 XBT_DEBUG("State already visited (equal to state %d), exploration stopped on this path.", visited_state->other_num == -1 ? visited_state->num : visited_state->other_num);
142 XBT_DEBUG("There are no more processes to interleave. (depth %d)", xbt_fifo_size(mc_stack) + 1);
144 /* Trash the current state, no longer needed */
145 xbt_fifo_shift(mc_stack);
146 XBT_DEBUG("Delete state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
147 MC_state_delete(state, !state->in_visited_states ? 1 : 0);
149 visited_state = nullptr;
151 /* Check for deadlocks */
152 if (mc_model_checker->checkDeadlock()) {
153 MC_show_deadlock(nullptr);
154 return SIMGRID_MC_EXIT_DEADLOCK;
157 /* Traverse the stack backwards until a state with a non empty interleave
158 set is found, deleting all the states that have it empty in the way.
159 For each deleted state, check if the request that has generated it
160 (from it's predecesor state), depends on any other previous request
161 executed before it. If it does then add it to the interleave set of the
162 state that executed that previous request. */
164 while ((state = (mc_state_t) xbt_fifo_shift(mc_stack))) {
165 if (reductionMode_ == simgrid::mc::ReductionMode::dpor) {
166 req = MC_state_get_internal_request(state);
167 if (req->call == SIMCALL_MUTEX_LOCK || req->call == SIMCALL_MUTEX_TRYLOCK)
168 xbt_die("Mutex is currently not supported with DPOR, "
169 "use --cfg=model-check/reduction:none");
170 const smx_process_t issuer = MC_smx_simcall_get_issuer(req);
171 xbt_fifo_foreach(mc_stack, item, prev_state, mc_state_t) {
172 if (reductionMode_ != simgrid::mc::ReductionMode::none
173 && simgrid::mc::request_depend(req, MC_state_get_internal_request(prev_state))) {
174 if (XBT_LOG_ISENABLED(mc_safety, xbt_log_priority_debug)) {
175 XBT_DEBUG("Dependent Transitions:");
176 smx_simcall_t prev_req = MC_state_get_executed_request(prev_state, &value);
177 req_str = simgrid::mc::request_to_string(prev_req, value, simgrid::mc::RequestType::internal);
178 XBT_DEBUG("%s (state=%d)", req_str, prev_state->num);
180 prev_req = MC_state_get_executed_request(state, &value);
181 req_str = simgrid::mc::request_to_string(prev_req, value, simgrid::mc::RequestType::executed);
182 XBT_DEBUG("%s (state=%d)", req_str, state->num);
186 if (!MC_state_process_is_done(prev_state, issuer))
187 MC_state_interleave_process(prev_state, issuer);
189 XBT_DEBUG("Process %p is in done set", req->issuer);
193 } else if (req->issuer == MC_state_get_internal_request(prev_state)->issuer) {
195 XBT_DEBUG("Simcall %d and %d with same issuer", req->call, MC_state_get_internal_request(prev_state)->call);
200 const smx_process_t previous_issuer = MC_smx_simcall_get_issuer(MC_state_get_internal_request(prev_state));
201 XBT_DEBUG("Simcall %d, process %lu (state %d) and simcall %d, process %lu (state %d) are independant",
202 req->call, issuer->pid, state->num,
203 MC_state_get_internal_request(prev_state)->call,
204 previous_issuer->pid,
211 if (MC_state_interleave_size(state) && xbt_fifo_size(mc_stack) < _sg_mc_max_depth) {
212 /* We found a back-tracking point, let's loop */
213 XBT_DEBUG("Back-tracking to state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
214 xbt_fifo_unshift(mc_stack, state);
216 XBT_DEBUG("Back-tracking to state %d at depth %d done", state->num, xbt_fifo_size(mc_stack));
219 XBT_DEBUG("Delete state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
220 MC_state_delete(state, !state->in_visited_states ? 1 : 0);
226 XBT_INFO("No property violation found.");
227 MC_print_statistics(mc_stats);
228 return SIMGRID_MC_EXIT_SUCCESS;
231 void SafetyChecker::init()
233 reductionMode_ = simgrid::mc::reduction_mode;
234 if( _sg_mc_termination)
235 reductionMode_ = simgrid::mc::ReductionMode::none;
236 else if (reductionMode_ == simgrid::mc::ReductionMode::unset)
237 reductionMode_ = simgrid::mc::ReductionMode::dpor;
239 if (_sg_mc_termination)
240 XBT_INFO("Check non progressive cycles");
242 XBT_INFO("Check a safety property");
243 mc_model_checker->wait_for_requests();
245 XBT_DEBUG("Starting the safety algorithm");
247 /* Create exploration stack */
248 mc_stack = xbt_fifo_new();
250 simgrid::mc::visited_states.clear();
252 mc_state_t initial_state = MC_state_new();
254 XBT_DEBUG("**************************************************");
255 XBT_DEBUG("Initial state");
257 /* Wait for requests (schedules processes) */
258 mc_model_checker->wait_for_requests();
260 /* Get an enabled process and insert it in the interleave set of the initial state */
261 for (auto& p : mc_model_checker->process().simix_processes())
262 if (simgrid::mc::process_is_enabled(&p.copy)) {
263 MC_state_interleave_process(initial_state, &p.copy);
264 if (reductionMode_ != simgrid::mc::ReductionMode::none)
268 xbt_fifo_unshift(mc_stack, initial_state);
270 /* Save the initial state */
271 initial_global_state = xbt_new0(s_mc_global_t, 1);
272 initial_global_state->snapshot = simgrid::mc::take_snapshot(0);
275 SafetyChecker::SafetyChecker(Session& session) : Checker(session)
279 SafetyChecker::~SafetyChecker()