Implement snapshot commands in lttng-sessiond
authorDavid Goulet <dgoulet@efficios.com>
Tue, 16 Apr 2013 17:44:09 +0000 (13:44 -0400)
committerDavid Goulet <dgoulet@efficios.com>
Thu, 27 Jun 2013 18:03:31 +0000 (14:03 -0400)
Basic functionalities are implemented in the session daemon so handle
snapshots and send the command to the consumer but note that at this
commit the consumer does not handle snapshot.

Signed-off-by: David Goulet <dgoulet@efficios.com>
25 files changed:
include/lttng/lttng-error.h
src/bin/lttng-sessiond/Makefile.am
src/bin/lttng-sessiond/cmd.c
src/bin/lttng-sessiond/cmd.h
src/bin/lttng-sessiond/consumer.c
src/bin/lttng-sessiond/consumer.h
src/bin/lttng-sessiond/kernel-consumer.c
src/bin/lttng-sessiond/kernel-consumer.h
src/bin/lttng-sessiond/kernel.c
src/bin/lttng-sessiond/kernel.h
src/bin/lttng-sessiond/main.c
src/bin/lttng-sessiond/session.c
src/bin/lttng-sessiond/session.h
src/bin/lttng-sessiond/snapshot.c [new file with mode: 0644]
src/bin/lttng-sessiond/snapshot.h [new file with mode: 0644]
src/bin/lttng-sessiond/ust-app.c
src/bin/lttng-sessiond/ust-app.h
src/bin/lttng/commands/snapshot.c
src/common/consumer.h
src/common/error.c
src/common/kernel-consumer/kernel-consumer.c
src/common/sessiond-comm/sessiond-comm.h
src/common/uri.c
src/common/ust-consumer/ust-consumer.c
tests/unit/Makefile.am

