Logo AND Algorithmique Numérique Distribuée

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