#include "ust-app.h"
#include "ust-consumer.h"
#include "ust-ctl.h"
+#include "utils.h"
-/* Next available channel key. */
-static unsigned long next_channel_key;
-static unsigned long next_session_id;
+/* Next available channel key. Access under next_channel_key_lock. */
+static uint64_t _next_channel_key;
+static pthread_mutex_t next_channel_key_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Next available session ID. Access under next_session_id_lock. */
+static uint64_t _next_session_id;
+static pthread_mutex_t next_session_id_lock = PTHREAD_MUTEX_INITIALIZER;
/*
- * Return the atomically incremented value of next_channel_key.
+ * Return the incremented value of next_channel_key.
*/
-static inline unsigned long get_next_channel_key(void)
+static uint64_t get_next_channel_key(void)
{
- return uatomic_add_return(&next_channel_key, 1);
+ uint64_t ret;
+
+ pthread_mutex_lock(&next_channel_key_lock);
+ ret = ++_next_channel_key;
+ pthread_mutex_unlock(&next_channel_key_lock);
+ return ret;
}
/*
* Return the atomically incremented value of next_session_id.
*/
-static inline unsigned long get_next_session_id(void)
+static uint64_t get_next_session_id(void)
{
- return uatomic_add_return(&next_session_id, 1);
+ uint64_t ret;
+
+ pthread_mutex_lock(&next_session_id_lock);
+ ret = ++_next_session_id;
+ pthread_mutex_unlock(&next_session_id_lock);
+ return ret;
}
static void copy_channel_attr_to_ustctl(
free(stream);
}
+/*
+ * We need to execute ht_destroy outside of RCU read-side critical
+ * section and outside of call_rcu thread, so we postpone its execution
+ * using ht_cleanup_push. It is simpler than to change the semantic of
+ * the many callers of delete_ust_app_session().
+ */
+static
+void delete_ust_app_channel_rcu(struct rcu_head *head)
+{
+ struct ust_app_channel *ua_chan =
+ caa_container_of(head, struct ust_app_channel, rcu_head);
+
+ ht_cleanup_push(ua_chan->ctx);
+ ht_cleanup_push(ua_chan->events);
+ free(ua_chan);
+}
+
/*
* Delete ust app channel safely. RCU read lock must be held before calling
* this function.
assert(!ret);
delete_ust_app_ctx(sock, ua_ctx);
}
- lttng_ht_destroy(ua_chan->ctx);
/* Wipe events */
cds_lfht_for_each_entry(ua_chan->events->ht, &iter.iter, ua_event,
assert(!ret);
delete_ust_app_event(sock, ua_event);
}
- lttng_ht_destroy(ua_chan->events);
- /* Wipe and free registry from session registry. */
- registry = get_session_registry(ua_chan->session);
- if (registry) {
- ust_registry_channel_del_free(registry, ua_chan->key);
+ if (ua_chan->session->buffer_type == LTTNG_BUFFER_PER_PID) {
+ /* Wipe and free registry from session registry. */
+ registry = get_session_registry(ua_chan->session);
+ if (registry) {
+ ust_registry_channel_del_free(registry, ua_chan->key);
+ }
}
if (ua_chan->obj != NULL) {
lttng_fd_put(LTTNG_FD_APPS, 1);
free(ua_chan->obj);
}
- free(ua_chan);
+ call_rcu(&ua_chan->rcu_head, delete_ust_app_channel_rcu);
}
/*
- * Push metadata to consumer socket. The socket lock MUST be acquired.
+ * Push metadata to consumer socket.
+ *
+ * The socket lock MUST be acquired.
+ * The ust app session lock MUST be acquired.
*
* On success, return the len of metadata pushed or else a negative value.
*/
assert(registry);
assert(socket);
- /* Should never be 0 which is the initial state. */
- assert(registry->metadata_key);
+
+ /*
+ * 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.
+ *
+ * The ust app session mutex locked allows us to make this check without
+ * the registry lock.
+ */
+ if (registry->metadata_closed) {
+ return -EPIPE;
+ }
pthread_mutex_lock(®istry->lock);
*/
if (!registry->metadata_key) {
ret_val = 0;
- goto error_rcu_unlock;
+ goto end_rcu_unlock;
}
/* Get consumer socket to use to push the metadata.*/
return 0;
error_rcu_unlock:
+ /*
+ * 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;
+end_rcu_unlock:
rcu_read_unlock();
return ret_val;
}
if (!registry->metadata_key || registry->metadata_closed) {
ret = 0;
- goto error;
+ goto end;
}
/* Get consumer socket to use to push the metadata.*/
goto error;
}
- /* Metadata successfully closed. Flag the registry. */
- registry->metadata_closed = 1;
-
error:
+ /*
+ * Metadata closed. Even on error this means that the consumer is not
+ * responding or not found so either way a second close should NOT be emit
+ * for this registry.
+ */
+ registry->metadata_closed = 1;
+end:
rcu_read_unlock();
return ret;
}
+/*
+ * We need to execute ht_destroy outside of RCU read-side critical
+ * section and outside of call_rcu thread, so we postpone its execution
+ * using ht_cleanup_push. It is simpler than to change the semantic of
+ * the many callers of delete_ust_app_session().
+ */
+static
+void delete_ust_app_session_rcu(struct rcu_head *head)
+{
+ struct ust_app_session *ua_sess =
+ caa_container_of(head, struct ust_app_session, rcu_head);
+
+ ht_cleanup_push(ua_sess->channels);
+ free(ua_sess);
+}
+
/*
* Delete ust app session safely. RCU read lock must be held before calling
* this function.
assert(ua_sess);
+ pthread_mutex_lock(&ua_sess->lock);
+
registry = get_session_registry(ua_sess);
- if (registry) {
+ if (registry && !registry->metadata_closed) {
/* Push metadata for application before freeing the application. */
(void) push_metadata(registry, ua_sess->consumer);
/*
* Don't ask to close metadata for global per UID buffers. Close
- * metadata only on destroy trace session in this case.
+ * metadata only on destroy trace session in this case. Also, the
+ * previous push metadata could have flag the metadata registry to
+ * close so don't send a close command if closed.
*/
- if (ua_sess->buffer_type != LTTNG_BUFFER_PER_UID) {
+ if (ua_sess->buffer_type != LTTNG_BUFFER_PER_UID &&
+ !registry->metadata_closed) {
/* And ask to close it for this session registry. */
(void) close_metadata(registry, ua_sess->consumer);
}
assert(!ret);
delete_ust_app_channel(sock, ua_chan, app);
}
- lttng_ht_destroy(ua_sess->channels);
/* In case of per PID, the registry is kept in the session. */
if (ua_sess->buffer_type == LTTNG_BUFFER_PER_PID) {
sock, ret);
}
}
- free(ua_sess);
+ pthread_mutex_unlock(&ua_sess->lock);
+
+ call_rcu(&ua_sess->rcu_head, delete_ust_app_session_rcu);
}
/*
* Delete a traceable application structure from the global list. Never call
* this function outside of a call_rcu call.
+ *
+ * RCU read side lock should _NOT_ be held when calling this function.
*/
static
void delete_ust_app(struct ust_app *app)
int ret, sock;
struct ust_app_session *ua_sess, *tmp_ua_sess;
- rcu_read_lock();
-
/* Delete ust app sessions info */
sock = app->sock;
app->sock = -1;
- lttng_ht_destroy(app->sessions);
-
/* Wipe sessions */
cds_list_for_each_entry_safe(ua_sess, tmp_ua_sess, &app->teardown_head,
teardown_node) {
/* Free every object in the session and the session. */
+ rcu_read_lock();
delete_ust_app_session(sock, ua_sess, app);
+ rcu_read_unlock();
}
- lttng_ht_destroy(app->ust_objd);
+
+ ht_cleanup_push(app->sessions);
+ ht_cleanup_push(app->ust_objd);
/*
* Wait until we have deleted the application from the sock hash table
DBG2("UST app pid %d deleted", app->pid);
free(app);
-
- rcu_read_unlock();
}
/*
ua_sess->bits_per_long = app->bits_per_long;
/* There is only one consumer object per session possible. */
ua_sess->consumer = usess->consumer;
+ ua_sess->output_traces = usess->output_traces;
switch (ua_sess->buffer_type) {
case LTTNG_BUFFER_PER_PID:
ret = snprintf(ua_sess->path, sizeof(ua_sess->path),
- DEFAULT_UST_TRACE_PID_PATH "/%s-%d-%s/", app->name, app->pid,
+ DEFAULT_UST_TRACE_PID_PATH "/%s-%d-%s", app->name, app->pid,
datetime);
break;
case LTTNG_BUFFER_PER_UID:
struct ust_app *app, struct lttng_ht_iter *iter)
{
/* Get right UST app session from app */
- lttng_ht_lookup(app->sessions, (void *)((unsigned long) usess->id), iter);
+ lttng_ht_lookup(app->sessions, &usess->id, iter);
}
/*
struct ltt_ust_session *usess, struct ust_app *app)
{
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
__lookup_session_by_app(usess, app, &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node == NULL) {
goto error;
}
ret = ust_registry_session_init(®_pid->registry->reg.ust, app,
app->bits_per_long, app->uint8_t_alignment,
app->uint16_t_alignment, app->uint32_t_alignment,
- app->uint64_t_alignment, app->long_alignment, app->byte_order);
+ app->uint64_t_alignment, app->long_alignment,
+ app->byte_order, app->version.major,
+ app->version.minor);
if (ret < 0) {
goto error;
}
}
/* Initialize registry. */
- ret = ust_registry_session_init(®_uid->registry->reg.ust, app,
+ ret = ust_registry_session_init(®_uid->registry->reg.ust, NULL,
app->bits_per_long, app->uint8_t_alignment,
app->uint16_t_alignment, app->uint32_t_alignment,
- app->uint64_t_alignment, app->long_alignment, app->byte_order);
+ app->uint64_t_alignment, app->long_alignment,
+ app->byte_order, app->version.major,
+ app->version.minor);
if (ret < 0) {
goto error;
}
ua_sess = lookup_session_by_app(usess, app);
if (ua_sess == NULL) {
- DBG2("UST app pid: %d session id %d not found, creating it",
+ DBG2("UST app pid: %d session id %" PRIu64 " not found, creating it",
app->pid, usess->id);
ua_sess = alloc_ust_app_session(app);
if (ua_sess == NULL) {
ua_sess->handle = ret;
/* Add ust app session to app's HT */
- lttng_ht_node_init_ulong(&ua_sess->node,
- (unsigned long) ua_sess->tracing_id);
- lttng_ht_add_unique_ulong(app->sessions, &ua_sess->node);
+ lttng_ht_node_init_u64(&ua_sess->node,
+ ua_sess->tracing_id);
+ lttng_ht_add_unique_u64(app->sessions, &ua_sess->node);
DBG2("UST app session created successfully with handle %d", ret);
}
lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter);
ua_chan_node = lttng_ht_iter_get_node_str(&iter);
if (ua_chan_node == NULL) {
- DBG2("Unable to find channel %s in ust session id %u",
+ DBG2("Unable to find channel %s in ust session id %" PRIu64,
uchan->name, ua_sess->tracing_id);
goto error;
}
* Now get the channel from the consumer. This call wil populate the stream
* list of that channel and set the ust objects.
*/
- ret = ust_consumer_get_channel(socket, ua_chan);
- if (ret < 0) {
- goto error_destroy;
+ if (usess->consumer->enabled) {
+ ret = ust_consumer_get_channel(socket, ua_chan);
+ if (ret < 0) {
+ goto error_destroy;
+ }
}
rcu_read_unlock();
}
assert(reg_chan);
reg_chan->consumer_key = ua_chan->key;
+ reg_chan->subbuf_size = ua_chan->attr.subbuf_size;
/* Create and add a channel registry to session. */
ret = ust_registry_channel_add(reg_sess->reg.ust,
ret = ust_consumer_send_stream_to_ust(app, ua_chan, &stream);
if (ret < 0) {
+ (void) release_ust_app_stream(-1, &stream);
goto error_stream_unlock;
}
ret = do_consumer_create_channel(usess, ua_sess, ua_chan,
app->bits_per_long, reg_uid->registry->reg.ust);
if (ret < 0) {
+ /*
+ * Let's remove the previously created buffer registry channel so
+ * it's not visible anymore in the session registry.
+ */
+ ust_registry_channel_del_free(reg_uid->registry->reg.ust,
+ ua_chan->tracing_channel_id);
+ buffer_reg_channel_remove(reg_uid->registry, reg_chan);
+ buffer_reg_channel_destroy(reg_chan, LTTNG_DOMAIN_UST);
goto error;
}
* Create UST app channel and create it on the tracer. Set ua_chanp of the
* newly created channel if not NULL.
*
- * Called with UST app session lock held.
+ * Called with UST app session lock and RCU read-side lock held.
*
* Return 0 on success or else a negative value.
*/
if (ua_chan == NULL) {
/* Only malloc can fail here */
ret = -ENOMEM;
- goto error;
+ goto error_alloc;
}
shadow_copy_channel(ua_chan, uchan);
error:
delete_ust_app_channel(ua_chan->is_sent ? app->sock : -1, ua_chan, app);
+error_alloc:
return ret;
}
registry = get_session_registry(ua_sess);
assert(registry);
- /* Metadata already exists for this registry. */
- if (registry->metadata_key) {
+ /* Metadata already exists for this registry or it was closed previously */
+ if (registry->metadata_key || registry->metadata_closed) {
ret = 0;
goto error;
}
metadata->attr.overwrite = DEFAULT_CHANNEL_OVERWRITE;
metadata->attr.subbuf_size = default_get_metadata_subbuf_size();
metadata->attr.num_subbuf = DEFAULT_METADATA_SUBBUF_NUM;
- metadata->attr.switch_timer_interval = DEFAULT_UST_CHANNEL_SWITCH_TIMER;
- metadata->attr.read_timer_interval = DEFAULT_UST_CHANNEL_READ_TIMER;
+ metadata->attr.switch_timer_interval = DEFAULT_METADATA_SWITCH_TIMER;
+ metadata->attr.read_timer_interval = DEFAULT_METADATA_READ_TIMER;
metadata->attr.output = LTTNG_UST_MMAP;
metadata->attr.type = LTTNG_UST_CHAN_METADATA;
} else {
metadata->attr.type = LTTNG_UST_CHAN_METADATA;
}
- /* Get the right consumer socket for the application. */
- socket = consumer_find_socket_by_bitness(app->bits_per_long, consumer);
- if (!socket) {
- ret = -EINVAL;
- goto error_consumer;
- }
-
/* Need one fd for the channel. */
ret = lttng_fd_get(LTTNG_FD_APPS, 1);
if (ret < 0) {
goto error;
}
+ /* Get the right consumer socket for the application. */
+ socket = consumer_find_socket_by_bitness(app->bits_per_long, consumer);
+ if (!socket) {
+ ret = -EINVAL;
+ goto error_consumer;
+ }
+
/*
* Keep metadata key so we can identify it on the consumer side. Assign it
* to the registry *before* we ask the consumer so we avoid the race of the
ret = ust_consumer_ask_channel(ua_sess, metadata, consumer, socket,
registry);
if (ret < 0) {
- /*
- * Safe because the metadata obj pointer is not set so the delete below
- * will not put a FD back again.
- */
- lttng_fd_put(LTTNG_FD_APPS, 1);
+ /* Nullify the metadata key so we don't try to close it later on. */
+ registry->metadata_key = 0;
goto error_consumer;
}
*/
ret = consumer_setup_metadata(socket, metadata->key);
if (ret < 0) {
- /*
- * Safe because the metadata obj pointer is not set so the delete below
- * will not put a FD back again.
- */
- lttng_fd_put(LTTNG_FD_APPS, 1);
+ /* Nullify the metadata key so we don't try to close it later on. */
+ registry->metadata_key = 0;
goto error_consumer;
}
metadata->key, app->pid);
error_consumer:
+ lttng_fd_put(LTTNG_FD_APPS, 1);
delete_ust_app_channel(-1, metadata, app);
error:
return ret;
lta->v_major = msg->major;
lta->v_minor = msg->minor;
- lta->sessions = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ lta->sessions = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
lta->ust_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
lta->notify_sock = -1;
* session so the delete session will NOT push/close a second time.
*/
registry = get_session_registry(ua_sess);
- if (registry) {
+ if (registry && !registry->metadata_closed) {
/* Push metadata for application before freeing the application. */
(void) push_metadata(registry, ua_sess->consumer);
/*
* Don't ask to close metadata for global per UID buffers. Close
- * metadata only on destroy trace session in this case.
+ * metadata only on destroy trace session in this case. Also, the
+ * previous push metadata could have flag the metadata registry to
+ * close so don't send a close command if closed.
*/
- if (ua_sess->buffer_type != LTTNG_BUFFER_PER_UID) {
+ if (ua_sess->buffer_type != LTTNG_BUFFER_PER_UID &&
+ !registry->metadata_closed) {
/* And ask to close it for this session registry. */
(void) close_metadata(registry, ua_sess->consumer);
}
/*
* Free and clean all traceable apps of the global list.
+ *
+ * Should _NOT_ be called with RCU read-side lock held.
*/
void ust_app_clean_list(void)
{
ret = lttng_ht_del(ust_app_ht_by_notify_sock, &iter);
assert(!ret);
}
+ rcu_read_unlock();
/* Destroy is done only when the ht is empty */
- lttng_ht_destroy(ust_app_ht);
- lttng_ht_destroy(ust_app_ht_by_sock);
- lttng_ht_destroy(ust_app_ht_by_notify_sock);
-
- rcu_read_unlock();
+ ht_cleanup_push(ust_app_ht);
+ ht_cleanup_push(ust_app_ht_by_sock);
+ ht_cleanup_push(ust_app_ht_by_notify_sock);
}
/*
goto error;
}
- DBG2("UST app disabling channel %s from global domain for session id %d",
+ DBG2("UST app disabling channel %s from global domain for session id %" PRIu64,
uchan->name, usess->id);
rcu_read_lock();
goto error;
}
- DBG2("UST app enabling channel %s to global domain for session id %d",
+ DBG2("UST app enabling channel %s to global domain for session id %" PRIu64,
uchan->name, usess->id);
rcu_read_lock();
struct ust_app_event *ua_event;
DBG("UST app disabling event %s for all apps in channel "
- "%s for session id %d", uevent->attr.name, uchan->name, usess->id);
+ "%s for session id %" PRIu64,
+ uevent->attr.name, uchan->name, usess->id);
rcu_read_lock();
lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
if (ua_chan_node == NULL) {
- DBG2("Channel %s not found in session id %d for app pid %d."
+ DBG2("Channel %s not found in session id %" PRIu64 " for app pid %d."
"Skipping", uchan->name, usess->id, app->pid);
continue;
}
struct ust_app_event *ua_event;
DBG("UST app disabling all event for all apps in channel "
- "%s for session id %d", uchan->name, usess->id);
+ "%s for session id %" PRIu64, uchan->name, usess->id);
rcu_read_lock();
assert(usess);
assert(uchan);
- DBG2("UST app adding channel %s to UST domain for session id %d",
+ DBG2("UST app adding channel %s to UST domain for session id %" PRIu64,
uchan->name, usess->id);
rcu_read_lock();
struct ust_app_channel *ua_chan;
struct ust_app_event *ua_event;
- DBG("UST app enabling event %s for all apps for session id %d",
+ DBG("UST app enabling event %s for all apps for session id %" PRIu64,
uevent->attr.name, usess->id);
/*
struct ust_app_session *ua_sess;
struct ust_app_channel *ua_chan;
- DBG("UST app creating event %s for all apps for session id %d",
+ DBG("UST app creating event %s for all apps for session id %" PRIu64,
uevent->attr.name, usess->id);
rcu_read_lock();
/*
* Start tracing for a specific UST session and app.
*/
+static
int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app)
{
int ret = 0;
/*
* Stop tracing for a specific UST session and app.
*/
+static
int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app)
{
int ret = 0;
- struct lttng_ht_iter iter;
struct ust_app_session *ua_sess;
- struct ust_app_channel *ua_chan;
struct ust_registry_session *registry;
DBG("Stopping tracing for ust app pid %d", app->pid);
health_code_update();
+ registry = get_session_registry(ua_sess);
+ assert(registry);
+
+ if (!registry->metadata_closed) {
+ /* Push metadata for application before freeing the application. */
+ (void) push_metadata(registry, ua_sess->consumer);
+ }
+
+ pthread_mutex_unlock(&ua_sess->lock);
+end_no_session:
+ rcu_read_unlock();
+ health_code_update();
+ return 0;
+
+error_rcu_unlock:
+ pthread_mutex_unlock(&ua_sess->lock);
+ rcu_read_unlock();
+ health_code_update();
+ return -1;
+}
+
+/*
+ * Flush buffers for a specific UST session and app.
+ */
+static
+int ust_app_flush_trace(struct ltt_ust_session *usess, struct ust_app *app)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct ust_app_session *ua_sess;
+ struct ust_app_channel *ua_chan;
+
+ DBG("Flushing buffers for ust app pid %d", app->pid);
+
+ rcu_read_lock();
+
+ if (!app->compatible) {
+ goto end_no_session;
+ }
+
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ goto end_no_session;
+ }
+
+ pthread_mutex_lock(&ua_sess->lock);
+
+ health_code_update();
+
/* Flushing buffers */
cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan,
node.node) {
health_code_update();
- registry = get_session_registry(ua_sess);
- assert(registry);
- /* Push metadata for application before freeing the application. */
- (void) push_metadata(registry, ua_sess->consumer);
-
pthread_mutex_unlock(&ua_sess->lock);
end_no_session:
rcu_read_unlock();
health_code_update();
return 0;
-
-error_rcu_unlock:
- pthread_mutex_unlock(&ua_sess->lock);
- rcu_read_unlock();
- health_code_update();
- return -1;
}
/*
int ret;
struct ust_app_session *ua_sess;
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
DBG("Destroy tracing for ust app pid %d", app->pid);
}
__lookup_session_by_app(usess, app, &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node == NULL) {
/* Session is being or is deleted. */
goto end;
rcu_read_lock();
- /* Flush all per UID buffers associated to that session. */
- if (usess->buffer_type == LTTNG_BUFFER_PER_UID) {
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ ret = ust_app_stop_trace(usess, app);
+ if (ret < 0) {
+ /* Continue to next apps even on error */
+ continue;
+ }
+ }
+
+ /* Flush buffers and push metadata (for UID buffers). */
+ switch (usess->buffer_type) {
+ case LTTNG_BUFFER_PER_UID:
+ {
struct buffer_reg_uid *reg;
+
+ /* Flush all per UID buffers associated to that session. */
cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) {
+ struct ust_registry_session *ust_session_reg;
struct buffer_reg_channel *reg_chan;
struct consumer_socket *socket;
*/
(void) consumer_flush_channel(socket, reg_chan->consumer_key);
}
+
+ ust_session_reg = reg->registry->reg.ust;
+ if (!ust_session_reg->metadata_closed) {
+ /* Push metadata. */
+ (void) push_metadata(ust_session_reg, usess->consumer);
+ }
}
- }
- cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
- ret = ust_app_stop_trace(usess, app);
- if (ret < 0) {
- /* Continue to next apps even on error */
- continue;
+ break;
+ }
+ case LTTNG_BUFFER_PER_PID:
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ ret = ust_app_flush_trace(usess, app);
+ if (ret < 0) {
+ /* Continue to next apps even on error */
+ continue;
+ }
}
+ break;
+ default:
+ assert(0);
+ break;
}
rcu_read_unlock();
assert(usess);
assert(sock >= 0);
- DBG2("UST app global update for app sock %d for session id %d", sock,
+ DBG2("UST app global update for app sock %d for session id %" PRIu64, sock,
usess->id);
rcu_read_lock();
DBG("Application socket %d is being teardown. Abort event notify",
sock);
ret = 0;
+ free(fields);
goto error_rcu_unlock;
}
- /* Lookup channel by UST object descriptor. Should always be found. */
+ /* Lookup channel by UST object descriptor. */
ua_chan = find_channel_by_objd(app, cobjd);
- assert(ua_chan);
+ if (!ua_chan) {
+ DBG("Application channel is being teardown. Abort event notify");
+ ret = 0;
+ free(fields);
+ goto error_rcu_unlock;
+ }
+
assert(ua_chan->session);
ua_sess = ua_chan->session;
} else {
/* Get current already assigned values. */
type = chan_reg->header_type;
+ free(fields);
+ /* Set to NULL so the error path does not do a double free. */
+ fields = NULL;
}
/* Channel id is set during the object creation. */
chan_id = chan_reg->chan_id;
pthread_mutex_unlock(®istry->lock);
error_rcu_unlock:
rcu_read_unlock();
+ if (ret) {
+ free(fields);
+ }
return ret;
}
DBG("Application socket %d is being teardown. Abort event notify",
sock);
ret = 0;
+ free(sig);
+ free(fields);
+ free(model_emf_uri);
goto error_rcu_unlock;
}
- /* Lookup channel by UST object descriptor. Should always be found. */
+ /* Lookup channel by UST object descriptor. */
ua_chan = find_channel_by_objd(app, cobjd);
- assert(ua_chan);
+ if (!ua_chan) {
+ DBG("Application channel is being teardown. Abort event notify");
+ ret = 0;
+ free(sig);
+ free(fields);
+ free(model_emf_uri);
+ goto error_rcu_unlock;
+ }
+
assert(ua_chan->session);
ua_sess = ua_chan->session;
pthread_mutex_lock(®istry->lock);
+ /*
+ * From this point on, this call acquires the ownership of the sig, fields
+ * and model_emf_uri meaning any free are done inside it if needed. These
+ * three variables MUST NOT be read/write after this.
+ */
ret_code = ust_registry_create_event(registry, chan_reg_key,
sobjd, cobjd, name, sig, nr_fields, fields, loglevel,
model_emf_uri, ua_sess->buffer_type, &event_id);
goto error;
}
- /* Add event to the UST registry coming from the notify socket. */
+ /*
+ * Add event to the UST registry coming from the notify socket. This
+ * call will free if needed the sig, fields and model_emf_uri. This
+ * code path loses the ownsership of these variables and transfer them
+ * to the this function.
+ */
ret = add_event_ust_registry(sock, sobjd, cobjd, name, sig, nr_fields,
fields, loglevel, model_emf_uri);
if (ret < 0) {
goto error;
}
+ /*
+ * The fields ownership are transfered to this function call meaning
+ * that if needed it will be freed. After this, it's invalid to access
+ * fields or clean it up.
+ */
ret = reply_ust_register_channel(sock, sobjd, cobjd, nr_fields,
fields);
if (ret < 0) {
call_rcu(&obj->head, close_notify_sock_rcu);
}
}
+
+/*
+ * Destroy a ust app data structure and free its memory.
+ */
+void ust_app_destroy(struct ust_app *app)
+{
+ if (!app) {
+ return;
+ }
+
+ call_rcu(&app->pid_n.head, delete_ust_app_rcu);
+}
+
+/*
+ * Take a snapshot for a given UST session. The snapshot is sent to the given
+ * output.
+ *
+ * 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, unsigned int nb_streams)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+ char pathname[PATH_MAX];
+ uint64_t max_stream_size = 0;
+
+ assert(usess);
+ assert(output);
+
+ rcu_read_lock();
+
+ /*
+ * Compute the maximum size of a single stream if a max size is asked by
+ * the caller.
+ */
+ if (output->max_size > 0 && nb_streams > 0) {
+ max_stream_size = output->max_size / nb_streams;
+ }
+
+ switch (usess->buffer_type) {
+ case LTTNG_BUFFER_PER_UID:
+ {
+ struct buffer_reg_uid *reg;
+
+ cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) {
+ struct buffer_reg_channel *reg_chan;
+ struct consumer_socket *socket;
+
+ /* Get consumer socket to use to push the metadata.*/
+ socket = consumer_find_socket_by_bitness(reg->bits_per_long,
+ usess->consumer);
+ if (!socket) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ memset(pathname, 0, sizeof(pathname));
+ ret = snprintf(pathname, sizeof(pathname),
+ DEFAULT_UST_TRACE_DIR "/" DEFAULT_UST_TRACE_UID_PATH,
+ reg->uid, reg->bits_per_long);
+ if (ret < 0) {
+ PERROR("snprintf snapshot path");
+ goto error;
+ }
+
+ /* Add the UST default trace dir to path. */
+ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter,
+ reg_chan, node.node) {
+
+ /*
+ * Make sure the maximum stream size is not lower than the
+ * subbuffer size or else it's an error since we won't be able to
+ * snapshot anything.
+ */
+ if (max_stream_size &&
+ reg_chan->subbuf_size > max_stream_size) {
+ ret = -EINVAL;
+ DBG3("UST app snapshot record maximum stream size %" PRIu64
+ " is smaller than subbuffer size of %" PRIu64,
+ max_stream_size, reg_chan->subbuf_size);
+ goto error;
+ }
+ ret = consumer_snapshot_channel(socket, reg_chan->consumer_key, output, 0,
+ usess->uid, usess->gid, pathname, wait,
+ max_stream_size);
+ 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);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+ break;
+ }
+ case LTTNG_BUFFER_PER_PID:
+ {
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ struct consumer_socket *socket;
+ struct lttng_ht_iter chan_iter;
+ struct ust_app_channel *ua_chan;
+ struct ust_app_session *ua_sess;
+ struct ust_registry_session *registry;
+
+ ua_sess = lookup_session_by_app(usess, app);
+ if (!ua_sess) {
+ /* Session not associated with this app. */
+ continue;
+ }
+
+ /* Get the right consumer socket for the application. */
+ socket = consumer_find_socket_by_bitness(app->bits_per_long,
+ output->consumer);
+ if (!socket) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Add the UST default trace dir to path. */
+ memset(pathname, 0, sizeof(pathname));
+ ret = snprintf(pathname, sizeof(pathname), DEFAULT_UST_TRACE_DIR "/%s",
+ ua_sess->path);
+ if (ret < 0) {
+ PERROR("snprintf snapshot path");
+ goto error;
+ }
+
+ cds_lfht_for_each_entry(ua_sess->channels->ht, &chan_iter.iter,
+ ua_chan, node.node) {
+ /*
+ * Make sure the maximum stream size is not lower than the
+ * subbuffer size or else it's an error since we won't be able to
+ * snapshot anything.
+ */
+ if (max_stream_size &&
+ ua_chan->attr.subbuf_size > max_stream_size) {
+ ret = -EINVAL;
+ DBG3("UST app snapshot record maximum stream size %" PRIu64
+ " is smaller than subbuffer size of %" PRIu64,
+ max_stream_size, ua_chan->attr.subbuf_size);
+ goto error;
+ }
+
+ ret = consumer_snapshot_channel(socket, ua_chan->key, output, 0,
+ ua_sess->euid, ua_sess->egid, pathname, wait,
+ max_stream_size);
+ 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);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
+
+error:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Return the number of streams for a UST session.
+ */
+unsigned int ust_app_get_nb_stream(struct ltt_ust_session *usess)
+{
+ unsigned int ret = 0;
+ struct ust_app *app;
+ struct lttng_ht_iter iter;
+
+ assert(usess);
+
+ switch (usess->buffer_type) {
+ case LTTNG_BUFFER_PER_UID:
+ {
+ struct buffer_reg_uid *reg;
+
+ cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) {
+ struct buffer_reg_channel *reg_chan;
+
+ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter,
+ reg_chan, node.node) {
+ ret += reg_chan->stream_count;
+ }
+ }
+ break;
+ }
+ case LTTNG_BUFFER_PER_PID:
+ {
+ rcu_read_lock();
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ struct ust_app_channel *ua_chan;
+ struct ust_app_session *ua_sess;
+ struct lttng_ht_iter chan_iter;
+
+ ua_sess = lookup_session_by_app(usess, app);
+ if (!ua_sess) {
+ /* Session not associated with this app. */
+ continue;
+ }
+
+ cds_lfht_for_each_entry(ua_sess->channels->ht, &chan_iter.iter,
+ ua_chan, node.node) {
+ ret += ua_chan->streams.count;
+ }
+ }
+ rcu_read_unlock();
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
+
+ return ret;
+}