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]

Exit probe


Hi,

Here's the prototype for the exit probe. It did not address the two issues that Will raised in the meeting yesterday (locking, and multiple probe for the same entry).

The attached files are

1. kernel-2.6.10-rprobe.patch - The exit probe patch.

2. testrprobe.c - This is the test module, when load it will insert a rprobe for any function entry address given by the entryaddr parameter when loading (ie "insmod testrprobe.ko entryaddr=0xc0abd3456).

3. loadtestrprobe.sh - use this script to load testrprobe module above, it will grep out the entry address for the test_erprobe2 function and use it as entryaddr parameter.

4. calltest2.c - This module symply invoke test_erprobe2 function when it is removed.


Thanks, Hien.
--- linux-2.6.10/include/linux/kprobes.h	2004-12-24 13:34:27.000000000 -0800
+++ linux-2.6.10.works/include/linux/kprobes.h	2005-03-04 11:14:38.000000000 -0800
@@ -34,12 +34,15 @@
 
 struct kprobe;
 struct pt_regs;
+struct rprobe_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 (*rprobe_handler_t) (struct kprobe *, struct pt_regs *);
+
 struct kprobe {
 	struct hlist_node hlist;
 
@@ -65,6 +68,9 @@
 
 	/* copy of the original instruction */
 	struct arch_specific_insn ainsn;
+	
+	/* point to rprobe */
+	struct rprobe *rp;
 };
 
 /*
@@ -82,6 +88,26 @@
 	kprobe_opcode_t *entry;	/* probe handling code to jump to */
 };
 
+struct rprobe {
+	rprobe_handler_t handler;
+	kprobe_fault_handler_t fault_handler;
+	int maxactive;
+	int nmissed;
+	int num_ri_running;
+	int unregistering;
+	struct kprobe *kprobe;
+	struct rprobe_instance *instances;	/* allocated memory */
+	struct list_head free_instances;
+};
+
+struct rprobe_instance {
+	struct list_head list;
+	struct hlist_node hlist;
+	struct rprobe *rp;
+	void *ret_addr;
+	void *stack_addr;
+};
+
 #ifdef CONFIG_KPROBES
 /* Locks kprobe: irq must be disabled */
 void lock_kprobes(void);
@@ -94,9 +120,12 @@
 	return kprobe_cpu == smp_processor_id();
 }
 
+asmlinkage void rprobe_trampoline(void) __asm__("rprobe_trampoline");
+
 extern int arch_prepare_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);
 
 /* Get the kprobe at this addr (if any).  Must have called lock_kprobes */
 struct kprobe *get_kprobe(void *addr);
@@ -109,6 +138,15 @@
 void unregister_jprobe(struct jprobe *p);
 void jprobe_return(void);
 
+int register_erprobe(struct kprobe *p, struct rprobe *rp);
+int register_jrprobe(struct jprobe *p, struct rprobe *rp);
+
+struct rprobe_instance *get_free_rinst(struct rprobe *rp);
+struct rprobe_instance *get_rinst(void *sara);
+void add_rprobe_inst_to_hash(struct rprobe_instance *ri); 
+
+void test_erprobe(void);
+int test_erprobe2(int a, int b, int c, char *str);
 #else
 static inline int kprobe_running(void)
 {
@@ -131,5 +169,13 @@
 static inline void jprobe_return(void)
 {
 }
+static inline int register_erprobe(struct kprobe *p, struct rprobe *rp)
+{
+	return -ENOSYS;
+}
+static inline int register_jrprobe(struct jprobe *p, struct rprobe *rp)
+{
+	return -ENOSYS;
+}
 #endif
 #endif				/* _LINUX_KPROBES_H */
--- linux-2.6.10/kernel/kprobes.c	2004-12-24 13:35:59.000000000 -0800
+++ linux-2.6.10.works/kernel/kprobes.c	2005-03-04 11:19:49.000000000 -0800
@@ -42,6 +42,11 @@
 
 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 rprobe_inst_table[RPROBE_INST_TABLE_SIZE];
+
 unsigned int kprobe_cpu = NR_CPUS;
 static spinlock_t kprobe_lock = SPIN_LOCK_UNLOCKED;
 
@@ -58,6 +63,24 @@
 	spin_unlock(&kprobe_lock);
 }
 
+struct kprobe trampoline_p = {
+		.addr = (kprobe_opcode_t *) &rprobe_trampoline,
+		.pre_handler = trampoline_probe_handler,
+		.rp = NULL
+};
+
+static void init_rprobe(void)
+{
+	int i;
+
+	/* Register a probe point at the rprobe trampoline */
+	register_kprobe(&trampoline_p);
+
+	/* Allocate rprobe instances table */	
+	for (i = 0; i < RPROBE_INST_TABLE_SIZE; i++)
+		INIT_HLIST_HEAD(&rprobe_inst_table[i]);
+}
+
 /* You have to be holding the kprobe_lock */
 struct kprobe *get_kprobe(void *addr)
 {
@@ -73,6 +96,34 @@
 	return NULL;
 }
 
