From: Mathieu Desnoyers Date: Sun, 10 Jan 2016 16:50:47 +0000 (-0500) Subject: Add CTF enum type support for UST registry X-Git-Tag: v2.8.0-rc1~194 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=10b56aefc3e1de5cee607994f64c3b8a7d85c391;p=lttng-tools.git Add CTF enum type support for UST registry Derived from initial implementation by: Geneviève Bastien Signed-off-by: Mathieu Desnoyers Signed-off-by: Jérémie Galarneau --- diff --git a/configure.ac b/configure.ac index 5e959a789..693c456a6 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/doc/man/lttng.1 b/doc/man/lttng.1 index db5a7c925..7aef3d57a 100644 --- a/doc/man/lttng.1 +++ b/doc/man/lttng.1 @@ -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 diff --git a/src/bin/lttng-sessiond/lttng-ust-ctl.h b/src/bin/lttng-sessiond/lttng-ust-ctl.h index b3de5a9ba..73a924418 100644 --- a/src/bin/lttng-sessiond/lttng-ust-ctl.h +++ b/src/bin/lttng-sessiond/lttng-ust-ctl.h @@ -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. */ diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index f0af34077..2bd48c7f3 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -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(®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. * @@ -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); diff --git a/src/bin/lttng-sessiond/ust-app.h b/src/bin/lttng-sessiond/ust-app.h index ff0720d18..3daccba86 100644 --- a/src/bin/lttng-sessiond/ust-app.h +++ b/src/bin/lttng-sessiond/ust-app.h @@ -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 diff --git a/src/bin/lttng-sessiond/ust-metadata.c b/src/bin/lttng-sessiond/ust-metadata.c index 71bcf7e7d..984d5f813 100644 --- a/src/bin/lttng-sessiond/ust-metadata.c +++ b/src/bin/lttng-sessiond/ust-metadata.c @@ -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; diff --git a/src/bin/lttng-sessiond/ust-registry.c b/src/bin/lttng-sessiond/ust-registry.c index 71fdc4fd8..80ea2e570 100644 --- a/src/bin/lttng-sessiond/ust-registry.c +++ b/src/bin/lttng-sessiond/ust-registry.c @@ -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 = ®_enum_a->entries[i]; + entries_b = ®_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 *) ®_enum_lookup, lttng_ht_seed), + ht_match_enum, ®_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(®_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 *) ®_enum_lookup, lttng_ht_seed), + ht_match_enum_id, ®_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(®_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, + ®_enum->node.node); + assert(nodep == ®_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 = ®_enum->node.node; + ret = lttng_ht_del(reg_session->enums, &iter); + assert(!ret); + call_rcu(®_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); + } } diff --git a/src/bin/lttng-sessiond/ust-registry.h b/src/bin/lttng-sessiond/ust-registry.h index b51b505f1..a8466ebf5 100644 --- a/src/bin/lttng-sessiond/ust-registry.h +++ b/src/bin/lttng-sessiond/ust-registry.h @@ -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 */ diff --git a/tests/regression/ust/Makefile.am b/tests/regression/ust/Makefile.am index 383570f5a..1b87cc9dd 100644 --- a/tests/regression/ust/Makefile.am +++ b/tests/regression/ust/Makefile.am @@ -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 index 000000000..be4978be3 --- /dev/null +++ b/tests/regression/ust/type-declarations/Makefile.am @@ -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 index 000000000..e81baf4b1 --- /dev/null +++ b/tests/regression/ust/type-declarations/README @@ -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 index 000000000..aca2d353f --- /dev/null +++ b/tests/regression/ust/type-declarations/test_type_declarations @@ -0,0 +1,30 @@ +#!/bin/bash +# +# Copyright (C) - 2014 Geneviève Bastien +# Copyright (C) - 2016 Mathieu Desnoyers +# +# 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 index 000000000..6f2d5ff68 --- /dev/null +++ b/tests/regression/ust/type-declarations/test_type_declarations.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +# +# Copyright (C) - 2014 Geneviève Bastien +# +# 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 index 000000000..fae09b713 --- /dev/null +++ b/tests/regression/ust/type-declarations/type-declarations.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 Geneviève Bastien + * + * 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 + +#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 index 000000000..45bccbfab --- /dev/null +++ b/tests/regression/ust/type-declarations/ust_tests_td.h @@ -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 + * + * 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 + +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 diff --git a/tests/with_bindings_regression b/tests/with_bindings_regression index d4057fb35..b9b70e3d6 100644 --- a/tests/with_bindings_regression +++ b/tests/with_bindings_regression @@ -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