--- /dev/null
+# Trigger notification example
+
+## Description
+This example is made-up of three executables.
+
+### `notification-client`
+
+```
+Usage: notification-client TRIGGER_NAME TRIGGER_NAME2 ...
+```
+
+A simple client that subscribes to the notifications emitted by the `TRIGGER_NAME` trigger.
+
+Multiple trigger names can be passed and subscribed to.
+
+
+### `instrumented-app`
+
+An application that emits the `trigger_example:my_event` event every 2 seconds.
+
+### `demo.sh`
+
+This script adds a trigger named `demo_trigger` which emits a notification when
+the user-space `trigger_example:my_event` event occurs.
+
+This script also adds a trigger named `demo_trigger_capture` which emits a
+notification when the user-space `trigger_example:my_event` event occurs and
+provides captured fields if present.
+
+Once the triggers have been setup, the notification-client is launched to print
+all notifications emitted by the `demo_trigger` and `demo_trigger_capture`
+trigger.
+
+## Running the example
+
+1) Launch a session daemon using:
+ ```
+ $ lttng-sessiond
+ ```
+2) Launch the `demo.sh` script
+3) Launch the `instrumented-app`
+
+The following output should be produced:
+
+```
+$ ./demo.sh
+Registering a notification trigger named "demo_trigger" for the trigger_example:my_event user-space event
+Trigger registered successfully.
+Trigger registered successfully.
+Subscribed to notifications of trigger "demo_trigger_capture"
+Subscribed to notifications of trigger "demo_trigger"
+[08-24-2020] 17:20:33.598221 - Received notification of event rule matches trigger "demo_trigger"
+[08-24-2020] 17:20:33.598855 - Received notification of event rule matches trigger "demo_trigger_capture"
+Captured field values:
+ Field: iteration Value: [Unsigned int] 0,
+ Field: does_not_exist Value: Capture unavailable,
+ Field: $ctx.vtid Value: [Unsigned int] 2302494,
+ Field: $ctx.procname Value: [String] instrumented-ap.
+[08-24-2020] 17:20:35.598556 - Received notification of event rule matches trigger "demo_trigger"
+[08-24-2020] 17:20:35.599293 - Received notification of event rule matches trigger "demo_trigger_capture"
+Captured field values:
+ Field: iteration Value: [Unsigned int] 1,
+ Field: does_not_exist Value: Capture unavailable,
+ Field: $ctx.vtid Value: [Unsigned int] 2302494,
+ Field: $ctx.procname Value: [String] instrumented-ap.
+[08-24-2020] 17:20:37.598977 - Received notification of event rule matches trigger "demo_trigger"
+[08-24-2020] 17:20:37.599676 - Received notification of event rule matches trigger "demo_trigger_capture"
+Captured field values:
+ Field: iteration Value: [Unsigned int] 2,
+ Field: does_not_exist Value: Capture unavailable,
+ Field: $ctx.vtid Value: [Unsigned int] 2302494,
+ Field: $ctx.procname Value: [String] instrumented-ap.
+[08-24-2020] 17:20:39.599430 - Received notification of event rule matches trigger "demo_trigger"
+[08-24-2020] 17:20:39.600178 - Received notification of event rule matches trigger "demo_trigger_capture"
+Captured field values:
+ Field: iteration Value: [Unsigned int] 3,
+ Field: does_not_exist Value: Capture unavailable,
+ Field: $ctx.vtid Value: [Unsigned int] 2302494,
+ Field: $ctx.procname Value: [String] instrumented-ap.
+...
+```
+
+```
+$ ./instrumented-app
+[08-24-2020] 17:20:33.597441 - Tracing event "trigger_example:my_event"
+[08-24-2020] 17:20:35.597703 - Tracing event "trigger_example:my_event"
+[08-24-2020] 17:20:37.597997 - Tracing event "trigger_example:my_event"
+...
+```
+
+
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#include <lttng/lttng.h>
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+static int print_capture(const struct lttng_condition *condition,
+ const struct lttng_event_field_value *capture,
+ unsigned int indent_level);
+static int print_array(const struct lttng_condition *condition,
+ const struct lttng_event_field_value *array,
+ unsigned int indent_level);
+
+static void indent(unsigned int indentation_level)
+{
+ unsigned int i;
+ for (i = 0; i < indentation_level; i++) {
+ printf(" ");
+ }
+}
+
+static void print_one_event_expr(const struct lttng_event_expr *event_expr)
+{
+ enum lttng_event_expr_type type;
+
+ type = lttng_event_expr_get_type(event_expr);
+
+ switch (type) {
+ case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+ {
+ const char *name;
+
+ name = lttng_event_expr_event_payload_field_get_name(
+ event_expr);
+ printf("%s", name);
+
+ break;
+ }
+
+ case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+ {
+ const char *name;
+
+ name = lttng_event_expr_channel_context_field_get_name(
+ event_expr);
+ printf("$ctx.%s", name);
+
+ break;
+ }
+
+ case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+ {
+ const char *provider_name;
+ const char *type_name;
+
+ provider_name = lttng_event_expr_app_specific_context_field_get_provider_name(
+ event_expr);
+ type_name = lttng_event_expr_app_specific_context_field_get_type_name(
+ event_expr);
+
+ printf("$app.%s:%s", provider_name, type_name);
+
+ break;
+ }
+
+ case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+ {
+ unsigned int index;
+ const struct lttng_event_expr *parent_expr;
+ enum lttng_event_expr_status status;
+
+ parent_expr = lttng_event_expr_array_field_element_get_parent_expr(
+ event_expr);
+ assert(parent_expr != NULL);
+
+ print_one_event_expr(parent_expr);
+
+ status = lttng_event_expr_array_field_element_get_index(
+ event_expr, &index);
+ assert(status == LTTNG_EVENT_EXPR_STATUS_OK);
+
+ printf("[%u]", index);
+
+ break;
+ }
+
+ default:
+ abort();
+ }
+}
+
+static bool action_group_contains_notify(
+ const struct lttng_action *action_group)
+{
+ unsigned int i, count;
+ enum lttng_action_status status =
+ lttng_action_list_get_count(action_group, &count);
+
+ if (status != LTTNG_ACTION_STATUS_OK) {
+ printf("Failed to get action count from action group\n");
+ exit(1);
+ }
+
+ for (i = 0; i < count; i++) {
+ const struct lttng_action *action =
+ lttng_action_list_get_at_index(action_group, i);
+ const enum lttng_action_type action_type =
+ lttng_action_get_type(action);
+
+ if (action_type == LTTNG_ACTION_TYPE_NOTIFY) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static int print_capture(const struct lttng_condition *condition,
+ const struct lttng_event_field_value *capture,
+ unsigned int indent_level)
+{
+ int ret = 0;
+ enum lttng_event_field_value_status event_field_status;
+ uint64_t u_val;
+ int64_t s_val;
+ double d_val;
+ const char *string_val = NULL;
+
+ switch (lttng_event_field_value_get_type(capture)) {
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT:
+ {
+ event_field_status =
+ lttng_event_field_value_unsigned_int_get_value(
+ capture, &u_val);
+ if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ ret = 1;
+ goto end;
+ }
+
+ printf("[Unsigned int] %" PRIu64, u_val);
+ break;
+ }
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT:
+ {
+ event_field_status =
+ lttng_event_field_value_signed_int_get_value(
+ capture, &s_val);
+ if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ ret = 1;
+ goto end;
+ }
+
+ printf("[Signed int] %" PRId64, s_val);
+ break;
+ }
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
+ {
+ event_field_status =
+ lttng_event_field_value_unsigned_int_get_value(
+ capture, &u_val);
+ if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ ret = 1;
+ goto end;
+ }
+
+ printf("[Unsigned enum] %" PRIu64, u_val);
+ break;
+ }
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
+ {
+ event_field_status =
+ lttng_event_field_value_signed_int_get_value(
+ capture, &s_val);
+ if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ ret = 1;
+ goto end;
+ }
+
+ printf("[Signed enum] %" PRId64, s_val);
+ break;
+ }
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL:
+ {
+ event_field_status = lttng_event_field_value_real_get_value(
+ capture, &d_val);
+ if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ ret = 1;
+ goto end;
+ }
+
+ printf("[Real] %lf", d_val);
+ break;
+ }
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING:
+ {
+ event_field_status = lttng_event_field_value_string_get_value(
+ capture, &string_val);
+ if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ ret = 1;
+ goto end;
+ }
+
+ printf("[String] %s", string_val);
+ break;
+ }
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY:
+ printf("[Array] [\n");
+ print_array(condition, capture, indent_level);
+ indent(indent_level);
+ printf("]\n");
+ break;
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN:
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID:
+ default:
+ ret = 1;
+ break;
+ }
+
+end:
+ return ret;
+}
+
+static void print_unavailabe(void)
+{
+ printf("Capture unavailable");
+}
+
+static int print_array(const struct lttng_condition *condition,
+ const struct lttng_event_field_value *array,
+ unsigned int indent_level)
+{
+ int ret = 0;
+ enum lttng_event_field_value_status event_field_status;
+ unsigned int captured_field_count;
+
+ event_field_status = lttng_event_field_value_array_get_length(
+ array, &captured_field_count);
+ if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ ret = 1;
+ goto end;
+ }
+
+ for (unsigned int i = 0; i < captured_field_count; i++) {
+ const struct lttng_event_field_value *captured_field = NULL;
+ const struct lttng_event_expr *expr =
+ lttng_condition_event_rule_matches_get_capture_descriptor_at_index(
+ condition, i);
+ assert(expr);
+
+ indent(indent_level + 1);
+
+ printf("Field: ");
+ print_one_event_expr(expr);
+ printf(" Value: ");
+
+ event_field_status =
+ lttng_event_field_value_array_get_element_at_index(
+ array, i, &captured_field);
+ if (event_field_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ if (event_field_status ==
+ LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) {
+ print_unavailabe();
+ } else {
+ ret = 1;
+ goto end;
+ }
+ } else {
+ print_capture(condition, captured_field,
+ indent_level + 1);
+ }
+
+ if (i + 1 < captured_field_count) {
+ printf(",");
+ } else {
+ printf(".");
+ }
+ printf("\n");
+ }
+
+end:
+ return ret;
+}
+
+static int print_captures(struct lttng_notification *notification)
+{
+ int ret = 0;
+ const struct lttng_evaluation *evaluation =
+ lttng_notification_get_evaluation(notification);
+ const struct lttng_condition *condition =
+ lttng_notification_get_condition(notification);
+
+ /* Status */
+ enum lttng_condition_status condition_status;
+ enum lttng_evaluation_event_rule_matches_status evaluation_status;
+
+ const struct lttng_event_field_value *captured_field_array = NULL;
+ unsigned int expected_capture_field_count;
+
+ assert(lttng_evaluation_get_type(evaluation) ==
+ LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES);
+
+ condition_status =
+ lttng_condition_event_rule_matches_get_capture_descriptor_count(
+ condition,
+ &expected_capture_field_count);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ ret = 1;
+ goto end;
+ }
+
+ if (expected_capture_field_count == 0) {
+ ret = 0;
+ goto end;
+ }
+
+ evaluation_status =
+ lttng_evaluation_event_rule_matches_get_captured_values(
+ evaluation, &captured_field_array);
+ if (evaluation_status != LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_OK) {
+ ret = 1;
+ goto end;
+ }
+
+ printf("Captured field values:\n");
+ print_array(condition, captured_field_array, 1);
+end:
+ return ret;
+}
+
+static int print_notification(struct lttng_notification *notification)
+{
+ int ret = 0;
+ const struct lttng_evaluation *evaluation =
+ lttng_notification_get_evaluation(notification);
+ const enum lttng_condition_type type =
+ lttng_evaluation_get_type(evaluation);
+
+ switch (type) {
+ case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+ printf("Received consumed size notification\n");
+ break;
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+ printf("Received buffer usage notification\n");
+ break;
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+ printf("Received session rotation ongoing notification\n");
+ break;
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+ printf("Received session rotation completed notification\n");
+ break;
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
+ {
+ const char *trigger_name;
+ enum lttng_trigger_status trigger_status;
+ char time_str[64];
+ struct timeval tv;
+ time_t the_time;
+ const struct lttng_trigger *trigger = NULL;
+
+ gettimeofday(&tv, NULL);
+ the_time = tv.tv_sec;
+
+ strftime(time_str, sizeof(time_str), "[%m-%d-%Y] %T",
+ localtime(&the_time));
+ printf("%s.%ld - ", time_str, tv.tv_usec);
+
+ trigger = lttng_notification_get_trigger(notification);
+ if (!trigger) {
+ fprintf(stderr, "Failed to retrieve notification's trigger");
+ goto end;
+ }
+
+ trigger_status = lttng_trigger_get_name(trigger, &trigger_name);
+ if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+ fprintf(stderr, "Failed to retrieve trigger's name");
+ goto end;
+ }
+
+ printf("Received notification of event rule matches trigger \"%s\"\n",
+ trigger_name);
+ ret = print_captures(notification);
+ break;
+ }
+ default:
+ fprintf(stderr, "Unknown notification type (%d)\n", type);
+ }
+
+end:
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ struct lttng_triggers *triggers = NULL;
+ unsigned int count, i, j, subcription_count = 0, trigger_count;
+ enum lttng_trigger_status trigger_status;
+ struct lttng_notification_channel *notification_channel = NULL;
+
+ if (argc < 2) {
+ fprintf(stderr, "Missing trigger name(s)\n");
+ fprintf(stderr, "Usage: notification-client TRIGGER_NAME ...");
+ ret = -1;
+ goto end;
+ }
+
+ trigger_count = argc - 1;
+
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ if (!notification_channel) {
+ fprintf(stderr, "Failed to create notification channel\n");
+ ret = -1;
+ goto end;
+ }
+
+ ret = lttng_list_triggers(&triggers);
+ if (ret != LTTNG_OK) {
+ fprintf(stderr, "Failed to list triggers\n");
+ goto end;
+ }
+
+ trigger_status = lttng_triggers_get_count(triggers, &count);
+ if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+ fprintf(stderr, "Failed to get trigger count\n");
+ ret = -1;
+ goto end;
+ }
+
+ for (i = 0; i < count; i++) {
+ const struct lttng_trigger *trigger =
+ lttng_triggers_get_at_index(triggers, i);
+ const struct lttng_condition *condition =
+ lttng_trigger_get_const_condition(trigger);
+ const struct lttng_action *action =
+ lttng_trigger_get_const_action(trigger);
+ const enum lttng_action_type action_type =
+ lttng_action_get_type(action);
+ enum lttng_notification_channel_status channel_status;
+ const char *trigger_name = NULL;
+ bool subscribe = false;
+
+ lttng_trigger_get_name(trigger, &trigger_name);
+ for (j = 0; j < trigger_count; j++) {
+ if (!strcmp(trigger_name, argv[j + 1])) {
+ subscribe = true;
+ break;
+ }
+ }
+
+ if (!subscribe) {
+ continue;
+ }
+
+ if (!((action_type == LTTNG_ACTION_TYPE_LIST &&
+ action_group_contains_notify(action)) ||
+ action_type == LTTNG_ACTION_TYPE_NOTIFY)) {
+ printf("The action of trigger \"%s\" is not \"notify\", skipping.\n",
+ trigger_name);
+ continue;
+ }
+
+ channel_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ if (channel_status ==
+ LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED) {
+ continue;
+ }
+ if (channel_status) {
+ fprintf(stderr, "Failed to subscribe to notifications of trigger \"%s\"\n",
+ trigger_name);
+ ret = -1;
+ goto end;
+ }
+
+ printf("Subscribed to notifications of trigger \"%s\"\n",
+ trigger_name);
+ subcription_count++;
+ }
+
+ if (subcription_count == 0) {
+ printf("No matching trigger with a notify action found.\n");
+ ret = 0;
+ goto end;
+ }
+
+ for (;;) {
+ struct lttng_notification *notification;
+ enum lttng_notification_channel_status channel_status;
+
+ channel_status =
+ lttng_notification_channel_get_next_notification(
+ notification_channel,
+ ¬ification);
+ switch (channel_status) {
+ case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED:
+ printf("Dropped notification\n");
+ break;
+ case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED:
+ ret = 0;
+ goto end;
+ case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK:
+ break;
+ case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED:
+ printf("Notification channel was closed by peer.\n");
+ break;
+ default:
+ fprintf(stderr, "A communication error occurred on the notification channel.\n");
+ ret = -1;
+ goto end;
+ }
+
+ ret = print_notification(notification);
+ lttng_notification_destroy(notification);
+ if (ret) {
+ goto end;
+ }
+ }
+end:
+ lttng_triggers_destroy(triggers);
+ lttng_notification_channel_destroy(notification_channel);
+ return !!ret;
+}