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 */
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 */
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 \
#define _GNU_SOURCE
#include <assert.h>
+#include <inttypes.h>
#include <urcu/list.h>
#include <urcu/uatomic.h>
/*
* 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)
{
}
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);
/* 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;
* 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;
/* 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;
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;
}
}
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;
}
}
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.
*/
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 */
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) {
rcu_read_unlock();
}
+ socket->type = data->type;
+
DBG3("Consumer socket created (fd: %d) and added to output",
data->cmd_sock);
*/
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);
/* 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;
}
/*
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;
+}
#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,
unsigned int data_sock_sent;
struct lttng_ht_node_ulong node;
+
+ enum lttng_consumer_type type;
};
struct consumer_data {
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);
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 */
* 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];
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 */
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;
}
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);
#include "consumer.h"
#include "kernel.h"
+#include "kernel-consumer.h"
#include "kern-modules.h"
/*
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;
+}
#define _LTT_KERNEL_CTL_H
#include "session.h"
+#include "snapshot.h"
#include "trace-kernel.h"
/*
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);
lus->uid = session->uid;
lus->gid = session->gid;
+
session->ust_session = lus;
/* Copy session output to the newly created UST session */
}
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:
pthread_mutex_destroy(&session->lock);
consumer_destroy_output(session->consumer);
+ snapshot_destroy(&session->snapshot);
free(session);
return LTTNG_OK;
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);
#include <urcu/list.h>
+#include <common/hashtable/hashtable.h>
+
+#include "snapshot.h"
#include "trace-kernel.h"
struct ltt_ust_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 */
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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 */
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;
+}
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 */
{
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 */
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;
}
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 */
[ 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"
}
}
+ 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;
*/
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;
}
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;
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;
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,
}
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;
}
$(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
$(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