Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[mc] Remote unwinding support
authorGabriel Corona <gabriel.corona@loria.fr>
Mon, 19 Jan 2015 14:54:13 +0000 (15:54 +0100)
committerGabriel Corona <gabriel.corona@loria.fr>
Fri, 30 Jan 2015 09:19:44 +0000 (10:19 +0100)
The contexts are still read directly from the current process memory
however.

16 files changed:
buildtools/Cmake/CompleteInFiles.cmake
buildtools/Cmake/DefinePackages.cmake
buildtools/Cmake/MakeLib.cmake
buildtools/Cmake/PrintArgs.cmake
buildtools/Cmake/src/internal_config.h.in
src/include/mc/datatypes.h
src/mc/mc_address_space.h
src/mc/mc_checkpoint.c
src/mc/mc_global.c
src/mc/mc_location.h
src/mc/mc_process.c
src/mc/mc_process.h
src/mc/mc_snapshot.h
src/mc/mc_unw.c [new file with mode: 0644]
src/mc/mc_unw.h [new file with mode: 0644]
src/mc/mc_unw_vmread.c [new file with mode: 0644]

index 9600214..b1ba41f 100644 (file)
@@ -172,6 +172,7 @@ CHECK_FUNCTION_EXISTS(asprintf HAVE_ASPRINTF)
 CHECK_FUNCTION_EXISTS(vasprintf HAVE_VASPRINTF)
 CHECK_FUNCTION_EXISTS(makecontext HAVE_MAKECONTEXT)
 CHECK_FUNCTION_EXISTS(mmap HAVE_MMAP)
