From 683d081a7f3734fcb5c8dd4424b0aa102117d1a0 Mon Sep 17 00:00:00 2001 From: Jonathan Rajotte Date: Tue, 3 Dec 2019 15:57:08 -0500 Subject: [PATCH 1/1] condition: implement event rule based condition MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit An event rule condition is met when a tracer hit an event matching the associated event rule. Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I550903c231d83cb3852e8ef8aee2abafe9069b10 --- include/Makefile.am | 2 + include/lttng/condition/condition.h | 1 + include/lttng/condition/event-rule-internal.h | 61 +++ include/lttng/condition/event-rule.h | 79 ++++ include/lttng/lttng.h | 1 + src/common/Makefile.am | 1 + src/common/conditions/condition.c | 4 + src/common/conditions/event-rule.c | 419 ++++++++++++++++++ src/common/event-rule/event-rule.c | 2 +- 9 files changed, 569 insertions(+), 1 deletion(-) create mode 100644 include/lttng/condition/event-rule-internal.h create mode 100644 include/lttng/condition/event-rule.h create mode 100644 src/common/conditions/event-rule.c diff --git a/include/Makefile.am b/include/Makefile.am index 5a3b5b5f3..957a01d25 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -132,6 +132,7 @@ lttngactioninclude_HEADERS= \ lttngconditioninclude_HEADERS= \ lttng/condition/condition.h \ lttng/condition/buffer-usage.h \ + lttng/condition/event-rule.h \ lttng/condition/session-consumed-size.h \ lttng/condition/session-rotation.h \ lttng/condition/evaluation.h @@ -164,6 +165,7 @@ noinst_HEADERS = \ lttng/action/stop-session-internal.h \ lttng/condition/condition-internal.h \ lttng/condition/buffer-usage-internal.h \ + lttng/condition/event-rule-internal.h \ lttng/condition/session-consumed-size-internal.h \ lttng/condition/evaluation-internal.h \ lttng/condition/session-rotation-internal.h \ diff --git a/include/lttng/condition/condition.h b/include/lttng/condition/condition.h index 877ccd1a3..78a206df3 100644 --- a/include/lttng/condition/condition.h +++ b/include/lttng/condition/condition.h @@ -21,6 +21,7 @@ enum lttng_condition_type { LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW = 102, LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING = 103, LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED = 104, + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT = 105, }; enum lttng_condition_status { diff --git a/include/lttng/condition/event-rule-internal.h b/include/lttng/condition/event-rule-internal.h new file mode 100644 index 000000000..a4e02c509 --- /dev/null +++ b/include/lttng/condition/event-rule-internal.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_CONDITION_EVENT_RULE_INTERNAL_H +#define LTTNG_CONDITION_EVENT_RULE_INTERNAL_H + +#include +#include +#include +#include + +struct lttng_condition_event_rule { + struct lttng_condition parent; + struct lttng_event_rule *rule; +}; + +struct lttng_condition_event_rule_comm { + /* length excludes the header's length. */ + uint32_t event_rule_length; + /* Event rule follows. */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_evaluation_event_rule { + struct lttng_evaluation parent; + char *name; +}; + +struct lttng_evaluation_event_rule_comm { + /* Includes the null terminator. */ + uint32_t trigger_name_length; + /* Trigger name. */ + char payload[]; +} LTTNG_PACKED; + + +LTTNG_HIDDEN +ssize_t lttng_condition_event_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **condition); + +LTTNG_HIDDEN +enum lttng_condition_status +lttng_condition_event_rule_borrow_rule_mutable( + const struct lttng_condition *condition, + struct lttng_event_rule **rule); + +LTTNG_HIDDEN +struct lttng_evaluation *lttng_evaluation_event_rule_create( + const char* trigger_name); + +LTTNG_HIDDEN +ssize_t lttng_evaluation_event_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation); + +#endif /* LTTNG_CONDITION_EVENT_RULE_INTERNAL_H */ diff --git a/include/lttng/condition/event-rule.h b/include/lttng/condition/event-rule.h new file mode 100644 index 000000000..973cbe69b --- /dev/null +++ b/include/lttng/condition/event-rule.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_CONDITION_EVENT_RULE_H +#define LTTNG_CONDITION_EVENT_RULE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Event rule conditions allows an action to be taken whenever an event matching + * the event rule is hit by the tracers. + * + * An event rule condition can also specify a payload to be captured at runtime. + * This is done via the capture descriptor. + * + * Note: the dynamic runtime capture of payload is only available for the + * trigger notification subsystem. + */ + +/* + * Create a newly allocated event rule condition. + * + * Returns a new condition on success, NULL on failure. This condition must be + * destroyed using lttng_condition_destroy(). + */ +extern struct lttng_condition *lttng_condition_event_rule_create( + struct lttng_event_rule *rule); + +/* + * Get the rule property of a event rule condition. + * + * The caller does not assume the ownership of the returned rule. The + * rule shall only be used for the duration of the condition's + * lifetime. + * + * Returns LTTNG_CONDITION_STATUS_OK and a pointer to the condition's rule + * on success, LTTNG_CONDITION_STATUS_INVALID if an invalid + * parameter is passed. */ +extern enum lttng_condition_status lttng_condition_event_rule_get_rule( + const struct lttng_condition *condition, + const struct lttng_event_rule **rule); + +/** + * lttng_evaluation_event_rule_hit are specialised lttng_evaluations which + * allow users to query a number of properties resulting from the evaluation + * of a condition which evaluated to true. + * + * The evaluation of a event rule hit yields two different results: + * TEMPORARY - The name of the triggers associated with the condition. + * TODO - The captured event payload if any + */ + +/* + * Get the trigger name property of a event rule hit evaluation. + * + * Returns LTTNG_EVALUATION_STATUS_OK on success and a trigger name + * or LTTNG_EVALUATION_STATUS_INVALID if + * an invalid parameter is passed. + */ +extern enum lttng_evaluation_status +lttng_evaluation_event_rule_get_trigger_name( + const struct lttng_evaluation *evaluation, + const char **name); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_CONDITION_EVENT_RULE_H */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index 1049e0f0d..bc900e7f0 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 9df1790fb..bbddbe7eb 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -38,6 +38,7 @@ libcommon_la_SOURCES = \ common.h \ conditions/buffer-usage.c \ conditions/condition.c \ + conditions/event-rule.c \ conditions/session-consumed-size.c \ conditions/session-rotation.c \ context.c context.h \ diff --git a/src/common/conditions/condition.c b/src/common/conditions/condition.c index cffe6cf5e..d1990414c 100644 --- a/src/common/conditions/condition.c +++ b/src/common/conditions/condition.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -169,6 +170,9 @@ ssize_t lttng_condition_create_from_payload( case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: create_from_payload = lttng_condition_session_rotation_completed_create_from_payload; break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT: + create_from_payload = lttng_condition_event_rule_create_from_payload; + break; default: ERR("Attempted to create condition of unknown type (%i)", (int) condition_comm->condition_type); diff --git a/src/common/conditions/event-rule.c b/src/common/conditions/event-rule.c new file mode 100644 index 000000000..06ba18fa5 --- /dev/null +++ b/src/common/conditions/event-rule.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_EVENT_RULE_CONDITION(condition) \ + (lttng_condition_get_type(condition) == \ + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) + +static bool is_event_rule_evaluation(const struct lttng_evaluation *evaluation) +{ + enum lttng_condition_type type = lttng_evaluation_get_type(evaluation); + + return type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; +} + +static bool lttng_condition_event_rule_validate( + const struct lttng_condition *condition); +static int lttng_condition_event_rule_serialize( + const struct lttng_condition *condition, + struct lttng_payload *payload); +static bool lttng_condition_event_rule_is_equal( + const struct lttng_condition *_a, + const struct lttng_condition *_b); +static void lttng_condition_event_rule_destroy( + struct lttng_condition *condition); + +static bool lttng_condition_event_rule_validate( + const struct lttng_condition *condition) +{ + bool valid = false; + struct lttng_condition_event_rule *event_rule; + + if (!condition) { + goto end; + } + + event_rule = container_of( + condition, struct lttng_condition_event_rule, parent); + if (!event_rule->rule) { + ERR("Invalid event rule condition: a rule must be set."); + goto end; + } + + valid = lttng_event_rule_validate(event_rule->rule); +end: + return valid; +} + +static int lttng_condition_event_rule_serialize( + const struct lttng_condition *condition, + struct lttng_payload *payload) +{ + int ret; + size_t header_offset, size_before_payload; + struct lttng_condition_event_rule *event_rule; + struct lttng_condition_event_rule_comm event_rule_comm = {}; + struct lttng_condition_event_rule_comm *header = NULL; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition)) { + ret = -1; + goto end; + } + + DBG("Serializing event rule condition"); + event_rule = container_of( + condition, struct lttng_condition_event_rule, parent); + + header_offset = payload->buffer.size; + ret = lttng_dynamic_buffer_append(&payload->buffer, &event_rule_comm, + sizeof(event_rule_comm)); + if (ret) { + goto end; + } + + size_before_payload = payload->buffer.size; + ret = lttng_event_rule_serialize(event_rule->rule, payload); + if (ret) { + goto end; + } + + /* Update payload size. */ + header = (struct lttng_condition_event_rule_comm *) + ((char *) payload->buffer.data + header_offset); + header->event_rule_length = payload->buffer.size - size_before_payload; + +end: + return ret; +} + +static bool lttng_condition_event_rule_is_equal( + const struct lttng_condition *_a, + const struct lttng_condition *_b) +{ + bool is_equal = false; + struct lttng_condition_event_rule *a, *b; + + a = container_of(_a, struct lttng_condition_event_rule, parent); + b = container_of(_b, struct lttng_condition_event_rule, parent); + + /* Both event rules must be set or both must be unset. */ + if ((a->rule && !b->rule) || (!a->rule && b->rule)) { + WARN("Comparing event_rule conditions with uninitialized rule"); + goto end; + } + + is_equal = lttng_event_rule_is_equal(a->rule, b->rule); +end: + return is_equal; +} + +static void lttng_condition_event_rule_destroy( + struct lttng_condition *condition) +{ + struct lttng_condition_event_rule *event_rule; + + event_rule = container_of( + condition, struct lttng_condition_event_rule, parent); + + lttng_event_rule_put(event_rule->rule); + free(event_rule); +} + +struct lttng_condition *lttng_condition_event_rule_create( + struct lttng_event_rule *rule) +{ + struct lttng_condition *parent = NULL; + struct lttng_condition_event_rule *condition = NULL; + + if (!rule) { + goto end; + } + + condition = zmalloc(sizeof(struct lttng_condition_event_rule)); + if (!condition) { + return NULL; + } + + lttng_condition_init(&condition->parent, + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT); + condition->parent.validate = lttng_condition_event_rule_validate, + condition->parent.serialize = lttng_condition_event_rule_serialize, + condition->parent.equal = lttng_condition_event_rule_is_equal, + condition->parent.destroy = lttng_condition_event_rule_destroy, + + lttng_event_rule_get(rule); + condition->rule = rule; + rule = NULL; + + parent = &condition->parent; +end: + return parent; +} + +LTTNG_HIDDEN +ssize_t lttng_condition_event_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **_condition) +{ + ssize_t offset, event_rule_length; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + const struct lttng_condition_event_rule_comm *header; + const struct lttng_payload_view header_view = + lttng_payload_view_from_view( + view, 0, sizeof(*header)); + + if (!view || !_condition) { + goto error; + } + + if (!lttng_payload_view_is_valid(&header_view)) { + ERR("Failed to initialize from malformed event rule condition: buffer too short to contain header"); + goto error; + } + + header = (const struct lttng_condition_event_rule_comm *) + header_view.buffer.data; + offset = sizeof(*header); + + /* lttng_event_rule payload. */ + { + struct lttng_payload_view event_rule_view = + lttng_payload_view_from_view(view, offset, -1); + + event_rule_length = lttng_event_rule_create_from_payload( + &event_rule_view, &event_rule); + } + + if (event_rule_length < 0 || !event_rule) { + goto error; + } + + if ((size_t) header->event_rule_length != event_rule_length) { + goto error; + } + + /* Move to the end of the payload. */ + offset += header->event_rule_length; + + /* Acquires a reference to the event rule. */ + condition = lttng_condition_event_rule_create(event_rule); + if (!condition) { + goto error; + } + + *_condition = condition; + condition = NULL; + goto end; + +error: + offset = -1; + +end: + lttng_event_rule_put(event_rule); + lttng_condition_put(condition); + return offset; +} + +LTTNG_HIDDEN +enum lttng_condition_status lttng_condition_event_rule_borrow_rule_mutable( + const struct lttng_condition *condition, + struct lttng_event_rule **rule) +{ + struct lttng_condition_event_rule *event_rule; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !rule) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + event_rule = container_of( + condition, struct lttng_condition_event_rule, parent); + if (!event_rule->rule) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + + *rule = event_rule->rule; +end: + return status; +} + +enum lttng_condition_status lttng_condition_event_rule_get_rule( + const struct lttng_condition *condition, + const struct lttng_event_rule **rule) +{ + struct lttng_event_rule *mutable_rule = NULL; + const enum lttng_condition_status status = + lttng_condition_event_rule_borrow_rule_mutable( + condition, &mutable_rule); + + *rule = mutable_rule; + return status; +} + +LTTNG_HIDDEN +ssize_t lttng_evaluation_event_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation) +{ + ssize_t ret, offset = 0; + const char *trigger_name; + struct lttng_evaluation *evaluation = NULL; + const struct lttng_evaluation_event_rule_comm *header; + const struct lttng_payload_view header_view = + lttng_payload_view_from_view( + view, 0, sizeof(*header)); + + if (!_evaluation) { + ret = -1; + goto error; + } + + if (!lttng_payload_view_is_valid(&header_view)) { + ERR("Failed to initialize from malformed event rule evaluation: buffer too short to contain header"); + ret = -1; + goto error; + } + + header = (typeof(header)) header_view.buffer.data; + + /* Map the originating trigger's name. */ + offset += sizeof(*header); + { + struct lttng_payload_view current_view = + lttng_payload_view_from_view(view, offset, + header->trigger_name_length); + + if (!lttng_payload_view_is_valid(¤t_view)) { + ERR("Failed to initialize from malformed event rule evaluation: buffer too short to contain trigger name"); + ret = -1; + goto error; + } + + trigger_name = current_view.buffer.data; + if (!lttng_buffer_view_contains_string(¤t_view.buffer, + trigger_name, header->trigger_name_length)) { + ERR("Failed to initialize from malformed event rule evaluation: invalid trigger name"); + ret = -1; + goto error; + } + } + + offset += header->trigger_name_length; + + evaluation = lttng_evaluation_event_rule_create(trigger_name); + if (!evaluation) { + ret = -1; + goto error; + } + + *_evaluation = evaluation; + evaluation = NULL; + ret = offset; + +error: + lttng_evaluation_destroy(evaluation); + return ret; +} + +static int lttng_evaluation_event_rule_serialize( + const struct lttng_evaluation *evaluation, + struct lttng_payload *payload) +{ + int ret = 0; + struct lttng_evaluation_event_rule *hit; + struct lttng_evaluation_event_rule_comm comm; + + hit = container_of( + evaluation, struct lttng_evaluation_event_rule, parent); + + assert(hit->name); + comm.trigger_name_length = strlen(hit->name) + 1; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append( + &payload->buffer, hit->name, comm.trigger_name_length); +end: + return ret; +} + +static void lttng_evaluation_event_rule_destroy( + struct lttng_evaluation *evaluation) +{ + struct lttng_evaluation_event_rule *hit; + + hit = container_of( + evaluation, struct lttng_evaluation_event_rule, parent); + free(hit->name); + free(hit); +} + +LTTNG_HIDDEN +struct lttng_evaluation *lttng_evaluation_event_rule_create( + const char *trigger_name) +{ + struct lttng_evaluation_event_rule *hit; + struct lttng_evaluation *evaluation = NULL; + + hit = zmalloc(sizeof(struct lttng_evaluation_event_rule)); + if (!hit) { + goto end; + } + + hit->name = strdup(trigger_name); + if (!hit->name) { + goto end; + } + + hit->parent.type = LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; + hit->parent.serialize = lttng_evaluation_event_rule_serialize; + hit->parent.destroy = lttng_evaluation_event_rule_destroy; + + evaluation = &hit->parent; + hit = NULL; + +end: + if (hit) { + lttng_evaluation_event_rule_destroy(&hit->parent); + } + + return evaluation; +} + +enum lttng_evaluation_status lttng_evaluation_event_rule_get_trigger_name( + const struct lttng_evaluation *evaluation, const char **name) +{ + struct lttng_evaluation_event_rule *hit; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + if (!evaluation || !is_event_rule_evaluation(evaluation) || !name) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + hit = container_of( + evaluation, struct lttng_evaluation_event_rule, parent); + *name = hit->name; +end: + return status; +} diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c index b6a0e96e7..fbcb7a018 100644 --- a/src/common/event-rule/event-rule.c +++ b/src/common/event-rule/event-rule.c @@ -161,7 +161,7 @@ ssize_t lttng_event_rule_create_from_payload( goto end; } - DBG("Deserializing event_rule from payload."); + DBG("Deserializing event_rule from payload"); event_rule_comm = (const struct lttng_event_rule_comm *) event_rule_comm_view.buffer.data; consumed += sizeof(*event_rule_comm); -- 2.34.1