introduce separate print functions for 'simple' and 'pretty' variands of

unwinding
This commit is contained in:
2020-04-05 13:33:19 +04:00
parent 3adc8eb1ef
commit e2e7535149
6 changed files with 124 additions and 125 deletions
+34 -16
View File
@@ -17,33 +17,51 @@
typedef struct pst_handler pst_handler; 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. * @defgroup unwind_simple
// Unwinds current stack trace using base information about program (i.e. print out address, function name) * @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. // Advanced unwind routines.
// Additionally to pst_unwind_simple(...) provides types of parameters and variables in functions and their values if possible // 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 // 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_ */ #endif /* __LIBPST_EXTERNAL_H_ */
+8 -16
View File
@@ -72,6 +72,7 @@ static bool get_frame(pst_function* fn)
fn->ctx->print_expr(fn->ctx, cfa_ops, cfa_nops, NULL); fn->ctx->print_expr(fn->ctx, cfa_ops, cfa_nops, NULL);
bool nret = true;
pst_decl(pst_dwarf_stack, stack, fn->ctx); 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)) { 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); 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 // setup context to match CFA for frame
fn->ctx->cfa = fn->cfa; fn->ctx->cfa = fn->cfa;
} else { } else {
nret = false;
pst_log(SEVERITY_ERROR, "Failed to calculate CFA expression"); pst_log(SEVERITY_ERROR, "Failed to calculate CFA expression");
} }
pst_dwarf_stack_fini(&stack); pst_dwarf_stack_fini(&stack);
return true; return nret;
} }
static pst_parameter* add_param(pst_function* fn) 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) bool pst_function_print_dwarf(pst_function* fn)
{ {
char* at = NULL; 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; 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) // 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); pst_parameter* param = next_param(fn, NULL);
@@ -244,7 +242,7 @@ bool pst_function_print_dwarf(pst_function* fn)
} }
if(!start_variable) { if(!start_variable) {
fn->ctx->print(fn->ctx, ");%s\n", at); fn->ctx->print(fn->ctx, ")%s\n", at);
} else { } else {
fn->ctx->print(fn->ctx, "}\n"); fn->ctx->print(fn->ctx, "}\n");
} }
@@ -388,12 +386,7 @@ bool pst_function_unwind(pst_function* fn)
file = filename; file = filename;
} }
fn->file = pst_strdup(file); 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); 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"); pst_log(SEVERITY_ERROR, "Failed to allocate memory");
return false; return false;
} }
fn->ctx->print(fn->ctx, " --> %s", function_name);
char* str = strchr(function_name, '('); char* str = strchr(function_name, '(');
if(str) { if(str) {
+39 -14
View File
@@ -156,7 +156,7 @@ bool pst_handler_handle_dwarf(pst_handler* h)
return true; 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.clean_print(&h->ctx);
h->ctx.print(&h->ctx, "DWARF-based stack trace information:\n"); 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); pst_function_print_dwarf(fn);
h->ctx.print(&h->ctx, "\n"); h->ctx.print(&h->ctx, "\n");
} }
return h->ctx.buff;
} }
char *debuginfo_path = NULL; char *debuginfo_path = NULL;
@@ -177,53 +179,76 @@ Dwfl_Callbacks callbacks = {
}; };
#include <dlfcn.h> #include <dlfcn.h>
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) bool pst_handler_unwind(pst_handler* h)
{ {
h->ctx.clean_print(&h->ctx); void* caller = NULL; // pointer to the function which requested to unwind stack
void* caller; // pointer to the function which requested to unwind stack
if(h->ctx.hcontext) {
#ifdef REG_RIP // x86_64 #ifdef REG_RIP // x86_64
caller = (void *) h->ctx.hcontext->uc_mcontext.gregs[REG_RIP]; caller = (void *) h->ctx.hcontext->uc_mcontext.gregs[REG_RIP];
h->ctx.sp = h->ctx.hcontext->uc_mcontext.gregs[REG_RSP]; h->ctx.sp = h->ctx.hcontext->uc_mcontext.gregs[REG_RSP];
pst_log(SEVERITY_DEBUG, "Original caller's SP: %#lX", h->ctx.sp); pst_log(SEVERITY_DEBUG, "Original caller's SP: %#lX", h->ctx.sp);
#elif defined(REG_EIP) // x86_32 #elif defined(REG_EIP) // x86_32
caller = (void *) uctx->uc_mcontext.gregs[REG_EIP]); caller = (void *) h->uc_mcontext.gregs[REG_EIP]);
#elif defined(__arm__) #elif defined(__arm__)
caller = (void *) uctx->uc_mcontext.arm_pc); caller = (void *) h->uc_mcontext.arm_pc);
#elif defined(__aarch64__) #elif defined(__aarch64__)
caller = (void *) uctx->uc_mcontext.pc); caller = (void *) h->uc_mcontext.pc);
#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) #elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__)
caller = (void *) uctx->uc_mcontext.regs->nip); caller = (void *) h->uc_mcontext.regs->nip);
#elif defined(__s390x__) #elif defined(__s390x__)
caller = (void *) uctx->uc_mcontext.psw.addr); caller = (void *) h->uc_mcontext.psw.addr);
#elif defined(__APPLE__) && defined(__x86_64__) #elif defined(__APPLE__) && defined(__x86_64__)
caller = (void *) uctx->uc_mcontext->__ss.__rip); caller = (void *) h->uc_mcontext->__ss.__rip);
#else #else
# error "unknown architecture!" # error "unknown architecture!"
#endif #endif
}
//handle = dlopen(NULL, RTLD_NOW); //handle = dlopen(NULL, RTLD_NOW);
if(caller) {
Dl_info info; Dl_info info;
dladdr(caller, &info); dladdr(caller, &info);
h->ctx.base_addr = (uint64_t)info.dli_fbase; 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); 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) { if(!h->ctx.dwfl) {
h->ctx.dwfl = dwfl_begin(&callbacks); h->ctx.dwfl = dwfl_begin(&callbacks);
if(h->ctx.dwfl == NULL) { 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; return false;
} }
if(dwfl_linux_proc_report(h->ctx.dwfl, getpid()) != 0 || dwfl_report_end(h->ctx.dwfl, NULL, NULL) !=0) { 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); dwfl_end(h->ctx.dwfl);
h->ctx.dwfl = NULL; h->ctx.dwfl = NULL;
return false; 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_getcontext(&h->ctx.context);
unw_init_local(&h->ctx.cursor, &h->ctx.context); unw_init_local(&h->ctx.cursor, &h->ctx.context);
@@ -239,14 +264,15 @@ bool pst_handler_unwind(pst_handler* h)
continue; continue;
} }
if(caller) {
if(pc == (uint64_t)caller) { if(pc == (uint64_t)caller) {
skip = 0; skip = 0;
} else if(skip) { } else if(skip) {
pst_log(SEVERITY_DEBUG, "Skipping frame #%d: PC = %#lX, SP = %#lX", i, pc, sp); pst_log(SEVERITY_DEBUG, "Skipping frame #%d: PC = %#lX, SP = %#lX", i, pc, sp);
continue; continue;
} }
}
h->ctx.print(&h->ctx, "[%-2d] ", i);
pst_log(SEVERITY_DEBUG, "Analyze frame #%d: PC = %#lX, SP = %#lX", i, pc, sp); pst_log(SEVERITY_DEBUG, "Analyze frame #%d: PC = %#lX, SP = %#lX", i, pc, sp);
pst_function* last = last_function(h); pst_function* last = last_function(h);
pst_function* fn = add_function(h, NULL); pst_function* fn = add_function(h, NULL);
@@ -256,7 +282,6 @@ bool pst_handler_unwind(pst_handler* h)
} else if(last) { } else if(last) {
last->parent = fn; last->parent = fn;
} }
h->ctx.print(&h->ctx, "\n");
} }
return true; return true;
+1 -1
View File
@@ -14,6 +14,7 @@
#include "utils/list_head.h" #include "utils/list_head.h"
#include "common.h" #include "common.h"
#include "context.h" #include "context.h"
#include "dwarf_function.h"
typedef struct pst_handler { typedef struct pst_handler {
pst_context ctx; // context of unwinding 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); void pst_handler_fini(pst_handler* h);
bool pst_handler_handle_dwarf(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); bool pst_handler_unwind(pst_handler* h);
#endif /* __PST_DWARF_HANDLER_H__ */ #endif /* __PST_DWARF_HANDLER_H__ */
+11 -51
View File
@@ -11,12 +11,17 @@
#include "utils/allocator.h" #include "utils/allocator.h"
#include "dwarf/dwarf_handler.h" #include "dwarf/dwarf_handler.h"
#include "dwarf/dwarf_function.h"
// allocate and initialize libpst library // 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 // global
if(!buff || !size) {
pst_alloc_init(&allocator); pst_alloc_init(&allocator);
} else {
pst_alloc_init_custom(&allocator, buff, size);
}
pst_log_init_console(&logger); pst_log_init_console(&logger);
pst_new(pst_handler, handler, hctx); pst_new(pst_handler, handler, hctx);
@@ -33,59 +38,14 @@ void pst_lib_fini(pst_handler* h)
pst_alloc_fini(&allocator); pst_alloc_fini(&allocator);
} }
// write stack trace information to provided file descriptor // save stack trace information to provided buffer in RAM
int pst_unwind_simple_fd(pst_handler* h, FILE* fd) int pst_unwind_simple(pst_handler* h)
{ {
int ret = pst_handler_unwind(h); return pst_handler_unwind(h);
if(ret) {
fwrite(h->ctx.buff, 1, strlen(h->ctx.buff), fd);
return 0;
}
return -1;
} }
// save stack trace information to provided buffer in RAM // 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); return pst_handler_handle_dwarf(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;
} }
+8 -4
View File
@@ -62,15 +62,19 @@ void FatalSignalHandler(int sig, siginfo_t* info, void* context)
printf("%s signal handled\n", strsignal(sig)); printf("%s signal handled\n", strsignal(sig));
if((context != 0) && (sig == SIGSEGV || sig == SIGABRT || sig == SIGBUS || sig == SIGFPE)) 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) { if(!handler) {
printf("Failed to allocate pst handler\n"); printf("Failed to allocate pst handler\n");
return; return;
} }
int ret = pst_unwind_simple_fd(handler, stdout); if(pst_unwind_simple(handler)) {
if(!ret) { printf("%s", pst_print_simple(handler));
pst_unwind_pretty_fd(handler, stdout); if(pst_unwind_pretty(handler)) {
printf("%s", pst_print_pretty(handler));
} else {
printf("Failed to use DWARF for unwind stack trace\n");
}
} else { } else {
printf("No stack trace obtained\n"); printf("No stack trace obtained\n");
} }