goto error;
}
- if (session->output_traces && !session->current_trace_chunk) {
+ if (session->output_traces && !session->current_trace_chunk &&
+ session_output_supports_trace_chunks(session)) {
struct lttng_trace_chunk *trace_chunk;
trace_chunk = session_create_new_trace_chunk(
- session, NULL, NULL);
+ session, NULL, NULL, NULL);
if (!trace_chunk) {
ret = LTTNG_ERR_CREATE_DIR_FAIL;
goto error;
if (session->most_recent_chunk_id.is_set &&
session->most_recent_chunk_id.value != 0 &&
- session->current_trace_chunk) {
- ret = cmd_rotate_session(session, NULL);
+ session->current_trace_chunk && session->output_traces) {
+ /*
+ * Perform a last rotation on destruction if rotations have
+ * occurred during the session's lifetime.
+ */
+ ret = cmd_rotate_session(session, NULL, false);
if (ret != LTTNG_OK) {
ERR("Failed to perform an implicit rotation as part of the destruction of session \"%s\": %s",
session->name, lttng_strerror(-ret));
if (reply_context) {
reply_context->implicit_rotation_on_destroy = true;
}
- }
+ } else if (session->has_been_started && session->current_trace_chunk &&
+ session_output_supports_trace_chunks(session)) {
+ /*
+ * The user has not triggered a session rotation. However, to
+ * ensure all data has been consumed, the session is rotated
+ * to a 'null' trace chunk before it is destroyed.
+ *
+ * This is a "quiet" rotation meaning that no notification is
+ * emitted and no renaming of the current trace chunk takes
+ * place.
+ */
+ ret = cmd_rotate_session(session, NULL, true);
+ if (ret != LTTNG_OK) {
+ ERR("Failed to perform a quiet rotation as part of the destruction of session \"%s\": %s",
+ session->name, lttng_strerror(-ret));
+ }
+ }
if (session->shm_path[0]) {
/*
* Return LTTNG_OK on success or a LTTNG_ERR code.
*/
static enum lttng_error_code set_relayd_for_snapshot(
- struct consumer_output *consumer,
- const struct snapshot_output *snap_output,
+ struct consumer_output *output,
const struct ltt_session *session)
{
enum lttng_error_code status = LTTNG_OK;
struct consumer_socket *socket;
LTTNG_OPTIONAL(uint64_t) current_chunk_id = {};
- assert(consumer);
- assert(snap_output);
+ assert(output);
assert(session);
DBG2("Set relayd object from snapshot output");
if (session->current_trace_chunk) {
- enum lttng_trace_chunk_status status = lttng_trace_chunk_get_id(
- session->current_trace_chunk, ¤t_chunk_id.value);
+ enum lttng_trace_chunk_status chunk_status =
+ lttng_trace_chunk_get_id(
+ session->current_trace_chunk,
+ ¤t_chunk_id.value);
- if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
+ if (chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK) {
current_chunk_id.is_set = true;
} else {
ERR("Failed to get current trace chunk id");
}
/* Ignore if snapshot consumer output is not network. */
- if (snap_output->consumer->type != CONSUMER_DST_NET) {
+ if (output->type != CONSUMER_DST_NET) {
goto error;
}
* snapshot output.
*/
rcu_read_lock();
- cds_lfht_for_each_entry(snap_output->consumer->socks->ht, &iter.iter,
+ cds_lfht_for_each_entry(output->socks->ht, &iter.iter,
socket, node.node) {
pthread_mutex_lock(socket->lock);
status = send_consumer_relayd_sockets(0, session->id,
- snap_output->consumer, socket,
+ output, socket,
session->name, session->hostname,
session->live_timer,
current_chunk_id.is_set ? ¤t_chunk_id.value : NULL,
*/
static enum lttng_error_code record_kernel_snapshot(
struct ltt_kernel_session *ksess,
- const struct snapshot_output *output,
+ const struct consumer_output *output,
const struct ltt_session *session,
int wait, uint64_t nb_packets_per_stream)
{
- int ret;
enum lttng_error_code status;
assert(ksess);
assert(output);
assert(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) {
- status = LTTNG_ERR_NOMEM;
- goto error;
- }
-
- status = set_relayd_for_snapshot(ksess->consumer, output, session);
- if (status != LTTNG_OK) {
- goto error_snapshot;
- }
-
- status = kernel_snapshot_record(ksess, output, wait, nb_packets_per_stream);
- if (status != LTTNG_OK) {
- goto error_snapshot;
- }
-
- goto end;
-
-error_snapshot:
- /* Clean up copied sockets so this output can use some other later on. */
- consumer_destroy_output_sockets(output->consumer);
-error:
-end:
+ status = kernel_snapshot_record(
+ ksess, output, wait, nb_packets_per_stream);
return status;
}
* Returns LTTNG_OK on success or a LTTNG_ERR error code.
*/
static enum lttng_error_code record_ust_snapshot(struct ltt_ust_session *usess,
- const struct snapshot_output *output,
- const struct ltt_session *session, int wait,
- uint64_t nb_packets_per_stream)
+ const struct consumer_output *output,
+ const struct ltt_session *session,
+ int wait, uint64_t nb_packets_per_stream)
{
- int ret;
enum lttng_error_code status;
assert(usess);
assert(output);
assert(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) {
- status = LTTNG_ERR_NOMEM;
- goto error;
- }
-
- status = set_relayd_for_snapshot(usess->consumer, output, session);
- if (status != LTTNG_OK) {
- goto error_snapshot;
- }
-
- status = ust_app_snapshot_record(usess, output, wait,
- nb_packets_per_stream);
- if (status != LTTNG_OK) {
- goto error_snapshot;
- }
-
- goto end;
-
-error_snapshot:
- /* Clean up copied sockets so this output can use some other later on. */
- consumer_destroy_output_sockets(output->consumer);
-error:
-end:
+ status = ust_app_snapshot_record(
+ usess, output, wait, nb_packets_per_stream);
return status;
}
enum lttng_error_code snapshot_record(struct ltt_session *session,
const struct snapshot_output *snapshot_output, int wait)
{
- int fmt_ret;
int64_t nb_packets_per_stream;
char snapshot_chunk_name[LTTNG_NAME_MAX];
- enum lttng_error_code ret = LTTNG_OK;
+ int ret;
+ enum lttng_error_code ret_code = LTTNG_OK;
struct lttng_trace_chunk *snapshot_trace_chunk;
+ struct consumer_output *original_ust_consumer_output = NULL;
+ struct consumer_output *original_kernel_consumer_output = NULL;
+ struct consumer_output *snapshot_ust_consumer_output = NULL;
+ struct consumer_output *snapshot_kernel_consumer_output = NULL;
- fmt_ret = snprintf(snapshot_chunk_name, sizeof(snapshot_chunk_name),
+ ret = snprintf(snapshot_chunk_name, sizeof(snapshot_chunk_name),
"%s-%s-%" PRIu64,
snapshot_output->name,
snapshot_output->datetime,
snapshot_output->nb_snapshot);
- if (fmt_ret < 0 || fmt_ret >= sizeof(snapshot_chunk_name)) {
+ if (ret < 0 || ret >= sizeof(snapshot_chunk_name)) {
ERR("Failed to format snapshot name");
- ret = LTTNG_ERR_INVALID;
- goto end;
+ ret_code = LTTNG_ERR_INVALID;
+ goto error;
}
DBG("Recording snapshot \"%s\" for session \"%s\" with chunk name \"%s\"",
snapshot_output->name, session->name,
snapshot_chunk_name);
+ if (!session->kernel_session && !session->ust_session) {
+ ERR("Failed to record snapshot as no channels exist");
+ ret_code = LTTNG_ERR_NO_CHANNEL;
+ goto error;
+ }
+
+ if (session->kernel_session) {
+ original_kernel_consumer_output =
+ session->kernel_session->consumer;
+ snapshot_kernel_consumer_output =
+ consumer_copy_output(snapshot_output->consumer);
+ ret = consumer_copy_sockets(snapshot_kernel_consumer_output,
+ original_kernel_consumer_output);
+ if (ret < 0) {
+ ERR("Failed to copy consumer sockets from snapshot output configuration");
+ ret_code = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ ret_code = set_relayd_for_snapshot(
+ snapshot_kernel_consumer_output, session);
+ if (ret_code != LTTNG_OK) {
+ ERR("Failed to setup relay daemon for kernel tracer snapshot");
+ goto error;
+ }
+ session->kernel_session->consumer =
+ snapshot_kernel_consumer_output;
+ }
+ if (session->ust_session) {
+ original_ust_consumer_output = session->ust_session->consumer;
+ snapshot_ust_consumer_output =
+ consumer_copy_output(snapshot_output->consumer);
+ ret = consumer_copy_sockets(snapshot_ust_consumer_output,
+ original_ust_consumer_output);
+ if (ret < 0) {
+ ERR("Failed to copy consumer sockets from snapshot output configuration");
+ ret_code = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ ret_code = set_relayd_for_snapshot(
+ snapshot_ust_consumer_output, session);
+ if (ret_code != LTTNG_OK) {
+ ERR("Failed to setup relay daemon for userspace tracer snapshot");
+ goto error;
+ }
+ session->ust_session->consumer =
+ snapshot_ust_consumer_output;
+ }
+
snapshot_trace_chunk = session_create_new_trace_chunk(session,
- snapshot_output_get_base_path(snapshot_output),
+ snapshot_kernel_consumer_output ?:
+ snapshot_ust_consumer_output,
+ consumer_output_get_base_path(
+ snapshot_output->consumer),
snapshot_chunk_name);
if (!snapshot_trace_chunk) {
- ret = LTTNG_ERR_CREATE_DIR_FAIL;
- goto end;
+ ERR("Failed to create temporary trace chunk to record a snapshot of session \"%s\"",
+ session->name);
+ ret_code = LTTNG_ERR_CREATE_DIR_FAIL;
+ goto error;
}
assert(!session->current_trace_chunk);
ret = session_set_trace_chunk(session, snapshot_trace_chunk, NULL);
lttng_trace_chunk_put(snapshot_trace_chunk);
snapshot_trace_chunk = NULL;
if (ret) {
- ret = LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER;
- goto end;
+ ERR("Failed to set temporary trace chunk to record a snapshot of session \"%s\"",
+ session->name);
+ ret_code = LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER;
+ goto error;
}
nb_packets_per_stream = get_session_nb_packets_per_stream(session,
snapshot_output->max_size);
if (nb_packets_per_stream < 0) {
- ret = LTTNG_ERR_MAX_SIZE_INVALID;
- goto end;
+ ret_code = LTTNG_ERR_MAX_SIZE_INVALID;
+ goto error;
}
if (session->kernel_session) {
- ret = record_kernel_snapshot(session->kernel_session,
- snapshot_output, session,
+ ret_code = record_kernel_snapshot(session->kernel_session,
+ snapshot_kernel_consumer_output, session,
wait, nb_packets_per_stream);
- if (ret != LTTNG_OK) {
- goto end;
+ if (ret_code != LTTNG_OK) {
+ goto error;
}
}
if (session->ust_session) {
- ret = record_ust_snapshot(session->ust_session,
- snapshot_output, session,
+ ret_code = record_ust_snapshot(session->ust_session,
+ snapshot_ust_consumer_output, session,
wait, nb_packets_per_stream);
- if (ret != LTTNG_OK) {
- goto end;
+ if (ret_code != LTTNG_OK) {
+ goto error;
}
}
*/
ERR("Failed to close snapshot trace chunk of session \"%s\"",
session->name);
- ret = -1;
+ ret_code = LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER;
}
if (session_set_trace_chunk(session, NULL, NULL)) {
ERR("Failed to release the current trace chunk of session \"%s\"",
session->name);
- ret = -1;
+ ret_code = LTTNG_ERR_UNK;
}
-end:
- return ret;
+error:
+ if (original_ust_consumer_output) {
+ session->ust_session->consumer = original_ust_consumer_output;
+ }
+ if (original_kernel_consumer_output) {
+ session->kernel_session->consumer =
+ original_kernel_consumer_output;
+ }
+ consumer_output_put(snapshot_ust_consumer_output);
+ consumer_output_put(snapshot_kernel_consumer_output);
+ return ret_code;
}
/*
{
enum lttng_error_code cmd_ret = LTTNG_OK;
int ret;
- unsigned int use_tmp_output = 0;
- struct snapshot_output tmp_output;
unsigned int snapshot_success = 0;
char datetime[16];
+ struct snapshot_output *tmp_output = NULL;
assert(session);
assert(output);
/* Use temporary output for the session. */
if (*output->ctrl_url != '\0') {
+ tmp_output = snapshot_output_alloc();
+ if (!tmp_output) {
+ cmd_ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+
ret = snapshot_output_init(session, output->max_size,
output->name,
output->ctrl_url, output->data_url,
session->consumer,
- &tmp_output, NULL);
+ tmp_output, NULL);
if (ret < 0) {
if (ret == -ENOMEM) {
cmd_ret = LTTNG_ERR_NOMEM;
goto error;
}
/* Use the global session count for the temporary snapshot. */
- tmp_output.nb_snapshot = session->snapshot.nb_snapshot;
+ tmp_output->nb_snapshot = session->snapshot.nb_snapshot;
/* Use the global datetime */
- memcpy(tmp_output.datetime, datetime, sizeof(datetime));
- use_tmp_output = 1;
- }
-
- if (use_tmp_output) {
- cmd_ret = snapshot_record(session, &tmp_output, wait);
+ memcpy(tmp_output->datetime, datetime, sizeof(datetime));
+ cmd_ret = snapshot_record(session, tmp_output, wait);
if (cmd_ret != LTTNG_OK) {
goto error;
}
rcu_read_lock();
cds_lfht_for_each_entry(session->snapshot.output_ht->ht,
&iter.iter, sout, node.node) {
+ struct snapshot_output output_copy;
+
/*
- * Make a local copy of the output and assign the
- * possible temporary value given by the caller.
+ * Make a local copy of the output and override output
+ * parameters with those provided as part of the
+ * command.
*/
- memcpy(&tmp_output, sout, sizeof(tmp_output));
+ memcpy(&output_copy, sout, sizeof(output_copy));
if (output->max_size != (uint64_t) -1ULL) {
- tmp_output.max_size = output->max_size;
+ output_copy.max_size = output->max_size;
}
- tmp_output.nb_snapshot = session->snapshot.nb_snapshot;
- memcpy(tmp_output.datetime, datetime, sizeof(datetime));
+ output_copy.nb_snapshot = session->snapshot.nb_snapshot;
+ memcpy(output_copy.datetime, datetime,
+ sizeof(datetime));
/* Use temporary name. */
if (*output->name != '\0') {
- if (lttng_strncpy(tmp_output.name, output->name,
- sizeof(tmp_output.name))) {
+ if (lttng_strncpy(output_copy.name,
+ output->name,
+ sizeof(output_copy.name))) {
cmd_ret = LTTNG_ERR_INVALID;
rcu_read_unlock();
goto error;
}
}
- cmd_ret = snapshot_record(session, &tmp_output, wait);
+ cmd_ret = snapshot_record(session, &output_copy, wait);
if (cmd_ret != LTTNG_OK) {
rcu_read_unlock();
goto error;
}
error:
+ if (tmp_output) {
+ snapshot_output_destroy(tmp_output);
+ }
return cmd_ret;
}
* Returns LTTNG_OK on success or else a negative LTTng error code.
*/
int cmd_rotate_session(struct ltt_session *session,
- struct lttng_rotate_session_return *rotate_return)
+ struct lttng_rotate_session_return *rotate_return,
+ bool quiet_rotation)
{
int ret;
uint64_t ongoing_rotation_chunk_id;
goto end;
}
- if (session->live_timer || !session->output_traces) {
+ /*
+ * Explicit rotation is not supported for live sessions.
+ * However, live sessions can perform a quiet rotation on
+ * destroy.
+ * Rotation is not supported for snapshot traces (no output).
+ */
+ if ((!quiet_rotation && session->live_timer) ||
+ !session->output_traces) {
cmd_ret = LTTNG_ERR_ROTATION_NOT_AVAILABLE;
goto end;
}
session->rotation_state = LTTNG_ROTATION_STATE_ONGOING;
if (session->active) {
- new_trace_chunk = session_create_new_trace_chunk(session,
+ new_trace_chunk = session_create_new_trace_chunk(session, NULL,
NULL, NULL);
if (!new_trace_chunk) {
cmd_ret = LTTNG_ERR_CREATE_DIR_FAIL;
}
ret = session_close_trace_chunk(session, chunk_being_archived,
- &((enum lttng_trace_chunk_command_type) {
- LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED}));
+ quiet_rotation ?
+ NULL :
+ &((enum lttng_trace_chunk_command_type){
+ LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED}));
if (ret) {
cmd_ret = LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER;
goto error;
}
+ session->quiet_rotation = quiet_rotation;
ret = timer_session_rotation_pending_check_start(session,
DEFAULT_ROTATE_PENDING_TIMER);
if (ret) {
session->chunk_being_archived = chunk_being_archived;
chunk_being_archived = NULL;
- ret = notification_thread_command_session_rotation_ongoing(
- notification_thread_handle,
- session->name, session->uid, session->gid,
- ongoing_rotation_chunk_id);
- if (ret != LTTNG_OK) {
- ERR("Failed to notify notification thread that a session rotation is ongoing for session %s",
- session->name);
- cmd_ret = ret;
+ if (!quiet_rotation) {
+ ret = notification_thread_command_session_rotation_ongoing(
+ notification_thread_handle,
+ session->name, session->uid, session->gid,
+ ongoing_rotation_chunk_id);
+ if (ret != LTTNG_OK) {
+ ERR("Failed to notify notification thread that a session rotation is ongoing for session %s",
+ session->name);
+ cmd_ret = ret;
+ }
}
DBG("Cmd rotate session %s, archive_id %" PRIu64 " sent",