Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Move methods not related to Memory out of RemoteProcessMemory
[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, std::unique_ptr<RemoteProcessMemory> memory)
20     : remote_memory_(std::move(memory)), channel_(sockfd)
21 {
22   auto* base = event_base_new();
23   base_.reset(base);
24
25   auto* socket_event = event_new(
26       base, get_channel().get_socket(), EV_READ | EV_PERSIST,
27       [](evutil_socket_t sig, short events, void* arg) {
28         auto checker = static_cast<simgrid::mc::CheckerSide*>(arg);
29         if (events == EV_READ) {
30           std::array<char, MC_MESSAGE_LENGTH> buffer;
31           ssize_t size = recv(checker->get_channel().get_socket(), buffer.data(), buffer.size(), MSG_DONTWAIT);
32           if (size == -1) {
33             XBT_ERROR("Channel::receive failure: %s", strerror(errno));
34             if (errno != EAGAIN)
35               throw simgrid::xbt::errno_error();
36           }
37
38           if (not checker->handle_message(buffer.data(), size))
39             checker->break_loop();
40         } else {
41           xbt_die("Unexpected event");
42         }
43       },
44       this);
45   event_add(socket_event, nullptr);
46   socket_event_.reset(socket_event);
47
48   auto* signal_event = event_new(
49       base, SIGCHLD, EV_SIGNAL | EV_PERSIST,
50       [](evutil_socket_t sig, short events, void* arg) {
51         auto checker = static_cast<simgrid::mc::CheckerSide*>(arg);
52         if (events == EV_SIGNAL) {
53           if (sig == SIGCHLD)
54             checker->handle_waitpid();
55           else
56             xbt_die("Unexpected signal: %d", sig);
57         } else {
58           xbt_die("Unexpected event");
59         }
60       },
61       this);
62   event_add(signal_event, nullptr);
63   signal_event_.reset(signal_event);
64 }
65
66 void CheckerSide::dispatch_events() const
67 {
68   event_base_dispatch(base_.get());
69 }
70
71 void CheckerSide::break_loop() const
72 {
73   event_base_loopbreak(base_.get());
74 }
75
76 bool CheckerSide::handle_message(const char* buffer, ssize_t size)
77 {
78   s_mc_message_t base_message;
79   xbt_assert(size >= (ssize_t)sizeof(base_message), "Broken message");
80   memcpy(&base_message, buffer, sizeof(base_message));
81
82   switch (base_message.type) {
83     case MessageType::INITIAL_ADDRESSES: {
84       s_mc_message_initial_addresses_t message;
85       xbt_assert(size == sizeof(message), "Broken message. Got %d bytes instead of %d.", (int)size,
86                  (int)sizeof(message));
87       memcpy(&message, buffer, sizeof(message));
88
89       get_remote_memory().init(message.mmalloc_default_mdp);
90       break;
91     }
92
93     case MessageType::IGNORE_HEAP: {
94       s_mc_message_ignore_heap_t message;
95       xbt_assert(size == sizeof(message), "Broken message");
96       memcpy(&message, buffer, sizeof(message));
97
98       IgnoredHeapRegion region;
99       region.block    = message.block;
100       region.fragment = message.fragment;
101       region.address  = message.address;
102       region.size     = message.size;
103       get_remote_memory().ignore_heap(region);
104       break;
105     }
106
107     case MessageType::UNIGNORE_HEAP: {
108       s_mc_message_ignore_memory_t message;
109       xbt_assert(size == sizeof(message), "Broken message");
110       memcpy(&message, buffer, sizeof(message));
111       get_remote_memory().unignore_heap((void*)(std::uintptr_t)message.addr, message.size);
112       break;
113     }
114
115     case MessageType::IGNORE_MEMORY: {
116       s_mc_message_ignore_memory_t message;
117       xbt_assert(size == sizeof(message), "Broken message");
118       memcpy(&message, buffer, sizeof(message));
119       get_remote_memory().ignore_region(message.addr, message.size);
120       break;
121     }
122
123     case MessageType::STACK_REGION: {
124       s_mc_message_stack_region_t message;
125       xbt_assert(size == sizeof(message), "Broken message");
126       memcpy(&message, buffer, sizeof(message));
127       get_remote_memory().stack_areas().push_back(message.stack_region);
128     } break;
129
130     case MessageType::REGISTER_SYMBOL: {
131       s_mc_message_register_symbol_t message;
132       xbt_assert(size == sizeof(message), "Broken message");
133       memcpy(&message, buffer, sizeof(message));
134       xbt_assert(not message.callback, "Support for client-side function proposition is not implemented.");
135       XBT_DEBUG("Received symbol: %s", message.name.data());
136
137       LivenessChecker::automaton_register_symbol(get_remote_memory(), message.name.data(), remote((int*)message.data));
138       break;
139     }
140
141     case MessageType::WAITING:
142       return false;
143
144     case MessageType::ASSERTION_FAILED:
145       Exploration::get_instance()->report_assertion_failure();
146       break;
147
148     default:
149       xbt_die("Unexpected message from model-checked application");
150   }
151   return true;
152 }
153
154 void CheckerSide::handle_waitpid()
155 {
156   XBT_DEBUG("Check for wait event");
157   int status;
158   pid_t pid;
159   while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
160     if (pid == -1) {
161       if (errno == ECHILD) { // No more children:
162         xbt_assert(not get_remote_memory().running(), "Inconsistent state");
163         break;
164       } else {
165         XBT_ERROR("Could not wait for pid");
166         throw simgrid::xbt::errno_error();
167       }
168     }
169
170     if (pid == get_remote_memory().pid()) {
171       // From PTRACE_O_TRACEEXIT:
172 #ifdef __linux__
173       if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) {
174         unsigned long eventmsg;
175         xbt_assert(ptrace(PTRACE_GETEVENTMSG, pid, 0, &eventmsg) != -1, "Could not get exit status");
176         status = static_cast<int>(eventmsg);
177         if (WIFSIGNALED(status))
178           Exploration::get_instance()->report_crash(status);
179       }
180 #endif
181
182       // We don't care about non-lethal signals, just reinject them:
183       if (WIFSTOPPED(status)) {
184         XBT_DEBUG("Stopped with signal %i", (int)WSTOPSIG(status));
185         errno = 0;
186 #ifdef __linux__
187         ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status));
188 #elif defined BSD
189         ptrace(PT_CONTINUE, pid, (caddr_t)1, WSTOPSIG(status));
190 #endif
191         xbt_assert(errno == 0, "Could not PTRACE_CONT");
192       }
193
194       else if (WIFSIGNALED(status)) {
195         Exploration::get_instance()->report_crash(status);
196       } else if (WIFEXITED(status)) {
197         XBT_DEBUG("Child process is over");
198         get_remote_memory().terminate();
199       }
200     }
201   }
202 }
203
204 } // namespace simgrid::mc