From: Jonathan Lebon Date: Tue, 28 Jan 2014 19:51:07 +0000 (-0500) Subject: implement iterate_over_callees() X-Git-Tag: release-2.5~265^2~6 X-Git-Url: https://sourceware.org/git/?a=commitdiff_plain;h=67959c62576af851d8ae220d836ca6b527ec6aea;p=systemtap.git implement iterate_over_callees() In this patch, we actually implement iterate_over_callees(). This function looks for inlined subroutines or GNU_call_sites and calls the callback for each found. The query_callee() callback for now simply does a query_statement on the callee found. Logic is added in the next patch to limit probing to calls from caller only. --- diff --git a/dwflpp.cxx b/dwflpp.cxx index b2f072a76..2730096fc 100644 --- a/dwflpp.cxx +++ b/dwflpp.cxx @@ -1787,6 +1787,210 @@ dwflpp::iterate_over_labels (Dwarf_Die *begin_die, while (dwarf_siblingof (&die, &die) == 0); } +// Mini 'query-like' struct to help us navigate callbacks during +// external function resolution +struct external_function_query { + dwflpp* dw; + const char* name; + Dwarf_Die die; + Dwarf_Addr addr; + bool resolved; + external_function_query(dwflpp* dw, const char* name): + dw(dw), name(name), resolved(false) {} +}; + +int +dwflpp::external_function_cu_callback (Dwarf_Die* cu, void *arg) +{ + external_function_query * efq = static_cast(arg); + efq->dw->focus_on_cu(cu); + return efq->dw->iterate_over_functions(external_function_func_callback, + efq, efq->name); +} + +int +dwflpp::external_function_func_callback (Dwarf_Die* func, void * arg) +{ + external_function_query * efq = static_cast(arg); + Dwarf_Attribute external; + Dwarf_Addr func_addr; + if (dwarf_attr_integrate(func, DW_AT_external, &external) != NULL && + dwarf_lowpc(func, &func_addr) == 0) + { + efq->die = *func; + efq->addr = func_addr; + efq->resolved = true; + return DWARF_CB_ABORT; // we found it so stop here + } + return DWARF_CB_OK; +} + +void +dwflpp::iterate_over_callees (Dwarf_Die *begin_die, + const string& sym, + long recursion_depth, + dwarf_query *q, + void (* callback)(const char *, + const char *, + int, + Dwarf_Die *, + Dwarf_Addr, + stack*, + dwarf_query *), + stack *callers) +{ + get_module_dwarf(); + + Dwarf_Die die, import; + + // DIE of abstract_origin found in die + Dwarf_Die origin; + + // callee's entry pc (= where we'll probe) + Dwarf_Addr func_addr; + + // caller's unwind pc during call (to match against bt for filtering) + Dwarf_Addr caller_uw_addr; + + Dwarf_Attribute attr; + + int dline; + const char *name, *file; + if (dwarf_child(begin_die, &die) != 0) + return; // die without children, bail out. + + bool free_callers = false; + if (callers == NULL) /* first call */ + { + callers = new stack(); + free_callers = true; + } + + do + { + bool inlined = false; + switch (dwarf_tag(&die)) + { + case DW_TAG_inlined_subroutine: + inlined = true; + case DW_TAG_GNU_call_site: + name = dwarf_diename (&die); + if (!name) + continue; + if (name != sym) + { + if (!name_has_wildcard(sym)) + continue; + if (!function_name_matches_pattern(name, sym)) + continue; + } + + /* In both cases (call sites and inlines), we want the + * abstract_origin. The difference is that in inlines, the addr is + * in the die itself, whereas for call sites, the addr is in the + * abstract_origin's die. + * Note that in the case of inlines, we're only calling back + * for that inline instance, not all. This is what we want, since + * it will only be triggered when 'called' from the target func, + * which is something we have to emulate for non-inlined funcs + * (which is the purpose of the caller_uw_addr below) */ + if (dwarf_attr_die(&die, DW_AT_abstract_origin, &origin) == NULL) + continue; + + // the low_pc of the die in either cases is the pc that would + // show up in a backtrace (inlines are a special case in which + // the die's low_pc is also the abstract_origin's low_pc = the + // 'start' of the inline instance) + if (dwarf_lowpc(&die, &caller_uw_addr) != 0) + continue; + + if (inlined) + func_addr = caller_uw_addr; + else if (dwarf_lowpc(&origin, &func_addr) != 0) + { + // function doesn't have a low_pc, is it external? + if (dwarf_attr_integrate(&origin, DW_AT_external, + &attr) != NULL) + { + // let's iterate over the CUs and find it. NB: it's + // possible we could have also done this by creating a + // probe point with .exported tacked on and rerunning it + // through derive_probe(). But since we're already on the + // dwflpp side of things, and we already have access to + // everything we need, let's try to be self-sufficient. + + // remember old focus + Dwarf_Die *old_cu = cu; + + external_function_query efq(this, dwarf_linkage_name(&origin) ?: name); + iterate_over_cus(external_function_cu_callback, &efq, false); + + // restore focus + cu = old_cu; + + if (!efq.resolved) // did we resolve it? + continue; + + func_addr = efq.addr; + origin = efq.die; + } + // non-external function without low_pc, jump ship + else continue; + } + + // We now have the addr to probe in func_addr, and the DIE + // from which to obtain file/line info in origin + + // Get the file/line number for this callee + file = dwarf_decl_file (&origin) ?: ""; + dwarf_decl_line (&origin, &dline); + + // add as a caller to match against + if (!inlined) + callers->push(caller_uw_addr); + + callback(name, file, dline, inlined ? &die : &origin, + func_addr, callers, q); + + // If it's a tail call, print a warning that it may not be caught + if (!inlined + && dwarf_attr_integrate(&die, DW_AT_GNU_tail_call, &attr) != NULL) + sess.print_warning (_F("Callee \"%s\" in function \"%s\" is a tail call: " + ".callee probe may not fire. Try placing the probe " + "directly on the callee function instead.", name, + dwarf_diename(begin_die))); + + if (recursion_depth > 1) // .callees(N) + iterate_over_callees(inlined ? &die : &origin, + sym, recursion_depth-1, q, + callback, callers); + + if (!inlined) + callers->pop(); + break; + + case DW_TAG_subprogram: + break; // don't leave our filtered func + + case DW_TAG_imported_unit: + // Iterate over the children of the imported unit as if they + // were inserted in place. + if (dwarf_attr_die(&die, DW_AT_import, &import)) + iterate_over_callees (&import, sym, recursion_depth, q, callback, callers); + break; + + default: + if (dwarf_haschildren (&die)) + iterate_over_callees (&die, sym, recursion_depth, q, callback, callers); + break; + } + } + while (dwarf_siblingof (&die, &die) == 0); + + if (free_callers && callers != NULL) + delete callers; +} + void dwflpp::collect_srcfiles_matching (string const & pattern, diff --git a/dwflpp.h b/dwflpp.h index 672101d71..839cfb79c 100644 --- a/dwflpp.h +++ b/dwflpp.h @@ -253,6 +253,19 @@ struct dwflpp Dwarf_Addr, dwarf_query *)); + void iterate_over_callees (Dwarf_Die *begin_die, + const std::string& sym, + long recursion_depth, + dwarf_query *q, + void (* callback)(const char *, + const char *, + int, + Dwarf_Die *, + Dwarf_Addr, + std::stack*, + dwarf_query *), + std::stack*callers=NULL); + int iterate_over_notes (void *object, void (*callback)(void *object, int type, const char *data, size_t len)); @@ -377,6 +390,8 @@ private: static int mod_function_caching_callback (Dwarf_Die* func, void *arg); static int cu_function_caching_callback (Dwarf_Die* func, void *arg); + static int external_function_cu_callback (Dwarf_Die* cu, void *arg); + static int external_function_func_callback (Dwarf_Die* func, void *arg); bool has_single_line_record (dwarf_query * q, char const * srcfile, int lineno); diff --git a/tapsets.cxx b/tapsets.cxx index 9eba9f93b..94db2469c 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -1569,6 +1570,19 @@ query_label (string const & func, } } +static void +query_callee (char const * callee, + char const * file, + int line, + Dwarf_Die *callee_die, + Dwarf_Addr callee_addr, + stack *callers, + dwarf_query * q) +{ + assert (q->has_function_str); + query_statement(callee, file, line, callee_die, callee_addr, q); +} + static void query_inline_instance_info (inline_instance_info & ii, dwarf_query * q) @@ -1906,6 +1920,38 @@ query_cu (Dwarf_Die * cudie, void * arg) q->dw.iterate_over_labels (&i->die, q->label_val, i->name, q, query_label); } + else if (q->has_callee || q->has_callees_num) + { + // .callee(str) --> str, .callees[(N)] --> "*" + string callee_val = q->has_callee ? q->callee_val : "*"; + long callees_num_val = q->has_callees_num ? q->callees_num_val : 1; + + // NB: We filter functions that do not match the file here rather than + // in query_callee because we only want the filtering to apply to the + // first level, not to callees that are recursed into if + // callees_num_val > 1. + for (func_info_map_t::iterator i = q->filtered_functions.begin(); + i != q->filtered_functions.end(); ++i) + { + if (q->spec_type != function_alone && + q->filtered_srcfiles.count(i->decl_file) == 0) + continue; + q->dw.iterate_over_callees (&i->die, callee_val, + callees_num_val, + q, query_callee); + } + + for (inline_instance_map_t::iterator i = q->filtered_inlines.begin(); + i != q->filtered_inlines.end(); ++i) + { + if (q->spec_type != function_alone && + q->filtered_srcfiles.count(i->decl_file) == 0) + continue; + q->dw.iterate_over_callees (&i->die, callee_val, + callees_num_val, + q, query_callee); + } + } else { // Otherwise, simply probe all resolved functions.