#include <common/sessiond-comm/relayd.h>
#include <common/uri.h>
#include <common/utils.h>
+#include <common/fd-tracker/utils.h>
#include "cmd.h"
#include "live.h"
rcu_read_lock();
cds_lfht_for_each_entry(session->ctf_traces_ht->ht, &iter.iter, ctf_trace,
node.node) {
+ bool trace_has_metadata_stream = false;
struct relay_stream *stream;
health_code_update();
continue;
}
+ /*
+ * Iterate over all the streams of the trace to see if we have a
+ * metadata stream.
+ */
+ cds_list_for_each_entry_rcu(
+ stream, &ctf_trace->stream_list, stream_node)
+ {
+ if (stream->is_metadata) {
+ trace_has_metadata_stream = true;
+ break;
+ }
+ }
+
+ /*
+ * If there is no metadata stream in this trace at the moment
+ * and we never sent one to the viewer, skip the trace. We
+ * accept that the viewer will not see this trace at all.
+ */
+ if (!trace_has_metadata_stream &&
+ !ctf_trace->metadata_stream_sent_to_viewer) {
+ ctf_trace_put(ctf_trace);
+ continue;
+ }
+
cds_list_for_each_entry_rcu(stream, &ctf_trace->stream_list, stream_node) {
struct relay_viewer_stream *vstream;
}
vstream = viewer_stream_get_by_id(stream->stream_handle);
if (!vstream) {
+ /*
+ * Save that we sent the metadata stream to the
+ * viewer. So that we know what trace the viewer
+ * is aware of.
+ */
+ if (stream->is_metadata) {
+ ctf_trace->metadata_stream_sent_to_viewer =
+ true;
+ }
vstream = viewer_stream_create(stream,
viewer_trace_chunk, seek_t);
if (!vstream) {
* Create a poll set with O_CLOEXEC and add the thread quit pipe to the set.
*/
static
-int create_thread_poll_set(struct lttng_poll_event *events, int size)
+int create_named_thread_poll_set(struct lttng_poll_event *events,
+ int size, const char *name)
{
int ret;
goto error;
}
- ret = lttng_poll_create(events, size, LTTNG_CLOEXEC);
- if (ret < 0) {
- goto error;
- }
+ ret = fd_tracker_util_poll_create(the_fd_tracker,
+ name, events, 1, LTTNG_CLOEXEC);
/* Add quit pipe */
ret = lttng_poll_add(events, thread_quit_pipe[0], LPOLLIN | LPOLLERR);
return 0;
}
+static
+int create_sock(void *data, int *out_fd)
+{
+ int ret;
+ struct lttcomm_sock *sock = data;
+
+ ret = lttcomm_create_sock(sock);
+ if (ret < 0) {
+ goto end;
+ }
+
+ *out_fd = sock->fd;
+end:
+ return ret;
+}
+
+static
+int close_sock(void *data, int *in_fd)
+{
+ struct lttcomm_sock *sock = data;
+
+ return sock->ops->close(sock);
+}
+
/*
* Create and init socket from uri.
*/
static
-struct lttcomm_sock *init_socket(struct lttng_uri *uri)
+struct lttcomm_sock *init_socket(struct lttng_uri *uri, const char *name)
{
- int ret;
+ int ret, sock_fd;
struct lttcomm_sock *sock = NULL;
+ char uri_str[LTTNG_PATH_MAX];
+ char *formated_name = NULL;
sock = lttcomm_alloc_sock_from_uri(uri);
if (sock == NULL) {
goto error;
}
- ret = lttcomm_create_sock(sock);
- if (ret < 0) {
- goto error;
+ /*
+ * Don't fail to create the socket if the name can't be built as it is
+ * only used for debugging purposes.
+ */
+ ret = uri_to_str_url(uri, uri_str, sizeof(uri_str));
+ uri_str[sizeof(uri_str) - 1] = '\0';
+ if (ret >= 0) {
+ ret = asprintf(&formated_name, "%s socket @ %s", name,
+ uri_str);
+ if (ret < 0) {
+ formated_name = NULL;
+ }
}
- DBG("Listening on sock %d for lttng-live", sock->fd);
+
+ ret = fd_tracker_open_unsuspendable_fd(the_fd_tracker, &sock_fd,
+ (const char **) (formated_name ? &formated_name : NULL),
+ 1, create_sock, sock);
+ free(formated_name);
+ DBG("Listening on %s socket %d", name, sock->fd);
ret = sock->ops->bind(sock);
if (ret < 0) {
health_code_update();
- live_control_sock = init_socket(live_uri);
+ live_control_sock = init_socket(live_uri, "Live listener");
if (!live_control_sock) {
goto error_sock_control;
}
/* Pass 2 as size here for the thread quit pipe and control sockets. */
- ret = create_thread_poll_set(&events, 2);
+ ret = create_named_thread_poll_set(&events, 2,
+ "Live listener thread epoll");
if (ret < 0) {
goto error_create_poll;
}
error:
error_poll_add:
error_testpoint:
- lttng_poll_clean(&events);
+ (void) fd_tracker_util_poll_clean(the_fd_tracker, &events);
error_create_poll:
if (live_control_sock->fd >= 0) {
- ret = live_control_sock->ops->close(live_control_sock);
+ int sock_fd = live_control_sock->fd;
+
+ ret = fd_tracker_close_unsuspendable_fd(the_fd_tracker,
+ &sock_fd, 1, close_sock,
+ live_control_sock);
if (ret) {
PERROR("close");
}
+ live_control_sock->fd = -1;
}
lttcomm_destroy_sock(live_control_sock);
error_sock_control:
int ret = 0;
const uint32_t connection_major = rstream->trace->session->major;
const uint32_t connection_minor = rstream->trace->session->minor;
+ enum lttng_trace_chunk_status chunk_status;
if (vstream->index_file) {
goto end;
ret = -ENOENT;
goto end;
}
- vstream->index_file = lttng_index_file_create_from_trace_chunk_read_only(
+ chunk_status = lttng_index_file_create_from_trace_chunk_read_only(
vstream->stream_file.trace_chunk, rstream->path_name,
rstream->channel_name, rstream->tracefile_size,
vstream->current_tracefile_id,
lttng_to_index_major(connection_major, connection_minor),
- lttng_to_index_minor(connection_major, connection_minor));
- if (!vstream->index_file) {
- ret = -1;
+ lttng_to_index_minor(connection_major, connection_minor),
+ true, &vstream->index_file);
+ if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+ if (chunk_status == LTTNG_TRACE_CHUNK_STATUS_NO_FILE) {
+ ret = -ENOENT;
+ } else {
+ ret = -1;
+ }
}
end:
{
int ret;
+ DBG("Check index status: index_received_seqcount %" PRIu64 " "
+ "index_sent_seqcount %" PRIu64 " "
+ "for stream %" PRIu64,
+ rstream->index_received_seqcount,
+ vstream->index_sent_seqcount,
+ vstream->stream->stream_handle);
if ((trace->session->connection_closed || rstream->closed)
&& rstream->index_received_seqcount
== vstream->index_sent_seqcount) {
index->status = htobe32(LTTNG_VIEWER_INDEX_HUP);
goto hup;
} else if (rstream->beacon_ts_end != -1ULL &&
+ (rstream->index_received_seqcount == 0 ||
+ (vstream->index_sent_seqcount != 0 &&
rstream->index_received_seqcount
- == vstream->index_sent_seqcount) {
+ <= vstream->index_sent_seqcount))) {
/*
* We've received a synchronization beacon and the last index
* available has been sent, the index for now is inactive.
* inform the client of a time interval during which we can
* guarantee that there are no events to read (and never will
* be).
+ *
+ * The sent seqcount can grow higher than receive seqcount on
+ * clear because the rotation performed by clear will push
+ * the index_sent_seqcount ahead (see
+ * viewer_stream_sync_tracefile_array_tail) and skip over
+ * packet sequence numbers.
*/
index->status = htobe32(LTTNG_VIEWER_INDEX_INACTIVE);
index->timestamp_end = htobe64(rstream->beacon_ts_end);
index->stream_id = htobe64(rstream->ctf_stream_id);
+ DBG("Check index status: inactive with beacon, for stream %" PRIu64,
+ vstream->stream->stream_handle);
goto index_ready;
- } else if (rstream->index_received_seqcount
- == vstream->index_sent_seqcount) {
+ } else if (rstream->index_received_seqcount == 0 ||
+ (vstream->index_sent_seqcount != 0 &&
+ rstream->index_received_seqcount
+ <= vstream->index_sent_seqcount)) {
/*
- * This checks whether received == sent seqcount. In
+ * This checks whether received <= sent seqcount. In
* this case, we have not received a beacon. Therefore,
* we can only ask the client to retry later.
+ *
+ * The sent seqcount can grow higher than receive seqcount on
+ * clear because the rotation performed by clear will push
+ * the index_sent_seqcount ahead (see
+ * viewer_stream_sync_tracefile_array_tail) and skip over
+ * packet sequence numbers.
*/
index->status = htobe32(LTTNG_VIEWER_INDEX_RETRY);
+ DBG("Check index status: retry for stream %" PRIu64,
+ vstream->stream->stream_handle);
goto index_ready;
} else if (!tracefile_array_seq_in_file(rstream->tfa,
vstream->current_tracefile_id,
DBG("Viewer stream %" PRIu64 " rotation",
vstream->stream->stream_handle);
ret = viewer_stream_rotate(vstream);
- if (ret < 0) {
- goto end;
- } else if (ret == 1) {
+ if (ret == 1) {
/* EOF across entire stream. */
index->status = htobe32(LTTNG_VIEWER_INDEX_HUP);
goto hup;
vstream->current_tracefile_id,
vstream->index_sent_seqcount)) {
index->status = htobe32(LTTNG_VIEWER_INDEX_RETRY);
+ DBG("Check index status: retry: "
+ "tracefile array sequence number %" PRIu64
+ " not in file for stream %" PRIu64,
+ vstream->index_sent_seqcount,
+ vstream->stream->stream_handle);
goto index_ready;
}
assert(tracefile_array_seq_in_file(rstream->tfa,
}
/* ret == 0 means successful so we continue. */
ret = 0;
-end:
return ret;
hup:
goto send_reply;
}
- /* Try to open an index if one is needed for that stream. */
- ret = try_open_index(vstream, rstream);
- if (ret < 0) {
- if (ret == -ENOENT) {
- /*
- * The index is created only when the first data
- * packet arrives, it might not be ready at the
- * beginning of the session
- */
- viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_RETRY);
- } else {
- /* Unhandled error. */
+ if (rstream->ongoing_rotation.is_set) {
+ /* Rotation is ongoing, try again later. */
+ viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_RETRY);
+ goto send_reply;
+ }
+
+ if (rstream->trace->session->ongoing_rotation) {
+ /* Rotation is ongoing, try again later. */
+ viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_RETRY);
+ goto send_reply;
+ }
+
+ if (rstream->trace_chunk) {
+ uint64_t rchunk_id, vchunk_id;
+
+ /*
+ * If the relay stream is not yet closed, ensure the viewer
+ * chunk matches the relay chunk after clear.
+ */
+ if (lttng_trace_chunk_get_id(rstream->trace_chunk,
+ &rchunk_id) != LTTNG_TRACE_CHUNK_STATUS_OK) {
viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_ERR);
+ goto send_reply;
+ }
+ if (lttng_trace_chunk_get_id(
+ conn->viewer_session->current_trace_chunk,
+ &vchunk_id) != LTTNG_TRACE_CHUNK_STATUS_OK) {
+ viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_ERR);
+ goto send_reply;
+ }
+
+ if (rchunk_id != vchunk_id) {
+ DBG("Relay and viewer chunk ids differ: "
+ "rchunk_id %" PRIu64 " vchunk_id %" PRIu64,
+ rchunk_id, vchunk_id);
+
+ lttng_trace_chunk_put(
+ conn->viewer_session->current_trace_chunk);
+ conn->viewer_session->current_trace_chunk = NULL;
+ ret = viewer_session_set_trace_chunk_copy(
+ conn->viewer_session,
+ rstream->trace_chunk);
+ if (ret) {
+ viewer_index.status =
+ htobe32(LTTNG_VIEWER_INDEX_ERR);
+ goto send_reply;
+ }
}
- goto send_reply;
+ }
+ if (conn->viewer_session->current_trace_chunk !=
+ vstream->stream_file.trace_chunk) {
+ bool acquired_reference;
+
+ DBG("Viewer session and viewer stream chunk differ: "
+ "vsession chunk %p vstream chunk %p",
+ conn->viewer_session->current_trace_chunk,
+ vstream->stream_file.trace_chunk);
+ lttng_trace_chunk_put(vstream->stream_file.trace_chunk);
+ acquired_reference = lttng_trace_chunk_get(conn->viewer_session->current_trace_chunk);
+ assert(acquired_reference);
+ vstream->stream_file.trace_chunk =
+ conn->viewer_session->current_trace_chunk;
+ viewer_stream_sync_tracefile_array_tail(vstream);
+ viewer_stream_close_files(vstream);
}
ret = check_index_status(vstream, rstream, ctf_trace, &viewer_index);
/* At this point, ret is 0 thus we will be able to read the index. */
assert(!ret);
+ /* Try to open an index if one is needed for that stream. */
+ ret = try_open_index(vstream, rstream);
+ if (ret == -ENOENT) {
+ if (rstream->closed) {
+ viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_HUP);
+ goto send_reply;
+ } else {
+ viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_RETRY);
+ goto send_reply;
+ }
+ }
+ if (ret < 0) {
+ viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_ERR);
+ goto send_reply;
+ }
+
/*
* vstream->stream_fd may be NULL if it has been closed by
* tracefile rotation, or if we are at the beginning of the
goto error_put;
}
+ /*
+ * It is possible the the file we are trying to open is
+ * missing if the stream has been closed (application exits with
+ * per-pid buffers) and a clear command has been performed.
+ */
status = lttng_trace_chunk_open_file(
vstream->stream_file.trace_chunk,
- file_path, O_RDONLY, 0, &fd);
+ file_path, O_RDONLY, 0, &fd, true);
if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+ if (status == LTTNG_TRACE_CHUNK_STATUS_NO_FILE &&
+ rstream->closed) {
+ viewer_index.status = htobe32(LTTNG_VIEWER_INDEX_HUP);
+ goto send_reply;
+ }
PERROR("Failed to open trace file for viewer stream");
goto error_put;
}
goto error;
}
- assert(vstream->metadata_sent <= vstream->stream->metadata_received);
-
- len = vstream->stream->metadata_received - vstream->metadata_sent;
- if (len == 0) {
+ if (vstream->metadata_sent >= vstream->stream->metadata_received) {
+ /*
+ * The live viewers expect to receive a NO_NEW_METADATA
+ * status before a stream disappears, otherwise they abort the
+ * entire live connection when receiving an error status.
+ *
+ * Clear feature resets the metadata_sent to 0 until the
+ * same metadata is received again.
+ */
reply.status = htobe32(LTTNG_VIEWER_NO_NEW_METADATA);
+ /*
+ * The live viewer considers a closed 0 byte metadata stream as
+ * an error.
+ */
+ if (vstream->metadata_sent > 0) {
+ vstream->stream->no_new_metadata_notified = true;
+ if (vstream->stream->closed) {
+ /* Release ownership for the viewer metadata stream. */
+ viewer_stream_put(vstream);
+ }
+ }
goto send_reply;
}
+ len = vstream->stream->metadata_received - vstream->metadata_sent;
+
/* first time, we open the metadata file */
if (!vstream->stream_file.fd) {
int fd;
goto error;
}
+ /*
+ * It is possible the the metadata file we are trying to open is
+ * missing if the stream has been closed (application exits with
+ * per-pid buffers) and a clear command has been performed.
+ */
status = lttng_trace_chunk_open_file(
vstream->stream_file.trace_chunk,
- file_path, O_RDONLY, 0, &fd);
+ file_path, O_RDONLY, 0, &fd, true);
if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+ if (status == LTTNG_TRACE_CHUNK_STATUS_NO_FILE) {
+ reply.status = htobe32(LTTNG_VIEWER_NO_NEW_METADATA);
+ len = 0;
+ if (vstream->stream->closed) {
+ viewer_stream_put(vstream);
+ }
+ goto send_reply;
+ }
PERROR("Failed to open metadata file for viewer stream");
goto error;
}
goto error;
}
vstream->metadata_sent += read_len;
- if (vstream->metadata_sent == vstream->stream->metadata_received
- && vstream->stream->closed) {
- /* Release ownership for the viewer metadata stream. */
- viewer_stream_put(vstream);
- }
-
reply.status = htobe32(LTTNG_VIEWER_METADATA_OK);
goto send_reply;
goto viewer_connections_ht_error;
}
- ret = create_thread_poll_set(&events, 2);
+ ret = create_named_thread_poll_set(&events, 2,
+ "Live viewer worker thread epoll");
if (ret < 0) {
goto error_poll_create;
}
exit:
error:
- lttng_poll_clean(&events);
+ (void) fd_tracker_util_poll_clean(the_fd_tracker, &events);
/* Cleanup remaining connection object. */
rcu_read_lock();
lttng_ht_destroy(viewer_connections_ht);
viewer_connections_ht_error:
/* Close relay conn pipes */
- utils_close_pipe(live_conn_pipe);
+ (void) fd_tracker_util_pipe_close(the_fd_tracker, live_conn_pipe);
if (err) {
DBG("Viewer worker thread exited with error");
}
*/
static int create_conn_pipe(void)
{
- return utils_create_pipe_cloexec(live_conn_pipe);
+ return fd_tracker_util_pipe_open_cloexec(the_fd_tracker,
+ "Live connection pipe", live_conn_pipe);
}
int relayd_live_join(void)