Source/PLCrashAsyncThread.h (74 lines of code) (raw):

/* * Author: Landon Fuller <landonf@plausiblelabs.com> * * Copyright (c) 2008-2013 Plausible Labs Cooperative, Inc. * All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef PLCRASH_ASYNC_THREAD_H #define PLCRASH_ASYNC_THREAD_H #ifdef __cplusplus extern "C" { #endif #include <sys/ucontext.h> #include "PLCrashAsync.h" #include <Availability.h> /** * @internal * @ingroup plcrash_async_thread * @{ */ /* Configure supported targets based on the host build architecture. There's currently * no deployed architecture on which simultaneous support for different processor families * is required (or supported), but -- in theory -- such cross-architecture support could be * enabled by modifying these defines. */ #if defined(__i386__) || defined(__x86_64__) /** Defined if x86-64 and x86-32 thread states are supported by the PLCrashReporter thread state API. */ #define PLCRASH_ASYNC_THREAD_X86_SUPPORT 1 #include <mach/i386/thread_state.h> /** Host architecture mcontext_t type. */ typedef _STRUCT_MCONTEXT pl_mcontext_t; /** Host architecture ucontext_t type. */ typedef _STRUCT_UCONTEXT pl_ucontext_t; #endif #if defined(__arm__) || defined(__arm64__) #include <mach/arm/thread_state.h> /** Host architecture mcontext_t type. */ typedef _STRUCT_MCONTEXT pl_mcontext_t; /** Host architecture ucontext_t type. */ typedef _STRUCT_UCONTEXT pl_ucontext_t; /** Defined if ARM thread states are supported by the PLCrashReporter thread state API. */ #define PLCRASH_ASYNC_THREAD_ARM_SUPPORT 1 #endif /** * Stack growth direction. */ typedef enum { /** The stack grows upwards on this platform. */ PLCRASH_ASYNC_THREAD_STACK_DIRECTION_UP = 1, /** The stack grows downwards on this platform. */ PLCRASH_ASYNC_THREAD_STACK_DIRECTION_DOWN = 2 } plcrash_async_thread_stack_direction_t; /** * @internal * * Target-neutral thread-state. * * The thread state maintains a set of valid registers; this may be used to implement delta * updates of threads' state, or otherwise express partial thread states, eg, when unwinding * a stack and not all registers can be restored. */ typedef struct plcrash_async_thread_state { /** Stack growth direction */ plcrash_async_thread_stack_direction_t stack_direction; /** General purpose register size, in bytes */ size_t greg_size; /** The set of available registers. */ uint64_t valid_regs; /* Union used to hold thread state for any supported architecture */ union { #ifdef PLCRASH_ASYNC_THREAD_ARM_SUPPORT /** Combined ARM 32/64 thread state */ struct { /** ARM thread state */ arm_unified_thread_state_t thread; } arm_state; #endif #ifdef PLCRASH_ASYNC_THREAD_X86_SUPPORT /** Combined x86 32/64 thread state */ struct { /** Thread state */ x86_thread_state_t thread; /** Exception state. */ x86_exception_state_t exception; } x86_state; #endif }; } plcrash_async_thread_state_t; /** Register number type */ typedef uint32_t plcrash_regnum_t; /** * General pseudo-registers common across platforms. * * Platform registers must be allocated starting at a 0 index, with no breaks. The following pseudo-register * values must be assigned to the corresponding platform register values (or in the case of the invalid register, * the constant value must be left unused). */ typedef enum { /** Instruction pointer */ PLCRASH_REG_IP = 0, /** Frame pointer */ PLCRASH_REG_FP = 1, /** Stack pointer */ PLCRASH_REG_SP = 2, /** * Invalid register. This value must not be assigned to a platform register. */ PLCRASH_REG_INVALID = INT32_MAX } plcrash_gen_regnum_t; #include "PLCrashAsyncThread_x86.h" #include "PLCrashAsyncThread_arm.h" /** Platform word type */ typedef plcrash_pdef_greg_t plcrash_greg_t; plcrash_error_t plcrash_async_thread_state_init (plcrash_async_thread_state_t *thread_state, cpu_type_t cpu_type); void plcrash_async_thread_state_mcontext_init (plcrash_async_thread_state_t *thread_state, pl_mcontext_t *mctx); plcrash_error_t plcrash_async_thread_state_mach_thread_init (plcrash_async_thread_state_t *thread_state, thread_t thread); /** * Callback function called by plcrash_log_writer_write_curthread(). * * @param state The thread state fetched by plcrash_log_writer_write_curthread(). * @param context Caller-provided context value. */ typedef plcrash_error_t (*plcrash_async_thread_state_current_callback)(plcrash_async_thread_state_t *state, void *context); /** * Fetch the calling thread's state and pass it to the given @a callback. * * @param callback Function to be called with the calling thread's state. * @param context Context value to be passed to @a callback. * * @note This is implemented with an assembly trampoline that fetches the current thread state to be passed to the @a callback. Solutions such * as getcontext() are not viable here, as returning from getcontext() mutates the state of the stack. */ plcrash_error_t plcrash_async_thread_state_current (plcrash_async_thread_state_current_callback callback, void *context); void plcrash_async_thread_state_copy (plcrash_async_thread_state_t *dest, const plcrash_async_thread_state_t *src); plcrash_async_thread_stack_direction_t plcrash_async_thread_state_get_stack_direction (const plcrash_async_thread_state_t *thread_state); size_t plcrash_async_thread_state_get_greg_size (const plcrash_async_thread_state_t *thread_state); bool plcrash_async_thread_state_has_reg (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum); void plcrash_async_thread_state_clear_reg (plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum); void plcrash_async_thread_state_clear_all_regs (plcrash_async_thread_state_t *thread_state); /* Platform specific funtions */ /** * Get a register's name. */ char const *plcrash_async_thread_state_get_reg_name (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum); /** * Get the total number of registers supported by @a thread_state. * * @param thread_state The target thread state. */ size_t plcrash_async_thread_state_get_reg_count (const plcrash_async_thread_state_t *thread_state); /** * Get a register value. */ plcrash_greg_t plcrash_async_thread_state_get_reg (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum); /** * Set a register value. */ void plcrash_async_thread_state_set_reg (plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum, plcrash_greg_t reg); /** * Clear all non-callee saved volatile registers in @a thread_state. The exact registers preserved depend on the target ABI. * * @param thread_state The thread state to clear. */ void plcrash_async_thread_state_clear_volatile_regs (plcrash_async_thread_state_t *thread_state); /** * Map a plcrash_regnum_t to its corresponding DWARF register value. Returns true if a mapping is available * for @a regnum, or false if no DWARF register value is available for @a regnum. * * @warning This API may require changes in the future to support specifying the register mapping type; eg, DWARF debug_frame * vs eh_frame, or similar. * * * @param thread_state The thread state to be used for performing the mapping. * @param regnum The register number to be mapped. * @param[out] dwarf_reg The mapped DWARF register value. */ bool plcrash_async_thread_state_map_reg_to_dwarf (plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum, uint64_t *dwarf_reg); /** * Map a DWARF register number to its plcrash_regnum_t representation. Returns true if a mapping is available * for @a dwarf_reg, or false if the mapping for @a dwarf_reg is unknown. * * @warning This API may require changes in the future to support specifying the register mapping type; eg, DWARF debug_frame * vs eh_frame, or similar. * * @param thread_state The thread state to be used for performing the mapping. * @param dwarf_reg The DWARF register number to be mapped. * @param[out] regnum The mapped register number. */ bool plcrash_async_thread_state_map_dwarf_to_reg (const plcrash_async_thread_state_t *thread_state, uint64_t dwarf_reg, plcrash_regnum_t *regnum); /* * @} */ #ifdef __cplusplus } #endif #endif /* PLCRASH_ASYNC_THREAD_H */