+struct rprobe_instance * get_free_rinst(struct rprobe *rp)
+{
+	if (list_empty(&rp->free_instances)) {
+		return NULL;
+	}
+	return (struct rprobe_instance *) rp->free_instances.next;
+}	
+
+struct rprobe_instance *get_rinst(void *sara)
+{
+	struct hlist_head *head;
+	struct hlist_node *node;
+
+	head = &rprobe_inst_table[hash_ptr(sara, RPROBE_HASH_BITS)];
+	hlist_for_each(node, head) {
+		struct rprobe_instance *ri = hlist_entry(node, 
+						struct rprobe_instance, hlist);
+		if (ri->stack_addr == sara)
+			return ri;
+	}
+	return NULL;
+}
+
+void add_rprobe_inst_to_hash(struct rprobe_instance *ri)
+{
+	hlist_add_head(&ri->hlist, &rprobe_inst_table[hash_ptr(ri->stack_addr, RPROBE_HASH_BITS)]);
+}
+		
 int register_kprobe(struct kprobe *p)
 {
 	int ret = 0;
@@ -109,6 +160,16 @@
 	hlist_del(&p->hlist);
 	flush_icache_range((unsigned long) p->addr,
 			   (unsigned long) p->addr + sizeof(kprobe_opcode_t));
+	
+	if (p->rp != NULL) {
+		/* Also clean up rprobe */
+		if (p->rp->num_ri_running != 0) {
+			/* Flag to tell the last running rprobe to clean up */
+			p->rp->unregistering = 1;
+		} else {
+			kfree(p->rp->instances);
+		}
+	}
 	spin_unlock_irqrestore(&kprobe_lock, flags);
 }
 
@@ -131,6 +192,79 @@
 	unregister_kprobe(&jp->kp);
 }
 
+int register_erprobe(struct kprobe *p, struct rprobe *rp)
+{
+	int ret = 0;
+	static int rprobe_init_setup = 0;
+	struct rprobe_instance *inst;
+	int maxinst, i;
+	
+	if (rprobe_init_setup == 0) {
+		init_rprobe();
+		rprobe_init_setup = 1;
+	}
+	/* Pre-allocate memory for max rprobe 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 rprobe_instance), 
+					GFP_KERNEL);
+	if (rp->instances == NULL) {
+		return -ENOMEM;
+	}
+	
+	INIT_LIST_HEAD(&rp->free_instances);
+	/* Put all rprobe_instance object 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_jrprobe(struct jprobe *jp, struct rprobe *rp)
+{
+
+	jp->kp.pre_handler = setjmp_pre_handler;
+	jp->kp.break_handler = longjmp_break_handler;
+
+	return register_erprobe(&jp->kp, rp);
+}
+	
+/* Just for testing - remove when done */
+void test_erprobe()
+{
+	int a,b,c;
+	a=15;
+	b=10;
+	c=20;
+	printk("test_erprobe invoked: a=%d, b=%d, c=%d.\n", a,b,c);
+}
+int test_erprobe2(int a, int b, int c, char *str)
+{
+	printk("test_erprobe2 invoked: a=%d, b=%d, c=%d, str=%s.\n", a,b,c, str);
+	return 1;
+}
+
 static int __init init_kprobes(void)
 {
 	int i, err = 0;
@@ -139,11 +273,12 @@
 	/* initialize all list heads */
 	for (i = 0; i < KPROBE_TABLE_SIZE; i++)
 		INIT_HLIST_HEAD(&kprobe_table[i]);
-
+	
 	err = register_die_notifier(&kprobe_exceptions_nb);
 	return err;
 }
 
+
 __initcall(init_kprobes);
 
 EXPORT_SYMBOL_GPL(register_kprobe);
@@ -151,3 +286,7 @@
 EXPORT_SYMBOL_GPL(register_jprobe);
 EXPORT_SYMBOL_GPL(unregister_jprobe);
 EXPORT_SYMBOL_GPL(jprobe_return);
+EXPORT_SYMBOL_GPL(register_erprobe);
+EXPORT_SYMBOL_GPL(register_jrprobe);
+EXPORT_SYMBOL_GPL(test_erprobe);
+EXPORT_SYMBOL_GPL(test_erprobe2);
--- linux-2.6.10/arch/i386/kernel/kprobes.c	2004-12-24 13:34:33.000000000 -0800
+++ linux-2.6.10.works/arch/i386/kernel/kprobes.c	2005-03-04 12:56:56.985648088 -0800
@@ -143,11 +143,38 @@
 	if (is_IF_modifier(p->opcode))
 		kprobe_saved_eflags &= ~IF_MASK;
 
+	if (p->rp != NULL) {
+		/* 
+	 	 * Get a rprobe instance off the free list and populate it
+	 	 * with the return addr, stack addr, and rp.
+	 	 */
+		struct rprobe_instance *ri;
+		unsigned long *sara = (unsigned long *)&regs->esp;
+
+		if ((ri = get_free_rinst(p->rp)) != NULL) {
+			INIT_HLIST_NODE(&ri->hlist);
+			ri->rp = p->rp;
+			ri->stack_addr = sara;
+			ri->ret_addr =  (void *) *sara;
+			/* Add probe instance in hash */
+			add_rprobe_inst_to_hash(ri);
+			/* Replace the return addr with trampoline addr */
+			*sara = (unsigned long) &rprobe_trampoline;
+			/* 
+			 * Remove obj in free list - 
+			 * will add it back when resume execution
+			 */
+			list_del(&ri->list);
+			p->rp->num_ri_running++;
+		} else {
+			p->rp->nmissed++;
+		}
+	}	
+
 	if (p->pre_handler(p, regs)) {
 		/* handler has already set things up, so skip ss setup */
 		return 1;
 	}
-
       ss_probe:
 	prepare_singlestep(p, regs);
 	kprobe_status = KPROBE_HIT_SS;
@@ -157,6 +184,23 @@
 	preempt_enable_no_resched();
 	return ret;
 }
+/*
+ * Called when we hit the probe point at rprobe_trampoline
+ */  
+int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
+{
+	struct rprobe_instance *ri;
+	unsigned long *sara = (unsigned long *)(&regs->esp - 1);
+	
+	if ((ri = get_rinst(sara)) == NULL) {
+		return 0;
+	}
+	if (ri->rp && !ri->rp->unregistering) {
+
+		return ri->rp->handler(p, regs);
+	}
+	return 0;
+}
 
 /*
  * Called after single-stepping.  p->addr is the address of the
@@ -210,6 +254,31 @@
 	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) rprobe_trampoline) {
+			struct rprobe_instance *ri;
+			unsigned long *sara = tos - 1;	/* RA already popped */
+			ri = get_rinst(sara);
+			if (ri != NULL) {
+				next_eip = (unsigned long)ri->ret_addr;
+				hlist_del(&ri->hlist);
+				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);
+					ri->rp->unregistering = 0;
+				} else {
+					/* put ri obj back to free list */
+					list_add(&ri->list, &ri->rp->free_instances);
+				}
+			}
+		}
+		break;
 	default:
 		break;
 	}
--- linux-2.6.10/arch/i386/kernel/entry.S	2004-12-24 13:34:27.000000000 -0800
+++ linux-2.6.10.works/arch/i386/kernel/entry.S	2005-03-04 11:33:50.784431951 -0800
@@ -140,6 +140,12 @@
 .previous
 
 
+#ifdef CONFIG_KPROBES
+ENTRY(rprobe_trampoline)
+	nop
+/* NOT REACHED */
+#endif
+
 ENTRY(ret_from_fork)
 	pushl %eax
 	call schedule_tail
#include <linux/module.h>
#include <linux/kprobes.h>

static unsigned long entryaddr;
module_param(entryaddr, ulong, 0);
MODULE_PARM_DESC(addr,
		 "\nfunction entry address.\n");

int inst_test_erprobe2 (void)
{
	printk("test_eprobe2 entry handler\n");
	jprobe_return();
	return 0;
}


int rp_handler(struct kprobe *p, struct pt_regs *regs)
{
	printk("rprobe handler: p->addr=0x%p\n", p->addr);
	printk("return code is stored in eax=0x%lx\n", regs->eax);
	return 0;
}
	
static struct jprobe jp = {
	.entry = (kprobe_opcode_t *) inst_test_erprobe2,
};


static struct rprobe rp = {
	.handler = rp_handler,
	.maxactive = 1,
	.nmissed = 0
};


static int init_testrp(void)
{
  if (entryaddr == 0 ) {
    printk("Need to input an function entry address as parameter.\n");
    return -EINVAL;
  }
  jp.kp.addr = (kprobe_opcode_t *) entryaddr;

  register_jrprobe(&jp, &rp);
  printk("exit probe init: instrumentation is enabled...\n");
  return 0;
}

static void cleanup_testrp(void)
{
  unregister_jprobe(&jp);
  printk("exit probe cleanup.\n");
}

module_init(init_testrp);
module_exit(cleanup_testrp);
MODULE_LICENSE("GPL");

Attachment: loadtestrprobe.sh
Description: Bourne shell script

#include <linux/module.h>
#include <linux/kprobes.h>

static int init_calltest(void)
{
	 printk("calltest:init\n");
	return 0;
}

static void cleanup_calltest(void)
{
	int ret;
	ret=test_erprobe2(25, 30, 40, "testing");
	printk("test_eprobe2 return code =%d\n", ret);
}

module_init(init_calltest);
module_exit(cleanup_calltest);
MODULE_LICENSE("GPL");


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