Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
1660265aef6505fde2bdb491e98710b5e4e91409
[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)
20     : remote_memory_(std::make_unique<simgrid::mc::RemoteProcessMemory>(pid))
21     , channel_(sockfd)
22     , running_(true)
23     , pid_(pid)
24 {
25   auto* base = event_base_new();
26   base_.reset(base);
27
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);
35           if (size == -1) {
36             XBT_ERROR("Channel::receive failure: %s", strerror(errno));
37             if (errno != EAGAIN)
38               throw simgrid::xbt::errno_error();
39           }
40
41           if (not checker->handle_message(buffer.data(), size))
42             checker->break_loop();
43         } else {
44           xbt_die("Unexpected event");
45         }
46       },
47       this);
48   event_add(socket_event, nullptr);
49   socket_event_.reset(socket_event);
50
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) {
56           if (sig == SIGCHLD)
57             checker->handle_waitpid();
58           else
59             xbt_die("Unexpected signal: %d", sig);
60         } else {
61           xbt_die("Unexpected event");
62         }
63       },
64       this);
65   event_add(signal_event, nullptr);
66   signal_event_.reset(signal_event);
67 }
68
69 void CheckerSide::dispatch_events() const
70 {
71   event_base_dispatch(base_.get());
72 }
73
74 void CheckerSide::break_loop() const
75 {
76   event_base_loopbreak(base_.get());
77 }
78
79 bool CheckerSide::handle_message(const char* buffer, ssize_t size)
80 {
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));
84
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));
91
92       get_remote_memory().init(message.mmalloc_default_mdp);
93       break;
94     }
95
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));
100
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);
107       break;
108     }
109
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);
115       break;
116     }
117
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);
123       break;
124     }
125
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);
131     } break;
132
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());
139
140       LivenessChecker::automaton_register_symbol(get_remote_memory(), message.name.data(), remote((int*)message.data));
141       break;
142     }
143
144     case MessageType::WAITING:
145       return false;
146
147     case MessageType::ASSERTION_FAILED:
148       Exploration::get_instance()->report_assertion_failure();
149       break;
150
151     default:
152       xbt_die("Unexpected message from model-checked application");
153   }
154   return true;
155 }
156
157 void CheckerSide::handle_waitpid()
158 {
159   XBT_DEBUG("Check for wait event");
160   int status;
161   pid_t pid;
162   while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
163     if (pid == -1) {
164       if (errno == ECHILD) { // No more children:
165         xbt_assert(not this->running(), "Inconsistent state");
166         break;
167       } else {
168         XBT_ERROR("Could not wait for pid");
169         throw simgrid::xbt::errno_error();
170       }
171     }
172
173     if (pid == get_pid()) {
174       // From PTRACE_O_TRACEEXIT:
175 #ifdef __linux__
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)) {
181           this->terminate();
182           Exploration::get_instance()->report_crash(status);
183         }
184       }
185 #endif
186
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));
190         errno = 0;
191 #ifdef __linux__
192         ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status));
193 #elif defined BSD
194         ptrace(PT_CONTINUE, pid, (caddr_t)1, WSTOPSIG(status));
195 #endif
196         xbt_assert(errno == 0, "Could not PTRACE_CONT");
197       }
198
199       else if (WIFSIGNALED(status)) {
200         this->terminate();
201         Exploration::get_instance()->report_crash(status);
202       } else if (WIFEXITED(status)) {
203         XBT_DEBUG("Child process is over");
204         this->terminate();
205       }
206     }
207   }
208 }
209
210 } // namespace simgrid::mc