Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
67eb84b45b7ca0c6849fbdb162e757ab9ba4ae6b
[simgrid.git] / src / mc / ModelChecker.cpp
1 /* Copyright (c) 2008-2017. 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.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.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
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<RemoteClient> 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   RemoteClient& 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::RemoteClient* process = &this->process();
148   if (process->running()) {
149     XBT_DEBUG("Killing process");
150     kill(process->pid(), SIGKILL);
151     process->terminate();
152   }
153 }
154
155 void ModelChecker::resume(simgrid::mc::RemoteClient& 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 const& 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 const& 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_message_ignore_heap_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_message_ignore_memory_t message;
225     if (size != sizeof(message))
226       xbt_die("Broken messsage");
227     memcpy(&message, buffer, sizeof(message));
228     process().unignore_heap((void*)(std::uintptr_t)message.addr, message.size);
229     break;
230     }
231
232   case MC_MESSAGE_IGNORE_MEMORY:
233     {
234     s_mc_message_ignore_memory_t message;
235     if (size != sizeof(message))
236       xbt_die("Broken messsage");
237     memcpy(&message, buffer, sizeof(message));
238     this->process().ignore_region(message.addr, message.size);
239     break;
240     }
241
242   case MC_MESSAGE_STACK_REGION:
243     {
244     s_mc_message_stack_region_t message;
245     if (size != sizeof(message))
246       xbt_die("Broken messsage");
247     memcpy(&message, buffer, sizeof(message));
248     this->process().stack_areas().push_back(message.stack_region);
249     }
250     break;
251
252   case MC_MESSAGE_REGISTER_SYMBOL:
253     {
254     s_mc_message_register_symbol_t message;
255     if (size != sizeof(message))
256       xbt_die("Broken message");
257     memcpy(&message, buffer, sizeof(message));
258     if (message.callback)
259       xbt_die("Support for client-side function proposition is not implemented.");
260     XBT_DEBUG("Received symbol: %s", message.name);
261
262     if (simgrid::mc::property_automaton == nullptr)
263       simgrid::mc::property_automaton = xbt_automaton_new();
264
265     simgrid::mc::RemoteClient* process  = &this->process();
266     simgrid::mc::RemotePtr<int> address = simgrid::mc::remote((int*)message.data);
267     simgrid::xbt::add_proposition(simgrid::mc::property_automaton, message.name,
268                                   [process, address]() { return process->read(address); });
269
270     break;
271     }
272
273   case MC_MESSAGE_WAITING:
274     return false;
275
276   case MC_MESSAGE_ASSERTION_FAILED:
277     MC_report_assertion_error();
278     this->exit(SIMGRID_MC_EXIT_SAFETY);
279     break;
280
281   default:
282     xbt_die("Unexpected message from model-checked application");
283
284   }
285   return true;
286 }
287
288 /** Terminate the model-checker application */
289 void ModelChecker::exit(int status)
290 {
291   // TODO, terminate the model checker politely instead of exiting rudely
292   if (process().running())
293     kill(process().pid(), SIGKILL);
294   ::exit(status);
295 }
296
297 void ModelChecker::handle_events(int fd, short events)
298 {
299   if (events == EV_READ) {
300     char buffer[MC_MESSAGE_LENGTH];
301     ssize_t size = process_->getChannel().receive(buffer, sizeof(buffer), false);
302     if (size == -1 && errno != EAGAIN)
303       throw simgrid::xbt::errno_error();
304     if (not handle_message(buffer, size)) {
305       event_base_loopbreak(base_);
306     }
307   }
308   else if (events == EV_SIGNAL) {
309     on_signal(fd);
310   }
311   else {
312     xbt_die("Unexpected event");
313   }
314 }
315
316 void ModelChecker::loop()
317 {
318   if (this->process().running())
319     event_base_dispatch(base_);
320 }
321
322 void ModelChecker::handle_waitpid()
323 {
324   XBT_DEBUG("Check for wait event");
325   int status;
326   pid_t pid;
327   while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
328     if (pid == -1) {
329       if (errno == ECHILD) {
330         // No more children:
331         if (this->process().running())
332           xbt_die("Inconsistent state");
333         else
334           break;
335       } else {
336         XBT_ERROR("Could not wait for pid");
337         throw simgrid::xbt::errno_error();
338       }
339     }
340
341     if (pid == this->process().pid()) {
342
343       // From PTRACE_O_TRACEEXIT:
344 #ifdef __linux__
345       if (status>>8 == (SIGTRAP | (PTRACE_EVENT_EXIT<<8))) {
346         if (ptrace(PTRACE_GETEVENTMSG, this->process().pid(), 0, &status) == -1)
347           xbt_die("Could not get exit status");
348         if (WIFSIGNALED(status)) {
349           MC_report_crash(status);
350           mc_model_checker->exit(SIMGRID_MC_EXIT_PROGRAM_CRASH);
351         }
352       }
353 #endif
354
355       // We don't care about signals, just reinject them:
356       if (WIFSTOPPED(status)) {
357         XBT_DEBUG("Stopped with signal %i", (int) WSTOPSIG(status));
358         errno = 0;
359 #ifdef __linux__
360         ptrace(PTRACE_CONT, this->process().pid(), 0, WSTOPSIG(status));
361 #elif defined BSD
362         ptrace(PT_CONTINUE, this->process().pid(), (caddr_t)1, WSTOPSIG(status));
363 #endif
364         if (errno != 0)
365           xbt_die("Could not PTRACE_CONT");
366       }
367
368       else if (WIFEXITED(status) || WIFSIGNALED(status)) {
369         XBT_DEBUG("Child process is over");
370         this->process().terminate();
371       }
372     }
373   }
374 }
375
376 void ModelChecker::on_signal(int signo)
377 {
378   if (signo == SIGCHLD)
379     this->handle_waitpid();
380 }
381
382 void ModelChecker::wait_for_requests()
383 {
384   this->resume(process());
385   if (this->process().running())
386     event_base_dispatch(base_);
387 }
388
389 void ModelChecker::handle_simcall(Transition const& transition)
390 {
391   s_mc_message_simcall_handle_t m;
392   memset(&m, 0, sizeof(m));
393   m.type  = MC_MESSAGE_SIMCALL_HANDLE;
394   m.pid   = transition.pid;
395   m.value = transition.argument;
396   this->process_->getChannel().send(m);
397   this->process_->clear_cache();
398   if (this->process_->running())
399     event_base_dispatch(base_);
400 }
401
402 bool ModelChecker::checkDeadlock()
403 {
404   int res;
405   if ((res = this->process().getChannel().send(MC_MESSAGE_DEADLOCK_CHECK)))
406     xbt_die("Could not check deadlock state");
407   s_mc_message_int_t message;
408   ssize_t s = mc_model_checker->process().getChannel().receive(message);
409   if (s == -1)
410     xbt_die("Could not receive message");
411   if (s != sizeof(message) || message.type != MC_MESSAGE_DEADLOCK_CHECK_REPLY)
412     xbt_die("Received unexpected message %s (%i, size=%i) "
413       "expected MC_MESSAGE_DEADLOCK_CHECK_REPLY (%i, size=%i)",
414       MC_message_type_name(message.type), (int) message.type, (int) s,
415       (int) MC_MESSAGE_DEADLOCK_CHECK_REPLY, (int) sizeof(message)
416       );
417   return message.value != 0;
418 }
419
420 }
421 }