+CHECK_FUNCTION_EXISTS(process_vm_readv HAVE_PROCESS_VM_READV)
 
 #Check if __thread is defined
 execute_process(
index 79bc327..cb222a5 100644 (file)
@@ -599,6 +599,9 @@ set(MC_SRC
   src/mc/mc_forward.h
   src/mc/mc_process.h
   src/mc/mc_process.c
+  src/mc/mc_unw.h
+  src/mc/mc_unw.c
+  src/mc/mc_unw_vmread.c
   src/mc/mc_mmalloc.h
   src/mc/mc_model_checker.h
   src/mc/mc_object_info.h
index 9518d4c..be1613f 100644 (file)
@@ -101,7 +101,7 @@ if(HAVE_MC)
   # The availability of libunwind was checked in CompleteInFiles.cmake
   #   (that includes FindLibunwind.cmake), so simply load it now.
   
-  SET(SIMGRID_DEP "${SIMGRID_DEP} -lunwind")
+  SET(SIMGRID_DEP "${SIMGRID_DEP} -lunwind -lunwind-ptrace")
 
   # Same for libdw
   SET(SIMGRID_DEP "${SIMGRID_DEP} -ldw")
index 5180926..9805a49 100644 (file)
@@ -62,6 +62,7 @@ if(enable_print_message)
   message("HAVE_ASPRINTF ...............: ${HAVE_ASPRINTF}")
   message("HAVE_VASPRINTF ..............: ${HAVE_VASPRINTF}")
   message("HAVE_MMAP ...................: ${HAVE_MMAP}")
+  message("HAVE_PROCESS_VM_READV .......: ${HAVE_PROCESS_VM_READV}")
   message("HAVE_THREAD_LOCAL_STORAGE ...: ${HAVE_THREAD_LOCAL_STORAGE}")
   message("HAVE_MMALLOC ................: ${HAVE_MMALLOC}")
   message("")
index 76f05f8..577178a 100644 (file)
 /* Define to 1 if mmap is available */
 #cmakedefine HAVE_MMAP @HAVE_MMAP@
 
+/* Define to 1 if process_vm_readv is available */
+#cmakedefine HAVE_PROCESS_VM_READV @HAVE_PROCESS_VM_READV@
+
 /* Define to 1 if you have the `getdtablesize' function. */
 #cmakedefine HAVE_GETDTABLESIZE @HAVE_GETDTABLESIZE@
 
index 20805a8..728bb0e 100644 (file)
@@ -7,8 +7,6 @@
 #ifndef MC_DATATYPE_H
 #define MC_DATATYPE_H
 
-#define UNW_LOCAL_ONLY
-
 #include "xbt/misc.h"
 #include "xbt/swag.h"
 #include "xbt/fifo.h"
index 49fb7d7..a8724bc 100644 (file)
@@ -49,7 +49,7 @@ typedef struct s_mc_address_space_class s_mc_address_space_class_t, *mc_address_
  *  It uses dynamic dispatch based on a vtable (`address_space_class`).
  */
 struct s_mc_address_space {
-  mc_address_space_class_t address_space_class;
+  const s_mc_address_space_class_t* address_space_class;
 };
 
 /** Class object (vtable) for the virtual address spaces
@@ -59,6 +59,7 @@ struct s_mc_address_space_class {
     mc_address_space_t address_space, e_adress_space_read_flags_t flags,
     void* target, const void* addr, size_t size,
     int process_index);
+  mc_process_t (*get_process)(mc_address_space_t address_space);
 };
 
 // ***** Virtual/non-final methods
@@ -78,4 +79,11 @@ const void* MC_address_space_read(
     process_index);
 }
 
+static inline __attribute__((always_inline))
+const void* MC_address_space_get_process(mc_address_space_t address_space)
+{
+  return address_space->address_space_class->get_process(address_space);
+}
+
+
 #endif
index fd6418d..bce4bbd 100644 (file)
@@ -5,7 +5,6 @@
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
 #define _GNU_SOURCE
-#define UNW_LOCAL_ONLY
 
 #include <unistd.h>
 
@@ -25,7 +24,6 @@
 
 #include "../simix/smx_private.h"
 
-#define UNW_LOCAL_ONLY
 #include <libunwind.h>
 #include <libelf.h>
 
@@ -35,6 +33,7 @@
 #include "mc_snapshot.h"
 #include "mc_object_info.h"
 #include "mc_mmu.h"
+#include "mc_unw.h"
 
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_checkpoint, mc,
                                 "Logging specific to mc_checkpoint");
@@ -47,13 +46,16 @@ static void MC_snapshot_stack_free(mc_snapshot_stack_t s)
   if (s) {
     xbt_dynar_free(&(s->local_variables));
     xbt_dynar_free(&(s->stack_frames));
+    mc_unw_destroy_context(s->context);
+    xbt_free(s->context);
     xbt_free(s);
   }
 }
 
 static void MC_snapshot_stack_free_voidp(void *s)
 {
-  MC_snapshot_stack_free((mc_snapshot_stack_t) * (void **) s);
+  mc_snapshot_stack_t stack = (mc_snapshot_stack_t) * (void **) s;
+  MC_snapshot_stack_free(stack);
 }
 
 static void local_variable_free(local_variable_t v)
@@ -452,7 +454,7 @@ static void MC_stack_frame_free_voipd(void *s)
   }
 }
 
-static xbt_dynar_t MC_unwind_stack_frames(unw_context_t* stack_context)
+static xbt_dynar_t MC_unwind_stack_frames(mc_unw_context_t stack_context)
 {
   mc_process_t process = &mc_model_checker->process;
   xbt_dynar_t result =
@@ -461,8 +463,7 @@ static xbt_dynar_t MC_unwind_stack_frames(unw_context_t* stack_context)
   unw_cursor_t c;
 
   // TODO, check condition check (unw_init_local==0 means end of frame)
-  // FIXME, cross-process support
-  if (unw_init_local(&c, stack_context) != 0) {
+  if (mc_unw_init_cursor(&c, stack_context) != 0) {
 
     xbt_die("Could not initialize stack unwinding");
 
@@ -501,11 +502,11 @@ static xbt_dynar_t MC_unwind_stack_frames(unw_context_t* stack_context)
           && !strcmp(frame->name, "smx_ctx_sysv_wrapper"))
         break;
 
-      int ret = ret = unw_step(&c);
+      int ret = unw_step(&c);
       if (ret == 0) {
         xbt_die("Unexpected end of stack.");
       } else if (ret < 0) {
-        xbt_die("Error while unwinding stack.");
+        xbt_die("Error while unwinding stack");
       }
     }
 
@@ -531,21 +532,15 @@ static xbt_dynar_t MC_take_snapshot_stacks(mc_snapshot_t * snapshot)
   xbt_dynar_foreach(stacks_areas, cursor, current_stack) {
     mc_snapshot_stack_t st = xbt_new(s_mc_snapshot_stack_t, 1);
 
-    // Take a copy of the context for our own purpose:
-    st->context = *(unw_context_t*)current_stack->context;
-#if defined(PROCESSOR_x86_64) || defined(PROCESSOR_i686)
-    // On x86_64, ucontext_t contains a pointer to itself for FP registers.
-    // We don't really need support for FR registers as they are caller saved
-    // and probably never use those fields as libunwind-x86_64 does not read
-    // FP registers from the unw_context_t
-    // but we fix the pointer in order to avoid dangling pointers:
-    st->context.uc_mcontext.fpregs = &st->context.__fpregs_mem;
-#else
-    // Do we need to do any fixup like this?
-    #error Target CPU type is not handled.
-#endif
+    unw_context_t* original_context = (unw_context_t*) current_stack->context;
+
+    st->context = xbt_new0(s_mc_unw_context_t, 1);
+    if (mc_unw_init_context(st->context, &mc_model_checker->process,
+      original_context) < 0) {
+      xbt_die("Could not initialise the libunwind context.");
+    }
 
-    st->stack_frames = MC_unwind_stack_frames(&st->context);
+    st->stack_frames = MC_unwind_stack_frames(st->context);
     st->local_variables = MC_get_local_variables_values(st->stack_frames, current_stack->process_index);
     st->process_index = current_stack->process_index;
 
index fabab0a..0401708 100644 (file)
@@ -20,7 +20,6 @@
 #include "xbt/dict.h"
 
 #ifdef HAVE_MC
-#define UNW_LOCAL_ONLY
 #include <libunwind.h>
 
 #include "../xbt/mmalloc/mmprivate.h"
 #include "mc_snapshot.h"
 #include "mc_liveness.h"
 #include "mc_private.h"
+#include "mc_unw.h"
 #endif
 #include "mc_record.h"
 
+
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_global, mc,
                                 "Logging specific to MC (global)");
 
index c624cf2..5c450ca 100644 (file)
@@ -9,7 +9,6 @@
 
 #include <stdint.h>
 
-#define UNW_LOCAL_ONLY
 #include <libunwind.h>
 #include <dwarf.h>
 #include <elfutils/libdw.h>
index 207efa9..b4aace4 100644 (file)
 
 #include <libgen.h>
 
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+
 #include "mc_process.h"
 #include "mc_object_info.h"
 #include "mc_address_space.h"
+#include "mc_unw.h"
 
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_process, mc,
                                 "MC process information");
@@ -24,10 +28,20 @@ XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_process, mc,
 static void MC_process_init_memory_map_info(mc_process_t process);
 static void MC_process_open_memory_file(mc_process_t process);
 
-static s_mc_address_space_class_t mc_process_class = {
-  .read = (void*) &MC_process_read
+static mc_process_t MC_process_get_process(mc_process_t p) {
+  return p;
+}
+
+static const s_mc_address_space_class_t mc_process_class = {
+  .read = (void*) &MC_process_read,
+  .get_process = (void*) MC_process_get_process
 };
 
+bool MC_is_process(mc_address_space_t p)
+{
+  return p->address_space_class == &mc_process_class;
+}
+
 void MC_process_init(mc_process_t process, pid_t pid)
 {
   process->address_space.address_space_class = &mc_process_class;
@@ -52,6 +66,15 @@ void MC_process_init(mc_process_t process, pid_t pid)
   MC_process_read(process, MC_ADDRESS_SPACE_READ_FLAGS_NONE,
     &process->heap_address, std_heap_var->address, sizeof(struct mdesc*),
     MC_PROCESS_INDEX_DISABLED);
+
+  process->unw_addr_space = unw_create_addr_space(&mc_unw_accessors  , __BYTE_ORDER);
+  if (process->process_flags & MC_PROCESS_SELF_FLAG) {
+    process->unw_underlying_addr_space = unw_local_addr_space;
+    process->unw_underlying_context = NULL;
+  } else {
+    process->unw_underlying_addr_space = unw_create_addr_space(&mc_unw_vmread_accessors, __BYTE_ORDER);
+    process->unw_underlying_context = _UPT_create(pid);
+  }
 }
 
 void MC_process_clear(mc_process_t process)
@@ -77,6 +100,16 @@ void MC_process_clear(mc_process_t process)
     close(process->memory_file);
   }
 
+  if (process->unw_underlying_addr_space != unw_local_addr_space) {
+    unw_destroy_addr_space(process->unw_underlying_addr_space);
+    _UPT_destroy(process->unw_underlying_context);
+  }
+  process->unw_underlying_context = NULL;
+  process->unw_underlying_addr_space = NULL;
+
+  unw_destroy_addr_space(process->unw_addr_space);
+  process->unw_addr_space = NULL;
+
   process->cache_flags = 0;
 
   free(process->heap);
@@ -134,6 +167,7 @@ const char* FILTERED_LIBS[] = {
   "libunwind",
   "libunwind-x86_64",
   "libunwind-x86",
+  "libunwind-ptrace",
   "libdw",
   "libdl",
   "librt",
@@ -333,23 +367,26 @@ dw_variable_t MC_process_find_variable_by_name(mc_process_t process, const char*
 
 // ***** Memory access
 
-static void MC_process_open_memory_file(mc_process_t process)
+int MC_process_vm_open(pid_t pid, int flags)
 {
-  if (MC_process_is_self(process) || process->memory_file >= 0)
-    return;
-
   const size_t buffer_size = 30;
   char buffer[buffer_size];
-  int res = snprintf(buffer, buffer_size, "/proc/%lli/mem", (long long) process->pid);
-  if (res < 0 || res>= buffer_size) {
-    XBT_ERROR("Could not open memory file descriptor for process %lli",
-      (long long) process->pid);
-    return;
+  int res = snprintf(buffer, buffer_size, "/proc/%lli/mem", (long long) pid);
+  if (res < 0 || res >= buffer_size) {
+    errno = ENAMETOOLONG;
+    return -1;
   }
+  return open(buffer, flags);
+}
+
+static void MC_process_open_memory_file(mc_process_t process)
+{
+  if (MC_process_is_self(process) || process->memory_file >= 0)
+    return;
 
-  int fd = open(buffer, O_RDWR);
+  int fd = MC_process_vm_open(process->pid, O_RDWR);
   if (fd<0)
-    xbt_die("Could not initialise memory access for remote process");
+    xbt_die("Could not open file for process virtual address space");
   process->memory_file = fd;
 }
 
index 9825fed..f5fd69f 100644 (file)
@@ -8,6 +8,7 @@
 #define MC_PROCESS_H
 
 #include <stdbool.h>
+#include <sys/types.h>
 
 #include "simgrid_config.h"
 
@@ -25,6 +26,8 @@
 
 SG_BEGIN_DECL()
 
+int MC_process_vm_open(pid_t pid, int flags);
+
 typedef enum {
   MC_PROCESS_NO_FLAG = 0,
   MC_PROCESS_SELF_FLAG = 1,
@@ -75,8 +78,31 @@ struct s_mc_process {
    *  use `MC_process_get_malloc_info` in order to use it.
    */
   malloc_info* heap_info;
+
+  // ***** Libunwind-data
+
+  /** Full-featured MC-aware libunwind address space for the process
+   *
+   *  This address space is using a mc_unw_context_t
+   *  (with mc_process_t/mc_address_space_t and unw_context_t).
+   */
+  unw_addr_space_t unw_addr_space;
+
+  /** Underlying libunwind addres-space
+   *
+   *  The `find_proc_info`, `put_unwind_info`, `get_dyn_info_list_addr`
+   *  operations of the native MC address space is currently delegated
+   *  to this address space (either the local or a ptrace unwinder).
+   */
+  unw_addr_space_t unw_underlying_addr_space;
+
+  /** The corresponding context
+   */
+  void* unw_underlying_context;
 };
 
+bool MC_is_process(mc_address_space_t p);
+
 void MC_process_init(mc_process_t process, pid_t pid);
 void MC_process_clear(mc_process_t process);
 
index 3daae1b..70b37f8 100644 (file)
@@ -20,6 +20,7 @@
 #include "mc_page_store.h"
 #include "mc_mmalloc.h"
 #include "mc_address_space.h"
+#include "mc_unw.h"
 
 SG_BEGIN_DECL()
 
@@ -239,7 +240,7 @@ typedef struct s_mc_stack_frame {
 
 typedef struct s_mc_snapshot_stack{
   xbt_dynar_t local_variables;
-  unw_context_t context;
+  mc_unw_context_t context;
   xbt_dynar_t stack_frames; // mc_stack_frame_t
   int process_index;
 }s_mc_snapshot_stack_t, *mc_snapshot_stack_t;
diff --git a/src/mc/mc_unw.c b/src/mc/mc_unw.c
new file mode 100644 (file)
index 0000000..ee67176
--- /dev/null
@@ -0,0 +1,238 @@
+/* Copyright (c) 2015. 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. */
+
+/** \file
+ *  Libunwind support for mc_address_space objects.
+ */
+
+// We need this for the register indices:
+#define _GNU_SOURCE
+
+#include <string.h>
+
+// On x86_64, libunwind unw_context_t has the same layout as ucontext_t:
+#include <sys/ucontext.h>
+
+#include <libunwind.h>
+
+#include "mc_object_info.h"
+#include "mc_process.h"
+#include "mc_unw.h"
+
+// ***** Implementation
+
+/** Get frame unwind information (libunwind method)
+ *
+ *  Delegates to the local/ptrace implementation.
+ */
+static int find_proc_info(unw_addr_space_t as,
+              unw_word_t ip, unw_proc_info_t *pip,
+              int need_unwind_info, void* arg)
+{
+  mc_unw_context_t context = (mc_unw_context_t) arg;
+  return unw_get_accessors(context->process->unw_underlying_addr_space)->find_proc_info(
+    context->process->unw_underlying_addr_space, ip, pip,
+    need_unwind_info, context->process->unw_underlying_context
+  );
+}
+
+/** Release frame unwind information (libunwind method)
+ *
+ *  Delegates to the local/ptrace implementation.
+ */
+static void put_unwind_info(unw_addr_space_t as,
+              unw_proc_info_t *pip, void* arg)
+{
+  mc_unw_context_t context = (mc_unw_context_t) arg;
+  return unw_get_accessors(context->process->unw_underlying_addr_space)->put_unwind_info(
+    context->process->unw_underlying_addr_space, pip,
+    context->process->unw_underlying_context
+  );
+}
+
+/** (libunwind method)
+ *
+ *  Not implemented.
+ */
+static int get_dyn_info_list_addr(unw_addr_space_t as,
+              unw_word_t *dilap, void* arg)
+{
+  mc_unw_context_t context = (mc_unw_context_t) arg;
+  return unw_get_accessors(context->process->unw_underlying_addr_space)->get_dyn_info_list_addr(
+    context->process->unw_underlying_addr_space,
+    dilap,
+    context->process->unw_underlying_context
+  );
+}
+
+/** Read from the target address space memory (libunwind method)
+ *
+ *  Delegates to the `mc_process_t`.
+ */
+static int access_mem(unw_addr_space_t as,
+              unw_word_t addr, unw_word_t *valp,
+              int write, void* arg)
+{
+  mc_unw_context_t context = (mc_unw_context_t) arg;
+  if (write)
+    return -UNW_EREADONLYREG;
+  MC_address_space_read(context->address_space,
+    0, valp, (void*) addr, sizeof(unw_word_t), MC_PROCESS_INDEX_ANY);
+  // We don't handle failure gracefully.
+  return 0;
+}
+
+static void* get_reg(unw_context_t* context, unw_regnum_t regnum)
+{
+#ifdef __x86_64
+  mcontext_t* mcontext = &context->uc_mcontext;
+  switch (regnum) {
+  case UNW_X86_64_RAX: return &mcontext->gregs[REG_RAX];
+  case UNW_X86_64_RDX: return &mcontext->gregs[REG_RDX];
+  case UNW_X86_64_RCX: return &mcontext->gregs[REG_RCX];
+  case UNW_X86_64_RBX: return &mcontext->gregs[REG_RBX];
+  case UNW_X86_64_RSI: return &mcontext->gregs[REG_RSI];
+  case UNW_X86_64_RDI: return &mcontext->gregs[REG_RDI];
+  case UNW_X86_64_RBP: return &mcontext->gregs[REG_RBP];
+  case UNW_X86_64_RSP: return &mcontext->gregs[REG_RSP];
+  case UNW_X86_64_R8:  return &mcontext->gregs[REG_R8];
+  case UNW_X86_64_R9:  return &mcontext->gregs[REG_R9];
+  case UNW_X86_64_R10: return &mcontext->gregs[REG_R10];
+  case UNW_X86_64_R11: return &mcontext->gregs[REG_R11];
+  case UNW_X86_64_R12: return &mcontext->gregs[REG_R12];
+  case UNW_X86_64_R13: return &mcontext->gregs[REG_R13];
+  case UNW_X86_64_R14: return &mcontext->gregs[REG_R14];
+  case UNW_X86_64_R15: return &mcontext->gregs[REG_R15];
+  case UNW_X86_64_RIP: return &mcontext->gregs[REG_RIP];
+  default: return NULL;
+  }
+#else
+  return NULL;
+#endif
+}
+
+/** Read a standard register (libunwind method)
+ */
+static int access_reg(unw_addr_space_t as,
+              unw_regnum_t regnum, unw_word_t *valp,
+              int write, void* arg)
+{
+  mc_unw_context_t as_context = (mc_unw_context_t) arg;
+  unw_context_t* context = &as_context->context;
+  if (write)
+    return -UNW_EREADONLYREG;
+  greg_t* preg = get_reg(context, regnum);
+  if (!preg)
+    return -UNW_EBADREG;
+  *valp = *preg;
+  return 0;
+}
+
+/** Read a floating-point register (libunwind method)
+ *
+ *  FP registers are caller-saved. The values saved by functions such as
+ *  `getcontext()` is not relevant for the caller. It is not really necessary
+ *  to save and handle them.
+ */
+static int access_fpreg(unw_addr_space_t as,
+              unw_regnum_t regnum, unw_fpreg_t *fpvalp,
+              int write, void* arg)
+{
+  return -UNW_EBADREG;
+}
+
+/** Resume the execution of the context (libunwind method)
+ *
+ * We don't use this.
+ */
+static int resume(unw_addr_space_t as,
+              unw_cursor_t *cp, void* arg)
+{
+  return -UNW_EUNSPEC;
+}
+
+/** Find informations about a function (libunwind method)
+ */
+static int get_proc_name(unw_addr_space_t as,
+              unw_word_t addr, char *bufp,
+              size_t buf_len, unw_word_t *offp,
+              void* arg)
+{
+  mc_unw_context_t context = (mc_unw_context_t) arg;
+  dw_frame_t frame = MC_process_find_function(context->process, (void*) addr);
+  if (!frame)
+    return - UNW_ENOINFO;
+  *offp = (unw_word_t) frame->low_pc - addr;
+
+  strncpy(bufp, frame->name, buf_len);
+  if (bufp[buf_len - 1]) {
+    bufp[buf_len - 1] = 0;
+    return -UNW_ENOMEM;
+  }
+
+  return 0;
+}
+
+// ***** Init
+
+unw_accessors_t mc_unw_accessors =
+  {
+    .find_proc_info             = &find_proc_info,
+    .put_unwind_info            = &put_unwind_info,
+    .get_dyn_info_list_addr     = &get_dyn_info_list_addr,
+    .access_mem                 = &access_mem,
+    .access_reg                 = &access_reg,
+    .access_fpreg               = &access_fpreg,
+    .resume                     = &resume,
+    .get_proc_name              = &get_proc_name
+  };
+
+// ***** Context management
+
+int mc_unw_init_context(
+  mc_unw_context_t context, mc_process_t process, unw_context_t* c)
+{
+  context->address_space = (mc_address_space_t) process;
+  context->process = process;
+
+  // Take a copy of the context for our own purpose:
+  context->context = *c;
+#if defined(PROCESSOR_x86_64) || defined(PROCESSOR_i686)
+  // On x86_64, ucontext_t contains a pointer to itself for FP registers.
+  // We don't really need support for FR registers as they are caller saved
+  // and probably never use those fields as libunwind-x86_64 does not read
+  // FP registers from the unw_context_t
+  // but we fix the pointer in order to avoid dangling pointers:
+  context->context.uc_mcontext.fpregs = &(context->context.__fpregs_mem);
+#else
+  // Do we need to do any fixup like this?
+  #error Target CPU type is not handled.
+#endif
+
+  return 0;
+}
+
+int mc_unw_destroy_context(mc_unw_context_t context)
+{
+  context->address_space = NULL;
+  context->process = NULL;
+  return 0;
+}
+
+// ***** Cursor management
+
+int mc_unw_init_cursor(unw_cursor_t *cursor, mc_unw_context_t context)
+{
+  if (!context->process || !context->address_space)
+    return -UNW_EUNSPEC;
+  mc_address_space_t as = context->address_space;
+
+  // Use local unwinding for current process:
+  if (MC_is_process(as) && MC_process_is_self((mc_process_t) as))
+    return unw_init_local(cursor, &context->context);
+
+  return unw_init_remote(cursor, context->process->unw_addr_space, context);
+}
diff --git a/src/mc/mc_unw.h b/src/mc/mc_unw.h
new file mode 100644 (file)
index 0000000..267c850
--- /dev/null
@@ -0,0 +1,82 @@
+/* Copyright (c) 2015. 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 MC_UNW_H
+#define MC_UNW_H
+
+/** \file
+ *  Libunwind implementation for the model-checker
+ *
+ *  Libunwind provides an pluggable stack unwinding API: the way the current
+ *  registers and memory is accessed, the way unwinding informations is found
+ *  is pluggable.
+ *
+ *  This component implements the libunwind API for he model-checker:
+ *
+ *    * reading memory from a mc_address_space_t;
+ *
+ *    * reading stack registers from a saved snapshot (context).
+ *
+ *  Parts of the libunwind information fetching is currently handled by the
+ *  standard `libunwind` implementations (either the local one or the ptrace one)
+ *  because parsing `.eh_frame` section is not fun and `libdw` does not help
+ *  much here.
+ */
+
+#include "mc_process.h"
+
+SG_BEGIN_DECL()
+
+// ***** Libunwind namespace
+
+/** Virtual table for our `libunwind-process_vm_readv` implementation.
+ *
+ *  This implementation reuse most the code of `libunwind-ptrace` but
+ *  does not use ptrace() to read the target process memory by
+ *  `process_vm_readv()` or `/dev/${pid}/mem` if possible.
+ *
+ *  Does not support any MC-specific behaviour (privatisation, snapshots)
+ *  and `ucontext_t`.
+ *
+ *  It works with `void*` contexts allocated with `_UPT_create(pid)`.
+ */
+extern unw_accessors_t mc_unw_vmread_accessors;
+
+/** Virtual table for our `libunwind` implementation
+ *
+ *  Stack unwinding on a `mc_process_t` (for memory, unwinding information)
+ *  and `ucontext_t` (for processor registers).
+ *
+ *  It works with the `s_mc_unw_context_t` context.
+ */
+extern unw_accessors_t mc_unw_accessors;
+
+// ***** Libunwind context
+
+/** A `libunwind` context
+ */
+typedef struct s_mc_unw_context {
+  mc_address_space_t address_space;
+  mc_process_t       process;
+  unw_context_t      context;
+} s_mc_unw_context_t, *mc_unw_context_t;
+
+/** Initialises an already allocated context */
+int mc_unw_init_context(
+  mc_unw_context_t context, mc_process_t process, unw_context_t* c);
+
+/** Destroys (but not not `free`) a context */
+int mc_unw_destroy_context(mc_unw_context_t context);
+
+// ***** Libunwind cursor
+
+/** Initialises a `libunwind` cursor */
+int mc_unw_init_cursor(unw_cursor_t *cursor, mc_unw_context_t context);
+
+SG_END_DECL()
+
+#endif
diff --git a/src/mc/mc_unw_vmread.c b/src/mc/mc_unw_vmread.c
new file mode 100644 (file)
index 0000000..742359e
--- /dev/null
@@ -0,0 +1,108 @@
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+
+#include "mc_unw.h"
+
+/** \file
+ *  Libunwind namespace implementation using process_vm_readv.
+ *.
+ *  This implem
+ */
+
+/** Partial structure of libunwind-ptrace context in order to get the PID
+ *
+ *  The context type for libunwind-race is an opaque type. We need to get the
+ *  PID which is the first field. This is a hack which might break if the
+ *  libunwind-ptrace structure changes.
+ */
+struct _UPT_info {
+  pid_t pid;
+  // Other things;
+};
+
+/** Get the PID of a `libunwind-ptrace` context
+ */
+static inline
+pid_t _UPT_getpid(void* arg)
+{
+  struct _UPT_info* info = arg;
+  return info->pid;
+}
+
+/** Read from the memory, avoid using `ptrace` (libunwind method)
+ */
+static int access_mem(const unw_addr_space_t as,
+              const unw_word_t addr, unw_word_t* const  valp,
+              const int write, void* const arg)
+{
+  if (write)
+    return - UNW_EINVAL;
+  ssize_t s;
+  pid_t pid = _UPT_getpid(arg);
+  size_t size = sizeof(unw_word_t);
+
+#ifdef HAVE_PROCESS_VM_READV
+  // process_vm_read implementation.
+  // This is only available since Linux 3.2.
+
+  struct iovec local = { valp, size };
+  struct iovec remote = { (void*) addr, size };
+  s = process_vm_readv(pid, &local, 1, &remote, 1, 0);
+  if (s >= 0) {
+    if (s != size)
+      return - UNW_EINVAL;
+    else
+      return 0;
+  }
+  if (s < 0 && errno != ENOSYS)
+    return - UNW_EINVAL;
+#endif
+
+  // /proc/${pid}/mem implementation.
+  // On recent kernels, we do not need to ptrace the target process.
+  // On older kernels, it is necessary to ptrace the target process.
+  size_t count = size;
+  off_t off = (off_t) addr;
+  char* buf = (char*) valp;
+  int fd = MC_process_vm_open(pid, O_RDONLY);
+  if (fd < 0)
+    return - UNW_EINVAL;
+  while (1) {
+    ssize_t s = pread(fd, buf, count, off);
+    if (s == 0) {
+      close(fd);
+      return - UNW_EINVAL;
+    }
+    if (s == -1)
+      break;
+    count -= s;
+    buf += s;
+    off += s;
+    if (count == 0) {
+      close(fd);
+      return 0;
+    }
+  }
+  close(fd);
+
+  // ptrace implementation.
+  // We need to have PTRACE_ATTACH-ed it before.
+  return _UPT_access_mem(as, addr, valp, write, arg);
+}
+
+unw_accessors_t mc_unw_vmread_accessors =
+  {
+    .find_proc_info             = &_UPT_find_proc_info,
+    .put_unwind_info            = &_UPT_put_unwind_info,
+    .get_dyn_info_list_addr     = &_UPT_get_dyn_info_list_addr,
+    .access_mem                 = &access_mem,
+    .access_reg                 = &_UPT_access_reg,
+    .access_fpreg               = &_UPT_access_fpreg,
+    .resume                     = &_UPT_resume,
+    .get_proc_name              = &_UPT_get_proc_name
+  };