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) : channel_(sockfd), running_(true), pid_(pid)
21 auto* base = event_base_new();
24 auto* socket_event = event_new(
25 base, get_channel().get_socket(), EV_READ | EV_PERSIST,
26 [](evutil_socket_t sig, short events, void* arg) {
27 auto checker = static_cast<simgrid::mc::CheckerSide*>(arg);
28 if (events == EV_READ) {
29 std::array<char, MC_MESSAGE_LENGTH> buffer;
30 ssize_t size = recv(checker->get_channel().get_socket(), buffer.data(), buffer.size(), MSG_DONTWAIT);
32 XBT_ERROR("Channel::receive failure: %s", strerror(errno));
34 throw simgrid::xbt::errno_error();
37 if (not checker->handle_message(buffer.data(), size))
38 checker->break_loop();
40 xbt_die("Unexpected event");
44 event_add(socket_event, nullptr);
45 socket_event_.reset(socket_event);
47 auto* signal_event = event_new(
48 base, SIGCHLD, EV_SIGNAL | EV_PERSIST,
49 [](evutil_socket_t sig, short events, void* arg) {
50 auto checker = static_cast<simgrid::mc::CheckerSide*>(arg);
51 if (events == EV_SIGNAL) {
53 checker->handle_waitpid();
55 xbt_die("Unexpected signal: %d", sig);
57 xbt_die("Unexpected event");
61 event_add(signal_event, nullptr);
62 signal_event_.reset(signal_event);
65 void CheckerSide::dispatch_events() const
67 event_base_dispatch(base_.get());
70 void CheckerSide::break_loop() const
72 event_base_loopbreak(base_.get());
75 bool CheckerSide::handle_message(const char* buffer, ssize_t size)
77 s_mc_message_t base_message;
78 xbt_assert(size >= (ssize_t)sizeof(base_message), "Broken message");
79 memcpy(&base_message, buffer, sizeof(base_message));
81 switch (base_message.type) {
82 case MessageType::INITIAL_ADDRESSES: {
83 s_mc_message_initial_addresses_t message;
84 xbt_assert(size == sizeof(message), "Broken message. Got %d bytes instead of %d.", (int)size,
85 (int)sizeof(message));
86 memcpy(&message, buffer, sizeof(message));
87 /* Create the memory address space, now that we have the mandatory information */
88 remote_memory_ = std::make_unique<simgrid::mc::RemoteProcessMemory>(pid_, message.mmalloc_default_mdp);
92 case MessageType::IGNORE_HEAP: {
93 s_mc_message_ignore_heap_t message;
94 xbt_assert(size == sizeof(message), "Broken message");
95 memcpy(&message, buffer, sizeof(message));
97 IgnoredHeapRegion region;
98 region.block = message.block;
99 region.fragment = message.fragment;
100 region.address = message.address;
101 region.size = message.size;
102 get_remote_memory().ignore_heap(region);
106 case MessageType::UNIGNORE_HEAP: {
107 s_mc_message_ignore_memory_t message;
108 xbt_assert(size == sizeof(message), "Broken message");
109 memcpy(&message, buffer, sizeof(message));
110 get_remote_memory().unignore_heap((void*)(std::uintptr_t)message.addr, message.size);
114 case MessageType::IGNORE_MEMORY: {
115 s_mc_message_ignore_memory_t message;
116 xbt_assert(size == sizeof(message), "Broken message");
117 memcpy(&message, buffer, sizeof(message));
118 get_remote_memory().ignore_region(message.addr, message.size);
122 case MessageType::STACK_REGION: {
123 s_mc_message_stack_region_t message;
124 xbt_assert(size == sizeof(message), "Broken message");
125 memcpy(&message, buffer, sizeof(message));
126 get_remote_memory().stack_areas().push_back(message.stack_region);
129 case MessageType::REGISTER_SYMBOL: {
130 s_mc_message_register_symbol_t message;
131 xbt_assert(size == sizeof(message), "Broken message");
132 memcpy(&message, buffer, sizeof(message));
133 xbt_assert(not message.callback, "Support for client-side function proposition is not implemented.");
134 XBT_DEBUG("Received symbol: %s", message.name.data());
136 LivenessChecker::automaton_register_symbol(get_remote_memory(), message.name.data(), remote((int*)message.data));
140 case MessageType::WAITING:
143 case MessageType::ASSERTION_FAILED:
144 Exploration::get_instance()->report_assertion_failure();
148 xbt_die("Unexpected message from model-checked application");
153 void CheckerSide::clear_memory_cache()
156 remote_memory_->clear_cache();
159 void CheckerSide::handle_waitpid()
161 XBT_DEBUG("Check for wait event");
164 while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
166 if (errno == ECHILD) { // No more children:
167 xbt_assert(not this->running(), "Inconsistent state");
170 XBT_ERROR("Could not wait for pid");
171 throw simgrid::xbt::errno_error();
175 if (pid == get_pid()) {
176 // From PTRACE_O_TRACEEXIT:
178 if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) {
179 unsigned long eventmsg;
180 xbt_assert(ptrace(PTRACE_GETEVENTMSG, pid, 0, &eventmsg) != -1, "Could not get exit status");
181 status = static_cast<int>(eventmsg);
182 if (WIFSIGNALED(status)) {
184 Exploration::get_instance()->report_crash(status);
189 // We don't care about non-lethal signals, just reinject them:
190 if (WIFSTOPPED(status)) {
191 XBT_DEBUG("Stopped with signal %i", (int)WSTOPSIG(status));
194 ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status));
196 ptrace(PT_CONTINUE, pid, (caddr_t)1, WSTOPSIG(status));
198 xbt_assert(errno == 0, "Could not PTRACE_CONT");
201 else if (WIFSIGNALED(status)) {
203 Exploration::get_instance()->report_crash(status);
204 } else if (WIFEXITED(status)) {
205 XBT_DEBUG("Child process is over");
212 } // namespace simgrid::mc