#include <common/common.h>
#include <common/sessiond-comm/sessiond-comm.h>
#include <common/relayd/relayd.h>
+#include <common/utils.h>
#include "channel.h"
#include "consumer.h"
DBG("Enabling channel %s for session %s", attr->name, session->name);
- /*
- * Don't try to enable a channel if the session has been started at
- * some point in time before. The tracer does not allow it.
- */
- if (session->started) {
- ret = LTTNG_ERR_TRACE_ALREADY_STARTED;
- goto error;
- }
-
rcu_read_lock();
switch (domain->type) {
kchan = trace_kernel_get_channel_by_name(attr->name,
session->kernel_session);
if (kchan == NULL) {
+ /*
+ * Don't try to create a channel if the session
+ * has been started at some point in time
+ * before. The tracer does not allow it.
+ */
+ if (session->started) {
+ ret = LTTNG_ERR_TRACE_ALREADY_STARTED;
+ goto error;
+ }
ret = channel_kernel_create(session->kernel_session, attr, wpipe);
+ if (attr->name[0] != '\0') {
+ session->kernel_session->has_non_default_channel = 1;
+ }
} else {
ret = channel_kernel_enable(session->kernel_session, kchan);
}
uchan = trace_ust_find_channel_by_name(chan_ht, attr->name);
if (uchan == NULL) {
+ /*
+ * Don't try to create a channel if the session
+ * has been started at some point in time
+ * before. The tracer does not allow it.
+ */
+ if (session->started) {
+ ret = LTTNG_ERR_TRACE_ALREADY_STARTED;
+ goto error;
+ }
ret = channel_ust_create(usess, attr, domain->buf_type);
+ if (attr->name[0] != '\0') {
+ usess->has_non_default_channel = 1;
+ }
} else {
ret = channel_ust_enable(usess, uchan);
}
ksess = session->kernel_session;
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (ksess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
kchan = trace_kernel_get_channel_by_name(channel_name, ksess);
if (kchan == NULL) {
ret = LTTNG_ERR_KERN_CHAN_NOT_FOUND;
usess = session->ust_session;
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (usess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
uchan = trace_ust_find_channel_by_name(usess->domain_global.channels,
channel_name);
if (uchan == NULL) {
ksess = session->kernel_session;
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (ksess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
kchan = trace_kernel_get_channel_by_name(channel_name, ksess);
if (kchan == NULL) {
ret = LTTNG_ERR_KERN_CHAN_NOT_FOUND;
usess = session->ust_session;
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (usess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
uchan = trace_ust_find_channel_by_name(usess->domain_global.channels,
channel_name);
if (uchan == NULL) {
}
chan_kern_created = 1;
}
-
/* Add kernel context to kernel tracer */
ret = context_kernel_add(session->kernel_session, ctx, channel_name);
if (ret != LTTNG_OK) {
case LTTNG_DOMAIN_UST:
{
struct ltt_ust_session *usess = session->ust_session;
+ unsigned int chan_count;
+
assert(usess);
- unsigned int chan_count =
- lttng_ht_get_count(usess->domain_global.channels);
+ chan_count = lttng_ht_get_count(usess->domain_global.channels);
if (chan_count == 0) {
struct lttng_channel *attr;
/* Create default channel */
{
struct ltt_kernel_channel *kchan;
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (session->kernel_session->has_non_default_channel
+ && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
kchan = trace_kernel_get_channel_by_name(channel_name,
session->kernel_session);
if (kchan == NULL) {
assert(usess);
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (usess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
/* Get channel from global UST domain */
uchan = trace_ust_find_channel_by_name(usess->domain_global.channels,
channel_name);
assert(session->kernel_session);
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (session->kernel_session->has_non_default_channel
+ && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
kchan = trace_kernel_get_channel_by_name(channel_name,
session->kernel_session);
if (kchan == NULL) {
assert(usess);
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (usess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
/* Get channel from global UST domain */
uchan = trace_ust_find_channel_by_name(usess->domain_global.channels,
channel_name);
return ret;
}
+/*
+ * Command LTTNG_CREATE_SESSION_SNAPSHOT processed by the client thread.
+ */
+int cmd_create_session_snapshot(char *name, struct lttng_uri *uris,
+ size_t nb_uri, lttng_sock_cred *creds)
+{
+ int ret;
+ struct ltt_session *session;
+ struct snapshot_output *new_output = NULL;
+
+ assert(name);
+ assert(creds);
+
+ /*
+ * Create session in no output mode with URIs set to NULL. The uris we've
+ * received are for a default snapshot output if one.
+ */
+ ret = cmd_create_session_uri(name, NULL, 0, creds);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ /* Get the newly created session pointer back. This should NEVER fail. */
+ session = session_find_by_name(name);
+ assert(session);
+
+ /* Flag session for snapshot mode. */
+ session->snapshot_mode = 1;
+
+ /* Skip snapshot output creation if no URI is given. */
+ if (nb_uri == 0) {
+ goto end;
+ }
+
+ new_output = snapshot_output_alloc();
+ if (!new_output) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error_snapshot_alloc;
+ }
+
+ ret = snapshot_output_init_with_uri(DEFAULT_SNAPSHOT_MAX_SIZE, NULL,
+ uris, nb_uri, session->consumer, new_output, &session->snapshot);
+ if (ret < 0) {
+ if (ret == -ENOMEM) {
+ ret = LTTNG_ERR_NOMEM;
+ } else {
+ ret = LTTNG_ERR_INVALID;
+ }
+ goto error_snapshot;
+ }
+
+ rcu_read_lock();
+ snapshot_add_output(&session->snapshot, new_output);
+ rcu_read_unlock();
+
+end:
+ return LTTNG_OK;
+
+error_snapshot:
+ snapshot_output_destroy(new_output);
+error_snapshot_alloc:
+ session_destroy(session);
+error:
+ return ret;
+}
+
/*
* Command LTTNG_DESTROY_SESSION processed by the client thread.
*/
strncpy(sessions[i].name, session->name, NAME_MAX);
sessions[i].name[NAME_MAX - 1] = '\0';
sessions[i].enabled = session->enabled;
+ sessions[i].snapshot_mode = session->snapshot_mode;
i++;
}
}
/*
* Command LTTNG_DATA_PENDING returning 0 if the data is NOT pending meaning
- * ready for trace analysis (or anykind of reader) or else 1 for pending data.
+ * ready for trace analysis (or any kind of reader) or else 1 for pending data.
*/
int cmd_data_pending(struct ltt_session *session)
{
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.
+ * Permission denied to create an output if the session is not
+ * set in no output mode.
*/
if (session->output_traces) {
ret = LTTNG_ERR_EPERM;
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) {
struct lttng_snapshot_output *output)
{
int ret;
- struct snapshot_output *sout;
+ struct snapshot_output *sout = NULL;
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.
+ * Permission 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 (output->id) {
+ DBG("Cmd snapshot del output id %" PRIu32 " for session %s", output->id,
+ session->name);
+ sout = snapshot_find_output_by_id(output->id, &session->snapshot);
+ } else if (*output->name != '\0') {
+ DBG("Cmd snapshot del output name %s for session %s", output->name,
+ session->name);
+ sout = snapshot_find_output_by_name(output->name, &session->snapshot);
+ }
if (!sout) {
ret = LTTNG_ERR_INVALID;
goto error;
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.
+ * Permission denied to create an output if the session is not
+ * set in no output mode.
*/
if (session->output_traces) {
ret = LTTNG_ERR_EPERM;
* 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.
+ * Return 0 on success or a LTTNG_ERR code.
*/
static int set_relayd_for_snapshot(struct consumer_output *consumer,
struct snapshot_output *snap_output, struct ltt_session *session)
{
- int ret = 0;
+ int ret = LTTNG_OK;
struct lttng_ht_iter iter;
struct consumer_socket *socket;
* snapshot output.
*/
rcu_read_lock();
- cds_lfht_for_each_entry(consumer->socks->ht, &iter.iter, socket,
- node.node) {
+ cds_lfht_for_each_entry(snap_output->consumer->socks->ht, &iter.iter,
+ socket, node.node) {
ret = send_consumer_relayd_sockets(0, session->id,
snap_output->consumer, socket);
- if (ret < 0) {
+ if (ret != LTTNG_OK) {
rcu_read_unlock();
goto error;
}
/*
* Record a kernel snapshot.
*
- * Return 0 on success or else a negative value.
+ * Return 0 on success or a LTTNG_ERR code.
*/
static int record_kernel_snapshot(struct ltt_kernel_session *ksess,
- struct snapshot_output *output, struct ltt_session *session, int wait)
+ struct snapshot_output *output, struct ltt_session *session,
+ int wait, int nb_streams)
{
int ret;
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;
+ /* Get the datetime for the snapshot output directory. */
+ ret = utils_get_current_time_str("%Y%m%d-%H%M%S", output->datetime,
+ sizeof(output->datetime));
+ if (!ret) {
+ ret = LTTNG_ERR_INVALID;
+ goto error;
}
- ret = set_relayd_for_snapshot(ksess->consumer, output, session);
+ /*
+ * Copy kernel session sockets so we can communicate with the right
+ * consumer for the snapshot record command.
+ */
+ ret = consumer_copy_sockets(output->consumer, ksess->consumer);
if (ret < 0) {
+ ret = LTTNG_ERR_NOMEM;
goto error;
}
- ret = kernel_snapshot_record(ksess, output, wait);
+ ret = set_relayd_for_snapshot(ksess->consumer, output, session);
+ if (ret != LTTNG_OK) {
+ goto error_snapshot;
+ }
+
+ ret = kernel_snapshot_record(ksess, output, wait, nb_streams);
if (ret < 0) {
- goto error;
+ if (ret == -EINVAL) {
+ ret = LTTNG_ERR_INVALID;
+ goto error_snapshot;
+ }
+
+ ret = LTTNG_ERR_SNAPSHOT_FAIL;
+ goto error_snapshot;
}
+ ret = LTTNG_OK;
+
+error_snapshot:
+ /* Clean up copied sockets so this output can use some other later on. */
+ consumer_destroy_output_sockets(output->consumer);
error:
return ret;
}
/*
* Record a UST snapshot.
*
- * Return 0 on success or else a negative value.
+ * Return 0 on success or a LTTNG_ERR error code.
*/
static int record_ust_snapshot(struct ltt_ust_session *usess,
- struct snapshot_output *output, struct ltt_session *session, int wait)
+ struct snapshot_output *output, struct ltt_session *session,
+ int wait, int nb_streams)
{
int ret;
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;
+ /* Get the datetime for the snapshot output directory. */
+ ret = utils_get_current_time_str("%Y%m%d-%H%M%S", output->datetime,
+ sizeof(output->datetime));
+ if (!ret) {
+ ret = LTTNG_ERR_INVALID;
+ goto error;
}
- ret = set_relayd_for_snapshot(usess->consumer, output, session);
+ /*
+ * Copy UST session sockets so we can communicate with the right
+ * consumer for the snapshot record command.
+ */
+ ret = consumer_copy_sockets(output->consumer, usess->consumer);
if (ret < 0) {
+ ret = LTTNG_ERR_NOMEM;
goto error;
}
- ret = ust_app_snapshot_record(usess, output, wait);
+ ret = set_relayd_for_snapshot(usess->consumer, output, session);
+ if (ret != LTTNG_OK) {
+ goto error_snapshot;
+ }
+
+ ret = ust_app_snapshot_record(usess, output, wait, nb_streams);
if (ret < 0) {
- goto error;
+ if (ret == -EINVAL) {
+ ret = LTTNG_ERR_INVALID;
+ goto error_snapshot;
+ }
+
+ ret = LTTNG_ERR_SNAPSHOT_FAIL;
+ goto error_snapshot;
}
+ ret = LTTNG_OK;
+
+error_snapshot:
+ /* Clean up copied sockets so this output can use some other later on. */
+ consumer_destroy_output_sockets(output->consumer);
error:
return ret;
}
+/*
+ * Returns the total number of streams for a session or a negative value
+ * on error.
+ */
+static unsigned int get_total_nb_stream(struct ltt_session *session)
+{
+ unsigned int total_streams = 0;
+
+ if (session->kernel_session) {
+ struct ltt_kernel_session *ksess = session->kernel_session;
+
+ total_streams += ksess->stream_count_global;
+ }
+
+ if (session->ust_session) {
+ struct ltt_ust_session *usess = session->ust_session;
+
+ total_streams += ust_app_get_nb_stream(usess);
+ }
+
+ return total_streams;
+}
+
/*
* Command LTTNG_SNAPSHOT_RECORD from lib lttng ctl.
*
struct lttng_snapshot_output *output, int wait)
{
int ret = LTTNG_OK;
- struct snapshot_output *tmp_sout = NULL;
+ unsigned int use_tmp_output = 0;
+ struct snapshot_output tmp_output;
+ unsigned int nb_streams, snapshot_success = 0;
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.
+ * Permission denied to create an output if the session is not
+ * set in no output mode.
*/
if (session->output_traces) {
ret = LTTNG_ERR_EPERM;
/* 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);
+ &tmp_output, NULL);
if (ret < 0) {
if (ret == -ENOMEM) {
ret = LTTNG_ERR_NOMEM;
}
goto error;
}
+ /* Use the global session count for the temporary snapshot. */
+ tmp_output.nb_snapshot = session->snapshot.nb_snapshot;
+ use_tmp_output = 1;
}
+ /*
+ * Get the total number of stream of that session which is used by the
+ * maximum size of the snapshot feature.
+ */
+ nb_streams = get_total_nb_stream(session);
+
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) {
+ if (use_tmp_output) {
+ ret = record_kernel_snapshot(ksess, &tmp_output, session,
+ wait, nb_streams);
+ if (ret != LTTNG_OK) {
goto error;
}
+ snapshot_success = 1;
} 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) {
+ /*
+ * Make a local copy of the output and assign the possible
+ * temporary value given by the caller.
+ */
+ memset(&tmp_output, 0, sizeof(tmp_output));
+ memcpy(&tmp_output, sout, sizeof(tmp_output));
+
+ /* Use temporary max size. */
+ if (output->max_size != (uint64_t) -1ULL) {
+ tmp_output.max_size = output->max_size;
+ }
+
+ /* Use temporary name. */
+ if (*output->name != '\0') {
+ strncpy(tmp_output.name, output->name,
+ sizeof(tmp_output.name));
+ }
+
+ tmp_output.nb_snapshot = session->snapshot.nb_snapshot;
+
+ ret = record_kernel_snapshot(ksess, &tmp_output,
+ session, wait, nb_streams);
+ if (ret != LTTNG_OK) {
rcu_read_unlock();
goto error;
}
+ snapshot_success = 1;
}
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) {
+ if (use_tmp_output) {
+ ret = record_ust_snapshot(usess, &tmp_output, session,
+ wait, nb_streams);
+ if (ret != LTTNG_OK) {
goto error;
}
+ snapshot_success = 1;
} 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) {
+ /*
+ * Make a local copy of the output and assign the possible
+ * temporary value given by the caller.
+ */
+ memset(&tmp_output, 0, sizeof(tmp_output));
+ memcpy(&tmp_output, sout, sizeof(tmp_output));
+
+ /* Use temporary max size. */
+ if (output->max_size != (uint64_t) -1ULL) {
+ tmp_output.max_size = output->max_size;
+ }
+
+ /* Use temporary name. */
+ if (*output->name != '\0') {
+ strncpy(tmp_output.name, output->name,
+ sizeof(tmp_output.name));
+ }
+
+ tmp_output.nb_snapshot = session->snapshot.nb_snapshot;
+
+ ret = record_ust_snapshot(usess, &tmp_output, session,
+ wait, nb_streams);
+ if (ret != LTTNG_OK) {
rcu_read_unlock();
goto error;
}
+ snapshot_success = 1;
}
rcu_read_unlock();
}
}
-error:
- if (tmp_sout) {
- snapshot_output_destroy(tmp_sout);
+ if (snapshot_success) {
+ session->snapshot.nb_snapshot++;
+ } else {
+ ret = LTTNG_ERR_SNAPSHOT_FAIL;
}
+
+error:
return ret;
}