X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fkernel.c;h=27b72a5afa133ae651b9e294a68871e50c5ee8ef;hp=d7c7984d201ba96c82d050eb71a48dca798c8e38;hb=refs%2Fheads%2Fsow-2020-0002-rev1;hpb=3a4595c2469472dee1656cde5f8882c2123efd3c diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index d7c7984d2..27b72a5af 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,28 @@ #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" #include "kern-modules.h" +#include "sessiond-config.h" #include "utils.h" #include "rotate.h" #include "modprobe.h" #include "tracker.h" +#include "trigger-error-accounting.h" +#include "notification-thread-commands.h" /* * Key used to reference a channel between the sessiond and the consumer. This @@ -45,9 +58,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_trigger_group_fd = -1; +static int kernel_tracer_trigger_group_notification_fd = -1; +static struct cds_lfht *kernel_tracer_token_ht; -#include -#include /* * Add context on a kernel channel. * @@ -219,6 +233,44 @@ error: return -1; } +/* + * Create a kernel channel, register it to the kernel tracer and add it to the + * kernel session. + */ +static +int kernel_create_trigger_group(int *trigger_group_fd) +{ + int ret; + int local_fd = -1; + + assert(trigger_group_fd); + + /* Kernel tracer channel creation */ + ret = kernctl_create_trigger_group(kernel_tracer_fd); + if (ret < 0) { + PERROR("ioctl kernel create trigger group"); + ret = -1; + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl session fd"); + } + + DBG("Kernel trigger group created (fd: %d)", + local_fd); + ret = 0; + +error: + *trigger_group_fd = 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. @@ -230,7 +282,7 @@ error: static int extract_userspace_probe_offset_function_elf( const struct lttng_userspace_probe_location *probe_location, - struct ltt_kernel_session *session, uint64_t *offset) + uid_t uid, gid_t gid, uint64_t *offset) { int fd; int ret = 0; @@ -267,8 +319,7 @@ int extract_userspace_probe_offset_function_elf( goto end; } - ret = run_as_extract_elf_symbol_offset(fd, symbol, session->uid, - session->gid, offset); + ret = run_as_extract_elf_symbol_offset(fd, symbol, uid, gid, offset); if (ret < 0) { DBG("userspace probe offset calculation failed for " "function %s", symbol); @@ -292,7 +343,7 @@ end: static int extract_userspace_probe_offset_tracepoint_sdt( const struct lttng_userspace_probe_location *probe_location, - struct ltt_kernel_session *session, uint64_t **offsets, + uid_t uid, gid_t gid, uint64_t **offsets, uint32_t *offsets_count) { enum lttng_userspace_probe_location_lookup_method_type lookup_method_type; @@ -338,7 +389,7 @@ int extract_userspace_probe_offset_tracepoint_sdt( } ret = run_as_extract_sdt_probe_offsets(fd, provider_name, probe_name, - session->uid, session->gid, offsets, offsets_count); + uid, gid, offsets, offsets_count); if (ret < 0) { DBG("userspace probe offset calculation failed for sdt " "probe %s:%s", provider_name, probe_name); @@ -359,29 +410,16 @@ end: return ret; } -/* - * Extract the offsets of the instrumentation point for the different lookup - * methods. - */ static -int userspace_probe_add_callsites(struct lttng_event *ev, - struct ltt_kernel_session *session, int fd) +int userspace_probe_add_callsite( + const struct lttng_userspace_probe_location *location, + uid_t uid, gid_t gid, int fd) { const struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; enum lttng_userspace_probe_location_lookup_method_type type; - const struct lttng_userspace_probe_location *location = NULL; int ret; - assert(ev); - assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); - - location = lttng_event_get_userspace_probe_location(ev); - if (!location) { - ret = -1; - goto end; - } - lookup_method = - lttng_userspace_probe_location_get_lookup_method(location); + lookup_method = lttng_userspace_probe_location_get_lookup_method(location); if (!lookup_method) { ret = -1; goto end; @@ -394,7 +432,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev, struct lttng_kernel_event_callsite callsite; uint64_t offset; - ret = extract_userspace_probe_offset_function_elf(location, session, &offset); + ret = extract_userspace_probe_offset_function_elf(location, + uid, gid, &offset); if (ret) { ret = LTTNG_ERR_PROBE_LOCATION_INVAL; goto end; @@ -403,8 +442,7 @@ int userspace_probe_add_callsites(struct lttng_event *ev, callsite.u.uprobe.offset = offset; ret = kernctl_add_callsite(fd, &callsite); if (ret) { - WARN("Adding callsite to userspace probe " - "event %s failed.", ev->name); + WARN("Adding callsite to ELF userspace probe failed."); ret = LTTNG_ERR_KERN_ENABLE_FAIL; goto end; } @@ -421,8 +459,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev, * This call allocates the offsets buffer. This buffer must be freed * by the caller */ - ret = extract_userspace_probe_offset_tracepoint_sdt(location, session, - &offsets, &offsets_count); + ret = extract_userspace_probe_offset_tracepoint_sdt(location, + uid, gid, &offsets, &offsets_count); if (ret) { ret = LTTNG_ERR_PROBE_LOCATION_INVAL; goto end; @@ -431,8 +469,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev, callsite.u.uprobe.offset = offsets[i]; ret = kernctl_add_callsite(fd, &callsite); if (ret) { - WARN("Adding callsite to userspace probe " - "event %s failed.", ev->name); + WARN("Adding callsite to SDT userspace probe " + "failed."); ret = LTTNG_ERR_KERN_ENABLE_FAIL; free(offsets); goto end; @@ -449,6 +487,71 @@ end: return ret; } +/* + * Extract the offsets of the instrumentation point for the different lookup + * methods. + */ +static +int userspace_probe_event_add_callsites(struct lttng_event *ev, + struct ltt_kernel_session *session, int fd) +{ + const struct lttng_userspace_probe_location *location = NULL; + int ret; + + assert(ev); + assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); + + location = lttng_event_get_userspace_probe_location(ev); + if (!location) { + ret = -1; + goto end; + } + + ret = userspace_probe_add_callsite(location, session->uid, session->gid, + fd); + if (ret) { + WARN("Adding callsite to userspace probe event \"%s\" " + "failed.", ev->name); + } + +end: + return ret; +} + +/* + * Extract the offsets of the instrumentation point for the different lookup + * methods. + */ +static int userspace_probe_event_rule_add_callsites( + const struct lttng_event_rule *rule, + const struct lttng_credentials *creds, + int fd) +{ + const struct lttng_userspace_probe_location *location = NULL; + enum lttng_event_rule_status status; + int ret; + + assert(rule); + assert(creds); + assert(lttng_event_rule_get_type(rule) == 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("Adding callsite to userspace probe object %d" + "failed.", 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. @@ -457,7 +560,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; @@ -518,7 +621,8 @@ int kernel_create_event(struct lttng_event *ev, } if (ev->type == LTTNG_EVENT_USERSPACE_PROBE) { - ret = userspace_probe_add_callsites(ev, channel->session, event->fd); + ret = userspace_probe_event_add_callsites(ev, channel->session, + event->fd); if (ret) { goto add_callsite_error; } @@ -675,6 +779,42 @@ error: return ret; } +/* + * Disable a kernel trigger. + */ +static +int kernel_disable_token_event_rule(struct ltt_kernel_token_event_rule *event) +{ + int ret; + + assert(event); + + rcu_read_lock(); + cds_lfht_del(kernel_tracer_token_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("disable kernel event"); + break; + } + goto error; + } + + event->enabled = 0; + DBG("Kernel trigger token %" PRIu64" disabled (fd: %d)", event->token, event->fd); + + return 0; + +error: + return ret; +} + static struct process_attr_tracker *_kernel_get_process_attr_tracker( struct ltt_kernel_session *session, @@ -1796,6 +1936,7 @@ LTTNG_HIDDEN int init_kernel_tracer(void) { int ret; + enum lttng_error_code error_code_ret; bool is_root = !getuid(); /* Modprobe lttng kernel modules */ @@ -1827,20 +1968,48 @@ 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)."); } + ret = kernel_create_trigger_group(&kernel_tracer_trigger_group_fd); + if (ret < 0) { + /* TODO: error handling if it is not supported etc. */ + WARN("Failed trigger group creation"); + kernel_tracer_trigger_group_fd = -1; + /* This is not fatal */ + } else { + + error_code_ret = kernel_create_trigger_group_notification_fd( + &kernel_tracer_trigger_group_notification_fd); + if (error_code_ret != LTTNG_OK) { + goto error_modules; + } + + trigger_error_accounting_register_kernel(kernel_tracer_trigger_group_fd); + if (ret < 0) { + goto error_modules; + } + } + + kernel_tracer_token_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, + CDS_LFHT_AUTO_RESIZE|CDS_LFHT_ACCOUNTING, NULL); + if (!kernel_tracer_token_ht) { + goto error_modules; + } + DBG("Kernel tracer fd %d", kernel_tracer_fd); + DBG("Kernel tracer trigger group fd %d", kernel_tracer_trigger_group_fd); + DBG("Kernel tracer trigger group notification fd %d", kernel_tracer_trigger_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: @@ -1875,6 +2044,40 @@ LTTNG_HIDDEN void cleanup_kernel_tracer(void) { int ret; + struct cds_lfht_iter iter; + + struct ltt_kernel_token_event_rule *rule = NULL; + cds_lfht_for_each_entry(kernel_tracer_token_ht, &iter, rule, ht_node) { + kernel_disable_token_event_rule(rule); + trace_kernel_destroy_token_event_rule(rule); + cds_lfht_del(kernel_tracer_token_ht, &rule->ht_node); + } + + DBG2("Closing kernel trigger group notification fd"); + if (kernel_tracer_trigger_group_notification_fd >= 0) { + + ret = notification_thread_command_remove_application( + notification_thread_handle, + kernel_tracer_trigger_group_notification_fd); + if (ret != LTTNG_OK) { + ERR("Failed to remove kernel trigger notification from notification thread"); + } + ret = close(kernel_tracer_trigger_group_notification_fd); + if (ret) { + PERROR("close"); + } + kernel_tracer_trigger_group_notification_fd = -1; + } + + /* TODO: do we iterate over the list to remove all token? */ + DBG2("Closing kernel trigger group fd"); + if (kernel_tracer_trigger_group_fd >= 0) { + ret = close(kernel_tracer_trigger_group_fd); + if (ret) { + PERROR("close"); + } + kernel_tracer_trigger_group_fd = -1; + } DBG2("Closing kernel fd"); if (kernel_tracer_fd >= 0) { @@ -1884,6 +2087,7 @@ void cleanup_kernel_tracer(void) } kernel_tracer_fd = -1; } + DBG("Unloading kernel modules"); modprobe_remove_lttng_all(); free(syscall_table); @@ -1974,3 +2178,322 @@ end: rcu_read_unlock(); return status; } + +enum lttng_error_code kernel_create_trigger_group_notification_fd( + int *trigger_group_notification_fd) +{ + enum lttng_error_code error_code_ret; + int local_fd = -1, ret; + + assert(trigger_group_notification_fd); + + ret = kernctl_create_trigger_group_notification_fd(kernel_tracer_trigger_group_fd); + if (ret < 0) { + PERROR("ioctl kernel create trigger group"); + error_code_ret = LTTNG_ERR_TRIGGER_GROUP_NOTIFICATION_FD; + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl session fd"); + error_code_ret = LTTNG_ERR_TRIGGER_GROUP_NOTIFICATION_FD; + goto error; + } + + DBG("Kernel trigger group notification created (fd: %d)", + local_fd); + error_code_ret = LTTNG_OK; + *trigger_group_notification_fd = local_fd; + +error: + return error_code_ret; +} + +enum lttng_error_code kernel_create_trigger_group_error_counter( + int *error_counter_fd, size_t nb_bucket) +{ + enum lttng_error_code error_code_ret; + int local_fd = -1, ret; + struct lttng_kernel_counter_conf error_counter_conf; + + assert(error_counter_fd); + + error_counter_conf.arithmetic = LTTNG_KERNEL_COUNTER_ARITHMETIC_MODULAR; + error_counter_conf.bitness = LTTNG_KERNEL_COUNTER_BITNESS_64BITS; + error_counter_conf.global_sum_step = 0; + error_counter_conf.number_dimensions = 1; + error_counter_conf.dimensions[0].size = nb_bucket; + error_counter_conf.dimensions[0].has_underflow = false; + error_counter_conf.dimensions[0].has_overflow = false; + + ret = kernctl_create_trigger_group_error_counter( + kernel_tracer_trigger_group_fd, &error_counter_conf); + if (ret < 0) { + PERROR("ioctl kernel create trigger group error counter"); + error_code_ret = LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER; + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + error_code_ret = LTTNG_ERR_TRIGGER_GROUP_ERROR_COUNTER; + PERROR("fcntl session fd"); + } + + DBG("Kernel trigger group error counter (fd: %d)", + local_fd); + error_code_ret = LTTNG_OK; + +error: + *error_counter_fd = local_fd; + return error_code_ret; +} + +enum lttng_error_code kernel_destroy_trigger_group_notification_fd( + int trigger_group_notification_fd) +{ + enum lttng_error_code ret = LTTNG_OK; + DBG("Closing trigger group notification fd %d", trigger_group_notification_fd); + if (trigger_group_notification_fd >= 0) { + ret = close(trigger_group_notification_fd); + if (ret) { + PERROR("close"); + } + } + return ret; +} + +static +unsigned long hash_trigger(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) +{ + struct ltt_kernel_token_event_rule *token; + const struct lttng_trigger *trigger = key; + + token = caa_container_of(node, struct ltt_kernel_token_event_rule, ht_node); + + return lttng_trigger_is_equal(trigger, token->trigger); +} + +static enum lttng_error_code kernel_create_token_event_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; + struct ltt_kernel_token_event_rule *event; + struct lttng_kernel_trigger kernel_trigger = {}; + unsigned int capture_bytecode_count = 0; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + + assert(trigger); + + condition = lttng_trigger_get_condition(trigger); + assert(condition); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + lttng_condition_event_rule_get_rule_mutable(condition, &event_rule); + assert(event_rule); + assert(lttng_event_rule_get_type(event_rule) != LTTNG_EVENT_RULE_TYPE_UNKNOWN); + + error_code_ret = trace_kernel_create_token_event_rule(trigger, token, + lttng_trigger_get_error_counter_index(trigger), &event); + if (error_code_ret != LTTNG_OK) { + goto error; + } + + trace_kernel_init_trigger_from_event_rule(event_rule, &kernel_trigger); + kernel_trigger.id = event->token; + kernel_trigger.error_counter_idx = lttng_trigger_get_error_counter_index(trigger); + + fd = kernctl_create_trigger(kernel_tracer_trigger_group_fd, &kernel_trigger); + if (fd < 0) { + switch (-fd) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + case ENOSYS: + WARN("Trigger type not implemented"); + error_code_ret = LTTNG_ERR_KERN_EVENT_ENOSYS; + break; + case ENOENT: + WARN("Event %s not found!", kernel_trigger.name); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + default: + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + PERROR("create trigger ioctl"); + } + goto free_event; + } + + event->fd = fd; + /* Prevent fd duplication after execlp() */ + err = fcntl(event->fd, F_SETFD, FD_CLOEXEC); + if (err < 0) { + PERROR("fcntl session fd"); + } + + if (event->filter) { + err = kernctl_filter(event->fd, event->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->fd); + if (ret) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto add_callsite_error; + } + } + + /* Set the capture bytecode */ + capture_bytecode_count = lttng_trigger_get_capture_bytecode_count(trigger); + for (unsigned int i = 0; i < capture_bytecode_count; i++) { + const struct lttng_bytecode *capture_bytecode = lttng_trigger_get_capture_bytecode_at_index(trigger, i); + ret = kernctl_capture(event->fd, capture_bytecode); + if (ret < 0) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto error; + } + } + + err = kernctl_enable(event->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel trigger"); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto enable_error; + } + + /* Add trigger to kernel token mapping in the hashtable. */ + cds_lfht_add(kernel_tracer_token_ht, hash_trigger(trigger), + &event->ht_node); + + DBG("Trigger %s created (fd: %d)", kernel_trigger.name, event->fd); + + return LTTNG_OK; + +add_callsite_error: +enable_error: +filter_error: + { + int closeret; + + closeret = close(event->fd); + if (closeret) { + PERROR("close event fd"); + } + } +free_event: + free(event); +error: + return error_code_ret; +} + +enum lttng_error_code kernel_register_trigger(struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds) +{ + enum lttng_error_code ret; + struct lttng_condition *condition; + struct lttng_event_rule *event_rule; + uint64_t token; + + /* TODO error handling */ + + rcu_read_lock(); + pthread_mutex_lock(¬ification_trigger_tokens_ht_lock); + + /* TODO: error checking and type checking */ + token = lttng_trigger_get_tracer_token(trigger); + condition = lttng_trigger_get_condition(trigger); + (void) lttng_condition_event_rule_get_rule_mutable(condition, &event_rule); + + assert(lttng_event_rule_get_domain_type(event_rule) == LTTNG_DOMAIN_KERNEL); + + ret = kernel_create_token_event_rule(trigger, cmd_creds, token); + if (ret != LTTNG_OK) { + ERR("Failed to create kernel trigger token."); + } + + rcu_read_unlock(); + pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock); + return ret; +} + +enum lttng_error_code kernel_unregister_trigger(struct lttng_trigger *trigger) +{ + struct ltt_kernel_token_event_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(); + pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock); + + cds_lfht_lookup(kernel_tracer_token_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_token_event_rule, ht_node); + + ret = kernel_disable_token_event_rule(token_event_rule_element); + if (ret) { + error_code_ret = LTTNG_ERR_FATAL; + goto error; + } + + trace_kernel_destroy_token_event_rule(token_event_rule_element); + + rcu_read_unlock(); + pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock); + + error_code_ret = LTTNG_OK; + +error: + return error_code_ret; +} + +int kernel_get_notification_fd(void) +{ + return kernel_tracer_trigger_group_notification_fd; +}