From 5e562a69a5432566c6ae78344ae51b80ced7f15b Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 16 Dec 2009 12:00:55 +0100 Subject: 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 --- runtime/runtime.h | 11 +++++++++++ runtime/stack.c | 11 ++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'runtime') 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 #include +/* If uprobes isn't in the kernel, pull it in from the runtime. */ +#if defined(CONFIG_UPROBES) || defined(CONFIG_UPROBES_MODULE) +#include +#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; } -- cgit From 2e7f844220b9419b2f05234b07c56bcdedf7afb2 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 16 Dec 2009 18:00:34 +0100 Subject: function to translate from the uretprobe trampoline back to the original return address * runtime/uprobes2/uprobes.c (uprobe_get_pc): new function --- runtime/uprobes2/uprobes.c | 38 ++++++++++++++++++++++++++++++++++++++ runtime/uprobes2/uprobes.h | 8 ++++++++ 2 files changed, 46 insertions(+) (limited to 'runtime') diff --git a/runtime/uprobes2/uprobes.c b/runtime/uprobes2/uprobes.c index bf454752..4c3a9c9c 100644 --- a/runtime/uprobes2/uprobes.c +++ b/runtime/uprobes2/uprobes.c @@ -2810,6 +2810,44 @@ static void uretprobe_set_trampoline(struct uprobe_process *uproc, } } +unsigned long uprobe_get_pc(struct uretprobe_instance *ri, unsigned long pc, + unsigned long sp) +{ + struct uretprobe *rp; + struct uprobe_kimg *uk; + struct uprobe_process *uproc; + unsigned long trampoline_addr; + struct hlist_node *r; + struct uretprobe_instance *ret_inst; + + if (!ri) + return 0; + rp = ri->rp; + uk = (struct uprobe_kimg *)rp->u.kdata; + if (!uk) + return 0; + uproc = uk->ppt->uproc; + if (IS_ERR(uproc->uretprobe_trampoline_addr)) + return pc; + trampoline_addr = (unsigned long)uproc->uretprobe_trampoline_addr; + if (pc != trampoline_addr) + return pc; + r = &ri->hlist; + hlist_for_each_entry_from(ret_inst, r, hlist) { + if (ret_inst->ret_addr == trampoline_addr) + continue; + /* First handler with a stack pointer lower than the + address (or equal) must be the one. */ + if (ret_inst->sp == sp || compare_stack_ptrs(ret_inst->sp, sp)) + return ret_inst->ret_addr; + } + printk(KERN_ERR "Original return address for trampoline not found at " + "0x%lx pid/tgid=%d/%d\n", sp, current->pid, current->tgid); + return 0; +} + +EXPORT_SYMBOL_GPL(uprobe_get_pc); + #else /* ! CONFIG_URETPROBES */ static void uretprobe_handle_entry(struct uprobe *u, struct pt_regs *regs, diff --git a/runtime/uprobes2/uprobes.h b/runtime/uprobes2/uprobes.h index ae0692f0..5d2a826e 100644 --- a/runtime/uprobes2/uprobes.h +++ b/runtime/uprobes2/uprobes.h @@ -88,6 +88,14 @@ extern void unregister_uretprobe(struct uretprobe *rp); /* For PRs 9940, 6852... */ extern void unmap_uprobe(struct uprobe *u); extern void unmap_uretprobe(struct uretprobe *rp); +/* + * Given a program counter, translate it back to the original address + * if it is the address of the trampoline. sp is the stack pointer for + * the frame that corresponds to the address. + */ +extern unsigned long uprobe_get_pc(struct uretprobe_instance *ri, + unsigned long pc, + unsigned long sp); #ifdef UPROBES_IMPLEMENTATION -- cgit From 4c5ce7a55108edb5203b3d69949f09c2284f1963 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 16 Dec 2009 18:04:34 +0100 Subject: backtrace through uprobes trampoline. Only works in uretprobes for the moment. * runtime/stack-x86_64.c (__stp_stack_print): Rewrite trampoline PC addresses if necessary. * runtime/stack-i386.c (__stp_stack_print): ditto * runtime/stack-arm.c (__stp_stack_print): Add extra argument * runtime/stack-ppc.c (__stp_stack_print): ditto * runtime/stack-s390.c (__stp_stack_print): ditto * runtime/stack.c (_stap_stack_print): call __stp_stack_print with uretprobe_instance. * testsuite/systemtap.context/uprobe_backtrace.stp: new test --- runtime/stack-arm.c | 2 +- runtime/stack-i386.c | 11 ++++++++++- runtime/stack-ppc.c | 2 +- runtime/stack-s390.c | 3 ++- runtime/stack-x86_64.c | 12 +++++++++++- runtime/stack.c | 2 +- 6 files changed, 26 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/stack-arm.c b/runtime/stack-arm.c index fcff0a3b..2760eadd 100644 --- a/runtime/stack-arm.c +++ b/runtime/stack-arm.c @@ -32,7 +32,7 @@ static int __init find_str_pc_offset(void) static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels, - struct task_struct *tsk) + struct task_struct *tsk, struct uretprobe_instance *ri) { #ifdef STP_USE_FRAME_POINTER int pc_offset = find_str_pc_offset(); diff --git a/runtime/stack-i386.c b/runtime/stack-i386.c index b447e495..fef11871 100644 --- a/runtime/stack-i386.c +++ b/runtime/stack-i386.c @@ -31,7 +31,7 @@ static void _stp_stack_print_fallback(unsigned long stack, int verbose, int leve #endif static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels, - struct task_struct *tsk) + struct task_struct *tsk, struct uretprobe_instance *ri) { unsigned long context = (unsigned long)®_SP(regs) & ~(THREAD_SIZE - 1); @@ -63,6 +63,15 @@ static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels, while (levels && (tsk || !arch_unw_user_mode(&info))) { int ret = unwind(&info, tsk); + unsigned long maybe_pc = 0; + if (ri) { + maybe_pc = uprobe_get_pc(ri, UNW_PC(&info), + UNW_SP(&info)); + if (!maybe_pc) + printk("SYSTEMTAP ERROR: uprobe_get_return returned 0\n"); + else + UNW_PC(&info) = maybe_pc; + } dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(&info), UNW_SP(&info)); if (ret == 0) { _stp_func_print(UNW_PC(&info), verbose, 1, tsk); diff --git a/runtime/stack-ppc.c b/runtime/stack-ppc.c index df2db15d..9670d06f 100644 --- a/runtime/stack-ppc.c +++ b/runtime/stack-ppc.c @@ -8,7 +8,7 @@ */ static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels, - struct task_struct *tsk) + struct task_struct *tsk, struct uretprobe_instance *ri) { unsigned long ip, newsp, lr = 0; int count = 0; diff --git a/runtime/stack-s390.c b/runtime/stack-s390.c index 14e9b7d8..7a53f794 100644 --- a/runtime/stack-s390.c +++ b/runtime/stack-s390.c @@ -67,7 +67,8 @@ __stp_show_stack (unsigned long sp, unsigned long low, static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels, - struct task_struct *tsk) + struct task_struct *tsk, + struct uretprobe_instance *ri) { unsigned long *_sp = (unsigned long *)®_SP(regs); unsigned long sp = (unsigned long)_sp; diff --git a/runtime/stack-x86_64.c b/runtime/stack-x86_64.c index 914242e0..3fc203f7 100644 --- a/runtime/stack-x86_64.c +++ b/runtime/stack-x86_64.c @@ -28,15 +28,25 @@ static void _stp_stack_print_fallback(unsigned long stack, int verbose, int leve static void __stp_stack_print(struct pt_regs *regs, int verbose, int levels, - struct task_struct *tsk) + struct task_struct *tsk, struct uretprobe_instance *ri) { #ifdef STP_USE_DWARF_UNWINDER + int start_levels = levels; // FIXME: large stack allocation struct unwind_frame_info info; arch_unw_init_frame_info(&info, regs); while (levels && (tsk || !arch_unw_user_mode(&info))) { int ret = unwind(&info, tsk); + unsigned long maybe_pc = 0; + if (ri) { + maybe_pc = uprobe_get_pc(ri, UNW_PC(&info), + UNW_SP(&info)); + if (!maybe_pc) + printk("SYSTEMTAP ERROR: uprobe_get_return returned 0\n"); + else + UNW_PC(&info) = maybe_pc; + } dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(&info), UNW_SP(&info)); if (ret == 0) { _stp_func_print(UNW_PC(&info), verbose, 1, tsk); diff --git a/runtime/stack.c b/runtime/stack.c index 9c23d530..27abee7e 100644 --- a/runtime/stack.c +++ b/runtime/stack.c @@ -134,7 +134,7 @@ static void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe else _stp_printf("%p ", (int64_t) REG_IP(regs)); - __stp_stack_print(regs, verbose, levels, tsk); + __stp_stack_print(regs, verbose, levels, tsk, ri); } /** Writes stack backtrace to a string -- cgit From d5a2bd44d30b45b6829eb27d70ffb6ceaa70c5bd Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 17 Dec 2009 16:18:34 +0100 Subject: support for a brief backtrace format This only prints symbol+offset, or an address if the symbol isn't known. * runtime/runtime.h (SYM_VERBOSE_NO, SYM_VERBOSE_FULL, SYM_VERBOSE_BRIEF): new constants * runtime/stack.c (_stp_stack_print): support brief format * runtime/sym.c (_stp_func_print): ditto * tapset/ucontext-unwind.stp (print_ubacktrace_brief): new function * testsuite/systemtap.context/fib.c: new test program * testsuite/systemtap.context/fib.stp: new test * testsuite/systemtap.context/fib.exp: new test --- runtime/runtime.h | 10 ++++++++++ runtime/stack-i386.c | 2 ++ runtime/stack-x86_64.c | 2 ++ runtime/stack.c | 22 ++++++++++++++-------- runtime/sym.c | 24 ++++++++++++++++++------ 5 files changed, 46 insertions(+), 14 deletions(-) (limited to 'runtime') diff --git a/runtime/runtime.h b/runtime/runtime.h index a7ee962c..0fd2a380 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -126,6 +126,16 @@ static struct #endif #endif +#ifndef SYM_VERBOSE_NO +#define SYM_VERBOSE_NO 0 +#endif +#ifndef SYM_VERBOSE_FULL +#define SYM_VERBOSE_FULL 1 +#endif +#ifndef SYM_VERBOSE_BRIEF +#define SYM_VERBOSE_BRIEF 2 +#endif + #include "alloc.c" #include "print.c" #include "string.c" diff --git a/runtime/stack-i386.c b/runtime/stack-i386.c index fef11871..4bd3cc53 100644 --- a/runtime/stack-i386.c +++ b/runtime/stack-i386.c @@ -63,6 +63,7 @@ static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels, while (levels && (tsk || !arch_unw_user_mode(&info))) { int ret = unwind(&info, tsk); +#if UPROBES_API_VERSION > 1 unsigned long maybe_pc = 0; if (ri) { maybe_pc = uprobe_get_pc(ri, UNW_PC(&info), @@ -72,6 +73,7 @@ static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels, else UNW_PC(&info) = maybe_pc; } +#endif dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(&info), UNW_SP(&info)); if (ret == 0) { _stp_func_print(UNW_PC(&info), verbose, 1, tsk); diff --git a/runtime/stack-x86_64.c b/runtime/stack-x86_64.c index 3fc203f7..80ebd3e7 100644 --- a/runtime/stack-x86_64.c +++ b/runtime/stack-x86_64.c @@ -38,6 +38,7 @@ static void __stp_stack_print(struct pt_regs *regs, int verbose, int levels, while (levels && (tsk || !arch_unw_user_mode(&info))) { int ret = unwind(&info, tsk); +#if UPROBES_API_VERSION > 1 unsigned long maybe_pc = 0; if (ri) { maybe_pc = uprobe_get_pc(ri, UNW_PC(&info), @@ -47,6 +48,7 @@ static void __stp_stack_print(struct pt_regs *regs, int verbose, int levels, else UNW_PC(&info) = maybe_pc; } +#endif dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(&info), UNW_SP(&info)); if (ret == 0) { _stp_func_print(UNW_PC(&info), verbose, 1, tsk); diff --git a/runtime/stack.c b/runtime/stack.c index 27abee7e..3d907a7f 100644 --- a/runtime/stack.c +++ b/runtime/stack.c @@ -112,15 +112,20 @@ static void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe if (verbose) { /* print the current address */ if (pi) { - _stp_print("Returning from: "); - _stp_symbol_print((unsigned long)_stp_probe_addr_r(pi)); - _stp_print("\nReturning to : "); + if (verbose == SYM_VERBOSE_FULL) { + _stp_print("Returning from: "); + _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); + if (verbose == SYM_VERBOSE_FULL) { + _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_func_print(ri->ret_addr, verbose, 0, tsk); } else { _stp_print_char(' '); if (tsk) @@ -128,7 +133,8 @@ static void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe else _stp_symbol_print(REG_IP(regs)); } - _stp_print_char('\n'); + if (verbose != SYM_VERBOSE_BRIEF) + _stp_print_char('\n'); } else if (pi) _stp_printf("%p %p ", (int64_t)(long)_stp_ret_addr_r(pi), (int64_t) REG_IP(regs)); else diff --git a/runtime/sym.c b/runtime/sym.c index 953161bc..cd0c8a71 100644 --- a/runtime/sym.c +++ b/runtime/sym.c @@ -374,19 +374,31 @@ static int _stp_func_print(unsigned long address, int verbose, int exact, else exstr = " (inexact)"; - name = _stp_kallsyms_lookup(address, &size, &offset, &modname, NULL, task); + name = _stp_kallsyms_lookup(address, &size, &offset, &modname, NULL, + task); if (name) { - if (verbose) { + switch (verbose) { + case SYM_VERBOSE_FULL: if (modname && *modname) _stp_printf(" %p : %s+%#lx/%#lx [%s]%s\n", - (int64_t) address, name, offset, size, modname, exstr); + (int64_t) address, name, offset, + size, modname, exstr); else - _stp_printf(" %p : %s+%#lx/%#lx%s\n", (int64_t) address, name, offset, size, exstr); - } else + _stp_printf(" %p : %s+%#lx/%#lx%s\n", + (int64_t) address, name, offset, size, + exstr); + break; + case SYM_VERBOSE_BRIEF: + _stp_printf("%s+%#lx\n", name, offset); + break; + case SYM_VERBOSE_NO: + default: _stp_printf("%p ", (int64_t) address); + } return 1; - } + } else if (verbose == SYM_VERBOSE_BRIEF) + _stp_printf("%p\n", (int64_t) address); return 0; } -- cgit