From fd0402f35bb247d0b28e02904da8daed6c5ba24e Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Tue, 1 Mar 2022 12:27:06 -0500 Subject: [PATCH] kprobe: Implement kprobes with event enablers Signed-off-by: Mathieu Desnoyers Change-Id: Idd1285baa8da6476620ddfc3c21d93a8d4ab08f7 --- include/lttng/events-internal.h | 13 +++- src/lttng-abi.c | 65 +++++++++++++--- src/lttng-events.c | 71 ++++++++++++----- src/lttng-kprobes.c | 131 +++++++++++++++----------------- 4 files changed, 177 insertions(+), 103 deletions(-) diff --git a/include/lttng/events-internal.h b/include/lttng/events-internal.h index d94d2541..eda1a7b2 100644 --- a/include/lttng/events-internal.h +++ b/include/lttng/events-internal.h @@ -89,6 +89,9 @@ struct lttng_uprobe_handler { struct lttng_kprobe { struct kprobe kp; char *symbol_name; + uint64_t addr; + uint64_t offset; + kprobe_pre_handler_t pre_handler; }; struct lttng_uprobe { @@ -1026,18 +1029,19 @@ static inline void lttng_syscall_table_set_wildcard_all(struct lttng_event_enabl #endif #ifdef CONFIG_KPROBES -int lttng_kprobes_register_event(const char *name, +int lttng_kprobes_init_event(const char *name, const char *symbol_name, uint64_t offset, uint64_t addr, struct lttng_kernel_event_common *event); +int lttng_kprobes_register_event(struct lttng_kernel_event_common *event); void lttng_kprobes_unregister_event(struct lttng_kernel_event_common *event); void lttng_kprobes_destroy_event_private(struct lttng_kernel_event_common *event); int lttng_kprobes_match_check(const char *symbol_name, uint64_t offset, uint64_t addr); #else static inline -int lttng_kprobes_register_event(const char *name, +int lttng_kprobes_init_event(const char *name, const char *symbol_name, uint64_t offset, uint64_t addr, @@ -1045,6 +1049,11 @@ int lttng_kprobes_register_event(const char *name, { return -ENOSYS; } +static inline +int lttng_kprobes_register_event(struct lttng_kernel_event_common *event) +{ + return -ENOSYS; +} static inline void lttng_kprobes_unregister_event(struct lttng_kernel_event_common *event) diff --git a/src/lttng-abi.c b/src/lttng-abi.c index 34f97e24..e5b03393 100644 --- a/src/lttng-abi.c +++ b/src/lttng-abi.c @@ -2119,10 +2119,10 @@ int lttng_abi_create_event_recorder_enabler(struct file *channel_file, case LTTNG_KERNEL_ABI_TRACEPOINT: lttng_fallthrough; case LTTNG_KERNEL_ABI_SYSCALL: + lttng_fallthrough; + case LTTNG_KERNEL_ABI_KPROBE: fops = <tng_event_session_enabler_fops; break; - case LTTNG_KERNEL_ABI_KPROBE: - lttng_fallthrough; case LTTNG_KERNEL_ABI_KRETPROBE: lttng_fallthrough; case LTTNG_KERNEL_ABI_UPROBE: @@ -2185,7 +2185,17 @@ int lttng_abi_create_event_recorder_enabler(struct file *channel_file, } case LTTNG_KERNEL_ABI_KPROBE: - lttng_fallthrough; + { + struct lttng_event_recorder_enabler *event_enabler; + + event_enabler = lttng_event_recorder_enabler_create(LTTNG_ENABLER_FORMAT_NAME, + event_param, channel); + if (event_enabler) + lttng_event_enabler_session_add(channel->parent.session, &event_enabler->parent); + priv = event_enabler; + break; + } + case LTTNG_KERNEL_ABI_UPROBE: { struct lttng_kernel_event_common *event; @@ -2307,10 +2317,11 @@ int lttng_abi_create_event_counter_enabler(struct file *channel_file, case LTTNG_KERNEL_ABI_TRACEPOINT: lttng_fallthrough; case LTTNG_KERNEL_ABI_SYSCALL: + lttng_fallthrough; + case LTTNG_KERNEL_ABI_KPROBE: fops = <tng_event_session_enabler_fops; break; - case LTTNG_KERNEL_ABI_KPROBE: - lttng_fallthrough; + case LTTNG_KERNEL_ABI_KRETPROBE: lttng_fallthrough; case LTTNG_KERNEL_ABI_UPROBE: @@ -2373,7 +2384,17 @@ int lttng_abi_create_event_counter_enabler(struct file *channel_file, } case LTTNG_KERNEL_ABI_KPROBE: - lttng_fallthrough; + { + struct lttng_event_counter_enabler *event_enabler; + + event_enabler = lttng_event_counter_enabler_create(LTTNG_ENABLER_FORMAT_NAME, + event_param, key_param, NULL, channel); + if (event_enabler) + lttng_event_enabler_session_add(channel->parent.session, &event_enabler->parent); + priv = event_enabler; + break; + } + case LTTNG_KERNEL_ABI_UPROBE: { struct lttng_kernel_event_common *event; @@ -2573,10 +2594,11 @@ int lttng_abi_create_event_notifier(struct file *event_notifier_group_file, case LTTNG_KERNEL_ABI_TRACEPOINT: lttng_fallthrough; case LTTNG_KERNEL_ABI_SYSCALL: + lttng_fallthrough; + case LTTNG_KERNEL_ABI_KPROBE: fops = <tng_event_notifier_enabler_fops; break; - case LTTNG_KERNEL_ABI_KPROBE: - lttng_fallthrough; + case LTTNG_KERNEL_ABI_KRETPROBE: lttng_fallthrough; case LTTNG_KERNEL_ABI_UPROBE: @@ -2659,7 +2681,17 @@ int lttng_abi_create_event_notifier(struct file *event_notifier_group_file, } case LTTNG_KERNEL_ABI_KPROBE: - lttng_fallthrough; + { + struct lttng_event_notifier_enabler *enabler; + + enabler = lttng_event_notifier_enabler_create(LTTNG_ENABLER_FORMAT_NAME, + event_notifier_param, event_notifier_group); + if (enabler) + lttng_event_notifier_enabler_group_add(event_notifier_group, enabler); + priv = enabler; + break; + } + case LTTNG_KERNEL_ABI_UPROBE: { struct lttng_kernel_event_common *event; @@ -3374,8 +3406,19 @@ long lttng_event_enabler_ioctl(struct file *file, unsigned int cmd, unsigned lon case LTTNG_KERNEL_ABI_DISABLE: return lttng_event_enabler_disable(event_enabler); case LTTNG_KERNEL_ABI_FILTER: - return lttng_event_enabler_attach_filter_bytecode(event_enabler, - (struct lttng_kernel_abi_filter_bytecode __user *) arg); + /* + * Filters are only supported by tracepoint and syscall instrumentation. + */ + switch (event_enabler->event_param.instrumentation) { + case LTTNG_KERNEL_ABI_TRACEPOINT: + lttng_fallthrough; + case LTTNG_KERNEL_ABI_SYSCALL: + return lttng_event_enabler_attach_filter_bytecode(event_enabler, + (struct lttng_kernel_abi_filter_bytecode __user *) arg); + + default: + return -EINVAL; + } case LTTNG_KERNEL_ABI_ADD_CALLSITE: return -EINVAL; default: diff --git a/src/lttng-events.c b/src/lttng-events.c index 860d8f7e..49888e32 100644 --- a/src/lttng-events.c +++ b/src/lttng-events.c @@ -688,11 +688,11 @@ int lttng_event_enable(struct lttng_kernel_event_common *event) case LTTNG_KERNEL_ABI_TRACEPOINT: lttng_fallthrough; case LTTNG_KERNEL_ABI_SYSCALL: + lttng_fallthrough; + case LTTNG_KERNEL_ABI_KPROBE: ret = -EINVAL; break; - case LTTNG_KERNEL_ABI_KPROBE: - lttng_fallthrough; case LTTNG_KERNEL_ABI_UPROBE: WRITE_ONCE(event->enabled, 1); break; @@ -747,11 +747,11 @@ int lttng_event_disable(struct lttng_kernel_event_common *event) case LTTNG_KERNEL_ABI_TRACEPOINT: lttng_fallthrough; case LTTNG_KERNEL_ABI_SYSCALL: + lttng_fallthrough; + case LTTNG_KERNEL_ABI_KPROBE: ret = -EINVAL; break; - case LTTNG_KERNEL_ABI_KPROBE: - lttng_fallthrough; case LTTNG_KERNEL_ABI_UPROBE: WRITE_ONCE(event->enabled, 0); break; @@ -1469,18 +1469,10 @@ struct lttng_kernel_event_common *_lttng_kernel_event_create(struct lttng_event_ break; case LTTNG_KERNEL_ABI_KPROBE: - /* - * Needs to be explicitly enabled after creation, since - * we may want to apply filters. - */ + /* Event will be enabled by enabler sync. */ event->enabled = 0; - event->priv->registered = 1; - /* - * Populate lttng_event structure before event - * registration. - */ - smp_wmb(); - ret = lttng_kprobes_register_event(event_name, + event->priv->registered = 0; + ret = lttng_kprobes_init_event(event_name, event_param->u.kprobe.symbol_name, event_param->u.kprobe.offset, event_param->u.kprobe.addr, @@ -1489,10 +1481,13 @@ struct lttng_kernel_event_common *_lttng_kernel_event_create(struct lttng_event_ ret = -EINVAL; goto register_error; } + /* + * Populate lttng_event structure before event + * registration. + */ + smp_wmb(); ret = try_module_get(event->priv->desc->owner); WARN_ON_ONCE(!ret); - ret = lttng_append_event_to_channel_map(event_enabler, event, event_name); - WARN_ON_ONCE(ret); break; case LTTNG_KERNEL_ABI_KRETPROBE: @@ -1674,7 +1669,7 @@ int lttng_kernel_counter_get_max_nr_elem(struct lttng_kernel_channel_counter *co return counter->ops->priv->counter_get_max_nr_elem(counter, max_nr_elem); } -/* Only used for tracepoints and system calls for now. */ +/* Used for tracepoints, system calls, and kprobe. */ static void register_event(struct lttng_kernel_event_common *event) { @@ -1696,7 +1691,9 @@ void register_event(struct lttng_kernel_event_common *event) break; case LTTNG_KERNEL_ABI_KPROBE: - lttng_fallthrough; + ret = lttng_kprobes_register_event(event); + break; + case LTTNG_KERNEL_ABI_UPROBE: ret = 0; break; @@ -2231,6 +2228,19 @@ int lttng_desc_match_enabler_check(const struct lttng_kernel_event_desc *desc, } break; + case LTTNG_KERNEL_ABI_KPROBE: + desc_name = desc->event_name; + switch (enabler->format_type) { + case LTTNG_ENABLER_FORMAT_STAR_GLOB: + return -EINVAL; + case LTTNG_ENABLER_FORMAT_NAME: + return lttng_match_enabler_name(desc_name, enabler_name); + default: + return -EINVAL; + } + break; + + default: WARN_ON_ONCE(1); return -EINVAL; @@ -2358,6 +2368,21 @@ void lttng_event_enabler_create_tracepoint_events_if_missing(struct lttng_event_ } } +/* Try to create the event associated with this kprobe enabler. */ +static +void lttng_event_enabler_create_kprobe_event_if_missing(struct lttng_event_enabler_common *event_enabler) +{ + struct lttng_kernel_event_common *event; + + event = _lttng_kernel_event_create(event_enabler, NULL, NULL); + if (IS_ERR(event)) { + if (PTR_ERR(event) != -EEXIST) { + printk(KERN_INFO "LTTng: Unable to create kprobe event %s\n", + event_enabler->event_param.name); + } + } +} + /* * Create event if it is missing and present in the list of tracepoint probes. * Should be called with sessions mutex held. @@ -2377,6 +2402,10 @@ void lttng_event_enabler_create_events_if_missing(struct lttng_event_enabler_com WARN_ON_ONCE(ret); break; + case LTTNG_KERNEL_ABI_KPROBE: + lttng_event_enabler_create_kprobe_event_if_missing(event_enabler); + break; + default: WARN_ON_ONCE(1); break; @@ -2915,6 +2944,8 @@ bool lttng_get_event_enabled_state(struct lttng_kernel_event_common *event) case LTTNG_KERNEL_ABI_TRACEPOINT: lttng_fallthrough; case LTTNG_KERNEL_ABI_SYSCALL: + lttng_fallthrough; + case LTTNG_KERNEL_ABI_KPROBE: /* Enable events */ list_for_each_entry(enabler_ref, &event->priv->enablers_ref_head, node) { if (enabler_ref->ref->enabled) { @@ -2958,6 +2989,8 @@ bool lttng_event_is_lazy_sync(struct lttng_kernel_event_common *event) case LTTNG_KERNEL_ABI_TRACEPOINT: lttng_fallthrough; case LTTNG_KERNEL_ABI_SYSCALL: + lttng_fallthrough; + case LTTNG_KERNEL_ABI_KPROBE: return true; default: diff --git a/src/lttng-kprobes.c b/src/lttng-kprobes.c index b2e96635..cdaf68ac 100644 --- a/src/lttng-kprobes.c +++ b/src/lttng-kprobes.c @@ -117,11 +117,39 @@ static const struct lttng_kernel_tracepoint_class tp_class = { .fields = event_fields, }; +static +int lttng_kp_init(struct lttng_kprobe *lttng_kp, const char *symbol_name, uint64_t offset, uint64_t addr, + kprobe_pre_handler_t pre_handler) +{ + /* Kprobes expects a NULL symbol name if unused */ + if (symbol_name[0] == '\0') + symbol_name = NULL; + + lttng_kp->pre_handler = pre_handler; + + if (symbol_name) { + lttng_kp->symbol_name = + kzalloc(LTTNG_KERNEL_ABI_SYM_NAME_LEN * sizeof(char), + GFP_KERNEL); + if (!lttng_kp->symbol_name) + return -ENOMEM; + memcpy(lttng_kp->symbol_name, symbol_name, + LTTNG_KERNEL_ABI_SYM_NAME_LEN * sizeof(char)); + } + + lttng_kp->offset = offset; + lttng_kp->addr = addr; + return 0; +} + /* - * Create event description + * Initialize event */ -static -int lttng_create_kprobe_event(const char *name, struct lttng_kernel_event_common *event) +int lttng_kprobes_init_event(const char *name, + const char *symbol_name, + uint64_t offset, + uint64_t addr, + struct lttng_kernel_event_common *event) { struct lttng_kernel_event_desc *desc; int ret; @@ -137,46 +165,22 @@ int lttng_create_kprobe_event(const char *name, struct lttng_kernel_event_common } desc->owner = THIS_MODULE; event->priv->desc = desc; - + ret = lttng_kp_init(&event->priv->u.kprobe, symbol_name, offset, addr, + lttng_kprobes_event_handler_pre); + if (ret) + goto error_init; return 0; +error_init: + kfree(desc->event_name); error_str: kfree(desc); return ret; } static -int _lttng_kprobes_register(const char *symbol_name, - uint64_t offset, - uint64_t addr, - struct lttng_kprobe *lttng_kp, - kprobe_pre_handler_t pre_handler) +int _lttng_kprobes_register(struct lttng_kprobe *lttng_kp) { - int ret; - - /* Kprobes expects a NULL symbol name if unused */ - if (symbol_name[0] == '\0') - symbol_name = NULL; - - memset(<tng_kp->kp, 0, sizeof(lttng_kp->kp)); - lttng_kp->kp.pre_handler = pre_handler; - - if (symbol_name) { - lttng_kp->symbol_name = - kzalloc(LTTNG_KERNEL_ABI_SYM_NAME_LEN * sizeof(char), - GFP_KERNEL); - if (!lttng_kp->symbol_name) { - ret = -ENOMEM; - goto name_error; - } - memcpy(lttng_kp->symbol_name, symbol_name, - LTTNG_KERNEL_ABI_SYM_NAME_LEN * sizeof(char)); - lttng_kp->kp.symbol_name = lttng_kp->symbol_name; - } - - lttng_kp->kp.offset = offset; - lttng_kp->kp.addr = (void *) (unsigned long) addr; - /* * Ensure the memory we just allocated don't notify page faults. * Well.. kprobes itself puts the page fault handler on the blacklist, @@ -184,42 +188,21 @@ int _lttng_kprobes_register(const char *symbol_name, */ wrapper_vmalloc_sync_mappings(); - ret = register_kprobe(<tng_kp->kp); - if (ret) - goto register_error; - - return 0; - -register_error: - kfree(lttng_kp->symbol_name); -name_error: - return ret; + /* + * Populate struct kprobe on each registration because kprobe internally + * does destructive changes to its state (e.g. addr=NULL). + */ + memset(<tng_kp->kp, 0, sizeof(lttng_kp->kp)); + lttng_kp->kp.symbol_name = lttng_kp->symbol_name; + lttng_kp->kp.addr = (void *)(unsigned long)lttng_kp->addr; + lttng_kp->kp.offset = lttng_kp->offset; + lttng_kp->kp.pre_handler = lttng_kp->pre_handler; + return register_kprobe(<tng_kp->kp); } -int lttng_kprobes_register_event(const char *name, - const char *symbol_name, - uint64_t offset, - uint64_t addr, - struct lttng_kernel_event_common *event) +int lttng_kprobes_register_event(struct lttng_kernel_event_common *event) { - int ret; - - ret = lttng_create_kprobe_event(name, event); - if (ret) - goto error; - - ret = _lttng_kprobes_register(symbol_name, offset, addr, - &event->priv->u.kprobe, lttng_kprobes_event_handler_pre); - if (ret) - goto register_error; - - return 0; - -register_error: - kfree(event->priv->desc->event_name); - kfree(event->priv->desc); -error: - return ret; + return _lttng_kprobes_register(&event->priv->u.kprobe); } void lttng_kprobes_unregister_event(struct lttng_kernel_event_common *event) @@ -230,15 +213,21 @@ void lttng_kprobes_unregister_event(struct lttng_kernel_event_common *event) int lttng_kprobes_match_check(const char *symbol_name, uint64_t offset, uint64_t addr) { struct lttng_kprobe lttng_kp; - int ret; + int ret = 0; memset(<tng_kp, 0, sizeof(lttng_kp)); - ret = _lttng_kprobes_register(symbol_name, offset, addr, <tng_kp, NULL); + ret = lttng_kp_init(<tng_kp, symbol_name, offset, addr, NULL); if (ret) - return -ENOENT; + return ret; + ret = _lttng_kprobes_register(<tng_kp); + if (ret) { + ret = -ENOENT; + goto end; + } unregister_kprobe(<tng_kp.kp); +end: kfree(lttng_kp.symbol_name); - return 0; + return ret; } -- 2.34.1