save call-site parameters in caller

This commit is contained in:
2020-01-25 17:25:59 +04:00
parent 4f94a9e1c2
commit daf58bce70
2 changed files with 226 additions and 64 deletions
+156 -60
View File
@@ -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;
+70 -4
View File
@@ -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 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