save call-site parameters in caller
This commit is contained in:
+155
-59
@@ -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);
|
||||
pst_call_site* st = add_call_site(target, oname);
|
||||
if(!st->handle_dwarf(&child)) {
|
||||
del_call_site(st);
|
||||
return false;
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
+69
-3
@@ -20,8 +20,11 @@
|
||||
#include <libunwind.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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 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
|
||||
|
||||
Reference in New Issue
Block a user