diff options
Diffstat (limited to 'tapsets.cxx')
-rw-r--r-- | tapsets.cxx | 736 |
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; } |