* Re: [patch 5/5] [kprobes] Tweak to the function return probe design [not found] ` <20050613190323.672988000@tuna.jf.intel.com> @ 2005-06-13 19:48 ` Ananth N Mavinakayanahalli 0 siblings, 0 replies; 2+ messages in thread From: Ananth N Mavinakayanahalli @ 2005-06-13 19:48 UTC (permalink / raw) To: rusty.lynch Cc: akpm, systemtap, linux-ia64, linux-kernel, Hien Nguyen, Prasanna S Panchamukhi, Andi Kleen, linuxppc64-dev rusty.lynch@intel.com wrote: Hi Rusty, Thanks for doing this. However... > + > + orig_ret_address = (unsigned long)ri->ret_addr; > + recycle_rp_inst(ri); > + > + if (orig_ret_address != (unsigned long) &kretprobe_trampoline) > + /* > + * This is the real return address. Any other > + * instances associated with this task are for > + * other calls deeper on the call stack > + */ > + break; > + } > + > + BUG_ON(!orig_ret_address); > + regs->nip = orig_ret_address; > + > + unlock_kprobes(); > + preempt_enable_no_resched(); ^^^^^^^ We don't need this here - on ppc64, we do a preempt_disable/enable in kprobe_exceptions_notify() and so this will cause a spurious preempt_enable(). Thanks, Ananth ^ permalink raw reply [flat|nested] 2+ messages in thread
* [patch 0/5] [kprobes] Tweak to the function return probe design
@ 2005-06-13 20:51 rusty.lynch
2005-06-13 20:51 ` [patch 5/5] " rusty.lynch
0 siblings, 1 reply; 2+ messages in thread
From: rusty.lynch @ 2005-06-13 20:51 UTC (permalink / raw)
To: linux-ia64, linux-kernel, linuxppc64-dev
(The following is a resend from this morning. The various kernel mailing list
did not seem to get my email, so I am resending the patch series from another
machine.)
From my experiences with adding return probes to x86_64 and ia64, and the
feedback on LKML to those patches, I think we can simplify the design
for return probes.
The following patch tweaks the original design such that:
* Instead of storing the stack address in the return probe instance, the
task pointer is stored. This gives us all we need in order to:
- find the correct return probe instance when we enter the trampoline
(even if we are recursing)
- find all left-over return probe instances when the task is going away
This has the side effect of simplifying the implementation since more
work can be done in kernel/kprobes.c since architecture specific knowledge
of the stack layout is no longer required. Specifically, we no longer have:
- arch_get_kprobe_task()
- arch_kprobe_flush_task()
- get_rp_inst_tsk()
- get_rp_inst()
- trampoline_post_handler() <see next bullet>
* Instead of splitting the return probe handling and cleanup logic across
the pre and post trampoline handlers, all the work is pushed into the
pre function (trampoline_probe_handler), and then we skip single stepping
the original function. In this case the original instruction to be single
stepped was just a NOP, and we can do without the extra interruption.
The new flow of events to having a return probe handler execute when a target
function exits is:
* At system initialization time, a kprobe is inserted at the beginning of
kretprobe_trampoline. kernel/kprobes.c use to handle this on it's own,
but ia64 needed to do this a little differently (i.e. a function pointer
is really a pointer to a structure containing the instruction pointer and
a global pointer), so I added the notion of arch_init(), so that
kernel/kprobes.c:init_kprobes() now allows architecture specific
initialization by calling arch_init() before exiting. Each architecture
now registers a kprobe on it's own trampoline function.
* register_kretprobe() will insert a kprobe at the beginning of the targeted
function with the kprobe pre_handler set to arch_prepare_kretprobe
(still no change)
* When the target function is entered, the kprobe is fired, calling
arch_prepare_kretprobe (still no change)
* In arch_prepare_kretprobe() we try to get a free instance and if one is
available then we fill out the instance with a pointer to the return probe,
the original return address, and a pointer to the task structure (instead
of the stack address.) Just like before we change the return address
to the trampoline function and mark the instance as used.
If multiple return probes are registered for a given target function,
then arch_prepare_kretprobe() will get called multiple times for the same
task (since our kprobe implementation is able to handle multiple kprobes
at the same address.) Past the first call to arch_prepare_kretprobe,
we end up with the original address stored in the return probe instance
pointing to our trampoline function. (This is a significant difference
from the original arch_prepare_kretprobe design.)
* Target function executes like normal and then returns to kretprobe_trampoline.
* kprobe inserted on the first instruction of kretprobe_trampoline is fired
and calls trampoline_probe_handler() (no change here)
* trampoline_probe_handler() consumes each of the instances associated with
the current task by calling the registered handler function and marking
the instance as unused until an instance is found that has a return address
different then the trampoline function.
(change similar to my previous ia64 RFC)
* If the task is killed with some left-over return probe instances (meaning
that a target function was entered, but never returned), then we just
free any instances associated with the task. (Not much different other
then we can handle this without calling architecture specific functions.)
There is a known problem that this patch does not yet solve where
registering a return probe flush_old_exec or flush_thread will put us
in a bad state. Most likely the best way to handle this is to not allow
registering return probes on these two functions.
(Significant change)
This patch series applies to the 2.6.12-rc6-mm1 kernel, and provides:
* kernel/kprobes.c changes
* i386 patch of existing return probes implementation
* x86_64 patch of existing return probe implementation
* ia64 implementation
* ppc64 implementation (provided by Ananth)
--rusty
^ permalink raw reply [flat|nested] 2+ messages in thread* [patch 5/5] [kprobes] Tweak to the function return probe design 2005-06-13 20:51 [patch 0/5] " rusty.lynch @ 2005-06-13 20:51 ` rusty.lynch 0 siblings, 0 replies; 2+ messages in thread From: rusty.lynch @ 2005-06-13 20:51 UTC (permalink / raw) To: linux-ia64, linux-kernel, linuxppc64-dev [-- Attachment #1: kprobes-return-probes-redux-ppc64.patch --] [-- Type: text/plain, Size: 5952 bytes --] (The following is a resend from this morning. The various kernel mailing list did not seem to get my email, so I am resending the patch series from another machine. I also included a fix to the problem that Ananth pointed out.) The following is a patch provided by Ananth Mavinakayanahalli that implements the new PPC64 specific parts of the new function return probe design. NOTE: Since getting Ananth's patch, I changed trampoline_probe_handler() to consume each of the outstanding return probem instances (feedback on my original RFC after Ananth cut a patch), and also added the arch_init() function (adding arch specific initialization.) I have cross compiled but have not testing this on a PPC64 machine. --rusty arch/ppc64/kernel/kprobes.c | 98 ++++++++++++++++++++++++++++++++++++++++++++ arch/ppc64/kernel/process.c | 4 + include/asm-ppc64/kprobes.h | 3 + 3 files changed, 105 insertions(+) Index: linux-2.6.12-rc6-mm1/arch/ppc64/kernel/kprobes.c =================================================================== --- linux-2.6.12-rc6-mm1.orig/arch/ppc64/kernel/kprobes.c +++ linux-2.6.12-rc6-mm1/arch/ppc64/kernel/kprobes.c @@ -100,6 +100,23 @@ static inline void restore_previous_kpro kprobe_saved_msr = kprobe_saved_msr_prev; } +void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs) +{ + struct kretprobe_instance *ri; + + if ((ri = get_free_rp_inst(rp)) != NULL) { + ri->rp = rp; + ri->task = current; + ri->ret_addr = (kprobe_opcode_t *)regs->link; + + /* Replace the return addr with trampoline addr */ + regs->link = (unsigned long)kretprobe_trampoline; + add_rp_inst(ri); + } else { + rp->nmissed++; + } +} + static inline int kprobe_handler(struct pt_regs *regs) { struct kprobe *p; @@ -190,6 +207,77 @@ no_kprobe: } /* + * Function return probe trampoline: + * - init_kprobes() establishes a probepoint here + * - When the probed function returns, this probe + * causes the handlers to fire + */ +void kretprobe_trampoline_holder(void) +{ + asm volatile(".global kretprobe_trampoline\n" + "kretprobe_trampoline:\n" + "nop\n"); +} + +/* + * Called when the probe at kretprobe trampoline is hit + */ +int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct kretprobe_instance *ri = NULL; + struct hlist_head *head; + struct hlist_node *node, *tmp; + unsigned long orig_ret_address = 0; + + head = kretprobe_inst_table_head(current); + + /* + * It is possible to have multiple instances associated with a given + * task either because an multiple functions in the call path + * have a return probe installed on them, and/or more then one return + * return probe was registered for a target function. + * + * We can handle this because: + * - instances are always inserted at the head of the list + * - when multiple return probes are registered for the same + * function, the first instance's ret_addr will point to the + * real return address, and all the rest will point to + * kretprobe_trampoline + */ + hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { + if (ri->task != current) + /* another task is sharing our hash bucket */ + continue; + + if (ri->rp && ri->rp->handler) + ri->rp->handler(ri, regs); + + orig_ret_address = (unsigned long)ri->ret_addr; + recycle_rp_inst(ri); + + if (orig_ret_address != (unsigned long) &kretprobe_trampoline) + /* + * This is the real return address. Any other + * instances associated with this task are for + * other calls deeper on the call stack + */ + break; + } + + BUG_ON(!orig_ret_address); + regs->nip = orig_ret_address; + + unlock_kprobes(); + + /* + * By returning a non-zero value, we are telling + * kprobe_handler() that we have handled unlocking + * and re-enabling preemption. + */ + return 1; +} + +/* * Called after single-stepping. p->addr is the address of the * instruction whose first byte has been replaced by the "breakpoint" * instruction. To avoid the SMP problems that can occur when we @@ -329,3 +417,13 @@ int longjmp_break_handler(struct kprobe memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs)); return 1; } + +static struct kprobe trampoline_p = { + .addr = (kprobe_opcode_t *) &kretprobe_trampoline, + .pre_handler = trampoline_probe_handler +}; + +int __init arch_init(void) +{ + return register_kprobe(&trampoline_p); +} Index: linux-2.6.12-rc6-mm1/arch/ppc64/kernel/process.c =================================================================== --- linux-2.6.12-rc6-mm1.orig/arch/ppc64/kernel/process.c +++ linux-2.6.12-rc6-mm1/arch/ppc64/kernel/process.c @@ -37,6 +37,7 @@ #include <linux/interrupt.h> #include <linux/utsname.h> #include <linux/perfctr.h> +#include <linux/kprobes.h> #include <asm/pgtable.h> #include <asm/uaccess.h> @@ -310,6 +311,8 @@ void show_regs(struct pt_regs * regs) void exit_thread(void) { + kprobe_flush_task(current); + #ifndef CONFIG_SMP if (last_task_used_math == current) last_task_used_math = NULL; @@ -325,6 +328,7 @@ void flush_thread(void) { struct thread_info *t = current_thread_info(); + kprobe_flush_task(current); if (t->flags & _TIF_ABI_PENDING) t->flags ^= (_TIF_ABI_PENDING | _TIF_32BIT); Index: linux-2.6.12-rc6-mm1/include/asm-ppc64/kprobes.h =================================================================== --- linux-2.6.12-rc6-mm1.orig/include/asm-ppc64/kprobes.h +++ linux-2.6.12-rc6-mm1/include/asm-ppc64/kprobes.h @@ -42,6 +42,9 @@ typedef unsigned int kprobe_opcode_t; #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)((func_descr_t *)pentry) +#define ARCH_SUPPORTS_KRETPROBES +void kretprobe_trampoline(void); + /* Architecture specific copy of original instruction */ struct arch_specific_insn { /* copy of original instruction */ -- ^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2005-06-13 21:00 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20050613190207.954385000@tuna.jf.intel.com>
[not found] ` <20050613190323.672988000@tuna.jf.intel.com>
2005-06-13 19:48 ` [patch 5/5] [kprobes] Tweak to the function return probe design Ananth N Mavinakayanahalli
2005-06-13 20:51 [patch 0/5] " rusty.lynch
2005-06-13 20:51 ` [patch 5/5] " rusty.lynch
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox