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] |
--- 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 *)®s->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 *)(®s->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] |