X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fkernel.c;h=646da3ba83affd3463a30dd7e2efb7a43b05535c;hp=4ee4bea64ff630abae9617624f86ce21cc87950d;hb=1831ae68b70dece8e9b847081526495adbbf05e5;hpb=25357057de5ae4dd2a572e8f9b893c1b90cbd60a diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index 4ee4bea64..646da3ba8 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -15,11 +15,20 @@ #include #include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include + #include "lttng-sessiond.h" #include "lttng-syscall.h" #include "consumer.h" @@ -29,6 +38,7 @@ #include "utils.h" #include "rotate.h" #include "modprobe.h" +#include "notification-thread-commands.h" /* * Key used to reference a channel between the sessiond and the consumer. This @@ -39,9 +49,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 ltt_kernel_token_event_rule_list kernel_tracer_token_list; -#include -#include /* * Add context on a kernel channel. * @@ -213,6 +224,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. @@ -224,7 +273,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; @@ -261,8 +310,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); @@ -286,7 +334,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; @@ -332,7 +380,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); @@ -353,29 +401,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; @@ -388,7 +423,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; @@ -397,8 +433,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; } @@ -415,8 +450,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; @@ -425,8 +460,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; @@ -443,6 +478,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, creds->uid, creds->gid, + 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. @@ -512,7 +612,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; } @@ -669,6 +770,36 @@ error: return ret; } +/* + * Disable a kernel trigger. + */ +int kernel_disable_token_event_rule(struct ltt_kernel_token_event_rule *event) +{ + int ret; + + assert(event); + + 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 lttng_tracker_list *get_id_tracker_list( struct ltt_kernel_session *session, enum lttng_tracker_type tracker_type) @@ -1732,20 +1863,37 @@ 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 { + ret = kernel_create_trigger_group_notification_fd(&kernel_tracer_trigger_group_notification_fd); + if (ret < 0) { + goto error_modules; + } + } + + CDS_INIT_LIST_HEAD(&kernel_tracer_token_list.head); + 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 notificationi 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: @@ -1781,6 +1929,31 @@ void cleanup_kernel_tracer(void) { int ret; + struct ltt_kernel_token_event_rule *rule, *rtmp; + cds_list_for_each_entry_safe(rule, rtmp, &kernel_tracer_token_list.head, list) { + kernel_disable_token_event_rule(rule); + trace_kernel_destroy_token_event_rule(rule); + } + + DBG2("Closing kernel trigger group notification fd"); + if (kernel_tracer_trigger_group_notification_fd >= 0) { + 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) { ret = close(kernel_tracer_fd); @@ -1789,6 +1962,8 @@ void cleanup_kernel_tracer(void) } kernel_tracer_fd = -1; } + + DBG("Unloading kernel modules"); modprobe_remove_lttng_all(); free(syscall_table); @@ -1879,3 +2054,274 @@ 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 ret = LTTNG_OK; + int local_fd = -1; + + 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"); + 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 notification created (fd: %d)", + local_fd); + ret = 0; + +error: + *trigger_group_notification_fd = local_fd; + return 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 int kernel_create_token_event_rule(struct lttng_event_rule *rule, + const struct lttng_credentials *creds, uint64_t token) +{ + int err, fd; + enum lttng_error_code ret; + struct ltt_kernel_token_event_rule *event; + struct lttng_kernel_trigger trigger; + + assert(rule); + + ret = trace_kernel_create_token_event_rule(rule, token, &event); + if (ret != LTTNG_OK) { + goto error; + } + + trace_kernel_init_trigger_from_event_rule(event->event_rule, &trigger); + trigger.id = event->token; + + fd = kernctl_create_trigger(kernel_tracer_trigger_group_fd, &trigger); + if (fd < 0) { + switch (-fd) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + case ENOSYS: + WARN("Trigger type not implemented"); + ret = LTTNG_ERR_KERN_EVENT_ENOSYS; + break; + case ENOENT: + WARN("Event %s not found!", trigger.name); + ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + default: + 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: + ret = LTTNG_ERR_FILTER_NOMEM; + break; + default: + ret = LTTNG_ERR_FILTER_INVAL; + break; + } + goto filter_error; + } + } + + if (lttng_event_rule_get_type(event->event_rule) == + LTTNG_EVENT_RULE_TYPE_UPROBE) { + ret = userspace_probe_event_rule_add_callsites( + rule, creds, event->fd); + if (ret) { + goto add_callsite_error; + } + } + + err = kernctl_enable(event->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel trigger"); + ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto enable_error; + } + + /* Add event to event list */ + cds_list_add(&event->list, &kernel_tracer_token_list.head); + + DBG("Trigger %s created (fd: %d)", trigger.name, event->fd); + + return 0; + +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 ret; +} + +enum lttng_error_code kernel_update_tokens(void) +{ + enum lttng_error_code ret = LTTNG_OK; + enum lttng_trigger_status t_status; + struct ltt_kernel_token_event_rule *token_event_rule_element; + struct lttng_triggers *triggers; + unsigned int count; + + /* TODO error handling */ + + /* Get list of token trigger from the notification thread here */ + rcu_read_lock(); + pthread_mutex_lock(¬ification_trigger_tokens_ht_lock); + ret = notification_thread_command_get_tokens(notification_thread_handle, &triggers); + if (ret != LTTNG_OK) { + ret = -1; + goto end; + } + + assert(triggers); + + t_status = lttng_triggers_get_count(triggers, &count); + if (t_status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + goto end; + } + + for (unsigned int i = 0; i < count; i++) { + struct lttng_condition *condition; + struct lttng_event_rule *event_rule; + struct lttng_trigger *trigger; + struct ltt_kernel_token_event_rule *k_token; + const struct lttng_credentials *creds; + uint64_t token; + + trigger = lttng_triggers_get_pointer_of_index(triggers, i); + assert(trigger); + + /* TODO: error checking and type checking */ + token = lttng_trigger_get_key(trigger); + condition = lttng_trigger_get_condition(trigger); + (void) lttng_condition_event_rule_get_rule_no_const(condition, &event_rule); + + if (lttng_event_rule_get_domain_type(event_rule) != LTTNG_DOMAIN_KERNEL) { + /* Skip ust related trigger */ + continue; + } + + creds = lttng_trigger_get_credentials(trigger); + /* Iterate over all known token trigger */ + k_token = trace_kernel_find_trigger_by_token(&kernel_tracer_token_list, token); + if (!k_token) { + ret = kernel_create_token_event_rule(event_rule, creds, token); + if (ret < 0) { + goto end; + } + } + } + + /* Remove all unknown trigger from the app + * TODO find a way better way then this, do it on the unregister command + * and be specific on the token to remove instead of going over all + * trigger known to the app. This is sub optimal. + */ + cds_list_for_each_entry (token_event_rule_element, &kernel_tracer_token_list.head, + list) { + uint64_t token; + bool found = false; + + token = token_event_rule_element->token; + + /* + * Check if the app event trigger still exists on the + * notification side. + * TODO: might want to change the backing data struct of the + * lttng_triggers object to allow quick lookup? + * For kernel mostly all of this can be removed once we delete + * on a per trigger basis. + */ + + for (unsigned int i = 0; i < count; i++) { + struct lttng_trigger *trigger; + uint64_t inner_token; + + trigger = lttng_triggers_get_pointer_of_index( + triggers, i); + assert(trigger); + + inner_token = lttng_trigger_get_key(trigger); + + if (inner_token == token) { + found = true; + break; + } + } + + if (found) { + /* Still valid */ + continue; + } + + kernel_disable_token_event_rule(token_event_rule_element); + trace_kernel_destroy_token_event_rule(token_event_rule_element); + } +end: + rcu_read_unlock(); + pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock); + return ret; + +} + +int kernel_get_notification_fd(void) +{ + return kernel_tracer_trigger_group_notification_fd; +}