#include <lttng/lttng.h>
#include "ust-registry.h"
+#include "ust-app.h"
+#include "utils.h"
/*
* Hash table match function for event in the registry.
return hash_key_u64(&xored_key, seed);
}
+/*
+ * Return negative value on error, 0 if OK.
+ *
+ * TODO: we could add stricter verification of more types to catch
+ * errors in liblttng-ust implementation earlier than consumption by the
+ * trace reader.
+ */
+static
+int validate_event_field(struct ustctl_field *field,
+ const char *event_name,
+ struct ust_app *app)
+{
+ switch(field->type.atype) {
+ case ustctl_atype_integer:
+ case ustctl_atype_enum:
+ case ustctl_atype_array:
+ case ustctl_atype_sequence:
+ case ustctl_atype_string:
+ break;
+
+ case ustctl_atype_float:
+ switch (field->type.u.basic._float.mant_dig) {
+ case 0:
+ WARN("UST application '%s' (pid: %d) has unknown float mantissa '%u' "
+ "in field '%s', rejecting event '%s'",
+ app->name, app->pid,
+ field->type.u.basic._float.mant_dig,
+ field->name,
+ event_name);
+ return -EINVAL;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ return -ENOENT;
+ }
+ return 0;
+}
+
+static
+int validate_event_fields(size_t nr_fields, struct ustctl_field *fields,
+ const char *event_name, struct ust_app *app)
+{
+ unsigned int i;
+
+ for (i = 0; i < nr_fields; i++) {
+ if (validate_event_field(&fields[i], event_name, app) < 0)
+ return -EINVAL;
+ }
+ return 0;
+}
+
/*
* Allocate event and initialize it. This does NOT set a valid event id from a
* registry.
*/
static struct ust_registry_event *alloc_event(int session_objd,
int channel_objd, char *name, char *sig, size_t nr_fields,
- struct ustctl_field *fields, int loglevel, char *model_emf_uri)
+ struct ustctl_field *fields, int loglevel, char *model_emf_uri,
+ struct ust_app *app)
{
struct ust_registry_event *event = NULL;
+ /*
+ * Ensure that the field content is valid.
+ */
+ if (validate_event_fields(nr_fields, fields, name, app) < 0) {
+ return NULL;
+ }
+
event = zmalloc(sizeof(*event));
if (!event) {
PERROR("zmalloc ust registry event");
int ust_registry_create_event(struct ust_registry_session *session,
uint64_t chan_key, int session_objd, int channel_objd, char *name,
char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel,
- char *model_emf_uri, int buffer_type, uint32_t *event_id_p)
+ char *model_emf_uri, int buffer_type, uint32_t *event_id_p,
+ struct ust_app *app)
{
int ret;
uint32_t event_id;
assert(sig);
assert(event_id_p);
+ rcu_read_lock();
+
/*
* This should not happen but since it comes from the UST tracer, an
* external party, don't assert and simply validate values.
*/
if (session_objd < 0 || channel_objd < 0) {
ret = -EINVAL;
- goto error;
+ goto error_free;
}
- rcu_read_lock();
-
chan = ust_registry_channel_find(session, chan_key);
if (!chan) {
ret = -EINVAL;
- goto error_unlock;
+ goto error_free;
}
/* Check if we've reached the maximum possible id. */
if (ust_registry_is_max_id(chan->used_event_id)) {
ret = -ENOENT;
- goto error_unlock;
+ goto error_free;
}
event = alloc_event(session_objd, channel_objd, name, sig, nr_fields,
- fields, loglevel, model_emf_uri);
+ fields, loglevel, model_emf_uri, app);
if (!event) {
ret = -ENOMEM;
- goto error_unlock;
+ goto error_free;
}
DBG3("UST registry creating event with event: %s, sig: %s, id: %u, "
rcu_read_unlock();
return 0;
+error_free:
+ free(sig);
+ free(fields);
+ free(model_emf_uri);
error_unlock:
rcu_read_unlock();
-error:
destroy_event(event);
return ret;
}
return;
}
+/*
+ * We need to execute ht_destroy outside of RCU read-side critical
+ * section and outside of call_rcu thread, so we postpone its execution
+ * using ht_cleanup_push. It is simpler than to change the semantic of
+ * the many callers of delete_ust_app_session().
+ */
+static
+void destroy_channel_rcu(struct rcu_head *head)
+{
+ struct ust_registry_channel *chan =
+ caa_container_of(head, struct ust_registry_channel, rcu_head);
+
+ if (chan->ht) {
+ ht_cleanup_push(chan->ht);
+ }
+ free(chan->ctx_fields);
+ free(chan);
+}
+
/*
* Destroy every element of the registry and free the memory. This does NOT
* free the registry pointer since it might not have been allocated before so
* it's the caller responsability.
- *
- * This MUST be called within a RCU read side lock section.
*/
static void destroy_channel(struct ust_registry_channel *chan)
{
assert(chan);
+ rcu_read_lock();
/* Destroy all event associated with this registry. */
cds_lfht_for_each_entry(chan->ht->ht, &iter.iter, event, node.node) {
/* Delete the node from the ht and free it. */
ust_registry_destroy_event(chan, event);
}
- lttng_ht_destroy(chan->ht);
-
- free(chan);
+ rcu_read_unlock();
+ call_rcu(&chan->rcu_head, destroy_channel_rcu);
}
/*
if (!chan) {
PERROR("zmalloc ust registry channel");
ret = -ENOMEM;
- goto error;
+ goto error_alloc;
}
chan->ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
lttng_ht_add_unique_u64(session->channels, &chan->node);
rcu_read_unlock();
+ return 0;
+
error:
+ destroy_channel(chan);
+error_alloc:
return ret;
}
{
struct lttng_ht_iter iter;
struct ust_registry_channel *chan;
+ int ret;
assert(session);
rcu_read_lock();
chan = ust_registry_channel_find(session, key);
if (!chan) {
+ rcu_read_unlock();
goto end;
}
iter.iter.node = &chan->node.node;
- lttng_ht_del(session->channels, &iter);
-
+ ret = lttng_ht_del(session->channels, &iter);
+ assert(!ret);
+ rcu_read_unlock();
destroy_channel(chan);
end:
- rcu_read_unlock();
return;
}
uint32_t uint32_t_alignment,
uint32_t uint64_t_alignment,
uint32_t long_alignment,
- int byte_order)
+ int byte_order,
+ uint32_t major,
+ uint32_t minor)
{
int ret;
struct ust_registry_session *session;
assert(sessionp);
- assert(app);
session = zmalloc(sizeof(*session));
if (!session) {
PERROR("zmalloc ust registry session");
- goto error;
+ goto error_alloc;
}
pthread_mutex_init(&session->lock, NULL);
}
pthread_mutex_lock(&session->lock);
- ret = ust_metadata_session_statedump(session, app);
+ ret = ust_metadata_session_statedump(session, app, major, minor);
pthread_mutex_unlock(&session->lock);
if (ret) {
ERR("Failed to generate session metadata (errno = %d)", ret);
return 0;
error:
+ ust_registry_session_destroy(session);
+ free(session);
+error_alloc:
return -1;
}
struct lttng_ht_iter iter;
struct ust_registry_channel *chan;
+ assert(reg);
+
/* On error, EBUSY can be returned if lock. Code flow error. */
ret = pthread_mutex_destroy(®->lock);
assert(!ret);
- rcu_read_lock();
- /* Destroy all event associated with this registry. */
- cds_lfht_for_each_entry(reg->channels->ht, &iter.iter, chan, node.node) {
- /* Delete the node from the ht and free it. */
- ret = lttng_ht_del(reg->channels, &iter);
- assert(!ret);
- destroy_channel(chan);
+ if (reg->channels) {
+ rcu_read_lock();
+ /* Destroy all event associated with this registry. */
+ cds_lfht_for_each_entry(reg->channels->ht, &iter.iter, chan,
+ node.node) {
+ /* Delete the node from the ht and free it. */
+ ret = lttng_ht_del(reg->channels, &iter);
+ assert(!ret);
+ destroy_channel(chan);
+ }
+ rcu_read_unlock();
+ ht_cleanup_push(reg->channels);
}
- lttng_ht_destroy(reg->channels);
- rcu_read_unlock();
free(reg->metadata);
}