Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
9c04307ab44bac70f58d19a529dadc63975a2a8d
[simgrid.git] / src / mc / remote / CheckerSide.cpp
1 /* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved.          */
2
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. */
5
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"
11
12 #include <csignal>
13 #include <sys/ptrace.h>
14 #include <sys/wait.h>
15
16 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_checkerside, mc, "MC communication with the application");
17
18 namespace simgrid::mc {
19 CheckerSide::CheckerSide(int sockfd, pid_t pid) : channel_(sockfd), running_(true), pid_(pid)
20 {
21   auto* base = event_base_new();
22   base_.reset(base);
23
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);
31           if (size == -1) {
32             XBT_ERROR("Channel::receive failure: %s", strerror(errno));
33             if (errno != EAGAIN)
34               throw simgrid::xbt::errno_error();
35           }
36
37           if (not checker->handle_message(buffer.data(), size))
38             checker->break_loop();
39         } else {
40           xbt_die("Unexpected event");
41         }
42       },
43       this);
44   event_add(socket_event, nullptr);
45   socket_event_.reset(socket_event);
46
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) {
52           if (sig == SIGCHLD)
53             checker->handle_waitpid();
54           else
55             xbt_die("Unexpected signal: %d", sig);
56         } else {
57           xbt_die("Unexpected event");
58         }
59       },
60       this);
61   event_add(signal_event, nullptr);
62   signal_event_.reset(signal_event);
63 }
64
65 void CheckerSide::dispatch_events() const
66 {
67   event_base_dispatch(base_.get());
68 }
69
70 void CheckerSide::break_loop() const
71 {
72   event_base_loopbreak(base_.get());
73 }
74
75 bool CheckerSide::handle_message(const char* buffer, ssize_t size)
76 {
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));
80
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);
89       break;
90     }
91
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));
96
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);
103       break;
104     }
105
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);
111       break;
112     }
113
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);
119       break;
120     }
121
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);
127     } break;
128
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());
135
136       LivenessChecker::automaton_register_symbol(get_remote_memory(), message.name.data(), remote((int*)message.data));
137       break;
138     }
139
140     case MessageType::WAITING:
141       return false;
142
143     case MessageType::ASSERTION_FAILED:
144       Exploration::get_instance()->report_assertion_failure();
145       break;
146
147     default:
148       xbt_die("Unexpected message from model-checked application");
149   }
150   return true;
151 }
152
153 void CheckerSide::clear_memory_cache()
154 {
155   if (remote_memory_)
156     remote_memory_->clear_cache();
157 }
158
159 void CheckerSide::handle_waitpid()
160 {
161   XBT_DEBUG("Check for wait event");
162   int status;
163   pid_t pid;
164   while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
165     if (pid == -1) {
166       if (errno == ECHILD) { // No more children:
167         xbt_assert(not this->running(), "Inconsistent state");
168         break;
169       } else {
170         XBT_ERROR("Could not wait for pid");
171         throw simgrid::xbt::errno_error();
172       }
173     }
174
175     if (pid == get_pid()) {
176       // From PTRACE_O_TRACEEXIT:
177 #ifdef __linux__
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)) {
183           this->terminate();
184           Exploration::get_instance()->report_crash(status);
185         }
186       }
187 #endif
188
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));
192         errno = 0;
193 #ifdef __linux__
194         ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status));
195 #elif defined BSD
196         ptrace(PT_CONTINUE, pid, (caddr_t)1, WSTOPSIG(status));
197 #endif
198         xbt_assert(errno == 0, "Could not PTRACE_CONT");
199       }
200
201       else if (WIFSIGNALED(status)) {
202         this->terminate();
203         Exploration::get_instance()->report_crash(status);
204       } else if (WIFEXITED(status)) {
205         XBT_DEBUG("Child process is over");
206         this->terminate();
207       }
208     }
209   }
210 }
211
212 } // namespace simgrid::mc