diff options
-rw-r--r-- | tapset/kretprobe.stp | 56 | ||||
-rw-r--r-- | tapsets.cxx | 187 | ||||
-rw-r--r-- | testsuite/systemtap.base/kretprobe-vars.exp | 4 | ||||
-rw-r--r-- | testsuite/systemtap.base/kretprobe-vars.stp | 70 | ||||
-rw-r--r-- | translate.cxx | 1 |
5 files changed, 291 insertions, 27 deletions
diff --git a/tapset/kretprobe.stp b/tapset/kretprobe.stp new file mode 100644 index 00000000..3742708f --- /dev/null +++ b/tapset/kretprobe.stp @@ -0,0 +1,56 @@ +// kretprobe data tapset +// Copyright (C) 2009 Red Hat Inc. +// +// This file is part of systemtap, and is free software. You can +// redistribute it and/or modify it under the terms of the GNU General +// Public License (GPL); either version 2, or (at your option) any +// later version. + + +// This is an internally-used tapset for sharing data between kretprobe +// entry / return handlers. See the function +// dwarf_var_expanding_visitor::gen_kretprobe_saved_return +// for details. +// +// Note, invalid calls are silently ignored... + +%{ +static void * +_kretprobe_data(struct kretprobe_instance *pi, size_t offset, size_t length) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) + size_t end = offset + length; + if (end > offset && pi && end <= pi->rp->data_size) + return &pi->data[offset]; +#endif + return NULL; +} +%} + +function _get_kretprobe_long:long(i:long) %{ /* pure */ + size_t offset = THIS->i * sizeof(int64_t); + const int64_t *data = _kretprobe_data(CONTEXT->pi, offset, sizeof(int64_t)); + THIS->__retvalue = data ? *data : 0; +%} + +function _set_kretprobe_long(i:long, value:long) %{ /* impure */ + size_t offset = THIS->i * sizeof(int64_t); + int64_t *data = _kretprobe_data(CONTEXT->pi, offset, sizeof(int64_t)); + if (data) + *data = THIS->value; +%} + +function _get_kretprobe_string:string(i:long) %{ /* pure */ + size_t offset = CONTEXT->pi_longs * sizeof(int64_t) + + THIS->i * MAXSTRINGLEN; + const char *data = _kretprobe_data(CONTEXT->pi, offset, MAXSTRINGLEN); + strlcpy(THIS->__retvalue, data ?: "", MAXSTRINGLEN); +%} + +function _set_kretprobe_string(i:long, value:string) %{ /* impure */ + size_t offset = CONTEXT->pi_longs * sizeof(int64_t) + + THIS->i * MAXSTRINGLEN; + char *data = _kretprobe_data(CONTEXT->pi, offset, MAXSTRINGLEN); + if (data) + strlcpy(data, THIS->value, MAXSTRINGLEN); +%} 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;"; diff --git a/testsuite/systemtap.base/kretprobe-vars.exp b/testsuite/systemtap.base/kretprobe-vars.exp new file mode 100644 index 00000000..5955b64f --- /dev/null +++ b/testsuite/systemtap.base/kretprobe-vars.exp @@ -0,0 +1,4 @@ +# Check that implicitly-saved $target variables have the same +# value as those manually saved on entry. +set test "kretprobe-vars" +stap_run $srcdir/$subdir/$test.stp no_load $all_pass_string -c "cat /dev/null" diff --git a/testsuite/systemtap.base/kretprobe-vars.stp b/testsuite/systemtap.base/kretprobe-vars.stp new file mode 100644 index 00000000..b932e202 --- /dev/null +++ b/testsuite/systemtap.base/kretprobe-vars.stp @@ -0,0 +1,70 @@ +# Check that implicitly-saved $target variables have the same +# value as those manually saved on entry. + + +# saved individual parameters +global read_file +global read_file_mode +global read_buf +global read_count +global read_pos + +# the saved string $$parms +global read_parms + +# error count +global errors + +probe begin +{ + println("systemtap starting probe") +} + +function check_num(name, ent, ret) +{ + if (ent != ret) { + printf("%s mismatch, entry:%#x vs. return:%#x\n", name, ent, ret) + errors++ + } +} +function check_str(name, ent, ret) +{ + if (ent != ret) { + printf("%s mismatch, entry:'%s' vs. return:'%s'\n", name, ent, ret) + errors++ + } +} + +probe kernel.function("vfs_read").call +{ + if (tid() != target()) + next + + read_file = $file + read_file_mode = $file->f_mode + read_buf = $buf + read_count = $count + read_pos = $pos + + read_parms = $$parms +} + +probe kernel.function("vfs_read").return +{ + if (tid() != target()) + next + + println("systemtap ending probe") + + check_num("file", read_file, $file) + check_num("file->f_mode", read_file_mode, $file->f_mode) + check_num("buf", read_buf, $buf) + check_num("count", read_count, $count) + check_num("pos", read_pos, $pos) + + check_str("parms", read_parms, $$parms) + + if (!errors) + println("systemtap test success") + exit() +} diff --git a/translate.cxx b/translate.cxx index ed415abb..cbb8f1e3 100644 --- a/translate.cxx +++ b/translate.cxx @@ -892,6 +892,7 @@ c_unparser::emit_common_header () o->newline() << "unsigned long *unwaddr;"; // unwaddr is caching unwound address in each probe handler on ia64. o->newline() << "struct kretprobe_instance *pi;"; + o->newline() << "int pi_longs;"; // int64_t count in pi->data, the rest is string_t o->newline() << "int regparm;"; o->newline() << "va_list *mark_va_list;"; o->newline() << "const char * marker_name;"; |