This is the mail archive of the systemtap@sources.redhat.com mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC][PATCH] PPC64: return address probes


Hi,

Here is the return address probes prototype for ppc64. This patch
applies on 2.6.12-rc4-mm2 + Rusty's arch_break_inst.patch and Hien's
kprobe_flush.patch.

The stack pointer and return address are disjoint on ppc64. Hence I had
to introduce struct arch_retaddr (instead of having a #ifdef in
include/linux/kprobes.h).

This patch is tested on Power3 uni, Power4 smp. Compile tested for x86,
x86_64 and sparc64.

Comments welcome!

Ananth
Signed-off-by: Ananth N Mavinakayanahalli <amavin@redhat.com>

 arch/ppc64/kernel/kprobes.c   |   95 +++++++++++++++++++++++++++++++++++++++---
 arch/ppc64/kernel/process.c   |    7 +++
 include/asm-i386/kprobes.h    |    3 +
 include/asm-ppc64/kprobes.h   |    8 +++
 include/asm-sparc64/kprobes.h |    3 +
 include/asm-x86_64/kprobes.h  |    3 +
 include/linux/kprobes.h       |    4 -
 7 files changed, 116 insertions(+), 7 deletions(-)

Index: linux-2.6.12-rc4/arch/ppc64/kernel/kprobes.c
===================================================================
--- linux-2.6.12-rc4.orig/arch/ppc64/kernel/kprobes.c	2005-05-19 08:34:43.000000000 -0400
+++ linux-2.6.12-rc4/arch/ppc64/kernel/kprobes.c	2005-05-19 18:04:16.000000000 -0400
@@ -88,6 +88,45 @@ static inline void prepare_singlestep(st
 		regs->nip = (unsigned long)&p->ainsn.insn;
 }
 
