From 44d1600b39a438f83d8341d9030ba9f8b8c87ad7 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Mon, 28 Feb 2022 14:15:00 -0500 Subject: [PATCH] kretprobes: implement event counter support Signed-off-by: Mathieu Desnoyers Change-Id: Id5142ae8d31bba2c9a806f75cbbfefdca303d523 --- include/lttng/events-internal.h | 27 +++++++-- src/lttng-abi.c | 100 +++++++++++++++++++++++++++++--- src/lttng-events.c | 84 +++++++++++---------------- src/lttng-kretprobes.c | 43 ++------------ src/lttng-syscalls.c | 6 +- 5 files changed, 156 insertions(+), 104 deletions(-) diff --git a/include/lttng/events-internal.h b/include/lttng/events-internal.h index b3f938bb..14ad198c 100644 --- a/include/lttng/events-internal.h +++ b/include/lttng/events-internal.h @@ -455,6 +455,15 @@ struct lttng_metadata_stream { bool coherent; /* Stream in a coherent state */ }; +struct lttng_kernel_event_pair { + /* Input */ + char name[LTTNG_KERNEL_ABI_SYM_NAME_LEN]; + + /* Output from lttng_kernel_event_create() */ + struct lttng_kernel_event_common *event[2]; + int refcount; +}; + struct lttng_kernel_channel_buffer_ops_private { struct lttng_kernel_channel_buffer_ops *pub; /* Public channel buffer ops interface */ @@ -1083,8 +1092,8 @@ void lttng_uprobes_destroy_event_private(struct lttng_kernel_event_common *event #endif #ifdef CONFIG_KRETPROBES -int lttng_kretprobes_register(const char *name, - const char *symbol_name, +struct lttng_kernel_event_desc *lttng_create_kretprobes_event_desc(const char *name); +int lttng_kretprobes_register(const char *symbol_name, uint64_t offset, uint64_t addr, struct lttng_kernel_event_common *event_entry, @@ -1095,8 +1104,12 @@ int lttng_kretprobes_event_enable_state(struct lttng_kernel_event_common *event, int enable); #else static inline -int lttng_kretprobes_register(const char *name, - const char *symbol_name, +struct lttng_kernel_event_desc *lttng_create_kretprobes_event_desc(const char *name) +{ + return NULL; +} +static inline +int lttng_kretprobes_register(const char *symbol_name, uint64_t offset, uint64_t addr, struct lttng_kernel_event_common *event_entry, @@ -1181,9 +1194,11 @@ struct lttng_kernel_channel_buffer *lttng_global_channel_create(struct lttng_ker void lttng_metadata_channel_buffer_destroy(struct lttng_kernel_channel_buffer *chan); struct lttng_kernel_event_common *_lttng_kernel_event_create(struct lttng_event_enabler_common *event_enabler, - const struct lttng_kernel_event_desc *event_desc); + const struct lttng_kernel_event_desc *event_desc, + struct lttng_kernel_event_pair *event_pair); struct lttng_kernel_event_common *lttng_kernel_event_create(struct lttng_event_enabler_common *event_enabler, - const struct lttng_kernel_event_desc *event_desc); + const struct lttng_kernel_event_desc *event_desc, + struct lttng_kernel_event_pair *event_pair); int lttng_channel_enable(struct lttng_kernel_channel_common *channel); int lttng_channel_disable(struct lttng_kernel_channel_common *channel); diff --git a/src/lttng-abi.c b/src/lttng-abi.c index 2675414e..17041bc3 100644 --- a/src/lttng-abi.c +++ b/src/lttng-abi.c @@ -2008,8 +2008,6 @@ int lttng_abi_create_event_recorder_enabler(struct file *channel_file, case LTTNG_KERNEL_ABI_KPROBE: lttng_fallthrough; - case LTTNG_KERNEL_ABI_KRETPROBE: - lttng_fallthrough; case LTTNG_KERNEL_ABI_UPROBE: { struct lttng_kernel_event_common *event; @@ -2025,7 +2023,7 @@ int lttng_abi_create_event_recorder_enabler(struct file *channel_file, * We tolerate no failure path after event creation. It * will stay invariant for the rest of the session. */ - event = lttng_kernel_event_create(&event_enabler->parent.parent, NULL); + event = lttng_kernel_event_create(&event_enabler->parent.parent, NULL, NULL); WARN_ON_ONCE(IS_ERR(event)); lttng_event_enabler_destroy(&event_enabler->parent.parent); if (IS_ERR(event)) { @@ -2036,6 +2034,51 @@ int lttng_abi_create_event_recorder_enabler(struct file *channel_file, break; } + case LTTNG_KERNEL_ABI_KRETPROBE: + { + struct lttng_kernel_event_common *event[2]; + struct lttng_event_recorder_enabler *event_enabler; + struct lttng_kernel_event_pair event_pair; + + if (strlen(event_param->name) + strlen("_entry") >= LTTNG_KERNEL_ABI_SYM_NAME_LEN) + return -EINVAL; + + memset(&event_pair, 0, sizeof(event_pair)); + event_enabler = lttng_event_recorder_enabler_create(LTTNG_ENABLER_FORMAT_NAME, + event_param, channel); + if (!event_enabler) { + ret = -ENOMEM; + goto event_error; + } + + strcpy(event_pair.name, event_param->name); + strcat(event_pair.name, "_entry"); + /* + * We tolerate no failure path after event creation. It + * will stay invariant for the rest of the session. + */ + event[0] = lttng_kernel_event_create(&event_enabler->parent.parent, NULL, &event_pair); + WARN_ON_ONCE(IS_ERR(event[0])); + if (IS_ERR(event[0])) { + lttng_event_enabler_destroy(&event_enabler->parent.parent); + ret = PTR_ERR(event[0]); + goto event_error; + } + + strcpy(event_pair.name, event_param->name); + strcat(event_pair.name, "_exit"); + event[1] = lttng_kernel_event_create(&event_enabler->parent.parent, NULL, &event_pair); + WARN_ON_ONCE(IS_ERR(event[1])); + + lttng_event_enabler_destroy(&event_enabler->parent.parent); + if (IS_ERR(event[1])) { + ret = PTR_ERR(event[1]); + goto event_error; + } + priv = event[0]; + break; + } + case LTTNG_KERNEL_ABI_FUNCTION: lttng_fallthrough; case LTTNG_KERNEL_ABI_NOOP: @@ -2153,8 +2196,6 @@ int lttng_abi_create_event_counter_enabler(struct file *channel_file, case LTTNG_KERNEL_ABI_KPROBE: lttng_fallthrough; - case LTTNG_KERNEL_ABI_KRETPROBE: - lttng_fallthrough; case LTTNG_KERNEL_ABI_UPROBE: { struct lttng_kernel_event_common *event; @@ -2170,7 +2211,7 @@ int lttng_abi_create_event_counter_enabler(struct file *channel_file, * We tolerate no failure path after event creation. It * will stay invariant for the rest of the session. */ - event = lttng_kernel_event_create(&event_enabler->parent.parent, NULL); + event = lttng_kernel_event_create(&event_enabler->parent.parent, NULL, NULL); WARN_ON_ONCE(IS_ERR(event)); lttng_event_enabler_destroy(&event_enabler->parent.parent); if (IS_ERR(event)) { @@ -2181,6 +2222,51 @@ int lttng_abi_create_event_counter_enabler(struct file *channel_file, break; } + case LTTNG_KERNEL_ABI_KRETPROBE: + { + struct lttng_kernel_event_common *event[2]; + struct lttng_event_counter_enabler *event_enabler; + struct lttng_kernel_event_pair event_pair; + + if (strlen(event_param->name) + strlen("_entry") >= LTTNG_KERNEL_ABI_SYM_NAME_LEN) + return -EINVAL; + + memset(&event_pair, 0, sizeof(event_pair)); + event_enabler = lttng_event_counter_enabler_create(LTTNG_ENABLER_FORMAT_NAME, + event_param, key_param, NULL, channel); + if (!event_enabler) { + ret = -ENOMEM; + goto event_error; + } + + strcpy(event_pair.name, event_param->name); + strcat(event_pair.name, "_entry"); + /* + * We tolerate no failure path after event creation. It + * will stay invariant for the rest of the session. + */ + event[0] = lttng_kernel_event_create(&event_enabler->parent.parent, NULL, &event_pair); + WARN_ON_ONCE(IS_ERR(event[0])); + if (IS_ERR(event[0])) { + lttng_event_enabler_destroy(&event_enabler->parent.parent); + ret = PTR_ERR(event[0]); + goto event_error; + } + + strcpy(event_pair.name, event_param->name); + strcat(event_pair.name, "_exit"); + event[1] = lttng_kernel_event_create(&event_enabler->parent.parent, NULL, &event_pair); + WARN_ON_ONCE(IS_ERR(event[1])); + + lttng_event_enabler_destroy(&event_enabler->parent.parent); + if (IS_ERR(event[1])) { + ret = PTR_ERR(event[1]); + goto event_error; + } + priv = event[0]; + break; + } + case LTTNG_KERNEL_ABI_FUNCTION: lttng_fallthrough; case LTTNG_KERNEL_ABI_NOOP: @@ -2420,7 +2506,7 @@ int lttng_abi_create_event_notifier(struct file *event_notifier_group_file, ret = -ENOMEM; goto event_notifier_error; } - event = lttng_kernel_event_create(&event_notifier_enabler->parent, NULL); + event = lttng_kernel_event_create(&event_notifier_enabler->parent, NULL, NULL); WARN_ON_ONCE(IS_ERR(event)); lttng_event_enabler_destroy(&event_notifier_enabler->parent); if (IS_ERR(event)) { diff --git a/src/lttng-events.c b/src/lttng-events.c index bbf329da..97c123b2 100644 --- a/src/lttng-events.c +++ b/src/lttng-events.c @@ -1396,7 +1396,8 @@ int lttng_append_event_to_channel_map(struct lttng_event_enabler_common *event_e * Needs to be called with sessions mutex held. */ struct lttng_kernel_event_common *_lttng_kernel_event_create(struct lttng_event_enabler_common *event_enabler, - const struct lttng_kernel_event_desc *event_desc) + const struct lttng_kernel_event_desc *event_desc, + struct lttng_kernel_event_pair *event_pair) { char key_string[LTTNG_KEY_TOKEN_STRING_LEN_MAX] = { 0 }; struct lttng_event_ht *events_name_ht = lttng_get_events_name_ht_from_enabler(event_enabler); @@ -1410,9 +1411,11 @@ struct lttng_kernel_event_common *_lttng_kernel_event_create(struct lttng_event_ const char *event_name; int ret; - if (!lttng_kernel_event_id_available(event_enabler)) { - ret = -EMFILE; - goto full; + if (event_pair == NULL || event_pair->refcount == 0) { + if (!lttng_kernel_event_id_available(event_enabler)) { + ret = -EMFILE; + goto full; + } } switch (itype) { @@ -1424,12 +1427,14 @@ struct lttng_kernel_event_common *_lttng_kernel_event_create(struct lttng_event_ lttng_fallthrough; case LTTNG_KERNEL_ABI_UPROBE: lttng_fallthrough; - case LTTNG_KERNEL_ABI_KRETPROBE: - lttng_fallthrough; case LTTNG_KERNEL_ABI_SYSCALL: event_name = event_param->name; break; + case LTTNG_KERNEL_ABI_KRETPROBE: + event_name = event_pair->name; + break; + case LTTNG_KERNEL_ABI_FUNCTION: lttng_fallthrough; case LTTNG_KERNEL_ABI_NOOP: @@ -1506,62 +1511,38 @@ struct lttng_kernel_event_common *_lttng_kernel_event_create(struct lttng_event_ case LTTNG_KERNEL_ABI_KRETPROBE: { - struct lttng_kernel_event_common *event_return; - - /* kretprobe defines 2 events */ /* * Needs to be explicitly enabled after creation, since * we may want to apply filters. */ event->enabled = 0; event->priv->registered = 1; - - event_return = lttng_kernel_event_alloc(event_enabler, key_head, key_string); - if (!event) { + event->priv->desc = lttng_create_kretprobes_event_desc(event_name); + if (!event->priv->desc) { ret = -ENOMEM; - goto alloc_error; + goto register_error; } + event_pair->event[event_pair->refcount++] = event; - event_return->enabled = 0; - event_return->priv->registered = 1; - - /* - * Populate lttng_event structure before kretprobe registration. - */ - smp_wmb(); - ret = lttng_kretprobes_register(event_name, - event_param->u.kretprobe.symbol_name, - event_param->u.kretprobe.offset, - event_param->u.kretprobe.addr, - event, event_return); - if (ret) { - lttng_kernel_event_free(event_return); - ret = -EINVAL; - goto register_error; + /* kretprobe defines 2 events. */ + if (event_pair->refcount == 2) { + /* + * Populate lttng_event structure before kretprobe registration. + */ + smp_wmb(); + ret = lttng_kretprobes_register(event_param->u.kretprobe.symbol_name, + event_param->u.kretprobe.offset, + event_param->u.kretprobe.addr, + event_pair->event[0], event_pair->event[1]); + if (ret) { + ret = -EINVAL; + goto register_error; + } } - /* Take 2 refs on the module: one per event. */ ret = try_module_get(event->priv->desc->owner); WARN_ON_ONCE(!ret); - ret = try_module_get(event_return->priv->desc->owner); - WARN_ON_ONCE(!ret); - ret = _lttng_event_recorder_metadata_statedump(event_return); - WARN_ON_ONCE(ret > 0); - if (ret) { - lttng_kernel_event_free(event_return); - module_put(event_return->priv->desc->owner); - module_put(event->priv->desc->owner); - goto statedump_error; - } - list_add(&event_return->priv->node, event_list_head); - if (key_head) { - struct lttng_kernel_event_counter_private *event_return_counter_priv = - container_of(event_return->priv, struct lttng_kernel_event_counter_private, parent.parent); - hlist_add_head(&event_return_counter_priv->hlist_key_node, key_head); - } ret = lttng_append_event_to_channel_map(event_enabler, event, event_name); WARN_ON_ONCE(ret); - ret = lttng_append_event_to_channel_map(event_enabler, event_return, event_name); - WARN_ON_ONCE(ret); break; } @@ -1662,12 +1643,13 @@ full: } struct lttng_kernel_event_common *lttng_kernel_event_create(struct lttng_event_enabler_common *event_enabler, - const struct lttng_kernel_event_desc *event_desc) + const struct lttng_kernel_event_desc *event_desc, + struct lttng_kernel_event_pair *event_pair) { struct lttng_kernel_event_common *event; mutex_lock(&sessions_mutex); - event = _lttng_kernel_event_create(event_enabler, event_desc); + event = _lttng_kernel_event_create(event_enabler, event_desc, event_pair); mutex_unlock(&sessions_mutex); return event; } @@ -2378,7 +2360,7 @@ void lttng_event_enabler_create_tracepoint_events_if_missing(struct lttng_event_ /* * We need to create an event for this event probe. */ - event = _lttng_kernel_event_create(event_enabler, desc); + event = _lttng_kernel_event_create(event_enabler, desc, NULL); if (IS_ERR(event)) { /* Skip if already found. */ if (PTR_ERR(event) == -EEXIST) diff --git a/src/lttng-kretprobes.c b/src/lttng-kretprobes.c index b1f4f7f8..1b8bbb0a 100644 --- a/src/lttng-kretprobes.c +++ b/src/lttng-kretprobes.c @@ -152,50 +152,31 @@ static const struct lttng_kernel_tracepoint_class tp_class = { /* * Create event description */ -static -int lttng_create_kprobe_event(const char *name, struct lttng_kernel_event_common *event, - enum lttng_kretprobe_type type) +struct lttng_kernel_event_desc *lttng_create_kretprobes_event_desc(const char *name) { struct lttng_kernel_event_desc *desc; char *alloc_name; size_t name_len; - const char *suffix = NULL; - int ret; desc = kzalloc(sizeof(*desc), GFP_KERNEL); if (!desc) - return -ENOMEM; + return NULL; name_len = strlen(name); - switch (type) { - case EVENT_ENTRY: - suffix = "_entry"; - break; - case EVENT_EXIT: - suffix = "_exit"; - break; - } - name_len += strlen(suffix); alloc_name = kmalloc(name_len + 1, GFP_KERNEL); - if (!alloc_name) { - ret = -ENOMEM; + if (!alloc_name) goto error_str; - } strcpy(alloc_name, name); - strcat(alloc_name, suffix); desc->event_name = alloc_name; desc->tp_class = &tp_class; desc->owner = THIS_MODULE; - event->priv->desc = desc; - - return 0; + return desc; error_str: kfree(desc); - return ret; + return NULL; } -int lttng_kretprobes_register(const char *name, - const char *symbol_name, +int lttng_kretprobes_register(const char *symbol_name, uint64_t offset, uint64_t addr, struct lttng_kernel_event_common *event_entry, @@ -208,12 +189,6 @@ int lttng_kretprobes_register(const char *name, if (symbol_name[0] == '\0') symbol_name = NULL; - ret = lttng_create_kprobe_event(name, event_entry, EVENT_ENTRY); - if (ret) - goto error; - ret = lttng_create_kprobe_event(name, event_exit, EVENT_EXIT); - if (ret) - goto event_exit_error; lttng_krp = kzalloc(sizeof(*lttng_krp), GFP_KERNEL); if (!lttng_krp) goto krp_error; @@ -266,12 +241,6 @@ register_error: name_error: kfree(lttng_krp); krp_error: - kfree(event_exit->priv->desc->event_name); - kfree(event_exit->priv->desc); -event_exit_error: - kfree(event_entry->priv->desc->event_name); - kfree(event_entry->priv->desc); -error: return ret; } diff --git a/src/lttng-syscalls.c b/src/lttng-syscalls.c index 415cb57f..aef50c46 100644 --- a/src/lttng-syscalls.c +++ b/src/lttng-syscalls.c @@ -577,7 +577,7 @@ void lttng_syscall_event_enabler_create_event(struct lttng_event_enabler_common WARN_ON_ONCE(!event_recorder_enabler); if (!event_recorder_enabler) return; - event = _lttng_kernel_event_create(&event_recorder_enabler->parent.parent, desc); + event = _lttng_kernel_event_create(&event_recorder_enabler->parent.parent, desc, NULL); WARN_ON_ONCE(IS_ERR(event)); lttng_event_enabler_destroy(&event_recorder_enabler->parent.parent); if (IS_ERR(event)) { @@ -624,7 +624,7 @@ void lttng_syscall_event_enabler_create_event(struct lttng_event_enabler_common event_notifier_enabler = lttng_event_notifier_enabler_create(LTTNG_ENABLER_FORMAT_NAME, &event_notifier_param, syscall_event_notifier_enabler->group); WARN_ON_ONCE(!event_notifier_enabler); - event = _lttng_kernel_event_create(&event_notifier_enabler->parent, desc); + event = _lttng_kernel_event_create(&event_notifier_enabler->parent, desc, NULL); WARN_ON_ONCE(IS_ERR(event)); lttng_event_enabler_destroy(&event_notifier_enabler->parent); if (IS_ERR(event)) { @@ -669,7 +669,7 @@ void lttng_syscall_event_enabler_create_event(struct lttng_event_enabler_common WARN_ON_ONCE(!event_counter_enabler); if (!event_counter_enabler) return; - event = _lttng_kernel_event_create(&event_counter_enabler->parent.parent, desc); + event = _lttng_kernel_event_create(&event_counter_enabler->parent.parent, desc, NULL); lttng_event_enabler_destroy(&event_counter_enabler->parent.parent); if (IS_ERR(event)) { if (PTR_ERR(event) != -EEXIST) -- 2.34.1