Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
MC: actually remove the comm channel from RemoteClientMemory
[simgrid.git] / src / mc / ModelChecker.cpp
1 /* Copyright (c) 2008-2020. 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/ModelChecker.hpp"
7 #include "src/mc/Session.hpp"
8 #include "src/mc/Transition.hpp"
9 #include "src/mc/checker/Checker.hpp"
10 #include "src/mc/mc_config.hpp"
11 #include "src/mc/mc_exit.hpp"
12 #include "src/mc/mc_private.hpp"
13 #include "src/mc/remote/RemoteClientMemory.hpp"
14 #include "xbt/automaton.hpp"
15 #include "xbt/system_error.hpp"
16
17 #include <sys/ptrace.h>
18 #include <sys/wait.h>
19
20 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_ModelChecker, mc, "ModelChecker");
21
22 ::simgrid::mc::ModelChecker* mc_model_checker = nullptr;
23
24 using simgrid::mc::remote;
25
26 #ifdef __linux__
27 # define WAITPID_CHECKED_FLAGS __WALL
28 #else
29 # define WAITPID_CHECKED_FLAGS 0
30 #endif
31
32 namespace simgrid {
33 namespace mc {
34
35 ModelChecker::ModelChecker(std::unique_ptr<RemoteClientMemory> process, int sockfd)
36     : event_loop_(sockfd), process_(std::move(process))
37 {
38 }
39
40 void ModelChecker::start()
41 {
42   event_loop_.start(
43       [](evutil_socket_t sig, short events, void* arg) { ((ModelChecker*)arg)->handle_events(sig, events); });
44
45   XBT_DEBUG("Waiting for the model-checked process");
46   int status;
47
48   // The model-checked process SIGSTOP itself to signal it's ready:
49   const pid_t pid = process_->pid();
50
51   pid_t res = waitpid(pid, &status, WAITPID_CHECKED_FLAGS);
52   if (res < 0 || not WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
53     xbt_die("Could not wait model-checked process");
54
55   process_->init();
56
57   if (not _sg_mc_dot_output_file.get().empty())
58     MC_init_dot_output();
59
60   setup_ignore();
61
62 #ifdef __linux__
63   ptrace(PTRACE_SETOPTIONS, pid, nullptr, PTRACE_O_TRACEEXIT);
64   ptrace(PTRACE_CONT, pid, 0, 0);
65 #elif defined BSD
66   ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
67 #else
68 # error "no ptrace equivalent coded for this platform"
69 #endif
70 }
71
72 static const std::pair<const char*, const char*> ignored_local_variables[] = {
73   std::pair<const char*, const char*>{  "e", "*" },
74   std::pair<const char*, const char*>{ "_log_ev", "*" },
75
76   /* Ignore local variable about time used for tracing */
77   std::pair<const char*, const char*>{ "start_time", "*" },
78 };
79
80 void ModelChecker::setup_ignore()
81 {
82   RemoteClientMemory& process = this->process();
83   for (std::pair<const char*, const char*> const& var :
84       ignored_local_variables)
85     process.ignore_local_variable(var.first, var.second);
86
87   /* Static variable used for tracing */
88   process.ignore_global_variable("counter");
89 }
90
91 void ModelChecker::shutdown()
92 {
93   XBT_DEBUG("Shuting down model-checker");
94
95   RemoteClientMemory* process = &this->process();
96   if (process->running()) {
97     XBT_DEBUG("Killing process");
98     kill(process->pid(), SIGKILL);
99     process->terminate();
100   }
101 }
102
103 void ModelChecker::resume(RemoteClientMemory& process)
104 {
105   int res = event_loop_.get_channel().send(MC_MESSAGE_CONTINUE);
106   if (res)
107     throw xbt::errno_error();
108   process.clear_cache();
109 }
110
111 static void MC_report_crash(int status)
112 {
113   XBT_INFO("**************************");
114   XBT_INFO("** CRASH IN THE PROGRAM **");
115   XBT_INFO("**************************");
116   if (WIFSIGNALED(status))
117     XBT_INFO("From signal: %s", strsignal(WTERMSIG(status)));
118   else if (WIFEXITED(status))
119     XBT_INFO("From exit: %i", WEXITSTATUS(status));
120   if (not xbt_log_no_loc)
121     XBT_INFO("%s core dump was generated by the system.", WCOREDUMP(status) ? "A" : "No");
122   XBT_INFO("Counter-example execution trace:");
123   for (auto const& s : mc_model_checker->getChecker()->get_textual_trace())
124     XBT_INFO("  %s", s.c_str());
125   dumpRecordPath();
126   session->log_state();
127   if (xbt_log_no_loc) {
128     XBT_INFO("Stack trace not displayed because you passed --log=no_loc");
129   } else {
130     XBT_INFO("Stack trace:");
131     mc_model_checker->process().dump_stack();
132   }
133 }
134
135 static void MC_report_assertion_error()
136 {
137   XBT_INFO("**************************");
138   XBT_INFO("*** PROPERTY NOT VALID ***");
139   XBT_INFO("**************************");
140   XBT_INFO("Counter-example execution trace:");
141   for (auto const& s : mc_model_checker->getChecker()->get_textual_trace())
142     XBT_INFO("  %s", s.c_str());
143   dumpRecordPath();
144   session->log_state();
145 }
146
147 bool ModelChecker::handle_message(const char* buffer, ssize_t size)
148 {
149   s_mc_message_t base_message;
150   xbt_assert(size >= (ssize_t)sizeof(base_message), "Broken message");
151   memcpy(&base_message, buffer, sizeof(base_message));
152
153   switch(base_message.type) {
154   case MC_MESSAGE_IGNORE_HEAP:
155     {
156     s_mc_message_ignore_heap_t message;
157     xbt_assert(size == sizeof(message), "Broken messsage");
158     memcpy(&message, buffer, sizeof(message));
159
160     IgnoredHeapRegion region;
161     region.block    = message.block;
162     region.fragment = message.fragment;
163     region.address  = message.address;
164     region.size     = message.size;
165     process().ignore_heap(region);
166     break;
167     }
168
169   case MC_MESSAGE_UNIGNORE_HEAP:
170     {
171     s_mc_message_ignore_memory_t message;
172     xbt_assert(size == sizeof(message), "Broken messsage");
173     memcpy(&message, buffer, sizeof(message));
174     process().unignore_heap((void*)(std::uintptr_t)message.addr, message.size);
175     break;
176     }
177
178   case MC_MESSAGE_IGNORE_MEMORY:
179     {
180     s_mc_message_ignore_memory_t message;
181     xbt_assert(size == sizeof(message), "Broken messsage");
182     memcpy(&message, buffer, sizeof(message));
183     this->process().ignore_region(message.addr, message.size);
184     break;
185     }
186
187   case MC_MESSAGE_STACK_REGION:
188     {
189     s_mc_message_stack_region_t message;
190     xbt_assert(size == sizeof(message), "Broken messsage");
191     memcpy(&message, buffer, sizeof(message));
192     this->process().stack_areas().push_back(message.stack_region);
193     }
194     break;
195
196   case MC_MESSAGE_REGISTER_SYMBOL:
197     {
198     s_mc_message_register_symbol_t message;
199     xbt_assert(size == sizeof(message), "Broken message");
200     memcpy(&message, buffer, sizeof(message));
201     xbt_assert(not message.callback, "Support for client-side function proposition is not implemented.");
202     XBT_DEBUG("Received symbol: %s", message.name);
203
204     if (property_automaton == nullptr)
205       property_automaton = xbt_automaton_new();
206
207     RemoteClientMemory* process = &this->process();
208     RemotePtr<int> address = remote((int*)message.data);
209     xbt::add_proposition(property_automaton, message.name, [process, address]() { return process->read(address); });
210
211     break;
212     }
213
214   case MC_MESSAGE_WAITING:
215     return false;
216
217   case MC_MESSAGE_ASSERTION_FAILED:
218     MC_report_assertion_error();
219     this->exit(SIMGRID_MC_EXIT_SAFETY);
220
221   default:
222     xbt_die("Unexpected message from model-checked application");
223   }
224   return true;
225 }
226
227 /** Terminate the model-checker application */
228 void ModelChecker::exit(int status)
229 {
230   // TODO, terminate the model checker politely instead of exiting rudely
231   if (process().running())
232     kill(process().pid(), SIGKILL);
233   ::exit(status);
234 }
235
236 void ModelChecker::handle_events(int sig, short events)
237 {
238   if (events == EV_READ) {
239     char buffer[MC_MESSAGE_LENGTH];
240     ssize_t size = event_loop_.get_channel().receive(buffer, sizeof(buffer), false);
241     if (size == -1 && errno != EAGAIN)
242       throw simgrid::xbt::errno_error();
243
244     if (not handle_message(buffer, size))
245       event_loop_.break_loop();
246   }
247   else if (events == EV_SIGNAL) {
248     if (sig == SIGCHLD)
249       this->handle_waitpid();
250   }
251   else {
252     xbt_die("Unexpected event");
253   }
254 }
255
256 void ModelChecker::handle_waitpid()
257 {
258   XBT_DEBUG("Check for wait event");
259   int status;
260   pid_t pid;
261   while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
262     if (pid == -1) {
263       if (errno == ECHILD) {
264         // No more children:
265         xbt_assert(not this->process().running(), "Inconsistent state");
266         break;
267       } else {
268         XBT_ERROR("Could not wait for pid");
269         throw simgrid::xbt::errno_error();
270       }
271     }
272
273     if (pid == this->process().pid()) {
274       // From PTRACE_O_TRACEEXIT:
275 #ifdef __linux__
276       if (status>>8 == (SIGTRAP | (PTRACE_EVENT_EXIT<<8))) {
277         xbt_assert(ptrace(PTRACE_GETEVENTMSG, this->process().pid(), 0, &status) != -1, "Could not get exit status");
278         if (WIFSIGNALED(status)) {
279           MC_report_crash(status);
280           mc_model_checker->exit(SIMGRID_MC_EXIT_PROGRAM_CRASH);
281         }
282       }
283 #endif
284
285       // We don't care about signals, just reinject them:
286       if (WIFSTOPPED(status)) {
287         XBT_DEBUG("Stopped with signal %i", (int) WSTOPSIG(status));
288         errno = 0;
289 #ifdef __linux__
290         ptrace(PTRACE_CONT, this->process().pid(), 0, WSTOPSIG(status));
291 #elif defined BSD
292         ptrace(PT_CONTINUE, this->process().pid(), (caddr_t)1, WSTOPSIG(status));
293 #endif
294         xbt_assert(errno == 0, "Could not PTRACE_CONT");
295       }
296
297       else if (WIFSIGNALED(status)) {
298         MC_report_crash(status);
299         mc_model_checker->exit(SIMGRID_MC_EXIT_PROGRAM_CRASH);
300       } else if (WIFEXITED(status)) {
301         XBT_DEBUG("Child process is over");
302         this->process().terminate();
303       }
304     }
305   }
306 }
307
308 void ModelChecker::wait_for_requests()
309 {
310   this->resume(process());
311   if (this->process().running())
312     event_loop_.dispatch();
313 }
314
315 void ModelChecker::handle_simcall(Transition const& transition)
316 {
317   s_mc_message_simcall_handle_t m;
318   memset(&m, 0, sizeof(m));
319   m.type  = MC_MESSAGE_SIMCALL_HANDLE;
320   m.pid   = transition.pid_;
321   m.value = transition.argument_;
322   event_loop_.get_channel().send(m);
323   this->process_->clear_cache();
324   if (this->process_->running())
325     event_loop_.dispatch();
326 }
327
328 bool ModelChecker::checkDeadlock()
329 {
330   int res = event_loop_.get_channel().send(MC_MESSAGE_DEADLOCK_CHECK);
331   xbt_assert(res == 0, "Could not check deadlock state");
332   s_mc_message_int_t message;
333   ssize_t s = event_loop_.get_channel().receive(message);
334   xbt_assert(s != -1, "Could not receive message");
335   xbt_assert(s == sizeof(message) && message.type == MC_MESSAGE_DEADLOCK_CHECK_REPLY,
336              "Received unexpected message %s (%i, size=%i) "
337              "expected MC_MESSAGE_DEADLOCK_CHECK_REPLY (%i, size=%i)",
338              MC_message_type_name(message.type), (int)message.type, (int)s, (int)MC_MESSAGE_DEADLOCK_CHECK_REPLY,
339              (int)sizeof(message));
340   return message.value != 0;
341 }
342
343 } // namespace mc
344 } // namespace simgrid