+struct task_struct *arch_get_kprobe_task(void *ptr)
+{
+	return ((struct thread_info *)(((unsigned long)ptr) &
+				(~(1 << THREAD_SHIFT) - 1)))->task;
+}
+
+void arch_kprobe_flush_task(struct task_struct *tk)
+{
+	struct kretprobe_instance *ri;
+
+	while ((ri = get_rp_inst_tsk(tk)) != NULL) {
+		*(ri->arch_retaddr.linkreg_ptr) = (unsigned long)ri->ret_addr;
+		recycle_rp_inst(ri);
+	}
+}
+
+void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs)
+{
+	struct kretprobe_instance *ri;
+	static void *orig_ret_addr;
+
+	/* Save the return address when this hits the first time */
+	if (regs->link != (unsigned long)kretprobe_trampoline)
+		orig_ret_addr = (void *)regs->link;
+
+	if ((ri = get_free_rp_inst(rp)) != NULL) {
+		ri->rp = rp;
+		ri->stack_addr = (void *)regs->gpr[1];
+		ri->ret_addr = orig_ret_addr;
+		ri->arch_retaddr.linkreg_ptr =
+			(kprobe_opcode_t *)&regs->link;
+		add_rp_inst(ri);
+		/* Replace original return address with trampoline address */
+		regs->link = (unsigned long)kretprobe_trampoline;
+	} else {
+		rp->nmissed++;
+	}
+}
+
 static inline int kprobe_handler(struct pt_regs *regs)
 {
 	struct kprobe *p;
@@ -168,6 +207,51 @@ no_kprobe:
 }
 
 /*
+ * Function return probe trampoline:
+ * 	- init_kprobes() establishes a probepoint here
+ * 	- When the probed function returns, this probe
+ * 		causes the trampoline 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 task_struct *tsk;
+	struct kretprobe_instance *ri;
+	struct hlist_head *head;
+	struct hlist_node *node;
+
+	tsk = arch_get_kprobe_task((void *)regs->gpr[1]);
+	head = kretprobe_inst_table_head(tsk);
+	hlist_for_each_entry(ri, node, head, hlist) {
+		if ((ri->stack_addr == (void *)regs->gpr[1]) && ri->rp) {
+			if (ri->rp->handler)
+				ri->rp->handler(ri, regs);
+		}
+	}
+	return 0;
+}
+
+void trampoline_post_handler(struct kprobe *p, struct pt_regs *regs,
+						unsigned long flags)
+{
+	struct kretprobe_instance *ri;
+
+	while ((ri = get_rp_inst((void *)regs->gpr[1]))) {
+		regs->nip = (unsigned long)ri->ret_addr;
+		recycle_rp_inst(ri);
+	}
+}
+
+/*
  * 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
@@ -183,8 +267,6 @@ static void resume_execution(struct kpro
 	ret = emulate_step(regs, p->ainsn.insn[0]);
 	if (ret == 0)
 		regs->nip = (unsigned long)p->addr + 4;
-
-	regs->msr &= ~MSR_SE;
 }
 
 static inline int post_kprobe_handler(struct pt_regs *regs)
@@ -195,9 +277,10 @@ static inline int post_kprobe_handler(st
 	if (current_kprobe->post_handler)
 		current_kprobe->post_handler(current_kprobe, regs, 0);
 
-	resume_execution(current_kprobe, regs);
-	regs->msr |= kprobe_saved_msr;
+	if (current_kprobe->post_handler != trampoline_post_handler)
+		resume_execution(current_kprobe, regs);
 
+	regs->msr |= kprobe_saved_msr;
 	unlock_kprobes();
 	preempt_enable_no_resched();
 
@@ -212,7 +295,7 @@ static inline int post_kprobe_handler(st
 	return 1;
 }
 
-/* Interrupts disabled, kprobe_lock held. */
+/* kprobe_lock held. */
 static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
 {
 	if (current_kprobe->fault_handler
@@ -221,6 +304,7 @@ static inline int kprobe_fault_handler(s
 
 	if (kprobe_status & KPROBE_HIT_SS) {
 		resume_execution(current_kprobe, regs);
+		regs->msr &= ~MSR_SE;
 		regs->msr |= kprobe_saved_msr;
 
 		unlock_kprobes();
Index: linux-2.6.12-rc4/arch/ppc64/kernel/process.c
===================================================================
--- linux-2.6.12-rc4.orig/arch/ppc64/kernel/process.c	2005-05-19 08:34:23.000000000 -0400
+++ linux-2.6.12-rc4/arch/ppc64/kernel/process.c	2005-05-19 08:42:03.000000000 -0400
@@ -310,6 +310,10 @@ void show_regs(struct pt_regs * regs)
 
 void exit_thread(void)
 {
+	struct task_struct *tsk = current;
+
+	kprobe_flush_task(tsk);
+
 #ifndef CONFIG_SMP
 	if (last_task_used_math == current)
 		last_task_used_math = NULL;
@@ -324,6 +328,9 @@ void exit_thread(void)
 void flush_thread(void)
 {
 	struct thread_info *t = current_thread_info();
+	struct task_struct *tsk = current;
+
+	kprobe_flush_task(tsk);
 
 	if (t->flags & _TIF_ABI_PENDING)
 		t->flags ^= (_TIF_ABI_PENDING | _TIF_32BIT);
Index: linux-2.6.12-rc4/include/asm-i386/kprobes.h
===================================================================
--- linux-2.6.12-rc4.orig/include/asm-i386/kprobes.h	2005-05-19 08:34:36.000000000 -0400
+++ linux-2.6.12-rc4/include/asm-i386/kprobes.h	2005-05-19 08:42:03.000000000 -0400
@@ -49,6 +49,9 @@ struct arch_specific_insn {
 	kprobe_opcode_t insn[MAX_INSN_SIZE];
 };
 
+struct arch_retaddr {
+};
+
 
 /* trap3/1 are intr gates for kprobes.  So, restore the status of IF,
  * if necessary, before executing the original int3/1 (trap) handler.
Index: linux-2.6.12-rc4/include/asm-ppc64/kprobes.h
===================================================================
--- linux-2.6.12-rc4.orig/include/asm-ppc64/kprobes.h	2005-05-07 01:20:31.000000000 -0400
+++ linux-2.6.12-rc4/include/asm-ppc64/kprobes.h	2005-05-19 08:42:03.000000000 -0400
@@ -41,6 +41,9 @@ typedef unsigned int kprobe_opcode_t;
 #define IS_TWI(instr)		(((instr) & 0xfc000000) == 0x0c000000)
 
 #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 {
@@ -48,6 +51,11 @@ struct arch_specific_insn {
 	kprobe_opcode_t insn[MAX_INSN_SIZE];
 };
 
+struct arch_retaddr {
+	/* return address on ppc64 is in regs->lr not on stack */
+	kprobe_opcode_t *linkreg_ptr;
+};
+
 #ifdef CONFIG_KPROBES
 extern int kprobe_exceptions_notify(struct notifier_block *self,
 				    unsigned long val, void *data);
Index: linux-2.6.12-rc4/include/asm-sparc64/kprobes.h
===================================================================
--- linux-2.6.12-rc4.orig/include/asm-sparc64/kprobes.h	2005-05-07 01:20:31.000000000 -0400
+++ linux-2.6.12-rc4/include/asm-sparc64/kprobes.h	2005-05-19 08:42:03.000000000 -0400
@@ -18,6 +18,9 @@ struct arch_specific_insn {
 	kprobe_opcode_t insn[MAX_INSN_SIZE];
 };
 
+struct arch_retaddr {
+};
+
 #ifdef CONFIG_KPROBES
 extern int kprobe_exceptions_notify(struct notifier_block *self,
 				    unsigned long val, void *data);
Index: linux-2.6.12-rc4/include/asm-x86_64/kprobes.h
===================================================================
--- linux-2.6.12-rc4.orig/include/asm-x86_64/kprobes.h	2005-05-07 01:20:31.000000000 -0400
+++ linux-2.6.12-rc4/include/asm-x86_64/kprobes.h	2005-05-19 08:42:03.000000000 -0400
@@ -45,6 +45,9 @@ struct arch_specific_insn {
 	kprobe_opcode_t *insn;
 };
 
+struct arch_retaddr {
+};
+
 /* trap3/1 are intr gates for kprobes.  So, restore the status of IF,
  * if necessary, before executing the original int3/1 (trap) handler.
  */
Index: linux-2.6.12-rc4/include/linux/kprobes.h
===================================================================
--- linux-2.6.12-rc4.orig/include/linux/kprobes.h	2005-05-19 08:34:46.000000000 -0400
+++ linux-2.6.12-rc4/include/linux/kprobes.h	2005-05-19 08:42:03.000000000 -0400
@@ -118,8 +118,7 @@ static inline void arch_prepare_kretprob
 					struct pt_regs *regs)
 {
 }
-static inline void arch_kprobe_flush_task(struct task_struct *tk,
-						spinlock_t *kp_lock)
+static inline void arch_kprobe_flush_task(struct task_struct *tk)
 {
 }
 #define arch_get_kprobe_task(ptr) ((struct task_struct *)NULL)
@@ -149,6 +148,7 @@ struct kretprobe_instance {
 	struct kretprobe *rp;
 	void *ret_addr;
 	void *stack_addr;
+	struct arch_retaddr arch_retaddr;
 };
 
 #ifdef CONFIG_KPROBES

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]