index be96d9f61f7b8d53cd42bb5a54bd5f7cd5f9dab1..8d2e1d0022bb101dc528e0b67a50019214fce6a5 100644 (file)
@@ -61,7 +61,7 @@ enum lttng_error_code {
        LTTNG_ERR_EXIST_SESS             = 28,  /* Session name already exist */
        LTTNG_ERR_NO_EVENT               = 29,  /* No event found */
        LTTNG_ERR_CONNECT_FAIL           = 30,  /* Unable to connect to unix socket */
-       /* 31 */
+       LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST  = 31,  /* Snapshot output already exists */
        LTTNG_ERR_EPERM                  = 32,  /* Permission denied */
        LTTNG_ERR_KERN_NA                = 33,  /* Kernel tracer unavalable */
        LTTNG_ERR_KERN_VERSION           = 34,  /* Kernel tracer not compatible */
@@ -80,7 +80,7 @@ enum lttng_error_code {
        LTTNG_ERR_KERN_STOP_FAIL         = 47,  /* Kernel stop trace failed */
        LTTNG_ERR_KERN_CONSUMER_FAIL     = 48,  /* Kernel consumer start failed */
        LTTNG_ERR_KERN_STREAM_FAIL       = 49,  /* Kernel create stream failed */
-       /* 50 */
+       LTTNG_ERR_START_SESSION_ONCE     = 50,  /* Session needs to be started once. */
        /* 51 */
        /* 52 */
        LTTNG_ERR_KERN_LIST_FAIL         = 53,  /* Kernel listing events failed */
index ae8b6eb1e476ef8310a75ee473fa7cbba3afd502..77cc1d21bd07acae7699d5a659175b8907797710 100644 (file)
@@ -25,7 +25,8 @@ lttng_sessiond_SOURCES = utils.c utils.h \
                        health.c health.h \
                        cmd.c cmd.h \
                        buffer-registry.c buffer-registry.h \
-                       testpoint.h ht-cleanup.c
+                       testpoint.h ht-cleanup.c \
+                       snapshot.c snapshot.h
 
 if HAVE_LIBLTTNG_UST_CTL
 lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \
index bcf349f9290476de0268dce0e9a535e331d6dedc..6f7c71d0549474746ef78243f3204cd9ba014ab2 100644 (file)
@@ -17,6 +17,7 @@
 
 #define _GNU_SOURCE
 #include <assert.h>
+#include <inttypes.h>
 #include <urcu/list.h>
 #include <urcu/uatomic.h>
 
@@ -546,7 +547,7 @@ error:
 /*
  * Connect to the relayd using URI and send the socket to the right consumer.
  */
-static int send_consumer_relayd_socket(int domain, struct ltt_session *session,
+static int send_consumer_relayd_socket(int domain, unsigned int session_id,
                struct lttng_uri *relayd_uri, struct consumer_output *consumer,
                struct consumer_socket *consumer_sock)
 {
@@ -560,11 +561,6 @@ static int send_consumer_relayd_socket(int domain, struct ltt_session *session,
        }
        assert(rsock);
 
-       /* If the control socket is connected, network session is ready */
-       if (relayd_uri->stype == LTTNG_STREAM_CONTROL) {
-               session->net_handle = 1;
-       }
-
        /* Set the network sequence index if not set. */
        if (consumer->net_seq_index == (uint64_t) -1ULL) {
                pthread_mutex_lock(&relayd_net_seq_idx_lock);
@@ -579,7 +575,7 @@ static int send_consumer_relayd_socket(int domain, struct ltt_session *session,
 
        /* Send relayd socket to consumer. */
        ret = consumer_send_relayd_socket(consumer_sock, rsock, consumer,
-                       relayd_uri->stype, session->id);
+                       relayd_uri->stype, session_id);
        if (ret < 0) {
                ret = LTTNG_ERR_ENABLE_CONSUMER_FAIL;
                goto close_sock;
@@ -620,18 +616,17 @@ error:
  * helper function to facilitate sending the information to the consumer for a
  * session.
  */
-static int send_consumer_relayd_sockets(int domain,
-               struct ltt_session *session, struct consumer_output *consumer,
-               struct consumer_socket *sock)
+static int send_consumer_relayd_sockets(int domain, unsigned int session_id,
+               struct consumer_output *consumer, struct consumer_socket *sock)
 {
        int ret = LTTNG_OK;
 
-       assert(session);
        assert(consumer);
+       assert(sock);
 
        /* Sending control relayd socket. */
        if (!sock->control_sock_sent) {
-               ret = send_consumer_relayd_socket(domain, session,
+               ret = send_consumer_relayd_socket(domain, session_id,
                                &consumer->dst.net.control, consumer, sock);
                if (ret != LTTNG_OK) {
                        goto error;
@@ -640,7 +635,7 @@ static int send_consumer_relayd_sockets(int domain,
 
        /* Sending data relayd socket. */
        if (!sock->data_sock_sent) {
-               ret = send_consumer_relayd_socket(domain, session,
+               ret = send_consumer_relayd_socket(domain, session_id,
                                &consumer->dst.net.data, consumer, sock);
                if (ret != LTTNG_OK) {
                        goto error;
@@ -682,12 +677,14 @@ int cmd_setup_relayd(struct ltt_session *session)
                        assert(socket->fd >= 0);
 
                        pthread_mutex_lock(socket->lock);
-                       ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_UST, session,
+                       ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_UST, session->id,
                                        usess->consumer, socket);
                        pthread_mutex_unlock(socket->lock);
                        if (ret != LTTNG_OK) {
                                goto error;
                        }
+                       /* Session is now ready for network streaming. */
+                       session->net_handle = 1;
                }
        }
 
@@ -699,12 +696,14 @@ int cmd_setup_relayd(struct ltt_session *session)
                        assert(socket->fd >= 0);
 
                        pthread_mutex_lock(socket->lock);
-                       ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_KERNEL, session,
+                       ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_KERNEL, session->id,
                                        ksess->consumer, socket);
                        pthread_mutex_unlock(socket->lock);
                        if (ret != LTTNG_OK) {
                                goto error;
                        }
+                       /* Session is now ready for network streaming. */
+                       session->net_handle = 1;
                }
        }
 
@@ -2138,6 +2137,440 @@ error:
        return ret;
 }
 
+/*
+ * Command LTTNG_SNAPSHOT_ADD_OUTPUT from the lttng ctl library.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR code.
+ */
+int cmd_snapshot_add_output(struct ltt_session *session,
+               struct lttng_snapshot_output *output, uint32_t *id)
+{
+       int ret;
+       struct snapshot_output *new_output;
+
+       assert(session);
+       assert(output);
+
+       DBG("Cmd snapshot add output for session %s", session->name);
+
+       /*
+        * Persmission denied to create an output if the session is not set in no
+        * output mode.
+        */
+       if (session->output_traces) {
+               ret = LTTNG_ERR_EPERM;
+               goto error;
+       }
+
+       /* Only one output is allowed until we have the "tee" feature. */
+       if (session->snapshot.nb_output == 1) {
+               ret = LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST;
+               goto error;
+       }
+
+       new_output = snapshot_output_alloc();
+       if (!new_output) {
+               ret = LTTNG_ERR_NOMEM;
+               goto error;
+       }
+
+       ret = snapshot_output_init(output->max_size, output->name,
+                       output->ctrl_url, output->data_url, session->consumer, new_output,
+                       &session->snapshot);
+       if (ret < 0) {
+               if (ret == -ENOMEM) {
+                       ret = LTTNG_ERR_NOMEM;
+               } else {
+                       ret = LTTNG_ERR_INVALID;
+               }
+               goto free_error;
+       }
+
+       /*
+        * Copy sockets so the snapshot output can use them on destroy.
+        */
+
+       if (session->ust_session) {
+               ret = consumer_copy_sockets(new_output->consumer,
+                               session->ust_session->consumer);
+               if (ret < 0) {
+                       goto free_error;
+               }
+               new_output->ust_sockets_copied = 1;
+       }
+       if (session->kernel_session) {
+               ret = consumer_copy_sockets(new_output->consumer,
+                               session->kernel_session->consumer);
+               if (ret < 0) {
+                       goto free_error;
+               }
+               new_output->kernel_sockets_copied = 1;
+       }
+
+       rcu_read_lock();
+       snapshot_add_output(&session->snapshot, new_output);
+       if (id) {
+               *id = new_output->id;
+       }
+       rcu_read_unlock();
+
+       return LTTNG_OK;
+
+free_error:
+       snapshot_output_destroy(new_output);
+error:
+       return ret;
+}
+
+/*
+ * Command LTTNG_SNAPSHOT_DEL_OUTPUT from lib lttng ctl.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR code.
+ */
+int cmd_snapshot_del_output(struct ltt_session *session,
+               struct lttng_snapshot_output *output)
+{
+       int ret;
+       struct snapshot_output *sout;
+
+       assert(session);
+       assert(output);
+
+       DBG("Cmd snapshot del output id %" PRIu32 " for session %s", output->id,
+                       session->name);
+
+       rcu_read_lock();
+
+       /*
+        * Persmission denied to create an output if the session is not set in no
+        * output mode.
+        */
+       if (session->output_traces) {
+               ret = LTTNG_ERR_EPERM;
+               goto error;
+       }
+
+       sout = snapshot_find_output_by_id(output->id, &session->snapshot);
+       if (!sout) {
+               ret = LTTNG_ERR_INVALID;
+               goto error;
+       }
+
+       snapshot_delete_output(&session->snapshot, sout);
+       snapshot_output_destroy(sout);
+       ret = LTTNG_OK;
+
+error:
+       rcu_read_unlock();
+       return ret;
+}
+
+/*
+ * Command LTTNG_SNAPSHOT_LIST_OUTPUT from lib lttng ctl.
+ *
+ * If no output is available, outputs is untouched and 0 is returned.
+ *
+ * Return the size of the newly allocated outputs or a negative LTTNG_ERR code.
+ */
+ssize_t cmd_snapshot_list_outputs(struct ltt_session *session,
+               struct lttng_snapshot_output **outputs)
+{
+       int ret, idx = 0;
+       struct lttng_snapshot_output *list;
+       struct lttng_ht_iter iter;
+       struct snapshot_output *output;
+
+       assert(session);
+       assert(outputs);
+
+       DBG("Cmd snapshot list outputs for session %s", session->name);
+
+       /*
+        * Persmission denied to create an output if the session is not set in no
+        * output mode.
+        */
+       if (session->output_traces) {
+               ret = LTTNG_ERR_EPERM;
+               goto error;
+       }
+
+       if (session->snapshot.nb_output == 0) {
+               ret = 0;
+               goto error;
+       }
+
+       list = zmalloc(session->snapshot.nb_output * sizeof(*list));
+       if (!list) {
+               ret = LTTNG_ERR_NOMEM;
+               goto error;
+       }
+
+       /* Copy list from session to the new list object. */
+       cds_lfht_for_each_entry(session->snapshot.output_ht->ht, &iter.iter,
+                       output, node.node) {
+               assert(output->consumer);
+               list[idx].id = output->id;
+               list[idx].max_size = output->max_size;
+               strncpy(list[idx].name, output->name, sizeof(list[idx].name));
+               if (output->consumer->type == CONSUMER_DST_LOCAL) {
+                       strncpy(list[idx].ctrl_url, output->consumer->dst.trace_path,
+                                       sizeof(list[idx].ctrl_url));
+               } else {
+                       /* Control URI. */
+                       ret = uri_to_str_url(&output->consumer->dst.net.control,
+                                       list[idx].ctrl_url, sizeof(list[idx].ctrl_url));
+                       if (ret < 0) {
+                               ret = LTTNG_ERR_NOMEM;
+                               goto free_error;
+                       }
+
+                       /* Data URI. */
+                       ret = uri_to_str_url(&output->consumer->dst.net.data,
+                                       list[idx].data_url, sizeof(list[idx].data_url));
+                       if (ret < 0) {
+                               ret = LTTNG_ERR_NOMEM;
+                               goto free_error;
+                       }
+               }
+               idx++;
+       }
+
+       *outputs = list;
+       return session->snapshot.nb_output;
+
+free_error:
+       free(list);
+error:
+       return -ret;
+}
+
+/*
+ * Send relayd sockets from snapshot output to consumer. Ignore request if the
+ * snapshot output is *not* set with a remote destination.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int set_relayd_for_snapshot(struct consumer_output *consumer,
+               struct snapshot_output *snap_output, struct ltt_session *session)
+{
+       int ret = 0;
+       struct lttng_ht_iter iter;
+       struct consumer_socket *socket;
+
+       assert(consumer);
+       assert(snap_output);
+       assert(session);
+
+       DBG2("Set relayd object from snapshot output");
+
+       /* Ignore if snapshot consumer output is not network. */
+       if (snap_output->consumer->type != CONSUMER_DST_NET) {
+               goto error;
+       }
+
+       /*
+        * For each consumer socket, create and send the relayd object of the
+        * snapshot output.
+        */
+       rcu_read_lock();
+       cds_lfht_for_each_entry(consumer->socks->ht, &iter.iter, socket,
+                       node.node) {
+               ret = send_consumer_relayd_sockets(0, session->id,
+                               snap_output->consumer, socket);
+               if (ret < 0) {
+                       rcu_read_unlock();
+                       goto error;
+               }
+       }
+       rcu_read_unlock();
+
+error:
+       return ret;
+}
+
+/*
+ * Record a kernel snapshot.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int record_kernel_snapshot(struct ltt_kernel_session *ksess,
+               struct snapshot_output *output, struct ltt_session *session, int wait)
+{
+       int ret;
+
+       assert(ksess);
+       assert(output);
+       assert(session);
+
+       if (!output->kernel_sockets_copied) {
+               ret = consumer_copy_sockets(output->consumer, ksess->consumer);
+               if (ret < 0) {
+                       goto error;
+               }
+               output->kernel_sockets_copied = 1;
+       }
+
+       ret = set_relayd_for_snapshot(ksess->consumer, output, session);
+       if (ret < 0) {
+               goto error;
+       }
+
+       ret = kernel_snapshot_record(ksess, output, wait);
+       if (ret < 0) {
+               goto error;
+       }
+
+error:
+       return ret;
+}
+
+/*
+ * Record a UST snapshot.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int record_ust_snapshot(struct ltt_ust_session *usess,
+               struct snapshot_output *output, struct ltt_session *session, int wait)
+{
+       int ret;
+
+       assert(usess);
+       assert(output);
+       assert(session);
+
+       if (!output->ust_sockets_copied) {
+               ret = consumer_copy_sockets(output->consumer, usess->consumer);
+               if (ret < 0) {
+                       goto error;
+               }
+               output->ust_sockets_copied = 1;
+       }
+
+       ret = set_relayd_for_snapshot(usess->consumer, output, session);
+       if (ret < 0) {
+               goto error;
+       }
+
+       ret = ust_app_snapshot_record(usess, output, wait);
+       if (ret < 0) {
+               goto error;
+       }
+
+error:
+       return ret;
+}
+
+/*
+ * Command LTTNG_SNAPSHOT_RECORD from lib lttng ctl.
+ *
+ * The wait parameter is ignored so this call always wait for the snapshot to
+ * complete before returning.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR code.
+ */
+int cmd_snapshot_record(struct ltt_session *session,
+               struct lttng_snapshot_output *output, int wait)
+{
+       int ret = LTTNG_OK;
+       struct snapshot_output *tmp_sout = NULL;
+
+       assert(session);
+
+       DBG("Cmd snapshot record for session %s", session->name);
+
+       /*
+        * Persmission denied to create an output if the session is not set in no
+        * output mode.
+        */
+       if (session->output_traces) {
+               ret = LTTNG_ERR_EPERM;
+               goto error;
+       }
+
+       /* The session needs to be started at least once. */
+       if (!session->started) {
+               ret = LTTNG_ERR_START_SESSION_ONCE;
+               goto error;
+       }
+
+       /* Use temporary output for the session. */
+       if (output && *output->ctrl_url != '\0') {
+               tmp_sout = snapshot_output_alloc();
+               if (!tmp_sout) {
+                       ret = LTTNG_ERR_NOMEM;
+                       goto error;
+               }
+
+               ret = snapshot_output_init(output->max_size, output->name,
+                               output->ctrl_url, output->data_url, session->consumer,
+                               tmp_sout, NULL);
+               if (ret < 0) {
+                       if (ret == -ENOMEM) {
+                               ret = LTTNG_ERR_NOMEM;
+                       } else {
+                               ret = LTTNG_ERR_INVALID;
+                       }
+                       goto error;
+               }
+       }
+
+       if (session->kernel_session) {
+               struct ltt_kernel_session *ksess = session->kernel_session;
+
+               if (tmp_sout) {
+                       ret = record_kernel_snapshot(ksess, tmp_sout, session, wait);
+                       if (ret < 0) {
+                               goto error;
+                       }
+               } else {
+                       struct snapshot_output *sout;
+                       struct lttng_ht_iter iter;
+
+                       rcu_read_lock();
+                       cds_lfht_for_each_entry(session->snapshot.output_ht->ht,
+                                       &iter.iter, sout, node.node) {
+                               ret = record_kernel_snapshot(ksess, sout, session, wait);
+                               if (ret < 0) {
+                                       rcu_read_unlock();
+                                       goto error;
+                               }
+                       }
+                       rcu_read_unlock();
+               }
+       }
+
+       if (session->ust_session) {
+               struct ltt_ust_session *usess = session->ust_session;
+
+               if (tmp_sout) {
+                       ret = record_ust_snapshot(usess, tmp_sout, session, wait);
+                       if (ret < 0) {
+                               goto error;
+                       }
+               } else {
+                       struct snapshot_output *sout;
+                       struct lttng_ht_iter iter;
+
+                       rcu_read_lock();
+                       cds_lfht_for_each_entry(session->snapshot.output_ht->ht,
+                                       &iter.iter, sout, node.node) {
+                               ret = record_ust_snapshot(usess, tmp_sout, session, wait);
+                               if (ret < 0) {
+                                       rcu_read_unlock();
+                                       goto error;
+                               }
+                       }
+                       rcu_read_unlock();
+               }
+       }
+
+error:
+       if (tmp_sout) {
+               snapshot_output_destroy(tmp_sout);
+       }
+       return ret;
+}
+
 /*
  * Init command subsystem.
  */
index d997f85cddbcb634e3511b9418df7492ac7d8fbb..5be1688c9c97b502bd3292820741a618eb3afa61 100644 (file)
@@ -81,8 +81,18 @@ void cmd_list_lttng_sessions(struct lttng_session *sessions, uid_t uid,
 ssize_t cmd_list_tracepoint_fields(int domain,
                struct lttng_event_field **fields);
 ssize_t cmd_list_tracepoints(int domain, struct lttng_event **events);
+ssize_t cmd_snapshot_list_outputs(struct ltt_session *session,
+               struct lttng_snapshot_output **outputs);
 
 int cmd_calibrate(int domain, struct lttng_calibrate *calibrate);
 int cmd_data_pending(struct ltt_session *session);
 
+/* Snapshot */
+int cmd_snapshot_add_output(struct ltt_session *session,
+               struct lttng_snapshot_output *output, uint32_t *id);
+int cmd_snapshot_del_output(struct ltt_session *session,
+               struct lttng_snapshot_output *output);
+int cmd_snapshot_record(struct ltt_session *session,
+               struct lttng_snapshot_output *output, int wait);
+
 #endif /* CMD_H */
index 2e20878856fd1ee1ab1637b6ff297044bda58d77..d91c3e60bf0e16156232ca3a2f251d0d6ae779e6 100644 (file)
@@ -170,7 +170,7 @@ void consumer_output_send_destroy_relayd(struct consumer_output *consumer)
        assert(consumer);
 
        /* Destroy any relayd connection */
-       if (consumer && consumer->type == CONSUMER_DST_NET) {
+       if (consumer->type == CONSUMER_DST_NET) {
                rcu_read_lock();
                cds_lfht_for_each_entry(consumer->socks->ht, &iter.iter, socket,
                                node.node) {
@@ -226,6 +226,8 @@ int consumer_create_socket(struct consumer_data *data,
                rcu_read_unlock();
        }
 
+       socket->type = data->type;
+
        DBG3("Consumer socket created (fd: %d) and added to output",
                        data->cmd_sock);
 
@@ -442,9 +444,8 @@ void consumer_destroy_output(struct consumer_output *obj)
  */
 struct consumer_output *consumer_copy_output(struct consumer_output *obj)
 {
+       int ret;
        struct lttng_ht *tmp_ht_ptr;
-       struct lttng_ht_iter iter;
-       struct consumer_socket *socket, *copy_sock;
        struct consumer_output *output;
 
        assert(obj);
@@ -461,27 +462,63 @@ struct consumer_output *consumer_copy_output(struct consumer_output *obj)
        /* Putting back the HT pointer and start copying socket(s). */
        output->socks = tmp_ht_ptr;
 
+       ret = consumer_copy_sockets(output, obj);
+       if (ret < 0) {
+               goto malloc_error;
+       }
+
+error:
+       return output;
+
+malloc_error:
+       consumer_destroy_output(output);
+       return NULL;
+}
+
+/*
+ * Copy consumer sockets from src to dst.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int consumer_copy_sockets(struct consumer_output *dst,
+               struct consumer_output *src)
+{
+       int ret = 0;
+       struct lttng_ht_iter iter;
+       struct consumer_socket *socket, *copy_sock;
+
+       assert(dst);
+       assert(src);
+
        rcu_read_lock();
-       cds_lfht_for_each_entry(obj->socks->ht, &iter.iter, socket, node.node) {
+       cds_lfht_for_each_entry(src->socks->ht, &iter.iter, socket, node.node) {
+               /* Ignore socket that are already there. */
+               copy_sock = consumer_find_socket(socket->fd, dst);
+               if (copy_sock) {
+                       continue;
+               }
+
                /* Create new socket object. */
                copy_sock = consumer_allocate_socket(socket->fd);
                if (copy_sock == NULL) {
                        rcu_read_unlock();
-                       goto malloc_error;
+                       ret = -ENOMEM;
+                       goto error;
                }
 
                copy_sock->registered = socket->registered;
+               /*
+                * This is valid because this lock is shared accross all consumer
+                * object being the global lock of the consumer data structure of the
+                * session daemon.
+                */
                copy_sock->lock = socket->lock;
-               consumer_add_socket(copy_sock, output);
+               consumer_add_socket(copy_sock, dst);
        }
        rcu_read_unlock();
 
 error:
-       return output;
-
-malloc_error:
-       consumer_destroy_output(output);
-       return NULL;
+       return ret;
 }
 
 /*
@@ -1143,3 +1180,70 @@ end:
        health_code_update();
        return ret;
 }
+
+/*
+ * Ask the consumer to snapshot a specific channel using the key.
+ *
+ * Return 0 on success or else a negative error.
+ */
+int consumer_snapshot_channel(struct consumer_socket *socket, uint64_t key,
+               struct snapshot_output *output, int metadata, uid_t uid, gid_t gid,
+               int wait)
+{
+       int ret;
+       struct lttcomm_consumer_msg msg;
+
+       assert(socket);
+       assert(socket->fd >= 0);
+       assert(output);
+       assert(output->consumer);
+
+       DBG("Consumer snapshot channel key %" PRIu64, key);
+
+       memset(&msg, 0, sizeof(msg));
+
+       msg.cmd_type = LTTNG_CONSUMER_SNAPSHOT_CHANNEL;
+       msg.u.snapshot_channel.key = key;
+       msg.u.snapshot_channel.max_size = output->max_size;
+       msg.u.snapshot_channel.metadata = metadata;
+
+       if (output->consumer->type == CONSUMER_DST_NET) {
+               msg.u.snapshot_channel.relayd_id = output->consumer->net_seq_index;
+               msg.u.snapshot_channel.use_relayd = 1;
+               ret = snprintf(msg.u.snapshot_channel.pathname,
+                               sizeof(msg.u.snapshot_channel.pathname), "%s/%s",
+                               output->consumer->subdir, DEFAULT_SNAPSHOT_NAME);
+               if (ret < 0) {
+                       ret = -LTTNG_ERR_NOMEM;
+                       goto error;
+               }
+       } else {
+               ret = snprintf(msg.u.snapshot_channel.pathname,
+                               sizeof(msg.u.snapshot_channel.pathname), "%s/%s",
+                               output->consumer->dst.trace_path, DEFAULT_SNAPSHOT_NAME);
+               if (ret < 0) {
+                       ret = -LTTNG_ERR_NOMEM;
+                       goto error;
+               }
+
+               /* Create directory. Ignore if exist. */
+               ret = run_as_mkdir_recursive(msg.u.snapshot_channel.pathname,
+                               S_IRWXU | S_IRWXG, uid, gid);
+               if (ret < 0) {
+                       if (ret != -EEXIST) {
+                               ERR("Trace directory creation error");
+                               goto error;
+                       }
+               }
+       }
+
+       health_code_update();
+       ret = consumer_send_msg(socket, &msg);
+       if (ret < 0) {
+               goto error;
+       }
+
+error:
+       health_code_update();
+       return ret;
+}
index 09f4545a03fdc1e40fb777aaff1c4f9cdf278b2d..3c5cf155d07ed14764d9504bbb4f4e0f70d69d95 100644 (file)
 #include <common/hashtable/hashtable.h>
 #include <lttng/lttng.h>
 
+#include "snapshot.h"
+
+struct snapshot;
+struct snapshot_output;
+
 enum consumer_dst_type {
        CONSUMER_DST_LOCAL,
        CONSUMER_DST_NET,
@@ -48,6 +53,8 @@ struct consumer_socket {
        unsigned int data_sock_sent;
 
        struct lttng_ht_node_ulong node;
+
+       enum lttng_consumer_type type;
 };
 
 struct consumer_data {
@@ -156,6 +163,8 @@ void consumer_add_socket(struct consumer_socket *sock,
 void consumer_del_socket(struct consumer_socket *sock,
                struct consumer_output *consumer);
 void consumer_destroy_socket(struct consumer_socket *sock);
+int consumer_copy_sockets(struct consumer_output *dst,
+               struct consumer_output *src);
 
 struct consumer_output *consumer_create_output(enum consumer_dst_type type);
 struct consumer_output *consumer_copy_output(struct consumer_output *obj);
@@ -233,4 +242,9 @@ int consumer_push_metadata(struct consumer_socket *socket,
                size_t target_offset);
 int consumer_flush_channel(struct consumer_socket *socket, uint64_t key);
 
+/* Snapshot command. */
+int consumer_snapshot_channel(struct consumer_socket *socket, uint64_t key,
+               struct snapshot_output *output, int metadata, uid_t uid, gid_t gid,
+               int wait);
+
 #endif /* _CONSUMER_H */
index f30c11ccc2fa2be882e77218220b5da7b746ed10..044b1a81f58f19540a527370beadade74a6f297c 100644 (file)
@@ -115,7 +115,7 @@ error:
  * Sending metadata to the consumer with command ADD_CHANNEL and ADD_STREAM.
  */
 int kernel_consumer_add_metadata(struct consumer_socket *sock,
-               struct ltt_kernel_session *session)
+               struct ltt_kernel_session *session, int no_monitor)
 {
        int ret;
        char tmp_path[PATH_MAX];
@@ -195,6 +195,14 @@ int kernel_consumer_add_metadata(struct consumer_socket *sock,
                        session->metadata_stream_fd,
                        0); /* CPU: 0 for metadata. */
 
+       /*
+        * Set the no monitor flag. If set to 1, it indicates the consumer to NOT
+        * monitor the stream but rather add it to a special list in the associated
+        * channel. This is used to handle ephemeral stream used by the snapshot
+        * command or store streams for the flight recorder mode.
+        */
+       lkm.u.stream.no_monitor = no_monitor;
+
        health_code_update();
 
        /* Send stream and file descriptor */
@@ -323,7 +331,7 @@ int kernel_consumer_send_session(struct consumer_socket *sock,
        DBG("Sending session stream to kernel consumer");
 
        if (session->metadata_stream_fd >= 0) {
-               ret = kernel_consumer_add_metadata(sock, session);
+               ret = kernel_consumer_add_metadata(sock, session, 0);
                if (ret < 0) {
                        goto error;
                }
index b9a424e541924e608cd9ba0a224f50eeb2772ca7..9deb6b949803f0cb728b7be2af13fffedb2a1f9a 100644 (file)
@@ -32,7 +32,7 @@ int kernel_consumer_add_stream(struct consumer_socket *sock,
                struct ltt_kernel_session *session);
 
 int kernel_consumer_add_metadata(struct consumer_socket *sock,
-               struct ltt_kernel_session *session);
+               struct ltt_kernel_session *session, int no_monitor);
 
 int kernel_consumer_add_channel(struct consumer_socket *sock,
                struct ltt_kernel_channel *channel, struct ltt_kernel_session *session);
index 69665058a0e098c0b7a2bbd9dc1042d6f7b25f0a..154b2d62306df654d63a148ebf5005bc6d09218c 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "consumer.h"
 #include "kernel.h"
+#include "kernel-consumer.h"
 #include "kern-modules.h"
 
 /*
@@ -788,3 +789,104 @@ void kernel_destroy_channel(struct ltt_kernel_channel *kchan)
                ksess->channel_count--;
        }
 }
+
+/*
+ * Take a snapshot for a given kernel session.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int kernel_snapshot_record(struct ltt_kernel_session *ksess,
+               struct snapshot_output *output, int wait)
+{
+       int ret, saved_metadata_fd;
+       struct consumer_socket *socket;
+       struct lttng_ht_iter iter;
+       struct ltt_kernel_metadata *saved_metadata;
+
+       assert(ksess);
+       assert(ksess->consumer);
+       assert(output);
+
+       DBG("Kernel snapshot record started");
+
+       /* Save current metadata since the following calls will change it. */
+       saved_metadata = ksess->metadata;
+       saved_metadata_fd = ksess->metadata_stream_fd;
+
+       rcu_read_lock();
+
+       ret = kernel_open_metadata(ksess);
+       if (ret < 0) {
+               ret = LTTNG_ERR_KERN_META_FAIL;
+               goto error;
+       }
+
+       ret = kernel_open_metadata_stream(ksess);
+       if (ret < 0) {
+               ret = LTTNG_ERR_KERN_META_FAIL;
+               goto error_open_stream;
+       }
+
+       /* Send metadata to consumer and snapshot everything. */
+       cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter,
+                       socket, node.node) {
+               struct consumer_output *saved_output;
+               struct ltt_kernel_channel *chan;
+               /* Code flow error */
+               assert(socket->fd >= 0);
+
+               /*
+                * Temporarly switch consumer output for our snapshot output. As long
+                * as the session lock is taken, this is safe.
+                */
+               saved_output = ksess->consumer;
+               ksess->consumer = output->consumer;
+
+               pthread_mutex_lock(socket->lock);
+               /* This stream must not be monitored by the consumer. */
+               ret = kernel_consumer_add_metadata(socket, ksess, 1);
+               ret = 0;
+               pthread_mutex_unlock(socket->lock);
+               /* Put back the savec consumer output into the session. */
+               ksess->consumer = saved_output;
+               if (ret < 0) {
+                       ret = LTTNG_ERR_KERN_CONSUMER_FAIL;
+                       goto error_consumer;
+               }
+
+               /* For each channel, ask the consumer to snapshot it. */
+               cds_list_for_each_entry(chan, &ksess->channel_list.head, list) {
+                       ret = consumer_snapshot_channel(socket, chan->fd, output, 0,
+                                       ksess->uid, ksess->gid, wait);
+                       if (ret < 0) {
+                               ret = LTTNG_ERR_KERN_CONSUMER_FAIL;
+                               goto error_consumer;
+                       }
+               }
+
+               /* Snapshot metadata, */
+               ret = consumer_snapshot_channel(socket, ksess->metadata->fd, output,
+                               1, ksess->uid, ksess->gid, wait);
+               if (ret < 0) {
+                       ret = LTTNG_ERR_KERN_CONSUMER_FAIL;
+                       goto error_consumer;
+               }
+       }
+
+error_consumer:
+       /* Close newly opened metadata stream. It's now on the consumer side. */
+       ret = close(ksess->metadata_stream_fd);
+       if (ret < 0) {
+               PERROR("close snapshot kernel");
+       }
+
+error_open_stream:
+       trace_kernel_destroy_metadata(ksess->metadata);
+error:
+       /* Restore metadata state.*/
+       ksess->metadata = saved_metadata;
+       ksess->metadata_stream_fd = saved_metadata_fd;
+
+       rcu_read_unlock();
+       return ret;
+}
index c0fe201d6720e68c70e1f3624c51b296a125fa52..a87405a4dfcd7e0372f4461b90f512a83a987713 100644 (file)
@@ -19,6 +19,7 @@
 #define _LTT_KERNEL_CTL_H
 
 #include "session.h"
+#include "snapshot.h"
 #include "trace-kernel.h"
 
 /*
@@ -53,6 +54,8 @@ int kernel_calibrate(int fd, struct lttng_kernel_calibrate *calibrate);
 int kernel_validate_version(int tracer_fd);
 void kernel_destroy_session(struct ltt_kernel_session *ksess);
 void kernel_destroy_channel(struct ltt_kernel_channel *kchan);
+int kernel_snapshot_record(struct ltt_kernel_session *ksess,
+               struct snapshot_output *output, int wait);
 
 int init_kernel_workarounds(void);
 
index 31f48a2739fb0216ef9645ca4bb4a51162244ed6..3b27c6f36fee19ca345add26ba6f427c2a253f27 100644 (file)
@@ -2377,6 +2377,7 @@ static int create_ust_session(struct ltt_session *session,
 
        lus->uid = session->uid;
        lus->gid = session->gid;
+
        session->ust_session = lus;
 
        /* Copy session output to the newly created UST session */
@@ -3182,22 +3183,63 @@ skip_domain:
        }
        case LTTNG_SNAPSHOT_ADD_OUTPUT:
        {
-               ret = LTTNG_ERR_UND;
+               struct lttcomm_lttng_output_id reply;
+
+               ret = cmd_snapshot_add_output(cmd_ctx->session,
+                               &cmd_ctx->lsm->u.snapshot_output.output, &reply.id);
+               if (ret != LTTNG_OK) {
+                       goto error;
+               }
+
+               ret = setup_lttng_msg(cmd_ctx, sizeof(reply));
+               if (ret < 0) {
+                       goto setup_error;
+               }
+
+               /* Copy output list into message payload */
+               memcpy(cmd_ctx->llm->payload, &reply, sizeof(reply));
+               ret = LTTNG_OK;
                break;
        }
        case LTTNG_SNAPSHOT_DEL_OUTPUT:
        {
-               ret = LTTNG_ERR_UND;
+               ret = cmd_snapshot_del_output(cmd_ctx->session,
+                               &cmd_ctx->lsm->u.snapshot_output.output);
                break;
        }
        case LTTNG_SNAPSHOT_LIST_OUTPUT:
        {
-               ret = LTTNG_ERR_UND;
+               ssize_t nb_output;
+               struct lttng_snapshot_output *outputs = NULL;
+
+               nb_output = cmd_snapshot_list_outputs(cmd_ctx->session, &outputs);
+               if (nb_output < 0) {
+                       ret = -nb_output;
+                       goto error;
+               }
+
+               ret = setup_lttng_msg(cmd_ctx,
+                               nb_output * sizeof(struct lttng_snapshot_output));
+               if (ret < 0) {
+                       free(outputs);
+                       goto setup_error;
+               }
+
+               if (outputs) {
+                       /* Copy output list into message payload */
+                       memcpy(cmd_ctx->llm->payload, outputs,
+                                       nb_output * sizeof(struct lttng_snapshot_output));
+                       free(outputs);
+               }
+
+               ret = LTTNG_OK;
                break;
        }
        case LTTNG_SNAPSHOT_RECORD:
        {
-               ret = LTTNG_ERR_UND;
+               ret = cmd_snapshot_record(cmd_ctx->session,
+                               &cmd_ctx->lsm->u.snapshot_record.output,
+                               cmd_ctx->lsm->u.snapshot_record.wait);
                break;
        }
        default:
index b6b24b76c3f9ae28cec31d3c9e3224351647a2ef..5f5c2a6ee08fe202cff46a1adf461ec3c6118d5f 100644 (file)
@@ -159,6 +159,7 @@ int session_destroy(struct ltt_session *session)
        pthread_mutex_destroy(&session->lock);
 
        consumer_destroy_output(session->consumer);
+       snapshot_destroy(&session->snapshot);
        free(session);
 
        return LTTNG_OK;
@@ -202,6 +203,12 @@ int session_create(char *name, uid_t uid, gid_t gid)
        new_session->uid = uid;
        new_session->gid = gid;
 
+       ret = snapshot_init(&new_session->snapshot);
+       if (ret < 0) {
+               ret = LTTNG_ERR_NOMEM;
+               goto error;
+       }
+
        /* Add new session to the session list */
        session_lock_list();
        new_session->id = add_session_list(new_session);
index a0b24b2d70259af1afc4067518ffcf78b9fb094d..63b5a068d033462f4d1167b46c0a78d9aa42cfd8 100644 (file)
@@ -20,6 +20,9 @@
 
 #include <urcu/list.h>
 
+#include <common/hashtable/hashtable.h>
+
+#include "snapshot.h"
 #include "trace-kernel.h"
 
 struct ltt_ust_session;
@@ -85,6 +88,11 @@ struct ltt_session {
 
        /* Did a start command occured before the kern/ust session creation? */
        unsigned int started;
+
+       /* Snapshot representation in a session. */
+       struct snapshot snapshot;
+       /* Indicate if the session has to output the traces or not. */
+       unsigned int output_traces;
 };
 
 /* Prototypes */
diff --git a/src/bin/lttng-sessiond/snapshot.c b/src/bin/lttng-sessiond/snapshot.c
new file mode 100644 (file)
index 0000000..77255fa
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <inttypes.h>
+#include <string.h>
+#include <urcu/uatomic.h>
+
+#include <common/defaults.h>
+
+#include "snapshot.h"
+
+/*
+ * Return the atomically incremented value of next_output_id.
+ */
+static inline unsigned long get_next_output_id(struct snapshot *snapshot)
+{
+       return uatomic_add_return(&snapshot->next_output_id, 1);
+}
+
+/*
+ * Initialize a snapshot output object using the given parameters. The name
+ * value and url can be NULL.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int snapshot_output_init(uint64_t max_size, const char *name,
+               const char *ctrl_url, const char *data_url,
+               struct consumer_output *consumer, struct snapshot_output *output,
+               struct snapshot *snapshot)
+{
+       int ret = 0, nb_uri, i;
+       struct lttng_uri *uris = NULL;
+
+       assert(output);
+
+       output->max_size = max_size;
+       if (snapshot) {
+               output->id = get_next_output_id(snapshot);
+       }
+       lttng_ht_node_init_ulong(&output->node, (unsigned long) output->id);
+
+       if (name) {
+               strncpy(output->name, name, sizeof(output->name));
+       } else {
+               /* Set default name. */
+               ret = snprintf(output->name, sizeof(output->name), "%s-%" PRIu32,
+                               DEFAULT_SNAPSHOT_NAME, output->id);
+               if (ret < 0) {
+                       ret = -ENOMEM;
+                       goto error;
+               }
+       }
+
+       if (!consumer) {
+               goto end;
+       }
+
+       /* Create an array of URIs from URLs. */
+       nb_uri = uri_parse_str_urls(ctrl_url, data_url, &uris);
+       if (nb_uri < 0) {
+               ret = nb_uri;
+               goto error;
+       }
+
+       output->consumer = consumer_copy_output(consumer);
+       if (!output->consumer) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       /* No URL given. */
+       if (nb_uri == 0) {
+               ret = 0;
+               goto end;
+       }
+
+       if (uris[0].dtype == LTTNG_DST_PATH) {
+               memset(output->consumer->dst.trace_path, 0,
+                               sizeof(output->consumer->dst.trace_path));
+               strncpy(output->consumer->dst.trace_path, uris[0].dst.path,
+                               sizeof(output->consumer->dst.trace_path));
+               output->consumer->type = CONSUMER_DST_LOCAL;
+               ret = 0;
+               goto end;
+       }
+
+       if (nb_uri != 2) {
+               /* Absolutely needs two URIs for network. */
+               ret = -LTTNG_ERR_INVALID;
+               goto error;
+       }
+
+       for (i = 0; i < nb_uri; i ++) {
+               /* Network URIs */
+               ret = consumer_set_network_uri(output->consumer, &uris[i]);
+               if (ret < 0) {
+                       goto error;
+               }
+       }
+
+error:
+end:
+       free(uris);
+       return ret;
+}
+
+struct snapshot_output *snapshot_output_alloc(void)
+{
+       return zmalloc(sizeof(struct snapshot_output));
+}
+
+/*
+ * Delete output from the snapshot object.
+ */
+void snapshot_delete_output(struct snapshot *snapshot,
+               struct snapshot_output *output)
+{
+       int ret;
+       struct lttng_ht_iter iter;
+
+       assert(snapshot);
+       assert(snapshot->output_ht);
+       assert(output);
+
+       iter.iter.node = &output->node.node;
+       rcu_read_lock();
+       ret = lttng_ht_del(snapshot->output_ht, &iter);
+       rcu_read_unlock();
+       assert(!ret);
+       /*
+        * This is safe because the ownership of a snapshot object is in a session
+        * for which the session lock need to be acquired to read and modify it.
+        */
+       snapshot->nb_output--;
+}
+
+/*
+ * Add output object to the snapshot.
+ */
+void snapshot_add_output(struct snapshot *snapshot,
+               struct snapshot_output *output)
+{
+       assert(snapshot);
+       assert(snapshot->output_ht);
+       assert(output);
+
+       rcu_read_lock();
+       lttng_ht_add_unique_ulong(snapshot->output_ht, &output->node);
+       rcu_read_unlock();
+       /*
+        * This is safe because the ownership of a snapshot object is in a session
+        * for which the session lock need to be acquired to read and modify it.
+        */
+       snapshot->nb_output++;
+}
+
+/*
+ * Destroy and free a snapshot output object.
+ */
+void snapshot_output_destroy(struct snapshot_output *obj)
+{
+       assert(obj);
+
+       if (obj->consumer) {
+               consumer_output_send_destroy_relayd(obj->consumer);
+               consumer_destroy_output(obj->consumer);
+       }
+       free(obj);
+}
+
+/*
+ * RCU read side lock MUST be acquired before calling this since the returned
+ * pointer is in a RCU hash table.
+ *
+ * Return the reference on success or else NULL.
+ */
+struct snapshot_output *snapshot_find_output_by_id(uint32_t id,
+               struct snapshot *snapshot)
+{
+       struct lttng_ht_node_ulong *node;
+       struct lttng_ht_iter iter;
+       struct snapshot_output *output = NULL;
+
+       assert(snapshot);
+
+       lttng_ht_lookup(snapshot->output_ht, (void *)((unsigned long) id), &iter);
+       node = lttng_ht_iter_get_node_ulong(&iter);
+       if (!node) {
+               DBG3("Snapshot output not found with id %" PRId32, id);
+               goto error;
+       }
+       output = caa_container_of(node, struct snapshot_output, node);
+
+error:
+       return output;
+}
+
+struct snapshot *snapshot_alloc(void)
+{
+       return zmalloc(sizeof(struct snapshot));
+}
+
+/*
+ * Initialized a snapshot object that was already allocated.
+ *
+ * Return 0 on success or else a negative errno value.
+ */
+int snapshot_init(struct snapshot *obj)
+{
+       int ret;
+
+       assert(obj);
+
+       memset(obj, 0, sizeof(struct snapshot));
+
+       obj->output_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+       if (!obj->output_ht) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       ret = 0;
+
+error:
+       return ret;
+}
+
+/*
+ * Destroy snapshot object but the pointer is not freed so it's safe to pass a
+ * static reference.
+ */
+void snapshot_destroy(struct snapshot *obj)
+{
+       struct lttng_ht_iter iter;
+       struct snapshot_output *output;
+
+       assert(obj);
+
+       rcu_read_lock();
+       cds_lfht_for_each_entry(obj->output_ht->ht, &iter.iter, output,
+                       node.node) {
+               snapshot_delete_output(obj, output);
+               snapshot_output_destroy(output);
+       }
+       rcu_read_unlock();
+}
diff --git a/src/bin/lttng-sessiond/snapshot.h b/src/bin/lttng-sessiond/snapshot.h
new file mode 100644 (file)
index 0000000..9121527
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef SNAPSHOT_H
+#define SNAPSHOT_H
+
+#include <limits.h>
+#include <stdint.h>
+
+#include <common/common.h>
+#include <common/hashtable/hashtable.h>
+#include <common/uri.h>
+
+#include "consumer.h"
+
+struct consumer_output;
+
+struct snapshot_output {
+       uint32_t id;
+       uint64_t max_size;
+       char name[NAME_MAX];
+       struct consumer_output *consumer;
+       int kernel_sockets_copied;
+       int ust_sockets_copied;
+
+       /* Indexed by ID. */
+       struct lttng_ht_node_ulong node;
+};
+
+struct snapshot {
+       unsigned long next_output_id;
+       size_t nb_output;
+       struct lttng_ht *output_ht;
+};
+
+/* Snapshot object. */
+struct snapshot *snapshot_alloc(void);
+void snapshot_destroy(struct snapshot *obj);
+int snapshot_init(struct snapshot *obj);
+void snapshot_delete_output(struct snapshot *snapshot,
+               struct snapshot_output *output);
+void snapshot_add_output(struct snapshot *snapshot,
+               struct snapshot_output *output);
+
+/* Snapshot output object. */
+struct snapshot_output *snapshot_output_alloc(void);
+void snapshot_output_destroy(struct snapshot_output *obj);
+int snapshot_output_init(uint64_t max_size, const char *name,
+               const char *ctrl_url, const char *data_url,
+               struct consumer_output *consumer, struct snapshot_output *output,
+               struct snapshot *snapshot);
+struct snapshot_output *snapshot_find_output_by_id(uint32_t id,
+               struct snapshot *snapshot);
+
+#endif /* SNAPSHOT_H */
index 0f553a671603845a6bdd40b9819505963d27c45b..dd0f6f8db96c81fcf1e2cf643c951c725857c272 100644 (file)
@@ -4845,3 +4845,66 @@ void ust_app_destroy(struct ust_app *app)
 
        call_rcu(&app->pid_n.head, delete_ust_app_rcu);
 }
+
+/*
+ * Take a snapshot for a given UST session. The snapshot is sent to the given
+ * output.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int ust_app_snapshot_record(struct ltt_ust_session *usess,
+               struct snapshot_output *output, int wait)
+{
+       int ret = 0;
+       struct lttng_ht_iter iter;
+       struct ust_app *app;
+
+       assert(usess);
+       assert(output);
+
+       rcu_read_lock();
+
+       cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+               struct consumer_socket *socket;
+               struct lttng_ht_iter chan_iter;
+               struct ust_app_channel *ua_chan;
+               struct ust_app_session *ua_sess;
+               struct ust_registry_session *registry;
+
+               ua_sess = lookup_session_by_app(usess, app);
+               if (!ua_sess) {
+                       /* Session not associated with this app. */
+                       continue;
+               }
+
+               /* Get the right consumer socket for the application. */
+               socket = consumer_find_socket_by_bitness(app->bits_per_long,
+                               output->consumer);
+               if (!socket) {
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               cds_lfht_for_each_entry(ua_sess->channels->ht, &chan_iter.iter,
+                               ua_chan, node.node) {
+                       ret = consumer_snapshot_channel(socket, ua_chan->key, output, 0,
+                                       ua_sess->euid, ua_sess->egid, wait);
+                       if (ret < 0) {
+                               goto error;
+                       }
+               }
+
+               registry = get_session_registry(ua_sess);
+               assert(registry);
+               ret = consumer_snapshot_channel(socket, registry->metadata_key, output,
+                               1, ua_sess->euid, ua_sess->egid, wait);
+               if (ret < 0) {
+                       goto error;
+               }
+
+       }
+
+error:
+       rcu_read_unlock();
+       return ret;
+}
index 30835e03fb064523b6298073e656b99b33bcf0ae..29866944057acaa8f491e372bff081908ac71eb3 100644 (file)
@@ -306,6 +306,8 @@ void ust_app_notify_sock_unregister(int sock);
 ssize_t ust_app_push_metadata(struct ust_registry_session *registry,
                struct consumer_socket *socket, int send_zero_data);
 void ust_app_destroy(struct ust_app *app);
+int ust_app_snapshot_record(struct ltt_ust_session *usess,
+               struct snapshot_output *output, int wait);
 
 #else /* HAVE_LIBLTTNG_UST_CTL */
 
@@ -503,6 +505,12 @@ void ust_app_destroy(struct ust_app *app)
 {
        return;
 }
+static inline
+int ust_app_snapshot_record(struct ltt_ust_session *usess,
+               struct snapshot_output *output, int wait)
+{
+       return 0;
+}
 
 #endif /* HAVE_LIBLTTNG_UST_CTL */
 
index 866b55a38549c3e0cc41cc179a9b3b2ae4d089a2..cc0dd9629d4d37902a7f6603313b89671ea9584a 100644 (file)
@@ -460,6 +460,9 @@ int cmd_snapshot(int argc, const char **argv)
 
        ret = handle_command(poptGetArgs(pc));
        if (ret < 0) {
+               if (ret == -LTTNG_ERR_EPERM) {
+                       ERR("The session needs to be set in no output mode (--no-output)");
+               }
                ERR("%s", lttng_strerror(ret));
                goto end;
        }
index b4c9aeafd135002c189a1c27e50a5c29a9cc53b2..d0986ef1e3203a420f2054b75da65ab94cdfcd77 100644 (file)
@@ -54,6 +54,8 @@ enum lttng_consumer_command {
        LTTNG_CONSUMER_CLOSE_METADATA,
        LTTNG_CONSUMER_SETUP_METADATA,
        LTTNG_CONSUMER_FLUSH_CHANNEL,
+       LTTNG_CONSUMER_SNAPSHOT_CHANNEL,
+       LTTNG_CONSUMER_SNAPSHOT_METADATA,
 };
 
 /* State of each fd in consumer */
index 36907c942896e539c49615f6b79763445633e7da..416365f08a77432bfc4e13233c09b6a957ea18cd 100644 (file)
@@ -108,6 +108,8 @@ static const char *error_string_array[] = {
        [ ERROR_INDEX(LTTNG_ERR_BUFFER_NOT_SUPPORTED)] = "Buffer type not supported",
        [ ERROR_INDEX(LTTNG_ERR_BUFFER_TYPE_MISMATCH)] = "Buffer type mismatch for session",
        [ ERROR_INDEX(LTTNG_ERR_NOMEM)] = "Not enough memory",
+       [ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST) ] = "Snapshot output already exists",
+       [ ERROR_INDEX(LTTNG_ERR_START_SESSION_ONCE) ] = "Session needs to be started once",
 
        /* Last element */
        [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code"
index 11830fc05a087f4f601a3156560ac5b7a6735365..d4a0d7c67c2810a649821cda28a2dc6748d0388d 100644 (file)
@@ -317,6 +317,13 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                        }
                }
 
+               if (msg.u.stream.no_monitor) {
+                       DBG("Kernel consumer add stream %s in no monitor mode with"
+                                       "relayd id %" PRIu64, new_stream->name,
+                                       new_stream->relayd_stream_id);
+                       break;
+               }
+
                /* Get the right pipe where the stream will be sent. */
                if (new_stream->metadata_flag) {
                        stream_pipe = ctx->consumer_metadata_pipe;
@@ -400,6 +407,15 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                 */
                break;
        }
+       case LTTNG_CONSUMER_SNAPSHOT_CHANNEL:
+       {
+               ret = consumer_send_status_msg(sock, ret_code);
+               if (ret < 0) {
+                       /* Somehow, the session daemon is not responding anymore. */
+                       goto end_nosignal;
+               }
+               break;
+       }
        default:
                goto end_nosignal;
        }
index aeca78e50b9d2b3efb998028ab8e49225d0e4ecc..07c645cc77c93ec021b503ae39eb030a96015a06 100644 (file)
@@ -324,6 +324,8 @@ struct lttcomm_consumer_msg {
                        uint64_t stream_key;
                        uint64_t channel_key;
                        int32_t cpu;    /* On which CPU this stream is assigned. */
+                       /* Tells the consumer if the stream should be or not monitored. */
+                       uint32_t no_monitor;
                } LTTNG_PACKED stream;  /* Only used by Kernel. */
                struct {
                        uint64_t net_index;
@@ -379,6 +381,15 @@ struct lttcomm_consumer_msg {
                struct {
                        uint64_t key;   /* Channel key. */
                } LTTNG_PACKED flush_channel;
+               struct {
+                       char pathname[PATH_MAX];
+                       /* Indicate if the snapshot goes on the relayd or locally. */
+                       uint32_t use_relayd;
+                       uint32_t metadata;              /* This a metadata snapshot. */
+                       uint64_t relayd_id;             /* Relayd id if apply. */
+                       uint64_t key;
+                       uint64_t max_size;
+               } LTTNG_PACKED snapshot_channel;
        } u;
 } LTTNG_PACKED;
 
index 8d7cf9074beb02ab5f9981edd1f790dfd042070d..e54eb387d14fefb0f1923cd1f01b4287fc8255ae 100644 (file)
@@ -194,13 +194,13 @@ int uri_to_str_url(struct lttng_uri *uri, char *dst, size_t size)
        if (uri->dtype == LTTNG_DST_PATH) {
                ipver = 0;
                addr = uri->dst.path;
-               (void) snprintf(proto, sizeof(proto), "file");
-               (void) snprintf(port, sizeof(port), "%s", "");
+               (void) snprintf(proto, sizeof(proto) + 1, "file");
+               (void) snprintf(port, sizeof(port) + 1, "%s", "");
        } else {
                ipver = (uri->dtype == LTTNG_DST_IPV4) ? 4 : 6;
                addr = (ipver == 4) ?  uri->dst.ipv4 : uri->dst.ipv6;
-               (void) snprintf(proto, sizeof(proto), "net%d", ipver);
-               (void) snprintf(port, sizeof(port), ":%d", uri->port);
+               (void) snprintf(proto, sizeof(proto) + 1, "net%d", ipver);
+               (void) snprintf(port, sizeof(port) + 1, ":%d", uri->port);
        }
 
        ret = snprintf(dst, size, "%s://%s%s%s%s/%s", proto,
index f783d4058c550033983045c93cbeddcc363028d3..8ade3b35f272bfd6a0d77eb42094f8e521cbb026 100644 (file)
@@ -1146,6 +1146,15 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx,
                }
                goto end_msg_sessiond;
        }
+       case LTTNG_CONSUMER_SNAPSHOT_CHANNEL:
+       {
+               ret = consumer_send_status_msg(sock, ret_code);
+               if (ret < 0) {
+                       /* Somehow, the session daemon is not responding anymore. */
+                       goto end_nosignal;
+               }
+               break;
+       }
        default:
                break;
        }
