+ lttng_ht_add_ulong(ua_chan->ctx, &ua_ctx->node);
+ cds_list_add_tail(&ua_ctx->list, &ua_chan->ctx_list);
+ }
+
+ /* Copy all events from ltt ust channel to ust app channel */
+ cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) {
+ ua_event = find_ust_app_event(ua_chan->events, uevent->attr.name,
+ uevent->filter, uevent->attr.loglevel, uevent->exclusion);
+ if (ua_event == NULL) {
+ DBG2("UST event %s not found on shadow copy channel",
+ uevent->attr.name);
+ ua_event = alloc_ust_app_event(uevent->attr.name, &uevent->attr);
+ if (ua_event == NULL) {
+ continue;
+ }
+ shadow_copy_event(ua_event, uevent);
+ add_unique_ust_app_event(ua_chan, ua_event);
+ }
+ }
+
+ DBG3("UST app shadow copy of channel %s done", ua_chan->name);
+}
+
+/*
+ * Copy data between a UST app session and a regular LTT session.
+ */
+static void shadow_copy_session(struct ust_app_session *ua_sess,
+ struct ltt_ust_session *usess, struct ust_app *app)
+{
+ struct lttng_ht_node_str *ua_chan_node;
+ struct lttng_ht_iter iter;
+ struct ltt_ust_channel *uchan;
+ struct ust_app_channel *ua_chan;
+ time_t rawtime;
+ struct tm *timeinfo;
+ char datetime[16];
+ int ret;
+ char tmp_shm_path[PATH_MAX];
+
+ /* Get date and time for unique app path */
+ time(&rawtime);
+ timeinfo = localtime(&rawtime);
+ strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo);
+
+ DBG2("Shadow copy of session handle %d", ua_sess->handle);
+
+ ua_sess->tracing_id = usess->id;
+ ua_sess->id = get_next_session_id();
+ ua_sess->uid = app->uid;
+ ua_sess->gid = app->gid;
+ ua_sess->euid = usess->uid;
+ ua_sess->egid = usess->gid;
+ ua_sess->buffer_type = usess->buffer_type;
+ ua_sess->bits_per_long = app->bits_per_long;
+
+ /* There is only one consumer object per session possible. */
+ consumer_output_get(usess->consumer);
+ ua_sess->consumer = usess->consumer;
+
+ ua_sess->output_traces = usess->output_traces;
+ ua_sess->live_timer_interval = usess->live_timer_interval;
+ copy_channel_attr_to_ustctl(&ua_sess->metadata_attr,
+ &usess->metadata_attr);
+
+ switch (ua_sess->buffer_type) {
+ case LTTNG_BUFFER_PER_PID:
+ ret = snprintf(ua_sess->path, sizeof(ua_sess->path),
+ DEFAULT_UST_TRACE_PID_PATH "/%s-%d-%s", app->name, app->pid,
+ datetime);
+ break;
+ case LTTNG_BUFFER_PER_UID:
+ ret = snprintf(ua_sess->path, sizeof(ua_sess->path),
+ DEFAULT_UST_TRACE_UID_PATH, ua_sess->uid, app->bits_per_long);
+ break;
+ default:
+ assert(0);
+ goto error;
+ }
+ if (ret < 0) {
+ PERROR("asprintf UST shadow copy session");
+ assert(0);
+ goto error;
+ }
+
+ strncpy(ua_sess->root_shm_path, usess->root_shm_path,
+ sizeof(ua_sess->root_shm_path));
+ ua_sess->root_shm_path[sizeof(ua_sess->root_shm_path) - 1] = '\0';
+ strncpy(ua_sess->shm_path, usess->shm_path,
+ sizeof(ua_sess->shm_path));
+ ua_sess->shm_path[sizeof(ua_sess->shm_path) - 1] = '\0';
+ if (ua_sess->shm_path[0]) {
+ switch (ua_sess->buffer_type) {
+ case LTTNG_BUFFER_PER_PID:
+ ret = snprintf(tmp_shm_path, sizeof(tmp_shm_path),
+ DEFAULT_UST_TRACE_PID_PATH "/%s-%d-%s",
+ app->name, app->pid, datetime);
+ break;
+ case LTTNG_BUFFER_PER_UID:
+ ret = snprintf(tmp_shm_path, sizeof(tmp_shm_path),
+ DEFAULT_UST_TRACE_UID_PATH,
+ app->uid, app->bits_per_long);
+ break;
+ default:
+ assert(0);
+ goto error;
+ }
+ if (ret < 0) {
+ PERROR("sprintf UST shadow copy session");
+ assert(0);
+ goto error;
+ }
+ strncat(ua_sess->shm_path, tmp_shm_path,
+ sizeof(ua_sess->shm_path) - strlen(ua_sess->shm_path) - 1);
+ ua_sess->shm_path[sizeof(ua_sess->shm_path) - 1] = '\0';
+ }
+
+ /* Iterate over all channels in global domain. */
+ cds_lfht_for_each_entry(usess->domain_global.channels->ht, &iter.iter,
+ uchan, node.node) {
+ struct lttng_ht_iter uiter;
+
+ 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) {
+ /* Session exist. Contiuing. */
+ continue;
+ }
+
+ DBG2("Channel %s not found on shadow session copy, creating it",
+ uchan->name);
+ ua_chan = alloc_ust_app_channel(uchan->name, ua_sess, &uchan->attr);
+ if (ua_chan == NULL) {
+ /* malloc failed FIXME: Might want to do handle ENOMEM .. */
+ continue;
+ }
+ shadow_copy_channel(ua_chan, uchan);
+ /*
+ * The concept of metadata channel does not exist on the tracing
+ * registry side of the session daemon so this can only be a per CPU
+ * channel and not metadata.
+ */
+ ua_chan->attr.type = LTTNG_UST_CHAN_PER_CPU;
+
+ lttng_ht_add_unique_str(ua_sess->channels, &ua_chan->node);
+ }
+ return;
+
+error:
+ consumer_output_put(ua_sess->consumer);
+}
+
+/*
+ * Lookup sesison wrapper.
+ */
+static
+void __lookup_session_by_app(struct ltt_ust_session *usess,
+ struct ust_app *app, struct lttng_ht_iter *iter)
+{
+ /* Get right UST app session from app */
+ lttng_ht_lookup(app->sessions, &usess->id, iter);
+}
+
+/*
+ * Return ust app session from the app session hashtable using the UST session
+ * id.
+ */
+static struct ust_app_session *lookup_session_by_app(
+ struct ltt_ust_session *usess, struct ust_app *app)
+{
+ struct lttng_ht_iter iter;
+ struct lttng_ht_node_u64 *node;
+
+ __lookup_session_by_app(usess, app, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
+ if (node == NULL) {
+ goto error;
+ }
+
+ return caa_container_of(node, struct ust_app_session, node);
+
+error:
+ return NULL;
+}
+
+/*
+ * Setup buffer registry per PID for the given session and application. If none
+ * is found, a new one is created, added to the global registry and
+ * initialized. If regp is valid, it's set with the newly created object.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int setup_buffer_reg_pid(struct ust_app_session *ua_sess,
+ struct ust_app *app, struct buffer_reg_pid **regp)
+{
+ int ret = 0;
+ struct buffer_reg_pid *reg_pid;
+
+ assert(ua_sess);
+ assert(app);
+
+ rcu_read_lock();
+
+ reg_pid = buffer_reg_pid_find(ua_sess->id);
+ if (!reg_pid) {
+ /*
+ * This is the create channel path meaning that if there is NO
+ * registry available, we have to create one for this session.
+ */
+ ret = buffer_reg_pid_create(ua_sess->id, ®_pid,
+ ua_sess->root_shm_path, ua_sess->shm_path);
+ if (ret < 0) {
+ goto error;
+ }
+ } else {
+ goto end;
+ }
+
+ /* Initialize registry. */
+ ret = ust_registry_session_init(®_pid->registry->reg.ust, app,
+ app->bits_per_long, app->uint8_t_alignment,
+ app->uint16_t_alignment, app->uint32_t_alignment,
+ app->uint64_t_alignment, app->long_alignment,
+ app->byte_order, app->version.major,
+ app->version.minor, reg_pid->root_shm_path,
+ reg_pid->shm_path,
+ ua_sess->euid, ua_sess->egid);
+ if (ret < 0) {
+ /*
+ * reg_pid->registry->reg.ust is NULL upon error, so we need to
+ * destroy the buffer registry, because it is always expected
+ * that if the buffer registry can be found, its ust registry is
+ * non-NULL.
+ */
+ buffer_reg_pid_destroy(reg_pid);
+ goto error;
+ }
+
+ buffer_reg_pid_add(reg_pid);
+
+ DBG3("UST app buffer registry per PID created successfully");
+
+end:
+ if (regp) {
+ *regp = reg_pid;
+ }
+error:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Setup buffer registry per UID for the given session and application. If none
+ * is found, a new one is created, added to the global registry and
+ * initialized. If regp is valid, it's set with the newly created object.
+ *
+ * Return 0 on success or else a negative value.
+ */
+static int setup_buffer_reg_uid(struct ltt_ust_session *usess,
+ struct ust_app_session *ua_sess,
+ struct ust_app *app, struct buffer_reg_uid **regp)
+{
+ int ret = 0;
+ struct buffer_reg_uid *reg_uid;
+
+ assert(usess);
+ assert(app);
+
+ rcu_read_lock();
+
+ reg_uid = buffer_reg_uid_find(usess->id, app->bits_per_long, app->uid);
+ if (!reg_uid) {
+ /*
+ * This is the create channel path meaning that if there is NO
+ * registry available, we have to create one for this session.
+ */
+ ret = buffer_reg_uid_create(usess->id, app->bits_per_long, app->uid,
+ LTTNG_DOMAIN_UST, ®_uid,
+ ua_sess->root_shm_path, ua_sess->shm_path);
+ if (ret < 0) {
+ goto error;
+ }
+ } else {
+ goto end;
+ }
+
+ /* Initialize registry. */
+ ret = ust_registry_session_init(®_uid->registry->reg.ust, NULL,
+ app->bits_per_long, app->uint8_t_alignment,
+ app->uint16_t_alignment, app->uint32_t_alignment,
+ app->uint64_t_alignment, app->long_alignment,
+ app->byte_order, app->version.major,
+ app->version.minor, reg_uid->root_shm_path,
+ reg_uid->shm_path, usess->uid, usess->gid);
+ if (ret < 0) {
+ /*
+ * reg_uid->registry->reg.ust is NULL upon error, so we need to
+ * destroy the buffer registry, because it is always expected
+ * that if the buffer registry can be found, its ust registry is
+ * non-NULL.
+ */
+ buffer_reg_uid_destroy(reg_uid, NULL);
+ goto error;
+ }
+ /* Add node to teardown list of the session. */
+ cds_list_add(®_uid->lnode, &usess->buffer_reg_uid_list);
+
+ buffer_reg_uid_add(reg_uid);
+
+ DBG3("UST app buffer registry per UID created successfully");
+end:
+ if (regp) {
+ *regp = reg_uid;
+ }
+error:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Create a session on the tracer side for the given app.
+ *
+ * On success, ua_sess_ptr is populated with the session pointer or else left
+ * untouched. If the session was created, is_created is set to 1. On error,
+ * it's left untouched. Note that ua_sess_ptr is mandatory but is_created can
+ * be NULL.
+ *
+ * Returns 0 on success or else a negative code which is either -ENOMEM or
+ * -ENOTCONN which is the default code if the ustctl_create_session fails.
+ */
+static int create_ust_app_session(struct ltt_ust_session *usess,
+ struct ust_app *app, struct ust_app_session **ua_sess_ptr,
+ int *is_created)
+{
+ int ret, created = 0;
+ struct ust_app_session *ua_sess;
+
+ assert(usess);
+ assert(app);
+ assert(ua_sess_ptr);
+
+ health_code_update();
+
+ ua_sess = lookup_session_by_app(usess, app);
+ if (ua_sess == NULL) {
+ DBG2("UST app pid: %d session id %" PRIu64 " not found, creating it",
+ app->pid, usess->id);
+ ua_sess = alloc_ust_app_session(app);
+ if (ua_sess == NULL) {
+ /* Only malloc can failed so something is really wrong */
+ ret = -ENOMEM;
+ goto error;
+ }
+ shadow_copy_session(ua_sess, usess, app);
+ created = 1;
+ }
+
+ switch (usess->buffer_type) {
+ case LTTNG_BUFFER_PER_PID:
+ /* Init local registry. */
+ ret = setup_buffer_reg_pid(ua_sess, app, NULL);
+ if (ret < 0) {
+ delete_ust_app_session(-1, ua_sess, app);
+ goto error;
+ }
+ break;
+ case LTTNG_BUFFER_PER_UID:
+ /* Look for a global registry. If none exists, create one. */
+ ret = setup_buffer_reg_uid(usess, ua_sess, app, NULL);
+ if (ret < 0) {
+ delete_ust_app_session(-1, ua_sess, app);
+ goto error;
+ }
+ break;
+ default:
+ assert(0);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ health_code_update();
+
+ if (ua_sess->handle == -1) {
+ pthread_mutex_lock(&app->sock_lock);
+ ret = ustctl_create_session(app->sock);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("Creating session for app pid %d with ret %d",
+ app->pid, ret);
+ } else {
+ DBG("UST app creating 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. This will get flagged ENOTCONN and the
+ * caller will handle it.
+ */
+ ret = 0;
+ }
+ delete_ust_app_session(-1, ua_sess, app);
+ if (ret != -ENOMEM) {
+ /*
+ * Tracer is probably gone or got an internal error so let's
+ * behave like it will soon unregister or not usable.
+ */
+ ret = -ENOTCONN;
+ }
+ goto error;
+ }
+
+ ua_sess->handle = ret;
+
+ /* Add ust app session to app's HT */
+ lttng_ht_node_init_u64(&ua_sess->node,
+ ua_sess->tracing_id);
+ lttng_ht_add_unique_u64(app->sessions, &ua_sess->node);
+ lttng_ht_node_init_ulong(&ua_sess->ust_objd_node, ua_sess->handle);
+ lttng_ht_add_unique_ulong(app->ust_sessions_objd,
+ &ua_sess->ust_objd_node);
+
+ DBG2("UST app session created successfully with handle %d", ret);
+ }
+
+ *ua_sess_ptr = ua_sess;
+ if (is_created) {
+ *is_created = created;
+ }
+
+ /* Everything went well. */
+ ret = 0;
+
+error:
+ health_code_update();
+ return ret;
+}
+
+/*
+ * Match function for a hash table lookup of ust_app_ctx.
+ *
+ * It matches an ust app context based on the context type and, in the case
+ * of perf counters, their name.
+ */
+static int ht_match_ust_app_ctx(struct cds_lfht_node *node, const void *_key)
+{
+ struct ust_app_ctx *ctx;
+ const struct lttng_ust_context *key;
+
+ assert(node);
+ assert(_key);
+
+ ctx = caa_container_of(node, struct ust_app_ctx, node.node);
+ key = _key;
+
+ /* Context type */
+ if (ctx->ctx.ctx != key->ctx) {
+ goto no_match;
+ }
+
+ /* Check the name in the case of perf thread counters. */
+ if (key->ctx == LTTNG_UST_CONTEXT_PERF_THREAD_COUNTER) {
+ if (strncmp(key->u.perf_counter.name,
+ ctx->ctx.u.perf_counter.name,
+ sizeof(key->u.perf_counter.name))) {
+ goto no_match;
+ }
+ }
+
+ /* Match. */
+ return 1;
+
+no_match:
+ return 0;
+}
+
+/*
+ * Lookup for an ust app context from an lttng_ust_context.
+ *
+ * Must be called while holding RCU read side lock.
+ * Return an ust_app_ctx object or NULL on error.
+ */
+static
+struct ust_app_ctx *find_ust_app_context(struct lttng_ht *ht,
+ struct lttng_ust_context *uctx)
+{
+ struct lttng_ht_iter iter;
+ struct lttng_ht_node_ulong *node;
+ struct ust_app_ctx *app_ctx = NULL;
+
+ assert(uctx);
+ assert(ht);
+
+ /* Lookup using the lttng_ust_context_type and a custom match fct. */
+ cds_lfht_lookup(ht->ht, ht->hash_fct((void *) uctx->ctx, lttng_ht_seed),
+ ht_match_ust_app_ctx, uctx, &iter.iter);
+ node = lttng_ht_iter_get_node_ulong(&iter);
+ if (!node) {
+ goto end;
+ }
+
+ app_ctx = caa_container_of(node, struct ust_app_ctx, node);
+
+end:
+ return app_ctx;
+}
+
+/*
+ * Create a context for the channel on the tracer.
+ *
+ * Called with UST app session lock held and a RCU read side lock.
+ */
+static
+int create_ust_app_channel_context(struct ust_app_session *ua_sess,
+ struct ust_app_channel *ua_chan, struct lttng_ust_context *uctx,
+ struct ust_app *app)
+{
+ int ret = 0;
+ struct ust_app_ctx *ua_ctx;
+
+ DBG2("UST app adding context to channel %s", ua_chan->name);
+
+ ua_ctx = find_ust_app_context(ua_chan->ctx, uctx);
+ if (ua_ctx) {
+ ret = -EEXIST;
+ goto error;
+ }
+
+ ua_ctx = alloc_ust_app_ctx(uctx);
+ if (ua_ctx == NULL) {
+ /* malloc failed */
+ ret = -1;
+ goto error;
+ }
+
+ lttng_ht_node_init_ulong(&ua_ctx->node, (unsigned long) ua_ctx->ctx.ctx);
+ lttng_ht_add_ulong(ua_chan->ctx, &ua_ctx->node);
+ cds_list_add_tail(&ua_ctx->list, &ua_chan->ctx_list);
+
+ ret = create_ust_channel_context(ua_chan, ua_ctx, app);
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * Enable on the tracer side a ust app event for the session and channel.
+ *
+ * Called with UST app session lock held.
+ */
+static
+int enable_ust_app_event(struct ust_app_session *ua_sess,
+ struct ust_app_event *ua_event, struct ust_app *app)
+{
+ int ret;
+
+ ret = enable_ust_event(app, ua_sess, ua_event);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ua_event->enabled = 1;
+
+error:
+ return ret;
+}
+
+/*
+ * Disable on the tracer side a ust app event for the session and channel.
+ */
+static int disable_ust_app_event(struct ust_app_session *ua_sess,
+ struct ust_app_event *ua_event, struct ust_app *app)
+{
+ int ret;
+
+ ret = disable_ust_event(app, ua_sess, ua_event);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ua_event->enabled = 0;
+
+error:
+ return ret;
+}
+
+/*
+ * Lookup ust app channel for session and disable it on the tracer side.
+ */
+static
+int disable_ust_app_channel(struct ust_app_session *ua_sess,
+ struct ust_app_channel *ua_chan, struct ust_app *app)
+{
+ int ret;
+
+ ret = disable_ust_channel(app, ua_sess, ua_chan);
+ if (ret < 0) {
+ goto error;
+ }
+
+ ua_chan->enabled = 0;
+
+error:
+ return ret;
+}
+
+/*
+ * Lookup ust app channel for session and enable it on the tracer side. This
+ * MUST be called with a RCU read side lock acquired.
+ */
+static int enable_ust_app_channel(struct ust_app_session *ua_sess,
+ struct ltt_ust_channel *uchan, struct ust_app *app)
+{
+ int ret = 0;
+ struct lttng_ht_iter iter;
+ struct lttng_ht_node_str *ua_chan_node;
+ struct ust_app_channel *ua_chan;
+
+ lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter);
+ ua_chan_node = lttng_ht_iter_get_node_str(&iter);
+ if (ua_chan_node == NULL) {
+ DBG2("Unable to find channel %s in ust session id %" PRIu64,
+ uchan->name, ua_sess->tracing_id);
+ goto error;