summaryrefslogtreecommitdiffstats
path: root/tapsets.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'tapsets.cxx')
-rw-r--r--tapsets.cxx736
1 files changed, 720 insertions, 16 deletions
diff --git a/tapsets.cxx b/tapsets.cxx
index 47bb20e2..9528066f 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -218,6 +218,16 @@ common_probe_entryfn_prologue (translator_output* o, string statestr,
o->newline() << "#ifdef STP_TIMING";
o->newline() << "c->statp = 0;";
o->newline() << "#endif";
+ // NB: The following would actually be incorrect.
+ // That's because cycles_sum/cycles_base values are supposed to survive
+ // between consecutive probes. Periodically (STP_OVERLOAD_INTERVAL
+ // cycles), the values will be reset.
+ /*
+ o->newline() << "#ifdef STP_OVERLOAD";
+ o->newline() << "c->cycles_sum = 0;";
+ o->newline() << "c->cycles_base = 0;";
+ o->newline() << "#endif";
+ */
}
@@ -869,7 +879,7 @@ struct dwflpp
void setup(bool kernel, bool debuginfo_needed = true)
{
// XXX: this is where the session -R parameter could come in
- static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug";
+ 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 ?
@@ -2742,6 +2752,14 @@ dwarf_query::build_blacklist()
blfn += "|raw_.*";
blfn += "|.*seq_.*lock.*";
+ // atomic functions
+ blfn += "|atomic_.*";
+ blfn += "|atomic64_.*";
+
+ // few other problematic cases
+ blfn += "|get_bh";
+ blfn += "|put_bh";
+
// Experimental
blfn += "|.*apic.*|.*APIC.*";
blfn += "|.*softirq.*";
@@ -2885,7 +2903,13 @@ dwarf_query::blacklisted_p(const string& funcname,
Dwarf_Addr addr)
{
if (section.substr(0, 6) == string(".init.") ||
- section.substr(0, 6) == string(".exit."))
+ section.substr(0, 6) == string(".exit.") ||
+ section.substr(0, 9) == string(".devinit.") ||
+ section.substr(0, 9) == string(".devexit.") ||
+ section.substr(0, 9) == string(".cpuinit.") ||
+ section.substr(0, 9) == string(".cpuexit.") ||
+ section.substr(0, 9) == string(".meminit.") ||
+ section.substr(0, 9) == string(".memexit."))
{
// NB: module .exit. routines could be probed in theory:
// if the exit handler in "struct module" is diverted,
@@ -3938,7 +3962,7 @@ dwarf_var_expanding_copy_visitor::visit_target_symbol (target_symbol *e)
expr_statement* es = new expr_statement;
es->tok = e->tok;
es->value = a;
- add_probe->body->statements.push_back (es);
+ add_probe->body = new block(add_probe->body, es);
vardecl* vd = new vardecl;
vd->tok = e->tok;
@@ -3971,7 +3995,7 @@ dwarf_var_expanding_copy_visitor::visit_target_symbol (target_symbol *e)
es->tok = e->tok;
es->value = a;
- add_probe->body->statements.push_back (es);
+ add_probe->body = new block(add_probe->body, es);
// (4) Provide the '_dwarf_tvar_{name}_{num}_tmp' variable to
// our parent so it can be used as a substitute for the target
@@ -4144,12 +4168,12 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname,
if (!null_die(scope_die))
{
dwarf_var_expanding_copy_visitor v (q, scope_die, dwfl_addr);
- require <block*> (&v, &(this->body), this->body);
+ 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->statements.insert(this->body->statements.begin(), 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.
@@ -4896,6 +4920,643 @@ module_info::~module_info()
// ------------------------------------------------------------------------
+// task_finder derived 'probes': These don't really exist. The whole
+// purpose of the task_finder_derived_probe_group is to make sure that
+// stap_start_task_finder()/stap_stop_task_finder() get called only
+// once and in the right place.
+// ------------------------------------------------------------------------
+
+struct task_finder_derived_probe: public derived_probe
+{
+ // Dummy constructor for gcc 3.4 compatibility
+ task_finder_derived_probe (): derived_probe (0) { assert(0); }
+};
+
+
+struct task_finder_derived_probe_group: public generic_dpg<task_finder_derived_probe>
+{
+public:
+ static void create_session_group (systemtap_session& s);
+
+ void emit_module_decls (systemtap_session& ) { }
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+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);
+ }
+}
+
+
+void
+task_finder_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ s.op->newline();
+ s.op->newline() << "/* ---- task finder ---- */";
+ 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) << "}";
+}
+
+
+void
+task_finder_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ s.op->newline();
+ s.op->newline() << "/* ---- task finder ---- */";
+ s.op->newline() << "stap_stop_task_finder();";
+}
+
+
+// ------------------------------------------------------------------------
+// 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.
+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_NFLAGS
+};
+
+struct utrace_derived_probe: public derived_probe
+{
+ bool has_path;
+ string path;
+ int64_t pid;
+ enum utrace_derived_probe_flags flags;
+ bool target_symbol_seen;
+
+ utrace_derived_probe (systemtap_session &s, probe* p, probe_point* l,
+ bool hp, string &pn, int64_t pd,
+ enum utrace_derived_probe_flags f);
+ void join_group (systemtap_session& s);
+};
+
+
+struct utrace_derived_probe_group: public generic_dpg<utrace_derived_probe>
+{
+private:
+ map<string, vector<utrace_derived_probe*> > probes_by_path;
+ typedef map<string, vector<utrace_derived_probe*> >::iterator p_b_path_iterator;
+ map<int64_t, vector<utrace_derived_probe*> > probes_by_pid;
+ typedef map<int64_t, vector<utrace_derived_probe*> >::iterator p_b_pid_iterator;
+ unsigned num_probes;
+ bool flags_seen[UDPF_NFLAGS];
+
+ void emit_probe_decl (systemtap_session& s, utrace_derived_probe *p);
+
+public:
+ utrace_derived_probe_group(): num_probes(0), flags_seen() { }
+
+ void enroll (utrace_derived_probe* probe);
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+struct utrace_var_expanding_copy_visitor: public var_expanding_copy_visitor
+{
+ utrace_var_expanding_copy_visitor(systemtap_session& s, const string& pn,
+ enum utrace_derived_probe_flags f):
+ sess (s), probe_name (pn), flags (f), target_symbol_seen (false) {}
+
+ systemtap_session& sess;
+ string probe_name;
+ enum utrace_derived_probe_flags flags;
+ bool target_symbol_seen;
+
+ void visit_target_symbol (target_symbol* e);
+};
+
+
+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),
+ 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;
+}
+
+
+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);
+}
+
+
+void
+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)
+ throw semantic_error ("only \"process(PATH_OR_PID).syscall\" and \"process(PATH_OR_PID).syscall.return\" probes support target symbols",
+ e->tok);
+
+ if (e->base_name != "$syscall")
+ throw semantic_error ("invalid target symbol for utrace probe, $syscall expected",
+ e->tok);
+
+ if (e->components.size() > 0)
+ {
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("utrace target variable '$syscall' may not be used as array",
+ e->tok);
+ break;
+ case target_symbol::comp_struct_member:
+ throw semantic_error("utrace target variable '$syscall' may not be used as a structure",
+ e->tok);
+ break;
+ default:
+ throw semantic_error ("invalid use of utrace target variable '$syscall'",
+ e->tok);
+ break;
+ }
+ }
+
+ bool lvalue = is_active_lvalue(e);
+ if (lvalue)
+ throw semantic_error("utrace $syscall variable is read-only", e->tok);
+
+ // 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.
+ functioncall* n = new functioncall;
+ n->tok = e->tok;
+ n->function = fname;
+ n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
+
+ provide <functioncall*> (this, n);
+}
+
+
+struct utrace_builder: public derived_probe_builder
+{
+ utrace_builder() {}
+ virtual void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ std::map<std::string, literal *> const & parameters,
+ vector<derived_probe *> & finished_results)
+ {
+ string path;
+ int64_t pid;
+
+ 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, "death"))
+ flags = UDPF_DEATH;
+ else if (has_null_param (parameters, "syscall"))
+ {
+ if (has_null_param (parameters, TOK_RETURN))
+ flags = UDPF_SYSCALL_EXIT;
+ else
+ flags = UDPF_SYSCALL_ENTRY;
+ }
+ else if (has_null_param (parameters, "clone"))
+ flags = UDPF_CLONE;
+ else if (has_null_param (parameters, "exec"))
+ flags = UDPF_EXEC;
+
+ // 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);
+ }
+
+ finished_results.push_back(new utrace_derived_probe(sess, base, location,
+ has_path, path, pid,
+ flags));
+ }
+};
+
+
+void
+utrace_derived_probe_group::enroll (utrace_derived_probe* p)
+{
+ if (p->has_path)
+ probes_by_path[p->path].push_back(p);
+ else
+ probes_by_pid[p->pid].push_back(p);
+ num_probes++;
+ flags_seen[p->flags] = true;
+
+ // XXX: multiple exec probes (for instance) for the same path (or
+ // pid) should all share a utrace report function, and have their
+ // handlers executed sequentially.
+}
+
+
+void
+utrace_derived_probe_group::emit_probe_decl (systemtap_session& s,
+ utrace_derived_probe *p)
+{
+ s.op->newline() << "{";
+ s.op->line() << " .tgt={";
+
+ if (p->has_path)
+ {
+ s.op->line() << " .pathname=\"" << p->path << "\",";
+ s.op->line() << " .pid=0,";
+ }
+ else
+ {
+ s.op->line() << " .pathname=NULL,";
+ s.op->line() << " .pid=" << p->pid << ",";
+ }
+
+ s.op->line() << " .callback=&_stp_utrace_probe_cb,";
+ s.op->line() << " },";
+ s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ",";
+ s.op->line() << " .ph=&" << p->name << ",";
+
+ // 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)),";
+ 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)),";
+ 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)),";
+ break;
+ case UDPF_SYSCALL_ENTRY:
+ 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)),";
+ break;
+ case UDPF_SYSCALL_EXIT:
+ 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)),";
+ break;
+ default:
+ throw semantic_error ("bad utrace probe flag");
+ break;
+ }
+ 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())
+ return;
+
+ s.op->newline();
+ s.op->newline() << "/* ---- utrace probes ---- */";
+ 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() << "struct utrace_engine_ops ops;";
+ s.op->newline() << "unsigned long flags;";
+ s.op->newline() << "int engine_attached;";
+ s.op->newline(-1) << "};";
+
+ // Output handler function for CLONE events
+ if (flags_seen[UDPF_CLONE])
+ {
+ 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->indent(1);
+ s.op->newline() << "struct stap_utrace_probe *p = (struct stap_utrace_probe *)engine->data;";
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING");
+ s.op->newline() << "c->probe_point = p->pp;";
+
+ // call probe function
+ s.op->newline() << "(*p->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+
+ s.op->newline() << "return UTRACE_ACTION_RESUME;";
+ s.op->newline(-1) << "}";
+ }
+
+ // Output handler function for EXEC and DEATH events
+ if (flags_seen[UDPF_EXEC] || flags_seen[UDPF_DEATH])
+ {
+ s.op->newline() << "static void stap_utrace_probe_handler(struct task_struct *tsk, struct stap_utrace_probe *p) {";
+ s.op->indent(1);
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING");
+ s.op->newline() << "c->probe_point = p->pp;";
+
+ // call probe function
+ s.op->newline() << "(*p->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+
+ s.op->newline() << "return;";
+ s.op->newline(-1) << "}";
+ }
+
+ // Output handler function for SYSCALL_ENTRY and SYSCALL_EXIT events
+ if (flags_seen[UDPF_SYSCALL_ENTRY] || flags_seen[UDPF_SYSCALL_EXIT])
+ {
+ 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);
+ s.op->newline() << "struct stap_utrace_probe *p = (struct stap_utrace_probe *)engine->data;";
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING");
+ s.op->newline() << "c->probe_point = p->pp;";
+ s.op->newline() << "c->regs = regs;";
+
+ // call probe function
+ s.op->newline() << "(*p->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+
+ s.op->newline() << "return UTRACE_ACTION_RESUME;";
+ s.op->newline(-1) << "}";
+ }
+
+ // 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->indent(1);
+ s.op->newline() << "int rc = 0;";
+ s.op->newline() << "struct stap_utrace_probe *p = container_of(tgt, struct stap_utrace_probe, tgt);";
+ s.op->newline() << "struct utrace_attached_engine *engine;";
+
+ s.op->newline() << "if (register_p) {";
+ s.op->indent(1);
+
+ 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):";
+ 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->indent(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)):";
+ 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->indent(1);
+ s.op->newline() << "int error = -PTR_ERR(engine);";
+ s.op->newline() << "if (error != ENOENT) {";
+ 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(-1) << "}";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "else if (unlikely(engine == NULL)) {";
+ 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->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(-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.
+ s.op->newline() << "else {";
+ s.op->indent(1);
+ // For death probes, go ahead and call the probe directly.
+ if (flags_seen[UDPF_DEATH])
+ {
+ s.op->newline() << "if (p->flags == UTRACE_EVENT(DEATH)) {";
+ s.op->indent(1);
+ s.op->newline() << "stap_utrace_probe_handler(tsk, p);";
+ s.op->newline(-1) << "}";
+ }
+ s.op->newline(-1) << "}";
+ s.op->newline() << "return rc;";
+ s.op->newline(-1) << "}";
+
+ s.op->newline() << "struct stap_utrace_probe stap_utrace_probes[] = {";
+ s.op->indent(1);
+
+ // Set up 'process(PATH)' probes
+ if (! probes_by_path.empty())
+ {
+ for (p_b_path_iterator it = probes_by_path.begin();
+ it != probes_by_path.end(); it++)
+ {
+ for (unsigned i = 0; i < it->second.size(); i++)
+ {
+ utrace_derived_probe *p = it->second[i];
+ emit_probe_decl(s, p);
+ }
+ }
+ }
+
+ // Set up 'process(PID)' probes
+ if (! probes_by_pid.empty())
+ {
+ for (p_b_pid_iterator it = probes_by_pid.begin();
+ it != probes_by_pid.end(); it++)
+ {
+ for (unsigned i = 0; i < it->second.size(); i++)
+ {
+ utrace_derived_probe *p = it->second[i];
+ emit_probe_decl(s, p);
+ }
+ }
+ }
+ s.op->newline(-1) << "};";
+}
+
+
+void
+utrace_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ if (probes_by_path.empty() && probes_by_pid.empty())
+ return;
+
+ s.op->newline();
+ s.op->newline() << "/* ---- utrace probes ---- */";
+
+ s.op->newline() << "for (i=0; i<" << num_probes << "; i++) {";
+ s.op->indent(1);
+ s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[i];";
+ s.op->newline() << "rc = stap_register_task_finder_target(&p->tgt);";
+ s.op->newline(-1) << "}";
+
+ // rollback all utrace probes
+ s.op->newline() << "if (rc) {";
+ s.op->indent(1);
+ s.op->newline() << "for (j=i-1; j>=0; j--) {";
+ s.op->indent(1);
+ s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[j];";
+
+ s.op->newline() << "if (p->engine_attached) {";
+ s.op->indent(1);
+ s.op->newline() << "stap_utrace_detach_ops(&p->ops);";
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}";
+
+ s.op->newline(-1) << "}";
+}
+
+
+void
+utrace_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes_by_path.empty() && probes_by_pid.empty()) return;
+
+ s.op->newline();
+ s.op->newline() << "/* ---- utrace probes ---- */";
+ s.op->newline() << "for (i=0; i<" << num_probes << "; i++) {";
+ s.op->indent(1);
+ s.op->newline() << "struct stap_utrace_probe *p = &stap_utrace_probes[i];";
+
+ s.op->newline() << "if (p->engine_attached) {";
+ s.op->indent(1);
+ s.op->newline() << "stap_utrace_detach_ops(&p->ops);";
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}";
+}
+
+
+// ------------------------------------------------------------------------
// user-space probes
// ------------------------------------------------------------------------
@@ -5359,6 +6020,7 @@ profile_derived_probe_group::emit_module_exit (systemtap_session& s)
}
+
// ------------------------------------------------------------------------
// procfs file derived probes
// ------------------------------------------------------------------------
@@ -5426,7 +6088,7 @@ procfs_derived_probe::procfs_derived_probe (systemtap_session &s, probe* p,
{
// Make a local-variable-expanded copy of the probe body
procfs_var_expanding_copy_visitor v (s, name, path, write);
- require <block*> (&v, &(this->body), base->body);
+ require <statement*> (&v, &(this->body), base->body);
target_symbol_seen = v.target_symbol_seen;
}
@@ -5837,6 +6499,7 @@ procfs_builder::build(systemtap_session & sess,
}
+
// ------------------------------------------------------------------------
// statically inserted macro-based derived probes
// ------------------------------------------------------------------------
@@ -6085,7 +6748,7 @@ mark_derived_probe::mark_derived_probe (systemtap_session &s,
// Now make a local-variable-expanded copy of the probe body
mark_var_expanding_copy_visitor v (sess, name, mark_args);
- require <block*> (&v, &(this->body), base->body);
+ require <statement*> (&v, &(this->body), base->body);
target_symbol_seen = v.target_symbol_seen;
if (sess.verbose > 2)
@@ -6237,7 +6900,19 @@ void
mark_derived_probe::join_group (systemtap_session& s)
{
if (! s.mark_derived_probes)
- s.mark_derived_probes = new mark_derived_probe_group ();
+ {
+ s.mark_derived_probes = new mark_derived_probe_group ();
+
+ // Make sure <linux/marker.h> is included early.
+ embeddedcode *ec = new embeddedcode;
+ ec->tok = NULL;
+ ec->code = string("#if ! defined(CONFIG_MARKERS)\n")
+ + string("#error \"Need CONFIG_MARKERS!\"\n")
+ + string("#endif\n")
+ + string("#include <linux/marker.h>\n");
+
+ s.embeds.push_back(ec);
+ }
s.mark_derived_probes->enroll (this);
}
@@ -6316,12 +6991,6 @@ mark_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->newline() << "/* ---- marker probes ---- */";
- // Warn of misconfigured kernels
- s.op->newline() << "#if ! defined(CONFIG_MARKERS)";
- s.op->newline() << "#error \"Need CONFIG_MARKERS!\"";
- s.op->newline() << "#endif";
- s.op->newline();
-
s.op->newline() << "struct stap_marker_probe {";
s.op->newline(1) << "const char * const name;";
s.op->newline() << "const char * const format;";
@@ -6473,6 +7142,11 @@ mark_builder::build(systemtap_session & sess,
string::size_type notwhite = format.find_first_not_of(" \t");
format.erase(0, notwhite);
+ // If the format is empty, make sure we add back a space
+ // character, which is what MARK_NOARGS expands to.
+ if (format.length() == 0)
+ format = " ";
+
if (sess.verbose>3)
clog << "'" << name << "' '" << module << "' '" << format
<< "'" << endl;
@@ -6535,6 +7209,7 @@ mark_builder::build(systemtap_session & sess,
}
+
// ------------------------------------------------------------------------
// hrtimer derived probes
// ------------------------------------------------------------------------
@@ -6828,6 +7503,7 @@ timer_builder::register_patterns(match_node *root)
}
+
// ------------------------------------------------------------------------
// perfmon derived probes
// ------------------------------------------------------------------------
@@ -6970,7 +7646,7 @@ perfmon_derived_probe::perfmon_derived_probe (probe* p, probe_point* l,
// Now make a local-variable-expanded copy of the probe body
perfmon_var_expanding_copy_visitor v (sess, probes_allocated-1);
- require <block*> (&v, &(this->body), base->body);
+ require <statement*> (&v, &(this->body), base->body);
if (sess.verbose > 1)
clog << "perfmon-based probe" << endl;
@@ -7278,6 +7954,28 @@ register_standard_tapsets(systemtap_session & s)
->bind_num(TOK_STATEMENT)->bind(TOK_ABSOLUTE)->bind(TOK_RETURN)
->bind(new uprobe_builder ());
+ // utrace user-space probes
+ s.pattern_root->bind_str(TOK_PROCESS)->bind("clone")
+ ->bind(new utrace_builder ());
+ s.pattern_root->bind_num(TOK_PROCESS)->bind("clone")
+ ->bind(new utrace_builder ());
+ s.pattern_root->bind_str(TOK_PROCESS)->bind("exec")
+ ->bind(new utrace_builder ());
+ s.pattern_root->bind_num(TOK_PROCESS)->bind("exec")
+ ->bind(new utrace_builder ());
+ s.pattern_root->bind_str(TOK_PROCESS)->bind("syscall")
+ ->bind(new utrace_builder ());
+ s.pattern_root->bind_num(TOK_PROCESS)->bind("syscall")
+ ->bind(new utrace_builder ());
+ s.pattern_root->bind_str(TOK_PROCESS)->bind("syscall")->bind(TOK_RETURN)
+ ->bind(new utrace_builder ());
+ s.pattern_root->bind_num(TOK_PROCESS)->bind("syscall")->bind(TOK_RETURN)
+ ->bind(new utrace_builder ());
+ s.pattern_root->bind_str(TOK_PROCESS)->bind("death")
+ ->bind(new utrace_builder ());
+ s.pattern_root->bind_num(TOK_PROCESS)->bind("death")
+ ->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")
@@ -7312,6 +8010,12 @@ all_session_groups(systemtap_session& s)
DOONE(hrtimer);
DOONE(perfmon);
DOONE(procfs);
+
+ // Another "order is important" item. We want to make sure we
+ // "register" the dummy task_finder probe group after all probe
+ // groups that use the task_finder.
+ DOONE(utrace);
+ DOONE(task_finder);
#undef DOONE
return g;
}