1 /* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved. */
3 /* This program is free software; you can redistribute it and/or modify it
4 * under the terms of the license (GNU LGPL) which comes with this package. */
6 #include "src/mc/remote/CheckerSide.hpp"
7 #include "src/mc/explo/Exploration.hpp"
8 #include "src/mc/explo/LivenessChecker.hpp"
9 #include "src/mc/sosp/RemoteProcessMemory.hpp"
10 #include "xbt/system_error.hpp"
13 #include <sys/ptrace.h>
16 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_checkerside, mc, "MC communication with the application");
18 namespace simgrid::mc {
19 CheckerSide::CheckerSide(int sockfd, pid_t pid)
20 : remote_memory_(std::make_unique<simgrid::mc::RemoteProcessMemory>(pid))
25 auto* base = event_base_new();
28 auto* socket_event = event_new(
29 base, get_channel().get_socket(), EV_READ | EV_PERSIST,
30 [](evutil_socket_t sig, short events, void* arg) {
31 auto checker = static_cast<simgrid::mc::CheckerSide*>(arg);
32 if (events == EV_READ) {
33 std::array<char, MC_MESSAGE_LENGTH> buffer;
34 ssize_t size = recv(checker->get_channel().get_socket(), buffer.data(), buffer.size(), MSG_DONTWAIT);
36 XBT_ERROR("Channel::receive failure: %s", strerror(errno));
38 throw simgrid::xbt::errno_error();
41 if (not checker->handle_message(buffer.data(), size))
42 checker->break_loop();
44 xbt_die("Unexpected event");
48 event_add(socket_event, nullptr);
49 socket_event_.reset(socket_event);
51 auto* signal_event = event_new(
52 base, SIGCHLD, EV_SIGNAL | EV_PERSIST,
53 [](evutil_socket_t sig, short events, void* arg) {
54 auto checker = static_cast<simgrid::mc::CheckerSide*>(arg);
55 if (events == EV_SIGNAL) {
57 checker->handle_waitpid();
59 xbt_die("Unexpected signal: %d", sig);
61 xbt_die("Unexpected event");
65 event_add(signal_event, nullptr);
66 signal_event_.reset(signal_event);
69 void CheckerSide::dispatch_events() const
71 event_base_dispatch(base_.get());
74 void CheckerSide::break_loop() const
76 event_base_loopbreak(base_.get());
79 bool CheckerSide::handle_message(const char* buffer, ssize_t size)
81 s_mc_message_t base_message;
82 xbt_assert(size >= (ssize_t)sizeof(base_message), "Broken message");
83 memcpy(&base_message, buffer, sizeof(base_message));
85 switch (base_message.type) {
86 case MessageType::INITIAL_ADDRESSES: {
87 s_mc_message_initial_addresses_t message;
88 xbt_assert(size == sizeof(message), "Broken message. Got %d bytes instead of %d.", (int)size,
89 (int)sizeof(message));
90 memcpy(&message, buffer, sizeof(message));
92 get_remote_memory().init(message.mmalloc_default_mdp);
96 case MessageType::IGNORE_HEAP: {
97 s_mc_message_ignore_heap_t message;
98 xbt_assert(size == sizeof(message), "Broken message");
99 memcpy(&message, buffer, sizeof(message));
101 IgnoredHeapRegion region;
102 region.block = message.block;
103 region.fragment = message.fragment;
104 region.address = message.address;
105 region.size = message.size;
106 get_remote_memory().ignore_heap(region);
110 case MessageType::UNIGNORE_HEAP: {
111 s_mc_message_ignore_memory_t message;
112 xbt_assert(size == sizeof(message), "Broken message");
113 memcpy(&message, buffer, sizeof(message));
114 get_remote_memory().unignore_heap((void*)(std::uintptr_t)message.addr, message.size);
118 case MessageType::IGNORE_MEMORY: {
119 s_mc_message_ignore_memory_t message;
120 xbt_assert(size == sizeof(message), "Broken message");
121 memcpy(&message, buffer, sizeof(message));
122 get_remote_memory().ignore_region(message.addr, message.size);
126 case MessageType::STACK_REGION: {
127 s_mc_message_stack_region_t message;
128 xbt_assert(size == sizeof(message), "Broken message");
129 memcpy(&message, buffer, sizeof(message));
130 get_remote_memory().stack_areas().push_back(message.stack_region);
133 case MessageType::REGISTER_SYMBOL: {
134 s_mc_message_register_symbol_t message;
135 xbt_assert(size == sizeof(message), "Broken message");
136 memcpy(&message, buffer, sizeof(message));
137 xbt_assert(not message.callback, "Support for client-side function proposition is not implemented.");
138 XBT_DEBUG("Received symbol: %s", message.name.data());
140 LivenessChecker::automaton_register_symbol(get_remote_memory(), message.name.data(), remote((int*)message.data));
144 case MessageType::WAITING:
147 case MessageType::ASSERTION_FAILED:
148 Exploration::get_instance()->report_assertion_failure();
152 xbt_die("Unexpected message from model-checked application");
157 void CheckerSide::handle_waitpid()
159 XBT_DEBUG("Check for wait event");
162 while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
164 if (errno == ECHILD) { // No more children:
165 xbt_assert(not this->running(), "Inconsistent state");
168 XBT_ERROR("Could not wait for pid");
169 throw simgrid::xbt::errno_error();
173 if (pid == get_pid()) {
174 // From PTRACE_O_TRACEEXIT:
176 if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) {
177 unsigned long eventmsg;
178 xbt_assert(ptrace(PTRACE_GETEVENTMSG, pid, 0, &eventmsg) != -1, "Could not get exit status");
179 status = static_cast<int>(eventmsg);
180 if (WIFSIGNALED(status)) {
182 Exploration::get_instance()->report_crash(status);
187 // We don't care about non-lethal signals, just reinject them:
188 if (WIFSTOPPED(status)) {
189 XBT_DEBUG("Stopped with signal %i", (int)WSTOPSIG(status));
192 ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status));
194 ptrace(PT_CONTINUE, pid, (caddr_t)1, WSTOPSIG(status));
196 xbt_assert(errno == 0, "Could not PTRACE_CONT");
199 else if (WIFSIGNALED(status)) {
201 Exploration::get_instance()->report_crash(status);
202 } else if (WIFEXITED(status)) {
203 XBT_DEBUG("Child process is over");
210 } // namespace simgrid::mc