diff --git a/include/libpst.h b/include/libpst.h index 29ae5f0..d861505 100644 --- a/include/libpst.h +++ b/include/libpst.h @@ -17,33 +17,51 @@ typedef struct pst_handler pst_handler; -// allocate libpst handler and initialize the library -pst_handler* pst_lib_init(ucontext_t* hctx); +/** + * @brief allocate libpst handler and initialize the library + * @param context context of process given to signal handler of a program. If NULL, then we are not in signal handler + * @param buff pointer to buffer by libpst custom allocator(i.e. no malloc() will beused). if NULL, then standard allocator will be used + * @param size of 'buff' + * + * @return pointer to handler to be used for all other operations. NULL in case of error + */ +pst_handler* pst_lib_init(ucontext_t* context, void* buff, uint32_t size); -// deallocate libpst handler and deinitialize the library -void pst_lib_fini(pst_handler* h); +/** + * @brief deallocate libpst handler and deinitialize the library + * @param handler library handler previously initialized by 'pst_lib_init()' + */ +void pst_lib_fini(pst_handler* handler); -// -// Base unwind routines. -// Unwinds current stack trace using base information about program (i.e. print out address, function name) -// +/** + * @defgroup unwind_simple + * @brief Unwinds current stack trace using base information about program (i.e. function address, function name and line) + */ -// write stack trace information to provided file descriptor -int pst_unwind_simple_fd(pst_handler* h, FILE* fd); +/** + * @ingroup unwind_simple + * @brief Save stack trace information to provided buffer in RAM + * @param handler The handler obtained by pst_lib_init() + * @return 1 on success, 0 on failure + */ +int pst_unwind_simple(pst_handler* handler); -// save stack trace information to provided buffer in RAM -int pst_unwind_simple(pst_handler* h, char* buff, uint32_t buff_size); +/** + * @ingroup unwind_simple + * @brief Print unwound stack trace to internal buffer + * @param handler The handler obtained by pst_lib_init() + * @return pointer on zero terminated C string, NULL on failure + */ +const char* pst_print_simple(pst_handler* handler); // // Advanced unwind routines. // Additionally to pst_unwind_simple(...) provides types of parameters and variables in functions and their values if possible // -// write stack trace information to provided file descriptor -int pst_unwind_pretty_fd(pst_handler* h, FILE* fd); - // save stack trace information to provided buffer in RAM -int pst_unwind_pretty(pst_handler* h, char* buff, uint32_t buff_size); +int pst_unwind_pretty(pst_handler* h); +const char* pst_print_pretty(pst_handler* h); #endif /* __LIBPST_EXTERNAL_H_ */ diff --git a/src/dwarf/dwarf_function.c b/src/dwarf/dwarf_function.c index d077b51..5b95597 100644 --- a/src/dwarf/dwarf_function.c +++ b/src/dwarf/dwarf_function.c @@ -72,6 +72,7 @@ static bool get_frame(pst_function* fn) fn->ctx->print_expr(fn->ctx, cfa_ops, cfa_nops, NULL); + bool nret = true; pst_decl(pst_dwarf_stack, stack, fn->ctx); if(pst_dwarf_stack_calc(&stack, cfa_ops, cfa_nops, NULL, NULL) && pst_dwarf_stack_get_value(&stack, &fn->cfa)) { pst_log(SEVERITY_INFO, "Function %s(...): CFA expression: %s ==> %#lX", fn->name, fn->ctx->buff, fn->cfa); @@ -79,12 +80,13 @@ static bool get_frame(pst_function* fn) // setup context to match CFA for frame fn->ctx->cfa = fn->cfa; } else { + nret = false; pst_log(SEVERITY_ERROR, "Failed to calculate CFA expression"); } pst_dwarf_stack_fini(&stack); - return true; + return nret; } static pst_parameter* add_param(pst_function* fn) @@ -189,16 +191,12 @@ bool handle_lexical_block(pst_function* fn, Dwarf_Die* result) bool pst_function_print_dwarf(pst_function* fn) { char* at = NULL; - if(!asprintf(&at, " at %s:%d, %p", fn->file, fn->line, (void*)fn->pc)) { + if(fn->file) { + if(!asprintf(&at, " at %s:%d, %p", fn->file, fn->line, (void*)fn->pc)) + return false; + } else if(!asprintf(&at, " at %p", (void*)fn->pc)){ return false; } - if(at[4] == ':' && at[5] == '-') { - free(at); - if(!asprintf(&at, " at %p", (void*)fn->pc)) { - return false; - } - } - //ctx->print(ctx, "%s:%d: ", file.c_str(), line); // handle return parameter and be safe if function haven't parameters (for example, dwar info for function is absent) pst_parameter* param = next_param(fn, NULL); @@ -244,7 +242,7 @@ bool pst_function_print_dwarf(pst_function* fn) } if(!start_variable) { - fn->ctx->print(fn->ctx, ");%s\n", at); + fn->ctx->print(fn->ctx, ")%s\n", at); } else { fn->ctx->print(fn->ctx, "}\n"); } @@ -388,12 +386,7 @@ bool pst_function_unwind(pst_function* fn) file = filename; } fn->file = pst_strdup(file); - fn->ctx->print(fn->ctx, "%s:%d", fn->file, fn->line); - } else { - fn->ctx->print(fn->ctx, "%p", (void*)fn->pc); } - } else { - fn->ctx->print(fn->ctx, "%p", (void*)fn->pc); } Dwfl_Module* module = dwfl_addrmodule(fn->ctx->dwfl, fn->pc); @@ -405,7 +398,6 @@ bool pst_function_unwind(pst_function* fn) pst_log(SEVERITY_ERROR, "Failed to allocate memory"); return false; } - fn->ctx->print(fn->ctx, " --> %s", function_name); char* str = strchr(function_name, '('); if(str) { diff --git a/src/dwarf/dwarf_handler.c b/src/dwarf/dwarf_handler.c index ed01dac..ecbd57f 100644 --- a/src/dwarf/dwarf_handler.c +++ b/src/dwarf/dwarf_handler.c @@ -156,7 +156,7 @@ bool pst_handler_handle_dwarf(pst_handler* h) return true; } -void pst_handler_print_dwarf(pst_handler* h) +const char* pst_print_pretty(pst_handler* h) { h->ctx.clean_print(&h->ctx); h->ctx.print(&h->ctx, "DWARF-based stack trace information:\n"); @@ -166,6 +166,8 @@ void pst_handler_print_dwarf(pst_handler* h) pst_function_print_dwarf(fn); h->ctx.print(&h->ctx, "\n"); } + + return h->ctx.buff; } char *debuginfo_path = NULL; @@ -177,53 +179,76 @@ Dwfl_Callbacks callbacks = { }; #include +const char* pst_print_simple(pst_handler* h) +{ + h->ctx.clean_print(&h->ctx); + h->ctx.print(&h->ctx, "Simple unwind-based stack trace:\n"); + uint32_t idx = 0; + for(pst_function* fn = next_function(h, NULL); fn; fn = next_function(h, fn)) { + h->ctx.print(&h->ctx, "[%-2d] ", idx); + h->ctx.print(fn->ctx, "%s() ", fn->name); + if(fn->file) { + h->ctx.print(fn->ctx, "at %s:%d, %p", fn->file, fn->line, (void*)fn->pc); + } else { + h->ctx.print(fn->ctx, "at %p", (void*)fn->pc); + } + h->ctx.print(&h->ctx, "\n"); + idx++; + } + + return h->ctx.buff; +} + bool pst_handler_unwind(pst_handler* h) { - h->ctx.clean_print(&h->ctx); - void* caller; // pointer to the function which requested to unwind stack + void* caller = NULL; // pointer to the function which requested to unwind stack -#ifdef REG_RIP // x86_64 - caller = (void *) h->ctx.hcontext->uc_mcontext.gregs[REG_RIP]; - h->ctx.sp = h->ctx.hcontext->uc_mcontext.gregs[REG_RSP]; - pst_log(SEVERITY_DEBUG, "Original caller's SP: %#lX", h->ctx.sp); -#elif defined(REG_EIP) // x86_32 - caller = (void *) uctx->uc_mcontext.gregs[REG_EIP]); -#elif defined(__arm__) - caller = (void *) uctx->uc_mcontext.arm_pc); -#elif defined(__aarch64__) - caller = (void *) uctx->uc_mcontext.pc); -#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) - caller = (void *) uctx->uc_mcontext.regs->nip); -#elif defined(__s390x__) - caller = (void *) uctx->uc_mcontext.psw.addr); -#elif defined(__APPLE__) && defined(__x86_64__) - caller = (void *) uctx->uc_mcontext->__ss.__rip); -#else -# error "unknown architecture!" -#endif + if(h->ctx.hcontext) { + #ifdef REG_RIP // x86_64 + caller = (void *) h->ctx.hcontext->uc_mcontext.gregs[REG_RIP]; + h->ctx.sp = h->ctx.hcontext->uc_mcontext.gregs[REG_RSP]; + pst_log(SEVERITY_DEBUG, "Original caller's SP: %#lX", h->ctx.sp); + #elif defined(REG_EIP) // x86_32 + caller = (void *) h->uc_mcontext.gregs[REG_EIP]); + #elif defined(__arm__) + caller = (void *) h->uc_mcontext.arm_pc); + #elif defined(__aarch64__) + caller = (void *) h->uc_mcontext.pc); + #elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) + caller = (void *) h->uc_mcontext.regs->nip); + #elif defined(__s390x__) + caller = (void *) h->uc_mcontext.psw.addr); + #elif defined(__APPLE__) && defined(__x86_64__) + caller = (void *) h->uc_mcontext->__ss.__rip); + #else + # error "unknown architecture!" + #endif + } //handle = dlopen(NULL, RTLD_NOW); - Dl_info info; - dladdr(caller, &info); - h->ctx.base_addr = (uint64_t)info.dli_fbase; - pst_log(SEVERITY_INFO, "Process address information: PC address: %p, base address: %p, object name: %s", caller, info.dli_fbase, info.dli_fname); + if(caller) { + Dl_info info; + dladdr(caller, &info); + h->ctx.base_addr = (uint64_t)info.dli_fbase; + pst_log(SEVERITY_INFO, "Process address information: PC address: %p, base address: %p, object name: %s", caller, info.dli_fbase, info.dli_fname); + } if(!h->ctx.dwfl) { h->ctx.dwfl = dwfl_begin(&callbacks); if(h->ctx.dwfl == NULL) { - h->ctx.print(&h->ctx, "Failed to initialize libdw session for parse stack frames"); + pst_log(SEVERITY_ERROR, "Failed to initialize libdw session to parse stack frames"); return false; } if(dwfl_linux_proc_report(h->ctx.dwfl, getpid()) != 0 || dwfl_report_end(h->ctx.dwfl, NULL, NULL) !=0) { - h->ctx.print(&h->ctx, "Failed to parse debug section of executable"); + pst_log(SEVERITY_ERROR, "Failed to parse debug section of executable"); dwfl_end(h->ctx.dwfl); h->ctx.dwfl = NULL; return false; } } - h->ctx.print(&h->ctx, "Stack trace: caller = %p\n", caller); + pst_log(SEVERITY_INFO, "Stack trace: caller = %p\n", caller); unw_getcontext(&h->ctx.context); unw_init_local(&h->ctx.cursor, &h->ctx.context); @@ -239,14 +264,15 @@ bool pst_handler_unwind(pst_handler* h) continue; } - if(pc == (uint64_t)caller) { - skip = 0; - } else if(skip) { - pst_log(SEVERITY_DEBUG, "Skipping frame #%d: PC = %#lX, SP = %#lX", i, pc, sp); - continue; + if(caller) { + if(pc == (uint64_t)caller) { + skip = 0; + } else if(skip) { + pst_log(SEVERITY_DEBUG, "Skipping frame #%d: PC = %#lX, SP = %#lX", i, pc, sp); + continue; + } } - h->ctx.print(&h->ctx, "[%-2d] ", i); pst_log(SEVERITY_DEBUG, "Analyze frame #%d: PC = %#lX, SP = %#lX", i, pc, sp); pst_function* last = last_function(h); pst_function* fn = add_function(h, NULL); @@ -256,7 +282,6 @@ bool pst_handler_unwind(pst_handler* h) } else if(last) { last->parent = fn; } - h->ctx.print(&h->ctx, "\n"); } return true; diff --git a/src/dwarf/dwarf_handler.h b/src/dwarf/dwarf_handler.h index 36ddd18..a387620 100644 --- a/src/dwarf/dwarf_handler.h +++ b/src/dwarf/dwarf_handler.h @@ -14,6 +14,7 @@ #include "utils/list_head.h" #include "common.h" #include "context.h" +#include "dwarf_function.h" typedef struct pst_handler { pst_context ctx; // context of unwinding @@ -26,7 +27,6 @@ pst_handler* pst_handler_new(ucontext_t* hctx); void pst_handler_fini(pst_handler* h); bool pst_handler_handle_dwarf(pst_handler* h); -void pst_handler_print_dwarf(pst_handler* h); bool pst_handler_unwind(pst_handler* h); #endif /* __PST_DWARF_HANDLER_H__ */ diff --git a/src/libpst.c b/src/libpst.c index beed1e3..0004129 100644 --- a/src/libpst.c +++ b/src/libpst.c @@ -11,12 +11,17 @@ #include "utils/allocator.h" #include "dwarf/dwarf_handler.h" +#include "dwarf/dwarf_function.h" // allocate and initialize libpst library -pst_handler* pst_lib_init(ucontext_t* hctx) +pst_handler* pst_lib_init(ucontext_t* hctx, void* buff, uint32_t size) { // global - pst_alloc_init(&allocator); + if(!buff || !size) { + pst_alloc_init(&allocator); + } else { + pst_alloc_init_custom(&allocator, buff, size); + } pst_log_init_console(&logger); pst_new(pst_handler, handler, hctx); @@ -33,59 +38,14 @@ void pst_lib_fini(pst_handler* h) pst_alloc_fini(&allocator); } -// write stack trace information to provided file descriptor -int pst_unwind_simple_fd(pst_handler* h, FILE* fd) +// save stack trace information to provided buffer in RAM +int pst_unwind_simple(pst_handler* h) { - int ret = pst_handler_unwind(h); - if(ret) { - fwrite(h->ctx.buff, 1, strlen(h->ctx.buff), fd); - return 0; - } - - return -1; + return pst_handler_unwind(h); } // save stack trace information to provided buffer in RAM -int pst_unwind_simple(pst_handler* h, char* buff, uint32_t buff_size) +int pst_unwind_pretty(pst_handler* h) { - int ret = pst_handler_unwind(h); - if(ret) { - uint32_t cnt = buff_size < sizeof(h->ctx.buff) ? buff_size : sizeof(h->ctx.buff); - memcpy(buff, h->ctx.buff, cnt - 2); - buff[cnt - 1] = 0; - - return 0; - } - - return -1; -} - - -// write stack trace information to provided file descriptor -int pst_unwind_pretty_fd(pst_handler* h, FILE* fd) -{ - int ret = pst_handler_handle_dwarf(h); - if(ret) { - pst_handler_print_dwarf(h); - fwrite(h->ctx.buff, 1, strlen(h->ctx.buff), fd); - return 0; - } - - return -1; -} - -// save stack trace information to provided buffer in RAM -int pst_unwind_pretty(pst_handler* h, char* buff, uint32_t buff_size) -{ - int ret = pst_handler_handle_dwarf(h); - if(ret) { - pst_handler_print_dwarf(h); - uint32_t cnt = buff_size < sizeof(h->ctx.buff) ? buff_size : sizeof(h->ctx.buff); - memcpy(buff, h->ctx.buff, cnt - 2); - buff[cnt - 1] = 0; - - return 0; - } - - return -1; + return pst_handler_handle_dwarf(h); } diff --git a/tests/main.c b/tests/main.c index faf105f..e6466b8 100644 --- a/tests/main.c +++ b/tests/main.c @@ -62,15 +62,19 @@ void FatalSignalHandler(int sig, siginfo_t* info, void* context) printf("%s signal handled\n", strsignal(sig)); if((context != 0) && (sig == SIGSEGV || sig == SIGABRT || sig == SIGBUS || sig == SIGFPE)) { - pst_handler* handler = pst_lib_init((ucontext_t*)context); + pst_handler* handler = pst_lib_init((ucontext_t*)context, NULL, 0); if(!handler) { printf("Failed to allocate pst handler\n"); return; } - int ret = pst_unwind_simple_fd(handler, stdout); - if(!ret) { - pst_unwind_pretty_fd(handler, stdout); + if(pst_unwind_simple(handler)) { + printf("%s", pst_print_simple(handler)); + if(pst_unwind_pretty(handler)) { + printf("%s", pst_print_pretty(handler)); + } else { + printf("Failed to use DWARF for unwind stack trace\n"); + } } else { printf("No stack trace obtained\n"); }