summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Moore <timoore@redhat.com>2009-12-16 12:00:55 +0100
committerTim Moore <timoore@redhat.com>2009-12-16 15:03:22 +0100
commit5e562a69a5432566c6ae78344ae51b80ced7f15b (patch)
tree3c8101132cad09ef61b0ccc89d1d0be0dc324fd8
parent39a3e39706a18dbf3698b52fc1cc7532d94078e8 (diff)
downloadsystemtap-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.h11
-rw-r--r--runtime/stack.c11
-rw-r--r--tapset/context-unwind.stp4
-rw-r--r--tapset/ucontext-unwind.stp4
-rw-r--r--tapsets.cxx23
-rw-r--r--testsuite/systemtap.context/uprobe_uaddr.exp58
-rw-r--r--testsuite/systemtap.context/uprobe_uaddr.stp24
-rw-r--r--translate.cxx1
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