X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Fust-consumer%2Fust-consumer.c;h=89109b919a85231615fdb7f86a3c98518a9d422a;hp=b48a3c821d386fbf3cbfaceb8b01d9bbcc4f724d;hb=d4d80f770fc0b0762b18b482381418f01aeb69db;hpb=212d67a218a0e805950f85bd95143a996e957322 diff --git a/src/common/ust-consumer/ust-consumer.c b/src/common/ust-consumer/ust-consumer.c index b48a3c821..89109b919 100644 --- a/src/common/ust-consumer/ust-consumer.c +++ b/src/common/ust-consumer/ust-consumer.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#define _GNU_SOURCE +#define _LGPL_SOURCE #include #include #include @@ -37,14 +37,17 @@ #include #include #include -#include -#include -#include +#include +#include +#include +#include #include #include #include "ust-consumer.h" +#define INT_MAX_STR_LEN 12 /* includes \0 */ + extern struct lttng_consumer_global_data consumer_data; extern int consumer_poll_timeout; extern volatile int consumer_quit; @@ -78,6 +81,7 @@ static void destroy_channel(struct lttng_consumer_channel *channel) */ if (channel->uchan) { lttng_ustconsumer_del_channel(channel); + lttng_ustconsumer_free_channel(channel); } free(channel); } @@ -122,14 +126,16 @@ static struct lttng_consumer_channel *allocate_channel(uint64_t session_id, uint64_t relayd_id, uint64_t key, enum lttng_event_output output, uint64_t tracefile_size, uint64_t tracefile_count, uint64_t session_id_per_pid, unsigned int monitor, - unsigned int live_timer_interval) + unsigned int live_timer_interval, + const char *root_shm_path, const char *shm_path) { assert(pathname); assert(name); return consumer_allocate_channel(key, session_id, pathname, name, uid, gid, relayd_id, output, tracefile_size, - tracefile_count, session_id_per_pid, monitor, live_timer_interval); + tracefile_count, session_id_per_pid, monitor, + live_timer_interval, root_shm_path, shm_path); } /* @@ -238,6 +244,26 @@ error: return ret; } +static +int get_stream_shm_path(char *stream_shm_path, const char *shm_path, int cpu) +{ + char cpu_nr[INT_MAX_STR_LEN]; /* int max len */ + int ret; + + strncpy(stream_shm_path, shm_path, PATH_MAX); + stream_shm_path[PATH_MAX - 1] = '\0'; + ret = snprintf(cpu_nr, INT_MAX_STR_LEN, "%i", cpu); + if (ret < 0) { + PERROR("snprintf"); + goto end; + } + strncat(stream_shm_path, cpu_nr, + PATH_MAX - strlen(stream_shm_path) - 1); + ret = 0; +end: + return ret; +} + /* * Create streams for the given channel using liblttng-ust-ctl. * @@ -326,8 +352,12 @@ static int create_ust_streams(struct lttng_consumer_channel *channel, /* Keep stream reference when creating metadata. */ if (channel->type == CONSUMER_CHANNEL_TYPE_METADATA) { channel->metadata_stream = stream; - stream->ust_metadata_poll_pipe[0] = ust_metadata_pipe[0]; - stream->ust_metadata_poll_pipe[1] = ust_metadata_pipe[1]; + if (channel->monitor) { + /* Set metadata poll pipe if we created one */ + memcpy(stream->ust_metadata_poll_pipe, + ust_metadata_pipe, + sizeof(ust_metadata_pipe)); + } } } @@ -338,20 +368,87 @@ error_alloc: return ret; } +/* + * create_posix_shm is never called concurrently within a process. + */ +static +int create_posix_shm(void) +{ + char tmp_name[NAME_MAX]; + int shmfd, ret; + + ret = snprintf(tmp_name, NAME_MAX, "/ust-shm-consumer-%d", getpid()); + if (ret < 0) { + PERROR("snprintf"); + return -1; + } + /* + * Allocate shm, and immediately unlink its shm oject, keeping + * only the file descriptor as a reference to the object. + * We specifically do _not_ use the / at the beginning of the + * pathname so that some OS implementations can keep it local to + * the process (POSIX leaves this implementation-defined). + */ + shmfd = shm_open(tmp_name, O_CREAT | O_EXCL | O_RDWR, 0700); + if (shmfd < 0) { + PERROR("shm_open"); + goto error_shm_open; + } + ret = shm_unlink(tmp_name); + if (ret < 0 && errno != ENOENT) { + PERROR("shm_unlink"); + goto error_shm_release; + } + return shmfd; + +error_shm_release: + ret = close(shmfd); + if (ret) { + PERROR("close"); + } +error_shm_open: + return -1; +} + +static int open_ust_stream_fd(struct lttng_consumer_channel *channel, + struct ustctl_consumer_channel_attr *attr, + int cpu) +{ + char shm_path[PATH_MAX]; + int ret; + + if (!channel->shm_path[0]) { + return create_posix_shm(); + } + ret = get_stream_shm_path(shm_path, channel->shm_path, cpu); + if (ret) { + goto error_shm_path; + } + return run_as_open(shm_path, + O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, + channel->uid, channel->gid); + +error_shm_path: + return -1; +} + /* * Create an UST channel with the given attributes and send it to the session * daemon using the ust ctl API. * * Return 0 on success or else a negative value. */ -static int create_ust_channel(struct ustctl_consumer_channel_attr *attr, - struct ustctl_consumer_channel **chanp) +static int create_ust_channel(struct lttng_consumer_channel *channel, + struct ustctl_consumer_channel_attr *attr, + struct ustctl_consumer_channel **ust_chanp) { - int ret; - struct ustctl_consumer_channel *channel; + int ret, nr_stream_fds, i, j; + int *stream_fds; + struct ustctl_consumer_channel *ust_channel; + assert(channel); assert(attr); - assert(chanp); + assert(ust_chanp); DBG3("Creating channel to ustctl with attr: [overwrite: %d, " "subbuf_size: %" PRIu64 ", num_subbuf: %" PRIu64 ", " @@ -360,17 +457,64 @@ static int create_ust_channel(struct ustctl_consumer_channel_attr *attr, attr->num_subbuf, attr->switch_timer_interval, attr->read_timer_interval, attr->output, attr->type); - channel = ustctl_create_channel(attr); - if (!channel) { + if (channel->type == CONSUMER_CHANNEL_TYPE_METADATA) + nr_stream_fds = 1; + else + nr_stream_fds = ustctl_get_nr_stream_per_channel(); + stream_fds = zmalloc(nr_stream_fds * sizeof(*stream_fds)); + if (!stream_fds) { + ret = -1; + goto error_alloc; + } + for (i = 0; i < nr_stream_fds; i++) { + stream_fds[i] = open_ust_stream_fd(channel, attr, i); + if (stream_fds[i] < 0) { + ret = -1; + goto error_open; + } + } + ust_channel = ustctl_create_channel(attr, stream_fds, nr_stream_fds); + if (!ust_channel) { ret = -1; goto error_create; } - - *chanp = channel; + channel->nr_stream_fds = nr_stream_fds; + channel->stream_fds = stream_fds; + *ust_chanp = ust_channel; return 0; error_create: +error_open: + for (j = i - 1; j >= 0; j--) { + int closeret; + + closeret = close(stream_fds[j]); + if (closeret) { + PERROR("close"); + } + if (channel->shm_path[0]) { + char shm_path[PATH_MAX]; + + closeret = get_stream_shm_path(shm_path, + channel->shm_path, j); + if (closeret) { + ERR("Cannot get stream shm path"); + } + closeret = run_as_unlink(shm_path, + channel->uid, channel->gid); + if (closeret) { + PERROR("unlink %s", shm_path); + } + } + } + /* Try to rmdir all directories under shm_path root. */ + if (channel->root_shm_path[0]) { + (void) run_as_recursive_rmdir(channel->root_shm_path, + channel->uid, channel->gid); + } + free(stream_fds); +error_alloc: return ret; } @@ -432,7 +576,7 @@ static int send_sessiond_channel(int sock, if (relayd_error) { *relayd_error = 1; } - ret_code = LTTNG_ERR_RELAYD_CONNECT_FAIL; + ret_code = LTTCOMM_CONSUMERD_RELAYD_FAIL; } if (net_seq_idx == -1ULL) { net_seq_idx = stream->net_seq_idx; @@ -524,7 +668,7 @@ static int ask_channel(struct lttng_consumer_local_data *ctx, int sock, channel->nb_init_stream_left = 0; /* The reply msg status is handled in the following call. */ - ret = create_ust_channel(attr, &channel->uchan); + ret = create_ust_channel(channel, attr, &channel->uchan); if (ret < 0) { goto end; } @@ -623,7 +767,54 @@ static int flush_channel(uint64_t chan_key) health_code_update(); - ustctl_flush_buffer(stream->ustream, 1); + pthread_mutex_lock(&stream->lock); + if (!stream->quiescent) { + ustctl_flush_buffer(stream->ustream, 0); + stream->quiescent = true; + } + pthread_mutex_unlock(&stream->lock); + } +error: + rcu_read_unlock(); + return ret; +} + +/* + * Clear quiescent state from channel's streams using the given key to + * retrieve the channel. + * + * Return 0 on success else an LTTng error code. + */ +static int clear_quiescent_channel(uint64_t chan_key) +{ + int ret = 0; + struct lttng_consumer_channel *channel; + struct lttng_consumer_stream *stream; + struct lttng_ht *ht; + struct lttng_ht_iter iter; + + DBG("UST consumer clear quiescent channel key %" PRIu64, chan_key); + + rcu_read_lock(); + channel = consumer_find_channel(chan_key); + if (!channel) { + ERR("UST consumer clear quiescent channel %" PRIu64 " not found", chan_key); + ret = LTTNG_ERR_UST_CHAN_NOT_FOUND; + goto error; + } + + ht = consumer_data.stream_per_chan_id_ht; + + /* For each stream of the channel id, clear quiescent state. */ + cds_lfht_for_each_entry_duplicate(ht->ht, + ht->hash_fct(&channel->key, lttng_ht_seed), ht->match_fct, + &channel->key, &iter.iter, stream, node_channel_id.node) { + + health_code_update(); + + pthread_mutex_lock(&stream->lock); + stream->quiescent = false; + pthread_mutex_unlock(&stream->lock); } error: rcu_read_unlock(); @@ -858,7 +1049,7 @@ error: * Returns 0 on success, < 0 on error */ static int snapshot_channel(uint64_t key, char *path, uint64_t relayd_id, - uint64_t max_stream_size, struct lttng_consumer_local_data *ctx) + uint64_t nb_packets_per_stream, struct lttng_consumer_local_data *ctx) { int ret; unsigned use_relayd = 0; @@ -885,6 +1076,8 @@ static int snapshot_channel(uint64_t key, char *path, uint64_t relayd_id, DBG("UST consumer snapshot channel %" PRIu64, key); cds_list_for_each_entry(stream, &channel->streams.head, send_node) { + /* Are we at a position _before_ the first available packet ? */ + bool before_first_packet = true; health_code_update(); @@ -918,7 +1111,13 @@ static int snapshot_channel(uint64_t key, char *path, uint64_t relayd_id, } } - ustctl_flush_buffer(stream->ustream, 1); + /* + * If tracing is active, we want to perform a "full" buffer flush. + * Else, if quiescent, it has already been done by the prior stop. + */ + if (!stream->quiescent) { + ustctl_flush_buffer(stream->ustream, 0); + } ret = lttng_ustconsumer_take_snapshot(stream); if (ret < 0) { @@ -940,16 +1139,18 @@ static int snapshot_channel(uint64_t key, char *path, uint64_t relayd_id, /* * The original value is sent back if max stream size is larger than - * the possible size of the snapshot. Also, we asume that the session + * the possible size of the snapshot. Also, we assume that the session * daemon should never send a maximum stream size that is lower than * subbuffer size. */ - consumed_pos = consumer_get_consumed_maxsize(consumed_pos, - produced_pos, max_stream_size); + consumed_pos = consumer_get_consume_start_pos(consumed_pos, + produced_pos, nb_packets_per_stream, + stream->max_sb_size); while (consumed_pos < produced_pos) { ssize_t read_len; unsigned long len, padded_len; + int lost_packet = 0; health_code_update(); @@ -963,6 +1164,15 @@ static int snapshot_channel(uint64_t key, char *path, uint64_t relayd_id, } DBG("UST consumer get subbuf failed. Skipping it."); consumed_pos += stream->max_sb_size; + + /* + * Start accounting lost packets only when we + * already have extracted packets (to match the + * content of the final snapshot). + */ + if (!before_first_packet) { + lost_packet = 1; + } continue; } @@ -998,6 +1208,16 @@ static int snapshot_channel(uint64_t key, char *path, uint64_t relayd_id, goto error_close_stream; } consumed_pos += stream->max_sb_size; + + /* + * Only account lost packets located between + * succesfully extracted packets (do not account before + * and after since they are not visible in the + * resulting snapshot). + */ + stream->chan->lost_packets += lost_packet; + lost_packet = 0; + before_first_packet = false; } /* Simply close the stream so we can use it on the next snapshot. */ @@ -1022,11 +1242,16 @@ error: } /* - * Receive the metadata updates from the sessiond. + * Receive the metadata updates from the sessiond. Supports receiving + * overlapping metadata, but is needs to always belong to a contiguous + * range starting from 0. + * Be careful about the locks held when calling this function: it needs + * the metadata cache flush to concurrently progress in order to + * complete. */ int lttng_ustconsumer_recv_metadata(int sock, uint64_t key, uint64_t offset, - uint64_t len, struct lttng_consumer_channel *channel, - int timer, int wait) + uint64_t len, uint64_t version, + struct lttng_consumer_channel *channel, int timer, int wait) { int ret, ret_code = LTTCOMM_CONSUMERD_SUCCESS; char *metadata_str; @@ -1053,7 +1278,8 @@ int lttng_ustconsumer_recv_metadata(int sock, uint64_t key, uint64_t offset, health_code_update(); pthread_mutex_lock(&channel->metadata_cache->lock); - ret = consumer_metadata_cache_write(channel, offset, len, metadata_str); + ret = consumer_metadata_cache_write(channel, offset, len, version, + metadata_str); if (ret < 0) { /* Unable to handle metadata. Notify session daemon. */ ret_code = LTTCOMM_CONSUMERD_ERROR_METADATA; @@ -1116,17 +1342,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, health_code_update(); - if (msg.cmd_type == LTTNG_CONSUMER_STOP) { - /* - * Notify the session daemon that the command is completed. - * - * On transport layer error, the function call will print an error - * message so handling the returned code is a bit useless since we - * return an error code anyway. - */ - (void) consumer_send_status_msg(sock, ret_code); - return -ENOENT; - } + /* deprecated */ + assert(msg.cmd_type != LTTNG_CONSUMER_STOP); health_code_update(); @@ -1154,7 +1371,7 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, relayd = consumer_find_relayd(index); if (relayd == NULL) { DBG("Unable to find relayd %" PRIu64, index); - ret_code = LTTNG_ERR_NO_CONSUMER; + ret_code = LTTCOMM_CONSUMERD_RELAYD_FAIL; } /* @@ -1216,7 +1433,9 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, msg.u.ask_channel.tracefile_count, msg.u.ask_channel.session_id_per_pid, msg.u.ask_channel.monitor, - msg.u.ask_channel.live_timer_interval); + msg.u.ask_channel.live_timer_interval, + msg.u.ask_channel.root_shm_path, + msg.u.ask_channel.shm_path); if (!channel) { goto end_channel_error; } @@ -1335,7 +1554,7 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, channel = consumer_find_channel(key); if (!channel) { ERR("UST consumer get channel key %" PRIu64 " not found", key); - ret_code = LTTNG_ERR_UST_CHAN_NOT_FOUND; + ret_code = LTTCOMM_CONSUMERD_CHAN_NOT_FOUND; goto end_msg_sessiond; } @@ -1416,12 +1635,25 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, goto end_msg_sessiond; } + case LTTNG_CONSUMER_CLEAR_QUIESCENT_CHANNEL: + { + int ret; + + ret = clear_quiescent_channel( + msg.u.clear_quiescent_channel.key); + if (ret != 0) { + ret_code = ret; + } + + goto end_msg_sessiond; + } case LTTNG_CONSUMER_PUSH_METADATA: { int ret; uint64_t len = msg.u.push_metadata.len; uint64_t key = msg.u.push_metadata.key; uint64_t offset = msg.u.push_metadata.target_offset; + uint64_t version = msg.u.push_metadata.version; struct lttng_consumer_channel *channel; DBG("UST consumer push metadata key %" PRIu64 " of len %" PRIu64, key, @@ -1443,6 +1675,15 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, health_code_update(); + if (!len) { + /* + * There is nothing to receive. We have simply + * checked whether the channel can be found. + */ + ret_code = LTTCOMM_CONSUMERD_SUCCESS; + goto end_msg_sessiond; + } + /* Tell session daemon we are ready to receive the metadata. */ ret = consumer_send_status_msg(sock, LTTCOMM_CONSUMERD_SUCCESS); if (ret < 0) { @@ -1456,14 +1697,14 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, health_poll_entry(); ret = lttng_consumer_poll_socket(consumer_sockpoll); health_poll_exit(); - if (ret < 0) { + if (ret) { goto error_fatal; } health_code_update(); ret = lttng_ustconsumer_recv_metadata(sock, key, offset, - len, channel, 0, 1); + len, version, channel, 0, 1); if (ret < 0) { /* error receiving from sessiond */ goto error_fatal; @@ -1491,17 +1732,17 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, ctx); if (ret < 0) { ERR("Snapshot metadata failed"); - ret_code = LTTNG_ERR_UST_META_FAIL; + ret_code = LTTCOMM_CONSUMERD_ERROR_METADATA; } } else { ret = snapshot_channel(msg.u.snapshot_channel.key, msg.u.snapshot_channel.pathname, msg.u.snapshot_channel.relayd_id, - msg.u.snapshot_channel.max_stream_size, + msg.u.snapshot_channel.nb_packets_per_stream, ctx); if (ret < 0) { ERR("Snapshot channel failed"); - ret_code = LTTNG_ERR_UST_CHAN_FAIL; + ret_code = LTTCOMM_CONSUMERD_CHANNEL_FAIL; } } @@ -1514,6 +1755,108 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, health_code_update(); break; } + case LTTNG_CONSUMER_DISCARDED_EVENTS: + { + int ret = 0; + uint64_t discarded_events; + struct lttng_ht_iter iter; + struct lttng_ht *ht; + struct lttng_consumer_stream *stream; + uint64_t id = msg.u.discarded_events.session_id; + uint64_t key = msg.u.discarded_events.channel_key; + + DBG("UST consumer discarded events command for session id %" + PRIu64, id); + rcu_read_lock(); + pthread_mutex_lock(&consumer_data.lock); + + ht = consumer_data.stream_list_ht; + + /* + * We only need a reference to the channel, but they are not + * directly indexed, so we just use the first matching stream + * to extract the information we need, we default to 0 if not + * found (no events are dropped if the channel is not yet in + * use). + */ + discarded_events = 0; + cds_lfht_for_each_entry_duplicate(ht->ht, + ht->hash_fct(&id, lttng_ht_seed), + ht->match_fct, &id, + &iter.iter, stream, node_session_id.node) { + if (stream->chan->key == key) { + discarded_events = stream->chan->discarded_events; + break; + } + } + pthread_mutex_unlock(&consumer_data.lock); + rcu_read_unlock(); + + DBG("UST consumer discarded events command for session id %" + PRIu64 ", channel key %" PRIu64, id, key); + + health_code_update(); + + /* Send back returned value to session daemon */ + ret = lttcomm_send_unix_sock(sock, &discarded_events, sizeof(discarded_events)); + if (ret < 0) { + PERROR("send discarded events"); + goto error_fatal; + } + + break; + } + case LTTNG_CONSUMER_LOST_PACKETS: + { + int ret; + uint64_t lost_packets; + struct lttng_ht_iter iter; + struct lttng_ht *ht; + struct lttng_consumer_stream *stream; + uint64_t id = msg.u.lost_packets.session_id; + uint64_t key = msg.u.lost_packets.channel_key; + + DBG("UST consumer lost packets command for session id %" + PRIu64, id); + rcu_read_lock(); + pthread_mutex_lock(&consumer_data.lock); + + ht = consumer_data.stream_list_ht; + + /* + * We only need a reference to the channel, but they are not + * directly indexed, so we just use the first matching stream + * to extract the information we need, we default to 0 if not + * found (no packets lost if the channel is not yet in use). + */ + lost_packets = 0; + cds_lfht_for_each_entry_duplicate(ht->ht, + ht->hash_fct(&id, lttng_ht_seed), + ht->match_fct, &id, + &iter.iter, stream, node_session_id.node) { + if (stream->chan->key == key) { + lost_packets = stream->chan->lost_packets; + break; + } + } + pthread_mutex_unlock(&consumer_data.lock); + rcu_read_unlock(); + + DBG("UST consumer lost packets command for session id %" + PRIu64 ", channel key %" PRIu64, id, key); + + health_code_update(); + + /* Send back returned value to session daemon */ + ret = lttcomm_send_unix_sock(sock, &lost_packets, + sizeof(lost_packets)); + if (ret < 0) { + PERROR("send lost packets"); + goto error_fatal; + } + + break; + } default: break; } @@ -1656,28 +1999,78 @@ int lttng_ustconsumer_get_current_timestamp( return ustctl_get_current_timestamp(stream->ustream, ts); } +int lttng_ustconsumer_get_sequence_number( + struct lttng_consumer_stream *stream, uint64_t *seq) +{ + assert(stream); + assert(stream->ustream); + assert(seq); + + return ustctl_get_sequence_number(stream->ustream, seq); +} + /* - * Called when the stream signal the consumer that it has hang up. + * Called when the stream signals the consumer that it has hung up. */ void lttng_ustconsumer_on_stream_hangup(struct lttng_consumer_stream *stream) { assert(stream); assert(stream->ustream); - ustctl_flush_buffer(stream->ustream, 0); + pthread_mutex_lock(&stream->lock); + if (!stream->quiescent) { + ustctl_flush_buffer(stream->ustream, 0); + stream->quiescent = true; + } + pthread_mutex_unlock(&stream->lock); stream->hangup_flush_done = 1; } void lttng_ustconsumer_del_channel(struct lttng_consumer_channel *chan) { + int i; + assert(chan); assert(chan->uchan); if (chan->switch_timer_enabled == 1) { consumer_timer_switch_stop(chan); } + for (i = 0; i < chan->nr_stream_fds; i++) { + int ret; + + ret = close(chan->stream_fds[i]); + if (ret) { + PERROR("close"); + } + if (chan->shm_path[0]) { + char shm_path[PATH_MAX]; + + ret = get_stream_shm_path(shm_path, chan->shm_path, i); + if (ret) { + ERR("Cannot get stream shm path"); + } + ret = run_as_unlink(shm_path, chan->uid, chan->gid); + if (ret) { + PERROR("unlink %s", shm_path); + } + } + } +} + +void lttng_ustconsumer_free_channel(struct lttng_consumer_channel *chan) +{ + assert(chan); + assert(chan->uchan); + consumer_metadata_cache_destroy(chan); ustctl_destroy_channel(chan->uchan); + /* Try to rmdir all directories under shm_path root. */ + if (chan->root_shm_path[0]) { + (void) run_as_recursive_rmdir(chan->root_shm_path, + chan->uid, chan->gid); + } + free(chan->stream_fds); } void lttng_ustconsumer_del_stream(struct lttng_consumer_stream *stream) @@ -1759,10 +2152,56 @@ static int get_index_values(struct ctf_packet_index *index, } index->stream_id = htobe64(index->stream_id); + ret = ustctl_get_instance_id(ustream, &index->stream_instance_id); + if (ret < 0) { + PERROR("ustctl_get_instance_id"); + goto error; + } + index->stream_instance_id = htobe64(index->stream_instance_id); + + ret = ustctl_get_sequence_number(ustream, &index->packet_seq_num); + if (ret < 0) { + PERROR("ustctl_get_sequence_number"); + goto error; + } + index->packet_seq_num = htobe64(index->packet_seq_num); + error: return ret; } +static +void metadata_stream_reset_cache(struct lttng_consumer_stream *stream, + struct consumer_metadata_cache *cache) +{ + DBG("Metadata stream update to version %" PRIu64, + cache->version); + stream->ust_metadata_pushed = 0; + stream->metadata_version = cache->version; + stream->reset_metadata_flag = 1; +} + +/* + * Check if the version of the metadata stream and metadata cache match. + * If the cache got updated, reset the metadata stream. + * The stream lock and metadata cache lock MUST be held. + * Return 0 on success, a negative value on error. + */ +static +int metadata_stream_check_version(struct lttng_consumer_stream *stream) +{ + int ret = 0; + struct consumer_metadata_cache *cache = stream->chan->metadata_cache; + + if (cache->version == stream->metadata_version) { + goto end; + } + metadata_stream_reset_cache(stream, cache); + +end: + return ret; +} + /* * Write up to one packet from the metadata cache to the channel. * @@ -1776,7 +2215,11 @@ int commit_one_metadata_packet(struct lttng_consumer_stream *stream) int ret; pthread_mutex_lock(&stream->chan->metadata_cache->lock); - if (stream->chan->metadata_cache->contiguous + ret = metadata_stream_check_version(stream); + if (ret < 0) { + goto end; + } + if (stream->chan->metadata_cache->max_offset == stream->ust_metadata_pushed) { ret = 0; goto end; @@ -1784,7 +2227,7 @@ int commit_one_metadata_packet(struct lttng_consumer_stream *stream) write_len = ustctl_write_one_packet_to_channel(stream->chan->uchan, &stream->chan->metadata_cache->data[stream->ust_metadata_pushed], - stream->chan->metadata_cache->contiguous + stream->chan->metadata_cache->max_offset - stream->ust_metadata_pushed); assert(write_len != 0); if (write_len < 0) { @@ -1794,7 +2237,7 @@ int commit_one_metadata_packet(struct lttng_consumer_stream *stream) } stream->ust_metadata_pushed += write_len; - assert(stream->chan->metadata_cache->contiguous >= + assert(stream->chan->metadata_cache->max_offset >= stream->ust_metadata_pushed); ret = write_len; @@ -1808,7 +2251,9 @@ end: * Sync metadata meaning request them to the session daemon and snapshot to the * metadata thread can consumer them. * - * Metadata stream lock MUST be acquired. + * Metadata stream lock is held here, but we need to release it when + * interacting with sessiond, else we cause a deadlock with live + * awaiting on metadata to be pushed out. * * Return 0 if new metadatda is available, EAGAIN if the metadata stream * is empty or a negative value on error. @@ -1822,6 +2267,7 @@ int lttng_ustconsumer_sync_metadata(struct lttng_consumer_local_data *ctx, assert(ctx); assert(metadata); + pthread_mutex_unlock(&metadata->lock); /* * Request metadata from the sessiond, but don't wait for the flush * because we locked the metadata thread. @@ -1830,6 +2276,7 @@ int lttng_ustconsumer_sync_metadata(struct lttng_consumer_local_data *ctx, if (ret < 0) { goto end; } + pthread_mutex_lock(&metadata->lock); ret = commit_one_metadata_packet(metadata); if (ret <= 0) { @@ -1862,6 +2309,112 @@ end: return ret; } +/* + * Return 0 on success else a negative value. + */ +static int notify_if_more_data(struct lttng_consumer_stream *stream, + struct lttng_consumer_local_data *ctx) +{ + int ret; + struct ustctl_consumer_stream *ustream; + + assert(stream); + assert(ctx); + + ustream = stream->ustream; + + /* + * First, we are going to check if there is a new subbuffer available + * before reading the stream wait_fd. + */ + /* Get the next subbuffer */ + ret = ustctl_get_next_subbuf(ustream); + if (ret) { + /* No more data found, flag the stream. */ + stream->has_data = 0; + ret = 0; + goto end; + } + + ret = ustctl_put_subbuf(ustream); + assert(!ret); + + /* This stream still has data. Flag it and wake up the data thread. */ + stream->has_data = 1; + + if (stream->monitor && !stream->hangup_flush_done && !ctx->has_wakeup) { + ssize_t writelen; + + writelen = lttng_pipe_write(ctx->consumer_wakeup_pipe, "!", 1); + if (writelen < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + ret = writelen; + goto end; + } + + /* The wake up pipe has been notified. */ + ctx->has_wakeup = 1; + } + ret = 0; + +end: + return ret; +} + +static +int update_stream_stats(struct lttng_consumer_stream *stream) +{ + int ret; + uint64_t seq, discarded; + + ret = ustctl_get_sequence_number(stream->ustream, &seq); + if (ret < 0) { + PERROR("ustctl_get_sequence_number"); + goto end; + } + /* + * Start the sequence when we extract the first packet in case we don't + * start at 0 (for example if a consumer is not connected to the + * session immediately after the beginning). + */ + if (stream->last_sequence_number == -1ULL) { + stream->last_sequence_number = seq; + } else if (seq > stream->last_sequence_number) { + stream->chan->lost_packets += seq - + stream->last_sequence_number - 1; + } else { + /* seq <= last_sequence_number */ + ERR("Sequence number inconsistent : prev = %" PRIu64 + ", current = %" PRIu64, + stream->last_sequence_number, seq); + ret = -1; + goto end; + } + stream->last_sequence_number = seq; + + ret = ustctl_get_events_discarded(stream->ustream, &discarded); + if (ret < 0) { + PERROR("kernctl_get_events_discarded"); + goto end; + } + if (discarded < stream->last_discarded_events) { + /* + * Overflow has occured. We assume only one wrap-around + * has occured. + */ + stream->chan->discarded_events += + (1ULL << (CAA_BITS_PER_LONG - 1)) - + stream->last_discarded_events + discarded; + } else { + stream->chan->discarded_events += discarded - + stream->last_discarded_events; + } + stream->last_discarded_events = discarded; + ret = 0; + +end: + return ret; +} + /* * Read subbuffer from the given stream. * @@ -1875,7 +2428,6 @@ int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream, unsigned long len, subbuf_size, padding; int err, write_index = 1; long ret = 0; - char dummy; struct ustctl_consumer_stream *ustream; struct ctf_packet_index index; @@ -1890,11 +2442,17 @@ int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream, ustream = stream->ustream; /* - * We can consume the 1 byte written into the wait_fd by UST. - * Don't trigger error if we cannot read this one byte (read - * returns 0), or if the error is EAGAIN or EWOULDBLOCK. + * We can consume the 1 byte written into the wait_fd by UST. Don't trigger + * error if we cannot read this one byte (read returns 0), or if the error + * is EAGAIN or EWOULDBLOCK. + * + * This is only done when the stream is monitored by a thread, before the + * flush is done after a hangup and if the stream is not flagged with data + * since there might be nothing to consume in the wait fd but still have + * data available flagged by the consumer wake up pipe. */ - if (stream->monitor && !stream->hangup_flush_done) { + if (stream->monitor && !stream->hangup_flush_done && !stream->has_data) { + char dummy; ssize_t readlen; readlen = lttng_read(stream->wait_fd, &dummy, 1); @@ -1940,6 +2498,13 @@ retry: if (ret < 0) { goto end; } + + /* Update the stream's sequence and discarded events count. */ + ret = update_stream_stats(stream); + if (ret < 0) { + PERROR("kernctl_get_events_discarded"); + goto end; + } } else { write_index = 0; } @@ -1980,6 +2545,17 @@ retry: err = ustctl_put_next_subbuf(ustream); assert(err == 0); + /* + * This will consumer the byte on the wait_fd if and only if there is not + * next subbuffer to be acquired. + */ + if (!stream->metadata_flag) { + ret = notify_if_more_data(stream, ctx); + if (ret < 0) { + goto end; + } + } + /* Write index if needed. */ if (!write_index) { goto end; @@ -1989,7 +2565,23 @@ retry: /* * In live, block until all the metadata is sent. */ + pthread_mutex_lock(&stream->metadata_timer_lock); + assert(!stream->missed_metadata_flush); + stream->waiting_on_metadata = true; + pthread_mutex_unlock(&stream->metadata_timer_lock); + err = consumer_stream_sync_metadata(ctx, stream->session_id); + + pthread_mutex_lock(&stream->metadata_timer_lock); + stream->waiting_on_metadata = false; + if (stream->missed_metadata_flush) { + stream->missed_metadata_flush = false; + pthread_mutex_unlock(&stream->metadata_timer_lock); + (void) consumer_flush_ust_index(stream); + } else { + pthread_mutex_unlock(&stream->metadata_timer_lock); + } + if (err < 0) { goto end; } @@ -2070,7 +2662,7 @@ int lttng_ustconsumer_data_pending(struct lttng_consumer_stream *stream) uint64_t contiguous, pushed; /* Ease our life a bit. */ - contiguous = stream->chan->metadata_cache->contiguous; + contiguous = stream->chan->metadata_cache->max_offset; pushed = stream->ust_metadata_pushed; /* @@ -2085,7 +2677,7 @@ int lttng_ustconsumer_data_pending(struct lttng_consumer_stream *stream) */ DBG("UST consumer metadata pending check: contiguous %" PRIu64 " vs pushed %" PRIu64, contiguous, pushed); - assert(((int64_t) contiguous - pushed) >= 0); + assert(((int64_t) (contiguous - pushed)) >= 0); if ((contiguous != pushed) || (((int64_t) contiguous - pushed) > 0 || contiguous == 0)) { ret = 1; /* Data is pending */ @@ -2199,6 +2791,10 @@ void lttng_ustconsumer_close_stream_wakeup(struct lttng_consumer_stream *stream) * function or any of its callees. Timers have a very strict locking * semantic with respect to teardown. Failure to respect this semantic * introduces deadlocks. + * + * DON'T hold the metadata lock when calling this function, else this + * can cause deadlock involving consumer awaiting for metadata to be + * pushed out due to concurrent interaction with the session daemon. */ int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx, struct lttng_consumer_channel *channel, int timer, int wait) @@ -2206,12 +2802,14 @@ int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx, struct lttcomm_metadata_request_msg request; struct lttcomm_consumer_msg msg; enum lttcomm_return_code ret_code = LTTCOMM_CONSUMERD_SUCCESS; - uint64_t len, key, offset; + uint64_t len, key, offset, version; int ret; assert(channel); assert(channel->metadata_cache); + memset(&request, 0, sizeof(request)); + /* send the metadata request to sessiond */ switch (consumer_data.type) { case LTTNG_CONSUMER64_UST: @@ -2284,6 +2882,7 @@ int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx, len = msg.u.push_metadata.len; key = msg.u.push_metadata.key; offset = msg.u.push_metadata.target_offset; + version = msg.u.push_metadata.version; assert(key == channel->key); if (len == 0) { @@ -2306,7 +2905,7 @@ int lttng_ustconsumer_request_metadata(struct lttng_consumer_local_data *ctx, health_code_update(); ret = lttng_ustconsumer_recv_metadata(ctx->consumer_metadata_socket, - key, offset, len, channel, timer, wait); + key, offset, len, version, channel, timer, wait); if (ret >= 0) { /* * Only send the status msg if the sessiond is alive meaning a positive @@ -2322,3 +2921,15 @@ end: pthread_mutex_unlock(&ctx->metadata_socket_lock); return ret; } + +/* + * Return the ustctl call for the get stream id. + */ +int lttng_ustconsumer_get_stream_id(struct lttng_consumer_stream *stream, + uint64_t *stream_id) +{ + assert(stream); + assert(stream_id); + + return ustctl_get_stream_id(stream->ustream, stream_id); +}