diff options
-rw-r--r-- | tapsets.cxx | 515 | ||||
-rw-r--r-- | translate.cxx | 3 |
2 files changed, 267 insertions, 251 deletions
diff --git a/tapsets.cxx b/tapsets.cxx index 5a3fbdc8..55873d51 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -675,13 +675,8 @@ struct dwflpp void get_module_dwarf(bool required = false, bool report = true) { - if (!module_dwarf && mod_info->dwarf_status != info_absent) - { - if (!sess.ignore_dwarf) - module_dwarf = dwfl_module_getdwarf(module, &module_bias); - mod_info->dwarf_status = (module_dwarf ? info_present : info_absent); - } - + module_dwarf = dwfl_module_getdwarf(module, &module_bias); + mod_info->dwarf_status = (module_dwarf ? info_present : info_absent); if (!module_dwarf && report) { string msg = "cannot find "; @@ -878,42 +873,8 @@ struct dwflpp { } - // Called by dwfl_linux_kernel_report_offline(). We may not have - // dwarf info for the kernel and/or modules, so remember this - // module's pathname in case we need to extract elf info from it. - // (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); - assert (this_session); - this_session->module_cache->cache[name] = mi; - - 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. - mi->dwarf_status = info_absent; - return 1; - } - else if (path) - { - mi->elf_path = path; - return 1; - } - - // No vmlinux. Here returning 0 to report_kernel() means go ahead - // and keep reporting modules. - assert(name == TOK_KERNEL); - mi->dwarf_status = info_absent; - return 0; - } - void setup(bool kernel, bool debuginfo_needed = true) + void setup_kernel(bool debuginfo_needed = true) { if (! sess.module_cache) sess.module_cache = new module_cache (); @@ -927,14 +888,6 @@ struct dwflpp static const char *debug_path = (debuginfo_env_arr ? debuginfo_env_arr : sess.kernel_release.c_str()); - static const Dwfl_Callbacks proc_callbacks = - { - dwfl_linux_proc_find_elf, - dwfl_standard_find_debuginfo, - NULL, - & debuginfo_path - }; - static const Dwfl_Callbacks kernel_callbacks = { dwfl_linux_kernel_find_elf, @@ -943,135 +896,109 @@ struct dwflpp & debuginfo_path }; - if (kernel) - { - dwfl = dwfl_begin (&kernel_callbacks); - if (!dwfl) - throw semantic_error ("cannot open dwfl"); - dwfl_report_begin (dwfl); - - int (*callback)(const char *name, const char *path); - if (sess.consult_symtab && !sess.module_cache->paths_collected) - { - callback = pathname_caching_callback; - 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, - debug_path, - /* selection predicate */ - callback); - this_session = 0; - - if (debuginfo_needed) - dwfl_assert (string("missing kernel ") + - sess.kernel_release + - string(" ") + - sess.architecture + - string(" debuginfo"), - rc); - - // XXX: it would be nice if we could do a single - // ..._report_offline call for an entire systemtap script, so - // that a selection predicate would filter out modules outside - // the union of all the requested wildcards. But we build - // derived_probes one-by-one and we don't have lookahead. - - // XXX: a special case: if we have only kernel.* probe points, - // we shouldn't waste time looking for module debug-info (and - // vice versa). - - // NB: the result of an _offline call is the assignment of - // virtualized addresses to relocatable objects such as - // modules. These have to be converted to real addresses at - // run time. See the dwarf_derived_probe ctor and its caller. - } - else - { - dwfl = dwfl_begin (&proc_callbacks); - dwfl_report_begin (dwfl); - if (!dwfl) - throw semantic_error ("cannot open dwfl"); - - throw semantic_error ("user-space probes not yet implemented"); - // XXX: Find pids or processes, do userspace stuff. - } + dwfl = dwfl_begin (&kernel_callbacks); + if (!dwfl) + throw semantic_error ("cannot open dwfl"); + dwfl_report_begin (dwfl); + + int rc = dwfl_linux_kernel_report_offline (dwfl, + debug_path, + NULL); + + if (debuginfo_needed) + dwfl_assert (string("missing kernel ") + + sess.kernel_release + + string(" ") + + sess.architecture + + string(" debuginfo"), + rc); + + // XXX: it would be nice if we could do a single + // ..._report_offline call for an entire systemtap script, so + // that a selection predicate would filter out modules outside + // the union of all the requested wildcards. But we build + // derived_probes one-by-one and we don't have lookahead. + // PR 3498. + + // XXX: a special case: if we have only kernel.* probe points, + // we shouldn't waste time looking for module debug-info (and + // vice versa). + + // NB: the result of an _offline call is the assignment of + // virtualized addresses to relocatable objects such as + // modules. These have to be converted to real addresses at + // run time. See the dwarf_derived_probe ctor and its caller. dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL)); } - - - // ----------------------------------------------------------------- - - static int module_caching_callback(Dwfl_Module * mod, - void **, - const char *name, - Dwarf_Addr addr, - void *param) + void setup_user(string module_name, bool debuginfo_needed = true) { - systemtap_session *sess = static_cast<systemtap_session*>(param); - module_cache_t *cache = sess->module_cache; - module_info *mi = NULL; + // 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_path_arr); - assert (cache); + static const Dwfl_Callbacks user_callbacks = + { + NULL, /* dwfl_linux_kernel_find_elf, */ + dwfl_standard_find_debuginfo, + dwfl_offline_section_address, + & debuginfo_path + }; - if (sess->ignore_vmlinux && name == TOK_KERNEL) - // This wouldn't be called for vmlinux if vmlinux weren't there. - return DWARF_CB_OK; + dwfl = dwfl_begin (&user_callbacks); + if (!dwfl) + throw semantic_error ("cannot open dwfl"); + dwfl_report_begin (dwfl); + + // XXX: need to map module_name to fully-qualified directory names, + // searching PATH etc. + + // XXX: should support buildid-based naming + + Dwfl_Module *mod = dwfl_report_offline (dwfl, + module_name.c_str(), + module_name.c_str(), + -1); + // XXX: save mod! + + if (debuginfo_needed) + dwfl_assert (string("missing process ") + + module_name + + string(" ") + + sess.architecture + + string(" debuginfo"), + mod); + + // NB: the result of an _offline call is the assignment of + // virtualized addresses to relocatable objects such as + // modules. These have to be converted to real addresses at + // run time. See the dwarf_derived_probe ctor and its caller. - if (cache->paths_collected) - mi = cache->cache[name]; - if (!mi) - { - mi = new module_info(name); - cache->cache[name] = mi; - } - mi->mod = mod; - mi->addr = addr; - return DWARF_CB_OK; + dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL)); } - void cache_modules_dwarf() - { - if (!sess.module_cache->dwarf_collected) - { - ptrdiff_t off = 0; - do - { - if (pending_interrupts) return; - off = dwfl_getmodules (dwfl, module_caching_callback, - & sess, off); - } - while (off > 0); - dwfl_assert("dwfl_getmodules", off == 0); - sess.module_cache->dwarf_collected = true; - } - } - void iterate_over_modules(int (* callback)(Dwfl_Module *, module_info *, + + // ----------------------------------------------------------------- + + void iterate_over_modules(int (* callback)(Dwfl_Module *, void **, const char *, Dwarf_Addr, void *), - void * data) + dwarf_query *data) { - cache_modules_dwarf(); - - map<string, module_info*>::iterator i; - for (i = sess.module_cache->cache.begin(); i != sess.module_cache->cache.end(); i++) + ptrdiff_t off = 0; + do { if (pending_interrupts) return; - module_info *mi = i->second; - int rc = callback (mi->mod, mi, mi->name, mi->addr, data); - if (rc != DWARF_CB_OK) break; + off = dwfl_getmodules (dwfl, callback, data, off); } + while (off > 0); + dwfl_assert("dwfl_getmodules", off == 0); } + // Defined after dwarf_query void query_modules(dwarf_query *q); @@ -2285,10 +2212,6 @@ struct dwflpp }; -systemtap_session* dwflpp::this_session = 0; // XXX: used only due to elfutils shortcoming - - - enum function_spec_type @@ -2385,6 +2308,8 @@ struct base_query // Extracted parameters. bool has_kernel; + bool has_module; + bool has_process; string module_val; // has_kernel => module_val = "kernel" virtual void handle_query_module() = 0; @@ -2400,15 +2325,17 @@ base_query::base_query(systemtap_session & sess, : sess(sess), base_probe(base_probe), base_loc(base_loc), dw(dw), results(results) { - has_kernel = has_null_param(params, TOK_KERNEL); + has_kernel = has_null_param (params, TOK_KERNEL); if (has_kernel) module_val = "kernel"; + + has_module = get_string_param (params, TOK_MODULE, module_val); + if (has_module) + has_process = false; else - { - bool has_module = get_string_param(params, TOK_MODULE, module_val); - assert (has_module); // no other options are possible by construction - (void) has_module; - } + has_process = get_string_param(params, TOK_PROCESS, module_val); + + assert (has_kernel || has_process || has_module); } bool @@ -2689,23 +2616,40 @@ dwflpp::iterate_over_functions (int (* callback)(Dwarf_Die * func, void * arg), struct dwarf_builder: public derived_probe_builder { dwflpp *kern_dw; + map <string,dwflpp*> user_dw; dwarf_builder(): kern_dw(0) {} - void build_no_more (systemtap_session &s) + + /* NB: not virtual, so can be called from dtor too: */ + void dwarf_build_no_more (bool verbose) { if (kern_dw) { - if (s.verbose > 3) - clog << "dwarf_builder releasing dwflpp" << endl; + if (verbose) + clog << "dwarf_builder releasing kernel dwflpp" << endl; delete kern_dw; kern_dw = 0; } + + for (map<string,dwflpp*>::iterator udi = user_dw.begin(); + udi != user_dw.end(); + udi ++) + { + if (verbose) + clog << "dwarf_builder releasing user dwflpp " << udi->first << endl; + delete udi->second; + } + user_dw.erase (user_dw.begin(), user_dw.end()); + } + + void build_no_more (systemtap_session &s) + { + dwarf_build_no_more (s.verbose > 3); } ~dwarf_builder() { - // XXX: in practice, NOTREACHED - delete kern_dw; + dwarf_build_no_more (false); } virtual void build(systemtap_session & sess, @@ -2887,6 +2831,10 @@ dwarf_query::handle_query_module() void dwarf_query::build_blacklist() { + // No blacklist for userspace. + if (has_process) + return; + // We build up the regexps in these strings // Add ^ anchors at the front; $ will be added just before regcomp. @@ -3095,9 +3043,11 @@ dwarf_query::parse_function_spec(string & spec) } +#if 0 // Forward declaration. -static int query_kernel_module (Dwfl_Module *, module_info *, const char *, +static int query_kernel_module (Dwfl_Module *, void **, const char *, Dwarf_Addr, void *); +#endif // XXX: pull this into dwflpp @@ -3123,6 +3073,9 @@ dwarf_query::blacklisted_p(const string& funcname, const string& section, Dwarf_Addr addr) { + if (has_process) + return false; // no blacklist for userspace + if (section.substr(0, 6) == string(".init.") || section.substr(0, 6) == string(".exit.") || section.substr(0, 9) == string(".devinit.") || @@ -3250,8 +3203,10 @@ dwarf_query::add_probe_point(const string& funcname, clog << "probe " << funcname << "@" << filename << ":" << line; if (string(module) == TOK_KERNEL) clog << " kernel"; - else + else if (has_module) clog << " module=" << module; + else if (has_process) + clog << " process=" << module; if (reloc_section != "") clog << " reloc=" << reloc_section; if (blacklist_section != "") clog << " section=" << blacklist_section; clog << " pc=0x" << hex << addr << dec; @@ -3725,9 +3680,10 @@ query_cu (Dwarf_Die * cudie, void * arg) } +#if 0 static int query_kernel_module (Dwfl_Module *mod, - module_info *, + void **, const char *name, Dwarf_Addr, void *arg) @@ -3741,6 +3697,8 @@ query_kernel_module (Dwfl_Module *mod, } return DWARF_CB_OK; } +#endif + static void validate_module_elf (Dwfl_Module *mod, const char *name, base_query *q) @@ -3807,17 +3765,61 @@ validate_module_elf (Dwfl_Module *mod, const char *name, base_query *q) << "\n"; } + + +static Dwarf_Addr +lookup_symbol_address (Dwfl_Module *m, const char* wanted) +{ + int syments = dwfl_module_getsymtab(m); + assert(syments); + for (int i = 1; i < syments; ++i) + { + GElf_Sym sym; + const char *name = dwfl_module_getsym(m, i, &sym, NULL); + if (name != NULL && strcmp(name, wanted) == 0) + return sym.st_value; + } + + return 0; +} + + + static int query_module (Dwfl_Module *mod, - module_info *mi, + void **, const char *name, Dwarf_Addr, void *arg) { - base_query * q = static_cast<base_query *>(arg); + base_query *q = static_cast<base_query *>(arg); try { + module_info* mi = q->sess.module_cache->cache[name]; + if (mi == 0) + { + mi = q->sess.module_cache->cache[name] = new module_info(name); + + const char *path = NULL; // XXX: unbreak this + + if (q->sess.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. + mi->dwarf_status = info_absent; + } + else if (path) + { + mi->elf_path = path; + } + + // No vmlinux. Here returning 0 to report_kernel() means go ahead + // and keep reporting modules. + mi->dwarf_status = info_absent; + } + // OK, enough of that module_info caching business. + q->dw.focus_on_module(mod, mi); // If we have enough information in the pattern to skip a module and @@ -3834,14 +3836,28 @@ query_module (Dwfl_Module *mod, if (mod) validate_module_elf(mod, name, q); else + assert(q->has_kernel); // and no vmlinux to examine + + if (q->sess.verbose>2) + cerr << "focused on module '" << q->dw.module_name << "'\n"; + + + // Collect a few kernel addresses. XXX: these belong better + // to the sess.module_info["kernel"] struct. + if (q->dw.module_name == TOK_KERNEL) { - assert(q->has_kernel); // and no vmlinux to examine - if (q->sess.verbose>2) - cerr << "focused on module '" << q->dw.module_name << "'\n"; + if (! q->sess.sym_kprobes_text_start) + q->sess.sym_kprobes_text_start = lookup_symbol_address (mod, "__kprobes_text_start"); + if (! q->sess.sym_kprobes_text_end) + q->sess.sym_kprobes_text_end = lookup_symbol_address (mod, "__kprobes_text_end"); + if (! q->sess.sym_stext) + q->sess.sym_stext = lookup_symbol_address (mod, "_stext"); } + // Finally, search the module for matches of the probe point. q->handle_query_module(); + // If we know that there will be no more matches, abort early. if (q->dw.module_name_final_match(q->module_val)) return DWARF_CB_ABORT; @@ -3858,20 +3874,7 @@ query_module (Dwfl_Module *mod, void dwflpp::query_modules(dwarf_query *q) { - string name = q->module_val; - if (name_has_wildcard(name)) - iterate_over_modules(&query_module, q); - else - { - cache_modules_dwarf(); - - 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); - } - } + iterate_over_modules(&query_module, q); } struct var_expanding_copy_visitor: public deep_copy_visitor @@ -4365,7 +4368,7 @@ dwarf_derived_probe::join_group (systemtap_session& s) dwarf_derived_probe::dwarf_derived_probe(const string& funcname, const string& filename, int line, - // module & section speficy a relocation + // module & section specify a relocation // base for <addr>, unless section=="" // (equivalently module=="kernel") const string& module, @@ -4431,11 +4434,15 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname, // number any particular match of the wildcards. vector<probe_point::component*> comps; - comps.push_back - (module == TOK_KERNEL - ? new probe_point::component(TOK_KERNEL) - : new probe_point::component(TOK_MODULE, new literal_string(module))); - + if (q.has_kernel) + comps.push_back (new probe_point::component(TOK_KERNEL)); + else if(q.has_module) + comps.push_back (new probe_point::component(TOK_MODULE, new literal_string(module))); + else if(q.has_process) + comps.push_back (new probe_point::component(TOK_PROCESS, new literal_string(module))); + else + assert (0); + string fn_or_stmt; if (q.has_function_str || q.has_function_num) fn_or_stmt = "function"; @@ -4527,7 +4534,7 @@ dwarf_derived_probe::register_patterns(match_node * root) register_function_and_statement_variants(root->bind_str(TOK_MODULE), dw); root->bind(TOK_KERNEL)->bind_num(TOK_STATEMENT)->bind(TOK_ABSOLUTE)->bind(dw); - // register_function_and_statement_variants(root->bind_str(TOK_PROCESS), dw); + register_function_and_statement_variants(root->bind_str(TOK_PROCESS), dw); } void @@ -4830,26 +4837,6 @@ dwarf_derived_probe_group::emit_module_exit (systemtap_session& s) } - -static Dwarf_Addr -lookup_symbol_address (Dwfl_Module *m, const char* wanted) -{ - int syments = dwfl_module_getsymtab(m); - assert(syments); - for (int i = 1; i < syments; ++i) - { - GElf_Sym sym; - const char *name = dwfl_module_getsym(m, i, &sym, NULL); - if (name != NULL && strcmp(name, wanted) == 0) - return sym.st_value; - } - - return 0; -} - - - - void dwarf_builder::build(systemtap_session & sess, probe * base, @@ -4861,39 +4848,68 @@ dwarf_builder::build(systemtap_session & sess, // XXX: but they should be per-session, as this builder object // may be reused if we try to cross-instrument multiple targets. - if (!kern_dw) + dwflpp* dw = 0; + + if (! sess.module_cache) + sess.module_cache = new module_cache (); + + string module_name; + if (has_null_param (parameters, TOK_KERNEL) + || get_param (parameters, TOK_MODULE, module_name)) { - kern_dw = new dwflpp(sess); - assert(kern_dw); - kern_dw->setup(true); - } + // kernel or kernel module target + if (! kern_dw) + { + kern_dw = new dwflpp(sess); + // XXX: PR 3498 + kern_dw->setup_kernel(); + } + dw = kern_dw; - Dwfl_Module* km = 0; - kern_dw->iterate_over_modules(&query_kernel_module, &km); - if (km) +#if 0 + // Extract some kernel-side blacklist/relocation information. + // XXX: This really should be per-module rather than per-kernel, since + // .ko's may conceivably contain __kprobe-marked code + Dwfl_Module* km = 0; + kern_dw->iterate_over_modules(&query_kernel_module, &km); + if (km) + { + + if (sess.verbose > 2) + { + clog << "control symbols:" + // abbreviate the names - they're for our debugging only anyway + << " kts: 0x" << hex << sess.sym_kprobes_text_start + << " kte: 0x" << sess.sym_kprobes_text_end + << " stext: 0x" << sess.sym_stext + << dec << endl; + } + } +#endif + } + else if (get_param (parameters, TOK_PROCESS, module_name)) { - if (! sess.sym_kprobes_text_start) - sess.sym_kprobes_text_start = lookup_symbol_address (km, "__kprobes_text_start"); - if (! sess.sym_kprobes_text_end) - sess.sym_kprobes_text_end = lookup_symbol_address (km, "__kprobes_text_end"); - if (! sess.sym_stext) - sess.sym_stext = lookup_symbol_address (km, "_stext"); - - if (sess.verbose > 2) + // user-space target; we use one dwflpp instance per module name + // (= program or shared library) + if (user_dw.find(module_name) == user_dw.end()) { - clog << "control symbols:" - // abbreviate the names - they're for our debugging only anyway - << " kts: 0x" << hex << sess.sym_kprobes_text_start - << " kte: 0x" << sess.sym_kprobes_text_end - << " stext: 0x" << sess.sym_stext - << dec << endl; + dw = new dwflpp(sess); + // XXX: PR 3498 + dw->setup_user(module_name); + user_dw[module_name] = dw; } + else + dw = user_dw[module_name]; } - dwflpp* dw = kern_dw; + dwarf_query q(sess, base, location, *dw, parameters, finished_results); - if (q.has_absolute) + + // XXX: kernel.statement.absolute is a special case that requires no + // dwfl processing. This code should be in a separate builder. + + if (q.has_kernel && q.has_absolute) { // assert guru mode for absolute probes if (! q.base_probe->privileged) @@ -4913,7 +4929,6 @@ dwarf_builder::build(systemtap_session & sess, return; } - // dw->iterate_over_modules(&query_module, &q); dw->query_modules(&q); } @@ -8455,7 +8470,7 @@ register_standard_tapsets(systemtap_session & s) s.pattern_root->bind("perfmon")->bind_str("counter") ->bind(new perfmon_builder()); - // dwarf-based kernel/module parts + // dwarf-based kprobe/uprobe parts dwarf_derived_probe::register_patterns(s.pattern_root); // XXX: user-space starter set diff --git a/translate.cxx b/translate.cxx index c62ba9fd..5956cba7 100644 --- a/translate.cxx +++ b/translate.cxx @@ -4507,7 +4507,7 @@ emit_symbol_data (systemtap_session& s) unwindsym_dump_context ctx = { s, kallsyms_out, 0, s.unwindsym_modules }; - // XXX: copied from tapsets.cxx, sadly + // XXX: copied from tapsets.cxx dwflpp::, sadly static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug:build"; static char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH"); @@ -4546,6 +4546,7 @@ emit_symbol_data (systemtap_session& s) // ---- step 2: process any user modules (files) listed + // XXX: see dwflpp::setup_user. static const Dwfl_Callbacks user_callbacks = { NULL, /* dwfl_linux_kernel_find_elf, */ |