index 2569512239365870dcdc1abaefea70ac310384ae..f517fdcbbc28c6bcfcca07f44b62992801365317 100644 (file)
@@ -30,6 +30,7 @@ SESSIONS=$(top_builddir)/src/bin/lttng-sessiond/session.o     \
         $(top_builddir)/src/bin/lttng-sessiond/consumer.o \
         $(top_builddir)/src/bin/lttng-sessiond/utils.o \
         $(top_builddir)/src/bin/lttng-sessiond/health.o \
+        $(top_builddir)/src/bin/lttng-sessiond/snapshot.o \
         $(top_builddir)/src/common/.libs/uri.o \
         $(top_builddir)/src/common/.libs/utils.o \
         $(top_builddir)/src/common/.libs/error.o
@@ -52,6 +53,7 @@ UST_DATA_TRACE=$(top_builddir)/src/bin/lttng-sessiond/trace-ust.o \
                   $(top_builddir)/src/bin/lttng-sessiond/fd-limit.o \
                   $(top_builddir)/src/bin/lttng-sessiond/health.o \
                   $(top_builddir)/src/bin/lttng-sessiond/session.o \
+                  $(top_builddir)/src/bin/lttng-sessiond/snapshot.o \
                   $(top_builddir)/src/common/.libs/uri.o \
                   $(top_builddir)/src/common/.libs/utils.o
 
This page took 0.08063 seconds and 4 git commands to generate.