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
'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:
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
enum ustctl_notify_cmd {
USTCTL_NOTIFY_CMD_EVENT = 0,
USTCTL_NOTIFY_CMD_CHANNEL = 1,
+ USTCTL_NOTIFY_CMD_ENUM = 2,
};
enum ustctl_channel_header {
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;
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;
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.
*/
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);
}
ht_cleanup_push(app->sessions);
+ ht_cleanup_push(app->ust_sessions_objd);
ht_cleanup_push(app->ust_objd);
/*
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);
}
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. */
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
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.
*
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);
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;
* 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
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)
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",
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;
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.
*
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
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';
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;
int ret;
struct lttng_ht_iter iter;
struct ust_registry_channel *chan;
+ struct ust_registry_enum *reg_enum;
if (!reg) {
return;
(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);
+ }
}
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];
/* User and group owning the session. */
uid_t uid;
gid_t gid;
+
+ /* Enumerations table. */
+ struct lttng_ht *enums;
};
struct ust_registry_channel {
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.
*
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 */
{
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 */
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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+#!/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
--- /dev/null
+#!/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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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>
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