/*
* Push metadata to consumer socket.
*
- * The socket lock MUST be acquired.
- * The ust app session lock MUST be acquired.
+ * RCU read-side lock must be held to guarantee existance of socket.
+ * Must be called with the ust app session lock held.
+ * Must be called with the registry lock held.
*
* On success, return the len of metadata pushed or else a negative value.
+ * Returning a -EPIPE return value means we could not send the metadata,
+ * but it can be caused by recoverable errors (e.g. the application has
+ * terminated concurrently).
*/
ssize_t ust_app_push_metadata(struct ust_registry_session *registry,
struct consumer_socket *socket, int send_zero_data)
assert(registry);
assert(socket);
- pthread_mutex_lock(®istry->lock);
-
/*
- * Means that no metadata was assigned to the session. This can happens if
- * no start has been done previously.
+ * Means that no metadata was assigned to the session. This can
+ * happens if no start has been done previously.
*/
if (!registry->metadata_key) {
- pthread_mutex_unlock(®istry->lock);
return 0;
}
/*
- * On a push metadata error either the consumer is dead or the metadata
- * channel has been destroyed because its endpoint might have died (e.g:
- * relayd). If so, the metadata closed flag is set to 1 so we deny pushing
- * metadata again which is not valid anymore on the consumer side.
+ * On a push metadata error either the consumer is dead or the
+ * metadata channel has been destroyed because its endpoint
+ * might have died (e.g: relayd), or because the application has
+ * exited. If so, the metadata closed flag is set to 1 so we
+ * deny pushing metadata again which is not valid anymore on the
+ * consumer side.
*/
if (registry->metadata_closed) {
- pthread_mutex_unlock(®istry->lock);
return -EPIPE;
}
registry->metadata_len_sent += len;
push_data:
- pthread_mutex_unlock(®istry->lock);
ret = consumer_push_metadata(socket, registry->metadata_key,
metadata_str, len, offset);
if (ret < 0) {
/*
- * There is an acceptable race here between the registry metadata key
- * assignment and the creation on the consumer. The session daemon can
- * concurrently push metadata for this registry while being created on
- * the consumer since the metadata key of the registry is assigned
- * *before* it is setup to avoid the consumer to ask for metadata that
- * could possibly be not found in the session daemon.
+ * There is an acceptable race here between the registry
+ * metadata key assignment and the creation on the
+ * consumer. The session daemon can concurrently push
+ * metadata for this registry while being created on the
+ * consumer since the metadata key of the registry is
+ * assigned *before* it is setup to avoid the consumer
+ * to ask for metadata that could possibly be not found
+ * in the session daemon.
*
- * The metadata will get pushed either by the session being stopped or
- * the consumer requesting metadata if that race is triggered.
+ * The metadata will get pushed either by the session
+ * being stopped or the consumer requesting metadata if
+ * that race is triggered.
*/
if (ret == -LTTCOMM_CONSUMERD_CHANNEL_FAIL) {
ret = 0;
}
- /* Update back the actual metadata len sent since it failed here. */
- pthread_mutex_lock(®istry->lock);
+ /*
+ * Update back the actual metadata len sent since it
+ * failed here.
+ */
registry->metadata_len_sent -= len;
- pthread_mutex_unlock(®istry->lock);
ret_val = ret;
goto error_push;
}
error:
if (ret_val) {
/*
- * On error, flag the registry that the metadata is closed. We were unable
- * to push anything and this means that either the consumer is not
- * responding or the metadata cache has been destroyed on the consumer.
+ * On error, flag the registry that the metadata is
+ * closed. We were unable to push anything and this
+ * means that either the consumer is not responding or
+ * the metadata cache has been destroyed on the
+ * consumer.
*/
registry->metadata_closed = 1;
}
- pthread_mutex_unlock(®istry->lock);
error_push:
free(metadata_str);
return ret_val;
* socket to send the metadata is retrieved from consumer, if sock
* is not NULL we use it to send the metadata.
* RCU read-side lock must be held while calling this function,
- * therefore ensuring existance of registry.
+ * therefore ensuring existance of registry. It also ensures existance
+ * of socket throughout this function.
*
* Return 0 on success else a negative error.
+ * Returning a -EPIPE return value means we could not send the metadata,
+ * but it can be caused by recoverable errors (e.g. the application has
+ * terminated concurrently).
*/
static int push_metadata(struct ust_registry_session *registry,
struct consumer_output *consumer)
assert(consumer);
pthread_mutex_lock(®istry->lock);
-
if (registry->metadata_closed) {
- pthread_mutex_unlock(®istry->lock);
- return -EPIPE;
+ ret_val = -EPIPE;
+ goto error;
}
/* Get consumer socket to use to push the metadata.*/
socket = consumer_find_socket_by_bitness(registry->bits_per_long,
consumer);
- pthread_mutex_unlock(®istry->lock);
if (!socket) {
ret_val = -1;
goto error;
}
- /*
- * TODO: Currently, we hold the socket lock around sampling of the next
- * metadata segment to ensure we send metadata over the consumer socket in
- * the correct order. This makes the registry lock nest inside the socket
- * lock.
- *
- * Please note that this is a temporary measure: we should move this lock
- * back into ust_consumer_push_metadata() when the consumer gets the
- * ability to reorder the metadata it receives.
- */
- pthread_mutex_lock(socket->lock);
ret = ust_app_push_metadata(registry, socket, 0);
- pthread_mutex_unlock(socket->lock);
if (ret < 0) {
ret_val = ret;
goto error;
}
-
+ pthread_mutex_unlock(®istry->lock);
return 0;
error:
-end:
+ pthread_mutex_unlock(®istry->lock);
return ret_val;
}
/*
* Send to the consumer a close metadata command for the given session. Once
* done, the metadata channel is deleted and the session metadata pointer is
- * nullified. The session lock MUST be acquired here unless the application is
+ * nullified. The session lock MUST be held unless the application is
* in the destroy path.
*
* Return 0 on success else a negative value.
pthread_mutex_lock(&ua_sess->lock);
+ assert(!ua_sess->deleted);
+ ua_sess->deleted = true;
+
registry = get_session_registry(ua_sess);
if (registry) {
/* Push metadata for application before freeing the application. */
}
pthread_mutex_unlock(&ua_sess->lock);
+ consumer_output_put(ua_sess->consumer);
+
call_rcu(&ua_sess->rcu_head, delete_ust_app_session_rcu);
}
/* Send channel to the application. */
ret = ust_consumer_send_channel_to_ust(app, ua_sess, ua_chan);
- if (ret < 0) {
+ if (ret == -EPIPE || ret == -LTTNG_UST_ERR_EXITING) {
+ ret = -ENOTCONN; /* Caused by app exiting. */
+ goto error;
+ } else if (ret < 0) {
goto error;
}
/* Send all streams to application. */
cds_list_for_each_entry_safe(stream, stmp, &ua_chan->streams.head, list) {
ret = ust_consumer_send_stream_to_ust(app, ua_chan, stream);
- if (ret < 0) {
+ if (ret == -EPIPE || ret == -LTTNG_UST_ERR_EXITING) {
+ ret = -ENOTCONN; /* Caused by app exiting. */
+ goto error;
+ } else if (ret < 0) {
goto error;
}
/* We don't need the stream anymore once sent to the tracer. */
ua_sess->egid = usess->gid;
ua_sess->buffer_type = usess->buffer_type;
ua_sess->bits_per_long = app->bits_per_long;
+
/* There is only one consumer object per session possible. */
+ consumer_output_get(usess->consumer);
ua_sess->consumer = usess->consumer;
+
ua_sess->output_traces = usess->output_traces;
ua_sess->live_timer_interval = usess->live_timer_interval;
copy_channel_attr_to_ustctl(&ua_sess->metadata_attr,
lttng_ht_add_unique_str(ua_sess->channels, &ua_chan->node);
}
+ return;
error:
- return;
+ consumer_output_put(ua_sess->consumer);
}
/*
assert(reg_chan);
reg_chan->consumer_key = ua_chan->key;
reg_chan->subbuf_size = ua_chan->attr.subbuf_size;
+ reg_chan->num_subbuf = ua_chan->attr.num_subbuf;
/* Create and add a channel registry to session. */
ret = ust_registry_channel_add(reg_sess->reg.ust,
/* Send channel to the application. */
ret = ust_consumer_send_channel_to_ust(app, ua_sess, ua_chan);
- if (ret < 0) {
+ if (ret == -EPIPE || ret == -LTTNG_UST_ERR_EXITING) {
+ ret = -ENOTCONN; /* Caused by app exiting. */
+ goto error;
+ } else if (ret < 0) {
goto error;
}
ret = ust_consumer_send_stream_to_ust(app, ua_chan, &stream);
if (ret < 0) {
(void) release_ust_app_stream(-1, &stream);
+ if (ret == -EPIPE || ret == -LTTNG_UST_ERR_EXITING) {
+ ret = -ENOTCONN; /* Caused by app exiting. */
+ goto error_stream_unlock;
+ } else if (ret < 0) {
+ goto error_stream_unlock;
+ }
goto error_stream_unlock;
}
/* Send buffers to the application. */
ret = send_channel_uid_to_ust(reg_chan, app, ua_sess, ua_chan);
if (ret < 0) {
- /*
- * Don't report error to the console, since it may be
- * caused by application concurrently exiting.
- */
+ if (ret != -ENOTCONN) {
+ ERR("Error sending channel to application");
+ }
goto error;
}
ret = send_channel_pid_to_ust(app, ua_sess, ua_chan);
if (ret < 0) {
- /*
- * Don't report error to the console, since it may be
- * caused by application concurrently exiting.
- */
+ if (ret != -ENOTCONN) {
+ ERR("Error sending channel to application");
+ }
goto error;
}
* need and send it to the application. This MUST be called with a RCU read
* side lock acquired.
*
- * Return 0 on success or else a negative value.
+ * Return 0 on success or else a negative value. Returns -ENOTCONN if
+ * the application exited concurrently.
*/
static int do_create_channel(struct ust_app *app,
struct ltt_ust_session *usess, struct ust_app_session *ua_sess,
*
* Called with UST app session lock and RCU read-side lock held.
*
- * Return 0 on success or else a negative value.
+ * Return 0 on success or else a negative value. Returns -ENOTCONN if
+ * the application exited concurrently.
*/
static int create_ust_app_channel(struct ust_app_session *ua_sess,
struct ltt_ust_channel *uchan, struct ust_app *app,
*/
pthread_mutex_lock(&ua_sess->lock);
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ continue;
+ }
+
/*
* Normally, this is done in the delete session process which is
* executed in the call rcu below. However, upon registration we can't
* or a timeout on it. We can't inform the caller that for a
* specific app, the session failed so lets continue here.
*/
+ ret = 0; /* Not an error. */
continue;
case -ENOMEM:
default:
assert(ua_sess);
pthread_mutex_lock(&ua_sess->lock);
+
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ continue;
+ }
+
if (!strncmp(uchan->name, DEFAULT_METADATA_NAME,
sizeof(uchan->name))) {
copy_channel_attr_to_ustctl(&ua_sess->metadata_attr, &uchan->attr);
}
pthread_mutex_unlock(&ua_sess->lock);
if (ret < 0) {
- if (ret == -ENOMEM) {
- /* No more memory is a fatal error. Stop right now. */
- goto error_rcu_unlock;
- }
/* Cleanup the created session if it's the case. */
if (created) {
destroy_app_session(app, ua_sess);
}
+ switch (ret) {
+ case -ENOTCONN:
+ /*
+ * The application's socket is not valid. Either a bad socket
+ * or a timeout on it. We can't inform the caller that for a
+ * specific app, the session failed so lets continue here.
+ */
+ ret = 0; /* Not an error. */
+ continue;
+ case -ENOMEM:
+ default:
+ goto error_rcu_unlock;
+ }
}
}
pthread_mutex_lock(&ua_sess->lock);
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ continue;
+ }
+
/* Lookup channel in the ust app session */
lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
- /* If the channel is not found, there is a code flow error */
- assert(ua_chan_node);
+ /*
+ * It is possible that the channel cannot be found is
+ * the channel/event creation occurs concurrently with
+ * an application exit.
+ */
+ if (!ua_chan_node) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ continue;
+ }
ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node);
}
pthread_mutex_lock(&ua_sess->lock);
+
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ continue;
+ }
+
/* Lookup channel in the ust app session */
lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
pthread_mutex_lock(&ua_sess->lock);
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ goto end;
+ }
+
/* Upon restart, we skip the setup, already done */
if (ua_sess->started) {
goto skip_setup;
pthread_mutex_lock(&ua_sess->lock);
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ goto end_no_session;
+ }
+
/*
* If started = 0, it means that stop trace has been called for a session
* that was never started. It's possible since we can have a fail start
pthread_mutex_lock(&ua_sess->lock);
+ if (ua_sess->deleted) {
+ goto end_deleted;
+ }
+
health_code_update();
/* Flushing buffers */
health_code_update();
+end_deleted:
pthread_mutex_unlock(&ua_sess->lock);
end_not_compatible:
int ust_app_flush_session(struct ltt_ust_session *usess)
{
- int ret;
+ int ret = 0;
DBG("Flushing session buffers for all ust apps");
/* Push metadata. */
(void) push_metadata(ust_session_reg, usess->consumer);
}
- ret = 0;
break;
}
case LTTNG_BUFFER_PER_PID:
break;
}
default:
+ ret = -1;
assert(0);
break;
}
-end_no_session:
rcu_read_unlock();
health_code_update();
return ret;
pthread_mutex_lock(&ua_sess->lock);
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ goto error;
+ }
+
/*
* We can iterate safely here over all UST app session since the create ust
* app session above made a shadow copy of the UST global domain from the
cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan,
node.node) {
ret = do_create_channel(app, usess, ua_sess, ua_chan);
- if (ret < 0) {
+ if (ret < 0 && ret != -ENOTCONN) {
/*
- * Stop everything. On error, the application failed, no more
- * file descriptor are available or ENOMEM so stopping here is
- * the only thing we can do for now.
+ * Stop everything. On error, the application
+ * failed, no more file descriptor are available
+ * or ENOMEM so stopping here is the only thing
+ * we can do for now. The only exception is
+ * -ENOTCONN, which indicates that the application
+ * has exit.
*/
goto error_unlock;
}
}
pthread_mutex_lock(&ua_sess->lock);
+
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ continue;
+ }
+
/* Lookup channel in the ust app session */
lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
}
pthread_mutex_lock(&ua_sess->lock);
+
+ if (ua_sess->deleted) {
+ ret = 0;
+ goto end_unlock;
+ }
+
/* Lookup channel in the ust app session */
lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter);
ua_chan_node = lttng_ht_iter_get_node_str(&iter);
* Return 0 on success or else a negative value.
*/
int ust_app_snapshot_record(struct ltt_ust_session *usess,
- struct snapshot_output *output, int wait, uint64_t max_stream_size)
+ struct snapshot_output *output, int wait,
+ uint64_t nb_packets_per_stream)
{
int ret = 0;
unsigned int snapshot_done = 0;
reg_chan, node.node) {
ret = consumer_snapshot_channel(socket, reg_chan->consumer_key,
output, 0, usess->uid, usess->gid, pathname, wait,
- max_stream_size);
+ nb_packets_per_stream);
if (ret < 0) {
goto error;
}
}
ret = consumer_snapshot_channel(socket,
reg->registry->reg.ust->metadata_key, output, 1,
- usess->uid, usess->gid, pathname, wait, max_stream_size);
+ usess->uid, usess->gid, pathname, wait, 0);
if (ret < 0) {
goto error;
}
ua_chan, node.node) {
ret = consumer_snapshot_channel(socket, ua_chan->key, output,
0, ua_sess->euid, ua_sess->egid, pathname, wait,
- max_stream_size);
+ nb_packets_per_stream);
if (ret < 0) {
goto error;
}
registry = get_session_registry(ua_sess);
assert(registry);
ret = consumer_snapshot_channel(socket, registry->metadata_key, output,
- 1, ua_sess->euid, ua_sess->egid, pathname, wait,
- max_stream_size);
+ 1, ua_sess->euid, ua_sess->egid, pathname, wait, 0);
if (ret < 0) {
goto error;
}
}
/*
- * Return the number of streams for a UST session.
+ * Return the size taken by one more packet per stream.
*/
-unsigned int ust_app_get_nb_stream(struct ltt_ust_session *usess)
+uint64_t ust_app_get_size_one_more_packet_per_stream(struct ltt_ust_session *usess,
+ uint64_t cur_nr_packets)
{
- unsigned int ret = 0;
+ uint64_t tot_size = 0;
struct ust_app *app;
struct lttng_ht_iter iter;
rcu_read_lock();
cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter,
reg_chan, node.node) {
- ret += reg_chan->stream_count;
+ if (cur_nr_packets >= reg_chan->num_subbuf) {
+ /*
+ * Don't take channel into account if we
+ * already grab all its packets.
+ */
+ continue;
+ }
+ tot_size += reg_chan->subbuf_size * reg_chan->stream_count;
}
rcu_read_unlock();
}
cds_lfht_for_each_entry(ua_sess->channels->ht, &chan_iter.iter,
ua_chan, node.node) {
- ret += ua_chan->streams.count;
+ if (cur_nr_packets >= ua_chan->attr.num_subbuf) {
+ /*
+ * Don't take channel into account if we
+ * already grab all its packets.
+ */
+ continue;
+ }
+ tot_size += ua_chan->attr.subbuf_size * ua_chan->streams.count;
}
}
rcu_read_unlock();
break;
}
- return ret;
+ return tot_size;
}