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>
12 #include <xbt/sysdep.h>
14 #include "src/mc/mc_state.h"
15 #include "src/mc/mc_comm_pattern.h"
16 #include "src/mc/mc_request.h"
17 #include "src/mc/mc_safety.h"
18 #include "src/mc/mc_private.h"
19 #include "src/mc/mc_record.h"
20 #include "src/mc/mc_smx.h"
21 #include "src/mc/Client.hpp"
22 #include "src/mc/CommunicationDeterminismChecker.hpp"
23 #include "src/mc/mc_exit.h"
24 #include "src/mc/VisitedState.hpp"
25 #include "src/mc/Transition.hpp"
27 using simgrid::mc::remote;
29 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_comm_determinism, mc,
30 "Logging specific to MC communication determinism detection");
32 /********** Global variables **********/
34 xbt_dynar_t initial_communications_pattern;
35 xbt_dynar_t incomplete_communications_pattern;
37 /********** Static functions ***********/
39 static e_mc_comm_pattern_difference_t compare_comm_pattern(simgrid::mc::PatternCommunication* comm1, simgrid::mc::PatternCommunication* comm2)
41 if(comm1->type != comm2->type)
43 if (comm1->rdv != comm2->rdv)
45 if (comm1->src_proc != comm2->src_proc)
47 if (comm1->dst_proc != comm2->dst_proc)
49 if (comm1->tag != comm2->tag)
51 if (comm1->data.size() != comm2->data.size())
52 return DATA_SIZE_DIFF;
53 if (comm1->data != comm2->data)
58 static char* print_determinism_result(e_mc_comm_pattern_difference_t diff, int process, simgrid::mc::PatternCommunication* comm, unsigned int cursor) {
61 if (comm->type == simgrid::mc::PatternCommunicationType::send)
62 type = bprintf("The send communications pattern of the process %d is different!", process - 1);
64 type = bprintf("The recv communications pattern of the process %d is different!", process - 1);
68 res = bprintf("%s Different type for communication #%d", type, cursor);
71 res = bprintf("%s Different rdv for communication #%d", type, cursor);
74 res = bprintf("%s Different tag for communication #%d", type, cursor);
77 res = bprintf("%s Different source for communication #%d", type, cursor);
80 res = bprintf("%s Different destination for communication #%d", type, cursor);
83 res = bprintf("%s\n Different data size for communication #%d", type, cursor);
86 res = bprintf("%s\n Different data for communication #%d", type, cursor);
96 static void update_comm_pattern(
97 simgrid::mc::PatternCommunication* comm_pattern,
98 simgrid::mc::RemotePtr<simgrid::kernel::activity::Comm> comm_addr)
100 // HACK, type punning
101 simgrid::mc::Remote<simgrid::kernel::activity::Comm> temp_comm;
102 mc_model_checker->process().read(temp_comm, comm_addr);
103 simgrid::kernel::activity::Comm* comm = temp_comm.getBuffer();
105 smx_actor_t src_proc = mc_model_checker->process().resolveActor(simgrid::mc::remote(comm->src_proc));
106 smx_actor_t dst_proc = mc_model_checker->process().resolveActor(simgrid::mc::remote(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_actor_get_host_name(src_proc);
110 comm_pattern->dst_host = MC_smx_actor_get_host_name(dst_proc);
111 if (comm_pattern->data.size() == 0 && comm->src_buff != nullptr) {
113 mc_model_checker->process().read(
114 &buff_size, remote(comm->dst_buff_size));
115 comm_pattern->data.resize(buff_size);
116 mc_model_checker->process().read_bytes(
117 comm_pattern->data.data(), comm_pattern->data.size(),
118 remote(comm->src_buff));
125 void CommunicationDeterminismChecker::deterministic_comm_pattern(
126 int process, simgrid::mc::PatternCommunication* comm, int backtracking)
128 simgrid::mc::PatternCommunicationList* list =
129 xbt_dynar_get_as(initial_communications_pattern, process, simgrid::mc::PatternCommunicationList*);
132 e_mc_comm_pattern_difference_t diff =
133 compare_comm_pattern(list->list[list->index_comm].get(), comm);
135 if (diff != NONE_DIFF) {
136 if (comm->type == simgrid::mc::PatternCommunicationType::send) {
137 this->send_deterministic = 0;
138 if (this->send_diff != nullptr)
139 xbt_free(this->send_diff);
140 this->send_diff = print_determinism_result(diff, process, comm, list->index_comm + 1);
142 this->recv_deterministic = 0;
143 if (this->recv_diff != nullptr)
144 xbt_free(this->recv_diff);
145 this->recv_diff = print_determinism_result(diff, process, comm, list->index_comm + 1);
147 if(_sg_mc_send_determinism && !this->send_deterministic){
148 XBT_INFO("*********************************************************");
149 XBT_INFO("***** Non-send-deterministic communications pattern *****");
150 XBT_INFO("*********************************************************");
151 XBT_INFO("%s", this->send_diff);
152 xbt_free(this->send_diff);
153 this->send_diff = nullptr;
154 simgrid::mc::session->logState();
155 mc_model_checker->exit(SIMGRID_MC_EXIT_NON_DETERMINISM);
156 }else if(_sg_mc_comms_determinism
157 && (!this->send_deterministic && !this->recv_deterministic)) {
158 XBT_INFO("****************************************************");
159 XBT_INFO("***** Non-deterministic communications pattern *****");
160 XBT_INFO("****************************************************");
161 XBT_INFO("%s", this->send_diff);
162 XBT_INFO("%s", this->recv_diff);
163 xbt_free(this->send_diff);
164 this->send_diff = nullptr;
165 xbt_free(this->recv_diff);
166 this->recv_diff = nullptr;
167 simgrid::mc::session->logState();
168 mc_model_checker->exit(SIMGRID_MC_EXIT_NON_DETERMINISM);
174 /********** Non Static functions ***********/
176 void CommunicationDeterminismChecker::get_comm_pattern(xbt_dynar_t list, smx_simcall_t request, e_mc_call_type_t call_type, int backtracking)
178 const smx_actor_t issuer = MC_smx_simcall_get_issuer(request);
179 simgrid::mc::PatternCommunicationList* initial_pattern = xbt_dynar_get_as(
180 initial_communications_pattern, issuer->pid, simgrid::mc::PatternCommunicationList*);
181 xbt_dynar_t incomplete_pattern = xbt_dynar_get_as(
182 incomplete_communications_pattern, issuer->pid, xbt_dynar_t);
184 std::unique_ptr<simgrid::mc::PatternCommunication> pattern =
185 std::unique_ptr<simgrid::mc::PatternCommunication>(
186 new simgrid::mc::PatternCommunication());
188 initial_pattern->index_comm + xbt_dynar_length(incomplete_pattern);
190 if (call_type == MC_CALL_TYPE_SEND) {
191 /* Create comm pattern */
192 pattern->type = simgrid::mc::PatternCommunicationType::send;
193 pattern->comm_addr = simcall_comm_isend__get__result(request);
195 simgrid::mc::Remote<simgrid::kernel::activity::Comm> temp_synchro;
196 mc_model_checker->process().read(temp_synchro, remote(
197 static_cast<simgrid::kernel::activity::Comm*>(pattern->comm_addr)));
198 simgrid::kernel::activity::Comm* synchro =
199 static_cast<simgrid::kernel::activity::Comm*>(temp_synchro.getBuffer());
201 char* remote_name = mc_model_checker->process().read<char*>(
202 (std::uint64_t)(synchro->mbox ? &synchro->mbox->name : &synchro->mbox_cpy->name));
203 pattern->rdv = mc_model_checker->process().read_string(remote_name);
204 pattern->src_proc = mc_model_checker->process().resolveActor(simgrid::mc::remote(synchro->src_proc))->pid;
205 pattern->src_host = MC_smx_actor_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->src_buff != nullptr){
213 pattern->data.resize(synchro->src_buff_size);
214 mc_model_checker->process().read_bytes(
215 pattern->data.data(), pattern->data.size(),
216 remote(synchro->src_buff));
218 if(mpi_request.detached){
219 if (!this->initial_communications_pattern_done) {
220 /* Store comm pattern */
221 simgrid::mc::PatternCommunicationList* list = xbt_dynar_get_as(
222 initial_communications_pattern, pattern->src_proc,
223 simgrid::mc::PatternCommunicationList*);
224 list->list.push_back(std::move(pattern));
226 /* Evaluate comm determinism */
227 this->deterministic_comm_pattern(pattern->src_proc, pattern.get(), backtracking);
229 initial_communications_pattern, pattern->src_proc, simgrid::mc::PatternCommunicationList*
234 } else if (call_type == MC_CALL_TYPE_RECV) {
235 pattern->type = simgrid::mc::PatternCommunicationType::receive;
236 pattern->comm_addr = simcall_comm_irecv__get__result(request);
238 struct s_smpi_mpi_request mpi_request;
239 mc_model_checker->process().read(
240 &mpi_request, remote((struct s_smpi_mpi_request*)simcall_comm_irecv__get__data(request)));
241 pattern->tag = mpi_request.tag;
243 simgrid::mc::Remote<simgrid::kernel::activity::Comm> temp_comm;
244 mc_model_checker->process().read(temp_comm, remote(
245 static_cast<simgrid::kernel::activity::Comm*>(pattern->comm_addr)));
246 simgrid::kernel::activity::Comm* comm = temp_comm.getBuffer();
249 mc_model_checker->process().read(&remote_name,
250 remote(comm->mbox ? &comm->mbox->name : &comm->mbox_cpy->name));
251 pattern->rdv = mc_model_checker->process().read_string(remote_name);
252 pattern->dst_proc = mc_model_checker->process().resolveActor(simgrid::mc::remote(comm->dst_proc))->pid;
253 pattern->dst_host = MC_smx_actor_get_host_name(issuer);
255 xbt_die("Unexpected call_type %i", (int) call_type);
257 XBT_DEBUG("Insert incomplete comm pattern %p for process %lu",
258 pattern.get(), issuer->pid);
260 xbt_dynar_get_as(incomplete_communications_pattern, issuer->pid, xbt_dynar_t);
261 simgrid::mc::PatternCommunication* pattern2 = pattern.release();
262 xbt_dynar_push(dynar, &pattern2);
266 void CommunicationDeterminismChecker::complete_comm_pattern(
267 xbt_dynar_t list, simgrid::mc::RemotePtr<simgrid::kernel::activity::Comm> comm_addr,
268 unsigned int issuer, int backtracking)
270 simgrid::mc::PatternCommunication* current_comm_pattern;
271 unsigned int cursor = 0;
272 std::unique_ptr<simgrid::mc::PatternCommunication> comm_pattern;
275 /* Complete comm pattern */
276 xbt_dynar_foreach(xbt_dynar_get_as(incomplete_communications_pattern, issuer, xbt_dynar_t), cursor, current_comm_pattern)
277 if (remote(current_comm_pattern->comm_addr) == comm_addr) {
278 update_comm_pattern(current_comm_pattern, comm_addr);
280 simgrid::mc::PatternCommunication* temp;
282 xbt_dynar_get_as(incomplete_communications_pattern, issuer, xbt_dynar_t),
284 comm_pattern = std::unique_ptr<simgrid::mc::PatternCommunication>(temp);
285 XBT_DEBUG("Remove incomplete comm pattern for process %u at cursor %u", issuer, cursor);
290 xbt_die("Corresponding communication not found!");
292 simgrid::mc::PatternCommunicationList* pattern = xbt_dynar_get_as(
293 initial_communications_pattern, issuer, simgrid::mc::PatternCommunicationList*);
295 if (!this->initial_communications_pattern_done)
296 /* Store comm pattern */
297 pattern->list.push_back(std::move(comm_pattern));
299 /* Evaluate comm determinism */
300 this->deterministic_comm_pattern(issuer, comm_pattern.get(), backtracking);
301 pattern->index_comm++;
305 CommunicationDeterminismChecker::CommunicationDeterminismChecker(Session& session)
311 CommunicationDeterminismChecker::~CommunicationDeterminismChecker()
316 RecordTrace CommunicationDeterminismChecker::getRecordTrace() // override
319 for (auto const& state : stack_)
320 res.push_back(state->getTransition());
324 std::vector<std::string> CommunicationDeterminismChecker::getTextualTrace() // override
326 std::vector<std::string> trace;
327 for (auto const& state : stack_) {
328 smx_simcall_t req = &state->executed_req;
330 trace.push_back(simgrid::mc::request_to_string(
331 req, state->transition.argument, simgrid::mc::RequestType::executed));
336 void CommunicationDeterminismChecker::logState() // override
339 if (_sg_mc_comms_determinism &&
340 !this->recv_deterministic &&
341 this->send_deterministic) {
342 XBT_INFO("******************************************************");
343 XBT_INFO("**** Only-send-deterministic communication pattern ****");
344 XBT_INFO("******************************************************");
345 XBT_INFO("%s", this->recv_diff);
346 } else if(_sg_mc_comms_determinism &&
347 !this->send_deterministic &&
348 this->recv_deterministic) {
349 XBT_INFO("******************************************************");
350 XBT_INFO("**** Only-recv-deterministic communication pattern ****");
351 XBT_INFO("******************************************************");
352 XBT_INFO("%s", this->send_diff);
354 XBT_INFO("Expanded states = %lu", expandedStatesCount_);
355 XBT_INFO("Visited states = %lu", mc_model_checker->visited_states);
356 XBT_INFO("Executed transitions = %lu", mc_model_checker->executed_transitions);
357 XBT_INFO("Send-deterministic : %s", !this->send_deterministic ? "No" : "Yes");
358 if (_sg_mc_comms_determinism)
359 XBT_INFO("Recv-deterministic : %s",
360 !this->recv_deterministic ? "No" : "Yes");
363 void CommunicationDeterminismChecker::prepare()
367 const int maxpid = MC_smx_get_maxpid();
369 // Create initial_communications_pattern elements:
370 initial_communications_pattern = simgrid::xbt::newDeleteDynar<simgrid::mc::PatternCommunicationList*>();
371 for (i=0; i < maxpid; i++){
372 simgrid::mc::PatternCommunicationList* process_list_pattern = new simgrid::mc::PatternCommunicationList();
373 xbt_dynar_insert_at(initial_communications_pattern, i, &process_list_pattern);
376 // Create incomplete_communications_pattern elements:
377 incomplete_communications_pattern = xbt_dynar_new(sizeof(xbt_dynar_t), xbt_dynar_free_voidp);
378 for (i=0; i < maxpid; i++){
379 xbt_dynar_t process_pattern = xbt_dynar_new(sizeof(simgrid::mc::PatternCommunication*), nullptr);
380 xbt_dynar_insert_at(incomplete_communications_pattern, i, &process_pattern);
383 std::unique_ptr<simgrid::mc::State> initial_state =
384 std::unique_ptr<simgrid::mc::State>(new simgrid::mc::State(++expandedStatesCount_));
386 XBT_DEBUG("********* Start communication determinism verification *********");
388 /* Get an enabled actor and insert it in the interleave set of the initial state */
389 for (auto& actor : mc_model_checker->process().actors())
390 if (simgrid::mc::actor_is_enabled(actor.copy.getBuffer()))
391 initial_state->interleave(actor.copy.getBuffer());
393 stack_.push_back(std::move(initial_state));
397 bool all_communications_are_finished()
399 for (size_t current_actor = 1; current_actor < MC_smx_get_maxpid(); current_actor++) {
400 xbt_dynar_t pattern = xbt_dynar_get_as(incomplete_communications_pattern, current_actor, xbt_dynar_t);
401 if (!xbt_dynar_is_empty(pattern)) {
402 XBT_DEBUG("Some communications are not finished, cannot stop the exploration ! State not visited.");
409 void CommunicationDeterminismChecker::restoreState()
411 /* Intermediate backtracking */
413 simgrid::mc::State* state = stack_.back().get();
414 if (state->system_state) {
415 simgrid::mc::restore_snapshot(state->system_state);
416 MC_restore_communications_pattern(state);
421 /* Restore the initial state */
422 simgrid::mc::session->restoreInitialState();
424 // int n = xbt_dynar_length(incomplete_communications_pattern);
425 unsigned n = MC_smx_get_maxpid();
426 assert(n == xbt_dynar_length(incomplete_communications_pattern));
427 assert(n == xbt_dynar_length(initial_communications_pattern));
428 for (unsigned j=0; j < n ; j++) {
429 xbt_dynar_reset((xbt_dynar_t)xbt_dynar_get_as(incomplete_communications_pattern, j, xbt_dynar_t));
430 xbt_dynar_get_as(initial_communications_pattern, j, simgrid::mc::PatternCommunicationList*)->index_comm = 0;
433 /* Traverse the stack from the state at position start and re-execute the transitions */
434 for (std::unique_ptr<simgrid::mc::State> const& state : stack_) {
435 if (state == stack_.back())
438 int req_num = state->transition.argument;
439 smx_simcall_t saved_req = &state->executed_req;
440 xbt_assert(saved_req);
442 /* because we got a copy of the executed request, we have to fetch the
443 real one, pointed by the request field of the issuer process */
445 const smx_actor_t issuer = MC_smx_simcall_get_issuer(saved_req);
446 smx_simcall_t req = &issuer->simcall;
448 /* TODO : handle test and testany simcalls */
449 e_mc_call_type_t call = MC_get_call_type(req);
450 mc_model_checker->handle_simcall(state->transition);
451 MC_handle_comm_pattern(call, req, req_num, nullptr, 1);
452 mc_model_checker->wait_for_requests();
454 /* Update statistics */
455 mc_model_checker->visited_states++;
456 mc_model_checker->executed_transitions++;
460 int CommunicationDeterminismChecker::main(void)
462 std::unique_ptr<simgrid::mc::VisitedState> visited_state = nullptr;
463 smx_simcall_t req = nullptr;
465 while (!stack_.empty()) {
467 /* Get current state */
468 simgrid::mc::State* state = stack_.back().get();
470 XBT_DEBUG("**************************************************");
471 XBT_DEBUG("Exploration depth = %zi (state = %d, interleaved processes = %zd)",
472 stack_.size(), state->num,
473 state->interleaveSize());
475 /* Update statistics */
476 mc_model_checker->visited_states++;
478 if (stack_.size() <= (std::size_t) _sg_mc_max_depth
479 && (req = MC_state_get_request(state)) != nullptr
480 && (visited_state == nullptr)) {
482 int req_num = state->transition.argument;
484 XBT_DEBUG("Execute: %s",
485 simgrid::mc::request_to_string(
486 req, req_num, simgrid::mc::RequestType::simix).c_str());
489 if (dot_output != nullptr)
490 req_str = simgrid::mc::request_get_dot_output(req, req_num);
492 mc_model_checker->executed_transitions++;
494 /* TODO : handle test and testany simcalls */
495 e_mc_call_type_t call = MC_CALL_TYPE_NONE;
496 if (_sg_mc_comms_determinism || _sg_mc_send_determinism)
497 call = MC_get_call_type(req);
499 /* Answer the request */
500 mc_model_checker->handle_simcall(state->transition);
501 /* After this call req is no longer useful */
503 if (!this->initial_communications_pattern_done)
504 MC_handle_comm_pattern(call, req, req_num, initial_communications_pattern, 0);
506 MC_handle_comm_pattern(call, req, req_num, nullptr, 0);
508 /* Wait for requests (schedules processes) */
509 mc_model_checker->wait_for_requests();
511 /* Create the new expanded state */
512 std::unique_ptr<simgrid::mc::State> next_state =
513 std::unique_ptr<simgrid::mc::State>(new simgrid::mc::State(++expandedStatesCount_));
515 /* If comm determinism verification, we cannot stop the exploration if
516 some communications are not finished (at least, data are transferred).
517 These communications are incomplete and they cannot be analyzed and
518 compared with the initial pattern. */
519 bool compare_snapshots = all_communications_are_finished()
520 && this->initial_communications_pattern_done;
522 if (_sg_mc_visited == 0
523 || (visited_state = visitedStates_.addVisitedState(
524 expandedStatesCount_, next_state.get(), compare_snapshots)) == nullptr) {
526 /* Get enabled actors and insert them in the interleave set of the next state */
527 for (auto& actor : mc_model_checker->process().actors())
528 if (simgrid::mc::actor_is_enabled(actor.copy.getBuffer()))
529 next_state->interleave(actor.copy.getBuffer());
531 if (dot_output != nullptr)
532 fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n",
533 state->num, next_state->num, req_str.c_str());
535 } else if (dot_output != nullptr)
536 fprintf(dot_output, "\"%d\" -> \"%d\" [%s];\n",
537 state->num, visited_state->other_num == -1 ? visited_state->num : visited_state->other_num, req_str.c_str());
539 stack_.push_back(std::move(next_state));
543 if (stack_.size() > (std::size_t) _sg_mc_max_depth)
544 XBT_WARN("/!\\ Max depth reached ! /!\\ ");
545 else if (visited_state != nullptr)
546 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);
548 XBT_DEBUG("There are no more processes to interleave. (depth %zi)",
551 if (!this->initial_communications_pattern_done)
552 this->initial_communications_pattern_done = 1;
554 /* Trash the current state, no longer needed */
555 XBT_DEBUG("Delete state %d at depth %zi",
556 state->num, stack_.size());
559 visited_state = nullptr;
561 /* Check for deadlocks */
562 if (mc_model_checker->checkDeadlock()) {
564 return SIMGRID_MC_EXIT_DEADLOCK;
567 while (!stack_.empty()) {
568 std::unique_ptr<simgrid::mc::State> state = std::move(stack_.back());
570 if (state->interleaveSize()
571 && stack_.size() < (std::size_t) _sg_mc_max_depth) {
572 /* We found a back-tracking point, let's loop */
573 XBT_DEBUG("Back-tracking to state %d at depth %zi",
574 state->num, stack_.size() + 1);
575 stack_.push_back(std::move(state));
577 this->restoreState();
579 XBT_DEBUG("Back-tracking to state %d at depth %zi done",
580 stack_.back()->num, stack_.size());
584 XBT_DEBUG("Delete state %d at depth %zi",
585 state->num, stack_.size() + 1);
591 simgrid::mc::session->logState();
592 return SIMGRID_MC_EXIT_SUCCESS;
595 int CommunicationDeterminismChecker::run()
597 XBT_INFO("Check communication determinism");
598 simgrid::mc::session->initialize();
602 this->initial_communications_pattern_done = 0;
603 this->recv_deterministic = 1;
604 this->send_deterministic = 1;
605 this->recv_diff = nullptr;
606 this->send_diff = nullptr;
608 int res = this->main();
612 Checker* createCommunicationDeterminismChecker(Session& session)
614 return new CommunicationDeterminismChecker(session);