X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=blobdiff_plain;f=formats%2Flttng-live%2Flttng-live-comm.c;h=e08926380efab45e306a01ec59607012f51fbc90;hp=b9bbbb6725da0aa2bcb7076a0e624b848db44206;hb=1e8e8e2fb8ef9e36cbc2c5a692e9fa1d11cbb52a;hpb=7840bb080e2b9bf478afa9ab8a7059f8447a5f1e diff --git a/formats/lttng-live/lttng-live-comm.c b/formats/lttng-live/lttng-live-comm.c index b9bbbb67..e0892638 100644 --- a/formats/lttng-live/lttng-live-comm.c +++ b/formats/lttng-live/lttng-live-comm.c @@ -26,18 +26,17 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include +#include #include #include #include @@ -51,6 +50,9 @@ #include #include +#include +#include +#include #include "lttng-live.h" #include "lttng-viewer-abi.h" @@ -102,7 +104,7 @@ ssize_t lttng_live_send(int fd, const void *buf, size_t len) ssize_t ret; do { - ret = send(fd, buf, len, MSG_NOSIGNAL); + ret = bt_send_nosigpipe(fd, buf, len); } while (ret < 0 && errno == EINTR); return ret; } @@ -133,7 +135,7 @@ int lttng_live_connect_viewer(struct lttng_live_ctx *ctx) server_addr.sin_family = AF_INET; server_addr.sin_port = htons(ctx->port); server_addr.sin_addr = *((struct in_addr *) host->h_addr); - bzero(&(server_addr.sin_zero), 8); + memset(&(server_addr.sin_zero), 0, 8); if (connect(ctx->control_sock, (struct sockaddr *) &server_addr, sizeof(struct sockaddr)) == -1) { @@ -155,6 +157,8 @@ int lttng_live_establish_connection(struct lttng_live_ctx *ctx) { struct lttng_viewer_cmd cmd; struct lttng_viewer_connect connect; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(connect); + char cmd_buf[cmd_buf_len]; int ret; ssize_t ret_len; @@ -164,27 +168,28 @@ int lttng_live_establish_connection(struct lttng_live_ctx *ctx) } cmd.cmd = htobe32(LTTNG_VIEWER_CONNECT); - cmd.data_size = sizeof(connect); - cmd.cmd_version = 0; + cmd.data_size = htobe64((uint64_t) sizeof(connect)); + cmd.cmd_version = htobe32(0); connect.viewer_session_id = -1ULL; /* will be set on recv */ connect.major = htobe32(LTTNG_LIVE_MAJOR); connect.minor = htobe32(LTTNG_LIVE_MINOR); connect.type = htobe32(LTTNG_VIEWER_CLIENT_COMMAND); - ret_len = lttng_live_send(ctx->control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - perror("[error] Error sending cmd"); - goto error; - } - assert(ret_len == sizeof(cmd)); + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &connect, sizeof(connect)); - ret_len = lttng_live_send(ctx->control_sock, &connect, sizeof(connect)); + ret_len = lttng_live_send(ctx->control_sock, cmd_buf, cmd_buf_len); if (ret_len < 0) { - perror("[error] Error sending version"); + perror("[error] Error sending cmd for establishing session"); goto error; } - assert(ret_len == sizeof(connect)); + assert(ret_len == cmd_buf_len); ret_len = lttng_live_recv(ctx->control_sock, &connect, sizeof(connect)); if (ret_len == 0) { @@ -244,7 +249,7 @@ void print_session_list(GPtrArray *session_list, const char *path) for (i = 0; i < session_list->len; i++) { relay_session = g_ptr_array_index(session_list, i); - fprintf(stdout, "%s/host/%s/%s (timer = %u, " + fprintf(LTTNG_LIVE_OUTPUT_FP, "%s/host/%s/%s (timer = %u, " "%u stream(s), %u client(s) connected)\n", path, relay_session->hostname, relay_session->name, relay_session->timer, @@ -262,9 +267,9 @@ void update_session_list(GPtrArray *session_list, char *hostname, for (i = 0; i < session_list->len; i++) { relay_session = g_ptr_array_index(session_list, i); - if ((strncmp(relay_session->hostname, hostname, NAME_MAX) == 0) && + if ((strncmp(relay_session->hostname, hostname, MAXNAMLEN) == 0) && strncmp(relay_session->name, - session_name, NAME_MAX) == 0) { + session_name, MAXNAMLEN) == 0) { relay_session->streams += streams; if (relay_session->clients < clients) relay_session->clients = clients; @@ -276,8 +281,8 @@ void update_session_list(GPtrArray *session_list, char *hostname, return; relay_session = g_new0(struct lttng_live_relay_session, 1); - relay_session->hostname = strndup(hostname, NAME_MAX); - relay_session->name = strndup(session_name, NAME_MAX); + relay_session->hostname = bt_strndup(hostname, MAXNAMLEN); + relay_session->name = bt_strndup(session_name, MAXNAMLEN); relay_session->clients = clients; relay_session->streams = streams; relay_session->timer = timer; @@ -305,8 +310,8 @@ int lttng_live_list_sessions(struct lttng_live_ctx *ctx, const char *path) } cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS); - cmd.data_size = 0; - cmd.cmd_version = 0; + cmd.data_size = htobe64((uint64_t) 0); + cmd.cmd_version = htobe32(0); ret_len = lttng_live_send(ctx->control_sock, &cmd, sizeof(cmd)); if (ret_len < 0) { @@ -351,8 +356,8 @@ int lttng_live_list_sessions(struct lttng_live_ctx *ctx, const char *path) be32toh(lsession.live_timer)); } else { if ((strncmp(lsession.session_name, ctx->session_name, - NAME_MAX) == 0) && (strncmp(lsession.hostname, - ctx->traced_hostname, NAME_MAX) == 0)) { + MAXNAMLEN) == 0) && (strncmp(lsession.hostname, + ctx->traced_hostname, MAXNAMLEN) == 0)) { printf_verbose("Reading from session %" PRIu64 "\n", session_id); g_array_append_val(ctx->session_ids, @@ -381,20 +386,23 @@ int lttng_live_ctf_trace_assign(struct lttng_live_viewer_stream *stream, int ret = 0; trace = g_hash_table_lookup(stream->session->ctf_traces, - (gpointer) ctf_trace_id); + &ctf_trace_id); if (!trace) { trace = g_new0(struct lttng_live_ctf_trace, 1); trace->ctf_trace_id = ctf_trace_id; - trace->streams = g_ptr_array_new(); + printf_verbose("Create trace ctf_trace_id %" PRIu64 "\n", ctf_trace_id); + BT_INIT_LIST_HEAD(&trace->stream_list); g_hash_table_insert(stream->session->ctf_traces, - (gpointer) ctf_trace_id, + &trace->ctf_trace_id, trace); } if (stream->metadata_flag) trace->metadata_stream = stream; + assert(!stream->in_trace); + stream->in_trace = 1; stream->ctf_trace = trace; - g_ptr_array_add(trace->streams, stream); + bt_list_add(&stream->trace_stream_node, &trace->stream_list); return ret; } @@ -421,6 +429,8 @@ int lttng_live_attach_session(struct lttng_live_ctx *ctx, uint64_t id) struct lttng_viewer_attach_session_request rq; struct lttng_viewer_attach_session_response rp; struct lttng_viewer_stream stream; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); + char cmd_buf[cmd_buf_len]; int ret, i; ssize_t ret_len; @@ -430,8 +440,8 @@ int lttng_live_attach_session(struct lttng_live_ctx *ctx, uint64_t id) } cmd.cmd = htobe32(LTTNG_VIEWER_ATTACH_SESSION); - cmd.data_size = sizeof(rq); - cmd.cmd_version = 0; + cmd.data_size = htobe64((uint64_t) sizeof(rq)); + cmd.cmd_version = htobe32(0); memset(&rq, 0, sizeof(rq)); rq.session_id = htobe64(id); @@ -439,19 +449,20 @@ int lttng_live_attach_session(struct lttng_live_ctx *ctx, uint64_t id) // rq.seek = htobe32(LTTNG_VIEWER_SEEK_BEGINNING); rq.seek = htobe32(LTTNG_VIEWER_SEEK_LAST); - ret_len = lttng_live_send(ctx->control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - perror("[error] Error sending cmd"); - goto error; - } - assert(ret_len == sizeof(cmd)); + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); - ret_len = lttng_live_send(ctx->control_sock, &rq, sizeof(rq)); + ret_len = lttng_live_send(ctx->control_sock, cmd_buf, cmd_buf_len); if (ret_len < 0) { - perror("[error] Error sending attach request"); + perror("[error] Error sending attach command and request"); goto error; } - assert(ret_len == sizeof(rq)); + assert(ret_len == cmd_buf_len); ret_len = lttng_live_recv(ctx->control_sock, &rp, sizeof(rp)); if (ret_len == 0) { @@ -498,18 +509,22 @@ int lttng_live_attach_session(struct lttng_live_ctx *ctx, uint64_t id) ret = 0; goto end; } - printf_verbose("Waiting for %" PRIu64 " streams:\n", - ctx->session->stream_count); - ctx->session->streams = g_new0(struct lttng_live_viewer_stream, - ctx->session->stream_count); + printf_verbose("Waiting for %d streams:\n", + be32toh(rp.streams_count)); for (i = 0; i < be32toh(rp.streams_count); i++) { - ret_len = lttng_live_recv(ctx->control_sock, &stream, sizeof(stream)); + struct lttng_live_viewer_stream *lvstream; + + lvstream = g_new0(struct lttng_live_viewer_stream, 1); + ret_len = lttng_live_recv(ctx->control_sock, &stream, + sizeof(stream)); if (ret_len == 0) { fprintf(stderr, "[error] Remote side has closed connection\n"); + g_free(lvstream); goto error; } if (ret_len < 0) { perror("[error] Error receiving stream"); + g_free(lvstream); goto error; } assert(ret_len == sizeof(stream)); @@ -519,21 +534,23 @@ int lttng_live_attach_session(struct lttng_live_ctx *ctx, uint64_t id) printf_verbose(" stream %" PRIu64 " : %s/%s\n", be64toh(stream.id), stream.path_name, stream.channel_name); - ctx->session->streams[i].id = be64toh(stream.id); - ctx->session->streams[i].session = ctx->session; + lvstream->id = be64toh(stream.id); + lvstream->session = ctx->session; - ctx->session->streams[i].mmap_size = 0; - ctx->session->streams[i].ctf_stream_id = -1ULL; + lvstream->mmap_size = 0; + lvstream->ctf_stream_id = -1ULL; if (be32toh(stream.metadata_flag)) { - ctx->session->streams[i].metadata_flag = 1; + lvstream->metadata_flag = 1; } - ret = lttng_live_ctf_trace_assign(&ctx->session->streams[i], + ret = lttng_live_ctf_trace_assign(lvstream, be64toh(stream.ctf_trace_id)); if (ret < 0) { + g_free(lvstream); goto error; } - + bt_list_add(&lvstream->session_stream_node, + &ctx->session->stream_list); } ret = 0; end: @@ -559,10 +576,11 @@ restart: id = g_array_index(ctx->session_ids, uint64_t, i); ret = lttng_live_get_new_streams(ctx, id); printf_verbose("Asking for new streams returns %d\n", ret); + if (lttng_live_should_quit()) { + ret = -1; + goto end; + } if (ret < 0) { - if (lttng_live_should_quit()) { - goto end; - } if (ret == -LTTNG_VIEWER_NEW_STREAMS_HUP) { printf_verbose("Session %" PRIu64 " closed\n", id); @@ -585,7 +603,12 @@ restart: nb_streams += ret; } } - ret = nb_streams; + if (ctx->session_ids->len == 0) { + /* All sessions are closed. */ + ret = -1; + } else { + ret = nb_streams; + } end: return ret; @@ -599,6 +622,11 @@ int append_metadata(struct lttng_live_ctx *ctx, struct lttng_live_viewer_stream *metadata; char *metadata_buf = NULL; + if (!viewer_stream->ctf_trace->handle) { + printf_verbose("append_metadata: trace handle not ready yet.\n"); + return 0; + } + printf_verbose("get_next_index: new metadata needed\n"); ret = get_new_metadata(ctx, viewer_stream, &metadata_buf); if (ret < 0) { @@ -639,6 +667,8 @@ int get_data_packet(struct lttng_live_ctx *ctx, struct lttng_viewer_cmd cmd; struct lttng_viewer_get_packet rq; struct lttng_viewer_trace_packet rp; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); + char cmd_buf[cmd_buf_len]; ssize_t ret_len; int ret; @@ -647,28 +677,30 @@ retry: ret = -1; goto end; } + cmd.cmd = htobe32(LTTNG_VIEWER_GET_PACKET); - cmd.data_size = sizeof(rq); - cmd.cmd_version = 0; + cmd.data_size = htobe64((uint64_t) sizeof(rq)); + cmd.cmd_version = htobe32(0); memset(&rq, 0, sizeof(rq)); rq.stream_id = htobe64(stream->id); rq.offset = htobe64(offset); rq.len = htobe32(len); - ret_len = lttng_live_send(ctx->control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - perror("[error] Error sending cmd"); - goto error; - } - assert(ret_len == sizeof(cmd)); + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); - ret_len = lttng_live_send(ctx->control_sock, &rq, sizeof(rq)); + ret_len = lttng_live_send(ctx->control_sock, cmd_buf, cmd_buf_len); if (ret_len < 0) { - perror("[error] Error sending get_data_packet request"); + perror("[error] Error sending get_data_packet cmd and request"); goto error; } - assert(ret_len == sizeof(rq)); + assert(ret_len == cmd_buf_len); ret_len = lttng_live_recv(ctx->control_sock, &rp, sizeof(rp)); if (ret_len == 0) { @@ -680,8 +712,8 @@ retry: goto error; } if (ret_len != sizeof(rp)) { - fprintf(stderr, "[error] get_data_packet: expected %" PRId64 - ", received %" PRId64 "\n", sizeof(rp), + fprintf(stderr, "[error] get_data_packet: expected %zu" + ", received %zd\n", sizeof(rp), ret_len); goto error; } @@ -792,6 +824,8 @@ int get_one_metadata_packet(struct lttng_live_ctx *ctx, struct lttng_viewer_metadata_packet rp; char *data = NULL; ssize_t ret_len; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); + char cmd_buf[cmd_buf_len]; if (lttng_live_should_quit()) { ret = -1; @@ -800,22 +834,26 @@ int get_one_metadata_packet(struct lttng_live_ctx *ctx, rq.stream_id = htobe64(metadata_stream->id); cmd.cmd = htobe32(LTTNG_VIEWER_GET_METADATA); - cmd.data_size = sizeof(rq); - cmd.cmd_version = 0; + cmd.data_size = htobe64((uint64_t) sizeof(rq)); + cmd.cmd_version = htobe32(0); - ret_len = lttng_live_send(ctx->control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - perror("[error] Error sending cmd"); - goto error; - } - assert(ret_len == sizeof(cmd)); + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); - ret_len = lttng_live_send(ctx->control_sock, &rq, sizeof(rq)); + printf_verbose("get_metadata for trace_id: %d, ctf_trace_id: %" PRIu64 "\n", + metadata_stream->ctf_trace->trace_id, + metadata_stream->ctf_trace->ctf_trace_id); + ret_len = lttng_live_send(ctx->control_sock, cmd_buf, cmd_buf_len); if (ret_len < 0) { - perror("[error] Error sending get_metadata request"); + perror("[error] Error sending get_metadata cmd and request"); goto error; } - assert(ret_len == sizeof(rq)); + assert(ret_len == cmd_buf_len); ret_len = lttng_live_recv(ctx->control_sock, &rp, sizeof(rp)); if (ret_len == 0) { @@ -928,10 +966,15 @@ int get_new_metadata(struct lttng_live_ctx *ctx, if (!len_read) { (void) poll(NULL, 0, ACTIVE_POLL_DELAY); } + if (ret < 0) { + break; /* Stop on error. */ + } } while (ret > 0 || !len_read); - if (fclose(metadata_stream->metadata_fp_write)) - perror("fclose"); + if (babeltrace_close_memstream(metadata_buf, &size, + metadata_stream->metadata_fp_write)) { + perror("babeltrace_close_memstream"); + } metadata_stream->metadata_fp_write = NULL; error: @@ -971,32 +1014,34 @@ int get_next_index(struct lttng_live_ctx *ctx, int ret; ssize_t ret_len; struct lttng_viewer_index *rp = &viewer_stream->current_index; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); + char cmd_buf[cmd_buf_len]; cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEXT_INDEX); - cmd.data_size = sizeof(rq); - cmd.cmd_version = 0; + cmd.data_size = htobe64((uint64_t) sizeof(rq)); + cmd.cmd_version = htobe32(0); memset(&rq, 0, sizeof(rq)); rq.stream_id = htobe64(viewer_stream->id); + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); retry: if (lttng_live_should_quit()) { ret = -1; goto end; } - ret_len = lttng_live_send(ctx->control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - perror("[error] Error sending cmd"); - goto error; - } - assert(ret_len == sizeof(cmd)); - - ret_len = lttng_live_send(ctx->control_sock, &rq, sizeof(rq)); + ret_len = lttng_live_send(ctx->control_sock, &cmd_buf, cmd_buf_len); if (ret_len < 0) { - perror("[error] Error sending get_next_index request"); + perror("[error] Error sending get_next_index cmd and request"); goto error; } - assert(ret_len == sizeof(rq)); + assert(ret_len == cmd_buf_len); ret_len = lttng_live_recv(ctx->control_sock, rp, sizeof(*rp)); if (ret_len == 0) { @@ -1014,6 +1059,13 @@ retry: switch (be32toh(rp->status)) { case LTTNG_VIEWER_INDEX_INACTIVE: printf_verbose("get_next_index: inactive\n"); + + if (index->ts_cycles.timestamp_end == + be64toh(rp->timestamp_end)) { + /* Already seen this timestamp. */ + (void) poll(NULL, 0, ACTIVE_POLL_DELAY); + } + memset(index, 0, sizeof(struct packet_index)); index->ts_cycles.timestamp_end = be64toh(rp->timestamp_end); *stream_id = be64toh(rp->stream_id); @@ -1052,6 +1104,11 @@ retry: viewer_stream->id = -1ULL; index->offset = EOF; ctx->session->stream_count--; + viewer_stream->in_trace = 0; + bt_list_del(&viewer_stream->trace_stream_node); + bt_list_del(&viewer_stream->session_stream_node); + g_free(viewer_stream); + *stream_id = be64toh(rp->stream_id); break; case LTTNG_VIEWER_INDEX_ERR: fprintf(stderr, "[error] get_next_index: error\n"); @@ -1116,7 +1173,7 @@ int handle_seek_position(size_t index, int whence, struct ctf_stream_pos *pos, struct ctf_file_stream *file_stream) { - int ret; + int ret = 0; switch (whence) { case SEEK_CUR: @@ -1250,14 +1307,30 @@ retry: } if (cur_index->content_size == 0) { + /* Beacon packet index */ if (file_stream->parent.stream_class) { file_stream->parent.cycles_timestamp = cur_index->ts_cycles.timestamp_end; file_stream->parent.real_timestamp = ctf_get_real_timestamp( &file_stream->parent, cur_index->ts_cycles.timestamp_end); + + /* + * Duplicate the data from the previous index, because + * the one we just received is only a beacon with no + * relevant information except the timestamp_end. We + * don't need to keep this timestamp_end because we already + * updated the file_stream timestamps, so we only need + * to keep the last real index data as prev_index. That + * way, we keep the original prev timestamps and + * discarded events counter. This is the same behaviour + * as if we were reading a local trace, we would not + * have fake indexes between real indexes. + */ + memcpy(cur_index, prev_index, sizeof(struct packet_index)); } } else { + /* Real packet index */ if (file_stream->parent.stream_class) { /* Convert the timestamps and append to the real_index. */ cur_index->ts_real.timestamp_begin = ctf_get_real_timestamp( @@ -1271,12 +1344,33 @@ retry: ctf_update_current_packet_index(&file_stream->parent, prev_index, cur_index); + /* + * We need to check if we are in trace read or called + * from packet indexing. In this last case, the + * collection is not there, so we cannot print the + * timestamps. + */ + if ((&file_stream->parent)->stream_class->trace->parent.collection) { + ctf_print_discarded_lost(stderr, &file_stream->parent); + } + file_stream->parent.cycles_timestamp = cur_index->ts_cycles.timestamp_begin; file_stream->parent.real_timestamp = cur_index->ts_real.timestamp_begin; } + /* + * Flush the output between attempts to grab a packet, thus + * ensuring we flush at least at the periodical timer period. + * This ensures the output remains reactive for interactive users and + * that the output is flushed when redirected to a file by the shell. + */ + if (fflush(LTTNG_LIVE_OUTPUT_FP) < 0) { + perror("fflush"); + goto end; + } + if (pos->packet_size == 0 || pos->offset == EOF) { goto end; } @@ -1316,8 +1410,8 @@ int lttng_live_create_viewer_session(struct lttng_live_ctx *ctx) } cmd.cmd = htobe32(LTTNG_VIEWER_CREATE_SESSION); - cmd.data_size = 0; - cmd.cmd_version = 0; + cmd.data_size = htobe64((uint64_t) 0); + cmd.cmd_version = htobe32(0); ret_len = lttng_live_send(ctx->control_sock, &cmd, sizeof(cmd)); if (ret_len < 0) { @@ -1355,10 +1449,22 @@ int del_traces(gpointer key, gpointer value, gpointer user_data) struct bt_context *bt_ctx = user_data; struct lttng_live_ctf_trace *trace = value; int ret; + struct lttng_live_viewer_stream *lvstream, *tmp; - ret = bt_context_remove_trace(bt_ctx, trace->trace_id); - if (ret < 0) - fprintf(stderr, "[error] removing trace from context\n"); + /* + * We don't have ownership of the live viewer stream, just + * remove them from our list. + */ + bt_list_for_each_entry_safe(lvstream, tmp, &trace->stream_list, + trace_stream_node) { + lvstream->in_trace = 0; + bt_list_del(&lvstream->trace_stream_node); + } + if (trace->in_use) { + ret = bt_context_remove_trace(bt_ctx, trace->trace_id); + if (ret < 0) + fprintf(stderr, "[error] removing trace from context\n"); + } /* remove the key/value pair from the HT. */ return 1; @@ -1368,7 +1474,7 @@ static int add_one_trace(struct lttng_live_ctx *ctx, struct lttng_live_ctf_trace *trace) { - int i, ret; + int ret; struct bt_context *bt_ctx = ctx->bt_ctx; struct lttng_live_viewer_stream *stream; struct bt_mmap_stream *new_mmap_stream; @@ -1376,6 +1482,9 @@ int add_one_trace(struct lttng_live_ctx *ctx, struct bt_trace_descriptor *td; struct bt_trace_handle *handle; + printf_verbose("Add one trace ctf_trace_id: %" PRIu64 + " (metadata_stream: %p)\n", + trace->ctf_trace_id, trace->metadata_stream); /* * We don't know how many streams we will receive for a trace, so * once we are done receiving the traces, we add all the traces @@ -1386,15 +1495,20 @@ int add_one_trace(struct lttng_live_ctx *ctx, * If a trace is already in the context, we just skip this function. */ if (trace->in_use) { + printf_verbose("Trace already in use\n"); ret = 0; goto end; } + /* + * add_one_trace can be called recursively if during the + * bt_context_add_trace call we need to fetch new streams, so we need to + * prevent a recursive call to process our current trace. + */ + trace->in_use = 1; BT_INIT_LIST_HEAD(&mmap_list.head); - for (i = 0; i < trace->streams->len; i++) { - stream = g_ptr_array_index(trace->streams, i); - + bt_list_for_each_entry(stream, &trace->stream_list, trace_stream_node) { if (!stream->metadata_flag) { new_mmap_stream = zmalloc(sizeof(struct bt_mmap_stream)); new_mmap_stream->priv = (void *) stream; @@ -1416,6 +1530,7 @@ int add_one_trace(struct lttng_live_ctx *ctx, goto end_free; } + printf_verbose("Metadata stream found\n"); trace->metadata_fp = babeltrace_fmemopen(metadata_buf, stream->metadata_len, "rb"); if (!trace->metadata_fp) { @@ -1452,7 +1567,7 @@ int add_one_trace(struct lttng_live_ctx *ctx, } trace->trace_id = ret; - trace->in_use = 1; + printf_verbose("Trace now in use, id = %d\n", trace->trace_id); goto end; @@ -1462,6 +1577,56 @@ end: return ret; } +/* + * Make sure all the traces we know have a metadata stream or loop on + * ask_new_streams until it is done. This must be called before we call + * add_one_trace. + * + * Return 0 when all known traces have a metadata stream, a negative value + * on error. + */ +static +int check_traces_metadata(struct lttng_live_ctx *ctx) +{ + int ret; + struct lttng_live_ctf_trace *trace; + GHashTableIter it; + gpointer key; + gpointer value; + +retry: + g_hash_table_iter_init(&it, ctx->session->ctf_traces); + while (g_hash_table_iter_next(&it, &key, &value)) { + trace = (struct lttng_live_ctf_trace *) value; + printf_verbose("Check trace %" PRIu64 " metadata\n", trace->ctf_trace_id); + while (!trace->metadata_stream) { + printf_verbose("Waiting for metadata stream\n"); + if (lttng_live_should_quit()) { + ret = 0; + goto end; + } + ret = ask_new_streams(ctx); + if (ret < 0) { + goto end; + } else if (ret == 0) { + (void) poll(NULL, 0, ACTIVE_POLL_DELAY); + } else { + /* + * If ask_new_stream got streams from a trace we did not know + * about until now, we have to reinitialize the iterator. + */ + goto retry; + } + } + } + + ret = 0; + +end: + printf_verbose("End check traces metadata\n"); + return ret; +} + static int add_traces(struct lttng_live_ctx *ctx) { @@ -1470,6 +1635,17 @@ int add_traces(struct lttng_live_ctx *ctx) GHashTableIter it; gpointer key; gpointer value; + unsigned int nr_traces; + + printf_verbose("Begin add traces\n"); + +retry: + nr_traces = g_hash_table_size(ctx->session->ctf_traces); + + ret = check_traces_metadata(ctx); + if (ret < 0) { + goto end; + } g_hash_table_iter_init(&it, ctx->session->ctf_traces); while (g_hash_table_iter_next(&it, &key, &value)) { @@ -1478,11 +1654,21 @@ int add_traces(struct lttng_live_ctx *ctx) if (ret < 0) { goto end; } + /* + * If a new trace got added while we were adding the trace, the + * iterator is invalid and we have to restart. + */ + if (g_hash_table_size(ctx->session->ctf_traces) != nr_traces) { + printf_verbose("New trace(s) added during add_one_trace()\n"); + printf_verbose("JORAJ: GREP HERE\n"); + goto retry; + } } ret = 0; end: + printf_verbose("End add traces\n"); return ret; } @@ -1499,6 +1685,8 @@ int lttng_live_get_new_streams(struct lttng_live_ctx *ctx, uint64_t id) int ret, i, nb_streams = 0; ssize_t ret_len; uint32_t stream_count; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); + char cmd_buf[cmd_buf_len]; if (lttng_live_should_quit()) { ret = -1; @@ -1506,25 +1694,26 @@ int lttng_live_get_new_streams(struct lttng_live_ctx *ctx, uint64_t id) } cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEW_STREAMS); - cmd.data_size = sizeof(rq); - cmd.cmd_version = 0; + cmd.data_size = htobe64((uint64_t) sizeof(rq)); + cmd.cmd_version = htobe32(0); memset(&rq, 0, sizeof(rq)); rq.session_id = htobe64(id); - ret_len = lttng_live_send(ctx->control_sock, &cmd, sizeof(cmd)); - if (ret_len < 0) { - perror("[error] Error sending cmd"); - goto error; - } - assert(ret_len == sizeof(cmd)); + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); - ret_len = lttng_live_send(ctx->control_sock, &rq, sizeof(rq)); + ret_len = lttng_live_send(ctx->control_sock, cmd_buf, cmd_buf_len); if (ret_len < 0) { - perror("[error] Error sending get_new_streams request"); + perror("[error] Error sending get_new_streams cmd and request"); goto error; } - assert(ret_len == sizeof(rq)); + assert(ret_len == cmd_buf_len); ret_len = lttng_live_recv(ctx->control_sock, &rp, sizeof(rp)); if (ret_len == 0) { @@ -1566,18 +1755,22 @@ int lttng_live_get_new_streams(struct lttng_live_ctx *ctx, uint64_t id) ret = 0; goto end; } - printf_verbose("Waiting for %" PRIu64 " streams:\n", - ctx->session->stream_count); - ctx->session->streams = g_new0(struct lttng_live_viewer_stream, - ctx->session->stream_count); + printf_verbose("Waiting for %d streams:\n", stream_count); + for (i = 0; i < stream_count; i++) { - ret_len = lttng_live_recv(ctx->control_sock, &stream, sizeof(stream)); + struct lttng_live_viewer_stream *lvstream; + + lvstream = g_new0(struct lttng_live_viewer_stream, 1); + ret_len = lttng_live_recv(ctx->control_sock, &stream, + sizeof(stream)); if (ret_len == 0) { fprintf(stderr, "[error] Remote side has closed connection\n"); + g_free(lvstream); goto error; } if (ret_len < 0) { perror("[error] Error receiving stream"); + g_free(lvstream); goto error; } assert(ret_len == sizeof(stream)); @@ -1587,22 +1780,24 @@ int lttng_live_get_new_streams(struct lttng_live_ctx *ctx, uint64_t id) printf_verbose(" stream %" PRIu64 " : %s/%s\n", be64toh(stream.id), stream.path_name, stream.channel_name); - ctx->session->streams[i].id = be64toh(stream.id); - ctx->session->streams[i].session = ctx->session; + lvstream->id = be64toh(stream.id); + lvstream->session = ctx->session; - ctx->session->streams[i].mmap_size = 0; - ctx->session->streams[i].ctf_stream_id = -1ULL; + lvstream->mmap_size = 0; + lvstream->ctf_stream_id = -1ULL; if (be32toh(stream.metadata_flag)) { - ctx->session->streams[i].metadata_flag = 1; + lvstream->metadata_flag = 1; } - ret = lttng_live_ctf_trace_assign(&ctx->session->streams[i], + ret = lttng_live_ctf_trace_assign(lvstream, be64toh(stream.ctf_trace_id)); if (ret < 0) { + g_free(lvstream); goto error; } nb_streams++; - + bt_list_add(&lvstream->session_stream_node, + &ctx->session->stream_list); } ret = nb_streams; end: @@ -1633,7 +1828,7 @@ int lttng_live_read(struct lttng_live_ctx *ctx) fmt_write = bt_lookup_format(g_quark_from_static_string("text")); if (!fmt_write) { fprintf(stderr, "[error] ctf-text error\n"); - goto end; + goto end_free; } td_write = fmt_write->open_trace(NULL, O_RDWR, NULL, NULL); @@ -1655,7 +1850,7 @@ int lttng_live_read(struct lttng_live_ctx *ctx) for (i = 0; i < ctx->session_ids->len; i++) { id = g_array_index(ctx->session_ids, uint64_t, i); - printf_verbose("Attaching to session %lu\n", id); + printf_verbose("Attaching to session %" PRIu64 "\n", id); ret = lttng_live_attach_session(ctx, id); printf_verbose("Attaching session returns %d\n", ret); if (ret < 0) { @@ -1685,6 +1880,7 @@ int lttng_live_read(struct lttng_live_ctx *ctx) } ret = ask_new_streams(ctx); if (ret < 0) { + ret = 0; goto end_free; } if (!ctx->session->stream_count) { @@ -1702,10 +1898,10 @@ int lttng_live_read(struct lttng_live_ctx *ctx) if (!iter) { if (lttng_live_should_quit()) { ret = 0; - goto end; + goto end_free; } fprintf(stderr, "[error] Iterator creation error\n"); - goto end; + goto end_free; } for (;;) { if (lttng_live_should_quit()) { @@ -1738,6 +1934,8 @@ int lttng_live_read(struct lttng_live_ctx *ctx) } end_free: + g_hash_table_foreach_remove(ctx->session->ctf_traces, + del_traces, ctx->bt_ctx); bt_context_put(ctx->bt_ctx); end: if (lttng_live_should_quit()) {