From 3aed4dca825a63757a3c95b9d41019660f83e02d Mon Sep 17 00:00:00 2001 From: Francis Deslauriers Date: Tue, 14 Nov 2017 14:23:11 -0500 Subject: [PATCH] uprobe: Support multiple call sites for the same uprobe event Signed-off-by: Francis Deslauriers Signed-off-by: Mathieu Desnoyers --- lttng-abi.c | 9 ++++ lttng-abi.h | 10 +++++ lttng-events.c | 14 +++++- lttng-events.h | 29 +++++++++---- probes/lttng-uprobes.c | 99 +++++++++++++++++++++++++++++------------- 5 files changed, 123 insertions(+), 38 deletions(-) diff --git a/lttng-abi.c b/lttng-abi.c index 32fa0bfa..9d3f7424 100644 --- a/lttng-abi.c +++ b/lttng-abi.c @@ -1474,6 +1474,15 @@ long lttng_event_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } } + case LTTNG_KERNEL_ADD_CALLSITE: + switch (*evtype) { + case LTTNG_TYPE_EVENT: + event = file->private_data; + return lttng_event_add_callsite(event, + (struct lttng_kernel_event_callsite __user *) arg); + case LTTNG_TYPE_ENABLER: + return -EINVAL; + } default: return -ENOIOCTLCMD; } diff --git a/lttng-abi.h b/lttng-abi.h index 762c10b0..db66bf37 100644 --- a/lttng-abi.h +++ b/lttng-abi.h @@ -76,9 +76,18 @@ struct lttng_kernel_function_tracer { struct lttng_kernel_uprobe { int fd; +} __attribute__((packed)); + +struct lttng_kernel_event_callsite_uprobe { uint64_t offset; } __attribute__((packed)); +struct lttng_kernel_event_callsite { + union { + struct lttng_kernel_event_callsite_uprobe uprobe; + } u; +} __attribute__((packed)); + /* * For syscall tracing, name = "*" means "enable all". */ @@ -220,6 +229,7 @@ struct lttng_kernel_filter_bytecode { /* Event FD ioctl */ #define LTTNG_KERNEL_FILTER _IO(0xF6, 0x90) +#define LTTNG_KERNEL_ADD_CALLSITE _IO(0xF6, 0x91) /* LTTng-specific ioctls for the lib ringbuffer */ /* returns the timestamp begin of the current sub-buffer */ diff --git a/lttng-events.c b/lttng-events.c index 67ed1697..3964df5f 100644 --- a/lttng-events.c +++ b/lttng-events.c @@ -749,6 +749,7 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, */ event->enabled = 0; event->registered = 1; + /* * Populate lttng_event structure before event * registration. @@ -757,7 +758,6 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, ret = lttng_uprobes_register(event_param->name, event_param->u.uprobe.fd, - event_param->u.uprobe.offset, event); if (ret) goto register_error; @@ -1466,6 +1466,18 @@ error_free: return ret; } +int lttng_event_add_callsite(struct lttng_event *event, + struct lttng_kernel_event_callsite __user *callsite) +{ + + switch (event->instrumentation) { + case LTTNG_KERNEL_UPROBE: + return lttng_uprobes_add_callsite(event, callsite); + default: + return -EINVAL; + } +} + int lttng_enabler_attach_context(struct lttng_enabler *enabler, struct lttng_kernel_context *context_param) { diff --git a/lttng-events.h b/lttng-events.h index f6fe1518..f74f9d52 100644 --- a/lttng-events.h +++ b/lttng-events.h @@ -285,6 +285,13 @@ struct lttng_enabler_ref { struct lttng_enabler *ref; /* backward ref */ }; +struct lttng_uprobe_handler { + struct lttng_event *event; + loff_t offset; + struct uprobe_consumer up_consumer; + struct list_head node; +}; + /* * lttng_event structure is referred to by the tracing fast path. It must be * kept small. @@ -311,9 +318,8 @@ struct lttng_event { char *symbol_name; } ftrace; struct { - struct uprobe_consumer up_consumer; struct inode *inode; - loff_t offset; + struct list_head head; } uprobe; } u; struct list_head list; /* Event list in session */ @@ -777,19 +783,26 @@ void lttng_kprobes_destroy_private(struct lttng_event *event) } #endif +int lttng_event_add_callsite(struct lttng_event *event, + struct lttng_kernel_event_callsite *callsite); #ifdef CONFIG_UPROBES int lttng_uprobes_register(const char *name, - int fd, - uint64_t offset, - struct lttng_event *event); + int fd, struct lttng_event *event); +int lttng_uprobes_add_callsite(struct lttng_event *event, + struct lttng_kernel_event_callsite *callsite); void lttng_uprobes_unregister(struct lttng_event *event); void lttng_uprobes_destroy_private(struct lttng_event *event); #else static inline int lttng_uprobes_register(const char *name, - int fd, - uint64_t offset, - struct lttng_event *event) + int fd, struct lttng_event *event) +{ + return -ENOSYS; +} + +static inline +int lttng_uprobes_add_callsite(struct lttng_event *event, + struct lttng_kernel_callsite_uprobe *callsite) { return -ENOSYS; } diff --git a/probes/lttng-uprobes.c b/probes/lttng-uprobes.c index 1f549c2f..47f3599f 100644 --- a/probes/lttng-uprobes.c +++ b/probes/lttng-uprobes.c @@ -22,9 +22,11 @@ */ #include +#include #include #include #include +#include #include #include #include @@ -35,8 +37,9 @@ static int lttng_uprobes_handler_pre(struct uprobe_consumer *uc, struct pt_regs *regs) { - struct lttng_event *event = - container_of(uc, struct lttng_event, u.uprobe.up_consumer); + struct lttng_uprobe_handler *uprobe_handler = + container_of(uc, struct lttng_uprobe_handler, up_consumer); + struct lttng_event *event = uprobe_handler->event; struct lttng_probe_ctx lttng_probe_ctx = { .event = event, .interruptible = !lttng_regs_irqs_disabled(regs), @@ -151,12 +154,59 @@ error: return inode; } -int lttng_uprobes_register(const char *name, - int fd, - uint64_t offset, - struct lttng_event *event) +int lttng_uprobes_add_callsite(struct lttng_event *event, + struct lttng_kernel_event_callsite __user *callsite) { - int ret; + int ret = 0; + struct lttng_uprobe_handler *uprobe_handler; + + if (!event) { + ret = -EINVAL; + goto end; + } + + uprobe_handler = kzalloc(sizeof(struct lttng_uprobe_handler), GFP_KERNEL); + if (!uprobe_handler) { + printk(KERN_WARNING "Error allocating uprobe_uprobe_handlers"); + ret = -ENOMEM; + goto end; + } + + /* Ensure the memory we just allocated don't trigger page faults. */ + wrapper_vmalloc_sync_all(); + + uprobe_handler->event = event; + uprobe_handler->up_consumer.handler = lttng_uprobes_handler_pre; + + ret = copy_from_user(&uprobe_handler->offset, &callsite->u.uprobe.offset, sizeof(uint64_t)); + if (ret) { + goto register_error; + } + + ret = wrapper_uprobe_register(event->u.uprobe.inode, + uprobe_handler->offset, &uprobe_handler->up_consumer); + if (ret) { + printk(KERN_WARNING "Error registering probe on inode %lu " + "and offset 0x%llx\n", event->u.uprobe.inode->i_ino, + uprobe_handler->offset); + ret = -1; + goto register_error; + } + + list_add(&uprobe_handler->node, &event->u.uprobe.head); + + return ret; + +register_error: + kfree(uprobe_handler); +end: + return ret; +} +EXPORT_SYMBOL_GPL(lttng_uprobes_add_callsite); + +int lttng_uprobes_register(const char *name, int fd, struct lttng_event *event) +{ + int ret = 0; struct inode *inode; ret = lttng_create_uprobe_event(name, event); @@ -169,29 +219,11 @@ int lttng_uprobes_register(const char *name, ret = -EBADF; goto inode_error; } - - memset(&event->u.uprobe.up_consumer, 0, - sizeof(event->u.uprobe.up_consumer)); - - event->u.uprobe.up_consumer.handler = lttng_uprobes_handler_pre; event->u.uprobe.inode = inode; - event->u.uprobe.offset = offset; + INIT_LIST_HEAD(&event->u.uprobe.head); - /* Ensure the memory we just allocated don't trigger page faults. */ - wrapper_vmalloc_sync_all(); - ret = wrapper_uprobe_register(event->u.uprobe.inode, - event->u.uprobe.offset, - &event->u.uprobe.up_consumer); - if (ret) { - printk(KERN_WARNING "Error registering probe on inode %lu " - "and offset %llu\n", event->u.uprobe.inode->i_ino, - event->u.uprobe.offset); - goto register_error; - } return 0; -register_error: - iput(event->u.uprobe.inode); inode_error: kfree(event->desc->name); kfree(event->desc); @@ -202,9 +234,18 @@ EXPORT_SYMBOL_GPL(lttng_uprobes_register); void lttng_uprobes_unregister(struct lttng_event *event) { - wrapper_uprobe_unregister(event->u.uprobe.inode, - event->u.uprobe.offset, - &event->u.uprobe.up_consumer); + struct lttng_uprobe_handler *iter, *tmp; + + /* + * Iterate over the list of handler, remove each handler from the list + * and free the struct. + */ + list_for_each_entry_safe(iter, tmp, &event->u.uprobe.head, node) { + wrapper_uprobe_unregister(event->u.uprobe.inode, iter->offset, + &iter->up_consumer); + list_del(&iter->node); + kfree(iter); + } } EXPORT_SYMBOL_GPL(lttng_uprobes_unregister); -- 2.34.1