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(
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
# 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")
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("")
/* 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@
#ifndef MC_DATATYPE_H
#define MC_DATATYPE_H
-#define UNW_LOCAL_ONLY
-
#include "xbt/misc.h"
#include "xbt/swag.h"
#include "xbt/fifo.h"
* 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
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
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
* under the terms of the license (GNU LGPL) which comes with this package. */
#define _GNU_SOURCE
-#define UNW_LOCAL_ONLY
#include <unistd.h>
#include "../simix/smx_private.h"
-#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <libelf.h>
#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");
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)
}
}
-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 =
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");
&& !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");
}
}
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;
#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)");
#include <stdint.h>
-#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <dwarf.h>
#include <elfutils/libdw.h>
#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");
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;
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)
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);
"libunwind",
"libunwind-x86_64",
"libunwind-x86",
+ "libunwind-ptrace",
"libdw",
"libdl",
"librt",
// ***** 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;
}
#define MC_PROCESS_H
#include <stdbool.h>
+#include <sys/types.h>
#include "simgrid_config.h"
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,
* 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);
#include "mc_page_store.h"
#include "mc_mmalloc.h"
#include "mc_address_space.h"
+#include "mc_unw.h"
SG_BEGIN_DECL()
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;
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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
--- /dev/null
+#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
+ };