diff options
author | Tim Moore <timoore@redhat.com> | 2009-12-16 12:00:55 +0100 |
---|---|---|
committer | Tim Moore <timoore@redhat.com> | 2009-12-16 15:03:22 +0100 |
commit | 5e562a69a5432566c6ae78344ae51b80ced7f15b (patch) | |
tree | 3c8101132cad09ef61b0ccc89d1d0be0dc324fd8 | |
parent | 39a3e39706a18dbf3698b52fc1cc7532d94078e8 (diff) | |
download | systemtap-steved-5e562a69a5432566c6ae78344ae51b80ced7f15b.tar.gz systemtap-steved-5e562a69a5432566c6ae78344ae51b80ced7f15b.tar.xz systemtap-steved-5e562a69a5432566c6ae78344ae51b80ced7f15b.zip |
set the IP in return probes to the returned-to instruction
It's easily available in kretprobes and uretprobes and is consistent
with the rest of the program state.
* translate.cxx (emit_common_header) : add uretprobe_instance to context.
* tapsets.cxx (common_probe_entryfn_prologue): Initialize ri in
context to 0.
(dwarf_derived_probe_group::emit_module_decls): Change IP to return
address in kretprobes.
(uprobe_derived_probe_group::emit_module_decls): enter_uretprobe_probe:
set ri (uretprobe_instance) in context. Change IP to return
address in uretprobes. Don't emit uprobe include and #define
* runtime/runtime.h : Add includes and #define for uprobes.
* runtime/stack.c (_stp_stack_print, _stp_stack_snprint): Add extra
argument for uretprobe_instance.
* tapset/context-unwind.stp (print_backtrace, backtrace): Pass NULL
for uretprobe_instance to _stp_stack_print.
* tapset/ucontext-unwind.stp (print_ubacktrace, ubacktrace): pass
uretprobe_instance to _stp_stack_print
* testsuite/systemtap.context/uprobe_uaddr.exp : new test for uaddr in
function probes
* testsuite/systemtap.context/uprobe_uaddr.stp : new file
-rw-r--r-- | runtime/runtime.h | 11 | ||||
-rw-r--r-- | runtime/stack.c | 11 | ||||
-rw-r--r-- | tapset/context-unwind.stp | 4 | ||||
-rw-r--r-- | tapset/ucontext-unwind.stp | 4 | ||||
-rw-r--r-- | tapsets.cxx | 23 | ||||
-rw-r--r-- | testsuite/systemtap.context/uprobe_uaddr.exp | 58 | ||||
-rw-r--r-- | testsuite/systemtap.context/uprobe_uaddr.stp | 24 | ||||
-rw-r--r-- | translate.cxx | 1 |
8 files changed, 117 insertions, 19 deletions
diff --git a/runtime/runtime.h b/runtime/runtime.h index ba583aeb..a7ee962c 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -31,6 +31,17 @@ #include <linux/sched.h> #include <linux/mm.h> +/* If uprobes isn't in the kernel, pull it in from the runtime. */ +#if defined(CONFIG_UPROBES) || defined(CONFIG_UPROBES_MODULE) +#include <linux/uprobes.h> +#else +#include "uprobes/uprobes.h" +#endif +#ifndef UPROBES_API_VERSION +#define UPROBES_API_VERSION 1 +#endif + + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #if !defined (CONFIG_DEBUG_FS) && !defined (CONFIG_DEBUG_FS_MODULE) #error "DebugFS is required and was not found in the kernel." diff --git a/runtime/stack.c b/runtime/stack.c index 25dbdbbd..9c23d530 100644 --- a/runtime/stack.c +++ b/runtime/stack.c @@ -107,7 +107,7 @@ static void _stp_stack_print_fallback(unsigned long stack, int verbose, int leve * @param regs A pointer to the struct pt_regs. */ -static void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe_instance *pi, int levels, struct task_struct *tsk) +static void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe_instance *pi, int levels, struct task_struct *tsk, struct uretprobe_instance *ri) { if (verbose) { /* print the current address */ @@ -116,6 +116,11 @@ static void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe _stp_symbol_print((unsigned long)_stp_probe_addr_r(pi)); _stp_print("\nReturning to : "); _stp_symbol_print((unsigned long)_stp_ret_addr_r(pi)); + } else if (ri) { + _stp_print("Returning from: "); + _stp_usymbol_print(ri->rp->u.vaddr, tsk); + _stp_print("\nReturning to : "); + _stp_usymbol_print(ri->ret_addr, tsk); } else { _stp_print_char(' '); if (tsk) @@ -138,14 +143,14 @@ static void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe * @param regs A pointer to the struct pt_regs. * @returns void */ -static void _stp_stack_snprint(char *str, int size, struct pt_regs *regs, int verbose, struct kretprobe_instance *pi, int levels, struct task_struct *tsk) +static void _stp_stack_snprint(char *str, int size, struct pt_regs *regs, int verbose, struct kretprobe_instance *pi, int levels, struct task_struct *tsk, struct uretprobe_instance *ri) { /* To get a string, we use a simple trick. First flush the print buffer, */ /* then call _stp_stack_print, then copy the result into the output string */ /* and clear the print buffer. */ _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); _stp_print_flush(); - _stp_stack_print(regs, verbose, pi, levels, tsk); + _stp_stack_print(regs, verbose, pi, levels, tsk, ri); strlcpy(str, pb->buf, size < (int)pb->len ? size : (int)pb->len); pb->len = 0; } diff --git a/tapset/context-unwind.stp b/tapset/context-unwind.stp index 741031c0..8f35a783 100644 --- a/tapset/context-unwind.stp +++ b/tapset/context-unwind.stp @@ -28,7 +28,7 @@ */ function print_backtrace () %{ if (CONTEXT->regs) { - _stp_stack_print(CONTEXT->regs, 1, CONTEXT->pi, MAXTRACE, NULL); + _stp_stack_print(CONTEXT->regs, 1, CONTEXT->pi, MAXTRACE, NULL, NULL); } else { _stp_printf("Systemtap probe: %s\n", CONTEXT->probe_point); } @@ -42,7 +42,7 @@ function print_backtrace () %{ */ function backtrace:string () %{ /* pure */ if (CONTEXT->regs) - _stp_stack_snprint (THIS->__retvalue, MAXSTRINGLEN, CONTEXT->regs, 0, CONTEXT->pi, MAXTRACE, NULL); + _stp_stack_snprint (THIS->__retvalue, MAXSTRINGLEN, CONTEXT->regs, 0, CONTEXT->pi, MAXTRACE, NULL, NULL); else strlcpy (THIS->__retvalue, "", MAXSTRINGLEN); %} diff --git a/tapset/ucontext-unwind.stp b/tapset/ucontext-unwind.stp index d699e588..e0d883b8 100644 --- a/tapset/ucontext-unwind.stp +++ b/tapset/ucontext-unwind.stp @@ -28,7 +28,7 @@ function print_ubacktrace () %{ /* unprivileged */ assert_is_myproc(); if (CONTEXT->regs) { _stp_stack_print(CONTEXT->regs, 1, CONTEXT->pi, MAXTRACE, - current); + current, CONTEXT->ri); } else { _stp_printf("Systemtap probe: %s\n", CONTEXT->probe_point); } @@ -47,7 +47,7 @@ function ubacktrace:string () %{ /* pure */ /* unprivileged */ if (CONTEXT->regs) _stp_stack_snprint (THIS->__retvalue, MAXSTRINGLEN, CONTEXT->regs, 0, CONTEXT->pi, MAXTRACE, - current); + current, CONTEXT->ri); else strlcpy (THIS->__retvalue, "", MAXSTRINGLEN); %} diff --git a/tapsets.cxx b/tapsets.cxx index cf6f97ef..5729757b 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -142,6 +142,7 @@ common_probe_entryfn_prologue (translator_output* o, string statestr, o->newline() << "#ifdef STP_TIMING"; o->newline() << "c->statp = 0;"; o->newline() << "#endif"; + o->newline() << "c->ri = 0;"; // 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 @@ -3373,8 +3374,15 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "{"; s.op->indent(1); s.op->newline() << "unsigned long kprobes_ip = REG_IP(c->regs);"; + s.op->newline() << "if (entry) {"; + s.op->indent(1); s.op->newline() << "SET_REG_IP(regs, (unsigned long) inst->rp->kp.addr);"; - s.op->newline() << "(entry ? sdp->entry_ph : sdp->ph) (c);"; + s.op->newline() << "(sdp->entry_ph) (c);"; + s.op->newline(-1) << "} else {"; + s.op->indent(1); + s.op->newline() << "SET_REG_IP(regs, (unsigned long)inst->ret_addr);"; + s.op->newline() << "(sdp->ph) (c);"; + s.op->newline(-1) << "}"; s.op->newline() << "SET_REG_IP(regs, kprobes_ip);"; s.op->newline(-1) << "}"; @@ -4569,16 +4577,6 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) if (probes.empty()) return; s.op->newline() << "/* ---- user probes ---- */"; - // If uprobes isn't in the kernel, pull it in from the runtime. - s.op->newline() << "#if defined(CONFIG_UPROBES) || defined(CONFIG_UPROBES_MODULE)"; - s.op->newline() << "#include <linux/uprobes.h>"; - s.op->newline() << "#else"; - s.op->newline() << "#include \"uprobes/uprobes.h\""; - s.op->newline() << "#endif"; - s.op->newline() << "#ifndef UPROBES_API_VERSION"; - s.op->newline() << "#define UPROBES_API_VERSION 1"; - s.op->newline() << "#endif"; - // We'll probably need at least this many: unsigned minuprobes = probes.size(); // .. but we don't want so many that .bss is inflated (PR10507): @@ -4723,6 +4721,7 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline(1) << "struct stap_uprobe *sup = container_of(inst->rp, struct stap_uprobe, urp);"; s.op->newline() << "const struct stap_uprobe_spec *sups = &stap_uprobe_specs [sup->spec_index];"; common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sups->pp"); + s.op->newline() << "c->ri = inst;"; s.op->newline() << "if (sup->spec_index < 0 ||" << "sup->spec_index >= " << probes.size() << ") return;"; // XXX: should not happen // XXX: kretprobes saves "c->pi = inst;" too @@ -4734,7 +4733,7 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "{"; s.op->indent(1); s.op->newline() << "unsigned long uprobes_ip = REG_IP(c->regs);"; - s.op->newline() << "SET_REG_IP(regs, inst->rp->u.vaddr);"; + s.op->newline() << "SET_REG_IP(regs, inst->ret_addr);"; s.op->newline() << "(*sups->ph) (c);"; s.op->newline() << "SET_REG_IP(regs, uprobes_ip);"; s.op->newline(-1) << "}"; diff --git a/testsuite/systemtap.context/uprobe_uaddr.exp b/testsuite/systemtap.context/uprobe_uaddr.exp new file mode 100644 index 00000000..521dfa57 --- /dev/null +++ b/testsuite/systemtap.context/uprobe_uaddr.exp @@ -0,0 +1,58 @@ +# Tests uaddr in function call and return probes. For a call probe we +# expect the address to be in the function; for a return probe it +# should be in the function's caller. + +set test "uprobe_uaddr" + +# Only run on make installcheck and utrace present. +if {! [installtest_p]} { untested "$test"; return } +if {! [utrace_p]} { untested "$test"; return } + +set testpath "$srcdir/$subdir" +set testsrc "$testpath/uprobe_stmt_num.c" +set testexe "[pwd]/$test" + +# We want debug info and no optimization (every line counts). +set testflags "additional_flags=-g additional_flags=-O0" +set teststp "$testpath/$test.stp" + +set res [target_compile $testsrc $testexe executable $testflags] +if { $res != "" } { + verbose "target_compile failed: $res" 2 + fail "unable to compile $testsrc" + return +} + +set cmd [concat stap -c $testexe $teststp] +send_log "cmd: $cmd\n" +catch {eval exec $cmd} output +send_log "cmd output:\n $output\n" + +set output_lines [split $output "\n"] + +set lines [llength $output_lines] +if { $lines == 6 } { + pass "$test" +} else { + fail "$test ($lines)" +} + +set result_funcs [list "main *" "func *" "func2 *" "func *" "main *"] + +foreach expected $result_funcs output $output_lines { + if {$expected != ""} { + if [string match $expected $output] { + pass "$test" + } else { + fail "$test $output" + } + } else { + break; + } +} + +if [string match "main *" [lindex $output_lines 5]] { + fail "$test return from main" +} else { + pass "$test" +} diff --git a/testsuite/systemtap.context/uprobe_uaddr.stp b/testsuite/systemtap.context/uprobe_uaddr.stp new file mode 100644 index 00000000..1528e559 --- /dev/null +++ b/testsuite/systemtap.context/uprobe_uaddr.stp @@ -0,0 +1,24 @@ +probe process("uprobe_uaddr").function("main").call { + println(usymdata(uaddr())) +} + +probe process("uprobe_uaddr").function("main").return { + println(usymdata(uaddr())) +} + +probe process("uprobe_uaddr").function("func").call { + println(usymdata(uaddr())) +} + +probe process("uprobe_uaddr").function("func").return { + println(usymdata(uaddr())) +} + +probe process("uprobe_uaddr").function("func2").call { + println(usymdata(uaddr())) +} + +probe process("uprobe_uaddr").function("func2").return { + println(usymdata(uaddr())) +} + diff --git a/translate.cxx b/translate.cxx index aca0d868..e6c8cde9 100644 --- a/translate.cxx +++ b/translate.cxx @@ -905,6 +905,7 @@ c_unparser::emit_common_header () o->newline() << "cycles_t cycles_base;"; o->newline() << "cycles_t cycles_sum;"; o->newline() << "#endif"; + o->newline() << "struct uretprobe_instance *ri;"; // PR10516: probe locals |