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] | |
To test sys_kretprobe module with relayfs ----------------------------------------- 1. Build sys_kretprobe and load it cd syscall modify KERNEL_SRC variable in mksys_kretprobe to fit your environment ./mksys_kretprobe mount -t relayfs relayfs /mnt/relay insmod sys_kretprobe.ko
2. Build logkprobe and start it cd logkprobe make ./logkprobe -b 128 -n 512
Thanks for your help, Hien
Attachment:
rprobe-2.6.11.mm4.tar.gz
Description: GNU Zip compressed data
--- linux-2.6.11.mm4/include/linux/kprobes.h 2005-03-01 23:37:50.000000000 -0800
+++ linux-2.6.11.mm4.works/include/linux/kprobes.h 2005-03-21 10:22:59.000000000 -0800
@@ -25,6 +25,9 @@
* Rusty Russell).
* 2004-July Suparna Bhattacharya <suparna@in.ibm.com> added jumper probes
* interface to access function arguments.
+ * 2005-Mar Hien Nguyen <hien@us.ibm.com> and Jim Keniston
+ * <jkenisto@us.ibm.com> Added function-return probes, aka exit
+ * probes.
*/
#include <linux/config.h>
#include <linux/list.h>
@@ -34,12 +37,15 @@
struct kprobe;
struct pt_regs;
+struct retprobe_instance;
typedef int (*kprobe_pre_handler_t) (struct kprobe *, struct pt_regs *);
typedef int (*kprobe_break_handler_t) (struct kprobe *, struct pt_regs *);
typedef void (*kprobe_post_handler_t) (struct kprobe *, struct pt_regs *,
unsigned long flags);
typedef int (*kprobe_fault_handler_t) (struct kprobe *, struct pt_regs *,
int trapnr);
+typedef int (*retprobe_handler_t) (struct retprobe_instance *, struct pt_regs *);
+
struct kprobe {
struct hlist_node hlist;
@@ -65,6 +71,9 @@
/* copy of the original instruction */
struct arch_specific_insn ainsn;
+
+ /* point to retprobe */
+ struct retprobe *rp;
};
/*
@@ -82,6 +91,35 @@
kprobe_opcode_t *entry; /* probe handling code to jump to */
};
+/*
+ * Function-return probe -
+ * Note:
+ * User needs to provide a handler function, and initialize maxactive.
+ * maxactive - The maximum number of instances of the probed function that
+ * can be active concurrently.
+ * nmissed - tracks the number of times the probed function's return was
+ * ignored, due to maxactive being too low.
+ *
+ */
+struct retprobe {
+ retprobe_handler_t handler;
+ int maxactive;
+ int nmissed;
+ int num_ri_running;
+ int unregistering;
+ struct kprobe *kprobe;
+ struct retprobe_instance *instances; /* allocated memory */
+ struct list_head free_instances;
+};
+
+struct retprobe_instance {
+ struct list_head list;
+ struct hlist_node hlist;
+ struct retprobe *rp;
+ void *ret_addr;
+ void *stack_addr;
+};
+
#ifdef CONFIG_KPROBES
/* Locks kprobe: irq must be disabled */
void lock_kprobes(void);
@@ -94,10 +132,14 @@
return kprobe_cpu == smp_processor_id();
}
+asmlinkage void retprobe_trampoline(void) __asm__("retprobe_trampoline");
+
extern int arch_prepare_kprobe(struct kprobe *p);
extern void arch_copy_kprobe(struct kprobe *p);
extern void arch_remove_kprobe(struct kprobe *p);
extern void show_registers(struct pt_regs *regs);
+extern int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs);
+extern struct task_struct *arch_get_kprobe_task(void *ptr);
/* Get the kprobe at this addr (if any). Must have called lock_kprobes */
struct kprobe *get_kprobe(void *addr);
@@ -110,6 +152,15 @@
void unregister_jprobe(struct jprobe *p);
void jprobe_return(void);
+int register_kretprobe(struct kprobe *p, struct retprobe *rp);
+int register_jretprobe(struct jprobe *p, struct retprobe *rp);
+
+struct retprobe_instance *get_free_rp_inst(struct retprobe *rp);
+struct retprobe_instance *get_rp_inst(void *sara);
+void add_retprobe_inst_to_hash(struct retprobe_instance *ri);
+void kprobe_flush_task(struct task_struct *tk);
+void recycle_retprobe_instance(struct retprobe_instance *ri);
+
#else
static inline int kprobe_running(void)
{
@@ -132,5 +183,13 @@
static inline void jprobe_return(void)
{
}
+static inline int register_kretprobe(struct kprobe *p, struct retprobe *rp)
+{
+ return -ENOSYS;
+}
+static inline int register_jretprobe(struct jprobe *p, struct retprobe *rp)
+{
+ return -ENOSYS;
+}
#endif
#endif /* _LINUX_KPROBES_H */
--- linux-2.6.11.mm4/kernel/kprobes.c 2005-03-21 13:44:08.000000000 -0800
+++ linux-2.6.11.mm4.works/kernel/kprobes.c 2005-03-21 10:11:18.000000000 -0800
@@ -27,6 +27,9 @@
* interface to access function arguments.
* 2004-Sep Prasanna S Panchamukhi <prasanna@in.ibm.com> Changed Kprobes
* exceptions notifier to be first on the priority list.
+ * 2005-Mar Hien Nguyen <hien@us.ibm.com> and Jim Keniston
+ * <jkenisto@us.ibm.com> Added function-return probes, aka exit
+ * probes.
*/
#include <linux/kprobes.h>
#include <linux/spinlock.h>
@@ -37,11 +40,19 @@
#include <asm/errno.h>
#include <asm/kdebug.h>
+#ifndef arch_supports_retprobes
+#define arch_supports_retprobes 0
+#endif
+
#define KPROBE_HASH_BITS 6
#define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS)
static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE];
+#define RPROBE_HASH_BITS KPROBE_HASH_BITS
+#define RPROBE_INST_TABLE_SIZE KPROBE_TABLE_SIZE
+static struct hlist_head retprobe_inst_table[RPROBE_INST_TABLE_SIZE];
+
unsigned int kprobe_cpu = NR_CPUS;
static DEFINE_SPINLOCK(kprobe_lock);
@@ -58,6 +69,26 @@
spin_unlock(&kprobe_lock);
}
+struct kprobe trampoline_p = {
+ .addr = (kprobe_opcode_t *) &retprobe_trampoline,
+ .pre_handler = trampoline_probe_handler,
+ .rp = NULL
+};
+
+/*
+ * Called once to register a probe point at the retprobe trampoline.
+ */
+static void init_retprobes(void)
+{
+ int i;
+
+ register_kprobe(&trampoline_p);
+
+ /* Allocate retprobe instances hash table */
+ for (i = 0; i < RPROBE_INST_TABLE_SIZE; i++)
+ INIT_HLIST_HEAD(&retprobe_inst_table[i]);
+}
+
/* You have to be holding the kprobe_lock */
struct kprobe *get_kprobe(void *addr)
{
@@ -73,7 +104,82 @@
return NULL;
}
-int register_kprobe(struct kprobe *p)
+struct retprobe_instance * get_free_rp_inst(struct retprobe *rp)
+{
+ if (list_empty(&rp->free_instances)) {
+ return NULL;
+ }
+ return (struct retprobe_instance *) rp->free_instances.next;
+}
+
+struct retprobe_instance *get_rp_inst(void *sara)
+{
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct task_struct *tsk;
+ struct retprobe_instance *ri;
+
+ tsk = arch_get_kprobe_task(sara);
+ head = &retprobe_inst_table[hash_ptr(tsk, RPROBE_HASH_BITS)];
+ hlist_for_each_entry(ri, node, head, hlist) {
+ if (ri->stack_addr == sara)
+ return ri;
+ }
+ return NULL;
+}
+
+void add_retprobe_inst_to_hash(struct retprobe_instance *ri)
+{
+ struct task_struct *tsk;
+ tsk = arch_get_kprobe_task(ri->stack_addr);
+ hlist_add_head(&ri->hlist, &retprobe_inst_table[hash_ptr(tsk, RPROBE_HASH_BITS)]);
+}
+
+void recycle_retprobe_instance(struct retprobe_instance *ri)
+{
+ ri->rp->num_ri_running--;
+ if (ri->rp->num_ri_running == 0 && ri->rp->unregistering == 1) {
+ /* This is the last running ri during unregister.
+ * Free memory to complete the unregister.
+ */
+ kfree(ri->rp->instances);
+ kfree(ri->rp);
+ } else {
+ /* put ri obj back to free list */
+ list_add(&ri->list, &ri->rp->free_instances);
+ }
+}
+
+/*
+ * This function is called from do_exit or do_execv when task tk's stack is
+ * about to be recycled. Recycle any function-return probe instances
+ * associated with this task. These represent probed functions that have
+ * been called but will never return.
+ */
+void kprobe_flush_task(struct task_struct *tk)
+{
+ unsigned long flags = 0;
+ struct retprobe_instance *ri;
+ struct task_struct *tsk;
+ struct hlist_head *head;
+ struct hlist_node *node;
+
+ if (!arch_supports_retprobes) {
+ return;
+ }
+ spin_lock_irqsave(&kprobe_lock, flags);
+ head = &retprobe_inst_table[hash_ptr(tk, RPROBE_HASH_BITS)];
+ hlist_for_each_entry(ri, node, head, hlist) {
+ tsk = arch_get_kprobe_task(ri->stack_addr);
+ if (tsk == tk) {
+ hlist_del_rcu(&ri->hlist);
+ recycle_retprobe_instance(ri);
+ }
+ }
+ spin_unlock_irqrestore(&kprobe_lock, flags);
+}
+
+int _register_kprobe(struct kprobe *p)
{
int ret = 0;
unsigned long flags = 0;
@@ -104,15 +210,47 @@
return ret;
}
+int register_kprobe(struct kprobe *p)
+{
+ p->rp = NULL;
+ return _register_kprobe(p);
+}
+
void unregister_kprobe(struct kprobe *p)
{
unsigned long flags;
arch_remove_kprobe(p);
spin_lock_irqsave(&kprobe_lock, flags);
+ if (get_kprobe(p->addr) == NULL) {
+ spin_unlock_irqrestore(&kprobe_lock, flags);
+ return;
+ }
*p->addr = p->opcode;
hlist_del(&p->hlist);
flush_icache_range((unsigned long) p->addr,
(unsigned long) p->addr + sizeof(kprobe_opcode_t));
+
+ if (p->rp != NULL) {
+ if (p->rp->num_ri_running != 0) {
+ int i;
+ struct retprobe *rp;
+ struct retprobe_instance *ri;
+ /*
+ * Make a copy of retprobe so we can relinquish
+ * the user's original.
+ */
+ rp = kmalloc(sizeof(struct retprobe), GFP_KERNEL);
+ BUG_ON(rp == NULL);
+ memcpy(rp, p->rp, sizeof(struct retprobe));
+ rp->unregistering = 1;
+ for (i = 0 ; i < p->rp->maxactive; i++) {
+ ri = p->rp->instances + i;
+ ri->rp = rp;
+ }
+ } else {
+ kfree(p->rp->instances);
+ }
+ }
spin_unlock_irqrestore(&kprobe_lock, flags);
}
@@ -135,6 +273,67 @@
unregister_kprobe(&jp->kp);
}
+int register_kretprobe(struct kprobe *p, struct retprobe *rp)
+{
+ int ret = 0;
+ static int retprobe_init_setup = 0;
+ struct retprobe_instance *inst;
+ int maxinst, i;
+
+ if (!arch_supports_retprobes) {
+ return -ENOSYS;
+ }
+ if (retprobe_init_setup == 0) {
+ init_retprobes();
+ retprobe_init_setup = 1;
+ }
+ /* Pre-allocate memory for max retprobe instances */
+ if (rp->maxactive > 0) {
+ maxinst = rp->maxactive;
+ } else {
+#ifdef CONFIG_PREEMPT
+ maxinst = max(10, 2 * NR_CPUS);
+#else
+ maxinst = NR_CPUS;
+#endif
+ }
+ rp->instances = kmalloc(maxinst * sizeof(struct retprobe_instance),
+ GFP_KERNEL);
+ if (rp->instances == NULL) {
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&rp->free_instances);
+ /* Put all retprobe_instance objects on the free list */
+ for (i = 0; i < maxinst; i++) {
+ inst = rp->instances + i;
+ list_add(&inst->list, &rp->free_instances);
+ }
+ rp->num_ri_running = 0;
+ rp->nmissed = 0;
+ rp->unregistering = 0;
+ rp->kprobe = p;
+ p->rp = rp;
+
+ /* Establish function entry probe point */
+ /* todo: we need to deal with probe that has been registered */
+
+ if((ret = _register_kprobe(p)) != 0) {
+ kfree(rp->instances);
+ return ret;
+ }
+ return ret;
+}
+
+int register_jretprobe(struct jprobe *jp, struct retprobe *rp)
+{
+
+ jp->kp.pre_handler = setjmp_pre_handler;
+ jp->kp.break_handler = longjmp_break_handler;
+
+ return register_kretprobe(&jp->kp, rp);
+}
+
static int __init init_kprobes(void)
{
int i, err = 0;
@@ -155,3 +354,5 @@
EXPORT_SYMBOL_GPL(register_jprobe);
EXPORT_SYMBOL_GPL(unregister_jprobe);
EXPORT_SYMBOL_GPL(jprobe_return);
+EXPORT_SYMBOL_GPL(register_kretprobe);
+EXPORT_SYMBOL_GPL(register_jretprobe);
--- linux-2.6.11.mm4/arch/i386/kernel/kprobes.c 2005-03-01 23:38:08.000000000 -0800
+++ linux-2.6.11.mm4.works/arch/i386/kernel/kprobes.c 2005-03-21 10:11:18.000000000 -0800
@@ -23,6 +23,9 @@
* Rusty Russell).
* 2004-July Suparna Bhattacharya <suparna@in.ibm.com> added jumper probes
* interface to access function arguments.
+ * 2005-Mar Hien Nguyen <hien@us.ibm.com> and Jim Keniston
+ * <jkenisto@us.ibm.com> Added function return-probes, aka exit
+ * probes.
*/
#include <linux/config.h>
@@ -60,6 +63,12 @@
return 0;
}
+struct task_struct *arch_get_kprobe_task(void *ptr)
+{
+ return ((struct thread_info *) (((unsigned long) ptr) & (~(THREAD_SIZE -1))))->task;
+}
+
+
int arch_prepare_kprobe(struct kprobe *p)
{
return 0;
@@ -159,6 +168,35 @@
if (is_IF_modifier(p->opcode))
kprobe_saved_eflags &= ~IF_MASK;
+ if (p->rp != NULL) {
+ /*
+ * Get a retprobe instance off the free list and populate it
+ * with the return addr, stack addr, and rp.
+ */
+ struct retprobe_instance *ri;
+ unsigned long *sara = (unsigned long *)®s->esp;
+
+ if ((ri = get_free_rp_inst(p->rp)) != NULL) {
+ INIT_HLIST_NODE(&ri->hlist);
+ ri->rp = p->rp;
+ ri->stack_addr = sara;
+ ri->ret_addr = (void *) *sara;
+ add_retprobe_inst_to_hash(ri);
+ /* Replace the return addr with trampoline addr */
+ *sara = (unsigned long) &retprobe_trampoline;
+ /*
+ * Remove obj in free list -
+ * will add it back when probed function returns
+ */
+ list_del(&ri->list);
+ p->rp->num_ri_running++;
+ } else {
+ p->rp->nmissed++;
+ }
+ /* retprobe allows kprobe handle to be null */
+ if (!p->pre_handler)
+ goto ss_probe;
+ }
if (p->pre_handler(p, regs)) {
/* handler has already set things up, so skip ss setup */
return 1;
@@ -173,6 +211,22 @@
preempt_enable_no_resched();
return ret;
}
+/*
+ * Called when we hit the probe point at retprobe_trampoline
+ */
+int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct retprobe_instance *ri;
+ unsigned long *sara = ((unsigned long *) ®s->esp) - 1;
+
+ if ((ri = get_rp_inst(sara)) == NULL) {
+ return 0;
+ }
+ if (ri->rp && !ri->rp->unregistering) {
+ return ri->rp->handler(ri, regs);
+ }
+ return 0;
+}
/*
* Called after single-stepping. p->addr is the address of the
@@ -226,6 +280,19 @@
case 0xea: /* jmp absolute -- eip is correct */
next_eip = regs->eip;
break;
+ case 0x90: /* nop */
+ /* Check to make sure this is from the trampoline probe */
+ if (orig_eip == (unsigned long) retprobe_trampoline) {
+ struct retprobe_instance *ri;
+ unsigned long *sara = tos - 1; /* RA already popped */
+ ri = get_rp_inst(sara);
+ if (ri != NULL) {
+ next_eip = (unsigned long)ri->ret_addr;
+ hlist_del(&ri->hlist);
+ recycle_retprobe_instance(ri);
+ }
+ }
+ break;
default:
break;
}
--- linux-2.6.11.mm4/arch/ppc64/kernel/kprobes.c 2005-03-21 13:43:36.000000000 -0800
+++ linux-2.6.11.mm4.works/arch/ppc64/kernel/kprobes.c 2005-03-21 10:11:18.000000000 -0800
@@ -53,6 +53,11 @@
return 0;
}
+struct task_struct *arch_get_kprobe_task(void *ptr)
+{
+ return NULL; /* Not implemented */
+}
+
void arch_copy_kprobe(struct kprobe *p)
{
memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
--- linux-2.6.11.mm4/arch/x86_64/kernel/kprobes.c 2005-03-21 13:43:38.000000000 -0800
+++ linux-2.6.11.mm4.works/arch/x86_64/kernel/kprobes.c 2005-03-21 10:11:18.000000000 -0800
@@ -76,6 +76,11 @@
return 0;
}
+struct task_struct *arch_get_kprobe_task(void *ptr)
+{
+ return NULL; /* Not implemented */
+}
+
int arch_prepare_kprobe(struct kprobe *p)
{
/* insn: must be on special executable page on x86_64. */
--- linux-2.6.11.mm4/arch/sparc64/kernel/kprobes.c 2005-03-01 23:38:26.000000000 -0800
+++ linux-2.6.11.mm4.works/arch/sparc64/kernel/kprobes.c 2005-03-21 10:11:18.000000000 -0800
@@ -53,6 +53,11 @@
{
}
+struct task_struct *arch_get_kprobe_task(void *ptr)
+{
+ return NULL; /* Not implemented */
+}
+
/* kprobe_status settings */
#define KPROBE_HIT_ACTIVE 0x00000001
#define KPROBE_HIT_SS 0x00000002
--- linux-2.6.11.mm4/arch/i386/kernel/entry.S 2005-03-21 13:43:34.000000000 -0800
+++ linux-2.6.11.mm4.works/arch/i386/kernel/entry.S 2005-03-21 10:11:18.000000000 -0800
@@ -136,6 +136,17 @@
.previous
+#ifdef CONFIG_KPROBES
+/*
+ * For function-return probes, init_retprobes() establishes a probepoint
+ * here. When a retprobed function returns, this probe is hit and
+ * trampoline_probe_handler() runs, calling the retprobe's handler.
+ */
+ENTRY(retprobe_trampoline)
+ nop
+/* NOT REACHED */
+#endif
+
ENTRY(ret_from_fork)
pushl %eax
call schedule_tail
--- linux-2.6.11.mm4/arch/i386/kernel/process.c 2005-03-21 13:43:34.000000000 -0800
+++ linux-2.6.11.mm4.works/arch/i386/kernel/process.c 2005-03-21 10:17:08.000000000 -0800
@@ -39,6 +39,7 @@
#include <linux/kallsyms.h>
#include <linux/ptrace.h>
#include <linux/random.h>
+#include <linux/kprobes.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
@@ -371,7 +372,13 @@
{
struct task_struct *tsk = current;
struct thread_struct *t = &tsk->thread;
-
+#ifdef CONFIG_KPROBES
+ /*
+ * Remove function-return probe instances associated with this task
+ * and put them back on the free list.
+ */
+ kprobe_flush_task(tsk);
+#endif
/* The process may have allocated an io port bitmap... nuke it. */
if (unlikely(NULL != t->io_bitmap_ptr)) {
int cpu = get_cpu();
@@ -395,7 +402,13 @@
void flush_thread(void)
{
struct task_struct *tsk = current;
-
+#ifdef CONFIG_KPROBES
+ /*
+ * Remove function-return probe instances associated with this task
+ * and put them back on the free list.
+ */
+ kprobe_flush_task(tsk);
+#endif
memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8);
memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
/*
--- linux-2.6.11.mm4/include/asm-i386/kprobes.h 2005-03-01 23:38:12.000000000 -0800
+++ linux-2.6.11.mm4.works/include/asm-i386/kprobes.h 2005-03-21 13:29:24.000000000 -0800
@@ -39,6 +39,7 @@
: (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
#define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry
+#define arch_supports_retprobes 1
/* Architecture specific copy of original instruction*/
struct arch_specific_insn {
| Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
|---|---|---|
| Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |