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 RecordTrace SafetyChecker::getRecordTrace() // override
73 xbt_fifo_item_t start = xbt_fifo_get_last_item(mc_stack);
74 for (xbt_fifo_item_t item = start; item; item = xbt_fifo_get_prev_item(item)) {
77 mc_state_t state = (mc_state_t) xbt_fifo_get_item_content(item);
79 smx_simcall_t saved_req = MC_state_get_executed_request(state, &value);
80 const smx_process_t issuer = MC_smx_simcall_get_issuer(saved_req);
81 const int pid = issuer->pid;
83 res.push_back(RecordTraceElement(pid, value));
86 return std::move(res);
89 int SafetyChecker::run()
93 char *req_str = nullptr;
95 smx_simcall_t req = nullptr;
96 mc_state_t state = nullptr, prev_state = NULL, next_state = NULL;
97 xbt_fifo_item_t item = nullptr;
98 std::unique_ptr<simgrid::mc::VisitedState> visited_state = nullptr;
100 while (xbt_fifo_size(mc_stack) > 0) {
102 /* Get current state */
103 state = (mc_state_t)xbt_fifo_get_item_content(xbt_fifo_get_first_item(mc_stack));
105 XBT_DEBUG("**************************************************");
107 ("Exploration depth=%d (state=%p, num %d)(%u interleave, user_max_depth %d)",
108 xbt_fifo_size(mc_stack), state, state->num,
109 MC_state_interleave_size(state), user_max_depth_reached);
111 /* Update statistics */
112 mc_stats->visited_states++;
114 /* If there are processes to interleave and the maximum depth has not been reached
115 then perform one step of the exploration algorithm */
116 if (xbt_fifo_size(mc_stack) <= _sg_mc_max_depth && !user_max_depth_reached
117 && (req = MC_state_get_request(state, &value)) && visited_state == nullptr) {
119 req_str = simgrid::mc::request_to_string(req, value, simgrid::mc::RequestType::simix);
120 XBT_DEBUG("Execute: %s", req_str);
123 if (dot_output != nullptr)
124 req_str = simgrid::mc::request_get_dot_output(req, value);
126 MC_state_set_executed_request(state, req, value);
127 mc_stats->executed_transitions++;
129 // TODO, bundle both operations in a single message
130 // MC_execute_transition(req, value)
132 /* Answer the request */
133 simgrid::mc::handle_simcall(req, value);
134 mc_model_checker->wait_for_requests();
136 /* Create the new expanded state */
137 next_state = MC_state_new();
139 if(_sg_mc_termination && is_exploration_stack_state(next_state)){
140 MC_show_non_termination();
141 return SIMGRID_MC_EXIT_NON_TERMINATION;
144 if (_sg_mc_visited == 0 || (visited_state = simgrid::mc::is_visited_state(next_state, true)) == nullptr) {
146 /* Get an enabled process and insert it in the interleave set of the next state */
147 for (auto& p : mc_model_checker->process().simix_processes())
148 if (simgrid::mc::process_is_enabled(&p.copy)) {
149 MC_state_interleave_process(next_state, &p.copy);
150 if (reductionMode_ != simgrid::mc::ReductionMode::none)
154 if (dot_output != nullptr)
155 std::fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n", state->num, next_state->num, req_str);
157 } else if (dot_output != nullptr)
158 std::fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n", state->num, visited_state->other_num == -1 ? visited_state->num : visited_state->other_num, req_str);
161 xbt_fifo_unshift(mc_stack, next_state);
163 if (dot_output != nullptr)
166 /* Let's loop again */
168 /* The interleave set is empty or the maximum depth is reached, let's back-track */
171 if ((xbt_fifo_size(mc_stack) > _sg_mc_max_depth) || user_max_depth_reached || visited_state != nullptr) {
173 if (user_max_depth_reached && visited_state == nullptr)
174 XBT_DEBUG("User max depth reached !");
175 else if (visited_state == nullptr)
176 XBT_WARN("/!\\ Max depth reached ! /!\\ ");
178 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);
181 XBT_DEBUG("There are no more processes to interleave. (depth %d)", xbt_fifo_size(mc_stack) + 1);
183 /* Trash the current state, no longer needed */
184 xbt_fifo_shift(mc_stack);
185 XBT_DEBUG("Delete state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
186 MC_state_delete(state, !state->in_visited_states ? 1 : 0);
188 visited_state = nullptr;
190 /* Check for deadlocks */
191 if (mc_model_checker->checkDeadlock()) {
193 return SIMGRID_MC_EXIT_DEADLOCK;
196 /* Traverse the stack backwards until a state with a non empty interleave
197 set is found, deleting all the states that have it empty in the way.
198 For each deleted state, check if the request that has generated it
199 (from it's predecesor state), depends on any other previous request
200 executed before it. If it does then add it to the interleave set of the
201 state that executed that previous request. */
203 while ((state = (mc_state_t) xbt_fifo_shift(mc_stack))) {
204 if (reductionMode_ == simgrid::mc::ReductionMode::dpor) {
205 req = MC_state_get_internal_request(state);
206 if (req->call == SIMCALL_MUTEX_LOCK || req->call == SIMCALL_MUTEX_TRYLOCK)
207 xbt_die("Mutex is currently not supported with DPOR, "
208 "use --cfg=model-check/reduction:none");
209 const smx_process_t issuer = MC_smx_simcall_get_issuer(req);
210 xbt_fifo_foreach(mc_stack, item, prev_state, mc_state_t) {
211 if (reductionMode_ != simgrid::mc::ReductionMode::none
212 && simgrid::mc::request_depend(req, MC_state_get_internal_request(prev_state))) {
213 if (XBT_LOG_ISENABLED(mc_safety, xbt_log_priority_debug)) {
214 XBT_DEBUG("Dependent Transitions:");
215 smx_simcall_t prev_req = MC_state_get_executed_request(prev_state, &value);
216 req_str = simgrid::mc::request_to_string(prev_req, value, simgrid::mc::RequestType::internal);
217 XBT_DEBUG("%s (state=%d)", req_str, prev_state->num);
219 prev_req = MC_state_get_executed_request(state, &value);
220 req_str = simgrid::mc::request_to_string(prev_req, value, simgrid::mc::RequestType::executed);
221 XBT_DEBUG("%s (state=%d)", req_str, state->num);
225 if (!MC_state_process_is_done(prev_state, issuer))
226 MC_state_interleave_process(prev_state, issuer);
228 XBT_DEBUG("Process %p is in done set", req->issuer);
232 } else if (req->issuer == MC_state_get_internal_request(prev_state)->issuer) {
234 XBT_DEBUG("Simcall %d and %d with same issuer", req->call, MC_state_get_internal_request(prev_state)->call);
239 const smx_process_t previous_issuer = MC_smx_simcall_get_issuer(MC_state_get_internal_request(prev_state));
240 XBT_DEBUG("Simcall %d, process %lu (state %d) and simcall %d, process %lu (state %d) are independant",
241 req->call, issuer->pid, state->num,
242 MC_state_get_internal_request(prev_state)->call,
243 previous_issuer->pid,
250 if (MC_state_interleave_size(state) && xbt_fifo_size(mc_stack) < _sg_mc_max_depth) {
251 /* We found a back-tracking point, let's loop */
252 XBT_DEBUG("Back-tracking to state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
253 xbt_fifo_unshift(mc_stack, state);
255 XBT_DEBUG("Back-tracking to state %d at depth %d done", state->num, xbt_fifo_size(mc_stack));
258 XBT_DEBUG("Delete state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
259 MC_state_delete(state, !state->in_visited_states ? 1 : 0);
265 XBT_INFO("No property violation found.");
266 MC_print_statistics(mc_stats);
267 return SIMGRID_MC_EXIT_SUCCESS;
270 void SafetyChecker::init()
272 reductionMode_ = simgrid::mc::reduction_mode;
273 if( _sg_mc_termination)
274 reductionMode_ = simgrid::mc::ReductionMode::none;
275 else if (reductionMode_ == simgrid::mc::ReductionMode::unset)
276 reductionMode_ = simgrid::mc::ReductionMode::dpor;
278 if (_sg_mc_termination)
279 XBT_INFO("Check non progressive cycles");
281 XBT_INFO("Check a safety property");
282 mc_model_checker->wait_for_requests();
284 XBT_DEBUG("Starting the safety algorithm");
286 /* Create exploration stack */
287 mc_stack = xbt_fifo_new();
289 simgrid::mc::visited_states.clear();
291 mc_state_t initial_state = MC_state_new();
293 XBT_DEBUG("**************************************************");
294 XBT_DEBUG("Initial state");
296 /* Wait for requests (schedules processes) */
297 mc_model_checker->wait_for_requests();
299 /* Get an enabled process and insert it in the interleave set of the initial state */
300 for (auto& p : mc_model_checker->process().simix_processes())
301 if (simgrid::mc::process_is_enabled(&p.copy)) {
302 MC_state_interleave_process(initial_state, &p.copy);
303 if (reductionMode_ != simgrid::mc::ReductionMode::none)
307 xbt_fifo_unshift(mc_stack, initial_state);
309 /* Save the initial state */
310 initial_global_state = xbt_new0(s_mc_global_t, 1);
311 initial_global_state->snapshot = simgrid::mc::take_snapshot(0);
314 SafetyChecker::SafetyChecker(Session& session) : Checker(session)
318 SafetyChecker::~SafetyChecker()