diff options
author | Josh Stone <jistone@redhat.com> | 2009-09-16 19:38:20 -0700 |
---|---|---|
committer | Josh Stone <jistone@redhat.com> | 2009-09-16 19:38:20 -0700 |
commit | 7d6d0afc24b43829511f3f1d0aeff0fefff56b54 (patch) | |
tree | fa0e55f71448e92074c5c5ef8bde4098bdf76fb7 | |
parent | 04ddd303348c0ffb71c6f57eacb2282bd1d61b3a (diff) | |
download | systemtap-steved-7d6d0afc24b43829511f3f1d0aeff0fefff56b54.tar.gz systemtap-steved-7d6d0afc24b43829511f3f1d0aeff0fefff56b54.tar.xz systemtap-steved-7d6d0afc24b43829511f3f1d0aeff0fefff56b54.zip |
PR10461: Match C++ scopes for namespaces and classes
The function spec for dwarf probes now supports scopes, so you can limit
the probes to specific namespaces or classes. Multiple scopes can be
specified, and they will be matched progressively outward.
probe process("foo").function("std::vector<*>::*") { ... }
probe process("foo").function("::global_function") { ... }
* dwflpp.cxx (dwflpp::get_parent_scope): New, finds the containing
scopes of the specified DIE.
(dwflpp::function_scope_matches): New, checks that the scopes
containing the function all match the given scope patterns.
* tapsets.cxx (dwarf_query::parse_function_spec): Rewrite, now handles
multiple scope separators too.
(query_dwarf_func): Check that the functions scopes match.
-rw-r--r-- | dwflpp.cxx | 58 | ||||
-rw-r--r-- | dwflpp.h | 3 | ||||
-rw-r--r-- | tapsets.cxx | 177 |
3 files changed, 167 insertions, 71 deletions
@@ -266,6 +266,33 @@ dwflpp::function_name_matches(const string& pattern) } +bool +dwflpp::function_scope_matches(const vector<string> scopes) +{ + // walk up the containing scopes + Dwarf_Die* die = function; + for (int i = scopes.size() - 1; i >= 0; --i) + { + die = get_parent_scope(die); + + // check if this scope matches, and prepend it if so + // NB: a NULL die is the global scope, compared as "" + string name = dwarf_diename(die) ?: ""; + if (name_has_wildcard(scopes[i]) ? + function_name_matches_pattern(name, scopes[i]) : + name == scopes[i]) + function_name = name + "::" + function_name; + else + return false; + + // make sure there's no more if we're at the global scope + if (!die && i > 0) + return false; + } + return true; +} + + static const char *offline_search_modname = NULL; static int offline_search_match_p = 0; @@ -591,6 +618,9 @@ dwflpp::cache_die_parents(cu_die_parent_cache_t* parents, Dwarf_Die* die) case DW_TAG_entry_point: case DW_TAG_inlined_subroutine: case DW_TAG_subprogram: + case DW_TAG_namespace: + case DW_TAG_class_type: + case DW_TAG_structure_type: parents->insert(make_pair(child.addr, *die)); cache_die_parents(parents, &child); break; @@ -743,6 +773,34 @@ dwflpp::getscopes(Dwarf_Addr pc) } +Dwarf_Die* +dwflpp::get_parent_scope(Dwarf_Die* die) +{ + Dwarf_Die specification; + if (dwarf_attr_die(die, DW_AT_specification, &specification)) + die = &specification; + + cu_die_parent_cache_t *parents = get_die_parents(); + cu_die_parent_cache_t::iterator it = parents->find(die->addr); + while (it != parents->end()) + { + Dwarf_Die* scope = &it->second; + switch (dwarf_tag (scope)) + { + case DW_TAG_namespace: + case DW_TAG_class_type: + case DW_TAG_structure_type: + return scope; + + default: + break; + } + it = parents->find(scope->addr); + } + return NULL; +} + + int dwflpp::global_alias_caching_callback(Dwarf_Die *die, void *arg) { @@ -192,6 +192,7 @@ struct dwflpp bool function_name_matches_pattern(const std::string& name, const std::string& pattern); bool function_name_matches(const std::string& pattern); + bool function_scope_matches(const std::vector<std::string> scopes); void iterate_over_modules(int (* callback)(Dwfl_Module *, void **, const char *, Dwarf_Addr, @@ -304,6 +305,8 @@ private: void cache_die_parents(cu_die_parent_cache_t* parents, Dwarf_Die* die); cu_die_parent_cache_t *get_die_parents(); + Dwarf_Die* get_parent_scope(Dwarf_Die* die); + /* The global alias cache is used to resolve any DIE found in a * module that is stubbed out with DW_AT_declaration with a defining * DIE found in a different module. The current assumption is that diff --git a/tapsets.cxx b/tapsets.cxx index 977a92fa..dd941251 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -591,8 +591,9 @@ struct dwarf_query : public base_query enum dbinfo_reqt dbinfo_reqt; enum dbinfo_reqt assess_dbinfo_reqt(); - function_spec_type parse_function_spec(string & spec); + void parse_function_spec(const string & spec); function_spec_type spec_type; + vector<string> scopes; string function; string file; line_t line_type; @@ -697,9 +698,9 @@ dwarf_query::dwarf_query(probe * base_probe, has_mark = false; if (has_function_str) - spec_type = parse_function_spec(function_str_val); + parse_function_spec(function_str_val); else if (has_statement_str) - spec_type = parse_function_spec(statement_str_val); + parse_function_spec(statement_str_val); dbinfo_reqt = assess_dbinfo_reqt(); query_done = false; @@ -869,93 +870,124 @@ dwarf_query::handle_query_module() } -function_spec_type -dwarf_query::parse_function_spec(string & spec) +void +dwarf_query::parse_function_spec(const string & spec) { - string::const_iterator i = spec.begin(), e = spec.end(); - - function.clear(); - file.clear(); - line[0] = 0; - line[1] = 0; + size_t src_pos, line_pos, dash_pos, scope_pos, next_scope_pos; - while (i != e && *i != '@') + // look for named scopes + scope_pos = 0; + next_scope_pos = spec.find("::"); + while (next_scope_pos != string::npos) { - if (*i == ':' || *i == '+') - goto bad; - function += *i++; + scopes.push_back(spec.substr(scope_pos, next_scope_pos - scope_pos)); + scope_pos = next_scope_pos + 2; + next_scope_pos = spec.find("::", scope_pos); } - if (i == e) + // look for a source separator + src_pos = spec.find('@', scope_pos); + if (src_pos == string::npos) { - if (sess.verbose>2) - clog << "parsed '" << spec - << "' -> func '" << function - << "'\n"; - return function_alone; + function = spec.substr(scope_pos); + spec_type = function_alone; } - - if (i++ == e) - goto bad; - - while (i != e && *i != ':' && *i != '+') - file += *i++; - if (*i == ':') + else { - if (*(i + 1) == '*') - line_type = WILDCARD; + function = spec.substr(scope_pos, src_pos - scope_pos); + + // look for a line-number separator + line_pos = spec.find_first_of(":+", src_pos); + if (line_pos == string::npos) + { + file = spec.substr(src_pos + 1); + spec_type = function_and_file; + } else - line_type = ABSOLUTE; - } - else if (*i == '+') - line_type = RELATIVE; + { + file = spec.substr(src_pos + 1, line_pos - src_pos - 1); + + // classify the line spec + spec_type = function_file_and_line; + if (spec[line_pos] == '+') + line_type = RELATIVE; + else if (spec[line_pos + 1] == '*' && + spec.length() == line_pos + 2) + line_type = WILDCARD; + else + line_type = ABSOLUTE; - if (i == e) - { - if (sess.verbose>2) - clog << "parsed '" << spec - << "' -> func '"<< function - << "', file '" << file - << "'\n"; - return function_and_file; + if (line_type != WILDCARD) + try + { + // try to parse either N or N-M + dash_pos = spec.find('-', line_pos + 1); + if (dash_pos == string::npos) + line[0] = line[1] = lex_cast<int>(spec.substr(line_pos + 1)); + else + { + line_type = RANGE; + line[0] = lex_cast<int>(spec.substr(line_pos + 1, + dash_pos - line_pos - 1)); + line[1] = lex_cast<int>(spec.substr(dash_pos + 1)); + } + } + catch (runtime_error & exn) + { + goto bad; + } + } } - if (i++ == e) + if (function.empty() || + (spec_type != function_alone && file.empty())) goto bad; - try + if (sess.verbose > 2) { - if (line_type != WILDCARD) - { - string::const_iterator dash = i; + clog << "parsed '" << spec << "'"; - while (dash != e && *dash != '-') - dash++; - if (dash == e) - line[0] = line[1] = lex_cast<int>(string(i, e)); - else - { - line_type = RANGE; - line[0] = lex_cast<int>(string(i, dash)); - line[1] = lex_cast<int>(string(dash + 1, e)); - } - } + if (!scopes.empty()) + clog << ", scope '" << scopes[0] << "'"; + for (unsigned i = 1; i < scopes.size(); ++i) + clog << "::'" << scopes[i] << "'"; - if (sess.verbose>2) - clog << "parsed '" << spec - << "' -> func '"<< function - << "', file '" << file - << "', line " << line << "\n"; - return function_file_and_line; - } - catch (runtime_error & exn) - { - goto bad; + clog << ", func '" << function << "'"; + + if (spec_type != function_alone) + clog << ", file '" << file << "'"; + + if (spec_type == function_file_and_line) + { + clog << ", line "; + switch (line_type) + { + case ABSOLUTE: + clog << line[0]; + break; + + case RELATIVE: + clog << "+" << line[0]; + break; + + case RANGE: + clog << line[0] << " - " << line[1]; + break; + + case WILDCARD: + clog << "*"; + break; + } + } + + clog << endl; } - bad: - throw semantic_error("malformed specification '" + spec + "'", - base_probe->tok); + return; + +bad: + throw semantic_error("malformed specification '" + spec + "'", + base_probe->tok); } @@ -1335,6 +1367,9 @@ query_dwarf_func (Dwarf_Die * func, base_query * bq) { q->dw.focus_on_function (func); + if (!q->dw.function_scope_matches(q->scopes)) + return DWARF_CB_OK; + // make sure that this function address hasn't // already been matched under an aliased name Dwarf_Addr addr; |