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 void MC_show_non_termination(void)
38 XBT_INFO("******************************************");
39 XBT_INFO("*** NON-PROGRESSIVE CYCLE DETECTED ***");
40 XBT_INFO("******************************************");
41 XBT_INFO("Counter-example execution trace:");
42 MC_dump_stack_safety(mc_stack);
43 MC_print_statistics(mc_stats);
46 static int snapshot_compare(mc_state_t state1, mc_state_t state2)
48 simgrid::mc::Snapshot* s1 = state1->system_state;
49 simgrid::mc::Snapshot* s2 = state2->system_state;
50 int num1 = state1->num;
51 int num2 = state2->num;
52 return snapshot_compare(num1, s1, num2, s2);
55 static int is_exploration_stack_state(mc_state_t current_state){
58 mc_state_t stack_state;
59 for(item = xbt_fifo_get_first_item(mc_stack); item != nullptr; item = xbt_fifo_get_next_item(item)) {
60 stack_state = (mc_state_t) xbt_fifo_get_item_content(item);
61 if(snapshot_compare(stack_state, current_state) == 0){
62 XBT_INFO("Non-progressive cycle : state %d -> state %d", stack_state->num, current_state->num);
69 int SafetyChecker::run()
73 char *req_str = nullptr;
75 smx_simcall_t req = nullptr;
76 mc_state_t state = nullptr, prev_state = NULL, next_state = NULL;
77 xbt_fifo_item_t item = nullptr;
78 std::unique_ptr<simgrid::mc::VisitedState> visited_state = nullptr;
80 while (xbt_fifo_size(mc_stack) > 0) {
82 /* Get current state */
83 state = (mc_state_t)xbt_fifo_get_item_content(xbt_fifo_get_first_item(mc_stack));
85 XBT_DEBUG("**************************************************");
87 ("Exploration depth=%d (state=%p, num %d)(%u interleave, user_max_depth %d)",
88 xbt_fifo_size(mc_stack), state, state->num,
89 MC_state_interleave_size(state), user_max_depth_reached);
91 /* Update statistics */
92 mc_stats->visited_states++;
94 /* If there are processes to interleave and the maximum depth has not been reached
95 then perform one step of the exploration algorithm */
96 if (xbt_fifo_size(mc_stack) <= _sg_mc_max_depth && !user_max_depth_reached
97 && (req = MC_state_get_request(state, &value)) && visited_state == nullptr) {
99 req_str = simgrid::mc::request_to_string(req, value, simgrid::mc::RequestType::simix);
100 XBT_DEBUG("Execute: %s", req_str);
103 if (dot_output != nullptr)
104 req_str = simgrid::mc::request_get_dot_output(req, value);
106 MC_state_set_executed_request(state, req, value);
107 mc_stats->executed_transitions++;
109 // TODO, bundle both operations in a single message
110 // MC_execute_transition(req, value)
112 /* Answer the request */
113 simgrid::mc::handle_simcall(req, value);
114 mc_model_checker->wait_for_requests();
116 /* Create the new expanded state */
117 next_state = MC_state_new();
119 if(_sg_mc_termination && is_exploration_stack_state(next_state)){
120 MC_show_non_termination();
121 return SIMGRID_MC_EXIT_NON_TERMINATION;
124 if (_sg_mc_visited == 0 || (visited_state = simgrid::mc::is_visited_state(next_state, true)) == nullptr) {
126 /* Get an enabled process and insert it in the interleave set of the next state */
127 for (auto& p : mc_model_checker->process().simix_processes())
128 if (simgrid::mc::process_is_enabled(&p.copy)) {
129 MC_state_interleave_process(next_state, &p.copy);
130 if (reductionMode_ != simgrid::mc::ReductionMode::none)
134 if (dot_output != nullptr)
135 std::fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n", state->num, next_state->num, req_str);
137 } else if (dot_output != nullptr)
138 std::fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n", state->num, visited_state->other_num == -1 ? visited_state->num : visited_state->other_num, req_str);
141 xbt_fifo_unshift(mc_stack, next_state);
143 if (dot_output != nullptr)
146 /* Let's loop again */
148 /* The interleave set is empty or the maximum depth is reached, let's back-track */
151 if ((xbt_fifo_size(mc_stack) > _sg_mc_max_depth) || user_max_depth_reached || visited_state != nullptr) {
153 if (user_max_depth_reached && visited_state == nullptr)
154 XBT_DEBUG("User max depth reached !");
155 else if (visited_state == nullptr)
156 XBT_WARN("/!\\ Max depth reached ! /!\\ ");
158 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);
161 XBT_DEBUG("There are no more processes to interleave. (depth %d)", xbt_fifo_size(mc_stack) + 1);
163 /* Trash the current state, no longer needed */
164 xbt_fifo_shift(mc_stack);
165 XBT_DEBUG("Delete state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
166 MC_state_delete(state, !state->in_visited_states ? 1 : 0);
168 visited_state = nullptr;
170 /* Check for deadlocks */
171 if (mc_model_checker->checkDeadlock()) {
173 return SIMGRID_MC_EXIT_DEADLOCK;
176 /* Traverse the stack backwards until a state with a non empty interleave
177 set is found, deleting all the states that have it empty in the way.
178 For each deleted state, check if the request that has generated it
179 (from it's predecesor state), depends on any other previous request
180 executed before it. If it does then add it to the interleave set of the
181 state that executed that previous request. */
183 while ((state = (mc_state_t) xbt_fifo_shift(mc_stack))) {
184 if (reductionMode_ == simgrid::mc::ReductionMode::dpor) {
185 req = MC_state_get_internal_request(state);
186 if (req->call == SIMCALL_MUTEX_LOCK || req->call == SIMCALL_MUTEX_TRYLOCK)
187 xbt_die("Mutex is currently not supported with DPOR, "
188 "use --cfg=model-check/reduction:none");
189 const smx_process_t issuer = MC_smx_simcall_get_issuer(req);
190 xbt_fifo_foreach(mc_stack, item, prev_state, mc_state_t) {
191 if (reductionMode_ != simgrid::mc::ReductionMode::none
192 && simgrid::mc::request_depend(req, MC_state_get_internal_request(prev_state))) {
193 if (XBT_LOG_ISENABLED(mc_safety, xbt_log_priority_debug)) {
194 XBT_DEBUG("Dependent Transitions:");
195 smx_simcall_t prev_req = MC_state_get_executed_request(prev_state, &value);
196 req_str = simgrid::mc::request_to_string(prev_req, value, simgrid::mc::RequestType::internal);
197 XBT_DEBUG("%s (state=%d)", req_str, prev_state->num);
199 prev_req = MC_state_get_executed_request(state, &value);
200 req_str = simgrid::mc::request_to_string(prev_req, value, simgrid::mc::RequestType::executed);
201 XBT_DEBUG("%s (state=%d)", req_str, state->num);
205 if (!MC_state_process_is_done(prev_state, issuer))
206 MC_state_interleave_process(prev_state, issuer);
208 XBT_DEBUG("Process %p is in done set", req->issuer);
212 } else if (req->issuer == MC_state_get_internal_request(prev_state)->issuer) {
214 XBT_DEBUG("Simcall %d and %d with same issuer", req->call, MC_state_get_internal_request(prev_state)->call);
219 const smx_process_t previous_issuer = MC_smx_simcall_get_issuer(MC_state_get_internal_request(prev_state));
220 XBT_DEBUG("Simcall %d, process %lu (state %d) and simcall %d, process %lu (state %d) are independant",
221 req->call, issuer->pid, state->num,
222 MC_state_get_internal_request(prev_state)->call,
223 previous_issuer->pid,
230 if (MC_state_interleave_size(state) && xbt_fifo_size(mc_stack) < _sg_mc_max_depth) {
231 /* We found a back-tracking point, let's loop */
232 XBT_DEBUG("Back-tracking to state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
233 xbt_fifo_unshift(mc_stack, state);
235 XBT_DEBUG("Back-tracking to state %d at depth %d done", state->num, xbt_fifo_size(mc_stack));
238 XBT_DEBUG("Delete state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
239 MC_state_delete(state, !state->in_visited_states ? 1 : 0);
245 XBT_INFO("No property violation found.");
246 MC_print_statistics(mc_stats);
247 return SIMGRID_MC_EXIT_SUCCESS;
250 void SafetyChecker::init()
252 reductionMode_ = simgrid::mc::reduction_mode;
253 if( _sg_mc_termination)
254 reductionMode_ = simgrid::mc::ReductionMode::none;
255 else if (reductionMode_ == simgrid::mc::ReductionMode::unset)
256 reductionMode_ = simgrid::mc::ReductionMode::dpor;
258 if (_sg_mc_termination)
259 XBT_INFO("Check non progressive cycles");
261 XBT_INFO("Check a safety property");
262 mc_model_checker->wait_for_requests();
264 XBT_DEBUG("Starting the safety algorithm");
266 /* Create exploration stack */
267 mc_stack = xbt_fifo_new();
269 simgrid::mc::visited_states.clear();
271 mc_state_t initial_state = MC_state_new();
273 XBT_DEBUG("**************************************************");
274 XBT_DEBUG("Initial state");
276 /* Wait for requests (schedules processes) */
277 mc_model_checker->wait_for_requests();
279 /* Get an enabled process and insert it in the interleave set of the initial state */
280 for (auto& p : mc_model_checker->process().simix_processes())
281 if (simgrid::mc::process_is_enabled(&p.copy)) {
282 MC_state_interleave_process(initial_state, &p.copy);
283 if (reductionMode_ != simgrid::mc::ReductionMode::none)
287 xbt_fifo_unshift(mc_stack, initial_state);
289 /* Save the initial state */
290 initial_global_state = xbt_new0(s_mc_global_t, 1);
291 initial_global_state->snapshot = simgrid::mc::take_snapshot(0);
294 SafetyChecker::SafetyChecker(Session& session) : Checker(session)
298 SafetyChecker::~SafetyChecker()