+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ pthread_mutex_lock(&app->sock_lock);
+ handle = ustctl_tracepoint_field_list(app->sock);
+ if (handle < 0) {
+ if (handle != -EPIPE && handle != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app list field getting handle failed for app pid %d",
+ app->pid);
+ }
+ pthread_mutex_unlock(&app->sock_lock);
+ continue;
+ }
+
+ while ((ret = ustctl_tracepoint_field_list_get(app->sock, handle,
+ &uiter)) != -LTTNG_UST_ERR_NOENT) {
+ /* Handle ustctl error. */
+ if (ret < 0) {
+ int release_ret;
+
+ if (ret != -LTTNG_UST_ERR_EXITING && ret != -EPIPE) {
+ ERR("UST app tp list field failed for app %d with ret %d",
+ app->sock, ret);
+ } else {
+ DBG3("UST app tp list field failed. Application is dead");
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally. Reset list and count for next app.
+ */
+ break;
+ }
+ free(tmp_event);
+ release_ret = ustctl_release_handle(app->sock, handle);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (release_ret < 0 &&
+ release_ret != -LTTNG_UST_ERR_EXITING &&
+ release_ret != -EPIPE) {
+ ERR("Error releasing app handle for app %d with ret %d", app->sock, release_ret);
+ }
+ goto rcu_error;
+ }
+
+ health_code_update();
+ if (count >= nbmem) {
+ /* In case the realloc fails, we free the memory */
+ struct lttng_event_field *new_tmp_event;
+ size_t new_nbmem;
+
+ new_nbmem = nbmem << 1;
+ DBG2("Reallocating event field list from %zu to %zu entries",
+ nbmem, new_nbmem);
+ new_tmp_event = realloc(tmp_event,
+ new_nbmem * sizeof(struct lttng_event_field));
+ if (new_tmp_event == NULL) {
+ int release_ret;
+
+ PERROR("realloc ust app event fields");
+ free(tmp_event);
+ ret = -ENOMEM;
+ release_ret = ustctl_release_handle(app->sock, handle);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (release_ret &&
+ release_ret != -LTTNG_UST_ERR_EXITING &&
+ release_ret != -EPIPE) {
+ ERR("Error releasing app handle for app %d with ret %d", app->sock, release_ret);
+ }
+ goto rcu_error;
+ }
+ /* Zero the new memory */
+ memset(new_tmp_event + nbmem, 0,
+ (new_nbmem - nbmem) * sizeof(struct lttng_event_field));
+ nbmem = new_nbmem;
+ tmp_event = new_tmp_event;
+ }
+
+ memcpy(tmp_event[count].field_name, uiter.field_name, LTTNG_UST_SYM_NAME_LEN);
+ /* Mapping between these enums matches 1 to 1. */
+ tmp_event[count].type = (enum lttng_event_field_type) uiter.type;
+ tmp_event[count].nowrite = uiter.nowrite;
+
+ memcpy(tmp_event[count].event.name, uiter.event_name, LTTNG_UST_SYM_NAME_LEN);
+ tmp_event[count].event.loglevel = uiter.loglevel;
+ tmp_event[count].event.type = LTTNG_EVENT_TRACEPOINT;
+ tmp_event[count].event.pid = app->pid;
+ tmp_event[count].event.enabled = -1;
+ count++;
+ }
+ ret = ustctl_release_handle(app->sock, handle);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (ret < 0 &&
+ ret != -LTTNG_UST_ERR_EXITING &&
+ ret != -EPIPE) {
+ ERR("Error releasing app handle for app %d with ret %d", app->sock, ret);
+ }
+ }
+
+ ret = count;
+ *fields = tmp_event;
+
+ DBG2("UST app list event fields done (%zu events)", count);
+
+rcu_error:
+ rcu_read_unlock();
+error:
+ health_code_update();
+ return ret;
+}
+
+/*
+ * 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)
+{
+ int ret;
+ struct ust_app *app;
+ struct lttng_ht_iter iter;
+
+ DBG2("UST app cleaning registered apps hash table");
+
+ rcu_read_lock();
+
+ /* Cleanup notify socket hash table */
+ if (ust_app_ht_by_notify_sock) {
+ cds_lfht_for_each_entry(ust_app_ht_by_notify_sock->ht, &iter.iter, app,
+ notify_sock_n.node) {
+ /*
+ * Assert that all notifiers are gone as all triggers
+ * are unregistered prior to this clean-up.
+ */
+ assert(lttng_ht_get_count(app->token_to_event_notifier_rule_ht) == 0);
+
+ ust_app_notify_sock_unregister(app->notify_sock);
+ }
+ }
+
+ if (ust_app_ht) {
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ ret = lttng_ht_del(ust_app_ht, &iter);
+ assert(!ret);
+ call_rcu(&app->pid_n.head, delete_ust_app_rcu);
+ }
+ }
+
+ /* Cleanup socket hash table */
+ if (ust_app_ht_by_sock) {
+ cds_lfht_for_each_entry(ust_app_ht_by_sock->ht, &iter.iter, app,
+ sock_n.node) {
+ ret = lttng_ht_del(ust_app_ht_by_sock, &iter);
+ assert(!ret);
+ }
+ }
+
+ rcu_read_unlock();
+
+ /* Destroy is done only when the ht is empty */
+ if (ust_app_ht) {
+ ht_cleanup_push(ust_app_ht);
+ }
+ if (ust_app_ht_by_sock) {
+ ht_cleanup_push(ust_app_ht_by_sock);
+ }
+ if (ust_app_ht_by_notify_sock) {
+ ht_cleanup_push(ust_app_ht_by_notify_sock);
+ }
+}
+
+/*
+ * Init UST app hash table.
+ */
+int ust_app_ht_alloc(void)
+{
+ ust_app_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ if (!ust_app_ht) {
+ return -1;
+ }
+ ust_app_ht_by_sock = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ if (!ust_app_ht_by_sock) {
+ return -1;
+ }
+ ust_app_ht_by_notify_sock = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ if (!ust_app_ht_by_notify_sock) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * For a specific UST session, disable the channel for all registered apps.
+ */
+int ust_app_disable_channel_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_channel *uchan)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct lttng_ht_node_str *ua_chan_node;
+ struct ust_app *app;
+ struct ust_app_session *ua_sess;
+ struct ust_app_channel *ua_chan;
+
+ assert(usess->active);
+ DBG2("UST app disabling channel %s from global domain for session id %" PRIu64,
+ uchan->name, usess->id);
+
+ rcu_read_lock();
+
+ /* For every registered applications */
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ struct lttng_ht_iter uiter;
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ continue;
+ }
+
+ /* Get channel */
+ lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter);
+ ua_chan_node = lttng_ht_iter_get_node_str(&uiter);
+ /* If the session if found for the app, the channel must be there */
+ assert(ua_chan_node);
+
+ ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node);
+ /* The channel must not be already disabled */
+ assert(ua_chan->enabled == 1);
+
+ /* Disable channel onto application */
+ ret = disable_ust_app_channel(ua_sess, ua_chan, app);
+ if (ret < 0) {
+ /* XXX: We might want to report this error at some point... */
+ continue;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * For a specific UST session, disable the channel for all registered apps.
+ */
+int ust_app_disable_map_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_map *umap)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct lttng_ht_node_str *ua_map_node;
+ struct ust_app *app;
+ struct ust_app_session *ua_sess;
+ struct ust_app_map *ua_map;
+
+ assert(usess->active);
+ DBG2("UST app disabling map %s from global domain for session id %" PRIu64,
+ umap->name, usess->id);
+
+ rcu_read_lock();
+
+ /* For every registered applications */
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ struct lttng_ht_iter uiter;
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ continue;
+ }
+
+ /* Get map */
+ lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter);
+ ua_map_node = lttng_ht_iter_get_node_str(&uiter);
+ /* If the session if found for the app, the map must be there */
+ assert(ua_map_node);
+
+ ua_map = caa_container_of(ua_map_node, struct ust_app_map, node);
+ /* The map must not be already disabled */
+ assert(ua_map->enabled == 1);
+
+ /* Disable map onto application */
+ ret = disable_ust_app_map(ua_sess, ua_map, app);
+ if (ret < 0) {
+ /* XXX: We might want to report this error at some point... */
+ continue;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * For a specific UST session, enable the channel for all registered apps.
+ */
+int ust_app_enable_channel_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_channel *uchan)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+ struct ust_app_session *ua_sess;
+
+ assert(usess->active);
+ DBG2("UST app enabling channel %s to global domain for session id %" PRIu64,
+ uchan->name, usess->id);
+
+ rcu_read_lock();
+
+ /* For every registered applications */
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ continue;
+ }
+
+ /* Enable channel onto application */
+ ret = enable_ust_app_channel(ua_sess, uchan, app);
+ if (ret < 0) {
+ /* XXX: We might want to report this error at some point... */
+ continue;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * For a specific UST session, enable the map for all registered apps.
+ */
+int ust_app_enable_map_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_map *umap)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+ struct ust_app_session *ua_sess;
+
+ assert(usess->active);
+ DBG2("UST app enabling map %s to global domain for session id %" PRIu64,
+ umap->name, usess->id);
+
+ rcu_read_lock();
+
+ /* For every registered applications */
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ continue;
+ }
+
+ /* Enable map onto application */
+ ret = enable_ust_app_map(ua_sess, umap, app);
+ if (ret < 0) {
+ /* XXX: We might want to report this error at some point... */
+ continue;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Disable an event in a channel and for a specific session.
+ */
+int ust_app_disable_channel_event_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter, uiter;
+ struct lttng_ht_node_str *ua_chan_node;
+ struct ust_app *app;
+ struct ust_app_session *ua_sess;
+ struct ust_app_channel *ua_chan;
+ struct ust_app_event *ua_event;
+
+ assert(usess->active);
+ DBG("UST app disabling event %s for all apps in channel "
+ "%s for session id %" PRIu64,
+ uevent->attr.name, uchan->name, usess->id);
+
+ rcu_read_lock();
+
+ /* For all registered applications */
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ /* Next app */
+ 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 (ua_chan_node == NULL) {
+ DBG2("Channel %s not found in session id %" PRIu64 " for app pid %d."
+ "Skipping", uchan->name, usess->id, app->pid);
+ continue;
+ }
+ ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node);
+
+ ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name,
+ uevent->filter, uevent->attr.loglevel,
+ uevent->exclusion, uevent->attr.token);
+ if (ua_event == NULL) {
+ DBG2("Event %s not found in channel %s for app pid %d."
+ "Skipping", uevent->attr.name, uchan->name, app->pid);
+ continue;
+ }
+
+ ret = disable_ust_app_event(ua_sess, ua_event, app);
+ if (ret < 0) {
+ /* XXX: Report error someday... */
+ continue;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Disable an event in a map and for a specific session.
+ */
+int ust_app_disable_map_event_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_map *umap, struct ltt_ust_event *uevent)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter, uiter;
+ struct lttng_ht_node_str *ua_map_node;
+ struct ust_app *app;
+ struct ust_app_session *ua_sess;
+ struct ust_app_map *ua_map;
+ struct ust_app_event *ua_event;
+
+ assert(usess->active);
+ DBG("UST app disabling event %s for all apps in map "
+ "%s for session id %" PRIu64,
+ uevent->attr.name, umap->name, usess->id);
+
+ rcu_read_lock();
+
+ /* For all registered applications */
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ /* Next app */
+ continue;
+ }
+
+ /* Lookup map in the ust app session */
+ lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter);
+ ua_map_node = lttng_ht_iter_get_node_str(&uiter);
+ if (ua_map_node == NULL) {
+ DBG2("map %s not found in session id %" PRIu64 " for app pid %d."
+ "Skipping", umap->name, usess->id, app->pid);
+ continue;
+ }
+ ua_map = caa_container_of(ua_map_node, struct ust_app_map, node);
+
+ ua_event = find_ust_app_event(ua_map->events, uevent->attr.name,
+ uevent->filter, uevent->attr.loglevel,
+ uevent->exclusion, uevent->attr.token);
+ if (ua_event == NULL) {
+ DBG2("Event %s not found in map %s for app pid %d."
+ "Skipping", uevent->attr.name, umap->name, app->pid);
+ continue;
+ }
+
+ ret = disable_ust_app_event(ua_sess, ua_event, app);
+ if (ret < 0) {
+ /* XXX: Report error someday... */
+ continue;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+/* The ua_sess lock must be held by the caller. */
+static
+int ust_app_channel_create(struct ltt_ust_session *usess,
+ struct ust_app_session *ua_sess,
+ struct ltt_ust_channel *uchan, struct ust_app *app,
+ struct ust_app_channel **_ua_chan)
+{
+ int ret = 0;
+ struct ust_app_channel *ua_chan = NULL;
+
+ assert(ua_sess);
+ ASSERT_LOCKED(ua_sess->lock);
+
+ if (!strncmp(uchan->name, DEFAULT_METADATA_NAME,
+ sizeof(uchan->name))) {
+ copy_channel_attr_to_ustctl(&ua_sess->metadata_attr,
+ &uchan->attr);
+ ret = 0;
+ } else {
+ struct ltt_ust_context *uctx = NULL;
+
+ /*
+ * Create channel onto application and synchronize its
+ * configuration.
+ */
+ ret = ust_app_channel_allocate(ua_sess, uchan,
+ LTTNG_UST_CHAN_PER_CPU, usess,
+ &ua_chan);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = ust_app_channel_send(app, usess,
+ ua_sess, ua_chan);
+ if (ret) {
+ goto error;
+ }
+
+ /* Add contexts. */
+ cds_list_for_each_entry(uctx, &uchan->ctx_list, list) {
+ ret = create_ust_app_channel_context(ua_chan,
+ &uctx->ctx, app);
+ if (ret) {
+ goto error;
+ }
+ }
+ }
+
+error:
+ if (ret < 0) {
+ 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. */
+ break;
+ case -ENOMEM:
+ default:
+ break;
+ }
+ }
+
+ if (ret == 0 && _ua_chan) {
+ /*
+ * Only return the application's channel on success. Note
+ * that the channel can still be part of the application's
+ * channel hashtable on error.
+ */
+ *_ua_chan = ua_chan;
+ }
+ return ret;
+}
+
+/* The ua_sess lock must be held by the caller. */
+static
+int ust_app_map_create(struct ltt_ust_session *usess,
+ struct ust_app_session *ua_sess,
+ struct ltt_ust_map *umap, struct ust_app *app,
+ struct ust_app_map **_ua_map)
+{
+ int ret = 0;
+ struct ust_app_map *ua_map = NULL;
+
+ assert(ua_sess);
+ ASSERT_LOCKED(ua_sess->lock);
+
+ /*
+ * Create map onto application and synchronize its
+ * configuration.
+ */
+ ret = ust_app_map_allocate(ua_sess, umap,
+ LTTNG_UST_CHAN_PER_CPU, usess,
+ &ua_map);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = ust_app_map_send(app, usess, ua_sess, ua_map);
+ if (ret) {
+ goto error;
+ }
+
+error:
+ if (ret < 0) {
+ 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. */
+ break;
+ case -ENOMEM:
+ default:
+ break;
+ }
+ }
+
+ if (ret == 0 && _ua_map) {
+ /*
+ * Only return the application's map on success. Note
+ * that the map can still be part of the application's
+ * map hashtable on error.
+ */
+ *_ua_map = ua_map;
+ }
+ return ret;
+}
+
+/*
+ * Enable event for a specific session and channel on the tracer.
+ */
+int ust_app_enable_channel_event_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter, uiter;
+ struct lttng_ht_node_str *ua_chan_node;
+ struct ust_app *app;
+ struct ust_app_session *ua_sess;
+ struct ust_app_channel *ua_chan;
+ struct ust_app_event *ua_event;
+
+ assert(usess->active);
+ DBG("UST app enabling event %s for all apps for session id %" PRIu64,
+ uevent->attr.name, usess->id);
+
+ /*
+ * NOTE: At this point, this function is called only if the session and
+ * channel passed are already created for all apps. and enabled on the
+ * tracer also.
+ */
+
+ rcu_read_lock();
+
+ /* For all registered applications */
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (!ua_sess) {
+ /* The application has problem or is probably dead. */
+ continue;
+ }
+
+ 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);
+ /*
+ * 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);
+
+ /* Get event node */
+ ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name,
+ uevent->filter, uevent->attr.loglevel, uevent->exclusion,
+ uevent->attr.token);
+ if (ua_event == NULL) {
+ DBG3("UST app enable event %s not found for app PID %d."
+ "Skipping app", uevent->attr.name, app->pid);
+ goto next_app;
+ }
+
+ ret = enable_ust_app_event(ua_sess, ua_event, app);
+ if (ret < 0) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ goto error;
+ }
+ next_app:
+ pthread_mutex_unlock(&ua_sess->lock);
+ }
+
+error:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Enable event for a specific session and map on the tracer.
+ */
+int ust_app_enable_map_event_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_map *umap, struct ltt_ust_event *uevent)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter, uiter;
+ struct lttng_ht_node_str *ua_map_node;
+ struct ust_app *app;
+ struct ust_app_session *ua_sess;
+ struct ust_app_map *ua_map;
+ struct ust_app_event *ua_event;
+
+ assert(usess->active);
+ DBG("UST app enabling event %s for all apps for session id %" PRIu64,
+ uevent->attr.name, usess->id);
+
+ /*
+ * NOTE: At this point, this function is called only if the session and
+ * map passed are already created for all apps. and enabled on the
+ * tracer also.
+ */
+
+ rcu_read_lock();
+
+ /* For all registered applications */
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (!ua_sess) {
+ /* The application has problem or is probably dead. */
+ continue;
+ }
+
+ pthread_mutex_lock(&ua_sess->lock);
+
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ continue;
+ }
+
+ /* Lookup map in the ust app session */
+ lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter);
+ ua_map_node = lttng_ht_iter_get_node_str(&uiter);
+ /*
+ * It is possible that the map cannot be found is
+ * the map/event creation occurs concurrently with
+ * an application exit.
+ */
+ if (!ua_map_node) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ continue;
+ }
+
+ ua_map = caa_container_of(ua_map_node, struct ust_app_map, node);
+
+ /* Get event node */
+ ua_event = find_ust_app_event(ua_map->events, uevent->attr.name,
+ uevent->filter, uevent->attr.loglevel, uevent->exclusion,
+ uevent->attr.token);
+ if (ua_event == NULL) {
+ DBG3("UST app enable event %s not found for app PID %d."
+ "Skipping app", uevent->attr.name, app->pid);
+ goto next_app;
+ }
+
+ ret = enable_ust_app_event(ua_sess, ua_event, app);
+ if (ret < 0) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ goto error;
+ }
+ next_app:
+ pthread_mutex_unlock(&ua_sess->lock);
+ }
+
+error:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * For a specific existing UST session and UST channel, creates the event for
+ * all registered apps.
+ */
+int ust_app_create_channel_event_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter, uiter;
+ struct lttng_ht_node_str *ua_chan_node;
+ struct ust_app *app;
+ struct ust_app_session *ua_sess;
+ struct ust_app_channel *ua_chan;
+
+ assert(usess->active);
+ DBG("UST app creating event %s for all apps for session id %" PRIu64,
+ uevent->attr.name, usess->id);
+
+ rcu_read_lock();
+
+ /* For all registered applications */
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (!ua_sess) {
+ /* The application has problem or is probably dead. */
+ continue;
+ }
+
+ 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);
+
+ ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node);
+
+ ret = create_ust_app_channel_event(ua_sess, ua_chan, uevent, app);
+ pthread_mutex_unlock(&ua_sess->lock);
+ if (ret < 0) {
+ if (ret != -LTTNG_UST_ERR_EXIST) {
+ /* Possible value at this point: -ENOMEM. If so, we stop! */
+ break;
+ }
+ DBG2("UST app event %s already exist on app PID %d",
+ uevent->attr.name, app->pid);
+ continue;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+static
+int snapshot_key_values(struct ustctl_daemon_counter *map_handle,
+ struct lttng_ht *key_to_bucket_index_ht, int cpu,
+ const char *key_filter, struct lttng_ht *values)
+{
+ int ret;
+ struct lttng_ht_iter key_iter;
+ struct ust_registry_map_index_ht_entry *map_index_entry;
+
+ /* Iterate over all the formated_key -> counter index */
+ cds_lfht_for_each_entry(key_to_bucket_index_ht->ht,
+ &key_iter.iter, map_index_entry, node.node) {
+ bool overflow = 0, underflow = 0;
+ int64_t local_value = 0;
+ size_t dimension_indexes[1] = {map_index_entry->index};
+
+ if (key_filter && strcmp(key_filter,
+ map_index_entry->formated_key) != 0) {
+ continue;
+ }
+
+ ret = ustctl_counter_read(map_handle,
+ dimension_indexes, cpu, &local_value,
+ &overflow, &underflow);
+ if (ret) {
+ ERR("Error getting counter value from the tracer: key = '%s'",
+ map_index_entry->formated_key);
+ ret = -1;
+ goto end;
+ }
+
+ map_add_or_increment_map_values(values,
+ map_index_entry->formated_key, local_value,
+ underflow, overflow);
+ }
+ ret = 0;
+end:
+ return ret;
+}
+
+static
+int ust_app_map_list_values_per_uid_with_bitness_and_cpu(
+ const struct ltt_ust_session *usess,
+ const struct ltt_ust_map *umap,
+ uint32_t app_bitness,
+ uint32_t cpu,
+ const char *key_filter,
+ struct lttng_ht *values)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct buffer_reg_uid *buf_reg_uid;
+ struct buffer_reg_map *buf_reg_map;
+ struct ust_registry_session *ust_reg_sess;
+ struct lttng_ht_node_u64 *ust_reg_map_node;
+ struct ust_registry_map *ust_reg_map;
+
+ buf_reg_uid = buffer_reg_uid_find(usess->id, app_bitness, usess->uid);
+ if (!buf_reg_uid) {
+ /*
+ * Buffer registry entry for uid not found. Probably no app for
+ * this UID at the moment.
+ */
+ DBG("No buffer registry entry found for uid: ust-sess-id = %"PRIu64", bitness = %"PRIu32", uid = %d",
+ usess->id, app_bitness, usess->uid);
+ /*
+ * Not an error. Leave the key value pair unchanged and return.
+ */
+ ret = 0;
+ goto end;
+ }
+
+ buf_reg_map = buffer_reg_map_find(umap->id, buf_reg_uid);
+ if (!buf_reg_uid) {
+ ERR("Error getting per-uid map buffer registry entry: map-id = %"PRIu64,
+ umap->id);
+ ret = -1;
+ goto end;
+ }
+
+ ust_reg_sess = buf_reg_uid->registry->reg.ust;
+
+ /* Get the ust_reg map object from the registry */
+ // FIXME: frdeso: This can be changed to ust_registry_map_find() right?
+
+ lttng_ht_lookup(ust_reg_sess->maps, (void *) &umap->id, &iter);
+ ust_reg_map_node = lttng_ht_iter_get_node_u64(&iter);
+ if (!ust_reg_map_node) {
+ ERR("Error getting per-uid map buffer registry entry: map-id = %"PRIu64,
+ umap->id);
+ ret = -1;
+ goto end;
+ }
+ ust_reg_map = caa_container_of(ust_reg_map_node,
+ struct ust_registry_map, node);
+
+ ret = snapshot_key_values(buf_reg_map->daemon_counter,
+ ust_reg_map->key_string_to_bucket_index_ht,
+ cpu, key_filter, values);
+ if (ret) {
+ abort();
+ }
+
+
+ ret = 0;
+end:
+ return ret;
+}
+
+static
+int ust_app_map_list_values_per_uid(const struct ltt_ust_session *usess,
+ const struct ltt_ust_map *umap,
+ const struct lttng_map_query *query,
+ struct lttng_map_content *map_content)
+{
+ int i, ret = 0;
+ enum lttng_map_query_status map_query_status;
+ const char *key_filter;
+ struct lttng_ht *values = NULL;
+ bool sum_cpus = lttng_map_query_get_config_sum_by_cpu(query);
+ enum lttng_map_query_config_buffer config_buffer;
+ enum lttng_map_query_config_cpu config_cpu;
+ int selected_cpu;
+
+ map_query_status = lttng_map_query_get_key_filter(query, &key_filter);
+ if (map_query_status == LTTNG_MAP_QUERY_STATUS_NONE) {
+ key_filter = NULL;
+ } else if (map_query_status != LTTNG_MAP_QUERY_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ config_cpu = lttng_map_query_get_config_cpu(query);
+ if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) {
+ unsigned int count;
+ map_query_status = lttng_map_query_get_cpu_count(query, &count);
+ assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK);
+ assert(count == 1);
+
+ map_query_status = lttng_map_query_get_cpu_at_index(query, 0,
+ &selected_cpu);
+ assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK);
+ }
+
+ config_buffer = lttng_map_query_get_config_buffer(query);
+ if (config_buffer == LTTNG_MAP_QUERY_CONFIG_BUFFER_UST_UID_SUBSET) {
+ unsigned int count;
+ uid_t selected_uid;
+
+ map_query_status = lttng_map_query_get_uid_count(query, &count);
+ assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK);
+ assert(count == 1);
+
+ map_query_status = lttng_map_query_get_uid_at_index(query, 0,
+ &selected_uid);
+ assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK);
+
+ if (selected_uid != usess->uid) {
+ ret = 0;
+ goto end;
+ }
+ }
+
+ if (sum_cpus) {
+ values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+ }
+
+ for (i = 0; i < umap->nr_cpu; i++) {
+ if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) {
+ if (selected_cpu != i) {
+ continue;
+ }
+ }
+
+ if (!sum_cpus) {
+ values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+ }
+
+ ret = ust_app_map_list_values_per_uid_with_bitness_and_cpu(
+ usess, umap, 32, i, key_filter,
+ values);
+ if (ret) {
+ abort();
+ }
+
+ ret = ust_app_map_list_values_per_uid_with_bitness_and_cpu(
+ usess, umap, 64, i, key_filter,
+ values);
+ if (ret) {
+ abort();
+ }
+ if (!sum_cpus) {
+ ret = map_new_content_section(map_content,
+ LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID,
+ sum_cpus, usess->uid, i, values);
+ if (ret) {
+ abort();
+ }
+
+ lttng_ht_destroy(values);
+ }
+ }
+
+ if (sum_cpus) {
+ ret = map_new_content_section(map_content,
+ LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID,
+ sum_cpus, usess->uid, 0, values);
+ if (ret) {
+ abort();
+ }
+ lttng_ht_destroy(values);
+ }
+
+end:
+ return ret;
+}
+
+static
+int append_dead_app_kv(struct ltt_ust_map *umap,
+ const char *key_filter,
+ struct lttng_map_content *map_content)
+{
+ int ret;
+ struct lttng_ht *dead_app_kv_ht;
+ struct map_kv_ht_entry *kv_entry;
+ struct lttng_ht_iter key_iter;
+
+ struct lttng_ht *values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+
+ pthread_mutex_lock(&(umap->dead_app_kv_values.lock));
+
+ assert(umap->dead_app_kv_values.dead_app_kv_values_64bits);
+ dead_app_kv_ht = umap->dead_app_kv_values.dead_app_kv_values_64bits;
+
+ cds_lfht_for_each_entry(dead_app_kv_ht->ht, &key_iter.iter, kv_entry,
+ node.node) {
+ if (key_filter && strcmp(key_filter, kv_entry->key) != 0) {
+ continue;
+ }
+ map_add_or_increment_map_values(values, kv_entry->key,
+ kv_entry->value, kv_entry->has_underflowed,
+ kv_entry->has_overflowed);
+ }
+
+ assert(umap->dead_app_kv_values.dead_app_kv_values_32bits);
+
+ dead_app_kv_ht = umap->dead_app_kv_values.dead_app_kv_values_32bits;
+ cds_lfht_for_each_entry(dead_app_kv_ht->ht, &key_iter.iter, kv_entry,
+ node.node) {
+ if (key_filter && strcmp(key_filter, kv_entry->key) != 0) {
+ continue;
+ }
+ map_add_or_increment_map_values(values, kv_entry->key,
+ kv_entry->value, kv_entry->has_underflowed,
+ kv_entry->has_overflowed);
+ }
+
+ pthread_mutex_unlock(&umap->dead_app_kv_values.lock);
+
+ ret = map_new_content_section(map_content,
+ LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED,
+ true, 0, 0, values);
+
+ lttng_ht_destroy(values);
+ if (ret) {
+ ERR("Error appending deadapp kv");
+ goto end;
+ }
+
+
+ ret = 0;
+
+end:
+ return ret;
+}
+
+static
+int ust_app_map_list_values_per_pid_with_bitness_and_cpu(
+ const struct ltt_ust_session *usess,
+ struct ust_app *app,
+ struct ltt_ust_map *umap,
+ uint32_t app_bitness,
+ uint32_t cpu,
+ const char *key_filter,
+ struct lttng_ht *values)
+{
+ int ret = 0;
+
+ struct lttng_ht_iter map_iter;
+ struct lttng_ht_node_str *ua_map_node;
+ struct ust_app_map *ua_map;
+ struct ust_app_session *ua_sess;
+ struct ust_registry_session *ust_reg_sess;
+ struct ust_registry_map *ust_reg_map;
+
+ if (app->bits_per_long != app_bitness) {
+ ret = 0;
+ goto end;;
+ }
+
+ ua_sess = lookup_session_by_app(usess, app);
+ if (!ua_sess) {
+ /* Session not associated with this app. */
+ ret = 0;
+ goto end;;
+ }
+
+ ust_reg_sess = get_session_registry(ua_sess);
+ if (!ust_reg_sess) {
+ DBG("Application session is being torn down. Skip application.");
+ ret = 0;
+ goto end;;
+ }
+
+ /* Lookup map in the ust app session */
+ lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &map_iter);
+ ua_map_node = lttng_ht_iter_get_node_str(&map_iter);
+
+ assert(ua_map_node != NULL);
+ ua_map = caa_container_of(ua_map_node, struct ust_app_map, node);
+
+ pthread_mutex_lock(&ust_reg_sess->lock);
+ ust_reg_map = ust_registry_map_find(ust_reg_sess, ua_map->key);
+ pthread_mutex_unlock(&ust_reg_sess->lock);
+ assert(ust_reg_map);
+
+ ret = snapshot_key_values(ua_map->map_handle,
+ ust_reg_map->key_string_to_bucket_index_ht,
+ cpu, key_filter, values);
+ if (ret) {
+ ERR("Error snapshoting the content of map");
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+static
+int ust_app_map_list_values_per_pid(const struct ltt_ust_session *usess,
+ struct ltt_ust_map *umap,
+ const struct lttng_map_query *query,
+ struct lttng_map_content *map_content)
+{
+ enum lttng_map_query_status map_query_status;
+ const char *key_filter;
+ struct lttng_ht *values;
+ bool sum_cpus = lttng_map_query_get_config_sum_by_cpu(query);
+ bool sum_pids = lttng_map_query_get_config_sum_by_pid(query);
+ enum lttng_map_query_config_cpu config_cpu;
+ int selected_cpu, i, ret = 0;
+ struct lttng_ht_iter app_iter;
+ struct ust_app *app;
+
+ values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+
+ map_query_status = lttng_map_query_get_key_filter(query, &key_filter);
+ if (map_query_status == LTTNG_MAP_QUERY_STATUS_NONE) {
+ key_filter = NULL;
+ } else if (map_query_status != LTTNG_MAP_QUERY_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ config_cpu = lttng_map_query_get_config_cpu(query);
+ if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) {
+ unsigned int count;
+ map_query_status = lttng_map_query_get_cpu_count(query, &count);
+ assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK);
+ assert(count == 1);
+
+ map_query_status = lttng_map_query_get_cpu_at_index(query, 0,
+ &selected_cpu);
+ assert(map_query_status == LTTNG_MAP_QUERY_STATUS_OK);
+ }
+
+ /* Sum all cpus and pids on the same table. */
+ if (sum_cpus && sum_pids) {
+ values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+ }
+
+ if (!sum_cpus && sum_pids) {
+ /* Iterate over all currently registered apps. */
+ for (i = 0; i < umap->nr_cpu; i++) {
+ values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+ cds_lfht_for_each_entry(ust_app_ht->ht, &app_iter.iter, app, pid_n.node) {
+ ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu(
+ usess, app, umap, 32, i, key_filter, values);
+ if (ret) {
+ abort();
+ }
+ ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu(
+ usess, app, umap, 64, i, key_filter, values);
+ if (ret) {
+ abort();
+ }
+ }
+ ret = map_new_content_section(map_content,
+ LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID,
+ sum_cpus, app->pid, i, values);
+ if (ret) {
+ abort();
+ }
+
+ lttng_ht_destroy(values);
+ }
+ } else {
+ /* Iterate over all currently registered apps. */
+ cds_lfht_for_each_entry(ust_app_ht->ht, &app_iter.iter, app, pid_n.node) {
+
+ if (sum_cpus && !sum_pids) {
+ values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+ }
+
+ for (i = 0; i < umap->nr_cpu; i++) {
+
+ if (config_cpu == LTTNG_MAP_QUERY_CONFIG_CPU_SUBSET) {
+ if (selected_cpu != i) {
+ continue;
+ }
+ }
+
+ if (!sum_cpus && !sum_pids) {
+ values = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+ }
+
+ ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu(
+ usess, app, umap, 32, i, key_filter, values);
+ if (ret) {
+ abort();
+ }
+ ret = ust_app_map_list_values_per_pid_with_bitness_and_cpu(
+ usess, app, umap, 64, i, key_filter, values);
+ if (ret) {
+ abort();
+ }
+
+ if (!sum_cpus && !sum_pids) {
+ ret = map_new_content_section(map_content,
+ LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID,
+ sum_cpus, app->pid, i, values);
+ if (ret) {
+ abort();
+ }
+
+ lttng_ht_destroy(values);
+ }
+ }
+ if (sum_cpus && !sum_pids) {
+ ret = map_new_content_section(map_content,
+ LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID,
+ sum_cpus, app->pid, i, values);
+ if (ret) {
+ abort();
+ }
+
+ lttng_ht_destroy(values);
+ }
+ }
+ }
+
+ if (sum_cpus && sum_pids) {
+ ret = map_new_content_section(map_content,
+ LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID,
+ sum_cpus, 0, 0, values);
+ if (ret) {
+ abort();
+ }
+ lttng_ht_destroy(values);
+ }
+
+ /* Append dead app aggregated key-value pairs. */
+ ret = append_dead_app_kv(umap, key_filter, map_content);
+ if (ret) {
+ ERR("Error appending values from dead apps map");
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+int ust_app_map_list_values(const struct ltt_ust_session *usess,
+ struct ltt_ust_map *umap,
+ const struct lttng_map_query *query,
+ struct lttng_map_content **map_content)
+{
+ int ret;
+ struct lttng_map_content *local_map_content = NULL;
+
+ local_map_content = lttng_map_content_create(usess->buffer_type);
+ if (!local_map_content) {
+ ERR("Error creating a map content list");
+ ret = -1;
+ goto end;
+ }
+ rcu_read_lock();
+ if (usess->buffer_type == LTTNG_BUFFER_PER_UID) {
+ ret = ust_app_map_list_values_per_uid(usess, umap, query,
+ local_map_content);
+ if (ret) {
+ ERR("Error adding per-uid map value");
+ ret = -1;
+ goto end;
+ }
+ } else {
+ ret = ust_app_map_list_values_per_pid(usess, umap, query,
+ local_map_content);
+ if (ret) {
+ ERR("Error adding per-pid map value");
+ ret = -1;
+ goto end;
+ }
+ }
+ *map_content = local_map_content;
+ local_map_content = NULL;
+ ret = 0;
+end:
+ rcu_read_unlock();
+
+ lttng_map_content_destroy(local_map_content);
+ return ret;
+}
+
+/*
+ * For a specific existing UST session and UST map, creates the event for
+ * all registered apps.
+ */
+int ust_app_create_map_event_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_map *umap, struct ltt_ust_event *uevent)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter, uiter;
+ struct lttng_ht_node_str *ua_map_node;
+ struct ust_app *app;
+ struct ust_app_session *ua_sess;
+ struct ust_app_map *ua_map;
+
+ assert(usess->active);
+ DBG("UST app creating event %s in map %s for all apps for session id %" PRIu64,
+ uevent->attr.name, umap->name, usess->id);
+
+ rcu_read_lock();
+
+ /* For all registered applications */
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (!ua_sess) {
+ /* The application has problem or is probably dead. */
+ continue;
+ }
+
+ pthread_mutex_lock(&ua_sess->lock);
+
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ continue;
+ }
+
+ /* Lookup map in the ust app session */
+ lttng_ht_lookup(ua_sess->maps, (void *)umap->name, &uiter);
+ ua_map_node = lttng_ht_iter_get_node_str(&uiter);
+ /* If the map is not found, there is a code flow error */
+ assert(ua_map_node);
+
+ ua_map = caa_container_of(ua_map_node, struct ust_app_map, node);
+
+ ret = create_ust_app_map_event(ua_sess, ua_map, uevent, app);
+ assert(!ret);
+ pthread_mutex_unlock(&ua_sess->lock);
+ if (ret < 0) {
+ if (ret != -LTTNG_UST_ERR_EXIST) {
+ /* Possible value at this point: -ENOMEM. If so, we stop! */
+ break;
+ }
+ DBG2("UST app event %s already exist on app PID %d",
+ uevent->attr.name, app->pid);
+ continue;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Start tracing for a specific UST session and app.
+ *
+ * Called with UST app session lock held.
+ *
+ */
+static
+int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app)
+{
+ int ret = 0;
+ struct ust_app_session *ua_sess;
+
+ DBG("Starting tracing for ust app pid %d", app->pid);
+
+ rcu_read_lock();
+
+ if (!app->compatible) {
+ goto end;
+ }
+
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ /* The session is in teardown process. Ignore and continue. */
+ goto end;
+ }
+
+ pthread_mutex_lock(&ua_sess->lock);
+
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ goto end;
+ }
+
+ if (ua_sess->enabled) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ goto end;
+ }
+
+ /* Upon restart, we skip the setup, already done */
+ if (ua_sess->started) {
+ goto skip_setup;
+ }
+
+ health_code_update();
+
+skip_setup:
+ /* This starts the UST tracing */
+ pthread_mutex_lock(&app->sock_lock);
+ ret = ustctl_start_session(app->sock, ua_sess->handle);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("Error starting tracing for app pid: %d (ret: %d)",
+ app->pid, ret);
+ } else {
+ DBG("UST app start session failed. Application is dead.");
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ pthread_mutex_unlock(&ua_sess->lock);
+ goto end;
+ }
+ goto error_unlock;
+ }
+
+ /* Indicate that the session has been started once */
+ ua_sess->started = 1;
+ ua_sess->enabled = 1;
+
+ pthread_mutex_unlock(&ua_sess->lock);
+
+ health_code_update();
+
+ /* Quiescent wait after starting trace */
+ pthread_mutex_lock(&app->sock_lock);
+ ret = ustctl_wait_quiescent(app->sock);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app wait quiescent failed for app pid %d ret %d",
+ app->pid, ret);
+ }
+
+end:
+ rcu_read_unlock();
+ health_code_update();
+ return 0;
+
+error_unlock:
+ pthread_mutex_unlock(&ua_sess->lock);
+ rcu_read_unlock();
+ health_code_update();
+ return -1;
+}
+
+/*
+ * 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 ust_app_session *ua_sess;
+ struct ust_registry_session *registry;
+
+ DBG("Stopping tracing 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);
+
+ 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
+ * from either the application manager thread or the command thread. Simply
+ * indicate that this is a stop error.
+ */
+ if (!ua_sess->started) {
+ goto error_rcu_unlock;
+ }
+
+ health_code_update();
+
+ /* This inhibits UST tracing */
+ pthread_mutex_lock(&app->sock_lock);
+ ret = ustctl_stop_session(app->sock, ua_sess->handle);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("Error stopping tracing for app pid: %d (ret: %d)",
+ app->pid, ret);
+ } else {
+ DBG("UST app stop session failed. Application is dead.");
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ goto end_unlock;
+ }
+ goto error_rcu_unlock;
+ }
+
+ health_code_update();
+ ua_sess->enabled = 0;
+
+ /* Quiescent wait after stopping trace */
+ pthread_mutex_lock(&app->sock_lock);
+ ret = ustctl_wait_quiescent(app->sock);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app wait quiescent failed for app pid %d ret %d",
+ app->pid, ret);
+ }
+
+ health_code_update();
+
+ registry = get_session_registry(ua_sess);
+
+ /* The UST app session is held registry shall not be null. */
+ assert(registry);
+
+ /* Push metadata for application before freeing the application. */
+ (void) push_metadata(registry, ua_sess->consumer);
+
+end_unlock:
+ 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;
+}
+
+static
+int ust_app_flush_app_session(struct ust_app *app,
+ struct ust_app_session *ua_sess)
+{
+ int ret, retval = 0;
+ struct lttng_ht_iter iter;
+ struct ust_app_channel *ua_chan;
+ struct consumer_socket *socket;
+
+ DBG("Flushing app session buffers for ust app pid %d", app->pid);
+
+ rcu_read_lock();
+
+ if (!app->compatible) {
+ goto end_not_compatible;
+ }
+
+ pthread_mutex_lock(&ua_sess->lock);
+
+ if (ua_sess->deleted) {
+ goto end_deleted;
+ }
+
+ health_code_update();
+
+ /* Flushing buffers */
+ socket = consumer_find_socket_by_bitness(app->bits_per_long,
+ ua_sess->consumer);
+
+ /* Flush buffers and push metadata. */
+ switch (ua_sess->buffer_type) {
+ case LTTNG_BUFFER_PER_PID:
+ cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan,
+ node.node) {
+ health_code_update();
+ ret = consumer_flush_channel(socket, ua_chan->key);
+ if (ret) {
+ ERR("Error flushing consumer channel");
+ retval = -1;
+ continue;
+ }
+ }
+ break;
+ case LTTNG_BUFFER_PER_UID:
+ default:
+ assert(0);
+ break;
+ }
+
+ health_code_update();
+
+end_deleted:
+ pthread_mutex_unlock(&ua_sess->lock);
+
+end_not_compatible:
+ rcu_read_unlock();
+ health_code_update();
+ return retval;
+}
+
+/*
+ * Flush buffers for all applications for a specific UST session.
+ * Called with UST session lock held.
+ */
+static
+int ust_app_flush_session(struct ltt_ust_session *usess)
+
+{
+ int ret = 0;
+
+ DBG("Flushing session buffers for all ust apps");
+
+ rcu_read_lock();
+
+ /* Flush buffers and push metadata. */
+ switch (usess->buffer_type) {
+ case LTTNG_BUFFER_PER_UID:
+ {
+ struct buffer_reg_uid *reg;
+ struct lttng_ht_iter iter;
+
+ /* 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 *buf_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) {
+ /* Ignore request if no consumer is found for the session. */
+ continue;
+ }
+
+ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter,
+ buf_reg_chan, node.node) {
+ /*
+ * The following call will print error values so the return
+ * code is of little importance because whatever happens, we
+ * have to try them all.
+ */
+ (void) consumer_flush_channel(socket, buf_reg_chan->consumer_key);
+ }
+
+ ust_session_reg = reg->registry->reg.ust;
+ /* Push metadata. */
+ (void) push_metadata(ust_session_reg, usess->consumer);
+ }
+ break;
+ }
+ case LTTNG_BUFFER_PER_PID:
+ {
+ struct ust_app_session *ua_sess;
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ continue;
+ }
+ (void) ust_app_flush_app_session(app, ua_sess);
+ }
+ break;
+ }
+ default:
+ ret = -1;
+ assert(0);
+ break;
+ }
+
+ rcu_read_unlock();
+ health_code_update();
+ return ret;
+}
+
+static
+int ust_app_clear_quiescent_app_session(struct ust_app *app,
+ struct ust_app_session *ua_sess)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct ust_app_channel *ua_chan;
+ struct consumer_socket *socket;
+
+ DBG("Clearing stream quiescent state for ust app pid %d", app->pid);
+
+ rcu_read_lock();
+
+ if (!app->compatible) {
+ goto end_not_compatible;
+ }
+
+ pthread_mutex_lock(&ua_sess->lock);
+
+ if (ua_sess->deleted) {
+ goto end_unlock;
+ }
+
+ health_code_update();
+
+ socket = consumer_find_socket_by_bitness(app->bits_per_long,
+ ua_sess->consumer);
+ if (!socket) {
+ ERR("Failed to find consumer (%" PRIu32 ") socket",
+ app->bits_per_long);
+ ret = -1;
+ goto end_unlock;
+ }
+
+ /* Clear quiescent state. */
+ switch (ua_sess->buffer_type) {
+ case LTTNG_BUFFER_PER_PID:
+ cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter,
+ ua_chan, node.node) {
+ health_code_update();
+ ret = consumer_clear_quiescent_channel(socket,
+ ua_chan->key);
+ if (ret) {
+ ERR("Error clearing quiescent state for consumer channel");
+ ret = -1;
+ continue;
+ }
+ }
+ break;
+ case LTTNG_BUFFER_PER_UID:
+ default:
+ assert(0);
+ ret = -1;
+ break;
+ }
+
+ health_code_update();
+
+end_unlock:
+ pthread_mutex_unlock(&ua_sess->lock);
+
+end_not_compatible:
+ rcu_read_unlock();
+ health_code_update();
+ return ret;
+}
+
+/*
+ * Clear quiescent state in each stream for all applications for a
+ * specific UST session.
+ * Called with UST session lock held.
+ */
+static
+int ust_app_clear_quiescent_session(struct ltt_ust_session *usess)
+
+{
+ int ret = 0;
+
+ DBG("Clearing stream quiescent state for all ust apps");
+
+ rcu_read_lock();
+
+ switch (usess->buffer_type) {
+ case LTTNG_BUFFER_PER_UID:
+ {
+ struct lttng_ht_iter iter;
+ struct buffer_reg_uid *reg;
+
+ /*
+ * Clear quiescent for all per UID buffers associated to
+ * that session.
+ */
+ cds_list_for_each_entry(reg, &usess->buffer_reg_uid_list, lnode) {
+ struct consumer_socket *socket;
+ struct buffer_reg_channel *buf_reg_chan;
+
+ /* Get associated consumer socket.*/
+ socket = consumer_find_socket_by_bitness(
+ reg->bits_per_long, usess->consumer);
+ if (!socket) {
+ /*
+ * Ignore request if no consumer is found for
+ * the session.
+ */
+ continue;
+ }
+
+ cds_lfht_for_each_entry(reg->registry->channels->ht,
+ &iter.iter, buf_reg_chan, node.node) {
+ /*
+ * The following call will print error values so
+ * the return code is of little importance
+ * because whatever happens, we have to try them
+ * all.
+ */
+ (void) consumer_clear_quiescent_channel(socket,
+ buf_reg_chan->consumer_key);
+ }
+ }
+ break;
+ }
+ case LTTNG_BUFFER_PER_PID:
+ {
+ struct ust_app_session *ua_sess;
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app,
+ pid_n.node) {
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ continue;
+ }
+ (void) ust_app_clear_quiescent_app_session(app,
+ ua_sess);
+ }
+ break;
+ }
+ default:
+ ret = -1;
+ assert(0);
+ break;
+ }
+
+ rcu_read_unlock();
+ health_code_update();
+ return ret;
+}
+
+/*
+ * Destroy a specific UST session in apps.
+ */
+static int destroy_trace(struct ltt_ust_session *usess, struct ust_app *app)
+{
+ int ret;
+ struct ust_app_session *ua_sess;
+ struct lttng_ht_iter iter;
+ struct lttng_ht_node_u64 *node;
+
+ DBG("Destroy tracing for ust app pid %d", app->pid);
+
+ rcu_read_lock();
+
+ if (!app->compatible) {
+ goto end;
+ }
+
+ __lookup_session_by_app(usess, app, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
+ if (node == NULL) {
+ /* Session is being or is deleted. */
+ goto end;
+ }
+ ua_sess = caa_container_of(node, struct ust_app_session, node);
+
+ health_code_update();
+ destroy_app_session(app, ua_sess);
+
+ health_code_update();
+
+ /* Quiescent wait after stopping trace */
+ pthread_mutex_lock(&app->sock_lock);
+ ret = ustctl_wait_quiescent(app->sock);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app wait quiescent failed for app pid %d ret %d",
+ app->pid, ret);
+ }
+end:
+ rcu_read_unlock();
+ health_code_update();
+ return 0;
+}
+
+/*
+ * Start tracing for the UST session.
+ */
+int ust_app_start_trace_all(struct ltt_ust_session *usess)
+{
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+
+ DBG("Starting all UST traces");
+
+ /*
+ * Even though the start trace might fail, flag this session active so
+ * other application coming in are started by default.
+ */
+ usess->active = 1;
+
+ rcu_read_lock();
+
+ /*
+ * In a start-stop-start use-case, we need to clear the quiescent state
+ * of each channel set by the prior stop command, thus ensuring that a
+ * following stop or destroy is sure to grab a timestamp_end near those
+ * operations, even if the packet is empty.
+ */
+ (void) ust_app_clear_quiescent_session(usess);
+
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ ust_app_global_update(usess, app);
+ }
+
+ rcu_read_unlock();
+
+ return 0;
+}
+
+/*
+ * Start tracing for the UST session.
+ * Called with UST session lock held.
+ */
+int ust_app_stop_trace_all(struct ltt_ust_session *usess)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+
+ DBG("Stopping all UST traces");
+
+ /*
+ * Even though the stop trace might fail, flag this session inactive so
+ * other application coming in are not started by default.
+ */
+ usess->active = 0;
+
+ rcu_read_lock();
+
+ 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;
+ }
+ }
+
+ (void) ust_app_flush_session(usess);
+
+ rcu_read_unlock();
+
+ return 0;
+}
+
+/*
+ * Destroy app UST session.
+ */
+int ust_app_destroy_trace_all(struct ltt_ust_session *usess)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+
+ DBG("Destroy all UST traces");
+
+ rcu_read_lock();
+
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ ret = destroy_trace(usess, app);
+ if (ret < 0) {
+ /* Continue to next apps even on error */
+ continue;
+ }
+ }
+
+ rcu_read_unlock();
+
+ return 0;
+}
+
+/* The ua_sess lock must be held by the caller. */
+static
+int find_or_create_ust_app_channel(
+ struct ltt_ust_session *usess,
+ struct ust_app_session *ua_sess,
+ struct ust_app *app,
+ struct ltt_ust_channel *uchan,
+ struct ust_app_channel **ua_chan)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct lttng_ht_node_str *ua_chan_node;
+
+ lttng_ht_lookup(ua_sess->channels, (void *) uchan->name, &iter);
+ ua_chan_node = lttng_ht_iter_get_node_str(&iter);
+ if (ua_chan_node) {
+ *ua_chan = caa_container_of(ua_chan_node,
+ struct ust_app_channel, node);
+ goto end;
+ }
+
+ ret = ust_app_channel_create(usess, ua_sess, uchan, app, ua_chan);
+ if (ret) {
+ goto end;
+ }
+end:
+ return ret;
+}
+
+/* The ua_sess lock must be held by the caller. */
+static
+int find_or_create_ust_app_map(
+ struct ltt_ust_session *usess,
+ struct ust_app_session *ua_sess,
+ struct ust_app *app,
+ struct ltt_ust_map *umap,
+ struct ust_app_map **ua_map)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct lttng_ht_node_str *ua_map_node;
+
+ lttng_ht_lookup(ua_sess->maps, (void *) umap->name, &iter);
+ ua_map_node = lttng_ht_iter_get_node_str(&iter);
+ if (ua_map_node) {
+ *ua_map = caa_container_of(ua_map_node,
+ struct ust_app_map, node);
+ goto end;
+ }
+
+ DBG("UST map id = %"PRIu64" not found. Creating it.", umap->id);
+ ret = ust_app_map_create(usess, ua_sess, umap, app, ua_map);
+ if (ret) {
+ goto end;
+ }
+end:
+ return ret;
+}
+
+static
+int ust_app_channel_synchronize_event(struct ust_app_channel *ua_chan,
+ struct ltt_ust_event *uevent, struct ust_app_session *ua_sess,
+ struct ust_app *app)
+{
+ int ret = 0;
+ struct ust_app_event *ua_event = NULL;
+
+ ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name,
+ uevent->filter, uevent->attr.loglevel, uevent->exclusion,
+ uevent->attr.token);
+ if (!ua_event) {
+ ret = create_ust_app_channel_event(ua_sess, ua_chan, uevent, app);
+
+ if (ret < 0) {
+ goto end;
+ }
+ } else {
+ if (ua_event->enabled != uevent->enabled) {
+ ret = uevent->enabled ?
+ enable_ust_app_event(ua_sess, ua_event, app) :
+ disable_ust_app_event(ua_sess, ua_event, app);
+ }
+ }
+
+end:
+ return ret;
+}
+
+/* Called with RCU read-side lock held. */
+static
+int ust_app_map_synchronize_event(struct ust_app_map *ua_map,
+ struct ltt_ust_event *uevent, struct ust_app_session *ua_sess,
+ struct ust_app *app)
+{
+ int ret = 0;
+ struct ust_app_event *ua_event = NULL;
+
+ ua_event = find_ust_app_event(ua_map->events, uevent->attr.name,
+ uevent->filter, uevent->attr.loglevel, uevent->exclusion,
+ uevent->attr.token);
+ if (!ua_event) {
+ ret = create_ust_app_map_event(ua_sess, ua_map, uevent, app);
+ if (ret < 0) {
+ goto end;
+ }
+ } else {
+ if (ua_event->enabled != uevent->enabled) {
+ ret = uevent->enabled ?
+ enable_ust_app_event(ua_sess, ua_event, app) :
+ disable_ust_app_event(ua_sess, ua_event, app);
+ }
+ }
+
+end:
+ return ret;
+}
+
+static
+void ust_app_synchronize_event_notifier_rules(struct ust_app *app)
+{
+ int ret = 0;
+ enum lttng_error_code ret_code;
+ enum lttng_trigger_status t_status;
+ struct lttng_ht_iter app_trigger_iter;
+ struct lttng_triggers *triggers = NULL;
+ struct ust_app_event_notifier_rule *event_notifier_rule;
+ unsigned int count, i;
+
+ /*
+ * Currrently, registering or unregistering a trigger with an
+ * event rule condition causes a full synchronization of the event
+ * notifiers.
+ *
+ * The first step attempts to add an event notifier for all registered
+ * triggers that apply to the user space tracers. Then, the
+ * application's event notifiers rules are all checked against the list
+ * of registered triggers. Any event notifier that doesn't have a
+ * matching trigger can be assumed to have been disabled.
+ *
+ * All of this is inefficient, but is put in place to get the feature
+ * rolling as it is simpler at this moment. It will be optimized Soon™
+ * to allow the state of enabled
+ * event notifiers to be synchronized in a piece-wise way.
+ */
+
+ /* Get all triggers using uid 0 (root) */
+ ret_code = notification_thread_command_list_triggers(
+ notification_thread_handle, 0, &triggers);
+ if (ret_code != LTTNG_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ assert(triggers);
+
+ t_status = lttng_triggers_get_count(triggers, &count);
+ if (t_status != LTTNG_TRIGGER_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ for (i = 0; i < count; i++) {
+ const struct lttng_condition *condition;
+ const struct lttng_event_rule *event_rule;
+ struct lttng_trigger *trigger;
+ const struct ust_app_event_notifier_rule *looked_up_event_notifier_rule;
+ enum lttng_condition_status condition_status;
+ uint64_t token;
+
+ trigger = lttng_triggers_borrow_mutable_at_index(triggers, i);
+ assert(trigger);
+
+ token = lttng_trigger_get_tracer_token(trigger);
+ condition = lttng_trigger_get_const_condition(trigger);
+
+ if (!lttng_trigger_needs_tracer_notifier(trigger)) {
+ continue;
+ }
+
+ condition_status = lttng_condition_on_event_get_rule(condition, &event_rule);
+ assert(condition_status == LTTNG_CONDITION_STATUS_OK);
+
+ if (lttng_event_rule_get_domain_type(event_rule) == LTTNG_DOMAIN_KERNEL) {
+ /* Skip kernel related triggers. */
+ continue;
+ }
+
+ /*
+ * Find or create the associated token event rule. The caller
+ * holds the RCU read lock, so this is safe to call without
+ * explicitly acquiring it here.
+ */
+ looked_up_event_notifier_rule = find_ust_app_event_notifier_rule(
+ app->token_to_event_notifier_rule_ht, token);
+ if (!looked_up_event_notifier_rule) {
+ ret = create_ust_app_event_notifier_rule(trigger, app);
+ if (ret < 0) {
+ goto end;
+ }
+ }
+ }
+
+ rcu_read_lock();
+ /* Remove all unknown event sources from the app. */
+ cds_lfht_for_each_entry (app->token_to_event_notifier_rule_ht->ht,
+ &app_trigger_iter.iter, event_notifier_rule,
+ node.node) {
+ const uint64_t app_token = event_notifier_rule->token;
+ bool found = false;
+
+ /*
+ * Check if the app event trigger still exists on the
+ * notification side.
+ */
+ for (i = 0; i < count; i++) {
+ uint64_t notification_thread_token;
+ const struct lttng_trigger *trigger =
+ lttng_triggers_get_at_index(
+ triggers, i);
+
+ assert(trigger);
+
+ notification_thread_token =
+ lttng_trigger_get_tracer_token(trigger);
+
+ if (notification_thread_token == app_token) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ /* Still valid. */
+ continue;
+ }
+
+ /*
+ * This trigger was unregistered, disable it on the tracer's
+ * side.
+ */
+ ret = lttng_ht_del(app->token_to_event_notifier_rule_ht,
+ &app_trigger_iter);
+ assert(ret == 0);
+
+ /* Callee logs errors. */
+ (void) disable_ust_object(app, event_notifier_rule->obj);
+
+ delete_ust_app_event_notifier_rule(
+ app->sock, event_notifier_rule, app);
+ }
+
+ rcu_read_unlock();
+
+end:
+ lttng_triggers_destroy(triggers);
+ return;
+}
+
+/*
+ * Called with RCU read-side lock held.
+ */
+static
+void ust_app_synchronize_all_channels(struct ltt_ust_session *usess,
+ struct ust_app_session *ua_sess,
+ struct ust_app *app)
+{
+ int ret = 0;
+ struct cds_lfht_iter uchan_iter;
+ struct ltt_ust_channel *uchan;
+
+ assert(usess);
+ assert(ua_sess);
+ assert(app);
+
+ cds_lfht_for_each_entry(usess->domain_global.channels->ht, &uchan_iter,
+ uchan, node.node) {
+ struct ust_app_channel *ua_chan;
+ struct cds_lfht_iter uevent_iter;
+ struct ltt_ust_event *uevent;
+
+ /*
+ * Search for a matching ust_app_channel. If none is found,
+ * create it. Creating the channel will cause the ua_chan
+ * structure to be allocated, the channel buffers to be
+ * allocated (if necessary) and sent to the application, and
+ * all enabled contexts will be added to the channel.
+ */
+ ret = find_or_create_ust_app_channel(usess, ua_sess,
+ app, uchan, &ua_chan);
+ if (ret) {
+ /* Tracer is probably gone or ENOMEM. */
+ goto end;
+ }
+
+ if (!ua_chan) {
+ /* ua_chan will be NULL for the metadata channel */
+ continue;
+ }
+
+ cds_lfht_for_each_entry(uchan->events->ht, &uevent_iter, uevent,
+ node.node) {
+ ret = ust_app_channel_synchronize_event(ua_chan,
+ uevent, ua_sess, app);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ if (ua_chan->enabled != uchan->enabled) {
+ ret = uchan->enabled ?
+ enable_ust_app_channel(ua_sess, uchan, app) :
+ disable_ust_app_channel(ua_sess, ua_chan, app);
+ if (ret) {
+ goto end;
+ }
+ }
+ }
+end:
+ return;
+}
+
+/*
+ * Called with RCU read-side lock held.
+ */
+static
+void ust_app_synchronize_all_maps(struct ltt_ust_session *usess,
+ struct ust_app_session *ua_sess,
+ struct ust_app *app)
+{
+ int ret = 0;
+ struct cds_lfht_iter umap_iter;
+ struct ltt_ust_map *umap;
+
+ assert(usess);
+ assert(ua_sess);
+ assert(app);
+
+ cds_lfht_for_each_entry(usess->domain_global.maps->ht, &umap_iter,
+ umap, node.node) {
+ struct ust_app_map *ua_map;
+ struct cds_lfht_iter uevent_iter;
+ struct ltt_ust_event *uevent;
+
+ DBG("Synchronizing UST map id = %"PRIu64, umap->id);
+
+ ret = find_or_create_ust_app_map(usess, ua_sess,
+ app, umap, &ua_map);
+ if (ret) {
+ /* Tracer is probably gone or ENOMEM. */
+ goto end;
+ }
+
+ DBG("Synchronizing all events of UST map id = %"PRIu64, umap->id);
+ cds_lfht_for_each_entry(umap->events->ht, &uevent_iter, uevent,
+ node.node) {
+ ret = ust_app_map_synchronize_event(ua_map,
+ uevent, ua_sess, app);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ if (ua_map->enabled != umap->enabled) {
+ if (umap->enabled) {
+ DBG("Map disabled on the tracer side but shouldn't");
+ ret = enable_ust_app_map(ua_sess, umap, app);
+ } else {
+ DBG("Map enabled on the tracer side but shouldn't");
+ ret = disable_ust_app_map(ua_sess, ua_map, app);
+ }
+ if (ret) {
+ goto end;
+ }
+ }
+ }
+end:
+ return;
+}
+
+/*
+ * The caller must ensure that the application is compatible and is tracked
+ * by the process attribute trackers.
+ */
+static
+void ust_app_synchronize(struct ltt_ust_session *usess,
+ struct ust_app *app)
+{
+ int ret = 0;
+ struct ust_app_session *ua_sess = NULL;
+
+ /*
+ * The application's configuration should only be synchronized for
+ * active sessions.
+ */
+ assert(usess->active);
+
+ ret = find_or_create_ust_app_session(usess, app, &ua_sess, NULL);
+ if (ret < 0) {
+ /* Tracer is probably gone or ENOMEM. */
+ goto error;
+ }
+ assert(ua_sess);
+
+
+ rcu_read_lock();
+
+ pthread_mutex_lock(&ua_sess->lock);
+ if (ua_sess->deleted) {
+ pthread_mutex_unlock(&ua_sess->lock);
+ goto end;
+ }
+ ust_app_synchronize_all_channels(usess, ua_sess, app);
+ ust_app_synchronize_all_maps(usess, ua_sess, app);
+
+ /*
+ * Create the metadata for the application. This returns gracefully if a
+ * metadata was already set for the session.
+ *
+ * The metadata channel must be created after the data channels as the
+ * consumer daemon assumes this ordering. When interacting with a relay
+ * daemon, the consumer will use this assumption to send the
+ * "STREAMS_SENT" message to the relay daemon.
+ */
+ ret = create_ust_app_metadata(ua_sess, app, usess->consumer);
+ if (ret < 0) {
+ goto error_unlock;
+ }
+
+ rcu_read_unlock();
+
+end:
+ pthread_mutex_unlock(&ua_sess->lock);
+ /* Everything went well at this point. */
+ return;
+
+error_unlock:
+ rcu_read_unlock();
+ pthread_mutex_unlock(&ua_sess->lock);
+error:
+ if (ua_sess) {
+ destroy_app_session(app, ua_sess);
+ }
+ return;
+}
+
+static
+void ust_app_global_destroy(struct ltt_ust_session *usess, struct ust_app *app)
+{
+ struct ust_app_session *ua_sess;
+
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ return;
+ }
+ destroy_app_session(app, ua_sess);
+}
+
+/*
+ * Add channels/events from UST global domain to registered apps at sock.
+ *
+ * Called with session lock held.
+ * Called with RCU read-side lock held.
+ */
+void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app)
+{
+ assert(usess);
+ assert(usess->active);
+
+ DBG2("UST app global update for app sock %d for session id %" PRIu64,
+ app->sock, usess->id);
+
+ if (!app->compatible) {
+ return;
+ }
+ if (trace_ust_id_tracker_lookup(LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID,
+ usess, app->pid) &&
+ trace_ust_id_tracker_lookup(
+ LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID,
+ usess, app->uid) &&
+ trace_ust_id_tracker_lookup(
+ LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID,
+ usess, app->gid)) {
+ /*
+ * Synchronize the application's internal tracing configuration
+ * and start tracing.
+ */
+ ust_app_synchronize(usess, app);
+ ust_app_start_trace(usess, app);
+ } else {
+ ust_app_global_destroy(usess, app);
+ }
+}
+
+/*
+ * Add all event notifiers to an application.
+ *
+ * Called with session lock held.
+ * Called with RCU read-side lock held.
+ */
+void ust_app_global_update_event_notifier_rules(struct ust_app *app)
+{
+ DBG2("UST application global event notifier rules update: app = '%s' (ppid: %d)",
+ app->name, app->ppid);
+
+ if (!app->compatible) {
+ return;
+ }
+
+ if (app->event_notifier_group.object == NULL) {
+ WARN("UST app global update of event notifiers for app skipped since communication handle is null: app = '%s' (ppid: %d)",
+ app->name, app->ppid);
+ return;
+ }
+
+ ust_app_synchronize_event_notifier_rules(app);
+}
+
+/*
+ * Called with session lock held.
+ */
+void ust_app_global_update_all(struct ltt_ust_session *usess)
+{
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ ust_app_global_update(usess, app);
+ }
+ rcu_read_unlock();
+}
+
+void ust_app_global_update_all_event_notifier_rules(void)
+{
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ ust_app_global_update_event_notifier_rules(app);
+ }
+
+ rcu_read_unlock();
+}
+
+void ust_app_update_event_notifier_error_count(struct lttng_trigger *trigger)
+{
+ uint64_t error_count = 0;
+ enum event_notifier_error_accounting_status status;
+ struct lttng_condition *condition = lttng_trigger_get_condition(trigger);
+
+ status = event_notifier_error_accounting_get_count(trigger, &error_count);
+ if (status != EVENT_NOTIFIER_ERROR_ACCOUNTING_STATUS_OK) {
+ ERR("Error getting trigger error count.");
+ }
+
+ lttng_condition_on_event_set_error_count(condition, error_count);
+}
+
+/*
+ * Add context to a specific channel for global UST domain.
+ */
+int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess,
+ struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx)
+{
+ int ret = 0;
+ struct lttng_ht_node_str *ua_chan_node;
+ struct lttng_ht_iter iter, uiter;
+ struct ust_app_channel *ua_chan = NULL;
+ struct ust_app_session *ua_sess;
+ struct ust_app *app;
+
+ assert(usess->active);
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ if (!app->compatible) {
+ /*
+ * TODO: In time, we should notice the caller of this error by
+ * telling him that this is a version error.
+ */
+ continue;
+ }
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ continue;
+ }
+
+ 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 (ua_chan_node == NULL) {
+ goto next_app;
+ }
+ ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel,
+ node);
+ ret = create_ust_app_channel_context(ua_chan, &uctx->ctx, app);
+ if (ret < 0) {
+ goto next_app;
+ }
+ next_app:
+ pthread_mutex_unlock(&ua_sess->lock);
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Receive registration and populate the given msg structure.
+ *
+ * On success return 0 else a negative value returned by the ustctl call.
+ */
+int ust_app_recv_registration(int sock, struct ust_register_msg *msg)
+{
+ int ret;
+ uint32_t pid, ppid, uid, gid;
+
+ assert(msg);
+
+ ret = ustctl_recv_reg_msg(sock, &msg->type, &msg->major, &msg->minor,
+ &pid, &ppid, &uid, &gid,
+ &msg->bits_per_long,
+ &msg->uint8_t_alignment,
+ &msg->uint16_t_alignment,
+ &msg->uint32_t_alignment,
+ &msg->uint64_t_alignment,
+ &msg->long_alignment,
+ &msg->byte_order,
+ msg->name);
+ if (ret < 0) {
+ switch (-ret) {
+ case EPIPE:
+ case ECONNRESET:
+ case LTTNG_UST_ERR_EXITING:
+ DBG3("UST app recv reg message failed. Application died");
+ break;
+ case LTTNG_UST_ERR_UNSUP_MAJOR:
+ ERR("UST app recv reg unsupported version %d.%d. Supporting %d.%d",
+ msg->major, msg->minor, LTTNG_UST_ABI_MAJOR_VERSION,
+ LTTNG_UST_ABI_MINOR_VERSION);
+ break;
+ default:
+ ERR("UST app recv reg message failed with ret %d", ret);
+ break;
+ }
+ goto error;
+ }
+ msg->pid = (pid_t) pid;
+ msg->ppid = (pid_t) ppid;
+ msg->uid = (uid_t) uid;
+ msg->gid = (gid_t) gid;
+
+error:
+ return ret;
+}
+
+/*
+ * Return a ust app session object using the application object and the
+ * session object descriptor has a key. If not found, NULL is returned.
+ * A RCU read side lock MUST be acquired when calling this function.
+*/
+static struct ust_app_session *find_session_by_objd(struct ust_app *app,
+ int objd)
+{
+ struct lttng_ht_node_ulong *node;
+ struct lttng_ht_iter iter;
+ struct ust_app_session *ua_sess = NULL;
+
+ assert(app);
+
+ lttng_ht_lookup(app->ust_sessions_objd, (void *)((unsigned long) objd), &iter);
+ node = lttng_ht_iter_get_node_ulong(&iter);
+ if (node == NULL) {
+ DBG2("UST app session find by objd %d not found", objd);
+ goto error;
+ }
+
+ ua_sess = caa_container_of(node, struct ust_app_session, ust_objd_node);
+
+error:
+ return ua_sess;
+}
+
+/*
+ * Return a ust app channel object using the application object and the channel
+ * object descriptor has a key. If not found, NULL is returned. A RCU read side
+ * lock MUST be acquired before calling this function.
+ */
+static struct ust_app_channel *find_channel_by_objd(struct ust_app *app,
+ int objd)
+{
+ struct lttng_ht_node_ulong *node;
+ struct lttng_ht_iter iter;
+ struct ust_app_channel *ua_chan = NULL;
+
+ assert(app);
+
+ lttng_ht_lookup(app->ust_chan_objd, (void *)((unsigned long) objd), &iter);
+ node = lttng_ht_iter_get_node_ulong(&iter);
+ if (node == NULL) {
+ DBG2("UST app channel find by objd %d not found", objd);
+ goto error;
+ }
+
+ ua_chan = caa_container_of(node, struct ust_app_channel, ust_objd_node);
+
+error:
+ return ua_chan;
+}
+
+/*
+ * Return a ust app map object using the application object and the map
+ * object descriptor has a key. If not found, NULL is returned. A RCU read side
+ * lock MUST be acquired before calling this function.
+ */
+static struct ust_app_map *find_map_by_objd(struct ust_app *app,
+ int objd)
+{
+ struct lttng_ht_node_ulong *node;
+ struct lttng_ht_iter iter;
+ struct ust_app_map *ua_map = NULL;
+
+ assert(app);
+
+ lttng_ht_lookup(app->ust_map_objd, (void *)((unsigned long) objd), &iter);
+ node = lttng_ht_iter_get_node_ulong(&iter);
+ if (node == NULL) {
+ DBG2("UST app map find by objd %d not found", objd);
+ goto error;
+ }
+
+ ua_map = caa_container_of(node, struct ust_app_map, ust_objd_node);
+
+error:
+ return ua_map;
+}
+
+/*
+ * Reply to a register channel notification from an application on the notify
+ * socket. The channel metadata is also created.
+ *
+ * The session UST registry lock is acquired in this function.
+ *
+ * On success 0 is returned else a negative value.
+ */
+static int reply_ust_register_channel(int sock, int cobjd,
+ size_t nr_fields, struct ustctl_field *fields)
+{
+ int ret, ret_code = 0;
+ uint32_t chan_id;
+ uint64_t chan_reg_key;
+ enum ustctl_channel_header type;
+ struct ust_app *app;
+ struct ust_app_channel *ua_chan;
+ struct ust_app_session *ua_sess;
+ struct ust_registry_session *registry;
+ struct ust_registry_channel *ust_reg_chan;
+
+ rcu_read_lock();
+
+ /* Lookup application. If not found, there is a code flow error. */
+ app = find_app_by_notify_sock(sock);
+ if (!app) {
+ DBG("Application socket %d is being torn down. Abort event notify",
+ sock);
+ ret = 0;
+ goto error_rcu_unlock;
+ }
+
+ /* Lookup channel by UST object descriptor. */
+ ua_chan = find_channel_by_objd(app, cobjd);
+ if (!ua_chan) {
+ DBG("Application channel is being torn down. Abort event notify");
+ ret = 0;
+ goto error_rcu_unlock;
+ }
+
+ assert(ua_chan->session);
+ ua_sess = ua_chan->session;
+
+ /* Get right session registry depending on the session buffer type. */
+ registry = get_session_registry(ua_sess);
+ if (!registry) {
+ DBG("Application session is being torn down. Abort event notify");
+ ret = 0;
+ goto error_rcu_unlock;
+ };
+
+ /* Depending on the buffer type, a different channel key is used. */
+ if (ua_sess->buffer_type == LTTNG_BUFFER_PER_UID) {
+ chan_reg_key = ua_chan->tracing_channel_id;
+ } else {
+ chan_reg_key = ua_chan->key;
+ }
+
+ pthread_mutex_lock(®istry->lock);
+
+ ust_reg_chan = ust_registry_channel_find(registry, chan_reg_key);
+ assert(ust_reg_chan);
+
+ if (!ust_reg_chan->register_done) {
+ /*
+ * TODO: eventually use the registry event count for
+ * this channel to better guess header type for per-pid
+ * buffers.
+ */
+ type = USTCTL_CHANNEL_HEADER_LARGE;
+ ust_reg_chan->nr_ctx_fields = nr_fields;
+ ust_reg_chan->ctx_fields = fields;
+ fields = NULL;
+ ust_reg_chan->header_type = type;
+ } else {
+ /* Get current already assigned values. */
+ type = ust_reg_chan->header_type;
+ }
+ /* Channel id is set during the object creation. */
+ chan_id = ust_reg_chan->chan_id;
+
+ /* Append to metadata */
+ if (!ust_reg_chan->metadata_dumped) {
+ ret_code = ust_metadata_channel_statedump(registry, ust_reg_chan);
+ if (ret_code) {
+ ERR("Error appending channel metadata (errno = %d)", ret_code);
+ goto reply;
+ }
+ }
+
+reply:
+ DBG3("UST app replying to register channel key %" PRIu64
+ " with id %u, type: %d, ret: %d", chan_reg_key, chan_id, type,
+ ret_code);
+
+ ret = ustctl_reply_register_channel(sock, chan_id, type, ret_code);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app reply channel failed with ret %d", ret);
+ } else {
+ DBG3("UST app reply channel failed. Application died");
+ }
+ goto error;
+ }
+
+ /* This channel registry registration is completed. */
+ ust_reg_chan->register_done = 1;
+
+error:
+ pthread_mutex_unlock(®istry->lock);
+error_rcu_unlock:
+ rcu_read_unlock();
+ free(fields);
+ return ret;
+}
+
+static int add_event_ust_chan_registry(int sock, struct ust_app *ua,
+ struct ust_app_channel *ua_chan, int sobjd, int cobjd, char *name,
+ char *sig, size_t nr_fields, struct ustctl_field *fields,
+ int loglevel_value, char *model_emf_uri)
+{
+ int ret, ret_code;
+ uint32_t event_id = 0;
+ uint64_t chan_reg_key;
+ struct ust_app_session *ua_sess;
+ struct ust_registry_session *registry;
+ /*
+ * The counter index is unused for channel events. It's only used for
+ * map events.
+ */
+ uint64_t counter_index = 0;
+
+ assert(ua_chan->session);
+ ua_sess = ua_chan->session;
+
+ registry = get_session_registry(ua_sess);
+ if (!registry) {
+ DBG("Application session is being torn down. Abort event notify");
+ ret = 0;
+ goto error;
+ }
+
+ if (ua_sess->buffer_type == LTTNG_BUFFER_PER_UID) {
+ chan_reg_key = ua_chan->tracing_channel_id;
+ } else {
+ chan_reg_key = ua_chan->key;
+ }
+
+ 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_chan_create_event(registry, chan_reg_key,
+ sobjd, cobjd, name, sig, nr_fields, fields,
+ loglevel_value, model_emf_uri, ua_sess->buffer_type,
+ &event_id, ua);
+ sig = NULL;
+ fields = NULL;
+ model_emf_uri = NULL;
+
+ /*
+ * The return value is returned to ustctl so in case of an error, the
+ * application can be notified. In case of an error, it's important not to
+ * return a negative error or else the application will get closed.
+ */
+ ret = ustctl_reply_register_event(sock, event_id, counter_index, ret_code);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app reply event failed with ret %d", ret);
+ } else {
+ DBG3("UST app reply event failed. Application died");
+ }
+ /*
+ * No need to wipe the create event since the application socket will
+ * get close on error hence cleaning up everything by itself.
+ */
+ goto error;
+ }
+
+ DBG3("UST registry event %s with id %" PRId32 " added successfully",
+ name, event_id);
+
+error:
+ pthread_mutex_unlock(®istry->lock);
+ return ret;
+}
+
+static int add_event_ust_map_registry(int sock, struct ust_app *ua,
+ struct ust_app_map *ua_map, int sobjd, int cobjd, char *name,
+ char *sig, size_t nr_fields, struct ustctl_field *fields,
+ int loglevel_value, char *model_emf_uri, uint64_t tracer_token)
+{
+ int ret, ret_code;
+ uint64_t map_reg_key, counter_index;
+ struct ust_app_session *ua_sess;
+ struct ust_registry_session *registry;
+
+ assert(ua_map->session);
+ ua_sess = ua_map->session;
+
+ registry = get_session_registry(ua_sess);
+ if (!registry) {
+ DBG("Application session is being torn down. Abort event notify");
+ ret = 0;
+ goto error;
+ }
+
+ if (ua_sess->buffer_type == LTTNG_BUFFER_PER_UID) {
+ map_reg_key = ua_map->tracing_map_id;
+ } else {
+ map_reg_key = ua_map->key;
+ }
+
+ 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.
+ */
+ DBG("Registry_map_create_event on map=%"PRIu64" with token=%"PRIu64,
+ map_reg_key, tracer_token);
+ ret_code = ust_registry_map_create_event(registry, map_reg_key,
+ sobjd, cobjd, name, sig, nr_fields, fields,
+ loglevel_value, model_emf_uri, ua_sess->buffer_type,
+ tracer_token, &counter_index, ua);
+ assert(!ret_code);
+
+ sig = NULL;
+ fields = NULL;
+ model_emf_uri = NULL;
+
+ /*
+ * The return value is returned to ustctl so in case of an error, the
+ * application can be notified. In case of an error, it's important not to
+ * return a negative error or else the application will get closed.
+ */
+ ret = ustctl_reply_register_event(sock, counter_index, counter_index,
+ ret_code);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app reply event failed with ret %d", ret);
+ } else {
+ DBG3("UST app reply event failed. Application died");
+ }
+ /*
+ * No need to wipe the create event since the application socket will
+ * get close on error hence cleaning up everything by itself.
+ */
+ goto error;
+ }
+
+ DBG3("UST registry map event %s with counter index %" PRIu64 " added successfully",
+ name, counter_index);
+
+error:
+ pthread_mutex_unlock(®istry->lock);
+ return ret;
+}
+
+
+/*
+ * Add event to the UST channel registry. When the event is added to the
+ * registry, the metadata is also created. Once done, this replies to the
+ * application with the appropriate error code.
+ *
+ * The session UST registry lock is acquired in the function.
+ *
+ * On success 0 is returned else a negative value.
+ */
+static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name,
+ char *sig, size_t nr_fields, struct ustctl_field *fields,
+ int loglevel_value, char *model_emf_uri, uint64_t tracer_token)
+{
+ int ret;
+ struct ust_app *app;
+ struct ust_app_channel *ua_chan = NULL;
+ struct ust_app_map *ua_map = NULL;
+
+ rcu_read_lock();
+
+ /* Lookup application. If not found, there is a code flow error. */
+ app = find_app_by_notify_sock(sock);
+ if (!app) {
+ DBG("Application socket %d is being torn down. Abort event notify",
+ sock);
+ ret = 0;
+ goto end;
+ }
+
+ /* Lookup channel by UST object descriptor. */
+ ua_chan = find_channel_by_objd(app, cobjd);
+ if (ua_chan) {
+ ret = add_event_ust_chan_registry(sock, app, ua_chan, sobjd, cobjd,
+ name, sig, nr_fields, fields, loglevel_value,
+ model_emf_uri);
+ if (ret) {
+ ERR("Error adding channel event to registry: event_name = '%s'", name);
+ }
+ goto found;
+ }
+
+ /* Lookup map by UST object descriptor. */
+ ua_map = find_map_by_objd(app, cobjd);
+ if (ua_map) {
+ ret = add_event_ust_map_registry(sock, app, ua_map, sobjd, cobjd,
+ name, sig, nr_fields, fields, loglevel_value,
+ model_emf_uri, tracer_token);
+ if (ret) {
+ ERR("Error adding map event to registry: event_name = '%s'", name);
+ goto end;
+ }
+ goto found;
+ }
+
+ if (!ua_chan && !ua_map) {
+ DBG("Application event container is being torn down. Abort event notify");
+ ret = 0;
+ goto end;
+ }
+
+found:
+ ret = 0;
+
+end:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Add enum to the UST session registry. Once done, this replies to the
+ * application with the appropriate error code.
+ *
+ * The session UST registry lock is acquired within this function.
+ *
+ * On success 0 is returned else a negative value.
+ */
+static int add_enum_ust_registry(int sock, int sobjd, char *name,
+ struct ustctl_enum_entry *entries, size_t nr_entries)
+{
+ int ret = 0, ret_code;
+ struct ust_app *app;
+ struct ust_app_session *ua_sess;
+ struct ust_registry_session *registry;
+ uint64_t enum_id = -1ULL;
+
+ rcu_read_lock();
+
+ /* Lookup application. If not found, there is a code flow error. */
+ app = find_app_by_notify_sock(sock);
+ if (!app) {
+ /* Return an error since this is not an error */
+ DBG("Application socket %d is being torn down. Aborting enum registration",
+ sock);
+ free(entries);
+ goto error_rcu_unlock;
+ }
+
+ /* Lookup session by UST object descriptor. */
+ ua_sess = find_session_by_objd(app, sobjd);
+ if (!ua_sess) {
+ /* Return an error since this is not an error */
+ DBG("Application session is being torn down (session not found). Aborting enum registration.");
+ free(entries);
+ goto error_rcu_unlock;
+ }
+
+ registry = get_session_registry(ua_sess);
+ if (!registry) {
+ DBG("Application session is being torn down (registry not found). Aborting enum registration.");
+ free(entries);
+ goto error_rcu_unlock;
+ }
+
+ pthread_mutex_lock(®istry->lock);
+
+ /*
+ * From this point on, the callee acquires the ownership of
+ * entries. The variable entries MUST NOT be read/written after
+ * call.
+ */
+ ret_code = ust_registry_create_or_find_enum(registry, sobjd, name,
+ entries, nr_entries, &enum_id);
+ entries = NULL;
+
+ /*
+ * The return value is returned to ustctl so in case of an error, the
+ * application can be notified. In case of an error, it's important not to
+ * return a negative error or else the application will get closed.
+ */
+ ret = ustctl_reply_register_enum(sock, enum_id, ret_code);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app reply enum failed with ret %d", ret);
+ } else {
+ DBG3("UST app reply enum failed. Application died");
+ }
+ /*
+ * No need to wipe the create enum since the application socket will
+ * get close on error hence cleaning up everything by itself.
+ */
+ goto error;
+ }
+
+ DBG3("UST registry enum %s added successfully or already found", name);
+
+error:
+ pthread_mutex_unlock(®istry->lock);
+error_rcu_unlock:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Handle application notification through the given notify socket.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int ust_app_recv_notify(int sock)
+{
+ int ret;
+ enum ustctl_notify_cmd cmd;
+
+ DBG3("UST app receiving notify from sock %d", sock);
+
+ ret = ustctl_recv_notify(sock, &cmd);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app recv notify failed with ret %d", ret);
+ } else {
+ DBG3("UST app recv notify failed. Application died");
+ }
+ goto error;
+ }
+
+ switch (cmd) {
+ case USTCTL_NOTIFY_CMD_EVENT:
+ {
+ int sobjd, cobjd, loglevel_value;
+ char name[LTTNG_UST_SYM_NAME_LEN], *sig, *model_emf_uri;
+ size_t nr_fields;
+ uint64_t tracer_token = 0;
+ struct ustctl_field *fields;
+
+ DBG2("UST app ustctl register event received");
+
+ ret = ustctl_recv_register_event(sock, &sobjd, &cobjd, name,
+ &loglevel_value, &sig, &nr_fields, &fields,
+ &model_emf_uri, &tracer_token);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app recv event failed with ret %d", ret);
+ } else {
+ DBG3("UST app recv event failed. Application died");
+ }
+ goto error;
+ }
+
+ /*
+ * 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_value, model_emf_uri, tracer_token);
+ if (ret < 0) {
+ goto error;
+ }
+
+ break;
+ }
+ case USTCTL_NOTIFY_CMD_CHANNEL:
+ {
+ int sobjd, cobjd;
+ size_t nr_fields;
+ struct ustctl_field *fields;
+
+ DBG2("UST app ustctl register channel received");
+
+ ret = ustctl_recv_register_channel(sock, &sobjd, &cobjd, &nr_fields,
+ &fields);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app recv channel failed with ret %d", ret);
+ } else {
+ DBG3("UST app recv channel failed. Application died");
+ }
+ 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, cobjd, nr_fields,
+ fields);
+ if (ret < 0) {
+ goto error;
+ }
+
+ break;
+ }
+ case USTCTL_NOTIFY_CMD_ENUM:
+ {
+ int sobjd;
+ char name[LTTNG_UST_SYM_NAME_LEN];
+ size_t nr_entries;
+ struct ustctl_enum_entry *entries;
+
+ DBG2("UST app ustctl register enum received");
+
+ ret = ustctl_recv_register_enum(sock, &sobjd, name,
+ &entries, &nr_entries);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app recv enum failed with ret %d", ret);
+ } else {
+ DBG3("UST app recv enum failed. Application died");
+ }
+ goto error;
+ }
+
+ /* Callee assumes ownership of entries */
+ ret = add_enum_ust_registry(sock, sobjd, name,
+ entries, nr_entries);
+ if (ret < 0) {
+ goto error;
+ }
+
+ break;
+ }
+ default:
+ /* Should NEVER happen. */
+ assert(0);
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * Once the notify socket hangs up, this is called. First, it tries to find the
+ * corresponding application. On failure, the call_rcu to close the socket is
+ * executed. If an application is found, it tries to delete it from the notify
+ * socket hash table. Whathever the result, it proceeds to the call_rcu.
+ *
+ * Note that an object needs to be allocated here so on ENOMEM failure, the
+ * call RCU is not done but the rest of the cleanup is.
+ */
+void ust_app_notify_sock_unregister(int sock)
+{
+ int err_enomem = 0;
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+ struct ust_app_notify_sock_obj *obj;
+
+ assert(sock >= 0);
+
+ rcu_read_lock();
+
+ obj = zmalloc(sizeof(*obj));
+ if (!obj) {
+ /*
+ * An ENOMEM is kind of uncool. If this strikes we continue the
+ * procedure but the call_rcu will not be called. In this case, we
+ * accept the fd leak rather than possibly creating an unsynchronized
+ * state between threads.
+ *
+ * TODO: The notify object should be created once the notify socket is
+ * registered and stored independantely from the ust app object. The
+ * tricky part is to synchronize the teardown of the application and
+ * this notify object. Let's keep that in mind so we can avoid this
+ * kind of shenanigans with ENOMEM in the teardown path.
+ */
+ err_enomem = 1;
+ } else {
+ obj->fd = sock;
+ }
+
+ DBG("UST app notify socket unregister %d", sock);
+
+ /*
+ * Lookup application by notify socket. If this fails, this means that the
+ * hash table delete has already been done by the application
+ * unregistration process so we can safely close the notify socket in a
+ * call RCU.
+ */
+ app = find_app_by_notify_sock(sock);
+ if (!app) {
+ goto close_socket;
+ }
+
+ iter.iter.node = &app->notify_sock_n.node;
+
+ /*
+ * Whatever happens here either we fail or succeed, in both cases we have
+ * to close the socket after a grace period to continue to the call RCU
+ * here. If the deletion is successful, the application is not visible
+ * anymore by other threads and is it fails it means that it was already
+ * deleted from the hash table so either way we just have to close the
+ * socket.
+ */
+ (void) lttng_ht_del(ust_app_ht_by_notify_sock, &iter);
+
+close_socket:
+ rcu_read_unlock();
+
+ /*
+ * Close socket after a grace period to avoid for the socket to be reused
+ * before the application object is freed creating potential race between
+ * threads trying to add unique in the global hash table.
+ */
+ if (!err_enomem) {
+ 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.
+ *
+ * Returns LTTNG_OK on success or a LTTNG_ERR error code.
+ */
+enum lttng_error_code ust_app_snapshot_record(
+ const struct ltt_ust_session *usess,
+ const struct consumer_output *output, int wait,
+ uint64_t nb_packets_per_stream)
+{
+ int ret = 0;
+ enum lttng_error_code status = LTTNG_OK;
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+ char *trace_path = NULL;
+
+ assert(usess);
+ assert(output);
+
+ rcu_read_lock();
+
+ 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 *buf_reg_chan;
+ struct consumer_socket *socket;
+ char pathname[PATH_MAX];
+ size_t consumer_path_offset = 0;
+
+ if (!reg->registry->reg.ust->metadata_key) {
+ /* Skip since no metadata is present */
+ continue;
+ }
+
+ /* Get consumer socket to use to push the metadata.*/
+ socket = consumer_find_socket_by_bitness(reg->bits_per_long,
+ usess->consumer);
+ if (!socket) {
+ status = LTTNG_ERR_INVALID;
+ 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");
+ status = LTTNG_ERR_INVALID;
+ goto error;
+ }
+ /* Free path allowed on previous iteration. */
+ free(trace_path);
+ trace_path = setup_channel_trace_path(usess->consumer, pathname,
+ &consumer_path_offset);
+ if (!trace_path) {
+ status = LTTNG_ERR_INVALID;
+ goto error;
+ }
+ /* Add the UST default trace dir to path. */
+ cds_lfht_for_each_entry(reg->registry->channels->ht, &iter.iter,
+ buf_reg_chan, node.node) {
+ status = consumer_snapshot_channel(socket,
+ buf_reg_chan->consumer_key,
+ output, 0, usess->uid,
+ usess->gid, &trace_path[consumer_path_offset], wait,
+ nb_packets_per_stream);
+ if (status != LTTNG_OK) {
+ goto error;
+ }
+ }
+ status = consumer_snapshot_channel(socket,
+ reg->registry->reg.ust->metadata_key, output, 1,
+ usess->uid, usess->gid, &trace_path[consumer_path_offset],
+ wait, 0);
+ if (status != LTTNG_OK) {
+ 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;
+ char pathname[PATH_MAX];
+ size_t consumer_path_offset = 0;
+
+ 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);
+ if (!socket) {
+ status = LTTNG_ERR_INVALID;
+ 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) {
+ status = LTTNG_ERR_INVALID;
+ PERROR("snprintf snapshot path");
+ goto error;
+ }
+ /* Free path allowed on previous iteration. */
+ free(trace_path);
+ trace_path = setup_channel_trace_path(usess->consumer, pathname,
+ &consumer_path_offset);
+ if (!trace_path) {
+ status = LTTNG_ERR_INVALID;
+ goto error;
+ }
+ cds_lfht_for_each_entry(ua_sess->channels->ht, &chan_iter.iter,
+ ua_chan, node.node) {
+ status = consumer_snapshot_channel(socket,
+ ua_chan->key, output, 0,
+ lttng_credentials_get_uid(&ua_sess->effective_credentials),
+ lttng_credentials_get_gid(&ua_sess->effective_credentials),
+ &trace_path[consumer_path_offset], wait,
+ nb_packets_per_stream);
+ switch (status) {
+ case LTTNG_OK:
+ break;
+ case LTTNG_ERR_CHAN_NOT_FOUND:
+ continue;
+ default:
+ goto error;
+ }
+ }
+
+ registry = get_session_registry(ua_sess);
+ if (!registry) {
+ DBG("Application session is being torn down. Skip application.");
+ continue;
+ }
+ status = consumer_snapshot_channel(socket,
+ registry->metadata_key, output, 1,
+ lttng_credentials_get_uid(&ua_sess->effective_credentials),
+ lttng_credentials_get_gid(&ua_sess->effective_credentials),
+ &trace_path[consumer_path_offset], wait, 0);
+ switch (status) {
+ case LTTNG_OK:
+ break;
+ case LTTNG_ERR_CHAN_NOT_FOUND:
+ continue;
+ default:
+ goto error;
+ }
+ }
+ break;