diff options
Diffstat (limited to 'tapsets.cxx')
-rw-r--r-- | tapsets.cxx | 1041 |
1 files changed, 663 insertions, 378 deletions
diff --git a/tapsets.cxx b/tapsets.cxx index 54b951cd..a45233fc 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -14,6 +14,7 @@ #include "translate.h" #include "session.h" #include "util.h" +#include "dwarf_wrappers.h" #include <cstdlib> #include <algorithm> @@ -61,7 +62,6 @@ extern "C" { using namespace std; using namespace __gnu_cxx; - // ------------------------------------------------------------------------ // Generic derived_probe_group: contains an ordinary vector of the // given type. It provides only the enrollment function. @@ -81,6 +81,10 @@ public: // begin/end/error probes are run right during registration / deregistration // ------------------------------------------------------------------------ +static string TOK_BEGIN("begin"); +static string TOK_END("end"); +static string TOK_ERROR("error"); + enum be_t { BEGIN, END, ERROR }; struct be_derived_probe: public derived_probe @@ -116,7 +120,6 @@ public: void emit_module_exit (systemtap_session& s); }; - struct be_builder: public derived_probe_builder { be_t type; @@ -126,13 +129,13 @@ struct be_builder: public derived_probe_builder virtual void build(systemtap_session &, probe * base, probe_point * location, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results) { int64_t priority; - if ((type == BEGIN && !get_param(parameters, "begin", priority)) || - (type == END && !get_param(parameters, "end", priority)) || - (type == ERROR && !get_param(parameters, "error", priority))) + if ((type == BEGIN && !get_param(parameters, TOK_BEGIN, priority)) || + (type == END && !get_param(parameters, TOK_END, priority)) || + (type == ERROR && !get_param(parameters, TOK_ERROR, priority))) priority = 0; finished_results.push_back( new be_derived_probe(base, location, type, priority)); @@ -414,6 +417,8 @@ be_derived_probe_group::emit_module_exit (systemtap_session& s) // never probes are never run // ------------------------------------------------------------------------ +static string TOK_NEVER("never"); + struct never_derived_probe: public derived_probe { never_derived_probe (probe* p): derived_probe (p) {} @@ -428,7 +433,7 @@ struct never_builder: public derived_probe_builder virtual void build(systemtap_session &, probe * base, probe_point * location, - std::map<std::string, literal *> const &, + literal_map_t const &, vector<derived_probe *> & finished_results) { finished_results.push_back(new never_derived_probe(base, location)); @@ -472,7 +477,7 @@ struct func_info { func_info() - : decl_file(NULL), decl_line(-1), addr(0), prologue_end(0) + : decl_file(NULL), decl_line(-1), addr(0), prologue_end(0), weak(false) { memset(&die, 0, sizeof(die)); } @@ -482,6 +487,7 @@ func_info Dwarf_Die die; Dwarf_Addr addr; Dwarf_Addr prologue_end; + bool weak; }; struct @@ -503,6 +509,7 @@ struct dwarf_query; // forward decls struct dwflpp; struct symbol_table; + struct module_info { @@ -559,13 +566,20 @@ symbol_table module_info *mod_info; // associated module map<string, func_info*> map_by_name; vector<func_info*> list_by_addr; +#ifdef __powerpc__ + GElf_Word opd_section; +#endif - void add_symbol(const char *name, Dwarf_Addr addr, Dwarf_Addr *high_addr); + void add_symbol(const char *name, bool weak, Dwarf_Addr addr, + Dwarf_Addr *high_addr); enum info_status read_symbols(FILE *f, const string& path); enum info_status read_from_elf_file(const string& path); enum info_status read_from_text_file(const string& path); enum info_status get_from_elf(); + void prepare_section_rejection(Dwfl_Module *mod); + bool reject_section(GElf_Word section); void mark_dwarf_redundancies(dwflpp *dw); + void purge_syscall_stubs(); func_info *lookup_symbol(const string& name); Dwarf_Addr lookup_symbol_address(const string& name); func_info *get_func_containing_address(Dwarf_Addr addr); @@ -595,6 +609,8 @@ dwarf_diename_integrate (Dwarf_Die *die) return dwarf_formstring (dwarf_attr_integrate (die, DW_AT_name, &attr_mem)); } +enum line_t { ABSOLUTE, RELATIVE, RANGE, WILDCARD }; + struct dwflpp { systemtap_session & sess; @@ -816,35 +832,6 @@ struct dwflpp return t; } - - // NB: "rc == 0" means OK in this case - static void dwfl_assert(string desc, int rc, string extra_msg = "") - { - string msg = "libdwfl failure (" + desc + "): "; - if (rc < 0) msg += dwfl_errmsg (rc); - else if (rc > 0) msg += strerror (rc); - if (rc != 0) - { - if (extra_msg.length() > 0) - msg += "\n" + extra_msg; - throw semantic_error (msg); - } - } - - void dwarf_assert(string desc, int rc) // NB: "rc == 0" means OK in this case - { - string msg = "libdw failure (" + desc + "): "; - if (rc < 0) msg += dwarf_errmsg (rc); - else if (rc > 0) msg += strerror (rc); - if (rc != 0) - throw semantic_error (msg); - } - - // static so pathname_caching_callback() can access them - static module_cache_t module_cache; - static bool ignore_vmlinux; - - dwflpp(systemtap_session & session) : sess(session), @@ -858,7 +845,6 @@ struct dwflpp cu(NULL), function(NULL) { - ignore_vmlinux = sess.ignore_vmlinux; } // Called by dwfl_linux_kernel_report_offline(). We may not have @@ -867,12 +853,16 @@ struct dwflpp // (Currently, we get all the elf info we need via elfutils -- if the // elf file exists -- so remembering the pathname isn't strictly needed. // But we still need to handle the case where there's no vmlinux.) + + static systemtap_session* this_session; // XXX: used only due to elfutils shortcoming + static int pathname_caching_callback(const char *name, const char *path) { module_info *mi = new module_info(name); - module_cache.cache[name] = mi; + assert (this_session); + this_session->module_cache->cache[name] = mi; - if (ignore_vmlinux && path && name == TOK_KERNEL) + if (this_session->ignore_vmlinux && path && name == TOK_KERNEL) { // report_kernel() in elfutils found vmlinux, but pretend it didn't. // Given a non-null path, returning 1 means keep reporting modules. @@ -894,12 +884,17 @@ struct dwflpp void setup(bool kernel, bool debuginfo_needed = true) { + if (! sess.module_cache) + sess.module_cache = new module_cache (); + // XXX: this is where the session -R parameter could come in static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug:build"; static char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH"); static char *debuginfo_path = (debuginfo_env_arr ? debuginfo_env_arr : debuginfo_path_arr); + static const char *debug_path = (debuginfo_env_arr ? + debuginfo_env_arr : sess.kernel_release.c_str()); static const Dwfl_Callbacks proc_callbacks = { @@ -925,17 +920,24 @@ struct dwflpp dwfl_report_begin (dwfl); int (*callback)(const char *name, const char *path); - if (sess.consult_symtab && !module_cache.paths_collected) + if (sess.consult_symtab && !sess.module_cache->paths_collected) { callback = pathname_caching_callback; - module_cache.paths_collected = true; + sess.module_cache->paths_collected = true; } else callback = NULL; + + // XXX: we should not need to set this static variable just + // for the callback. The following elfutils routine should + // take some void* parameter to pass context to the callback. + this_session = & sess; int rc = dwfl_linux_kernel_report_offline (dwfl, - sess.kernel_release.c_str(), + debug_path, /* selection predicate */ callback); + this_session = 0; + if (debuginfo_needed) dwfl_assert (string("missing kernel ") + sess.kernel_release + @@ -983,10 +985,13 @@ struct dwflpp Dwarf_Addr addr, void *param) { - module_cache_t *cache = static_cast<module_cache_t*>(param); + systemtap_session *sess = static_cast<systemtap_session*>(param); + module_cache_t *cache = sess->module_cache; module_info *mi = NULL; - if (ignore_vmlinux && name == TOK_KERNEL) + assert (cache); + + if (sess->ignore_vmlinux && name == TOK_KERNEL) // This wouldn't be called for vmlinux if vmlinux weren't there. return DWARF_CB_OK; @@ -1004,18 +1009,18 @@ struct dwflpp void cache_modules_dwarf() { - if (!module_cache.dwarf_collected) + if (!sess.module_cache->dwarf_collected) { ptrdiff_t off = 0; do { if (pending_interrupts) return; off = dwfl_getmodules (dwfl, module_caching_callback, - & module_cache, off); + & sess, off); } while (off > 0); - dwfl_assert("dwfl_getmodules", off); - module_cache.dwarf_collected = true; + dwfl_assert("dwfl_getmodules", off == 0); + sess.module_cache->dwarf_collected = true; } } @@ -1027,7 +1032,7 @@ struct dwflpp cache_modules_dwarf(); map<string, module_info*>::iterator i; - for (i = module_cache.cache.begin(); i != module_cache.cache.end(); i++) + for (i = sess.module_cache->cache.begin(); i != sess.module_cache->cache.end(); i++) { if (pending_interrupts) return; module_info *mi = i->second; @@ -1143,105 +1148,121 @@ struct dwflpp bool has_single_line_record (dwarf_query * q, char const * srcfile, int lineno); void iterate_over_srcfile_lines (char const * srcfile, - int lineno, + int lines[2], bool need_single_match, - void (* callback) (Dwarf_Line * line, void * arg), + enum line_t line_type, + void (* callback) (const dwarf_line_t& line, + void * arg), void *data) { Dwarf_Line **srcsp = NULL; size_t nsrcs = 0; dwarf_query * q = static_cast<dwarf_query *>(data); + int lineno = lines[0]; get_module_dwarf(); - dwarf_assert ("dwarf_getsrc_file", - dwarf_getsrc_file (module_dwarf, - srcfile, lineno, 0, - &srcsp, &nsrcs)); + if (line_type == RELATIVE) + { + Dwarf_Addr addr; + Dwarf_Line *line; + int line_number; + + dwarf_assert ("dwarf_entrypc", dwarf_entrypc (this->function, &addr)); + line = dwarf_getsrc_die (this->cu, addr); + dwarf_assert ("dwarf_getsrc_die", line == NULL); + dwarf_assert ("dwarf_lineno", dwarf_lineno (line, &line_number)); + lineno += line_number; + } + else if (line_type == WILDCARD) + function_line (&lineno); + + for (int l = lineno; ; l = l + 1) + { + dwarf_assert ("dwarf_getsrc_file", + dwarf_getsrc_file (module_dwarf, + srcfile, l, 0, + &srcsp, &nsrcs)); - // NB: Formerly, we used to filter, because: + if (line_type == WILDCARD || line_type == RANGE) + { + Dwarf_Addr line_addr; + dwarf_lineno (srcsp [0], &lineno); + if (lineno != l) + continue; + dwarf_lineaddr (srcsp [0], &line_addr); + if (dwarf_haspc (function, line_addr) != 1) + break; + } - // dwarf_getsrc_file gets one *near hits* for line numbers, not - // exact matches. For example, an existing file but a nonexistent - // line number will be rounded up to the next definition in that - // file. This may be similar to the GDB breakpoint algorithm, but - // we don't want to be so fuzzy in systemtap land. So we filter. + // NB: Formerly, we used to filter, because: - // But we now see the error of our ways, and skip this filtering. + // dwarf_getsrc_file gets one *near hits* for line numbers, not + // exact matches. For example, an existing file but a nonexistent + // line number will be rounded up to the next definition in that + // file. This may be similar to the GDB breakpoint algorithm, but + // we don't want to be so fuzzy in systemtap land. So we filter. - // XXX: the code also fails to match e.g. inline function - // definitions when the srcfile is a header file rather than the - // CU name. + // But we now see the error of our ways, and skip this filtering. - size_t remaining_nsrcs = nsrcs; -#if 0 - for (size_t i = 0; i < nsrcs; ++i) - { - int l_no; - Dwarf_Line* l = srcsp[i]; - dwarf_assert ("dwarf_lineno", dwarf_lineno (l, & l_no)); - if (l_no != lineno) - { - if (sess.verbose > 3) - clog << "skipping line number mismatch " - << "(" << l_no << " vs " << lineno << ")" - << " in file '" << srcfile << "'" - << "\n"; - srcsp[i] = 0; - remaining_nsrcs --; - } - } -#endif + // XXX: the code also fails to match e.g. inline function + // definitions when the srcfile is a header file rather than the + // CU name. - if (need_single_match && remaining_nsrcs > 1) - { - // We wanted a single line record (a unique address for the - // line) and we got a bunch of line records. We're going to - // skip this probe (throw an exception) but before we throw - // we're going to look around a bit to see if there's a low or - // high line number nearby which *doesn't* have this problem, - // so we can give the user some advice. - - int lo_try = -1; - int hi_try = -1; - for (size_t i = 1; i < 6; ++i) + size_t remaining_nsrcs = nsrcs; + + if (need_single_match && remaining_nsrcs > 1) { - if (lo_try == -1 && has_single_line_record(q, srcfile, lineno - i)) - lo_try = lineno - i; + // We wanted a single line record (a unique address for the + // line) and we got a bunch of line records. We're going to + // skip this probe (throw an exception) but before we throw + // we're going to look around a bit to see if there's a low or + // high line number nearby which *doesn't* have this problem, + // so we can give the user some advice. + + int lo_try = -1; + int hi_try = -1; + for (size_t i = 1; i < 6; ++i) + { + if (lo_try == -1 && has_single_line_record(q, srcfile, lineno - i)) + lo_try = lineno - i; - if (hi_try == -1 && has_single_line_record(q, srcfile, lineno + i)) - hi_try = lineno + i; - } + if (hi_try == -1 && has_single_line_record(q, srcfile, lineno + i)) + hi_try = lineno + i; + } - stringstream advice; - advice << "multiple addresses for " << srcfile << ":" << lineno; - if (lo_try > 0 || hi_try > 0) - { - advice << " (try "; - if (lo_try > 0) - advice << srcfile << ":" << lo_try; - if (lo_try > 0 && hi_try > 0) - advice << " or "; - if (hi_try > 0) - advice << srcfile << ":" << hi_try; - advice << ")"; - } - throw semantic_error (advice.str()); - } + stringstream advice; + advice << "multiple addresses for " << srcfile << ":" << lineno; + if (lo_try > 0 || hi_try > 0) + { + advice << " (try "; + if (lo_try > 0) + advice << srcfile << ":" << lo_try; + if (lo_try > 0 && hi_try > 0) + advice << " or "; + if (hi_try > 0) + advice << srcfile << ":" << hi_try; + advice << ")"; + } + throw semantic_error (advice.str()); + } - try - { - for (size_t i = 0; i < nsrcs; ++i) + try { - if (pending_interrupts) return; - if (srcsp [i]) // skip over mismatched lines - callback (srcsp[i], data); + for (size_t i = 0; i < nsrcs; ++i) + { + if (pending_interrupts) return; + if (srcsp [i]) // skip over mismatched lines + callback (dwarf_line_t(srcsp[i]), data); + } } - } - catch (...) - { - free (srcsp); - throw; + catch (...) + { + free (srcsp); + throw; + } + if (line_type != WILDCARD || l == lines[1]) + break; } free (srcsp); } @@ -1316,23 +1337,23 @@ struct dwflpp if (func->decl_file == 0) func->decl_file = ""; unsigned entrypc_srcline_idx = 0; - Dwarf_Line* entrypc_srcline = 0; + dwarf_line_t entrypc_srcline; // open-code binary search for exact match { unsigned l = 0, h = nlines; while (l < h) { entrypc_srcline_idx = (l + h) / 2; - Dwarf_Addr addr; - Dwarf_Line *lr = dwarf_onesrcline(lines, entrypc_srcline_idx); - dwarf_lineaddr (lr, &addr); + const dwarf_line_t lr(dwarf_onesrcline(lines, + entrypc_srcline_idx)); + Dwarf_Addr addr = lr.addr(); if (addr == entrypc) { entrypc_srcline = lr; break; } else if (l + 1 == h) { break; } // ran off bottom of tree else if (addr < entrypc) { l = entrypc_srcline_idx; } else { h = entrypc_srcline_idx; } } } - if (entrypc_srcline == 0) + if (!entrypc_srcline) throw semantic_error ("missing entrypc dwarf line record for function '" + func->name + "'"); @@ -1357,13 +1378,10 @@ struct dwflpp bool ranoff_end = false; while (postprologue_srcline_idx < nlines) { - Dwarf_Addr postprologue_addr; - Dwarf_Line *lr = dwarf_onesrcline(lines, postprologue_srcline_idx); - dwarf_lineaddr (lr, &postprologue_addr); - const char* postprologue_file = dwarf_linesrc (lr, NULL, NULL); - int postprologue_lineno; - dwfl_assert ("dwarf_lineno", - dwarf_lineno (lr, & postprologue_lineno)); + dwarf_line_t lr(dwarf_onesrcline(lines, postprologue_srcline_idx)); + Dwarf_Addr postprologue_addr = lr.addr(); + const char* postprologue_file = lr.linesrc(); + int postprologue_lineno = lr.lineno(); if (sess.verbose>2) clog << "checking line record 0x" << hex << postprologue_addr << dec @@ -1485,9 +1503,9 @@ struct dwflpp dwarf_decl_line (function, linep); } - bool die_has_pc (Dwarf_Die * die, Dwarf_Addr pc) + bool die_has_pc (Dwarf_Die & die, Dwarf_Addr pc) { - int res = dwarf_haspc (die, pc); + int res = dwarf_haspc (&die, pc); if (res == -1) dwarf_assert ("dwarf_haspc", res); return res == 1; @@ -1524,19 +1542,19 @@ struct dwflpp // We emit a comment approximating the variable+offset expression that // relocatable module probing code will need to have. Dwfl_Module *mod = dwfl_addrmodule (dwfl, address); - dwfl_assert ("dwfl_addrmodule", mod == NULL); + dwfl_assert ("dwfl_addrmodule", mod); int n = dwfl_module_relocations (mod); - dwfl_assert ("dwfl_module_relocations", n < 0); + dwfl_assert ("dwfl_module_relocations", n >= 0); int i = dwfl_module_relocate_address (mod, &address); - dwfl_assert ("dwfl_module_relocate_address", i < 0); + dwfl_assert ("dwfl_module_relocate_address", i >= 0); const char *modname = dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - dwfl_assert ("dwfl_module_info", modname == NULL); + dwfl_assert ("dwfl_module_info", modname); const char *secname = dwfl_module_relocation_info (mod, i, NULL); if (n > 0 && !(n == 1 && secname == NULL)) { - dwfl_assert ("dwfl_module_relocation_info", secname == NULL); + dwfl_assert ("dwfl_module_relocation_info", secname); if (n > 1 || secname[0] != '\0') { // This gives us the module name, and section name within the @@ -1581,7 +1599,6 @@ struct dwflpp void print_locals(Dwarf_Die *die, ostream &o) { // Try to get the first child of die. - bool local_found = false; Dwarf_Die child; if (dwarf_child (die, &child) == 0) { @@ -1594,7 +1611,6 @@ struct dwflpp case DW_TAG_variable: case DW_TAG_formal_parameter: o << " " << dwarf_diename (&child); - local_found = true; break; default: break; @@ -1602,9 +1618,6 @@ struct dwflpp } while (dwarf_siblingof (&child, &child) == 0); } - - if (! local_found) - o << " (none found)"; } Dwarf_Attribute * @@ -1642,8 +1655,7 @@ struct dwflpp print_locals (scopes, alternatives); throw semantic_error ("unable to find local '" + local + "'" + " near pc " + lex_cast_hex<string>(pc) - + " (alternatives:" + alternatives.str () - + ")"); + + (alternatives.str() == "" ? "" : (" (alternatives:" + alternatives.str () + ")"))); } for (int inner = 0; inner < nscopes; ++inner) @@ -2177,8 +2189,10 @@ struct dwflpp } }; -module_cache_t dwflpp::module_cache; -bool dwflpp::ignore_vmlinux = false; + +systemtap_session* dwflpp::this_session = 0; // XXX: used only due to elfutils shortcoming + + enum @@ -2252,7 +2266,7 @@ struct base_query probe * base_probe, probe_point * base_loc, dwflpp & dw, - map<string, literal *> const & params, + literal_map_t const & params, vector<derived_probe *> & results); virtual ~base_query() {} @@ -2263,13 +2277,13 @@ struct base_query vector<derived_probe *> & results; // Parameter extractors. - static bool has_null_param(map<string, literal *> const & params, + static bool has_null_param(literal_map_t const & params, string const & k); - static bool get_string_param(map<string, literal *> const & params, + static bool get_string_param(literal_map_t const & params, string const & k, string & v); - static bool get_number_param(map<string, literal *> const & params, + static bool get_number_param(literal_map_t const & params, string const & k, long & v); - static bool get_number_param(map<string, literal *> const & params, + static bool get_number_param(literal_map_t const & params, string const & k, Dwarf_Addr & v); // Extracted parameters. @@ -2284,7 +2298,7 @@ base_query::base_query(systemtap_session & sess, probe * base_probe, probe_point * base_loc, dwflpp & dw, - map<string, literal *> const & params, + literal_map_t const & params, vector<derived_probe *> & results) : sess(sess), base_probe(base_probe), base_loc(base_loc), dw(dw), results(results) @@ -2301,7 +2315,7 @@ base_query::base_query(systemtap_session & sess, } bool -base_query::has_null_param(map<string, literal *> const & params, +base_query::has_null_param(literal_map_t const & params, string const & k) { return derived_probe_builder::has_null_param(params, k); @@ -2309,7 +2323,7 @@ base_query::has_null_param(map<string, literal *> const & params, bool -base_query::get_string_param(map<string, literal *> const & params, +base_query::get_string_param(literal_map_t const & params, string const & k, string & v) { return derived_probe_builder::get_param (params, k, v); @@ -2317,7 +2331,7 @@ base_query::get_string_param(map<string, literal *> const & params, bool -base_query::get_number_param(map<string, literal *> const & params, +base_query::get_number_param(literal_map_t const & params, string const & k, long & v) { int64_t value; @@ -2328,7 +2342,7 @@ base_query::get_number_param(map<string, literal *> const & params, bool -base_query::get_number_param(map<string, literal *> const & params, +base_query::get_number_param(literal_map_t const & params, string const & k, Dwarf_Addr & v) { int64_t value; @@ -2337,6 +2351,8 @@ base_query::get_number_param(map<string, literal *> const & params, return present; } +typedef map<Dwarf_Addr, inline_instance_info> inline_instance_map_t; +typedef map<Dwarf_Addr, func_info> func_info_map_t; struct dwarf_query : public base_query { @@ -2344,7 +2360,7 @@ struct dwarf_query : public base_query probe * base_probe, probe_point * base_loc, dwflpp & dw, - map<string, literal *> const & params, + literal_map_t const & params, vector<derived_probe *> & results); virtual void handle_query_module(); @@ -2404,14 +2420,15 @@ struct dwarf_query : public base_query function_spec_type spec_type; string function; string file; - int line; + line_t line_type; + int line[2]; bool query_done; // Found exact match set<char const *> filtered_srcfiles; // Map official entrypc -> func_info object - map<Dwarf_Addr, inline_instance_info> filtered_inlines; - map<Dwarf_Addr, func_info> filtered_functions; + inline_instance_map_t filtered_inlines; + func_info_map_t filtered_functions; bool choose_next_line; Dwarf_Addr entrypc_for_next_line; }; @@ -2451,14 +2468,13 @@ dwflpp::has_single_line_record (dwarf_query * q, char const * srcfile, int linen // We also try to filter out lines that leave the selected // functions (if any). - Dwarf_Line *line = srcsp[0]; - Dwarf_Addr addr; - dwarf_lineaddr (line, &addr); + dwarf_line_t line(srcsp[0]); + Dwarf_Addr addr = line.addr(); - for (map<Dwarf_Addr, func_info>::iterator i = q->filtered_functions.begin(); + for (func_info_map_t::iterator i = q->filtered_functions.begin(); i != q->filtered_functions.end(); ++i) { - if (q->dw.die_has_pc (&(i->second.die), addr)) + if (q->dw.die_has_pc (i->second.die, addr)) { if (q->sess.verbose>4) clog << "alternative line " << lineno << " accepted: fn=" << i->second.name << endl; @@ -2466,10 +2482,10 @@ dwflpp::has_single_line_record (dwarf_query * q, char const * srcfile, int linen } } - for (map<Dwarf_Addr, inline_instance_info>::iterator i = q->filtered_inlines.begin(); + for (inline_instance_map_t::iterator i = q->filtered_inlines.begin(); i != q->filtered_inlines.end(); ++i) { - if (q->dw.die_has_pc (&(i->second.die), addr)) + if (q->dw.die_has_pc (i->second.die, addr)) { if (sess.verbose>4) clog << "alternative line " << lineno << " accepted: ifn=" << i->second.name << endl; @@ -2570,7 +2586,7 @@ struct dwarf_builder: public derived_probe_builder virtual void build(systemtap_session & sess, probe * base, probe_point * location, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results); }; @@ -2579,7 +2595,7 @@ dwarf_query::dwarf_query(systemtap_session & sess, probe * base_probe, probe_point * base_loc, dwflpp & dw, - map<string, literal *> const & params, + literal_map_t const & params, vector<derived_probe *> & results) : base_query(sess, base_probe, base_loc, dw, params, results) { @@ -2871,11 +2887,12 @@ dwarf_query::parse_function_spec(string & spec) function.clear(); file.clear(); - line = 0; + line[0] = 0; + line[1] = 0; while (i != e && *i != '@') { - if (*i == ':') + if (*i == ':' || *i == '+') goto bad; function += *i++; } @@ -2892,8 +2909,17 @@ dwarf_query::parse_function_spec(string & spec) if (i++ == e) goto bad; - while (i != e && *i != ':') + while (i != e && *i != ':' && *i != '+') file += *i++; + if (*i == ':') + { + if (*(i + 1) == '*') + line_type = WILDCARD; + else + line_type = ABSOLUTE; + } + else if (*i == '+') + line_type = RELATIVE; if (i == e) { @@ -2910,7 +2936,22 @@ dwarf_query::parse_function_spec(string & spec) try { - line = lex_cast<int>(string(i, e)); + if (line_type != WILDCARD) + { + string::const_iterator dash = i; + + 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 (sess.verbose>2) clog << "parsed '" << spec << "' -> func '"<< function @@ -3016,7 +3057,7 @@ string dwarf_query::get_blacklist_section(Dwarf_Addr addr) { Elf_Scn* scn = 0; size_t shstrndx; - dw.dwfl_assert ("getshstrndx", elf_getshstrndx (elf, &shstrndx)); + dwfl_assert ("getshstrndx", elf_getshstrndx (elf, &shstrndx)); while ((scn = elf_nextscn (elf, scn)) != NULL) { GElf_Shdr shdr_mem; @@ -3268,20 +3309,18 @@ query_func_info (Dwarf_Addr entrypc, static void -query_srcfile_line (Dwarf_Line * line, void * arg) +query_srcfile_line (const dwarf_line_t& line, void * arg) { dwarf_query * q = static_cast<dwarf_query *>(arg); - Dwarf_Addr addr; - dwarf_lineaddr(line, &addr); + Dwarf_Addr addr = line.addr(); - int lineno; - dwarf_lineno (line, &lineno); + int lineno = line.lineno(); - for (map<Dwarf_Addr, func_info>::iterator i = q->filtered_functions.begin(); + for (func_info_map_t::iterator i = q->filtered_functions.begin(); i != q->filtered_functions.end(); ++i) { - if (q->dw.die_has_pc (&(i->second.die), addr)) + if (q->dw.die_has_pc (i->second.die, addr)) { if (q->sess.verbose>3) clog << "function DIE lands on srcfile\n"; @@ -3294,17 +3333,17 @@ query_srcfile_line (Dwarf_Line * line, void * arg) } } - for (map<Dwarf_Addr, inline_instance_info>::iterator i + for (inline_instance_map_t::iterator i = q->filtered_inlines.begin(); i != q->filtered_inlines.end(); ++i) { - if (q->dw.die_has_pc (&(i->second.die), addr)) + if (q->dw.die_has_pc (i->second.die, addr)) { if (q->sess.verbose>3) clog << "inline instance DIE lands on srcfile\n"; if (q->has_statement_str) query_statement (i->second.name, i->second.decl_file, - q->line, &(i->second.die), addr, q); + q->line[0], &(i->second.die), addr, q); else query_inline_instance_info (i->first, i->second, q); } @@ -3390,7 +3429,7 @@ query_dwarf_func (Dwarf_Die * func, void * arg) Dwarf_Die d; q->dw.function_die (&d); - if (q->dw.die_has_pc (&d, query_addr)) + if (q->dw.die_has_pc (d, query_addr)) record_this_function = true; } @@ -3476,7 +3515,24 @@ query_cu (Dwarf_Die * cudie, void * arg) if (q->filtered_srcfiles.empty()) return DWARF_CB_OK; } - + // Verify that a raw address matches the beginning of a + // statement. This is a somewhat lame check that the address + // is at the start of an assembly instruction. + if (q->has_statement_num) + { + Dwarf_Addr queryaddr = q->statement_num_val; + dwarf_line_t address_line(dwarf_getsrc_die(cudie, queryaddr)); + Dwarf_Addr lineaddr = 0; + if (address_line) + lineaddr = address_line.addr(); + if (!address_line || lineaddr != queryaddr) + { + stringstream msg; + msg << "address 0x" << hex << queryaddr + << "does not match the begining of a statement"; + throw semantic_error(msg.str()); + } + } // Pick up [entrypc, name, DIE] tuples for all the functions // matching the query, and fill in the prologue endings of them // all in a single pass. @@ -3497,18 +3553,18 @@ query_cu (Dwarf_Die * cudie, void * arg) for (set<char const *>::const_iterator i = q->filtered_srcfiles.begin(); i != q->filtered_srcfiles.end(); ++i) q->dw.iterate_over_srcfile_lines (*i, q->line, q->has_statement_str, - query_srcfile_line, q); + q->line_type, query_srcfile_line, q); } else { // Otherwise, simply probe all resolved functions. - for (map<Dwarf_Addr, func_info>::iterator i = q->filtered_functions.begin(); + for (func_info_map_t::iterator i = q->filtered_functions.begin(); i != q->filtered_functions.end(); ++i) query_func_info (i->first, i->second, q); // And all inline instances (if we're not excluding inlines with ".call") if (! q->has_call) - for (map<Dwarf_Addr, inline_instance_info>::iterator i + for (inline_instance_map_t::iterator i = q->filtered_inlines.begin(); i != q->filtered_inlines.end(); ++i) query_inline_instance_info (i->first, i->second, q); } @@ -3576,7 +3632,7 @@ validate_module_elf (Dwfl_Module *mod, const char *name, base_query *q) GElf_Ehdr ehdr_mem; GElf_Ehdr* em = gelf_getehdr (elf, &ehdr_mem); - if (em == 0) { q->dw.dwfl_assert ("dwfl_getehdr", dwfl_errno()); } + if (em == 0) { dwfl_assert ("dwfl_getehdr", dwfl_errno()); } int elf_machine = em->e_machine; const char* debug_filename = ""; const char* main_filename = ""; @@ -3681,8 +3737,8 @@ dwflpp::query_modules(dwarf_query *q) { cache_modules_dwarf(); - map<string, module_info*>::iterator i = module_cache.cache.find(name); - if (i != module_cache.cache.end()) + map<string, module_info*>::iterator i = sess.module_cache->cache.find(name); + if (i != sess.module_cache->cache.end()) { module_info *mi = i->second; query_module(mi->mod, mi, name.c_str(), mi->addr, q); @@ -4621,7 +4677,7 @@ void dwarf_builder::build(systemtap_session & sess, probe * base, probe_point * location, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results) { // NB: the kernel/user dwlfpp objects are long-lived. @@ -4697,12 +4753,18 @@ symbol_table::~symbol_table() } void -symbol_table::add_symbol(const char *name, Dwarf_Addr addr, - Dwarf_Addr *high_addr) +symbol_table::add_symbol(const char *name, bool weak, Dwarf_Addr addr, + Dwarf_Addr *high_addr) { +#ifdef __powerpc__ + // Map ".sys_foo" to "sys_foo". + if (name[0] == '.') + name++; +#endif func_info *fi = new func_info(); fi->addr = addr; fi->name = name; + fi->weak = weak; map_by_name[fi->name] = fi; // TODO: Use a multimap in case there are multiple static // functions with the same name? @@ -4753,8 +4815,8 @@ symbol_table::read_symbols(FILE *f, const string& path) free(mod); goto done; } - if (type == 'T' || type == 't') - add_symbol(name, (Dwarf_Addr) addr, &high_addr); + if (type == 'T' || type == 't' || type == 'W') + add_symbol(name, (type == 'W'), (Dwarf_Addr) addr, &high_addr); free(name); } @@ -4809,6 +4871,55 @@ symbol_table::read_from_text_file(const string& path) return status; } +void +symbol_table::prepare_section_rejection(Dwfl_Module *mod) +{ +#ifdef __powerpc__ + /* + * The .opd section contains function descriptors that can look + * just like function entry points. For example, there's a function + * descriptor called "do_exit" that links to the entry point ".do_exit". + * Reject all symbols in .opd. + */ + opd_section = SHN_UNDEF; + Dwarf_Addr bias; + Elf* elf = (dwarf_getelf (dwfl_module_getdwarf (mod, &bias)) + ?: dwfl_module_getelf (mod, &bias)); + Elf_Scn* scn = 0; + size_t shstrndx; + + if (!elf) + return; + if (elf_getshstrndx(elf, &shstrndx) != 0) + return; + while ((scn = elf_nextscn(elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr(scn, &shdr_mem); + if (!shdr) + continue; + const char *name = elf_strptr(elf, shstrndx, shdr->sh_name); + if (!strcmp(name, ".opd")) + { + opd_section = elf_ndxscn(scn); + return; + } + } +#endif +} + +bool +symbol_table::reject_section(GElf_Word section) +{ + if (section == SHN_UNDEF) + return true; +#ifdef __powerpc__ + if (section == opd_section) + return true; +#endif + return false; +} + enum info_status symbol_table::get_from_elf() { @@ -4816,12 +4927,16 @@ symbol_table::get_from_elf() Dwfl_Module *mod = mod_info->mod; int syments = dwfl_module_getsymtab(mod); assert(syments); + prepare_section_rejection(mod); for (int i = 1; i < syments; ++i) { GElf_Sym sym; - const char *name = dwfl_module_getsym(mod, i, &sym, NULL); - if (name && GELF_ST_TYPE(sym.st_info) == STT_FUNC) - add_symbol(name, sym.st_value, &high_addr); + GElf_Word section; + const char *name = dwfl_module_getsym(mod, i, &sym, §ion); + if (name && GELF_ST_TYPE(sym.st_info) == STT_FUNC && + !reject_section(section)) + add_symbol(name, (GELF_ST_BIND(sym.st_info) == STB_WEAK), + sym.st_value, &high_addr); } return info_present; } @@ -4913,6 +5028,33 @@ symbol_table::lookup_symbol_address(const string& name) return 0; } +// This is the kernel symbol table. The kernel macro cond_syscall creates +// a weak symbol for each system call and maps it to sys_ni_syscall. +// For system calls not implemented elsewhere, this weak symbol shows up +// in the kernel symbol table. Following the precedent of dwarfful stap, +// we refuse to consider such symbols. Here we delete them from our +// symbol table. +// TODO: Consider generalizing this and/or making it part of blacklist +// processing. +void +symbol_table::purge_syscall_stubs() +{ + Dwarf_Addr stub_addr = lookup_symbol_address("sys_ni_syscall"); + if (stub_addr == 0) + return; + for (size_t i = 0; i < list_by_addr.size(); i++) + { + func_info *fi = list_by_addr.at(i); + if (fi->weak && fi->addr == stub_addr && fi->name != "sys_ni_syscall") + { + list_by_addr.erase(list_by_addr.begin()+i); + map_by_name.erase(fi->name); + delete fi; + i--; + } + } +} + void module_info::get_symtab(dwarf_query *q) { @@ -4968,6 +5110,9 @@ module_info::get_symtab(dwarf_query *q) // precedes the call to query_module_symtab(). So we should never read // a module's symbol table without first having tried to get its dwarf. sym_table->mark_dwarf_redundancies(&q->dw); + + if (name == TOK_KERNEL) + sym_table->purge_syscall_stubs(); } module_info::~module_info() @@ -5052,25 +5197,19 @@ task_finder_derived_probe_group::emit_module_exit (systemtap_session& s) // utrace user-space probes // ------------------------------------------------------------------------ -// Since we don't have access to <linux/utrace.h>, we'll have to -// define our own version of the UTRACE_EVENT flags. +static string TOK_THREAD("thread"); +static string TOK_SYSCALL("syscall"); + +// Note that these flags don't match up exactly with UTRACE_EVENT +// flags (and that's OK). enum utrace_derived_probe_flags { UDPF_NONE, - UDPF_QUIESCE, // UTRACE_EVENT(QUIESCE) - UDPF_REAP, // UTRACE_EVENT(REAP) - UDPF_CLONE, // UTRACE_EVENT(CLONE) - UDPF_VFORK_DONE, // UTRACE_EVENT(VFORK_DONE) - UDPF_EXEC, // UTRACE_EVENT(EXEC) - UDPF_EXIT, // UTRACE_EVENT(EXIT) - UDPF_DEATH, // UTRACE_EVENT(DEATH) - UDPF_SYSCALL_ENTRY, // UTRACE_EVENT(SYSCALL_ENTRY) - UDPF_SYSCALL_EXIT, // UTRACE_EVENT(SYSCALL_EXIT) - UDPF_SIGNAL, // UTRACE_EVENT(SIGNAL) - UDPF_SIGNAL_IGN, // UTRACE_EVENT(SIGNAL_IGN) - UDPF_SIGNAL_STOP, // UTRACE_EVENT(SIGNAL_STOP) - UDPF_SIGNAL_TERM, // UTRACE_EVENT(SIGNAL_TERM) - UDPF_SIGNAL_CORE, // UTRACE_EVENT(SIGNAL_CORE) - UDPF_JCTL, // UTRACE_EVENT(JCTL) + UDPF_BEGIN, // process begin + UDPF_END, // process end + UDPF_THREAD_BEGIN, // thread begin + UDPF_THREAD_END, // thread end + UDPF_SYSCALL, // syscall entry + UDPF_SYSCALL_RETURN, // syscall exit UDPF_NFLAGS }; @@ -5100,6 +5239,9 @@ private: bool flags_seen[UDPF_NFLAGS]; void emit_probe_decl (systemtap_session& s, utrace_derived_probe *p); + void emit_vm_callback_probe_decl (systemtap_session& s, bool has_path, + string path, int64_t pid, + string vm_callback); public: utrace_derived_probe_group(): num_probes(0), flags_seen() { } @@ -5144,15 +5286,9 @@ void utrace_derived_probe::join_group (systemtap_session& s) { if (! s.utrace_derived_probes) - { + { s.utrace_derived_probes = new utrace_derived_probe_group (); - - // Make sure <linux/tracehook.h> is included early. - embeddedcode *ec = new embeddedcode; - ec->tok = NULL; - ec->code = string("#include <linux/tracehook.h>\n"); - s.embeds.push_back(ec); - } + } s.utrace_derived_probes->enroll (this); task_finder_derived_probe_group::create_session_group (s); @@ -5164,7 +5300,7 @@ utrace_var_expanding_copy_visitor::visit_target_symbol (target_symbol* e) { assert(e->base_name.size() > 0 && e->base_name[0] == '$'); - if (flags != UDPF_SYSCALL_ENTRY && flags != UDPF_SYSCALL_EXIT) + if (flags != UDPF_SYSCALL && flags != UDPF_SYSCALL_RETURN) throw semantic_error ("only \"process(PATH_OR_PID).syscall\" and \"process(PATH_OR_PID).syscall.return\" probes support target symbols", e->tok); @@ -5198,28 +5334,11 @@ utrace_var_expanding_copy_visitor::visit_target_symbol (target_symbol* e) // Remember that we've seen a target variable. target_symbol_seen = true; - // Synthesize a function. - functiondecl *fdecl = new functiondecl; - fdecl->tok = e->tok; - embeddedcode *ec = new embeddedcode; - ec->tok = e->tok; - - string fname = (string("_utrace_syscall_get") + "_" - + lex_cast<string>(tick++)); - string locvalue = "CONTEXT->data"; - - ec->code = string("THIS->__retvalue = *tracehook_syscall_callno(CONTEXT->regs); /* pure */"); - - fdecl->name = fname; - fdecl->body = ec; - fdecl->type = pe_long; - - sess.functions.push_back(fdecl); - - // Synthesize a functioncall. + // We're going to substitute a synthesized 'syscall_nr' function + // call for the '$syscall' reference. functioncall* n = new functioncall; n->tok = e->tok; - n->function = fname; + n->function = "syscall_nr"; n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session provide <functioncall*> (this, n); @@ -5232,7 +5351,7 @@ struct utrace_builder: public derived_probe_builder virtual void build(systemtap_session & sess, probe * base, probe_point * location, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results) { string path; @@ -5243,19 +5362,24 @@ struct utrace_builder: public derived_probe_builder enum utrace_derived_probe_flags flags = UDPF_NONE; assert (has_path || has_pid); - if (has_null_param (parameters, "death")) - flags = UDPF_DEATH; - else if (has_null_param (parameters, "syscall")) + if (has_null_param (parameters, TOK_THREAD)) + { + if (has_null_param (parameters, TOK_BEGIN)) + flags = UDPF_THREAD_BEGIN; + else if (has_null_param (parameters, TOK_END)) + flags = UDPF_THREAD_END; + } + else if (has_null_param (parameters, TOK_SYSCALL)) { if (has_null_param (parameters, TOK_RETURN)) - flags = UDPF_SYSCALL_EXIT; + flags = UDPF_SYSCALL_RETURN; else - flags = UDPF_SYSCALL_ENTRY; + flags = UDPF_SYSCALL; } - else if (has_null_param (parameters, "clone")) - flags = UDPF_CLONE; - else if (has_null_param (parameters, "exec")) - flags = UDPF_EXEC; + else if (has_null_param (parameters, TOK_BEGIN)) + flags = UDPF_BEGIN; + else if (has_null_param (parameters, TOK_END)) + flags = UDPF_END; // If we have a path, we need to validate it. if (has_path) @@ -5338,6 +5462,7 @@ utrace_derived_probe_group::emit_probe_decl (systemtap_session& s, } s.op->line() << " .callback=&_stp_utrace_probe_cb,"; + s.op->line() << " .vm_callback=NULL,"; s.op->line() << " },"; s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ","; s.op->line() << " .ph=&" << p->name << ","; @@ -5345,29 +5470,46 @@ utrace_derived_probe_group::emit_probe_decl (systemtap_session& s, // Handle flags switch (p->flags) { - case UDPF_CLONE: - s.op->line() << " .ops={ .report_clone=stap_utrace_probe_clone, .report_death=stap_utrace_task_finder_report_death },"; - s.op->line() << " .flags=(UTRACE_EVENT(CLONE)|UTRACE_EVENT(DEATH)),"; + // Look in _stp_utrace_probe_cb for description of why quiesce is + // used here. + case UDPF_BEGIN: // process begin + s.op->line() << " .flags=(UDPF_BEGIN),"; + s.op->line() << " .ops={ .report_quiesce=stap_utrace_probe_quiesce },"; + s.op->line() << " .events=(UTRACE_ACTION_QUIESCE|UTRACE_EVENT(QUIESCE)),"; break; - case UDPF_EXEC: - // Notice we're not setting up a .ops/.report_exec handler here. - // Instead, we'll just call the probe directly when we get - // notified the exec happened. - s.op->line() << " .flags=(UTRACE_EVENT(EXEC)),"; + case UDPF_THREAD_BEGIN: // thread begin + s.op->line() << " .flags=(UDPF_THREAD_BEGIN),"; + s.op->line() << " .ops={ .report_quiesce=stap_utrace_probe_quiesce },"; + s.op->line() << " .events=(UTRACE_ACTION_QUIESCE|UTRACE_EVENT(QUIESCE)),"; break; - case UDPF_DEATH: - // Notice we're not setting up a .ops/.report_death handler - // here. Instead, we'll just call the probe directly when we - // get notified the death happened. - s.op->line() << " .flags=(UTRACE_EVENT(DEATH)),"; + + // Notice we're not setting up a .ops/.report_death handler for + // either UDPF_END or UDPF_THREAD_END. Instead, we'll just call + // the probe directly when we get notified. + case UDPF_END: // process end + s.op->line() << " .flags=(UDPF_END),"; break; - case UDPF_SYSCALL_ENTRY: + case UDPF_THREAD_END: // thread end + s.op->line() << " .flags=(UDPF_THREAD_END),"; + break; + + // For UDPF_SYSCALL/UDPF_SYSCALL_RETURN probes, the .report_death + // handler isn't strictly necessary. However, it helps to keep + // our attaches/detaches symmetrical. + case UDPF_SYSCALL: + s.op->line() << " .flags=(UDPF_SYSCALL),"; s.op->line() << " .ops={ .report_syscall_entry=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death },"; - s.op->line() << " .flags=(UTRACE_EVENT(SYSCALL_ENTRY)|UTRACE_EVENT(DEATH)),"; + s.op->line() << " .events=(UTRACE_EVENT(SYSCALL_ENTRY)|UTRACE_EVENT(DEATH)),"; break; - case UDPF_SYSCALL_EXIT: + case UDPF_SYSCALL_RETURN: + s.op->line() << " .flags=(UDPF_SYSCALL_RETURN),"; s.op->line() << " .ops={ .report_syscall_exit=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death },"; - s.op->line() << " .flags=(UTRACE_EVENT(SYSCALL_EXIT)|UTRACE_EVENT(DEATH)),"; + s.op->line() << " .events=(UTRACE_EVENT(SYSCALL_EXIT)|UTRACE_EVENT(DEATH)),"; + break; + case UDPF_NONE: + s.op->line() << " .flags=(UDPF_NONE),"; + s.op->line() << " .ops={ },"; + s.op->line() << " .events=0,"; break; default: throw semantic_error ("bad utrace probe flag"); @@ -5379,6 +5521,40 @@ utrace_derived_probe_group::emit_probe_decl (systemtap_session& s, void +utrace_derived_probe_group::emit_vm_callback_probe_decl (systemtap_session& s, + bool has_path, + string path, + int64_t pid, + string vm_callback) +{ + s.op->newline() << "{"; + s.op->line() << " .tgt={"; + + if (has_path) + { + s.op->line() << " .pathname=\"" << path << "\","; + s.op->line() << " .pid=0,"; + } + else + { + s.op->line() << " .pathname=NULL,"; + s.op->line() << " .pid=" << pid << ","; + } + + s.op->line() << " .callback=NULL,"; + s.op->line() << " .vm_callback=&" << vm_callback << ","; + s.op->line() << " },"; + s.op->line() << " .pp=\"internal\","; + s.op->line() << " .ph=NULL,"; + s.op->line() << " .flags=(UDPF_NONE),"; + s.op->line() << " .ops={ NULL },"; + s.op->line() << " .events=0,"; + s.op->line() << " .engine_attached=0,"; + s.op->line() << " },"; +} + + +void utrace_derived_probe_group::emit_module_decls (systemtap_session& s) { if (probes_by_path.empty() && probes_by_pid.empty()) @@ -5386,20 +5562,34 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline(); s.op->newline() << "/* ---- utrace probes ---- */"; + s.op->newline() << "enum utrace_derived_probe_flags {"; + s.op->indent(1); + s.op->newline() << "UDPF_NONE,"; + s.op->newline() << "UDPF_BEGIN,"; + s.op->newline() << "UDPF_END,"; + s.op->newline() << "UDPF_THREAD_BEGIN,"; + s.op->newline() << "UDPF_THREAD_END,"; + s.op->newline() << "UDPF_SYSCALL,"; + s.op->newline() << "UDPF_SYSCALL_RETURN,"; + s.op->newline() << "UDPF_NFLAGS"; + s.op->newline(-1) << "};"; + s.op->newline() << "struct stap_utrace_probe {"; s.op->indent(1); s.op->newline() << "struct stap_task_finder_target tgt;"; s.op->newline() << "const char *pp;"; s.op->newline() << "void (*ph) (struct context*);"; + s.op->newline() << "enum utrace_derived_probe_flags flags;"; s.op->newline() << "struct utrace_engine_ops ops;"; - s.op->newline() << "unsigned long flags;"; + s.op->newline() << "unsigned long events;"; s.op->newline() << "int engine_attached;"; s.op->newline(-1) << "};"; - // Output handler function for CLONE events - if (flags_seen[UDPF_CLONE]) + + // Output handler function for UDPF_BEGIN and UDPF_THREAD_BEGIN + if (flags_seen[UDPF_BEGIN] || flags_seen[UDPF_THREAD_BEGIN]) { - s.op->newline() << "static u32 stap_utrace_probe_clone(struct utrace_attached_engine *engine, struct task_struct *parent, unsigned long clone_flags, struct task_struct *child) {"; + s.op->newline() << "static u32 stap_utrace_probe_quiesce(struct utrace_attached_engine *engine, struct task_struct *tsk) {"; s.op->indent(1); s.op->newline() << "struct stap_utrace_probe *p = (struct stap_utrace_probe *)engine->data;"; @@ -5410,12 +5600,15 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "(*p->ph) (c);"; common_probe_entryfn_epilogue (s.op); - s.op->newline() << "return UTRACE_ACTION_RESUME;"; + // UTRACE_ACTION_NEWSTATE not needed here to clear quiesce since + // we're detaching - utrace automatically restarts the thread. + s.op->newline() << "debug_task_finder_detach();"; + s.op->newline() << "return UTRACE_ACTION_DETACH;"; s.op->newline(-1) << "}"; } - // Output handler function for EXEC and DEATH events - if (flags_seen[UDPF_EXEC] || flags_seen[UDPF_DEATH]) + // Output handler function for UDPF_END and UDPF_THREAD_END + if (flags_seen[UDPF_END] || flags_seen[UDPF_THREAD_END]) { s.op->newline() << "static void stap_utrace_probe_handler(struct task_struct *tsk, struct stap_utrace_probe *p) {"; s.op->indent(1); @@ -5432,7 +5625,7 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) } // Output handler function for SYSCALL_ENTRY and SYSCALL_EXIT events - if (flags_seen[UDPF_SYSCALL_ENTRY] || flags_seen[UDPF_SYSCALL_EXIT]) + if (flags_seen[UDPF_SYSCALL] || flags_seen[UDPF_SYSCALL_RETURN]) { s.op->newline() << "static u32 stap_utrace_probe_syscall(struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs) {"; s.op->indent(1); @@ -5450,9 +5643,9 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline(-1) << "}"; } - // Output task finder callback routine that gets called for all + // Output task_finder callback routine that gets called for all // utrace probe types. - s.op->newline() << "static int _stp_utrace_probe_cb(struct task_struct *tsk, int register_p, struct stap_task_finder_target *tgt) {"; + s.op->newline() << "static int _stp_utrace_probe_cb(struct task_struct *tsk, int register_p, int process_p, struct stap_task_finder_target *tgt) {"; s.op->indent(1); s.op->newline() << "int rc = 0;"; s.op->newline() << "struct stap_utrace_probe *p = container_of(tgt, struct stap_utrace_probe, tgt);"; @@ -5463,74 +5656,135 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "switch (p->flags) {"; s.op->indent(1); - // When registering an exec probe, we can't install a utrace engine, - // since we're already in a exec event. So, we just call the probe - // directly. Note that for existing threads, this won't really work - // since our state isn't STAP_SESSION_RUNNING yet. But that's OK, - // since this isn't really a 'exec' event - it is a notification - // that task_finder found an interesting process. - if (flags_seen[UDPF_EXEC]) - { - s.op->newline() << "case UTRACE_EVENT(EXEC):"; + + // When receiving a UTRACE_EVENT(CLONE) event, we can't call the + // begin/thread.begin probe directly. So, we'll just attach an + // engine that waits for the thread to quiesce. When the thread + // quiesces, then call the probe. + if (flags_seen[UDPF_BEGIN]) + { + s.op->newline() << "case UDPF_BEGIN:"; s.op->indent(1); - s.op->newline() << "stap_utrace_probe_handler(tsk, p);"; - s.op->newline() << "break;"; - s.op->indent(-1); - } - // For death probes, do nothing at registration time. We'll handle - // these in the 'register_p == 0' case. - if (flags_seen[UDPF_DEATH]) - { - s.op->newline() << "case UTRACE_EVENT(DEATH):"; + s.op->newline() << "if (process_p) {"; + s.op->indent(1); + s.op->newline() << "rc = stap_utrace_attach(tsk, &p->ops, p, p->events);"; + s.op->newline() << "if (rc == 0) {"; s.op->indent(1); + s.op->newline() << "p->engine_attached = 1;"; + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; s.op->newline() << "break;"; s.op->indent(-1); - } - // Attach an engine for CLONE, SYSCALL_ENTRY, and SYSCALL_EXIT events. - if (flags_seen[UDPF_CLONE] || flags_seen[UDPF_SYSCALL_ENTRY] - || flags_seen[UDPF_SYSCALL_EXIT]) - { - s.op->newline() << "case (UTRACE_EVENT(CLONE)|UTRACE_EVENT(DEATH)):"; - s.op->newline() << "case (UTRACE_EVENT(SYSCALL_ENTRY)|UTRACE_EVENT(DEATH)):"; - s.op->newline() << "case (UTRACE_EVENT(SYSCALL_EXIT)|UTRACE_EVENT(DEATH)):"; + } + if (flags_seen[UDPF_THREAD_BEGIN]) + { + s.op->newline() << "case UDPF_THREAD_BEGIN:"; s.op->indent(1); - s.op->newline() << "engine = utrace_attach(tsk, UTRACE_ATTACH_CREATE, &p->ops, p);"; - s.op->newline() << "if (IS_ERR(engine)) {"; + s.op->newline() << "if (! process_p) {"; s.op->indent(1); - s.op->newline() << "int error = -PTR_ERR(engine);"; - s.op->newline() << "if (error != ENOENT) {"; + s.op->newline() << "rc = stap_utrace_attach(tsk, &p->ops, p, p->events);"; + s.op->newline() << "if (rc == 0) {"; s.op->indent(1); - s.op->newline() << "_stp_error(\"utrace_attach returned error %d on pid %d\", error, (int)tsk->pid);"; - s.op->newline() << "rc = error;"; + s.op->newline() << "p->engine_attached = 1;"; s.op->newline(-1) << "}"; s.op->newline(-1) << "}"; - s.op->newline() << "else if (unlikely(engine == NULL)) {"; + s.op->newline() << "break;"; + s.op->indent(-1); + } + + // For end/thread_end probes, do nothing at registration time. + // We'll handle these in the 'register_p == 0' case. + if (flags_seen[UDPF_END] || flags_seen[UDPF_THREAD_END]) + { + s.op->newline() << "case UDPF_END:"; + s.op->newline() << "case UDPF_THREAD_END:"; s.op->indent(1); - s.op->newline() << "_stp_error(\"utrace_attach returned NULL on pid %d!\", (int)tsk->pid);"; - s.op->newline() << "rc = ENOENT;"; - s.op->newline(-1) << "}"; - s.op->newline() << "else {"; + s.op->newline() << "break;"; + s.op->indent(-1); + } + + // Attach an engine for SYSCALL_ENTRY and SYSCALL_EXIT events. + if (flags_seen[UDPF_SYSCALL] || flags_seen[UDPF_SYSCALL_RETURN]) + { + s.op->newline() << "case UDPF_SYSCALL:"; + s.op->newline() << "case UDPF_SYSCALL_RETURN:"; + s.op->indent(1); + s.op->newline() << "rc = stap_utrace_attach(tsk, &p->ops, p, p->events);"; + s.op->newline() << "if (rc == 0) {"; s.op->indent(1); - s.op->newline() << "utrace_set_flags(tsk, engine, p->flags);"; s.op->newline() << "p->engine_attached = 1;"; s.op->newline(-1) << "}"; s.op->newline() << "break;"; s.op->indent(-1); } + + s.op->newline() << "default:"; + s.op->indent(1); + s.op->newline() << "_stp_error(\"unhandled flag value %d at %s:%d\", p->flags, __FUNCTION__, __LINE__);"; + s.op->newline() << "break;"; + s.op->indent(-1); s.op->newline(-1) << "}"; s.op->newline(-1) << "}"; + // Since this engine could be attached to multiple threads, don't - // cleanup here. We'll cleanup at module unload time. + // call stap_utrace_detach_ops() here. s.op->newline() << "else {"; s.op->indent(1); + s.op->newline() << "switch (p->flags) {"; + s.op->indent(1); // For death probes, go ahead and call the probe directly. - if (flags_seen[UDPF_DEATH]) + if (flags_seen[UDPF_END]) + { + s.op->newline() << "case UDPF_END:"; + s.op->indent(1); + s.op->newline() << "if (process_p) {"; + s.op->indent(1); + s.op->newline() << "stap_utrace_probe_handler(tsk, p);"; + s.op->newline(-1) << "}"; + s.op->newline() << "break;"; + s.op->indent(-1); + } + if (flags_seen[UDPF_THREAD_END]) { - s.op->newline() << "if (p->flags == UTRACE_EVENT(DEATH)) {"; + s.op->newline() << "case UDPF_THREAD_END:"; + s.op->indent(1); + s.op->newline() << "if (! process_p) {"; s.op->indent(1); s.op->newline() << "stap_utrace_probe_handler(tsk, p);"; s.op->newline(-1) << "}"; + s.op->newline() << "break;"; + s.op->indent(-1); } + + // For begin/thread_begin probes, at deregistration time we'll try + // to detach. This will only be necessary if the new thread/process + // got killed before the probe got run in the UTRACE_EVENT(QUIESCE) + // handler. + if (flags_seen[UDPF_BEGIN] || flags_seen[UDPF_THREAD_BEGIN] + || flags_seen[UDPF_SYSCALL] || flags_seen[UDPF_SYSCALL_RETURN]) + { + s.op->newline() << "case UDPF_BEGIN:"; + s.op->newline() << "case UDPF_THREAD_BEGIN:"; + s.op->newline() << "case UDPF_SYSCALL:"; + s.op->newline() << "case UDPF_SYSCALL_RETURN:"; + s.op->indent(1); + s.op->newline() << "engine = utrace_attach(tsk, UTRACE_ATTACH_MATCH_OPS, &p->ops, 0);"; + s.op->newline() << "if (! IS_ERR(engine) && engine != NULL) {"; + s.op->indent(1); + s.op->newline() << "utrace_detach(tsk, engine);"; + s.op->newline() << "debug_task_finder_detach();"; + + s.op->newline(-1) << "}"; + s.op->newline() << "break;"; + s.op->indent(-1); + } + + s.op->newline() << "default:"; + s.op->indent(1); + s.op->newline() << "_stp_error(\"unhandled flag value %d at %s:%d\", p->flags, __FUNCTION__, __LINE__);"; + s.op->newline() << "break;"; + s.op->indent(-1); + s.op->newline(-1) << "}"; s.op->newline(-1) << "}"; s.op->newline() << "return rc;"; s.op->newline(-1) << "}"; @@ -5544,6 +5798,14 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) for (p_b_path_iterator it = probes_by_path.begin(); it != probes_by_path.end(); it++) { + // Emit a "fake" probe decl that is really a hook for to get + // our vm_callback called. + string path = it->first; + s.op->newline() << "#ifdef DEBUG_TASK_FINDER_VMA"; + emit_vm_callback_probe_decl (s, true, path, (int64_t)0, + "__stp_tf_vm_cb"); + s.op->newline() << "#endif"; + for (unsigned i = 0; i < it->second.size(); i++) { utrace_derived_probe *p = it->second[i]; @@ -5558,6 +5820,13 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) for (p_b_pid_iterator it = probes_by_pid.begin(); it != probes_by_pid.end(); it++) { + // Emit a "fake" probe decl that is really a hook for to get + // our vm_callback called. + s.op->newline() << "#ifdef DEBUG_TASK_FINDER_VMA"; + emit_vm_callback_probe_decl (s, false, NULL, it->first, + "__stp_tf_vm_cb"); + s.op->newline() << "#endif"; + for (unsigned i = 0; i < it->second.size(); i++) { utrace_derived_probe *p = it->second[i]; @@ -5667,7 +5936,7 @@ struct uprobe_builder: public derived_probe_builder virtual void build(systemtap_session & sess, probe * base, probe_point * location, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results) { int64_t process, address; @@ -5802,6 +6071,8 @@ uprobe_derived_probe_group::emit_module_exit (systemtap_session& s) // ------------------------------------------------------------------------ +static string TOK_TIMER("timer"); + struct timer_derived_probe: public derived_probe { int64_t interval, randomize; @@ -5983,7 +6254,7 @@ struct profile_builder: public derived_probe_builder virtual void build(systemtap_session & sess, probe * base, probe_point * location, - std::map<std::string, literal *> const &, + literal_map_t const &, vector<derived_probe *> & finished_results) { finished_results.push_back(new profile_derived_probe(sess, base, location)); @@ -6090,6 +6361,10 @@ profile_derived_probe_group::emit_module_exit (systemtap_session& s) // ------------------------------------------------------------------------ +static string TOK_PROCFS("procfs"); +static string TOK_READ("read"); +static string TOK_WRITE("write"); + struct procfs_derived_probe: public derived_probe { string path; @@ -6496,7 +6771,7 @@ struct procfs_builder: public derived_probe_builder virtual void build(systemtap_session & sess, probe * base, probe_point * location, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results); }; @@ -6505,13 +6780,13 @@ void procfs_builder::build(systemtap_session & sess, probe * base, probe_point * location, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results) { string path; - bool has_procfs = get_param(parameters, "procfs", path); - bool has_read = (parameters.find("read") != parameters.end()); - bool has_write = (parameters.find("write") != parameters.end()); + bool has_procfs = get_param(parameters, TOK_PROCFS, path); + bool has_read = (parameters.find(TOK_READ) != parameters.end()); + bool has_write = (parameters.find(TOK_WRITE) != parameters.end()); // If no procfs path, default to "command". The runtime will do // this for us, but if we don't do it here, we'll think the @@ -6568,6 +6843,8 @@ procfs_builder::build(systemtap_session & sess, // statically inserted macro-based derived probes // ------------------------------------------------------------------------ +static string TOK_MARK("mark"); +static string TOK_FORMAT("format"); struct mark_arg { @@ -6802,9 +7079,9 @@ mark_derived_probe::mark_derived_probe (systemtap_session &s, { // create synthetic probe point name; preserve condition vector<probe_point::component*> comps; - comps.push_back (new probe_point::component ("kernel")); - comps.push_back (new probe_point::component ("mark", new literal_string (probe_name))); - comps.push_back (new probe_point::component ("format", new literal_string (probe_format))); + comps.push_back (new probe_point::component (TOK_KERNEL)); + comps.push_back (new probe_point::component (TOK_MARK, new literal_string (probe_name))); + comps.push_back (new probe_point::component (TOK_FORMAT, new literal_string (probe_format))); this->sole_location()->components = comps; // expand the marker format @@ -7161,7 +7438,7 @@ public: void build(systemtap_session & sess, probe * base, probe_point * location, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results); }; @@ -7170,13 +7447,13 @@ void mark_builder::build(systemtap_session & sess, probe * base, probe_point *loc, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results) { string mark_str_val; - bool has_mark_str = get_param (parameters, "mark", mark_str_val); + bool has_mark_str = get_param (parameters, TOK_MARK, mark_str_val); string mark_format_val; - bool has_mark_format = get_param (parameters, "format", mark_format_val); + bool has_mark_format = get_param (parameters, TOK_FORMAT, mark_format_val); assert (has_mark_str); (void) has_mark_str; @@ -7460,7 +7737,7 @@ struct timer_builder: public derived_probe_builder { virtual void build(systemtap_session & sess, probe * base, probe_point * location, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results); static void register_patterns(match_node *root); @@ -7470,7 +7747,7 @@ void timer_builder::build(systemtap_session & sess, probe * base, probe_point * location, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results) { int64_t period, rand=0; @@ -7538,7 +7815,7 @@ timer_builder::register_patterns(match_node *root) { derived_probe_builder *builder = new timer_builder(); - root = root->bind("timer"); + root = root->bind(TOK_TIMER); root->bind_num("s")->bind(builder); root->bind_num("s")->bind_num("randomize")->bind(builder); @@ -7682,7 +7959,7 @@ struct perfmon_builder: public derived_probe_builder virtual void build(systemtap_session & sess, probe * base, probe_point * location, - std::map<std::string, literal *> const & parameters, + literal_map_t const & parameters, vector<derived_probe *> & finished_results) { string event; @@ -7994,18 +8271,19 @@ perfmon_derived_probe_group::emit_module_init (translator_output* o) void register_standard_tapsets(systemtap_session & s) { - s.pattern_root->bind("begin")->bind(new be_builder(BEGIN)); - s.pattern_root->bind_num("begin")->bind(new be_builder(BEGIN)); - s.pattern_root->bind("end")->bind(new be_builder(END)); - s.pattern_root->bind_num("end")->bind(new be_builder(END)); - s.pattern_root->bind("error")->bind(new be_builder(ERROR)); - s.pattern_root->bind_num("error")->bind(new be_builder(ERROR)); + s.pattern_root->bind(TOK_BEGIN)->bind(new be_builder(BEGIN)); + s.pattern_root->bind_num(TOK_BEGIN)->bind(new be_builder(BEGIN)); + s.pattern_root->bind(TOK_END)->bind(new be_builder(END)); + s.pattern_root->bind_num(TOK_END)->bind(new be_builder(END)); + s.pattern_root->bind(TOK_ERROR)->bind(new be_builder(ERROR)); + s.pattern_root->bind_num(TOK_ERROR)->bind(new be_builder(ERROR)); - s.pattern_root->bind("never")->bind(new never_builder()); + s.pattern_root->bind(TOK_NEVER)->bind(new never_builder()); timer_builder::register_patterns(s.pattern_root); - s.pattern_root->bind("timer")->bind("profile")->bind(new profile_builder()); - s.pattern_root->bind("perfmon")->bind_str("counter")->bind(new perfmon_builder()); + s.pattern_root->bind(TOK_TIMER)->bind("profile")->bind(new profile_builder()); + s.pattern_root->bind("perfmon")->bind_str("counter") + ->bind(new perfmon_builder()); // dwarf-based kernel/module parts dwarf_derived_probe::register_patterns(s.pattern_root); @@ -8019,37 +8297,44 @@ register_standard_tapsets(systemtap_session & s) ->bind(new uprobe_builder ()); // utrace user-space probes - s.pattern_root->bind_str(TOK_PROCESS)->bind("clone") + s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_BEGIN) ->bind(new utrace_builder ()); - s.pattern_root->bind_num(TOK_PROCESS)->bind("clone") + s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_BEGIN) ->bind(new utrace_builder ()); - s.pattern_root->bind_str(TOK_PROCESS)->bind("exec") + s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_END) ->bind(new utrace_builder ()); - s.pattern_root->bind_num(TOK_PROCESS)->bind("exec") + s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_END) ->bind(new utrace_builder ()); - s.pattern_root->bind_str(TOK_PROCESS)->bind("syscall") + s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_BEGIN) ->bind(new utrace_builder ()); - s.pattern_root->bind_num(TOK_PROCESS)->bind("syscall") + s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_BEGIN) ->bind(new utrace_builder ()); - s.pattern_root->bind_str(TOK_PROCESS)->bind("syscall")->bind(TOK_RETURN) + s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_END) ->bind(new utrace_builder ()); - s.pattern_root->bind_num(TOK_PROCESS)->bind("syscall")->bind(TOK_RETURN) + s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_END) ->bind(new utrace_builder ()); - s.pattern_root->bind_str(TOK_PROCESS)->bind("death") + s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_SYSCALL) ->bind(new utrace_builder ()); - s.pattern_root->bind_num(TOK_PROCESS)->bind("death") + s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_SYSCALL) + ->bind(new utrace_builder ()); + s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_SYSCALL)->bind(TOK_RETURN) + ->bind(new utrace_builder ()); + s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_SYSCALL)->bind(TOK_RETURN) ->bind(new utrace_builder ()); // marker-based parts - s.pattern_root->bind("kernel")->bind_str("mark")->bind(new mark_builder()); - s.pattern_root->bind("kernel")->bind_str("mark")->bind_str("format") + s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_MARK) + ->bind(new mark_builder()); + s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_MARK)->bind_str(TOK_FORMAT) ->bind(new mark_builder()); // procfs parts - s.pattern_root->bind("procfs")->bind("read")->bind(new procfs_builder()); - s.pattern_root->bind_str("procfs")->bind("read")->bind(new procfs_builder()); - s.pattern_root->bind("procfs")->bind("write")->bind(new procfs_builder()); - s.pattern_root->bind_str("procfs")->bind("write")->bind(new procfs_builder()); + s.pattern_root->bind(TOK_PROCFS)->bind(TOK_READ)->bind(new procfs_builder()); + s.pattern_root->bind_str(TOK_PROCFS)->bind(TOK_READ) + ->bind(new procfs_builder()); + s.pattern_root->bind(TOK_PROCFS)->bind(TOK_WRITE)->bind(new procfs_builder()); + s.pattern_root->bind_str(TOK_PROCFS)->bind(TOK_WRITE) + ->bind(new procfs_builder()); } |