summaryrefslogtreecommitdiffstats
path: root/tapsets.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'tapsets.cxx')
-rw-r--r--tapsets.cxx808
1 files changed, 797 insertions, 11 deletions
diff --git a/tapsets.cxx b/tapsets.cxx
index 01c838d9..f99fbef4 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -2619,7 +2619,38 @@ struct uprobe_derived_probe: public derived_probe
void join_group (systemtap_session& s);
};
+struct kprobe_derived_probe: public derived_probe
+{
+ kprobe_derived_probe (probe *base,
+ probe_point *location,
+ const string& name,
+ int64_t stmt_addr,
+ bool has_return,
+ bool has_statement
+ );
+ string symbol_name;
+ Dwarf_Addr addr;
+ bool has_return;
+ bool has_statement;
+ bool has_maxactive;
+ long maxactive_val;
+ bool access_var;
+ void printsig (std::ostream &o) const;
+ void join_group (systemtap_session& s);
+};
+struct kprobe_derived_probe_group: public derived_probe_group
+{
+private:
+ multimap<string,kprobe_derived_probe*> probes_by_module;
+ typedef multimap<string,kprobe_derived_probe*>::iterator p_b_m_iterator;
+
+public:
+ void enroll (kprobe_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 dwarf_derived_probe_group: public derived_probe_group
{
@@ -4449,7 +4480,6 @@ struct dwarf_var_expanding_visitor: public var_expanding_visitor
};
-
unsigned var_expanding_visitor::tick = 0;
void
@@ -5104,9 +5134,64 @@ struct dwarf_cast_expanding_visitor: public var_expanding_visitor
dwarf_cast_expanding_visitor(systemtap_session& s, dwarf_builder& db):
s(s), db(db) {}
void visit_cast_op (cast_op* e);
+ void filter_special_modules(string& module);
};
+void dwarf_cast_expanding_visitor::filter_special_modules(string& module)
+{
+ // look for "kmod<path/to/header>" or "umod<path/to/header>"
+ // for those cases, build a module including that header
+ if (module.rfind('>') == module.size() - 1 &&
+ (module.compare(0, 5, "kmod<") == 0 ||
+ module.compare(0, 5, "umod<") == 0))
+ {
+ string cached_module;
+ if (s.use_cache)
+ {
+ // see if the cached module exists
+ find_typequery_hash(s, module, cached_module);
+ if (!cached_module.empty())
+ {
+ int fd = open(cached_module.c_str(), O_RDONLY);
+ if (fd != -1)
+ {
+ if (s.verbose > 2)
+ clog << "Pass 2: using cached " << cached_module << endl;
+ module = cached_module;
+ close(fd);
+ return;
+ }
+ }
+ }
+
+ // no cached module, time to make it
+ int rc;
+ string new_module, header = module.substr(5, module.size() - 6);
+ if (module[0] == 'k')
+ rc = make_typequery_kmod(s, header, new_module);
+ else
+ rc = make_typequery_umod(s, header, new_module);
+ if (rc == 0)
+ {
+ module = new_module;
+
+ if (s.use_cache)
+ {
+ // try to save typequery in the cache
+ if (s.verbose > 2)
+ clog << "Copying " << new_module
+ << " to " << cached_module << endl;
+ if (copy_file(new_module.c_str(),
+ cached_module.c_str()) != 0)
+ cerr << "Copy failed (\"" << new_module << "\" to \""
+ << cached_module << "\"): " << strerror(errno) << endl;
+ }
+ }
+ }
+}
+
+
void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
{
bool lvalue = is_active_lvalue(e);
@@ -5125,6 +5210,7 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
size_t mod_begin = mod_end + 1;
mod_end = e->module.find(':', mod_begin);
string module = e->module.substr(mod_begin, mod_end - mod_begin);
+ filter_special_modules(module);
// NB: This uses '/' to distinguish between kernel modules and userspace,
// which means that userspace modules won't get any PATH searching.
@@ -5134,10 +5220,21 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
// kernel or kernel module target
if (! db.kern_dw)
{
- db.kern_dw = new dwflpp(s);
- db.kern_dw->setup_kernel(true);
+ dw = new dwflpp(s);
+ try
+ {
+ dw->setup_kernel(true);
+ }
+ catch (const semantic_error& er)
+ {
+ /* ignore and go to the next module */
+ delete dw;
+ continue;
+ }
+ db.kern_dw = dw;
}
- dw = db.kern_dw;
+ else
+ dw = db.kern_dw;
}
else
{
@@ -5148,7 +5245,16 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
if (db.user_dw.find(module) == db.user_dw.end())
{
dw = new dwflpp(s);
- dw->setup_user(module);
+ try
+ {
+ dw->setup_user(module);
+ }
+ catch (const semantic_error& er)
+ {
+ /* ignore and go to the next module */
+ delete dw;
+ continue;
+ }
db.user_dw[module] = dw;
}
else
@@ -5468,6 +5574,23 @@ dwarf_derived_probe_group::enroll (dwarf_derived_probe* p)
// sequentially.
}
+/*
+void
+dwarf_derived_probe_group::enroll (kprobe_derived_probe* p)
+{
+ dwarf_derived_probe *dw_probe = new dwarf_derived_probe (p->symbol_name,
+ "",0,
+ p->module_name,
+ p->section_name,
+ 0,0,
+ p->q,NULL);
+ probes_by_module.insert (make_pair (p->module, p));
+
+ // XXX: probes put at the same address should all share a
+ // single kprobe/kretprobe, and have their handlers executed
+ // sequentially.
+}
+*/
void
dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
@@ -5550,6 +5673,7 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
CALCIT(module);
CALCIT(section);
CALCIT(pp);
+#undef CALCIT
s.op->newline() << "const unsigned long address;";
s.op->newline() << "void (* const ph) (struct context*);";
@@ -6319,7 +6443,6 @@ emit_vma_callback_probe_decl (systemtap_session& s,
s.op->line() << " },";
}
-
// ------------------------------------------------------------------------
// task_finder derived 'probes': These don't really exist. The whole
// purpose of the task_finder_derived_probe_group is to make sure that
@@ -6728,17 +6851,24 @@ public:
struct utrace_var_expanding_visitor: public var_expanding_visitor
{
- utrace_var_expanding_visitor(systemtap_session& s, const string& pn,
+ utrace_var_expanding_visitor(systemtap_session& s, probe_point* l,
+ const string& pn,
enum utrace_derived_probe_flags f):
- sess (s), probe_name (pn), flags (f), target_symbol_seen (false) {}
+ sess (s), base_loc (l), probe_name (pn), flags (f),
+ target_symbol_seen (false), add_block(NULL), add_probe(NULL) {}
systemtap_session& sess;
+ probe_point* base_loc;
string probe_name;
enum utrace_derived_probe_flags flags;
bool target_symbol_seen;
+ block *add_block;
+ probe *add_probe;
+ std::map<std::string, symbol *> return_ts_map;
void visit_target_symbol_arg (target_symbol* e);
void visit_target_symbol_context (target_symbol* e);
+ void visit_target_symbol_cached (target_symbol* e);
void visit_target_symbol (target_symbol* e);
};
@@ -6753,10 +6883,23 @@ utrace_derived_probe::utrace_derived_probe (systemtap_session &s,
target_symbol_seen(false)
{
// Expand local variables in the probe body
- utrace_var_expanding_visitor v (s, name, flags);
+ utrace_var_expanding_visitor v (s, l, name, flags);
this->body = v.require (this->body);
target_symbol_seen = v.target_symbol_seen;
+ // 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);
+ s.files.push_back(f);
+ }
+
// 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.
@@ -6816,6 +6959,216 @@ utrace_derived_probe::join_group (systemtap_session& s)
void
+utrace_var_expanding_visitor::visit_target_symbol_cached (target_symbol* e)
+{
+ // Get the full name of the target symbol.
+ stringstream ts_name_stream;
+ e->print(ts_name_stream);
+ string ts_name = ts_name_stream.str();
+
+ // Check and make sure we haven't already seen this target
+ // variable in this return probe. If we have, just return our
+ // last replacement.
+ map<string, symbol *>::iterator i = return_ts_map.find(ts_name);
+ if (i != return_ts_map.end())
+ {
+ provide (i->second);
+ return;
+ }
+
+ // We've got to do several things here to handle target
+ // variables in return probes.
+
+ // (1) Synthesize a global array which is the cache of the
+ // target variable value. We don't need a nesting level counter
+ // like the dwarf_var_expanding_visitor::visit_target_symbol()
+ // does since a particular thread can only be in one system
+ // calls at a time. The array will look like this:
+ //
+ // _utrace_tvar_{name}_{num}
+ string aname = (string("_utrace_tvar_")
+ + e->base_name.substr(1)
+ + "_" + lex_cast<string>(tick++));
+ vardecl* vd = new vardecl;
+ vd->name = aname;
+ vd->tok = e->tok;
+ sess.globals.push_back (vd);
+
+ // (2) Create a new code block we're going to insert at the
+ // beginning of this probe to get the cached value into a
+ // temporary variable. We'll replace the target variable
+ // reference with the temporary variable reference. The code
+ // will look like this:
+ //
+ // _utrace_tvar_tid = tid()
+ // _utrace_tvar_{name}_{num}_tmp
+ // = _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+ // delete _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+
+ // (2a) Synthesize the tid temporary expression, which will look
+ // like this:
+ //
+ // _utrace_tvar_tid = tid()
+ symbol* tidsym = new symbol;
+ tidsym->name = string("_utrace_tvar_tid");
+ tidsym->tok = e->tok;
+
+ if (add_block == NULL)
+ {
+ add_block = new block;
+ add_block->tok = e->tok;
+
+ // Synthesize a functioncall to grab the thread id.
+ functioncall* fc = new functioncall;
+ fc->tok = e->tok;
+ fc->function = string("tid");
+
+ // Assign the tid to '_utrace_tvar_tid'.
+ assignment* a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = tidsym;
+ a->right = fc;
+
+ expr_statement* es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+ add_block->statements.push_back (es);
+ }
+
+ // (2b) Synthesize an array reference and assign it to a
+ // temporary variable (that we'll use as replacement for the
+ // target variable reference). It will look like this:
+ //
+ // _utrace_tvar_{name}_{num}_tmp
+ // = _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+
+ arrayindex* ai_tvar = new arrayindex;
+ ai_tvar->tok = e->tok;
+
+ symbol* sym = new symbol;
+ sym->name = aname;
+ sym->tok = e->tok;
+ ai_tvar->base = sym;
+
+ ai_tvar->indexes.push_back(tidsym);
+
+ symbol* tmpsym = new symbol;
+ tmpsym->name = aname + "_tmp";
+ tmpsym->tok = e->tok;
+
+ assignment* a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = tmpsym;
+ a->right = ai_tvar;
+
+ expr_statement* es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+
+ add_block->statements.push_back (es);
+
+ // (2c) Delete the array value. It will look like this:
+ //
+ // delete _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+
+ delete_statement* ds = new delete_statement;
+ ds->tok = e->tok;
+ ds->value = ai_tvar;
+ add_block->statements.push_back (ds);
+
+ // (3) We need an entry probe that saves the value for us in the
+ // global array we created. Create the entry probe, which will
+ // look like this:
+ //
+ // probe process(PATH_OR_PID).syscall {
+ // _utrace_tvar_tid = tid()
+ // _utrace_tvar_{name}_{num}[_utrace_tvar_tid] = ${param}
+ // }
+ //
+ // Why the temporary for tid()? If we end up caching more
+ // than one target variable, we can reuse the temporary instead
+ // of calling tid() multiple times.
+
+ if (add_probe == NULL)
+ {
+ add_probe = new probe;
+ add_probe->tok = e->tok;
+
+ // We need the name of the current probe point, minus the
+ // ".return". Create a new probe point, copying all the
+ // components, stopping when we see the ".return"
+ // component.
+ probe_point* pp = new probe_point;
+ for (unsigned c = 0; c < base_loc->components.size(); c++)
+ {
+ if (base_loc->components[c]->functor == "return")
+ break;
+ else
+ pp->components.push_back(base_loc->components[c]);
+ }
+ pp->tok = e->tok;
+ pp->optional = base_loc->optional;
+ add_probe->locations.push_back(pp);
+
+ add_probe->body = new block;
+ add_probe->body->tok = e->tok;
+
+ // Synthesize a functioncall to grab the thread id.
+ functioncall* fc = new functioncall;
+ fc->tok = e->tok;
+ fc->function = string("tid");
+
+ // Assign the tid to '_utrace_tvar_tid'.
+ assignment* a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = tidsym;
+ a->right = fc;
+
+ expr_statement* es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+ add_probe->body = new block(add_probe->body, es);
+
+ vardecl* vd = new vardecl;
+ vd->tok = e->tok;
+ vd->name = tidsym->name;
+ vd->type = pe_long;
+ vd->set_arity(0);
+ add_probe->locals.push_back(vd);
+ }
+
+ // Save the value, like this:
+ //
+ // _utrace_tvar_{name}_{num}[_utrace_tvar_tid] = ${param}
+ a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = ai_tvar;
+ a->right = e;
+
+ es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+
+ add_probe->body = new block(add_probe->body, es);
+
+ // (4) Provide the '_utrace_tvar_{name}_{num}_tmp' variable to
+ // our parent so it can be used as a substitute for the target
+ // symbol.
+ provide (tmpsym);
+
+ // (5) Remember this replacement since we might be able to reuse
+ // it later if the same return probe references this target
+ // symbol again.
+ return_ts_map[ts_name] = tmpsym;
+ return;
+}
+
+
+void
utrace_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
{
string argnum_s = e->base_name.substr(4,e->base_name.length()-4);
@@ -6903,8 +7256,30 @@ utrace_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
throw semantic_error ("only \"process(PATH_OR_PID).syscall.return\" support $return.", e->tok);
fname = "_utrace_syscall_return";
}
+ else if (sname == "$syscall")
+ {
+ // If we've got a syscall entry probe, we can just call the
+ // right function.
+ if (flags == UDPF_SYSCALL) {
+ fname = "_utrace_syscall_nr";
+ }
+ // If we're in a syscal return probe, we can't really access
+ // $syscall. So, similar to what
+ // dwarf_var_expanding_visitor::visit_target_symbol() does,
+ // we'll create an syscall entry probe to cache $syscall, then
+ // we'll access the cached value in the syscall return probe.
+ else {
+ visit_target_symbol_cached (e);
+
+ // Remember that we've seen a target variable.
+ target_symbol_seen = true;
+ return;
+ }
+ }
else
- fname = "_utrace_syscall_nr";
+ {
+ throw semantic_error ("unknown target variable", e->tok);
+ }
// Remember that we've seen a target variable.
target_symbol_seen = true;
@@ -7258,7 +7633,7 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->indent(1);
s.op->newline() << "switch (p->flags) {";
s.op->indent(1);
- // For death probes, go ahead and call the probe directly.
+ // For end probes, go ahead and call the probe directly.
if (flags_seen[UDPF_END])
{
s.op->newline() << "case UDPF_END:";
@@ -7952,8 +8327,402 @@ uprobe_derived_probe_group::emit_module_exit (systemtap_session& s)
s.op->newline() << "mutex_destroy (& stap_uprobes_lock);";
}
+// ------------------------------------------------------------------------
+// Kprobe derived probes
+// ------------------------------------------------------------------------
+
+static string TOK_KPROBE("kprobe");
+
+kprobe_derived_probe::kprobe_derived_probe (probe *base,
+ probe_point *location,
+ const string& name,
+ int64_t stmt_addr,
+ bool if_return,
+ bool if_statement
+):
+ derived_probe (base, location),
+ symbol_name (name), addr (stmt_addr),
+ has_return (if_return), has_statement (if_statement)
+{
+ this->tok = base->tok;
+ this->access_var = false;
+
+#ifndef USHRT_MAX
+#define USHRT_MAX 32767
+#endif
+
+ // Expansion of $target variables in the probe body produces an error during translate phase
+ vector<probe_point::component*> comps;
+
+ if (has_return)
+ comps.push_back (new probe_point::component(TOK_RETURN));
+
+ this->sole_location()->components = comps;
+}
+
+void kprobe_derived_probe::printsig (ostream& o) const
+{
+ sole_location()->print (o);
+ o << " /* " << " name = " << symbol_name << "*/";
+ printsig_nested (o);
+}
+
+void kprobe_derived_probe::join_group (systemtap_session& s)
+{
+
+ if (! s.kprobe_derived_probes)
+ s.kprobe_derived_probes = new kprobe_derived_probe_group ();
+ s.kprobe_derived_probes->enroll (this);
+}
+void kprobe_derived_probe_group::enroll (kprobe_derived_probe* p)
+{
+ probes_by_module.insert (make_pair (p->symbol_name, p));
+ // probes of same symbol should share single kprobe/kretprobe
+}
+
+void
+kprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes_by_module.empty()) return;
+
+ s.op->newline() << "/* ---- kprobe-based probes ---- */";
+
+ // Warn of misconfigured kernels
+ s.op->newline() << "#if ! defined(CONFIG_KPROBES)";
+ s.op->newline() << "#error \"Need CONFIG_KPROBES!\"";
+ s.op->newline() << "#endif";
+ s.op->newline();
+
+ // Forward declare the master entry functions
+ s.op->newline() << "static int enter_kprobe_probe (struct kprobe *inst,";
+ s.op->line() << " struct pt_regs *regs);";
+ s.op->newline() << "static int enter_kretprobe_probe (struct kretprobe_instance *inst,";
+ s.op->line() << " struct pt_regs *regs);";
+
+ // Emit an array of kprobe/kretprobe pointers
+ s.op->newline() << "#if defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline() << "static void * stap_unreg_kprobes[" << probes_by_module.size() << "];";
+ s.op->newline() << "#endif";
+
+ // Emit the actual probe list.
+
+ s.op->newline() << "static struct stap_dwarfless_kprobe {";
+ s.op->newline(1) << "union { struct kprobe kp; struct kretprobe krp; } u;";
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "struct kprobe dummy;";
+ s.op->newline() << "#endif";
+ s.op->newline(-1) << "} stap_dwarfless_kprobes[" << probes_by_module.size() << "];";
+ // NB: bss!
+
+ s.op->newline() << "static struct stap_dwarfless_probe {";
+ s.op->newline(1) << "const unsigned return_p:1;";
+ s.op->newline() << "const unsigned maxactive_p:1;";
+ s.op->newline() << "const unsigned statement_p:1;";
+ s.op->newline() << "unsigned registered_p:1;";
+ s.op->newline() << "const unsigned short maxactive_val;";
+
+ // Function Names are mostly small and uniform enough to justify putting
+ // char[MAX]'s into the array instead of relocated char*'s.
+
+ size_t pp_name_max = 0, symbol_string_name_max = 0;
+ size_t pp_name_tot = 0, symbol_string_name_tot = 0;
+ for (p_b_m_iterator it = probes_by_module.begin(); it != probes_by_module.end(); it++)
+ {
+ kprobe_derived_probe* p = it->second;
+#define DOIT(var,expr) do { \
+ size_t var##_size = (expr) + 1; \
+ var##_max = max (var##_max, var##_size); \
+ var##_tot += var##_size; } while (0)
+ DOIT(pp_name, lex_cast_qstring(*p->sole_location()).size());
+ DOIT(symbol_string_name, p->symbol_name.size());
+#undef DOIT
+ }
+
+#define CALCIT(var) \
+ s.op->newline() << "const char " << #var << "[" << var##_name_max << "] ;";
+
+ CALCIT(pp);
+ CALCIT(symbol_string);
+#undef CALCIT
+
+ s.op->newline() << "const unsigned long address;";
+ s.op->newline() << "void (* const ph) (struct context*);";
+ s.op->newline(-1) << "} stap_dwarfless_probes[] = {";
+ s.op->indent(1);
+
+ for (p_b_m_iterator it = probes_by_module.begin(); it != probes_by_module.end(); it++)
+ {
+ kprobe_derived_probe* p = it->second;
+ s.op->newline() << "{";
+ if (p->has_return)
+ s.op->line() << " .return_p=1,";
+
+ if (p->has_maxactive)
+ {
+ s.op->line() << " .maxactive_p=1,";
+ assert (p->maxactive_val >= 0 && p->maxactive_val <= USHRT_MAX);
+ s.op->line() << " .maxactive_val=" << p->maxactive_val << ",";
+ }
+ if (p->has_statement)
+ {
+ s.op->line() << " .statement_p=1,";
+ s.op->line() << " .address=(unsigned long)0x" << hex << p->addr << dec << "ULL,";
+ s.op->line() << " .symbol_string=\"" << "\",";
+ }
+ else
+ {
+ s.op->line() << " .address=(unsigned long)0x" << hex << 0 << dec << "ULL,";
+ s.op->line() << " .symbol_string=\"" << p->symbol_name << "\",";
+ }
+
+ s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ",";
+ s.op->line() << " .ph=&" << p->name;
+ s.op->line() << " },";
+ }
+
+ s.op->newline(-1) << "};";
+
+ // Emit the kprobes callback function
+ s.op->newline();
+ s.op->newline() << "static int enter_kprobe_probe (struct kprobe *inst,";
+ s.op->line() << " struct pt_regs *regs) {";
+ // NB: as of PR5673, the kprobe|kretprobe union struct is in BSS
+ s.op->newline(1) << "int kprobe_idx = ((uintptr_t)inst-(uintptr_t)stap_dwarfless_kprobes)/sizeof(struct stap_dwarfless_kprobe);";
+ // Check that the index is plausible
+ s.op->newline() << "struct stap_dwarfless_probe *sdp = &stap_dwarfless_probes[";
+ s.op->line() << "((kprobe_idx >= 0 && kprobe_idx < " << probes_by_module.size() << ")?";
+ s.op->line() << "kprobe_idx:0)"; // NB: at least we avoid memory corruption
+ // XXX: it would be nice to give a more verbose error though; BUG_ON later?
+ s.op->line() << "];";
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp");
+ s.op->newline() << "c->regs = regs;";
+ s.op->newline() << "(*sdp->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline() << "return 0;";
+ s.op->newline(-1) << "}";
+
+ // Same for kretprobes
+ s.op->newline();
+ s.op->newline() << "static int enter_kretprobe_probe (struct kretprobe_instance *inst,";
+ s.op->line() << " struct pt_regs *regs) {";
+ s.op->newline(1) << "struct kretprobe *krp = inst->rp;";
+
+ // NB: as of PR5673, the kprobe|kretprobe union struct is in BSS
+ s.op->newline() << "int kprobe_idx = ((uintptr_t)krp-(uintptr_t)stap_dwarfless_kprobes)/sizeof(struct stap_dwarfless_kprobe);";
+ // Check that the index is plausible
+ s.op->newline() << "struct stap_dwarfless_probe *sdp = &stap_dwarfless_probes[";
+ s.op->line() << "((kprobe_idx >= 0 && kprobe_idx < " << probes_by_module.size() << ")?";
+ s.op->line() << "kprobe_idx:0)"; // NB: at least we avoid memory corruption
+ // XXX: it would be nice to give a more verbose error though; BUG_ON later?
+ s.op->line() << "];";
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp");
+ s.op->newline() << "c->regs = regs;";
+ s.op->newline() << "c->pi = inst;"; // for assisting runtime's backtrace logic
+ s.op->newline() << "(*sdp->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline() << "return 0;";
+ s.op->newline(-1) << "}";
+}
+
+
+void
+kprobe_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+#define CHECK_STMT(var) \
+ s.op->newline() << "if (sdp->statement_p) {"; \
+ s.op->newline() << var << ".symbol_name = NULL;"; \
+ s.op->newline() << "} else {"; \
+ s.op->newline() << var << ".symbol_name = sdp->symbol_string;"; \
+ s.op->newline() << "}";
+
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline() << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "unsigned long relocated_addr = sdp->address;";
+ 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;";
+ CHECK_STMT("kp->u.krp.kp");
+ s.op->newline() << "if (sdp->maxactive_p) {";
+ s.op->newline(1) << "kp->u.krp.maxactive = sdp->maxactive_val;";
+ s.op->newline(-1) << "} else {";
+ s.op->newline(1) << "kp->u.krp.maxactive = max(10, 4*NR_CPUS);";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "kp->u.krp.handler = &enter_kretprobe_probe;";
+ // to ensure safeness of bspcache, always use aggr_kprobe on ia64
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "kp->dummy.pre_handler = NULL;";
+ s.op->newline() << "kp->dummy.addr = kp->u.krp.kp.addr;";
+ CHECK_STMT("kp->dummy");
+ s.op->newline() << "rc = register_kprobe (& kp->dummy);";
+ s.op->newline() << "if (rc == 0) {";
+ s.op->newline(1) << "rc = register_kretprobe (& kp->u.krp);";
+ s.op->newline() << "if (rc != 0)";
+ s.op->newline(1) << "unregister_kprobe (& kp->dummy);";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "#else";
+ s.op->newline() << "rc = register_kretprobe (& kp->u.krp);";
+ s.op->newline() << "#endif";
+ s.op->newline(-1) << "} else {";
+ // to ensure safeness of bspcache, always use aggr_kprobe on ia64
+ s.op->newline(1) << "kp->u.kp.addr = (void *) relocated_addr;";
+ CHECK_STMT("kp->u.kp");
+ s.op->newline(1) << "kp->u.kp.pre_handler = &enter_kprobe_probe;";
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "kp->dummy.addr = kp->u.kp.addr;";
+ s.op->newline() << "kp->dummy.pre_handler = NULL;";
+ CHECK_STMT("kp->dummy");
+ s.op->newline() << "rc = register_kprobe (& kp->dummy);";
+ s.op->newline() << "if (rc == 0) {";
+ s.op->newline(1) << "rc = register_kprobe (& kp->u.kp);";
+ s.op->newline() << "if (rc != 0)";
+ s.op->newline(1) << "unregister_kprobe (& kp->dummy);";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "#else";
+ s.op->newline() << "rc = register_kprobe (& kp->u.kp);";
+ s.op->newline() << "#endif";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "if (rc) {"; // PR6749: tolerate a failed register_*probe.
+ s.op->newline(1) << "sdp->registered_p = 0;";
+ s.op->newline() << "if (rc == -EINVAL)";
+ s.op->newline() << "{";
+ s.op->newline() << " _stp_error (\"Error registering kprobe,possibly an incorrect name %s OR addr = %p, rc = %d \", sdp->symbol_string, sdp->address, rc);";
+ s.op->newline() << " atomic_set (&session_state, STAP_SESSION_ERROR);";
+ s.op->newline() << " goto out;";
+ s.op->newline() << "}";
+ s.op->newline() << "else";
+ s.op->newline() << "_stp_warn (\"probe %s for %s registration error (rc %d)\", probe_point, sdp->pp, rc);";
+ s.op->newline() << "rc = 0;"; // continue with other probes
+ // XXX: shall we increment numskipped?
+ s.op->newline(-1) << "}";
+
+ s.op->newline() << "else sdp->registered_p = 1;";
+ s.op->newline(-1) << "}"; // for loop
+#undef CHECK_STMT
+}
+
+void
+kprobe_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ //Unregister kprobes by batch interfaces.
+ s.op->newline() << "#if defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline() << "j = 0;";
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "if (!sdp->return_p)";
+ s.op->newline(1) << "stap_unreg_kprobes[j++] = &kp->u.kp;";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "unregister_kprobes((struct kprobe **)stap_unreg_kprobes, j);";
+ s.op->newline() << "j = 0;";
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "if (sdp->return_p)";
+ s.op->newline(1) << "stap_unreg_kprobes[j++] = &kp->u.krp;";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "unregister_kretprobes((struct kretprobe **)stap_unreg_kprobes, j);";
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "j = 0;";
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "stap_unreg_kprobes[j++] = &kp->dummy;";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "unregister_kprobes((struct kprobe **)stap_unreg_kprobes, j);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "#endif";
+
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "if (sdp->return_p) {";
+ s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline(1) << "unregister_kretprobe (&kp->u.krp);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "atomic_add (kp->u.krp.nmissed, & skipped_count);";
+ s.op->newline() << "#ifdef STP_TIMING";
+ s.op->newline() << "if (kp->u.krp.nmissed)";
+ s.op->newline(1) << "_stp_warn (\"Skipped due to missed kretprobe/1 on '%s': %d\\n\", sdp->pp, kp->u.krp.nmissed);";
+ s.op->newline(-1) << "#endif";
+ s.op->newline() << "atomic_add (kp->u.krp.kp.nmissed, & skipped_count);";
+ s.op->newline() << "#ifdef STP_TIMING";
+ s.op->newline() << "if (kp->u.krp.kp.nmissed)";
+ s.op->newline(1) << "_stp_warn (\"Skipped due to missed kretprobe/2 on '%s': %d\\n\", sdp->pp, kp->u.krp.kp.nmissed);";
+ s.op->newline(-1) << "#endif";
+ s.op->newline(-1) << "} else {";
+ s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline(1) << "unregister_kprobe (&kp->u.kp);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "atomic_add (kp->u.kp.nmissed, & skipped_count);";
+ s.op->newline() << "#ifdef STP_TIMING";
+ s.op->newline() << "if (kp->u.kp.nmissed)";
+ s.op->newline(1) << "_stp_warn (\"Skipped due to missed kprobe on '%s': %d\\n\", sdp->pp, kp->u.kp.nmissed);";
+ s.op->newline(-1) << "#endif";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES) && defined(__ia64__)";
+ s.op->newline() << "unregister_kprobe (&kp->dummy);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "sdp->registered_p = 0;";
+ s.op->newline(-1) << "}";
+}
+
+struct kprobe_builder: public derived_probe_builder
+{
+ kprobe_builder() {}
+ virtual void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results);
+};
+
+
+void
+kprobe_builder::build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results)
+{
+ string function_string_val, module_string_val;
+ int64_t statement_num_val = 0;
+ bool has_function_str, has_module_str, has_statement_num, has_absolute, has_return;
+
+ has_function_str = this->get_param(parameters, TOK_FUNCTION, function_string_val);
+ has_module_str = this->get_param(parameters, TOK_MODULE, module_string_val);
+ has_return = this->has_null_param (parameters, TOK_RETURN);
+ has_statement_num = this->get_param(parameters, TOK_STATEMENT, statement_num_val);
+ has_absolute = this->has_null_param (parameters, TOK_ABSOLUTE);
+
+ if ( has_function_str )
+ {
+ if ( has_module_str )
+ function_string_val = module_string_val + ":" + function_string_val;
+ finished_results.push_back ( new kprobe_derived_probe ( base,
+ location, function_string_val,
+ 0, has_return,
+ has_statement_num) );
+ }
+ else
+ {
+ // assert guru mode for absolute probes
+ if ( has_statement_num && has_absolute && !base->privileged )
+ throw semantic_error ("absolute statement probe in unprivileged script", base->tok);
+
+ finished_results.push_back(new kprobe_derived_probe ( base,
+ location,"",
+ statement_num_val, has_return,
+ has_statement_num));
+ }
+}
// ------------------------------------------------------------------------
// timer derived probes
// ------------------------------------------------------------------------
@@ -10977,6 +11746,10 @@ perfmon_derived_probe_group::emit_module_init (translator_output* o)
}
#endif
+// ------------------------------------------------------------------------
+// kprobes-based probes,which postpone symbol resolution until runtime.
+// ------------------------------------------------------------------------
+
// ------------------------------------------------------------------------
// Standard tapset registry.
@@ -11075,6 +11848,18 @@ register_standard_tapsets(systemtap_session & s)
s.pattern_root->bind(TOK_PROCFS)->bind(TOK_WRITE)->bind(new procfs_builder());
s.pattern_root->bind_str(TOK_PROCFS)->bind(TOK_WRITE)
->bind(new procfs_builder());
+
+ // Kprobe based probe
+ s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_FUNCTION)
+ ->bind(new kprobe_builder());
+ s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_MODULE)
+ ->bind_str(TOK_FUNCTION)->bind(new kprobe_builder());
+ s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_FUNCTION)->bind(TOK_RETURN)
+ ->bind(new kprobe_builder());
+ s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_MODULE)
+ ->bind_str(TOK_FUNCTION)->bind(TOK_RETURN)->bind(new kprobe_builder());
+ s.pattern_root->bind(TOK_KPROBE)->bind_num(TOK_STATEMENT)
+ ->bind(TOK_ABSOLUTE)->bind(new kprobe_builder());
}
@@ -11097,6 +11882,7 @@ all_session_groups(systemtap_session& s)
DOONE(profile);
DOONE(mark);
DOONE(tracepoint);
+ DOONE(kprobe);
DOONE(hrtimer);
DOONE(perfmon);
DOONE(procfs);