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] |