From 208547c8a823a2df7a6e945697aacd2a7a38cbaf Mon Sep 17 00:00:00 2001 From: Gabriel Corona Date: Mon, 19 Jan 2015 15:54:13 +0100 Subject: [PATCH] [mc] Remote unwinding support The contexts are still read directly from the current process memory however. --- buildtools/Cmake/CompleteInFiles.cmake | 1 + buildtools/Cmake/DefinePackages.cmake | 3 + buildtools/Cmake/MakeLib.cmake | 2 +- buildtools/Cmake/PrintArgs.cmake | 1 + buildtools/Cmake/src/internal_config.h.in | 3 + src/include/mc/datatypes.h | 2 - src/mc/mc_address_space.h | 10 +- src/mc/mc_checkpoint.c | 39 ++-- src/mc/mc_global.c | 3 +- src/mc/mc_location.h | 1 - src/mc/mc_process.c | 63 ++++-- src/mc/mc_process.h | 26 +++ src/mc/mc_snapshot.h | 3 +- src/mc/mc_unw.c | 238 ++++++++++++++++++++++ src/mc/mc_unw.h | 82 ++++++++ src/mc/mc_unw_vmread.c | 108 ++++++++++ 16 files changed, 543 insertions(+), 42 deletions(-) create mode 100644 src/mc/mc_unw.c create mode 100644 src/mc/mc_unw.h create mode 100644 src/mc/mc_unw_vmread.c diff --git a/buildtools/Cmake/CompleteInFiles.cmake b/buildtools/Cmake/CompleteInFiles.cmake index 9600214c05..b1ba41ffd8 100644 --- a/buildtools/Cmake/CompleteInFiles.cmake +++ b/buildtools/Cmake/CompleteInFiles.cmake @@ -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( diff --git a/buildtools/Cmake/DefinePackages.cmake b/buildtools/Cmake/DefinePackages.cmake index 79bc327587..cb222a5bf7 100644 --- a/buildtools/Cmake/DefinePackages.cmake +++ b/buildtools/Cmake/DefinePackages.cmake @@ -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 diff --git a/buildtools/Cmake/MakeLib.cmake b/buildtools/Cmake/MakeLib.cmake index 9518d4c9a6..be1613f8f4 100644 --- a/buildtools/Cmake/MakeLib.cmake +++ b/buildtools/Cmake/MakeLib.cmake @@ -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") diff --git a/buildtools/Cmake/PrintArgs.cmake b/buildtools/Cmake/PrintArgs.cmake index 518092640a..9805a49fc1 100644 --- a/buildtools/Cmake/PrintArgs.cmake +++ b/buildtools/Cmake/PrintArgs.cmake @@ -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("") diff --git a/buildtools/Cmake/src/internal_config.h.in b/buildtools/Cmake/src/internal_config.h.in index 76f05f8540..577178acd6 100644 --- a/buildtools/Cmake/src/internal_config.h.in +++ b/buildtools/Cmake/src/internal_config.h.in @@ -132,6 +132,9 @@ /* 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@ diff --git a/src/include/mc/datatypes.h b/src/include/mc/datatypes.h index 20805a8549..728bb0e5a1 100644 --- a/src/include/mc/datatypes.h +++ b/src/include/mc/datatypes.h @@ -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" diff --git a/src/mc/mc_address_space.h b/src/mc/mc_address_space.h index 49fb7d79aa..a8724bc35a 100644 --- a/src/mc/mc_address_space.h +++ b/src/mc/mc_address_space.h @@ -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 diff --git a/src/mc/mc_checkpoint.c b/src/mc/mc_checkpoint.c index fd6418d1ff..bce4bbd5c6 100644 --- a/src/mc/mc_checkpoint.c +++ b/src/mc/mc_checkpoint.c @@ -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 @@ -25,7 +24,6 @@ #include "../simix/smx_private.h" -#define UNW_LOCAL_ONLY #include #include @@ -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; diff --git a/src/mc/mc_global.c b/src/mc/mc_global.c index fabab0ae63..04017085e3 100644 --- a/src/mc/mc_global.c +++ b/src/mc/mc_global.c @@ -20,7 +20,6 @@ #include "xbt/dict.h" #ifdef HAVE_MC -#define UNW_LOCAL_ONLY #include #include "../xbt/mmalloc/mmprivate.h" @@ -32,9 +31,11 @@ #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)"); diff --git a/src/mc/mc_location.h b/src/mc/mc_location.h index c624cf2cf2..5c450caae7 100644 --- a/src/mc/mc_location.h +++ b/src/mc/mc_location.h @@ -9,7 +9,6 @@ #include -#define UNW_LOCAL_ONLY #include #include #include diff --git a/src/mc/mc_process.c b/src/mc/mc_process.c index 207efa9c83..b4aace4fde 100644 --- a/src/mc/mc_process.c +++ b/src/mc/mc_process.c @@ -14,9 +14,13 @@ #include +#include +#include + #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; } diff --git a/src/mc/mc_process.h b/src/mc/mc_process.h index 9825fed414..f5fd69f976 100644 --- a/src/mc/mc_process.h +++ b/src/mc/mc_process.h @@ -8,6 +8,7 @@ #define MC_PROCESS_H #include +#include #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); diff --git a/src/mc/mc_snapshot.h b/src/mc/mc_snapshot.h index 3daae1b479..70b37f8b6e 100644 --- a/src/mc/mc_snapshot.h +++ b/src/mc/mc_snapshot.h @@ -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 index 0000000000..ee671769fb --- /dev/null +++ b/src/mc/mc_unw.c @@ -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 + +// On x86_64, libunwind unw_context_t has the same layout as ucontext_t: +#include + +#include + +#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 index 0000000000..267c850ca8 --- /dev/null +++ b/src/mc/mc_unw.h @@ -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 index 0000000000..742359ef3a --- /dev/null +++ b/src/mc/mc_unw_vmread.c @@ -0,0 +1,108 @@ +#define _GNU_SOURCE + +#include +#include + +#include +#include + +#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 + }; -- 2.20.1