Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of https://framagit.org/simgrid/simgrid
authormlaurent <mathieu.laurent@ens-rennes.fr>
Mon, 20 Mar 2023 15:52:48 +0000 (16:52 +0100)
committermlaurent <mathieu.laurent@ens-rennes.fr>
Mon, 20 Mar 2023 15:52:48 +0000 (16:52 +0100)
32 files changed:
MANIFEST.in
include/simgrid/forward.h
src/mc/ModelChecker.cpp [deleted file]
src/mc/ModelChecker.hpp [deleted file]
src/mc/VisitedState.cpp
src/mc/api/RemoteApp.cpp
src/mc/api/RemoteApp.hpp
src/mc/compare.cpp
src/mc/explo/DFSExplorer.cpp
src/mc/explo/Exploration.cpp
src/mc/explo/Exploration.hpp
src/mc/explo/LivenessChecker.cpp
src/mc/mc_base.cpp
src/mc/mc_forward.hpp
src/mc/remote/Channel.hpp
src/mc/remote/CheckerSide.cpp
src/mc/remote/CheckerSide.hpp
src/mc/sosp/Region.cpp
src/mc/sosp/Region.hpp
src/mc/sosp/RemoteProcessMemory.cpp
src/mc/sosp/RemoteProcessMemory.hpp
src/mc/sosp/Snapshot.cpp
src/mc/sosp/Snapshot.hpp
src/mc/sosp/Snapshot_test.cpp
src/mc/transition/Transition.cpp
src/mc/transition/Transition.hpp
src/mc/transition/TransitionActorJoin.cpp
src/mc/transition/TransitionAny.cpp
src/mc/transition/TransitionComm.cpp
teshsuite/mc/dwarf-expression/dwarf-expression.cpp
teshsuite/mc/dwarf/dwarf.cpp
tools/cmake/DefinePackages.cmake

index f173a85..3bd5a10 100644 (file)
@@ -2147,8 +2147,6 @@ include src/kernel/xml/simgrid.dtd
 include src/kernel/xml/simgrid_dtd.c
 include src/kernel/xml/simgrid_dtd.h
 include src/mc/AddressSpace.hpp
-include src/mc/ModelChecker.cpp
-include src/mc/ModelChecker.hpp
 include src/mc/VisitedState.cpp
 include src/mc/VisitedState.hpp
 include src/mc/api/ActorState.hpp
@@ -2156,6 +2154,8 @@ include src/mc/api/RemoteApp.cpp
 include src/mc/api/RemoteApp.hpp
 include src/mc/api/State.cpp
 include src/mc/api/State.hpp
+include src/mc/api/guide/BasicGuide.hpp
+include src/mc/api/guide/GuidedState.hpp
 include src/mc/compare.cpp
 include src/mc/datatypes.h
 include src/mc/explo/CommunicationDeterminismChecker.cpp
@@ -2603,6 +2603,7 @@ include tools/cmake/Modules/FindLibunwind.cmake
 include tools/cmake/Modules/FindNS3.cmake
 include tools/cmake/Modules/FindPAPI.cmake
 include tools/cmake/Modules/FindValgrind.cmake
+include tools/cmake/Modules/nlohmann_jsonConfig.cmake
 include tools/cmake/Modules/pybind11Config.cmake
 include tools/cmake/Option.cmake
 include tools/cmake/Tests.cmake
index 96bcd6e..dae9277 100644 (file)
@@ -209,6 +209,7 @@ class Profile;
 } // namespace kernel
 namespace mc {
 class State;
+class RemoteApp;
 }
 } // namespace simgrid
 
