From 44760c20f4fc255b63894ca758cf2ee5f253220b Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Tue, 11 Feb 2020 15:59:02 -0500 Subject: [PATCH] sessiond: agent: enable events matching event notifiers MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Per event notifier domain agents -------------------------------- A `struct agent` instance encapsulates the agent events of an agent domain for a given session. In the context of event notifiers, there is no session involved to scope the agent instance. Hence, per-domain instances are maintained to control event-notifiers enabled by triggers. Agent event enable count ------------------------ Agents act as a pre-filter on user space tracer events. In order to honor triggers use event notifiers (on-event-hit conditions), the register/unregister trigger commands are modified to ensure 'agent' domain events are created and enabled/disabled suitably for the user space tracer to be invoked. Note that since agent events are a "filter" before event enablers, an event-rule targetting a ring buffer and an identical event-rule targetting an event notifier can be enabled. The action to take when the event is hit is completely opaque to the agents. In such cases, the same agent event instance needs to be enabled and the current implementation doesn't allow duplicate agent events. Hence, `struct agent_event`'s enabled state is now a counter which accounts for all enabled event-rules that require this agent event to be enabled. The agent event is enabled when one or more event rules matching it are enabled and it is disabled when that count reached zero. To ensure no code checks for the agent_event's enabled state by comparing to '1', a new `AGENT_EVENT_IS_ENABLED` macro is introduced. The existing code using the `enabled` attribute directly is modified to use it. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I363db1e4bd7d7d73c75b8576c6323ee41e31aa00 Depends-on: lttng-ust: I5a800fc92e588c2a6a0e26282b0ad5f31c044479 --- .../lttng/event-rule/event-rule-internal.h | 21 ++ src/bin/lttng-sessiond/agent-thread.c | 13 + src/bin/lttng-sessiond/agent.c | 167 +++++++++++- src/bin/lttng-sessiond/agent.h | 41 ++- src/bin/lttng-sessiond/client.c | 6 + src/bin/lttng-sessiond/cmd.c | 86 ++++++- src/bin/lttng-sessiond/event.c | 240 ++++++++++++++++-- src/bin/lttng-sessiond/event.h | 5 + src/bin/lttng-sessiond/globals.c | 1 + src/bin/lttng-sessiond/main.c | 8 + src/bin/lttng-sessiond/save.c | 2 +- src/bin/lttng-sessiond/ust-app.c | 100 ++++---- src/bin/lttng-sessiond/utils.c | 23 ++ src/bin/lttng-sessiond/utils.h | 2 + src/common/event-rule/event-rule.c | 30 +++ src/common/event-rule/tracepoint.c | 38 +++ 16 files changed, 669 insertions(+), 114 deletions(-) diff --git a/include/lttng/event-rule/event-rule-internal.h b/include/lttng/event-rule/event-rule-internal.h index 31465c769..a8ad34fe8 100644 --- a/include/lttng/event-rule/event-rule-internal.h +++ b/include/lttng/event-rule/event-rule-internal.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,8 @@ typedef enum lttng_event_rule_generate_exclusions_status ( struct lttng_event_exclusion **exclusions); typedef unsigned long (*event_rule_hash_cb)( const struct lttng_event_rule *event_rule); +typedef struct lttng_event *(*event_rule_generate_lttng_event_cb)( + const struct lttng_event_rule *event_rule); struct lttng_event_rule { struct urcu_ref ref; @@ -67,6 +70,7 @@ struct lttng_event_rule { event_rule_get_filter_bytecode_cb get_filter_bytecode; event_rule_generate_exclusions_cb generate_exclusions; event_rule_hash_cb hash; + event_rule_generate_lttng_event_cb generate_lttng_event; }; struct lttng_event_rule_comm { @@ -140,4 +144,21 @@ const char *lttng_event_rule_type_str(enum lttng_event_rule_type type); LTTNG_HIDDEN unsigned long lttng_event_rule_hash(const struct lttng_event_rule *rule); +/* + * This is a compatibility helper allowing us to generate a sessiond-side (not + * communication) `struct lttng_event` object from an event rule. + * + * This effectively bridges older parts of the code using those structures and + * new event-rule based code. + * + * The caller owns the returned object. + */ +LTTNG_HIDDEN +struct lttng_event *lttng_event_rule_generate_lttng_event( + const struct lttng_event_rule *rule); + +/* Test if an event rule targets an agent domain. */ +LTTNG_HIDDEN +bool lttng_event_rule_targets_agent_domain(const struct lttng_event_rule *rule); + #endif /* LTTNG_EVENT_RULE_INTERNAL_H */ diff --git a/src/bin/lttng-sessiond/agent-thread.c b/src/bin/lttng-sessiond/agent-thread.c index 06ef377a3..2015498ff 100644 --- a/src/bin/lttng-sessiond/agent-thread.c +++ b/src/bin/lttng-sessiond/agent-thread.c @@ -59,6 +59,8 @@ static void update_agent_app(const struct agent_app *app) { struct ltt_session *session, *stmp; struct ltt_session_list *list; + struct agent *trigger_agent; + struct lttng_ht_iter iter; list = session_get_list(); assert(list); @@ -82,6 +84,17 @@ static void update_agent_app(const struct agent_app *app) session_unlock(session); session_put(session); } + + rcu_read_lock(); + /* + * We are protected against the addition of new events by the session + * list lock being held. + */ + cds_lfht_for_each_entry (trigger_agents_ht_by_domain->ht, &iter.iter, + trigger_agent, node.node) { + agent_update(trigger_agent, app); + } + rcu_read_unlock(); } /* diff --git a/src/bin/lttng-sessiond/agent.c b/src/bin/lttng-sessiond/agent.c index e5978be5c..84728b782 100644 --- a/src/bin/lttng-sessiond/agent.c +++ b/src/bin/lttng-sessiond/agent.c @@ -11,6 +11,13 @@ #include #include +#include +#include +#include +#include +#include +#include + #include #include @@ -101,7 +108,8 @@ no_match: } /* - * Match function for the events hash table lookup by name and loglevel. + * Match function for the events hash table lookup by name, log level and + * filter expression. */ static int ht_match_event(struct cds_lfht_node *node, const void *_key) @@ -681,7 +689,7 @@ int agent_enable_event(struct agent_event *event, } } - event->enabled = 1; + event->enabled_count++; ret = LTTNG_OK; error: @@ -787,7 +795,17 @@ int agent_disable_event(struct agent_event *event, struct lttng_ht_iter iter; assert(event); - if (!event->enabled) { + if (!AGENT_EVENT_IS_ENABLED(event)) { + goto end; + } + + if (--event->enabled_count != 0) { + /* + * Agent event still enabled. Disable the agent event only when + * all "users" have disabled it (event notifiers, event rules, + * etc.). + */ + ret = LTTNG_OK; goto end; } @@ -806,7 +824,8 @@ int agent_disable_event(struct agent_event *event, } } - event->enabled = 0; + /* event->enabled_count is now 0. */ + assert(!AGENT_EVENT_IS_ENABLED(event)); error: rcu_read_unlock(); @@ -1206,6 +1225,67 @@ void agent_find_events_by_name(const char *name, struct agent *agt, ht_match_event_by_name, &key, &iter->iter); } +/* + * Find the agent event matching a trigger. + * + * RCU read side lock MUST be acquired. It must be held for as long as + * the returned agent_event is used. + * + * Return object if found else NULL. + */ +struct agent_event *agent_find_event_by_trigger( + const struct lttng_trigger *trigger, struct agent *agt) +{ + enum lttng_condition_status c_status; + enum lttng_event_rule_status er_status; + enum lttng_domain_type domain; + const struct lttng_condition *condition; + const struct lttng_event_rule *rule; + const char *name; + const char *filter_expression; + /* Unused when loglevel_type is 'ALL'. */ + int loglevel_value = 0; + enum lttng_loglevel_type loglevel_type; + + assert(agt); + assert(agt->events); + + condition = lttng_trigger_get_const_condition(trigger); + + assert(lttng_condition_get_type(condition) == + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + c_status = lttng_condition_event_rule_get_rule(condition, &rule); + assert(c_status == LTTNG_CONDITION_STATUS_OK); + + assert(lttng_event_rule_get_type(rule) == + LTTNG_EVENT_RULE_TYPE_TRACEPOINT); + + domain = lttng_event_rule_get_domain_type(rule); + assert(domain == LTTNG_DOMAIN_JUL || domain == LTTNG_DOMAIN_LOG4J || + domain == LTTNG_DOMAIN_PYTHON); + + /* Get the event's pattern ('name' in the legacy terminology). */ + er_status = lttng_event_rule_tracepoint_get_pattern(rule, &name); + assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); + + /* Get the internal filter expression. */ + filter_expression = lttng_event_rule_get_filter(rule); + + er_status = lttng_event_rule_tracepoint_get_log_level_type( + rule, &loglevel_type); + assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); + + if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { + er_status = lttng_event_rule_tracepoint_get_log_level( + rule, &loglevel_value); + assert(er_status == LTTNG_EVENT_RULE_STATUS_OK); + } + + return agent_find_event(name, loglevel_type, loglevel_value, + filter_expression, agt); +} + /* * Get the next agent event duplicate by name. This should be called * after a call to agent_find_events_by_name() to iterate on events. @@ -1233,8 +1313,10 @@ void agent_event_next_duplicate(const char *name, * Return object if found else NULL. */ struct agent_event *agent_find_event(const char *name, - enum lttng_loglevel_type loglevel_type, int loglevel_value, - char *filter_expression, struct agent *agt) + enum lttng_loglevel_type loglevel_type, + int loglevel_value, + const char *filter_expression, + struct agent *agt) { struct lttng_ht_node_str *node; struct lttng_ht_iter iter; @@ -1337,14 +1419,8 @@ void agent_destroy(struct agent *agt) */ int agent_app_ht_alloc(void) { - int ret = 0; - agent_apps_ht_by_sock = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - if (!agent_apps_ht_by_sock) { - ret = -1; - } - - return ret; + return agent_apps_ht_by_sock ? 0 : -1; } /* @@ -1422,7 +1498,7 @@ void agent_update(const struct agent *agt, const struct agent_app *app) cds_lfht_for_each_entry(agt->events->ht, &iter.iter, event, node.node) { /* Skip event if disabled. */ - if (!event->enabled) { + if (!AGENT_EVENT_IS_ENABLED(event)) { continue; } @@ -1447,3 +1523,66 @@ void agent_update(const struct agent *agt, const struct agent_app *app) rcu_read_unlock(); } + +/* + * Allocate the per-event notifier domain agent hash table. It is lazily + * populated as domains are used. + */ +int agent_by_event_notifier_domain_ht_create(void) +{ + trigger_agents_ht_by_domain = lttng_ht_new(0, LTTNG_HT_TYPE_U64); + return trigger_agents_ht_by_domain ? 0 : -1; +} + +/* + * Clean-up the per-event notifier domain agent hash table and destroy it. + */ +void agent_by_event_notifier_domain_ht_destroy(void) +{ + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + + if (!trigger_agents_ht_by_domain) { + return; + } + + rcu_read_lock(); + cds_lfht_for_each_entry (trigger_agents_ht_by_domain->ht, &iter.iter, + node, node) { + struct agent *agent = + caa_container_of(node, struct agent, node); + const int ret = lttng_ht_del( + trigger_agents_ht_by_domain, &iter); + + assert(ret == 0); + agent_destroy(agent); + } + + rcu_read_unlock(); + lttng_ht_destroy(trigger_agents_ht_by_domain); +} + +struct agent *agent_find_by_event_notifier_domain( + enum lttng_domain_type domain_type) +{ + struct agent *agt = NULL; + struct lttng_ht_node_u64 *node; + struct lttng_ht_iter iter; + const uint64_t key = (uint64_t) domain_type; + + assert(trigger_agents_ht_by_domain); + + DBG3("Per-event notifier domain agent lookup for domain '%s'", + lttng_domain_type_str(domain_type)); + + lttng_ht_lookup(trigger_agents_ht_by_domain, &key, &iter); + node = lttng_ht_iter_get_node_u64(&iter); + if (!node) { + goto end; + } + + agt = caa_container_of(node, struct agent, node); + +end: + return agt; +} diff --git a/src/bin/lttng-sessiond/agent.h b/src/bin/lttng-sessiond/agent.h index f8e67efda..e82f32627 100644 --- a/src/bin/lttng-sessiond/agent.h +++ b/src/bin/lttng-sessiond/agent.h @@ -24,11 +24,15 @@ */ extern struct lttng_ht *agent_apps_ht_by_sock; +/* + * Hash table that contains the trigger agents by domain */ +extern struct lttng_ht *trigger_agents_ht_by_domain; + struct agent_ht_key { const char *name; int loglevel_value; enum lttng_loglevel_type loglevel_type; - char *filter_expression; + const char *filter_expression; }; /* @@ -68,6 +72,7 @@ struct agent_app { /* * Agent event representation. + * Accesses to this structure are protected by the session list lock. */ struct agent_event { /* Name of the event. */ @@ -76,9 +81,18 @@ struct agent_event { enum lttng_loglevel_type loglevel_type; /* - * Tells if the event is enabled or not on the agent. + * Tells if the event is enabled or not on the agent. While this can be + * implicitly tested as a boolean, it is in fact a reference count and + * the AGENT_EVENT_IS_ENABLED macro should be used to prevent accidental + * comparisons to non-zero literals (e.g. '1'). + * + * Multiple triggers and events can map to the same agent event as it + * is merely a "filter" in front of a user space tracer enabler. + * + * This count is updated to ensure an event is only disabled when all + * matching enablers are disabled. */ - unsigned int enabled:1; + unsigned int enabled_count; /* Hash table node of the agent domain object. */ struct lttng_ht_node_str node; @@ -89,8 +103,12 @@ struct agent_event { struct lttng_event_exclusion *exclusion; }; +#define AGENT_EVENT_IS_ENABLED(agent_event) (!!agent_event->enabled_count) + /* - * Agent object containing events enabled/disabled for it. + * Agent object containing events enabled/disabled for a given domain in a + * scope. The scope is typically a session, but can also be "global" in the + * context of event notifiers: see event_notifiers_find_agent(). */ struct agent { /* @@ -133,8 +151,10 @@ struct agent_event *agent_create_event(const char *name, void agent_add_event(struct agent_event *event, struct agent *agt); struct agent_event *agent_find_event(const char *name, - enum lttng_loglevel_type loglevel_type, int loglevel_value, - char *filter_expression, struct agent *agt); + enum lttng_loglevel_type loglevel_type, + int loglevel_value, + const char *filter_expression, + struct agent *agt); void agent_find_events_by_name(const char *name, struct agent *agt, struct lttng_ht_iter* iter); void agent_event_next_duplicate(const char *name, @@ -167,4 +187,13 @@ void agent_update(const struct agent *agt, const struct agent_app *app); int agent_list_events(struct lttng_event **events, enum lttng_domain_type domain); +struct agent_event *agent_find_event_by_trigger( + const struct lttng_trigger *trigger, struct agent *agt); + +/* Global event notifier per-domain agents. */ +struct agent *agent_find_by_event_notifier_domain( + enum lttng_domain_type domain_type); +void agent_by_event_notifier_domain_ht_destroy(void); +int agent_by_event_notifier_domain_ht_create(void); + #endif /* LTTNG_SESSIOND_AGENT_H */ diff --git a/src/bin/lttng-sessiond/client.c b/src/bin/lttng-sessiond/client.c index 9e4d446ae..09e683a71 100644 --- a/src/bin/lttng-sessiond/client.c +++ b/src/bin/lttng-sessiond/client.c @@ -43,6 +43,7 @@ #include "utils.h" #include "manage-consumer.h" #include "clear.h" +#include "agent-thread.h" static bool is_root; @@ -1078,6 +1079,11 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int *sock, case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_LOG4J: case LTTNG_DOMAIN_PYTHON: + if (!agent_tracing_is_enabled()) { + ret = LTTNG_ERR_AGENT_TRACING_DISABLED; + goto error; + } + /* Fallthrough */ case LTTNG_DOMAIN_UST: { if (!ust_app_supported()) { diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 2ba825da0..d8b063753 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -488,7 +488,7 @@ static int list_lttng_agent_events(struct agent *agt, cds_lfht_for_each_entry ( agt->events->ht, &iter.iter, agent_event, node.node) { struct lttng_event event = { - .enabled = agent_event->enabled, + .enabled = AGENT_EVENT_IS_ENABLED(agent_event), .loglevel = agent_event->loglevel_value, .loglevel_type = agent_event->loglevel_type, }; @@ -4377,7 +4377,7 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c if (ret_code != LTTNG_OK) { DBG("Failed to register trigger to notification thread: trigger name = '%s', trigger owner uid = %d, error code = %d", trigger_name, (int) trigger_owner, ret_code); - goto end_notification_thread; + goto end; } trigger_status = lttng_trigger_get_name(trigger, &trigger_name); @@ -4388,16 +4388,19 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c if (ret_code != LTTNG_OK) { ERR("Failed to determine if event modifies event notifiers: trigger name = '%s', trigger owner uid = %d, error code = %d", trigger_name, (int) trigger_owner, ret_code); - goto end_notification_thread; + goto end; } /* * Synchronize tracers if the trigger adds an event notifier. */ if (must_update_event_notifier) { - if (lttng_trigger_get_underlying_domain_type_restriction( - trigger) == LTTNG_DOMAIN_KERNEL) { + const enum lttng_domain_type trigger_domain = + lttng_trigger_get_underlying_domain_type_restriction(trigger); + switch (trigger_domain) { + case LTTNG_DOMAIN_KERNEL: + { ret_code = kernel_register_event_notifier( trigger, cmd_creds); if (ret_code != LTTNG_OK) { @@ -4413,11 +4416,36 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c (int) trigger_owner, ret_code); } + } + break; + } + case LTTNG_DOMAIN_UST: + ust_app_global_update_all_event_notifier_rules(); + break; + case LTTNG_DOMAIN_NONE: + abort(); + default: + { + /* Agent domains. */ + struct agent *agt = agent_find_by_event_notifier_domain( + trigger_domain); + + if (!agt) { + agt = agent_create(trigger_domain); + if (!agt) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + agent_add(agt, trigger_agents_ht_by_domain); + } + ret_code = trigger_agent_enable(trigger, agt); + if (ret_code != LTTNG_OK) { goto end; } - } else { - ust_app_global_update_all_event_notifier_rules(); + + break; + } } } @@ -4430,9 +4458,7 @@ enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_c */ lttng_trigger_get(trigger); *return_trigger = trigger; - -end_notification_thread: - /* Ownership of trigger was transferred. */ + /* Ownership of trigger was transferred to caller. */ trigger = NULL; end: return ret_code; @@ -4491,15 +4517,49 @@ enum lttng_error_code cmd_unregister_trigger(const struct lttng_credentials *cmd /* * Synchronize tracers if the trigger removes an event notifier. + * Do this even if the trigger unregistration failed to at least stop + * the tracers from producing notifications associated with this + * event notifier. */ if (must_update_event_notifier) { - if (lttng_trigger_get_underlying_domain_type_restriction( - trigger) == LTTNG_DOMAIN_KERNEL) { + const enum lttng_domain_type trigger_domain = + lttng_trigger_get_underlying_domain_type_restriction( + trigger); + switch (trigger_domain) { + case LTTNG_DOMAIN_KERNEL: + { ret_code = kernel_unregister_event_notifier( trigger); - } else { + break; + } + case LTTNG_DOMAIN_UST: ust_app_global_update_all_event_notifier_rules(); + break; + case LTTNG_DOMAIN_NONE: + abort(); + default: + { + /* Agent domains. */ + struct agent *agt = agent_find_by_event_notifier_domain( + trigger_domain); + + if (!agt) { + agt = agent_create(trigger_domain); + if (!agt) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + agent_add(agt, trigger_agents_ht_by_domain); + } + + ret_code = trigger_agent_disable(trigger, agt); + if (ret_code != LTTNG_OK) { + goto end; + } + + break; + } } } diff --git a/src/bin/lttng-sessiond/event.c b/src/bin/lttng-sessiond/event.c index d8abc60f5..40eb26560 100644 --- a/src/bin/lttng-sessiond/event.c +++ b/src/bin/lttng-sessiond/event.c @@ -12,6 +12,10 @@ #include #include +#include +#include +#include +#include #include #include #include @@ -27,6 +31,7 @@ #include "trace-kernel.h" #include "trace-ust.h" #include "agent.h" +#include "utils.h" /* * Add unique UST event based on the event name, filter bytecode and loglevel. @@ -366,6 +371,20 @@ error: return ret; } +static void agent_enable_all(struct agent *agt) +{ + struct agent_event *aevent; + struct lttng_ht_iter iter; + + /* Flag every event as enabled. */ + rcu_read_lock(); + cds_lfht_for_each_entry ( + agt->events->ht, &iter.iter, aevent, node.node) { + aevent->enabled_count++; + } + rcu_read_unlock(); +} + /* * Enable all agent event for a given UST session. * @@ -376,8 +395,6 @@ int event_agent_enable_all(struct ltt_ust_session *usess, struct lttng_filter_bytecode *filter ,char *filter_expression) { int ret; - struct agent_event *aevent; - struct lttng_ht_iter iter; assert(usess); @@ -389,13 +406,7 @@ int event_agent_enable_all(struct ltt_ust_session *usess, goto error; } - /* Flag every event that they are now enabled. */ - rcu_read_lock(); - cds_lfht_for_each_entry(agt->events->ht, &iter.iter, aevent, - node.node) { - aevent->enabled = 1; - } - rcu_read_unlock(); + agent_enable_all(agt); ret = LTTNG_OK; @@ -467,28 +478,17 @@ end: return ret; } -/* - * Enable a single agent event for a given UST session. - * - * Return LTTNG_OK on success or else a LTTNG_ERR* code. - */ -int event_agent_enable(struct ltt_ust_session *usess, - struct agent *agt, struct lttng_event *event, +static int agent_enable(struct agent *agt, + struct lttng_event *event, struct lttng_filter_bytecode *filter, char *filter_expression) { int ret, created = 0; struct agent_event *aevent; - assert(usess); assert(event); assert(agt); - DBG("Event agent enabling %s for session %" PRIu64 " with loglevel type %d " - ", loglevel %d and filter \"%s\"", event->name, - usess->id, event->loglevel_type, event->loglevel, - filter_expression ? filter_expression : "NULL"); - aevent = agent_find_event(event->name, event->loglevel_type, event->loglevel, filter_expression, agt); if (!aevent) { @@ -502,7 +502,7 @@ int event_agent_enable(struct ltt_ust_session *usess, filter = NULL; filter_expression = NULL; created = 1; - assert(!aevent->enabled); + assert(!AGENT_EVENT_IS_ENABLED(aevent)); } if (created && aevent->filter) { @@ -514,7 +514,7 @@ int event_agent_enable(struct ltt_ust_session *usess, } /* Already enabled? */ - if (aevent->enabled) { + if (AGENT_EVENT_IS_ENABLED(aevent)) { ret = LTTNG_OK; goto end; } @@ -542,6 +542,119 @@ end: return ret; } +/* + * Enable a single agent event for a given UST session. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int event_agent_enable(struct ltt_ust_session *usess, + struct agent *agt, + struct lttng_event *event, + struct lttng_filter_bytecode *filter, + char *filter_expression) +{ + assert(usess); + assert(event); + assert(agt); + + DBG("Enabling agent event: event pattern = '%s', session id = %" PRIu64 ", loglevel type = %d, loglevel = %d, filter expression = '%s'", + event->name, usess->id, event->loglevel_type, + event->loglevel, + filter_expression ? filter_expression : "(none)"); + + return agent_enable(agt, event, filter, filter_expression); +} + +/* + * Enable a single agent event for a trigger. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int trigger_agent_enable(const struct lttng_trigger *trigger, struct agent *agt) +{ + int ret; + enum lttng_condition_status c_status; + enum lttng_trigger_status t_status; + enum lttng_domain_type d_type; + const struct lttng_condition *condition; + const struct lttng_event_rule *rule; + const char *filter_expression; + char *filter_expression_copy = NULL; + const struct lttng_filter_bytecode *filter_bytecode; + struct lttng_filter_bytecode *filter_bytecode_copy = NULL; + struct lttng_event *event = NULL; + uid_t trigger_owner_uid = 0; + const char *trigger_name; + + assert(trigger); + assert(agt); + + t_status = lttng_trigger_get_name(trigger, &trigger_name); + if (t_status != LTTNG_TRIGGER_STATUS_OK) { + trigger_name = "(unnamed)"; + } + + t_status = lttng_trigger_get_owner_uid(trigger, &trigger_owner_uid); + assert(t_status == LTTNG_TRIGGER_STATUS_OK); + + condition = lttng_trigger_get_const_condition(trigger); + + assert(lttng_condition_get_type(condition) == + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + + c_status = lttng_condition_event_rule_get_rule(condition, &rule); + assert(c_status == LTTNG_CONDITION_STATUS_OK); + + assert(lttng_event_rule_get_type(rule) == + LTTNG_EVENT_RULE_TYPE_TRACEPOINT); + + d_type = lttng_event_rule_get_domain_type(rule); + assert(d_type == agt->domain); + + event = lttng_event_rule_generate_lttng_event(rule); + if (!event) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + + /* Get the internal filter expression and bytecode. */ + filter_expression = lttng_event_rule_get_filter(rule); + if (filter_expression) { + filter_expression_copy = strdup(filter_expression); + if (!filter_expression_copy) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + + /* Get the filter bytecode */ + filter_bytecode = lttng_event_rule_get_filter_bytecode(rule); + if (filter_bytecode) { + filter_bytecode_copy = + lttng_filter_bytecode_copy(filter_bytecode); + if (!filter_bytecode_copy) { + ret = LTTNG_ERR_NOMEM; + goto end; + } + } + } + + DBG("Enabling agent event from trigger: trigger name = '%s', trigger owner uid = %d, token = %" PRIu64, + trigger_name, trigger_owner_uid, + lttng_trigger_get_tracer_token(trigger)); + + ret = agent_enable(agt, event, filter_bytecode_copy, + filter_expression_copy); + /* Ownership was passed even in case of error. */ + filter_expression_copy = NULL; + filter_bytecode_copy = NULL; + +end: + free(filter_expression_copy); + free(filter_bytecode_copy); + free(event); + return ret; +} + /* * Return the default event name associated with the provided UST domain. Return * NULL on error. @@ -567,6 +680,43 @@ const char *event_get_default_agent_ust_name(enum lttng_domain_type domain) return default_event_name; } +static int trigger_agent_disable_one(const struct lttng_trigger *trigger, + struct agent *agt, + struct agent_event *aevent) + +{ + int ret; + + assert(agt); + assert(trigger); + assert(aevent); + + /* + * Actual ust event un-registration happens on the trigger + * un-registration at that point. + */ + + DBG("Event agent disabling %s (loglevel type %d, loglevel value %d) for trigger %" PRIu64, + aevent->name, aevent->loglevel_type, + aevent->loglevel_value, lttng_trigger_get_tracer_token(trigger)); + + /* Already disabled? */ + if (!AGENT_EVENT_IS_ENABLED(aevent)) { + goto end; + } + + ret = agent_disable_event(aevent, agt->domain); + if (ret != LTTNG_OK) { + goto error; + } + +end: + return LTTNG_OK; + +error: + return ret; +} + /* * Disable a given agent event for a given UST session. * @@ -590,7 +740,7 @@ static int event_agent_disable_one(struct ltt_ust_session *usess, usess->id); /* Already disabled? */ - if (!aevent->enabled) { + if (!AGENT_EVENT_IS_ENABLED(aevent)) { goto end; } @@ -659,6 +809,44 @@ error: return ret; } +/* + * Disable agent event matching a given trigger. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int trigger_agent_disable( + const struct lttng_trigger *trigger, struct agent *agt) +{ + int ret = LTTNG_OK; + struct agent_event *aevent; + + assert(trigger); + assert(agt); + + DBG("Event agent disabling for trigger %" PRIu64, + lttng_trigger_get_tracer_token(trigger)); + + rcu_read_lock(); + aevent = agent_find_event_by_trigger(trigger, agt); + + if (aevent == NULL) { + DBG2("Event agent NOT found by trigger %" PRIu64, + lttng_trigger_get_tracer_token(trigger)); + ret = LTTNG_ERR_UST_EVENT_NOT_FOUND; + goto end; + } + + ret = trigger_agent_disable_one(trigger, agt, aevent); + + if (ret != LTTNG_OK) { + goto end; + } + +end: + rcu_read_unlock(); + return ret; +} + /* * Disable all agent events matching a given name for a given UST session. * @@ -732,7 +920,7 @@ int event_agent_disable_all(struct ltt_ust_session *usess, rcu_read_lock(); cds_lfht_for_each_entry(agt->events->ht, &iter.iter, aevent, node.node) { - if (!aevent->enabled) { + if (!AGENT_EVENT_IS_ENABLED(aevent)) { continue; } diff --git a/src/bin/lttng-sessiond/event.h b/src/bin/lttng-sessiond/event.h index 1c646db37..c7a849c0b 100644 --- a/src/bin/lttng-sessiond/event.h +++ b/src/bin/lttng-sessiond/event.h @@ -42,6 +42,11 @@ int event_agent_disable(struct ltt_ust_session *usess, struct agent *agt, const char *event_name); int event_agent_disable_all(struct ltt_ust_session *usess, struct agent *agt); +int trigger_agent_enable( + const struct lttng_trigger *trigger, struct agent *agt); +int trigger_agent_disable( + const struct lttng_trigger *trigger, struct agent *agt); + const char *event_get_default_agent_ust_name(enum lttng_domain_type domain); #endif /* _LTT_EVENT_H */ diff --git a/src/bin/lttng-sessiond/globals.c b/src/bin/lttng-sessiond/globals.c index 20aa790a7..85c1eec90 100644 --- a/src/bin/lttng-sessiond/globals.c +++ b/src/bin/lttng-sessiond/globals.c @@ -22,6 +22,7 @@ struct health_app *health_sessiond; struct notification_thread_handle *notification_thread_handle; struct lttng_ht *agent_apps_ht_by_sock = NULL; +struct lttng_ht *trigger_agents_ht_by_domain = NULL; struct lttng_kernel_tracer_version kernel_tracer_version; struct lttng_kernel_tracer_abi_version kernel_tracer_abi_version; diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 9f8c9a332..a344cd476 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -314,6 +314,9 @@ static void sessiond_cleanup(void) pthread_mutex_destroy(&session_list->lock); + DBG("Cleaning up all per-event notifier domain agents"); + agent_by_event_notifier_domain_ht_destroy(); + DBG("Cleaning up all agent apps"); agent_app_ht_clean(); DBG("Closing all UST sockets"); @@ -1596,6 +1599,11 @@ int main(int argc, char **argv) goto stop_threads; } + if (agent_by_event_notifier_domain_ht_create()) { + ERR("Failed to allocate per-event notifier domain agent hash table"); + retval = -1; + goto stop_threads; + } /* * These actions must be executed as root. We do that *after* setting up * the sockets path because we MUST make the check for another daemon using diff --git a/src/bin/lttng-sessiond/save.c b/src/bin/lttng-sessiond/save.c index 052928c89..6ca756365 100644 --- a/src/bin/lttng-sessiond/save.c +++ b/src/bin/lttng-sessiond/save.c @@ -1190,7 +1190,7 @@ int init_ust_event_from_agent_event(struct ltt_ust_event *ust_event, int ret; enum lttng_ust_loglevel_type ust_loglevel_type; - ust_event->enabled = agent_event->enabled; + ust_event->enabled = AGENT_EVENT_IS_ENABLED(agent_event); ust_event->attr.instrumentation = LTTNG_UST_TRACEPOINT; if (lttng_strncpy(ust_event->attr.name, agent_event->name, LTTNG_SYMBOL_NAME_LEN)) { diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index 5ab55ec29..0f8664cc3 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -41,6 +41,7 @@ #include "lttng-sessiond.h" #include "notification-thread-commands.h" #include "rotate.h" +#include "event.h" struct lttng_ht *ust_app_ht; struct lttng_ht *ust_app_ht_by_sock; @@ -1316,29 +1317,6 @@ error: return NULL; } -/* - * Allocate a filter and copy the given original filter. - * - * Return allocated filter or NULL on error. - */ -static struct lttng_filter_bytecode *copy_filter_bytecode( - struct lttng_filter_bytecode *orig_f) -{ - struct lttng_filter_bytecode *filter = NULL; - - /* Copy filter bytecode */ - filter = zmalloc(sizeof(*filter) + orig_f->len); - if (!filter) { - PERROR("zmalloc alloc filter bytecode"); - goto error; - } - - memcpy(filter, orig_f, sizeof(*filter) + orig_f->len); - -error: - return filter; -} - /* * Create a liblttng-ust filter bytecode from given bytecode. * @@ -1944,43 +1922,57 @@ static int init_ust_event_notifier_from_event_rule( memset(event_notifier, 0, sizeof(*event_notifier)); - status = lttng_event_rule_tracepoint_get_pattern(rule, &pattern); - if (status != LTTNG_EVENT_RULE_STATUS_OK) { - /* At this point, this is a fatal error. */ - abort(); - } + if (lttng_event_rule_targets_agent_domain(rule)) { + /* + * Special event for agents + * The actual meat of the event is in the filter that will be + * attached later on. + * Set the default values for the agent event. + */ + pattern = event_get_default_agent_ust_name( + lttng_event_rule_get_domain_type(rule)); + loglevel = 0; + ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; + } else { + status = lttng_event_rule_tracepoint_get_pattern( + rule, &pattern); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + /* At this point, this is a fatal error. */ + abort(); + } - status = lttng_event_rule_tracepoint_get_log_level_type( - rule, &loglevel_type); - if (status != LTTNG_EVENT_RULE_STATUS_OK) { - /* At this point, this is a fatal error. */ - abort(); - } + status = lttng_event_rule_tracepoint_get_log_level_type( + rule, &loglevel_type); + if (status != LTTNG_EVENT_RULE_STATUS_OK) { + /* At this point, this is a fatal error. */ + abort(); + } - switch (loglevel_type) { - case LTTNG_EVENT_LOGLEVEL_ALL: - ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; - break; - case LTTNG_EVENT_LOGLEVEL_RANGE: - ust_loglevel_type = LTTNG_UST_LOGLEVEL_RANGE; - break; - case LTTNG_EVENT_LOGLEVEL_SINGLE: - ust_loglevel_type = LTTNG_UST_LOGLEVEL_SINGLE; - break; - default: - /* Unknown log level specification type. */ - abort(); - } + switch (loglevel_type) { + case LTTNG_EVENT_LOGLEVEL_ALL: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL; + break; + case LTTNG_EVENT_LOGLEVEL_RANGE: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_RANGE; + break; + case LTTNG_EVENT_LOGLEVEL_SINGLE: + ust_loglevel_type = LTTNG_UST_LOGLEVEL_SINGLE; + break; + default: + /* Unknown log level specification type. */ + abort(); + } - if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { - status = lttng_event_rule_tracepoint_get_log_level( - rule, &loglevel); - assert(status == LTTNG_EVENT_RULE_STATUS_OK); + if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) { + status = lttng_event_rule_tracepoint_get_log_level( + rule, &loglevel); + assert(status == LTTNG_EVENT_RULE_STATUS_OK); + } } event_notifier->event.instrumentation = LTTNG_UST_TRACEPOINT; ret = lttng_strncpy(event_notifier->event.name, pattern, - LTTNG_UST_SYM_NAME_LEN - 1); + LTTNG_UST_SYM_NAME_LEN - 1); if (ret) { ERR("Failed to copy event rule pattern to notifier: pattern = '%s' ", pattern); @@ -2119,7 +2111,7 @@ static void shadow_copy_event(struct ust_app_event *ua_event, /* Copy filter bytecode */ if (uevent->filter) { - ua_event->filter = copy_filter_bytecode(uevent->filter); + ua_event->filter = lttng_filter_bytecode_copy(uevent->filter); /* Filter might be NULL here in case of ENONEM. */ } diff --git a/src/bin/lttng-sessiond/utils.c b/src/bin/lttng-sessiond/utils.c index f84859fc6..febf23058 100644 --- a/src/bin/lttng-sessiond/utils.c +++ b/src/bin/lttng-sessiond/utils.c @@ -98,3 +98,26 @@ const char *consumer_output_get_base_path(const struct consumer_output *output) output->dst.session_root_path : output->dst.net.base_dir; } + +/* + * Allocate a filter and copy the given original filter. + * + * Return allocated filter or NULL on error. + */ +struct lttng_filter_bytecode *lttng_filter_bytecode_copy( + const struct lttng_filter_bytecode *orig_f) +{ + struct lttng_filter_bytecode *filter = NULL; + + /* Copy filter bytecode */ + filter = zmalloc(sizeof(*filter) + orig_f->len); + if (!filter) { + PERROR("zmalloc alloc filter bytecode"); + goto error; + } + + memcpy(filter, orig_f, sizeof(*filter) + orig_f->len); + +error: + return filter; +} diff --git a/src/bin/lttng-sessiond/utils.h b/src/bin/lttng-sessiond/utils.h index ff8deaec4..2492bb0f6 100644 --- a/src/bin/lttng-sessiond/utils.h +++ b/src/bin/lttng-sessiond/utils.h @@ -19,5 +19,7 @@ int loglevels_match(int a_loglevel_type, int a_loglevel_value, int b_loglevel_type, int b_loglevel_value, int loglevel_all_type); const char *session_get_base_path(const struct ltt_session *session); const char *consumer_output_get_base_path(const struct consumer_output *output); +struct lttng_filter_bytecode *lttng_filter_bytecode_copy( + const struct lttng_filter_bytecode *orig_f); #endif /* _LTT_UTILS_H */ diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c index a37559c70..98ff37411 100644 --- a/src/common/event-rule/event-rule.c +++ b/src/common/event-rule/event-rule.c @@ -276,6 +276,36 @@ lttng_event_rule_generate_exclusions(const struct lttng_event_rule *rule, } LTTNG_HIDDEN +struct lttng_event *lttng_event_rule_generate_lttng_event( + const struct lttng_event_rule *rule) +{ + assert(rule->generate_lttng_event); + return rule->generate_lttng_event(rule); +} + +LTTNG_HIDDEN +bool lttng_event_rule_targets_agent_domain(const struct lttng_event_rule *rule) +{ + bool targets_agent_domain = false; + enum lttng_domain_type type = lttng_event_rule_get_domain_type(rule); + + switch (type) { + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + targets_agent_domain = true; + break; + case LTTNG_DOMAIN_UST: + case LTTNG_DOMAIN_KERNEL: + targets_agent_domain = false; + break; + default: + abort(); + }; + + return targets_agent_domain; +} + const char *lttng_event_rule_type_str(enum lttng_event_rule_type type) { switch (type) { diff --git a/src/common/event-rule/tracepoint.c b/src/common/event-rule/tracepoint.c index 6bdb754bc..d6f3b9fb7 100644 --- a/src/common/event-rule/tracepoint.c +++ b/src/common/event-rule/tracepoint.c @@ -16,6 +16,7 @@ #include #include #include +#include #define IS_TRACEPOINT_EVENT_RULE(rule) \ (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT) @@ -599,6 +600,41 @@ static unsigned long lttng_event_rule_tracepoint_hash( return hash; } +static struct lttng_event *lttng_event_rule_tracepoint_generate_lttng_event( + const struct lttng_event_rule *rule) +{ + int ret; + const struct lttng_event_rule_tracepoint *tracepoint; + struct lttng_event *local_event = NULL; + struct lttng_event *event = NULL; + + tracepoint = container_of( + rule, const struct lttng_event_rule_tracepoint, parent); + + local_event = zmalloc(sizeof(*local_event)); + if (!local_event) { + goto error; + } + + local_event->type = LTTNG_EVENT_TRACEPOINT; + ret = lttng_strncpy(local_event->name, tracepoint->pattern, + sizeof(local_event->name)); + if (ret) { + ERR("Truncation occurred when copying event rule pattern to `lttng_event` structure: pattern = '%s'", + tracepoint->pattern); + goto error; + } + + local_event->loglevel_type = tracepoint->loglevel.type; + local_event->loglevel = tracepoint->loglevel.value; + + event = local_event; + local_event = NULL; +error: + free(local_event); + return event; +} + struct lttng_event_rule *lttng_event_rule_tracepoint_create( enum lttng_domain_type domain_type) { @@ -629,6 +665,8 @@ struct lttng_event_rule *lttng_event_rule_tracepoint_create( tp_rule->parent.generate_exclusions = lttng_event_rule_tracepoint_generate_exclusions; tp_rule->parent.hash = lttng_event_rule_tracepoint_hash; + tp_rule->parent.generate_lttng_event = + lttng_event_rule_tracepoint_generate_lttng_event; tp_rule->domain = domain_type; tp_rule->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL; -- 2.34.1