1 /* Copyright (c) 2008-2015. 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. */
10 #include <xbt/dynar.hpp>
13 #include <xbt/sysdep.h>
15 #include "src/mc/mc_state.h"
16 #include "src/mc/mc_comm_pattern.h"
17 #include "src/mc/mc_request.h"
18 #include "src/mc/mc_safety.h"
19 #include "src/mc/mc_private.h"
20 #include "src/mc/mc_record.h"
21 #include "src/mc/mc_smx.h"
22 #include "src/mc/Client.hpp"
23 #include "src/mc/CommunicationDeterminismChecker.hpp"
24 #include "src/mc/mc_exit.h"
26 using simgrid::mc::remote;
28 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_comm_determinism, mc,
29 "Logging specific to MC communication determinism detection");
31 /********** Global variables **********/
33 xbt_dynar_t initial_communications_pattern;
34 xbt_dynar_t incomplete_communications_pattern;
36 /********** Static functions ***********/
38 static e_mc_comm_pattern_difference_t compare_comm_pattern(mc_comm_pattern_t comm1, mc_comm_pattern_t comm2) {
39 if(comm1->type != comm2->type)
41 if (strcmp(comm1->rdv, comm2->rdv) != 0)
43 if (comm1->src_proc != comm2->src_proc)
45 if (comm1->dst_proc != comm2->dst_proc)
47 if (comm1->tag != comm2->tag)
49 if (comm1->data_size != comm2->data_size)
50 return DATA_SIZE_DIFF;
51 if(comm1->data == nullptr && comm2->data == NULL)
53 if(comm1->data != nullptr && comm2->data !=NULL) {
54 if (!memcmp(comm1->data, comm2->data, comm1->data_size))
62 static char* print_determinism_result(e_mc_comm_pattern_difference_t diff, int process, mc_comm_pattern_t comm, unsigned int cursor) {
65 if(comm->type == SIMIX_COMM_SEND)
66 type = bprintf("The send communications pattern of the process %d is different!", process - 1);
68 type = bprintf("The recv communications pattern of the process %d is different!", process - 1);
72 res = bprintf("%s Different type for communication #%d", type, cursor);
75 res = bprintf("%s Different rdv for communication #%d", type, cursor);
78 res = bprintf("%s Different tag for communication #%d", type, cursor);
81 res = bprintf("%s Different source for communication #%d", type, cursor);
84 res = bprintf("%s Different destination for communication #%d", type, cursor);
87 res = bprintf("%s\n Different data size for communication #%d", type, cursor);
90 res = bprintf("%s\n Different data for communication #%d", type, cursor);
100 static void update_comm_pattern(mc_comm_pattern_t comm_pattern, smx_synchro_t comm_addr)
102 s_smx_synchro_t comm;
103 mc_model_checker->process().read(&comm, remote(comm_addr));
105 smx_process_t src_proc = MC_smx_resolve_process(comm.comm.src_proc);
106 smx_process_t dst_proc = MC_smx_resolve_process(comm.comm.dst_proc);
107 comm_pattern->src_proc = src_proc->pid;
108 comm_pattern->dst_proc = dst_proc->pid;
109 comm_pattern->src_host = MC_smx_process_get_host_name(src_proc);
110 comm_pattern->dst_host = MC_smx_process_get_host_name(dst_proc);
111 if (comm_pattern->data_size == -1 && comm.comm.src_buff != nullptr) {
113 mc_model_checker->process().read(
114 &buff_size, remote(comm.comm.dst_buff_size));
115 comm_pattern->data_size = buff_size;
116 comm_pattern->data = xbt_malloc0(comm_pattern->data_size);
117 mc_model_checker->process().read_bytes(
118 comm_pattern->data, comm_pattern->data_size,
119 remote(comm.comm.src_buff));
123 static void deterministic_comm_pattern(int process, mc_comm_pattern_t comm, int backtracking) {
125 mc_list_comm_pattern_t list =
126 xbt_dynar_get_as(initial_communications_pattern, process, mc_list_comm_pattern_t);
129 mc_comm_pattern_t initial_comm =
130 xbt_dynar_get_as(list->list, list->index_comm, mc_comm_pattern_t);
131 e_mc_comm_pattern_difference_t diff =
132 compare_comm_pattern(initial_comm, comm);
134 if (diff != NONE_DIFF) {
135 if (comm->type == SIMIX_COMM_SEND){
136 simgrid::mc::initial_global_state->send_deterministic = 0;
137 if(simgrid::mc::initial_global_state->send_diff != nullptr)
138 xbt_free(simgrid::mc::initial_global_state->send_diff);
139 simgrid::mc::initial_global_state->send_diff = print_determinism_result(diff, process, comm, list->index_comm + 1);
141 simgrid::mc::initial_global_state->recv_deterministic = 0;
142 if(simgrid::mc::initial_global_state->recv_diff != nullptr)
143 xbt_free(simgrid::mc::initial_global_state->recv_diff);
144 simgrid::mc::initial_global_state->recv_diff = print_determinism_result(diff, process, comm, list->index_comm + 1);
146 if(_sg_mc_send_determinism && !simgrid::mc::initial_global_state->send_deterministic){
147 XBT_INFO("*********************************************************");
148 XBT_INFO("***** Non-send-deterministic communications pattern *****");
149 XBT_INFO("*********************************************************");
150 XBT_INFO("%s", simgrid::mc::initial_global_state->send_diff);
151 xbt_free(simgrid::mc::initial_global_state->send_diff);
152 simgrid::mc::initial_global_state->send_diff = nullptr;
153 MC_print_statistics(mc_stats);
154 mc_model_checker->exit(SIMGRID_MC_EXIT_NON_DETERMINISM);
155 }else if(_sg_mc_comms_determinism
156 && (!simgrid::mc::initial_global_state->send_deterministic
157 && !simgrid::mc::initial_global_state->recv_deterministic)) {
158 XBT_INFO("****************************************************");
159 XBT_INFO("***** Non-deterministic communications pattern *****");
160 XBT_INFO("****************************************************");
161 XBT_INFO("%s", simgrid::mc::initial_global_state->send_diff);
162 XBT_INFO("%s", simgrid::mc::initial_global_state->recv_diff);
163 xbt_free(simgrid::mc::initial_global_state->send_diff);
164 simgrid::mc::initial_global_state->send_diff = nullptr;
165 xbt_free(simgrid::mc::initial_global_state->recv_diff);
166 simgrid::mc::initial_global_state->recv_diff = nullptr;
167 MC_print_statistics(mc_stats);
168 mc_model_checker->exit(SIMGRID_MC_EXIT_NON_DETERMINISM);
173 MC_comm_pattern_free(comm);
177 /********** Non Static functions ***********/
179 void MC_get_comm_pattern(xbt_dynar_t list, smx_simcall_t request, e_mc_call_type_t call_type, int backtracking)
181 const smx_process_t issuer = MC_smx_simcall_get_issuer(request);
182 mc_list_comm_pattern_t initial_pattern = xbt_dynar_get_as(
183 initial_communications_pattern, issuer->pid, mc_list_comm_pattern_t);
184 xbt_dynar_t incomplete_pattern = xbt_dynar_get_as(
185 incomplete_communications_pattern, issuer->pid, xbt_dynar_t);
187 mc_comm_pattern_t pattern = xbt_new0(s_mc_comm_pattern_t, 1);
188 pattern->data_size = -1;
189 pattern->data = nullptr;
191 initial_pattern->index_comm + xbt_dynar_length(incomplete_pattern);
193 if (call_type == MC_CALL_TYPE_SEND) {
194 /* Create comm pattern */
195 pattern->type = SIMIX_COMM_SEND;
196 pattern->comm_addr = simcall_comm_isend__get__result(request);
198 s_smx_synchro_t synchro = mc_model_checker->process().read<s_smx_synchro_t>(
199 (std::uint64_t) pattern->comm_addr);
201 char* remote_name = mc_model_checker->process().read<char*>(
202 (std::uint64_t)(synchro.comm.rdv ? &synchro.comm.rdv->name : &synchro.comm.rdv_cpy->name));
203 pattern->rdv = mc_model_checker->process().read_string(remote_name);
204 pattern->src_proc = MC_smx_resolve_process(synchro.comm.src_proc)->pid;
205 pattern->src_host = MC_smx_process_get_host_name(issuer);
207 struct s_smpi_mpi_request mpi_request =
208 mc_model_checker->process().read<s_smpi_mpi_request>(
209 (std::uint64_t) simcall_comm_isend__get__data(request));
210 pattern->tag = mpi_request.tag;
212 if(synchro.comm.src_buff != nullptr){
213 pattern->data_size = synchro.comm.src_buff_size;
214 pattern->data = xbt_malloc0(pattern->data_size);
215 mc_model_checker->process().read_bytes(
216 pattern->data, pattern->data_size, remote(synchro.comm.src_buff));
218 if(mpi_request.detached){
219 if (!simgrid::mc::initial_global_state->initial_communications_pattern_done) {
220 /* Store comm pattern */
223 initial_communications_pattern, pattern->src_proc, mc_list_comm_pattern_t
227 /* Evaluate comm determinism */
228 deterministic_comm_pattern(pattern->src_proc, pattern, backtracking);
230 initial_communications_pattern, pattern->src_proc, mc_list_comm_pattern_t
235 } else if (call_type == MC_CALL_TYPE_RECV) {
236 pattern->type = SIMIX_COMM_RECEIVE;
237 pattern->comm_addr = simcall_comm_irecv__get__result(request);
239 struct s_smpi_mpi_request mpi_request;
240 mc_model_checker->process().read(
241 &mpi_request, remote((struct s_smpi_mpi_request*)simcall_comm_irecv__get__data(request)));
242 pattern->tag = mpi_request.tag;
244 s_smx_synchro_t synchro;
245 mc_model_checker->process().read(&synchro, remote(pattern->comm_addr));
248 mc_model_checker->process().read(&remote_name,
249 remote(synchro.comm.rdv ? &synchro.comm.rdv->name : &synchro.comm.rdv_cpy->name));
250 pattern->rdv = mc_model_checker->process().read_string(remote_name);
251 pattern->dst_proc = MC_smx_resolve_process(synchro.comm.dst_proc)->pid;
252 pattern->dst_host = MC_smx_process_get_host_name(issuer);
254 xbt_die("Unexpected call_type %i", (int) call_type);
257 xbt_dynar_get_as(incomplete_communications_pattern, issuer->pid, xbt_dynar_t),
260 XBT_DEBUG("Insert incomplete comm pattern %p for process %lu", pattern, issuer->pid);
263 void MC_complete_comm_pattern(xbt_dynar_t list, smx_synchro_t comm_addr, unsigned int issuer, int backtracking) {
264 mc_comm_pattern_t current_comm_pattern;
265 unsigned int cursor = 0;
266 mc_comm_pattern_t comm_pattern;
269 /* Complete comm pattern */
270 xbt_dynar_foreach(xbt_dynar_get_as(incomplete_communications_pattern, issuer, xbt_dynar_t), cursor, current_comm_pattern)
271 if (current_comm_pattern->comm_addr == comm_addr) {
272 update_comm_pattern(current_comm_pattern, comm_addr);
275 xbt_dynar_get_as(incomplete_communications_pattern, issuer, xbt_dynar_t),
276 cursor, &comm_pattern);
277 XBT_DEBUG("Remove incomplete comm pattern for process %u at cursor %u", issuer, cursor);
282 xbt_die("Corresponding communication not found!");
284 mc_list_comm_pattern_t pattern = xbt_dynar_get_as(
285 initial_communications_pattern, issuer, mc_list_comm_pattern_t);
287 if (!simgrid::mc::initial_global_state->initial_communications_pattern_done)
288 /* Store comm pattern */
289 xbt_dynar_push(pattern->list, &comm_pattern);
291 /* Evaluate comm determinism */
292 deterministic_comm_pattern(issuer, comm_pattern, backtracking);
293 pattern->index_comm++;
298 /************************ Main algorithm ************************/
303 CommunicationDeterminismChecker::CommunicationDeterminismChecker(Session& session)
309 CommunicationDeterminismChecker::~CommunicationDeterminismChecker()
314 // TODO, deduplicate with SafetyChecker
315 RecordTrace CommunicationDeterminismChecker::getRecordTrace() // override
319 xbt_fifo_item_t start = xbt_fifo_get_last_item(mc_stack);
320 for (xbt_fifo_item_t item = start; item; item = xbt_fifo_get_prev_item(item)) {
322 // Find (pid, value):
323 simgrid::mc::State* state = (simgrid::mc::State*) xbt_fifo_get_item_content(item);
325 smx_simcall_t saved_req = MC_state_get_executed_request(state, &value);
326 const smx_process_t issuer = MC_smx_simcall_get_issuer(saved_req);
327 const int pid = issuer->pid;
329 res.push_back(RecordTraceElement(pid, value));
332 return std::move(res);
335 // TODO, deduplicate with SafetyChecker
336 std::vector<std::string> CommunicationDeterminismChecker::getTextualTrace() // override
338 std::vector<std::string> res;
339 for (xbt_fifo_item_t item = xbt_fifo_get_last_item(mc_stack);
340 item; item = xbt_fifo_get_prev_item(item)) {
341 simgrid::mc::State* state = (simgrid::mc::State*)xbt_fifo_get_item_content(item);
343 smx_simcall_t req = MC_state_get_executed_request(state, &value);
345 char* req_str = simgrid::mc::request_to_string(
346 req, value, simgrid::mc::RequestType::executed);
347 res.push_back(req_str);
351 return std::move(res);
354 void CommunicationDeterminismChecker::prepare()
356 simgrid::mc::State* initial_state = nullptr;
358 const int maxpid = MC_smx_get_maxpid();
360 simgrid::mc::visited_states.clear();
362 // Create initial_communications_pattern elements:
363 initial_communications_pattern = xbt_dynar_new(sizeof(mc_list_comm_pattern_t), MC_list_comm_pattern_free_voidp);
364 for (i=0; i < maxpid; i++){
365 mc_list_comm_pattern_t process_list_pattern = xbt_new0(s_mc_list_comm_pattern_t, 1);
366 process_list_pattern->list = xbt_dynar_new(sizeof(mc_comm_pattern_t), MC_comm_pattern_free_voidp);
367 process_list_pattern->index_comm = 0;
368 xbt_dynar_insert_at(initial_communications_pattern, i, &process_list_pattern);
371 // Create incomplete_communications_pattern elements:
372 incomplete_communications_pattern = xbt_dynar_new(sizeof(xbt_dynar_t), xbt_dynar_free_voidp);
373 for (i=0; i < maxpid; i++){
374 xbt_dynar_t process_pattern = xbt_dynar_new(sizeof(mc_comm_pattern_t), nullptr);
375 xbt_dynar_insert_at(incomplete_communications_pattern, i, &process_pattern);
378 initial_state = MC_state_new();
380 XBT_DEBUG("********* Start communication determinism verification *********");
382 /* Wait for requests (schedules processes) */
383 mc_model_checker->wait_for_requests();
385 /* Get an enabled process and insert it in the interleave set of the initial state */
386 for (auto& p : mc_model_checker->process().simix_processes())
387 if (simgrid::mc::process_is_enabled(&p.copy))
388 MC_state_interleave_process(initial_state, &p.copy);
390 xbt_fifo_unshift(mc_stack, initial_state);
394 bool all_communications_are_finished()
396 for (size_t current_process = 1; current_process < MC_smx_get_maxpid(); current_process++) {
397 xbt_dynar_t pattern = xbt_dynar_get_as(
398 incomplete_communications_pattern, current_process, xbt_dynar_t);
399 if (!xbt_dynar_is_empty(pattern)) {
400 XBT_DEBUG("Some communications are not finished, cannot stop the exploration ! State not visited.");
407 int CommunicationDeterminismChecker::main(void)
410 char *req_str = nullptr;
412 std::unique_ptr<simgrid::mc::VisitedState> visited_state = nullptr;
413 smx_simcall_t req = nullptr;
414 simgrid::mc::State* state = nullptr;
415 simgrid::mc::State* next_state = nullptr;
417 while (xbt_fifo_size(mc_stack) > 0) {
419 /* Get current state */
420 state = (simgrid::mc::State*) xbt_fifo_get_item_content(xbt_fifo_get_first_item(mc_stack));
422 XBT_DEBUG("**************************************************");
423 XBT_DEBUG("Exploration depth = %d (state = %d, interleaved processes = %d)",
424 xbt_fifo_size(mc_stack), state->num,
425 MC_state_interleave_size(state));
427 /* Update statistics */
428 mc_stats->visited_states++;
430 if ((xbt_fifo_size(mc_stack) <= _sg_mc_max_depth)
431 && (req = MC_state_get_request(state, &value))
432 && (visited_state == nullptr)) {
434 req_str = simgrid::mc::request_to_string(req, value, simgrid::mc::RequestType::simix);
435 XBT_DEBUG("Execute: %s", req_str);
438 if (dot_output != nullptr)
439 req_str = simgrid::mc::request_get_dot_output(req, value);
441 MC_state_set_executed_request(state, req, value);
442 mc_stats->executed_transitions++;
444 /* TODO : handle test and testany simcalls */
445 e_mc_call_type_t call = MC_CALL_TYPE_NONE;
446 if (_sg_mc_comms_determinism || _sg_mc_send_determinism)
447 call = MC_get_call_type(req);
449 /* Answer the request */
450 simgrid::mc::handle_simcall(req, value); /* After this call req is no longer useful */
452 if(!initial_global_state->initial_communications_pattern_done)
453 MC_handle_comm_pattern(call, req, value, initial_communications_pattern, 0);
455 MC_handle_comm_pattern(call, req, value, nullptr, 0);
457 /* Wait for requests (schedules processes) */
458 mc_model_checker->wait_for_requests();
460 /* Create the new expanded state */
461 next_state = MC_state_new();
463 /* If comm determinism verification, we cannot stop the exploration if
464 some communications are not finished (at least, data are transfered).
465 These communications are incomplete and they cannot be analyzed and
466 compared with the initial pattern. */
467 bool compare_snapshots = all_communications_are_finished()
468 && initial_global_state->initial_communications_pattern_done;
470 if (_sg_mc_visited == 0 || (visited_state = simgrid::mc::is_visited_state(next_state, compare_snapshots)) == nullptr) {
472 /* Get enabled processes and insert them in the interleave set of the next state */
473 for (auto& p : mc_model_checker->process().simix_processes())
474 if (simgrid::mc::process_is_enabled(&p.copy))
475 MC_state_interleave_process(next_state, &p.copy);
477 if (dot_output != nullptr)
478 fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n", state->num, next_state->num, req_str);
480 } else if (dot_output != nullptr)
481 fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n",
482 state->num, visited_state->other_num == -1 ? visited_state->num : visited_state->other_num, req_str);
484 xbt_fifo_unshift(mc_stack, next_state);
486 if (dot_output != nullptr)
491 if (xbt_fifo_size(mc_stack) > _sg_mc_max_depth)
492 XBT_WARN("/!\\ Max depth reached ! /!\\ ");
493 else if (visited_state != nullptr)
494 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);
496 XBT_DEBUG("There are no more processes to interleave. (depth %d)", xbt_fifo_size(mc_stack));
498 if (!initial_global_state->initial_communications_pattern_done)
499 initial_global_state->initial_communications_pattern_done = 1;
501 /* Trash the current state, no longer needed */
502 xbt_fifo_shift(mc_stack);
503 MC_state_delete(state, !state->in_visited_states ? 1 : 0);
504 XBT_DEBUG("Delete state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
506 visited_state = nullptr;
508 /* Check for deadlocks */
509 if (mc_model_checker->checkDeadlock()) {
511 return SIMGRID_MC_EXIT_DEADLOCK;
514 while ((state = (simgrid::mc::State*) xbt_fifo_shift(mc_stack)) != nullptr)
515 if (MC_state_interleave_size(state) && xbt_fifo_size(mc_stack) < _sg_mc_max_depth) {
516 /* We found a back-tracking point, let's loop */
517 XBT_DEBUG("Back-tracking to state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
518 xbt_fifo_unshift(mc_stack, state);
522 XBT_DEBUG("Back-tracking to state %d at depth %d done", state->num, xbt_fifo_size(mc_stack));
526 XBT_DEBUG("Delete state %d at depth %d", state->num, xbt_fifo_size(mc_stack) + 1);
527 MC_state_delete(state, !state->in_visited_states ? 1 : 0);
533 MC_print_statistics(mc_stats);
534 return SIMGRID_MC_EXIT_SUCCESS;
537 int CommunicationDeterminismChecker::run()
539 XBT_INFO("Check communication determinism");
540 mc_model_checker->wait_for_requests();
542 if (mc_mode == MC_MODE_CLIENT)
543 // This will move somehwere else:
544 simgrid::mc::Client::get()->handleMessages();
546 /* Create exploration stack */
547 mc_stack = xbt_fifo_new();
551 initial_global_state = std::unique_ptr<s_mc_global_t>(new s_mc_global_t());
552 initial_global_state->snapshot = simgrid::mc::take_snapshot(0);
553 initial_global_state->initial_communications_pattern_done = 0;
554 initial_global_state->recv_deterministic = 1;
555 initial_global_state->send_deterministic = 1;
556 initial_global_state->recv_diff = nullptr;
557 initial_global_state->send_diff = nullptr;
559 int res = this->main();
560 initial_global_state = nullptr;