From e6c12e3dd164fcab19db5985f8729e3d1767571c Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Tue, 6 Dec 2011 14:58:27 -0500 Subject: [PATCH] Add wildcard support Signed-off-by: Mathieu Desnoyers --- include/lttng/ust-events.h | 38 +++++++ liblttng-ust/ltt-events.c | 70 +++++++++++++ liblttng-ust/ltt-probes.c | 196 +++++++++++++++++++++++++++++++++++ liblttng-ust/lttng-ust-abi.c | 100 +++++++++++++++++- 4 files changed, 403 insertions(+), 1 deletion(-) diff --git a/include/lttng/ust-events.h b/include/lttng/ust-events.h index 7f5cff84..b74a1767 100644 --- a/include/lttng/ust-events.h +++ b/include/lttng/ust-events.h @@ -216,6 +216,31 @@ struct loglevel_entry { char name[0]; }; +/* + * Entry describing a per-session active wildcard, along with the event + * attribute and channel information configuring the events that need to + * be enabled. + */ +struct session_wildcard { + struct ltt_channel *chan; + struct lttng_ctx *ctx; /* TODO */ + struct lttng_ust_event event_param; + struct cds_list_head events; /* list of events enabled */ + struct cds_list_head list; /* per-session list of wildcards */ + struct cds_list_head session_list; + struct wildcard_entry *entry; + unsigned int enabled:1; +}; + +/* + * Entry describing an active wildcard (per name) for all sessions. + */ +struct wildcard_entry { + struct cds_list_head list; + struct cds_list_head session_list; + char name[0]; +}; + struct lttng_event_desc { const char *name; void *probe_callback; @@ -265,6 +290,7 @@ struct ltt_event { } u; struct cds_list_head list; /* Event list */ struct cds_list_head loglevel_list; /* Event list for loglevel */ + struct cds_list_head wildcard_list; /* Event list for wildcard */ struct ust_pending_probe *pending_probe; unsigned int metadata_dumped:1; }; @@ -342,6 +368,7 @@ struct ltt_session { struct cds_list_head chan; /* Channel list head */ struct cds_list_head events; /* Event list head */ struct cds_list_head loglevels; /* Loglevel list head */ + struct cds_list_head wildcards; /* Wildcard list head */ struct cds_list_head list; /* Session list */ unsigned int free_chan_id; /* Next chan ID to allocate */ uuid_t uuid; /* Trace session unique ID */ @@ -433,4 +460,15 @@ int ltt_loglevel_create(struct ltt_channel *chan, struct lttng_ust_event *event_param, struct session_loglevel **sl); +struct wildcard_entry *match_wildcard(const char *name); +struct session_wildcard *add_wildcard(const char *name, + struct ltt_channel *chan, + struct lttng_ust_event *event_param); +void _remove_wildcard(struct session_wildcard *wildcard); +int ltt_wildcard_enable(struct session_wildcard *wildcard); +int ltt_wildcard_disable(struct session_wildcard *wildcard); +int ltt_wildcard_create(struct ltt_channel *chan, + struct lttng_ust_event *event_param, + struct session_wildcard **sl); + #endif /* _LTTNG_UST_EVENTS_H */ diff --git a/liblttng-ust/ltt-events.c b/liblttng-ust/ltt-events.c index 7882f6d2..9650e8b0 100644 --- a/liblttng-ust/ltt-events.c +++ b/liblttng-ust/ltt-events.c @@ -75,6 +75,7 @@ struct ust_pending_probe { static void _ltt_event_destroy(struct ltt_event *event); static void _ltt_loglevel_destroy(struct session_loglevel *sl); +static void _ltt_wildcard_destroy(struct session_wildcard *sw); static void _ltt_channel_destroy(struct ltt_channel *chan); static int _ltt_event_unregister(struct ltt_event *event); static @@ -176,6 +177,38 @@ int pending_probe_fix_events(const struct lttng_event_desc *desc) } } + /* Wildcard */ + { + struct wildcard_entry *wildcard; + + wildcard = match_wildcard(desc->name); + if (strcmp(desc->name, "lttng_ust:metadata") && wildcard) { + struct session_wildcard *sw; + + cds_list_for_each_entry(sw, &wildcard->session_list, + session_list) { + struct ltt_event *ev; + int ret; + + memcpy(&event_param, &sw->event_param, + sizeof(event_param)); + memcpy(event_param.name, + desc->name, + sizeof(event_param.name)); + /* create event */ + ret = ltt_event_create(sw->chan, + &event_param, NULL, + &ev); + if (ret) { + DBG("Error creating event"); + continue; + } + cds_list_add(&ev->wildcard_list, + &sw->events); + } + } + } + head = &pending_probe_table[hash & (PENDING_PROBE_HASH_SIZE - 1)]; cds_hlist_for_each_entry_safe(e, node, p, head, node) { struct ltt_event *event; @@ -216,6 +249,7 @@ struct ltt_session *ltt_session_create(void) CDS_INIT_LIST_HEAD(&session->chan); CDS_INIT_LIST_HEAD(&session->events); CDS_INIT_LIST_HEAD(&session->loglevels); + CDS_INIT_LIST_HEAD(&session->wildcards); uuid_generate(session->uuid); cds_list_add(&session->list, &sessions); return session; @@ -226,6 +260,7 @@ void ltt_session_destroy(struct ltt_session *session) struct ltt_channel *chan, *tmpchan; struct ltt_event *event, *tmpevent; struct session_loglevel *loglevel, *tmploglevel; + struct session_wildcard *wildcard, *tmpwildcard; int ret; CMM_ACCESS_ONCE(session->active) = 0; @@ -236,6 +271,8 @@ void ltt_session_destroy(struct ltt_session *session) synchronize_trace(); /* Wait for in-flight events to complete */ cds_list_for_each_entry_safe(loglevel, tmploglevel, &session->loglevels, list) _ltt_loglevel_destroy(loglevel); + cds_list_for_each_entry_safe(wildcard, tmpwildcard, &session->wildcards, list) + _ltt_wildcard_destroy(wildcard); cds_list_for_each_entry_safe(event, tmpevent, &session->events, list) _ltt_event_destroy(event); cds_list_for_each_entry_safe(chan, tmpchan, &session->chan, list) @@ -413,6 +450,26 @@ void _ltt_loglevel_destroy(struct session_loglevel *sl) _remove_loglevel(sl); } +int ltt_wildcard_create(struct ltt_channel *chan, + struct lttng_ust_event *event_param, + struct session_wildcard **_sw) +{ + struct session_wildcard *sw; + + sw = add_wildcard(event_param->name, chan, event_param); + if (!sw || IS_ERR(sw)) { + return PTR_ERR(sw); + } + *_sw = sw; + return 0; +} + +static +void _ltt_wildcard_destroy(struct session_wildcard *sw) +{ + _remove_wildcard(sw); +} + /* * Supports event creation while tracing session is active. */ @@ -810,6 +867,19 @@ int _ltt_event_metadata_statedump(struct ltt_session *session, if (ret) goto end; + if (event->desc->loglevel) { + const struct tracepoint_loglevel_entry *ll_entry; + + ll_entry = *event->desc->loglevel; + ret = lttng_metadata_printf(session, + " loglevel.identifier = \"%s\";\n" + " loglevel.value = %lld;\n", + ll_entry->identifier, + (long long) ll_entry->value); + if (ret) + goto end; + } + if (event->ctx) { ret = lttng_metadata_printf(session, " context := struct {\n"); diff --git a/liblttng-ust/ltt-probes.c b/liblttng-ust/ltt-probes.c index b683b81b..9667497a 100644 --- a/liblttng-ust/ltt-probes.c +++ b/liblttng-ust/ltt-probes.c @@ -34,6 +34,12 @@ static CDS_LIST_HEAD(probe_list); #define LOGLEVEL_TABLE_SIZE (1 << LOGLEVEL_HASH_BITS) static struct cds_hlist_head loglevel_table[LOGLEVEL_TABLE_SIZE]; +/* + * Wildcard list, containing the active wildcards. + * Protected by ust lock. + */ +static CDS_LIST_HEAD(wildcard_list); + static const struct lttng_probe_desc *find_provider(const char *provider) { @@ -416,3 +422,193 @@ int ltt_loglevel_disable(struct session_loglevel *loglevel) loglevel->enabled = 0; return 0; } + +/* WILDCARDS */ + +/* + * Return wildcard for a given event name if the event name match the + * one of the wildcards. + * Must be called with ust lock held. + * Returns NULL if not present. + */ +struct wildcard_entry *match_wildcard(const char *name) +{ + struct wildcard_entry *e; + + cds_list_for_each_entry(e, &wildcard_list, list) { + /* If only contain '*' */ + if (strlen(e->name) == 1) + return e; + /* Compare excluding final '*' */ + if (!strncmp(name, e->name, strlen(e->name) - 1)) + return e; + } + return NULL; +} + +/* + * marshall all probes/all events and create those that fit the + * wildcard. Add them to the events list as created. + */ +static +void _probes_create_wildcard_events(struct wildcard_entry *entry, + struct session_wildcard *wildcard) +{ + struct lttng_probe_desc *probe_desc; + struct lttng_ust_event event_param; + int i; + + cds_list_for_each_entry(probe_desc, &probe_list, head) { + for (i = 0; i < probe_desc->nr_events; i++) { + const struct lttng_event_desc *event_desc; + int match = 0; + + event_desc = probe_desc->event_desc[i]; + /* compare excluding final '*' */ + assert(strlen(entry->name) > 0); + if (strcmp(event_desc->name, "lttng_ust:metadata") + && (strlen(entry->name) == 1 + || !strncmp(event_desc->name, entry->name, + strlen(entry->name) - 1))) { + match = 1; + } + if (match) { + struct ltt_event *ev; + int ret; + + memcpy(&event_param, &wildcard->event_param, + sizeof(event_param)); + memcpy(event_param.name, + event_desc->name, + sizeof(event_param.name)); + /* create event */ + ret = ltt_event_create(wildcard->chan, + &event_param, NULL, + &ev); + if (ret) { + DBG("Error creating event"); + continue; + } + cds_list_add(&ev->wildcard_list, + &wildcard->events); + } + } + } +} + +/* + * Add the wildcard to the wildcard hash table. Must be called with + * ust lock held. + */ +struct session_wildcard *add_wildcard(const char *name, + struct ltt_channel *chan, + struct lttng_ust_event *event_param) +{ + struct wildcard_entry *e; + struct session_wildcard *sw; + size_t name_len = strlen(name) + 1; + int found = 0; + + /* wildcard entry */ + cds_list_for_each_entry(e, &wildcard_list, list) { + if (!strcmp(name, e->name)) { + found = 1; + break; + } + } + + if (!found) { + /* + * Using zmalloc here to allocate a variable length element. Could + * cause some memory fragmentation if overused. + */ + e = zmalloc(sizeof(struct wildcard_entry) + name_len); + if (!e) + return ERR_PTR(-ENOMEM); + memcpy(&e->name[0], name, name_len); + cds_list_add(&e->list, &wildcard_list); + CDS_INIT_LIST_HEAD(&e->session_list); + } + + /* session wildcard */ + cds_list_for_each_entry(sw, &e->session_list, session_list) { + if (chan == sw->chan) { + DBG("wildcard %s busy for this channel", name); + return ERR_PTR(-EEXIST); /* Already there */ + } + } + sw = zmalloc(sizeof(struct session_wildcard)); + if (!sw) + return ERR_PTR(-ENOMEM); + sw->chan = chan; + sw->enabled = 1; + memcpy(&sw->event_param, event_param, sizeof(sw->event_param)); + sw->event_param.instrumentation = LTTNG_UST_TRACEPOINT; + CDS_INIT_LIST_HEAD(&sw->events); + cds_list_add(&sw->list, &chan->session->wildcards); + cds_list_add(&sw->session_list, &e->session_list); + sw->entry = e; + _probes_create_wildcard_events(e, sw); + return sw; +} + +/* + * Remove the wildcard from the wildcard hash table. Must be called with + * ust_lock held. Only called at session teardown. + */ +void _remove_wildcard(struct session_wildcard *wildcard) +{ + struct ltt_event *ev, *tmp; + + /* + * Just remove the events owned (for enable/disable) by this + * wildcard from the list. The session teardown will take care + * of freeing the event memory. + */ + cds_list_for_each_entry_safe(ev, tmp, &wildcard->events, list) { + cds_list_del(&ev->list); + } + cds_list_del(&wildcard->session_list); + cds_list_del(&wildcard->list); + if (cds_list_empty(&wildcard->entry->session_list)) { + cds_list_del(&wildcard->entry->list); + free(wildcard->entry); + } + free(wildcard); +} + +int ltt_wildcard_enable(struct session_wildcard *wildcard) +{ + struct ltt_event *ev; + int ret; + + if (wildcard->enabled) + return -EEXIST; + cds_list_for_each_entry(ev, &wildcard->events, list) { + ret = ltt_event_enable(ev); + if (ret) { + DBG("Error: enable error.\n"); + return ret; + } + } + wildcard->enabled = 1; + return 0; +} + +int ltt_wildcard_disable(struct session_wildcard *wildcard) +{ + struct ltt_event *ev; + int ret; + + if (!wildcard->enabled) + return -EEXIST; + cds_list_for_each_entry(ev, &wildcard->events, list) { + ret = ltt_event_disable(ev); + if (ret) { + DBG("Error: disable error.\n"); + return ret; + } + } + wildcard->enabled = 0; + return 0; +} diff --git a/liblttng-ust/lttng-ust-abi.c b/liblttng-ust/lttng-ust-abi.c index 25f213bb..716a2cc9 100644 --- a/liblttng-ust/lttng-ust-abi.c +++ b/liblttng-ust/lttng-ust-abi.c @@ -204,6 +204,7 @@ static const struct lttng_ust_objd_ops lttng_channel_ops; static const struct lttng_ust_objd_ops lttng_metadata_ops; static const struct lttng_ust_objd_ops lttng_event_ops; static const struct lttng_ust_objd_ops lttng_loglevel_ops; +static const struct lttng_ust_objd_ops lttng_wildcard_ops; static const struct lttng_ust_objd_ops lib_ring_buffer_objd_ops; static const struct lttng_ust_objd_ops lttng_tracepoint_list_ops; @@ -697,6 +698,44 @@ objd_error: return ret; } +static +int lttng_abi_create_wildcard(int channel_objd, + struct lttng_ust_event *event_param) +{ + struct ltt_channel *channel = objd_private(channel_objd); + struct session_wildcard *wildcard; + int wildcard_objd, ret; + + event_param->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; + wildcard_objd = objd_alloc(NULL, <tng_wildcard_ops); + if (wildcard_objd < 0) { + ret = wildcard_objd; + goto objd_error; + } + /* + * We tolerate no failure path after wildcard creation. It will + * stay invariant for the rest of the session. + */ + ret = ltt_wildcard_create(channel, event_param, &wildcard); + if (ret < 0) { + goto wildcard_error; + } + objd_set_private(wildcard_objd, wildcard); + /* The wildcard holds a reference on the channel */ + objd_ref(channel_objd); + return wildcard_objd; + +wildcard_error: + { + int err; + + err = lttng_ust_objd_unref(wildcard_objd); + assert(!err); + } +objd_error: + return ret; +} + /** * lttng_channel_cmd - lttng control through object descriptors * @@ -740,7 +779,12 @@ long lttng_channel_cmd(int objd, unsigned int cmd, unsigned long arg) if (event_param->instrumentation == LTTNG_UST_TRACEPOINT_LOGLEVEL) { return lttng_abi_create_loglevel(objd, event_param); } else { - return lttng_abi_create_event(objd, event_param); + if (event_param->name[strlen(event_param->name) - 1] == '*') { + /* If ends with wildcard, create wildcard. */ + return lttng_abi_create_wildcard(objd, event_param); + } else { + return lttng_abi_create_event(objd, event_param); + } } } case LTTNG_UST_CONTEXT: @@ -1003,6 +1047,60 @@ static const struct lttng_ust_objd_ops lttng_loglevel_ops = { .cmd = lttng_loglevel_cmd, }; +/** + * lttng_wildcard_cmd - lttng control through object descriptors + * + * @objd: the object descriptor + * @cmd: the command + * @arg: command arg + * + * This object descriptor implements lttng commands: + * LTTNG_UST_CONTEXT + * Prepend a context field to each record of events of this + * wildcard. + * LTTNG_UST_ENABLE + * Enable recording for these wildcard events (weak enable) + * LTTNG_UST_DISABLE + * Disable recording for these wildcard events (strong disable) + */ +static +long lttng_wildcard_cmd(int objd, unsigned int cmd, unsigned long arg) +{ + struct session_wildcard *wildcard = objd_private(objd); + + switch (cmd) { + case LTTNG_UST_CONTEXT: + return -ENOSYS; /* not implemented yet */ +#if 0 + return lttng_abi_add_context(objd, + (struct lttng_ust_context *) arg, + &wildcard->ctx, wildcard->chan->session); +#endif + case LTTNG_UST_ENABLE: + return ltt_wildcard_enable(wildcard); + case LTTNG_UST_DISABLE: + return ltt_wildcard_disable(wildcard); + default: + return -EINVAL; + } +} + +static +int lttng_wildcard_release(int objd) +{ + struct session_wildcard *wildcard = objd_private(objd); + + if (wildcard) + return lttng_ust_objd_unref(wildcard->chan->objd); + return 0; +} + +/* TODO: filter control ioctl */ +static const struct lttng_ust_objd_ops lttng_wildcard_ops = { + .release = lttng_wildcard_release, + .cmd = lttng_wildcard_cmd, +}; + void lttng_ust_abi_exit(void) { lttng_ust_abi_close_in_progress = 1; -- 2.34.1