X-Git-Url: http://bilbo.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/blobdiff_plain/9d195a1f6e6006762e394ab69955d0824bbfe2d8..6a908b79ea45f85f305620c09375b72483b7eee9:/src/mc/remote/CheckerSide.cpp diff --git a/src/mc/remote/CheckerSide.cpp b/src/mc/remote/CheckerSide.cpp index dcf347a25a..a19f0934e0 100644 --- a/src/mc/remote/CheckerSide.cpp +++ b/src/mc/remote/CheckerSide.cpp @@ -10,7 +10,6 @@ #include "xbt/system_error.hpp" #ifdef __linux__ -#include #include #endif @@ -49,12 +48,6 @@ XBT_ATTRIB_NORETURN static void run_child_process(int socket, const std::vector< sigemptyset(&mask); xbt_assert(sigprocmask(SIG_SETMASK, &mask, nullptr) >= 0, "Could not unblock signals"); xbt_assert(prctl(PR_SET_PDEATHSIG, SIGHUP) == 0, "Could not PR_SET_PDEATHSIG"); - - // Make sure that the application process layout is not randomized, so that the info we gather is stable over re-execs - if (personality(ADDR_NO_RANDOMIZE) == -1) { - XBT_ERROR("Could not set the NO_RANDOMIZE personality"); - throw xbt::errno_error(); - } #endif // Remove CLOEXEC to pass the socket to the application @@ -126,10 +119,8 @@ static void wait_application_process(pid_t pid) errno); } -void CheckerSide::setup_events() +void CheckerSide::setup_events(bool socket_only) { - if (base_ != nullptr) - event_base_free(base_.get()); auto* base = event_base_new(); base_.reset(base); @@ -157,38 +148,42 @@ void CheckerSide::setup_events() this); event_add(socket_event_, nullptr); - signal_event_ = event_new( - base, SIGCHLD, EV_SIGNAL | EV_PERSIST, - [](evutil_socket_t sig, short events, void* arg) { - auto checker = static_cast(arg); - if (events == EV_SIGNAL) { - if (sig == SIGCHLD) - checker->handle_waitpid(); - else - xbt_die("Unexpected signal: %d", sig); - } else { - xbt_die("Unexpected event"); - } - }, - this); - event_add(signal_event_, nullptr); + if (socket_only) { + signal_event_ = nullptr; + } else { + signal_event_ = event_new( + base, SIGCHLD, EV_SIGNAL | EV_PERSIST, + [](evutil_socket_t sig, short events, void* arg) { + auto checker = static_cast(arg); + if (events == EV_SIGNAL) { + if (sig == SIGCHLD) + checker->handle_waitpid(); + else + xbt_die("Unexpected signal: %d", sig); + } else { + xbt_die("Unexpected event"); + } + }, + this); + event_add(signal_event_, nullptr); + } } +/* When this constructor is called, no other checkerside exists */ CheckerSide::CheckerSide(const std::vector& args, bool need_memory_info) : running_(true) { - bool need_ptrace = not need_memory_info; - // Create an AF_LOCAL socketpair used for exchanging messages between the model-checker process (ancestor) // and the application process (child) int sockets[2]; - xbt_assert(socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != -1, "Could not create socketpair"); + xbt_assert(socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != -1, "Could not create socketpair: %s", + strerror(errno)); pid_ = fork(); - xbt_assert(pid_ >= 0, "Could not fork model-checked process"); + xbt_assert(pid_ >= 0, "Could not fork application process"); if (pid_ == 0) { // Child ::close(sockets[1]); - run_child_process(sockets[0], args, need_ptrace); + run_child_process(sockets[0], args, need_memory_info); // We need ptrace if we need the mem info DIE_IMPOSSIBLE; } @@ -196,18 +191,18 @@ CheckerSide::CheckerSide(const std::vector& args, bool need_memory_info) ::close(sockets[0]); channel_.reset_socket(sockets[1]); - setup_events(); - if (need_ptrace) + setup_events(false); /* we need a signal handler too */ + if (need_memory_info) { + // setup ptrace and sync with the app wait_application_process(pid_); - // Request the initial memory on need - if (need_memory_info) { - channel_.send(MessageType::INITIAL_ADDRESSES); - s_mc_message_initial_addresses_reply_t answer; + // Request the initial memory on need + channel_.send(MessageType::NEED_MEMINFO); + s_mc_message_need_meminfo_reply_t answer; ssize_t answer_size = channel_.receive(answer); xbt_assert(answer_size != -1, "Could not receive message"); - xbt_assert(answer.type == MessageType::INITIAL_ADDRESSES_REPLY, - "The received message is not the INITIAL_ADDRESS_REPLY I was expecting but of type %s", + xbt_assert(answer.type == MessageType::NEED_MEMINFO_REPLY, + "The received message is not the NEED_MEMINFO_REPLY I was expecting but of type %s", to_c_str(answer.type)); xbt_assert(answer_size == sizeof answer, "Broken message (size=%zd; expected %zu)", answer_size, sizeof answer); @@ -222,20 +217,43 @@ CheckerSide::~CheckerSide() { event_del(socket_event_); event_free(socket_event_); - event_del(signal_event_); - event_free(signal_event_); - - if (running()) { - errno = 0; - xbt_assert(kill(get_pid(), SIGKILL) == 0); - do { - errno = 0; - waitpid(get_pid(), nullptr, 0); - xbt_assert(errno == 0); - } while (EAGAIN == errno); + if (signal_event_ != nullptr) { + event_del(signal_event_); + event_free(signal_event_); } } +/* This constructor is called when cloning a checkerside to get its application to fork away */ +CheckerSide::CheckerSide(int socket, CheckerSide* child_checker) + : channel_(socket), running_(true), child_checker_(child_checker) +{ + setup_events(true); // We already have a signal handled in that case + + s_mc_message_int_t answer; + ssize_t s = get_channel().receive(answer); + xbt_assert(s != -1, "Could not receive answer to FORK_REPLY"); + xbt_assert(s == sizeof answer, "Broken message (size=%zd; expected %zu)", s, sizeof answer); + xbt_assert(answer.type == MessageType::FORK_REPLY, + "Received unexpected message %s (%i); expected MessageType::FORK_REPLY (%i)", to_c_str(answer.type), + (int)answer.type, (int)MessageType::FORK_REPLY); + pid_ = answer.value; + + wait_for_requests(); +} + +std::unique_ptr CheckerSide::clone(int master_socket) +{ + s_mc_message_int_t m = {}; + m.type = MessageType::FORK; + m.value = getpid(); + xbt_assert(get_channel().send(m) == 0, "Could not ask the app to fork on need."); + + int sock = accept(master_socket, nullptr /* I know who's connecting*/, nullptr); + xbt_assert(sock > 0, "Cannot accept the incomming connection of the forked app: %s.", strerror(errno)); + + return std::make_unique(sock, this); +} + void CheckerSide::finalize(bool terminate_asap) { s_mc_message_int_t m = {}; @@ -342,14 +360,14 @@ bool CheckerSide::handle_message(const char* buffer, ssize_t size) break; default: - xbt_die("Unexpected message from model-checked application"); + xbt_die("Unexpected message from the application"); } return true; } void CheckerSide::wait_for_requests() { - /* Resume the application */ + XBT_DEBUG("Resume the application"); if (get_channel().send(MessageType::CONTINUE) != 0) throw xbt::errno_error(); clear_memory_cache(); @@ -364,56 +382,79 @@ void CheckerSide::clear_memory_cache() remote_memory_->clear_cache(); } -void CheckerSide::handle_waitpid() +void CheckerSide::handle_dead_child(int status) { - XBT_DEBUG("Check for wait event"); - int status; - pid_t pid; - while ((pid = waitpid(-1, &status, WNOHANG)) != 0) { - if (pid == -1) { - if (errno == ECHILD) { // No more children: - xbt_assert(not this->running(), "Inconsistent state"); - break; - } else { - xbt_die("Could not wait for pid: %s", strerror(errno)); - } - } - - if (pid == get_pid()) { - // From PTRACE_O_TRACEEXIT: + // From PTRACE_O_TRACEEXIT: #ifdef __linux__ - if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) { - unsigned long eventmsg; - xbt_assert(ptrace(PTRACE_GETEVENTMSG, pid, 0, &eventmsg) != -1, "Could not get exit status"); - status = static_cast(eventmsg); - if (WIFSIGNALED(status)) { - this->terminate(); - Exploration::get_instance()->report_crash(status); - } - } + if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) { + unsigned long eventmsg; + xbt_assert(ptrace(PTRACE_GETEVENTMSG, pid_, 0, &eventmsg) != -1, "Could not get exit status"); + status = static_cast(eventmsg); + if (WIFSIGNALED(status)) { + this->terminate(); + Exploration::get_instance()->report_crash(status); + } + } #endif - // We don't care about non-lethal signals, just reinject them: - if (WIFSTOPPED(status)) { - XBT_DEBUG("Stopped with signal %i", (int)WSTOPSIG(status)); - errno = 0; + // We don't care about non-lethal signals, just reinject them: + if (WIFSTOPPED(status)) { + XBT_DEBUG("Stopped with signal %i", (int)WSTOPSIG(status)); + errno = 0; #ifdef __linux__ - ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status)); + ptrace(PTRACE_CONT, pid_, 0, WSTOPSIG(status)); #elif defined BSD - ptrace(PT_CONTINUE, pid, (caddr_t)1, WSTOPSIG(status)); + ptrace(PT_CONTINUE, pid_, (caddr_t)1, WSTOPSIG(status)); #endif - xbt_assert(errno == 0, "Could not PTRACE_CONT"); - } + xbt_assert(errno == 0, "Could not PTRACE_CONT: %s", strerror(errno)); + } - else if (WIFSIGNALED(status)) { - this->terminate(); - Exploration::get_instance()->report_crash(status); - } else if (WIFEXITED(status)) { - XBT_DEBUG("Child process is over"); - this->terminate(); + else if (WIFSIGNALED(status)) { + this->terminate(); + Exploration::get_instance()->report_crash(status); + } else if (WIFEXITED(status)) { + XBT_DEBUG("Child process is over"); + this->terminate(); + } +} + +void CheckerSide::handle_waitpid() +{ + XBT_DEBUG("Check for wait event"); + + if (child_checker_ == nullptr) { // Wait directly + int status; + pid_t pid; + while ((pid = waitpid(-1, &status, WNOHANG)) != 0) { + if (pid == -1) { + if (errno == ECHILD) { // No more children: + xbt_assert(not this->running(), "Inconsistent state"); + break; + } else { + xbt_die("Could not wait for pid: %s", strerror(errno)); + } } + + if (pid == get_pid()) + handle_dead_child(status); } + + } else { // Ask our proxy to wait for us + + s_mc_message_int_t request = {}; + request.type = MessageType::WAIT_CHILD; + request.value = pid_; + xbt_assert(child_checker_->get_channel().send(request) == 0, + "Could not ask my child to waitpid its child for me: %s", strerror(errno)); + + s_mc_message_int_t answer; + ssize_t answer_size = child_checker_->get_channel().receive(answer); + xbt_assert(answer_size != -1, "Could not receive message"); + xbt_assert(answer.type == MessageType::WAIT_CHILD_REPLY, + "The received message is not the WAIT_CHILD_REPLY I was expecting but of type %s", + to_c_str(answer.type)); + xbt_assert(answer_size == sizeof answer, "Broken message (size=%zd; expected %zu)", answer_size, sizeof answer); + handle_dead_child(answer.value); } } - } // namespace simgrid::mc