X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fkernel.c;h=25d744d1107ef11cf342f21990e0b4bffbac6fa3;hb=refs%2Fheads%2Fsow-2020-0002-rev2;hp=1fc36e33e9b45b1c7b941c3261f7c8ad0c07dfe9;hpb=0a398bc4ce5e8f150e71cc7196162e677d571821;p=lttng-tools.git diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index 1fc36e33e..25d744d11 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,16 +26,34 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "event-notifier-error-accounting.h" #include "lttng-sessiond.h" #include "lttng-syscall.h" +#include "condition-internal.h" #include "consumer.h" #include "kernel.h" #include "kernel-consumer.h" #include "kern-modules.h" +#include "map.h" +#include "sessiond-config.h" #include "utils.h" #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 +64,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 +239,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 +530,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_USERSPACE_PROBE); + + status = lttng_event_rule_userspace_probe_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. @@ -473,7 +576,7 @@ end: int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel, char *filter_expression, - struct lttng_filter_bytecode *filter) + struct lttng_bytecode *filter) { int err, fd; enum lttng_error_code ret; @@ -692,6 +795,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 +1980,90 @@ 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 { + enum event_notifier_error_accounting_status error_accounting_status; + 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; + } + + error_accounting_status = event_notifier_error_accounting_register_kernel( + kernel_tracer_event_notifier_group_fd); + if (error_accounting_status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) { + ERR("Error initializing event notifier error accounting for kernel tracer."); + error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING; + 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: @@ -1888,18 +2082,54 @@ error: LTTNG_HIDDEN void cleanup_kernel_tracer(void) { - int ret; + DBG2("Closing kernel event notifier group notification file descriptor"); + if (kernel_tracer_event_notifier_group_notification_fd >= 0) { + int 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) { + const int 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) { + const int 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); + const int 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); } @@ -1908,13 +2138,8 @@ bool kernel_tracer_is_initialized(void) { return kernel_tracer_fd >= 0; } - -/* - * Clear a kernel session. - * - * Return LTTNG_OK on success or else an LTTng error code. - */ -enum lttng_error_code kernel_clear_session(struct ltt_session *session) +static +enum lttng_error_code kernel_clear_session_channels(struct ltt_session *session) { int ret; enum lttng_error_code status = LTTNG_OK; @@ -1925,9 +2150,6 @@ enum lttng_error_code kernel_clear_session(struct ltt_session *session) assert(ksess); assert(ksess->consumer); - DBG("Clear kernel session %s (session %" PRIu64 ")", - session->name, session->id); - rcu_read_lock(); if (ksess->active) { @@ -1974,6 +2196,7 @@ enum lttng_error_code kernel_clear_session(struct ltt_session *session) } } + goto end; error: switch (-ret) { @@ -1988,3 +2211,834 @@ end: rcu_read_unlock(); return status; } + +static +enum lttng_error_code kernel_map_clear_all(struct ltt_kernel_map *map) +{ + enum lttng_error_code status; + uint64_t descr_count, i; + int ret; + + assert(map); + + ret = kernctl_counter_map_descriptor_count(map->fd, &descr_count); + if (ret) { + ERR("Error getting map descriptor count"); + status = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + for(i = 0; i < descr_count; i++) { + struct lttng_kernel_counter_map_descriptor descriptor = {0}; + struct lttng_kernel_counter_clear counter_clear = {0}; + + descriptor.descriptor_index = i; + + ret = kernctl_counter_map_descriptor(map->fd, &descriptor); + if (ret) { + ERR("Error getting map descriptor %"PRIu64, i); + status = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + counter_clear.index.number_dimensions = 1; + counter_clear.index.dimension_indexes[0] = descriptor.array_index; + + ret = kernctl_counter_clear(map->fd, &counter_clear); + if (ret) { + ERR("Error clearing value of map descriptor %"PRIu64, i); + status = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + } + + status = LTTNG_OK; +end: + return status; +} + +static +enum lttng_error_code kernel_clear_session_maps(struct ltt_session *session) +{ + enum lttng_error_code status = LTTNG_OK; + struct ltt_kernel_map *map; + struct ltt_kernel_session *ksess = session->kernel_session; + + assert(ksess); + + cds_list_for_each_entry(map, &ksess->map_list.head, list) { + DBG("Clear kernel map %" PRIu64 ", session %s", + map->key, session->name); + status = kernel_map_clear_all(map); + if (status != LTTNG_OK) { + ERR("Clearing all values of map"); + goto end; + } + } + +end: + return status; +} + +/* + * Clear a kernel session. + * + * Return LTTNG_OK on success or else an LTTng error code. + */ +enum lttng_error_code kernel_clear_session(struct ltt_session *session) +{ + enum lttng_error_code status = LTTNG_OK; + struct ltt_kernel_session *ksess = session->kernel_session; + + assert(ksess); + assert(ksess->consumer); + + DBG("Clear kernel session %s (session %" PRIu64 ")", + session->name, session->id); + + rcu_read_lock(); + + if (ksess->active) { + ERR("Expecting inactive session %s (%" PRIu64 ")", session->name, session->id); + status = LTTNG_ERR_FATAL; + goto end; + } + + status = kernel_clear_session_channels(session); + if (status != LTTNG_OK) { + goto end; + } + /* + * Iterate and clear all kernel maps. + */ + status = kernel_clear_session_maps(session); + if (status != LTTNG_OK) { + goto end; + } + + +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 +int add_key_token(struct lttng_kernel_key_token *kernel_key_token, + const struct lttng_map_key_token *key_token) +{ + int ret; + switch (key_token->type) { + case LTTNG_MAP_KEY_TOKEN_TYPE_STRING: + { + const struct lttng_map_key_token_string *str_token; + str_token = (typeof(str_token)) key_token; + + kernel_key_token->type = LTTNG_KERNEL_KEY_TOKEN_STRING; + kernel_key_token->arg.string_ptr = (uint64_t) str_token->string; + + break; + } + case LTTNG_MAP_KEY_TOKEN_TYPE_VARIABLE: + { + const struct lttng_map_key_token_variable *var_token; + var_token = (typeof(var_token)) key_token; + switch (var_token->type) { + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_EVENT_NAME: + kernel_key_token->type = LTTNG_KERNEL_KEY_TOKEN_EVENT_NAME; + break; + case LTTNG_MAP_KEY_TOKEN_VARIABLE_TYPE_PROVIDER_NAME: + /* The kernel events don't have providers */ + ERR("Provider variable token type not supported for kernel tracer"); + ret = -1; + goto end; + default: + abort(); + } + + break; + } + default: + abort(); + } + ret = 0; +end: + return ret; +} + +enum lttng_error_code kernel_create_event_counter( + struct ltt_kernel_map *kmap, + const struct lttng_credentials *creds, + uint64_t action_tracer_token, + const struct lttng_event_rule *event_rule, + struct lttng_map_key *key) +{ + int err, fd, ret = 0; + unsigned int i, key_token_count; + enum lttng_error_code error_code_ret; + enum lttng_map_key_status status; + struct ltt_kernel_event_counter *event_counter; + struct lttng_kernel_counter_event k_counter_event = {}; + + + event_counter = zmalloc(sizeof(*event_counter)); + if (!event_counter) { + error_code_ret = LTTNG_ERR_NOMEM; + goto error; + } + + trace_kernel_init_event_counter_from_event_rule(event_rule, + &k_counter_event); + event_counter->fd = -1; + event_counter->enabled = 1; + event_counter->action_tracer_token = action_tracer_token; + event_counter->filter = lttng_event_rule_get_filter_bytecode(event_rule); + + k_counter_event.event.token = action_tracer_token; + + /* Set the key pattern for this event counter. */ + k_counter_event.key.nr_dimensions = 1; + + status = lttng_map_key_get_token_count(key, &key_token_count); + if (status != LTTNG_MAP_KEY_STATUS_OK) { + error_code_ret = LTTNG_ERR_UNK; + goto error; + } + + assert(key_token_count > 0); + + k_counter_event.key.key_dimensions[0].nr_key_tokens = key_token_count; + + for (i = 0; i < key_token_count; i++) { + const struct lttng_map_key_token *token = + lttng_map_key_get_token_at_index(key, i); + + ret = add_key_token(&k_counter_event.key.key_dimensions[0].key_tokens[i], + token); + if (ret) { + ERR("Error appending map key token"); + error_code_ret = LTTNG_ERR_INVALID; + goto error; + } + } + + fd = kernctl_create_counter_event(kmap->fd, &k_counter_event); + if (fd < 0) { + switch (-fd) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + case ENOSYS: + WARN("Event counter type not implemented"); + error_code_ret = LTTNG_ERR_KERN_EVENT_ENOSYS; + break; + case ENOENT: + WARN("Event counter %s not found!", k_counter_event.event.name); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + default: + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + PERROR("create event counter ioctl"); + } + } + + event_counter->fd = fd; + event_counter->enabled = true; + + /* Prevent fd duplication after execlp() */ + err = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (err < 0) { + PERROR("fcntl session fd"); + } + + if (event_counter->filter) { + err = kernctl_filter(event_counter->fd, event_counter->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_USERSPACE_PROBE) { + ret = userspace_probe_event_rule_add_callsites( + event_rule, creds, event_counter->fd); + if (ret) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto add_callsite_error; + } + } + + err = kernctl_enable(event_counter->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel counter event"); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto enable_error; + } + + /* Add event to event list */ + rcu_read_lock(); + lttng_ht_node_init_u64(&event_counter->ht_node, + event_counter->action_tracer_token); + lttng_ht_add_unique_u64(kmap->event_counters_ht, + &event_counter->ht_node); + rcu_read_unlock(); + kmap->event_count++; + + DBG("Kernel event counter %s created (fd: %d)", + event_counter->event->name, + event_counter->fd); + error_code_ret = LTTNG_OK; + +add_callsite_error: +filter_error: +enable_error: +error: + return error_code_ret; +} + +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 = {}; + unsigned int capture_bytecode_count = 0, i; + const struct lttng_condition *condition = NULL; + const struct lttng_event_rule *event_rule = NULL; + enum lttng_condition_status cond_status; + + assert(trigger); + + condition = lttng_trigger_get_const_condition(trigger); + assert(condition); + + condition_type = lttng_condition_get_type(condition); + assert(condition_type == LTTNG_CONDITION_TYPE_ON_EVENT); + + /* Does not acquire a reference. */ + condition_status = lttng_condition_on_event_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, + lttng_condition_on_event_get_error_counter_index(condition), + &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 free_event; + } + + kernel_event_notifier.event.token = event_notifier_rule->token; + kernel_event_notifier.error_counter_idx = + lttng_condition_on_event_get_error_counter_index(condition); + + 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_USERSPACE_PROBE) { + 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; + } + } + + /* Set the capture bytecode if any */ + cond_status = lttng_condition_on_event_get_capture_descriptor_count(condition, &capture_bytecode_count); + assert(cond_status == LTTNG_CONDITION_STATUS_OK); + for (i = 0; i < capture_bytecode_count; i++) { + const struct lttng_bytecode *capture_bytecode = + lttng_condition_on_event_get_capture_bytecode_at_index( + condition, i); + if (capture_bytecode == NULL) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto error; + } + + ret = kernctl_capture(event_notifier_rule->fd, capture_bytecode); + if (ret < 0) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto 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_on_event_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 event notifier rule"); + } + + 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; +} + +struct key_ht_entry { + char *key; + struct lttng_ht_node_str node; +}; + +enum lttng_error_code kernel_list_map_values(const struct ltt_kernel_map *map, + const struct lttng_map_query *query, + struct lttng_map_content **map_content) +{ + enum lttng_map_status map_status; + enum lttng_error_code ret_code; + const char *map_name = NULL; + uint64_t descr_count, descr_idx, cpu_idx; + struct lttng_map_content *local_map_content; + struct lttng_ht *key_ht; + struct lttng_ht *values = NULL; + struct lttng_ht_node_str *node; + struct key_ht_entry *ht_entry; + struct lttng_ht_iter iter; + enum lttng_map_query_status map_query_status; + const char *key_filter; + bool sum_cpus = lttng_map_query_get_config_sum_by_cpu(query); + enum lttng_map_query_config_cpu config_cpu; + int ret; + int selected_cpu; + + + local_map_content = lttng_map_content_create(LTTNG_BUFFER_GLOBAL); + if (!local_map_content) { + ERR("Error creating map content"); + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + map_query_status = lttng_map_query_get_key_filter(query, &key_filter); + if (map_query_status == LTTNG_MAP_QUERY_STATUS_NONE) { + key_filter = NULL; + } else if (map_query_status != LTTNG_MAP_QUERY_STATUS_OK) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + config_cpu = lttng_map_query_get_config_cpu(query); + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + unsigned int count; + map_query_status = lttng_map_query_get_cpu_count(query, &count); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + assert(count == 1); + + map_query_status = lttng_map_query_get_cpu_at_index(query, 0, + &selected_cpu); + assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK); + } + + map_status = lttng_map_get_name(map->map, &map_name); + assert(map_status == LTTNG_MAP_STATUS_OK); + + DBG("Listing kernel map values: map-name = '%s'", map_name); + + ret = kernctl_counter_map_descriptor_count(map->fd, &descr_count); + if (ret) { + ERR("Error getting map descriptor count"); + ret_code = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + /* + * The kernel tracer sends us descriptors that may be identical aside + * from their user token field. This ABI was design this way to cover a + * potential use case where the user wants to know what enabler might + * have contributed to a specific bucket. + * + * We use this hashtable to de-duplicate keys. + */ + if (sum_cpus) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + if (!values) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + } + + DBG("Querying kernel for all map values: " + "map-name = '%s', key-value count = %"PRIu64, + map_name, descr_count); + for (cpu_idx = 0; cpu_idx < utils_get_number_of_possible_cpus(); cpu_idx++) { + struct lttng_kernel_counter_read value = {0}; + + if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) { + if (selected_cpu != cpu_idx) { + continue; + } + } + + if (!sum_cpus) { + values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + assert(values); + } + + key_ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + if (!key_ht) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + for(descr_idx = 0; descr_idx < descr_count; descr_idx++) { + struct lttng_kernel_counter_map_descriptor descriptor = {0}; + + DBG("Querying kernel for map key-value descriptor: " + "map-name = '%s', descriptor = %"PRIu64, + map_name, descr_idx); + descriptor.descriptor_index = descr_idx; + + ret = kernctl_counter_map_descriptor(map->fd, &descriptor); + if (ret) { + ERR("Error getting map descriptor %"PRIu64, descr_idx); + ret_code = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + if (key_filter && strcmp(key_filter, descriptor.key) != 0) { + continue; + } + + lttng_ht_lookup(key_ht, descriptor.key, &iter); + node = lttng_ht_iter_get_node_str(&iter); + if (node) { + /* This key was already appended to the list. */ + continue; + } + + + value.index.number_dimensions = 1; + value.index.dimension_indexes[0] = descriptor.array_index; + value.cpu = cpu_idx; + + DBG("Querying kernel for map descriptor value: " + "map-name = '%s', counter-index = %"PRIu64, + map_name, descriptor.array_index); + ret = kernctl_counter_read_value(map->fd, &value); + if (ret) { + ERR("Error getting value of map descriptor %"PRIu64, descr_idx); + ret_code = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + goto end; + } + + map_add_or_increment_map_values(values, descriptor.key, + value.value.value, value.value.underflow, + value.value.overflow); + + ht_entry = zmalloc(sizeof(*ht_entry)); + assert(ht_entry); + ht_entry->key = strdup(descriptor.key); + lttng_ht_node_init_str(&ht_entry->node, ht_entry->key); + lttng_ht_add_unique_str(key_ht, &ht_entry->node); + } + + if (!sum_cpus) { + ret = map_new_content_section(local_map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL, + sum_cpus, 0, + cpu_idx, values); + if (ret) { + abort(); + } + + lttng_ht_destroy(values); + } + + /* + * Remove all the keys before destroying the hashtable. + */ + cds_lfht_for_each_entry(key_ht->ht, &iter.iter, ht_entry, node.node) { + struct lttng_ht_iter entry_iter; + + entry_iter.iter.node = &ht_entry->node.node; + lttng_ht_del(key_ht, &entry_iter); + + free(ht_entry); + } + + lttng_ht_destroy(key_ht); + } + + if (sum_cpus) { + ret = map_new_content_section(local_map_content, + LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL, + sum_cpus, 0, 0, values); + if (ret) { + abort(); + } + lttng_ht_destroy(values); + } + + + *map_content = local_map_content; + local_map_content = NULL; + ret_code = LTTNG_OK; + +end: + lttng_map_content_destroy(local_map_content); + return ret_code; +} + +int kernel_get_notification_fd(void) +{ + return kernel_tracer_event_notifier_group_notification_fd; +}