diff --git a/src/mc/ModelChecker.cpp b/src/mc/ModelChecker.cpp
deleted file mode 100644 (file)
index cec73c6..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/* Copyright (c) 2008-2023. The SimGrid Team. All rights reserved.          */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/mc/ModelChecker.hpp"
-#include "src/mc/explo/Exploration.hpp"
-#include "src/mc/explo/LivenessChecker.hpp"
-#include "src/mc/mc_config.hpp"
-#include "src/mc/mc_exit.hpp"
-#include "src/mc/mc_private.hpp"
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-#include "src/mc/transition/TransitionComm.hpp"
-#include "xbt/automaton.hpp"
-#include "xbt/system_error.hpp"
-
-#include <array>
-#include <csignal>
-#include <sys/ptrace.h>
-#include <sys/wait.h>
-
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_ModelChecker, mc, "ModelChecker");
-
-::simgrid::mc::ModelChecker* mc_model_checker = nullptr;
-
-namespace simgrid::mc {
-
-ModelChecker::ModelChecker(std::unique_ptr<RemoteProcessMemory> remote_memory)
-    : remote_process_memory_(std::move(remote_memory))
-{
-}
-
-bool ModelChecker::handle_message(const char* buffer, ssize_t size)
-{
-  s_mc_message_t base_message;
-  xbt_assert(size >= (ssize_t)sizeof(base_message), "Broken message");
-  memcpy(&base_message, buffer, sizeof(base_message));
-
-  switch(base_message.type) {
-    case MessageType::INITIAL_ADDRESSES: {
-      s_mc_message_initial_addresses_t message;
-      xbt_assert(size == sizeof(message), "Broken message. Got %d bytes instead of %d.", (int)size, (int)sizeof(message));
-      memcpy(&message, buffer, sizeof(message));
-
-      get_remote_process_memory().init(message.mmalloc_default_mdp);
-      break;
-    }
-
-    case MessageType::IGNORE_HEAP: {
-      s_mc_message_ignore_heap_t message;
-      xbt_assert(size == sizeof(message), "Broken message");
-      memcpy(&message, buffer, sizeof(message));
-
-      IgnoredHeapRegion region;
-      region.block    = message.block;
-      region.fragment = message.fragment;
-      region.address  = message.address;
-      region.size     = message.size;
-      get_remote_process_memory().ignore_heap(region);
-      break;
-    }
-
-    case MessageType::UNIGNORE_HEAP: {
-      s_mc_message_ignore_memory_t message;
-      xbt_assert(size == sizeof(message), "Broken message");
-      memcpy(&message, buffer, sizeof(message));
-      get_remote_process_memory().unignore_heap((void*)(std::uintptr_t)message.addr, message.size);
-      break;
-    }
-
-    case MessageType::IGNORE_MEMORY: {
-      s_mc_message_ignore_memory_t message;
-      xbt_assert(size == sizeof(message), "Broken message");
-      memcpy(&message, buffer, sizeof(message));
-      this->get_remote_process_memory().ignore_region(message.addr, message.size);
-      break;
-    }
-
-    case MessageType::STACK_REGION: {
-      s_mc_message_stack_region_t message;
-      xbt_assert(size == sizeof(message), "Broken message");
-      memcpy(&message, buffer, sizeof(message));
-      this->get_remote_process_memory().stack_areas().push_back(message.stack_region);
-    } break;
-
-    case MessageType::REGISTER_SYMBOL: {
-      s_mc_message_register_symbol_t message;
-      xbt_assert(size == sizeof(message), "Broken message");
-      memcpy(&message, buffer, sizeof(message));
-      xbt_assert(not message.callback, "Support for client-side function proposition is not implemented.");
-      XBT_DEBUG("Received symbol: %s", message.name.data());
-
-      LivenessChecker::automaton_register_symbol(get_remote_process_memory(), message.name.data(),
-                                                 remote((int*)message.data));
-      break;
-    }
-
-    case MessageType::WAITING:
-      return false;
-
-    case MessageType::ASSERTION_FAILED:
-      exploration_->report_assertion_failure();
-      break;
-
-    default:
-      xbt_die("Unexpected message from model-checked application");
-  }
-  return true;
-}
-
-void ModelChecker::handle_waitpid(pid_t pid_to_wait)
-{
-  XBT_DEBUG("Check for wait event");
-  int status;
-  pid_t pid;
-  while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
-    if (pid == -1) {
-      if (errno == ECHILD) { // No more children:
-        xbt_assert(not this->get_remote_process_memory().running(), "Inconsistent state");
-        break;
-      } else {
-        XBT_ERROR("Could not wait for pid");
-        throw simgrid::xbt::errno_error();
-      }
-    }
-
-    if (pid == pid_to_wait) {
-      // From PTRACE_O_TRACEEXIT:
-#ifdef __linux__
-      if (status>>8 == (SIGTRAP | (PTRACE_EVENT_EXIT<<8))) {
-        unsigned long eventmsg;
-        xbt_assert(ptrace(PTRACE_GETEVENTMSG, pid_to_wait, 0, &eventmsg) != -1, "Could not get exit status");
-        status = static_cast<int>(eventmsg);
-        if (WIFSIGNALED(status))
-          exploration_->report_crash(status);
-      }
-#endif
-
-      // We don't care about non-lethal signals, just reinject them:
-      if (WIFSTOPPED(status)) {
-        XBT_DEBUG("Stopped with signal %i", (int) WSTOPSIG(status));
-        errno = 0;
-#ifdef __linux__
-        ptrace(PTRACE_CONT, pid_to_wait, 0, WSTOPSIG(status));
-#elif defined BSD
-        ptrace(PT_CONTINUE, pid_to_wait, (caddr_t)1, WSTOPSIG(status));
-#endif
-        xbt_assert(errno == 0, "Could not PTRACE_CONT");
-      }
-
-      else if (WIFSIGNALED(status)) {
-        exploration_->report_crash(status);
-      } else if (WIFEXITED(status)) {
-        XBT_DEBUG("Child process is over");
-        this->get_remote_process_memory().terminate();
-      }
-    }
-  }
-}
-
-} // namespace simgrid::mc
diff --git a/src/mc/ModelChecker.hpp b/src/mc/ModelChecker.hpp
deleted file mode 100644 (file)
index 7b91263..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved.          */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_MODEL_CHECKER_HPP
-#define SIMGRID_MC_MODEL_CHECKER_HPP
-
-#include "src/mc/remote/CheckerSide.hpp"
-#include "src/mc/remote/RemotePtr.hpp"
-#include "src/mc/sosp/PageStore.hpp"
-#include "xbt/base.h"
-
-#include <memory>
-
-namespace simgrid::mc {
-
-/** State of the model-checker (global variables for the model checker)
- */
-class ModelChecker {
-  std::unique_ptr<RemoteProcessMemory> remote_process_memory_;
-  Exploration* exploration_ = nullptr;
-
-public:
-  ModelChecker(ModelChecker const&) = delete;
-  ModelChecker& operator=(ModelChecker const&) = delete;
-  explicit ModelChecker(std::unique_ptr<RemoteProcessMemory> remote_simulation);
-
-  RemoteProcessMemory& get_remote_process_memory() { return *remote_process_memory_; }
-
-  Exploration* get_exploration() const { return exploration_; }
-  void set_exploration(Exploration* exploration) { exploration_ = exploration; }
-
-  void handle_waitpid(pid_t pid_to_wait);                // FIXME move to RemoteApp
-  bool handle_message(const char* buffer, ssize_t size); // FIXME move to RemoteApp
-};
-
-} // namespace simgrid::mc
-
-#endif
index b974d01..a52b8fc 100644 (file)
@@ -58,7 +58,8 @@ VisitedStates::addVisitedState(unsigned long state_number, simgrid::mc::State* g
 
   for (auto i = range_begin; i != range_end; ++i) {
     auto& visited_state = *i;
-    if (*visited_state->system_state_.get() == *new_state->system_state_.get()) {
+    if (visited_state->system_state_->equals_to(*new_state->system_state_.get(),
+                                                remote_app.get_remote_process_memory())) {
       // The state has been visited:
 
       std::unique_ptr<simgrid::mc::VisitedState> old_state = std::move(visited_state);
index 969e5c3..2ddd5c0 100644 (file)
 
 #include <algorithm>
 #include <array>
-#include <boost/tokenizer.hpp>
 #include <memory>
 #include <numeric>
 #include <string>
 
-#include <fcntl.h>
-#ifdef __linux__
-#include <sys/prctl.h>
-#endif
 #include <sys/ptrace.h>
 #include <sys/wait.h>
 
-#ifdef __linux__
-#define WAITPID_CHECKED_FLAGS __WALL
-#else
-#define WAITPID_CHECKED_FLAGS 0
-#endif
-
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_Session, mc, "Model-checker session");
 XBT_LOG_EXTERNAL_CATEGORY(mc_global);
 
-static simgrid::config::Flag<std::string> _sg_mc_setenv{
-    "model-check/setenv", "Extra environment variables to pass to the child process (ex: 'AZE=aze;QWE=qwe').", "",
-    [](std::string_view value) {
-      xbt_assert(value.empty() || value.find('=', 0) != std::string_view::npos,
-                 "The 'model-check/setenv' parameter must be like 'AZE=aze', but it does not contain an equal sign.");
-    }};
-
 namespace simgrid::mc {
 
-XBT_ATTRIB_NORETURN static void run_child_process(int socket, const std::vector<char*>& args)
-{
-  /* On startup, simix_global_init() calls simgrid::mc::Client::initialize(), which checks whether the MC_ENV_SOCKET_FD
-   * env variable is set. If so, MC mode is assumed, and the client is setup from its side
-   */
-
-#ifdef __linux__
-  // Make sure we do not outlive our parent
-  sigset_t mask;
-  sigemptyset(&mask);
-  xbt_assert(sigprocmask(SIG_SETMASK, &mask, nullptr) >= 0, "Could not unblock signals");
-  xbt_assert(prctl(PR_SET_PDEATHSIG, SIGHUP) == 0, "Could not PR_SET_PDEATHSIG");
-#endif
-
-  // Remove CLOEXEC to pass the socket to the application
-  int fdflags = fcntl(socket, F_GETFD, 0);
-  xbt_assert(fdflags != -1 && fcntl(socket, F_SETFD, fdflags & ~FD_CLOEXEC) != -1,
-             "Could not remove CLOEXEC for socket");
-
-  setenv(MC_ENV_SOCKET_FD, std::to_string(socket).c_str(), 1);
-
-  /* Setup the tokenizer that parses the cfg:model-check/setenv parameter */
-  using Tokenizer = boost::tokenizer<boost::char_separator<char>>;
-  boost::char_separator<char> semicol_sep(";");
-  boost::char_separator<char> equal_sep("=");
-  Tokenizer token_vars(_sg_mc_setenv.get(), semicol_sep); /* Iterate over all FOO=foo parts */
-  for (const auto& token : token_vars) {
-    std::vector<std::string> kv;
-    Tokenizer token_kv(token, equal_sep);
-    for (const auto& t : token_kv) /* Iterate over 'FOO' and then 'foo' in that 'FOO=foo' */
-      kv.push_back(t);
-    xbt_assert(kv.size() == 2, "Parse error on 'model-check/setenv' value %s. Does it contain an equal sign?",
-               token.c_str());
-    XBT_INFO("setenv '%s'='%s'", kv[0].c_str(), kv[1].c_str());
-    setenv(kv[0].c_str(), kv[1].c_str(), 1);
-  }
-
-  /* And now, exec the child process */
-  int i = 1;
-  while (args[i] != nullptr && args[i][0] == '-')
-    i++;
-
-  xbt_assert(args[i] != nullptr,
-             "Unable to find a binary to exec on the command line. Did you only pass config flags?");
-
-  execvp(args[i], args.data() + i);
-  XBT_CRITICAL("The model-checked process failed to exec(%s): %s.\n"
-               "        Make sure that your binary exists on disk and is executable.",
-               args[i], strerror(errno));
-  if (strchr(args[i], '=') != nullptr)
-    XBT_CRITICAL("If you want to pass environment variables to the application, please use --cfg=model-check/setenv:%s",
-                 args[i]);
-
-  xbt_die("Aborting now.");
-}
-
 RemoteApp::RemoteApp(const std::vector<char*>& args)
 {
-  // Create an AF_LOCAL socketpair used for exchanging messages
-  // between the model-checker process (ourselves) and the model-checked
-  // process:
-  int sockets[2];
-  xbt_assert(socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != -1, "Could not create socketpair");
-
-  pid_t pid = fork();
-  xbt_assert(pid >= 0, "Could not fork model-checked process");
-
-  if (pid == 0) { // Child
-    ::close(sockets[1]);
-    run_child_process(sockets[0], args);
-    DIE_IMPOSSIBLE;
-  }
+  checker_side_ = std::make_unique<simgrid::mc::CheckerSide>(args);
 
-  // Parent (model-checker):
-  ::close(sockets[0]);
-
-  xbt_assert(mc_model_checker == nullptr, "Did you manage to start the MC twice in this process?");
-
-  auto memory      = std::make_unique<simgrid::mc::RemoteProcessMemory>(pid);
-  model_checker_   = std::make_unique<simgrid::mc::ModelChecker>(std::move(memory));
-  mc_model_checker = model_checker_.get();
-  checker_side_    = std::make_unique<simgrid::mc::CheckerSide>(sockets[1], model_checker_.get());
-
-  start();
-
-  /* Take the initial snapshot */
-  wait_for_requests();
-  initial_snapshot_ =
-      std::make_shared<simgrid::mc::Snapshot>(0, page_store_, model_checker_->get_remote_process_memory());
+  initial_snapshot_ = std::make_shared<simgrid::mc::Snapshot>(0, page_store_, checker_side_->get_remote_memory());
 }
 
 RemoteApp::~RemoteApp()
 {
   initial_snapshot_ = nullptr;
-  if (model_checker_) {
-    shutdown();
-    model_checker_   = nullptr;
-    mc_model_checker = nullptr;
-  }
-}
-void RemoteApp::start()
-{
-  XBT_DEBUG("Waiting for the model-checked process");
-  int status;
-
-  // The model-checked process SIGSTOP itself to signal it's ready:
-  const pid_t pid = get_remote_process_memory().pid();
-
-  xbt_assert(waitpid(pid, &status, WAITPID_CHECKED_FLAGS) == pid && WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP,
-             "Could not wait model-checked process");
-
-  errno = 0;
-#ifdef __linux__
-  ptrace(PTRACE_SETOPTIONS, pid, nullptr, PTRACE_O_TRACEEXIT);
-  ptrace(PTRACE_CONT, pid, 0, 0);
-#elif defined BSD
-  ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
-#else
-#error "no ptrace equivalent coded for this platform"
-#endif
-  xbt_assert(errno == 0,
-             "Ptrace does not seem to be usable in your setup (errno: %d). "
-             "If you run from within a docker, adding `--cap-add SYS_PTRACE` to the docker line may help. "
-             "If it does not help, please report this bug.",
-             errno);
+  shutdown();
 }
+
 void RemoteApp::restore_initial_state() const
 {
-  this->initial_snapshot_->restore(model_checker_->get_remote_process_memory());
+  this->initial_snapshot_->restore(checker_side_->get_remote_memory());
 }
 
 unsigned long RemoteApp::get_maxpid() const
@@ -282,38 +151,32 @@ void RemoteApp::check_deadlock() const
              to_c_str(message.type), (int)message.type, (int)MessageType::DEADLOCK_CHECK_REPLY);
 
   if (message.value != 0) {
+    auto* explo = Exploration::get_instance();
     XBT_CINFO(mc_global, "Counter-example execution trace:");
-    for (auto const& frame : model_checker_->get_exploration()->get_textual_trace())
+    for (auto const& frame : explo->get_textual_trace())
       XBT_CINFO(mc_global, "  %s", frame.c_str());
     XBT_INFO("You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with "
              "--cfg=model-check/replay:'%s'",
-             model_checker_->get_exploration()->get_record_trace().to_string().c_str());
-    model_checker_->get_exploration()->log_state();
+             explo->get_record_trace().to_string().c_str());
+    explo->log_state();
     throw DeadlockError();
   }
 }
 
 void RemoteApp::wait_for_requests()
 {
-  /* Resume the application */
-  if (checker_side_->get_channel().send(MessageType::CONTINUE) != 0)
-    throw xbt::errno_error();
-  get_remote_process_memory().clear_cache();
-
-  if (this->get_remote_process_memory().running())
-    checker_side_->dispatch();
+  checker_side_->wait_for_requests();
 }
 
 void RemoteApp::shutdown()
 {
   XBT_DEBUG("Shutting down model-checker");
 
-  RemoteProcessMemory& process = get_remote_process_memory();
-  if (process.running()) {
+  if (checker_side_->running()) {
     XBT_DEBUG("Killing process");
     finalize_app(true);
-    kill(process.pid(), SIGKILL);
-    process.terminate();
+    kill(checker_side_->get_pid(), SIGKILL);
+    checker_side_->terminate();
   }
 }
 
@@ -326,8 +189,8 @@ Transition* RemoteApp::handle_simcall(aid_t aid, int times_considered, bool new_
   checker_side_->get_channel().send(m);
 
   get_remote_process_memory().clear_cache();
-  if (this->get_remote_process_memory().running())
-    checker_side_->dispatch(); // The app may send messages while processing the transition
+  if (checker_side_->running())
+    checker_side_->dispatch_events(); // The app may send messages while processing the transition
 
   s_mc_message_simcall_execute_answer_t answer;
   ssize_t s = checker_side_->get_channel().receive(answer);
index 77e07f6..d6e5eee 100644 (file)
@@ -7,9 +7,10 @@
 #define SIMGRID_MC_REMOTE_APP_HPP
 
 #include "simgrid/forward.h"
-#include "src/mc/ModelChecker.hpp"
 #include "src/mc/api/ActorState.hpp"
+#include "src/mc/remote/CheckerSide.hpp"
 #include "src/mc/remote/RemotePtr.hpp"
+#include "src/mc/sosp/PageStore.hpp"
 
 #include <functional>
 
@@ -17,16 +18,15 @@ namespace simgrid::mc {
 
 /** High-level view of the verified application, from the model-checker POV
  *
- *  This is expected to become the interface used by model-checking
- *  algorithms to control the execution of the model-checked process
- *  and the exploration of the execution graph. Model-checking
- *  algorithms should be able to be written in high-level languages
- *  (e.g. Python) using bindings on this interface.
+ *  This is expected to become the interface used by model-checking algorithms to control the execution of
+ *  the application process during the exploration of the execution graph.
+ *
+ *  One day, this will allow parallel exploration, ie, the handling of several application processes (each encapsulated
+ * in a separate CheckerSide objects) that explore several parts of the exploration graph.
  */
 class XBT_PUBLIC RemoteApp {
 private:
   std::unique_ptr<CheckerSide> checker_side_;
-  std::unique_ptr<ModelChecker> model_checker_;
   PageStore page_store_{500};
   std::shared_ptr<simgrid::mc::Snapshot> initial_snapshot_;
 
@@ -46,7 +46,6 @@ public:
 
   ~RemoteApp();
 
-  void start();
   void restore_initial_state() const;
   void wait_for_requests();
 
@@ -68,7 +67,7 @@ public:
   Transition* handle_simcall(aid_t aid, int times_considered, bool new_transition);
 
   /* Get the memory of the remote process */
-  RemoteProcessMemory& get_remote_process_memory() { return model_checker_->get_remote_process_memory(); }
+  RemoteProcessMemory& get_remote_process_memory() { return checker_side_->get_remote_memory(); }
 
   PageStore& get_page_store() { return page_store_; }
 };
index 71fac79..9fe8072 100644 (file)
@@ -1206,13 +1206,19 @@ static bool local_variables_differ(const simgrid::mc::RemoteProcessMemory& proce
 }
 
 namespace simgrid::mc {
-
-bool Snapshot::operator==(const Snapshot& other)
+bool Snapshot::equals_to(const Snapshot& other, RemoteProcessMemory& memory)
 {
-  // TODO, make this a field of ModelChecker or something similar
-  static StateComparator state_comparator;
+  /* TODO: the memory parameter should be eventually removed. It seems to be there because each snapshot lacks some sort
+    of metadata. That's OK for now (letting appart the fact that we cannot have a nice operator== because we need that
+    extra parameter), but it will fall short when we want to have parallel explorations, with more than one
+    RemoteProcess. At the very least, snapshots will need to know the remote process they are corresponding to, and more
+    probably they will need to embeed all their metadata to let the remoteprocesses die before the end of the
+    exploration. */
+
+  /* TODO: This method should moved to Snapshot.cpp, but it needs the StateComparator that is declared locally to this
+   * file only. */
 
-  RemoteProcessMemory& memory = mc_model_checker->get_remote_process_memory();
+  static StateComparator state_comparator; // TODO, make this a field of a persistant state object
 
   if (hash_ != other.hash_) {
     XBT_VERB("(%ld - %ld) Different hash: 0x%" PRIx64 "--0x%" PRIx64, this->num_state_, other.num_state_, this->hash_,
index e61a250..c58d581 100644 (file)
@@ -42,7 +42,8 @@ xbt::signal<void(RemoteApp&)> DFSExplorer::on_log_state_signal;
 void DFSExplorer::check_non_termination(const State* current_state)
 {
   for (auto const& state : stack_) {
-    if (*state->get_system_state() == *current_state->get_system_state()) {
+    if (state->get_system_state()->equals_to(*current_state->get_system_state(),
+                                             get_remote_app().get_remote_process_memory())) {
       XBT_INFO("Non-progressive cycle: state %ld -> state %ld", state->get_num(), current_state->get_num());
       XBT_INFO("******************************************");
       XBT_INFO("*** NON-PROGRESSIVE CYCLE DETECTED ***");
@@ -288,7 +289,7 @@ void DFSExplorer::backtrack()
     for (std::unique_ptr<State> const& state : stack_) {
       if (state == stack_.back()) /* If we are arrived on the target state, don't replay the outgoing transition */
         break;
-      state->get_transition()->replay();
+      state->get_transition()->replay(get_remote_app());
       on_transition_replay_signal(state->get_transition(), get_remote_app());
       visited_states_count_++;
     }
index 0e44b04..1dbe4c4 100644 (file)
@@ -18,9 +18,12 @@ namespace simgrid::mc {
 static simgrid::config::Flag<std::string> cfg_dot_output_file{
     "model-check/dot-output", "Name of dot output file corresponding to graph state", ""};
 
+Exploration* Exploration::instance_ = nullptr; // singleton instance
+
 Exploration::Exploration(const std::vector<char*>& args) : remote_app_(std::make_unique<RemoteApp>(args))
 {
-  mc_model_checker->set_exploration(this);
+  xbt_assert(instance_ == nullptr, "Cannot have more than one exploration instance");
+  instance_ = this;
 
   if (not cfg_dot_output_file.get().empty()) {
     dot_output_ = fopen(cfg_dot_output_file.get().c_str(), "w");
@@ -35,6 +38,7 @@ Exploration::~Exploration()
 {
   if (dot_output_ != nullptr)
     fclose(dot_output_);
+  instance_ = nullptr;
 }
 
 void Exploration::dot_output(const char* fmt, ...)
@@ -87,7 +91,6 @@ void Exploration::report_crash(int status)
     get_remote_app().get_remote_process_memory().dump_stack();
   }
 
-  get_remote_app().get_remote_process_memory().terminate();
   system_exit(SIMGRID_MC_EXIT_PROGRAM_CRASH);
 }
 void Exploration::report_assertion_failure()
index ee3c452..c027256 100644 (file)
@@ -29,6 +29,7 @@ namespace simgrid::mc {
 // abstract
 class Exploration : public xbt::Extendable<Exploration> {
   std::unique_ptr<RemoteApp> remote_app_;
+  static Exploration* instance_;
 
   FILE* dot_output_ = nullptr;
 
@@ -36,6 +37,7 @@ public:
   explicit Exploration(const std::vector<char*>& args);
   virtual ~Exploration();
 
+  static Exploration* get_instance() { return instance_; }
   // No copy:
   Exploration(Exploration const&) = delete;
   Exploration& operator=(Exploration const&) = delete;
index bd33237..7290930 100644 (file)
@@ -23,7 +23,7 @@ VisitedPair::VisitedPair(int pair_num, xbt_automaton_state_t prop_state,
                          RemoteApp& remote_app)
     : num(pair_num), prop_state_(prop_state)
 {
-  auto& memory     = mc_model_checker->get_remote_process_memory();
+  auto& memory     = remote_app.get_remote_process_memory();
   this->app_state_ = std::move(app_state);
   if (not this->app_state_->get_system_state())
     this->app_state_->set_system_state(std::make_shared<Snapshot>(pair_num, remote_app.get_page_store(), memory));
@@ -74,7 +74,8 @@ std::shared_ptr<VisitedPair> LivenessChecker::insert_acceptance_pair(simgrid::mc
       std::shared_ptr<simgrid::mc::VisitedPair> const& pair_test = *i;
       if (xbt_automaton_state_compare(pair_test->prop_state_, new_pair->prop_state_) != 0 ||
           *(pair_test->atomic_propositions) != *(new_pair->atomic_propositions) ||
-          (*pair_test->app_state_->get_system_state() != *new_pair->app_state_->get_system_state()))
+          (not pair_test->app_state_->get_system_state()->equals_to(*new_pair->app_state_->get_system_state(),
+                                                                    get_remote_app().get_remote_process_memory())))
         continue;
       XBT_INFO("Pair %d already reached (equal to pair %d) !", new_pair->num, pair_test->num);
       exploration_stack_.pop_back();
@@ -119,7 +120,7 @@ void LivenessChecker::replay()
     std::shared_ptr<State> state = pair->app_state_;
 
     if (pair->exploration_started) {
-      state->get_transition()->replay();
+      state->get_transition()->replay(get_remote_app());
       XBT_DEBUG("Replay (depth = %d) : %s (%p)", depth, state->get_transition()->to_string().c_str(), state.get());
     }
 
@@ -151,7 +152,8 @@ int LivenessChecker::insert_visited_pair(std::shared_ptr<VisitedPair> visited_pa
     const VisitedPair* pair_test = i->get();
     if (xbt_automaton_state_compare(pair_test->prop_state_, visited_pair->prop_state_) != 0 ||
         *(pair_test->atomic_propositions) != *(visited_pair->atomic_propositions) ||
-        (*pair_test->app_state_->get_system_state() != *visited_pair->app_state_->get_system_state()))
+        (not pair_test->app_state_->get_system_state()->equals_to(*visited_pair->app_state_->get_system_state(),
+                                                                  get_remote_app().get_remote_process_memory())))
       continue;
     if (pair_test->other_num == -1)
       visited_pair->other_num = pair_test->num;
index c21dc14..acd042d 100644 (file)
@@ -13,7 +13,6 @@
 #include "src/mc/mc_replay.hpp"
 
 #if SIMGRID_HAVE_MC
-#include "src/mc/ModelChecker.hpp"
 #include "src/mc/api/RemoteApp.hpp"
 #include "src/mc/remote/AppSide.hpp"
 #include "src/mc/sosp/RemoteProcessMemory.hpp"
index b9da6df..6922bd7 100644 (file)
@@ -15,7 +15,6 @@ namespace simgrid::mc {
 
 class PageStore;
 class ChunkedData;
-class ModelChecker;
 class AddressSpace;
 class RemoteProcessMemory;
 class Snapshot;
@@ -25,13 +24,9 @@ class Type;
 class Variable;
 class Transition;
 class Frame;
-class ActorInformation;
 
 class Session;
 class Exploration;
 } // namespace simgrid::mc
 
-// TODO, try to get rid of the global ModelChecker variable
-extern simgrid::mc::ModelChecker* mc_model_checker;
-
 #endif
index da1f649..fa270b1 100644 (file)
@@ -22,6 +22,7 @@ class Channel {
   template <class M> static constexpr bool messageType() { return std::is_class_v<M> && std::is_trivial_v<M>; }
 
 public:
+  Channel() = default;
   explicit Channel(int sock) : socket_(sock) {}
   ~Channel();
 
@@ -50,6 +51,7 @@ public:
   }
 
   int get_socket() const { return socket_; }
+  void reset_socket(int socket) { socket_ = socket; }
 };
 } // namespace simgrid::mc
 
index 0b735e8..bd7b16b 100644 (file)
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
 #include "src/mc/remote/CheckerSide.hpp"
-#include "src/mc/ModelChecker.hpp"
+#include "src/mc/explo/Exploration.hpp"
+#include "src/mc/explo/LivenessChecker.hpp"
 #include "src/mc/sosp/RemoteProcessMemory.hpp"
 #include "xbt/system_error.hpp"
+
+#ifdef __linux__
+#include <sys/personality.h>
+#include <sys/prctl.h>
+#endif
+
+#include <boost/tokenizer.hpp>
 #include <csignal>
+#include <fcntl.h>
+#include <sys/ptrace.h>
 #include <sys/wait.h>
 
+#ifdef __linux__
+#define WAITPID_CHECKED_FLAGS __WALL
+#else
+#define WAITPID_CHECKED_FLAGS 0
+#endif
+
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_checkerside, mc, "MC communication with the application");
 
+static simgrid::config::Flag<std::string> _sg_mc_setenv{
+    "model-check/setenv", "Extra environment variables to pass to the child process (ex: 'AZE=aze;QWE=qwe').", "",
+    [](std::string_view value) {
+      xbt_assert(value.empty() || value.find('=', 0) != std::string_view::npos,
+                 "The 'model-check/setenv' parameter must be like 'AZE=aze', but it does not contain an equal sign.");
+    }};
+
 namespace simgrid::mc {
-CheckerSide::CheckerSide(int sockfd, ModelChecker* mc) : channel_(sockfd)
+
+XBT_ATTRIB_NORETURN static void run_child_process(int socket, const std::vector<char*>& args)
+{
+  /* On startup, simix_global_init() calls simgrid::mc::Client::initialize(), which checks whether the MC_ENV_SOCKET_FD
+   * env variable is set. If so, MC mode is assumed, and the client is setup from its side
+   */
+
+#ifdef __linux__
+  // Make sure we do not outlive our parent
+  sigset_t mask;
+  sigemptyset(&mask);
+  xbt_assert(sigprocmask(SIG_SETMASK, &mask, nullptr) >= 0, "Could not unblock signals");
+  xbt_assert(prctl(PR_SET_PDEATHSIG, SIGHUP) == 0, "Could not PR_SET_PDEATHSIG");
+
+  // Make sure that the application process layout is not randomized, so that the info we gather is stable over re-execs
+  if (personality(ADDR_NO_RANDOMIZE) == -1) {
+    XBT_ERROR("Could not set the NO_RANDOMIZE personality");
+    throw xbt::errno_error();
+  }
+#endif
+
+  // Remove CLOEXEC to pass the socket to the application
+  int fdflags = fcntl(socket, F_GETFD, 0);
+  xbt_assert(fdflags != -1 && fcntl(socket, F_SETFD, fdflags & ~FD_CLOEXEC) != -1,
+             "Could not remove CLOEXEC for socket");
+
+  setenv(MC_ENV_SOCKET_FD, std::to_string(socket).c_str(), 1);
+
+  /* Setup the tokenizer that parses the cfg:model-check/setenv parameter */
+  using Tokenizer = boost::tokenizer<boost::char_separator<char>>;
+  boost::char_separator<char> semicol_sep(";");
+  boost::char_separator<char> equal_sep("=");
+  Tokenizer token_vars(_sg_mc_setenv.get(), semicol_sep); /* Iterate over all FOO=foo parts */
+  for (const auto& token : token_vars) {
+    std::vector<std::string> kv;
+    Tokenizer token_kv(token, equal_sep);
+    for (const auto& t : token_kv) /* Iterate over 'FOO' and then 'foo' in that 'FOO=foo' */
+      kv.push_back(t);
+    xbt_assert(kv.size() == 2, "Parse error on 'model-check/setenv' value %s. Does it contain an equal sign?",
+               token.c_str());
+    XBT_INFO("setenv '%s'='%s'", kv[0].c_str(), kv[1].c_str());
+    setenv(kv[0].c_str(), kv[1].c_str(), 1);
+  }
+
+  /* And now, exec the child process */
+  int i = 1;
+  while (args[i] != nullptr && args[i][0] == '-')
+    i++;
+
+  xbt_assert(args[i] != nullptr,
+             "Unable to find a binary to exec on the command line. Did you only pass config flags?");
+
+  execvp(args[i], args.data() + i);
+  XBT_CRITICAL("The model-checked process failed to exec(%s): %s.\n"
+               "        Make sure that your binary exists on disk and is executable.",
+               args[i], strerror(errno));
+  if (strchr(args[i], '=') != nullptr)
+    XBT_CRITICAL("If you want to pass environment variables to the application, please use --cfg=model-check/setenv:%s",
+                 args[i]);
+
+  xbt_die("Aborting now.");
+}
+
+static void wait_application_process(pid_t pid)
+{
+  XBT_DEBUG("Waiting for the model-checked process");
+  int status;
+
+  // The model-checked process SIGSTOP itself to signal it's ready:
+  xbt_assert(waitpid(pid, &status, WAITPID_CHECKED_FLAGS) == pid && WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP,
+             "Could not wait model-checked process");
+
+  errno = 0;
+#ifdef __linux__
+  ptrace(PTRACE_SETOPTIONS, pid, nullptr, PTRACE_O_TRACEEXIT);
+  ptrace(PTRACE_CONT, pid, 0, 0);
+#elif defined BSD
+  ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
+#else
+#error "no ptrace equivalent coded for this platform"
+#endif
+  xbt_assert(errno == 0,
+             "Ptrace does not seem to be usable in your setup (errno: %d). "
+             "If you run from within a docker, adding `--cap-add SYS_PTRACE` to the docker line may help. "
+             "If it does not help, please report this bug.",
+             errno);
+}
+
+void CheckerSide::setup_events()
 {
   auto* base = event_base_new();
   base_.reset(base);
@@ -31,7 +142,7 @@ CheckerSide::CheckerSide(int sockfd, ModelChecker* mc) : channel_(sockfd)
               throw simgrid::xbt::errno_error();
           }
 
-          if (not mc_model_checker->handle_message(buffer.data(), size))
+          if (not checker->handle_message(buffer.data(), size))
             checker->break_loop();
         } else {
           xbt_die("Unexpected event");
@@ -44,22 +155,48 @@ CheckerSide::CheckerSide(int sockfd, ModelChecker* mc) : channel_(sockfd)
   auto* signal_event = event_new(
       base, SIGCHLD, EV_SIGNAL | EV_PERSIST,
       [](evutil_socket_t sig, short events, void* arg) {
-        auto mc = static_cast<simgrid::mc::ModelChecker*>(arg);
+        auto checker = static_cast<simgrid::mc::CheckerSide*>(arg);
         if (events == EV_SIGNAL) {
           if (sig == SIGCHLD)
-            mc->handle_waitpid(mc->get_remote_process_memory().pid());
+            checker->handle_waitpid();
           else
             xbt_die("Unexpected signal: %d", sig);
         } else {
           xbt_die("Unexpected event");
         }
       },
-      mc);
+      this);
   event_add(signal_event, nullptr);
   signal_event_.reset(signal_event);
 }
 
-void CheckerSide::dispatch() const
+CheckerSide::CheckerSide(const std::vector<char*>& args) : running_(true)
+{
+  // Create an AF_LOCAL socketpair used for exchanging messages between the model-checker process (ancestor)
+  // and the application process (child)
+  int sockets[2];
+  xbt_assert(socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != -1, "Could not create socketpair");
+
+  pid_ = fork();
+  xbt_assert(pid_ >= 0, "Could not fork model-checked process");
+
+  if (pid_ == 0) { // Child
+    ::close(sockets[1]);
+    run_child_process(sockets[0], args);
+    DIE_IMPOSSIBLE;
+  }
+
+  // Parent (model-checker):
+  ::close(sockets[0]);
+  channel_.reset_socket(sockets[1]);
+
+  setup_events();
+  wait_application_process(pid_);
+
+  wait_for_requests();
+}
+
+void CheckerSide::dispatch_events() const
 {
   event_base_dispatch(base_.get());
 }
@@ -69,4 +206,152 @@ void CheckerSide::break_loop() const
   event_base_loopbreak(base_.get());
 }
 
+bool CheckerSide::handle_message(const char* buffer, ssize_t size)
+{
+  s_mc_message_t base_message;
+  xbt_assert(size >= (ssize_t)sizeof(base_message), "Broken message");
+  memcpy(&base_message, buffer, sizeof(base_message));
+
+  switch (base_message.type) {
+    case MessageType::INITIAL_ADDRESSES: {
+      s_mc_message_initial_addresses_t message;
+      xbt_assert(size == sizeof(message), "Broken message. Got %d bytes instead of %d.", (int)size,
+                 (int)sizeof(message));
+      memcpy(&message, buffer, sizeof(message));
+      /* Create the memory address space, now that we have the mandatory information */
+      remote_memory_ = std::make_unique<simgrid::mc::RemoteProcessMemory>(pid_, message.mmalloc_default_mdp);
+      break;
+    }
+
+    case MessageType::IGNORE_HEAP: {
+      s_mc_message_ignore_heap_t message;
+      xbt_assert(size == sizeof(message), "Broken message");
+      memcpy(&message, buffer, sizeof(message));
+
+      IgnoredHeapRegion region;
+      region.block    = message.block;
+      region.fragment = message.fragment;
+      region.address  = message.address;
+      region.size     = message.size;
+      get_remote_memory().ignore_heap(region);
+      break;
+    }
+
+    case MessageType::UNIGNORE_HEAP: {
+      s_mc_message_ignore_memory_t message;
+      xbt_assert(size == sizeof(message), "Broken message");
+      memcpy(&message, buffer, sizeof(message));
+      get_remote_memory().unignore_heap((void*)(std::uintptr_t)message.addr, message.size);
+      break;
+    }
+
+    case MessageType::IGNORE_MEMORY: {
+      s_mc_message_ignore_memory_t message;
+      xbt_assert(size == sizeof(message), "Broken message");
+      memcpy(&message, buffer, sizeof(message));
+      get_remote_memory().ignore_region(message.addr, message.size);
+      break;
+    }
+
+    case MessageType::STACK_REGION: {
+      s_mc_message_stack_region_t message;
+      xbt_assert(size == sizeof(message), "Broken message");
+      memcpy(&message, buffer, sizeof(message));
+      get_remote_memory().stack_areas().push_back(message.stack_region);
+    } break;
+
+    case MessageType::REGISTER_SYMBOL: {
+      s_mc_message_register_symbol_t message;
+      xbt_assert(size == sizeof(message), "Broken message");
+      memcpy(&message, buffer, sizeof(message));
+      xbt_assert(not message.callback, "Support for client-side function proposition is not implemented.");
+      XBT_DEBUG("Received symbol: %s", message.name.data());
+
+      LivenessChecker::automaton_register_symbol(get_remote_memory(), message.name.data(), remote((int*)message.data));
+      break;
+    }
+
+    case MessageType::WAITING:
+      return false;
+
+    case MessageType::ASSERTION_FAILED:
+      Exploration::get_instance()->report_assertion_failure();
+      break;
+
+    default:
+      xbt_die("Unexpected message from model-checked application");
+  }
+  return true;
+}
+
+void CheckerSide::wait_for_requests()
+{
+  /* Resume the application */
+  if (get_channel().send(MessageType::CONTINUE) != 0)
+    throw xbt::errno_error();
+  clear_memory_cache();
+
+  if (running())
+    dispatch_events();
+}
+
+void CheckerSide::clear_memory_cache()
+{
+  if (remote_memory_)
+    remote_memory_->clear_cache();
+}
+
+void CheckerSide::handle_waitpid()
+{
+  XBT_DEBUG("Check for wait event");
+  int status;
+  pid_t pid;
+  while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
+    if (pid == -1) {
+      if (errno == ECHILD) { // No more children:
+        xbt_assert(not this->running(), "Inconsistent state");
+        break;
+      } else {
+        XBT_ERROR("Could not wait for pid");
+        throw simgrid::xbt::errno_error();
+      }
+    }
+
+    if (pid == get_pid()) {
+      // From PTRACE_O_TRACEEXIT:
+#ifdef __linux__
+      if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) {
+        unsigned long eventmsg;
+        xbt_assert(ptrace(PTRACE_GETEVENTMSG, pid, 0, &eventmsg) != -1, "Could not get exit status");
+        status = static_cast<int>(eventmsg);
+        if (WIFSIGNALED(status)) {
+          this->terminate();
+          Exploration::get_instance()->report_crash(status);
+        }
+      }
+#endif
+
+      // We don't care about non-lethal signals, just reinject them:
+      if (WIFSTOPPED(status)) {
+        XBT_DEBUG("Stopped with signal %i", (int)WSTOPSIG(status));
+        errno = 0;
+#ifdef __linux__
+        ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status));
+#elif defined BSD
+        ptrace(PT_CONTINUE, pid, (caddr_t)1, WSTOPSIG(status));
+#endif
+        xbt_assert(errno == 0, "Could not PTRACE_CONT");
+      }
+
+      else if (WIFSIGNALED(status)) {
+        this->terminate();
+        Exploration::get_instance()->report_crash(status);
+      } else if (WIFEXITED(status)) {
+        XBT_DEBUG("Child process is over");
+        this->terminate();
+      }
+    }
+  }
+}
+
 } // namespace simgrid::mc
index fced9be..2461d33 100644 (file)
 
 namespace simgrid::mc {
 
+/* CheckerSide: All what the checker needs to interact with a given application process */
+
 class CheckerSide {
   std::unique_ptr<event_base, decltype(&event_base_free)> base_{nullptr, &event_base_free};
   std::unique_ptr<event, decltype(&event_free)> socket_event_{nullptr, &event_free};
   std::unique_ptr<event, decltype(&event_free)> signal_event_{nullptr, &event_free};
-
+  std::unique_ptr<RemoteProcessMemory> remote_memory_;
   Channel channel_;
 
+  bool running_ = false;
+  pid_t pid_;
+
+  void setup_events(); // Part of the initialization
+  void clear_memory_cache();
+  void handle_waitpid();
+
 public:
-  explicit CheckerSide(int sockfd, ModelChecker* mc);
+  explicit CheckerSide(const std::vector<char*>& args);
 
   // No copy:
   CheckerSide(CheckerSide const&) = delete;
   CheckerSide& operator=(CheckerSide const&) = delete;
   CheckerSide& operator=(CheckerSide&&) = delete;
 
+  /* Communicating with the application */
   Channel const& get_channel() const { return channel_; }
   Channel& get_channel() { return channel_; }
 
-  void dispatch() const;
+  bool handle_message(const char* buffer, ssize_t size);
+  void dispatch_events() const;
   void break_loop() const;
+  void wait_for_requests();
+
+  /* Interacting with the application process */
+  pid_t get_pid() const { return pid_; }
+  bool running() const { return running_; }
+  void terminate() { running_ = false; }
+  RemoteProcessMemory& get_remote_memory() { return *remote_memory_.get(); }
 };
 
 } // namespace simgrid::mc
index 538e12f..acbc080 100644 (file)
@@ -4,7 +4,6 @@
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
 #include "src/mc/sosp/Region.hpp"
-#include "src/mc/ModelChecker.hpp"
 #include "src/mc/mc_config.hpp"
 #include "src/mc/mc_forward.hpp"
 #include "src/mc/sosp/RemoteProcessMemory.hpp"
 
 namespace simgrid::mc {
 
-Region::Region(PageStore& store, RegionType region_type, void* start_addr, size_t size)
+Region::Region(PageStore& store, RemoteProcessMemory& memory, RegionType region_type, void* start_addr, size_t size)
     : region_type_(region_type), start_addr_(start_addr), size_(size)
 {
   xbt_assert((((uintptr_t)start_addr) & (xbt_pagesize - 1)) == 0, "Start address not at the beginning of a page");
 
-  chunks_ = ChunkedData(store, mc_model_checker->get_remote_process_memory(), RemotePtr<void>(start_addr),
-                        mmu::chunk_count(size));
+  chunks_ = ChunkedData(store, memory, RemotePtr<void>(start_addr), mmu::chunk_count(size));
 }
 
 /** @brief Restore a region from a snapshot
  *
  *  @param region     Target region
  */
-void Region::restore() const
+void Region::restore(RemoteProcessMemory& memory) const
 {
   xbt_assert(((start().address()) & (xbt_pagesize - 1)) == 0, "Not at the beginning of a page");
   xbt_assert(simgrid::mc::mmu::chunk_count(size()) == get_chunks().page_count());
@@ -38,7 +36,7 @@ void Region::restore() const
   for (size_t i = 0; i != get_chunks().page_count(); ++i) {
     auto* target_page       = (void*)simgrid::mc::mmu::join(i, (std::uintptr_t)(void*)start().address());
     const void* source_page = get_chunks().page(i);
-    mc_model_checker->get_remote_process_memory().write_bytes(source_page, xbt_pagesize, remote(target_page));
+    memory.write_bytes(source_page, xbt_pagesize, remote(target_page));
   }
 }
 
index 01af81c..cf7b748 100644 (file)
@@ -35,7 +35,7 @@ private:
   ChunkedData chunks_;
 
 public:
-  Region(PageStore& store, RegionType type, void* start_addr, size_t size);
+  Region(PageStore& store, RemoteProcessMemory& memory, RegionType type, void* start_addr, size_t size);
   Region(Region const&) = delete;
   Region& operator=(Region const&) = delete;
   Region(Region&& that)            = delete;
@@ -58,7 +58,7 @@ public:
   bool contain(RemotePtr<void> p) const { return p >= start() && p < end(); }
 
   /** @brief Restore a region from a snapshot */
-  void restore() const;
+  void restore(RemoteProcessMemory& memory) const;
 
   /** @brief Read memory that was snapshotted in this region
    *
index 2ebe51b..18093b3 100644 (file)
@@ -7,9 +7,12 @@
 
 #include "src/mc/sosp/RemoteProcessMemory.hpp"
 
+#include "src/mc/explo/Exploration.hpp"
+#include "src/mc/explo/LivenessChecker.hpp"
 #include "src/mc/sosp/Snapshot.hpp"
 #include "xbt/file.hpp"
 #include "xbt/log.h"
+#include "xbt/system_error.hpp"
 
 #include <fcntl.h>
 #include <libunwind-ptrace.h>
@@ -103,9 +106,7 @@ int open_vm(pid_t pid, int flags)
 
 // ***** RemoteProcessMemory
 
-RemoteProcessMemory::RemoteProcessMemory(pid_t pid) : AddressSpace(this), pid_(pid), running_(true) {}
-
-void RemoteProcessMemory::init(xbt_mheap_t mmalloc_default_mdp)
+RemoteProcessMemory::RemoteProcessMemory(pid_t pid, xbt_mheap_t mmalloc_default_mdp) : AddressSpace(this), pid_(pid)
 {
   this->heap_address = remote(mmalloc_default_mdp);
 
@@ -450,4 +451,5 @@ void RemoteProcessMemory::dump_stack() const
   _UPT_destroy(context);
   unw_destroy_addr_space(as);
 }
+
 } // namespace simgrid::mc
index bb2d04a..eeff932 100644 (file)
 
 namespace simgrid::mc {
 
-class ActorInformation {
-public:
-  /** MCed address of the process */
-  RemotePtr<kernel::actor::ActorImpl> address{nullptr};
-  Remote<kernel::actor::ActorImpl> copy;
-
-  /** Hostname (owned by `mc_model_checker->hostnames_`) */
-  const std::string* hostname = nullptr;
-  std::string name;
-
-  void clear()
-  {
-    name.clear();
-    address  = nullptr;
-    hostname = nullptr;
-  }
-};
-
 struct IgnoredRegion {
   std::uint64_t addr;
   std::size_t size;
@@ -50,14 +32,14 @@ struct IgnoredHeapRegion {
   std::size_t size;
 };
 
-/** The Application's process memory, seen from the Checker perspective
- *
- *  Responsibilities:
+/** The Application's process memory, seen from the Checker perspective. This class is not needed if you don't need to
+ * introspect the application process.
  *
- *  - reading from the process memory (`AddressSpace`);
- *  - accessing the system state of the process (heap, …);
- *  - stack unwinding;
- *  - etc.
+ *  Responsabilities:
+ *    - reading from the process memory (`AddressSpace`);
+ *    - accessing the system state of the process (heap, …);
+ *    - stack unwinding;
+ *    - etc.
  */
 class RemoteProcessMemory final : public AddressSpace {
 private:
@@ -68,9 +50,8 @@ private:
   static constexpr int cache_malloc = 2;
 
 public:
-  explicit RemoteProcessMemory(pid_t pid);
+  explicit RemoteProcessMemory(pid_t pid, xbt_mheap_t mmalloc_default_mdp);
   ~RemoteProcessMemory() override;
-  void init(xbt_mheap_t mmalloc_default_mdp);
 
   RemoteProcessMemory(RemoteProcessMemory const&)            = delete;
   RemoteProcessMemory(RemoteProcessMemory&&)                 = delete;
@@ -132,17 +113,11 @@ public:
   std::vector<IgnoredRegion> const& ignored_regions() const { return ignored_regions_; }
   void ignore_region(std::uint64_t address, std::size_t size);
 
-  pid_t pid() const { return pid_; }
-
   bool in_maestro_stack(RemotePtr<void> p) const
   {
     return p >= this->maestro_stack_start_ && p < this->maestro_stack_end_;
   }
 
-  bool running() const { return running_; }
-
-  void terminate() { running_ = false; }
-
   void ignore_global_variable(const char* name) const
   {
     for (std::shared_ptr<ObjectInformation> const& info : this->object_infos)
@@ -165,8 +140,7 @@ private:
   void refresh_heap();
   void refresh_malloc_info();
 
-  pid_t pid_    = -1;
-  bool running_ = false;
+  pid_t pid_ = -1;
   std::vector<xbt::VmMap> memory_map_;
   RemotePtr<void> maestro_stack_start_;
   RemotePtr<void> maestro_stack_end_;
index 7d9caba..fee842d 100644 (file)
@@ -13,19 +13,20 @@ namespace simgrid::mc {
 /************************************* Take Snapshot ************************************/
 /****************************************************************************************/
 
-void Snapshot::snapshot_regions(RemoteProcessMemory& process_memory)
+void Snapshot::snapshot_regions(RemoteProcessMemory& memory)
 {
   snapshot_regions_.clear();
 
-  for (auto const& object_info : process_memory.object_infos)
-    add_region(RegionType::Data, object_info.get(), object_info->start_rw, object_info->end_rw - object_info->start_rw);
+  for (auto const& object_info : memory.object_infos)
+    add_region(RegionType::Data, memory, object_info.get(), object_info->start_rw,
+               object_info->end_rw - object_info->start_rw);
 
-  const s_xbt_mheap_t* heap = process_memory.get_heap();
+  const s_xbt_mheap_t* heap = memory.get_heap();
   void* start_heap = heap->base;
   void* end_heap   = heap->breakval;
 
-  add_region(RegionType::Heap, nullptr, start_heap, (char*)end_heap - (char*)start_heap);
-  heap_bytes_used_ = mmalloc_get_bytes_used_remote(heap->heaplimit, process_memory.get_malloc_info());
+  add_region(RegionType::Heap, memory, nullptr, start_heap, (char*)end_heap - (char*)start_heap);
+  heap_bytes_used_ = mmalloc_get_bytes_used_remote(heap->heaplimit, memory.get_malloc_info());
 }
 
 /** @brief Checks whether the variable is in scope for a given IP.
@@ -47,7 +48,7 @@ static bool valid_variable(const simgrid::mc::Variable* var, simgrid::mc::Frame*
 }
 
 static void fill_local_variables_values(mc_stack_frame_t stack_frame, Frame* scope,
-                                        std::vector<s_local_variable_t>& result)
+                                        std::vector<s_local_variable_t>& result, AddressSpace* memory)
 {
   if (not scope || not scope->range.contain(stack_frame->ip))
     return;
@@ -72,9 +73,9 @@ static void fill_local_variables_values(mc_stack_frame_t stack_frame, Frame* sco
     if (current_variable.address != nullptr)
       new_var.address = current_variable.address;
     else if (not current_variable.location_list.empty()) {
-      dwarf::Location location = simgrid::dwarf::resolve(current_variable.location_list, current_variable.object_info,
-                                                         &(stack_frame->unw_cursor), (void*)stack_frame->frame_base,
-                                                         &mc_model_checker->get_remote_process_memory());
+      dwarf::Location location =
+          simgrid::dwarf::resolve(current_variable.location_list, current_variable.object_info,
+                                  &(stack_frame->unw_cursor), (void*)stack_frame->frame_base, memory);
 
       xbt_assert(location.in_memory(), "Cannot handle non-address variable");
       new_var.address = location.address();
@@ -86,20 +87,21 @@ static void fill_local_variables_values(mc_stack_frame_t stack_frame, Frame* sco
 
   // Recursive processing of nested scopes:
   for (Frame& nested_scope : scope->scopes)
-    fill_local_variables_values(stack_frame, &nested_scope, result);
+    fill_local_variables_values(stack_frame, &nested_scope, result, memory);
 }
 
-static std::vector<s_local_variable_t> get_local_variables_values(std::vector<s_mc_stack_frame_t>& stack_frames)
+static std::vector<s_local_variable_t> get_local_variables_values(std::vector<s_mc_stack_frame_t>& stack_frames,
+                                                                  AddressSpace* memory)
 {
   std::vector<s_local_variable_t> variables;
   for (s_mc_stack_frame_t& stack_frame : stack_frames)
-    fill_local_variables_values(&stack_frame, stack_frame.frame, variables);
+    fill_local_variables_values(&stack_frame, stack_frame.frame, variables, memory);
   return variables;
 }
 
-static std::vector<s_mc_stack_frame_t> unwind_stack_frames(UnwindContext* stack_context)
+static std::vector<s_mc_stack_frame_t> unwind_stack_frames(UnwindContext* stack_context,
+                                                           const RemoteProcessMemory* process_memory)
 {
-  const RemoteProcessMemory* process_memory = &mc_model_checker->get_remote_process_memory();
   std::vector<s_mc_stack_frame_t> result;
 
   unw_cursor_t c = stack_context->cursor();
@@ -160,8 +162,8 @@ void Snapshot::snapshot_stacks(RemoteProcessMemory& process_memory)
 
     st.context.initialize(process_memory, &context);
 
-    st.stack_frames    = unwind_stack_frames(&st.context);
-    st.local_variables = get_local_variables_values(st.stack_frames);
+    st.stack_frames    = unwind_stack_frames(&st.context, &process_memory);
+    st.local_variables = get_local_variables_values(st.stack_frames, &process_memory);
 
     unw_word_t sp = st.stack_frames[0].sp;
 
@@ -217,14 +219,15 @@ Snapshot::Snapshot(long num_state, PageStore& store, RemoteProcessMemory& memory
   ignore_restore();
 }
 
-void Snapshot::add_region(RegionType type, ObjectInformation* object_info, void* start_addr, std::size_t size)
+void Snapshot::add_region(RegionType type, RemoteProcessMemory& memory, ObjectInformation* object_info,
+                          void* start_addr, std::size_t size)
 {
   if (type == RegionType::Data)
     xbt_assert(object_info, "Missing object info for object.");
   else if (type == RegionType::Heap)
     xbt_assert(not object_info, "Unexpected object info for heap region.");
 
-  auto* region = new Region(page_store_, type, start_addr, size);
+  auto* region = new Region(page_store_, memory, type, start_addr, size);
   region->object_info(object_info);
   snapshot_regions_.push_back(std::unique_ptr<Region>(region));
 }
@@ -276,7 +279,7 @@ void Snapshot::restore(RemoteProcessMemory& memory) const
 
   // Restore regions
   for (std::unique_ptr<Region> const& region : snapshot_regions_) {
-    region->restore();
+    region->restore(memory);
   }
 
   ignore_restore();
index 6066335..04e871c 100644 (file)
@@ -6,7 +6,6 @@
 #ifndef SIMGRID_MC_SNAPSHOT_HPP
 #define SIMGRID_MC_SNAPSHOT_HPP
 
-#include "src/mc/ModelChecker.hpp"
 #include "src/mc/inspect/mc_unw.hpp"
 #include "src/mc/sosp/Region.hpp"
 #include "src/mc/sosp/RemoteProcessMemory.hpp"
@@ -77,8 +76,7 @@ public:
   Region* get_region(const void* addr, Region* hinted_region) const;
   void restore(RemoteProcessMemory& memory) const;
 
-  bool operator==(const Snapshot& other);
-  bool operator!=(const Snapshot& other) { return not(*this == other); }
+  bool equals_to(const Snapshot& other, RemoteProcessMemory& memory);
 
   // To be private
   long num_state_;
@@ -91,9 +89,10 @@ public:
   std::vector<s_mc_snapshot_ignored_data_t> ignored_data_;
 
 private:
-  void add_region(RegionType type, ObjectInformation* object_info, void* start_addr, std::size_t size);
-  void snapshot_regions(RemoteProcessMemory& process_memory);
-  void snapshot_stacks(RemoteProcessMemory& process_memory);
+  void add_region(RegionType type, RemoteProcessMemory& memory, ObjectInformation* object_info, void* start_addr,
+                  std::size_t size);
+  void snapshot_regions(RemoteProcessMemory& memory);
+  void snapshot_stacks(RemoteProcessMemory& memory);
   void handle_ignore();
   void ignore_restore() const;
   hash_type do_hash() const;
index 662c7a1..237065b 100644 (file)
@@ -16,6 +16,7 @@
 using simgrid::mc::Region;
 class snap_test_helper {
   static simgrid::mc::PageStore page_store_;
+  static std::unique_ptr<simgrid::mc::RemoteProcessMemory> memory_;
 
 public:
   static void init_memory(void* mem, size_t size);
@@ -34,18 +35,12 @@ public:
   static void compare_region_parts();
   static void read_pointer();
 
-  static void cleanup()
-  {
-    delete mc_model_checker;
-    mc_model_checker = nullptr;
-  }
-
-  static std::unique_ptr<simgrid::mc::RemoteProcessMemory> process;
+  static void cleanup() { memory_ = nullptr; }
 };
 
 // static member variables init.
-std::unique_ptr<simgrid::mc::RemoteProcessMemory> snap_test_helper::process = nullptr;
 simgrid::mc::PageStore snap_test_helper::page_store_(500);
+std::unique_ptr<simgrid::mc::RemoteProcessMemory> snap_test_helper::memory_ = nullptr;
 
 void snap_test_helper::init_memory(void* mem, size_t size)
 {
@@ -60,9 +55,7 @@ void snap_test_helper::Init()
   REQUIRE(xbt_pagesize == getpagesize());
   REQUIRE(1 << xbt_pagebits == xbt_pagesize);
 
-  process = std::make_unique<simgrid::mc::RemoteProcessMemory>(getpid());
-  process->init(nullptr);
-  mc_model_checker = new ::simgrid::mc::ModelChecker(std::move(process));
+  memory_ = std::make_unique<simgrid::mc::RemoteProcessMemory>(getpid(), nullptr);
 }
 
 snap_test_helper::prologue_return snap_test_helper::prologue(int n)
@@ -75,11 +68,12 @@ snap_test_helper::prologue_return snap_test_helper::prologue(int n)
 
   // Init memory and take snapshots:
   init_memory(source, byte_size);
-  auto* region0 = new simgrid::mc::Region(page_store_, simgrid::mc::RegionType::Data, source, byte_size);
+  auto* region0 =
+      new simgrid::mc::Region(page_store_, *memory_.get(), simgrid::mc::RegionType::Data, source, byte_size);
   for (int i = 0; i < n; i += 2) {
     init_memory((char*)source + i * xbt_pagesize, xbt_pagesize);
   }
-  auto* region = new simgrid::mc::Region(page_store_, simgrid::mc::RegionType::Data, source, byte_size);
+  auto* region = new simgrid::mc::Region(page_store_, *memory_.get(), simgrid::mc::RegionType::Data, source, byte_size);
 
   void* destination = mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
   INFO("Could not allocate destination memory");
@@ -157,13 +151,15 @@ void snap_test_helper::compare_region_parts()
   }
 }
 
+int some_global_variable  = 42;
+void* some_global_pointer = &some_global_variable;
 void snap_test_helper::read_pointer()
 {
   prologue_return ret = prologue(1);
-  memcpy(ret.src, &mc_model_checker, sizeof(void*));
-  const simgrid::mc::Region region2(page_store_, simgrid::mc::RegionType::Data, ret.src, ret.size);
+  memcpy(ret.src, &some_global_pointer, sizeof(void*));
+  const simgrid::mc::Region region2(page_store_, *memory_.get(), simgrid::mc::RegionType::Data, ret.src, ret.size);
   INFO("Mismtach in MC_region_read_pointer()");
-  REQUIRE(MC_region_read_pointer(&region2, ret.src) == mc_model_checker);
+  REQUIRE(MC_region_read_pointer(&region2, ret.src) == some_global_pointer);
 
   munmap(ret.dstn, ret.size);
   munmap(ret.src, ret.size);
index 1b6f8c3..f2ce4ab 100644 (file)
@@ -10,7 +10,6 @@
 #include <simgrid/config.h>
 
 #if SIMGRID_HAVE_MC
-#include "src/mc/ModelChecker.hpp"
 #include "src/mc/explo/Exploration.hpp"
 #include "src/mc/transition/TransitionActorJoin.hpp"
 #include "src/mc/transition/TransitionAny.hpp"
@@ -45,13 +44,13 @@ std::string Transition::dot_string() const
   return xbt::string_printf("label = \"[(%ld)] %s\", color = %s, fontcolor = %s", aid_, Transition::to_c_str(type_),
                             color, color);
 }
-void Transition::replay() const
+void Transition::replay(RemoteApp& app) const
 {
   replayed_transitions_++;
 
 #if SIMGRID_HAVE_MC
-  mc_model_checker->get_exploration()->get_remote_app().handle_simcall(aid_, times_considered_, false);
-  mc_model_checker->get_exploration()->get_remote_app().wait_for_requests();
+  app.handle_simcall(aid_, times_considered_, false);
+  app.wait_for_requests();
 #endif
 }
 
index 64175c1..17fb7e6 100644 (file)
@@ -66,7 +66,7 @@ public:
   virtual std::string dot_string() const;
 
   /* Moves the application toward a path that was already explored, but don't change the current transition */
-  void replay() const;
+  void replay(RemoteApp& app) const;
 
   virtual bool depends(const Transition* other) const { return true; }
 
index ef9874a..49331de 100644 (file)
@@ -8,7 +8,6 @@
 #include "xbt/asserts.h"
 #include "xbt/string.hpp"
 #if SIMGRID_HAVE_MC
-#include "src/mc/ModelChecker.hpp"
 #include "src/mc/api/RemoteApp.hpp"
 #include "src/mc/api/State.hpp"
 #endif
index 20f28d7..0f10bb1 100644 (file)
@@ -8,7 +8,6 @@
 #include "xbt/asserts.h"
 #include "xbt/string.hpp"
 #if SIMGRID_HAVE_MC
-#include "src/mc/ModelChecker.hpp"
 #include "src/mc/api/RemoteApp.hpp"
 #include "src/mc/api/State.hpp"
 #endif
index 512519b..376c301 100644 (file)
@@ -8,7 +8,6 @@
 #include "xbt/asserts.h"
 #include "xbt/string.hpp"
 #if SIMGRID_HAVE_MC
-#include "src/mc/ModelChecker.hpp"
 #include "src/mc/api/RemoteApp.hpp"
 #include "src/mc/api/State.hpp"
 #endif
index 2e50663..cccdce3 100644 (file)
@@ -148,8 +148,7 @@ static void test_deref(simgrid::dwarf::ExpressionContext const& state)
 
 int main()
 {
-  auto* process = new simgrid::mc::RemoteProcessMemory(getpid());
-  process->init(nullptr);
+  auto* process = new simgrid::mc::RemoteProcessMemory(getpid(), nullptr);
 
   simgrid::dwarf::ExpressionContext state;
   state.address_space = (simgrid::mc::AddressSpace*) process;
index b564b1c..aa86545 100644 (file)
@@ -122,8 +122,7 @@ int main(int argc, char** argv)
   const simgrid::mc::Variable* var;
   simgrid::mc::Type* type;
 
-  simgrid::mc::RemoteProcessMemory process(getpid());
-  process.init(nullptr);
+  simgrid::mc::RemoteProcessMemory process(getpid(), nullptr);
 
   test_global_variable(process, process.binary_info.get(), "some_local_variable", &some_local_variable, sizeof(int));
 
index be3a556..528ed02 100644 (file)
@@ -598,16 +598,17 @@ set(MC_SRC
   src/mc/transition/TransitionSynchro.cpp
   src/mc/transition/TransitionSynchro.hpp
 
-  src/mc/AddressSpace.hpp
-  src/mc/ModelChecker.cpp
-  src/mc/ModelChecker.hpp
-  src/mc/VisitedState.cpp
-  src/mc/VisitedState.hpp
+  src/mc/api/guide/BasicGuide.hpp
+  src/mc/api/guide/GuidedState.hpp
   src/mc/api/ActorState.hpp
   src/mc/api/State.cpp
   src/mc/api/State.hpp
   src/mc/api/RemoteApp.cpp
   src/mc/api/RemoteApp.hpp
+
+  src/mc/AddressSpace.hpp
+  src/mc/VisitedState.cpp
+  src/mc/VisitedState.hpp
   src/mc/compare.cpp
   src/mc/mc_exit.hpp
   src/mc/mc_forward.hpp
@@ -1054,6 +1055,7 @@ set(CMAKE_SOURCE_FILES
   tools/cmake/Modules/FindNS3.cmake
   tools/cmake/Modules/FindPAPI.cmake
   tools/cmake/Modules/FindValgrind.cmake
+  tools/cmake/Modules/nlohmann_jsonConfig.cmake
   tools/cmake/Modules/pybind11Config.cmake
   tools/cmake/Option.cmake
   tools/cmake/Tests.cmake