From 9aa8ffcea9980d24cc9c9f13d9dd51e46e6283bf Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Sep 2009 15:45:28 -0700 Subject: PR10594: Provide a cached dwarf_getscopes_die This avoids repeated DIE traversal by caching all parents on the first call, so future calls are just a simple walk up parent links. * dwflpp.cxx (dwflpp::getscopes_die): New cached function that mimics libdw's dwarf_getscopes_die using cached parent links. (dwflpp::cache_die_parents): New function to build the parent cache. (dwflpp::~dwflpp): Clean up the parent caches. (dwflpp::iterate_over_labels): Use the cached getscopes_die. (dwflpp::find_variable_and_frame_base): Ditto. * tapsets.cxx (dwarf_derived_probe::saveargs): Ditto. (uprobe_derived_probe::saveargs): Ditto. (dwarf_var_expanding_visitor::visit_target_symbol_context): Ditto. --- dwflpp.cxx | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 5 deletions(-) (limited to 'dwflpp.cxx') diff --git a/dwflpp.cxx b/dwflpp.cxx index 2437630e..d96f3eda 100644 --- a/dwflpp.cxx +++ b/dwflpp.cxx @@ -56,6 +56,10 @@ extern "C" { } +// debug flag to compare to the uncached version from libdw +// #define DEBUG_DWFLPP_GETSCOPES 1 + + using namespace std; using namespace __gnu_cxx; @@ -110,6 +114,10 @@ dwflpp::~dwflpp() it != global_alias_cache.end(); ++it) delete it->second; + for (mod_cu_die_parent_cache_t::iterator it = cu_die_parent_cache.begin(); + it != cu_die_parent_cache.end(); ++it) + delete it->second; + if (dwfl) dwfl_end(dwfl); } @@ -591,6 +599,89 @@ dwflpp::iterate_over_inline_instances (int (* callback)(Dwarf_Die * die, void * } +void +dwflpp::cache_die_parents(cu_die_parent_cache_t* parents, Dwarf_Die* die) +{ + // Record and recurse through DIEs we care about + Dwarf_Die child, import; + if (dwarf_child(die, &child) == 0) + do + { + switch (dwarf_tag (&child)) + { + // normal tags to recurse + case DW_TAG_compile_unit: + case DW_TAG_module: + case DW_TAG_lexical_block: + case DW_TAG_with_stmt: + case DW_TAG_catch_block: + case DW_TAG_try_block: + case DW_TAG_entry_point: + case DW_TAG_inlined_subroutine: + case DW_TAG_subprogram: + parents->insert(make_pair(child.addr, *die)); + cache_die_parents(parents, &child); + break; + + // record only, nothing to recurse + case DW_TAG_label: + parents->insert(make_pair(child.addr, *die)); + break; + + // imported dies should be followed + case DW_TAG_imported_unit: + if (dwarf_attr_die(&child, DW_AT_import, &import)) + { + parents->insert(make_pair(import.addr, *die)); + cache_die_parents(parents, &import); + } + break; + + // nothing to do for other tags + default: + break; + } + } + while (dwarf_siblingof(&child, &child) == 0); +} + + +vector +dwflpp::getscopes_die(Dwarf_Die* die) +{ + assert (cu); + + cu_die_parent_cache_t *parents = cu_die_parent_cache[cu->addr]; + if (!parents) + { + parents = new cu_die_parent_cache_t; + cu_die_parent_cache[cu->addr] = parents; + cache_die_parents(parents, cu); + if (sess.verbose > 4) + clog << "die parent cache " << module_name << ":" << cu_name() + << " size " << parents->size() << endl; + } + + vector scopes; + scopes.push_back(*die); + for (cu_die_parent_cache_t::iterator it = parents->find(die->addr); + it != parents->end(); it = parents->find(it->second.addr)) + scopes.push_back(it->second); + +#ifdef DEBUG_DWFLPP_GETSCOPES + Dwarf_Die *dscopes; + int nscopes = dwarf_getscopes_die(die, &dscopes); + + assert(nscopes == (int)scopes.size()); + for (unsigned i = 0; i < scopes.size(); ++i) + assert(scopes[i].addr == dscopes[i].addr); + free(dscopes); +#endif + + return scopes; +} + + int dwflpp::global_alias_caching_callback(Dwarf_Die *die, void *arg) { @@ -1025,9 +1116,8 @@ dwflpp::iterate_over_labels (Dwarf_Die *begin_die, Dwarf_Addr stmt_addr; if (dwarf_lowpc (&die, &stmt_addr) == 0) { - Dwarf_Die *scopes; - int nscopes = dwarf_getscopes_die (&die, &scopes); - if (nscopes > 1) + vector scopes = getscopes_die(&die); + if (scopes.size() > 1) callback(current_function, name, file, dline, &scopes[1], stmt_addr, q); } @@ -1491,6 +1581,7 @@ dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die, * as returned by dwarf_getscopes for the address, starting with the * declaring_scope that the variable was found in. */ + vector vscopes; for (int inner = declaring_scope; inner < nscopes && fb_attr == NULL; ++inner) @@ -1511,8 +1602,10 @@ dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die, * subroutine is inlined to find the appropriate frame base. */ if (declaring_scope != -1) { - nscopes = dwarf_getscopes_die (&scopes[inner], &scopes); - if (nscopes == -1) + vscopes = getscopes_die(&scopes[inner]); + scopes = &vscopes[0]; + nscopes = vscopes.size(); + if (!nscopes) throw semantic_error ("unable to get die scopes for '" + local + "' in an inlined subroutines", e->tok); -- cgit From c9efa5c99498ccb3d2f0f87bc373da7dfd1cc067 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Sep 2009 16:13:16 -0700 Subject: Simplify deleting all map values * util.h (delete_map): New templated map deleter. * dwflpp.cxx (dwflpp::~dwflpp): Use it. * tapsets.cxx (symbol_table::~symbol_table): Use it. --- dwflpp.cxx | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) (limited to 'dwflpp.cxx') diff --git a/dwflpp.cxx b/dwflpp.cxx index d96f3eda..36b016c7 100644 --- a/dwflpp.cxx +++ b/dwflpp.cxx @@ -98,25 +98,11 @@ dwflpp::~dwflpp() { free(cached_scopes); - for (module_cu_cache_t::iterator it = module_cu_cache.begin(); - it != module_cu_cache.end(); ++it) - delete it->second; - - for (mod_cu_function_cache_t::iterator it = cu_function_cache.begin(); - it != cu_function_cache.end(); ++it) - delete it->second; - - for (cu_inl_function_cache_t::iterator it = cu_inl_function_cache.begin(); - it != cu_inl_function_cache.end(); ++it) - delete it->second; - - for (mod_cu_type_cache_t::iterator it = global_alias_cache.begin(); - it != global_alias_cache.end(); ++it) - delete it->second; - - for (mod_cu_die_parent_cache_t::iterator it = cu_die_parent_cache.begin(); - it != cu_die_parent_cache.end(); ++it) - delete it->second; + delete_map(module_cu_cache); + delete_map(cu_function_cache); + delete_map(cu_inl_function_cache); + delete_map(global_alias_cache); + delete_map(cu_die_parent_cache); if (dwfl) dwfl_end(dwfl); -- cgit From 729455a739d4755269f20b73d2db231db2a1fdd7 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 10 Sep 2009 17:06:11 -0700 Subject: PR10594 cont'd: Use parent die cache for variable lookup Variable lookup is usually done through the scopes from dwarf_getscopes at a particular pc. This requires an expensive traversal to find the inner-most die containing the pc. For cases where that containing die is known, e.g. at a particular function entry, we can do much better with our die_parent_cache. This may also help get more accurate variable scopes in cases where multiple dies contain a pc and the innermost isn't what we're trying to probe. For example, an inlined call chain of foo->bar->baz may all have the same entry pc, but if the probe was on function("bar"), we would want the variables in bar's scope, not baz's. * dwflpp.h (struct dwflpp): Remove pc_cached_scopes, num_cached_scopes, and cached_scopes, as they are now remembered by the caller. * dwflpp.cxx (dwflpp::getscopes): New - the DIE version uses the parent cache, and the pc version just defers to dwarf_getscopes. (dwflpp::print_locals, literal_stmt_for_local): Take a scopes vector. (dwflpp::find_variable_and_frame_base): Take a scopes vector from the caller instead of computing it every time. (dwflpp::dwarf_getscopes_cached): Removed. * tapsets.cxx (dwarf_var_expanding_visitor::getscopes): New cached lookup function which gets the scopes from the DIE if possible. (dwarf_var_expanding_visitor::visit_target_symbol): Call getscopes. --- dwflpp.cxx | 181 ++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 118 insertions(+), 63 deletions(-) (limited to 'dwflpp.cxx') diff --git a/dwflpp.cxx b/dwflpp.cxx index 36b016c7..3e451916 100644 --- a/dwflpp.cxx +++ b/dwflpp.cxx @@ -70,8 +70,7 @@ static string TOK_KERNEL("kernel"); dwflpp::dwflpp(systemtap_session & session, const string& name, bool kernel_p): sess(session), module(NULL), module_bias(0), mod_info(NULL), module_start(0), module_end(0), cu(NULL), dwfl(NULL), - module_dwarf(NULL), function(NULL), blacklist_enabled(false), - pc_cached_scopes(0), num_cached_scopes(0), cached_scopes(NULL) + module_dwarf(NULL), function(NULL), blacklist_enabled(false) { if (kernel_p) setup_kernel(name); @@ -87,8 +86,7 @@ dwflpp::dwflpp(systemtap_session & session, const string& name, bool kernel_p): dwflpp::dwflpp(systemtap_session & session, const vector& names): sess(session), module(NULL), module_bias(0), mod_info(NULL), module_start(0), module_end(0), cu(NULL), dwfl(NULL), - module_dwarf(NULL), function(NULL), blacklist_enabled(false), - pc_cached_scopes(0), num_cached_scopes(0), cached_scopes(NULL) + module_dwarf(NULL), function(NULL), blacklist_enabled(false) { setup_user(names); } @@ -96,8 +94,6 @@ dwflpp::dwflpp(systemtap_session & session, const vector& names): dwflpp::~dwflpp() { - free(cached_scopes); - delete_map(module_cu_cache); delete_map(cu_function_cache); delete_map(cu_inl_function_cache); @@ -179,9 +175,6 @@ dwflpp::focus_on_cu(Dwarf_Die * c) // Reset existing pointers and names function_name.clear(); function = NULL; - - free(cached_scopes); - cached_scopes = NULL; } @@ -632,30 +625,42 @@ dwflpp::cache_die_parents(cu_die_parent_cache_t* parents, Dwarf_Die* die) } -vector -dwflpp::getscopes_die(Dwarf_Die* die) +cu_die_parent_cache_t* +dwflpp::get_die_parents() { assert (cu); - cu_die_parent_cache_t *parents = cu_die_parent_cache[cu->addr]; + cu_die_parent_cache_t *& parents = cu_die_parent_cache[cu->addr]; if (!parents) { parents = new cu_die_parent_cache_t; - cu_die_parent_cache[cu->addr] = parents; cache_die_parents(parents, cu); if (sess.verbose > 4) clog << "die parent cache " << module_name << ":" << cu_name() << " size " << parents->size() << endl; } + return parents; +} + + +vector +dwflpp::getscopes_die(Dwarf_Die* die) +{ + cu_die_parent_cache_t *parents = get_die_parents(); vector scopes; - scopes.push_back(*die); - for (cu_die_parent_cache_t::iterator it = parents->find(die->addr); - it != parents->end(); it = parents->find(it->second.addr)) - scopes.push_back(it->second); + Dwarf_Die *scope = die; + cu_die_parent_cache_t::iterator it; + do + { + scopes.push_back(*scope); + it = parents->find(scope->addr); + scope = &it->second; + } + while (it != parents->end()); #ifdef DEBUG_DWFLPP_GETSCOPES - Dwarf_Die *dscopes; + Dwarf_Die *dscopes = NULL; int nscopes = dwarf_getscopes_die(die, &dscopes); assert(nscopes == (int)scopes.size()); @@ -668,6 +673,83 @@ dwflpp::getscopes_die(Dwarf_Die* die) } +std::vector +dwflpp::getscopes(Dwarf_Die* die) +{ + cu_die_parent_cache_t *parents = get_die_parents(); + + vector scopes; + + Dwarf_Die origin; + Dwarf_Die *scope = die; + cu_die_parent_cache_t::iterator it; + do + { + scopes.push_back(*scope); + if (dwarf_tag(scope) == DW_TAG_inlined_subroutine && + dwarf_attr_die(scope, DW_AT_abstract_origin, &origin)) + scope = &origin; + + it = parents->find(scope->addr); + scope = &it->second; + } + while (it != parents->end()); + +#ifdef DEBUG_DWFLPP_GETSCOPES + // there isn't an exact libdw equivalent, but if dwarf_getscopes on the + // entrypc returns the same first die, then all the scopes should match + Dwarf_Addr pc; + if (die_entrypc(die, &pc)) + { + Dwarf_Die *dscopes = NULL; + int nscopes = dwarf_getscopes(cu, pc, &dscopes); + if (nscopes > 0 && dscopes[0].addr == die->addr) + { + assert(nscopes == (int)scopes.size()); + for (unsigned i = 0; i < scopes.size(); ++i) + assert(scopes[i].addr == dscopes[i].addr); + } + free(dscopes); + } +#endif + + return scopes; +} + + +std::vector +dwflpp::getscopes(Dwarf_Addr pc) +{ + // The die_parent_cache doesn't help us without knowing where the pc is + // contained, so we have to do this one the old fashioned way. + + assert (cu); + + vector scopes; + + Dwarf_Die* dwarf_scopes; + int nscopes = dwarf_getscopes(cu, pc, &dwarf_scopes); + if (nscopes > 0) + { + scopes.assign(dwarf_scopes, dwarf_scopes + nscopes); + free(dwarf_scopes); + } + +#ifdef DEBUG_DWFLPP_GETSCOPES + // check that getscopes on the starting die gets the same result + if (!scopes.empty()) + { + vector other = getscopes(&scopes[0]); + assert(scopes.size() == other.size()); + for (unsigned i = 0; i < scopes.size(); ++i) + assert(scopes[i].addr == other[i].addr); + } +#endif + + return scopes; +} + + int dwflpp::global_alias_caching_callback(Dwarf_Die *die, void *arg) { @@ -1488,11 +1570,13 @@ dwflpp::loc2c_emit_address (void *arg, struct obstack *pool, void -dwflpp::print_locals(Dwarf_Die *die, ostream &o) +dwflpp::print_locals(vector& scopes, ostream &o) { + // XXX Shouldn't this be walking up to outer scopes too? + // Try to get the first child of die. Dwarf_Die child; - if (dwarf_child (die, &child) == 0) + if (dwarf_child (&scopes[0], &child) == 0) { do { @@ -1517,34 +1601,19 @@ dwflpp::print_locals(Dwarf_Die *die, ostream &o) Dwarf_Attribute * -dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die, +dwflpp::find_variable_and_frame_base (vector& scopes, Dwarf_Addr pc, string const & local, const target_symbol *e, Dwarf_Die *vardie, Dwarf_Attribute *fb_attr_mem) { - Dwarf_Die *scopes; - int nscopes = 0; + Dwarf_Die *scope_die = &scopes[0]; Dwarf_Attribute *fb_attr = NULL; assert (cu); - nscopes = dwarf_getscopes_cached (pc, &scopes); - if (nscopes <= 0) - { - throw semantic_error ("unable to find any scopes containing " - + lex_cast_hex(pc) - + ((scope_die == NULL) ? "" - : (string (" in ") - + (dwarf_diename(scope_die) ?: "") - + "(" + (dwarf_diename(cu) ?: "") - + ")")) - + " while searching for local '" + local + "'", - e->tok); - } - - int declaring_scope = dwarf_getscopevar (scopes, nscopes, + int declaring_scope = dwarf_getscopevar (&scopes[0], scopes.size(), local.c_str(), 0, NULL, 0, 0, vardie); @@ -1567,18 +1636,19 @@ dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die, * as returned by dwarf_getscopes for the address, starting with the * declaring_scope that the variable was found in. */ - vector vscopes; - for (int inner = declaring_scope; - inner < nscopes && fb_attr == NULL; + vector physcopes, *fbscopes = &scopes; + for (size_t inner = declaring_scope; + inner < fbscopes->size() && fb_attr == NULL; ++inner) { - switch (dwarf_tag (&scopes[inner])) + Dwarf_Die& scope = (*fbscopes)[inner]; + switch (dwarf_tag (&scope)) { default: continue; case DW_TAG_subprogram: case DW_TAG_entry_point: - fb_attr = dwarf_attr_integrate (&scopes[inner], + fb_attr = dwarf_attr_integrate (&scope, DW_AT_frame_base, fb_attr_mem); break; @@ -1588,13 +1658,12 @@ dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die, * subroutine is inlined to find the appropriate frame base. */ if (declaring_scope != -1) { - vscopes = getscopes_die(&scopes[inner]); - scopes = &vscopes[0]; - nscopes = vscopes.size(); - if (!nscopes) + physcopes = getscopes_die(&scope); + if (physcopes.empty()) throw semantic_error ("unable to get die scopes for '" + local + "' in an inlined subroutines", e->tok); + fbscopes = &physcopes; inner = 0; // zero is current scope, for look will increase. declaring_scope = -1; } @@ -2172,7 +2241,7 @@ dwflpp::express_as_string (string prelude, string -dwflpp::literal_stmt_for_local (Dwarf_Die *scope_die, +dwflpp::literal_stmt_for_local (vector& scopes, Dwarf_Addr pc, string const & local, const target_symbol *e, @@ -2182,7 +2251,7 @@ dwflpp::literal_stmt_for_local (Dwarf_Die *scope_die, Dwarf_Die vardie; Dwarf_Attribute fb_attr_mem, *fb_attr = NULL; - fb_attr = find_variable_and_frame_base (scope_die, pc, local, e, + fb_attr = find_variable_and_frame_base (scopes, pc, local, e, &vardie, &fb_attr_mem); if (sess.verbose>2) @@ -2685,20 +2754,6 @@ dwflpp::literal_addr_to_sym_addr(Dwarf_Addr lit_addr) return lit_addr; } -int -dwflpp::dwarf_getscopes_cached (Dwarf_Addr pc, Dwarf_Die **scopes) -{ - if (!cached_scopes || pc != pc_cached_scopes) - { - free(cached_scopes); - cached_scopes = NULL; - pc_cached_scopes = pc; - num_cached_scopes = dwarf_getscopes(cu, pc, &cached_scopes); - } - *scopes = cached_scopes; - return num_cached_scopes; -} - /* Returns the call frame address operations for the given program counter * in the libdw address space. */ -- cgit