summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--hash.cxx16
-rw-r--r--main.cxx25
-rw-r--r--runtime/loc2c-runtime.h7
-rw-r--r--runtime/task_finder.c16
-rw-r--r--tapset/ChangeLog3
-rw-r--r--tapset/utrace.stp11
-rw-r--r--tapsets.cxx1066
-rw-r--r--translate.cxx3
-rw-r--r--util.cxx85
-rw-r--r--util.h2
11 files changed, 759 insertions, 489 deletions
diff --git a/ChangeLog b/ChangeLog
index 9b59e6e5..4bcf5736 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,6 @@
2008-08-11 Frank Ch. Eigler <fche@elastic.org>
PR5049
- PR5049
* tapsets.cxx (cu_name_matches, collect_srcfiles_matching):
Implicitly but optionally prefix probe source filenames with "*/".
* stapprobes.5.in: Document this.
@@ -32,6 +31,19 @@
* Makefile.in: Regenerated.
2008-08-09 Frank Ch. Eigler <fche@elastic.org>
+
+ * main.cxx (main): Don't override $PATH etc.
+ * tapsets.cxx (base_query ctor, dwarf_builder::build,
+ itrace_builder, utrace_derived_probe): Use find_executable()
+ to resolve FOO path in process("FOO").
+ * util.cxx (find_executable): Duplicate execvp logic.
+
+2008-08-09 Frank Ch. Eigler <fche@elastic.org>
+
+ * util.cxx (find_executable): Reorganize, simplify, canonicalize.
+ * util.h, hash.cxx: Corresponding changes.
+
+2008-08-09 Frank Ch. Eigler <fche@elastic.org>
* Makefile.am (example index): Only warn and instruct on index
regeneration. Do not actually perform it as the source tree
diff --git a/hash.cxx b/hash.cxx
index 2c1bfb25..79458319 100644
--- a/hash.cxx
+++ b/hash.cxx
@@ -126,16 +126,14 @@ find_hash (systemtap_session& s, const string& script)
h.add(s.runtime_path);
// Hash compiler path, size, and mtime. We're just going to assume
- // we'll be using gcc, which should be correct most of the time.
- string gcc_path;
- if (find_executable("gcc", gcc_path))
+ // we'll be using gcc. XXX: getting kbuild to spit out out would be
+ // better.
+ string gcc_path = find_executable ("gcc");
+ if (stat(gcc_path.c_str(), &st) == 0)
{
- if (stat(gcc_path.c_str(), &st) == 0)
- {
- h.add(gcc_path);
- h.add(st.st_size);
- h.add(st.st_mtime);
- }
+ h.add(gcc_path);
+ h.add(st.st_size);
+ h.add(st.st_mtime);
}
// Hash the systemtap size and mtime. We could use VERSION/DATE,
diff --git a/main.cxx b/main.cxx
index 8c99cf7e..a2ceb656 100644
--- a/main.cxx
+++ b/main.cxx
@@ -696,25 +696,11 @@ main (int argc, char * const argv [])
int rc = 0;
- // override PATH and LC_ALL
- const char *path = "/bin:/sbin:/usr/bin:/usr/sbin";
- rc = setenv("PATH", path, 1) || setenv("LC_ALL", "C", 1);
- if (rc)
- {
- const char* e = strerror (errno);
- cerr << "setenv (\"PATH=" << path << "\" + \"LC_ALL=C\"): "
- << e << endl;
- }
-
- // Get rid of a few standard environment variables (which might
- // cause us to do unintended things).
- rc = unsetenv("IFS") || unsetenv("CDPATH") || unsetenv("ENV")
- || unsetenv("BASH_ENV");
- if (rc)
- {
- const char* e = strerror (errno);
- cerr << "unsetenv failed: " << e << endl;
- }
+ // For PR1477, we used to override $PATH and $LC_ALL and other stuff
+ // here. We seem to use complete pathnames in
+ // buildrun.cxx/tapsets.cxx now, so this is not necessary. Further,
+ // it interferes with util.cxx:find_executable(), used for $PATH
+ // resolution.
s.kernel_base_release.assign(s.kernel_release, 0, s.kernel_release.find('-'));
@@ -993,6 +979,7 @@ main (int argc, char * const argv [])
if (rc || s.last_pass == 3 || pending_interrupts) goto cleanup;
// PASS 4: COMPILATION
+
times (& tms_before);
gettimeofday (&tv_before, NULL);
rc = compile_pass (s);
diff --git a/runtime/loc2c-runtime.h b/runtime/loc2c-runtime.h
index 215676ee..1247da51 100644
--- a/runtime/loc2c-runtime.h
+++ b/runtime/loc2c-runtime.h
@@ -113,16 +113,13 @@
kernel mode, it is not saved in the trap frame (struct pt_regs).
The `esp' (and `xss') fields are valid only for a user-mode trap.
For a kernel mode trap, the interrupted state's esp is actually an
- address inside where the `struct pt_regs' on the kernel trap stack points.
-
- For now we assume all traps are from kprobes in kernel-mode code.
- For extra paranoia, could do BUG_ON((regs->xcs & 3) == 3). */
+ address inside where the `struct pt_regs' on the kernel trap stack points. */
#define dwarf_register_0(regs) regs->eax
#define dwarf_register_1(regs) regs->ecx
#define dwarf_register_2(regs) regs->edx
#define dwarf_register_3(regs) regs->ebx
-#define dwarf_register_4(regs) ((long) &regs->esp)
+#define dwarf_register_4(regs) (user_mode(regs) ? regs->esp : (long)&regs->esp)
#define dwarf_register_5(regs) regs->ebp
#define dwarf_register_6(regs) regs->esi
#define dwarf_register_7(regs) regs->edi
diff --git a/runtime/task_finder.c b/runtime/task_finder.c
index 1832c795..541e5a04 100644
--- a/runtime/task_finder.c
+++ b/runtime/task_finder.c
@@ -1,5 +1,15 @@
+#ifndef TASK_FINDER_C
+#define TASK_FINDER_C
+
+#if ! defined(CONFIG_UTRACE)
+#error "Need CONFIG_UTRACE!"
+#endif
+
+#include <linux/utrace.h>
#include <linux/list.h>
#include <linux/binfmts.h>
+#include <linux/mount.h>
+
#include "syscall.h"
#include "task_finder_vma.c"
@@ -583,7 +593,7 @@ __stp_utrace_task_finder_target_death(struct utrace_attached_engine *engine,
// Call the callback
rc = tgt->callback(tsk, 0,
- (atomic_read(&tsk->signal->live) == 0),
+ (tsk->signal == NULL) || (atomic_read(&tsk->signal->live) == 0),
tgt);
if (rc != 0) {
_stp_error("death callback for %d failed: %d",
@@ -961,6 +971,7 @@ stap_start_task_finder(void)
struct task_struct *grp, *tsk;
char *mmpath_buf;
+ debug_task_finder_report();
mmpath_buf = _stp_kmalloc(PATH_MAX);
if (mmpath_buf == NULL) {
_stp_error("Unable to allocate space for path");
@@ -1080,3 +1091,6 @@ stap_stop_task_finder(void)
debug_task_finder_report();
atomic_set(&__stp_task_finder_state, __STP_TF_STOPPED);
}
+
+
+#endif /* TASK_FINDER_C */
diff --git a/tapset/ChangeLog b/tapset/ChangeLog
index b2592e1e..779a2e30 100644
--- a/tapset/ChangeLog
+++ b/tapset/ChangeLog
@@ -1,3 +1,6 @@
+2008-08-07 Frank Ch. Eigler <fche@elastic.org>
+
+ * utrace.stp: New file, for use by utrace $var expansions.
2008-08-04 Wenji Huang <wenji.huang@oracle.com>
diff --git a/tapset/utrace.stp b/tapset/utrace.stp
new file mode 100644
index 00000000..3831ca3c
--- /dev/null
+++ b/tapset/utrace.stp
@@ -0,0 +1,11 @@
+/* utrace-only subset of register accessors */
+
+
+%{
+#include "syscall.h"
+%}
+
+
+function _utrace_syscall_nr:long () %{
+ THIS->__retvalue = __stp_user_syscall_nr(CONTEXT->regs); /* pure */
+%}
diff --git a/tapsets.cxx b/tapsets.cxx
index af9fda52..a321b4e8 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 ";
@@ -863,7 +858,7 @@ struct dwflpp
bool t = (fnmatch(pattern.c_str(), cu_name.c_str(), 0) == 0 ||
fnmatch(prefixed_pattern.c_str(), cu_name.c_str(), 0) == 0);
if (t && sess.verbose>3)
- clog << "pattern '" << pattern << "' "
+ clog << "pattern '" << prefixed_pattern << "' "
<< "matches "
<< "CU '" << cu_name << "'" << "\n";
return t;
@@ -884,42 +879,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 ();
@@ -933,14 +894,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,
@@ -949,135 +902,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);
@@ -2296,10 +2223,6 @@ struct dwflpp
};
-systemtap_session* dwflpp::this_session = 0; // XXX: used only due to elfutils shortcoming
-
-
-
enum
function_spec_type
@@ -2337,9 +2260,7 @@ struct dwarf_derived_probe: public derived_probe
long maxactive_val;
void printsig (std::ostream &o) const;
-
void join_group (systemtap_session& s);
-
void emit_probe_local_init(translator_output * o);
// Pattern registration helpers.
@@ -2353,6 +2274,40 @@ struct dwarf_derived_probe: public derived_probe
};
+struct uprobe_derived_probe: public derived_probe
+{
+ bool return_p;
+ string module; // * => unrestricted
+ int pid; // 0 => unrestricted
+ string section; // empty => absolute address
+ Dwarf_Addr address;
+ // bool has_maxactive;
+ // long maxactive_val;
+
+ uprobe_derived_probe (const string& function,
+ const string& filename,
+ int line,
+ const string& module,
+ int pid,
+ const string& section,
+ Dwarf_Addr dwfl_addr,
+ Dwarf_Addr addr,
+ dwarf_query & q,
+ Dwarf_Die* scope_die);
+
+ // alternate constructor for process(PID).statement(ADDR).absolute
+ uprobe_derived_probe (probe *base,
+ probe_point *location,
+ int pid,
+ Dwarf_Addr addr,
+ bool return_p);
+
+ void printsig (std::ostream &o) const;
+ void join_group (systemtap_session& s);
+};
+
+
+
struct dwarf_derived_probe_group: public derived_probe_group
{
private:
@@ -2396,6 +2351,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;
@@ -2411,15 +2368,21 @@ 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);
+ if (has_process)
+ module_val = find_executable (module_val);
}
+
+ assert (has_kernel || has_process || has_module);
}
bool
@@ -2700,23 +2663,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,
@@ -2898,6 +2878,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.
@@ -3106,9 +3090,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
@@ -3134,6 +3120,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.") ||
@@ -3223,7 +3212,6 @@ dwarf_query::add_probe_point(const string& funcname,
Dwarf_Die* scope_die,
Dwarf_Addr addr)
{
- dwarf_derived_probe *probe = NULL;
string reloc_section; // base section for relocation purposes
Dwarf_Addr reloc_addr = addr; // relocated
string blacklist_section; // linking section for blacklist purposes
@@ -3261,8 +3249,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;
@@ -3282,9 +3272,20 @@ dwarf_query::add_probe_point(const string& funcname,
if (! bad)
{
sess.unwindsym_modules.insert (module);
- probe = new dwarf_derived_probe(funcname, filename, line,
- module, reloc_section, addr, reloc_addr, *this, scope_die);
- results.push_back(probe);
+
+ if (has_process)
+ {
+ results.push_back (new uprobe_derived_probe(funcname, filename, line,
+ module, 0, reloc_section, addr, reloc_addr,
+ *this, scope_die));
+ }
+ else
+ {
+ assert (has_kernel || has_module);
+ results.push_back (new dwarf_derived_probe(funcname, filename, line,
+ module, reloc_section, addr, reloc_addr,
+ *this, scope_die));
+ }
}
}
@@ -3736,9 +3737,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)
@@ -3752,6 +3754,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)
@@ -3818,17 +3822,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
@@ -3845,14 +3893,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;
@@ -3869,20 +3931,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
@@ -4436,7 +4485,7 @@ dwarf_derived_probe::printsig (ostream& o) const
// function instances. This is distinct from the verbose/clog
// output, since this part goes into the cache hash calculations.
sole_location()->print (o);
- o << " /* pc=0x" << hex << addr << dec << " */";
+ o << " /* pc=" << section << "+0x" << hex << addr << dec << " */";
printsig_nested (o);
}
@@ -4454,7 +4503,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,
@@ -4520,11 +4569,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";
@@ -4616,7 +4669,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
@@ -4802,7 +4855,7 @@ dwarf_derived_probe_group::emit_module_init (systemtap_session& s)
s.op->newline() << "struct stap_dwarf_kprobe *kp = & stap_dwarf_kprobes[i];";
s.op->newline() << "unsigned long relocated_addr = _stp_module_relocate (sdp->module, sdp->section, sdp->address);";
s.op->newline() << "if (relocated_addr == 0) continue;"; // quietly; assume module is absent
- s.op->newline() << "probe_point = sdp->pp;";
+ s.op->newline() << "probe_point = sdp->pp;"; // for error messages
s.op->newline() << "if (sdp->return_p) {";
s.op->newline(1) << "kp->u.krp.kp.addr = (void *) relocated_addr;";
s.op->newline() << "if (sdp->maxactive_p) {";
@@ -4919,26 +4972,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,
@@ -4950,39 +4983,70 @@ 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)
+ module_name = find_executable (module_name); // canonicalize it
+
+ // 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)
@@ -5002,7 +5066,6 @@ dwarf_builder::build(systemtap_session & sess,
return;
}
- // dw->iterate_over_modules(&query_module, &q);
dw->query_modules(&q);
}
@@ -5391,21 +5454,7 @@ void
task_finder_derived_probe_group::create_session_group (systemtap_session& s)
{
if (! s.task_finder_derived_probes)
- {
- s.task_finder_derived_probes = new task_finder_derived_probe_group();
-
- // Make sure we've got the stuff we need early in the output code.
- embeddedcode *ec = new embeddedcode;
- ec->tok = NULL;
- ec->code = string("#if ! defined(CONFIG_UTRACE)\n")
- + string("#error \"Need CONFIG_UTRACE!\"\n")
- + string("#endif\n")
- + string("#include <linux/utrace.h>\n")
- + string("#include <linux/mount.h>\n")
- + string("#include \"task_finder.c\"\n");
-
- s.embeds.push_back(ec);
- }
+ s.task_finder_derived_probes = new task_finder_derived_probe_group();
}
@@ -5417,8 +5466,7 @@ task_finder_derived_probe_group::emit_module_init (systemtap_session& s)
s.op->newline() << "rc = stap_start_task_finder();";
s.op->newline() << "if (rc) {";
- s.op->indent(1);
- s.op->newline() << "stap_stop_task_finder();";
+ s.op->newline(1) << "stap_stop_task_finder();";
s.op->newline(-1) << "}";
}
@@ -5513,41 +5561,11 @@ struct itrace_builder: public derived_probe_builder
// If we have a path, we need to validate it.
if (has_path)
- {
- string::size_type start_pos, end_pos;
- string component;
-
- // Make sure it starts with '/'.
- if (path[0] != '/')
- throw semantic_error ("process path must start with a '/'",
- location->tok);
-
- start_pos = 1; // get past the initial '/'
- while ((end_pos = path.find('/', start_pos)) != string::npos)
- {
- component = path.substr(start_pos, end_pos - start_pos);
- // Make sure it isn't empty.
- if (component.size() == 0)
- throw semantic_error ("process path component cannot be empty",
- location->tok);
- // Make sure it isn't relative.
- else if (component == "." || component == "..")
- throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok);
-
- start_pos = end_pos + 1;
- }
- component = path.substr(start_pos);
- // Make sure it doesn't end with '/'.
- if (component.size() == 0)
- throw semantic_error ("process path cannot end with a '/'", location->tok);
- // Make sure it isn't relative.
- else if (component == "." || component == "..")
- throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok);
- }
+ path = find_executable (path);
finished_results.push_back(new itrace_derived_probe(sess, base, location,
has_path, path, pid,
- single_step
+ single_step
));
}
};
@@ -5776,26 +5794,64 @@ struct utrace_var_expanding_copy_visitor: public var_expanding_copy_visitor
string probe_name;
enum utrace_derived_probe_flags flags;
bool target_symbol_seen;
- static bool syscall_function_added;
void visit_target_symbol (target_symbol* e);
};
-bool utrace_var_expanding_copy_visitor::syscall_function_added = false;
-
utrace_derived_probe::utrace_derived_probe (systemtap_session &s,
probe* p, probe_point* l,
bool hp, string &pn, int64_t pd,
enum utrace_derived_probe_flags f):
- derived_probe(p, l), has_path(hp), path(pn), pid(pd), flags(f),
+ derived_probe (p, new probe_point (*l) /* .components soon rewritten */ ),
+ has_path(hp), path(pn), pid(pd), flags(f),
target_symbol_seen(false)
{
// Make a local-variable-expanded copy of the probe body
utrace_var_expanding_copy_visitor v (s, name, flags);
require <statement*> (&v, &(this->body), base->body);
target_symbol_seen = v.target_symbol_seen;
+
+ // Reset the sole element of the "locations" vector as a
+ // "reverse-engineered" form of the incoming (q.base_loc) probe
+ // point. This allows a user to see what program etc.
+ // number any particular match of the wildcards.
+
+ vector<probe_point::component*> comps;
+ if (hp)
+ comps.push_back (new probe_point::component(TOK_PROCESS, new literal_string(path)));
+ else
+ comps.push_back (new probe_point::component(TOK_PROCESS, new literal_number(pid)));
+ switch (flags)
+ {
+ case UDPF_THREAD_BEGIN:
+ comps.push_back (new probe_point::component(TOK_THREAD));
+ comps.push_back (new probe_point::component(TOK_BEGIN));
+ break;
+ case UDPF_THREAD_END:
+ comps.push_back (new probe_point::component(TOK_THREAD));
+ comps.push_back (new probe_point::component(TOK_END));
+ break;
+ case UDPF_SYSCALL:
+ comps.push_back (new probe_point::component(TOK_SYSCALL));
+ break;
+ case UDPF_SYSCALL_RETURN:
+ comps.push_back (new probe_point::component(TOK_SYSCALL));
+ comps.push_back (new probe_point::component(TOK_RETURN));
+ break;
+ case UDPF_BEGIN:
+ comps.push_back (new probe_point::component(TOK_BEGIN));
+ break;
+ case UDPF_END:
+ comps.push_back (new probe_point::component(TOK_END));
+ break;
+ default:
+ assert (0);
+ }
+
+ // Overwrite it.
+ this->sole_location()->components = comps;
}
@@ -5851,27 +5907,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 to grab the syscall .
- if (! syscall_function_added)
- {
- functiondecl *fdecl = new functiondecl;
- fdecl->tok = e->tok;
- embeddedcode *ec = new embeddedcode;
- ec->tok = e->tok;
- ec->code = string("THIS->__retvalue = __stp_user_syscall_nr(CONTEXT->regs); /* pure */");
-
- fdecl->name = string("_syscall_nr_get");
- fdecl->body = ec;
- fdecl->type = pe_long;
- sess.functions.push_back(fdecl);
- syscall_function_added = true;
- }
-
// We're going to substitute a synthesized '_syscall_nr_get'
// function call for the '$syscall' reference.
functioncall* n = new functioncall;
n->tok = e->tok;
- n->function = "_syscall_nr_get";
+ n->function = "_utrace_syscall_nr";
n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
provide <functioncall*> (this, n);
@@ -5893,6 +5933,7 @@ struct utrace_builder: public derived_probe_builder
bool has_path = get_param (parameters, TOK_PROCESS, path);
bool has_pid = get_param (parameters, TOK_PROCESS, pid);
enum utrace_derived_probe_flags flags = UDPF_NONE;
+
assert (has_path || has_pid);
if (has_null_param (parameters, TOK_THREAD))
@@ -5916,42 +5957,10 @@ struct utrace_builder: public derived_probe_builder
// If we have a path, we need to validate it.
if (has_path)
- {
- string::size_type start_pos, end_pos;
- string component;
-
- // XXX: these checks should be done in terms of filesystem
- // operations.
-
- // Make sure it starts with '/'.
- if (path[0] != '/')
- throw semantic_error ("process path must start with a '/'",
- location->tok);
-
- start_pos = 1; // get past the initial '/'
- while ((end_pos = path.find('/', start_pos)) != string::npos)
- {
- component = path.substr(start_pos, end_pos - start_pos);
- // Make sure it isn't empty.
- if (component.size() == 0)
- throw semantic_error ("process path component cannot be empty",
- location->tok);
- // Make sure it isn't relative.
- else if (component == "." || component == "..")
- throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok);
-
- start_pos = end_pos + 1;
- }
- component = path.substr(start_pos);
- // Make sure it doesn't end with '/'.
- if (component.size() == 0)
- throw semantic_error ("process path cannot end with a '/'", location->tok);
- // Make sure it isn't relative.
- else if (component == "." || component == "..")
- throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok);
-
- sess.unwindsym_modules.insert (path);
- }
+ {
+ path = find_executable (path);
+ sess.unwindsym_modules.insert (path);
+ }
finished_results.push_back(new utrace_derived_probe(sess, base, location,
has_path, path, pid,
@@ -6095,6 +6104,8 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->newline();
s.op->newline() << "/* ---- utrace probes ---- */";
+ s.op->newline() << "#include \"task_finder.c\"";
+
s.op->newline() << "enum utrace_derived_probe_flags {";
s.op->indent(1);
s.op->newline() << "UDPF_NONE,";
@@ -6420,15 +6431,6 @@ utrace_derived_probe_group::emit_module_exit (systemtap_session& s)
// user-space probes
// ------------------------------------------------------------------------
-struct uprobe_derived_probe: public derived_probe
-{
- uint64_t process, address;
- bool return_p;
- uprobe_derived_probe (systemtap_session &s, probe* p, probe_point* l,
- uint64_t, uint64_t, bool);
- void join_group (systemtap_session& s);
-};
-
struct uprobe_derived_probe_group: public generic_dpg<uprobe_derived_probe>
{
@@ -6439,12 +6441,130 @@ public:
};
-uprobe_derived_probe::uprobe_derived_probe (systemtap_session &s,
- probe* p, probe_point* l,
- uint64_t pp, uint64_t aa, bool rr):
- derived_probe(p, l), process(pp), address(aa), return_p (rr)
+uprobe_derived_probe::uprobe_derived_probe (const string& function,
+ const string& filename,
+ int line,
+ const string& module,
+ int pid,
+ const string& section,
+ Dwarf_Addr dwfl_addr,
+ Dwarf_Addr addr,
+ dwarf_query & q,
+ Dwarf_Die* scope_die /* may be null */):
+ derived_probe (q.base_probe, new probe_point (*q.base_loc) /* .components soon rewritten */ ),
+ return_p (q.has_return), module (module), pid (pid), section (section), address (addr)
{
- s.need_uprobes = true;
+ // Assert relocation invariants
+ if (section == "" && dwfl_addr != addr) // addr should be absolute
+ throw semantic_error ("missing relocation base against", q.base_loc->tok);
+ if (section != "" && dwfl_addr == addr) // addr should be an offset
+ throw semantic_error ("inconsistent relocation address", q.base_loc->tok);
+
+ // For now, we don't try to handle relocatable addresses
+ if (section != "") throw semantic_error ("cannot relocate user-space probes yet", q.base_loc->tok);
+
+ this->tok = q.base_probe->tok;
+
+ // Make a target-variable-expanded copy of the probe body
+ if (!null_die(scope_die))
+ {
+ dwarf_var_expanding_copy_visitor v (q, scope_die, dwfl_addr); // XXX: user-space deref's!
+ require <statement*> (&v, &(this->body), this->body);
+
+ // If during target-variable-expanding the probe, we added a new block
+ // of code, add it to the start of the probe.
+ if (v.add_block)
+ this->body = new block(v.add_block, this->body);
+
+ // If when target-variable-expanding the probe, we added a new
+ // probe, add it in a new file to the list of files to be processed.
+ if (v.add_probe)
+ {
+ stapfile *f = new stapfile;
+ f->probes.push_back(v.add_probe);
+ q.sess.files.push_back(f);
+ }
+ }
+ // else - null scope_die - $target variables will produce an error during translate phase
+
+ // Reset the sole element of the "locations" vector as a
+ // "reverse-engineered" form of the incoming (q.base_loc) probe
+ // point. This allows a user to see what function / file / line
+ // number any particular match of the wildcards.
+
+ vector<probe_point::component*> comps;
+ 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";
+ else
+ fn_or_stmt = "statement";
+
+ if (q.has_function_str || q.has_statement_str)
+ {
+ string retro_name = function;
+ if (filename != "")
+ retro_name += ("@" + string (filename));
+ if (line != -1)
+ retro_name += (":" + lex_cast<string> (line));
+ comps.push_back
+ (new probe_point::component
+ (fn_or_stmt, new literal_string (retro_name)));
+ }
+ else if (q.has_function_num || q.has_statement_num)
+ {
+ Dwarf_Addr retro_addr;
+ if (q.has_function_num)
+ retro_addr = q.function_num_val;
+ else
+ retro_addr = q.statement_num_val;
+ comps.push_back (new probe_point::component
+ (fn_or_stmt,
+ new literal_number(retro_addr))); // XXX: should be hex if possible
+
+ if (q.has_absolute)
+ comps.push_back (new probe_point::component (TOK_ABSOLUTE));
+ }
+
+ if (q.has_call)
+ comps.push_back (new probe_point::component(TOK_CALL));
+ if (q.has_inline)
+ comps.push_back (new probe_point::component(TOK_INLINE));
+ if (return_p)
+ comps.push_back (new probe_point::component(TOK_RETURN));
+ /*
+ if (has_maxactive)
+ comps.push_back (new probe_point::component
+ (TOK_MAXACTIVE, new literal_number(maxactive_val)));
+ */
+
+ // Overwrite it.
+ this->sole_location()->components = comps;
+}
+
+
+uprobe_derived_probe::uprobe_derived_probe (probe *base,
+ probe_point *location,
+ int pid,
+ Dwarf_Addr addr,
+ bool has_return):
+ derived_probe (base, location), // location is not rewritten here
+ return_p (has_return), pid (pid), address (addr)
+{
+}
+
+
+void
+uprobe_derived_probe::printsig (ostream& o) const
+{
+ // Same as dwarf_derived_probe.
+ sole_location()->print (o);
+ o << " /* pc=" << section << "+0x" << hex << address << dec << " */";
+ printsig_nested (o);
}
@@ -6454,6 +6574,7 @@ uprobe_derived_probe::join_group (systemtap_session& s)
if (! s.uprobe_derived_probes)
s.uprobe_derived_probes = new uprobe_derived_probe_group ();
s.uprobe_derived_probes->enroll (this);
+ task_finder_derived_probe_group::create_session_group (s);
}
@@ -6475,8 +6596,7 @@ struct uprobe_builder: public derived_probe_builder
bool rr = has_null_param (parameters, TOK_RETURN);
assert (b1 && b2); // by pattern_root construction
- finished_results.push_back(new uprobe_derived_probe(sess, base, location,
- process, address, rr));
+ finished_results.push_back(new uprobe_derived_probe(base, location, process, address, rr));
}
};
@@ -6487,29 +6607,48 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
if (probes.empty()) return;
s.op->newline() << "/* ---- user probes ---- */";
+ s.need_uprobes = true; // Ask buildrun.cxx to build extra module if needed
+
// If uprobes isn't in the kernel, pull it in from the runtime.
s.op->newline() << "#if defined(CONFIG_UPROBES) || defined(CONFIG_UPROBES_MODULE)";
s.op->newline() << "#include <linux/uprobes.h>";
s.op->newline() << "#else";
s.op->newline() << "#include \"uprobes/uprobes.h\"";
s.op->newline() << "#endif";
+ s.op->newline() << "#include \"task_finder.c\"";
+
+ s.op->newline() << "#ifndef MAXUPROBES";
+ s.op->newline() << "#define MAXUPROBES 16"; // maximum possible armed uprobes per process() probe point
+ s.op->newline() << "#endif";
+ s.op->newline() << "#define NUMUPROBES (MAXUPROBES*" << probes.size() << ")";
+ // In .bss, the shared pool of uprobe/uretprobe structs. These are
+ // too big to embed in the initialized .data stap_uprobe_spec array.
s.op->newline() << "struct stap_uprobe {";
s.op->newline(1) << "union { struct uprobe up; struct uretprobe urp; };";
- s.op->newline() << "unsigned registered_p:1;";
- s.op->newline() << "unsigned return_p:1;";
- s.op->newline() << "unsigned long process;";
+ s.op->newline() << "int spec_index;"; // index into stap_uprobe_specs; <0 == free && unregistered
+ s.op->newline(-1) << "} stap_uprobes [NUMUPROBES];";
+ s.op->newline() << "DEFINE_MUTEX(stap_uprobes_lock);"; // protects against concurrent registration/unregistration
+
+ s.op->newline() << "struct stap_uprobe_spec {";
+ s.op->newline(1) << "struct stap_task_finder_target finder;";
s.op->newline() << "unsigned long address;";
s.op->newline() << "const char *pp;";
s.op->newline() << "void (*ph) (struct context*);";
- s.op->newline(-1) << "} stap_uprobes [] = {";
+ s.op->newline() << "unsigned return_p:1;";
+ s.op->newline(-1) << "} stap_uprobe_specs [] = {";
s.op->indent(1);
for (unsigned i =0; i<probes.size(); i++)
{
uprobe_derived_probe* p = probes[i];
s.op->newline() << "{";
+ s.op->line() << " .finder = {";
+ if (p->pid != 0)
+ s.op->line() << " .pid=" << p->pid << ", ";
+ else
+ s.op->line() << " .pathname=" << lex_cast_qstring(p->module) << ", ";
+ s.op->line() << "},";
s.op->line() << " .address=0x" << hex << p->address << dec << "UL,";
- s.op->line() << " .process=" << p->process << ",";
s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ",";
s.op->line() << " .ph=&" << p->name << ",";
if (p->return_p) s.op->line() << " .return_p=1,";
@@ -6517,78 +6656,167 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
}
s.op->newline(-1) << "};";
- s.op->newline();
- s.op->newline() << "void enter_uprobe_probe (struct uprobe *inst, struct pt_regs *regs) {";
+ s.op->newline() << "static void enter_uprobe_probe (struct uprobe *inst, struct pt_regs *regs) {";
s.op->newline(1) << "struct stap_uprobe *sup = container_of(inst, struct stap_uprobe, up);";
+ s.op->newline() << "struct stap_uprobe_spec *sups = &stap_uprobe_specs [sup->spec_index];";
common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING");
- s.op->newline() << "c->probe_point = sup->pp;";
+ s.op->newline() << "if (sup->spec_index < 0 ||"
+ << "sup->spec_index >= " << probes.size() << ") return;"; // XXX: should not happen
+ s.op->newline() << "c->probe_point = sups->pp;";
s.op->newline() << "c->regs = regs;";
- s.op->newline() << "(*sup->ph) (c);";
+ s.op->newline() << "(*sups->ph) (c);";
common_probe_entryfn_epilogue (s.op);
s.op->newline(-1) << "}";
- s.op->newline() << "void enter_uretprobe_probe (struct uretprobe_instance *inst, struct pt_regs *regs) {";
+ s.op->newline() << "static void enter_uretprobe_probe (struct uretprobe_instance *inst, struct pt_regs *regs) {";
s.op->newline(1) << "struct stap_uprobe *sup = container_of(inst->rp, struct stap_uprobe, urp);";
+ s.op->newline() << "struct stap_uprobe_spec *sups = &stap_uprobe_specs [sup->spec_index];";
common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING");
- s.op->newline() << "c->probe_point = sup->pp;";
+ s.op->newline() << "if (sup->spec_index < 0 ||"
+ << "sup->spec_index >= " << probes.size() << ") return;"; // XXX: should not happen
+ s.op->newline() << "c->probe_point = sups->pp;";
// XXX: kretprobes saves "c->pi = inst;" too
s.op->newline() << "c->regs = regs;";
- s.op->newline() << "(*sup->ph) (c);";
+ s.op->newline() << "(*sups->ph) (c);";
common_probe_entryfn_epilogue (s.op);
s.op->newline(-1) << "}";
-}
+ // XXX: This sort of plain process.begin/end callback will only work
+ // for uprobes on unrelocatable executables.
+ //
+ // NB: Because these utrace callbacks only occur before / after
+ // userspace instructions run, there is no concurrency control issue
+ // between active uprobe callbacks and these registration /
+ // unregistration pieces.
+
+ // We protect the stap_uprobe->spec_index (which also serves as a
+ // free/busy flag) value with the outer protective probes_lock
+ // mutex, to protect it against concurrent registration /
+ // unregistration. XXX: This should become less naive and let the
+ // mutex only protect stap_uprobe slot allocation and let the uprobe
+ // calls occur outside the critical section.
-void
-uprobe_derived_probe_group::emit_module_init (systemtap_session& s)
-{
- if (probes.empty()) return;
- s.op->newline() << "/* ---- user probes ---- */";
+ s.op->newline();
+ s.op->newline() << "static int stap_uprobe_process_found (struct task_struct *tsk, int register_p, int process_p, struct stap_task_finder_target *tgt) {";
+ s.op->newline(1) << "struct stap_uprobe_spec *sups = container_of(tgt, struct stap_uprobe_spec, finder);";
+ s.op->newline() << "int spec_index = (sups - stap_uprobe_specs);";
+ s.op->newline() << "int handled_p = 0;";
+ s.op->newline() << "int rc = 0;";
+ s.op->newline() << "int i;";
+ // s.op->newline() << "printk (KERN_EMERG \"probe %d %d %d %p\\n\", tsk->tgid, register_p, process_p, tgt);";
+ s.op->newline() << "if (! process_p) return 0;"; // we don't care about threads
+ s.op->newline() << "mutex_lock (& stap_uprobes_lock);";
- s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
+ s.op->newline() << "for (i=0; i<NUMUPROBES; i++) {"; // XXX: slow linear search
s.op->newline(1) << "struct stap_uprobe *sup = & stap_uprobes[i];";
- s.op->newline() << "probe_point = sup->pp;";
- s.op->newline() << "if (sup->return_p) {";
- s.op->newline(1) << "sup->urp.u.pid = sup->process;";
- s.op->newline() << "sup->urp.u.vaddr = sup->address;";
+
+ // register new uprobe
+ s.op->newline() << "if (register_p && sup->spec_index < 0) {";
+ s.op->newline(1) << "sup->spec_index = spec_index;";
+ s.op->newline() << "if (sups->return_p) {";
+ s.op->newline(1) << "sup->urp.u.pid = tsk->tgid;";
+ s.op->newline() << "sup->urp.u.vaddr = sups->address;";
s.op->newline() << "sup->urp.handler = &enter_uretprobe_probe;";
s.op->newline() << "rc = register_uretprobe (& sup->urp);";
s.op->newline(-1) << "} else {";
- s.op->newline(1) << "sup->up.pid = sup->process;";
- s.op->newline() << "sup->up.vaddr = sup->address;";
+ s.op->newline(1) << "sup->up.pid = tsk->tgid;";
+ s.op->newline() << "sup->up.vaddr = sups->address;";
s.op->newline() << "sup->up.handler = &enter_uprobe_probe;";
s.op->newline() << "rc = register_uprobe (& sup->up);";
s.op->newline(-1) << "}";
- s.op->newline() << "if (rc) {";
- s.op->newline(1) << "for (j=i-1; j>=0; j--) {"; // partial rollback
- s.op->newline(1) << "struct stap_uprobe *sup2 = & stap_uprobes[j];";
- s.op->newline() << "if (sup2->return_p) unregister_uretprobe (&sup2->urp);";
- s.op->newline() << "else unregister_uprobe (&sup2->up);";
- // NB: we don't have to clear sup2->registered_p, since the module_exit code is
- // not run for this early-abort case.
+
+ s.op->newline() << "if (rc) {"; // failed to register
+ s.op->newline(1) << "sup->spec_index = -1;";
+ s.op->newline(-1) << "} else {";
+ s.op->newline(1) << "handled_p = 1;"; // success
s.op->newline(-1) << "}";
- s.op->newline() << "break;"; // don't attempt to register any more probes
+ s.op->newline() << "break;"; // exit free slot search whether or not handled_p
+
+ // unregister old uprobe
+ s.op->newline(-1) << "} else if (!register_p && "
+ << "((sups->return_p && sup->urp.u.pid == tsk->tgid) ||" // dying uretprobe
+ << "(!sups->return_p && sup->up.pid == tsk->tgid))) {"; // dying uprobe
+ // XXX: or just check sup->spec_index == spec_index?
+ s.op->newline(1) << "sup->spec_index = -1;";
+ s.op->newline() << "if (sups->return_p) {";
+ s.op->newline(1) << "unregister_uretprobe (& sup->urp);";
+ s.op->newline(-1) << "} else {";
+ s.op->newline(1) << "unregister_uprobe (& sup->up);";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "handled_p = 1;";
+ // XXX: Do we need to keep searching the array for other processes, or
+ // can we break just like for the register-new case?
+
+ s.op->newline(-1) << "}"; // if/else
+
+ s.op->newline(-1) << "}"; // stap_uprobes[] loop
+ s.op->newline() << "mutex_unlock (& stap_uprobes_lock);";
+ s.op->newline() << "if (! handled_p) {";
+ s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {";
+ s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);";
+ s.op->newline() << "_stp_exit ();";
s.op->newline(-1) << "}";
- s.op->newline() << "else sup->registered_p = 1;";
s.op->newline(-1) << "}";
+
+ s.op->newline() << "return 0;"; // XXX: or rc?
+ s.op->newline(-1) << "}";
+ s.op->assert_0_indent();
+
+ s.op->newline();
}
void
-uprobe_derived_probe_group::emit_module_exit (systemtap_session& s)
+uprobe_derived_probe_group::emit_module_init (systemtap_session& s)
{
if (probes.empty()) return;
s.op->newline() << "/* ---- user probes ---- */";
+ s.op->newline() << "for (j=0; j<NUMUPROBES; j++) {";
+ s.op->newline(1) << "struct stap_uprobe *sup = & stap_uprobes[j];";
+ s.op->newline() << "sup->spec_index = -1;"; // free slot
+ s.op->newline(-1) << "}";
+ s.op->newline() << "mutex_init (& stap_uprobes_lock);";
+
s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
- s.op->newline(1) << "struct stap_uprobe *sup = & stap_uprobes[i];";
- s.op->newline() << "if (! sup->registered_p) continue;";
+ s.op->newline(1) << "struct stap_uprobe_spec *sups = & stap_uprobe_specs[i];";
+ s.op->newline() << "probe_point = sups->pp;"; // for error messages
+ s.op->newline() << "sups->finder.callback = & stap_uprobe_process_found;";
+ s.op->newline() << "rc = stap_register_task_finder_target (& sups->finder);";
+
+ // NB: if (rc), there is no need (XXX: nor any way) to clean up any
+ // finders already registered, since mere registration does not
+ // cause any utrace or memory allocation actions. That happens only
+ // later, once the task finder engine starts running. So, for a
+ // partial initialization requiring unwind, we need do nothing.
+ s.op->newline() << "if (rc) break;";
+
+ s.op->newline(-1) << "}";
+}
+
+
+void
+uprobe_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes.empty()) return;
+ s.op->newline() << "/* ---- user probes ---- */";
+
+ // NB: there is no stap_unregister_task_finder_target call;
+ // important stuff like utrace cleanups are done by
+ // __stp_task_finder_cleanup()
+
+ s.op->newline() << "for (j=0; j<NUMUPROBES; j++) {";
+ s.op->newline(1) << "struct stap_uprobe *sup = & stap_uprobes[j];";
+ s.op->newline() << "struct stap_uprobe_spec *sups = &stap_uprobe_specs [sup->spec_index];";
+ s.op->newline() << "if (sup->spec_index < 0) continue;"; // free slot
+ s.op->newline() << "if (sups->return_p) unregister_uretprobe (&sup->urp);";
+ s.op->newline() << "else unregister_uprobe (&sup->up);";
+ s.op->newline() << "sup->spec_index = -1;";
// s.op->newline() << "atomic_add (sdp->u.krp.nmissed, & skipped_count);";
// s.op->newline() << "atomic_add (sdp->u.krp.kp.nmissed, & skipped_count);";
- s.op->newline() << "if (sup->return_p) unregister_uretprobe (&sup->urp);";
- s.op->newline() << "else unregister_uprobe (&sup->up);";
- s.op->newline() << "sup->registered_p = 0;";
s.op->newline(-1) << "}";
+
+ s.op->newline() << "mutex_destroy (& stap_uprobes_lock);";
}
@@ -8812,7 +9040,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 c4370cc8..03ac0941 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, */
diff --git a/util.cxx b/util.cxx
index 97425d76..ab0a1a91 100644
--- a/util.cxx
+++ b/util.cxx
@@ -154,50 +154,69 @@ tokenize(const string& str, vector<string>& tokens,
}
-// Find an executable by name in $PATH.
-bool
-find_executable(const char *name, string& retpath)
+// Resolve an executable name to a canonical full path name, with the
+// same policy as execvp(). A program name not containing a slash
+// will be searched along the $PATH.
+
+string find_executable(const string& name)
{
- const char *p;
- string path;
- vector<string> dirs;
- struct stat st1, st2;
+ string retpath;
+
+ if (name.size() == 0)
+ return name;
+
+ struct stat st;
- if (*name == '/')
+ if (name.find('/') != string::npos) // slash in the path already?
{
retpath = name;
- return true;
}
-
- p = getenv("PATH");
- if (!p)
- return false;
- path = p;
-
- // Split PATH up.
- tokenize(path, dirs, string(":"));
-
- // Search the path looking for the first executable of the right name.
- for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); i++)
+ else // Nope, search $PATH.
{
- string fname = *i + "/" + name;
- const char *f = fname.c_str();
-
- // Look for a normal executable file.
- if (access(f, X_OK) == 0
- && lstat(f, &st1) == 0
- && stat(f, &st2) == 0
- && S_ISREG(st2.st_mode))
+ char *path = getenv("PATH");
+ if (path)
{
- // Found it!
- retpath = fname;
- return true;
- }
+ // Split PATH up.
+ vector<string> dirs;
+ tokenize(string(path), dirs, string(":"));
+
+ // Search the path looking for the first executable of the right name.
+ for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); i++)
+ {
+ string fname = *i + "/" + name;
+ const char *f = fname.c_str();
+
+ // Look for a normal executable file.
+ if (access(f, X_OK) == 0
+ && stat(f, &st) == 0
+ && S_ISREG(st.st_mode))
+ {
+ retpath = fname;
+ break;
+ }
+ }
+ }
+ }
+
+
+ // Could not find the program on the $PATH. We'll just fall back to
+ // the unqualified name, which our caller will probably fail with.
+ if (retpath == "")
+ retpath = name;
+
+ // Canonicalize the path name.
+ char *cf = canonicalize_file_name (retpath.c_str());
+ if (cf)
+ {
+ retpath = string(cf);
+ free (cf);
}
- return false;
+ return retpath;
}
+
+
const string cmdstr_quoted(const string& cmd)
{
// original cmd : substr1
diff --git a/util.h b/util.h
index 97fa7062..7c627ee9 100644
--- a/util.h
+++ b/util.h
@@ -10,7 +10,7 @@ int copy_file(const char *src, const char *dest);
int create_dir(const char *dir);
void tokenize(const std::string& str, std::vector<std::string>& tokens,
const std::string& delimiters);
-bool find_executable(const char *name, std::string& retpath);
+std::string find_executable(const std::string& name);
const std::string cmdstr_quoted(const std::string& cmd);