1 /* Copyright (c) 2008-2020. The SimGrid Team. All rights reserved. */
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. */
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"
17 #include <sys/ptrace.h>
20 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_ModelChecker, mc, "ModelChecker");
22 ::simgrid::mc::ModelChecker* mc_model_checker = nullptr;
24 using simgrid::mc::remote;
27 # define WAITPID_CHECKED_FLAGS __WALL
29 # define WAITPID_CHECKED_FLAGS 0
35 ModelChecker::ModelChecker(std::unique_ptr<RemoteClient> process) : process_(std::move(process)) {}
37 ModelChecker::~ModelChecker()
39 if (socket_event_ != nullptr)
40 event_free(socket_event_);
41 if (signal_event_ != nullptr)
42 event_free(signal_event_);
44 event_base_free(base_);
47 void ModelChecker::start()
49 base_ = event_base_new();
50 event_callback_fn event_callback = [](evutil_socket_t fd, short events, void *arg)
52 ((ModelChecker *)arg)->handle_events(fd, events);
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_,
59 event_callback, this);
60 event_add(signal_event_, NULL);
62 XBT_DEBUG("Waiting for the model-checked process");
65 // The model-checked process SIGSTOP itself to signal it's ready:
66 const pid_t pid = process_->pid();
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");
74 if (not _sg_mc_dot_output_file.get().empty())
80 ptrace(PTRACE_SETOPTIONS, pid, nullptr, PTRACE_O_TRACEEXIT);
81 ptrace(PTRACE_CONT, pid, 0, 0);
83 ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
85 # error "no ptrace equivalent coded for this platform"
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", "*" },
93 /* Ignore local variable about time used for tracing */
94 std::pair<const char*, const char*>{ "start_time", "*" },
97 void ModelChecker::setup_ignore()
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);
104 /* Static variable used for tracing */
105 process.ignore_global_variable("counter");
108 void ModelChecker::shutdown()
110 XBT_DEBUG("Shuting down model-checker");
112 RemoteClient* process = &this->process();
113 if (process->running()) {
114 XBT_DEBUG("Killing process");
115 kill(process->pid(), SIGKILL);
116 process->terminate();
120 void ModelChecker::resume(RemoteClient& process)
122 int res = process.get_channel().send(MC_MESSAGE_CONTINUE);
124 throw xbt::errno_error();
125 process.clear_cache();
128 static void MC_report_crash(int status)
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());
143 session->log_state();
144 if (xbt_log_no_loc) {
145 XBT_INFO("Stack trace not displayed because you passed --log=no_loc");
147 XBT_INFO("Stack trace:");
148 mc_model_checker->process().dump_stack();
152 static void MC_report_assertion_error()
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());
161 session->log_state();
164 bool ModelChecker::handle_message(const char* buffer, ssize_t size)
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));
170 switch(base_message.type) {
171 case MC_MESSAGE_IGNORE_HEAP:
173 s_mc_message_ignore_heap_t message;
174 xbt_assert(size == sizeof(message), "Broken messsage");
175 memcpy(&message, buffer, sizeof(message));
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);
186 case MC_MESSAGE_UNIGNORE_HEAP:
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);
195 case MC_MESSAGE_IGNORE_MEMORY:
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);
204 case MC_MESSAGE_STACK_REGION:
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);
213 case MC_MESSAGE_REGISTER_SYMBOL:
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);
221 if (property_automaton == nullptr)
222 property_automaton = xbt_automaton_new();
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); });
231 case MC_MESSAGE_WAITING:
234 case MC_MESSAGE_ASSERTION_FAILED:
235 MC_report_assertion_error();
236 this->exit(SIMGRID_MC_EXIT_SAFETY);
239 xbt_die("Unexpected message from model-checked application");
244 /** Terminate the model-checker application */
245 void ModelChecker::exit(int status)
247 // TODO, terminate the model checker politely instead of exiting rudely
248 if (process().running())
249 kill(process().pid(), SIGKILL);
253 void ModelChecker::handle_events(int fd, short events)
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_);
264 else if (events == EV_SIGNAL) {
268 xbt_die("Unexpected event");
272 void ModelChecker::loop()
274 if (this->process().running())
275 event_base_dispatch(base_);
278 void ModelChecker::handle_waitpid()
280 XBT_DEBUG("Check for wait event");
283 while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
285 if (errno == ECHILD) {
287 xbt_assert(not this->process().running(), "Inconsistent state");
290 XBT_ERROR("Could not wait for pid");
291 throw simgrid::xbt::errno_error();
295 if (pid == this->process().pid()) {
296 // From PTRACE_O_TRACEEXIT:
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);
307 // We don't care about signals, just reinject them:
308 if (WIFSTOPPED(status)) {
309 XBT_DEBUG("Stopped with signal %i", (int) WSTOPSIG(status));
312 ptrace(PTRACE_CONT, this->process().pid(), 0, WSTOPSIG(status));
314 ptrace(PT_CONTINUE, this->process().pid(), (caddr_t)1, WSTOPSIG(status));
316 xbt_assert(errno == 0, "Could not PTRACE_CONT");
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();
330 void ModelChecker::on_signal(int signo)
332 if (signo == SIGCHLD)
333 this->handle_waitpid();
336 void ModelChecker::wait_for_requests()
338 this->resume(process());
339 if (this->process().running())
340 event_base_dispatch(base_);
343 void ModelChecker::handle_simcall(Transition const& transition)
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_);
356 bool ModelChecker::checkDeadlock()
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;
372 } // namespace simgrid