From ea9a44f03f3f6e144b5f7f312eee2a5f7a849ecb Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Fri, 17 Aug 2018 13:25:58 -0400 Subject: [PATCH] Build a list of triggers applying to a given session on creation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit A hash table associating session names (which are unique) to a lttng_session_trigger_list object is added to the notification subsystem. This hash table is populated on the creation of a session and a list is initialized with matching triggers known at that time. Signed-off-by: Jérémie Galarneau --- .../notification-thread-events.c | 248 +++++++++++++++++- .../notification-thread-internal.h | 1 + src/bin/lttng-sessiond/notification-thread.c | 14 + src/bin/lttng-sessiond/notification-thread.h | 21 +- 4 files changed, 277 insertions(+), 7 deletions(-) diff --git a/src/bin/lttng-sessiond/notification-thread-events.c b/src/bin/lttng-sessiond/notification-thread-events.c index f437d018b..bddede988 100644 --- a/src/bin/lttng-sessiond/notification-thread-events.c +++ b/src/bin/lttng-sessiond/notification-thread-events.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -58,10 +59,53 @@ struct lttng_trigger_list_element { struct lttng_channel_trigger_list { struct channel_key channel_key; + /* List of struct lttng_trigger_list_element. */ struct cds_list_head list; + /* Node in the channel_triggers_ht */ struct cds_lfht_node channel_triggers_ht_node; }; +/* + * List of triggers applying to a given session. + * + * See: + * - lttng_session_trigger_list_create() + * - lttng_session_trigger_list_build() + * - lttng_session_trigger_list_destroy() + * - lttng_session_trigger_list_add() + */ +struct lttng_session_trigger_list { + /* + * Not owned by this; points to the session_info structure's + * session name. + */ + const char *session_name; + /* List of struct lttng_trigger_list_element. */ + struct cds_list_head list; + /* Node in the session_triggers_ht */ + struct cds_lfht_node session_triggers_ht_node; + /* + * Weak reference to the notification system's session triggers + * hashtable. + * + * The session trigger list structure structure is owned by + * the session's session_info. + * + * The session_info is kept alive the the channel_infos holding a + * reference to it (reference counting). When those channels are + * destroyed (at runtime or on teardown), the reference they hold + * to the session_info are released. On destruction of session_info, + * session_info_destroy() will remove the list of triggers applying + * to this session from the notification system's state. + * + * This implies that the session_triggers_ht must be destroyed + * after the channels. + */ + struct cds_lfht *session_triggers_ht; + /* Used for delayed RCU reclaim. */ + struct rcu_head rcu_node; +}; + struct lttng_trigger_ht_element { struct lttng_trigger *trigger; struct cds_lfht_node node; @@ -174,6 +218,7 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger, uid_t channel_uid, gid_t channel_gid); +/* session_info API */ static void session_info_destroy(void *_data); static @@ -182,7 +227,8 @@ static void session_info_put(struct session_info *session_info); static struct session_info *session_info_create(const char *name, - uid_t uid, gid_t gid); + uid_t uid, gid_t gid, + struct lttng_session_trigger_list *trigger_list); static void session_info_add_channel(struct session_info *session_info, struct channel_info *channel_info); @@ -190,6 +236,23 @@ static void session_info_remove_channel(struct session_info *session_info, struct channel_info *channel_info); +/* lttng_session_trigger_list API */ +static +struct lttng_session_trigger_list *lttng_session_trigger_list_create( + const char *session_name, + struct cds_lfht *session_triggers_ht); +static +struct lttng_session_trigger_list *lttng_session_trigger_list_build( + const struct notification_thread_state *state, + const char *session_name); +static +void lttng_session_trigger_list_destroy( + struct lttng_session_trigger_list *list); +static +int lttng_session_trigger_list_add(struct lttng_session_trigger_list *list, + const struct lttng_trigger *trigger); + + static int match_client(struct cds_lfht_node *node, const void *key) { @@ -414,6 +477,7 @@ void session_info_destroy(void *_data) ERR("[notification-thread] Failed to destroy channel information hash table"); } } + lttng_session_trigger_list_destroy(session_info->trigger_list); free(session_info->name); free(session_info); } @@ -437,7 +501,8 @@ void session_info_put(struct session_info *session_info) } static -struct session_info *session_info_create(const char *name, uid_t uid, gid_t gid) +struct session_info *session_info_create(const char *name, uid_t uid, gid_t gid, + struct lttng_session_trigger_list *trigger_list) { struct session_info *session_info; @@ -462,6 +527,7 @@ struct session_info *session_info_create(const char *name, uid_t uid, gid_t gid) } session_info->uid = uid; session_info->gid = gid; + session_info->trigger_list = trigger_list; end: return session_info; error: @@ -1001,6 +1067,166 @@ int match_session(struct cds_lfht_node *node, const void *key) return !strcmp(session_info->name, name); } +/* + * Allocate an empty lttng_session_trigger_list for the session named + * 'session_name'. + * + * No ownership of 'session_name' is assumed by the session trigger list. + * It is the caller's responsability to ensure the session name is alive + * for as long as this list is. + */ +static +struct lttng_session_trigger_list *lttng_session_trigger_list_create( + const char *session_name, + struct cds_lfht *session_triggers_ht) +{ + struct lttng_session_trigger_list *list; + + list = zmalloc(sizeof(*list)); + if (!list) { + goto end; + } + list->session_name = session_name; + CDS_INIT_LIST_HEAD(&list->list); + cds_lfht_node_init(&list->session_triggers_ht_node); + list->session_triggers_ht = session_triggers_ht; + + rcu_read_lock(); + /* Publish the list through the session_triggers_ht. */ + cds_lfht_add(session_triggers_ht, + hash_key_str(session_name, lttng_ht_seed), + &list->session_triggers_ht_node); + rcu_read_unlock(); +end: + return list; +} + +static +void free_session_trigger_list_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct lttng_session_trigger_list, + rcu_node)); +} + +static +void lttng_session_trigger_list_destroy(struct lttng_session_trigger_list *list) +{ + struct lttng_trigger_list_element *trigger_list_element, *tmp; + + /* Empty the list element by element, and then free the list itself. */ + cds_list_for_each_entry_safe(trigger_list_element, tmp, + &list->list, node) { + cds_list_del(&trigger_list_element->node); + free(trigger_list_element); + } + rcu_read_lock(); + /* Unpublish the list from the session_triggers_ht. */ + cds_lfht_del(list->session_triggers_ht, + &list->session_triggers_ht_node); + rcu_read_unlock(); + call_rcu(&list->rcu_node, free_session_trigger_list_rcu); +} + +static +int lttng_session_trigger_list_add(struct lttng_session_trigger_list *list, + const struct lttng_trigger *trigger) +{ + int ret = 0; + struct lttng_trigger_list_element *new_element = + zmalloc(sizeof(*new_element)); + + if (!new_element) { + ret = -1; + goto end; + } + CDS_INIT_LIST_HEAD(&new_element->node); + new_element->trigger = trigger; + cds_list_add(&new_element->node, &list->list); +end: + return ret; +} + +static +bool trigger_applies_to_session(const struct lttng_trigger *trigger, + const char *session_name) +{ + bool applies = false; + const struct lttng_condition *condition; + + condition = lttng_trigger_get_const_condition(trigger); + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + { + enum lttng_condition_status condition_status; + const char *condition_session_name; + + condition_status = lttng_condition_session_rotation_get_session_name( + condition, &condition_session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + ERR("[notification-thread] Failed to retrieve session rotation condition's session name"); + goto end; + } + + assert(condition_session_name); + applies = !strcmp(condition_session_name, session_name); + break; + } + default: + goto end; + } +end: + return applies; +} + +/* + * Allocate and initialize an lttng_session_trigger_list which contains + * all triggers that apply to the session named 'session_name'. + * + * No ownership of 'session_name' is assumed by the session trigger list. + * It is the caller's responsability to ensure the session name is alive + * for as long as this list is. + */ +static +struct lttng_session_trigger_list *lttng_session_trigger_list_build( + const struct notification_thread_state *state, + const char *session_name) +{ + int trigger_count = 0; + struct lttng_session_trigger_list *session_trigger_list = NULL; + struct lttng_trigger_ht_element *trigger_ht_element = NULL; + struct cds_lfht_iter iter; + + session_trigger_list = lttng_session_trigger_list_create(session_name, + state->session_triggers_ht); + + /* Add all triggers applying to the session named 'session_name'. */ + cds_lfht_for_each_entry(state->triggers_ht, &iter, trigger_ht_element, + node) { + int ret; + + if (!trigger_applies_to_session(trigger_ht_element->trigger, + session_name)) { + continue; + } + + ret = lttng_session_trigger_list_add(session_trigger_list, + trigger_ht_element->trigger); + if (ret) { + goto error; + } + + trigger_count++; + } + + DBG("[notification-thread] Found %i triggers that apply to newly created session", + trigger_count); + return session_trigger_list; +error: + lttng_session_trigger_list_destroy(session_trigger_list); + return NULL; +} + static struct session_info *find_or_create_session_info( struct notification_thread_state *state, @@ -1009,6 +1235,7 @@ struct session_info *find_or_create_session_info( struct session_info *session = NULL; struct cds_lfht_node *node; struct cds_lfht_iter iter; + struct lttng_session_trigger_list *trigger_list; rcu_read_lock(); cds_lfht_lookup(state->sessions_ht, @@ -1024,21 +1251,30 @@ struct session_info *find_or_create_session_info( sessions_ht_node); assert(session->uid == uid); assert(session->gid == gid); - goto end; + goto error; } - session = session_info_create(name, uid, gid); + trigger_list = lttng_session_trigger_list_build(state, name); + if (!trigger_list) { + goto error; + } + + session = session_info_create(name, uid, gid, trigger_list); if (!session) { ERR("[notification-thread] Failed to allocation session info for session \"%s\" (uid = %i, gid = %i)", name, uid, gid); - goto end; + goto error; } + trigger_list = NULL; cds_lfht_add(state->sessions_ht, hash_key_str(name, lttng_ht_seed), &session->sessions_ht_node); -end: rcu_read_unlock(); return session; +error: + rcu_read_unlock(); + session_info_put(session); + return NULL; } static diff --git a/src/bin/lttng-sessiond/notification-thread-internal.h b/src/bin/lttng-sessiond/notification-thread-internal.h index b135f128c..75d052b07 100644 --- a/src/bin/lttng-sessiond/notification-thread-internal.h +++ b/src/bin/lttng-sessiond/notification-thread-internal.h @@ -38,6 +38,7 @@ struct session_info { * the value is of type (struct channel_info *). */ struct cds_lfht *channel_infos_ht; + struct lttng_session_trigger_list *trigger_list; /* Node in the notification thread state's sessions_ht. */ struct cds_lfht_node sessions_ht_node; uint64_t consumed_data_size; diff --git a/src/bin/lttng-sessiond/notification-thread.c b/src/bin/lttng-sessiond/notification-thread.c index a8acd6f24..4ce38e180 100644 --- a/src/bin/lttng-sessiond/notification-thread.c +++ b/src/bin/lttng-sessiond/notification-thread.c @@ -359,6 +359,14 @@ void fini_thread_state(struct notification_thread_state *state) ret = cds_lfht_destroy(state->sessions_ht, NULL); assert(!ret); } + /* + * Must be destroyed after all channels have been destroyed. + * See comment in struct lttng_session_trigger_list. + */ + if (state->session_triggers_ht) { + ret = cds_lfht_destroy(state->session_triggers_ht, NULL); + assert(!ret); + } if (state->notification_channel_socket >= 0) { notification_channel_socket_destroy( state->notification_channel_socket); @@ -407,6 +415,12 @@ int init_thread_state(struct notification_thread_handle *handle, goto error; } + state->session_triggers_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, + CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); + if (!state->session_triggers_ht) { + goto error; + } + state->channel_state_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); if (!state->channel_state_ht) { diff --git a/src/bin/lttng-sessiond/notification-thread.h b/src/bin/lttng-sessiond/notification-thread.h index 2169b2efb..d223789c7 100644 --- a/src/bin/lttng-sessiond/notification-thread.h +++ b/src/bin/lttng-sessiond/notification-thread.h @@ -67,12 +67,22 @@ struct notification_thread_handle { * - channel_triggers_ht: * associates a channel key to a list of * struct lttng_trigger_list_nodes. The triggers in this list are - * those that have conditions that apply to this channel. + * those that have conditions that apply to a particular channel. * A channel entry is only created when a channel is added; the * list of triggers applying to such a channel is built at that * moment. * This hash table owns the list, but not the triggers themselves. * + * - session_triggers_ht: + * associates a session name to a list of + * struct lttng_trigger_list_nodes. The triggers in this list are + * those that have conditions that apply to a particular session. + * A session entry is only created when a session is created; the + * list of triggers applying to this new session is built at that + * moment. This happens at the time of creation of a session_info. + * Likewise, the list is destroyed at the time of the session_info's + * destruction. + * * - channel_state_ht: * associates a pair (channel key, channel domain) to its last * sampled state received from the consumer daemon @@ -123,12 +133,18 @@ struct notification_thread_handle { * triggers which apply to this new channel, * - triggers identified are added to the channel_triggers_ht. * - add channel to channels_ht + * - if it is the first channel of a session, a session_info is created and + * added to the sessions_ht. A list of the triggers associated with that + * session is built, and it is added to session_triggers_ht. * * 2) Destruction of a tracing channel * - remove entry from channel_triggers_ht, releasing the list wrapper and * elements, * - remove entry from the channel_state_ht. * - remove channel from channels_ht + * - if it was the last known channel of a session, the session_info + * structure is torndown, which in return destroys the list of triggers + * applying to that session. * * 3) Registration of a trigger * - if the trigger's action is of type "notify", @@ -137,6 +153,7 @@ struct notification_thread_handle { * - add list of clients (even if it is empty) to the * notification_trigger_clients_ht, * - add trigger to channel_triggers_ht (if applicable), + * - add trigger to session_triggers_ht (if applicable), * - add trigger to triggers_ht * - evaluate the trigger's condition right away to react if that condition * is true from the beginning. @@ -145,6 +162,7 @@ struct notification_thread_handle { * - if the trigger's action is of type "notify", * - remove the trigger from the notification_trigger_clients_ht, * - remove trigger from channel_triggers_ht (if applicable), + * - remove trigger from session_triggers_ht (if applicable), * - remove trigger from triggers_ht * * 5) Reception of a channel monitor sample from the consumer daemon @@ -179,6 +197,7 @@ struct notification_thread_state { struct lttng_poll_event events; struct cds_lfht *client_socket_ht; struct cds_lfht *channel_triggers_ht; + struct cds_lfht *session_triggers_ht; struct cds_lfht *channel_state_ht; struct cds_lfht *notification_trigger_clients_ht; struct cds_lfht *channels_ht; -- 2.34.1