Fix: cmd_rotate_session() returns unexpected error codes
[lttng-tools.git] / src / bin / lttng-sessiond / kernel.c
index e5f068a0cd9515408a34b4621201e8b75f714a81..4327db24bdb3291f575da0d90c143e280c7bdbfa 100644 (file)
@@ -33,6 +33,7 @@
 #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
@@ -40,6 +41,8 @@
  */
 static uint64_t next_kernel_channel_key;
 
+#include <lttng/userspace-probe.h>
+#include <lttng/userspace-probe-internal.h>
 /*
  * Add context on a kernel channel.
  *
@@ -190,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.
@@ -200,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;
@@ -267,6 +520,7 @@ int kernel_create_event(struct lttng_event *ev,
 
        return 0;
 
+add_callsite_error:
 enable_error:
 filter_error:
        {
@@ -989,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);
@@ -996,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;
@@ -1043,7 +1305,8 @@ int kernel_snapshot_record(struct ltt_kernel_session *ksess,
                        ret = consumer_snapshot_channel(socket, chan->key, output, 0,
                                        ksess->uid, ksess->gid,
                                        DEFAULT_KERNEL_TRACE_DIR, wait,
-                                       nb_packets_per_stream);
+                                       nb_packets_per_stream,
+                                       trace_archive_id);
                        if (ret < 0) {
                                ret = LTTNG_ERR_KERN_CONSUMER_FAIL;
                                (void) kernel_consumer_destroy_metadata(socket,
@@ -1055,7 +1318,8 @@ int kernel_snapshot_record(struct ltt_kernel_session *ksess,
                /* Snapshot metadata, */
                ret = consumer_snapshot_channel(socket, ksess->metadata->key, output,
                                1, ksess->uid, ksess->gid,
-                               DEFAULT_KERNEL_TRACE_DIR, wait, 0);
+                               DEFAULT_KERNEL_TRACE_DIR, wait, 0,
+                               trace_archive_id);
                if (ret < 0) {
                        ret = LTTNG_ERR_KERN_CONSUMER_FAIL;
                        goto error_consumer;
@@ -1133,3 +1397,66 @@ int kernel_supports_ring_buffer_snapshot_sample_positions(int tracer_fd)
 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;
+}
This page took 0.028811 seconds and 4 git commands to generate.