From 1cddd773bcca07aaa6b61948a5c8548e475750b3 Mon Sep 17 00:00:00 2001 From: Nikolai Nosov Date: Thu, 16 Jan 2020 14:54:42 +0400 Subject: [PATCH] use two stage unwinding: 1st is simply detection of function name using libunwind and save functions cursor. 2nd - use dwarf information to cath as more info as possible. this approach also allows to dereference DW_OP_GNU_value operation since it need access to caller frame from callee context --- framework/sysutils.cpp | 85 ++++++++++++++++++++++++++++-------------- framework/sysutils.h | 24 +++++++----- 2 files changed, 73 insertions(+), 36 deletions(-) diff --git a/framework/sysutils.cpp b/framework/sysutils.cpp index a23039d..b82a1fa 100644 --- a/framework/sysutils.cpp +++ b/framework/sysutils.cpp @@ -19,9 +19,9 @@ #include "sysutils.h" #include "logger/log.h" -#define USEI_LIBUNWIND +#define USE_LIBUNWIND -#ifdef USEI_LIBUNWIND +#ifdef USE_LIBUNWIND #include #endif @@ -396,9 +396,31 @@ pst_parameter* __pst_function::next_param(pst_parameter* p) bool __pst_function::print_dwarf() { - ctx->print("%s:%u: ", file.c_str(), line); + char* at = NULL; + if(!asprintf(&at, " at %s:%d (%p)", file.c_str(), line, (void*)pc)) { + return false; + } + if(at[4] == ':' && at[5] == '-') { + free(at); + if(!asprintf(&at, " at %p", (void*)pc)) { + return false; + } + } + //ctx->print("%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(NULL); + if(param && param->is_return) { + // print return value type, function name and start list of parameters + param->print_dwarf(); + ctx->print(" %s(", name.c_str()); + param = next_param(param); + } else { + ctx->print("%s(", name.c_str()); + } + bool first = true; bool start_variable = false; - for(pst_parameter* param = next_param(NULL); param; param = next_param(param)) { + for(; param; param = next_param(param)) { if(param->is_return) { // print return value type, function name and start list of parameters param->print_dwarf(); @@ -408,7 +430,7 @@ bool __pst_function::print_dwarf() if(param->is_variable) { if(!start_variable) { - ctx->print(")\n"); + ctx->print(")%s\n", at); ctx->print("{\n"); start_variable = true; } @@ -430,17 +452,19 @@ bool __pst_function::print_dwarf() } if(!start_variable) { - ctx->print(");\n"); + ctx->print(");%s\n", at); } else { ctx->print("}\n"); } + free(at); return true; } bool __pst_function::handle_dwarf(Dwarf_Die* d) { die = d; + ctx->cursor = cursor; Dwarf_Attribute attr_mem; Dwarf_Attribute* attr; @@ -455,9 +479,9 @@ bool __pst_function::handle_dwarf(Dwarf_Die* d) // } unw_proc_info_t info; - unw_get_proc_info(&ctx->cursor, &info); + unw_get_proc_info(&cursor, &info); unw_word_t sp; - unw_get_reg(&ctx->cursor, UNW_REG_SP, &sp); + unw_get_reg(&cursor, UNW_REG_SP, &sp); ctx->log(SEVERITY_DEBUG, "Found function in debug info. name = %s(...), PC = 0x%lX, LOW_PC = 0x%lX, HIGH_PC = 0x%lX, offset from base address: 0x%lX, BASE_PC = 0x%lX, offset from start of function: 0x%lX, stack address: 0x%lX", dwarf_diename(d), pc, lowpc, highpc, pc - ctx->base_addr, info.start_ip, info.start_ip - ctx->base_addr, sp); @@ -553,7 +577,7 @@ bool __pst_function::handle_dwarf(Dwarf_Die* d) { case DW_TAG_GNU_call_site_parameter: { Dwarf_Addr pc; - unw_get_reg(&ctx->cursor, UNW_REG_IP, &pc); + unw_get_reg(&cursor, UNW_REG_IP, &pc); dwarf_stack stack(ctx); char str[1024]; uint64_t value; if(dwarf_hasattr(&child, DW_AT_location)) { @@ -562,7 +586,9 @@ bool __pst_function::handle_dwarf(Dwarf_Die* d) attr = dwarf_attr(&child, DW_AT_location, &attr_mem); if(handle_location(ctx, attr, stack, pc, str, sizeof(str))) { if(stack.get_value(value)) { + ctx->log(SEVERITY_DEBUG, "%s:%d", __PRETTY_FUNCTION__, __LINE__); ctx->log(SEVERITY_DEBUG, "Call site Location: %s ==> 0x%lX", str, value); + ctx->log(SEVERITY_DEBUG, "%s:%d", __PRETTY_FUNCTION__, __LINE__); } } } @@ -726,7 +752,9 @@ bool __pst_handler::get_dwarf_function(pst_function* fun) int tag = dwarf_tag(&result); if(tag == DW_TAG_subprogram || tag == DW_TAG_entry_point || tag == DW_TAG_inlined_subroutine) { if(!strcmp(fun->name.c_str(), dwarf_diename(&result))) { + ctx.log(SEVERITY_DEBUG, "%s:%d", __PRETTY_FUNCTION__, __LINE__); return fun->handle_dwarf(&result); + ctx.log(SEVERITY_DEBUG, "%s:%d", __PRETTY_FUNCTION__, __LINE__); } } } while(dwarf_siblingof(&result, &result) == 0); @@ -734,9 +762,9 @@ bool __pst_handler::get_dwarf_function(pst_function* fun) return nret; } -pst_function* __pst_handler::add_function() +pst_function* __pst_handler::add_function(__pst_function* fun) { - pst_function* f = new pst_function(&ctx); + pst_function* f = new pst_function(&ctx, fun); functions.InsertLast(f); return f; @@ -768,6 +796,15 @@ pst_function* __pst_handler::next_function(pst_function* f) return next; } +bool __pst_handler::handle_dwarf() +{ + for(pst_function* fun = next_function(NULL); fun; fun = next_function(fun)) { + get_dwarf_function(fun); + } + + return true; +} + void __pst_handler::print_dwarf() { ctx.offset = 0; @@ -809,6 +846,7 @@ bool __pst_handler::unwind() #else # error "unknown architecture!" #endif + ctx.offset = 0; handle = dlopen(NULL, RTLD_NOW); Dl_info info; @@ -817,12 +855,12 @@ bool __pst_handler::unwind() ctx.log(SEVERITY_INFO, "Process address information: base address: %p, object name: %s", info.dli_fbase, info.dli_fname); int skipped = 0; -#ifndef USEI_LIBUNWIND +#ifndef USE_LIBUNWIND int size = 0; void * array[50]; size = backtrace(array, 50); - for(int i = 0; i < size && array[i] != caller_address; ++i, ++skipped); - print("Stack trace(caller = %p. Total stack frames: %d, skipped: %d):\n", caller_address, size, skipped); + for(int i = 0; i < size && array[i] != caller; ++i, ++skipped); + ctx.print("Stack trace(caller = %p. Total stack frames: %d, skipped: %d):\n", caller, size, skipped); #else unw_word_t pc; @@ -850,9 +888,10 @@ bool __pst_handler::unwind() return false; } -#ifndef USEI_LIBUNWIND +#ifndef USE_LIBUNWIND for (int i = skipped, idx = 0; i < size ; ++i, ++idx) { - print("[%-2d] ", idx); + uint64_t pc = (uint64_t)array[i]; + ctx.print("[%-2d] ", idx); Dwarf_Addr addr = (uintptr_t)array[i]; #else for (int i = skipped, idx = 0; true ; ++i, ++idx) { @@ -862,25 +901,17 @@ bool __pst_handler::unwind() #endif module = dwfl_addrmodule(dwfl, addr); get_frame(); - pst_function* fun = add_function(); - if(fun->unwind(dwfl, module, pc)) { - if(!get_dwarf_function(fun)) { - del_function(fun); - } - } else { + pst_function* fun = add_function(next_function(NULL)/* first element */); + if(!fun->unwind(dwfl, module, pc)) { del_function(fun); } ctx.print("\n"); -#ifdef USEI_LIBUNWIND +#ifdef USE_LIBUNWIND if(unw_step(&ctx.cursor) <= 0) { break; } #endif } -// for(int op = DW_OP_breg0; op <= DW_OP_breg16; op++ ) { -// printf("{0x%X, 0x%X, \"%s\"},\n", op, op - DW_OP_breg0, unw_regname(op - DW_OP_breg0)); -// } - return true; } diff --git a/framework/sysutils.h b/framework/sysutils.h index 2ab3944..4167664 100644 --- a/framework/sysutils.h +++ b/framework/sysutils.h @@ -73,7 +73,7 @@ typedef struct __pst_parameter : public SC_ListNode { void* loc; // pointer to location of parameter's value bool is_return; // whether this parameter is return value of the function bool is_variable;// whether this parameter is function variable or argument of function - bool has_value; + bool has_value; // whether we got value of parameter or not uint64_t value; // value of parameter pst_context* ctx; } pst_parameter; @@ -81,12 +81,15 @@ typedef struct __pst_parameter : public SC_ListNode { typedef struct __pst_handler pst_handler; typedef struct __pst_function : public SC_ListNode { - __pst_function(pst_context* ctx) : ctx(ctx) { + __pst_function(pst_context* _ctx, __pst_function* _parent) : ctx(_ctx) { pc = 0; line = -1; die = NULL; lowpc = 0; highpc = 0; + parent = NULL; + cursor = _ctx->cursor; + parent = _parent; } ~__pst_function() { @@ -102,15 +105,17 @@ typedef struct __pst_function : public SC_ListNode { bool handle_dwarf(Dwarf_Die* d); bool print_dwarf(); - Dwarf_Addr lowpc; - Dwarf_Addr highpc; + Dwarf_Addr lowpc; // start of the function + Dwarf_Addr highpc; // next address after the end of the function Dwarf_Die* die; // DWARF DIE containing definition of the function std::string name; // function's name - SC_ListHead params; // array of function's parameters - unw_word_t pc; // pointer to the start of the function + SC_ListHead params; // function's parameters + unw_word_t pc; // address between lowpc & highpc (plus base address offset). actually, currently executed command + unw_cursor_t cursor; // copy of stack state of the function int line; // line in code where function is defined - std::string file; // file name (DWARF Compilation Unit) where is function defined - pst_context* ctx; + std::string file; // file name (DWARF Compilation Unit) where function is defined + pst_function* parent; // parent function in call trace (caller) + pst_context* ctx; // context of unwinding } pst_function; typedef struct __pst_handler { @@ -134,10 +139,11 @@ typedef struct __pst_handler { } void clear(); - pst_function* add_function(); + pst_function* add_function(__pst_function* fun); void del_function(pst_function* f); pst_function* next_function(pst_function* f); + bool handle_dwarf(); void print_dwarf(); bool unwind(); bool get_frame();