X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fkernel.c;h=4327db24bdb3291f575da0d90c143e280c7bdbfa;hp=e478499336896f5d739e087c8ee91e3a395886cd;hb=d5a1b7aa06b4c924b1cd30623758343c74ecab5c;hpb=1cfb4b98d79d3dd709037ee6827f238a5616832a diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index e47849933..4327db24b 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -33,9 +33,20 @@ #include "kernel-consumer.h" #include "kern-modules.h" #include "utils.h" +#include "rotate.h" +/* + * Key used to reference a channel between the sessiond and the consumer. This + * is only read and updated with the session_list lock held. + */ +static uint64_t next_kernel_channel_key; + +#include +#include /* * Add context on a kernel channel. + * + * Assumes the ownership of ctx. */ int kernel_add_channel_context(struct ltt_kernel_channel *chan, struct ltt_kernel_context *ctx) @@ -63,12 +74,16 @@ int kernel_add_channel_context(struct ltt_kernel_channel *chan, goto error; } } + ret = 0; end: cds_list_add_tail(&ctx->list, &chan->ctx_list); - return 0; - + ctx->in_list = true; + ctx = NULL; error: + if (ctx) { + trace_kernel_destroy_context(ctx); + } return ret; } @@ -163,8 +178,10 @@ int kernel_create_channel(struct ltt_kernel_session *session, cds_list_add(&lkc->list, &session->channel_list.head); session->channel_count++; lkc->session = session; + lkc->key = ++next_kernel_channel_key; - DBG("Kernel channel %s created (fd: %d)", lkc->channel->name, lkc->fd); + DBG("Kernel channel %s created (fd: %d, key: %" PRIu64 ")", + lkc->channel->name, lkc->fd, lkc->key); return 0; @@ -176,6 +193,236 @@ error: return -1; } +/* + * Compute the offset of the instrumentation byte in the binary based on the + * function probe location using the ELF lookup method. + * + * Returns 0 on success and set the offset out parameter to the offset of the + * elf symbol + * Returns -1 on 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) +{ + int fd; + int ret = 0; + const char *symbol = NULL; + const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; + enum lttng_userspace_probe_location_lookup_method_type lookup_method_type; + + assert(lttng_userspace_probe_location_get_type(probe_location) == + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION); + + lookup = lttng_userspace_probe_location_get_lookup_method( + probe_location); + if (!lookup) { + ret = -1; + goto end; + } + + lookup_method_type = + lttng_userspace_probe_location_lookup_method_get_type(lookup); + + assert(lookup_method_type == + LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF); + + symbol = lttng_userspace_probe_location_function_get_function_name( + probe_location); + if (!symbol) { + ret = -1; + goto end; + } + + fd = lttng_userspace_probe_location_function_get_binary_fd(probe_location); + if (fd < 0) { + ret = -1; + goto end; + } + + ret = run_as_extract_elf_symbol_offset(fd, symbol, session->uid, + session->gid, offset); + if (ret < 0) { + DBG("userspace probe offset calculation failed for " + "function %s", symbol); + goto end; + } + + DBG("userspace probe elf offset for %s is 0x%jd", symbol, (intmax_t)(*offset)); +end: + return ret; +} + +/* + * Compute the offsets of the instrumentation bytes in the binary based on the + * tracepoint probe location using the SDT lookup method. This function + * allocates the offsets buffer, the caller must free it. + * + * Returns 0 on success and set the offset out parameter to the offsets of the + * SDT tracepoint. + * Returns -1 on error. + */ +static +int extract_userspace_probe_offset_tracepoint_sdt( + const struct lttng_userspace_probe_location *probe_location, + struct ltt_kernel_session *session, uint64_t **offsets, + uint32_t *offsets_count) +{ + enum lttng_userspace_probe_location_lookup_method_type lookup_method_type; + const struct lttng_userspace_probe_location_lookup_method *lookup = NULL; + const char *probe_name = NULL, *provider_name = NULL; + int ret = 0; + int fd, i; + + assert(lttng_userspace_probe_location_get_type(probe_location) == + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT); + + lookup = lttng_userspace_probe_location_get_lookup_method(probe_location); + if (!lookup) { + ret = -1; + goto end; + } + + lookup_method_type = + lttng_userspace_probe_location_lookup_method_get_type(lookup); + + assert(lookup_method_type == + LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT); + + + probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name( + probe_location); + if (!probe_name) { + ret = -1; + goto end; + } + + provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name( + probe_location); + if (!provider_name) { + ret = -1; + goto end; + } + + fd = lttng_userspace_probe_location_tracepoint_get_binary_fd(probe_location); + if (fd < 0) { + ret = -1; + goto end; + } + + ret = run_as_extract_sdt_probe_offsets(fd, provider_name, probe_name, + session->uid, session->gid, offsets, offsets_count); + if (ret < 0) { + DBG("userspace probe offset calculation failed for sdt " + "probe %s:%s", provider_name, probe_name); + goto end; + } + + if (*offsets_count == 0) { + DBG("no userspace probe offset found"); + goto end; + } + + DBG("%u userspace probe SDT offsets found for %s:%s at:", + *offsets_count, provider_name, probe_name); + for (i = 0; i < *offsets_count; i++) { + DBG("\t0x%jd", (intmax_t)((*offsets)[i])); + } +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) +{ + 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); + if (!lookup_method) { + ret = -1; + goto end; + } + + type = lttng_userspace_probe_location_lookup_method_get_type(lookup_method); + switch (type) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + { + struct lttng_kernel_event_callsite callsite; + uint64_t offset; + + ret = extract_userspace_probe_offset_function_elf(location, session, &offset); + if (ret) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto end; + } + + callsite.u.uprobe.offset = offset; + ret = kernctl_add_callsite(fd, &callsite); + if (ret) { + WARN("Adding callsite to userspace probe " + "event %s failed.", ev->name); + ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto end; + } + break; + } + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + { + int i; + uint64_t *offsets = NULL; + uint32_t offsets_count; + struct lttng_kernel_event_callsite callsite; + + /* + * 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); + if (ret) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto end; + } + for (i = 0; i < offsets_count; i++) { + 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); + ret = LTTNG_ERR_KERN_ENABLE_FAIL; + free(offsets); + goto end; + } + } + free(offsets); + break; + } + default: + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto end; + } +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. @@ -186,60 +433,80 @@ int kernel_create_event(struct lttng_event *ev, char *filter_expression, struct lttng_filter_bytecode *filter) { - int ret; + int err, fd; + enum lttng_error_code ret; struct ltt_kernel_event *event; assert(ev); assert(channel); /* We pass ownership of filter_expression and filter */ - event = trace_kernel_create_event(ev, filter_expression, - filter); - if (event == NULL) { - ret = -1; + ret = trace_kernel_create_event(ev, filter_expression, + filter, &event); + if (ret != LTTNG_OK) { goto error; } - ret = kernctl_create_event(channel->fd, event->event); - if (ret < 0) { - switch (-ret) { + fd = kernctl_create_event(channel->fd, event->event); + if (fd < 0) { + switch (-fd) { case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; break; case ENOSYS: WARN("Event type not implemented"); + ret = LTTNG_ERR_KERN_EVENT_ENOSYS; break; case ENOENT: WARN("Event %s not found!", ev->name); + ret = LTTNG_ERR_KERN_ENABLE_FAIL; break; default: + ret = LTTNG_ERR_KERN_ENABLE_FAIL; PERROR("create event ioctl"); } goto free_event; } event->type = ev->type; - event->fd = ret; + event->fd = fd; /* Prevent fd duplication after execlp() */ - ret = fcntl(event->fd, F_SETFD, FD_CLOEXEC); - if (ret < 0) { + err = fcntl(event->fd, F_SETFD, FD_CLOEXEC); + if (err < 0) { PERROR("fcntl session fd"); } if (filter) { - ret = kernctl_filter(event->fd, filter); - if (ret) { + err = kernctl_filter(event->fd, filter); + if (err < 0) { + switch (-err) { + case ENOMEM: + ret = LTTNG_ERR_FILTER_NOMEM; + break; + default: + ret = LTTNG_ERR_FILTER_INVAL; + break; + } goto filter_error; } } - ret = kernctl_enable(event->fd); - if (ret < 0) { - switch (-ret) { + if (ev->type == LTTNG_EVENT_USERSPACE_PROBE) { + ret = userspace_probe_add_callsites(ev, channel->session, 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 event"); + ret = LTTNG_ERR_KERN_ENABLE_FAIL; break; } goto enable_error; @@ -253,6 +520,7 @@ int kernel_create_event(struct lttng_event *ev, return 0; +add_callsite_error: enable_error: filter_error: { @@ -285,7 +553,8 @@ int kernel_disable_channel(struct ltt_kernel_channel *chan) } chan->enabled = 0; - DBG("Kernel channel %s disabled (fd: %d)", chan->channel->name, chan->fd); + DBG("Kernel channel %s disabled (fd: %d, key: %" PRIu64 ")", + chan->channel->name, chan->fd, chan->key); return 0; @@ -309,7 +578,8 @@ int kernel_enable_channel(struct ltt_kernel_channel *chan) } chan->enabled = 1; - DBG("Kernel channel %s enabled (fd: %d)", chan->channel->name, chan->fd); + DBG("Kernel channel %s enabled (fd: %d, key: %" PRIu64 ")", + chan->channel->name, chan->fd, chan->key); return 0; @@ -519,6 +789,7 @@ int kernel_open_metadata(struct ltt_kernel_session *session) } lkm->fd = ret; + lkm->key = ++next_kernel_channel_key; /* Prevent fd duplication after execlp() */ ret = fcntl(lkm->fd, F_SETFD, FD_CLOEXEC); if (ret < 0) { @@ -808,38 +1079,38 @@ error: /* * Get kernel version and validate it. */ -int kernel_validate_version(int tracer_fd) +int kernel_validate_version(int tracer_fd, + struct lttng_kernel_tracer_version *version, + struct lttng_kernel_tracer_abi_version *abi_version) { int ret; - struct lttng_kernel_tracer_version version; - struct lttng_kernel_tracer_abi_version abi_version; - ret = kernctl_tracer_version(tracer_fd, &version); + ret = kernctl_tracer_version(tracer_fd, version); if (ret < 0) { ERR("Failed to retrieve the lttng-modules version"); goto error; } /* Validate version */ - if (version.major != VERSION_MAJOR) { + if (version->major != VERSION_MAJOR) { ERR("Kernel tracer major version (%d) is not compatible with lttng-tools major version (%d)", - version.major, VERSION_MAJOR); + version->major, VERSION_MAJOR); goto error_version; } - ret = kernctl_tracer_abi_version(tracer_fd, &abi_version); + ret = kernctl_tracer_abi_version(tracer_fd, abi_version); if (ret < 0) { ERR("Failed to retrieve lttng-modules ABI version"); goto error; } - if (abi_version.major != LTTNG_MODULES_ABI_MAJOR_VERSION) { + if (abi_version->major != LTTNG_MODULES_ABI_MAJOR_VERSION) { ERR("Kernel tracer ABI version (%d.%d) does not match the expected ABI major version (%d.*)", - abi_version.major, abi_version.minor, + abi_version->major, abi_version->minor, LTTNG_MODULES_ABI_MAJOR_VERSION); goto error; } DBG2("Kernel tracer version validated (%d.%d, ABI %d.%d)", - version.major, version.minor, - abi_version.major, abi_version.minor); + version->major, version->minor, + abi_version->major, abi_version->minor); return 0; error_version: @@ -972,6 +1243,8 @@ int kernel_snapshot_record(struct ltt_kernel_session *ksess, struct consumer_socket *socket; struct lttng_ht_iter iter; struct ltt_kernel_metadata *saved_metadata; + struct ltt_session *session; + uint64_t trace_archive_id; assert(ksess); assert(ksess->consumer); @@ -979,6 +1252,12 @@ int kernel_snapshot_record(struct ltt_kernel_session *ksess, DBG("Kernel snapshot record started"); + session = session_find_by_id(ksess->id); + assert(session); + assert(pthread_mutex_trylock(&session->lock)); + assert(session_trylock_list()); + trace_archive_id = session->current_archive_id; + /* Save current metadata since the following calls will change it. */ saved_metadata = ksess->metadata; saved_metadata_fd = ksess->metadata_stream_fd; @@ -1023,12 +1302,11 @@ int kernel_snapshot_record(struct ltt_kernel_session *ksess, /* For each channel, ask the consumer to snapshot it. */ cds_list_for_each_entry(chan, &ksess->channel_list.head, list) { - pthread_mutex_lock(socket->lock); - ret = consumer_snapshot_channel(socket, chan->fd, output, 0, + ret = consumer_snapshot_channel(socket, chan->key, output, 0, ksess->uid, ksess->gid, DEFAULT_KERNEL_TRACE_DIR, wait, - nb_packets_per_stream); - pthread_mutex_unlock(socket->lock); + nb_packets_per_stream, + trace_archive_id); if (ret < 0) { ret = LTTNG_ERR_KERN_CONSUMER_FAIL; (void) kernel_consumer_destroy_metadata(socket, @@ -1038,11 +1316,10 @@ int kernel_snapshot_record(struct ltt_kernel_session *ksess, } /* Snapshot metadata, */ - pthread_mutex_lock(socket->lock); - ret = consumer_snapshot_channel(socket, ksess->metadata->fd, output, + ret = consumer_snapshot_channel(socket, ksess->metadata->key, output, 1, ksess->uid, ksess->gid, - DEFAULT_KERNEL_TRACE_DIR, wait, 0); - pthread_mutex_unlock(socket->lock); + DEFAULT_KERNEL_TRACE_DIR, wait, 0, + trace_archive_id); if (ret < 0) { ret = LTTNG_ERR_KERN_CONSUMER_FAIL; goto error_consumer; @@ -1088,3 +1365,98 @@ int kernel_syscall_mask(int chan_fd, char **syscall_mask, uint32_t *nr_bits) return kernctl_syscall_mask(chan_fd, syscall_mask, nr_bits); } + +/* + * Check for the support of the RING_BUFFER_SNAPSHOT_SAMPLE_POSITIONS via abi + * version number. + * + * Return 1 on success, 0 when feature is not supported, negative value in case + * of errors. + */ +int kernel_supports_ring_buffer_snapshot_sample_positions(int tracer_fd) +{ + int ret = 0; // Not supported by default + struct lttng_kernel_tracer_abi_version abi; + + ret = kernctl_tracer_abi_version(tracer_fd, &abi); + if (ret < 0) { + ERR("Failed to retrieve lttng-modules ABI version"); + goto error; + } + + /* + * RING_BUFFER_SNAPSHOT_SAMPLE_POSITIONS was introduced in 2.3 + */ + if (abi.major >= 2 && abi.minor >= 3) { + /* Supported */ + ret = 1; + } else { + /* Not supported */ + ret = 0; + } +error: + return ret; +} + +/* + * Rotate a kernel session. + * + * Return LTTNG_OK on success or else an LTTng error code. + */ +enum lttng_error_code kernel_rotate_session(struct ltt_session *session) +{ + int ret; + enum lttng_error_code status = LTTNG_OK; + struct consumer_socket *socket; + struct lttng_ht_iter iter; + struct ltt_kernel_session *ksess = session->kernel_session; + + assert(ksess); + assert(ksess->consumer); + + DBG("Rotate kernel session %s started (session %" PRIu64 ")", + session->name, session->id); + + rcu_read_lock(); + + /* + * Note that this loop will end after one iteration given that there is + * only one kernel consumer. + */ + cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter, + socket, node.node) { + struct ltt_kernel_channel *chan; + + /* For each channel, ask the consumer to rotate it. */ + cds_list_for_each_entry(chan, &ksess->channel_list.head, list) { + DBG("Rotate kernel channel %" PRIu64 ", session %s", + chan->key, session->name); + ret = consumer_rotate_channel(socket, chan->key, + ksess->uid, ksess->gid, ksess->consumer, + ksess->consumer->subdir, + /* is_metadata_channel */ false, + session->current_archive_id); + if (ret < 0) { + status = LTTNG_ERR_KERN_CONSUMER_FAIL; + goto error; + } + } + + /* + * Rotate the metadata channel. + */ + ret = consumer_rotate_channel(socket, ksess->metadata->key, + ksess->uid, ksess->gid, ksess->consumer, + ksess->consumer->subdir, + /* is_metadata_channel */ true, + session->current_archive_id); + if (ret < 0) { + status = LTTNG_ERR_KERN_CONSUMER_FAIL; + goto error; + } + } + +error: + rcu_read_unlock(); + return status; +}