Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Rewrite without delegated constructor.
[simgrid.git] / src / mc / ModelChecker.cpp
1 /* Copyright (c) 2008-2018. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include <cassert>
8
9 #include <sys/types.h>
10 #include <sys/wait.h>
11 #include <sys/socket.h>
12 #include <sys/ptrace.h>
13
14 #include <memory>
15 #include <system_error>
16
17 #include "xbt/automaton.h"
18 #include "xbt/automaton.hpp"
19 #include "xbt/log.h"
20 #include "xbt/system_error.hpp"
21
22 #include "simgrid/sg_config.hpp"
23
24 #include "src/mc/ModelChecker.hpp"
25 #include "src/mc/ModelChecker.hpp"
26 #include "src/mc/PageStore.hpp"
27 #include "src/mc/Transition.hpp"
28 #include "src/mc/checker/Checker.hpp"
29 #include "src/mc/mc_exit.hpp"
30 #include "src/mc/mc_private.hpp"
31 #include "src/mc/mc_record.hpp"
32 #include "src/mc/remote/mc_protocol.h"
33
34 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_ModelChecker, mc, "ModelChecker");
35
36 ::simgrid::mc::ModelChecker* mc_model_checker = nullptr;
37 extern std::string _sg_mc_dot_output_file;
38
39 using simgrid::mc::remote;
40
41 #ifdef __linux__
42 # define WAITPID_CHECKED_FLAGS __WALL
43 #else
44 # define WAITPID_CHECKED_FLAGS 0
45 #endif
46
47 namespace simgrid {
48 namespace mc {
49
50 ModelChecker::ModelChecker(std::unique_ptr<RemoteClient> process)
51     : base_(nullptr)
52     , socket_event_(nullptr)
53     , signal_event_(nullptr)
54     , page_store_(500)
55     , process_(std::move(process))
56     , parent_snapshot_(nullptr)
57 {
58
59 }
60
61 ModelChecker::~ModelChecker() {
62   if (socket_event_ != nullptr)
63     event_free(socket_event_);
64   if (signal_event_ != nullptr)
65     event_free(signal_event_);
66   if (base_ != nullptr)
67     event_base_free(base_);
68 }
69
70 void ModelChecker::start()
71 {
72   const pid_t pid = process_->pid();
73
74   base_ = event_base_new();
75   event_callback_fn event_callback = [](evutil_socket_t fd, short events, void *arg)
76   {
77     ((ModelChecker *)arg)->handle_events(fd, events);
78   };
79   socket_event_ = event_new(base_,
80                             process_->getChannel().getSocket(),
81                             EV_READ|EV_PERSIST,
82                             event_callback, this);
83   event_add(socket_event_, NULL);
84   signal_event_ = event_new(base_,
85                             SIGCHLD,
86                             EV_SIGNAL|EV_PERSIST,
87                             event_callback, this);
88   event_add(signal_event_, NULL);
89
90   XBT_DEBUG("Waiting for the model-checked process");
91   int status;
92
93   // The model-checked process SIGSTOP itself to signal it's ready:
94   pid_t res = waitpid(pid, &status, WAITPID_CHECKED_FLAGS);
95   if (res < 0 || not WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
96     xbt_die("Could not wait model-checked process");
97
98   process_->init();
99
100   if (not _sg_mc_dot_output_file.empty())
101     MC_init_dot_output();
102
103   setup_ignore();
104
105 #ifdef __linux__
106   ptrace(PTRACE_SETOPTIONS, pid, nullptr, PTRACE_O_TRACEEXIT);
107   ptrace(PTRACE_CONT, pid, 0, 0);
108 #elif defined BSD
109   ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
110 #else
111 # error "no ptrace equivalent coded for this platform"
112 #endif
113 }
114
115 static const std::pair<const char*, const char*> ignored_local_variables[] = {
116   std::pair<const char*, const char*>{  "e", "*" },
117   std::pair<const char*, const char*>{ "__ex_cleanup", "*" },
118   std::pair<const char*, const char*>{ "__ex_mctx_en", "*" },
119   std::pair<const char*, const char*>{ "__ex_mctx_me", "*" },
120   std::pair<const char*, const char*>{ "__xbt_ex_ctx_ptr", "*" },
121   std::pair<const char*, const char*>{ "_log_ev", "*" },
122   std::pair<const char*, const char*>{ "_throw_ctx", "*" },
123   std::pair<const char*, const char*>{ "ctx", "*" },
124
125   std::pair<const char*, const char*>{ "self", "simcall_BODY_mc_snapshot" },
126   std::pair<const char*, const char*>{ "next_context", "smx_ctx_sysv_suspend_serial" },
127   std::pair<const char*, const char*>{ "i", "smx_ctx_sysv_suspend_serial" },
128
129   /* Ignore local variable about time used for tracing */
130   std::pair<const char*, const char*>{ "start_time", "*" },
131 };
132
133 void ModelChecker::setup_ignore()
134 {
135   RemoteClient& process = this->process();
136   for (std::pair<const char*, const char*> const& var :
137       ignored_local_variables)
138     process.ignore_local_variable(var.first, var.second);
139
140   /* Static variable used for tracing */
141   process.ignore_global_variable("counter");
142 }
143
144 void ModelChecker::shutdown()
145 {
146   XBT_DEBUG("Shuting down model-checker");
147
148   simgrid::mc::RemoteClient* process = &this->process();
149   if (process->running()) {
150     XBT_DEBUG("Killing process");
151     kill(process->pid(), SIGKILL);
152     process->terminate();
153   }
154 }
155
156 void ModelChecker::resume(simgrid::mc::RemoteClient& process)
157 {
158   int res = process.getChannel().send(MC_MESSAGE_CONTINUE);
159   if (res)
160     throw simgrid::xbt::errno_error();
161   process.clear_cache();
162 }
163
164 static void MC_report_crash(int status)
165 {
166   XBT_INFO("**************************");
167   XBT_INFO("** CRASH IN THE PROGRAM **");
168   XBT_INFO("**************************");
169   if (WIFSIGNALED(status))
170     XBT_INFO("From signal: %s", strsignal(WTERMSIG(status)));
171   else if (WIFEXITED(status))
172     XBT_INFO("From exit: %i", WEXITSTATUS(status));
173   if (WCOREDUMP(status))
174     XBT_INFO("A core dump was generated by the system.");
175   else
176     XBT_INFO("No core dump was generated by the system.");
177   XBT_INFO("Counter-example execution trace:");
178   simgrid::mc::dumpRecordPath();
179   for (auto const& s : mc_model_checker->getChecker()->getTextualTrace())
180     XBT_INFO("%s", s.c_str());
181   simgrid::mc::session->logState();
182   XBT_INFO("Stack trace:");
183   mc_model_checker->process().dumpStack();
184 }
185
186 static void MC_report_assertion_error()
187 {
188   XBT_INFO("**************************");
189   XBT_INFO("*** PROPERTY NOT VALID ***");
190   XBT_INFO("**************************");
191   XBT_INFO("Counter-example execution trace:");
192   simgrid::mc::dumpRecordPath();
193   for (auto const& s : mc_model_checker->getChecker()->getTextualTrace())
194     XBT_INFO("%s", s.c_str());
195   simgrid::mc::session->logState();
196 }
197
198 bool ModelChecker::handle_message(char* buffer, ssize_t size)
199 {
200   s_mc_message_t base_message;
201   if (size < (ssize_t) sizeof(base_message))
202     xbt_die("Broken message");
203   memcpy(&base_message, buffer, sizeof(base_message));
204
205   switch(base_message.type) {
206
207   case MC_MESSAGE_IGNORE_HEAP:
208     {
209     s_mc_message_ignore_heap_t message;
210     if (size != sizeof(message))
211       xbt_die("Broken messsage");
212     memcpy(&message, buffer, sizeof(message));
213
214     IgnoredHeapRegion region;
215     region.block    = message.block;
216     region.fragment = message.fragment;
217     region.address  = message.address;
218     region.size     = message.size;
219     process().ignore_heap(region);
220     break;
221     }
222
223   case MC_MESSAGE_UNIGNORE_HEAP:
224     {
225     s_mc_message_ignore_memory_t message;
226     if (size != sizeof(message))
227       xbt_die("Broken messsage");
228     memcpy(&message, buffer, sizeof(message));
229     process().unignore_heap((void*)(std::uintptr_t)message.addr, message.size);
230     break;
231     }
232
233   case MC_MESSAGE_IGNORE_MEMORY:
234     {
235     s_mc_message_ignore_memory_t message;
236     if (size != sizeof(message))
237       xbt_die("Broken messsage");
238     memcpy(&message, buffer, sizeof(message));
239     this->process().ignore_region(message.addr, message.size);
240     break;
241     }
242
243   case MC_MESSAGE_STACK_REGION:
244     {
245     s_mc_message_stack_region_t message;
246     if (size != sizeof(message))
247       xbt_die("Broken messsage");
248     memcpy(&message, buffer, sizeof(message));
249     this->process().stack_areas().push_back(message.stack_region);
250     }
251     break;
252
253   case MC_MESSAGE_REGISTER_SYMBOL:
254     {
255     s_mc_message_register_symbol_t message;
256     if (size != sizeof(message))
257       xbt_die("Broken message");
258     memcpy(&message, buffer, sizeof(message));
259     if (message.callback)
260       xbt_die("Support for client-side function proposition is not implemented.");
261     XBT_DEBUG("Received symbol: %s", message.name);
262
263     if (simgrid::mc::property_automaton == nullptr)
264       simgrid::mc::property_automaton = xbt_automaton_new();
265
266     simgrid::mc::RemoteClient* process  = &this->process();
267     simgrid::mc::RemotePtr<int> address = simgrid::mc::remote((int*)message.data);
268     simgrid::xbt::add_proposition(simgrid::mc::property_automaton, message.name,
269                                   [process, address]() { return process->read(address); });
270
271     break;
272     }
273
274   case MC_MESSAGE_WAITING:
275     return false;
276
277   case MC_MESSAGE_ASSERTION_FAILED:
278     MC_report_assertion_error();
279     this->exit(SIMGRID_MC_EXIT_SAFETY);
280     break;
281
282   default:
283     xbt_die("Unexpected message from model-checked application");
284
285   }
286   return true;
287 }
288
289 /** Terminate the model-checker application */
290 void ModelChecker::exit(int status)
291 {
292   // TODO, terminate the model checker politely instead of exiting rudely
293   if (process().running())
294     kill(process().pid(), SIGKILL);
295   ::exit(status);
296 }
297
298 void ModelChecker::handle_events(int fd, short events)
299 {
300   if (events == EV_READ) {
301     char buffer[MC_MESSAGE_LENGTH];
302     ssize_t size = process_->getChannel().receive(buffer, sizeof(buffer), false);
303     if (size == -1 && errno != EAGAIN)
304       throw simgrid::xbt::errno_error();
305     if (not handle_message(buffer, size)) {
306       event_base_loopbreak(base_);
307     }
308   }
309   else if (events == EV_SIGNAL) {
310     on_signal(fd);
311   }
312   else {
313     xbt_die("Unexpected event");
314   }
315 }
316
317 void ModelChecker::loop()
318 {
319   if (this->process().running())
320     event_base_dispatch(base_);
321 }
322
323 void ModelChecker::handle_waitpid()
324 {
325   XBT_DEBUG("Check for wait event");
326   int status;
327   pid_t pid;
328   while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
329     if (pid == -1) {
330       if (errno == ECHILD) {
331         // No more children:
332         if (this->process().running())
333           xbt_die("Inconsistent state");
334         else
335           break;
336       } else {
337         XBT_ERROR("Could not wait for pid");
338         throw simgrid::xbt::errno_error();
339       }
340     }
341
342     if (pid == this->process().pid()) {
343
344       // From PTRACE_O_TRACEEXIT:
345 #ifdef __linux__
346       if (status>>8 == (SIGTRAP | (PTRACE_EVENT_EXIT<<8))) {
347         if (ptrace(PTRACE_GETEVENTMSG, this->process().pid(), 0, &status) == -1)
348           xbt_die("Could not get exit status");
349         if (WIFSIGNALED(status)) {
350           MC_report_crash(status);
351           mc_model_checker->exit(SIMGRID_MC_EXIT_PROGRAM_CRASH);
352         }
353       }
354 #endif
355
356       // We don't care about signals, just reinject them:
357       if (WIFSTOPPED(status)) {
358         XBT_DEBUG("Stopped with signal %i", (int) WSTOPSIG(status));
359         errno = 0;
360 #ifdef __linux__
361         ptrace(PTRACE_CONT, this->process().pid(), 0, WSTOPSIG(status));
362 #elif defined BSD
363         ptrace(PT_CONTINUE, this->process().pid(), (caddr_t)1, WSTOPSIG(status));
364 #endif
365         if (errno != 0)
366           xbt_die("Could not PTRACE_CONT");
367       }
368
369       else if (WIFEXITED(status) || WIFSIGNALED(status)) {
370         XBT_DEBUG("Child process is over");
371         this->process().terminate();
372       }
373     }
374   }
375 }
376
377 void ModelChecker::on_signal(int signo)
378 {
379   if (signo == SIGCHLD)
380     this->handle_waitpid();
381 }
382
383 void ModelChecker::wait_for_requests()
384 {
385   this->resume(process());
386   if (this->process().running())
387     event_base_dispatch(base_);
388 }
389
390 void ModelChecker::handle_simcall(Transition const& transition)
391 {
392   s_mc_message_simcall_handle_t m;
393   memset(&m, 0, sizeof(m));
394   m.type  = MC_MESSAGE_SIMCALL_HANDLE;
395   m.pid   = transition.pid;
396   m.value = transition.argument;
397   this->process_->getChannel().send(m);
398   this->process_->clear_cache();
399   if (this->process_->running())
400     event_base_dispatch(base_);
401 }
402
403 bool ModelChecker::checkDeadlock()
404 {
405   int res;
406   if ((res = this->process().getChannel().send(MC_MESSAGE_DEADLOCK_CHECK)))
407     xbt_die("Could not check deadlock state");
408   s_mc_message_int_t message;
409   ssize_t s = mc_model_checker->process().getChannel().receive(message);
410   if (s == -1)
411     xbt_die("Could not receive message");
412   if (s != sizeof(message) || message.type != MC_MESSAGE_DEADLOCK_CHECK_REPLY)
413     xbt_die("Received unexpected message %s (%i, size=%i) "
414       "expected MC_MESSAGE_DEADLOCK_CHECK_REPLY (%i, size=%i)",
415       MC_message_type_name(message.type), (int) message.type, (int) s,
416       (int) MC_MESSAGE_DEADLOCK_CHECK_REPLY, (int) sizeof(message)
417       );
418   return message.value != 0;
419 }
420
421 }
422 }