Logo AND Algorithmique Numérique Distribuée

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