From 6dc3064a30b0cc7cfa9fdd22da1963525dfb7388 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 16 Apr 2013 13:44:09 -0400 Subject: [PATCH] Implement snapshot commands in lttng-sessiond 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 --- include/lttng/lttng-error.h | 4 +- src/bin/lttng-sessiond/Makefile.am | 3 +- src/bin/lttng-sessiond/cmd.c | 463 ++++++++++++++++++- src/bin/lttng-sessiond/cmd.h | 10 + src/bin/lttng-sessiond/consumer.c | 126 ++++- src/bin/lttng-sessiond/consumer.h | 14 + src/bin/lttng-sessiond/kernel-consumer.c | 12 +- src/bin/lttng-sessiond/kernel-consumer.h | 2 +- src/bin/lttng-sessiond/kernel.c | 102 ++++ src/bin/lttng-sessiond/kernel.h | 3 + src/bin/lttng-sessiond/main.c | 50 +- src/bin/lttng-sessiond/session.c | 7 + src/bin/lttng-sessiond/session.h | 8 + src/bin/lttng-sessiond/snapshot.c | 262 +++++++++++ src/bin/lttng-sessiond/snapshot.h | 69 +++ src/bin/lttng-sessiond/ust-app.c | 63 +++ src/bin/lttng-sessiond/ust-app.h | 8 + src/bin/lttng/commands/snapshot.c | 3 + src/common/consumer.h | 2 + src/common/error.c | 2 + src/common/kernel-consumer/kernel-consumer.c | 16 + src/common/sessiond-comm/sessiond-comm.h | 11 + src/common/uri.c | 8 +- src/common/ust-consumer/ust-consumer.c | 9 + tests/unit/Makefile.am | 2 + 25 files changed, 1219 insertions(+), 40 deletions(-) create mode 100644 src/bin/lttng-sessiond/snapshot.c create mode 100644 src/bin/lttng-sessiond/snapshot.h diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index be96d9f61..8d2e1d002 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -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 */ diff --git a/src/bin/lttng-sessiond/Makefile.am b/src/bin/lttng-sessiond/Makefile.am index ae8b6eb1e..77cc1d21b 100644 --- a/src/bin/lttng-sessiond/Makefile.am +++ b/src/bin/lttng-sessiond/Makefile.am @@ -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 \ diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index bcf349f92..6f7c71d05 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -17,6 +17,7 @@ #define _GNU_SOURCE #include +#include #include #include @@ -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. */ diff --git a/src/bin/lttng-sessiond/cmd.h b/src/bin/lttng-sessiond/cmd.h index d997f85cd..5be1688c9 100644 --- a/src/bin/lttng-sessiond/cmd.h +++ b/src/bin/lttng-sessiond/cmd.h @@ -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 */ diff --git a/src/bin/lttng-sessiond/consumer.c b/src/bin/lttng-sessiond/consumer.c index 2e2087885..d91c3e60b 100644 --- a/src/bin/lttng-sessiond/consumer.c +++ b/src/bin/lttng-sessiond/consumer.c @@ -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; +} diff --git a/src/bin/lttng-sessiond/consumer.h b/src/bin/lttng-sessiond/consumer.h index 09f4545a0..3c5cf155d 100644 --- a/src/bin/lttng-sessiond/consumer.h +++ b/src/bin/lttng-sessiond/consumer.h @@ -22,6 +22,11 @@ #include #include +#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 */ diff --git a/src/bin/lttng-sessiond/kernel-consumer.c b/src/bin/lttng-sessiond/kernel-consumer.c index f30c11ccc..044b1a81f 100644 --- a/src/bin/lttng-sessiond/kernel-consumer.c +++ b/src/bin/lttng-sessiond/kernel-consumer.c @@ -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; } diff --git a/src/bin/lttng-sessiond/kernel-consumer.h b/src/bin/lttng-sessiond/kernel-consumer.h index b9a424e54..9deb6b949 100644 --- a/src/bin/lttng-sessiond/kernel-consumer.h +++ b/src/bin/lttng-sessiond/kernel-consumer.h @@ -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); diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index 69665058a..154b2d623 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -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; +} diff --git a/src/bin/lttng-sessiond/kernel.h b/src/bin/lttng-sessiond/kernel.h index c0fe201d6..a87405a4d 100644 --- a/src/bin/lttng-sessiond/kernel.h +++ b/src/bin/lttng-sessiond/kernel.h @@ -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); diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 31f48a273..3b27c6f36 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -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: diff --git a/src/bin/lttng-sessiond/session.c b/src/bin/lttng-sessiond/session.c index b6b24b76c..5f5c2a6ee 100644 --- a/src/bin/lttng-sessiond/session.c +++ b/src/bin/lttng-sessiond/session.c @@ -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); diff --git a/src/bin/lttng-sessiond/session.h b/src/bin/lttng-sessiond/session.h index a0b24b2d7..63b5a068d 100644 --- a/src/bin/lttng-sessiond/session.h +++ b/src/bin/lttng-sessiond/session.h @@ -20,6 +20,9 @@ #include +#include + +#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 index 000000000..77255fa2c --- /dev/null +++ b/src/bin/lttng-sessiond/snapshot.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * 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 +#include +#include +#include + +#include + +#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 index 000000000..91215272a --- /dev/null +++ b/src/bin/lttng-sessiond/snapshot.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * 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 +#include + +#include +#include +#include + +#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 */ diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index 0f553a671..dd0f6f8db 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -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; +} diff --git a/src/bin/lttng-sessiond/ust-app.h b/src/bin/lttng-sessiond/ust-app.h index 30835e03f..298669440 100644 --- a/src/bin/lttng-sessiond/ust-app.h +++ b/src/bin/lttng-sessiond/ust-app.h @@ -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 */ diff --git a/src/bin/lttng/commands/snapshot.c b/src/bin/lttng/commands/snapshot.c index 866b55a38..cc0dd9629 100644 --- a/src/bin/lttng/commands/snapshot.c +++ b/src/bin/lttng/commands/snapshot.c @@ -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; } diff --git a/src/common/consumer.h b/src/common/consumer.h index b4c9aeafd..d0986ef1e 100644 --- a/src/common/consumer.h +++ b/src/common/consumer.h @@ -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 */ diff --git a/src/common/error.c b/src/common/error.c index 36907c942..416365f08 100644 --- a/src/common/error.c +++ b/src/common/error.c @@ -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" diff --git a/src/common/kernel-consumer/kernel-consumer.c b/src/common/kernel-consumer/kernel-consumer.c index 11830fc05..d4a0d7c67 100644 --- a/src/common/kernel-consumer/kernel-consumer.c +++ b/src/common/kernel-consumer/kernel-consumer.c @@ -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; } diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index aeca78e50..07c645cc7 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -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; diff --git a/src/common/uri.c b/src/common/uri.c index 8d7cf9074..e54eb387d 100644 --- a/src/common/uri.c +++ b/src/common/uri.c @@ -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, diff --git a/src/common/ust-consumer/ust-consumer.c b/src/common/ust-consumer/ust-consumer.c index f783d4058..8ade3b35f 100644 --- a/src/common/ust-consumer/ust-consumer.c +++ b/src/common/ust-consumer/ust-consumer.c @@ -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; } diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 256951223..f517fdcbb 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -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 -- 2.34.1