diff --git a/framework/sysutils.cpp b/framework/sysutils.cpp index a02cc17..460818a 100644 --- a/framework/sysutils.cpp +++ b/framework/sysutils.cpp @@ -310,6 +310,94 @@ bool __pst_parameter::handle_dwarf(Dwarf_Die* result) return true; } +pst_call_site_param* __pst_call_site::add_param() +{ + pst_call_site_param* p = new pst_call_site_param(ctx); + params.InsertLast(p); + + return p; +} + +void __pst_call_site::del_param(pst_call_site_param* p) +{ + params.Remove(p); + delete p; +} + +pst_call_site_param* __pst_call_site::next_param(pst_call_site_param* p) +{ + pst_call_site_param* param = NULL; + if(p == NULL) { + param = (pst_call_site_param*)params.First(); + } else { + param = (pst_call_site_param*)params.Next(p); + } + + return param; +} + +bool __pst_call_site::handle_dwarf(Dwarf_Die* child) +{ + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + do { + switch(dwarf_tag(child)) + { + case DW_TAG_GNU_call_site_parameter: { + Dwarf_Addr pc; + unw_get_reg(ctx->curr_frame, UNW_REG_IP, &pc); + + char str[1024]; + // expression represent where callee parameter will be stored + pst_call_site_param* param = new pst_call_site_param(ctx); + if(dwarf_hasattr(child, DW_AT_location)) { + // handle location expression here + // determine location of parameter in stack/heap or CPU registers + attr = dwarf_attr(child, DW_AT_location, &attr_mem); + if(handle_location(ctx, attr, param->location, pc, str, sizeof(str))) { + uint64_t value = 0; + if(param->location.get_value(value)) { + ctx->log(SEVERITY_DEBUG, "\tDW_TAG_GNU_call_site_parameter: \"%s\" ==> 0x%lX", str, value); + } else { + ctx->log(SEVERITY_WARNING, "Failed to get value of calculated DW_AT_location expression: %s", str); + } + } else { + ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", str); + del_param(param); + return false; + } + } + + // expression represents call parameter's value + if(dwarf_hasattr(child, DW_AT_GNU_call_site_value)) { + // handle value expression here + attr = dwarf_attr(child, DW_AT_GNU_call_site_value, &attr_mem); + dwarf_stack stack(ctx); + if(handle_location(ctx, attr, stack, pc, str, sizeof(str))) { + if(stack.get_value(param->value)) { + ctx->log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_value:\"%s\" ==> 0x%lX", str, param->value); + } else { + ctx->log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_location expression: %s", str); + del_param(param); + return false; + } + } else { + ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", str); + del_param(param); + return false; + } + } + break; + } + default: + break; + } + } while (dwarf_siblingof (child, child) == 0); + + return true; +} + pst_parameter* __pst_function::add_param() { pst_parameter* p = new pst_parameter(ctx); @@ -344,6 +432,55 @@ pst_parameter* __pst_function::next_param(pst_parameter* p) return next; } +pst_call_site* __pst_function::add_call_site(uint64_t target, const char* origin) +{ + pst_call_site* st = new pst_call_site(ctx, target, origin); + call_sites.InsertLast(st); + if(target) { + mCallStToTarget.Insert(&target, sizeof(target), st); + } + + if(origin) { + mCallStToOrigin.Insert(origin, strlen(origin), st); + } + + return st; +} +void __pst_function::del_call_site(pst_call_site* st) +{ + call_sites.Remove(st); + if(st->target && mCallStToTarget.Lookup(&st->target, sizeof(st->target))) { + mCallStToTarget.Remove(); + } + + if(st->origin && mCallStToTarget.Lookup(st->origin, strlen(st->origin))) { + mCallStToTarget.Remove(); + } + + delete st; +} +pst_call_site* __pst_function::next_call_site(pst_call_site* st) +{ + pst_call_site* next = NULL; + if(st) { + next = (pst_call_site*)call_sites.First(); + } else { + next = (pst_call_site*)call_sites.Next(st); + } + + return next; +} + +pst_call_site* __pst_function::call_site_by_origin(const char* origin) +{ + return (pst_call_site*)mCallStToOrigin.Lookup(origin, strlen(origin)); +} + +pst_call_site* __pst_function::call_site_by_target(uint64_t target) +{ + return (pst_call_site*)mCallStToTarget.Lookup(&target, sizeof(target)); +} + bool __pst_function::print_dwarf() { char* at = NULL; @@ -490,87 +627,46 @@ bool __pst_function::handle_call_site(Dwarf_Die* result) ctx->log(SEVERITY_DEBUG, "***** DW_TAG_GNU_call_site contents:"); // reference to DIE which represents callee's parameter if compiler knows where it is at compile time + const char* oname = NULL; if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { - Dwarf_Die child; - if(dwarf_child (&origin, &child) == 0) { - do { - switch (dwarf_tag (&child)) - { - case DW_TAG_variable: - case DW_TAG_formal_parameter: - ctx->log(SEVERITY_DEBUG, "\tDW_AT_abstract_origin: %s('%s')", dwarf_diename(&origin), dwarf_diename (&child)); - break; - default: - break; - } - } while (dwarf_siblingof (&child, &child) == 0); - } + oname = dwarf_diename(&origin); + ctx->log(SEVERITY_DEBUG, "\tDW_AT_abstract_origin: '%s'", oname); } // The call site may have a DW_AT_call_site_target attribute which is a DWARF expression. For indirect calls or jumps where it is unknown at // compile time which subprogram will be called the expression computes the address of the subprogram that will be called. + uint64_t target = 0; if(dwarf_hasattr (result, DW_AT_GNU_call_site_target)) { attr = dwarf_attr(result, DW_AT_GNU_call_site_target, &attr_mem); if(attr) { dwarf_stack stack(ctx); char str[1024]; str[0] = 0; - uint64_t value; if(handle_location(ctx, &attr_mem, stack, pc, str, sizeof(str))) { - if(stack.get_value(value)) { - ctx->log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_target: %s ==> %#lX", str, (void*)value); + if(stack.get_value(target)) { + ctx->log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_target: %s ==> %#lX", str, (void*)target); } else { ctx->log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_GNU_call_site_target expression: %s", str); + return false; } } else { ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_GNU_call_site_target expression: %s", str); + return false; } } } + + if(target == 0 && oname == NULL) { + ctx->log(SEVERITY_ERROR, "Cannot determine both call-site target and origin"); + return false; + } + Dwarf_Die child; if(dwarf_child (result, &child) == 0) { - do { - switch (dwarf_tag (&child)) - { - case DW_TAG_GNU_call_site_parameter: { - Dwarf_Addr pc; - unw_get_reg(&cursor, UNW_REG_IP, &pc); - - dwarf_stack stack(ctx); char str[1024]; uint64_t value; - // expression represent where calle parameter vill be stored - if(dwarf_hasattr(&child, DW_AT_location)) { - // handle location expression here - // determine location of parameter in stack/heap or CPU registers - 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, "\tDW_TAG_GNU_call_site_parameter: \"%s\" ==> 0x%lX", str, value); - } else { - ctx->log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_location expression: %s", str); - } - } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", str); - } - } - // expression represents call parameter's value - if(dwarf_hasattr(&child, DW_AT_GNU_call_site_value)) { - // handle value expression here - attr = dwarf_attr(&child, DW_AT_GNU_call_site_value, &attr_mem); - if(handle_location(ctx, attr, stack, pc, str, sizeof(str))) { - if(stack.get_value(value)) { - ctx->log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_value:\"%s\" ==> 0x%lX", str, value); - } else { - ctx->log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_location expression: %s", str); - } - } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", str); - } - } - break; - } - default: - break; - } - } while (dwarf_siblingof (&child, &child) == 0); + pst_call_site* st = add_call_site(target, oname); + if(!st->handle_dwarf(&child)) { + del_call_site(st); + return false; + } } return true; diff --git a/framework/sysutils.h b/framework/sysutils.h index d1de3d5..d553b89 100644 --- a/framework/sysutils.h +++ b/framework/sysutils.h @@ -20,8 +20,11 @@ #include #include #include +#include #include "common.h" +#include "dictionary.h" +#include "dwarf_operations.h" typedef struct __pst_function pst_function; @@ -76,6 +79,57 @@ typedef struct __pst_parameter : public SC_ListNode { pst_context* ctx; } pst_parameter; +// DW_TAG_call_site_parameter +typedef struct __pst_call_site_param : public SC_ListNode { + __pst_call_site_param(pst_context* ctx) : location(ctx) { + param = NULL; name = NULL; value = 0; + } + + ~__pst_call_site_param() { + if(name) { + free(name); name = NULL; + } + } + + Dwarf_Die* param; // reference to parameter DIE in callee (DW_AT_call_parameter) + char* name; // name of parameter if present (DW_AT_name) + dwarf_stack location; // DWARF stack containing location expression + uint64_t value; // parameter's value +} pst_call_site_param; + +// DW_TAG_call_site +typedef struct __pst_call_site : public SC_ListNode { + __pst_call_site(pst_context* c, uint64_t tgt, const char* orn) { + target = tgt; + if(orn) { + origin = strdup(orn); + } else { + origin = NULL; + } + die = NULL; + ctx = c; + } + + ~__pst_call_site() { + if(origin) { + free(origin); + origin = NULL; + } + } + + pst_call_site_param* add_param(); + void del_param(pst_call_site_param* p); + pst_call_site_param* next_param(pst_call_site_param* p); + + bool handle_dwarf(Dwarf_Die* die); + + uint64_t target; // pointer to callee function's (it's Low PC + base address) + char* origin; // name of callee function + Dwarf_Die* die; // DIE of function for which this call site has parameters + SC_ListHead params; // list of parameters and their values + pst_context* ctx; +} pst_call_site; + typedef struct __pst_handler pst_handler; typedef struct __pst_function : public SC_ListNode { @@ -100,20 +154,32 @@ typedef struct __pst_function : public SC_ListNode { void del_param(pst_parameter* p); pst_parameter* next_param(pst_parameter* p); + pst_call_site* add_call_site(uint64_t target, const char* origin); + void del_call_site(pst_call_site* st); + pst_call_site* next_call_site(pst_call_site* st); + pst_call_site* call_site_by_origin(const char* origin); + pst_call_site* call_site_by_target(uint64_t target); + bool unwind(Dwfl* dwfl, Dwfl_Module* module, Dwarf_Addr addr); bool handle_dwarf(Dwarf_Die* d); bool print_dwarf(); bool handle_lexical_block(Dwarf_Die* result); bool handle_call_site(Dwarf_Die* result); - Dwarf_Addr lowpc; // start of the function - Dwarf_Addr highpc; // next address after the end of the function + Dwarf_Addr lowpc; // offset to start of the function against base address + Dwarf_Addr highpc; // offset to the next address after the end of the function against base address Dwarf_Die* die; // DWARF DIE containing definition of the function std::string name; // function's name SC_ListHead params; // function's parameters + + // call-site + SC_ListHead call_sites; // Call-Site definitions + SC_Dict mCallStToTarget; // map pointer to caller to call-site + SC_Dict mCallStToOrigin; // map caller name to call-site + unw_word_t pc; // address between lowpc & highpc (plus base address offset). actually, currently executed command - unw_word_t sp; // SP register in function's frame - unw_word_t cfa; + unw_word_t sp; // SP register in function's frame + unw_word_t cfa; // CFA (Canonical Frame Adress) of the function 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 function is defined