Add CTF enum type support for UST registry
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Sun, 10 Jan 2016 16:50:47 +0000 (11:50 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Thu, 14 Jan 2016 16:31:55 +0000 (11:31 -0500)
Derived from initial implementation by:
Geneviève Bastien <gbastien+lttng@versatic.net>

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
16 files changed:
configure.ac
doc/man/lttng.1
src/bin/lttng-sessiond/lttng-ust-ctl.h
src/bin/lttng-sessiond/ust-app.c
src/bin/lttng-sessiond/ust-app.h
src/bin/lttng-sessiond/ust-metadata.c
src/bin/lttng-sessiond/ust-registry.c
src/bin/lttng-sessiond/ust-registry.h
tests/regression/ust/Makefile.am
tests/regression/ust/type-declarations/Makefile.am [new file with mode: 0644]
tests/regression/ust/type-declarations/README [new file with mode: 0644]
tests/regression/ust/type-declarations/test_type_declarations [new file with mode: 0755]
tests/regression/ust/type-declarations/test_type_declarations.py [new file with mode: 0644]
tests/regression/ust/type-declarations/type-declarations.c [new file with mode: 0644]
tests/regression/ust/type-declarations/ust_tests_td.h [new file with mode: 0644]
tests/with_bindings_regression

index 5e959a7898a39b327afcec9b300460c0d22386b4..693c456a620d482e38d28bb84c1b74f43afc490d 100644 (file)
@@ -843,6 +843,7 @@ AC_CONFIG_FILES([
        tests/regression/ust/python-logging/Makefile
        tests/regression/ust/getcpu-override/Makefile
        tests/regression/ust/clock-override/Makefile
+       tests/regression/ust/type-declarations/Makefile
        tests/stress/Makefile
        tests/unit/Makefile
        tests/unit/ini_config/Makefile
index db5a7c9255a5df045e22796b2d3779e65962cf82..7aef3d57a4cd88e5adfafecc637243a042e190ea 100644 (file)
@@ -607,6 +607,7 @@ Expression examples:
   'intfield > 500 && intfield < 503'
   '(strfield == "test" || intfield != 10) && intfield > 33'
   'doublefield > 1.1 && intfield < 5.3'
+  'enumfield == 1234'
 .fi
 
 Wildcards are allowed at the end of strings:
@@ -614,7 +615,8 @@ Wildcards are allowed at the end of strings:
 In string literals, the escape character is a '\\'. Use '\\*' for
 the '*' character, and '\\\\' for the '\\' character sequence. Wildcard
 matches any sequence of characters, including an empty sub-string
-(matches 0 or more characters).
+(matches 0 or more characters). Enumeration fields can currently only be
+compared as integers.
 
 Context information can be used for filtering. The examples below shows
 usage of context filtering on the process name (using a wildcard), process ID
index b3de5a9ba61846f557ec39b0df4cecd1b486bca1..73a9244187aa9c6aef8536749439cd6482e6228b 100644 (file)
@@ -227,6 +227,7 @@ enum ustctl_socket_type {
 enum ustctl_notify_cmd {
        USTCTL_NOTIFY_CMD_EVENT = 0,
        USTCTL_NOTIFY_CMD_CHANNEL = 1,
+       USTCTL_NOTIFY_CMD_ENUM = 2,
 };
 
 enum ustctl_channel_header {
@@ -274,6 +275,13 @@ struct ustctl_float_type {
        char padding[USTCTL_UST_FLOAT_TYPE_PADDING];
 } LTTNG_PACKED;
 
+#define USTCTL_UST_ENUM_ENTRY_PADDING  32
+struct ustctl_enum_entry {
+       uint64_t start, end;            /* start and end are inclusive */
+       char string[LTTNG_UST_SYM_NAME_LEN];
+       char padding[USTCTL_UST_ENUM_ENTRY_PADDING];
+};
+
 #define USTCTL_UST_BASIC_TYPE_PADDING  296
 union _ustctl_basic_type {
        struct ustctl_integer_type integer;
@@ -281,6 +289,9 @@ union _ustctl_basic_type {
                enum ustctl_string_encodings encoding;
        } string;
        struct ustctl_float_type _float;
+       struct {
+               char name[LTTNG_UST_SYM_NAME_LEN];
+       } enumeration;
        char padding[USTCTL_UST_BASIC_TYPE_PADDING];
 } LTTNG_PACKED;
 
@@ -375,6 +386,22 @@ int ustctl_reply_register_event(int sock,
        uint32_t id,                    /* event id (input) */
        int ret_code);                  /* return code. 0 ok, negative error */
 
+/*
+ * Returns 0 on success, negative UST or system error value on error.
+ */
+int ustctl_recv_register_enum(int sock,
+       int *session_objd,
+       char *enum_name,
+       struct ustctl_enum_entry **entries,
+       unsigned int *nr_entries);
+
+/*
+ * Returns 0 on success, negative error value on error.
+ */
+int ustctl_reply_register_enum(int sock,
+       int64_t id,                     /* enum id (input) */
+       int ret_code);
+
 /*
  * Returns 0 on success, negative UST or system error value on error.
  */
index f0af340770ca59bd98ddda1d48e0ffd4d01bd262..2bd48c7f30cf4e4552d7eb85b0ef6b6b557109f3 100644 (file)
@@ -791,7 +791,12 @@ void delete_ust_app_session(int sock, struct ust_app_session *ua_sess,
                        ERR("UST app sock %d release session handle failed with ret %d",
                                        sock, ret);
                }
+               /* Remove session from application UST object descriptor. */
+               iter.iter.node = &ua_sess->ust_objd_node.node;
+               ret = lttng_ht_del(app->ust_sessions_objd, &iter);
+               assert(!ret);
        }
+
        pthread_mutex_unlock(&ua_sess->lock);
 
        consumer_output_put(ua_sess->consumer);
@@ -825,6 +830,7 @@ void delete_ust_app(struct ust_app *app)
        }
 
        ht_cleanup_push(app->sessions);
+       ht_cleanup_push(app->ust_sessions_objd);
        ht_cleanup_push(app->ust_objd);
 
        /*
@@ -2138,6 +2144,9 @@ static int create_ust_app_session(struct ltt_ust_session *usess,
                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);
        }
@@ -3212,6 +3221,7 @@ struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock)
        lta->v_minor = msg->minor;
        lta->sessions = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
        lta->ust_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+       lta->ust_sessions_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
        lta->notify_sock = -1;
 
        /* Copy name and make sure it's NULL terminated. */
@@ -5030,6 +5040,33 @@ 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
@@ -5275,6 +5312,86 @@ error_rcu_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. Aborting enum registration.");
+               free(entries);
+               goto error_rcu_unlock;
+       }
+
+       registry = get_session_registry(ua_sess);
+       assert(registry);
+
+       pthread_mutex_lock(&registry->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(&registry->lock);
+error_rcu_unlock:
+       rcu_read_unlock();
+       return ret;
+}
+
 /*
  * Handle application notification through the given notify socket.
  *
@@ -5365,6 +5482,35 @@ int ust_app_recv_notify(int sock)
 
                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);
index ff0720d18c503518ec485253a225964ef3377dee..3daccba8622bd96a603a895b86d551ff98e2b57e 100644 (file)
@@ -192,6 +192,11 @@ struct ust_app_session {
        uint64_t id;    /* Unique session identifier */
        struct lttng_ht *channels; /* Registered channels */
        struct lttng_ht_node_u64 node;
+       /*
+        * Node indexed by UST session object descriptor (handle). Stored in the
+        * ust_sessions_objd hash table in the ust_app object.
+        */
+       struct lttng_ht_node_ulong ust_objd_node;
        char path[PATH_MAX];
        /* UID/GID of the application owning the session */
        uid_t uid;
@@ -272,6 +277,10 @@ struct ust_app {
         * Hash table containing ust_app_channel indexed by channel objd.
         */
        struct lttng_ht *ust_objd;
+       /*
+        * Hash table containing ust_app_session indexed by objd.
+        */
+       struct lttng_ht *ust_sessions_objd;
 
        /*
         * If this application is of the agent domain and this is non negative then
index 71bcf7e7d325f61764de8832b8877a0b5e7e30ed..984d5f813ccf9277e6e221b7299f8f80a7d50954 100644 (file)
@@ -179,6 +179,102 @@ end:
        return ret;
 }
 
+/* Called with session registry mutex held. */
+static
+int ust_metadata_enum_statedump(struct ust_registry_session *session,
+               const char *enum_name,
+               uint64_t enum_id,
+               const struct ustctl_integer_type *container_type,
+               const char *field_name)
+{
+       struct ust_registry_enum *reg_enum;
+       const struct ustctl_enum_entry *entries;
+       size_t nr_entries;
+       int ret = 0;
+       size_t i;
+
+       rcu_read_lock();
+       reg_enum = ust_registry_lookup_enum_by_id(session, enum_name, enum_id);
+       rcu_read_unlock();
+       /* reg_enum can still be used because session registry mutex is held. */
+       if (!reg_enum) {
+               ret = -ENOENT;
+               goto end;
+       }
+       entries = reg_enum->entries;
+       nr_entries = reg_enum->nr_entries;
+
+       ret = lttng_metadata_printf(session,
+               "               enum : integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u; } {\n",
+               container_type->size,
+               container_type->alignment,
+               container_type->signedness,
+               (container_type->encoding == ustctl_encode_none)
+                       ? "none"
+                       : (container_type->encoding == ustctl_encode_UTF8)
+                               ? "UTF8"
+                               : "ASCII",
+               container_type->base);
+       if (ret) {
+               goto end;
+       }
+       /* Dump all entries */
+       for (i = 0; i < nr_entries; i++) {
+               const struct ustctl_enum_entry *entry = &entries[i];
+               int j, len;
+
+               ret = lttng_metadata_printf(session,
+                               "                       \"");
+               if (ret) {
+                       goto end;
+               }
+               len = strlen(entry->string);
+               /* Escape the character '"' */
+               for (j = 0; j < len; j++) {
+                       char c = entry->string[j];
+
+                       switch (c) {
+                       case '"':
+                               ret = lttng_metadata_printf(session,
+                                               "\\\"");
+                               break;
+                       case '\\':
+                               ret = lttng_metadata_printf(session,
+                                               "\\\\");
+                               break;
+                       default:
+                               ret = lttng_metadata_printf(session,
+                                               "%c", c);
+                               break;
+                       }
+                       if (ret) {
+                               goto end;
+                       }
+               }
+               ret = lttng_metadata_printf(session,
+                               "\" = ");
+               if (ret) {
+                       goto end;
+               }
+               if (entry->start == entry->end) {
+                       ret = lttng_metadata_printf(session,
+                                       "%d,\n",
+                                       entry->start);
+               } else {
+                       ret = lttng_metadata_printf(session,
+                                       "%d ... %d,\n",
+                                       entry->start, entry->end);
+               }
+               if (ret) {
+                       goto end;
+               }
+       }
+       ret = lttng_metadata_printf(session, "          } _%s;\n",
+                       field_name);
+end:
+       return ret;
+}
+
 static
 int _lttng_field_statedump(struct ust_registry_session *session,
                const struct ustctl_field *field)
@@ -210,6 +306,13 @@ int _lttng_field_statedump(struct ust_registry_session *session,
                        field->type.u.basic.integer.reverse_byte_order ? bo_reverse : bo_native,
                        field->name);
                break;
+       case ustctl_atype_enum:
+               ret = ust_metadata_enum_statedump(session,
+                       field->type.u.basic.enumeration.name,
+                       field->type.u.basic.enumeration.id,
+                       &field->type.u.basic.enumeration.container_type,
+                       field->name);
+               break;
        case ustctl_atype_float:
                ret = lttng_metadata_printf(session,
                        "               floating_point { exp_dig = %u; mant_dig = %u; align = %u;%s } _%s;\n",
@@ -219,8 +322,6 @@ int _lttng_field_statedump(struct ust_registry_session *session,
                        field->type.u.basic.integer.reverse_byte_order ? bo_reverse : bo_native,
                        field->name);
                break;
-       case ustctl_atype_enum:
-               return -EINVAL;
        case ustctl_atype_array:
        {
                const struct ustctl_basic_type *elem_type;
index 71fdc4fd8294d48c8f335222b2af2381cb1972e0..80ea2e57054de736ac0801b01d4ed0bb8b0cb6a5 100644 (file)
@@ -73,6 +73,109 @@ static unsigned long ht_hash_event(void *_key, unsigned long seed)
        return hash_key_u64(&xored_key, seed);
 }
 
+static int compare_enums(const struct ust_registry_enum *reg_enum_a,
+               const struct ust_registry_enum *reg_enum_b)
+{
+       int ret = 0;
+       size_t i;
+
+       assert(strcmp(reg_enum_a->name, reg_enum_b->name) == 0);
+       if (reg_enum_a->nr_entries != reg_enum_b->nr_entries) {
+               ret = -1;
+               goto end;
+       }
+       for (i = 0; i < reg_enum_a->nr_entries; i++) {
+               const struct ustctl_enum_entry *entries_a, *entries_b;
+
+               entries_a = &reg_enum_a->entries[i];
+               entries_b = &reg_enum_b->entries[i];
+               if (entries_a->start != entries_b->start) {
+                       ret = -1;
+                       goto end;
+               }
+               if (entries_a->end != entries_b->end) {
+                       ret = -1;
+                       goto end;
+               }
+               if (strcmp(entries_a->string, entries_b->string)) {
+                       ret = -1;
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+/*
+ * Hash table match function for enumerations in the session. Match is
+ * performed on enumeration name, and confirmed by comparing the enum
+ * entries.
+ */
+static int ht_match_enum(struct cds_lfht_node *node, const void *_key)
+{
+       struct ust_registry_enum *_enum;
+       const struct ust_registry_enum *key;
+
+       assert(node);
+       assert(_key);
+
+       _enum = caa_container_of(node, struct ust_registry_enum,
+                       node.node);
+       assert(_enum);
+       key = _key;
+
+       if (strncmp(_enum->name, key->name, LTTNG_UST_SYM_NAME_LEN)) {
+               goto no_match;
+       }
+       if (compare_enums(_enum, key)) {
+               goto no_match;
+       }
+
+       /* Match. */
+       return 1;
+
+no_match:
+       return 0;
+}
+
+/*
+ * Hash table match function for enumerations in the session. Match is
+ * performed by enumeration ID.
+ */
+static int ht_match_enum_id(struct cds_lfht_node *node, const void *_key)
+{
+       struct ust_registry_enum *_enum;
+       const struct ust_registry_enum *key = _key;
+
+       assert(node);
+       assert(_key);
+
+       _enum = caa_container_of(node, struct ust_registry_enum, node.node);
+       assert(_enum);
+
+       if (_enum->id != key->id) {
+               goto no_match;
+       }
+
+       /* Match. */
+       return 1;
+
+no_match:
+       return 0;
+}
+
+/*
+ * Hash table hash function for enumerations in the session. The
+ * enumeration name is used for hashing.
+ */
+static unsigned long ht_hash_enum(void *_key, unsigned long seed)
+{
+       struct ust_registry_enum *key = _key;
+
+       assert(key);
+       return hash_key_str(key->name, seed);
+}
+
 /*
  * Return negative value on error, 0 if OK.
  *
@@ -377,6 +480,175 @@ void ust_registry_destroy_event(struct ust_registry_channel *chan,
        return;
 }
 
+static void destroy_enum(struct ust_registry_enum *reg_enum)
+{
+       if (!reg_enum) {
+               return;
+       }
+       free(reg_enum->entries);
+       free(reg_enum);
+}
+
+static void destroy_enum_rcu(struct rcu_head *head)
+{
+       struct ust_registry_enum *reg_enum =
+               caa_container_of(head, struct ust_registry_enum, rcu_head);
+
+       destroy_enum(reg_enum);
+}
+
+/*
+ * Lookup enumeration by name and comparing enumeration entries.
+ * Needs to be called from RCU read-side critical section.
+ */
+struct ust_registry_enum *
+       ust_registry_lookup_enum(struct ust_registry_session *session,
+               const struct ust_registry_enum *reg_enum_lookup)
+{
+       struct ust_registry_enum *reg_enum = NULL;
+       struct lttng_ht_node_str *node;
+       struct lttng_ht_iter iter;
+
+       cds_lfht_lookup(session->enums->ht,
+                       ht_hash_enum((void *) &reg_enum_lookup, lttng_ht_seed),
+                       ht_match_enum, &reg_enum_lookup, &iter.iter);
+       node = lttng_ht_iter_get_node_str(&iter);
+       if (!node) {
+               goto end;
+       }
+       reg_enum = caa_container_of(node, struct ust_registry_enum, node);
+end:
+       return reg_enum;
+}
+
+/*
+ * Lookup enumeration by enum ID.
+ * Needs to be called from RCU read-side critical section.
+ */
+struct ust_registry_enum *
+       ust_registry_lookup_enum_by_id(struct ust_registry_session *session,
+               const char *enum_name, uint64_t enum_id)
+{
+       struct ust_registry_enum *reg_enum = NULL;
+       struct lttng_ht_node_str *node;
+       struct lttng_ht_iter iter;
+       struct ust_registry_enum reg_enum_lookup;
+
+       memset(&reg_enum_lookup, 0, sizeof(reg_enum_lookup));
+       strncpy(reg_enum_lookup.name, enum_name, LTTNG_UST_SYM_NAME_LEN);
+       reg_enum_lookup.name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
+       reg_enum_lookup.id = enum_id;
+       cds_lfht_lookup(session->enums->ht,
+                       ht_hash_enum((void *) &reg_enum_lookup, lttng_ht_seed),
+                       ht_match_enum_id, &reg_enum_lookup, &iter.iter);
+       node = lttng_ht_iter_get_node_str(&iter);
+       if (!node) {
+               goto end;
+       }
+       reg_enum = caa_container_of(node, struct ust_registry_enum, node);
+end:
+       return reg_enum;
+}
+
+/*
+ * Create a ust_registry_enum from the given parameters and add it to the
+ * registry hash table, or find it if already there.
+ *
+ * On success, return 0 else a negative value.
+ *
+ * Should be called with session registry mutex held.
+ *
+ * We receive ownership of entries.
+ */
+int ust_registry_create_or_find_enum(struct ust_registry_session *session,
+               int session_objd, char *enum_name,
+               struct ustctl_enum_entry *entries, size_t nr_entries,
+               uint64_t *enum_id)
+{
+       int ret = 0;
+       struct cds_lfht_node *nodep;
+       struct ust_registry_enum *reg_enum = NULL, *old_reg_enum;
+
+       assert(session);
+       assert(enum_name);
+
+       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) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       /* Check if the enumeration was already dumped */
+       reg_enum = zmalloc(sizeof(*reg_enum));
+       if (!reg_enum) {
+               PERROR("zmalloc ust registry enumeration");
+               ret = -ENOMEM;
+               goto end;
+       }
+       strncpy(reg_enum->name, enum_name, LTTNG_UST_SYM_NAME_LEN);
+       reg_enum->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
+       /* entries will be owned by reg_enum. */
+       reg_enum->entries = entries;
+       reg_enum->nr_entries = nr_entries;
+       entries = NULL;
+
+       old_reg_enum = ust_registry_lookup_enum(session, reg_enum);
+       if (old_reg_enum) {
+               DBG("enum %s already in sess_objd: %u", enum_name, session_objd);
+               /* Fall through. Use prior enum. */
+               destroy_enum(reg_enum);
+               reg_enum = old_reg_enum;
+       } else {
+               DBG("UST registry creating enum: %s, sess_objd: %u",
+                               enum_name, session_objd);
+               if (session->next_enum_id == -1ULL) {
+                       ret = -EOVERFLOW;
+                       destroy_enum(reg_enum);
+                       goto end;
+               }
+               reg_enum->id = session->next_enum_id++;
+               cds_lfht_node_init(&reg_enum->node.node);
+               nodep = cds_lfht_add_unique(session->enums->ht,
+                               ht_hash_enum(reg_enum, lttng_ht_seed),
+                               ht_match_enum_id, reg_enum,
+                               &reg_enum->node.node);
+               assert(nodep == &reg_enum->node.node);
+       }
+       DBG("UST registry reply with enum %s with id %" PRIu64 " in sess_objd: %u",
+                       enum_name, reg_enum->id, session_objd);
+       *enum_id = reg_enum->id;
+end:
+       free(entries);
+       rcu_read_unlock();
+       return ret;
+}
+
+/*
+ * For a given enumeration in a registry, delete the entry and destroy
+ * the enumeration.
+ * This MUST be called within a RCU read side lock section.
+ */
+void ust_registry_destroy_enum(struct ust_registry_session *reg_session,
+               struct ust_registry_enum *reg_enum)
+{
+       int ret;
+       struct lttng_ht_iter iter;
+
+       assert(reg_session);
+       assert(reg_enum);
+
+       /* Delete the node first. */
+       iter.iter.node = &reg_enum->node.node;
+       ret = lttng_ht_del(reg_session->enums, &iter);
+       assert(!ret);
+       call_rcu(&reg_enum->rcu_head, destroy_enum_rcu);
+}
+
 /*
  * We need to execute ht_destroy outside of RCU read-side critical
  * section and outside of call_rcu thread, so we postpone its execution
@@ -574,6 +846,7 @@ int ust_registry_session_init(struct ust_registry_session **sessionp,
        session->metadata_fd = -1;
        session->uid = euid;
        session->gid = egid;
+       session->next_enum_id = 0;
        strncpy(session->root_shm_path, root_shm_path,
                sizeof(session->root_shm_path));
        session->root_shm_path[sizeof(session->root_shm_path) - 1] = '\0';
@@ -609,6 +882,15 @@ int ust_registry_session_init(struct ust_registry_session **sessionp,
                session->metadata_fd = ret;
        }
 
+       session->enums = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+       if (!session->enums) {
+               ret = -ENOMEM;
+               goto error;
+       }
+       /* hash/match functions are specified at call site. */
+       session->enums->match_fct = NULL;
+       session->enums->hash_fct = NULL;
+
        session->channels = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
        if (!session->channels) {
                goto error;
@@ -648,6 +930,7 @@ void ust_registry_session_destroy(struct ust_registry_session *reg)
        int ret;
        struct lttng_ht_iter iter;
        struct ust_registry_channel *chan;
+       struct ust_registry_enum *reg_enum;
 
        if (!reg) {
                return;
@@ -690,4 +973,15 @@ void ust_registry_session_destroy(struct ust_registry_session *reg)
                (void) run_as_recursive_rmdir(reg->root_shm_path,
                                reg->uid, reg->gid);
        }
+       /* Destroy the enum hash table */
+       if (reg->enums) {
+               rcu_read_lock();
+               /* Destroy all enum entries associated with this registry. */
+               cds_lfht_for_each_entry(reg->enums->ht, &iter.iter, reg_enum,
+                               node.node) {
+                       ust_registry_destroy_enum(reg, reg_enum);
+               }
+               rcu_read_unlock();
+               ht_cleanup_push(reg->enums);
+       }
 }
index b51b505f12994b0b8219b4efb846f32e2f5f5523..a8466ebf58c64c9260968e77a8e0573925f9b959 100644 (file)
@@ -45,6 +45,8 @@ struct ust_registry_session {
        uint32_t next_channel_id;
        /* Once this value reaches UINT32_MAX, no more id can be allocated. */
        uint32_t used_channel_id;
+       /* Next enumeration ID available. */
+       uint64_t next_enum_id;
        /* Universal unique identifier used by the tracer. */
        unsigned char uuid[UUID_LEN];
 
@@ -91,6 +93,9 @@ struct ust_registry_session {
        /* User and group owning the session. */
        uid_t uid;
        gid_t gid;
+
+       /* Enumerations table. */
+       struct lttng_ht *enums;
 };
 
 struct ust_registry_channel {
@@ -155,6 +160,17 @@ struct ust_registry_event {
        struct lttng_ht_node_u64 node;
 };
 
+struct ust_registry_enum {
+       char name[LTTNG_UST_SYM_NAME_LEN];
+       struct ustctl_enum_entry *entries;
+       size_t nr_entries;
+       uint64_t id;    /* enum id in session */
+       /* Enumeration node in session hash table. */
+       struct lttng_ht_node_str node;
+       /* For delayed reclaim. */
+       struct rcu_head rcu_head;
+};
+
 /*
  * Validate that the id has reached the maximum allowed or not.
  *
@@ -260,6 +276,13 @@ int ust_metadata_channel_statedump(struct ust_registry_session *session,
 int ust_metadata_event_statedump(struct ust_registry_session *session,
                struct ust_registry_channel *chan,
                struct ust_registry_event *event);
+int ust_registry_create_or_find_enum(struct ust_registry_session *session,
+               int session_objd, char *name,
+               struct ustctl_enum_entry *entries, size_t nr_entries,
+               uint64_t *enum_id);
+struct ust_registry_enum *
+       ust_registry_lookup_enum_by_id(struct ust_registry_session *session,
+               const char *name, uint64_t id);
 
 #else /* HAVE_LIBLTTNG_UST_CTL */
 
@@ -339,6 +362,21 @@ int ust_metadata_event_statedump(struct ust_registry_session *session,
 {
        return 0;
 }
+static inline
+int ust_registry_create_or_find_enum(struct ust_registry_session *session,
+               int session_objd, char *name,
+               struct ustctl_enum_entry *entries, size_t nr_entries,
+               uint64_t *enum_id)
+{
+       return 0;
+}
+static inline
+struct ust_registry_enum *
+       ust_registry_lookup_enum_by_id(struct ust_registry_session *session,
+               const char *name, uint64_t id)
+{
+       return NULL;
+}
 
 #endif /* HAVE_LIBLTTNG_UST_CTL */
 
index 383570f5ae4fd2578c1efcb25745e03f06a9b3a7..1b87cc9dddac8c553ac1b73804cf95a88cf476b7 100644 (file)
@@ -2,7 +2,7 @@ if HAVE_LIBLTTNG_UST_CTL
 SUBDIRS = nprocesses high-throughput low-throughput before-after multi-session \
                overlap buffers-pid linking daemon exit-fast fork libc-wrapper \
                periodical-metadata-flush java-jul java-log4j python-logging \
-               getcpu-override clock-override
+               getcpu-override clock-override type-declarations
 
 if HAVE_OBJCOPY
 SUBDIRS += baddr-statedump ust-dl
diff --git a/tests/regression/ust/type-declarations/Makefile.am b/tests/regression/ust/type-declarations/Makefile.am
new file mode 100644 (file)
index 0000000..be4978b
--- /dev/null
@@ -0,0 +1,29 @@
+AM_CPPFLAGS = -I$(srcdir)
+
+noinst_PROGRAMS = type-declarations
+type_declarations_SOURCES = type-declarations.c ust_tests_td.h
+type_declarations_LDADD = -llttng-ust
+
+if LTTNG_TOOLS_BUILD_WITH_LIBDL
+type_declarations_LDADD += -ldl
+endif
+if LTTNG_TOOLS_BUILD_WITH_LIBC_DL
+type_declarations_LDADD += -lc
+endif
+
+noinst_SCRIPTS = test_type_declarations test_type_declarations.py
+EXTRA_DIST = test_type_declarations test_type_declarations.py
+
+all-local:
+       @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+               for script in $(EXTRA_DIST); do \
+                       cp -f $(srcdir)/$$script $(builddir); \
+               done; \
+       fi
+
+clean-local:
+       @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+               for script in $(EXTRA_DIST); do \
+                       rm -f $(builddir)/$$script; \
+               done; \
+       fi
diff --git a/tests/regression/ust/type-declarations/README b/tests/regression/ust/type-declarations/README
new file mode 100644 (file)
index 0000000..e81baf4
--- /dev/null
@@ -0,0 +1,22 @@
+Type declarations test
+-----------------------------
+
+This test checks if tracepoints using type declarations work correctly.
+
+DESCRIPTION
+-----------
+
+This test launches a process which generates events with fields using type
+declarations.
+
+The test makes sure the events are present and the fields have all the
+correct data.
+
+DEPENDENCIES
+------------
+
+To run this test, you will need:
+
+  - lttng-tools (with python bindings)
+  - babeltrace
+  - python 3.0 or better
diff --git a/tests/regression/ust/type-declarations/test_type_declarations b/tests/regression/ust/type-declarations/test_type_declarations
new file mode 100755 (executable)
index 0000000..aca2d35
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright (C) - 2014 Geneviève Bastien <gbastien@versatic.net>
+# Copyright (C) - 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+TEST_DESC="UST tracer - Test tracepoints using ctf type declarations"
+
+CURDIR=$(dirname $0)
+TESTDIR=${CURDIR}/../../..
+
+source $TESTDIR/utils/utils.sh
+
+start_lttng_sessiond_notap
+
+python3 ${CURDIR}/test_type_declarations.py
+
+stop_lttng_sessiond_notap
diff --git a/tests/regression/ust/type-declarations/test_type_declarations.py b/tests/regression/ust/type-declarations/test_type_declarations.py
new file mode 100644 (file)
index 0000000..6f2d5ff
--- /dev/null
@@ -0,0 +1,114 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) - 2014 Geneviève Bastien <gbastien@versatic.net>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License, version 2 only, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import os
+import subprocess
+import re
+import shutil
+import sys
+
+test_path = os.path.dirname(os.path.abspath(__file__)) + "/"
+test_utils_path = test_path
+for i in range(4):
+    test_utils_path = os.path.dirname(test_utils_path)
+test_utils_path = test_utils_path + "/utils"
+sys.path.append(test_utils_path)
+from test_utils import *
+
+NR_TESTS = 9
+current_test = 1
+print("1..{0}".format(NR_TESTS))
+
+# Check if a sessiond is running... bail out if none found.
+if session_daemon_alive() == 0:
+    bail("No sessiond running. Please make sure you are running this test with the \"run\" shell script and verify that the lttng tools are properly installed.")
+
+session_info = create_session()
+enable_ust_tracepoint_event(session_info, "ust_tests_td*")
+start_session(session_info)
+
+test_env = os.environ.copy()
+test_env["LTTNG_UST_REGISTER_TIMEOUT"] = "-1"
+
+td_process = subprocess.Popen(test_path + "type-declarations", stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=test_env)
+
+if sys.version_info >= (3, 3):
+    try:
+        td_process.wait(5)
+    except TimeoutExpired:
+        td_process.kill()
+        bail("Failed to run type-declarations test application.")
+else:
+    td_process.wait()
+
+print_test_result(td_process.returncode == 0, current_test, "Test application exited normally")
+current_test += 1
+
+stop_session(session_info)
+
+# Check event fields using type declarations are present
+try:
+    babeltrace_process = subprocess.Popen(["babeltrace", session_info.trace_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+except FileNotFoundError:
+    bail("Could not open babeltrace. Please make sure it is installed.")
+
+event_lines = []
+for event_line in babeltrace_process.stdout:
+    event_line = event_line.decode('utf-8').replace("\n", "")
+    event_lines.append(event_line)
+babeltrace_process.wait()
+
+print_test_result(babeltrace_process.returncode == 0, current_test, "Resulting trace is readable")
+current_test += 1
+
+if babeltrace_process.returncode != 0:
+    bail("Unreadable trace; can't proceed with analysis.")
+
+print_test_result(len(event_lines) == 4, current_test, "Correct number of events found in resulting trace")
+current_test += 1
+
+if len(event_lines) != 4:
+    bail("Unexpected number of events found in resulting trace (" + session_info.trace_path + ")." )
+
+match = re.search(r".*ust_tests_td:(.*):.*enumfield = \( \"(.*)\" :.*enumfield_bis = \( \"(.*)\" :.*enumfield_third = .*:.*", event_lines[0])
+print_test_result(match is not None and match.group(1) == "tptest", current_test,\
+                      "First tracepoint is present")
+current_test += 1
+
+print_test_result(match is not None and match.group(2) == "zero", current_test,\
+                      "First tracepoint's enum value maps to zero")
+current_test += 1
+
+print_test_result(match is not None and match.group(3) == "one", current_test,\
+                      "First tracepoint's second enum value maps to one")
+current_test += 1
+
+match = re.search(r".*ust_tests_td:(.*):.*enumfield = \( \"(.*)\" :.*", event_lines[1])
+print_test_result(match is not None and match.group(1) == "tptest_bis", current_test,\
+                      "Second tracepoint is present")
+current_test += 1
+
+print_test_result(match is not None and match.group(2) == "zero", current_test,\
+                      "Second tracepoint's enum value maps to zero")
+current_test += 1
+
+match = re.search(r".*ust_tests_td:(.*):.*enumfield = \( \"(.*)\" :.*enumfield_bis = \( \"(.*)\" .*", event_lines[2])
+
+print_test_result(match is not None and match.group(2) == "one", current_test,\
+                      "Third tracepoint's enum value maps to one")
+
+shutil.rmtree(session_info.tmp_directory)
diff --git a/tests/regression/ust/type-declarations/type-declarations.c b/tests/regression/ust/type-declarations/type-declarations.c
new file mode 100644 (file)
index 0000000..fae09b7
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 Geneviève Bastien <gbastien@versatic.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+/*
+ * This test generates a few events and exits.
+ */
+
+#include <unistd.h>
+
+#define TRACEPOINT_DEFINE
+#define TRACEPOINT_CREATE_PROBES
+#include "ust_tests_td.h"
+
+int main(int argc, char *argv[])
+{
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               tracepoint(ust_tests_td, tptest, i % 2, (i+1) % 2, i % 21);
+               tracepoint(ust_tests_td, tptest_bis,  i % 2);
+       }
+
+       return 0;
+}
diff --git a/tests/regression/ust/type-declarations/ust_tests_td.h b/tests/regression/ust/type-declarations/ust_tests_td.h
new file mode 100644 (file)
index 0000000..45bccbf
--- /dev/null
@@ -0,0 +1,79 @@
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER ust_tests_td
+
+#if !defined(_TRACEPOINT_UST_TESTS_TD_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_UST_TESTS_TD_H
+
+/*
+ * Copyright (C) 2014 Geneviève Bastien <gbastien@versatic.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <lttng/tracepoint.h>
+
+TRACEPOINT_ENUM(ust_tests_td, testenum,
+       TP_ENUM_VALUES(
+               ctf_enum_value("zero", 0)
+               ctf_enum_value("one", 1)
+       )
+)
+
+TRACEPOINT_ENUM(ust_tests_td, testenum2,
+       TP_ENUM_VALUES(
+               ctf_enum_value("zero", 0)
+               ctf_enum_value("five", 5)
+               ctf_enum_range("ten_to_twenty", 10, 20)
+       )
+)
+
+/*
+ * Enumeration field is used twice to make sure the global type declaration
+ * is entered only once in the metadata file.
+ */
+TRACEPOINT_EVENT(ust_tests_td, tptest,
+       TP_ARGS(int, enumval, int, enumval2, int, enumval3),
+       TP_FIELDS(
+               ctf_enum(ust_tests_td, testenum, int, enumfield, enumval)
+               ctf_enum(ust_tests_td, testenum, long long,
+                               enumfield_bis, enumval2)
+               ctf_enum(ust_tests_td, testenum2, unsigned int,
+                               enumfield_third, enumval3)
+       )
+)
+
+/*
+ * Another tracepoint using the global types to make sure each global type is
+ * entered only once in the metadata file.
+ */
+TRACEPOINT_EVENT(ust_tests_td, tptest_bis,
+       TP_ARGS(int, enumval),
+       TP_FIELDS(
+               ctf_enum(ust_tests_td, testenum, unsigned char,
+                               enumfield, enumval)
+       )
+)
+
+#endif /* _TRACEPOINT_UST_TESTS_TD_H */
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "./ust_tests_td.h"
+
+/* This part must be outside ifdef protection */
+#include <lttng/tracepoint-event.h>
index d4057fb35d3eba7419f1191a184d32004dea861f..b9b70e3d66826c9e7186a72eae8d9437a2f92dd2 100644 (file)
@@ -5,3 +5,4 @@ regression/ust/fork/test_fork
 regression/ust/libc-wrapper/test_libc-wrapper
 regression/ust/baddr-statedump/test_baddr-statedump
 regression/ust/ust-dl/test_ust-dl
+regression/ust/type-declarations/test_type_declarations
\ No newline at end of file
This page took 0.063564 seconds and 4 git commands to generate.