From 5c504c41e4d470012cb98c849aa82086d1ba1bde Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Thu, 23 Jan 2020 14:14:14 -0500 Subject: [PATCH] trigger: introduce firing policies MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit A firing policy controls the rate of firing of a trigger. Two firing policy mode are implemented: LTTNG_TRIGGER_FIRING_POLICY_FIRE_EVERY_N The triggers's actions are executed every N times the condition occurs. LTTNG_TRIGGER_FIRING_POLICY_ONCE_AFTER_N The triggers's actions are executed once the condition was met N times. Firing policies will be moved to the specific `action` objects in a follow-up commit as not all actions can implement the firing policies. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: Ifaeeaaec7b6f2bed57d0d5f4ed8546762ec02e8d --- include/lttng/trigger/trigger-internal.h | 23 ++++ include/lttng/trigger/trigger.h | 37 ++++++ src/common/trigger.c | 143 +++++++++++++++++++++++ 3 files changed, 203 insertions(+) diff --git a/include/lttng/trigger/trigger-internal.h b/include/lttng/trigger/trigger-internal.h index 6942ece78..873684769 100644 --- a/include/lttng/trigger/trigger-internal.h +++ b/include/lttng/trigger/trigger-internal.h @@ -30,6 +30,11 @@ struct lttng_trigger { char *name; /* For now only the uid portion of the credentials is used. */ struct lttng_credentials creds; + struct { + enum lttng_trigger_firing_policy type; + uint64_t threshold; + uint64_t current_count; + } firing_policy; /* * Internal use only. * The unique token passed to the tracer to identify an event-rule @@ -55,6 +60,10 @@ struct lttng_trigger_comm { uint32_t length; /* Includes '\0' terminator. */ uint32_t name_length; + /* Firing policy. */ + /* Maps to enum lttng_trigger_firing_policy. */ + uint8_t firing_policy_type; + uint64_t firing_policy_threshold; /* A null-terminated name, a condition, and an action follow. */ char payload[]; } LTTNG_PACKED; @@ -161,4 +170,18 @@ LTTNG_HIDDEN void lttng_trigger_set_credentials(struct lttng_trigger *trigger, const struct lttng_credentials *creds); + +/* + * Fire the trigger. + * Increments the occurrence count. + */ +LTTNG_HIDDEN +void lttng_trigger_fire(struct lttng_trigger *trigger); + +/* + * Check if the trigger would fire. + */ +LTTNG_HIDDEN +bool lttng_trigger_should_fire(const struct lttng_trigger *trigger); + #endif /* LTTNG_TRIGGER_INTERNAL_H */ diff --git a/include/lttng/trigger/trigger.h b/include/lttng/trigger/trigger.h index b8e6080d9..a0c312ddd 100644 --- a/include/lttng/trigger/trigger.h +++ b/include/lttng/trigger/trigger.h @@ -9,6 +9,7 @@ #define LTTNG_TRIGGER_H #include +#include struct lttng_action; struct lttng_condition; @@ -35,6 +36,11 @@ enum lttng_trigger_status { LTTNG_TRIGGER_STATUS_PERMISSION_DENIED = -6, }; +enum lttng_trigger_firing_policy { + LTTNG_TRIGGER_FIRING_POLICY_EVERY_N = 0, + LTTNG_TRIGGER_FIRING_POLICY_ONCE_AFTER_N = 1, +}; + /* * Create a trigger object associating a condition and an action. * @@ -127,6 +133,37 @@ extern enum lttng_trigger_status lttng_trigger_get_name( extern enum lttng_trigger_status lttng_trigger_set_name( struct lttng_trigger *trigger, const char *name); +/* + * Set the trigger firing policy. + * + * This is optional. By default a trigger is set to fire each time the + * associated condition occurs. + * + * Threshold is the number of times the condition must be hit before the policy + * is enacted. + * + * Return LTTNG_TRIGGER_STATUS_OK on success, LTTNG_TRIGGER_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_trigger_status lttng_trigger_set_firing_policy( + struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy policy_type, + uint64_t threshold); + +/* + * Get the trigger firing policy. + * + * Threshold is the number of time the condition must be hit before the policy is + * enacted. + * + * Return LTTNG_TRIGGER_STATUS_OK on success, LTTNG_TRIGGER_STATUS_INVALID + * if invalid parameters are passed. + */ +extern enum lttng_trigger_status lttng_trigger_get_firing_policy( + const struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy *policy_type, + uint64_t *threshold); + /* * Destroy (frees) a trigger object. */ diff --git a/src/common/trigger.c b/src/common/trigger.c index e038fa81a..8c43fdbd8 100644 --- a/src/common/trigger.c +++ b/src/common/trigger.c @@ -55,6 +55,9 @@ struct lttng_trigger *lttng_trigger_create( urcu_ref_init(&trigger->ref); + trigger->firing_policy.type = LTTNG_TRIGGER_FIRING_POLICY_EVERY_N; + trigger->firing_policy.threshold = 1; + lttng_condition_get(condition); trigger->condition = condition; @@ -126,6 +129,23 @@ void lttng_trigger_destroy(struct lttng_trigger *trigger) lttng_trigger_put(trigger); } +static bool is_firing_policy_valid(enum lttng_trigger_firing_policy policy) +{ + bool valid = false; + + switch (policy) { + case LTTNG_TRIGGER_FIRING_POLICY_EVERY_N: + case LTTNG_TRIGGER_FIRING_POLICY_ONCE_AFTER_N: + valid = true; + break; + default: + valid = false; + break; + } + + return valid; +} + LTTNG_HIDDEN ssize_t lttng_trigger_create_from_payload( struct lttng_payload_view *src_view, @@ -136,6 +156,8 @@ ssize_t lttng_trigger_create_from_payload( struct lttng_action *action = NULL; const struct lttng_trigger_comm *trigger_comm; const char *name = NULL; + uint64_t firing_policy_threshold; + enum lttng_trigger_firing_policy firing_policy; struct lttng_credentials creds = { .uid = LTTNG_OPTIONAL_INIT_UNSET, .gid = LTTNG_OPTIONAL_INIT_UNSET, @@ -170,6 +192,13 @@ ssize_t lttng_trigger_create_from_payload( offset += sizeof(*trigger_comm); + firing_policy = trigger_comm->firing_policy_type; + if (!is_firing_policy_valid(firing_policy)) { + ret =-1; + goto end; + } + + firing_policy_threshold = trigger_comm->firing_policy_threshold; if (trigger_comm->name_length != 0) { /* Name. */ const struct lttng_payload_view name_view = @@ -258,6 +287,19 @@ ssize_t lttng_trigger_create_from_payload( } } + /* Set the policy. */ + { + const enum lttng_trigger_status status = + lttng_trigger_set_firing_policy(trigger, + firing_policy, + firing_policy_threshold); + + if (status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + goto end; + } + } + ret = offset; error: @@ -299,6 +341,8 @@ int lttng_trigger_serialize(const struct lttng_trigger *trigger, } trigger_comm.name_length = size_name; + trigger_comm.firing_policy_type = (uint8_t) trigger->firing_policy.type; + trigger_comm.firing_policy_threshold = (uint64_t) trigger->firing_policy.threshold; header_offset = payload->buffer.size; ret = lttng_dynamic_buffer_append(&payload->buffer, &trigger_comm, @@ -337,6 +381,14 @@ LTTNG_HIDDEN bool lttng_trigger_is_equal( const struct lttng_trigger *a, const struct lttng_trigger *b) { + if (a->firing_policy.type != b->firing_policy.type) { + return false; + } + + if (a->firing_policy.threshold != b->firing_policy.threshold) { + return false; + } + /* * Name is not taken into account since it is cosmetic only. */ @@ -738,3 +790,94 @@ enum lttng_trigger_status lttng_trigger_get_owner_uid( end: return ret; } + +enum lttng_trigger_status lttng_trigger_set_firing_policy( + struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy policy_type, + uint64_t threshold) +{ + enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK; + assert(trigger); + + if (threshold < 1) { + ret = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + trigger->firing_policy.type = policy_type; + trigger->firing_policy.threshold = threshold; + +end: + return ret; +} + +enum lttng_trigger_status lttng_trigger_get_firing_policy( + const struct lttng_trigger *trigger, + enum lttng_trigger_firing_policy *policy_type, + uint64_t *threshold) +{ + enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK; + + if (!trigger || !policy_type || !threshold) { + status = LTTNG_TRIGGER_STATUS_INVALID; + goto end; + } + + *policy_type = trigger->firing_policy.type; + *threshold = trigger->firing_policy.threshold; + +end: + return status; +} + +LTTNG_HIDDEN +bool lttng_trigger_should_fire(const struct lttng_trigger *trigger) +{ + bool ready_to_fire = false; + + assert(trigger); + + switch (trigger->firing_policy.type) { + case LTTNG_TRIGGER_FIRING_POLICY_EVERY_N: + if (trigger->firing_policy.current_count < trigger->firing_policy.threshold) { + ready_to_fire = true; + } + break; + case LTTNG_TRIGGER_FIRING_POLICY_ONCE_AFTER_N: + if (trigger->firing_policy.current_count < trigger->firing_policy.threshold) { + ready_to_fire = true; + } + break; + default: + abort(); + }; + + return ready_to_fire; +} + +LTTNG_HIDDEN +void lttng_trigger_fire(struct lttng_trigger *trigger) +{ + assert(trigger); + + trigger->firing_policy.current_count++; + + switch (trigger->firing_policy.type) { + case LTTNG_TRIGGER_FIRING_POLICY_EVERY_N: + if (trigger->firing_policy.current_count == trigger->firing_policy.threshold) { + trigger->firing_policy.current_count = 0; + } + + break; + case LTTNG_TRIGGER_FIRING_POLICY_ONCE_AFTER_N: + /* + * TODO: + * As an optimisation, deactivate the trigger condition and + * remove any checks in the traced application or kernel since + * the trigger will never fire again. + */ + break; + default: + abort(); + }; +} -- 2.34.1