From: Ehsan Azimi Date: Fri, 27 Nov 2020 23:12:04 +0000 (+0100) Subject: Introduce mc::mc_api (pull request 1 -- #349) X-Git-Tag: v3.26~74 X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/commitdiff_plain/6b8d58b226c5c14d9b27d7ba55230d7ba7cfbd16 Introduce mc::mc_api (pull request 1 -- #349) * mc_api class introduced, SafetyChecher's constructor and main() in simgrid_mc.cpp call mc_api's functions * SafetyChecker::restore_state() calls mc_api functions * SafetyChecker::backtrack() calls mc_api functions * SafetyChecker::log_state() calls APIs of mc_api * SafetyChecker::get_textual_trace() uses mc_api * SafetyChecker::check_non_termination() uses mc_api * SafetyChecker::run() uses mc_api * mc_assert() deleted * get_maxpid() and take_snapshot() in mc_api * copy_incomplete_comm_pattern() and copy_index_comm_pattern() in mc_api * mc_api::mc_state_choose_request() updated * In VisitedState class, get_remote_heap_bytes() from mc_api is called * prepare() and run() call APIs of mc_api * CommunicationDeterminismChecker::real_run() uses APIs of mc_api * CommunicationDeterminismChecker::restoreState() uses APIs of mc_api * CommunicationDeterminismChecker::log_state() uses APIs of mc_api * mc_pattern.hpp created * get_pattern_comm_rdv() defined in mc_api and used in CommDet checker * mc_api::get_pattern_comm_rdv() mc_api::get_pattern_comm_addr() mc_api::get_pattern_comm_src_proc() mc_api::get_pattern_comm_data() mc_api::get_actor_host_name() The above functions are defined in mc_api and being used in comm_dete checker * conflict with simgrid/master resolved * unused variable removed * call APIs from facade layer * mc_api::get_smpi_request_tag() defined and used in comm_deter checker * mc_api::get_pattern_comm_dst_proc() defined and used in comm_deter checker * mc_api::check_send_request_detached() defined and used in comm_deter checker * CommunicationDeterminismChecker::deterministic_comm_pattern() uses APIs of mc_api * mc_api clean up * mc_api::get_src_actor() defined, it used in update_comm_pattern() of comm. deter. checker * mc_api::get_dst_actor() defined, it used in update_comm_pattern() of comm. deter. checker * mc_api::get_actor_host_name() used by update_comm_pattern() in comm. deter. checker * mc_api::get_pattern_comm_data() defined, it used by update_comm_pattern() in comm. deter. checker Co-authored-by: Ehsan Azimi --- diff --git a/src/mc/VisitedState.cpp b/src/mc/VisitedState.cpp index 1f30705589..fa49142bf6 100644 --- a/src/mc/VisitedState.cpp +++ b/src/mc/VisitedState.cpp @@ -10,22 +10,20 @@ #include #include #include +#include "src/mc/mc_api.hpp" XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_VisitedState, mc, "Logging specific to state equality detection mechanisms"); +using mcapi = simgrid::mc::mc_api; + namespace simgrid { namespace mc { /** @brief Save the current state */ VisitedState::VisitedState(unsigned long state_number) : num(state_number) -{ - simgrid::mc::RemoteSimulation* process = &(mc_model_checker->get_remote_simulation()); - this->heap_bytes_used = mmalloc_get_bytes_used_remote( - process->get_heap()->heaplimit, - process->get_malloc_info()); - - this->actors_count = mc_model_checker->get_remote_simulation().actors().size(); - +{ + this->heap_bytes_used = mcapi::get().get_remote_heap_bytes(); + this->actors_count = mcapi::get().mc_get_remote_simulation().actors().size(); this->system_state = std::make_shared(state_number); } @@ -58,7 +56,7 @@ VisitedStates::addVisitedState(unsigned long state_number, simgrid::mc::State* g if (compare_snapshots) for (auto i = range.first; i != range.second; ++i) { auto& visited_state = *i; - if (snapshot_equal(visited_state->system_state.get(), new_state->system_state.get())) { + if (mcapi::get().snapshot_equal(visited_state->system_state.get(), new_state->system_state.get())) { // The state has been visited: std::unique_ptr old_state = diff --git a/src/mc/checker/CommunicationDeterminismChecker.cpp b/src/mc/checker/CommunicationDeterminismChecker.cpp index 4022f9087c..9ac7aa3909 100644 --- a/src/mc/checker/CommunicationDeterminismChecker.cpp +++ b/src/mc/checker/CommunicationDeterminismChecker.cpp @@ -6,6 +6,7 @@ #include "src/mc/checker/CommunicationDeterminismChecker.hpp" #include "src/kernel/activity/MailboxImpl.hpp" #include "src/mc/Session.hpp" +#include "src/mc/mc_api.hpp" #include "src/mc/mc_config.hpp" #include "src/mc/mc_exit.hpp" #include "src/mc/mc_private.hpp" @@ -19,6 +20,7 @@ #include using simgrid::mc::remote; +using mcapi = simgrid::mc::mc_api; XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_comm_determinism, mc, "Logging specific to MC communication determinism detection"); @@ -33,7 +35,7 @@ static simgrid::mc::CommPatternDifference compare_comm_pattern(const simgrid::mc const simgrid::mc::PatternCommunication* comm2) { using simgrid::mc::CommPatternDifference; - if(comm1->type != comm2->type) + if (comm1->type != comm2->type) return CommPatternDifference::TYPE; if (comm1->rdv != comm2->rdv) return CommPatternDifference::RDV; @@ -62,7 +64,7 @@ static char* print_determinism_result(simgrid::mc::CommPatternDifference diff, i type = bprintf("The recv communications pattern of the process %d is different!", process - 1); using simgrid::mc::CommPatternDifference; - switch(diff) { + switch (diff) { case CommPatternDifference::TYPE: res = bprintf("%s Different type for communication #%u", type, cursor); break; @@ -95,26 +97,21 @@ static char* print_determinism_result(simgrid::mc::CommPatternDifference diff, i static void update_comm_pattern(simgrid::mc::PatternCommunication* comm_pattern, simgrid::mc::RemotePtr comm_addr) { - // HACK, type punning - simgrid::mc::Remote temp_comm; - mc_model_checker->get_remote_simulation().read(temp_comm, comm_addr); - const simgrid::kernel::activity::CommImpl* comm = temp_comm.get_buffer(); - - smx_actor_t src_proc = - mc_model_checker->get_remote_simulation().resolve_actor(simgrid::mc::remote(comm->src_actor_.get())); - smx_actor_t dst_proc = - mc_model_checker->get_remote_simulation().resolve_actor(simgrid::mc::remote(comm->dst_actor_.get())); + auto src_proc = mcapi::get().get_src_actor(comm_addr); + auto dst_proc = mcapi::get().get_dst_actor(comm_addr); comm_pattern->src_proc = src_proc->get_pid(); comm_pattern->dst_proc = dst_proc->get_pid(); - comm_pattern->src_host = MC_smx_actor_get_host_name(src_proc); - comm_pattern->dst_host = MC_smx_actor_get_host_name(dst_proc); - if (comm_pattern->data.empty() && comm->src_buff_ != nullptr) { - size_t buff_size; - mc_model_checker->get_remote_simulation().read(&buff_size, remote(comm->dst_buff_size_)); - comm_pattern->data.resize(buff_size); - mc_model_checker->get_remote_simulation().read_bytes(comm_pattern->data.data(), comm_pattern->data.size(), - remote(comm->src_buff_)); - } + comm_pattern->src_host = mcapi::get().get_actor_host_name(src_proc); + comm_pattern->dst_host = mcapi::get().get_actor_host_name(dst_proc); + + if (comm_pattern->data.empty()) { + auto pattern_data = mcapi::get().get_pattern_comm_data(comm_addr); + if (pattern_data.data() != nullptr) { + auto data_size = pattern_data.size(); + comm_pattern->data.resize(data_size); + memcpy(comm_pattern->data.data(), pattern_data.data(), data_size); + } + } } namespace simgrid { @@ -124,8 +121,8 @@ void CommunicationDeterminismChecker::deterministic_comm_pattern(int process, co int backtracking) { if (not backtracking) { - PatternCommunicationList& list = initial_communications_pattern[process]; - CommPatternDifference diff = compare_comm_pattern(list.list[list.index_comm].get(), comm); + PatternCommunicationList& list = initial_communications_pattern[process]; + CommPatternDifference diff = compare_comm_pattern(list.list[list.index_comm].get(), comm); if (diff != CommPatternDifference::NONE) { if (comm->type == PatternCommunicationType::send) { @@ -146,8 +143,8 @@ void CommunicationDeterminismChecker::deterministic_comm_pattern(int process, co XBT_INFO("%s", this->send_diff); xbt_free(this->send_diff); this->send_diff = nullptr; - mc::session->log_state(); - mc_model_checker->exit(SIMGRID_MC_EXIT_NON_DETERMINISM); + mcapi::get().s_log_state(); + mcapi::get().mc_exit(SIMGRID_MC_EXIT_NON_DETERMINISM); } else if (_sg_mc_comms_determinism && (not this->send_deterministic && not this->recv_deterministic)) { XBT_INFO("****************************************************"); XBT_INFO("***** Non-deterministic communications pattern *****"); @@ -162,8 +159,8 @@ void CommunicationDeterminismChecker::deterministic_comm_pattern(int process, co xbt_free(this->recv_diff); this->recv_diff = nullptr; } - mc::session->log_state(); - mc_model_checker->exit(SIMGRID_MC_EXIT_NON_DETERMINISM); + mcapi::get().s_log_state(); + mcapi::get().mc_exit(SIMGRID_MC_EXIT_NON_DETERMINISM); } } } @@ -173,7 +170,7 @@ void CommunicationDeterminismChecker::deterministic_comm_pattern(int process, co void CommunicationDeterminismChecker::get_comm_pattern(smx_simcall_t request, CallType call_type, int backtracking) { - const smx_actor_t issuer = MC_smx_simcall_get_issuer(request); + const smx_actor_t issuer = mcapi::get().mc_smx_simcall_get_issuer(request); const mc::PatternCommunicationList& initial_pattern = initial_communications_pattern[issuer->get_pid()]; const std::vector& incomplete_pattern = incomplete_communications_pattern[issuer->get_pid()]; @@ -183,33 +180,24 @@ void CommunicationDeterminismChecker::get_comm_pattern(smx_simcall_t request, Ca if (call_type == CallType::SEND) { /* Create comm pattern */ pattern->type = PatternCommunicationType::send; - pattern->comm_addr = static_cast(simcall_comm_isend__getraw__result(request)); - - Remote temp_synchro; - mc_model_checker->get_remote_simulation().read(temp_synchro, remote(pattern->comm_addr)); - const kernel::activity::CommImpl* synchro = temp_synchro.get_buffer(); - - char* remote_name = mc_model_checker->get_remote_simulation().read(RemotePtr( - (uint64_t)(synchro->get_mailbox() ? &synchro->get_mailbox()->name_ : &synchro->mbox_cpy->name_))); - pattern->rdv = mc_model_checker->get_remote_simulation().read_string(RemotePtr(remote_name)); - pattern->src_proc = - mc_model_checker->get_remote_simulation().resolve_actor(mc::remote(synchro->src_actor_.get()))->get_pid(); - pattern->src_host = MC_smx_actor_get_host_name(issuer); + pattern->comm_addr = mcapi::get().get_pattern_comm_addr(request); + pattern->rdv = mcapi::get().get_pattern_comm_rdv(pattern->comm_addr); + pattern->src_proc = mcapi::get().get_pattern_comm_src_proc(pattern->comm_addr); + pattern->src_host = mc_api::get().get_actor_host_name(issuer); #if HAVE_SMPI - simgrid::smpi::Request mpi_request; - mc_model_checker->get_remote_simulation().read( - &mpi_request, remote(static_cast(simcall_comm_isend__get__data(request)))); - pattern->tag = mpi_request.tag(); + pattern->tag = mcapi::get().get_smpi_request_tag(request, simgrid::simix::Simcall::COMM_ISEND); #endif - - if (synchro->src_buff_ != nullptr) { - pattern->data.resize(synchro->src_buff_size_); - mc_model_checker->get_remote_simulation().read_bytes(pattern->data.data(), pattern->data.size(), - remote(synchro->src_buff_)); + auto pattern_data = mcapi::get().get_pattern_comm_data(pattern->comm_addr); + if (pattern_data.data() != nullptr) { + auto data_size = pattern_data.size(); + pattern->data.resize(data_size); + memcpy(pattern->data.data(), pattern_data.data(), data_size); } + #if HAVE_SMPI - if(mpi_request.detached()){ + auto send_detached = mcapi::get().check_send_request_detached(request); + if (send_detached) { if (this->initial_communications_pattern_done) { /* Evaluate comm determinism */ this->deterministic_comm_pattern(pattern->src_proc, pattern.get(), backtracking); @@ -222,30 +210,18 @@ void CommunicationDeterminismChecker::get_comm_pattern(smx_simcall_t request, Ca } #endif } else if (call_type == CallType::RECV) { - pattern->type = PatternCommunicationType::receive; - pattern->comm_addr = static_cast(simcall_comm_irecv__getraw__result(request)); + pattern->type = PatternCommunicationType::receive; + pattern->comm_addr = mcapi::get().get_pattern_comm_addr(request); #if HAVE_SMPI - smpi::Request mpi_request; - mc_model_checker->get_remote_simulation().read( - &mpi_request, remote(static_cast(simcall_comm_irecv__get__data(request)))); - pattern->tag = mpi_request.tag(); + pattern->tag = mcapi::get().get_smpi_request_tag(request, simgrid::simix::Simcall::COMM_IRECV); #endif - - Remote temp_comm; - mc_model_checker->get_remote_simulation().read(temp_comm, remote(pattern->comm_addr)); - const kernel::activity::CommImpl* comm = temp_comm.get_buffer(); - - char* remote_name; - mc_model_checker->get_remote_simulation().read( - &remote_name, remote(comm->get_mailbox() ? &xbt::string::to_string_data(comm->get_mailbox()->name_).data - : &xbt::string::to_string_data(comm->mbox_cpy->name_).data)); - pattern->rdv = mc_model_checker->get_remote_simulation().read_string(RemotePtr(remote_name)); - pattern->dst_proc = - mc_model_checker->get_remote_simulation().resolve_actor(mc::remote(comm->dst_actor_.get()))->get_pid(); - pattern->dst_host = MC_smx_actor_get_host_name(issuer); + auto comm_addr = pattern->comm_addr; + pattern->rdv = mcapi::get().get_pattern_comm_rdv(comm_addr); + pattern->dst_proc = mcapi::get().get_pattern_comm_dst_proc(comm_addr); + pattern->dst_host = mcapi::get().get_actor_host_name(issuer); } else - xbt_die("Unexpected call_type %i", (int) call_type); + xbt_die("Unexpected call_type %i", (int)call_type); XBT_DEBUG("Insert incomplete comm pattern %p for process %ld", pattern.get(), issuer->get_pid()); incomplete_communications_pattern[issuer->get_pid()].push_back(pattern.release()); @@ -278,9 +254,7 @@ void CommunicationDeterminismChecker::complete_comm_pattern(RemotePtr CommunicationDeterminismChecker::get_textual_trace() // std::vector trace; for (auto const& state : stack_) { smx_simcall_t req = &state->executed_req_; - trace.push_back(request_to_string(req, state->transition_.argument_, RequestType::executed)); + trace.push_back(mcapi::get().request_to_string(req, state->transition_.argument_, RequestType::executed)); } return trace; } @@ -319,8 +293,8 @@ void CommunicationDeterminismChecker::log_state() // override } } XBT_INFO("Expanded states = %lu", expanded_states_count_); - XBT_INFO("Visited states = %lu", mc_model_checker->visited_states); - XBT_INFO("Executed transitions = %lu", mc_model_checker->executed_transitions); + XBT_INFO("Visited states = %lu", mcapi::get().mc_get_visited_states()); + XBT_INFO("Executed transitions = %lu", mcapi::get().mc_get_executed_trans()); XBT_INFO("Send-deterministic : %s", this->send_deterministic ? "Yes" : "No"); if (_sg_mc_comms_determinism) XBT_INFO("Recv-deterministic : %s", this->recv_deterministic ? "Yes" : "No"); @@ -328,7 +302,7 @@ void CommunicationDeterminismChecker::log_state() // override void CommunicationDeterminismChecker::prepare() { - const int maxpid = MC_smx_get_maxpid(); + const int maxpid = mcapi::get().get_maxpid(); initial_communications_pattern.resize(maxpid); incomplete_communications_pattern.resize(maxpid); @@ -339,8 +313,9 @@ void CommunicationDeterminismChecker::prepare() XBT_DEBUG("********* Start communication determinism verification *********"); /* Get an enabled actor and insert it in the interleave set of the initial state */ - for (auto& actor : mc_model_checker->get_remote_simulation().actors()) - if (mc::actor_is_enabled(actor.copy.get_buffer())) + auto actors = mcapi::get().get_actors(); + for (auto& actor : actors) + if (mcapi::get().actor_is_enabled(actor.copy.get_buffer()->get_pid())) initial_state->add_interleaving_set(actor.copy.get_buffer()); stack_.push_back(std::move(initial_state)); @@ -348,7 +323,8 @@ void CommunicationDeterminismChecker::prepare() static inline bool all_communications_are_finished() { - for (size_t current_actor = 1; current_actor < MC_smx_get_maxpid(); current_actor++) { + auto maxpid = mcapi::get().get_maxpid(); + for (size_t current_actor = 1; current_actor < maxpid; current_actor++) { if (not incomplete_communications_pattern[current_actor].empty()) { XBT_DEBUG("Some communications are not finished, cannot stop the exploration! State not visited."); return false; @@ -362,18 +338,18 @@ void CommunicationDeterminismChecker::restoreState() /* Intermediate backtracking */ State* last_state = stack_.back().get(); if (last_state->system_state_) { - last_state->system_state_->restore(&mc_model_checker->get_remote_simulation()); + last_state->system_state_->restore(&mcapi::get().mc_get_remote_simulation()); MC_restore_communications_pattern(last_state); return; } /* Restore the initial state */ - mc::session->restore_initial_state(); + mcapi::get().s_restore_initial_state(); - unsigned n = MC_smx_get_maxpid(); + unsigned n = mcapi::get().get_maxpid(); assert(n == incomplete_communications_pattern.size()); assert(n == initial_communications_pattern.size()); - for (unsigned j=0; j < n ; j++) { + for (unsigned j = 0; j < n; j++) { incomplete_communications_pattern[j].clear(); initial_communications_pattern[j].index_comm = 0; } @@ -383,32 +359,32 @@ void CommunicationDeterminismChecker::restoreState() if (state == stack_.back()) break; - int req_num = state->transition_.argument_; + int req_num = state->transition_.argument_; const s_smx_simcall* saved_req = &state->executed_req_; xbt_assert(saved_req); /* because we got a copy of the executed request, we have to fetch the real one, pointed by the request field of the issuer process */ - const smx_actor_t issuer = MC_smx_simcall_get_issuer(saved_req); + const smx_actor_t issuer = mcapi::get().mc_smx_simcall_get_issuer(saved_req); smx_simcall_t req = &issuer->simcall_; /* TODO : handle test and testany simcalls */ CallType call = MC_get_call_type(req); - mc_model_checker->handle_simcall(state->transition_); + mcapi::get().handle_simcall(state->transition_); MC_handle_comm_pattern(call, req, req_num, 1); - mc_model_checker->wait_for_requests(); + mcapi::get().mc_wait_for_requests(); /* Update statistics */ - mc_model_checker->visited_states++; - mc_model_checker->executed_transitions++; + mcapi::get().mc_inc_visited_states(); + mcapi::get().mc_inc_executed_trans(); } } void CommunicationDeterminismChecker::real_run() { std::unique_ptr visited_state = nullptr; - smx_simcall_t req = nullptr; + smx_simcall_t req = nullptr; while (not stack_.empty()) { /* Get current state */ @@ -419,23 +395,23 @@ void CommunicationDeterminismChecker::real_run() cur_state->interleave_size()); /* Update statistics */ - mc_model_checker->visited_states++; + mcapi::get().mc_inc_visited_states(); if (stack_.size() <= (std::size_t)_sg_mc_max_depth) - req = MC_state_choose_request(cur_state); + req = mcapi::get().mc_state_choose_request(cur_state); else req = nullptr; if (req != nullptr && visited_state == nullptr) { int req_num = cur_state->transition_.argument_; - XBT_DEBUG("Execute: %s", request_to_string(req, req_num, RequestType::simix).c_str()); + XBT_DEBUG("Execute: %s", mcapi::get().request_to_string(req, req_num, RequestType::simix).c_str()); std::string req_str; if (dot_output != nullptr) - req_str = request_get_dot_output(req, req_num); + req_str = mcapi::get().request_get_dot_output(req, req_num); - mc_model_checker->executed_transitions++; + mcapi::get().mc_inc_executed_trans(); /* TODO : handle test and testany simcalls */ CallType call = CallType::NONE; @@ -443,13 +419,13 @@ void CommunicationDeterminismChecker::real_run() call = MC_get_call_type(req); /* Answer the request */ - mc_model_checker->handle_simcall(cur_state->transition_); + mcapi::get().handle_simcall(cur_state->transition_); /* After this call req is no longer useful */ MC_handle_comm_pattern(call, req, req_num, 0); /* Wait for requests (schedules processes) */ - mc_model_checker->wait_for_requests(); + mcapi::get().mc_wait_for_requests(); /* Create the new expanded state */ ++expanded_states_count_; @@ -467,8 +443,9 @@ void CommunicationDeterminismChecker::real_run() if (visited_state == nullptr) { /* Get enabled actors and insert them in the interleave set of the next state */ - for (auto& actor : mc_model_checker->get_remote_simulation().actors()) - if (simgrid::mc::actor_is_enabled(actor.copy.get_buffer())) + auto actors = mcapi::get().mc_get_remote_simulation().actors(); + for (auto& actor : actors) + if (mcapi::get().actor_is_enabled(actor.copy.get_buffer()->get_pid())) next_state->add_interleaving_set(actor.copy.get_buffer()); if (dot_output != nullptr) @@ -480,11 +457,11 @@ void CommunicationDeterminismChecker::real_run() stack_.push_back(std::move(next_state)); } else { - if (stack_.size() > (std::size_t) _sg_mc_max_depth) + if (stack_.size() > (std::size_t)_sg_mc_max_depth) XBT_WARN("/!\\ Max depth reached! /!\\ "); else if (visited_state != nullptr) XBT_DEBUG("State already visited (equal to state %d), exploration stopped on this path.", - visited_state->original_num == -1 ? visited_state->num : visited_state->original_num); + visited_state->original_num == -1 ? visited_state->num : visited_state->original_num); else XBT_DEBUG("There are no more processes to interleave. (depth %zu)", stack_.size()); @@ -497,8 +474,8 @@ void CommunicationDeterminismChecker::real_run() visited_state = nullptr; /* Check for deadlocks */ - if (mc_model_checker->checkDeadlock()) { - MC_show_deadlock(); + if (mcapi::get().mc_check_deadlock()) { + mcapi::get().mc_show_deadlock(); throw simgrid::mc::DeadlockError(); } @@ -522,13 +499,13 @@ void CommunicationDeterminismChecker::real_run() } } - mc::session->log_state(); + mcapi::get().s_log_state(); } void CommunicationDeterminismChecker::run() { XBT_INFO("Check communication determinism"); - mc::session->initialize(); + mcapi::get().s_initialize(); this->prepare(); this->real_run(); diff --git a/src/mc/checker/LivenessChecker.cpp b/src/mc/checker/LivenessChecker.cpp index 377c3aa1b5..4d360523ec 100644 --- a/src/mc/checker/LivenessChecker.cpp +++ b/src/mc/checker/LivenessChecker.cpp @@ -10,12 +10,15 @@ #include "src/mc/mc_private.hpp" #include "src/mc/mc_request.hpp" #include "src/mc/mc_smx.hpp" +#include "src/mc/mc_api.hpp" #include #include XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_liveness, mc, "Logging specific to algorithms for liveness properties verification"); +using mcapi = simgrid::mc::mc_api; + /********* Static functions *********/ namespace simgrid { @@ -368,7 +371,7 @@ void LivenessChecker::run() } } - smx_simcall_t req = MC_state_choose_request(current_pair->graph_state.get()); + smx_simcall_t req = mcapi::get().mc_state_choose_request(current_pair->graph_state.get()); int req_num = current_pair->graph_state->transition_.argument_; if (dot_output != nullptr) { diff --git a/src/mc/checker/SafetyChecker.cpp b/src/mc/checker/SafetyChecker.cpp index feb4463064..c8d29ec987 100644 --- a/src/mc/checker/SafetyChecker.cpp +++ b/src/mc/checker/SafetyChecker.cpp @@ -23,9 +23,12 @@ #include "src/mc/mc_record.hpp" #include "src/mc/mc_request.hpp" #include "src/mc/mc_smx.hpp" +#include "src/mc/mc_api.hpp" #include "src/xbt/mmalloc/mmprivate.h" +using mcapi = simgrid::mc::mc_api; + XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_safety, mc, "Logging specific to MC safety verification "); namespace simgrid { @@ -34,16 +37,17 @@ namespace mc { void SafetyChecker::check_non_termination(const State* current_state) { for (auto state = stack_.rbegin(); state != stack_.rend(); ++state) - if (snapshot_equal((*state)->system_state_.get(), current_state->system_state_.get())) { + if (mcapi::get().snapshot_equal((*state)->system_state_.get(), current_state->system_state_.get())) { XBT_INFO("Non-progressive cycle: state %d -> state %d", (*state)->num_, current_state->num_); XBT_INFO("******************************************"); XBT_INFO("*** NON-PROGRESSIVE CYCLE DETECTED ***"); XBT_INFO("******************************************"); XBT_INFO("Counter-example execution trace:"); - for (auto const& s : mc_model_checker->getChecker()->get_textual_trace()) + auto checker = mcapi::get().mc_get_checker(); + for (auto const& s : checker->get_textual_trace()) XBT_INFO(" %s", s.c_str()); - dumpRecordPath(); - session->log_state(); + mcapi::get().mc_dump_record_path(); + mcapi::get().s_log_state(); throw TerminationError(); } @@ -63,7 +67,7 @@ std::vector SafetyChecker::get_textual_trace() // override for (auto const& state : stack_) { int value = state->transition_.argument_; smx_simcall_t req = &state->executed_req_; - trace.push_back(request_to_string(req, value, RequestType::executed)); + trace.push_back(mcapi::get().request_to_string(req, value, RequestType::executed)); } return trace; } @@ -71,8 +75,8 @@ std::vector SafetyChecker::get_textual_trace() // override void SafetyChecker::log_state() // override { XBT_INFO("Expanded states = %lu", expanded_states_count_); - XBT_INFO("Visited states = %lu", mc_model_checker->visited_states); - XBT_INFO("Executed transitions = %lu", mc_model_checker->executed_transitions); + XBT_INFO("Visited states = %lu", mcapi::get().mc_get_visited_states()); + XBT_INFO("Executed transitions = %lu", mcapi::get().mc_get_executed_trans()); } void SafetyChecker::run() @@ -89,7 +93,7 @@ void SafetyChecker::run() XBT_VERB("Exploration depth=%zu (state=%p, num %d)(%zu interleave)", stack_.size(), state, state->num_, state->interleave_size()); - mc_model_checker->visited_states++; + mcapi::get().mc_inc_visited_states(); // Backtrack if we reached the maximum depth if (stack_.size() > (std::size_t)_sg_mc_max_depth) { @@ -110,7 +114,7 @@ void SafetyChecker::run() // Search an enabled transition in the current state; backtrack if the interleave set is empty // get_request also sets state.transition to be the one corresponding to the returned req - smx_simcall_t req = MC_state_choose_request(state); + smx_simcall_t req = mcapi::get().mc_state_choose_request(state); // req is now the transition of the process that was selected to be executed if (req == nullptr) { @@ -122,16 +126,16 @@ void SafetyChecker::run() // If there are processes to interleave and the maximum depth has not been // reached then perform one step of the exploration algorithm. - XBT_DEBUG("Execute: %s", request_to_string(req, state->transition_.argument_, RequestType::simix).c_str()); + XBT_DEBUG("Execute: %s", mcapi::get().request_to_string(req, state->transition_.argument_, RequestType::simix).c_str()); std::string req_str; if (dot_output != nullptr) - req_str = request_get_dot_output(req, state->transition_.argument_); + req_str = mcapi::get().request_get_dot_output(req, state->transition_.argument_); - mc_model_checker->executed_transitions++; + mcapi::get().mc_inc_executed_trans(); /* Actually answer the request: let execute the selected request (MCed does one step) */ - this->get_session().execute(state->transition_); + mcapi::get().execute(state->transition_); /* Create the new expanded state (copy the state of MCed into our MCer data) */ ++expanded_states_count_; @@ -147,9 +151,10 @@ void SafetyChecker::run() /* If this is a new state (or if we don't care about state-equality reduction) */ if (visited_state_ == nullptr) { /* Get an enabled process and insert it in the interleave set of the next state */ - for (auto& remoteActor : mc_model_checker->get_remote_simulation().actors()) { + auto actors = mcapi::get().get_actors(); + for (auto& remoteActor : actors) { auto actor = remoteActor.copy.get_buffer(); - if (actor_is_enabled(actor)) { + if (mcapi::get().actor_is_enabled(actor->get_pid())) { next_state->add_interleaving_set(actor); if (reductionMode_ == ReductionMode::dpor) break; // With DPOR, we take the first enabled transition @@ -168,7 +173,7 @@ void SafetyChecker::run() } XBT_INFO("No property violation found."); - session->log_state(); + mcapi::get().s_log_state(); } void SafetyChecker::backtrack() @@ -176,8 +181,8 @@ void SafetyChecker::backtrack() stack_.pop_back(); /* Check for deadlocks */ - if (mc_model_checker->checkDeadlock()) { - MC_show_deadlock(); + if (mcapi::get().mc_check_deadlock()) { + mcapi::get().mc_show_deadlock(); throw DeadlockError(); } @@ -194,19 +199,19 @@ void SafetyChecker::backtrack() if (req->call_ == simix::Simcall::MUTEX_LOCK || req->call_ == simix::Simcall::MUTEX_TRYLOCK) xbt_die("Mutex is currently not supported with DPOR, use --cfg=model-check/reduction:none"); - const kernel::actor::ActorImpl* issuer = MC_smx_simcall_get_issuer(req); + const kernel::actor::ActorImpl* issuer = mcapi::get().mc_smx_simcall_get_issuer(req); for (auto i = stack_.rbegin(); i != stack_.rend(); ++i) { State* prev_state = i->get(); - if (request_depend(req, &prev_state->internal_req_)) { + if (mcapi::get().request_depend(req, &prev_state->internal_req_)) { if (XBT_LOG_ISENABLED(mc_safety, xbt_log_priority_debug)) { XBT_DEBUG("Dependent Transitions:"); int value = prev_state->transition_.argument_; smx_simcall_t prev_req = &prev_state->executed_req_; - XBT_DEBUG("%s (state=%d)", simgrid::mc::request_to_string(prev_req, value, RequestType::internal).c_str(), + XBT_DEBUG("%s (state=%d)", mcapi::get().request_to_string(prev_req, value, RequestType::internal).c_str(), prev_state->num_); value = state->transition_.argument_; prev_req = &state->executed_req_; - XBT_DEBUG("%s (state=%d)", simgrid::mc::request_to_string(prev_req, value, RequestType::executed).c_str(), + XBT_DEBUG("%s (state=%d)", mcapi::get().request_to_string(prev_req, value, RequestType::executed).c_str(), state->num_); } @@ -216,14 +221,14 @@ void SafetyChecker::backtrack() XBT_DEBUG("Process %p is in done set", req->issuer_); break; } else if (req->issuer_ == prev_state->internal_req_.issuer_) { - XBT_DEBUG("Simcall %s and %s with same issuer", SIMIX_simcall_name(req->call_), - SIMIX_simcall_name(prev_state->internal_req_.call_)); + XBT_DEBUG("Simcall %s and %s with same issuer", mcapi::get().simix_simcall_name(req->call_), + mcapi::get().simix_simcall_name(prev_state->internal_req_.call_)); break; } else { - const kernel::actor::ActorImpl* previous_issuer = MC_smx_simcall_get_issuer(&prev_state->internal_req_); + const kernel::actor::ActorImpl* previous_issuer = mcapi::get().mc_smx_simcall_get_issuer(&prev_state->internal_req_); XBT_DEBUG("Simcall %s, process %ld (state %d) and simcall %s, process %ld (state %d) are independent", - SIMIX_simcall_name(req->call_), issuer->get_pid(), state->num_, - SIMIX_simcall_name(prev_state->internal_req_.call_), previous_issuer->get_pid(), prev_state->num_); + mcapi::get().simix_simcall_name(req->call_), issuer->get_pid(), state->num_, + mcapi::get().simix_simcall_name(prev_state->internal_req_.call_), previous_issuer->get_pid(), prev_state->num_); } } } @@ -246,21 +251,21 @@ void SafetyChecker::restore_state() /* Intermediate backtracking */ const State* last_state = stack_.back().get(); if (last_state->system_state_) { - last_state->system_state_->restore(&mc_model_checker->get_remote_simulation()); + last_state->system_state_->restore(&mcapi::get().mc_get_remote_simulation()); return; } /* Restore the initial state */ - session->restore_initial_state(); + mcapi::get().s_restore_initial_state(); /* Traverse the stack from the state at position start and re-execute the transitions */ for (std::unique_ptr const& state : stack_) { if (state == stack_.back()) break; - session->execute(state->transition_); + mcapi::get().execute(state->transition_); /* Update statistics */ - mc_model_checker->visited_states++; - mc_model_checker->executed_transitions++; + mcapi::get().mc_inc_visited_states(); + mcapi::get().mc_inc_executed_trans(); } } @@ -278,7 +283,8 @@ SafetyChecker::SafetyChecker(Session& s) : Checker(s) XBT_INFO("Check a safety property. Reduction is: %s.", (reductionMode_ == ReductionMode::none ? "none" : (reductionMode_ == ReductionMode::dpor ? "dpor" : "unknown"))); - session->initialize(); + + mcapi::get().s_initialize(); XBT_DEBUG("Starting the safety algorithm"); @@ -289,8 +295,9 @@ SafetyChecker::SafetyChecker(Session& s) : Checker(s) XBT_DEBUG("Initial state"); /* Get an enabled actor and insert it in the interleave set of the initial state */ - for (auto& actor : mc_model_checker->get_remote_simulation().actors()) - if (actor_is_enabled(actor.copy.get_buffer())) { + auto actors = mcapi::get().get_actors(); + for (auto& actor : actors) + if (mcapi::get().actor_is_enabled(actor.copy.get_buffer()->get_pid())) { initial_state->add_interleaving_set(actor.copy.get_buffer()); if (reductionMode_ != ReductionMode::none) break; diff --git a/src/mc/checker/simgrid_mc.cpp b/src/mc/checker/simgrid_mc.cpp index 9168cc9b5b..e382e9ff9f 100644 --- a/src/mc/checker/simgrid_mc.cpp +++ b/src/mc/checker/simgrid_mc.cpp @@ -10,6 +10,7 @@ #include "src/mc/mc_config.hpp" #include "src/mc/mc_exit.hpp" #include "src/internal_config.h" +#include "src/mc/mc_api.hpp" #if HAVE_SMPI #include "smpi/smpi.h" @@ -19,6 +20,8 @@ #include #include +using mcapi = simgrid::mc::mc_api; + static inline char** argvdup(int argc, char** argv) { @@ -54,15 +57,7 @@ int main(int argc, char** argv) smpi_init_options(); // only performed once #endif sg_config_init(&argc, argv); - simgrid::mc::session = new simgrid::mc::Session([argv_copy] { - int i = 1; - while (argv_copy[i] != nullptr && argv_copy[i][0] == '-') - i++; - xbt_assert(argv_copy[i] != nullptr, - "Unable to find a binary to exec on the command line. Did you only pass config flags?"); - execvp(argv_copy[i], argv_copy + i); - xbt_die("The model-checked process failed to exec(): %s", strerror(errno)); - }); + mcapi::get().initialize(argv_copy); delete[] argv_copy; auto checker = create_checker(*simgrid::mc::session); @@ -77,6 +72,6 @@ int main(int argc, char** argv) res = SIMGRID_MC_EXIT_LIVENESS; } checker = nullptr; - simgrid::mc::session->close(); + mcapi::get().s_close(); return res; } diff --git a/src/mc/mc_api.cpp b/src/mc/mc_api.cpp new file mode 100644 index 0000000000..29e895bc10 --- /dev/null +++ b/src/mc/mc_api.cpp @@ -0,0 +1,491 @@ +#include "mc_api.hpp" + +#include "src/kernel/activity/MailboxImpl.hpp" +#include "src/mc/Session.hpp" +#include "src/mc/mc_comm_pattern.hpp" +#include "src/mc/mc_private.hpp" +#include "src/mc/mc_record.hpp" +#include "src/mc/mc_smx.hpp" +#include "src/mc/remote/RemoteSimulation.hpp" +#include "src/mc/mc_pattern.hpp" + +#include +#include + +#if HAVE_SMPI +#include "src/smpi/include/smpi_request.hpp" +#endif + +XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_api, mc, "Logging specific to MC Fasade APIs "); + +using Simcall = simgrid::simix::Simcall; + +namespace simgrid { +namespace mc { + +/* Search an enabled transition for the given process. + * + * This can be seen as an iterator returning the next transition of the process. + * + * We only consider the processes that are both + * - marked "to be interleaved" in their ActorState (controlled by the checker algorithm). + * - which simcall can currently be executed (like a comm where the other partner is already known) + * Once we returned the last enabled transition of a process, it is marked done. + * + * Things can get muddled with the WAITANY and TESTANY simcalls, that are rewritten on the fly to a bunch of WAIT + * (resp TEST) transitions using the transition.argument field to remember what was the last returned sub-transition. + */ +static inline smx_simcall_t MC_state_choose_request_for_process(simgrid::mc::State* state, smx_actor_t actor) +{ + /* reset the outgoing transition */ + simgrid::mc::ActorState* procstate = &state->actor_states_[actor->get_pid()]; + state->transition_.pid_ = -1; + state->transition_.argument_ = -1; + state->executed_req_.call_ = Simcall::NONE; + + if (not simgrid::mc::actor_is_enabled(actor)) + return nullptr; // Not executable in the application + + smx_simcall_t req = nullptr; + switch (actor->simcall_.call_) { + case Simcall::COMM_WAITANY: + state->transition_.argument_ = -1; + while (procstate->times_considered < simcall_comm_waitany__get__count(&actor->simcall_)) { + if (simgrid::mc::request_is_enabled_by_idx(&actor->simcall_, procstate->times_considered)) { + state->transition_.argument_ = procstate->times_considered; + ++procstate->times_considered; + break; + } + ++procstate->times_considered; + } + + if (procstate->times_considered >= simcall_comm_waitany__get__count(&actor->simcall_)) + procstate->set_done(); + if (state->transition_.argument_ != -1) + req = &actor->simcall_; + break; + + case Simcall::COMM_TESTANY: { + unsigned start_count = procstate->times_considered; + state->transition_.argument_ = -1; + while (procstate->times_considered < simcall_comm_testany__get__count(&actor->simcall_)) { + if (simgrid::mc::request_is_enabled_by_idx(&actor->simcall_, procstate->times_considered)) { + state->transition_.argument_ = procstate->times_considered; + ++procstate->times_considered; + break; + } + ++procstate->times_considered; + } + + if (procstate->times_considered >= simcall_comm_testany__get__count(&actor->simcall_)) + procstate->set_done(); + + if (state->transition_.argument_ != -1 || start_count == 0) + req = &actor->simcall_; + + break; + } + + case Simcall::COMM_WAIT: { + simgrid::mc::RemotePtr remote_act = + remote(simcall_comm_wait__getraw__comm(&actor->simcall_)); + simgrid::mc::Remote temp_act; + mc_model_checker->get_remote_simulation().read(temp_act, remote_act); + const simgrid::kernel::activity::CommImpl* act = temp_act.get_buffer(); + if (act->src_actor_.get() && act->dst_actor_.get()) + state->transition_.argument_ = 0; // OK + else if (act->src_actor_.get() == nullptr && act->type_ == simgrid::kernel::activity::CommImpl::Type::READY && + act->detached()) + state->transition_.argument_ = 0; // OK + else + state->transition_.argument_ = -1; // timeout + procstate->set_done(); + req = &actor->simcall_; + break; + } + + case Simcall::MC_RANDOM: { + int min_value = simcall_mc_random__get__min(&actor->simcall_); + state->transition_.argument_ = procstate->times_considered + min_value; + procstate->times_considered++; + if (state->transition_.argument_ == simcall_mc_random__get__max(&actor->simcall_)) + procstate->set_done(); + req = &actor->simcall_; + break; + } + + default: + procstate->set_done(); + state->transition_.argument_ = 0; + req = &actor->simcall_; + break; + } + if (not req) + return nullptr; + + state->transition_.pid_ = actor->get_pid(); + state->executed_req_ = *req; + // Fetch the data of the request and translate it: + state->internal_req_ = *req; + + /* The waitany and testany request are transformed into a wait or test request over the corresponding communication + * action so it can be treated later by the dependence function. */ + switch (req->call_) { + case Simcall::COMM_WAITANY: { + state->internal_req_.call_ = Simcall::COMM_WAIT; + simgrid::kernel::activity::CommImpl* remote_comm; + remote_comm = mc_model_checker->get_remote_simulation().read( + remote(simcall_comm_waitany__get__comms(req) + state->transition_.argument_)); + mc_model_checker->get_remote_simulation().read(state->internal_comm_, remote(remote_comm)); + simcall_comm_wait__set__comm(&state->internal_req_, state->internal_comm_.get_buffer()); + simcall_comm_wait__set__timeout(&state->internal_req_, 0); + break; + } + + case Simcall::COMM_TESTANY: + state->internal_req_.call_ = Simcall::COMM_TEST; + + if (state->transition_.argument_ > 0) { + simgrid::kernel::activity::CommImpl* remote_comm = mc_model_checker->get_remote_simulation().read( + remote(simcall_comm_testany__get__comms(req) + state->transition_.argument_)); + mc_model_checker->get_remote_simulation().read(state->internal_comm_, remote(remote_comm)); + } + + simcall_comm_test__set__comm(&state->internal_req_, state->internal_comm_.get_buffer()); + simcall_comm_test__set__result(&state->internal_req_, state->transition_.argument_); + break; + + case Simcall::COMM_WAIT: + mc_model_checker->get_remote_simulation().read_bytes(&state->internal_comm_, sizeof(state->internal_comm_), + remote(simcall_comm_wait__getraw__comm(req))); + simcall_comm_wait__set__comm(&state->executed_req_, state->internal_comm_.get_buffer()); + simcall_comm_wait__set__comm(&state->internal_req_, state->internal_comm_.get_buffer()); + break; + + case Simcall::COMM_TEST: + mc_model_checker->get_remote_simulation().read_bytes(&state->internal_comm_, sizeof(state->internal_comm_), + remote(simcall_comm_test__getraw__comm(req))); + simcall_comm_test__set__comm(&state->executed_req_, state->internal_comm_.get_buffer()); + simcall_comm_test__set__comm(&state->internal_req_, state->internal_comm_.get_buffer()); + break; + + default: + /* No translation needed */ + break; + } + + return req; +} + +void mc_api::initialize(char** argv) +{ + simgrid::mc::session = new simgrid::mc::Session([argv] { + int i = 1; + while (argv[i] != nullptr && argv[i][0] == '-') + i++; + xbt_assert(argv[i] != nullptr, + "Unable to find a binary to exec on the command line. Did you only pass config flags?"); + execvp(argv[i], argv + i); + xbt_die("The model-checked process failed to exec(): %s", strerror(errno)); + }); +} + +std::vector& mc_api::get_actors() const +{ + return mc_model_checker->get_remote_simulation().actors(); +} + +bool mc_api::actor_is_enabled(aid_t pid) const +{ + return session->actor_is_enabled(pid); +} + +unsigned long mc_api::get_maxpid() const +{ + return MC_smx_get_maxpid(); +} + +void mc_api::copy_incomplete_comm_pattern(const simgrid::mc::State* state) const +{ + MC_state_copy_incomplete_communications_pattern((simgrid::mc::State*)state); +} + +void mc_api::copy_index_comm_pattern(const simgrid::mc::State* state) const +{ + MC_state_copy_index_communications_pattern((simgrid::mc::State*)state); +} + +kernel::activity::CommImpl* mc_api::get_pattern_comm_addr(smx_simcall_t request) const +{ + auto comm_addr = simcall_comm_isend__getraw__result(request); + return static_cast(comm_addr); +} + +std::string mc_api::get_pattern_comm_rdv(void* addr) const +{ + Remote temp_synchro; + mc_model_checker->get_remote_simulation().read(temp_synchro, remote((simgrid::kernel::activity::CommImpl*)addr)); + const kernel::activity::CommImpl* synchro = temp_synchro.get_buffer(); + + char* remote_name = mc_model_checker->get_remote_simulation().read(RemotePtr( + (uint64_t)(synchro->get_mailbox() ? &synchro->get_mailbox()->get_name() : &synchro->mbox_cpy->get_name()))); + auto rdv = mc_model_checker->get_remote_simulation().read_string(RemotePtr(remote_name)); + return rdv; +} + +unsigned long mc_api::get_pattern_comm_src_proc(void* addr) const +{ + Remote temp_synchro; + mc_model_checker->get_remote_simulation().read(temp_synchro, remote((simgrid::kernel::activity::CommImpl*)addr)); + const kernel::activity::CommImpl* synchro = temp_synchro.get_buffer(); + auto src_proc = mc_model_checker->get_remote_simulation().resolve_actor(mc::remote(synchro->src_actor_.get()))->get_pid(); + return src_proc; +} + +unsigned long mc_api::get_pattern_comm_dst_proc(void* addr) const +{ + Remote temp_synchro; + mc_model_checker->get_remote_simulation().read(temp_synchro, remote((simgrid::kernel::activity::CommImpl*)addr)); + const kernel::activity::CommImpl* synchro = temp_synchro.get_buffer(); + auto src_proc = mc_model_checker->get_remote_simulation().resolve_actor(mc::remote(synchro->dst_actor_.get()))->get_pid(); + return src_proc; +} + +std::vector mc_api::get_pattern_comm_data(void* addr) const +{ + Remote temp_synchro; + mc_model_checker->get_remote_simulation().read(temp_synchro, remote((simgrid::kernel::activity::CommImpl*)addr)); + const kernel::activity::CommImpl* synchro = temp_synchro.get_buffer(); + + std::vector buffer {}; + if (synchro->src_buff_ != nullptr) { + buffer.resize(synchro->src_buff_size_); + mc_model_checker->get_remote_simulation().read_bytes(buffer.data(), buffer.size(), + remote(synchro->src_buff_)); + } + return buffer; +} + +std::vector mc_api::get_pattern_comm_data(mc::RemotePtr const& comm_addr) const +{ + simgrid::mc::Remote temp_comm; + mc_model_checker->get_remote_simulation().read(temp_comm, comm_addr); + const simgrid::kernel::activity::CommImpl* comm = temp_comm.get_buffer(); + + std::vector buffer {}; + if (comm->src_buff_ != nullptr) { + buffer.resize(comm->src_buff_size_); + mc_model_checker->get_remote_simulation().read_bytes(buffer.data(), buffer.size(), + remote(comm->src_buff_)); + } + return buffer; +} + +const char* mc_api::get_actor_host_name(smx_actor_t actor) const +{ + const char* host_name = MC_smx_actor_get_host_name(actor); + return host_name; +} + +bool mc_api::check_send_request_detached(smx_simcall_t const& simcall) const +{ + simgrid::smpi::Request mpi_request; + mc_model_checker->get_remote_simulation().read( + &mpi_request, remote(static_cast(simcall_comm_isend__get__data(simcall)))); + return mpi_request.detached(); +} + +smx_actor_t mc_api::get_src_actor(mc::RemotePtr const& comm_addr) const +{ + simgrid::mc::Remote temp_comm; + mc_model_checker->get_remote_simulation().read(temp_comm, comm_addr); + const simgrid::kernel::activity::CommImpl* comm = temp_comm.get_buffer(); + + auto src_proc = mc_model_checker->get_remote_simulation().resolve_actor(simgrid::mc::remote(comm->src_actor_.get())); + return src_proc; +} + +smx_actor_t mc_api::get_dst_actor(mc::RemotePtr const& comm_addr) const +{ + simgrid::mc::Remote temp_comm; + mc_model_checker->get_remote_simulation().read(temp_comm, comm_addr); + const simgrid::kernel::activity::CommImpl* comm = temp_comm.get_buffer(); + + auto dst_proc = mc_model_checker->get_remote_simulation().resolve_actor(simgrid::mc::remote(comm->dst_actor_.get())); + return dst_proc; +} + +std::size_t mc_api::get_remote_heap_bytes() const +{ + RemoteSimulation& process = mc_model_checker->get_remote_simulation(); + auto heap_bytes_used = mmalloc_get_bytes_used_remote(process.get_heap()->heaplimit, process.get_malloc_info()); + return heap_bytes_used; +} + +void mc_api::s_initialize() const +{ + session->initialize(); +} + +ModelChecker* mc_api::get_model_checker() const +{ + return mc_model_checker; +} + +void mc_api::mc_inc_visited_states() const +{ + mc_model_checker->visited_states++; +} + +void mc_api::mc_inc_executed_trans() const +{ + mc_model_checker->executed_transitions++; +} + +unsigned long mc_api::mc_get_visited_states() const +{ + return mc_model_checker->visited_states; +} + +unsigned long mc_api::mc_get_executed_trans() const +{ + return mc_model_checker->executed_transitions; +} + +bool mc_api::mc_check_deadlock() const +{ + return mc_model_checker->checkDeadlock(); +} + +void mc_api::mc_show_deadlock() const +{ + MC_show_deadlock(); +} + +smx_actor_t mc_api::mc_smx_simcall_get_issuer(s_smx_simcall const* req) const +{ + return MC_smx_simcall_get_issuer(req); +} + +bool mc_api::mc_is_null() const +{ + auto is_null = (mc_model_checker == nullptr) ? true : false; + return is_null; +} + +Checker* mc_api::mc_get_checker() const +{ + return mc_model_checker->getChecker(); +} + +RemoteSimulation& mc_api::mc_get_remote_simulation() const +{ + return mc_model_checker->get_remote_simulation(); +} + +void mc_api::handle_simcall(Transition const& transition) const +{ + mc_model_checker->handle_simcall(transition); +} + +void mc_api::mc_wait_for_requests() const +{ + mc_model_checker->wait_for_requests(); +} + +void mc_api::mc_exit(int status) const +{ + mc_model_checker->exit(status); +} + +std::string const& mc_api::mc_get_host_name(std::string const& hostname) const +{ + return mc_model_checker->get_host_name(hostname); +} + +void mc_api::mc_dump_record_path() const +{ + simgrid::mc::dumpRecordPath(); +} + +smx_simcall_t mc_api::mc_state_choose_request(simgrid::mc::State* state) const +{ + for (auto& actor : mc_model_checker->get_remote_simulation().actors()) { + /* Only consider the actors that were marked as interleaving by the checker algorithm */ + if (not state->actor_states_[actor.copy.get_buffer()->get_pid()].is_todo()) + continue; + + smx_simcall_t res = MC_state_choose_request_for_process(state, actor.copy.get_buffer()); + if (res) + return res; + } + return nullptr; +} + +bool mc_api::request_depend(smx_simcall_t req1, smx_simcall_t req2) const +{ + return simgrid::mc::request_depend(req1, req2); +} + +std::string mc_api::request_to_string(smx_simcall_t req, int value, RequestType request_type) const +{ + return simgrid::mc::request_to_string(req, value, request_type).c_str(); +} + +std::string mc_api::request_get_dot_output(smx_simcall_t req, int value) const +{ + return simgrid::mc::request_get_dot_output(req, value); +} + +const char* mc_api::simix_simcall_name(simgrid::simix::Simcall kind) const +{ + return SIMIX_simcall_name(kind); +} + +#if HAVE_SMPI +int mc_api::get_smpi_request_tag(smx_simcall_t const& simcall, simgrid::simix::Simcall type) const +{ + simgrid::smpi::Request mpi_request; + void* simcall_data = nullptr; + if (type == Simcall::COMM_ISEND) + simcall_data = simcall_comm_isend__get__data(simcall); + else if (type == Simcall::COMM_IRECV) + simcall_data = simcall_comm_irecv__get__data(simcall); + mc_model_checker->get_remote_simulation().read(&mpi_request, remote(static_cast(simcall_data))); + return mpi_request.tag(); +} +#endif + +bool mc_api::snapshot_equal(const Snapshot* s1, const Snapshot* s2) const +{ + return simgrid::mc::snapshot_equal(s1, s2); +} + +simgrid::mc::Snapshot* mc_api::take_snapshot(int num_state) const +{ + auto snapshot = new simgrid::mc::Snapshot(num_state); + return snapshot; +} + +void mc_api::s_close() const +{ + session->close(); +} + +void mc_api::s_restore_initial_state() const +{ + session->restore_initial_state(); +} + +void mc_api::execute(Transition const& transition) +{ + session->execute(transition); +} + +void mc_api::s_log_state() const +{ + session->log_state(); +} + +} // namespace mc +} // namespace simgrid diff --git a/src/mc/mc_api.hpp b/src/mc/mc_api.hpp new file mode 100644 index 0000000000..c6def81d75 --- /dev/null +++ b/src/mc/mc_api.hpp @@ -0,0 +1,106 @@ +#ifndef SIMGRID_MC_API_HPP +#define SIMGRID_MC_API_HPP + +#include +#include + +#include "simgrid/forward.h" +#include "src/mc/mc_forward.hpp" +#include "src/mc/mc_request.hpp" +#include "src/mc/mc_state.hpp" +#include "xbt/base.h" + +namespace simgrid { +namespace mc { + +/* +** This class aimes to implement FACADE APIs for simgrid. The FACADE layer sits between the CheckerSide +** (Unfolding_Checker, DPOR, ...) layer and the +** AppSide layer. The goal is to drill down into the entagled details in the CheckerSide layer and break down the +** detailes in a way that the CheckerSide eventually +** be capable to acquire the required information through the FACADE layer rather than the direct access to the AppSide. +*/ + +class mc_api { +private: + mc_api() = default; + +public: + // No copy: + mc_api(mc_api const&) = delete; + void operator=(mc_api const&) = delete; + + static mc_api& get() + { + static mc_api mcapi; + return mcapi; + } + + void initialize(char** argv); + + // ACTOR APIs + std::vector& get_actors() const; + bool actor_is_enabled(aid_t pid) const; + unsigned long get_maxpid() const; + + // COMMUNICATION APIs + void copy_incomplete_comm_pattern(const simgrid::mc::State* state) const; + void copy_index_comm_pattern(const simgrid::mc::State* state) const; + kernel::activity::CommImpl* get_pattern_comm_addr(smx_simcall_t request) const; + std::string get_pattern_comm_rdv(void* addr) const; + unsigned long get_pattern_comm_src_proc(void* addr) const; + unsigned long get_pattern_comm_dst_proc(void* addr) const; + std::vector get_pattern_comm_data(void* addr) const; + std::vector get_pattern_comm_data(mc::RemotePtr const& comm_addr) const; + const char* get_actor_host_name(smx_actor_t actor) const; + bool check_send_request_detached(smx_simcall_t const& simcall) const; + smx_actor_t get_src_actor(mc::RemotePtr const& comm_addr) const; + smx_actor_t get_dst_actor(mc::RemotePtr const& comm_addr) const; + + // REMOTE APIs + std::size_t get_remote_heap_bytes() const; + + // MODEL CHECKER APIs + ModelChecker* get_model_checker() const; + void mc_inc_visited_states() const; + void mc_inc_executed_trans() const; + unsigned long mc_get_visited_states() const; + unsigned long mc_get_executed_trans() const; + bool mc_check_deadlock() const; + void mc_show_deadlock() const; + smx_actor_t mc_smx_simcall_get_issuer(s_smx_simcall const* req) const; + bool mc_is_null() const; + Checker* mc_get_checker() const; + RemoteSimulation& mc_get_remote_simulation() const; + void handle_simcall(Transition const& transition) const; + void mc_wait_for_requests() const; + void mc_exit(int status) const; + std::string const& mc_get_host_name(std::string const& hostname) const; + void mc_dump_record_path() const; + smx_simcall_t mc_state_choose_request(simgrid::mc::State* state) const; + + // SIMCALL APIs + bool request_depend(smx_simcall_t req1, smx_simcall_t req2) const; + std::string request_to_string(smx_simcall_t req, int value, RequestType request_type) const; + std::string request_get_dot_output(smx_simcall_t req, int value) const; + const char *simix_simcall_name(simgrid::simix::Simcall kind) const; + #if HAVE_SMPI + int get_smpi_request_tag(smx_simcall_t const& simcall, simgrid::simix::Simcall type) const; + #endif + + // SNAPSHOT APIs + bool snapshot_equal(const Snapshot* s1, const Snapshot* s2) const; + simgrid::mc::Snapshot* take_snapshot(int num_state) const; + + // SESSION APIs + void s_initialize() const; + void s_close() const; + void s_restore_initial_state() const; + void execute(Transition const& transition); + void s_log_state() const; +}; + +} // namespace mc +} // namespace simgrid + +#endif \ No newline at end of file diff --git a/src/mc/mc_base.cpp b/src/mc/mc_base.cpp index eba010b50b..ab35bd1765 100644 --- a/src/mc/mc_base.cpp +++ b/src/mc/mc_base.cpp @@ -70,12 +70,14 @@ void wait_for_requests() // Called from both MCer and MCed: bool actor_is_enabled(smx_actor_t actor) { +// #del #if SIMGRID_HAVE_MC // If in the MCer, ask the client app since it has all the data if (mc_model_checker != nullptr) { return simgrid::mc::session->actor_is_enabled(actor->get_pid()); } #endif +// # // Now, we are in the client app, no need for remote memory reading. smx_simcall_t req = &actor->simcall_; diff --git a/src/mc/mc_pattern.hpp b/src/mc/mc_pattern.hpp new file mode 100644 index 0000000000..6909e11b66 --- /dev/null +++ b/src/mc/mc_pattern.hpp @@ -0,0 +1,94 @@ +/* Copyright (c) 2007-2020. The SimGrid Team. All rights reserved. */ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ + +#ifndef SIMGRID_MC_PATTERN_H +#define SIMGRID_MC_PATTERN_H + +#include "src/kernel/activity/CommImpl.hpp" + +namespace simgrid { +namespace mc { + +enum class PatternCommunicationType { + none = 0, + send = 1, + receive = 2, +}; + +class PatternCommunication { +public: + int num = 0; + simgrid::kernel::activity::CommImpl* comm_addr; + PatternCommunicationType type = PatternCommunicationType::send; + unsigned long src_proc = 0; + unsigned long dst_proc = 0; + const char* src_host = nullptr; + const char* dst_host = nullptr; + std::string rdv; + std::vector data; + int tag = 0; + int index = 0; + + PatternCommunication() { std::memset(&comm_addr, 0, sizeof(comm_addr)); } + + PatternCommunication dup() const + { + simgrid::mc::PatternCommunication res; + // num? + res.comm_addr = this->comm_addr; + res.type = this->type; + // src_proc? + // dst_proc? + res.dst_proc = this->dst_proc; + res.dst_host = this->dst_host; + res.rdv = this->rdv; + res.data = this->data; + // tag? + res.index = this->index; + return res; + } +}; + +/* On every state, each actor has an entry of the following type. + * This represents both the actor and its transition because + * an actor cannot have more than one enabled transition at a given time. + */ +class ActorState { + /* Possible exploration status of an actor transition in a state. + * Either the checker did not consider the transition, or it was considered and still to do, or considered and done. + */ + enum class InterleavingType { + /** This actor transition is not considered by the checker (yet?) */ + disabled = 0, + /** The checker algorithm decided that this actor transitions should be done at some point */ + todo, + /** The checker algorithm decided that this should be done, but it was done in the meanwhile */ + done, + }; + + /** Exploration control information */ + InterleavingType state = InterleavingType::disabled; + +public: + /** Number of times that the process was considered to be executed */ + // TODO, make this private + unsigned int times_considered = 0; + + bool is_disabled() const { return this->state == InterleavingType::disabled; } + bool is_done() const { return this->state == InterleavingType::done; } + bool is_todo() const { return this->state == InterleavingType::todo; } + /** Mark that we should try executing this process at some point in the future of the checker algorithm */ + void consider() + { + this->state = InterleavingType::todo; + this->times_considered = 0; + } + void set_done() { this->state = InterleavingType::done; } +}; + +} // namespace mc +} // namespace simgrid + +#endif diff --git a/src/mc/mc_state.cpp b/src/mc/mc_state.cpp index de4158b47f..c078742b53 100644 --- a/src/mc/mc_state.cpp +++ b/src/mc/mc_state.cpp @@ -4,14 +4,13 @@ * under the terms of the license (GNU LGPL) which comes with this package. */ #include "src/mc/mc_state.hpp" -#include "src/mc/mc_comm_pattern.hpp" #include "src/mc/mc_config.hpp" -#include "src/mc/mc_request.hpp" -#include "src/mc/mc_smx.hpp" +#include "src/mc/mc_api.hpp" #include using simgrid::mc::remote; +using mcapi = simgrid::mc::mc_api; namespace simgrid { namespace mc { @@ -19,14 +18,15 @@ namespace mc { State::State(unsigned long state_number) : num_(state_number) { this->internal_comm_.clear(); - - actor_states_.resize(MC_smx_get_maxpid()); + auto maxpid = mcapi::get().get_maxpid(); + actor_states_.resize(maxpid); /* Stateful model checking */ if ((_sg_mc_checkpoint > 0 && (state_number % _sg_mc_checkpoint == 0)) || _sg_mc_termination) { - system_state_ = std::make_shared(num_); + auto snapshot_ptr = mcapi::get().take_snapshot(num_); + system_state_ = std::shared_ptr(snapshot_ptr); if (_sg_mc_comms_determinism || _sg_mc_send_determinism) { - MC_state_copy_incomplete_communications_pattern(this); - MC_state_copy_index_communications_pattern(this); + mcapi::get().copy_incomplete_comm_pattern(this); + mcapi::get().copy_index_comm_pattern(this); } } } @@ -43,173 +43,3 @@ Transition State::get_transition() const } } - -/* Search an enabled transition for the given process. - * - * This can be seen as an iterator returning the next transition of the process. - * - * We only consider the processes that are both - * - marked "to be interleaved" in their ActorState (controlled by the checker algorithm). - * - which simcall can currently be executed (like a comm where the other partner is already known) - * Once we returned the last enabled transition of a process, it is marked done. - * - * Things can get muddled with the WAITANY and TESTANY simcalls, that are rewritten on the fly to a bunch of WAIT - * (resp TEST) transitions using the transition.argument field to remember what was the last returned sub-transition. - */ -static inline smx_simcall_t MC_state_choose_request_for_process(simgrid::mc::State* state, smx_actor_t actor) -{ - using simgrid::simix::Simcall; - - /* reset the outgoing transition */ - simgrid::mc::ActorState* procstate = &state->actor_states_[actor->get_pid()]; - state->transition_.pid_ = -1; - state->transition_.argument_ = -1; - state->executed_req_.call_ = Simcall::NONE; - - if (not simgrid::mc::actor_is_enabled(actor)) - return nullptr; // Not executable in the application - - smx_simcall_t req = nullptr; - switch (actor->simcall_.call_) { - case Simcall::COMM_WAITANY: - state->transition_.argument_ = -1; - while (procstate->times_considered < simcall_comm_waitany__get__count(&actor->simcall_)) { - if (simgrid::mc::request_is_enabled_by_idx(&actor->simcall_, procstate->times_considered)) { - state->transition_.argument_ = procstate->times_considered; - ++procstate->times_considered; - break; - } - ++procstate->times_considered; - } - - if (procstate->times_considered >= simcall_comm_waitany__get__count(&actor->simcall_)) - procstate->set_done(); - if (state->transition_.argument_ != -1) - req = &actor->simcall_; - break; - - case Simcall::COMM_TESTANY: { - unsigned start_count = procstate->times_considered; - state->transition_.argument_ = -1; - while (procstate->times_considered < simcall_comm_testany__get__count(&actor->simcall_)) { - if (simgrid::mc::request_is_enabled_by_idx(&actor->simcall_, procstate->times_considered)) { - state->transition_.argument_ = procstate->times_considered; - ++procstate->times_considered; - break; - } - ++procstate->times_considered; - } - - if (procstate->times_considered >= simcall_comm_testany__get__count(&actor->simcall_)) - procstate->set_done(); - - if (state->transition_.argument_ != -1 || start_count == 0) - req = &actor->simcall_; - - break; - } - - case Simcall::COMM_WAIT: { - simgrid::mc::RemotePtr remote_act = - remote(simcall_comm_wait__getraw__comm(&actor->simcall_)); - simgrid::mc::Remote temp_act; - mc_model_checker->get_remote_simulation().read(temp_act, remote_act); - const simgrid::kernel::activity::CommImpl* act = temp_act.get_buffer(); - if (act->src_actor_.get() && act->dst_actor_.get()) - state->transition_.argument_ = 0; // OK - else if (act->src_actor_.get() == nullptr && act->type_ == simgrid::kernel::activity::CommImpl::Type::READY && - act->detached()) - state->transition_.argument_ = 0; // OK - else - state->transition_.argument_ = -1; // timeout - procstate->set_done(); - req = &actor->simcall_; - break; - } - - case Simcall::MC_RANDOM: { - int min_value = simcall_mc_random__get__min(&actor->simcall_); - state->transition_.argument_ = procstate->times_considered + min_value; - procstate->times_considered++; - if (state->transition_.argument_ == simcall_mc_random__get__max(&actor->simcall_)) - procstate->set_done(); - req = &actor->simcall_; - break; - } - - default: - procstate->set_done(); - state->transition_.argument_ = 0; - req = &actor->simcall_; - break; - } - if (not req) - return nullptr; - - state->transition_.pid_ = actor->get_pid(); - state->executed_req_ = *req; - // Fetch the data of the request and translate it: - state->internal_req_ = *req; - - /* The waitany and testany request are transformed into a wait or test request over the corresponding communication - * action so it can be treated later by the dependence function. */ - switch (req->call_) { - case Simcall::COMM_WAITANY: { - state->internal_req_.call_ = Simcall::COMM_WAIT; - simgrid::kernel::activity::CommImpl* remote_comm; - remote_comm = mc_model_checker->get_remote_simulation().read( - remote(simcall_comm_waitany__get__comms(req) + state->transition_.argument_)); - mc_model_checker->get_remote_simulation().read(state->internal_comm_, remote(remote_comm)); - simcall_comm_wait__set__comm(&state->internal_req_, state->internal_comm_.get_buffer()); - simcall_comm_wait__set__timeout(&state->internal_req_, 0); - break; - } - - case Simcall::COMM_TESTANY: - state->internal_req_.call_ = Simcall::COMM_TEST; - - if (state->transition_.argument_ > 0) { - simgrid::kernel::activity::CommImpl* remote_comm = mc_model_checker->get_remote_simulation().read( - remote(simcall_comm_testany__get__comms(req) + state->transition_.argument_)); - mc_model_checker->get_remote_simulation().read(state->internal_comm_, remote(remote_comm)); - } - - simcall_comm_test__set__comm(&state->internal_req_, state->internal_comm_.get_buffer()); - simcall_comm_test__set__result(&state->internal_req_, state->transition_.argument_); - break; - - case Simcall::COMM_WAIT: - mc_model_checker->get_remote_simulation().read_bytes(&state->internal_comm_, sizeof(state->internal_comm_), - remote(simcall_comm_wait__getraw__comm(req))); - simcall_comm_wait__set__comm(&state->executed_req_, state->internal_comm_.get_buffer()); - simcall_comm_wait__set__comm(&state->internal_req_, state->internal_comm_.get_buffer()); - break; - - case Simcall::COMM_TEST: - mc_model_checker->get_remote_simulation().read_bytes(&state->internal_comm_, sizeof(state->internal_comm_), - remote(simcall_comm_test__getraw__comm(req))); - simcall_comm_test__set__comm(&state->executed_req_, state->internal_comm_.get_buffer()); - simcall_comm_test__set__comm(&state->internal_req_, state->internal_comm_.get_buffer()); - break; - - default: - /* No translation needed */ - break; - } - - return req; -} - -smx_simcall_t MC_state_choose_request(simgrid::mc::State* state) -{ - for (auto& actor : mc_model_checker->get_remote_simulation().actors()) { - /* Only consider the actors that were marked as interleaving by the checker algorithm */ - if (not state->actor_states_[actor.copy.get_buffer()->get_pid()].is_todo()) - continue; - - smx_simcall_t res = MC_state_choose_request_for_process(state, actor.copy.get_buffer()); - if (res) - return res; - } - return nullptr; -} diff --git a/src/mc/mc_state.hpp b/src/mc/mc_state.hpp index 54736a5972..2f347e3032 100644 --- a/src/mc/mc_state.hpp +++ b/src/mc/mc_state.hpp @@ -6,90 +6,13 @@ #ifndef SIMGRID_MC_STATE_HPP #define SIMGRID_MC_STATE_HPP -#include "src/kernel/activity/CommImpl.hpp" #include "src/mc/Transition.hpp" #include "src/mc/sosp/Snapshot.hpp" +#include "src/mc/mc_pattern.hpp" namespace simgrid { namespace mc { -enum class PatternCommunicationType { - none = 0, - send = 1, - receive = 2, -}; - -class PatternCommunication { -public: - int num = 0; - simgrid::kernel::activity::CommImpl* comm_addr; - PatternCommunicationType type = PatternCommunicationType::send; - unsigned long src_proc = 0; - unsigned long dst_proc = 0; - const char* src_host = nullptr; - const char* dst_host = nullptr; - std::string rdv; - std::vector data; - int tag = 0; - int index = 0; - - PatternCommunication() { std::memset(&comm_addr, 0, sizeof(comm_addr)); } - - PatternCommunication dup() const - { - simgrid::mc::PatternCommunication res; - // num? - res.comm_addr = this->comm_addr; - res.type = this->type; - // src_proc? - // dst_proc? - res.dst_proc = this->dst_proc; - res.dst_host = this->dst_host; - res.rdv = this->rdv; - res.data = this->data; - // tag? - res.index = this->index; - return res; - } -}; - -/* On every state, each actor has an entry of the following type. - * This represents both the actor and its transition because - * an actor cannot have more than one enabled transition at a given time. - */ -class ActorState { - /* Possible exploration status of an actor transition in a state. - * Either the checker did not consider the transition, or it was considered and still to do, or considered and done. - */ - enum class InterleavingType { - /** This actor transition is not considered by the checker (yet?) */ - disabled = 0, - /** The checker algorithm decided that this actor transitions should be done at some point */ - todo, - /** The checker algorithm decided that this should be done, but it was done in the meanwhile */ - done, - }; - - /** Exploration control information */ - InterleavingType state = InterleavingType::disabled; - -public: - /** Number of times that the process was considered to be executed */ - // TODO, make this private - unsigned int times_considered = 0; - - bool is_disabled() const { return this->state == InterleavingType::disabled; } - bool is_done() const { return this->state == InterleavingType::done; } - bool is_todo() const { return this->state == InterleavingType::todo; } - /** Mark that we should try executing this process at some point in the future of the checker algorithm */ - void consider() - { - this->state = InterleavingType::todo; - this->times_considered = 0; - } - void set_done() { this->state = InterleavingType::done; } -}; - /* A node in the exploration graph (kind-of) */ class XBT_PRIVATE State { public: @@ -133,6 +56,4 @@ public: } } -XBT_PRIVATE smx_simcall_t MC_state_choose_request(simgrid::mc::State* state); - #endif diff --git a/tools/cmake/DefinePackages.cmake b/tools/cmake/DefinePackages.cmake index 1062af7dbe..27bf5f86ad 100644 --- a/tools/cmake/DefinePackages.cmake +++ b/tools/cmake/DefinePackages.cmake @@ -611,7 +611,7 @@ set(MC_SRC src/mc/checker/SimcallInspector.hpp src/mc/checker/LivenessChecker.cpp src/mc/checker/LivenessChecker.hpp - + src/mc/inspect/DwarfExpression.hpp src/mc/inspect/DwarfExpression.cpp src/mc/inspect/Frame.hpp @@ -660,7 +660,10 @@ set(MC_SRC src/mc/Session.hpp src/mc/mc_comm_pattern.cpp src/mc/mc_comm_pattern.hpp + src/mc/mc_pattern.hpp src/mc/compare.cpp + src/mc/mc_api.cpp + src/mc/mc_api.hpp src/mc/mc_hash.hpp src/mc/mc_hash.cpp src/mc/mc_ignore.hpp