summaryrefslogtreecommitdiffstats
path: root/tapsets.cxx
diff options
context:
space:
mode:
authorJosh Stone <jistone@redhat.com>2009-11-10 18:37:02 -0800
committerJosh Stone <jistone@redhat.com>2009-11-10 18:48:34 -0800
commitaf234c407dbab3e62994863272a63b612b0c8c63 (patch)
tree7b2fb7929bbc776a3eae96ef311f890e319a0bf2 /tapsets.cxx
parentb513cd75dac185ac258dc8260a01891c30be6251 (diff)
downloadsystemtap-steved-af234c407dbab3e62994863272a63b612b0c8c63.tar.gz
systemtap-steved-af234c407dbab3e62994863272a63b612b0c8c63.tar.xz
systemtap-steved-af234c407dbab3e62994863272a63b612b0c8c63.zip
PR5916: Exploit kretprobe data storage area
Since 2.6.25, kretprobes can carry a data packet to be filled in an entry_handler. This patch lets us store our implicitly-saved $target variables in .return probes in that data area. * tapset/kretprobe.stp: New get/set functions for kretprobe data. * translate.cxx (c_unparser::emit_common_header): Add context->pi_longs. * tapsets.cxx (dwarf_var_expanding_visitor::visit_target_symbol_saved_return): Switch between the old and new methods of saving $vars in .return probes. (dwarf_var_expanding_visitor::gen_mapped_saved_return): The old way. (dwarf_var_expanding_visitor::gen_kretprobe_saved_return): The new way. (dwarf_derived_probe::join_group): Don't register paired entry-handlers. (dwarf_derived_probe::dwarf_derived_probe): Remember saved-var details. (dwarf_derived_probe_group::emit_module_decls): Output saved-var details. Also split the kretprobe handler whether we're on entry or return. (dwarf_derived_probe_group::emit_module_init): Prepare the entry handler. * testsuite/systemtap.base/kretprobe-vars.stp: Test implicit $var saving.
Diffstat (limited to 'tapsets.cxx')
-rw-r--r--tapsets.cxx187
1 files changed, 160 insertions, 27 deletions
diff --git a/tapsets.cxx b/tapsets.cxx
index 21eeabbe..c972de5f 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -129,6 +129,7 @@ common_probe_entryfn_prologue (translator_output* o, string statestr,
o->newline() << "c->probe_point = " << new_pp << ";";
// reset unwound address cache
o->newline() << "c->pi = 0;";
+ o->newline() << "c->pi_longs = 0;";
o->newline() << "c->regparm = 0;";
o->newline() << "c->marker_name = NULL;";
o->newline() << "c->marker_format = NULL;";
@@ -353,6 +354,9 @@ struct dwarf_derived_probe: public derived_probe
long maxactive_val;
bool access_vars;
+ unsigned saved_longs, saved_strings;
+ dwarf_derived_probe* entry_handler;
+
void printsig (std::ostream &o) const;
virtual void join_group (systemtap_session& s);
void emit_probe_local_init(translator_output * o);
@@ -376,7 +380,8 @@ protected:
Dwarf_Addr addr,
bool has_return):
derived_probe(base, location), addr(addr), has_return(has_return),
- has_maxactive(0), maxactive_val(0), access_vars(false)
+ has_maxactive(0), maxactive_val(0), access_vars(false),
+ saved_longs(0), saved_strings(0), entry_handler(0)
{}
private:
@@ -1814,12 +1819,16 @@ struct dwarf_var_expanding_visitor: public var_expanding_visitor
Dwarf_Addr addr;
block *add_block;
block *add_call_probe; // synthesized from .return probes with saved $vars
- map<std::string, symbol *> return_ts_map;
+ unsigned saved_longs, saved_strings; // data saved within kretprobes
+ map<std::string, expression *> return_ts_map;
vector<Dwarf_Die> scopes;
bool visited;
dwarf_var_expanding_visitor(dwarf_query & q, Dwarf_Die *sd, Dwarf_Addr a):
- q(q), scope_die(sd), addr(a), add_block(NULL), add_call_probe(NULL), visited(false) {}
+ q(q), scope_die(sd), addr(a), add_block(NULL), add_call_probe(NULL),
+ saved_longs(0), saved_strings(0), visited(false) {}
+ expression* gen_mapped_saved_return(target_symbol* e);
+ expression* gen_kretprobe_saved_return(target_symbol* e);
void visit_target_symbol_saved_return (target_symbol* e);
void visit_target_symbol_context (target_symbol* e);
void visit_target_symbol (target_symbol* e);
@@ -1893,13 +1902,33 @@ dwarf_var_expanding_visitor::visit_target_symbol_saved_return (target_symbol* e)
// 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);
+ map<string, expression *>::iterator i = return_ts_map.find(ts_name);
if (i != return_ts_map.end())
{
provide (i->second);
return;
}
+ expression *exp;
+ if (!q.has_process &&
+ strverscmp(q.sess.kernel_base_release.c_str(), "2.6.25") >= 0)
+ exp = gen_kretprobe_saved_return(e);
+ else
+ exp = gen_mapped_saved_return(e);
+
+ // Provide the variable to our parent so it can be used as a
+ // substitute for the target symbol.
+ provide (exp);
+
+ // 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] = exp;
+}
+
+expression*
+dwarf_var_expanding_visitor::gen_mapped_saved_return(target_symbol* e)
+{
// We've got to do several things here to handle target
// variables in return probes.
@@ -2128,12 +2157,75 @@ dwarf_var_expanding_visitor::visit_target_symbol_saved_return (target_symbol* e)
// (4) Provide the '_dwarf_tvar_{name}_{num}_tmp' variable to
// our parent so it can be used as a substitute for the target
// symbol.
- provide (tmpsym);
+ return 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;
+
+expression*
+dwarf_var_expanding_visitor::gen_kretprobe_saved_return(target_symbol* e)
+{
+ // The code for this is simple.
+ //
+ // .call:
+ // _set_kretprobe_long(index, $value)
+ //
+ // .return:
+ // _get_kretprobe_long(index)
+ //
+ // (or s/long/string/ for things like $$parms)
+
+ unsigned index;
+ string setfn, getfn;
+
+ // Cheesy way to predetermine that this is a string -- if the second
+ // character is a '$', then we're looking at a $$vars, $$parms, or $$locals.
+ // XXX We need real type resolution here, especially if we are ever to
+ // support an @entry construct.
+ if (e->base_name[1] == '$')
+ {
+ index = saved_strings++;
+ setfn = "_set_kretprobe_string";
+ getfn = "_get_kretprobe_string";
+ }
+ else
+ {
+ index = saved_longs++;
+ setfn = "_set_kretprobe_long";
+ getfn = "_get_kretprobe_long";
+ }
+
+ // Create the entry code
+ // _set_kretprobe_{long|string}(index, $value)
+
+ if (add_call_probe == NULL)
+ {
+ add_call_probe = new block;
+ add_call_probe->tok = e->tok;
+ }
+
+ functioncall* set_fc = new functioncall;
+ set_fc->tok = e->tok;
+ set_fc->function = setfn;
+ set_fc->args.push_back(new literal_number(index));
+ set_fc->args.back()->tok = e->tok;
+ set_fc->args.push_back(e);
+
+ expr_statement* set_es = new expr_statement;
+ set_es->tok = e->tok;
+ set_es->value = set_fc;
+
+ add_call_probe->statements.push_back(set_es);
+
+ // Create the return code
+ // _get_kretprobe_{long|string}(index)
+
+ functioncall* get_fc = new functioncall;
+ get_fc->tok = e->tok;
+ get_fc->function = getfn;
+ get_fc->args.push_back(new literal_number(index));
+ get_fc->args.back()->tok = e->tok;
+
+ return get_fc;
}
@@ -2720,6 +2812,10 @@ dwarf_derived_probe::printsig (ostream& o) const
void
dwarf_derived_probe::join_group (systemtap_session& s)
{
+ // skip probes which are paired entry-handlers
+ if (!has_return && (saved_longs || saved_strings))
+ return;
+
if (! s.dwarf_derived_probes)
s.dwarf_derived_probes = new dwarf_derived_probe_group ();
s.dwarf_derived_probes->enroll (this);
@@ -2747,7 +2843,9 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname,
has_return (q.has_return),
has_maxactive (q.has_maxactive),
maxactive_val (q.maxactive_val),
- access_vars(false)
+ access_vars(false),
+ saved_longs(0), saved_strings(0),
+ entry_handler(0)
{
if (q.has_process)
{
@@ -2804,21 +2902,20 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname,
q.base_probe->body = v.add_call_probe;
q.has_return = false;
q.has_call = true;
-
+
if (q.has_process)
- {
- uprobe_derived_probe *synthetic = new uprobe_derived_probe (funcname, filename, line,
- module, section, dwfl_addr,
- addr, q, scope_die);
- q.results.push_back (synthetic);
- }
+ entry_handler = new uprobe_derived_probe (funcname, filename, line,
+ module, section, dwfl_addr,
+ addr, q, scope_die);
else
- {
- dwarf_derived_probe *synthetic = new dwarf_derived_probe (funcname, filename, line,
- module, section, dwfl_addr,
- addr, q, scope_die);
- q.results.push_back (synthetic);
- }
+ entry_handler = new dwarf_derived_probe (funcname, filename, line,
+ module, section, dwfl_addr,
+ addr, q, scope_die);
+
+ saved_longs = entry_handler->saved_longs = v.saved_longs;
+ saved_strings = entry_handler->saved_strings = v.saved_strings;
+
+ q.results.push_back (entry_handler);
q.has_return = true;
q.has_call = false;
@@ -3119,6 +3216,10 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->newline() << "unsigned registered_p:1;";
s.op->newline() << "const unsigned short maxactive_val;";
+ // data saved in the kretprobe_instance packet
+ s.op->newline() << "const unsigned short saved_longs;";
+ s.op->newline() << "const unsigned short saved_strings;";
+
// Let's find some stats for the three embedded strings. Maybe they
// are small and uniform enough to justify putting char[MAX]'s into
// the array instead of relocated char*'s.
@@ -3161,6 +3262,7 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->newline() << "const unsigned long address;";
s.op->newline() << "void (* const ph) (struct context*);";
+ s.op->newline() << "void (* const entry_ph) (struct context*);";
s.op->newline(-1) << "} stap_dwarf_probes[] = {";
s.op->indent(1);
@@ -3176,6 +3278,15 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
assert (p->maxactive_val >= 0 && p->maxactive_val <= USHRT_MAX);
s.op->line() << " .maxactive_val=" << p->maxactive_val << ",";
}
+ if (p->saved_longs || p->saved_strings)
+ {
+ if (p->saved_longs)
+ s.op->line() << " .saved_longs=" << p->saved_longs << ",";
+ if (p->saved_strings)
+ s.op->line() << " .saved_strings=" << p->saved_strings << ",";
+ if (p->entry_handler)
+ s.op->line() << " .entry_ph=&" << p->entry_handler->name << ",";
+ }
if (p->locations[0]->optional)
s.op->line() << " .optional_p=1,";
s.op->line() << " .address=(unsigned long)0x" << hex << p->addr << dec << "ULL,";
@@ -3220,8 +3331,8 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
// 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() << "static int enter_kretprobe_common (struct kretprobe_instance *inst,";
+ s.op->line() << " struct pt_regs *regs, int entry) {";
s.op->newline(1) << "struct kretprobe *krp = inst->rp;";
// NB: as of PR5673, the kprobe|kretprobe union struct is in BSS
@@ -3235,7 +3346,10 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
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
+
+ // for assisting runtime's backtrace logic and accessing kretprobe data packets
+ s.op->newline() << "c->pi = inst;";
+ s.op->newline() << "c->pi_longs = sdp->saved_longs;";
// Make it look like the IP is set as it wouldn't have been replaced
// by a breakpoint instruction when calling real probe handler. Reset
@@ -3244,13 +3358,25 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->indent(1);
s.op->newline() << "unsigned long kprobes_ip = REG_IP(c->regs);";
s.op->newline() << "SET_REG_IP(regs, (unsigned long) inst->rp->kp.addr);";
- s.op->newline() << "(*sdp->ph) (c);";
+ s.op->newline() << "(entry ? sdp->entry_ph : sdp->ph) (c);";
s.op->newline() << "SET_REG_IP(regs, kprobes_ip);";
s.op->newline(-1) << "}";
common_probe_entryfn_epilogue (s.op);
s.op->newline() << "return 0;";
s.op->newline(-1) << "}";
+
+ 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) << "return enter_kretprobe_common(inst, regs, 0);";
+ s.op->newline(-1) << "}";
+
+ s.op->newline();
+ s.op->newline() << "static int enter_kretprobe_entry_probe (struct kretprobe_instance *inst,";
+ s.op->line() << " struct pt_regs *regs) {";
+ s.op->newline(1) << "return enter_kretprobe_common(inst, regs, 1);";
+ s.op->newline(-1) << "}";
}
@@ -3271,6 +3397,13 @@ dwarf_derived_probe_group::emit_module_init (systemtap_session& s)
s.op->newline(1) << "kp->u.krp.maxactive = KRETACTIVE;";
s.op->newline(-1) << "}";
s.op->newline() << "kp->u.krp.handler = &enter_kretprobe_probe;";
+ s.op->newline() << "#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)";
+ s.op->newline() << "if (sdp->entry_ph) {";
+ s.op->newline(1) << "kp->u.krp.entry_handler = &enter_kretprobe_entry_probe;";
+ s.op->newline() << "kp->u.krp.data_size = sdp->saved_longs * sizeof(int64_t) + ";
+ s.op->newline() << " sdp->saved_strings * MAXSTRINGLEN;";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "#endif";
// to ensure safeness of bspcache, always use aggr_kprobe on ia64
s.op->newline() << "#ifdef __ia64__";
s.op->newline() << "kp->dummy.addr = kp->u.krp.kp.addr;";