From 352b58f55f53e7d11fb286ddc26a3bd0ecdd02f5 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Mon, 23 Mar 2020 17:09:18 -0400 Subject: [PATCH] sessiond: kernel triggers: add infrastructure to create event notifiers MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add the infrastructure to initialize the kernel tracer event notifier group and individual event notifiers from event rules issued from triggers. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I20127f655018260e45566d09d11c2852cd3b3f97 Depends-on: lttng-ust: I5a800fc92e588c2a6a0e26282b0ad5f31c044479 --- include/lttng/lttng-error.h | 1 + src/bin/lttng-sessiond/kernel.c | 543 +++++++++++++++++++++++++- src/bin/lttng-sessiond/kernel.h | 13 + src/bin/lttng-sessiond/main.c | 12 + src/bin/lttng-sessiond/trace-kernel.c | 272 ++++++++++++- src/bin/lttng-sessiond/trace-kernel.h | 22 ++ src/common/error.c | 1 + src/common/kernel-ctl/kernel-ctl.c | 2 +- src/common/kernel-ctl/kernel-ctl.h | 2 +- 9 files changed, 854 insertions(+), 14 deletions(-) diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index 9170948a1..db0f5e0f7 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -174,6 +174,7 @@ enum lttng_error_code { LTTNG_ERR_GROUP_NOT_FOUND = 161, /* Group not found. */ LTTNG_ERR_UNSUPPORTED_DOMAIN = 162, /* Unsupported domain used. */ LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY = 163, /* Operation does not apply to the process attribute tracker's tracking policy */ + LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD = 164, /* Error initializing event notifier group notification file descriptor */ /* MUST be last element of the manually-assigned section of the enum */ LTTNG_ERR_NR, diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index 1fc36e33e..4bb8045e7 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -25,8 +26,17 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + #include "lttng-sessiond.h" #include "lttng-syscall.h" +#include "condition-internal.h" #include "consumer.h" #include "kernel.h" #include "kernel-consumer.h" @@ -35,6 +45,7 @@ #include "rotate.h" #include "modprobe.h" #include "tracker.h" +#include "notification-thread-commands.h" /* * Key used to reference a channel between the sessiond and the consumer. This @@ -45,9 +56,10 @@ static uint64_t next_kernel_channel_key; static const char *module_proc_lttng = "/proc/lttng"; static int kernel_tracer_fd = -1; +static int kernel_tracer_event_notifier_group_fd = -1; +static int kernel_tracer_event_notifier_group_notification_fd = -1; +static struct cds_lfht *kernel_token_to_event_notifier_rule_ht; -#include -#include /* * Add context on a kernel channel. * @@ -219,6 +231,51 @@ error: return -1; } +/* + * Create a kernel event notifier group, register it to the kernel tracer and + * add it to the kernel session. + */ +static int kernel_create_event_notifier_group(int *event_notifier_group_fd) +{ + int ret; + int local_fd = -1; + + assert(event_notifier_group_fd); + + /* Kernel event notifier group creation. */ + ret = kernctl_create_event_notifier_group(kernel_tracer_fd); + if (ret < 0) { + PERROR("Failed to create kernel event notifier group"); + ret = -1; + goto error; + } + + local_fd = ret; + + /* Prevent fd duplication after execlp(). */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("Failed to set FD_CLOEXEC on kernel event notifier group file descriptor: fd = %d", + local_fd); + goto error; + } + + DBG("Created kernel event notifier group: fd = %d", local_fd); + *event_notifier_group_fd = local_fd; + local_fd = -1; + ret = 0; +error: + if (local_fd >= 0) { + ret = close(local_fd); + if (ret) { + PERROR("Failed to close kernel event notifier group file descriptor: fd = %d", + local_fd); + } + } + + return ret; +} + /* * Compute the offset of the instrumentation byte in the binary based on the * function probe location using the ELF lookup method. @@ -465,6 +522,44 @@ end: return ret; } +/* + * Extract the offsets of the instrumentation point for the different look-up + * methods. + */ +static int userspace_probe_event_rule_add_callsites( + const struct lttng_event_rule *rule, + const struct lttng_credentials *creds, + int fd) +{ + int ret; + enum lttng_event_rule_status status; + enum lttng_event_rule_type event_rule_type; + const struct lttng_userspace_probe_location *location = NULL; + + assert(rule); + assert(creds); + + event_rule_type = lttng_event_rule_get_type(rule); + assert(event_rule_type == LTTNG_EVENT_RULE_TYPE_UPROBE); + + status = lttng_event_rule_uprobe_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) { + ret = -1; + goto end; + } + + ret = userspace_probe_add_callsite(location, + lttng_credentials_get_uid(creds), + lttng_credentials_get_gid(creds), fd); + if (ret) { + WARN("Failed to add callsite to user space probe object: fd = %d", + fd); + } + +end: + return ret; +} + /* * Create a kernel event, enable it to the kernel tracer and add it to the * channel event list of the kernel session. @@ -692,6 +787,42 @@ error: return ret; } +/* + * Disable a kernel event notifier. + */ +static +int kernel_disable_event_notifier_rule(struct ltt_kernel_event_notifier_rule *event) +{ + int ret; + + assert(event); + + rcu_read_lock(); + cds_lfht_del(kernel_token_to_event_notifier_rule_ht, &event->ht_node); + rcu_read_unlock(); + + ret = kernctl_disable(event->fd); + if (ret < 0) { + switch (-ret) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("Failed to disable kernel event notifier: fd = %d, token = %" PRIu64, + event->fd, event->token); + break; + } + goto error; + } + + event->enabled = 0; + DBG("Disabled kernel event notifier: fd = %d, token = %" PRIu64, + event->fd, event->token); + +error: + return ret; +} + static struct process_attr_tracker *_kernel_get_process_attr_tracker( struct ltt_kernel_session *session, @@ -1841,35 +1972,81 @@ int init_kernel_tracer(void) if (ret < 0) { goto error_modules; } - if (ret < 1) { WARN("Kernel tracer does not support buffer monitoring. " "The monitoring timer of channels in the kernel domain " "will be set to 0 (disabled)."); } - DBG("Kernel tracer fd %d", kernel_tracer_fd); + ret = kernel_supports_event_notifiers(); + if (ret < 0) { + ERR("Failed to check for kernel tracer event notifier support"); + goto error_modules; + } + ret = kernel_create_event_notifier_group(&kernel_tracer_event_notifier_group_fd); + if (ret < 0) { + /* This is not fatal. */ + WARN("Failed to create kernel event notifier group"); + kernel_tracer_event_notifier_group_fd = -1; + } else { + const enum lttng_error_code error_code_ret = + kernel_create_event_notifier_group_notification_fd( + &kernel_tracer_event_notifier_group_notification_fd); + + if (error_code_ret != LTTNG_OK) { + goto error_modules; + } + + kernel_token_to_event_notifier_rule_ht = cds_lfht_new( + DEFAULT_HT_SIZE, 1, 0, + CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, + NULL); + if (!kernel_token_to_event_notifier_rule_ht) { + goto error_token_ht; + } + } + + DBG("Kernel tracer initialized: kernel tracer fd = %d, event notifier group fd = %d, event notifier group notification fd = %d", + kernel_tracer_fd, kernel_tracer_event_notifier_group_fd, + kernel_tracer_event_notifier_group_notification_fd); ret = syscall_init_table(kernel_tracer_fd); if (ret < 0) { ERR("Unable to populate syscall table. Syscall tracing won't " "work for this session daemon."); } + return 0; error_version: modprobe_remove_lttng_control(); ret = close(kernel_tracer_fd); if (ret) { - PERROR("close"); + PERROR("Failed to close kernel tracer file descriptor: fd = %d", + kernel_tracer_fd); } kernel_tracer_fd = -1; return LTTNG_ERR_KERN_VERSION; + +error_token_ht: + ret = close(kernel_tracer_event_notifier_group_notification_fd); + if (ret) { + PERROR("Failed to close kernel tracer event notifier group notification file descriptor: fd = %d", + kernel_tracer_event_notifier_group_notification_fd); + } + error_modules: + ret = close(kernel_tracer_event_notifier_group_fd); + if (ret) { + PERROR("Failed to close kernel tracer event notifier group file descriptor: fd = %d", + kernel_tracer_event_notifier_group_fd); + } + ret = close(kernel_tracer_fd); if (ret) { - PERROR("close"); + PERROR("Failed to close kernel tracer file descriptor: fd = %d", + kernel_tracer_fd); } error_open: @@ -1889,15 +2066,61 @@ LTTNG_HIDDEN void cleanup_kernel_tracer(void) { int ret; + struct cds_lfht_iter iter; + struct ltt_kernel_event_notifier_rule *rule = NULL; + + rcu_read_lock(); + cds_lfht_for_each_entry(kernel_token_to_event_notifier_rule_ht, &iter, rule, ht_node) { + kernel_disable_event_notifier_rule(rule); + trace_kernel_destroy_event_notifier_rule(rule); + } + rcu_read_unlock(); + + DBG2("Closing kernel event notifier group notification file descriptor"); + if (kernel_tracer_event_notifier_group_notification_fd >= 0) { + ret = notification_thread_command_remove_tracer_event_source( + notification_thread_handle, + kernel_tracer_event_notifier_group_notification_fd); + if (ret != LTTNG_OK) { + ERR("Failed to remove kernel event notifier notification from notification thread"); + } + + ret = close(kernel_tracer_event_notifier_group_notification_fd); + if (ret) { + PERROR("Failed to close kernel event notifier group notification file descriptor: fd = %d", + kernel_tracer_event_notifier_group_notification_fd); + } + + kernel_tracer_event_notifier_group_notification_fd = -1; + } + + if (kernel_token_to_event_notifier_rule_ht) { + ret = cds_lfht_destroy(kernel_token_to_event_notifier_rule_ht, NULL); + assert(ret == 0); + } + + DBG2("Closing kernel event notifier group file descriptor"); + if (kernel_tracer_event_notifier_group_fd >= 0) { + ret = close(kernel_tracer_event_notifier_group_fd); + if (ret) { + PERROR("Failed to close kernel event notifier group file descriptor: fd = %d", + kernel_tracer_event_notifier_group_fd); + } + + kernel_tracer_event_notifier_group_fd = -1; + } DBG2("Closing kernel fd"); if (kernel_tracer_fd >= 0) { ret = close(kernel_tracer_fd); if (ret) { - PERROR("close"); + PERROR("Failed to close kernel tracer file descriptor: fd = %d", + kernel_tracer_fd); } + kernel_tracer_fd = -1; } + DBG("Unloading kernel modules"); modprobe_remove_lttng_all(); free(syscall_table); @@ -1988,3 +2211,309 @@ end: rcu_read_unlock(); return status; } + +enum lttng_error_code kernel_create_event_notifier_group_notification_fd( + int *event_notifier_group_notification_fd) +{ + int local_fd = -1, ret; + enum lttng_error_code error_code_ret; + + assert(event_notifier_group_notification_fd); + + ret = kernctl_create_event_notifier_group_notification_fd( + kernel_tracer_event_notifier_group_fd); + if (ret < 0) { + PERROR("Failed to create kernel event notifier group notification file descriptor"); + error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD; + goto error; + } + + local_fd = ret; + + /* Prevent fd duplication after execlp(). */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("Failed to set FD_CLOEXEC on kernel event notifier group notification file descriptor: fd = %d", + local_fd); + error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD; + goto error; + } + + DBG("Created kernel notifier group notification file descriptor: fd = %d", + local_fd); + error_code_ret = LTTNG_OK; + *event_notifier_group_notification_fd = local_fd; + local_fd = -1; + +error: + if (local_fd >= 0) { + ret = close(local_fd); + if (ret) { + PERROR("Failed to close kernel event notifier group notification file descriptor: fd = %d", + local_fd); + } + } + + return error_code_ret; +} + +enum lttng_error_code kernel_destroy_event_notifier_group_notification_fd( + int event_notifier_group_notification_fd) +{ + enum lttng_error_code ret_code = LTTNG_OK; + + DBG("Closing event notifier group notification file descriptor: fd = %d", + event_notifier_group_notification_fd); + if (event_notifier_group_notification_fd >= 0) { + const int ret = close(event_notifier_group_notification_fd); + if (ret) { + PERROR("Failed to close event notifier group notification file descriptor: fd = %d", + event_notifier_group_notification_fd); + } + } + + return ret_code; +} + +static +unsigned long hash_trigger(const struct lttng_trigger *trigger) +{ + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + + return lttng_condition_hash(condition); +} + +static +int match_trigger(struct cds_lfht_node *node, const void *key) +{ + const struct ltt_kernel_event_notifier_rule *event_notifier_rule; + const struct lttng_trigger *trigger = key; + + event_notifier_rule = caa_container_of(node, + const struct ltt_kernel_event_notifier_rule, ht_node); + + return lttng_trigger_is_equal(trigger, event_notifier_rule->trigger); +} + +static enum lttng_error_code kernel_create_event_notifier_rule( + struct lttng_trigger *trigger, + const struct lttng_credentials *creds, uint64_t token) +{ + int err, fd, ret = 0; + enum lttng_error_code error_code_ret; + enum lttng_condition_status condition_status; + enum lttng_condition_type condition_type; + enum lttng_event_rule_type event_rule_type; + struct ltt_kernel_event_notifier_rule *event_notifier_rule; + struct lttng_kernel_event_notifier kernel_event_notifier = {}; + const struct lttng_condition *condition = NULL; + const struct lttng_event_rule *event_rule = NULL; + + assert(trigger); + + condition = lttng_trigger_get_const_condition(trigger); + assert(condition); + + condition_type = lttng_condition_get_type(condition); + assert(condition_type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + /* Does not acquire a reference. */ + condition_status = lttng_condition_event_rule_get_rule( + condition, &event_rule); + assert(condition_status == LTTNG_CONDITION_STATUS_OK); + assert(event_rule); + + event_rule_type = lttng_event_rule_get_type(event_rule); + assert(event_rule_type != LTTNG_EVENT_RULE_TYPE_UNKNOWN); + + error_code_ret = trace_kernel_create_event_notifier_rule(trigger, token, + &event_notifier_rule); + if (error_code_ret != LTTNG_OK) { + goto error; + } + + error_code_ret = trace_kernel_init_event_notifier_from_event_rule( + event_rule, &kernel_event_notifier); + if (error_code_ret != LTTNG_OK) { + goto error; + } + + kernel_event_notifier.event.token = event_notifier_rule->token; + + fd = kernctl_create_event_notifier( + kernel_tracer_event_notifier_group_fd, + &kernel_event_notifier); + if (fd < 0) { + switch (-fd) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + case ENOSYS: + WARN("Failed to create kernel event notifier: not notifier type not implemented"); + error_code_ret = LTTNG_ERR_KERN_EVENT_ENOSYS; + break; + case ENOENT: + WARN("Failed to create kernel event notifier: not found: name = '%s'", + kernel_event_notifier.event.name); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + default: + PERROR("Failed to create kernel event notifier: error code = %d, name = '%s'", + fd, kernel_event_notifier.event.name); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + } + goto free_event; + } + + event_notifier_rule->fd = fd; + /* Prevent fd duplication after execlp(). */ + err = fcntl(event_notifier_rule->fd, F_SETFD, FD_CLOEXEC); + if (err < 0) { + PERROR("Failed to set FD_CLOEXEC on kernel event notifier file descriptor: fd = %d", + fd); + error_code_ret = LTTNG_ERR_FATAL; + goto set_cloexec_error; + } + + if (event_notifier_rule->filter) { + err = kernctl_filter(event_notifier_rule->fd, event_notifier_rule->filter); + if (err < 0) { + switch (-err) { + case ENOMEM: + error_code_ret = LTTNG_ERR_FILTER_NOMEM; + break; + default: + error_code_ret = LTTNG_ERR_FILTER_INVAL; + break; + } + goto filter_error; + } + } + + if (lttng_event_rule_get_type(event_rule) == + LTTNG_EVENT_RULE_TYPE_UPROBE) { + ret = userspace_probe_event_rule_add_callsites( + event_rule, creds, event_notifier_rule->fd); + if (ret) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto add_callsite_error; + } + } + + err = kernctl_enable(event_notifier_rule->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel event notifier"); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto enable_error; + } + + /* Add trigger to kernel token mapping in the hash table. */ + rcu_read_lock(); + cds_lfht_add(kernel_token_to_event_notifier_rule_ht, hash_trigger(trigger), + &event_notifier_rule->ht_node); + rcu_read_unlock(); + + DBG("Created kernel event notifier: name = '%s', fd = %d", + kernel_event_notifier.event.name, + event_notifier_rule->fd); + + return LTTNG_OK; + +add_callsite_error: +enable_error: +set_cloexec_error: +filter_error: + { + const int close_ret = close(event_notifier_rule->fd); + + if (close_ret) { + PERROR("Failed to close kernel event notifier file descriptor: fd = %d", + event_notifier_rule->fd); + } + } +free_event: + free(event_notifier_rule); +error: + return error_code_ret; +} + +enum lttng_error_code kernel_register_event_notifier( + struct lttng_trigger *trigger, + const struct lttng_credentials *cmd_creds) +{ + enum lttng_error_code ret; + enum lttng_condition_status status; + enum lttng_domain_type domain_type; + const struct lttng_event_rule *event_rule; + const struct lttng_condition *const condition = + lttng_trigger_get_const_condition(trigger); + const uint64_t token = lttng_trigger_get_tracer_token(trigger); + + assert(condition); + + /* Does not acquire a reference to the event rule. */ + status = lttng_condition_event_rule_get_rule( + condition, &event_rule); + assert(status == LTTNG_CONDITION_STATUS_OK); + + domain_type = lttng_event_rule_get_domain_type(event_rule); + assert(domain_type == LTTNG_DOMAIN_KERNEL); + + ret = kernel_create_event_notifier_rule(trigger, cmd_creds, token); + if (ret != LTTNG_OK) { + ERR("Failed to create kernel trigger"); + } + + return ret; +} + +enum lttng_error_code kernel_unregister_event_notifier( + const struct lttng_trigger *trigger) +{ + struct ltt_kernel_event_notifier_rule *token_event_rule_element; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + enum lttng_error_code error_code_ret; + int ret; + + rcu_read_lock(); + + cds_lfht_lookup(kernel_token_to_event_notifier_rule_ht, + hash_trigger(trigger), match_trigger, trigger, &iter); + + node = cds_lfht_iter_get_node(&iter); + if (!node) { + error_code_ret = LTTNG_ERR_TRIGGER_NOT_FOUND; + goto error; + } + + token_event_rule_element = caa_container_of(node, + struct ltt_kernel_event_notifier_rule, ht_node); + + ret = kernel_disable_event_notifier_rule(token_event_rule_element); + if (ret) { + error_code_ret = LTTNG_ERR_FATAL; + goto error; + } + + trace_kernel_destroy_event_notifier_rule(token_event_rule_element); + error_code_ret = LTTNG_OK; + +error: + rcu_read_unlock(); + + return error_code_ret; +} + +int kernel_get_notification_fd(void) +{ + return kernel_tracer_event_notifier_group_notification_fd; +} diff --git a/src/bin/lttng-sessiond/kernel.h b/src/bin/lttng-sessiond/kernel.h index 1adf69253..db05cfb44 100644 --- a/src/bin/lttng-sessiond/kernel.h +++ b/src/bin/lttng-sessiond/kernel.h @@ -82,4 +82,17 @@ bool kernel_tracer_is_initialized(void); enum lttng_error_code kernel_create_channel_subdirectories( const struct ltt_kernel_session *ksess); +enum lttng_error_code kernel_create_event_notifier_group_notification_fd( + int *event_notifier_group_notification_fd); +enum lttng_error_code kernel_destroy_event_notifier_group_notification_fd( + int event_notifier_group_notification_fd); + +enum lttng_error_code kernel_register_event_notifier( + struct lttng_trigger *trigger, + const struct lttng_credentials *cmd_creds); +enum lttng_error_code kernel_unregister_event_notifier( + const struct lttng_trigger *trigger); + +int kernel_get_notification_fd(void); + #endif /* _LTT_KERNEL_CTL_H */ diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 9cc15d720..e646a6de5 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -1726,6 +1726,18 @@ int main(int argc, char **argv) retval = -1; goto stop_threads; } + + if (kernel_get_notification_fd() >= 0) { + ret = notification_thread_command_add_tracer_event_source( + notification_thread_handle, + kernel_get_notification_fd(), + LTTNG_DOMAIN_KERNEL); + if (ret != LTTNG_OK) { + ERR("Failed to add kernel trigger event source to notification thread"); + retval = -1; + goto stop_threads; + } + } } /* Load sessions. */ diff --git a/src/bin/lttng-sessiond/trace-kernel.c b/src/bin/lttng-sessiond/trace-kernel.c index a8154a3cc..6e8a8b2de 100644 --- a/src/bin/lttng-sessiond/trace-kernel.c +++ b/src/bin/lttng-sessiond/trace-kernel.c @@ -13,9 +13,18 @@ #include #include +#include #include #include - +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -369,9 +378,6 @@ enum lttng_error_code trace_kernel_create_event( */ attr->instrumentation = LTTNG_KERNEL_UPROBE; - /* - * Only the elf lookup method is supported at the moment. - */ lookup = lttng_userspace_probe_location_get_lookup_method( location); if (!lookup) { @@ -471,6 +477,230 @@ error: return ret; } +/* + * Allocate and initialize a kernel token event rule. + * + * Return pointer to structure or NULL. + */ +enum lttng_error_code trace_kernel_create_event_notifier_rule( + struct lttng_trigger *trigger, + uint64_t token, + struct ltt_kernel_event_notifier_rule **event_notifier_rule) +{ + enum lttng_error_code ret = LTTNG_OK; + enum lttng_condition_type condition_type; + enum lttng_event_rule_type event_rule_type; + enum lttng_condition_status condition_status; + struct ltt_kernel_event_notifier_rule *local_kernel_token_event_rule; + const struct lttng_condition *condition = NULL; + const struct lttng_event_rule *event_rule = NULL; + + assert(event_notifier_rule); + + condition = lttng_trigger_get_condition(trigger); + assert(condition); + + condition_type = lttng_condition_get_type(condition); + assert(condition_type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + condition_status = lttng_condition_event_rule_get_rule( + condition, &event_rule); + assert(condition_status == LTTNG_CONDITION_STATUS_OK); + assert(event_rule); + + event_rule_type = lttng_event_rule_get_type(event_rule); + assert(event_rule_type != LTTNG_EVENT_RULE_TYPE_UNKNOWN); + + local_kernel_token_event_rule = + zmalloc(sizeof(struct ltt_kernel_event_notifier_rule)); + if (local_kernel_token_event_rule == NULL) { + PERROR("Failed to allocate ltt_kernel_token_event_rule structure"); + ret = LTTNG_ERR_NOMEM; + goto error; + } + + local_kernel_token_event_rule->fd = -1; + local_kernel_token_event_rule->enabled = 1; + local_kernel_token_event_rule->token = token; + + /* Get the reference of the event rule. */ + lttng_trigger_get(trigger); + + local_kernel_token_event_rule->trigger = trigger; + /* The event rule still owns the filter and bytecode. */ + local_kernel_token_event_rule->filter = + lttng_event_rule_get_filter_bytecode(event_rule); + + DBG3("Created kernel event notifier rule: token = %" PRIu64, + local_kernel_token_event_rule->token); +error: + *event_notifier_rule = local_kernel_token_event_rule; + return ret; +} + +/* + * Initialize a kernel trigger from an event rule. + */ +enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( + const struct lttng_event_rule *rule, + struct lttng_kernel_event_notifier *kernel_event_notifier) +{ + enum lttng_error_code ret; + const char *name; + + switch (lttng_event_rule_get_type(rule)) { + case LTTNG_EVENT_RULE_TYPE_KPROBE: + { + uint64_t address = 0, offset = 0; + const char *symbol_name = NULL; + const struct lttng_kernel_probe_location *location = NULL; + enum lttng_kernel_probe_location_status k_status; + enum lttng_event_rule_status status; + + status = lttng_event_rule_kprobe_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + switch (lttng_kernel_probe_location_get_type(location)) { + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: + { + k_status = lttng_kernel_probe_location_address_get_address( + location, &address); + assert(k_status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK); + break; + } + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: + { + k_status = lttng_kernel_probe_location_symbol_get_offset( + location, &offset); + assert(k_status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK); + symbol_name = lttng_kernel_probe_location_symbol_get_name( + location); + break; + } + default: + abort(); + } + + kernel_event_notifier->event.instrumentation = LTTNG_KERNEL_KPROBE; + kernel_event_notifier->event.u.kprobe.addr = address; + kernel_event_notifier->event.u.kprobe.offset = offset; + if (symbol_name) { + const int copy_ret = lttng_strncpy( + kernel_event_notifier->event.u.kprobe.symbol_name, + symbol_name, LTTNG_KERNEL_SYM_NAME_LEN); + + if (copy_ret) { + ret = LTTNG_ERR_INVALID; + goto error; + } + } + + kernel_event_notifier->event.u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + status = lttng_event_rule_kprobe_get_name(rule, &name); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + ret = LTTNG_OK; + break; + } + case LTTNG_EVENT_RULE_TYPE_UPROBE: + { + const struct lttng_userspace_probe_location* location = NULL; + const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; + enum lttng_event_rule_status status; + + status = lttng_event_rule_uprobe_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + kernel_event_notifier->event.instrumentation = LTTNG_KERNEL_UPROBE; + + lookup = lttng_userspace_probe_location_get_lookup_method( + location); + if (!lookup) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * From the kernel tracer's perspective, all userspace probe + * event types are all the same: a file and an offset. + */ + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + /* Get the file descriptor on the target binary. */ + kernel_event_notifier->event.u.uprobe.fd = + lttng_userspace_probe_location_function_get_binary_fd(location); + + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + /* Get the file descriptor on the target binary. */ + kernel_event_notifier->event.u.uprobe.fd = + lttng_userspace_probe_location_tracepoint_get_binary_fd(location); + break; + default: + abort(); + } + + status = lttng_event_rule_uprobe_get_name(rule, &name); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + ret = LTTNG_OK; + break; + } + case LTTNG_EVENT_RULE_TYPE_TRACEPOINT: + { + const enum lttng_domain_type domain = + lttng_event_rule_get_domain_type(rule); + const enum lttng_event_rule_status status = + lttng_event_rule_tracepoint_get_pattern( + rule, &name); + + assert(domain == LTTNG_DOMAIN_KERNEL); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + kernel_event_notifier->event.instrumentation = + LTTNG_KERNEL_TRACEPOINT; + + ret = LTTNG_OK; + break; + } + case LTTNG_EVENT_RULE_TYPE_SYSCALL: + { + const enum lttng_event_rule_status status = + lttng_event_rule_syscall_get_pattern( + rule, &name); + + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + + kernel_event_notifier->event.instrumentation = + LTTNG_KERNEL_SYSCALL; + kernel_event_notifier->event.u.syscall.abi = + LTTNG_KERNEL_SYSCALL_ABI_ALL; + kernel_event_notifier->event.u.syscall.entryexit = + LTTNG_KERNEL_SYSCALL_ENTRY; + kernel_event_notifier->event.u.syscall.match = + LTTNG_KERNEL_SYSCALL_MATCH_NAME; + ret = LTTNG_OK; + break; + } + case LTTNG_EVENT_RULE_TYPE_KRETPROBE: + default: + abort(); + break; + } + + ret = lttng_strncpy(kernel_event_notifier->event.name, name, + LTTNG_KERNEL_SYM_NAME_LEN); + if (ret) { + ret = LTTNG_ERR_INVALID; + goto error; + } + +error: + return ret; +} /* * Allocate and initialize a kernel metadata. * @@ -613,7 +843,7 @@ void trace_kernel_destroy_event(struct ltt_kernel_event *event) PERROR("close"); } } else { - DBG("[trace] Tearing down event (no associated fd)"); + DBG("[trace] Tearing down event (no associated file descriptor)"); } /* Remove from event list */ @@ -626,6 +856,38 @@ void trace_kernel_destroy_event(struct ltt_kernel_event *event) free(event); } +/* + * Cleanup kernel event structure. + */ +static void free_token_event_rule_rcu(struct rcu_head *rcu_node) +{ + struct ltt_kernel_event_notifier_rule *rule = caa_container_of(rcu_node, + struct ltt_kernel_event_notifier_rule, rcu_node); + + free(rule); +} + +void trace_kernel_destroy_event_notifier_rule( + struct ltt_kernel_event_notifier_rule *event) +{ + assert(event); + + if (event->fd >= 0) { + const int ret = close(event->fd); + + DBG("Closing kernel event notifier rule file descriptor: fd = %d", + event->fd); + if (ret) { + PERROR("Failed to close kernel event notifier file descriptor: fd = %d", + event->fd); + } + } else { + DBG("Destroying kernel event notifier rule (no associated file descriptor)"); + } + + lttng_trigger_put(event->trigger); + call_rcu(&event->rcu_node, free_token_event_rule_rcu); +} /* * Cleanup kernel context structure. */ diff --git a/src/bin/lttng-sessiond/trace-kernel.h b/src/bin/lttng-sessiond/trace-kernel.h index dd6f21edf..4b564ee4d 100644 --- a/src/bin/lttng-sessiond/trace-kernel.h +++ b/src/bin/lttng-sessiond/trace-kernel.h @@ -52,6 +52,20 @@ struct ltt_kernel_event { struct lttng_userspace_probe_location *userspace_probe_location; }; +/* Kernel event */ +struct ltt_kernel_event_notifier_rule { + int fd; + int enabled; + enum lttng_event_type type; + struct lttng_trigger *trigger; + uint64_t token; + const struct lttng_filter_bytecode *filter; + struct lttng_userspace_probe_location *userspace_probe_location; + struct cds_lfht_node ht_node; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; +}; + /* Kernel channel */ struct ltt_kernel_channel { int fd; @@ -150,8 +164,15 @@ struct ltt_kernel_stream *trace_kernel_create_stream(const char *name, unsigned int count); struct ltt_kernel_context *trace_kernel_create_context( struct lttng_kernel_context *ctx); +enum lttng_error_code trace_kernel_create_event_notifier_rule( + struct lttng_trigger *trigger, + uint64_t token, + struct ltt_kernel_event_notifier_rule **event_notifier_rule); struct ltt_kernel_context *trace_kernel_copy_context( struct ltt_kernel_context *ctx); +enum lttng_error_code trace_kernel_init_event_notifier_from_event_rule( + const struct lttng_event_rule *rule, + struct lttng_kernel_event_notifier *kernel_event_notifier); /* * Destroy functions free() the data structure and remove from linked list if @@ -163,6 +184,7 @@ void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel); void trace_kernel_destroy_event(struct ltt_kernel_event *event); void trace_kernel_destroy_stream(struct ltt_kernel_stream *stream); void trace_kernel_destroy_context(struct ltt_kernel_context *ctx); +void trace_kernel_destroy_event_notifier_rule(struct ltt_kernel_event_notifier_rule *rule); void trace_kernel_free_session(struct ltt_kernel_session *session); #endif /* _LTT_TRACE_KERNEL_H */ diff --git a/src/common/error.c b/src/common/error.c index 7ededdb73..b8dabdf6e 100644 --- a/src/common/error.c +++ b/src/common/error.c @@ -239,6 +239,7 @@ static const char *error_string_array[] = { [ ERROR_INDEX(LTTNG_ERR_GROUP_NOT_FOUND) ] = "Group not found", [ ERROR_INDEX(LTTNG_ERR_UNSUPPORTED_DOMAIN) ] = "Unsupported domain used", [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY) ] = "Operation does not apply to the process attribute tracker's tracking policy", + [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD) ] = "Failed to create an event notifier group notification file descriptor", /* Last element */ [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code" diff --git a/src/common/kernel-ctl/kernel-ctl.c b/src/common/kernel-ctl/kernel-ctl.c index f7b92f045..f5efaf328 100644 --- a/src/common/kernel-ctl/kernel-ctl.c +++ b/src/common/kernel-ctl/kernel-ctl.c @@ -437,7 +437,7 @@ int kernctl_create_event_notifier(int group_fd, LTTNG_KERNEL_EVENT_NOTIFIER_CREATE, event_notifier); } -int kernctl_filter(int fd, struct lttng_filter_bytecode *filter) +int kernctl_filter(int fd, const struct lttng_filter_bytecode *filter) { struct lttng_kernel_filter_bytecode *kb; uint32_t len; diff --git a/src/common/kernel-ctl/kernel-ctl.h b/src/common/kernel-ctl/kernel-ctl.h index b73a1d68e..5668468e6 100644 --- a/src/common/kernel-ctl/kernel-ctl.h +++ b/src/common/kernel-ctl/kernel-ctl.h @@ -36,7 +36,7 @@ int kernctl_create_event_notifier(int fd, const struct lttng_kernel_event_notifier *event_notifier); /* Apply on event file descriptor. */ -int kernctl_filter(int fd, struct lttng_filter_bytecode *filter); +int kernctl_filter(int fd, const struct lttng_filter_bytecode *filter); int kernctl_add_callsite(int fd, struct lttng_kernel_event_callsite *callsite); int kernctl_tracepoint_list(int fd); -- 2.34.1