struct cmd_destroy_session_reply_context {
int reply_sock_fd;
bool implicit_rotation_on_destroy;
+ /*
+ * Indicates whether or not an error occurred while launching the
+ * destruction of a session.
+ */
+ enum lttng_error_code destruction_status;
};
static enum lttng_error_code wait_on_path(void *path);
static int validate_ust_event_name(const char *);
static int cmd_enable_event_internal(struct ltt_session *session,
- struct lttng_domain *domain,
+ const struct lttng_domain *domain,
char *channel_name, struct lttng_event *event,
char *filter_expression,
struct lttng_filter_bytecode *filter,
DBG("Setting relayd for session %s", session->name);
+ rcu_read_lock();
if (session->current_trace_chunk) {
enum lttng_trace_chunk_status status = lttng_trace_chunk_get_id(
session->current_trace_chunk, ¤t_chunk_id.value);
}
}
- rcu_read_lock();
-
if (usess && usess->consumer && usess->consumer->type == CONSUMER_DST_NET
&& usess->consumer->enabled) {
/* For each consumer socket, send relayd sockets */
/*
* Start a kernel session by opening all necessary streams.
*/
-static int start_kernel_session(struct ltt_kernel_session *ksess)
+int start_kernel_session(struct ltt_kernel_session *ksess)
{
int ret;
struct ltt_kernel_channel *kchan;
return ret;
}
+int stop_kernel_session(struct ltt_kernel_session *ksess)
+{
+ struct ltt_kernel_channel *kchan;
+ bool error_occurred = false;
+ int ret;
+
+ if (!ksess || !ksess->active) {
+ return LTTNG_OK;
+ }
+ DBG("Stopping kernel tracing");
+
+ ret = kernel_stop_session(ksess);
+ if (ret < 0) {
+ ret = LTTNG_ERR_KERN_STOP_FAIL;
+ goto error;
+ }
+
+ kernel_wait_quiescent();
+
+ /* Flush metadata after stopping (if exists) */
+ if (ksess->metadata_stream_fd >= 0) {
+ ret = kernel_metadata_flush_buffer(ksess->metadata_stream_fd);
+ if (ret < 0) {
+ ERR("Kernel metadata flush failed");
+ error_occurred = true;
+ }
+ }
+
+ /* Flush all buffers after stopping */
+ cds_list_for_each_entry(kchan, &ksess->channel_list.head, list) {
+ ret = kernel_flush_buffer(kchan);
+ if (ret < 0) {
+ ERR("Kernel flush buffer error");
+ error_occurred = true;
+ }
+ }
+
+ ksess->active = 0;
+ if (error_occurred) {
+ ret = LTTNG_ERR_UNK;
+ } else {
+ ret = LTTNG_OK;
+ }
+error:
+ return ret;
+}
+
/*
* Command LTTNG_DISABLE_CHANNEL processed by the client thread.
*/
* The wpipe arguments is used as a notifier for the kernel thread.
*/
int cmd_enable_channel(struct ltt_session *session,
- struct lttng_domain *domain, struct lttng_channel *attr, int wpipe)
+ const struct lttng_domain *domain, const struct lttng_channel *_attr, int wpipe)
{
int ret;
struct ltt_ust_session *usess = session->ust_session;
struct lttng_ht *chan_ht;
size_t len;
+ struct lttng_channel attr;
assert(session);
- assert(attr);
+ assert(_attr);
assert(domain);
- len = lttng_strnlen(attr->name, sizeof(attr->name));
+ attr = *_attr;
+ len = lttng_strnlen(attr.name, sizeof(attr.name));
/* Validate channel name */
- if (attr->name[0] == '.' ||
- memchr(attr->name, '/', len) != NULL) {
+ if (attr.name[0] == '.' ||
+ memchr(attr.name, '/', len) != NULL) {
ret = LTTNG_ERR_INVALID_CHANNEL_NAME;
goto end;
}
- DBG("Enabling channel %s for session %s", attr->name, session->name);
+ DBG("Enabling channel %s for session %s", attr.name, session->name);
rcu_read_lock();
* beacons for inactive streams.
*/
if (session->live_timer > 0) {
- attr->attr.live_timer_interval = session->live_timer;
- attr->attr.switch_timer_interval = 0;
+ attr.attr.live_timer_interval = session->live_timer;
+ attr.attr.switch_timer_interval = 0;
}
/* Check for feature support */
WARN("Kernel tracer does not support buffer monitoring. "
"Setting the monitor interval timer to 0 "
"(disabled) for channel '%s' of session '%s'",
- attr-> name, session->name);
- lttng_channel_set_monitor_timer_interval(attr, 0);
+ attr.name, session->name);
+ lttng_channel_set_monitor_timer_interval(&attr, 0);
}
break;
}
{
struct ltt_kernel_channel *kchan;
- kchan = trace_kernel_get_channel_by_name(attr->name,
+ kchan = trace_kernel_get_channel_by_name(attr.name,
session->kernel_session);
if (kchan == NULL) {
if (session->snapshot.nb_output > 0 ||
session->snapshot_mode) {
/* Enforce mmap output for snapshot sessions. */
- attr->attr.output = LTTNG_EVENT_MMAP;
+ attr.attr.output = LTTNG_EVENT_MMAP;
}
- ret = channel_kernel_create(session->kernel_session, attr, wpipe);
- if (attr->name[0] != '\0') {
+ ret = channel_kernel_create(session->kernel_session, &attr, wpipe);
+ if (attr.name[0] != '\0') {
session->kernel_session->has_non_default_channel = 1;
}
} else {
* adhered to.
*/
if (domain->type == LTTNG_DOMAIN_JUL) {
- if (strncmp(attr->name, DEFAULT_JUL_CHANNEL_NAME,
+ if (strncmp(attr.name, DEFAULT_JUL_CHANNEL_NAME,
LTTNG_SYMBOL_NAME_LEN)) {
ret = LTTNG_ERR_INVALID_CHANNEL_NAME;
goto error;
}
} else if (domain->type == LTTNG_DOMAIN_LOG4J) {
- if (strncmp(attr->name, DEFAULT_LOG4J_CHANNEL_NAME,
+ if (strncmp(attr.name, DEFAULT_LOG4J_CHANNEL_NAME,
LTTNG_SYMBOL_NAME_LEN)) {
ret = LTTNG_ERR_INVALID_CHANNEL_NAME;
goto error;
}
} else if (domain->type == LTTNG_DOMAIN_PYTHON) {
- if (strncmp(attr->name, DEFAULT_PYTHON_CHANNEL_NAME,
+ if (strncmp(attr.name, DEFAULT_PYTHON_CHANNEL_NAME,
LTTNG_SYMBOL_NAME_LEN)) {
ret = LTTNG_ERR_INVALID_CHANNEL_NAME;
goto error;
chan_ht = usess->domain_global.channels;
- uchan = trace_ust_find_channel_by_name(chan_ht, attr->name);
+ uchan = trace_ust_find_channel_by_name(chan_ht, attr.name);
if (uchan == NULL) {
- ret = channel_ust_create(usess, attr, domain->buf_type);
- if (attr->name[0] != '\0') {
+ ret = channel_ust_create(usess, &attr, domain->buf_type);
+ if (attr.name[0] != '\0') {
usess->has_non_default_channel = 1;
}
} else {
goto error;
}
- if (ret == LTTNG_OK && attr->attr.output != LTTNG_EVENT_MMAP) {
+ if (ret == LTTNG_OK && attr.attr.output != LTTNG_EVENT_MMAP) {
session->has_non_mmap_channel = true;
}
error:
* Command LTTNG_DISABLE_EVENT processed by the client thread.
*/
int cmd_disable_event(struct ltt_session *session,
- enum lttng_domain_type domain, char *channel_name,
- struct lttng_event *event)
+ enum lttng_domain_type domain, const char *channel_name,
+ const struct lttng_event *event)
{
int ret;
- char *event_name;
+ const char *event_name;
DBG("Disable event command for event \'%s\'", event->name);
* Command LTTNG_ADD_CONTEXT processed by the client thread.
*/
int cmd_add_context(struct ltt_session *session, enum lttng_domain_type domain,
- char *channel_name, struct lttng_event_context *ctx, int kwpipe)
+ char *channel_name, const struct lttng_event_context *ctx, int kwpipe)
{
int ret, chan_kern_created = 0, chan_ust_created = 0;
char *app_ctx_provider_name = NULL, *app_ctx_name = NULL;
* enable the events through which all "agent" events are funeled.
*/
static int _cmd_enable_event(struct ltt_session *session,
- struct lttng_domain *domain,
+ const struct lttng_domain *domain,
char *channel_name, struct lttng_event *event,
char *filter_expression,
struct lttng_filter_bytecode *filter,
* Command LTTNG_ENABLE_EVENT processed by the client thread.
* We own filter, exclusion, and filter_expression.
*/
-int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain,
+int cmd_enable_event(struct ltt_session *session,
+ const struct lttng_domain *domain,
char *channel_name, struct lttng_event *event,
char *filter_expression,
struct lttng_filter_bytecode *filter,
* reserved names.
*/
static int cmd_enable_event_internal(struct ltt_session *session,
- struct lttng_domain *domain,
+ const struct lttng_domain *domain,
char *channel_name, struct lttng_event *event,
char *filter_expression,
struct lttng_filter_bytecode *filter,
unsigned long nb_chan = 0;
struct ltt_kernel_session *ksession;
struct ltt_ust_session *usess;
+ const bool session_rotated_after_last_stop =
+ session->rotated_after_last_stop;
assert(session);
goto error;
}
+ if (session->rotation_state == LTTNG_ROTATION_STATE_ONGOING &&
+ !session->current_trace_chunk) {
+ /*
+ * A rotation was launched while the session was stopped and
+ * it has not been completed yet. It is not possible to start
+ * the session since starting the session here would require a
+ * rotation from "NULL" to a new trace chunk. That rotation
+ * would overlap with the ongoing rotation, which is not
+ * supported.
+ */
+ WARN("Refusing to start session \"%s\" as a rotation launched after the last \"stop\" is still ongoing",
+ session->name);
+ ret = LTTNG_ERR_ROTATION_PENDING;
+ goto error;
+ }
+
/*
* Starting a session without channel is useless since after that it's not
* possible to enable channel thus inform the client.
goto error;
}
+ session->active = 1;
+ session->rotated_after_last_stop = false;
if (session->output_traces && !session->current_trace_chunk) {
- struct lttng_trace_chunk *trace_chunk;
+ if (!session->has_been_started) {
+ struct lttng_trace_chunk *trace_chunk;
- trace_chunk = session_create_new_trace_chunk(
- session, NULL, NULL, NULL);
- if (!trace_chunk) {
- ret = LTTNG_ERR_CREATE_DIR_FAIL;
- goto error;
- }
- assert(!session->current_trace_chunk);
- ret = session_set_trace_chunk(session, trace_chunk, NULL);
- lttng_trace_chunk_put(trace_chunk);
- if (ret) {
- ret = LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER;
- goto error;
+ DBG("Creating initial trace chunk of session \"%s\"",
+ session->name);
+ trace_chunk = session_create_new_trace_chunk(
+ session, NULL, NULL, NULL);
+ if (!trace_chunk) {
+ ret = LTTNG_ERR_CREATE_DIR_FAIL;
+ goto error;
+ }
+ assert(!session->current_trace_chunk);
+ ret = session_set_trace_chunk(session, trace_chunk,
+ NULL);
+ lttng_trace_chunk_put(trace_chunk);
+ if (ret) {
+ ret = LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER;
+ goto error;
+ }
+ } else {
+ DBG("Rotating session \"%s\" from its current \"NULL\" trace chunk to a new chunk",
+ session->name);
+ /*
+ * Rotate existing streams into the new chunk.
+ * This is a "quiet" rotation has no client has
+ * explicitly requested this operation.
+ *
+ * There is also no need to wait for the rotation
+ * to complete as it will happen immediately. No data
+ * was produced as the session was stopped, so the
+ * rotation should happen on reception of the command.
+ */
+ ret = cmd_rotate_session(session, NULL, true);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
}
}
}
}
- /* Flag this after a successful start. */
- session->has_been_started = 1;
- session->active = 1;
-
/*
* Clear the flag that indicates that a rotation was done while the
* session was stopped.
ret = LTTNG_OK;
error:
+ if (ret == LTTNG_OK) {
+ /* Flag this after a successful start. */
+ session->has_been_started |= 1;
+ } else {
+ session->active = 0;
+ /* Restore initial state on error. */
+ session->rotated_after_last_stop =
+ session_rotated_after_last_stop;
+ }
return ret;
}
int cmd_stop_trace(struct ltt_session *session)
{
int ret;
- struct ltt_kernel_channel *kchan;
struct ltt_kernel_session *ksession;
struct ltt_ust_session *usess;
- bool error_occurred = false;
assert(session);
- DBG("Begin stop session %s (id %" PRIu64 ")", session->name, session->id);
+ DBG("Begin stop session \"%s\" (id %" PRIu64 ")", session->name, session->id);
/* Short cut */
ksession = session->kernel_session;
usess = session->ust_session;
goto error;
}
- /* Kernel tracer */
- if (ksession && ksession->active) {
- DBG("Stop kernel tracing");
-
- ret = kernel_stop_session(ksession);
- if (ret < 0) {
- ret = LTTNG_ERR_KERN_STOP_FAIL;
- goto error;
- }
-
- kernel_wait_quiescent();
-
- /* Flush metadata after stopping (if exists) */
- if (ksession->metadata_stream_fd >= 0) {
- ret = kernel_metadata_flush_buffer(ksession->metadata_stream_fd);
- if (ret < 0) {
- ERR("Kernel metadata flush failed");
- error_occurred = true;
- }
- }
-
- /* Flush all buffers after stopping */
- cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) {
- ret = kernel_flush_buffer(kchan);
- if (ret < 0) {
- ERR("Kernel flush buffer error");
- error_occurred = true;
- }
- }
-
- ksession->active = 0;
- DBG("Kernel session stopped %s (id %" PRIu64 ")", session->name,
- session->id);
+ ret = stop_kernel_session(ksession);
+ if (ret != LTTNG_OK) {
+ goto error;
}
if (usess && usess->active) {
}
}
+ DBG("Completed stop session \"%s\" (id %" PRIu64 ")", session->name,
+ session->id);
/* Flag inactive after a successful stop. */
session->active = 0;
- ret = !error_occurred ? LTTNG_OK : LTTNG_ERR_UNK;
+ ret = LTTNG_OK;
+
+error:
+ return ret;
+}
+
+/*
+ * Set the base_path of the session only if subdir of a control uris is set.
+ * Return LTTNG_OK on success, otherwise LTTNG_ERR_*.
+ */
+static int set_session_base_path_from_uris(struct ltt_session *session,
+ size_t nb_uri,
+ struct lttng_uri *uris)
+{
+ int ret;
+ size_t i;
+
+ for (i = 0; i < nb_uri; i++) {
+ if (uris[i].stype != LTTNG_STREAM_CONTROL ||
+ uris[i].subdir[0] == '\0') {
+ /* Not interested in these URIs */
+ continue;
+ }
+
+ if (session->base_path != NULL) {
+ free(session->base_path);
+ session->base_path = NULL;
+ }
+ /* Set session base_path */
+ session->base_path = strdup(uris[i].subdir);
+ if (!session->base_path) {
+ PERROR("Failed to copy base path \"%s\" to session \"%s\"",
+ uris[i].subdir, session->name);
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ DBG2("Setting base path \"%s\" for session \"%s\"",
+ session->base_path, session->name);
+ }
+ ret = LTTNG_OK;
error:
return ret;
}
goto error;
}
+ /*
+ * Set the session base path if any. This is done inside
+ * cmd_set_consumer_uri to preserve backward compatibility of the
+ * previous session creation api vs the session descriptor api.
+ */
+ ret = set_session_base_path_from_uris(session, nb_uri, uris);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
/* Set the "global" consumer URIs */
for (i = 0; i < nb_uri; i++) {
- ret = add_uri_to_consumer(session,
- session->consumer,
- &uris[i], LTTNG_DOMAIN_NONE);
+ ret = add_uri_to_consumer(session, session->consumer, &uris[i],
+ LTTNG_DOMAIN_NONE);
if (ret != LTTNG_OK) {
goto error;
}
const char *session_name;
struct ltt_session *new_session = NULL;
enum lttng_session_descriptor_status descriptor_status;
- const char *base_path;
session_lock_list();
if (home_path) {
ret_code = LTTNG_ERR_INVALID;
goto end;
}
- ret = lttng_session_descriptor_get_base_path(descriptor, &base_path);
- if (ret) {
- ret_code = LTTNG_ERR_INVALID;
- goto end;
- }
+
ret_code = session_create(session_name, creds->uid, creds->gid,
- base_path, &new_session);
+ &new_session);
if (ret_code != LTTNG_OK) {
goto end;
}
struct lttng_trace_archive_location *location = NULL;
struct lttcomm_lttng_msg llm = {
.cmd_type = LTTNG_DESTROY_SESSION,
- .ret_code = LTTNG_OK,
+ .ret_code = reply_context->destruction_status,
.pid = UINT32_MAX,
.cmd_header_size =
sizeof(struct lttcomm_session_destroy_command_header),
int *sock_fd)
{
int ret;
+ enum lttng_error_code destruction_last_error = LTTNG_OK;
struct cmd_destroy_session_reply_context *reply_context = NULL;
if (sock_fd) {
/* Carry on with the destruction of the session. */
ERR("Failed to stop session \"%s\" as part of its destruction: %s",
session->name, lttng_strerror(-ret));
+ destruction_last_error = ret;
}
}
session)) {
ERR("Failed to stop the \"rotation schedule\" timer of session %s",
session->name);
+ destruction_last_error = LTTNG_ERR_TIMER_STOP_ERROR;
}
}
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));
+ destruction_last_error = -ret;
}
if (reply_context) {
reply_context->implicit_rotation_on_destroy = 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));
+ destruction_last_error = -ret;
}
}
*/
session_destroy(session);
if (reply_context) {
+ reply_context->destruction_status = destruction_last_error;
ret = session_add_destroy_notifier(session,
cmd_destroy_session_reply,
(void *) reply_context);
* 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)
+ const struct lttng_snapshot_output *output, uint32_t *id)
{
int ret;
struct snapshot_output *new_output;
* 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)
+ const struct lttng_snapshot_output *output)
{
int ret;
struct snapshot_output *sout = NULL;
* 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)
+ const struct lttng_snapshot_output *output, int wait)
{
enum lttng_error_code cmd_ret = LTTNG_OK;
int ret;
struct lttng_trace_chunk *chunk_being_archived = NULL;
struct lttng_trace_chunk *new_trace_chunk = NULL;
enum lttng_trace_chunk_status chunk_status;
+ bool failed_to_rotate = false;
+ enum lttng_error_code rotation_fail_code = LTTNG_OK;
assert(session);
goto end;
}
+ /* Unsupported feature in lttng-modules before 2.8 (lack of sequence number). */
+ if (session->kernel_session && !kernel_supports_ring_buffer_packet_sequence_number()) {
+ cmd_ret = LTTNG_ERR_ROTATION_NOT_AVAILABLE_KERNEL;
+ goto end;
+ }
+
if (session->rotation_state == LTTNG_ROTATION_STATE_ONGOING) {
DBG("Refusing to launch a rotation; a rotation is already in progress for session %s",
session->name);
cmd_ret = LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP;
goto end;
}
-
- session->rotation_state = LTTNG_ROTATION_STATE_ONGOING;
-
if (session->active) {
new_trace_chunk = session_create_new_trace_chunk(session, NULL,
NULL, NULL);
}
}
- /* The current trace chunk becomes the chunk being archived. */
+ /*
+ * The current trace chunk becomes the chunk being archived.
+ *
+ * After this point, "chunk_being_archived" must absolutely
+ * be closed on the consumer(s), otherwise it will never be
+ * cleaned-up, which will result in a leak.
+ */
ret = session_set_trace_chunk(session, new_trace_chunk,
&chunk_being_archived);
if (ret) {
goto error;
}
- assert(chunk_being_archived);
- chunk_status = lttng_trace_chunk_get_id(chunk_being_archived,
- &ongoing_rotation_chunk_id);
- assert(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK);
-
if (session->kernel_session) {
cmd_ret = kernel_rotate_session(session);
if (cmd_ret != LTTNG_OK) {
- goto error;
+ failed_to_rotate = true;
+ rotation_fail_code = cmd_ret;
}
}
if (session->ust_session) {
cmd_ret = ust_app_rotate_session(session);
if (cmd_ret != LTTNG_OK) {
+ failed_to_rotate = true;
+ rotation_fail_code = cmd_ret;
+ }
+ }
+
+ if (!session->active) {
+ session->rotated_after_last_stop = true;
+ }
+
+ if (!chunk_being_archived) {
+ DBG("Rotating session \"%s\" from a \"NULL\" trace chunk to a new trace chunk, skipping completion check",
+ session->name);
+ if (failed_to_rotate) {
+ cmd_ret = rotation_fail_code;
goto error;
}
+ cmd_ret = LTTNG_OK;
+ goto end;
}
+ session->rotation_state = LTTNG_ROTATION_STATE_ONGOING;
+ chunk_status = lttng_trace_chunk_get_id(chunk_being_archived,
+ &ongoing_rotation_chunk_id);
+ assert(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK);
+
ret = session_close_trace_chunk(session, chunk_being_archived,
quiet_rotation ?
NULL :
goto error;
}
+ if (failed_to_rotate) {
+ cmd_ret = rotation_fail_code;
+ goto error;
+ }
+
session->quiet_rotation = quiet_rotation;
ret = timer_session_rotation_pending_check_start(session,
DEFAULT_ROTATE_PENDING_TIMER);
goto error;
}
- if (!session->active) {
- session->rotated_after_last_stop = true;
- }
-
if (rotate_return) {
rotate_return->rotation_id = ongoing_rotation_chunk_id;
}
switch (rotation_state) {
case LTTNG_ROTATION_STATE_NO_ROTATION:
- DBG("Reporting that no rotation has occured within the lifetime of session \"%s\"",
+ DBG("Reporting that no rotation has occurred within the lifetime of session \"%s\"",
session->name);
goto end;
case LTTNG_ROTATION_STATE_EXPIRED: