X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fcmd.c;h=a17cc678ecab2363a95166b82f1c794a15c8ff96;hp=b5bde4a9db637ba25d1246c55b2e8bdfcf595c0f;hb=3285a971b225891df3dc7c2796176ce1f94665fe;hpb=d295668767ac8234e83984e1812d342d03293d88 diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index b5bde4a9d..a17cc678e 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,16 @@ /* Sleep for 100ms between each check for the shm path's deletion. */ #define SESSION_DESTROY_SHM_PATH_CHECK_DELAY_US 100000 +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); /* @@ -101,7 +112,7 @@ static uint64_t relayd_net_seq_idx; 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, @@ -906,6 +917,10 @@ static enum lttng_error_code add_uri_to_consumer( ret = lttng_strncpy(consumer->dst.session_root_path, uri->dst.path, sizeof(consumer->dst.session_root_path)); + if (ret) { + ret_code = LTTNG_ERR_FATAL; + goto error; + } consumer->type = CONSUMER_DST_LOCAL; break; } @@ -1032,7 +1047,10 @@ static enum lttng_error_code send_consumer_relayd_socket( struct consumer_output *consumer, struct consumer_socket *consumer_sock, const char *session_name, const char *hostname, - int session_live_timer) + const char *base_path, int session_live_timer, + const uint64_t *current_chunk_id, + time_t session_creation_time, + bool session_name_contains_creation_time) { int ret; struct lttcomm_relayd_sock *rsock = NULL; @@ -1060,7 +1078,9 @@ static enum lttng_error_code send_consumer_relayd_socket( /* Send relayd socket to consumer. */ ret = consumer_send_relayd_socket(consumer_sock, rsock, consumer, relayd_uri->stype, session_id, - session_name, hostname, session_live_timer); + session_name, hostname, base_path, + session_live_timer, current_chunk_id, + session_creation_time, session_name_contains_creation_time); if (ret < 0) { status = LTTNG_ERR_ENABLE_CONSUMER_FAIL; goto close_sock; @@ -1107,7 +1127,9 @@ static enum lttng_error_code send_consumer_relayd_sockets( enum lttng_domain_type domain, unsigned int session_id, struct consumer_output *consumer, struct consumer_socket *sock, const char *session_name, - const char *hostname, int session_live_timer) + const char *hostname, const char *base_path, int session_live_timer, + const uint64_t *current_chunk_id, time_t session_creation_time, + bool session_name_contains_creation_time) { enum lttng_error_code status = LTTNG_OK; @@ -1118,7 +1140,9 @@ static enum lttng_error_code send_consumer_relayd_sockets( if (!sock->control_sock_sent) { status = send_consumer_relayd_socket(session_id, &consumer->dst.net.control, consumer, sock, - session_name, hostname, session_live_timer); + session_name, hostname, base_path, session_live_timer, + current_chunk_id, session_creation_time, + session_name_contains_creation_time); if (status != LTTNG_OK) { goto error; } @@ -1128,7 +1152,9 @@ static enum lttng_error_code send_consumer_relayd_sockets( if (!sock->data_sock_sent) { status = send_consumer_relayd_socket(session_id, &consumer->dst.net.data, consumer, sock, - session_name, hostname, session_live_timer); + session_name, hostname, base_path, session_live_timer, + current_chunk_id, session_creation_time, + session_name_contains_creation_time); if (status != LTTNG_OK) { goto error; } @@ -1150,14 +1176,28 @@ int cmd_setup_relayd(struct ltt_session *session) struct ltt_kernel_session *ksess; struct consumer_socket *socket; struct lttng_ht_iter iter; + LTTNG_OPTIONAL(uint64_t) current_chunk_id = {}; - assert(session); + assert(session); usess = session->ust_session; ksess = session->kernel_session; DBG("Setting relayd for session %s", session->name); + if (session->current_trace_chunk) { + enum lttng_trace_chunk_status status = lttng_trace_chunk_get_id( + session->current_trace_chunk, ¤t_chunk_id.value); + + if (status == LTTNG_TRACE_CHUNK_STATUS_OK) { + current_chunk_id.is_set = true; + } else { + ERR("Failed to get current trace chunk id"); + ret = LTTNG_ERR_UNK; + goto error; + } + } + rcu_read_lock(); if (usess && usess->consumer && usess->consumer->type == CONSUMER_DST_NET @@ -1169,7 +1209,11 @@ int cmd_setup_relayd(struct ltt_session *session) ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_UST, session->id, usess->consumer, socket, session->name, session->hostname, - session->live_timer); + session->base_path, + session->live_timer, + current_chunk_id.is_set ? ¤t_chunk_id.value : NULL, + session->creation_time, + session->name_contains_creation_time); pthread_mutex_unlock(socket->lock); if (ret != LTTNG_OK) { goto error; @@ -1191,7 +1235,11 @@ int cmd_setup_relayd(struct ltt_session *session) ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_KERNEL, session->id, ksess->consumer, socket, session->name, session->hostname, - session->live_timer); + session->base_path, + session->live_timer, + current_chunk_id.is_set ? ¤t_chunk_id.value : NULL, + session->creation_time, + session->name_contains_creation_time); pthread_mutex_unlock(socket->lock); if (ret != LTTNG_OK) { goto error; @@ -1213,7 +1261,7 @@ error: /* * Start a kernel session by opening all necessary streams. */ -static int start_kernel_session(struct ltt_kernel_session *ksess, int wpipe) +static int start_kernel_session(struct ltt_kernel_session *ksess) { int ret; struct ltt_kernel_channel *kchan; @@ -1265,7 +1313,7 @@ static int start_kernel_session(struct ltt_kernel_session *ksess, int wpipe) } /* Quiescent wait after starting trace */ - kernel_wait_quiescent(wpipe); + kernel_wait_quiescent(); ksess->active = 1; @@ -1297,7 +1345,7 @@ int cmd_disable_channel(struct ltt_session *session, goto error; } - kernel_wait_quiescent(kernel_tracer_fd); + kernel_wait_quiescent(); break; } case LTTNG_DOMAIN_UST: @@ -1355,7 +1403,7 @@ int cmd_track_pid(struct ltt_session *session, enum lttng_domain_type domain, goto error; } - kernel_wait_quiescent(kernel_tracer_fd); + kernel_wait_quiescent(); break; } case LTTNG_DOMAIN_UST: @@ -1406,7 +1454,7 @@ int cmd_untrack_pid(struct ltt_session *session, enum lttng_domain_type domain, goto error; } - kernel_wait_quiescent(kernel_tracer_fd); + kernel_wait_quiescent(); break; } case LTTNG_DOMAIN_UST: @@ -1439,27 +1487,29 @@ error: * 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(); @@ -1478,21 +1528,21 @@ int cmd_enable_channel(struct ltt_session *session, * 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 */ switch (domain->type) { case LTTNG_DOMAIN_KERNEL: { - if (kernel_supports_ring_buffer_snapshot_sample_positions(kernel_tracer_fd) != 1) { + if (kernel_supports_ring_buffer_snapshot_sample_positions() != 1) { /* Sampling position of buffer is not supported */ 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; } @@ -1517,16 +1567,16 @@ int cmd_enable_channel(struct ltt_session *session, { 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 { @@ -1537,7 +1587,7 @@ int cmd_enable_channel(struct ltt_session *session, goto error; } - kernel_wait_quiescent(kernel_tracer_fd); + kernel_wait_quiescent(); break; } case LTTNG_DOMAIN_UST: @@ -1556,19 +1606,19 @@ int cmd_enable_channel(struct ltt_session *session, * 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; @@ -1577,10 +1627,10 @@ int cmd_enable_channel(struct ltt_session *session, 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 { @@ -1593,7 +1643,7 @@ int cmd_enable_channel(struct ltt_session *session, 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: @@ -1606,11 +1656,11 @@ end: * 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); @@ -1672,7 +1722,7 @@ int cmd_disable_event(struct ltt_session *session, goto error_unlock; } - kernel_wait_quiescent(kernel_tracer_fd); + kernel_wait_quiescent(); break; } case LTTNG_DOMAIN_UST: @@ -1783,7 +1833,7 @@ error: * 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; @@ -1954,7 +2004,7 @@ end: * 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, @@ -2122,7 +2172,7 @@ static int _cmd_enable_event(struct ltt_session *session, goto error; } - kernel_wait_quiescent(kernel_tracer_fd); + kernel_wait_quiescent(); break; } case LTTNG_DOMAIN_UST: @@ -2351,7 +2401,8 @@ error: * 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, @@ -2368,7 +2419,7 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, * 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, @@ -2390,7 +2441,7 @@ ssize_t cmd_list_tracepoints(enum lttng_domain_type domain, switch (domain) { case LTTNG_DOMAIN_KERNEL: - nb_events = kernel_list_events(kernel_tracer_fd, events); + nb_events = kernel_list_events(events); if (nb_events < 0) { ret = LTTNG_ERR_KERN_LIST_FAIL; goto error; @@ -2553,7 +2604,7 @@ int cmd_start_trace(struct ltt_session *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; @@ -2570,7 +2621,7 @@ int cmd_start_trace(struct ltt_session *session) /* Kernel tracing */ if (ksession != NULL) { DBG("Start kernel tracing session %s", session->name); - ret = start_kernel_session(ksession, kernel_tracer_fd); + ret = start_kernel_session(ksession); if (ret != LTTNG_OK) { goto error; } @@ -2647,13 +2698,14 @@ int cmd_stop_trace(struct ltt_session *session) goto error; } - kernel_wait_quiescent(kernel_tracer_fd); + 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; } } @@ -2662,6 +2714,7 @@ int cmd_stop_trace(struct ltt_session *session) ret = kernel_flush_buffer(kchan); if (ret < 0) { ERR("Kernel flush buffer error"); + error_occurred = true; } } @@ -2841,6 +2894,7 @@ enum lttng_error_code cmd_create_session_from_descriptor( 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) { @@ -2863,8 +2917,13 @@ enum lttng_error_code cmd_create_session_from_descriptor( 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, - &new_session); + base_path, &new_session); if (ret_code != LTTNG_OK) { goto end; } @@ -3003,26 +3062,136 @@ error: return ret_code; } +static +void cmd_destroy_session_reply(const struct ltt_session *session, + void *_reply_context) +{ + int ret; + ssize_t comm_ret; + const struct cmd_destroy_session_reply_context *reply_context = + _reply_context; + struct lttng_dynamic_buffer payload; + struct lttcomm_session_destroy_command_header cmd_header; + struct lttng_trace_archive_location *location = NULL; + struct lttcomm_lttng_msg llm = { + .cmd_type = LTTNG_DESTROY_SESSION, + .ret_code = reply_context->destruction_status, + .pid = UINT32_MAX, + .cmd_header_size = + sizeof(struct lttcomm_session_destroy_command_header), + .data_size = 0, + }; + size_t payload_size_before_location; + + lttng_dynamic_buffer_init(&payload); + + ret = lttng_dynamic_buffer_append(&payload, &llm, sizeof(llm)); + if (ret) { + ERR("Failed to append session destruction message"); + goto error; + } + + cmd_header.rotation_state = + (int32_t) (reply_context->implicit_rotation_on_destroy ? + session->rotation_state : + LTTNG_ROTATION_STATE_NO_ROTATION); + ret = lttng_dynamic_buffer_append(&payload, &cmd_header, + sizeof(cmd_header)); + if (ret) { + ERR("Failed to append session destruction command header"); + goto error; + } + + if (!reply_context->implicit_rotation_on_destroy) { + DBG("No implicit rotation performed during the destruction of session \"%s\", sending reply", + session->name); + goto send_reply; + } + if (session->rotation_state != LTTNG_ROTATION_STATE_COMPLETED) { + DBG("Rotation state of session \"%s\" is not \"completed\", sending session destruction reply", + session->name); + goto send_reply; + } + + location = session_get_trace_archive_location(session); + if (!location) { + ERR("Failed to get the location of the trace archive produced during the destruction of session \"%s\"", + session->name); + goto error; + } + + payload_size_before_location = payload.size; + comm_ret = lttng_trace_archive_location_serialize(location, + &payload); + if (comm_ret < 0) { + ERR("Failed to serialize the location of the trace archive produced during the destruction of session \"%s\"", + session->name); + goto error; + } + /* Update the message to indicate the location's length. */ + ((struct lttcomm_lttng_msg *) payload.data)->data_size = + payload.size - payload_size_before_location; +send_reply: + comm_ret = lttcomm_send_unix_sock(reply_context->reply_sock_fd, + payload.data, payload.size); + if (comm_ret != (ssize_t) payload.size) { + ERR("Failed to send result of the destruction of session \"%s\" to client", + session->name); + } +error: + ret = close(reply_context->reply_sock_fd); + if (ret) { + PERROR("Failed to close client socket in deferred session destroy reply"); + } + lttng_dynamic_buffer_reset(&payload); + free(_reply_context); +} + /* * Command LTTNG_DESTROY_SESSION processed by the client thread. * * Called with session lock held. */ int cmd_destroy_session(struct ltt_session *session, - struct notification_thread_handle *notification_thread_handle) + struct notification_thread_handle *notification_thread_handle, + 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) { + reply_context = zmalloc(sizeof(*reply_context)); + if (!reply_context) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + reply_context->reply_sock_fd = *sock_fd; + } /* Safety net */ assert(session); - DBG("Begin destroy session %s (id %" PRIu64 ")", session->name, session->id); + DBG("Begin destroy session %s (id %" PRIu64 ")", session->name, + session->id); + if (session->active) { + DBG("Session \"%s\" is active, attempting to stop it before destroying it", + session->name); + ret = cmd_stop_trace(session); + if (ret != LTTNG_OK && ret != LTTNG_ERR_TRACE_ALREADY_STOPPED) { + /* 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; + } + } if (session->rotation_schedule_timer_enabled) { if (timer_session_rotation_schedule_timer_stop( session)) { ERR("Failed to stop the \"rotation schedule\" timer of session %s", session->name); + destruction_last_error = LTTNG_ERR_TIMER_STOP_ERROR; } } @@ -3033,11 +3202,35 @@ int cmd_destroy_session(struct ltt_session *session, 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)); + destruction_last_error = -ret; + } + if (reply_context) { + reply_context->implicit_rotation_on_destroy = true; + } + } else if (session->has_been_started && session->current_trace_chunk) { + /* + * 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)); + destruction_last_error = -ret; } } @@ -3101,8 +3294,20 @@ int cmd_destroy_session(struct ltt_session *session, * _at least_ up to the point when that reference is released. */ session_destroy(session); - ret = LTTNG_OK; - + if (reply_context) { + reply_context->destruction_status = destruction_last_error; + ret = session_add_destroy_notifier(session, + cmd_destroy_session_reply, + (void *) reply_context); + if (ret) { + ret = LTTNG_ERR_FATAL; + goto end; + } else { + *sock_fd = -1; + } + } + ret = LTTNG_OK; +end: return ret; } @@ -3521,7 +3726,7 @@ error: * 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; @@ -3589,7 +3794,7 @@ error: * 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; @@ -4050,37 +4255,66 @@ end: * 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 lttng_ht_iter iter; struct consumer_socket *socket; + LTTNG_OPTIONAL(uint64_t) current_chunk_id = {}; + const char *base_path; - 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 chunk_status = + lttng_trace_chunk_get_id( + session->current_trace_chunk, + ¤t_chunk_id.value); + + if (chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK) { + current_chunk_id.is_set = true; + } else { + ERR("Failed to get current trace chunk id"); + status = LTTNG_ERR_UNK; + goto error; + } + } + /* Ignore if snapshot consumer output is not network. */ - if (snap_output->consumer->type != CONSUMER_DST_NET) { + if (output->type != CONSUMER_DST_NET) { goto error; } + /* + * The snapshot record URI base path overrides the session + * base path. + */ + if (output->dst.net.control.subdir[0] != '\0') { + base_path = output->dst.net.control.subdir; + } else { + base_path = session->base_path; + } + /* * For each consumer socket, create and send the relayd object of the * 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); + base_path, + session->live_timer, + current_chunk_id.is_set ? ¤t_chunk_id.value : NULL, + session->creation_time, + session->name_contains_creation_time); pthread_mutex_unlock(socket->lock); if (status != LTTNG_OK) { rcu_read_unlock(); @@ -4100,44 +4334,18 @@ error: */ 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; } @@ -4147,45 +4355,18 @@ end: * 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; } @@ -4281,82 +4462,155 @@ static 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); + strcpy(snapshot_kernel_consumer_output->chunk_path, + snapshot_chunk_name); + 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); + strcpy(snapshot_ust_consumer_output->chunk_path, + snapshot_chunk_name); + 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; } } - if (session_close_trace_chunk(session, session->current_trace_chunk)) { + if (session_close_trace_chunk( + session, session->current_trace_chunk, NULL, NULL)) { /* * Don't goto end; make sure the chunk is closed for the session * to allow future snapshots. */ 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; } /* @@ -4368,14 +4622,13 @@ end: * 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; - 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); @@ -4407,11 +4660,17 @@ int cmd_snapshot_record(struct ltt_session *session, /* 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; @@ -4421,15 +4680,11 @@ int cmd_snapshot_record(struct ltt_session *session, 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; } @@ -4441,30 +4696,35 @@ int cmd_snapshot_record(struct ltt_session *session, 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; @@ -4481,6 +4741,9 @@ int cmd_snapshot_record(struct ltt_session *session, } error: + if (tmp_output) { + snapshot_output_destroy(tmp_output); + } return cmd_ret; } @@ -4516,7 +4779,8 @@ int cmd_set_session_shm_path(struct ltt_session *session, * 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; @@ -4532,13 +4796,20 @@ int cmd_rotate_session(struct ltt_session *session, 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; } /* Unsupported feature in lttng-relayd before 2.11. */ - if (session->consumer->type == CONSUMER_DST_NET && + if (!quiet_rotation && session->consumer->type == CONSUMER_DST_NET && (session->consumer->relay_major_version == 2 && session->consumer->relay_minor_version < 11)) { cmd_ret = LTTNG_ERR_ROTATION_NOT_AVAILABLE_RELAY; @@ -4566,7 +4837,7 @@ int cmd_rotate_session(struct ltt_session *session, 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; @@ -4587,14 +4858,6 @@ int cmd_rotate_session(struct ltt_session *session, &ongoing_rotation_chunk_id); assert(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK); - chunk_status = lttng_trace_chunk_set_close_command( - chunk_being_archived, - LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED); - if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { - cmd_ret = LTTNG_ERR_FATAL; - goto error; - } - if (session->kernel_session) { cmd_ret = kernel_rotate_session(session); if (cmd_ret != LTTNG_OK) { @@ -4608,12 +4871,18 @@ int cmd_rotate_session(struct ltt_session *session, } } - ret = session_close_trace_chunk(session, chunk_being_archived); + ret = session_close_trace_chunk(session, chunk_being_archived, + quiet_rotation ? + NULL : + &((enum lttng_trace_chunk_command_type){ + LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED}), + session->last_chunk_path); 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) { @@ -4631,14 +4900,16 @@ int cmd_rotate_session(struct ltt_session *session, 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", @@ -4726,8 +4997,21 @@ int cmd_rotate_get_info(struct ltt_session *session, sizeof(info_return->location.local.absolute_path); info_return->location_type = (int8_t) LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL; + fmt_ret = asprintf(&chunk_path, + "%s/" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY "/%s", + session_get_base_path(session), + session->last_archived_chunk_name); + if (fmt_ret == -1) { + PERROR("Failed to format the path of the last archived trace chunk"); + info_return->status = LTTNG_ROTATION_STATUS_ERROR; + cmd_ret = LTTNG_ERR_UNK; + goto end; + } break; case CONSUMER_DST_NET: + { + uint16_t ctrl_port, data_port; + current_tracing_path_reply = info_return->location.relay.relative_path; current_tracing_path_reply_len = @@ -4746,25 +5030,23 @@ int cmd_rotate_get_info(struct ltt_session *session, goto end; } - session_get_net_consumer_ports(session, - &info_return->location.relay.ports.control, - &info_return->location.relay.ports.data); + session_get_net_consumer_ports(session, &ctrl_port, &data_port); + info_return->location.relay.ports.control = ctrl_port; + info_return->location.relay.ports.data = data_port; info_return->location_type = (int8_t) LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY; + chunk_path = strdup(session->last_chunk_path); + if (!chunk_path) { + ERR("Failed to allocate the path of the last archived trace chunk"); + info_return->status = LTTNG_ROTATION_STATUS_ERROR; + cmd_ret = LTTNG_ERR_UNK; + goto end; + } break; + } default: abort(); } - fmt_ret = asprintf(&chunk_path, - "%s/" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY "/%s", - session_get_base_path(session), - session->last_archived_chunk_name); - if (fmt_ret == -1) { - PERROR("Failed to format the path of the last archived trace chunk"); - info_return->status = LTTNG_ROTATION_STATUS_ERROR; - cmd_ret = LTTNG_ERR_UNK; - goto end; - } fmt_ret = lttng_strncpy(current_tracing_path_reply, chunk_path, current_tracing_path_reply_len);