AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
-AlignOperands: true
+AlignOperands: false
AlignTrailingComments: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
/src/bin/lttng-crash/lttng-crash
/src/bin/lttng-relayd/lttng-relayd
/src/lib/lttng-ctl/lttng-ctl.pc
-/src/lib/lttng-ctl/filter/filter-grammar-test
-/src/lib/lttng-ctl/filter/filter-lexer.c
-/src/lib/lttng-ctl/filter/filter-parser.c
-/src/lib/lttng-ctl/filter/filter-parser.h
-/src/lib/lttng-ctl/filter/filter-parser.output
+/src/common/filter/filter-grammar-test
+/src/common/filter/filter-lexer.c
+/src/common/filter/filter-parser.c
+/src/common/filter/filter-parser.h
+/src/common/filter/filter-parser.output
/extras/bindings/swig/python/lttng.i
/extras/bindings/swig/python/lttng.py
/tests/unit/test_directory_handle
/tests/unit/test_relayd_backward_compat_group_by_session
/tests/unit/test_fd_tracker
+/tests/unit/test_event_rule
+/tests/unit/test_condition
kernel_all_events_basic
kernel_event_basic
ust_global_event_wildcard
/tests/regression/tools/notification/notification
/tests/regression/tools/rotation/schedule_api
/tests/regression/tools/notification/rotation
+/tests/regression/tools/trigger/base_client
+/tests/regression/tools/trigger/trigger
/tests/regression/ust/overlap/demo/demo
/tests/regression/ust/linking/demo_builtin
/tests/regression/ust/linking/demo_static
# examples
/doc/examples/rotation/rotate-client
+/doc/examples/trigger-on-event/performance/consumer
+/doc/examples/trigger-on-event/performance/producer
+/doc/examples/trigger-on-event/performance/performance.a
/benchmark/
AC_SUBST(POPT_LIBS)
PKG_CHECK_MODULES([libxml2], [libxml-2.0 >= 2.7.6])
+PKG_CHECK_MODULES([msgpack], [msgpack])
AC_CHECK_FUNC([clock_gettime], [AC_DEFINE_UNQUOTED([LTTNG_HAVE_CLOCK_GETTIME], 1, [Has clock_gettime() support.])])
# In a scenario where lttng-tools is built from a distribution tarball and in a
# out-of-tree manner, the generated "version.i" has priority on the one from
# the source (distribution tarball) and must be found first.
-AM_CPPFLAGS="-I\$(top_builddir)/include -I\$(top_srcdir)/include -I\$(top_srcdir)/src -include config.h $AM_CPPFLAGS"
+AM_CPPFLAGS="-I\$(top_builddir)/include -I\$(top_srcdir)/include -I\$(top_builddir)/src -I\$(top_srcdir)/src -include config.h $AM_CPPFLAGS"
AC_SUBST(AM_CPPFLAGS)
lttngincludedir="${includedir}/lttng"
lttngtriggerincludedir="${includedir}/lttng/trigger"
AC_SUBST(lttngtriggerincludedir)
+lttngeventruleincludedir="${includedir}/lttng/event-rule"
+AC_SUBST(lttngeventruleincludedir)
+
lttnglibexecdir="${libdir}/lttng/libexec"
AC_SUBST(lttnglibexecdir)
extras/core-handler/Makefile
src/Makefile
src/common/Makefile
+ src/common/argpar/Makefile
+ src/common/bytecode/Makefile
src/common/kernel-ctl/Makefile
src/common/kernel-consumer/Makefile
src/common/consumer/Makefile
src/common/config/Makefile
src/common/string-utils/Makefile
src/common/fd-tracker/Makefile
+ src/common/filter/Makefile
src/lib/Makefile
src/lib/lttng-ctl/Makefile
- src/lib/lttng-ctl/filter/Makefile
src/lib/lttng-ctl/lttng-ctl.pc
src/bin/Makefile
src/bin/lttng-consumerd/Makefile
tests/regression/tools/working-directory/Makefile
tests/regression/tools/relayd-grouping/Makefile
tests/regression/tools/clear/Makefile
+ tests/regression/tools/trigger/Makefile
+ tests/regression/tools/trigger/start-stop/Makefile
+ tests/regression/tools/trigger/utils/Makefile
tests/regression/ust/Makefile
tests/regression/ust/nprocesses/Makefile
tests/regression/ust/high-throughput/Makefile
--- /dev/null
+# Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+# OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
+#
+# Permission is hereby granted to use or copy this program for any
+# purpose, provided the above notices are retained on all copies.
+# Permission to modify the code and to distribute modified code is
+# granted, provided the above notices are retained, and a notice that
+# the code was modified is included with the above copyright notice.
+#
+# This Makefile is not using automake so that users may see how to build
+# a program with tracepoint provider probes compiled as static libraries.
+#
+# This makefile is purposefully kept simple to support GNU and BSD make.
+
+LOCAL_CPPFLAGS += -I.
+LIBS_INSTRUMENTED_APP = -ldl -llttng-ust
+LIBS_NOTIFICATION_CLIENT = -ldl -llttng-ctl
+AM_V_P := :
+AR ?= ar
+
+all: instrumented-app notification-client
+
+tracepoint-trigger-example.o: tracepoint-trigger-example.c tracepoint-trigger-example.h
+ @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \
+ $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+ $(CFLAGS) -c -o $@ $<
+
+tracepoint-trigger-example.a: tracepoint-trigger-example.o
+ @if $(AM_V_P); then set -x; else echo " AR $@"; fi; \
+ $(AR) -rc $@ tracepoint-trigger-example.o
+
+instrumented-app.o: instrumented-app.c
+ @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \
+ $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+ $(CFLAGS) -c -o $@ $<
+
+instrumented-app: instrumented-app.o tracepoint-trigger-example.a
+ @if $(AM_V_P); then set -x; else echo " CCLD $@"; fi; \
+ $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \
+ $(CFLAGS) instrumented-app.o tracepoint-trigger-example.a $(LIBS_INSTRUMENTED_APP)
+
+notification-client.o: notification-client.c
+ @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \
+ $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+ $(CFLAGS) -c -o $@ $<
+
+notification-client: notification-client.o
+ @if $(AM_V_P); then set -x; else echo " CCLD $@"; fi; \
+ $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \
+ $(CFLAGS) notification-client.o tracepoint-trigger-example.a $(LIBS_NOTIFICATION_CLIENT)
+
+.PHONY: clean
+clean:
+ rm -f *.o *.a instrumented-app notification-client
--- /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.
+
+## Building
+
+Simply run the included Makefile.
+
+## 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.
+Subscribed to notifications of trigger "demo_trigger"
+[02-14-2020] 18:13:34.779652 - Received notification of event rule trigger "demo_trigger_capture"
+Captured field values:
+ Unsigned int: 0,
+ CAPTURE UNAVAILABE
+[02-14-2020] 18:13:34.779766 - Received notification of event rule trigger "demo_trigger"
+[02-14-2020] 18:13:36.779798 - Received notification of event rule trigger "demo_trigger_capture"
+Captured field values:
+ Unsigned int: 1,
+ CAPTURE UNAVAILABE
+[02-14-2020] 18:13:36.779888 - Received notification of event rule trigger "demo_trigger"
+[02-14-2020] 18:13:38.780234 - Received notification of event rule trigger "demo_trigger_capture"
+Captured field values:
+ Unsigned int: 2,
+ CAPTURE UNAVAILABE
+[02-14-2020] 18:13:38.780514 - Received notification of event rule trigger "demo_trigger"
+[02-14-2020] 18:13:40.780574 - Received notification of event rule trigger "demo_trigger_capture"
+Captured field values:
+ Unsigned int: 3,
+ CAPTURE UNAVAILABE
+[02-14-2020] 18:13:40.780656 - Received notification of event rule trigger "demo_trigger"
+```
+
+```
+$ ./instrumented-app
+[02-14-2020] 18:13:34.779433 - Tracing event "trigger_example:my_event"
+[02-14-2020] 18:13:36.779693 - Tracing event "trigger_example:my_event"
+[02-14-2020] 18:13:38.780010 - Tracing event "trigger_example:my_event"
+[02-14-2020] 18:13:40.780286 - Tracing event "trigger_example:my_event"
+```
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+# SPDX-License-Identifier: MIT
+
+EVENT_NAME=trigger_example:my_event
+TRIGGER_NAME=demo_trigger
+TRIGGER_NAME_CAPTURE=demo_trigger_capture
+
+lttng list > /dev/null 2>&1
+if [ $? -ne 0 ]; then
+ echo "Could not connect to session daemon, are you sure it is running?"
+ exit 1
+fi
+
+echo "Registering a notification trigger named \"$TRIGGER_NAME\" for the $EVENT_NAME user-space event"
+lttng add-trigger --id $TRIGGER_NAME --condition on-event --userspace $EVENT_NAME --action notify
+lttng add-trigger --id $TRIGGER_NAME_CAPTURE --condition on-event --userspace $EVENT_NAME --capture 'iteration' --capture 'does_not_exist' --action notify
+
+./notification-client $TRIGGER_NAME $TRIGGER_NAME_CAPTURE
+
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#include "tracepoint-trigger-example.h"
+
+#include <lttng/tracepoint.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ uint64_t i;
+
+ for (i = 0; i < UINT64_MAX; i++) {
+ char time_str[64];
+ struct timeval tv;
+ time_t the_time;
+
+ gettimeofday(&tv, NULL);
+ the_time = tv.tv_sec;
+
+ strftime(time_str, sizeof(time_str), "[%m-%d-%Y] %T",
+ localtime(&the_time));
+ printf("%s.%ld - Tracing event \"trigger_example:my_event\"\n", time_str, tv.tv_usec);
+
+ tracepoint(trigger_example, my_event, i);
+ sleep(2);
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#include <lttng/condition/event-rule.h>
+#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_event_field_value *capture,
+ unsigned int indent_level);
+static int print_array(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 bool action_group_contains_notify(
+ const struct lttng_action *action_group)
+{
+ unsigned int i, count;
+ enum lttng_action_status status =
+ lttng_action_group_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_group_get_at_index_const(
+ 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_event_field_value *capture,
+ unsigned int indent_level)
+{
+ int ret = 0;
+ enum lttng_event_field_value_status event_field_status;
+ enum lttng_event_field_value_type type;
+ uint64_t u_val;
+ int64_t s_val;
+ double d_val;
+ const char *string_val = NULL;
+
+ indent(indent_level);
+
+ 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:
+ {
+ string_val = lttng_event_field_value_string_get_value(capture);
+ if (string_val == NULL) {
+ ret = 1;
+ goto end;
+ }
+
+ printf("String: %s", string_val);
+ break;
+ }
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY:
+ printf("Array: [\n");
+ print_array(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(unsigned int indent_level)
+{
+ indent(indent_level);
+ printf("CAPTURE UNAVAILABE");
+}
+
+static int print_array(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;
+ 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(indent_level + 1);
+ } else {
+ ret = 1;
+ goto end;
+ }
+ }
+ print_capture(captured_field, indent_level + 1);
+
+ if (i + 1 < captured_field_count) {
+ 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_status evaluation_status;
+ enum lttng_event_field_value_status event_field_status;
+
+ const struct lttng_event_field_value *captured_field_array = NULL;
+ unsigned int expected_capture_field_count;
+ unsigned int captured_field_count;
+
+ assert(lttng_evaluation_get_type(evaluation) ==
+ LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+ condition_status =
+ lttng_condition_event_rule_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_get_captured_values(
+ evaluation, &captured_field_array);
+ if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) {
+ ret = 1;
+ goto end;
+ }
+
+ printf("Captured field values:\n");
+ print_array(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_HIT:
+ {
+ const char *trigger_name;
+ enum lttng_evaluation_status evaluation_status;
+ char time_str[64];
+ struct timeval tv;
+ time_t the_time;
+
+ 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);
+
+ evaluation_status =
+ lttng_evaluation_event_rule_get_trigger_name(
+ evaluation, &trigger_name);
+ if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) {
+ fprintf(stderr, "Failed to get trigger name of event rule notification\n");
+ ret = -1;
+ break;
+ }
+
+ printf("Received notification of event rule trigger \"%s\"\n",
+ trigger_name);
+ ret = print_captures(notification);
+ break;
+ }
+ default:
+ fprintf(stderr, "Unknown notification type (%d)\n", type);
+ }
+
+ 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) {
+ 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_GROUP &&
+ 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;
+}
--- /dev/null
+# Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+# OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
+#
+# Permission is hereby granted to use or copy this program for any
+# purpose, provided the above notices are retained on all copies.
+# Permission to modify the code and to distribute modified code is
+# granted, provided the above notices are retained, and a notice that
+# the code was modified is included with the above copyright notice.
+#
+# This Makefile is not using automake so that users may see how to build
+# a program with tracepoint provider probes compiled as static libraries.
+#
+# This makefile is purposefully kept simple to support GNU and BSD make.
+
+LOCAL_CPPFLAGS += -I.
+LIBS_INSTRUMENTED_APP = -ldl -llttng-ust
+LIBS_NOTIFICATION_CLIENT = -ldl -llttng-ctl
+LIBS_PERFORMANCE_CLIENT = -ldl -llttng-ust -llttng-ctl
+AM_V_P := :
+AR ?= ar
+
+all: producer consumer
+
+performance.o: performance.c performance.h
+ @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \
+ $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+ $(CFLAGS) -c -o $@ $<
+
+performance.a: performance.o
+ @if $(AM_V_P); then set -x; else echo " AR $@"; fi; \
+ $(AR) -rc $@ performance.o
+
+producer.o: producer.c
+ @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \
+ $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+ $(CFLAGS) -c -o $@ $<
+
+consumer.o: consumer.c
+ @if $(AM_V_P); then set -x; else echo " CC $@"; fi; \
+ $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) \
+ $(CFLAGS) -c -o $@ $<
+
+producer: producer.o performance.a
+ @if $(AM_V_P); then set -x; else echo " CCLD $@"; fi; \
+ $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \
+ $(CFLAGS) producer.o performance.a $(LIBS_PERFORMANCE_CLIENT)
+
+consumer: consumer.o performance.a
+ @if $(AM_V_P); then set -x; else echo " CCLD $@"; fi; \
+ $(CC) -o $@ $(LDFLAGS) $(CPPFLAGS) $(AM_LDFLAGS) $(AM_CFLAGS) \
+ $(CFLAGS) consumer.o performance.a $(LIBS_PERFORMANCE_CLIENT)
+
+.PHONY: clean
+clean:
+ rm -f *.o *.a producer consumer
--- /dev/null
+# Trigger notification end-to-end latency analysis
+
+## Description
+This analysis is made-up of five executables.
+
+### `producer`
+
+```
+Usage: producer UNIQUE_ID NB_EVENT DELAY_MS
+```
+
+An application that emits `NB_EVENT` times the `performance:hit` event every
+`DELAY_MS` milliseconds.
+
+
+### `consumer`
+```
+Usage: consumer UNIQUE_ID NB_EVENT TRIGGER_NAME
+```
+
+A simple notification client that subscribes to the notifications emitted by the
+`TRIGGER_NAME` trigger. The consumer expects `NB_EVENT` notification and on each
+valid reception emits a `performance:receive` event.
+
+
+### `perform-experience.sh`
+
+```
+Usage: perform-experience.sh SOURCE_ID TRACE_DIRECTORY_NAME DELAY_MS NB_EVENT`
+```
+
+This script performs a complete end-to-end trigger latency experience with
+`DELAY_MS` between each trigger hit and `NB_EVENT` times.
+
+The resulting lttng-ust trace is stored inside `$(pwd)/trace/TRACE_DIRECTORY_NAME`
+
+### `generate-data.sh`
+
+```
+Usage: generate-data.sh
+```
+
+This script performs all configured experiences and apply the customs workload
+as necessary.
+
+The resulting traces are stored inside `$(pwd)/trace/`
+
+This script in its current form will run for about 25 hours.
+
+This script depends on `perform-experience.sh`.
+
+### `generate-graph.sh`
+
+```
+Usage: generate-graph.sh
+```
+
+This script generate all histograms and saved them individually as pdf files. It
+also generate a `summary.pdf` files that contains all pdfs in order of trigger frequency.
+
+This script does not have to run on the machine that produced the data.
+
+This script requires the presence of the "trace/" folder to work.
+
+This script depends on the `bt_plugin_plot.py` babeltrace 2 plugins. Hence this
+script requires Babeltrace 2 with python bindings and python plugin support.
+
+The `bt_plugin_plot.py` requires `matplotlib`.
+
+
+## Building
+
+Simply run the included Makefile.
+
+## Running the complete
+
+1) Launch a session daemon using:
+ ```
+ $ lttng-sessiond
+ ```
+2) Launch `generate-data.sh`
+3) Wait ~25 hours
+3) Launch `generate-graph.sh`
--- /dev/null
+import bt2
+import itertools
+import matplotlib.pyplot as plt
+import sys
+import statistics
+import csv
+from collections import defaultdict
+
+
+class DataLogger(object):
+ def __init__(self, name="Untitled"):
+ self._name = name
+
+ def get_name(self):
+ return self._name
+
+ def get_x_data(self):
+ raise NotImplementedError
+
+ def get_y_data(self):
+ raise NotImplementedError
+
+ def received_event(self, ts, event):
+ raise NotImplementedError
+
+
+class DurationDataLogger(DataLogger):
+ """
+ This class allow to create a duration histogram for the given pair of
+ event and unique tuple key generator.
+
+ """
+ def __init__(self, start_event, end_event, *args, **kwargs):
+ super(DurationDataLogger, self).__init__(*args, **kwargs)
+
+ (self._event_start, self._start_fields) = start_event
+ (self._event_end, self._end_fields) = end_event
+
+ self._durations = []
+ self._pair = dict()
+
+ def get_x_data(self):
+ return self._durations
+
+ def received_event(self, ts, event):
+ if event.name == self._event_start:
+ key = ()
+ for field in self._start_fields:
+ value = event.payload_field[str(field)]
+ key = key + (value,)
+ self._pair[key] = ts
+ return
+
+ if event.name == self._event_end:
+ key = ()
+ for field in self._end_fields:
+ value = event.payload_field[str(field)]
+ key = key + (value,)
+
+ if key not in self._pair:
+ print("unmatched end event")
+ return
+
+ start_ts = self._pair[key]
+ duration = (ts - start_ts) / 1000000.0
+ self._durations.append(duration)
+
+class DurationCSVDataLogger(DataLogger):
+ """
+ This class allow to create a duration histogram for the given csv.
+ """
+ def __init__(self, filepath, *args, **kwargs):
+ super(DurationCSVDataLogger, self).__init__(*args, **kwargs)
+
+ self._filepath = filepath
+
+
+ self._durations = []
+ with open(filepath, newline='') as file:
+ reader = csv.reader(file, quoting=csv.QUOTE_NONE)
+ next(reader)
+ for row in reader:
+ self._durations.append(float(row[0]))
+
+ def get_x_data(self):
+ return self._durations
+
+ def received_event(self, ts, event):
+ return
+
+
+class Plot(object):
+ def __init__(
+ self, loggers, title="Untitled", x_label="Untitled", y_label="Untitled"
+ ):
+ self._loggers = loggers
+ self._title = title
+ self._x_label = x_label
+ self._y_label = y_label
+
+ def received_event(self, ts, event):
+ for logger in self._loggers:
+ logger.received_event(ts, event)
+
+ def plot(self):
+ raise NotImplementedError
+
+ def generate_csv(self):
+ raise NotImplementedError
+
+ @staticmethod
+ def _format_filename(title, ext):
+ title = title.lower()
+ title = "".join("-" if not c.isalnum() else c for c in title)
+ title = "".join(
+ ["".join(j) if i != "-" else i for (i, j) in itertools.groupby(title)]
+ )
+ return f"{title}.{ext}"
+
+class HistogramPlot(Plot):
+ def __init__(self, *args, **kwargs):
+ super(HistogramPlot, self).__init__(*args, **kwargs)
+
+ @staticmethod
+ def get_statistics_header():
+ return ["minimum", "maximum", "mean", "pstdev", "count"]
+
+ @staticmethod
+ def get_statistics(samples):
+ stats = []
+ stats.append('%f' % min(samples))
+ stats.append('%f' % max(samples))
+ stats.append('%f' % statistics.mean(samples))
+ stats.append('%f' % statistics.pstdev(samples))
+ stats.append('%d' % len(samples))
+ return stats
+
+ def plot(self):
+ sys.argv = ['']
+ complete_set = [];
+ logger_statistic = defaultdict(dict)
+
+ figure = plt.figure()
+ plt.title(self._title)
+ plt.xlabel(self._x_label, figure=figure)
+ plt.ylabel(self._y_label, figure=figure)
+ plt.yscale('log', nonposy='clip')
+
+ table_rows_label = []
+ table_celltext = []
+ for logger in self._loggers:
+ x = logger.get_x_data()
+ table_rows_label.append(logger.get_name())
+ table_celltext.append(HistogramPlot.get_statistics(x))
+
+ complete_set +=x;
+ plt.hist(x, bins='auto', alpha=0.5, figure=figure, label=logger.get_name())
+
+ table_rows_label.append("all")
+ table_celltext.append(HistogramPlot.get_statistics(complete_set))
+ the_table = plt.table(cellText=table_celltext,
+ rowLabels=table_rows_label,
+ colLabels=HistogramPlot.get_statistics_header(),
+ loc='bottom',
+ bbox=[0.0,-0.45,1,.28],
+ )
+
+ the_table.auto_set_font_size(False)
+ the_table.set_fontsize(8)
+
+ plt.subplots_adjust(bottom=0.20)
+ plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
+ plt.savefig(Plot._format_filename(self._title, "pdf"), bbox_inches="tight")
+
+ def generate_csv(self):
+ for logger in self._loggers:
+ x_data = logger.get_x_data()
+ with open(Plot._format_filename(self._title, "%s.csv" % logger.get_name()), 'w', newline='') as export:
+ wr = csv.writer(export, quoting=csv.QUOTE_NONE)
+ wr.writerow([self._x_label])
+ for x in x_data:
+ wr.writerow([x])
+
+
+@bt2.plugin_component_class
+class PlotSink(bt2._UserSinkComponent):
+ def __init__(self, config, params, obj):
+ self._plots = []
+
+ if "histograms" in params:
+ for plot in params["histograms"]:
+ self._plots.append(PlotSink.create_histogram(plot))
+
+ self._add_input_port("in")
+
+ def _user_consume(self):
+ msg = next(self._iter)
+ if type(msg) in [
+ bt2._PacketBeginningMessageConst,
+ bt2._PacketEndMessageConst,
+ bt2._StreamBeginningMessageConst,
+ bt2._StreamEndMessageConst,
+ ]:
+ return
+
+ ts = msg.default_clock_snapshot.value
+ for plot in self._plots:
+ plot.received_event(ts, msg.event)
+
+ def _user_finalize(self):
+ {plot.plot() for plot in self._plots}
+ {plot.generate_csv () for plot in self._plots}
+ return
+
+ def _user_graph_is_configured(self):
+ self._iter = self._create_message_iterator(self._input_ports["in"])
+
+ @staticmethod
+ def create_histogram(params):
+ loggers = []
+ for logger in params[3]:
+ if logger[0] == "duration":
+ logger = PlotSink.create_duration_logger(logger)
+ elif logger[0] == "duration-csv":
+ logger = PlotSink.create_duration_logger_csv(logger)
+ else:
+ raise ValueError
+
+ loggers.append(logger)
+
+ title = str(params[0])
+ x_label = str(params[1])
+ y_label = str(params[2])
+
+ return HistogramPlot(loggers, title=title, x_label=x_label,
+ y_label=y_label)
+
+ @staticmethod
+ def create_duration_logger(params):
+ return DurationDataLogger(
+ (str(params[2]), params[3]),
+ (str(params[4]), params[5]),
+ name=str(params[1]),
+ )
+
+ def create_duration_logger_csv(params):
+ return DurationCSVDataLogger(
+ str(params[2]),
+ name=str(params[1]),
+ )
+
+
+bt2.register_plugin(
+ module_name=__name__,
+ name="plot",
+ description="Plot Sink",
+ author="EfficiOS inc.",
+ license="GPL",
+ version=(1, 0, 0),
+)
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2020 Jonathan Rajotte-Julien
+ * <jonathan.rajotte-julein@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+#include "performance.h"
+
+#include <lttng/tracepoint.h>
+
+#include <lttng/condition/event-rule.h>
+#include <lttng/lttng.h>
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+bool action_group_contains_notify(const struct lttng_action *action_group)
+{
+ unsigned int i, count;
+ enum lttng_action_status status =
+ lttng_action_group_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_group_get_at_index_const(
+ 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;
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ int id, nb_reception;
+ struct lttng_triggers *triggers = NULL;
+ unsigned int count, i, subcription_count = 0;
+ enum lttng_trigger_status trigger_status;
+ struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_notification *notification;
+ enum lttng_notification_channel_status channel_status;
+ const struct lttng_evaluation *evaluation;
+ enum lttng_condition_type type;
+
+ if (argc != 4) {
+ fprintf(stderr, "Missing unique_id\n");
+ fprintf(stderr, "Missing nb_event\n");
+ fprintf(stderr, "Missing trigger name\n");
+ fprintf(stderr, "Usage: notification-client TRIGGER_NAME\n");
+ ret = 1;
+ goto end;
+ }
+
+ id = atoi(argv[1]);
+ nb_reception = atoi(argv[2]);
+
+ 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) {
+ 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;
+
+ lttng_trigger_get_name(trigger, &trigger_name);
+ if (strcmp(trigger_name, argv[3])) {
+ continue;
+ }
+
+ if (!((action_type == LTTNG_ACTION_TYPE_GROUP &&
+ 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) {
+ 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 (int i = 0; i < nb_reception; i++) {
+ 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");
+ sleep(1);
+ continue;
+ 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;
+ }
+
+ evaluation = lttng_notification_get_evaluation(notification);
+ type = lttng_evaluation_get_type(evaluation);
+
+ if (type != LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+ assert(0);
+ }
+
+ tracepoint(performance, receive, id, i);
+ lttng_notification_destroy(notification);
+ if (ret) {
+ goto end;
+ }
+ }
+
+end:
+ lttng_triggers_destroy(triggers);
+ lttng_notification_channel_destroy(notification_channel);
+ return !!ret;
+}
--- /dev/null
+#!/bin/bash -x
+#
+# Copyright (C) 2020 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: MIT
+
+cpu_stressor=$(nproc)
+
+while read -r load delay count; do
+ stress-ng --cpu-load "$load" --cpu "$cpu_stressor" &
+ stress_ng_id=$!
+
+ # Let the workload stabilize
+ sleep 5
+
+ ./perform-experience.sh 0 "${load}_cpuload_${delay}ms" "$delay" "$count"
+ kill "$stress_ng_id"
+ wait
+done << EOF
+0 1 10000
+25 1 10000
+50 1 10000
+75 1 10000
+100 1 10000
+0 10 5000
+25 10 5000
+50 10 5000
+75 10 5000
+100 10 5000
+0 100 6000
+25 100 6000
+50 100 6000
+75 100 6000
+100 100 6000
+0 1000 6000
+25 1000 6000
+50 1000 6000
+75 1000 6000
+100 1000 6000
+0 10000 600
+25 10000 600
+50 10000 600
+75 10000 600
+100 10000 600
+0 60000 100
+25 60000 100
+50 60000 100
+75 60000 100
+100 60000 60
+EOF
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2020 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: MIT
+
+plugin_path=$(dirname "$0")
+
+DATA1="[\"duration\", \"D1\", \"performance:hit\", [\"source\", \"iteration\"], \"performance:receive\", [\"source\", \"iteration\"]]"
+
+while read -r load delay count; do
+ S=$(echo "scale=3; 1 / ( $delay / 1000 )" | bc | awk '{printf "%.3f", $0}' );
+ hz=${S/.000/}
+ hz_title=${hz/./-}
+
+ echo "Graphing Hz: ${hz} CPU: ${load}"
+
+ PLOT1="[\"Trigger latency, Freq:${hz}Hz, CPU load: ${load}%\", \"T (ms)\", \"count\", [$DATA1]]"
+
+ babeltrace2 --plugin-path="$plugin_path" --component sink.plot.PlotSink \
+ --params="histograms=[$PLOT1]" \
+ "./trace/${load}_cpuload_${delay}ms"
+done << EOF
+0 1 10000
+25 1 10000
+50 1 10000
+75 1 10000
+100 1 10000
+0 10 5000
+25 10 5000
+50 10 5000
+75 10 5000
+100 10 5000
+0 100 6000
+25 100 6000
+50 100 6000
+75 100 6000
+100 100 6000
+0 1000 1500
+25 1000 1500
+50 1000 1500
+75 1000 1500
+100 1000 1500
+0 10000 300
+25 10000 300
+50 10000 300
+75 10000 300
+100 10000 300
+0 60000 50
+25 60000 50
+50 60000 50
+75 60000 50
+100 60000 50
+EOF
+
+pdf_unite=""
+csvs=""
+# Generate united graph and base pdf list to unite
+while read -r delay ; do
+ S=$(echo "scale=3; 1 / ( $delay / 1000 )" | bc | awk '{printf "%.3f", $0}' );
+ hz=${S/.000/}
+ hz_title=${hz/./-}
+ local_pdf_unite=""
+ echo "Combining graphs for Hz: ${hz} hz_title: $hz_title"
+
+ loggers=""
+ for load in 100 75 50 25 0; do
+ path=trigger-latency-freq-${hz_title}hz-cpu-load-${load}-.D1.csv
+ csvs="$csvs $path"
+ loggers="[\"duration-csv\", \"${load}% CPU\", \"${path}\"], $loggers"
+ local_pdf_unite="${local_pdf_unite} trigger-latency-freq-${hz_title}hz-cpu-load-${load}-.pdf"
+ done
+ pdf_unite="$pdf_unite $local_pdf_unite"
+
+ PLOT1="[\"Trigger latency, Freq:${hz}Hz\", \"T (ms)\", \"count\", [$loggers]]"
+ babeltrace2 --plugin-path="$plugin_path" --component sink.plot.PlotSink \
+ --params="histograms=[$PLOT1]" \
+ "./trace/0_cpuload_${delay}ms"
+done << EOF
+1
+10
+100
+1000
+10000
+60000
+EOF
+
+# Add united graphs to the pdfunite cmd
+while read -r delay ; do
+ S=$(echo "scale=3; 1 / ( $delay / 1000 )" | bc | awk '{printf "%.3f", $0}' );
+ hz=${S/.000/}
+ hz_title=${hz/./-}
+
+ pdf_unite="trigger-latency-freq-${hz_title}hz.pdf $pdf_unite"
+
+done << EOF
+60000
+10000
+1000
+100
+10
+1
+EOF
+
+rm -rf $csvs
+
+pdfunite $pdf_unite summary.pdf
+
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2020 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: MIT
+
+EVENT_NAME_HIT=performance:hit
+EVENT_NAME_RECEIVE=performance:receive
+TRIGGER_NAME=performance_hit
+
+if [ -z "$1" ]; then
+ echo "missing source id int value"
+ exit 1
+fi
+
+if [ -z "$2" ]; then
+ echo "missing trace directory name"
+ exit 1
+fi
+
+if [ -z "$3" ]; then
+ echo "missing loop delay"
+ exit 1
+fi
+
+if [ -z "$4" ]; then
+ echo "missing loop count"
+ exit 1
+fi
+
+key_id="$1"
+trace_directory="$(pwd)/trace/$2"
+delay=$3
+count=$4
+
+if ! lttng list > /dev/null 2>&1 ; then
+ echo "Could not connect to session daemon, are you sure it is running?"
+ exit 1
+fi
+
+lttng create performance --output="$trace_directory"
+lttng enable-event -u $EVENT_NAME_HIT,$EVENT_NAME_RECEIVE -s performance
+lttng start
+
+filter="source==$key_id"
+lttng add-trigger --id ${TRIGGER_NAME} --condition on-event --userspace $EVENT_NAME_HIT --filter="$filter" --action notify
+
+./consumer "$key_id" "$count" $TRIGGER_NAME &
+
+# Cheap way to synchronize and ensure that the consumer is ready to consume
+sleep 2
+
+./producer "$key_id" "$count" "$delay" &
+
+wait
+
+lttng remove-trigger ${TRIGGER_NAME}
+
+lttng stop
+lttng destroy performance
+
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#define TRACEPOINT_DEFINE
+#define TRACEPOINT_CREATE_PROBES
+#include "performance.h"
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER performance
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "./performance.h"
+
+#if !defined(_TRACEPOINT_PERFORMANCE_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_PERFORMANCE_H
+
+#include <lttng/tracepoint.h>
+
+TRACEPOINT_EVENT(performance, hit,
+ TP_ARGS(int, source,
+ int, iteration),
+ TP_FIELDS(
+ ctf_integer(uint64_t, source, source)
+ ctf_integer(uint64_t, iteration, iteration)
+ )
+)
+
+TRACEPOINT_EVENT(performance, receive,
+ TP_ARGS(int, source,
+ int, iteration),
+ TP_FIELDS(
+ ctf_integer(uint64_t, source, source)
+ ctf_integer(uint64_t, iteration, iteration)
+ )
+)
+
+#endif /* _TRACEPOINT_PERFORMANCE_H */
+
+#include <lttng/tracepoint-event.h>
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2020 Jonathan Rajotte-Julien <jonathan.rajotte-julein@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+#include "performance.h"
+
+#include <lttng/tracepoint.h>
+
+#include <lttng/condition/event-rule.h>
+#include <lttng/lttng.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <assert.h>
+
+int main(int argc, char **argv)
+{
+ int ret = 0;
+ int nb_hit;
+ int id;
+ long long sleep_ms;
+ struct timespec sleep_time;
+ struct timespec sleep_rm;
+
+ if (argc != 4) {
+ fprintf(stderr, "Missing unique_id\n");
+ fprintf(stderr, "Missing number of event \n");
+ fprintf(stderr, "Missing delay between event in ms \n");
+ fprintf(stderr, "Usage: producer id bn_event delay_ms\n");
+ ret = 1;
+ goto end;
+ }
+
+ id = atoi(argv[1]);
+ nb_hit = atoi(argv[2]);
+ sleep_ms = atoll(argv[3]);
+
+ sleep_time.tv_sec = sleep_ms / 1000;
+ sleep_time.tv_nsec = (sleep_ms % 1000) * 1000000;
+
+ for (int i = 0; i < nb_hit; i++) {
+ tracepoint(performance, hit, 0, i);
+ nanosleep(&sleep_time, &sleep_rm);
+ }
+
+end:
+ return !!ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#define TRACEPOINT_DEFINE
+#define TRACEPOINT_CREATE_PROBES
+#include "tracepoint-trigger-example.h"
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER trigger_example
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "./tracepoint-trigger-example.h"
+
+#if !defined(_TRACEPOINT_TRIGGER_EXAMPLE_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_TRIGGER_EXAMPLE_H
+
+#include <lttng/tracepoint.h>
+
+TRACEPOINT_EVENT(trigger_example, my_event,
+ TP_ARGS(int, iteration),
+ TP_FIELDS(
+ ctf_integer(uint64_t, iteration, iteration)
+ )
+)
+
+#endif /* _TRACEPOINT_TRIGGER_EXAMPLE_H */
+
+#include <lttng/tracepoint-event.h>
lttng-rotate \
lttng-enable-rotation \
lttng-disable-rotation \
- lttng-clear
+ lttng-clear \
+ lttng-add-trigger \
+ lttng-remove-trigger \
+ lttng-list-triggers
+
MAN3_NAMES =
MAN8_NAMES = lttng-sessiond lttng-relayd
MAN1_NO_ASCIIDOC_NAMES =
--- /dev/null
+lttng-add-trigger(1)
+=====================
+:revdate: 27 May 2020
+
+
+NAME
+----
+lttng-add-trigger - Create LTTng triggers
+
+
+SYNOPSIS
+--------
+
+[verse]
+*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *add-trigger* [--id ID]
+ [--fire-every N] [--fire-once-after N]
+ --condition CONDITION-NAME CONDITION-ARGS
+ --action ACTION-NAME ACTION-ARGS
+ [--action ACTION-NAME ACTION-ARGS]...
+
+
+DESCRIPTION
+-----------
+
+The `lttng add-trigger` command is used to create triggers. A
+trigger is an association between a *condition* and one or more
+*actions*. When the condition associated to a trigger is met, the
+actions associated to that trigger are executed. The tracing does not
+have to be active for the conditions to be met, and triggers are
+independent from tracing sessions.
+
+By default, a trigger fires every time its condition is met. The
+'--fire-every' and '--fire-once-after' options can be used to change
+this mode.
+
+The syntax by which conditions and actions are specified is described
+below.
+
+[[conditions]]
+Conditions
+~~~~~~~~~~
+
+Conditions are specified with the `--condition` option, followed by a
+condition name, and possibly some more arguments, depending on the
+specific condition. There must be exactly one condition given in the
+`lttng add-trigger` invocation.
+
+The available conditions are:
+
+Event rule: `on-event [event rule arguments]`::
+ This type of condition is met when the tracer encounters an event
+ matching the given even rule. The arguments describing the event
+ rule are the same as those describing the event rules of the
+ man:lttng-enable-event(1) command, with these exceptions:
+
+ - It is not possible to use filter operands that use values from
+ the context.
+
++
+Fields to capture can be specified with the option:--capture option, followed by
+a capture expression. Zero or more captures can be configured. See the
+<<capture-expr, Capture expression>> section below for more information.
+
+[[actions]]
+Actions
+~~~~~~~
+
+Actions are specified with the `--action` option, followed by an action
+name, and possibly some more arguments, depending on the specific
+action. There must be at least one action given in the
+`lttng add-trigger` invocation.
+
+The available actions are:
+
+Notify: *notify*::
+ This action causes the LTTng session daemon to send a notification,
+ through its notification mechanism (see man:lttng-sessiond(8)).
+ Some details about the condition evaluation are sent along with the
+ notification.
+
+Start session: *start-session* session-name::
+ This action causes the LTTng session daemon to start tracing for the
+ session with the given name. If no session with the given name exist
+ at the time the condition is met, nothing is done.
+
+Stop session: *stop-session* session-name::
+ This action causes the LTTng session daemon to stop tracing for the
+ session with the given name. If no session with the given name exist
+ at the time the condition is met, nothing is done.
+
+Rotate session: *rotate-session* session-name::
+ This action causes the LTTng session daemon to rotate the session
+ with the given name. See man:lttng-rotate(1) for more information
+ about the session rotation concept. If no session with the given
+ name exist at the time the condition is met, nothing is done.
+
+Snapshot session: *snapshot-session* session-name::
+ This action causes the LTTng session daemon to take a snapshot of the
+ session with the given name. See man:lttng-snapshot(1) for more
+ information about the session snapshot concept. If no session with
+ the given name exist at the time the condition is met, nothing is
+ done.
+
+
+[[capture-expr]]
+Capture expression
+~~~~~~~~~~~~~~~~~~
+
+A capture expression can be specified with the option:--capture option when
+creating a new on-event condition. If the capture expression corresponds with an
+event's field when tracing, the runtime dynamic value corresponding to the
+capture expression is captured.
+
+NOTE: Make sure to **single-quote** the capture expression when running
+the command from a shell, as capture expressions typically include
+characters having a special meaning for most shells.
+
+* Supported field types:
+ - integer,
+ - unsigned integer,
+ - floating point value,
+ - fixed-size array of integers,
+ - variable-size array of integers (sequence),
+ - enumeration,
+ - text string,
+ - element of any allowing previous type.
+
+* The dynamic value of an event field is captured by using its name as a C
+ identifier.
++
+The square bracket notation is available, like in the C
+language, to access array/sequence field.
+Only a constant, positive integer number can be used within square
+brackets. If the index is out of bounds, the capture expression
+evaluates to `unavailable`.
++
+An enumeration field's value is an integer.
++
+When the capture's field does not exist, the capture expression
+evaluates to `unavailable`.
++
+Examples: `my_field`, `target_cpu`, `seq[7]`
+
+* The dynamic value of a statically-known context field is captured by
+ prefixing its name with `$ctx.`. See man:lttng-add-context(1) to get a list of
+ available contexts.
++
+When the expression's statically-known context field does not exist,
+the capture expression evaluates to `unavailable`.
++
+Examples: `$ctx.prio`, `$ctx.preemptible`,
+`$ctx.perf:cpu:stalled-cycles-frontend`.
++
+NOTE: The statically-known context field does NOT need to be added using the
+man:lttng-add-context(1) command. The statically-known context fields are
+always available in the context of triggers.
+
+* The dynamic value of an application-specific context field is captured by
+ prefixing its name with `$app.` (follows the format used to add such a context
+ field with the man:lttng-add-context(1) command).
++
+When the expression's application-specific context field does not exist,
+the capture expression evaluates to `unavailable`.
++
+Example: `$app.server:cur_user`.
++
+NOTE: The application-specific context field does NOT need to be added using the
+man:lttng-add-context(1) command. The application-specific context fields fields
+are always available in the context of triggers.
+
+
+OPTIONS
+-------
+
+option:--condition::
+ Define the condition for the trigger. See the
+ <<conditions,CONDITIONS>> section for more details.
+
+option:--action::
+ Define an action for the trigger. See the <<actions,ACTIONS>>
+ section for more details.
+
+option:--id='ID'::
+ Set the id of the trigger to 'ID'. If omitted, an id will
+ automatically be assigned to the trigger by the session daemon.
++
+If a trigger with the specified 'ID' already exists, the trigger
+creation will fail.
+
+option:--fire-every 'N'::
+ Execute the trigger's actions every 'N' times the condition is met.
+
+option:--fire-once-after 'N'::
+ Execute the trigger's actions once after 'N' times the condition is
+ met, then never after that.
+
+include::common-cmd-help-options.txt[]
+
+
+include::common-cmd-footer.txt[]
+
+
+SEE ALSO
+--------
+man:lttng-list-triggers(1),
+man:lttng-remove-trigger(1),
+man:lttng(1)
--- /dev/null
+lttng-list-triggers(1)
+======================
+:revdate: 20 January 2020
+
+
+NAME
+----
+lttng-list-triggers - List LTTng triggers
+
+
+SYNOPSIS
+--------
+
+[verse]
+*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *list-triggers*
+
+
+DESCRIPTION
+-----------
+
+The `lttng list-triggers` command list the existing triggers.
+
+
+OPTIONS
+-------
+
+include::common-cmd-help-options.txt[]
+
+
+include::common-cmd-footer.txt[]
+
+
+SEE ALSO
+--------
+man:lttng-add-trigger(1),
+man:lttng-remove-trigger(1),
+man:lttng(1)
--- /dev/null
+lttng-remove-trigger(1)
+========================
+:revdate: 20 January 2020
+
+
+NAME
+----
+lttng-remove-trigger - Remove LTTng triggers
+
+
+SYNOPSIS
+--------
+
+[verse]
+*lttng* ['linkgenoptions:(GENERAL OPTIONS)'] *remove-trigger* 'ID'
+
+
+DESCRIPTION
+-----------
+
+The `lttng remove-trigger` command removes the trigger with the given
+'ID'.
+
+
+OPTIONS
+-------
+
+include::common-cmd-help-options.txt[]
+
+
+include::common-cmd-footer.txt[]
+
+
+SEE ALSO
+--------
+man:lttng-add-trigger(1),
+man:lttng-list-trigger(1),
+man:lttng(1)
lttng/channel.h \
lttng/domain.h \
lttng/event.h \
+ lttng/event-expr.h \
+ lttng/event-field-value.h \
lttng/handle.h \
lttng/session.h \
lttng/lttng-error.h \
lttngactioninclude_HEADERS= \
lttng/action/action.h \
- lttng/action/notify.h
+ lttng/action/group.h \
+ lttng/action/notify.h \
+ lttng/action/rotate-session.h \
+ lttng/action/snapshot-session.h \
+ lttng/action/start-session.h \
+ lttng/action/stop-session.h
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
lttngtriggerinclude_HEADERS= \
lttng/trigger/trigger.h
+lttngeventruleinclude_HEADERS= \
+ lttng/event-rule/event-rule.h \
+ lttng/event-rule/kprobe.h \
+ lttng/event-rule/kretprobe.h \
+ lttng/event-rule/syscall.h \
+ lttng/event-rule/uprobe.h \
+ lttng/event-rule/tracepoint.h
+
noinst_HEADERS = \
lttng/snapshot-internal.h \
lttng/health-internal.h \
lttng/save-internal.h \
lttng/load-internal.h \
lttng/action/action-internal.h \
+ lttng/action/group-internal.h \
lttng/action/notify-internal.h \
+ lttng/action/rotate-session-internal.h \
+ lttng/action/snapshot-session-internal.h \
+ lttng/action/start-session-internal.h \
+ 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 \
lttng/endpoint-internal.h \
lttng/notification/channel-internal.h \
lttng/channel-internal.h \
+ lttng/domain-internal.h \
lttng/event-internal.h \
+ lttng/event-expr-internal.h \
+ lttng/event-field-value-internal.h \
lttng/rotate-internal.h \
lttng/ref-internal.h \
lttng/location-internal.h \
lttng/userspace-probe-internal.h \
lttng/session-internal.h \
lttng/session-descriptor-internal.h \
+ lttng/event-rule/event-rule-internal.h \
+ lttng/event-rule/kprobe-internal.h \
+ lttng/event-rule/kretprobe-internal.h \
+ lttng/event-rule/uprobe-internal.h \
+ lttng/event-rule/syscall-internal.h \
+ lttng/event-rule/tracepoint-internal.h \
version.h \
version.i
#include <common/dynamic-buffer.h>
#include <stdbool.h>
#include <sys/types.h>
+#include <urcu/ref.h>
typedef bool (*action_validate_cb)(struct lttng_action *action);
typedef void (*action_destroy_cb)(struct lttng_action *action);
typedef int (*action_serialize_cb)(struct lttng_action *action,
struct lttng_dynamic_buffer *buf);
+typedef bool (*action_equal_cb)(const struct lttng_action *a,
+ const struct lttng_action *b);
typedef ssize_t (*action_create_from_buffer_cb)(
const struct lttng_buffer_view *view,
struct lttng_action **action);
struct lttng_action {
+ struct urcu_ref ref;
enum lttng_action_type type;
action_validate_cb validate;
action_serialize_cb serialize;
+ action_equal_cb equal;
action_destroy_cb destroy;
};
enum lttng_action_type type,
action_validate_cb validate,
action_serialize_cb serialize,
+ action_equal_cb equal,
action_destroy_cb destroy);
LTTNG_HIDDEN
enum lttng_action_type lttng_action_get_type_const(
const struct lttng_action *action);
+LTTNG_HIDDEN
+bool lttng_action_is_equal(const struct lttng_action *a,
+ const struct lttng_action *b);
+
+LTTNG_HIDDEN
+void lttng_action_get(struct lttng_action *action);
+
+LTTNG_HIDDEN
+void lttng_action_put(struct lttng_action *action);
+
+LTTNG_HIDDEN
+const char* lttng_action_type_string(enum lttng_action_type action_type);
+
#endif /* LTTNG_ACTION_INTERNAL_H */
enum lttng_action_type {
LTTNG_ACTION_TYPE_UNKNOWN = -1,
LTTNG_ACTION_TYPE_NOTIFY = 0,
+ LTTNG_ACTION_TYPE_START_SESSION = 1,
+ LTTNG_ACTION_TYPE_STOP_SESSION = 2,
+ LTTNG_ACTION_TYPE_ROTATE_SESSION = 3,
+ LTTNG_ACTION_TYPE_SNAPSHOT_SESSION = 4,
+ LTTNG_ACTION_TYPE_GROUP = 5,
+};
+
+enum lttng_action_status {
+ LTTNG_ACTION_STATUS_OK = 0,
+ LTTNG_ACTION_STATUS_ERROR = -1,
+ LTTNG_ACTION_STATUS_UNKNOWN = -2,
+ LTTNG_ACTION_STATUS_INVALID = -3,
+ LTTNG_ACTION_STATUS_UNSET = -4,
};
/*
* Returns the type of an action on success, LTTNG_ACTION_TYPE_UNKNOWN on error.
*/
extern enum lttng_action_type lttng_action_get_type(
- struct lttng_action *action);
+ const struct lttng_action *action);
/*
* Destroy (frees) an action object.
--- /dev/null
+/*
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LTTNG_ACTION_GROUP_INTERNAL_H
+#define LTTNG_ACTION_GROUP_INTERNAL_H
+
+#include <sys/types.h>
+
+#include <common/macros.h>
+
+struct lttng_action;
+struct lttng_buffer_view;
+
+/*
+ * Create an action group from a buffer view.
+ *
+ * On success, return the number of bytes consumed from `view`, and the created
+ * group in `*group`. On failure, return -1.
+ */
+LTTNG_HIDDEN
+extern ssize_t lttng_action_group_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_action **group);
+#endif /* LTTNG_ACTION_GROUP_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_ACTION_GROUP_H
+#define LTTNG_ACTION_GROUP_H
+
+struct lttng_action;
+struct lttng_action_group;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated action group object.
+ *
+ * Returns a new action group on success, NULL on failure. This action group
+ * must be destroyed using lttng_action_group_destroy().
+ */
+extern struct lttng_action *lttng_action_group_create(void);
+
+/*
+ * Add an action to an lttng_action object of type LTTNG_ACTION_GROUP.
+ *
+ * The group takes ownership of the action.
+ */
+extern enum lttng_action_status lttng_action_group_add_action(
+ struct lttng_action *group, struct lttng_action *action);
+
+extern enum lttng_action_status lttng_action_group_get_count(
+ const struct lttng_action *group, unsigned int *count);
+
+extern const struct lttng_action *lttng_action_group_get_at_index_const(
+ const struct lttng_action *group,
+ unsigned int index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_ACTION_GROUP_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_ACTION_ROTATE_SESSION_INTERNAL_H
+#define LTTNG_ACTION_ROTATE_SESSION_INTERNAL_H
+
+#include <sys/types.h>
+
+#include <common/macros.h>
+
+struct lttng_action;
+struct lttng_buffer_view;
+
+/*
+ * Create a "rotate session" action from a buffer view.
+ *
+ * On success, return the number of bytes consumed from `view`, and the created
+ * action in `*action`. On failure, return -1.
+ */
+LTTNG_HIDDEN
+extern ssize_t lttng_action_rotate_session_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_action **action);
+
+#endif /* LTTNG_ACTION_ROTATE_SESSION_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_ACTION_ROTATE_SESSION_H
+#define LTTNG_ACTION_ROTATE_SESSION_H
+
+struct lttng_action;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated rotate-session action object.
+ *
+ * A rotate session action object must have a session name set to be considered
+ * valid when used with a trigger object (lttng_trigger). A name can be set
+ * using `lttng_action_rotate_session_set_session_name`.
+ *
+ * Returns a new action on success, NULL on failure. This action must be
+ * destroyed using lttng_action_destroy().
+ */
+extern struct lttng_action *lttng_action_rotate_session_create(void);
+
+/*
+ * Set the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_ROTATE_SESSION.
+ */
+extern enum lttng_action_status lttng_action_rotate_session_set_session_name(
+ struct lttng_action *action, const char *session_name);
+
+/*
+ * Get the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_ROTATE_SESSION.
+ */
+extern enum lttng_action_status lttng_action_rotate_session_get_session_name(
+ const struct lttng_action *action, const char **session_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_ACTION_ROTATE_SESSION_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H
+#define LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H
+
+#include <sys/types.h>
+
+#include <common/macros.h>
+
+struct lttng_action;
+struct lttng_buffer_view;
+
+/*
+ * Create a "snapshot session" action from a buffer view.
+ *
+ * On success, return the number of bytes consumed from `view`, and the created
+ * action in `*action`. On failure, return -1.
+ */
+LTTNG_HIDDEN
+extern ssize_t lttng_action_snapshot_session_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_action **action);
+
+#endif /* LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_ACTION_SNAPSHOT_SESSION_H
+#define LTTNG_ACTION_SNAPSHOT_SESSION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct lttng_action;
+struct lttng_snapshot_output;
+
+/*
+ * Create a newly allocated snapshot-session action object.
+ *
+ * A snapshot session action object must have a session name set to be
+ * considered valid when used with a trigger object (lttng_trigger). A name can
+ * be set using `lttng_action_snapshot_session_set_session_name`.
+ *
+ * Returns a new action on success, NULL on failure. This action must be
+ * destroyed using lttng_action_destroy().
+ */
+extern struct lttng_action *lttng_action_snapshot_session_create(void);
+
+/*
+ * Set the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_SNAPSHOT_SESSION.
+ */
+extern enum lttng_action_status lttng_action_snapshot_session_set_session_name(
+ struct lttng_action *action, const char *session_name);
+
+/*
+ * Get the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_SNAPSHOT_SESSION.
+ */
+extern enum lttng_action_status lttng_action_snapshot_session_get_session_name(
+ const struct lttng_action *action, const char **session_name);
+
+/*
+ * Set an explicit snapshot output for this snapshot session action.
+ *
+ * The given snapshot output will be used instead of the session's
+ * default snapshot output.
+ *
+ * This function takes ownership of the given snapshot output.
+ */
+extern enum lttng_action_status lttng_action_snapshot_session_set_output(
+ struct lttng_action *action,
+ struct lttng_snapshot_output *output);
+
+/*
+ * Get the explicit snapshot output for this snapshot session action.
+ */
+extern enum lttng_action_status lttng_action_snapshot_session_get_output_const(
+ const struct lttng_action *action,
+ const struct lttng_snapshot_output **output);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_ACTION_SNAPSHOT_SESSION_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_ACTION_START_SESSION_INTERNAL_H
+#define LTTNG_ACTION_START_SESSION_INTERNAL_H
+
+#include <sys/types.h>
+
+#include <common/macros.h>
+
+struct lttng_action;
+struct lttng_buffer_view;
+
+/*
+ * Create a "start session" action from a buffer view.
+ *
+ * On success, return the number of bytes consumed from `view`, and the created
+ * action in `*action`. On failure, return -1.
+ */
+LTTNG_HIDDEN
+extern ssize_t lttng_action_start_session_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_action **action);
+
+#endif /* LTTNG_ACTION_START_SESSION_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_ACTION_START_SESSION_H
+#define LTTNG_ACTION_START_SESSION_H
+
+struct lttng_action;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated start-session action object.
+ *
+ * A start session action object must have a session name set to be considered
+ * valid when used with a trigger object (lttng_trigger). A name can be set
+ * using `lttng_action_start_session_set_session_name`.
+ *
+ * Returns a new action on success, NULL on failure. This action must be
+ * destroyed using lttng_action_destroy().
+ */
+extern struct lttng_action *lttng_action_start_session_create(void);
+
+/*
+ * Set the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_START_SESSION.
+ */
+extern enum lttng_action_status lttng_action_start_session_set_session_name(
+ struct lttng_action *action, const char *session_name);
+
+/*
+ * Get the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_START_SESSION.
+ */
+extern enum lttng_action_status lttng_action_start_session_get_session_name(
+ const struct lttng_action *action, const char **session_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_ACTION_START_SESSION_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_ACTION_STOP_SESSION_INTERNAL_H
+#define LTTNG_ACTION_STOP_SESSION_INTERNAL_H
+
+#include <sys/types.h>
+
+#include <common/macros.h>
+
+struct lttng_action;
+struct lttng_buffer_view;
+
+/*
+ * Create a "stop session" action from a buffer view.
+ *
+ * On success, return the number of bytes consumed from `view`, and the created
+ * action in `*action`. On failure, return -1.
+ */
+LTTNG_HIDDEN
+extern ssize_t lttng_action_stop_session_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_action **action);
+
+#endif /* LTTNG_ACTION_STOP_SESSION_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_ACTION_STOP_SESSION_H
+#define LTTNG_ACTION_STOP_SESSION_H
+
+struct lttng_action;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated stop-session action object.
+ *
+ * A stop session action object must have a session name set to be considered
+ * valid when used with a trigger object (lttng_trigger). A name can be set
+ * using `lttng_action_stop_session_set_session_name`.
+ *
+ * Returns a new action on success, NULL on failure. This action must be
+ * destroyed using lttng_action_destroy().
+ */
+extern struct lttng_action *lttng_action_stop_session_create(void);
+
+/*
+ * Set the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_STOP_SESSION.
+ */
+extern enum lttng_action_status lttng_action_stop_session_set_session_name(
+ struct lttng_action *action, const char *session_name);
+
+/*
+ * Get the session name of an lttng_action object of type
+ * LTTNG_ACTION_TYPE_STOP_SESSION.
+ */
+extern enum lttng_action_status lttng_action_stop_session_get_session_name(
+ const struct lttng_action *action, const char **session_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_ACTION_STOP_SESSION_H */
typedef bool (*condition_validate_cb)(const struct lttng_condition *condition);
typedef int (*condition_serialize_cb)(
const struct lttng_condition *condition,
- struct lttng_dynamic_buffer *buf);
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send);
typedef bool (*condition_equal_cb)(const struct lttng_condition *a,
const struct lttng_condition *b);
typedef ssize_t (*condition_create_from_buffer_cb)(
LTTNG_HIDDEN
int lttng_condition_serialize(const struct lttng_condition *condition,
- struct lttng_dynamic_buffer *buf);
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send);
LTTNG_HIDDEN
bool lttng_condition_is_equal(const struct lttng_condition *a,
const struct lttng_condition *b);
+LTTNG_HIDDEN
+const char *lttng_condition_type_str(enum lttng_condition_type type);
+
#endif /* LTTNG_CONDITION_INTERNAL_H */
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 {
LTTNG_CONDITION_STATUS_UNKNOWN = -2,
LTTNG_CONDITION_STATUS_INVALID = -3,
LTTNG_CONDITION_STATUS_UNSET = -4,
+ LTTNG_CONDITION_STATUS_UNSUPPORTED = -4,
};
/*
#define LTTNG_EVALUATION_INTERNAL_H
#include <lttng/condition/evaluation.h>
+#include <lttng/condition/condition.h>
#include <common/macros.h>
#include <common/buffer-view.h>
#include <common/dynamic-buffer.h>
enum lttng_condition_type type);
LTTNG_HIDDEN
-ssize_t lttng_evaluation_create_from_buffer(const struct lttng_buffer_view *view,
+ssize_t lttng_evaluation_create_from_buffer(
+ const struct lttng_condition *condition,
+ const struct lttng_buffer_view *view,
struct lttng_evaluation **evaluation);
LTTNG_HIDDEN
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_CONDITION_event_rule_INTERNAL_H
+#define LTTNG_CONDITION_event_rule_INTERNAL_H
+
+#include <lttng/condition/condition-internal.h>
+#include <common/buffer-view.h>
+#include <common/macros.h>
+#include <lttng/condition/evaluation-internal.h>
+#include <common/dynamic-array.h>
+#include <lttng/event-field-value.h>
+
+struct lttng_capture_descriptor {
+ /* The index at which the capture for this descriptor in the received
+ * payload from the tracer. This is populated on sessiond side.
+ * -1 is uninitialized.
+ * This is necessary since a single trigger can have multiple notify
+ * action, only an ordered set of capture desciptor is passed to the tracer.
+ */
+ int32_t capture_index;
+ struct lttng_event_expr *event_expression;
+};
+
+struct lttng_condition_event_rule {
+ struct lttng_condition parent;
+ struct lttng_event_rule *rule;
+
+ /* Array of `struct lttng_capture_descriptor *` */
+ struct lttng_dynamic_pointer_array capture_descriptors;
+};
+
+struct lttng_evaluation_event_rule {
+ struct lttng_evaluation parent;
+ char *name;
+
+ /* MessagePack-encoded captured event field values */
+ struct lttng_dynamic_buffer capture_payload;
+
+ /*
+ * The content of this array event field value is the decoded
+ * version of `capture_payload` above.
+ *
+ * This is a cache: it's not serialized/deserialized in
+ * communications from/to the library and the session daemon.
+ */
+ struct lttng_event_field_value *captured_values;
+};
+
+struct lttng_evaluation_event_rule_comm {
+ uint32_t trigger_name_length;
+ /* Trigger name */
+ char payload[];
+} LTTNG_PACKED;
+
+struct lttng_condition_event_rule_capture_bytecode_element
+{
+ struct lttng_event_expr *expression;
+ struct lttng_bytecode *bytecode;
+};
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_event_rule_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_condition **condition);
+
+LTTNG_HIDDEN
+enum lttng_condition_status
+lttng_condition_event_rule_get_rule_no_const(
+ const struct lttng_condition *condition,
+ struct lttng_event_rule **rule);
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_event_rule_create(
+ const struct lttng_condition_event_rule *condition,
+ const char* trigger_name,
+ const char *capture_payload, size_t capture_payload_size,
+ bool decode_capture_payload);
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_event_rule_create_from_buffer(
+ const struct lttng_condition_event_rule *condition,
+ const struct lttng_buffer_view *view,
+ struct lttng_evaluation **_evaluation);
+
+/*
+ * The returned `lttng_dynamic_pointer_array` contains a index ordered set of
+ * `lttng_condition_event_rule_capture_bytecode_element`.
+ * This ensure that minimal work will be done by the tracer for cases where
+ * multiple identical capture expression are present.
+ */
+LTTNG_HIDDEN
+enum lttng_error_code
+lttng_condition_event_rule_generate_capture_descriptor_bytecode_set(
+ struct lttng_condition *condition,
+ struct lttng_dynamic_pointer_array *bytecode_set);
+
+LTTNG_HIDDEN
+struct lttng_capture_descriptor *
+lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+ const struct lttng_condition *condition, unsigned int index);
+
+#endif /* LTTNG_CONDITION_event_rule_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_CONDITION_EVENT_RULE_H
+#define LTTNG_CONDITION_EVENT_RULE_H
+
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/evaluation.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct lttng_event_expr;
+struct lttng_event_field_value;
+
+/*
+ * TODO: overall desciption of an event rule condition.
+ */
+
+/*
+ * Create a newly allocated event rule condition.
+ *
+ * Rule will be copied internally.
+ *
+ * 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);
+
+/*
+ * Sets `*field_val` to the array event field value of the event rule
+ * condition evaluation `evaluation` which contains its captured values.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVALUATION_STATUS_OK`:
+ * Success.
+ *
+ * `*field_val` is an array event field value with a length of at
+ * least one.
+ *
+ * `LTTNG_EVALUATION_STATUS_INVALID`:
+ * * `evaluation` is `NULL`.
+ * * The type of the condition of `evaluation` is not
+ * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`.
+ * * The condition of `evaluation` has no capture descriptors.
+ * * `field_val` is `NULL`.
+ */
+extern enum lttng_evaluation_status
+lttng_evaluation_get_captured_values(
+ const struct lttng_evaluation *evaluation,
+ const struct lttng_event_field_value **field_val);
+
+/*
+ * Appends (transfering the ownership) the capture descriptor `expr` to
+ * the event rule condition `condition`.
+ *
+ * Returns:
+ *
+ * `LTTNG_CONDITION_STATUS_OK`:
+ * Success.
+ *
+ * `LTTNG_CONDITION_STATUS_ERROR`:
+ * Memory error.
+ *
+ * `LTTNG_CONDITION_STATUS_INVALID`:
+ * * `condition` is `NULL`.
+ * * The type of `condition` is not
+ * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`.
+ * * `expr` is `NULL`.
+ * * `expr` is not a locator expression, that is, its type is not
+ * one of:
+ *
+ * * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD`
+ * * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`
+ * * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`
+ * * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`
+ * `LTTNG_CONDITION_STATUS_UNSUPPORTED`:
+ * * The associated event-rule does not support runtime capture.
+ */
+extern enum lttng_condition_status
+lttng_condition_event_rule_append_capture_descriptor(
+ struct lttng_condition *condition,
+ struct lttng_event_expr *expr);
+
+/*
+ * Sets `*count` to the number of capture descriptors in the event rule
+ * condition `condition`.
+ *
+ * Returns:
+ *
+ * `LTTNG_CONDITION_STATUS_OK`:
+ * Success.
+ *
+ * `LTTNG_CONDITION_STATUS_INVALID`:
+ * * `condition` is `NULL`.
+ * * The type of `condition` is not
+ * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`.
+ * * `count` is `NULL`.
+ */
+extern enum lttng_condition_status
+lttng_condition_event_rule_get_capture_descriptor_count(
+ const struct lttng_condition *condition, unsigned int *count);
+
+/*
+ * Returns the capture descriptor (borrowed) of the event rule condition
+ * `condition` at the index `index`, or `NULL` if:
+ *
+ * * `condition` is `NULL`.
+ * * The type of `condition` is not
+ * `LTTNG_CONDITION_TYPE_EVENT_RULE_HIT`.
+ * * `index` is greater than or equal to the number of capture
+ * descriptors in `condition` (as returned by
+ * lttng_condition_event_rule_get_capture_descriptor_count()).
+ */
+extern const struct lttng_event_expr *
+lttng_condition_event_rule_get_capture_descriptor_at_index(
+ const struct lttng_condition *condition, unsigned int index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_CONDITION_EVENT_RULE_H */
--- /dev/null
+/*
+ * Copyright (C) 2020 - EfficiOS, inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LTTNG_DOMAIN_INTERNAL_H
+#define LTTNG_DOMAIN_INTERNAL_H
+
+#include "lttng/domain.h"
+#include "common/macros.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LTTNG_HIDDEN
+const char *lttng_domain_type_str(enum lttng_domain_type domain_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_DOMAIN_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_EXPR_INTERNAL_H
+#define LTTNG_EVENT_EXPR_INTERNAL_H
+
+#include <assert.h>
+#include <common/macros.h>
+#include <lttng/event-expr.h>
+#include <urcu/ref.h>
+
+struct lttng_event_expr {
+ struct urcu_ref ref;
+ enum lttng_event_expr_type type;
+};
+
+/*
+ * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD` and
+ * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`.
+ */
+struct lttng_event_expr_field {
+ struct lttng_event_expr parent;
+ char *name;
+};
+
+/* `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD` */
+struct lttng_event_expr_app_specific_context_field {
+ struct lttng_event_expr parent;
+ char *provider_name;
+ char *type_name;
+};
+
+/* `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT` */
+struct lttng_event_expr_array_field_element {
+ struct lttng_event_expr parent;
+
+ /* Owned by this */
+ struct lttng_event_expr *array_field_expr;
+
+ unsigned int index;
+};
+
+/*
+ * Returns whether or not `expr` is an l-value (locator value).
+ */
+static inline
+bool lttng_event_expr_is_lvalue(const struct lttng_event_expr *expr)
+{
+ assert(expr);
+ return expr->type == LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD ||
+ expr->type == LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD ||
+ expr->type == LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD ||
+ expr->type == LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT;
+}
+
+LTTNG_HIDDEN
+void lttng_event_expr_get(struct lttng_event_expr *expr);
+
+LTTNG_HIDDEN
+void lttng_event_expr_put(struct lttng_event_expr *expr);
+
+#endif /* LTTNG_EVENT_EXPR_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_EXPR_H
+#define LTTNG_EVENT_EXPR_H
+
+#include <stdbool.h>
+
+struct lttng_event_expr;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Types of an event expression.
+ */
+enum lttng_event_expr_type {
+ /*
+ * Returned by lttng_event_expr_get_type() with an invalid
+ * parameter.
+ */
+ LTTNG_EVENT_EXPR_TYPE_INVALID = -1,
+
+ /*
+ * The named payload field of an event.
+ *
+ * Command-line expression example:
+ *
+ * next_prio
+ */
+ LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD = 0,
+
+ /*
+ * The named per-channel context field of an event.
+ *
+ * Command-line expression example:
+ *
+ * $ctx.vpid
+ */
+ LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD = 1,
+
+ /*
+ * The named application-specific context field of an event.
+ *
+ * Command-line expression example:
+ *
+ * $app.iga:active-clients
+ */
+ LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD = 2,
+
+ /*
+ * The element of an array field.
+ *
+ * Command-line expression example:
+ *
+ * my_field[4]
+ * $ctx.some_context[5][1]
+ */
+ LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT = 3,
+};
+
+/*
+ * Event expression API status codes.
+ */
+enum lttng_event_expr_status {
+ /*
+ * Invalid parameter.
+ */
+ LTTNG_EVENT_EXPR_STATUS_INVALID = -1,
+
+ /*
+ * Success.
+ */
+ LTTNG_EVENT_EXPR_STATUS_OK = 0,
+};
+
+/*
+ * Returns the type of the event expression `expr`, or
+ * `LTTNG_EVENT_EXPR_TYPE_INVALID` if `expr` is `NULL`.
+ */
+extern enum lttng_event_expr_type lttng_event_expr_get_type(
+ const struct lttng_event_expr *expr);
+
+/*
+ * Creates an event payload field expression for the payload field named
+ * `field_name`.
+ *
+ * Returns `NULL` if:
+ *
+ * * There's a memory error.
+ * * `field_name` is `NULL`.
+ */
+extern struct lttng_event_expr *lttng_event_expr_event_payload_field_create(
+ const char *field_name);
+
+/*
+ * Returns the field name of the event payload field expression `expr`,
+ * or `NULL` if:
+ *
+ * * `expr` is `NULL`.
+ * * The type of `expr` is not
+ * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD`.
+ */
+extern const char *lttng_event_expr_event_payload_field_get_name(
+ const struct lttng_event_expr *expr);
+
+/*
+ * Creates a per-channel context field expression for the per-channel
+ * context field named `field_name`.
+ *
+ * Returns `NULL` if:
+ *
+ * * There's a memory error.
+ * * `field_name` is `NULL`.
+ */
+extern struct lttng_event_expr *
+lttng_event_expr_channel_context_field_create(const char *field_name);
+
+/*
+ * Returns the field name of the per-channel context field
+ * expression `expr`, or `NULL` if:
+ *
+ * `expr` is `NULL`.
+ * * The type of `expr` is not
+ * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`.
+ */
+extern const char *lttng_event_expr_channel_context_field_get_name(
+ const struct lttng_event_expr *expr);
+
+/*
+ * Creates an application-specific context field expression for the
+ * application-specific context field provided by the provider named
+ * `provider_name` and having the type named `type_name`.
+ *
+ * Returns `NULL` if:
+ *
+ * * There's a memory error.
+ * * `provider_name` is `NULL`.
+ * * `type_name` is `NULL`.
+ */
+extern struct lttng_event_expr *
+lttng_event_expr_app_specific_context_field_create(
+ const char *provider_name, const char *type_name);
+
+/*
+ * Returns the provider name of the application-specific context field
+ * expression `expr`, or `NULL` if:
+ *
+ * * `expr` is `NULL`.
+ * * The type of `expr` is not
+ * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`.
+ */
+extern const char *
+lttng_event_expr_app_specific_context_field_get_provider_name(
+ const struct lttng_event_expr *expr);
+
+/*
+ * Returns the type name of the application-specific context field
+ * expression `expr`, or `NULL` if:
+ *
+ * * `expr` is `NULL`.
+ * * The type of `expr` is not
+ * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`.
+ */
+extern const char *
+lttng_event_expr_app_specific_context_field_get_type_name(
+ const struct lttng_event_expr *expr);
+
+/*
+ * Creates an array field element expression for the parent array field
+ * `array_field_expr` (transfering the ownership) and the index `index`.
+ *
+ * Returns `NULL` if:
+ *
+ * * There's a memory error.
+ * * `array_field_expr` is `NULL`.
+ * * `array_field_expr` is not a locator expression, that is, its type
+ * is not one of:
+ *
+ * * `LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD`
+ * * `LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD`
+ * * `LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD`
+ * * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`
+ */
+extern struct lttng_event_expr *lttng_event_expr_array_field_element_create(
+ struct lttng_event_expr *array_field_expr,
+ unsigned int index);
+
+/*
+ * Returns the parent array field expression of the array field element
+ * expression `expr`, or `NULL` if:
+ *
+ * * `expr` is `NULL`.
+ * * The type of `expr` is not
+ * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`.
+ */
+extern const struct lttng_event_expr *
+lttng_event_expr_array_field_element_get_parent_expr(
+ const struct lttng_event_expr *expr);
+
+/*
+ * Sets `*index` to the index of the array field element expression
+ * `expr`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_EXPR_STATUS_OK`:
+ * Success.
+ *
+ * `LTTNG_EVENT_EXPR_STATUS_INVALID`:
+ * * `expr` is `NULL`.
+ * * The type of `expr` is not
+ * `LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT`.
+ * * `index` is `NULL`.
+ */
+extern enum lttng_event_expr_status
+lttng_event_expr_array_field_element_get_index(
+ const struct lttng_event_expr *expr, unsigned int *index);
+
+/*
+ * Returns whether or not the event expressions `expr_a` and `expr_b`
+ * are equal.
+ *
+ * `expr_a` and `expr_b` can be `NULL`.
+ */
+extern bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a,
+ const struct lttng_event_expr *expr_b);
+
+/*
+ * Destroys the event expression `expr` if not `NULL`.
+ */
+extern void lttng_event_expr_destroy(struct lttng_event_expr *expr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_EXPR_H */
--- /dev/null
+/*
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_FIELD_VALUE_INTERNAL_H
+#define LTTNG_EVENT_FIELD_VALUE_INTERNAL_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <lttng/event-field-value.h>
+#include <common/dynamic-array.h>
+
+struct lttng_event_field_value {
+ enum lttng_event_field_value_type type;
+};
+
+/*
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT`.
+ */
+struct lttng_event_field_value_uint {
+ struct lttng_event_field_value parent;
+ uint64_t val;
+};
+
+/*
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT`.
+ */
+struct lttng_event_field_value_int {
+ struct lttng_event_field_value parent;
+ int64_t val;
+};
+
+/*
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` and
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM` (base).
+ */
+struct lttng_event_field_value_enum {
+ struct lttng_event_field_value parent;
+
+ /*
+ * Array of `char *` (owned by this).
+ */
+ struct lttng_dynamic_pointer_array labels;
+};
+
+/*
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM`.
+ */
+struct lttng_event_field_value_enum_uint {
+ struct lttng_event_field_value_enum parent;
+ uint64_t val;
+};
+
+/*
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`.
+ */
+struct lttng_event_field_value_enum_int {
+ struct lttng_event_field_value_enum parent;
+ int64_t val;
+};
+
+/* `LTTNG_EVENT_FIELD_VALUE_TYPE_REAL` */
+struct lttng_event_field_value_real {
+ struct lttng_event_field_value parent;
+ double val;
+};
+
+/* `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING` */
+struct lttng_event_field_value_string {
+ struct lttng_event_field_value parent;
+
+ /* Owned by this */
+ char *val;
+};
+
+/* `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING` */
+struct lttng_event_field_value_array {
+ struct lttng_event_field_value parent;
+
+ /*
+ * Array of `struct lttng_event_field_value *` (owned by this).
+ *
+ * A `NULL` element means it's unavailable
+ * (`LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE` status).
+ */
+ struct lttng_dynamic_pointer_array elems;
+};
+
+/*
+ * NOTE JORAJ: This was previously public. The only slight problem with that is
+ * that as of today (2020-05-26) there is no plan/sessiond for the tracer to
+ * actually provide this information. This was already known in [1]. For now
+ * this code have no value since it only confuse the end user. At upstreaming
+ * time we will need to decide if we want to remove all code pertaining to enum
+ * label, at least on the lttng-tools API.
+ *
+ * [1] https://support.efficios.com/issues/792
+ *
+ * Sets `*count` to the number of labels of the enumeration event field
+ * value `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ * Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ * * `field_val` is `NULL`.
+ * * The type of `field_val` is not
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` or
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`.
+ * * `count` is `NULL`.
+ */
+LTTNG_HIDDEN
+enum lttng_event_field_value_status
+lttng_event_field_value_enum_get_label_count(
+ const struct lttng_event_field_value *field_val,
+ unsigned int *count);
+
+/*
+ * NOTE JORAJ: see NOTE JORAJ off lttng_event_field_value_enum_get_label_count
+ *
+ * Returns the label at index `index` of the enumeration event field
+ * value `field_val`, or `NULL` if:
+ *
+ * * `field_val` is `NULL`.
+ * * The type of `field_val` is not
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM` or
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`.
+ * * `index` is greater than or equal to the label count of `field_val`,
+ * as returned by lttng_event_field_value_enum_get_label_count().
+ */
+LTTNG_HIDDEN
+const char *lttng_event_field_value_enum_get_label_at_index(
+ const struct lttng_event_field_value *field_val,
+ unsigned int index);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_uint_create(
+ uint64_t val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_int_create(
+ int64_t val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_enum_uint_create(
+ uint64_t val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_enum_int_create(
+ int64_t val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_real_create(double val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_string_create(
+ const char *val);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_string_create_with_size(
+ const char *val, size_t size);
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_array_create(void);
+
+LTTNG_HIDDEN
+int lttng_event_field_value_enum_append_label(
+ struct lttng_event_field_value *field_val, const char *label);
+
+LTTNG_HIDDEN
+int lttng_event_field_value_enum_append_label_with_size(
+ struct lttng_event_field_value *field_val, const char *label,
+ size_t size);
+
+LTTNG_HIDDEN
+int lttng_event_field_value_array_append(
+ struct lttng_event_field_value *array_field_val,
+ struct lttng_event_field_value *field_val);
+
+LTTNG_HIDDEN
+int lttng_event_field_value_array_append_unavailable(
+ struct lttng_event_field_value *array_field_val);
+
+LTTNG_HIDDEN
+void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val);
+
+#endif /* LTTNG_EVENT_FIELD_VALUE_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_FIELD_VALUE_H
+#define LTTNG_EVENT_FIELD_VALUE_H
+
+#include <stdint.h>
+
+struct lttng_event_field_value;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Types of a event field value expression.
+ */
+enum lttng_event_field_value_type {
+ /*
+ * Unknown.
+ */
+ LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN = -2,
+
+ /*
+ * Returned by lttng_event_field_value_get_type() with an
+ * invalid parameter.
+ */
+ LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID = -1,
+
+ /*
+ * Unsigned integer event field value.
+ */
+ LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT = 0,
+
+ /*
+ * Signed integer event field value.
+ */
+ LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT = 1,
+
+ /*
+ * Unsigned enumeration event field value.
+ *
+ * This type conceptually inherits
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT`.
+ */
+ LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM = 2,
+
+ /*
+ * Signed enumeration event field value.
+ *
+ * This type conceptually inherits
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT`.
+ */
+ LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM = 3,
+
+ /*
+ * Real event field value.
+ */
+ LTTNG_EVENT_FIELD_VALUE_TYPE_REAL = 4,
+
+ /*
+ * String event field value.
+ */
+ LTTNG_EVENT_FIELD_VALUE_TYPE_STRING = 5,
+
+ /*
+ * Array event field value.
+ */
+ LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY = 6,
+};
+
+/*
+ * Event field value API status codes.
+ */
+enum lttng_event_field_value_status {
+ /*
+ * Event field value is not available.
+ */
+ LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE = -2,
+
+ /*
+ * Invalid parameter.
+ */
+ LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID = -1,
+
+ /*
+ * Success.
+ */
+ LTTNG_EVENT_FIELD_VALUE_STATUS_OK = 0,
+};
+
+/*
+ * Returns the type of the event field value `field_val`, or:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN`:
+ * The type of `field_val` is unknown as of this version of the
+ * LTTng control library.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID`:
+ * `field_val` is `NULL`.
+ */
+extern enum lttng_event_field_value_type lttng_event_field_value_get_type(
+ const struct lttng_event_field_value *field_val);
+
+/*
+ * Sets `*val` to the raw value of the unsigned integer/enumeration
+ * event field value `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ * Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ * * `field_val` is `NULL`.
+ * * The type of `field_val` is not
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT` or
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM`.
+ * * `val` is `NULL`.
+ */
+extern enum lttng_event_field_value_status
+lttng_event_field_value_unsigned_int_get_value(
+ const struct lttng_event_field_value *field_val, uint64_t *val);
+
+/*
+ * Sets `*val` to the raw value of the signed integer/enumeration event
+ * field value `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ * Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ * * `field_val` is `NULL`.
+ * * The type of `field_val` is not
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT` or
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM`.
+ * * `val` is `NULL`.
+ */
+extern enum lttng_event_field_value_status
+lttng_event_field_value_signed_int_get_value(
+ const struct lttng_event_field_value *field_val, int64_t *val);
+
+/*
+ * Sets `*val` to the raw value of the real event field value
+ * `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ * Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ * * `field_val` is `NULL`.
+ * * The type of `field_val` is not
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_REAL`.
+ * * `val` is `NULL`.
+ */
+extern enum lttng_event_field_value_status
+lttng_event_field_value_real_get_value(
+ const struct lttng_event_field_value *field_val, double *val);
+
+/*
+ * Returns the raw value (an UTF-8 C string) of the string event field
+ * value `field_val`, or `NULL` if:
+ *
+ * * `field_val` is `NULL`.
+ * * The type of `field_val` is not
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_STRING`.
+ */
+extern const char *lttng_event_field_value_string_get_value(
+ const struct lttng_event_field_value *field_val);
+
+/*
+ * Sets `*length` to the length (the number of contained elements) of
+ * the array event field value `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ * Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ * * `field_val` is `NULL`.
+ * * The type of `field_val` is not
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY`.
+ * * `length` is `NULL`.
+ */
+extern enum lttng_event_field_value_status
+lttng_event_field_value_array_get_length(
+ const struct lttng_event_field_value *field_val,
+ unsigned int *length);
+
+/*
+ * Sets `*elem_field_val` to the event field value at index `index` in
+ * the array event field value `field_val`.
+ *
+ * Returns:
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_OK`:
+ * Success.
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID`:
+ * * `field_val` is `NULL`.
+ * * The type of `field_val` is not
+ * `LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY`.
+ * * `index` is greater than or equal to the length of `field_val`,
+ * as returned by lttng_event_field_value_array_get_length().
+ *
+ * `LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE`:
+ * * No event field value exists at index `index` within
+ * `field_val`.
+ */
+extern enum lttng_event_field_value_status
+lttng_event_field_value_array_get_element_at_index(
+ const struct lttng_event_field_value *field_val,
+ unsigned int index,
+ const struct lttng_event_field_value **elem_field_val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_FIELD_VALUE_H */
LTTNG_HIDDEN
struct lttng_event *lttng_event_copy(const struct lttng_event *event);
+// FIXME: the implementation of these should be moved to some common file,
+// they should not be in the enable_events.c file.
+
+LTTNG_HIDDEN
+int loglevel_str_to_value(const char *inputstr);
+
+LTTNG_HIDDEN
+int loglevel_log4j_str_to_value(const char *inputstr);
+
+LTTNG_HIDDEN
+int loglevel_jul_str_to_value(const char *inputstr);
+
+LTTNG_HIDDEN
+int loglevel_python_str_to_value(const char *inputstr);
+
#endif /* LTTNG_EVENT_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_INTERNAL_H
+#define LTTNG_EVENT_RULE_INTERNAL_H
+
+#include <common/buffer-view.h>
+#include <common/dynamic-buffer.h>
+#include <common/macros.h>
+#include <lttng/domain.h>
+#include <lttng/event.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/lttng-error.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <urcu/ref.h>
+
+typedef void (*event_rule_destroy_cb)(struct lttng_event_rule *event_rule);
+typedef bool (*event_rule_validate_cb)(
+ const struct lttng_event_rule *event_rule);
+typedef int (*event_rule_serialize_cb)(
+ const struct lttng_event_rule *event_rule,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send);
+typedef bool (*event_rule_equal_cb)(const struct lttng_event_rule *a,
+ const struct lttng_event_rule *b);
+typedef ssize_t (*event_rule_create_from_buffer_cb)(
+ const struct lttng_buffer_view *view,
+ struct lttng_event_rule **event_rule);
+typedef enum lttng_error_code (*event_rule_populate_cb)(
+ struct lttng_event_rule *event_rule, uid_t uid, gid_t gid);
+typedef const char *(*event_rule_get_filter_cb)(
+ const struct lttng_event_rule *event_rule);
+typedef const struct lttng_bytecode *(
+ *event_rule_get_filter_bytecode_cb)(
+ const struct lttng_event_rule *event_rule);
+typedef struct lttng_event_exclusion *(*event_rule_generate_exclusions_cb)(
+ 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;
+ enum lttng_event_rule_type type;
+ event_rule_validate_cb validate;
+ event_rule_serialize_cb serialize;
+ event_rule_equal_cb equal;
+ event_rule_destroy_cb destroy;
+ event_rule_populate_cb populate;
+ event_rule_get_filter_cb get_filter;
+ event_rule_get_filter_bytecode_cb get_filter_bytecode;
+ event_rule_generate_exclusions_cb generate_exclusions;
+ event_rule_generate_lttng_event_cb generate_lttng_event;
+};
+
+struct lttng_event_rule_comm {
+ /* enum lttng_event_rule_type */
+ int8_t event_rule_type;
+ char payload[];
+};
+
+LTTNG_HIDDEN
+void lttng_event_rule_init(struct lttng_event_rule *event_rule,
+ enum lttng_event_rule_type type);
+
+LTTNG_HIDDEN
+bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule);
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_create_from_buffer(
+ const struct lttng_buffer_view *buffer,
+ struct lttng_event_rule **event_rule);
+
+LTTNG_HIDDEN
+int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send);
+
+LTTNG_HIDDEN
+bool lttng_event_rule_is_equal(const struct lttng_event_rule *a,
+ const struct lttng_event_rule *b);
+
+LTTNG_HIDDEN
+bool lttng_event_rule_get(struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+void lttng_event_rule_put(struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+enum lttng_domain_type lttng_event_rule_get_domain_type(
+ const struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+enum lttng_error_code lttng_event_rule_populate(
+ struct lttng_event_rule *rule, uid_t uid, gid_t gid);
+
+/*
+ * If not present/implemented returns NULL
+ * Caller DO NOT own the returned object
+ */
+LTTNG_HIDDEN
+const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule);
+
+/*
+ * If not present/implemented returns NULL
+ * Caller DO NOT own the returned object
+ */
+LTTNG_HIDDEN
+const struct lttng_bytecode *lttng_event_rule_get_filter_bytecode(
+ const struct lttng_event_rule *rule);
+
+/*
+ * If not present return NULL
+ * Caller OWN the returned object
+ * TODO: should this be done another way?
+ */
+LTTNG_HIDDEN
+struct lttng_event_exclusion *lttng_event_rule_generate_exclusions(
+ struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+const char *lttng_event_rule_type_str(enum lttng_event_rule_type type);
+
+/*
+ * This is compatibility helper, allowing us to generate a sessiond side (not
+ * communication) struct lttng_event object. This facilitate integration with
+ * current code.
+ * Caller OWN the returned object
+ */
+LTTNG_HIDDEN
+struct lttng_event *lttng_event_rule_generate_lttng_event(
+ const struct lttng_event_rule *rule);
+
+/* Quick helper to test if the event rule apply to an agent domain */
+LTTNG_HIDDEN
+bool lttng_event_rule_is_agent(const struct lttng_event_rule *rule);
+
+#endif /* LTTNG_EVENT_RULE_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_H
+#define LTTNG_EVENT_RULE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct lttng_event_rule;
+
+enum lttng_event_rule_type {
+ LTTNG_EVENT_RULE_TYPE_UNKNOWN = -1,
+ LTTNG_EVENT_RULE_TYPE_TRACEPOINT = 100,
+ LTTNG_EVENT_RULE_TYPE_SYSCALL = 101,
+ LTTNG_EVENT_RULE_TYPE_KPROBE = 102,
+ LTTNG_EVENT_RULE_TYPE_KRETPROBE = 103,
+ LTTNG_EVENT_RULE_TYPE_UPROBE = 104,
+};
+
+enum lttng_event_rule_status {
+ LTTNG_EVENT_RULE_STATUS_OK = 0,
+ LTTNG_EVENT_RULE_STATUS_ERROR = -1,
+ LTTNG_EVENT_RULE_STATUS_UNKNOWN = -2,
+ LTTNG_EVENT_RULE_STATUS_INVALID = -3,
+ LTTNG_EVENT_RULE_STATUS_UNSET = -4,
+ LTTNG_EVENT_RULE_STATUS_UNSUPPORTED = -5,
+};
+
+/**
+ * An event rule describes a set of criteria to be used as a discriminant in
+ * regards to a set of events.
+ */
+
+/*
+ * Get the event rule type.
+ *
+ * Returns the type of an event rule on success, LTTNG_EVENT_RULE_UNKNOWN on
+ * error.
+ */
+extern enum lttng_event_rule_type lttng_event_rule_get_type(
+ const struct lttng_event_rule *event_rule);
+
+/*
+ * Destroy an event rule object.
+ */
+extern void lttng_event_rule_destroy(struct lttng_event_rule *rule);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_KPROBE_INTERNAL_H
+#define LTTNG_EVENT_RULE_KPROBE_INTERNAL_H
+
+#include <common/buffer-view.h>
+#include <common/macros.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kprobe.h>
+
+struct lttng_event_rule_kprobe {
+ struct lttng_event_rule parent;
+ char *name;
+ struct {
+ uint64_t address;
+ uint64_t offset;
+ char *symbol_name;
+ bool set;
+ } probe;
+};
+
+struct lttng_event_rule_kprobe_comm {
+ uint32_t name_len;
+ uint32_t probe_symbol_name_len;
+ uint64_t probe_address;
+ uint64_t probe_offset;
+ /* name, source symbol_name */
+ char payload[];
+} LTTNG_PACKED;
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_kprobe_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_event_rule **rule);
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kprobe_get_address(
+ const struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kprobe_get_offset(
+ const struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+const char *lttng_event_rule_kprobe_get_symbol_name(
+ const struct lttng_event_rule *rule);
+
+#endif /* LTTNG_EVENT_RULE_KPROBE_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_KPROBE_H
+#define LTTNG_EVENT_RULE_KPROBE_H
+
+#include <lttng/event-rule/event-rule.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated kprobe event rule.
+ *
+ * Returns a new event rule on success, NULL on failure. The returned event rule
+ * must be destroyed using lttng_event_rule_destroy().
+ */
+extern struct lttng_event_rule *lttng_event_rule_kprobe_create(void);
+
+/*
+ * Set the source of a kprobe event rule.
+ *
+ * Possible formats for the source argument:
+ * Address (0x prefix supported)
+ * Symbol name
+ * Symbol name and offset (SYMBOL+OFFSET format)
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_source(
+ struct lttng_event_rule *rule, const char *source);
+
+/*
+ * Set the name of a kprobe event rule.
+ *
+ * The name is copied internally.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kprobe_set_name(
+ struct lttng_event_rule *rule, const char *name);
+
+/*
+ * Get the name of a kprobe event rule.
+ *
+ * The caller does not assume the ownership of the returned name.
+ * The name shall only only be used for the duration of the event
+ * rule's lifetime, or before a different name is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on
+ * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed,
+ * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kprobe_get_name(
+ const struct lttng_event_rule *rule, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_KPROBE_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_KRETPROBE_INTERNAL_H
+#define LTTNG_EVENT_RULE_KRETPROBE_INTERNAL_H
+
+#include <common/buffer-view.h>
+#include <common/macros.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kretprobe.h>
+
+struct lttng_event_rule_kretprobe {
+ struct lttng_event_rule parent;
+ char *name;
+ struct {
+ uint64_t address;
+ uint64_t offset;
+ char *symbol_name;
+ } probe;
+};
+
+struct lttng_event_rule_kretprobe_comm {
+ uint32_t name_len;
+ uint32_t probe_symbol_len;
+ /*name, probe symbol_name */
+ char payload[];
+} LTTNG_PACKED;
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_kretprobe_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_event_rule **rule);
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kretprobe_get_address(
+ const struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kretprobe_get_offset(
+ const struct lttng_event_rule *rule);
+
+LTTNG_HIDDEN
+const char *lttng_event_rule_kretprobe_get_symbol_name(
+ const struct lttng_event_rule *rule);
+
+#endif /* LTTNG_EVENT_RULE_KRETPROBE_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_KRETPROBE_H
+#define LTTNG_EVENT_RULE_KRETPROBE_H
+
+#include <lttng/event-rule/event-rule.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * TODO:
+ */
+extern struct lttng_event_rule *lttng_event_rule_kretprobe_create(void);
+
+/*
+ * Set the source of a kretprobe event rule.
+ *
+ * TODO: list possible format
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kretprobe_set_source(
+ struct lttng_event_rule *rule, const char *source);
+
+/*
+ * Set the name of a kretprobe event rule.
+ *
+ * The name is copied.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kretprobe_set_name(
+ struct lttng_event_rule *rule, const char *name);
+
+/*
+ * Get the name of a kretprobe event rule.
+ *
+ * The caller does not assume the ownership of the returned name.
+ * The name shall only only be used for the duration of the event
+ * rule's lifetime, or before a different name is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on
+ * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed,
+ * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_kretprobe_get_name(
+ const struct lttng_event_rule *rule, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_KRETPROBE_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H
+#define LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H
+
+#include <common/buffer-view.h>
+#include <common/macros.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/syscall.h>
+
+struct lttng_event_rule_syscall {
+ struct lttng_event_rule parent;
+ char *pattern;
+ char *filter_expression;
+
+ /* internal use only */
+ struct {
+ char *filter;
+ struct lttng_bytecode *bytecode;
+ } internal_filter;
+};
+
+struct lttng_event_rule_syscall_comm {
+ uint32_t pattern_len;
+ uint32_t filter_expression_len;
+ /* pattern, filter expression */
+ char payload[];
+} LTTNG_PACKED;
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_syscall_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_event_rule **rule);
+
+#endif /* LTTNG_EVENT_RULE_SYSCALL_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_SYSCALL_H
+#define LTTNG_EVENT_RULE_SYSCALL_H
+
+#include <lttng/event-rule/event-rule.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated syscall event rule.
+ *
+ * Returns a new event rule on success, NULL on failure. This event rule must be
+ * destroyed using lttng_event_rule_destroy().
+ */
+extern struct lttng_event_rule *lttng_event_rule_syscall_create(void);
+
+/*
+ * Set the pattern of a syscall event rule.
+ *
+ * Pattern can contain wildcard '*'. See man lttng-enable-event.
+ *
+ * The pattern is copied internally.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_syscall_set_pattern(
+ struct lttng_event_rule *rule, const char *pattern);
+
+/*
+ * Get the pattern of a syscall event rule.
+ *
+ * The caller does not assume the ownership of the returned pattern. The
+ * pattern shall only only be used for the duration of the event rule's
+ * lifetime, or before a different pattern is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's pattern
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid
+ * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern
+ * was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_syscall_get_pattern(
+ const struct lttng_event_rule *rule, const char **pattern);
+
+/*
+ * Set the filter expression of a syscall event rule.
+ *
+ * The expression is copied internally.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_syscall_set_filter(
+ struct lttng_event_rule *rule, const char *expression);
+
+/*
+ * Get the filter expression of a syscall event rule.
+ *
+ * The caller does not assume the ownership of the returned filter expression.
+ * The filter expression shall only only be used for the duration of the event
+ * rule's lifetime, or before a different filter expression is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's filter
+ * expression on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid
+ * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a filter expression
+ * was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_syscall_get_filter(
+ const struct lttng_event_rule *rule, const char **expression);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_SYSCALL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H
+#define LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H
+
+#include <common/buffer-view.h>
+#include <common/macros.h>
+#include <lttng/domain.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/event.h>
+
+struct lttng_event_rule_tracepoint {
+ struct lttng_event_rule parent;
+
+ /* Domain */
+ enum lttng_domain_type domain;
+
+ /* Name pattern */
+ char *pattern;
+
+ /* Filter */
+ char *filter_expression;
+
+ /* Loglevel */
+ struct {
+ enum lttng_loglevel_type type;
+ int value;
+ } loglevel;
+
+ /* Exclusions */
+
+ struct {
+ char **values;
+ unsigned int count;
+ } exclusions;
+
+ /* internal use only */
+ struct {
+ char *filter;
+ struct lttng_bytecode *bytecode;
+ } internal_filter;
+};
+
+struct lttng_event_rule_tracepoint_comm {
+ /* enum lttng_domain_type */
+ int8_t domain_type;
+ /* enum lttng_event_logleven_type */
+ int8_t loglevel_type;
+ int32_t loglevel_value;
+ uint32_t pattern_len;
+ uint32_t filter_expression_len;
+ uint32_t exclusions_count;
+ uint32_t exclusions_len;
+ /*
+ * pattern, filter expression and exclusions each terminating with '\0'
+ */
+ char payload[];
+} LTTNG_PACKED;
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_tracepoint_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_event_rule **rule);
+
+#endif /* LTTNG_EVENT_RULE_TRACEPOINT_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_TRACEPOINT_H
+#define LTTNG_EVENT_RULE_TRACEPOINT_H
+
+#include <lttng/domain.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated tracepoint event rule.
+ *
+ * Returns a new event rule on success, NULL on failure. This event rule must be
+ * destroyed using lttng_event_rule_destroy().
+ */
+extern struct lttng_event_rule *lttng_event_rule_tracepoint_create(
+ enum lttng_domain_type domain);
+
+/*
+ * Set the pattern of a tracepoint event rule.
+ *
+ * Pattern can contain wildcard '*'. See man lttng-enable-event.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_pattern(
+ struct lttng_event_rule *rule, const char *pattern);
+
+/*
+ * Get the pattern of a tracepoint event rule.
+ *
+ * The caller does not assume the ownership of the returned pattern. The
+ * pattern shall only only be used for the duration of the event rule's
+ * lifetime, or before a different pattern is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's pattern
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid
+ * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern
+ * was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_pattern(
+ const struct lttng_event_rule *rule, const char **pattern);
+
+/*
+ * Get the domain type of a tracepoint event rule.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the domain type output parameter
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is
+ * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a pattern was not set prior to
+ * this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_domain_type(
+ const struct lttng_event_rule *rule,
+ enum lttng_domain_type *type);
+
+/*
+ * Set the filter expression of a tracepoint event rule.
+ *
+ * The expression is copied internally.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_filter(
+ struct lttng_event_rule *rule, const char *expression);
+
+/*
+ * Get the filter expression of a tracepoint event rule.
+ *
+ * The caller does not assume the ownership of the returned filter expression.
+ * The filter expression shall only only be used for the duration of the event
+ * rule's lifetime, or before a different filter expression is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's filter
+ * expression on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid
+ * parameter is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a filter expression
+ * was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_filter(
+ const struct lttng_event_rule *rule, const char **expression);
+
+/*
+ * Set the single loglevel of a tracepoint event rule.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel(
+ struct lttng_event_rule *rule, int level);
+
+/*
+ * Set the loglevel range of a tracepoint event rule.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status
+lttng_event_rule_tracepoint_set_loglevel_range(
+ struct lttng_event_rule *rule, int level);
+
+/*
+ * Set the loglevel to all of a tracepoint event rule.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status
+lttng_event_rule_tracepoint_set_loglevel_all(struct lttng_event_rule *rule);
+
+/*
+ * Get the loglevel type of a tracepoint event rule.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the loglevel type output
+ * parameter on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter
+ * is passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a loglevel was not set prior
+ * to this call.
+ */
+extern enum lttng_event_rule_status
+lttng_event_rule_tracepoint_get_loglevel_type(
+ const struct lttng_event_rule *rule,
+ enum lttng_loglevel_type *type);
+
+/*
+ * Get the loglevel of a tracepoint event rule.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the loglevel output parameter
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is
+ * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a loglevel was not set prior to
+ * this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_get_loglevel(
+ const struct lttng_event_rule *rule, int *level);
+
+/*
+ * Set the exclusions property of a event rule.
+ *
+ * The passed exclusions will be copied to the event_rule.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK on success,
+ * LTTNG_EVENT_RULE_STATUS_INVALID if invalid parameters are passed, or
+ * LTTNG_EVENT_RULE_STATUS_UNSUPPORTED if this property is not supported by the
+ * domain.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_tracepoint_set_exclusions(
+ struct lttng_event_rule *rule,
+ unsigned int count,
+ const char **exclusions);
+
+/*
+ * Get the exclusions property count of a event rule.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the count output parameter
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is
+ * passed.
+ */
+extern enum lttng_event_rule_status
+lttng_event_rule_tracepoint_get_exclusions_count(
+ const struct lttng_event_rule *rule, unsigned int *count);
+
+/*
+ * Get the event rule exclusion at the given index.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and sets the exclusion output parameter
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is
+ * passed.
+ */
+extern enum lttng_event_rule_status
+lttng_event_rule_tracepoint_get_exclusion_at_index(
+ const struct lttng_event_rule *rule,
+ unsigned int index,
+ const char **exclusion);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_TRACEPOINT_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_UPROBE_INTERNAL_H
+#define LTTNG_EVENT_RULE_UPROBE_INTERNAL_H
+
+#include <common/buffer-view.h>
+#include <common/macros.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/uprobe.h>
+
+struct lttng_event_rule_uprobe {
+ struct lttng_event_rule parent;
+ char *name;
+ struct lttng_userspace_probe_location *location;
+};
+
+struct lttng_event_rule_uprobe_comm {
+ uint32_t name_len;
+ uint32_t location_len;
+ /*name, location object */
+ char payload[];
+} LTTNG_PACKED;
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_uprobe_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_event_rule **rule);
+
+LTTNG_HIDDEN
+struct lttng_userspace_probe_location *
+lttng_event_rule_uprobe_get_location_no_const(
+ const struct lttng_event_rule *rule);
+
+#endif /* LTTNG_EVENT_RULE_UPROBE_INTERNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_EVENT_RULE_UPROBE_H
+#define LTTNG_EVENT_RULE_UPROBE_H
+
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/userspace-probe.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Create a newly allocated uprobe event rule.
+ *
+ * Returns a new event rule on success, NULL on failure. This event rule must be
+ * destroyed using lttng_event_rule_destroy().
+ */
+extern struct lttng_event_rule *lttng_event_rule_uprobe_create(void);
+
+/*
+ * Set the location of a uprobe event rule.
+ *
+ * The location is copied internally.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_location(
+ struct lttng_event_rule *rule,
+ const struct lttng_userspace_probe_location *location);
+
+/*
+ * Get the location of a uprobe event rule.
+ *
+ * The caller does not assume the ownership of the returned location.
+ * The location shall only be used for the duration of the event
+ * rule's lifetime, or before a different location is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's location
+ * on success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is
+ * passed, or LTTNG_EVENT_RULE_STATUS_UNSET if a location was not set prior to
+ * this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_location(
+ const struct lttng_event_rule *rule,
+ const struct lttng_userspace_probe_location **location);
+
+/*
+ * Set the name of a uprobe event rule.
+ *
+ * The name is copied internally.
+ *
+ * Return LTTNG_EVENT_RULE_STATUS_OK on success, LTTNG_EVENT_RULE_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_uprobe_set_name(
+ struct lttng_event_rule *rule, const char *name);
+
+/*
+ * Get the name of a uprobe event rule.
+ *
+ * The caller does not assume the ownership of the returned name.
+ * The name shall only only be used for the duration of the event
+ * rule's lifetime, or before a different name is set.
+ *
+ * Returns LTTNG_EVENT_RULE_STATUS_OK and a pointer to the event rule's name on
+ * success, LTTNG_EVENT_RULE_STATUS_INVALID if an invalid parameter is passed,
+ * or LTTNG_EVENT_RULE_STATUS_UNSET if a name was not set prior to this call.
+ */
+extern enum lttng_event_rule_status lttng_event_rule_uprobe_get_name(
+ const struct lttng_event_rule *rule, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LTTNG_EVENT_RULE_UPROBE_H */
/* Include every LTTng ABI/API available. */
#include <lttng/action/action.h>
+#include <lttng/action/group.h>
#include <lttng/action/notify.h>
+#include <lttng/action/rotate-session.h>
+#include <lttng/action/snapshot-session.h>
+#include <lttng/action/start-session.h>
+#include <lttng/action/stop-session.h>
#include <lttng/channel.h>
#include <lttng/clear-handle.h>
#include <lttng/clear.h>
#include <lttng/condition/buffer-usage.h>
#include <lttng/condition/condition.h>
#include <lttng/condition/evaluation.h>
+#include <lttng/condition/event-rule.h>
#include <lttng/condition/session-consumed-size.h>
#include <lttng/condition/session-rotation.h>
#include <lttng/destruction-handle.h>
#include <lttng/domain.h>
#include <lttng/endpoint.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/kprobe.h>
+#include <lttng/event-rule/kretprobe.h>
+#include <lttng/event-rule/syscall.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/event-rule/uprobe.h>
#include <lttng/event.h>
+#include <lttng/event-expr.h>
+#include <lttng/event-field-value.h>
#include <lttng/handle.h>
#include <lttng/health.h>
#include <lttng/load.h>
size_t count;
/*
- * Containes snapshot output object.
+ * Contains snapshot output object.
*/
struct lttng_snapshot_output *array;
};
*/
/* Return snapshot ID. */
-uint32_t lttng_snapshot_output_get_id(struct lttng_snapshot_output *output);
+uint32_t lttng_snapshot_output_get_id(const struct lttng_snapshot_output *output);
/* Return maximum size of a snapshot. */
-uint64_t lttng_snapshot_output_get_maxsize(struct lttng_snapshot_output *output);
+uint64_t lttng_snapshot_output_get_maxsize(const struct lttng_snapshot_output *output);
/* Return snapshot name. */
-const char *lttng_snapshot_output_get_name(struct lttng_snapshot_output *output);
+const char *lttng_snapshot_output_get_name(const struct lttng_snapshot_output *output);
/* Return snapshot control URL in a text format. */
-const char *lttng_snapshot_output_get_ctrl_url(struct lttng_snapshot_output *output);
+const char *lttng_snapshot_output_get_ctrl_url(const struct lttng_snapshot_output *output);
/* Return snapshot data URL in a text format. */
-const char *lttng_snapshot_output_get_data_url(struct lttng_snapshot_output *output);
+const char *lttng_snapshot_output_get_data_url(const struct lttng_snapshot_output *output);
/*
* Snapshot output setter family functions.
/* Set the snapshot name. */
int lttng_snapshot_output_set_name(const char *name,
struct lttng_snapshot_output *output);
+
+/*
+ * Set the output destination to be a path on the local filesystem.
+ *
+ * The path must be absolute. It can optionally begin with `file://`.
+ */
+int lttng_snapshot_output_set_local_path(const char *path,
+ struct lttng_snapshot_output *output);
+
+/*
+ * Set the output destination to be the network from a combined control/data
+ * URL.
+ *
+ * `url` must start with `net://` or `net6://`.
+ */
+int lttng_snapshot_output_set_network_url(const char *url,
+ struct lttng_snapshot_output *output);
+
+/*
+ * Set the output destination to be the network using separate URLs for control
+ * and data.
+ *
+ * `ctrl_url` and `data_url` must start with `tcp://` or `tcp6://`.
+ */
+int lttng_snapshot_output_set_network_urls(
+ const char *ctrl_url, const char *data_url,
+ struct lttng_snapshot_output *output);
+
+// Deprecated?
+
/* Set the control URL. Local and remote URL are supported. */
int lttng_snapshot_output_set_ctrl_url(const char *url,
struct lttng_snapshot_output *output);
#include <common/macros.h>
#include <common/buffer-view.h>
#include <common/dynamic-buffer.h>
+#include <common/dynamic-array.h>
+#include <common/credentials.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
+#include <urcu/ref.h>
struct lttng_trigger {
+ struct urcu_ref ref; /* internal use only */
+ bool owns_internal_objects; /* internal use only */
+
struct lttng_condition *condition;
struct lttng_action *action;
+ char *name;
+ struct { /* Internal use only */
+ bool set;
+ uint64_t value;
+ } key;
+ struct { /* internal use only */
+ struct lttng_credentials credentials;
+ bool set;
+ } creds;
+ struct {
+ enum lttng_trigger_firing_policy_type type;
+ uint64_t threshold;
+ uint64_t current_count;
+ } firing_policy;
+
+ /* This ordered set is used to hold the capture bytecodoes and their
+ * expression. lttng_action_capture_bytecode_element.
+ * We could only have bytecodes here... the expression are a left over
+ * from the generation process of the set. They are used for comparison
+ * during the gathering process. They are refcounted (TODO) and are the same
+ * object that are present un the underlying action object/s
+ */
+ struct lttng_dynamic_pointer_array capture_bytecode_set;
+};
+
+struct lttng_triggers {
+ struct lttng_dynamic_pointer_array array;
};
struct lttng_trigger_comm {
/* length excludes its own length. */
+ uint32_t name_length /* Includes '\0' */;
uint32_t length;
- /* A condition and action object follow. */
+ uint8_t policy_type;
+ uint64_t policy_threshold;
+ /* A name, condition and action object follow. */
char payload[];
} LTTNG_PACKED;
+struct lttng_triggers_comm {
+ uint32_t count;
+ uint32_t length;
+ /* Count * lttng_trigger_comm structure */
+ char payload[];
+};
+
LTTNG_HIDDEN
ssize_t lttng_trigger_create_from_buffer(const struct lttng_buffer_view *view,
struct lttng_trigger **trigger);
LTTNG_HIDDEN
-int lttng_trigger_serialize(struct lttng_trigger *trigger,
- struct lttng_dynamic_buffer *buf);
+int lttng_trigger_serialize(const struct lttng_trigger *trigger,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send);
+
+LTTNG_HIDDEN
+bool lttng_trigger_validate(const struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+int lttng_trigger_assign(struct lttng_trigger *dst,
+ const struct lttng_trigger *src);
+
+LTTNG_HIDDEN
+void lttng_trigger_set_key(struct lttng_trigger *trigger, uint64_t key);
+
+LTTNG_HIDDEN
+uint64_t lttng_trigger_get_key(const struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+int lttng_trigger_generate_name(struct lttng_trigger *trigger, uint64_t offset);
+
+LTTNG_HIDDEN
+bool lttng_trigger_is_equal(const struct lttng_trigger *a,
+ const struct lttng_trigger *b);
+
+LTTNG_HIDDEN
+void lttng_trigger_get(struct lttng_trigger *trigger);
+
+LTTNG_HIDDEN
+void lttng_trigger_put(struct lttng_trigger *trigger);
+
+/*
+ * Allocate a new list of lttng_trigger.
+ * The returned object must be freed via lttng_triggers_destroy.
+ */
+LTTNG_HIDDEN
+struct lttng_triggers *lttng_triggers_create(void);
+
+/*
+ * Return the non-const pointer of an element at index "index" of a
+ * lttng_triggers.
+ *
+ * The ownership of the lttng_triggers element is NOT transfered.
+ * The returned object can NOT be freed via lttng_trigger_destroy.
+ */
+LTTNG_HIDDEN
+struct lttng_trigger *lttng_triggers_get_pointer_of_index(
+ const struct lttng_triggers *triggers, unsigned int index);
+
+/*
+ * TODO:
+ */
+LTTNG_HIDDEN
+int lttng_triggers_add(
+ struct lttng_triggers *triggers, struct lttng_trigger *trigger);
+
+/*
+ * Serialize a trigger collection to a lttng_dynamic_buffer.
+ * Return LTTNG_OK on success, negative lttng error code on error.
+ */
+LTTNG_HIDDEN
+int lttng_triggers_serialize(const struct lttng_triggers *triggers,
+ struct lttng_dynamic_buffer *buffer);
+
+LTTNG_HIDDEN
+ssize_t lttng_triggers_create_from_buffer(const struct lttng_buffer_view *view,
+ struct lttng_triggers **triggers);
+
+LTTNG_HIDDEN
+const struct lttng_credentials *lttng_trigger_get_credentials(
+ const struct lttng_trigger *trigger);
LTTNG_HIDDEN
-const struct lttng_condition *lttng_trigger_get_const_condition(
+void lttng_trigger_set_credentials(
+ struct lttng_trigger *trigger, uid_t uid, gid_t git);
+
+LTTNG_HIDDEN
+bool lttng_trigger_is_ready_to_fire(
+ struct lttng_trigger *trigger);
+
+/*
+ * Return the type of any uderlying domain requirement. If no particular
+ * requirement is needed return LTTNG_DOMAIN_NONE.
+ */
+LTTNG_HIDDEN
+enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction(
const struct lttng_trigger *trigger);
LTTNG_HIDDEN
-const struct lttng_action *lttng_trigger_get_const_action(
+unsigned int lttng_trigger_get_capture_bytecode_count(
const struct lttng_trigger *trigger);
LTTNG_HIDDEN
-bool lttng_trigger_validate(struct lttng_trigger *trigger);
+const struct lttng_bytecode *
+lttng_trigger_get_capture_bytecode_at_index(
+ const struct lttng_trigger *trigger, unsigned int index);
#endif /* LTTNG_TRIGGER_INTERNAL_H */
struct lttng_action;
struct lttng_condition;
struct lttng_trigger;
+/* A collection of trigger */
+struct lttng_triggers;
#ifdef __cplusplus
extern "C" {
LTTNG_REGISTER_TRIGGER_STATUS_INVALID = -1,
};
+enum lttng_trigger_status {
+ LTTNG_TRIGGER_STATUS_OK = 0,
+ LTTNG_TRIGGER_STATUS_ERROR = -1,
+ LTTNG_TRIGGER_STATUS_UNKNOWN = -2,
+ LTTNG_TRIGGER_STATUS_INVALID = -3,
+ LTTNG_TRIGGER_STATUS_UNSET = -4,
+ LTTNG_TRIGGER_STATUS_UNSUPPORTED = -5,
+};
+
+enum lttng_trigger_firing_policy_type {
+ LTTNG_TRIGGER_FIRE_EVERY_N = 0,
+ LTTNG_TRIGGER_FIRE_ONCE_AFTER_N = 1,
+};
+
/*
* Create a trigger object associating a condition and an action.
*
* The caller retains the ownership of both the condition and action
* and both must be kept alive for the lifetime of the trigger object.
*
+ * If the action is a notification action with capture descriptors,
+ * the condition must be an event rule condition.
+ *
* A trigger must be registered in order to become activate and can
* be destroyed after its registration.
*
extern struct lttng_condition *lttng_trigger_get_condition(
struct lttng_trigger *trigger);
+const struct lttng_condition *lttng_trigger_get_const_condition(
+ const struct lttng_trigger *trigger);
+
/*
* Get the action of a trigger.
*
extern struct lttng_action *lttng_trigger_get_action(
struct lttng_trigger *trigger);
+const struct lttng_action *lttng_trigger_get_const_action(
+ const struct lttng_trigger *trigger);
+
+/*
+ * Get the name of a trigger.
+ *
+ * The caller does not assume the ownership of the returned name.
+ * The name shall only only be used for the duration of the trigger's
+ * lifetime, or before a different name is set.
+ *
+ * Returns LTTNG_TRIGGER_STATUS_OK and a pointer to the trigger's name on
+ * success, LTTNG_TRIGGER_STATUS_INVALID if an invalid parameter is passed,
+ * or LTTNG_TRIGGER_STATUS_UNSET if a name was not set prior to this call.
+ */
+extern enum lttng_trigger_status lttng_trigger_get_name(
+ const struct lttng_trigger *trigger, const char **name);
+
+/*
+ * Set the trigger name.
+ *
+ * A name is optional.
+ * A name will be assigned on trigger registration if no name is set.
+ *
+ * The name is copied.
+ *
+ * Return LTTNG_TRIGGER_STATUS_OK on success, LTTNG_TRIGGER_STATUS_INVALID
+ * if invalid parameters are passed.
+ */
+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 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_set_firing_policy(
+ struct lttng_trigger *trigger,
+ enum lttng_trigger_firing_policy_type policy_type,
+ unsigned long long threshold);
+extern enum lttng_trigger_status lttng_trigger_get_firing_policy(
+ const struct lttng_trigger *trigger,
+ enum lttng_trigger_firing_policy_type *policy_type,
+ unsigned long long *threshold);
+
/*
* Destroy (frees) a trigger object.
*/
*
* Return 0 on success, a negative LTTng error code on error.
*/
-extern int lttng_unregister_trigger(struct lttng_trigger *trigger);
+extern int lttng_unregister_trigger(const struct lttng_trigger *trigger);
+
+/*
+ * List current triggers.
+ *
+ * On success, triggers is allocated.
+ * The trigger collection must be free by the caller with lttng_destroy_triggers
+ *
+ * Returns 0 on success, else a negative LTTng error code.
+ */
+extern int lttng_list_triggers(struct lttng_triggers **triggers);
+
+/*
+ * Get a trigger from the collection at a given index.
+ *
+ * Note that the collection maintains the ownership of the returned trigger.
+ * It must not be destroyed by the user, nor should it be held beyond the
+ * lifetime of the trigger collection.
+ *
+ * Returns a trigger, or NULL on error.
+ */
+extern const struct lttng_trigger *lttng_triggers_get_at_index(
+ const struct lttng_triggers *triggers, unsigned int index);
+
+/*
+ * Get the number of trigger in a tracker id list.
+ *
+ * Return LTTNG_TRIGGER_STATUS_OK on success,
+ * LTTNG_TRIGGER_STATUS_INVALID when passed invalid parameters.
+ */
+extern enum lttng_trigger_status lttng_triggers_get_count(
+ const struct lttng_triggers *triggers, unsigned int *count);
+
+/*
+ * Destroy a trigger collection.
+ */
+extern void lttng_triggers_destroy(struct lttng_triggers *ids);
+
#ifdef __cplusplus
}
#include <common/dynamic-buffer.h>
#include <common/buffer-view.h>
+typedef bool (*userspace_probe_location_equal_cb)(
+ const struct lttng_userspace_probe_location *a,
+ const struct lttng_userspace_probe_location *b);
+
/*
* No elf-specific comm structure is defined since no elf-specific payload is
* currently needed.
struct lttng_userspace_probe_location {
enum lttng_userspace_probe_location_type type;
struct lttng_userspace_probe_location_lookup_method *lookup_method;
+ userspace_probe_location_equal_cb equal;
};
struct lttng_userspace_probe_location_function {
struct lttng_userspace_probe_location *lttng_userspace_probe_location_copy(
const struct lttng_userspace_probe_location *location);
+LTTNG_HIDDEN
+bool lttng_userspace_probe_location_lookup_method_is_equal(
+ const struct lttng_userspace_probe_location_lookup_method *a,
+ const struct lttng_userspace_probe_location_lookup_method *b);
+
+LTTNG_HIDDEN
+bool lttng_userspace_probe_location_is_equal(
+ const struct lttng_userspace_probe_location *a,
+ const struct lttng_userspace_probe_location *b);
+
+LTTNG_HIDDEN
+int lttng_userspace_probe_location_set_binary_fd(
+ struct lttng_userspace_probe_location *location, int fd);
+
#endif /* LTTNG_USERSPACE_PROBE_INTERNAL_H */
manage-kernel.c manage-kernel.h \
manage-consumer.c manage-consumer.h \
clear.c clear.h \
- tracker.c tracker.h
+ tracker.c tracker.h \
+ action-executor.c action-executor.h
if HAVE_LIBLTTNG_UST_CTL
lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \
lttng_sessiond_SOURCES += lttng-sessiond.h main.c
# link on liblttngctl for check if sessiond is already alive.
-lttng_sessiond_LDADD = -lurcu-common -lurcu $(KMOD_LIBS) \
+lttng_sessiond_LDADD = -lurcu-common -lmsgpackc -lurcu $(KMOD_LIBS) \
$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \
$(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \
$(top_builddir)/src/common/kernel-ctl/libkernel-ctl.la \
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "action-executor.h"
+#include "cmd.h"
+#include "health-sessiond.h"
+#include "lttng-sessiond.h"
+#include "notification-thread-internal.h"
+#include "session.h"
+#include "thread.h"
+#include <common/macros.h>
+#include <lttng/action/group.h>
+#include <lttng/action/notify-internal.h>
+#include <lttng/action/notify.h>
+#include <lttng/action/rotate-session.h>
+#include <lttng/action/snapshot-session.h>
+#include <lttng/action/start-session.h>
+#include <lttng/action/stop-session.h>
+#include <lttng/condition/evaluation.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/lttng-error.h>
+#include <lttng/trigger/trigger-internal.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <urcu/list.h>
+
+#define THREAD_NAME "Action Executor"
+#define MAX_QUEUED_WORK_COUNT 8192
+
+struct action_work_item {
+ uint64_t id;
+ struct lttng_trigger *trigger;
+ struct notification_client_list *client_list;
+ struct cds_list_head list_node;
+ struct lttng_trigger_notification *trigger_notification;
+};
+
+struct action_executor {
+ struct lttng_thread *thread;
+ struct notification_thread_handle *notification_thread_handle;
+ struct {
+ uint64_t pending_count;
+ struct cds_list_head list;
+ pthread_cond_t cond;
+ pthread_mutex_t lock;
+ } work;
+ bool should_quit;
+ uint64_t next_work_item_id;
+};
+
+typedef int (*action_executor_handler)(struct action_executor *executor,
+ const struct action_work_item *,
+ const struct lttng_action *action);
+
+static int action_executor_notify_handler(struct action_executor *executor,
+ const struct action_work_item *,
+ const struct lttng_action *);
+static int action_executor_start_session_handler(struct action_executor *executor,
+ const struct action_work_item *,
+ const struct lttng_action *);
+static int action_executor_stop_session_handler(struct action_executor *executor,
+ const struct action_work_item *,
+ const struct lttng_action *);
+static int action_executor_rotate_session_handler(struct action_executor *executor,
+ const struct action_work_item *,
+ const struct lttng_action *);
+static int action_executor_snapshot_session_handler(struct action_executor *executor,
+ const struct action_work_item *,
+ const struct lttng_action *);
+static int action_executor_group_handler(struct action_executor *executor,
+ const struct action_work_item *,
+ const struct lttng_action *);
+static int action_executor_generic_handler(struct action_executor *executor,
+ const struct action_work_item *,
+ const struct lttng_action *);
+
+static const action_executor_handler action_executors[] = {
+ [LTTNG_ACTION_TYPE_NOTIFY] = action_executor_notify_handler,
+ [LTTNG_ACTION_TYPE_START_SESSION] = action_executor_start_session_handler,
+ [LTTNG_ACTION_TYPE_STOP_SESSION] = action_executor_stop_session_handler,
+ [LTTNG_ACTION_TYPE_ROTATE_SESSION] = action_executor_rotate_session_handler,
+ [LTTNG_ACTION_TYPE_SNAPSHOT_SESSION] = action_executor_snapshot_session_handler,
+ [LTTNG_ACTION_TYPE_GROUP] = action_executor_group_handler,
+};
+
+static const char *get_action_name(const struct lttng_action *action)
+{
+ const char *action_type_names[] = {
+ [LTTNG_ACTION_TYPE_NOTIFY] = "Notify",
+ [LTTNG_ACTION_TYPE_START_SESSION] = "Start session",
+ [LTTNG_ACTION_TYPE_STOP_SESSION] = "Stop session",
+ [LTTNG_ACTION_TYPE_ROTATE_SESSION] = "Rotate session",
+ [LTTNG_ACTION_TYPE_SNAPSHOT_SESSION] = "Snapshot session",
+ [LTTNG_ACTION_TYPE_GROUP] = "Group",
+ };
+
+ return action_type_names[lttng_action_get_type(action)];
+}
+
+static const char *get_trigger_name(const struct lttng_trigger *trigger)
+{
+ const char *trigger_name;
+ enum lttng_trigger_status trigger_status;
+
+ trigger_status = lttng_trigger_get_name(trigger, &trigger_name);
+ assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+ return trigger_name;
+}
+
+static int client_handle_transmission_status(
+ struct notification_client *client,
+ enum client_transmission_status status,
+ void *user_data)
+{
+ int ret = 0;
+ struct action_executor *executor = user_data;
+ bool update_communication = true;
+
+ ASSERT_LOCKED(client->lock);
+
+ switch (status) {
+ case CLIENT_TRANSMISSION_STATUS_COMPLETE:
+ DBG("Successfully sent full notification to client, client_id = %" PRIu64,
+ client->id);
+ update_communication = false;
+ break;
+ case CLIENT_TRANSMISSION_STATUS_QUEUED:
+ DBG("Queued notification in client outgoing buffer, client_id = %" PRIu64,
+ client->id);
+ break;
+ case CLIENT_TRANSMISSION_STATUS_FAIL:
+ DBG("Communication error occurred while sending notification to client, client_id = %" PRIu64,
+ client->id);
+ client->communication.active = false;
+ break;
+ default:
+ ERR("Fatal error encoutered while sending notification to client, client_id = %" PRIu64,
+ client->id);
+ client->communication.active = false;
+ ret = -1;
+ goto end;
+ }
+
+ if (!update_communication) {
+ goto end;
+ }
+
+ ret = notification_thread_client_communication_update(
+ executor->notification_thread_handle, client->id,
+ status);
+end:
+ return ret;
+}
+
+static int action_executor_notify_handler(struct action_executor *executor,
+ const struct action_work_item *work_item,
+ const struct lttng_action *action)
+{
+ int ret = 0;
+ struct lttng_evaluation *evaluation = NULL;
+ struct lttng_trigger_notification *notification = work_item->trigger_notification;
+ unsigned int capture_count = 0;
+
+ assert(work_item->client_list);
+
+ if (LTTNG_CONDITION_STATUS_OK !=
+ lttng_condition_event_rule_get_capture_descriptor_count(
+ lttng_trigger_get_const_condition(work_item->trigger),
+ &capture_count)) {
+ ERR("Get capture count");
+ ret = -1;
+ goto end;
+ }
+
+ if (!notification->capture_buffer && capture_count != 0) {
+ ERR("Expected capture but capture buffer is null");
+ ret = -1;
+ goto end;
+ }
+
+ evaluation = lttng_evaluation_event_rule_create(
+ container_of(lttng_trigger_get_const_condition(work_item->trigger),
+ struct lttng_condition_event_rule,
+ parent),
+ get_trigger_name(work_item->trigger),
+ notification->capture_buffer,
+ notification->capture_buf_size, false);
+ if (!evaluation) {
+ ERR("Failed to create event rule hit evaluation");
+ ret = -1;
+ goto end;
+ }
+
+ ret = notification_client_list_send_evaluation(work_item->client_list,
+ lttng_trigger_get_const_condition(work_item->trigger),
+ evaluation,
+ lttng_trigger_get_credentials(work_item->trigger), NULL,
+ client_handle_transmission_status, executor);
+end:
+ lttng_evaluation_destroy(evaluation);
+ return ret;
+}
+
+static int action_executor_start_session_handler(struct action_executor *executor,
+ const struct action_work_item *work_item,
+ const struct lttng_action *action)
+{
+ int ret = 0;
+ const char *session_name;
+ enum lttng_action_status action_status;
+ struct ltt_session *session;
+
+ action_status = lttng_action_start_session_get_session_name(
+ action, &session_name);
+ if (action_status != LTTNG_ACTION_STATUS_OK) {
+ ERR("Failed to get session name from \"%s\" action",
+ get_action_name(action));
+ ret = -1;
+ goto end;
+ }
+
+ session_lock_list();
+ session = session_find_by_name(session_name);
+ if (session) {
+ enum lttng_error_code cmd_ret;
+
+ session_lock(session);
+ cmd_ret = cmd_start_trace(session);
+ session_unlock(session);
+
+ switch (cmd_ret) {
+ case LTTNG_OK:
+ DBG("Successfully started session \"%s\" on behalf of trigger \"%s\"",
+ session_name,
+ get_trigger_name(work_item->trigger));
+ break;
+ case LTTNG_ERR_TRACE_ALREADY_STARTED:
+ DBG("Attempted to start session \"%s\" on behalf of trigger \"%s\" but it was already started",
+ session_name,
+ get_trigger_name(work_item->trigger));
+ break;
+ default:
+ WARN("Failed to start session \"%s\" on behalf of trigger \"%s\": %s",
+ session_name,
+ get_trigger_name(work_item->trigger),
+ lttng_strerror(-cmd_ret));
+ break;
+ }
+ session_put(session);
+ } else {
+ DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"",
+ session_name, get_action_name(action),
+ get_trigger_name(work_item->trigger));
+ }
+ session_unlock_list();
+end:
+ return ret;
+}
+
+static int action_executor_stop_session_handler(struct action_executor *executor,
+ const struct action_work_item *work_item,
+ const struct lttng_action *action)
+{
+ int ret = 0;
+ const char *session_name;
+ enum lttng_action_status action_status;
+ struct ltt_session *session;
+
+ action_status = lttng_action_stop_session_get_session_name(
+ action, &session_name);
+ if (action_status != LTTNG_ACTION_STATUS_OK) {
+ ERR("Failed to get session name from \"%s\" action",
+ get_action_name(action));
+ ret = -1;
+ goto end;
+ }
+
+ session_lock_list();
+ session = session_find_by_name(session_name);
+ if (session) {
+ enum lttng_error_code cmd_ret;
+
+ session_lock(session);
+ cmd_ret = cmd_stop_trace(session);
+ session_unlock(session);
+
+ switch (cmd_ret) {
+ case LTTNG_OK:
+ DBG("Successfully stopped session \"%s\" on behalf of trigger \"%s\"",
+ session_name,
+ get_trigger_name(work_item->trigger));
+ break;
+ case LTTNG_ERR_TRACE_ALREADY_STOPPED:
+ DBG("Attempted to stop session \"%s\" on behalf of trigger \"%s\" but it was already stopped",
+ session_name,
+ get_trigger_name(work_item->trigger));
+ break;
+ default:
+ WARN("Failed to stop session \"%s\" on behalf of trigger \"%s\": %s",
+ session_name,
+ get_trigger_name(work_item->trigger),
+ lttng_strerror(-cmd_ret));
+ break;
+ }
+ session_put(session);
+ } else {
+ DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"",
+ session_name, get_action_name(action),
+ get_trigger_name(work_item->trigger));
+ }
+ session_unlock_list();
+end:
+ return ret;
+}
+
+static int action_executor_rotate_session_handler(struct action_executor *executor,
+ const struct action_work_item *work_item,
+ const struct lttng_action *action)
+{
+ int ret = 0;
+ const char *session_name;
+ enum lttng_action_status action_status;
+ struct ltt_session *session;
+
+ action_status = lttng_action_rotate_session_get_session_name(
+ action, &session_name);
+ if (action_status != LTTNG_ACTION_STATUS_OK) {
+ ERR("Failed to get session name from \"%s\" action",
+ get_action_name(action));
+ ret = -1;
+ goto end;
+ }
+
+ session_lock_list();
+ session = session_find_by_name(session_name);
+ if (session) {
+ enum lttng_error_code cmd_ret;
+
+ session_lock(session);
+ cmd_ret = cmd_rotate_session(session, NULL, false,
+ LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED);
+ session_unlock(session);
+
+ switch (cmd_ret) {
+ case LTTNG_OK:
+ DBG("Successfully started rotation of session \"%s\" on behalf of trigger \"%s\"",
+ session_name,
+ get_trigger_name(work_item->trigger));
+ break;
+ case LTTNG_ERR_ROTATION_PENDING:
+ DBG("Attempted to start a rotation of session \"%s\" on behalf of trigger \"%s\" but a rotation is already ongoing",
+ session_name,
+ get_trigger_name(work_item->trigger));
+ break;
+ case LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP:
+ case LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR:
+ DBG("Attempted to start a rotation of session \"%s\" on behalf of trigger \"%s\" but a rotation has already been completed since the last stop or clear",
+ session_name,
+ get_trigger_name(work_item->trigger));
+ break;
+ default:
+ WARN("Failed to start a rotation of session \"%s\" on behalf of trigger \"%s\": %s",
+ session_name,
+ get_trigger_name(work_item->trigger),
+ lttng_strerror(-cmd_ret));
+ break;
+ }
+ session_put(session);
+ } else {
+ DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"",
+ session_name, get_action_name(action),
+ get_trigger_name(work_item->trigger));
+ }
+ session_unlock_list();
+end:
+ return ret;
+}
+
+static int action_executor_snapshot_session_handler(struct action_executor *executor,
+ const struct action_work_item *work_item,
+ const struct lttng_action *action)
+{
+ int ret = 0;
+ const char *session_name;
+ enum lttng_action_status action_status;
+ struct ltt_session *session;
+ const struct lttng_snapshot_output default_snapshot_output = {
+ .max_size = UINT64_MAX,
+ };
+ const struct lttng_snapshot_output *snapshot_output =
+ &default_snapshot_output;
+
+ action_status = lttng_action_snapshot_session_get_session_name(
+ action, &session_name);
+ if (action_status != LTTNG_ACTION_STATUS_OK) {
+ ERR("Failed to get session name from \"%s\" action",
+ get_action_name(action));
+ ret = -1;
+ goto end;
+ }
+
+ action_status = lttng_action_snapshot_session_get_output_const(
+ action, &snapshot_output);
+ if (action_status != LTTNG_ACTION_STATUS_OK &&
+ action_status != LTTNG_ACTION_STATUS_UNSET) {
+ ERR("Failed to get output from \"%s\" action",
+ get_action_name(action));
+ ret = -1;
+ goto end;
+ }
+
+ session_lock_list();
+ session = session_find_by_name(session_name);
+ if (session) {
+ enum lttng_error_code cmd_ret;
+
+ session_lock(session);
+ cmd_ret = cmd_snapshot_record(session, snapshot_output, 0);
+ session_unlock(session);
+
+ switch (cmd_ret) {
+ case LTTNG_OK:
+ DBG("Successfully recorded snapshot of session \"%s\" on behalf of trigger \"%s\"",
+ session_name,
+ get_trigger_name(work_item->trigger));
+ break;
+ default:
+ WARN("Failed to record snapshot of session \"%s\" on behalf of trigger \"%s\": %s",
+ session_name,
+ get_trigger_name(work_item->trigger),
+ lttng_strerror(-cmd_ret));
+ break;
+ }
+ session_put(session);
+ } else {
+ DBG("Failed to find session \"%s\" by name while executing \"%s\" action of trigger \"%s\"",
+ session_name, get_action_name(action),
+ get_trigger_name(work_item->trigger));
+ }
+ session_unlock_list();
+end:
+ return ret;
+}
+
+static int action_executor_group_handler(struct action_executor *executor,
+ const struct action_work_item *work_item,
+ const struct lttng_action *action_group)
+{
+ int ret = 0;
+ unsigned int i, count;
+ enum lttng_action_status action_status;
+
+ action_status = lttng_action_group_get_count(action_group, &count);
+ if (action_status != LTTNG_ACTION_STATUS_OK) {
+ /* Fatal error. */
+ ERR("Failed to get count of action in action group");
+ ret = -1;
+ goto end;
+ }
+
+ DBG("Action group has %u action%s", count, count != 1 ? "s" : "");
+ for (i = 0; i < count; i++) {
+ const struct lttng_action *action =
+ lttng_action_group_get_at_index_const(
+ action_group, i);
+
+ ret = action_executor_generic_handler(
+ executor, work_item, action);
+ if (ret) {
+ ERR("Stopping the execution of the action group of trigger \"%s\" following a fatal error",
+ get_trigger_name(work_item->trigger));
+ goto end;
+ }
+ }
+end:
+ return ret;
+}
+
+static int action_executor_generic_handler(struct action_executor *executor,
+ const struct action_work_item *work_item,
+ const struct lttng_action *action)
+{
+ DBG("Executing action \"%s\" of trigger \"%s\" action work item %" PRIu64,
+ get_action_name(action),
+ get_trigger_name(work_item->trigger),
+ work_item->id);
+
+ return action_executors[lttng_action_get_type(action)](
+ executor, work_item, action);
+}
+
+static int action_work_item_execute(struct action_executor *executor,
+ struct action_work_item *work_item)
+{
+ int ret;
+ const struct lttng_action *action =
+ lttng_trigger_get_const_action(work_item->trigger);
+
+ DBG("Starting execution of action work item %" PRIu64 " of trigger \"%s\"",
+ work_item->id, get_trigger_name(work_item->trigger));
+ ret = action_executor_generic_handler(executor, work_item, action);
+ DBG("Completed execution of action work item %" PRIu64 " of trigger \"%s\"",
+ work_item->id, get_trigger_name(work_item->trigger));
+ return ret;
+}
+
+static void action_work_item_destroy(struct action_work_item *work_item)
+{
+ lttng_trigger_put(work_item->trigger);
+ notification_client_list_put(work_item->client_list);
+ lttng_trigger_notification_destroy(work_item->trigger_notification);
+ free(work_item);
+}
+
+static void *action_executor_thread(void *_data)
+{
+ struct action_executor *executor = _data;
+
+ assert(executor);
+
+ health_register(health_sessiond, HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR);
+
+ rcu_register_thread();
+ rcu_thread_online();
+
+ DBG("Entering work execution loop");
+ pthread_mutex_lock(&executor->work.lock);
+ while (!executor->should_quit) {
+ int ret;
+ struct action_work_item *work_item;
+
+ health_code_update();
+ if (executor->work.pending_count == 0) {
+ health_poll_entry();
+ DBG("No work items enqueued, entering wait");
+ pthread_cond_wait(&executor->work.cond,
+ &executor->work.lock);
+ DBG("Woke-up from wait");
+ health_poll_exit();
+ continue;
+ }
+
+ /* Pop item from front of the list with work lock held. */
+ work_item = cds_list_first_entry(&executor->work.list,
+ struct action_work_item, list_node);
+ cds_list_del(&work_item->list_node);
+ executor->work.pending_count--;
+
+ /*
+ * Work can be performed without holding the work lock,
+ * allowing new items to be queued.
+ */
+ pthread_mutex_unlock(&executor->work.lock);
+ ret = action_work_item_execute(executor, work_item);
+ action_work_item_destroy(work_item);
+ if (ret) {
+ /* Fatal error. */
+ break;
+ }
+ health_code_update();
+ pthread_mutex_lock(&executor->work.lock);
+ }
+ pthread_mutex_unlock(&executor->work.lock);
+ DBG("Left work execution loop");
+
+ health_code_update();
+
+ rcu_thread_offline();
+ rcu_unregister_thread();
+ health_unregister(health_sessiond);
+
+ return NULL;
+}
+
+static bool shutdown_action_executor_thread(void *_data)
+{
+ struct action_executor *executor = _data;
+
+ /* TODO. */
+ executor->should_quit = true;
+ pthread_cond_signal(&executor->work.cond);
+ return true;
+}
+
+static void clean_up_action_executor_thread(void *_data)
+{
+ struct action_executor *executor = _data;
+
+ assert(cds_list_empty(&executor->work.list));
+
+ pthread_mutex_destroy(&executor->work.lock);
+ pthread_cond_destroy(&executor->work.cond);
+ free(executor);
+}
+
+struct action_executor *action_executor_create(
+ struct notification_thread_handle *handle)
+{
+ struct action_executor *executor = zmalloc(sizeof(*executor));
+
+ if (!executor) {
+ goto end;
+ }
+
+ CDS_INIT_LIST_HEAD(&executor->work.list);
+ pthread_cond_init(&executor->work.cond, NULL);
+ pthread_mutex_init(&executor->work.lock, NULL);
+ executor->notification_thread_handle = handle;
+
+ executor->thread = lttng_thread_create(THREAD_NAME,
+ action_executor_thread, shutdown_action_executor_thread,
+ clean_up_action_executor_thread, executor);
+end:
+ return executor;
+}
+
+void action_executor_destroy(struct action_executor *executor)
+{
+ struct action_work_item *work_item, *tmp;
+
+ /* TODO Wait for work list to drain? */
+ lttng_thread_shutdown(executor->thread);
+ pthread_mutex_lock(&executor->work.lock);
+ if (executor->work.pending_count != 0) {
+ WARN("%" PRIu64
+ " trigger action%s still queued for execution and will be discarded",
+ executor->work.pending_count,
+ executor->work.pending_count == 1 ? " is" :
+ "s are");
+ }
+
+ cds_list_for_each_entry_safe (
+ work_item, tmp, &executor->work.list, list_node) {
+ WARN("Discarding action work item %" PRIu64
+ " associated to trigger \"%s\"",
+ work_item->id, get_trigger_name(work_item->trigger));
+ cds_list_del(&work_item->list_node);
+ action_work_item_destroy(work_item);
+ }
+ pthread_mutex_unlock(&executor->work.lock);
+ lttng_thread_put(executor->thread);
+}
+
+/* RCU read-lock must be held by the caller. */
+enum action_executor_status action_executor_enqueue(
+ struct action_executor *executor,
+ struct lttng_trigger *trigger,
+ struct notification_client_list *client_list,
+ struct lttng_trigger_notification *trigger_notification)
+{
+ enum action_executor_status executor_status = ACTION_EXECUTOR_STATUS_OK;
+ const uint64_t work_item_id = executor->next_work_item_id++;
+ struct action_work_item *work_item;
+ bool signal = false;
+
+ pthread_mutex_lock(&executor->work.lock);
+ /* Check for queue overflow. */
+ if (executor->work.pending_count >= MAX_QUEUED_WORK_COUNT) {
+ /* Most likely spammy, remove if it is the case. */
+ DBG("Refusing to enqueue action for trigger \"%s\" as work item %" PRIu64
+ " (overflow)",
+ get_trigger_name(trigger), work_item_id);
+ executor_status = ACTION_EXECUTOR_STATUS_OVERFLOW;
+ goto error_unlock;
+ }
+
+ work_item = zmalloc(sizeof(*work_item));
+ if (!work_item) {
+ PERROR("Failed to allocate action executor work item on behalf of trigger \"%s\"",
+ get_trigger_name(trigger));
+ executor_status = ACTION_EXECUTOR_STATUS_ERROR;
+ goto error_unlock;
+ }
+
+ lttng_trigger_get(trigger);
+ if (client_list) {
+ const bool reference_acquired =
+ notification_client_list_get(client_list);
+
+ assert(reference_acquired);
+ }
+
+ *work_item = (typeof(*work_item)){
+ .id = work_item_id,
+ .trigger = trigger,
+ .client_list = client_list,
+ .list_node = CDS_LIST_HEAD_INIT(work_item->list_node),
+ .trigger_notification = trigger_notification,
+ };
+ cds_list_add_tail(&work_item->list_node, &executor->work.list);
+ executor->work.pending_count++;
+ DBG("Enqueued action for trigger \"%s\" as work item %" PRIu64,
+ get_trigger_name(trigger), work_item_id);
+ signal = true;
+
+ /*
+ * Note:
+ * Ownership of the lttng_trigger_notification object passed to the work
+ * item object incidentally to the executor list.
+ * Caller is responsible for freeing in case of error.
+ */
+
+error_unlock:
+ pthread_mutex_unlock(&executor->work.lock);
+ if (signal) {
+ pthread_cond_signal(&executor->work.cond);
+ }
+ return executor_status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef ACTION_EXECUTOR_H
+#define ACTION_EXECUTOR_H
+
+struct action_executor;
+struct notification_thread_handle;
+struct lttng_trigger;
+struct notification_client_list;
+struct lttng_trigger_notification;
+
+enum action_executor_status {
+ ACTION_EXECUTOR_STATUS_OK,
+ ACTION_EXECUTOR_STATUS_OVERFLOW,
+ ACTION_EXECUTOR_STATUS_ERROR,
+ ACTION_EXECUTOR_STATUS_INVALID,
+};
+
+struct action_executor *action_executor_create(
+ struct notification_thread_handle *handle);
+
+void action_executor_destroy(struct action_executor *executor);
+
+enum action_executor_status action_executor_enqueue(
+ struct action_executor *executor,
+ struct lttng_trigger *trigger,
+ struct notification_client_list *list,
+ struct lttng_trigger_notification *priv_data);
+
+#endif /* ACTION_EXECUTOR_H */
{
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);
session_unlock(session);
session_put(session);
}
+
+ /* Do we need more locking here? maybe against trigger add? */
+ rcu_read_lock();
+ 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();
}
/*
#include <urcu/uatomic.h>
#include <urcu/rculist.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule.h>
+
#include <common/common.h>
#include <common/sessiond-comm/agent.h>
}
/*
- * Match function for the events hash table lookup by name and loglevel.
+ * Match function for the events hash table lookup by name, loglevel and
+ * filter_expression.
*/
static int ht_match_event(struct cds_lfht_node *node,
const void *_key)
}
event->enabled = 1;
+ event->user_refcount++;
ret = LTTNG_OK;
error:
goto end;
}
+ if (event->user_refcount - 1 != 0) {
+ /*
+ * Disable the agent event only when all users (trigger etc.)
+ * have disabled it.
+ */
+
+ event->user_refcount--;
+ ret = LTTNG_OK;
+ goto end;
+ }
+
rcu_read_lock();
cds_lfht_for_each_entry(agent_apps_ht_by_sock->ht, &iter.iter, app,
}
}
+ event->user_refcount = 0;
event->enabled = 0;
error:
*/
struct agent_event *agent_create_event(const char *name,
enum lttng_loglevel_type loglevel_type, int loglevel_value,
- struct lttng_filter_bytecode *filter, char *filter_expression)
+ struct lttng_bytecode *filter, char *filter_expression)
{
struct agent_event *event = NULL;
ht_match_event_by_name, &key, &iter->iter);
}
+/*
+ * Find the agent event matching the trigger.
+ *
+ * RCU read side lock MUST be acquired. It must be kept 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 d_type;
+ const struct lttng_condition *condition;
+ const struct lttng_event_rule *rule;
+ const char *name;
+ const char *filter_expression;
+ /* TODO validate if this is the unset value or no */
+ 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);
+
+ d_type = lttng_event_rule_get_domain_type(rule);
+ assert(d_type == LTTNG_DOMAIN_JUL || d_type == LTTNG_DOMAIN_LOG4J ||
+ d_type == LTTNG_DOMAIN_PYTHON);
+
+ /* Get the name (aka pattern) */
+ 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_loglevel_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_loglevel(
+ 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.
* 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;
return ret;
}
+/*
+ * Allocate agent_apps_ht_by_sock.
+ */
+int trigger_agent_ht_alloc(void)
+{
+ int ret = 0;
+
+ trigger_agents_ht_by_domain = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+ if (!trigger_agents_ht_by_domain) {
+ ret = -1;
+ }
+
+ return ret;
+}
+
/*
* Destroy a agent application by socket.
*/
lttng_ht_destroy(agent_apps_ht_by_sock);
}
+/*
+ * Clean-up the trigger agent hash table and destroy it.
+ */
+void trigger_agent_ht_clean(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;
+
+ (void) lttng_ht_del(trigger_agents_ht_by_domain, &iter);
+
+ agent = caa_container_of(node, struct agent, node);
+ agent_destroy(agent);
+ }
+ rcu_read_unlock();
+
+ lttng_ht_destroy(trigger_agents_ht_by_domain);
+}
+
/*
* Update a agent application (given socket) using the given agent.
*
rcu_read_unlock();
}
+
+struct agent *trigger_find_agent(enum lttng_domain_type domain_type)
+{
+ struct agent *agt = NULL;
+ struct lttng_ht_node_u64 *node;
+ struct lttng_ht_iter iter;
+ uint64_t key;
+
+ assert(trigger_agents_ht_by_domain);
+
+ DBG3("Trigger agent lookup for domain %d", domain_type);
+
+ key = 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;
+}
*/
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;
};
/*
struct lttng_ht_node_str node;
/* Filter associated with the event. NULL if none. */
- struct lttng_filter_bytecode *filter;
+ struct lttng_bytecode *filter;
char *filter_expression;
struct lttng_event_exclusion *exclusion;
+
+ /*
+ * Multiple triggers and events can use this agent event.
+ * The event can only be disabled when the count is zero.
+ */
+ unsigned int user_refcount;
};
/*
/* Agent event API. */
struct agent_event *agent_create_event(const char *name,
enum lttng_loglevel_type loglevel_type, int loglevel_value,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
char *filter_expression);
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,
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);
+
+/* todo: find a better place for this */
+struct agent *trigger_find_agent(enum lttng_domain_type domain_type);
+void trigger_agent_ht_clean(void);
+int trigger_agent_ht_alloc(void);
+
#endif /* LTTNG_SESSIOND_AGENT_H */
#include "utils.h"
#include "manage-consumer.h"
#include "clear.h"
+#include "agent-thread.h"
static bool is_root;
{
int fd, ret;
struct lttng_userspace_probe_location *probe_location;
- const struct lttng_userspace_probe_location_lookup_method *lookup = NULL;
struct lttng_dynamic_buffer probe_location_buffer;
struct lttng_buffer_view buffer_view;
* Set the file descriptor received from the client through the unix
* socket in the probe location.
*/
- lookup = lttng_userspace_probe_location_get_lookup_method(probe_location);
- if (!lookup) {
- ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
- goto error;
- }
-
- /*
- * From the kernel tracer's perspective, all userspace probe event types
- * are all the same: a file and an offset.
- */
- switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) {
- case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
- ret = lttng_userspace_probe_location_function_set_binary_fd(
- probe_location, fd);
- break;
- case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
- ret = lttng_userspace_probe_location_tracepoint_set_binary_fd(
- probe_location, fd);
- break;
- default:
- ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
- goto error;
- }
-
+ ret = lttng_userspace_probe_location_set_binary_fd(probe_location, fd);
if (ret) {
ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
goto error;
int ret = LTTNG_OK;
int need_tracing_session = 1;
int need_domain;
+ int need_consumerd = 1;
DBG("Processing client command %d", cmd_ctx->lsm->cmd_type);
case LTTNG_SET_SESSION_SHM_PATH:
case LTTNG_REGENERATE_METADATA:
case LTTNG_REGENERATE_STATEDUMP:
- case LTTNG_REGISTER_TRIGGER:
- case LTTNG_UNREGISTER_TRIGGER:
case LTTNG_ROTATE_SESSION:
case LTTNG_ROTATION_GET_INFO:
case LTTNG_ROTATION_SET_SCHEDULE:
case LTTNG_SESSION_LIST_ROTATION_SCHEDULES:
case LTTNG_CLEAR_SESSION:
+ case LTTNG_LIST_TRIGGERS:
need_domain = 0;
break;
default:
need_domain = 1;
}
+ /* Needs a functioning consumerd */
+ switch (cmd_ctx->lsm->cmd_type) {
+ case LTTNG_REGISTER_TRIGGER:
+ case LTTNG_UNREGISTER_TRIGGER:
+ need_consumerd = 0;
+ break;
+ default:
+ need_consumerd = 1;
+ break;
+ }
+
if (config.no_kernel && need_domain
&& cmd_ctx->lsm->domain.type == LTTNG_DOMAIN_KERNEL) {
if (!is_root) {
case LTTNG_DATA_PENDING:
case LTTNG_ROTATE_SESSION:
case LTTNG_ROTATION_GET_INFO:
+ case LTTNG_REGISTER_TRIGGER:
+ case LTTNG_LIST_TRIGGERS:
break;
default:
/* Setup lttng message with no payload */
case LTTNG_SAVE_SESSION:
case LTTNG_REGISTER_TRIGGER:
case LTTNG_UNREGISTER_TRIGGER:
+ case LTTNG_LIST_TRIGGERS:
need_tracing_session = 0;
break;
default:
}
/* Consumer is in an ERROR state. Report back to client */
- if (uatomic_read(&kernel_consumerd_state) == CONSUMER_ERROR) {
+ if (need_consumerd && uatomic_read(&kernel_consumerd_state) ==
+ CONSUMER_ERROR) {
ret = LTTNG_ERR_NO_KERNCONSUMERD;
goto error;
}
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()) {
ret = LTTNG_ERR_NO_UST;
goto error;
}
+
/* Consumer is in an ERROR state. Report back to client */
- if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) {
+ if (need_consumerd && uatomic_read(&ust_consumerd_state) ==
+ CONSUMER_ERROR) {
ret = LTTNG_ERR_NO_USTCONSUMERD;
goto error;
}
{
struct lttng_event *ev = NULL;
struct lttng_event_exclusion *exclusion = NULL;
- struct lttng_filter_bytecode *bytecode = NULL;
+ struct lttng_bytecode *bytecode = NULL;
char *filter_expression = NULL;
/* Handle exclusion events and receive it from the client. */
}
case LTTNG_REGISTER_TRIGGER:
{
+ struct lttng_dynamic_buffer payload;
+ struct lttng_trigger *return_trigger;
+
+ lttng_dynamic_buffer_init(&payload);
ret = cmd_register_trigger(cmd_ctx, *sock,
- notification_thread_handle);
+ notification_thread_handle, &return_trigger);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ ret = lttng_trigger_serialize(return_trigger, &payload, NULL);
+ if (ret) {
+ ERR("Failed to serialize trigger in reply to \"register trigger\" command");
+ ret = LTTNG_ERR_NOMEM;
+ lttng_trigger_destroy(return_trigger);
+ goto error;
+ }
+ ret = setup_lttng_msg_no_cmd_header(cmd_ctx, payload.data,
+ payload.size);
+ if (ret) {
+ lttng_trigger_destroy(return_trigger);
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ lttng_trigger_destroy(return_trigger);
+ lttng_dynamic_buffer_reset(&payload);
+ ret = LTTNG_OK;
break;
}
case LTTNG_UNREGISTER_TRIGGER:
ret = cmd_clear_session(cmd_ctx->session, sock);
break;
}
+ case LTTNG_LIST_TRIGGERS:
+ {
+ struct lttng_dynamic_buffer payload;
+ struct lttng_triggers *return_triggers;
+
+ lttng_dynamic_buffer_init(&payload);
+ ret = cmd_list_triggers(cmd_ctx, *sock,
+ notification_thread_handle, &return_triggers);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ ret = lttng_triggers_serialize(return_triggers, &payload);
+ if (ret) {
+ ERR("Failed to serialize triggers in reply to \"list triggers\" command");
+ ret = LTTNG_ERR_NOMEM;
+ lttng_triggers_destroy(return_triggers);
+ goto error;
+ }
+ ret = setup_lttng_msg_no_cmd_header(cmd_ctx, payload.data,
+ payload.size);
+ if (ret) {
+ ret = LTTNG_ERR_NOMEM;
+ lttng_triggers_destroy(return_triggers);
+ goto error;
+ }
+ lttng_dynamic_buffer_reset(&payload);
+ lttng_triggers_destroy(return_triggers);
+ ret = LTTNG_OK;
+ break;
+ }
default:
ret = LTTNG_ERR_UND;
break;
#include <lttng/location-internal.h>
#include <lttng/trigger/trigger-internal.h>
#include <lttng/condition/condition.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/uprobe-internal.h>
+#include <lttng/event-rule/tracepoint.h>
#include <lttng/action/action.h>
#include <lttng/channel.h>
#include <lttng/channel-internal.h>
const struct lttng_domain *domain,
char *channel_name, struct lttng_event *event,
char *filter_expression,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
struct lttng_event_exclusion *exclusion,
int wpipe);
const struct lttng_domain *domain,
char *channel_name, struct lttng_event *event,
char *filter_expression,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
struct lttng_event_exclusion *exclusion,
int wpipe, bool internal_event)
{
case LTTNG_EVENT_ALL:
{
char *filter_expression_a = NULL;
- struct lttng_filter_bytecode *filter_a = NULL;
+ struct lttng_bytecode *filter_a = NULL;
/*
* We need to duplicate filter_expression and filter,
{
char *filter_expression_copy = NULL;
- struct lttng_filter_bytecode *filter_copy = NULL;
+ struct lttng_bytecode *filter_copy = NULL;
if (filter) {
const size_t filter_size = sizeof(
- struct lttng_filter_bytecode)
+ struct lttng_bytecode)
+ filter->len;
filter_copy = zmalloc(filter_size);
const struct lttng_domain *domain,
char *channel_name, struct lttng_event *event,
char *filter_expression,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
struct lttng_event_exclusion *exclusion,
int wpipe)
{
const struct lttng_domain *domain,
char *channel_name, struct lttng_event *event,
char *filter_expression,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
struct lttng_event_exclusion *exclusion,
int wpipe)
{
return ret;
}
+/* TODO: is this the best place to perform this? (code wise) */
+/*
+ * Set sock to -1 if reception of more information is not necessary e.g on
+ * unregister. TODO find a better way.
+ *
+ * On success LTTNG_OK. On error, returns lttng_error code.
+ * */
+static enum lttng_error_code prepare_trigger_object(struct lttng_trigger *trigger, int sock)
+{
+ enum lttng_error_code ret;
+ /* Internal object of the trigger might have to "generate" and
+ * "populate" internal field e.g filter bytecode
+ */
+ struct lttng_condition *condition = NULL;
+
+ condition = lttng_trigger_get_condition(trigger);
+ if (!condition) {
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+
+ switch (lttng_condition_get_type(condition)) {
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ {
+ struct lttng_event_rule *event_rule;
+ const struct lttng_credentials *credential = lttng_trigger_get_credentials(trigger);
+ lttng_condition_event_rule_get_rule_no_const(
+ condition, &event_rule);
+ ret = lttng_event_rule_populate(event_rule, credential->uid, credential->gid);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+
+ switch (lttng_event_rule_get_type(event_rule)) {
+ case LTTNG_EVENT_RULE_TYPE_UPROBE:
+ {
+ int fd;
+ struct lttng_userspace_probe_location *location = lttng_event_rule_uprobe_get_location_no_const(event_rule);
+
+ if (sock < 0) {
+ /* Nothing to receive */
+ break;
+ }
+ /*
+ * Receive the file descriptor to the target binary from
+ * the client.
+ */
+ DBG("Receiving userspace probe target FD from client ...");
+ ret = lttcomm_recv_fds_unix_sock(sock, &fd, 1);
+ if (ret <= 0) {
+ DBG("Nothing recv() from client userspace probe fd... continuing");
+ ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+ goto end;
+ }
+
+ /*
+ * Set the file descriptor received from the client
+ * through the unix socket in the probe location.
+ */
+ ret = lttng_userspace_probe_location_set_binary_fd(
+ location, fd);
+ if (ret) {
+ ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+ goto end;
+ }
+
+ break;
+ }
+ default:
+ /* Nothing to do */
+ break;
+ }
+
+ /* Generate the capture bytecode set */
+ ret = lttng_condition_event_rule_generate_capture_descriptor_bytecode_set(
+ condition, &trigger->capture_bytecode_set);
+ break;
+ }
+ default:
+ {
+ ret = LTTNG_OK;
+ break;
+ }
+ }
+
+
+end:
+ return ret;
+}
+
+/* Caller must call lttng_destroy_trigger on the returned trigger object */
int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock,
- struct notification_thread_handle *notification_thread)
+ struct notification_thread_handle *notification_thread,
+ struct lttng_trigger **return_trigger)
{
int ret;
size_t trigger_len;
goto end;
}
+ /*
+ * Since we return the trigger object, take a reference to it
+ * Caller is responsible for calling lttng_destroy_trigger on it.
+ * This thread does not OWN the trigger.
+ */
+ lttng_trigger_get(trigger);
+
+ /* Set the trigger credential */
+ lttng_trigger_set_credentials(trigger, cmd_ctx->creds.uid, cmd_ctx->creds.gid);
+
+ /* Prepare internal trigger object if needed on reception.
+ * Handles also special treatment for certain internal object of the
+ * trigger (e.g uprobe event rule binary fd.
+ */
+ ret = prepare_trigger_object(trigger, sock);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+
+ /* Inform the notification thread */
ret = notification_thread_command_register_trigger(notification_thread,
trigger);
- /* Ownership of trigger was transferred. */
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+
+ /* Synchronize tracers, only if needed */
+ /* TODO: maybe extract somewhere else */
+ {
+ struct lttng_condition *condition = NULL;
+ condition = lttng_trigger_get_condition(trigger);
+ if (!condition) {
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+
+ if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+ const struct lttng_event_rule *rule = NULL;
+ (void) lttng_condition_event_rule_get_rule(condition, &rule);
+ if (!rule) {
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+ if (lttng_event_rule_get_domain_type(rule) == LTTNG_DOMAIN_KERNEL) {
+ /* TODO: get the token value from the
+ * notification thread and only perform an
+ * enable and a disable.... This is NOT
+ * OPTIMIZED AT ALL
+ */
+ kernel_update_tokens();
+ } else {
+ /* TODO: get the token value from the
+ * notification thread and only perform an
+ * enable and a disable.... This is NOT
+ * OPTIMIZED AT ALL
+ */
+ ust_app_global_update_all_tokens();
+ /* Agent handling */
+ if (lttng_event_rule_is_agent(rule)) {
+ struct agent *agt;
+ const char *pattern;
+ enum lttng_domain_type domain_type;
+ domain_type = lttng_event_rule_get_domain_type(
+ rule);
+ (void) lttng_event_rule_tracepoint_get_pattern(
+ rule, &pattern);
+ agt = trigger_find_agent(domain_type);
+ if (!agt) {
+ agt = agent_create(domain_type);
+ if (!agt) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ agent_add(agt, trigger_agents_ht_by_domain);
+ }
+
+ ret = trigger_agent_enable(
+ trigger, agt);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+ }
+ }
+ }
+ }
+
+ /* Return an image of the updated object to the client */
+ *return_trigger = trigger;
+ /* Ownership of trigger was transferred to caller. */
trigger = NULL;
end:
lttng_trigger_destroy(trigger);
goto end;
}
+ lttng_trigger_set_credentials(trigger, cmd_ctx->creds.uid, cmd_ctx->creds.gid);
+
+ ret = prepare_trigger_object(trigger, -1);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+
ret = notification_thread_command_unregister_trigger(notification_thread,
trigger);
+
+ /* Synchronize tracers, only if needed */
+ /* TODO: maybe extract somewhere else */
+ {
+ struct lttng_condition *condition = NULL;
+ condition = lttng_trigger_get_condition(trigger);
+ if (!condition) {
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+
+ if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+ const struct lttng_event_rule *rule = NULL;
+ (void) lttng_condition_event_rule_get_rule(condition, &rule);
+ if (!rule) {
+ ret = LTTNG_ERR_INVALID_TRIGGER;
+ goto end;
+ }
+ if (lttng_event_rule_get_domain_type(rule) == LTTNG_DOMAIN_KERNEL) {
+ /* TODO: get the token value from the
+ * notification thread and only perform an
+ * enable and a disable.... This is NOT
+ * OPTIMIZED AT ALL
+ */
+ kernel_update_tokens();
+ } else {
+ /* TODO: get the token value from the
+ * notification thread and only perform an
+ * enable and a disable.... This is NOT
+ * OPTIMIZED AT ALL
+ */
+ ust_app_global_update_all_tokens();
+ if (lttng_event_rule_is_agent(rule)) {
+ struct agent *agt;
+ const char *pattern;
+ enum lttng_domain_type domain_type;
+
+ domain_type = lttng_event_rule_get_domain_type(
+ rule);
+ (void) lttng_event_rule_tracepoint_get_pattern(
+ rule, &pattern);
+
+ agt = trigger_find_agent(domain_type);
+ if (!agt) {
+ ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
+ goto end;
+ }
+ ret = trigger_agent_disable(
+ trigger, agt);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+ }
+ }
+ }
+ }
+
end:
lttng_trigger_destroy(trigger);
lttng_dynamic_buffer_reset(&trigger_buffer);
return ret;
}
+int cmd_list_triggers(struct command_ctx *cmd_ctx, int sock,
+ struct notification_thread_handle *notification_thread,
+ struct lttng_triggers **return_triggers)
+{
+ int ret = 0;
+ enum lttng_error_code ret_code;
+ struct lttng_triggers *triggers = NULL;
+
+ /* Get list of token trigger from the notification thread here */
+ ret_code = notification_thread_command_list_triggers(notification_thread_handle, cmd_ctx->creds.uid, cmd_ctx->creds.gid, &triggers);
+ if (ret_code != LTTNG_OK) {
+ ret = ret_code;
+ goto end;
+ }
+
+ /* Return a "view" of the current triggers */
+ *return_triggers = triggers;
+ triggers = NULL;
+ ret = LTTNG_OK;
+end:
+ lttng_triggers_destroy(triggers);
+ return ret;
+}
/*
* Send relayd sockets from snapshot output to consumer. Ignore request if the
* snapshot output is *not* set with a remote destination.
char *channel_name, const struct lttng_event_context *ctx, int kwpipe);
int cmd_set_filter(struct ltt_session *session, enum lttng_domain_type domain,
char *channel_name, struct lttng_event *event,
- struct lttng_filter_bytecode *bytecode);
+ struct lttng_bytecode *bytecode);
int cmd_enable_event(struct ltt_session *session, const struct lttng_domain *domain,
char *channel_name, struct lttng_event *event,
char *filter_expression,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
struct lttng_event_exclusion *exclusion,
int wpipe);
int cmd_regenerate_statedump(struct ltt_session *session);
int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock,
- struct notification_thread_handle *notification_thread_handle);
+ struct notification_thread_handle *notification_thread_handle,
+ struct lttng_trigger **return_trigger);
int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock,
struct notification_thread_handle *notification_thread_handle);
+int cmd_list_triggers(struct command_ctx *cmd_ctx, int sock,
+ struct notification_thread_handle *notification_thread_handle,
+ struct lttng_triggers **return_triggers);
+
int cmd_rotate_session(struct ltt_session *session,
struct lttng_rotate_session_return *rotate_return,
bool quiet_rotation,
{
struct ltt_session *sess, *stmp;
const struct ltt_session_list *session_list = session_get_list();
+ struct ust_app *app;
/* Consumer is in an ERROR state. Stop any application update. */
if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) {
return;
}
+ rcu_read_lock();
+ assert(app_sock >= 0);
+ app = ust_app_find_by_sock(app_sock);
+ if (app == NULL) {
+ /*
+ * Application can be unregistered before so
+ * this is possible hence simply stopping the
+ * update.
+ */
+ DBG3("UST app update failed to find app sock %d",
+ app_sock);
+ goto unlock_rcu;
+ }
+
+ /* Update all tokens for the app */
+ ust_app_global_update_tokens(app);
+
/* For all tracing session(s) */
cds_list_for_each_entry_safe(sess, stmp, &session_list->head, list) {
- struct ust_app *app;
-
if (!session_get(sess)) {
continue;
}
goto unlock_session;
}
- rcu_read_lock();
- assert(app_sock >= 0);
- app = ust_app_find_by_sock(app_sock);
- if (app == NULL) {
- /*
- * Application can be unregistered before so
- * this is possible hence simply stopping the
- * update.
- */
- DBG3("UST app update failed to find app sock %d",
- app_sock);
- goto unlock_rcu;
- }
ust_app_global_update(sess->ust_session, app);
- unlock_rcu:
- rcu_read_unlock();
unlock_session:
session_unlock(sess);
session_put(sess);
}
+
+unlock_rcu:
+ rcu_read_unlock();
+
}
/*
/* Set app version. This call will print an error if needed. */
(void) ust_app_version(app);
+ (void) ust_app_setup_trigger_group(app);
+
/* Send notify socket through the notify pipe. */
ret = send_socket_to_thread(
notifiers->apps_cmd_notify_pipe_write_fd,
#include <string.h>
#include <lttng/lttng.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <common/bytecode/bytecode.h>
#include <common/error.h>
#include <common/sessiond-comm/sessiond-comm.h>
#include <common/filter.h>
#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.
assert(event);
key.name = event->attr.name;
- key.filter = (struct lttng_filter_bytecode *) event->filter;
+ key.filter = (struct lttng_bytecode *) event->filter;
key.loglevel_type = event->attr.loglevel_type;
key.loglevel_value = event->attr.loglevel;
key.exclusion = event->exclusion;
*/
int event_kernel_enable_event(struct ltt_kernel_channel *kchan,
struct lttng_event *event, char *filter_expression,
- struct lttng_filter_bytecode *filter)
+ struct lttng_bytecode *filter)
{
int ret;
struct ltt_kernel_event *kevent;
int event_ust_enable_tracepoint(struct ltt_ust_session *usess,
struct ltt_ust_channel *uchan, struct lttng_event *event,
char *filter_expression,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
struct lttng_event_exclusion *exclusion,
bool internal_event)
{
return ret;
}
+static void agent_enable_all(struct agent *agt)
+{
+ struct agent_event *aevent;
+ struct lttng_ht_iter iter;
+
+ /* 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();
+}
+
/*
* Enable all agent event for a given UST session.
*
*/
int event_agent_enable_all(struct ltt_ust_session *usess,
struct agent *agt, struct lttng_event *event,
- struct lttng_filter_bytecode *filter ,char *filter_expression)
+ struct lttng_bytecode *filter ,char *filter_expression)
{
int ret;
- struct agent_event *aevent;
- struct lttng_ht_iter iter;
assert(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;
* contexts yet. Not an issue for now, since they are not generated by
* the lttng-ctl library.
*/
-static int add_filter_app_ctx(struct lttng_filter_bytecode *bytecode,
+static int add_filter_app_ctx(struct lttng_bytecode *bytecode,
const char *filter_expression, struct agent *agt)
{
int ret = LTTNG_OK;
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,
+static int agent_enable(struct agent *agt,
+ struct lttng_event *event,
+ struct lttng_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) {
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_bytecode *filter,
+ char *filter_expression)
+{
+ 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");
+
+ 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_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_bytecode *filter_bytecode;
+ struct lttng_bytecode *filter_bytecode_copy = NULL;
+ struct lttng_event *event = NULL;
+
+ assert(trigger);
+ assert(agt);
+
+ 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 = bytecode_copy (filter_bytecode);
+ if (!filter_bytecode_copy) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ }
+ }
+
+ DBG("Event agent enabling %s for trigger %" PRIu64
+ " with loglevel type %d "
+ ", loglevel %d and filter \"%s\"",
+ event->name, lttng_trigger_get_key(trigger),
+ event->loglevel_type, event->loglevel,
+ filter_expression ? filter_expression : "NULL");
+
+ 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.
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_key(trigger));
+
+ /* Already disabled? */
+ if (!aevent->enabled) {
+ 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.
*
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_key(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_key(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.
*
int event_kernel_enable_event(struct ltt_kernel_channel *kchan,
struct lttng_event *event, char *filter_expression,
- struct lttng_filter_bytecode *filter);
+ struct lttng_bytecode *filter);
int event_ust_enable_tracepoint(struct ltt_ust_session *usess,
struct ltt_ust_channel *uchan, struct lttng_event *event,
char *filter_expression,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
struct lttng_event_exclusion *exclusion,
bool internal_event);
int event_ust_disable_tracepoint(struct ltt_ust_session *usess,
struct ltt_ust_channel *uchan);
int event_agent_enable(struct ltt_ust_session *usess, struct agent *agt,
- struct lttng_event *event, struct lttng_filter_bytecode *filter,
+ struct lttng_event *event, struct lttng_bytecode *filter,
char *filter_expression);
int event_agent_enable_all(struct ltt_ust_session *usess, struct agent *agt,
- struct lttng_event *event, struct lttng_filter_bytecode *filter,
+ struct lttng_event *event, struct lttng_bytecode *filter,
char *filter_expression);
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 */
struct health_app *health_sessiond;
struct notification_thread_handle *notification_thread_handle;
+pthread_mutex_t notification_trigger_tokens_ht_lock = PTHREAD_MUTEX_INITIALIZER;
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;
HEALTH_SESSIOND_TYPE_NOTIFICATION = 8,
HEALTH_SESSIOND_TYPE_ROTATION = 9,
HEALTH_SESSIOND_TYPE_TIMER = 10,
+ HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR = 11,
NR_HEALTH_SESSIOND_TYPES,
};
#include <sys/types.h>
#include <common/common.h>
+#include <common/hashtable/utils.h>
#include <common/trace-chunk.h>
#include <common/kernel-ctl/kernel-ctl.h>
#include <common/kernel-ctl/kernel-ioctl.h>
#include <common/sessiond-comm/sessiond-comm.h>
+#include <lttng/userspace-probe.h>
+#include <lttng/userspace-probe-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/uprobe-internal.h>
+
#include "lttng-sessiond.h"
#include "lttng-syscall.h"
#include "consumer.h"
#include "utils.h"
#include "rotate.h"
#include "modprobe.h"
+#include "notification-thread-commands.h"
/*
* Key used to reference a channel between the sessiond and the consumer. This
static const char *module_proc_lttng = "/proc/lttng";
static int kernel_tracer_fd = -1;
+static int kernel_tracer_trigger_group_fd = -1;
+static int kernel_tracer_trigger_group_notification_fd = -1;
+static struct ltt_kernel_token_event_rule_list kernel_tracer_token_list;
-#include <lttng/userspace-probe.h>
-#include <lttng/userspace-probe-internal.h>
/*
* Add context on a kernel channel.
*
return -1;
}
+/*
+ * Create a kernel channel, register it to the kernel tracer and add it to the
+ * kernel session.
+ */
+static
+int kernel_create_trigger_group(int *trigger_group_fd)
+{
+ int ret;
+ int local_fd = -1;
+
+ assert(trigger_group_fd);
+
+ /* Kernel tracer channel creation */
+ ret = kernctl_create_trigger_group(kernel_tracer_fd);
+ if (ret < 0) {
+ PERROR("ioctl kernel create trigger group");
+ ret = -1;
+ goto error;
+ }
+
+ /* Store locally */
+ local_fd = ret;
+
+ /* Prevent fd duplication after execlp() */
+ ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC);
+ if (ret < 0) {
+ PERROR("fcntl session fd");
+ }
+
+ DBG("Kernel trigger group created (fd: %d)",
+ local_fd);
+ ret = 0;
+
+error:
+ *trigger_group_fd = local_fd;
+ return ret;
+}
+
/*
* Compute the offset of the instrumentation byte in the binary based on the
* function probe location using the ELF lookup method.
static
int extract_userspace_probe_offset_function_elf(
const struct lttng_userspace_probe_location *probe_location,
- struct ltt_kernel_session *session, uint64_t *offset)
+ uid_t uid, gid_t gid, uint64_t *offset)
{
int fd;
int ret = 0;
goto end;
}
- ret = run_as_extract_elf_symbol_offset(fd, symbol, session->uid,
- session->gid, offset);
+ ret = run_as_extract_elf_symbol_offset(fd, symbol, uid, gid, offset);
if (ret < 0) {
DBG("userspace probe offset calculation failed for "
"function %s", symbol);
static
int extract_userspace_probe_offset_tracepoint_sdt(
const struct lttng_userspace_probe_location *probe_location,
- struct ltt_kernel_session *session, uint64_t **offsets,
+ uid_t uid, gid_t gid, uint64_t **offsets,
uint32_t *offsets_count)
{
enum lttng_userspace_probe_location_lookup_method_type lookup_method_type;
}
ret = run_as_extract_sdt_probe_offsets(fd, provider_name, probe_name,
- session->uid, session->gid, offsets, offsets_count);
+ uid, gid, offsets, offsets_count);
if (ret < 0) {
DBG("userspace probe offset calculation failed for sdt "
"probe %s:%s", provider_name, probe_name);
return ret;
}
-/*
- * Extract the offsets of the instrumentation point for the different lookup
- * methods.
- */
static
-int userspace_probe_add_callsites(struct lttng_event *ev,
- struct ltt_kernel_session *session, int fd)
+int userspace_probe_add_callsite(
+ const struct lttng_userspace_probe_location *location,
+ uid_t uid, gid_t gid, int fd)
{
const struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
enum lttng_userspace_probe_location_lookup_method_type type;
- const struct lttng_userspace_probe_location *location = NULL;
int ret;
- assert(ev);
- assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE);
-
- location = lttng_event_get_userspace_probe_location(ev);
- if (!location) {
- ret = -1;
- goto end;
- }
- lookup_method =
- lttng_userspace_probe_location_get_lookup_method(location);
+ lookup_method = lttng_userspace_probe_location_get_lookup_method(location);
if (!lookup_method) {
ret = -1;
goto end;
struct lttng_kernel_event_callsite callsite;
uint64_t offset;
- ret = extract_userspace_probe_offset_function_elf(location, session, &offset);
+ ret = extract_userspace_probe_offset_function_elf(location,
+ uid, gid, &offset);
if (ret) {
ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
goto end;
callsite.u.uprobe.offset = offset;
ret = kernctl_add_callsite(fd, &callsite);
if (ret) {
- WARN("Adding callsite to userspace probe "
- "event %s failed.", ev->name);
+ WARN("Adding callsite to ELF userspace probe failed.");
ret = LTTNG_ERR_KERN_ENABLE_FAIL;
goto end;
}
* This call allocates the offsets buffer. This buffer must be freed
* by the caller
*/
- ret = extract_userspace_probe_offset_tracepoint_sdt(location, session,
- &offsets, &offsets_count);
+ ret = extract_userspace_probe_offset_tracepoint_sdt(location,
+ uid, gid, &offsets, &offsets_count);
if (ret) {
ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
goto end;
callsite.u.uprobe.offset = offsets[i];
ret = kernctl_add_callsite(fd, &callsite);
if (ret) {
- WARN("Adding callsite to userspace probe "
- "event %s failed.", ev->name);
+ WARN("Adding callsite to SDT userspace probe "
+ "failed.");
ret = LTTNG_ERR_KERN_ENABLE_FAIL;
free(offsets);
goto end;
return ret;
}
+/*
+ * Extract the offsets of the instrumentation point for the different lookup
+ * methods.
+ */
+static
+int userspace_probe_event_add_callsites(struct lttng_event *ev,
+ struct ltt_kernel_session *session, int fd)
+{
+ const struct lttng_userspace_probe_location *location = NULL;
+ int ret;
+
+ assert(ev);
+ assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE);
+
+ location = lttng_event_get_userspace_probe_location(ev);
+ if (!location) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = userspace_probe_add_callsite(location, session->uid, session->gid,
+ fd);
+ if (ret) {
+ WARN("Adding callsite to userspace probe event \"%s\" "
+ "failed.", ev->name);
+ }
+
+end:
+ return ret;
+}
+
+/*
+ * Extract the offsets of the instrumentation point for the different lookup
+ * methods.
+ */
+static int userspace_probe_event_rule_add_callsites(
+ const struct lttng_event_rule *rule,
+ const struct lttng_credentials *creds,
+ int fd)
+{
+ const struct lttng_userspace_probe_location *location = NULL;
+ enum lttng_event_rule_status status;
+ int ret;
+
+ assert(rule);
+ assert(creds);
+ assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE);
+
+ status = lttng_event_rule_uprobe_get_location(rule, &location);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = userspace_probe_add_callsite(location, creds->uid, creds->gid,
+ fd);
+ if (ret) {
+ WARN("Adding callsite to userspace probe object %d"
+ "failed.", fd);
+ }
+
+end:
+ return ret;
+}
+
/*
* Create a kernel event, enable it to the kernel tracer and add it to the
* channel event list of the kernel session.
int kernel_create_event(struct lttng_event *ev,
struct ltt_kernel_channel *channel,
char *filter_expression,
- struct lttng_filter_bytecode *filter)
+ struct lttng_bytecode *filter)
{
int err, fd;
enum lttng_error_code ret;
}
if (ev->type == LTTNG_EVENT_USERSPACE_PROBE) {
- ret = userspace_probe_add_callsites(ev, channel->session, event->fd);
+ ret = userspace_probe_event_add_callsites(ev, channel->session,
+ event->fd);
if (ret) {
goto add_callsite_error;
}
return ret;
}
+/*
+ * Disable a kernel trigger.
+ */
+static
+int kernel_disable_token_event_rule(struct ltt_kernel_token_event_rule *event)
+{
+ int ret;
+
+ assert(event);
+
+ ret = kernctl_disable(event->fd);
+ if (ret < 0) {
+ switch (-ret) {
+ case EEXIST:
+ ret = LTTNG_ERR_KERN_EVENT_EXIST;
+ break;
+ default:
+ PERROR("disable kernel event");
+ break;
+ }
+ goto error;
+ }
+
+ event->enabled = 0;
+ DBG("Kernel trigger token %" PRIu64" disabled (fd: %d)", event->token, event->fd);
+
+ return 0;
+
+error:
+ return ret;
+}
+
static
struct process_attr_tracker *_kernel_get_process_attr_tracker(
struct ltt_kernel_session *session,
if (ret < 0) {
goto error_modules;
}
-
if (ret < 1) {
WARN("Kernel tracer does not support buffer monitoring. "
"The monitoring timer of channels in the kernel domain "
"will be set to 0 (disabled).");
}
+ ret = kernel_create_trigger_group(&kernel_tracer_trigger_group_fd);
+ if (ret < 0) {
+ /* TODO: error handling if it is not supported etc. */
+ WARN("Failed trigger group creation");
+ kernel_tracer_trigger_group_fd = -1;
+ /* This is not fatal */
+ } else {
+ ret = kernel_create_trigger_group_notification_fd(&kernel_tracer_trigger_group_notification_fd);
+ if (ret < 0) {
+ goto error_modules;
+ }
+ }
+
+ CDS_INIT_LIST_HEAD(&kernel_tracer_token_list.head);
+
DBG("Kernel tracer fd %d", kernel_tracer_fd);
+ DBG("Kernel tracer trigger group fd %d", kernel_tracer_trigger_group_fd);
+ DBG("Kernel tracer trigger group notificationi fd %d", kernel_tracer_trigger_group_notification_fd);
ret = syscall_init_table(kernel_tracer_fd);
if (ret < 0) {
ERR("Unable to populate syscall table. Syscall tracing won't "
"work for this session daemon.");
}
+
return 0;
error_version:
{
int ret;
+ struct ltt_kernel_token_event_rule *rule, *rtmp;
+ cds_list_for_each_entry_safe(rule, rtmp, &kernel_tracer_token_list.head, list) {
+ kernel_disable_token_event_rule(rule);
+ trace_kernel_destroy_token_event_rule(rule);
+ }
+
+ DBG2("Closing kernel trigger group notification fd");
+ if (kernel_tracer_trigger_group_notification_fd >= 0) {
+ ret = close(kernel_tracer_trigger_group_notification_fd);
+ if (ret) {
+ PERROR("close");
+ }
+ kernel_tracer_trigger_group_notification_fd = -1;
+ }
+
+ /* TODO: do we iterate over the list to remove all token? */
+ DBG2("Closing kernel trigger group fd");
+ if (kernel_tracer_trigger_group_fd >= 0) {
+ ret = close(kernel_tracer_trigger_group_fd);
+ if (ret) {
+ PERROR("close");
+ }
+ kernel_tracer_trigger_group_fd = -1;
+ }
+
DBG2("Closing kernel fd");
if (kernel_tracer_fd >= 0) {
ret = close(kernel_tracer_fd);
}
kernel_tracer_fd = -1;
}
+
DBG("Unloading kernel modules");
modprobe_remove_lttng_all();
free(syscall_table);
rcu_read_unlock();
return status;
}
+
+enum lttng_error_code kernel_create_trigger_group_notification_fd(
+ int *trigger_group_notification_fd)
+{
+ enum lttng_error_code ret = LTTNG_OK;
+ int local_fd = -1;
+
+ assert(trigger_group_notification_fd);
+
+ ret = kernctl_create_trigger_group_notification_fd(kernel_tracer_trigger_group_fd);
+ if (ret < 0) {
+ PERROR("ioctl kernel create trigger group");
+ ret = -1;
+ goto error;
+ }
+
+ /* Store locally */
+ local_fd = ret;
+
+ /* Prevent fd duplication after execlp() */
+ ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC);
+ if (ret < 0) {
+ PERROR("fcntl session fd");
+ }
+
+ DBG("Kernel trigger group notification created (fd: %d)",
+ local_fd);
+ ret = 0;
+
+error:
+ *trigger_group_notification_fd = local_fd;
+ return ret;
+}
+
+enum lttng_error_code kernel_destroy_trigger_group_notification_fd(
+ int trigger_group_notification_fd)
+{
+ enum lttng_error_code ret = LTTNG_OK;
+ DBG("Closing trigger group notification fd %d", trigger_group_notification_fd);
+ if (trigger_group_notification_fd >= 0) {
+ ret = close(trigger_group_notification_fd);
+ if (ret) {
+ PERROR("close");
+ }
+ }
+ return ret;
+}
+
+static int kernel_create_token_event_rule(struct lttng_trigger *trigger,
+ const struct lttng_credentials *creds, uint64_t token)
+{
+ int err, fd;
+ enum lttng_error_code ret;
+ struct ltt_kernel_token_event_rule *event;
+ struct lttng_kernel_trigger kernel_trigger;
+ unsigned int capture_bytecode_count = 0;
+ struct lttng_condition *condition = NULL;
+ struct lttng_event_rule *event_rule = NULL;
+
+ assert(trigger);
+
+ condition = lttng_trigger_get_condition(trigger);
+ assert(condition);
+ assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+ lttng_condition_event_rule_get_rule_no_const(condition, &event_rule);
+ assert(event_rule);
+ assert(lttng_event_rule_get_type(event_rule) != LTTNG_EVENT_RULE_TYPE_UNKNOWN);
+
+ /* TODO: Should we check that the event-rule is kernel oriented here? */
+
+ ret = trace_kernel_create_token_event_rule(event_rule, token, &event);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ trace_kernel_init_trigger_from_event_rule(event->event_rule, &kernel_trigger);
+ kernel_trigger.id = event->token;
+
+ fd = kernctl_create_trigger(kernel_tracer_trigger_group_fd, &kernel_trigger);
+ if (fd < 0) {
+ switch (-fd) {
+ case EEXIST:
+ ret = LTTNG_ERR_KERN_EVENT_EXIST;
+ break;
+ case ENOSYS:
+ WARN("Trigger type not implemented");
+ ret = LTTNG_ERR_KERN_EVENT_ENOSYS;
+ break;
+ case ENOENT:
+ WARN("Event %s not found!", kernel_trigger.name);
+ ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ break;
+ default:
+ ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ PERROR("create trigger ioctl");
+ }
+ goto free_event;
+ }
+
+ event->fd = fd;
+ /* Prevent fd duplication after execlp() */
+ err = fcntl(event->fd, F_SETFD, FD_CLOEXEC);
+ if (err < 0) {
+ PERROR("fcntl session fd");
+ }
+
+ if (event->filter) {
+ err = kernctl_filter(event->fd, event->filter);
+ if (err < 0) {
+ switch (-err) {
+ case ENOMEM:
+ ret = LTTNG_ERR_FILTER_NOMEM;
+ break;
+ default:
+ ret = LTTNG_ERR_FILTER_INVAL;
+ break;
+ }
+ goto filter_error;
+ }
+ }
+
+ if (lttng_event_rule_get_type(event->event_rule) ==
+ LTTNG_EVENT_RULE_TYPE_UPROBE) {
+ ret = userspace_probe_event_rule_add_callsites(
+ event->event_rule, creds, event->fd);
+ if (ret) {
+ goto add_callsite_error;
+ }
+ }
+
+ /* Set the capture bytecode */
+ capture_bytecode_count = lttng_trigger_get_capture_bytecode_count(trigger);
+ for (unsigned int i = 0; i < capture_bytecode_count; i++) {
+ const struct lttng_bytecode *capture_bytecode = lttng_trigger_get_capture_bytecode_at_index(trigger, i);
+ ret = kernctl_capture(event->fd, capture_bytecode);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+
+ err = kernctl_enable(event->fd);
+ if (err < 0) {
+ switch (-err) {
+ case EEXIST:
+ ret = LTTNG_ERR_KERN_EVENT_EXIST;
+ break;
+ default:
+ PERROR("enable kernel trigger");
+ ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ break;
+ }
+ goto enable_error;
+ }
+
+ /* Add event to event list */
+ cds_list_add(&event->list, &kernel_tracer_token_list.head);
+
+ DBG("Trigger %s created (fd: %d)", kernel_trigger.name, event->fd);
+
+ return 0;
+
+add_callsite_error:
+enable_error:
+filter_error:
+ {
+ int closeret;
+
+ closeret = close(event->fd);
+ if (closeret) {
+ PERROR("close event fd");
+ }
+ }
+free_event:
+ free(event);
+error:
+ return ret;
+}
+
+enum lttng_error_code kernel_update_tokens(void)
+{
+ enum lttng_error_code ret = LTTNG_OK;
+ enum lttng_trigger_status t_status;
+ struct ltt_kernel_token_event_rule *token_event_rule_element;
+ struct lttng_triggers *triggers;
+ unsigned int count;
+
+ /* TODO error handling */
+
+ /* Get list of token trigger from the notification thread here */
+ rcu_read_lock();
+ pthread_mutex_lock(¬ification_trigger_tokens_ht_lock);
+ ret = notification_thread_command_get_tokens(notification_thread_handle, &triggers);
+ if (ret != LTTNG_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ assert(triggers);
+
+ t_status = lttng_triggers_get_count(triggers, &count);
+ if (t_status != LTTNG_TRIGGER_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ for (unsigned int i = 0; i < count; i++) {
+ struct lttng_condition *condition;
+ struct lttng_event_rule *event_rule;
+ struct lttng_trigger *trigger;
+ struct ltt_kernel_token_event_rule *k_token;
+ const struct lttng_credentials *creds;
+ uint64_t token;
+
+ trigger = lttng_triggers_get_pointer_of_index(triggers, i);
+ assert(trigger);
+
+ /* TODO: error checking and type checking */
+ token = lttng_trigger_get_key(trigger);
+ condition = lttng_trigger_get_condition(trigger);
+ (void) lttng_condition_event_rule_get_rule_no_const(condition, &event_rule);
+
+ if (lttng_event_rule_get_domain_type(event_rule) != LTTNG_DOMAIN_KERNEL) {
+ /* Skip ust related trigger */
+ continue;
+ }
+
+ creds = lttng_trigger_get_credentials(trigger);
+ /* Iterate over all known token trigger */
+ k_token = trace_kernel_find_trigger_by_token(&kernel_tracer_token_list, token);
+ if (!k_token) {
+ ret = kernel_create_token_event_rule(trigger, creds, token);
+ if (ret < 0) {
+ goto end;
+ }
+ }
+ }
+
+ /* Remove all unknown trigger from the app
+ * TODO find a way better way then this, do it on the unregister command
+ * and be specific on the token to remove instead of going over all
+ * trigger known to the app. This is sub optimal.
+ */
+ cds_list_for_each_entry (token_event_rule_element, &kernel_tracer_token_list.head,
+ list) {
+ uint64_t token;
+ bool found = false;
+
+ token = token_event_rule_element->token;
+
+ /*
+ * Check if the app event trigger still exists on the
+ * notification side.
+ * TODO: might want to change the backing data struct of the
+ * lttng_triggers object to allow quick lookup?
+ * For kernel mostly all of this can be removed once we delete
+ * on a per trigger basis.
+ */
+
+ for (unsigned int i = 0; i < count; i++) {
+ struct lttng_trigger *trigger;
+ uint64_t inner_token;
+
+ trigger = lttng_triggers_get_pointer_of_index(
+ triggers, i);
+ assert(trigger);
+
+ inner_token = lttng_trigger_get_key(trigger);
+
+ if (inner_token == token) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ /* Still valid */
+ continue;
+ }
+
+ kernel_disable_token_event_rule(token_event_rule_element);
+ trace_kernel_destroy_token_event_rule(token_event_rule_element);
+ }
+end:
+ lttng_triggers_destroy(triggers);
+ rcu_read_unlock();
+ pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock);
+ return ret;
+
+}
+
+int kernel_get_notification_fd(void)
+{
+ return kernel_tracer_trigger_group_notification_fd;
+}
int kernel_create_channel(struct ltt_kernel_session *session,
struct lttng_channel *chan);
int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel,
- char *filter_expression, struct lttng_filter_bytecode *filter);
+ char *filter_expression, struct lttng_bytecode *filter);
int kernel_disable_channel(struct ltt_kernel_channel *chan);
int kernel_disable_event(struct ltt_kernel_event *event);
int kernel_enable_event(struct ltt_kernel_event *event);
enum lttng_error_code kernel_create_channel_subdirectories(
const struct ltt_kernel_session *ksess);
+enum lttng_error_code kernel_create_trigger_group_notification_fd(
+ int *trigger_group_notification_fd);
+enum lttng_error_code kernel_destroy_trigger_group_notification_fd(
+ int trigger_group_notification_fd);
+enum lttng_error_code kernel_update_tokens(void);
+int kernel_get_notification_fd(void);
+
#endif /* _LTT_KERNEL_CTL_H */
/* Notification thread handle. */
extern struct notification_thread_handle *notification_thread_handle;
+extern pthread_mutex_t notification_trigger_tokens_ht_lock;
/*
* This contains extra data needed for processing a command received by the
pthread_mutex_destroy(&session_list->lock);
+ DBG("Cleaning up all trigger agents");
+ trigger_agent_ht_clean();
+
DBG("Cleaning up all agent apps");
agent_app_ht_clean();
DBG("Closing all UST sockets");
struct lttng_thread *notification_thread = NULL;
struct lttng_thread *register_apps_thread = NULL;
+ logger_set_thread_name("Main", false);
init_kernel_workarounds();
rcu_register_thread();
goto stop_threads;
}
+ if (trigger_agent_ht_alloc()) {
+ ERR("Failed to allocate trigger 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
notification_thread_handle = notification_thread_handle_create(
ust32_channel_monitor_pipe,
ust64_channel_monitor_pipe,
- kernel_channel_monitor_pipe);
+ kernel_channel_monitor_pipe,
+ kernel_get_notification_fd());
if (!notification_thread_handle) {
retval = -1;
ERR("Failed to create notification thread shared data");
#define LTTNG_MOD_OPTIONAL 0
/* LTTng kernel tracer mandatory core modules list */
+/* TODO: the new trigger client might not be present in previous lttng-modules
+ * should it be optional?
+ * Can we reuse this to also know of the trigger feature is supported?
+ */
struct kern_modules_param kern_modules_control_core[] = {
{ (char *) "lttng-ring-buffer-client-discard" },
{ (char *) "lttng-ring-buffer-client-overwrite" },
{ (char *) "lttng-ring-buffer-client-mmap-discard" },
{ (char *) "lttng-ring-buffer-client-mmap-overwrite" },
{ (char *) "lttng-ring-buffer-metadata-mmap-client" },
+ { (char *) "lttng-ring-buffer-trigger-client" },
};
/* LTTng kernel tracer probe modules list */
#include "notification-thread.h"
#include "notification-thread-commands.h"
#include <common/error.h>
+#include <common/macros.h>
#include <unistd.h>
#include <stdint.h>
#include <inttypes.h>
static
void init_notification_thread_command(struct notification_thread_command *cmd)
{
- memset(cmd, 0, sizeof(*cmd));
CDS_INIT_LIST_HEAD(&cmd->cmd_list_node);
lttng_waiter_init(&cmd->reply_waiter);
}
return -1;
}
+static
+struct notification_thread_command *notification_thread_command_copy(
+ const struct notification_thread_command *original_cmd)
+{
+ struct notification_thread_command *new_cmd;
+
+ new_cmd = zmalloc(sizeof(*new_cmd));
+ if (!new_cmd) {
+ goto end;
+ }
+
+ *new_cmd = *original_cmd;
+ init_notification_thread_command(new_cmd);
+end:
+ return new_cmd;
+}
+
+static
+int run_command_no_wait(struct notification_thread_handle *handle,
+ const struct notification_thread_command *in_cmd)
+{
+ int ret;
+ uint64_t notification_counter = 1;
+ struct notification_thread_command *new_cmd =
+ notification_thread_command_copy(in_cmd);
+
+ if (!new_cmd) {
+ goto error;
+ }
+ new_cmd->is_async = true;
+
+ pthread_mutex_lock(&handle->cmd_queue.lock);
+ /* Add to queue. */
+ cds_list_add_tail(&new_cmd->cmd_list_node,
+ &handle->cmd_queue.list);
+ /* Wake-up thread. */
+ ret = lttng_write(lttng_pipe_get_writefd(handle->cmd_queue.event_pipe),
+ ¬ification_counter, sizeof(notification_counter));
+ if (ret != sizeof(notification_counter)) {
+ PERROR("write to notification thread's queue event fd");
+ /*
+ * Remove the command from the list so the notification
+ * thread does not process it.
+ */
+ cds_list_del(&new_cmd->cmd_list_node);
+ goto error_unlock_queue;
+ }
+ pthread_mutex_unlock(&handle->cmd_queue.lock);
+ return 0;
+error_unlock_queue:
+ free(new_cmd);
+ pthread_mutex_unlock(&handle->cmd_queue.lock);
+error:
+ return -1;
+}
+
enum lttng_error_code notification_thread_command_register_trigger(
struct notification_thread_handle *handle,
struct lttng_trigger *trigger)
{
int ret;
enum lttng_error_code ret_code;
- struct notification_thread_command cmd;
+ struct notification_thread_command cmd = {};
init_notification_thread_command(&cmd);
{
int ret;
enum lttng_error_code ret_code;
- struct notification_thread_command cmd;
+ struct notification_thread_command cmd = {};
init_notification_thread_command(&cmd);
{
int ret;
enum lttng_error_code ret_code;
- struct notification_thread_command cmd;
+ struct notification_thread_command cmd = {};
init_notification_thread_command(&cmd);
{
int ret;
enum lttng_error_code ret_code;
- struct notification_thread_command cmd;
+ struct notification_thread_command cmd = {};
init_notification_thread_command(&cmd);
{
int ret;
enum lttng_error_code ret_code;
- struct notification_thread_command cmd;
+ struct notification_thread_command cmd = {};
init_notification_thread_command(&cmd);
{
int ret;
enum lttng_error_code ret_code;
- struct notification_thread_command cmd;
+ struct notification_thread_command cmd = {};
init_notification_thread_command(&cmd);
return ret_code;
}
+enum lttng_error_code notification_thread_command_add_application(
+ struct notification_thread_handle *handle,
+ struct lttng_pipe *pipe)
+{
+ int ret;
+ enum lttng_error_code ret_code;
+ struct notification_thread_command cmd = {};
+
+ init_notification_thread_command(&cmd);
+
+ cmd.type = NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION;
+ cmd.parameters.application.read_side_trigger_event_application_pipe = lttng_pipe_get_readfd(pipe);
+
+ ret = run_command_wait(handle, &cmd);
+ if (ret) {
+ ret_code = LTTNG_ERR_UNK;
+ goto end;
+ }
+ ret_code = cmd.reply_code;
+end:
+ return ret_code;
+}
+
+enum lttng_error_code notification_thread_command_remove_application(
+ struct notification_thread_handle *handle,
+ struct lttng_pipe *pipe)
+{
+ int ret;
+ enum lttng_error_code ret_code;
+ struct notification_thread_command cmd = {};
+
+ init_notification_thread_command(&cmd);
+
+ cmd.type = NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION;
+ cmd.parameters.application.read_side_trigger_event_application_pipe = lttng_pipe_get_readfd(pipe);
+
+ ret = run_command_wait(handle, &cmd);
+ if (ret) {
+ ret_code = LTTNG_ERR_UNK;
+ goto end;
+ }
+ ret_code = cmd.reply_code;
+end:
+ return ret_code;
+}
+
+enum lttng_error_code notification_thread_command_get_tokens(
+ struct notification_thread_handle *handle,
+ struct lttng_triggers **tokens_triggers)
+{
+ int ret;
+ enum lttng_error_code ret_code;
+ struct notification_thread_command cmd = {};
+
+ assert(handle);
+ assert(tokens_triggers);
+
+ init_notification_thread_command(&cmd);
+
+ cmd.type = NOTIFICATION_COMMAND_TYPE_GET_TOKENS;
+
+ ret = run_command_wait(handle, &cmd);
+ if (ret) {
+ ret_code = LTTNG_ERR_UNK;
+ goto end;
+ }
+ ret_code = cmd.reply_code;
+ *tokens_triggers = cmd.reply.get_tokens.triggers;
+
+end:
+ return ret_code;
+}
+
+enum lttng_error_code notification_thread_command_list_triggers(
+ struct notification_thread_handle *handle,
+ uid_t uid,
+ gid_t gid,
+ struct lttng_triggers **triggers)
+{
+ int ret;
+ enum lttng_error_code ret_code;
+ struct notification_thread_command cmd = {};
+
+ assert(handle);
+ assert(triggers);
+
+ init_notification_thread_command(&cmd);
+
+ cmd.type = NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS;
+ cmd.parameters.list_triggers.uid = uid;
+ cmd.parameters.list_triggers.gid = gid;
+
+ ret = run_command_wait(handle, &cmd);
+ if (ret) {
+ ret_code = LTTNG_ERR_UNK;
+ goto end;
+ }
+ ret_code = cmd.reply_code;
+ *triggers = cmd.reply.list_triggers.triggers;
+
+end:
+ return ret_code;
+}
+
void notification_thread_command_quit(
struct notification_thread_handle *handle)
{
int ret;
- struct notification_thread_command cmd;
+ struct notification_thread_command cmd = {};
init_notification_thread_command(&cmd);
ret = run_command_wait(handle, &cmd);
assert(!ret && cmd.reply_code == LTTNG_OK);
}
+
+int notification_thread_client_communication_update(
+ struct notification_thread_handle *handle,
+ notification_client_id id,
+ enum client_transmission_status transmission_status)
+{
+ struct notification_thread_command cmd = {};
+
+ init_notification_thread_command(&cmd);
+
+ cmd.type = NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE;
+ cmd.parameters.client_communication_update.id = id;
+ cmd.parameters.client_communication_update.status = transmission_status;
+ return run_command_no_wait(handle, &cmd);
+}
+
+/*
+ * Takes ownership of the payload if present.
+ */
+LTTNG_HIDDEN
+struct lttng_trigger_notification *lttng_trigger_notification_create(
+ uint64_t id,
+ enum lttng_domain_type domain,
+ char *payload,
+ size_t payload_size)
+{
+ struct lttng_trigger_notification *notification = NULL;
+
+ assert(domain != LTTNG_DOMAIN_NONE);
+
+ if (payload) {
+ assert(payload_size > 0);
+ } else {
+ assert(payload_size == 0);
+ }
+
+ notification = zmalloc(sizeof(struct lttng_trigger_notification));
+ if (notification == NULL) {
+ ERR("[notification-thread] Error allocating notification ");
+ goto end;
+ }
+
+ notification->id = id;
+ notification->type = domain;
+ notification->capture_buffer = payload;
+ notification->capture_buf_size = payload_size;
+
+end:
+ return notification;
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_notification_destroy(
+ struct lttng_trigger_notification *notification)
+{
+ if (!notification) {
+ return;
+ }
+
+ if(notification->capture_buffer) {
+ free(notification->capture_buffer);
+ }
+ free(notification);
+}
#include "notification-thread-internal.h"
#include "notification-thread-events.h"
#include <common/waiter.h>
+#include <stdbool.h>
struct notification_thread_data;
struct lttng_trigger;
NOTIFICATION_COMMAND_TYPE_REMOVE_CHANNEL,
NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_ONGOING,
NOTIFICATION_COMMAND_TYPE_SESSION_ROTATION_COMPLETED,
+ NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION,
+ NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION,
+ NOTIFICATION_COMMAND_TYPE_GET_TOKENS,
+ NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS,
NOTIFICATION_COMMAND_TYPE_QUIT,
+ NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE,
};
struct notification_thread_command {
uint64_t trace_archive_chunk_id;
struct lttng_trace_archive_location *location;
} session_rotation;
+ /* Add/Remove application */
+ struct {
+ int read_side_trigger_event_application_pipe;
+ } application;
+ /* List triggers */
+ struct {
+ /* Credential */
+ uid_t uid;
+ gid_t gid;
+ } list_triggers;
+ /* Client communication update. */
+ struct {
+ notification_client_id id;
+ enum client_transmission_status status;
+ } client_communication_update;
+
} parameters;
+ union {
+ struct {
+ struct lttng_triggers *triggers;
+ } get_tokens;
+ struct {
+ struct lttng_triggers *triggers;
+ } list_triggers;
+ } reply;
+
/* lttng_waiter on which to wait for command reply (optional). */
struct lttng_waiter reply_waiter;
enum lttng_error_code reply_code;
+ bool is_async;
};
enum lttng_error_code notification_thread_command_register_trigger(
uint64_t trace_archive_chunk_id,
struct lttng_trace_archive_location *location);
+enum lttng_error_code notification_thread_command_add_application(
+ struct notification_thread_handle *handle,
+ struct lttng_pipe *trigger_event_application_pipe);
+
+enum lttng_error_code notification_thread_command_remove_application(
+ struct notification_thread_handle *handle,
+ struct lttng_pipe *trigger_event_application_pipe);
+
+/* Must hold the notification_trigger_tokens_ht_lock to protect against
+ * insertion removal of triggers TODO: is it the case even with refcounting? */
+/* todo find a better way....*/
+enum lttng_error_code notification_thread_command_get_tokens(
+ struct notification_thread_handle *handle,
+ struct lttng_triggers **triggers);
+
+/* TODO: for now we borrow with no refcount the trigger. THIS IS DANGEROUS */
+enum lttng_error_code notification_thread_command_list_triggers(
+ struct notification_thread_handle *handle,
+ uid_t uid,
+ gid_t gid,
+ struct lttng_triggers **triggers);
+
void notification_thread_command_quit(
struct notification_thread_handle *handle);
#include <common/macros.h>
#include <lttng/condition/condition.h>
#include <lttng/action/action-internal.h>
+#include <lttng/action/group-internal.h>
+#include <lttng/domain-internal.h>
#include <lttng/notification/notification-internal.h>
#include <lttng/condition/condition-internal.h>
#include <lttng/condition/buffer-usage-internal.h>
#include <lttng/condition/session-consumed-size-internal.h>
#include <lttng/condition/session-rotation-internal.h>
+#include <lttng/condition/event-rule-internal.h>
#include <lttng/notification/channel-internal.h>
+#include <lttng/trigger/trigger-internal.h>
#include <time.h>
#include <unistd.h>
struct lttng_trigger_list_element {
/* No ownership of the trigger object is assumed. */
- const struct lttng_trigger *trigger;
+ struct lttng_trigger *trigger;
struct cds_list_head node;
};
struct lttng_trigger_ht_element {
struct lttng_trigger *trigger;
struct cds_lfht_node node;
+ struct cds_lfht_node node_by_name;
/* call_rcu delayed reclaim. */
struct rcu_head rcu_node;
};
struct cds_list_head node;
};
-struct notification_client_list_element {
- struct notification_client *client;
- struct cds_list_head node;
-};
-
-struct notification_client_list {
- const struct lttng_trigger *trigger;
- struct cds_list_head list;
- struct cds_lfht_node notification_trigger_ht_node;
- /* call_rcu delayed reclaim. */
- struct rcu_head rcu_node;
-};
-
-struct notification_client {
- int socket;
- /* Client protocol version. */
- uint8_t major, minor;
- uid_t uid;
- gid_t gid;
- /*
- * Indicates if the credentials and versions of the client have been
- * checked.
- */
- bool validated;
- /*
- * Conditions to which the client's notification channel is subscribed.
- * List of struct lttng_condition_list_node. The condition member is
- * owned by the client.
- */
- struct cds_list_head condition_list;
- struct cds_lfht_node client_socket_ht_node;
- struct {
- struct {
- /*
- * During the reception of a message, the reception
- * buffers' "size" is set to contain the current
- * message's complete payload.
- */
- struct lttng_dynamic_buffer buffer;
- /* Bytes left to receive for the current message. */
- size_t bytes_to_receive;
- /* Type of the message being received. */
- enum lttng_notification_channel_message_type msg_type;
- /*
- * Indicates whether or not credentials are expected
- * from the client.
- */
- bool expect_creds;
- /*
- * Indicates whether or not credentials were received
- * from the client.
- */
- bool creds_received;
- /* Only used during credentials reception. */
- lttng_sock_cred creds;
- } inbound;
- struct {
- /*
- * Indicates whether or not a notification addressed to
- * this client was dropped because a command reply was
- * already buffered.
- *
- * A notification is dropped whenever the buffer is not
- * empty.
- */
- bool dropped_notification;
- /*
- * Indicates whether or not a command reply is already
- * buffered. In this case, it means that the client is
- * not consuming command replies before emitting a new
- * one. This could be caused by a protocol error or a
- * misbehaving/malicious client.
- */
- bool queued_command_reply;
- struct lttng_dynamic_buffer buffer;
- } outbound;
- } communication;
- /* call_rcu delayed reclaim. */
- struct rcu_head rcu_node;
-};
-
struct channel_state_sample {
struct channel_key key;
struct cds_lfht_node channel_state_ht_node;
struct lttng_session_trigger_list *list);
static
int lttng_session_trigger_list_add(struct lttng_session_trigger_list *list,
- const struct lttng_trigger *trigger);
+ struct lttng_trigger *trigger);
+static
+int client_handle_transmission_status(
+ struct notification_client *client,
+ enum client_transmission_status transmission_status,
+ struct notification_thread_state *state);
static
-int match_client(struct cds_lfht_node *node, const void *key)
+int match_client_socket(struct cds_lfht_node *node, const void *key)
{
/* This double-cast is intended to supress pointer-to-cast warning. */
- int socket = (int) (intptr_t) key;
- struct notification_client *client;
+ const int socket = (int) (intptr_t) key;
+ const struct notification_client *client = caa_container_of(node,
+ struct notification_client, client_socket_ht_node);
- client = caa_container_of(node, struct notification_client,
- client_socket_ht_node);
+ return client->socket == socket;
+}
+
+static
+int match_client_id(struct cds_lfht_node *node, const void *key)
+{
+ /* This double-cast is intended to supress pointer-to-cast warning. */
+ const notification_client_id id = *((notification_client_id *) key);
+ const struct notification_client *client = caa_container_of(
+ node, struct notification_client, client_id_ht_node);
- return !!(client->socket == socket);
+ return client->id == id;
}
static
}
static
-int match_condition(struct cds_lfht_node *node, const void *key)
+int match_trigger(struct cds_lfht_node *node, const void *key)
{
- struct lttng_condition *condition_key = (struct lttng_condition *) key;
- struct lttng_trigger_ht_element *trigger;
- struct lttng_condition *condition;
+ bool match = false;
+ struct lttng_trigger *trigger_key = (struct lttng_trigger *) key;
+ struct lttng_trigger_ht_element *trigger_ht_element;
+ const struct lttng_credentials *creds_key;
+ const struct lttng_credentials *creds_node;
- trigger = caa_container_of(node, struct lttng_trigger_ht_element,
+ trigger_ht_element = caa_container_of(node, struct lttng_trigger_ht_element,
node);
- condition = lttng_trigger_get_condition(trigger->trigger);
- assert(condition);
- return !!lttng_condition_is_equal(condition_key, condition);
+ match = lttng_trigger_is_equal(trigger_key, trigger_ht_element->trigger);
+ if (!match) {
+ goto end;
+ }
+
+ /* Validate credential */
+ /* TODO: this could be moved to lttng_trigger_equal depending on how we
+ * handle root behaviour on disable and listing.
+ */
+ creds_key = lttng_trigger_get_credentials(trigger_key);
+ creds_node = lttng_trigger_get_credentials(trigger_ht_element->trigger);
+ match = lttng_credentials_is_equal(creds_key, creds_node);
+end:
+ return !!match;
+}
+
+static
+int match_trigger_token(struct cds_lfht_node *node, const void *key)
+{
+ const uint64_t *_key = key;
+ struct notification_trigger_tokens_ht_element *element;
+
+ element = caa_container_of(node, struct notification_trigger_tokens_ht_element,
+ node);
+ return *_key == element->token ;
}
static
assert(condition_key);
client_list = caa_container_of(node, struct notification_client_list,
- notification_trigger_ht_node);
+ notification_trigger_clients_ht_node);
condition = lttng_trigger_get_const_condition(client_list->trigger);
return !!lttng_condition_is_equal(condition_key, condition);
return !strcmp(session_info->name, name);
}
+/*
+ * Match function for string node.
+ */
+static int match_str(struct cds_lfht_node *node, const void *key)
+{
+ struct lttng_trigger_ht_element *trigger_ht_element;
+ const char *name;
+
+ trigger_ht_element = caa_container_of(node, struct lttng_trigger_ht_element,
+ node_by_name);
+
+ /* TODO error checking */
+ lttng_trigger_get_name(trigger_ht_element->trigger, &name);
+
+ return hash_match_key_str(name, (void *) key);
+}
+
static
unsigned long lttng_condition_buffer_usage_hash(
const struct lttng_condition *_condition)
return hash;
}
+static
+unsigned long lttng_condition_event_rule_hash(
+ const struct lttng_condition *_condition)
+{
+ unsigned long hash, condition_type;
+ struct lttng_condition_event_rule *condition;
+
+ condition = container_of(_condition,
+ struct lttng_condition_event_rule, parent);
+ condition_type = (unsigned long) condition->parent.type;
+ hash = hash_key_ulong((void *) condition_type, lttng_ht_seed);
+
+ /* TODO: further hasg using the event rule? on pattern maybe?*/
+ return hash;
+}
+
/*
* The lttng_condition hashing code is kept in this file (rather than
* condition.c) since it makes use of GPLv2 code (hashtable utils), which we
case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
return lttng_condition_session_rotation_hash(condition);
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ return lttng_condition_event_rule_hash(condition);
default:
ERR("[notification-thread] Unexpected condition type caught");
abort();
return key_hash ^ domain_hash;
}
+static
+unsigned long hash_client_socket(int socket)
+{
+ return hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed);
+}
+
+static
+unsigned long hash_client_id(notification_client_id id)
+{
+ return hash_key_u64(&id, lttng_ht_seed);
+}
+
/*
* Get the type of object to which a given condition applies. Bindings let
* the notification system evaluate a trigger's condition when a given
case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
return LTTNG_OBJECT_TYPE_SESSION;
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ return LTTNG_OBJECT_TYPE_NONE;
default:
return LTTNG_OBJECT_TYPE_UNKNOWN;
}
assert(name);
- session_info = zmalloc(sizeof(*session_info));
+ session_info = malloc(sizeof(*session_info));
if (!session_info) {
goto end;
}
return NULL;
}
-/* RCU read lock must be held by the caller. */
+LTTNG_HIDDEN
+bool notification_client_list_get(struct notification_client_list *list)
+{
+ return urcu_ref_get_unless_zero(&list->ref);
+}
+
+static
+void free_notification_client_list_rcu(struct rcu_head *node)
+{
+ free(caa_container_of(node, struct notification_client_list,
+ rcu_node));
+}
+
+static
+void notification_client_list_release(struct urcu_ref *list_ref)
+{
+ struct notification_client_list *list =
+ container_of(list_ref, typeof(*list), ref);
+ struct notification_client_list_element *client_list_element, *tmp;
+
+ if (list->notification_trigger_clients_ht) {
+ rcu_read_lock();
+ cds_lfht_del(list->notification_trigger_clients_ht,
+ &list->notification_trigger_clients_ht_node);
+ rcu_read_unlock();
+ list->notification_trigger_clients_ht = NULL;
+ }
+ cds_list_for_each_entry_safe(client_list_element, tmp,
+ &list->list, node) {
+ free(client_list_element);
+ }
+ pthread_mutex_destroy(&list->lock);
+ call_rcu(&list->rcu_node, free_notification_client_list_rcu);
+}
+
+static
+struct notification_client_list *notification_client_list_create(
+ const struct lttng_trigger *trigger)
+{
+ struct notification_client_list *client_list =
+ zmalloc(sizeof(*client_list));
+
+ if (!client_list) {
+ goto error;
+ }
+ pthread_mutex_init(&client_list->lock, NULL);
+ urcu_ref_init(&client_list->ref);
+ cds_lfht_node_init(&client_list->notification_trigger_clients_ht_node);
+ CDS_INIT_LIST_HEAD(&client_list->list);
+ client_list->trigger = trigger;
+error:
+ return client_list;
+}
+
+static
+void publish_notification_client_list(
+ struct notification_thread_state *state,
+ struct notification_client_list *list)
+{
+ const struct lttng_condition *condition =
+ lttng_trigger_get_const_condition(list->trigger);
+
+ assert(!list->notification_trigger_clients_ht);
+
+ list->notification_trigger_clients_ht =
+ state->notification_trigger_clients_ht;
+
+ rcu_read_lock();
+ cds_lfht_add(state->notification_trigger_clients_ht,
+ lttng_condition_hash(condition),
+ &list->notification_trigger_clients_ht_node);
+ rcu_read_unlock();
+}
+
+LTTNG_HIDDEN
+void notification_client_list_put(struct notification_client_list *list)
+{
+ if (!list) {
+ return;
+ }
+ return urcu_ref_put(&list->ref, notification_client_list_release);
+}
+
+/* Provides a reference to the returned list. */
static
struct notification_client_list *get_client_list_from_condition(
struct notification_thread_state *state,
{
struct cds_lfht_node *node;
struct cds_lfht_iter iter;
+ struct notification_client_list *list = NULL;
+ rcu_read_lock();
cds_lfht_lookup(state->notification_trigger_clients_ht,
lttng_condition_hash(condition),
match_client_list_condition,
condition,
&iter);
node = cds_lfht_iter_get_node(&iter);
-
- return node ? caa_container_of(node,
- struct notification_client_list,
- notification_trigger_ht_node) : NULL;
+ if (node) {
+ list = container_of(node, struct notification_client_list,
+ notification_trigger_clients_ht_node);
+ list = notification_client_list_get(list) ? list : NULL;
+ }
+ rcu_read_unlock();
+ return list;
}
-/* This function must be called with the RCU read lock held. */
static
int evaluate_channel_condition_for_client(
const struct lttng_condition *condition,
struct channel_state_sample *last_sample = NULL;
struct lttng_channel_trigger_list *channel_trigger_list = NULL;
+ rcu_read_lock();
+
/* Find the channel associated with the condition. */
cds_lfht_for_each_entry(state->channel_triggers_ht, &iter,
channel_trigger_list, channel_triggers_ht_node) {
*session_uid = channel_info->session_info->uid;
*session_gid = channel_info->session_info->gid;
end:
+ rcu_read_unlock();
return ret;
}
return session_name;
}
-/* This function must be called with the RCU read lock held. */
static
int evaluate_session_condition_for_client(
const struct lttng_condition *condition,
const char *session_name;
struct session_info *session_info = NULL;
+ rcu_read_lock();
session_name = get_condition_session_name(condition);
/* Find the session associated with the trigger. */
end_session_put:
session_info_put(session_info);
end:
+ rcu_read_unlock();
return ret;
}
-/* This function must be called with the RCU read lock held. */
static
int evaluate_condition_for_client(const struct lttng_trigger *trigger,
const struct lttng_condition *condition,
{
int ret;
struct lttng_evaluation *evaluation = NULL;
- struct notification_client_list client_list = { 0 };
+ struct notification_client_list client_list = {
+ .lock = PTHREAD_MUTEX_INITIALIZER,
+ };
struct notification_client_list_element client_list_element = { 0 };
uid_t object_uid = 0;
gid_t object_gid = 0;
&evaluation, &object_uid, &object_gid);
break;
case LTTNG_OBJECT_TYPE_NONE:
+ DBG("[notification-thread] Newly subscribed-to condition not binded to object, nothing to evaluate");
ret = 0;
goto end;
case LTTNG_OBJECT_TYPE_UNKNOWN:
* Create a temporary client list with the client currently
* subscribing.
*/
- cds_lfht_node_init(&client_list.notification_trigger_ht_node);
+ cds_lfht_node_init(&client_list.notification_trigger_clients_ht_node);
CDS_INIT_LIST_HEAD(&client_list.list);
client_list.trigger = trigger;
enum lttng_notification_channel_status *_status)
{
int ret = 0;
- struct notification_client_list *client_list;
+ struct notification_client_list *client_list = NULL;
struct lttng_condition_list_element *condition_list_element = NULL;
struct notification_client_list_element *client_list_element = NULL;
enum lttng_notification_channel_status status =
goto error;
}
- rcu_read_lock();
-
/*
* Add the newly-subscribed condition to the client's subscription list.
*/
* since this trigger is not registered yet.
*/
free(client_list_element);
- goto end_unlock;
+ goto end;
}
/*
* The condition to which the client just subscribed is evaluated
* at this point so that conditions that are already TRUE result
* in a notification being sent out.
+ *
+ * The client_list's trigger is used without locking the list itself.
+ * This is correct since the list doesn't own the trigger and the
+ * object is immutable.
*/
if (evaluate_condition_for_client(client_list->trigger, condition,
client, state)) {
WARN("[notification-thread] Evaluation of a condition on client subscription failed, aborting.");
ret = -1;
free(client_list_element);
- goto end_unlock;
+ goto end;
}
/*
*/
client_list_element->client = client;
CDS_INIT_LIST_HEAD(&client_list_element->node);
+
+ pthread_mutex_lock(&client_list->lock);
cds_list_add(&client_list_element->node, &client_list->list);
-end_unlock:
- rcu_read_unlock();
+ pthread_mutex_unlock(&client_list->lock);
end:
if (_status) {
*_status = status;
}
+ if (client_list) {
+ notification_client_list_put(client_list);
+ }
return ret;
error:
free(condition_list_element);
* Remove the client from the list of clients interested the trigger
* matching the condition.
*/
- rcu_read_lock();
client_list = get_client_list_from_condition(state, condition);
if (!client_list) {
- goto end_unlock;
+ goto end;
}
+ pthread_mutex_lock(&client_list->lock);
cds_list_for_each_entry_safe(client_list_element, client_tmp,
&client_list->list, node) {
- if (client_list_element->client->socket != client->socket) {
+ if (client_list_element->client->id != client->id) {
continue;
}
cds_list_del(&client_list_element->node);
free(client_list_element);
break;
}
-end_unlock:
- rcu_read_unlock();
+ pthread_mutex_unlock(&client_list->lock);
+ notification_client_list_put(client_list);
+ client_list = NULL;
end:
lttng_condition_destroy(condition);
if (_status) {
void notification_client_destroy(struct notification_client *client,
struct notification_thread_state *state)
{
- struct lttng_condition_list_element *condition_list_element, *tmp;
-
if (!client) {
return;
}
- /* Release all conditions to which the client was subscribed. */
- cds_list_for_each_entry_safe(condition_list_element, tmp,
- &client->condition_list, node) {
- (void) notification_thread_client_unsubscribe(client,
- condition_list_element->condition, state, NULL);
- }
-
+ /*
+ * The client object is not reachable by other threads, no need to lock
+ * the client here.
+ */
if (client->socket >= 0) {
(void) lttcomm_close_unix_sock(client->socket);
+ client->socket = -1;
}
+ client->communication.active = false;
lttng_dynamic_buffer_reset(&client->communication.inbound.buffer);
lttng_dynamic_buffer_reset(&client->communication.outbound.buffer);
+ pthread_mutex_destroy(&client->lock);
call_rcu(&client->rcu_node, free_notification_client_rcu);
}
struct notification_client *client = NULL;
cds_lfht_lookup(state->client_socket_ht,
- hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed),
- match_client,
+ hash_client_socket(socket),
+ match_client_socket,
(void *) (unsigned long) socket,
&iter);
node = cds_lfht_iter_get_node(&iter);
return client;
}
+/*
+ * Call with rcu_read_lock held (and hold for the lifetime of the returned
+ * client pointer).
+ */
+static
+struct notification_client *get_client_from_id(notification_client_id id,
+ struct notification_thread_state *state)
+{
+ struct cds_lfht_iter iter;
+ struct cds_lfht_node *node;
+ struct notification_client *client = NULL;
+
+ cds_lfht_lookup(state->client_id_ht,
+ hash_client_id(id),
+ match_client_id,
+ &id,
+ &iter);
+ node = cds_lfht_iter_get_node(&iter);
+ if (!node) {
+ goto end;
+ }
+
+ client = caa_container_of(node, struct notification_client,
+ client_id_ht_node);
+end:
+ return client;
+}
+
static
bool buffer_usage_condition_applies_to_channel(
const struct lttng_condition *condition,
static
int lttng_session_trigger_list_add(struct lttng_session_trigger_list *list,
- const struct lttng_trigger *trigger)
+ struct lttng_trigger *trigger)
{
int ret = 0;
struct lttng_trigger_list_element *new_element =
struct cds_lfht_iter iter;
struct session_info *session_info = NULL;
- DBG("[notification-thread] Adding channel %s from session %s, channel key = %" PRIu64 " in %s domain",
+ DBG("[notification-thread] Adding channel %s from session %s, channel key = %" PRIu64
+ " in %s domain",
channel_name, session_name, channel_key_int,
- channel_domain == LTTNG_DOMAIN_KERNEL ? "kernel" : "user space");
+ lttng_domain_type_str(channel_domain));
CDS_INIT_LIST_HEAD(&trigger_list);
struct channel_key key = { .key = channel_key, .domain = domain };
struct channel_info *channel_info;
- DBG("[notification-thread] Removing channel key = %" PRIu64 " in %s domain",
- channel_key, domain == LTTNG_DOMAIN_KERNEL ? "kernel" : "user space");
+ DBG("[notification-thread] Removing channel key = %" PRIu64
+ " in %s domain",
+ channel_key, lttng_domain_type_str(domain));
rcu_read_lock();
struct notification_client_list *client_list;
struct lttng_evaluation *evaluation = NULL;
enum lttng_condition_type condition_type;
+ bool client_list_is_empty;
trigger = trigger_list_element->trigger;
condition = lttng_trigger_get_const_condition(trigger);
client_list = get_client_list_from_condition(state, condition);
assert(client_list);
- if (cds_list_empty(&client_list->list)) {
+ pthread_mutex_lock(&client_list->lock);
+ client_list_is_empty = cds_list_empty(&client_list->list);
+ pthread_mutex_unlock(&client_list->lock);
+ if (client_list_is_empty) {
/*
* No clients interested in the evaluation's result,
* skip it.
/* Internal error */
ret = -1;
cmd_result = LTTNG_ERR_UNK;
- goto end;
+ goto put_list;
}
/* Dispatch evaluation result to all clients. */
session_info->uid,
session_info->gid);
lttng_evaluation_destroy(evaluation);
+put_list:
+ notification_client_list_put(client_list);
if (caa_unlikely(ret)) {
- goto end;
+ break;
}
}
end:
}
static
-int condition_is_supported(struct lttng_condition *condition)
+int handle_notification_thread_command_add_application(
+ struct notification_thread_handle *handle,
+ struct notification_thread_state *state,
+ int read_side_trigger_event_application_pipe,
+ enum lttng_error_code *_cmd_result)
{
- int ret;
+ int ret = 0;
+ enum lttng_error_code cmd_result = LTTNG_OK;
+ struct notification_event_trigger_source_element *element = NULL;
- switch (lttng_condition_get_type(condition)) {
- case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
- case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
- {
- enum lttng_domain_type domain;
+ element = zmalloc(sizeof(*element));
+ if (!element) {
+ cmd_result = LTTNG_ERR_NOMEM;
+ ret = -1;
+ goto end;
+ }
- ret = lttng_condition_buffer_usage_get_domain_type(condition,
- &domain);
- if (ret) {
- ret = -1;
- goto end;
- }
+ CDS_INIT_LIST_HEAD(&element->node);
+ element->fd = read_side_trigger_event_application_pipe;
- if (domain != LTTNG_DOMAIN_KERNEL) {
- ret = 1;
- goto end;
- }
+ pthread_mutex_lock(&handle->event_trigger_sources.lock);
+ cds_list_add(&element->node, &handle->event_trigger_sources.list);
+ pthread_mutex_unlock(&handle->event_trigger_sources.lock);
- /*
- * Older kernel tracers don't expose the API to monitor their
- * buffers. Therefore, we reject triggers that require that
- * mechanism to be available to be evaluated.
- */
- ret = kernel_supports_ring_buffer_snapshot_sample_positions();
- break;
- }
- default:
- ret = 1;
+ /* TODO: remove on failure to add to list? */
+
+ /* Adding the read side pipe to the event poll */
+ ret = lttng_poll_add(&state->events,
+ read_side_trigger_event_application_pipe,
+ LPOLLIN | LPOLLERR);
+
+ DBG3("[notification-thread] Adding application event source from fd: %d", read_side_trigger_event_application_pipe);
+ if (ret < 0) {
+ /* TODO: what should be the value of cmd_result??? */
+ ERR("[notification-thread] Failed to add event source pipe fd to pollset");
+ goto end;
}
+
end:
+ *_cmd_result = cmd_result;
return ret;
}
-/* Must be called with RCU read lock held. */
static
-int bind_trigger_to_matching_session(const struct lttng_trigger *trigger,
- struct notification_thread_state *state)
+int handle_notification_thread_command_remove_application(
+ struct notification_thread_handle *handle,
+ struct notification_thread_state *state,
+ int read_side_trigger_event_application_pipe,
+ enum lttng_error_code *_cmd_result)
{
int ret = 0;
- const struct lttng_condition *condition;
- const char *session_name;
- struct lttng_session_trigger_list *trigger_list;
+ enum lttng_error_code cmd_result = LTTNG_OK;
- 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:
+ /* TODO: missing a lock propably to revisit */
+ struct notification_event_trigger_source_element *source_element, *tmp;
+ cds_list_for_each_entry_safe(source_element, tmp,
+ &handle->event_trigger_sources.list, node) {
+ if (source_element->fd != read_side_trigger_event_application_pipe) {
+ continue;
+ }
+
+ DBG("[notification-thread] Removed event source from event source list");
+ cds_list_del(&source_element->node);
+ free(source_element);
+ break;
+ }
+
+ DBG3("[notification-thread] Removing application event source from fd: %d", read_side_trigger_event_application_pipe);
+ /* Removing the read side pipe to the event poll */
+ ret = lttng_poll_del(&state->events,
+ read_side_trigger_event_application_pipe);
+ if (ret < 0) {
+ /* TODO: what should be the value of cmd_result??? */
+ ERR("[notification-thread] Failed to remove event source pipe fd from pollset");
+ goto end;
+ }
+
+end:
+ *_cmd_result = cmd_result;
+ return ret;
+}
+
+static int handle_notification_thread_command_get_tokens(
+ struct notification_thread_handle *handle,
+ struct notification_thread_state *state,
+ struct lttng_triggers **triggers,
+ enum lttng_error_code *_cmd_result)
+{
+ int ret = 0, i = 0;
+ enum lttng_error_code cmd_result = LTTNG_OK;
+ struct cds_lfht_iter iter;
+ struct notification_trigger_tokens_ht_element *element;
+ struct lttng_triggers *local_triggers = NULL;
+
+ local_triggers = lttng_triggers_create();
+ if (!local_triggers) {
+ cmd_result = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry (
+ state->trigger_tokens_ht, &iter, element, node) {
+ ret = lttng_triggers_add(local_triggers, element->trigger);
+ if (ret < 0) {
+ cmd_result = LTTNG_ERR_FATAL;
+ ret = -1;
+ goto end;
+ }
+
+ /* Ownership is shared with the lttng_triggers object */
+ lttng_trigger_get(element->trigger);
+
+ i++;
+ }
+
+ /* Passing ownership up */
+ *triggers = local_triggers;
+ local_triggers = NULL;
+
+end:
+ rcu_read_unlock();
+ lttng_triggers_destroy(local_triggers);
+ *_cmd_result = cmd_result;
+ return ret;
+}
+
+static
+int handle_notification_thread_command_list_triggers(
+ struct notification_thread_handle *handle,
+ struct notification_thread_state *state,
+ uid_t uid,
+ gid_t gid,
+ struct lttng_triggers **triggers,
+ enum lttng_error_code *_cmd_result)
+{
+ int ret = 0, i = 0;
+ enum lttng_error_code cmd_result = LTTNG_OK;
+ struct cds_lfht_iter iter;
+ struct lttng_trigger_ht_element *trigger_ht_element;
+ struct lttng_triggers *local_triggers = NULL;
+ const struct lttng_credentials *creds;
+
+ long scb, sca;
+ unsigned long count;
+
+ rcu_read_lock();
+ cds_lfht_count_nodes(state->triggers_ht, &scb, &count, &sca);
+
+ /* TODO check downcasting */
+ local_triggers = lttng_triggers_create();
+ if (!local_triggers) {
+ cmd_result = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ cds_lfht_for_each_entry (state->triggers_ht, &iter,
+ trigger_ht_element, node) {
+ /* Only return the trigger for which the requestion client have
+ * access. For now the root user can only list its own
+ * triggers.
+ * TODO: root user behavior
+ */
+ creds = lttng_trigger_get_credentials(trigger_ht_element->trigger);
+ if ((uid != creds->uid) || (gid != creds->gid)) {
+ continue;
+ }
+
+ ret = lttng_triggers_add(local_triggers, trigger_ht_element->trigger);
+ if (ret < 0) {
+ ret = -1;
+ goto end;
+ }
+ /* Ownership is shared with the lttng_triggers object */
+ lttng_trigger_get(trigger_ht_element->trigger);
+
+ i++;
+ }
+
+ /* Passing ownership up */
+ *triggers = local_triggers;
+ local_triggers = NULL;
+
+end:
+ rcu_read_unlock();
+ lttng_triggers_destroy(local_triggers);
+ *_cmd_result = cmd_result;
+ return ret;
+}
+
+static
+int condition_is_supported(struct lttng_condition *condition)
+{
+ int ret;
+
+ switch (lttng_condition_get_type(condition)) {
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+ {
+ enum lttng_domain_type domain;
+
+ ret = lttng_condition_buffer_usage_get_domain_type(condition,
+ &domain);
+ if (ret) {
+ ret = -1;
+ goto end;
+ }
+
+ if (domain != LTTNG_DOMAIN_KERNEL) {
+ ret = 1;
+ goto end;
+ }
+
+ /*
+ * Older kernel tracers don't expose the API to monitor their
+ * buffers. Therefore, we reject triggers that require that
+ * mechanism to be available to be evaluated.
+ */
+ ret = kernel_supports_ring_buffer_snapshot_sample_positions();
+ break;
+ }
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ {
+ /* TODO:
+ * Check for kernel support.
+ * Check for ust support ??
+ */
+ ret = 1;
+ break;
+ }
+ default:
+ ret = 1;
+ }
+end:
+ return ret;
+}
+
+static
+int action_is_supported(struct lttng_action *action)
+{
+ int ret;
+
+ switch (lttng_action_get_type(action)) {
+ case LTTNG_ACTION_TYPE_NOTIFY:
+ case LTTNG_ACTION_TYPE_START_SESSION:
+ case LTTNG_ACTION_TYPE_STOP_SESSION:
+ case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+ case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+ {
+ /* TODO validate that this is true for kernel in regards to
+ * rotation and snapshot. Start stop is not a problem notify
+ * either.
+ */
+ /* For now all type of actions are supported */
+ ret = 1;
+ break;
+ }
+ case LTTNG_ACTION_TYPE_GROUP:
+ {
+ /* TODO: Iterate over all internal actions and validate that
+ * they are supported
+ */
+ ret = 1;
+ break;
+
+ }
+ default:
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/* Must be called with RCU read lock held. */
+static
+int bind_trigger_to_matching_session(struct lttng_trigger *trigger,
+ struct notification_thread_state *state)
+{
+ int ret = 0;
+ const struct lttng_condition *condition;
+ const char *session_name;
+ struct lttng_session_trigger_list *trigger_list;
+
+ 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 status;
/* Must be called with RCU read lock held. */
static
-int bind_trigger_to_matching_channels(const struct lttng_trigger *trigger,
+int bind_trigger_to_matching_channels(struct lttng_trigger *trigger,
struct notification_thread_state *state)
{
int ret = 0;
return ret;
}
-/*
- * FIXME A client's credentials are not checked when registering a trigger, nor
- * are they stored alongside with the trigger.
- *
- * The effects of this are benign since:
- * - The client will succeed in registering the trigger, as it is valid,
- * - The trigger will, internally, be bound to the channel/session,
- * - The notifications will not be sent since the client's credentials
- * are checked against the channel at that moment.
- *
- * If this function returns a non-zero value, it means something is
- * fundamentally broken and the whole subsystem/thread will be torn down.
- *
- * If a non-fatal error occurs, just set the cmd_result to the appropriate
- * error code.
- */
static
-int handle_notification_thread_command_register_trigger(
+bool trigger_name_taken(struct notification_thread_state *state, const char *name)
+{
+ struct cds_lfht_node *triggers_by_name_ht_node;
+ struct cds_lfht_iter iter;
+ /* TODO change hashing for trigger */
+ cds_lfht_lookup(state->triggers_by_name_ht,
+ hash_key_str(name, lttng_ht_seed),
+ match_str,
+ name,
+ &iter);
+ triggers_by_name_ht_node = cds_lfht_iter_get_node(&iter);
+ if (triggers_by_name_ht_node) {
+ return true;
+ } else {
+ return false;
+ }
+
+}
+
+static
+void generate_trigger_name(struct notification_thread_state *state, struct lttng_trigger *trigger, const char **name)
+{
+ /* Here the offset criteria guarantee an end. This will be a nice
+ * bikeshedding conversation. I would simply generate uuid and use them
+ * as trigger name.
+ */
+ bool taken = false;
+ do {
+ lttng_trigger_generate_name(trigger, state->trigger_id.name_offset);
+ /* TODO error checking */
+ lttng_trigger_get_name(trigger, name);
+ taken = trigger_name_taken(state, *name);
+ if (taken) {
+ state->trigger_id.name_offset++;
+ }
+ } while (taken || state->trigger_id.name_offset == UINT32_MAX);
+}
+
+static int action_notify_register_trigger(
struct notification_thread_state *state,
- struct lttng_trigger *trigger,
- enum lttng_error_code *cmd_result)
+ struct lttng_trigger *trigger)
{
+
int ret = 0;
struct lttng_condition *condition;
struct notification_client *client;
struct notification_client_list *client_list = NULL;
- struct lttng_trigger_ht_element *trigger_ht_element = NULL;
- struct notification_client_list_element *client_list_element, *tmp;
- struct cds_lfht_node *node;
struct cds_lfht_iter iter;
- bool free_trigger = true;
-
- rcu_read_lock();
+ struct notification_client_list_element *client_list_element, *tmp;
condition = lttng_trigger_get_condition(trigger);
assert(condition);
- ret = condition_is_supported(condition);
- if (ret < 0) {
- goto error;
- } else if (ret == 0) {
- *cmd_result = LTTNG_ERR_NOT_SUPPORTED;
- goto error;
- } else {
- /* Feature is supported, continue. */
- ret = 0;
- }
-
- trigger_ht_element = zmalloc(sizeof(*trigger_ht_element));
- if (!trigger_ht_element) {
- ret = -1;
- goto error;
- }
-
- /* Add trigger to the trigger_ht. */
- cds_lfht_node_init(&trigger_ht_element->node);
- trigger_ht_element->trigger = trigger;
-
- node = cds_lfht_add_unique(state->triggers_ht,
- lttng_condition_hash(condition),
- match_condition,
- condition,
- &trigger_ht_element->node);
- if (node != &trigger_ht_element->node) {
- /* Not a fatal error, simply report it to the client. */
- *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
- goto error_free_ht_element;
- }
-
- /*
- * Ownership of the trigger and of its wrapper was transfered to
- * the triggers_ht.
- */
- trigger_ht_element = NULL;
- free_trigger = false;
-
- /*
- * The rest only applies to triggers that have a "notify" action.
- * It is not skipped as this is the only action type currently
- * supported.
- */
- client_list = zmalloc(sizeof(*client_list));
+ client_list = notification_client_list_create(trigger);
if (!client_list) {
ret = -1;
- goto error_free_ht_element;
+ goto end;
}
- cds_lfht_node_init(&client_list->notification_trigger_ht_node);
- CDS_INIT_LIST_HEAD(&client_list->list);
- client_list->trigger = trigger;
/* Build a list of clients to which this new trigger applies. */
cds_lfht_for_each_entry(state->client_socket_ht, &iter, client,
client_list_element = zmalloc(sizeof(*client_list_element));
if (!client_list_element) {
ret = -1;
- goto error_free_client_list;
+ goto error_put_client_list;
}
CDS_INIT_LIST_HEAD(&client_list_element->node);
client_list_element->client = client;
cds_list_add(&client_list_element->node, &client_list->list);
}
- cds_lfht_add(state->notification_trigger_clients_ht,
- lttng_condition_hash(condition),
- &client_list->notification_trigger_ht_node);
-
switch (get_condition_binding_object(condition)) {
case LTTNG_OBJECT_TYPE_SESSION:
/* Add the trigger to the list if it matches a known session. */
ret = bind_trigger_to_matching_session(trigger, state);
if (ret) {
- goto error_free_client_list;
+ goto error_put_client_list;
}
break;
case LTTNG_OBJECT_TYPE_CHANNEL:
*/
ret = bind_trigger_to_matching_channels(trigger, state);
if (ret) {
- goto error_free_client_list;
+ goto error_put_client_list;
}
break;
case LTTNG_OBJECT_TYPE_NONE:
default:
ERR("[notification-thread] Unknown object type on which to bind a newly registered trigger was encountered");
ret = -1;
- goto error_free_client_list;
+ goto error_put_client_list;
}
/*
* current state. Otherwise, the next evaluation cycle may only see
* that the evaluations remain the same (true for samples n-1 and n) and
* the client will never know that the condition has been met.
+ *
+ * No need to lock the list here as it has not been published yet.
*/
cds_list_for_each_entry_safe(client_list_element, tmp,
&client_list->list, node) {
ret = evaluate_condition_for_client(trigger, condition,
client_list_element->client, state);
if (ret) {
- goto error_free_client_list;
+ goto error_put_client_list;
}
}
* Client list ownership transferred to the
* notification_trigger_clients_ht.
*/
+ publish_notification_client_list(state, client_list);
client_list = NULL;
+error_put_client_list:
+ notification_client_list_put(client_list);
+end:
+ return ret;
+}
- *cmd_result = LTTNG_OK;
-error_free_client_list:
- if (client_list) {
- cds_list_for_each_entry_safe(client_list_element, tmp,
- &client_list->list, node) {
- free(client_list_element);
+static bool action_is_notify(const struct lttng_action *action)
+{
+ /* TODO for action groups we need to iterate over all of them */
+ enum lttng_action_type type = lttng_action_get_type_const(action);
+ bool ret = false;
+ enum lttng_action_status status;
+ const struct lttng_action *tmp;
+ unsigned int i, count;
+
+ switch (type) {
+ case LTTNG_ACTION_TYPE_NOTIFY:
+ ret = true;
+ break;
+ case LTTNG_ACTION_TYPE_GROUP:
+ status = lttng_action_group_get_count(action, &count);
+ if (status != LTTNG_ACTION_STATUS_OK) {
+ assert(0);
+ }
+ for (i = 0; i < count; i++) {
+ tmp = lttng_action_group_get_at_index_const(action, i);
+ assert(tmp);
+ ret = action_is_notify(tmp);
+ if (ret) {
+ break;
+ }
}
- free(client_list);
+ break;
+ default:
+ ret = false;
+ break;
}
+
+ return ret;
+}
+
+/*
+ * TODO: REVIEW THIS COMMENT.
+ * FIXME A client's credentials are not checked when registering a trigger, nor
+ * are they stored alongside with the trigger.
+ *
+ * The effects of this are benign since:
+ * - The client will succeed in registering the trigger, as it is valid,
+ * - The trigger will, internally, be bound to the channel/session,
+ * - The notifications will not be sent since the client's credentials
+ * are checked against the channel at that moment.
+ *
+ * If this function returns a non-zero value, it means something is
+ * fundamentally broken and the whole subsystem/thread will be torn down.
+ *
+ * If a non-fatal error occurs, just set the cmd_result to the appropriate
+ * error code.
+ */
+static
+int handle_notification_thread_command_register_trigger(
+ struct notification_thread_state *state,
+ struct lttng_trigger *trigger,
+ enum lttng_error_code *cmd_result)
+{
+ int ret = 0;
+ int is_supported;
+ struct lttng_condition *condition;
+ struct lttng_action *action;
+ struct lttng_trigger_ht_element *trigger_ht_element = NULL;
+ struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element = NULL;
+ struct cds_lfht_node *node;
+ const char* trigger_name;
+ bool free_trigger = true;
+
+ assert(trigger->creds.set);
+
+ rcu_read_lock();
+
+ /* Set the trigger's key */
+ lttng_trigger_set_key(trigger, state->trigger_id.token_generator);
+
+ if (lttng_trigger_get_name(trigger, &trigger_name) ==
+ LTTNG_TRIGGER_STATUS_UNSET) {
+ generate_trigger_name(state, trigger, &trigger_name);
+ } else if (trigger_name_taken(state, trigger_name)) {
+ /* Not a fatal error */
+ *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+ ret = 0;
+ goto error;
+ }
+
+ condition = lttng_trigger_get_condition(trigger);
+ assert(condition);
+
+ action = lttng_trigger_get_action(trigger);
+ assert(action);
+
+ is_supported = condition_is_supported(condition);
+ if (is_supported < 0) {
+ goto error;
+ } else if (is_supported == 0) {
+ ret = 0;
+ *cmd_result = LTTNG_ERR_NOT_SUPPORTED;
+ goto error;
+ }
+
+ is_supported = action_is_supported(action);
+ if (is_supported < 0) {
+ goto error;
+ } else if (is_supported == 0) {
+ ret = 0;
+ *cmd_result = LTTNG_ERR_NOT_SUPPORTED;
+ goto error;
+ }
+
+ trigger_ht_element = zmalloc(sizeof(*trigger_ht_element));
+ if (!trigger_ht_element) {
+ ret = -1;
+ goto error;
+ }
+
+ /* Add trigger to the trigger_ht. */
+ cds_lfht_node_init(&trigger_ht_element->node);
+ cds_lfht_node_init(&trigger_ht_element->node_by_name);
+ trigger_ht_element->trigger = trigger;
+
+ node = cds_lfht_add_unique(state->triggers_ht,
+ lttng_condition_hash(condition),
+ match_trigger,
+ trigger,
+ &trigger_ht_element->node);
+ if (node != &trigger_ht_element->node) {
+ /* Not a fatal error, simply report it to the client. */
+ *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+ goto error_free_ht_element;
+ }
+
+ node = cds_lfht_add_unique(state->triggers_by_name_ht,
+ hash_key_str(trigger_name, lttng_ht_seed), match_str,
+ trigger_name, &trigger_ht_element->node_by_name);
+ if (node != &trigger_ht_element->node_by_name) {
+ /* This should never happen */
+ /* Not a fatal error, simply report it to the client. */
+ /* TODO remove from the trigger_ht */
+ *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+ goto error_free_ht_element;
+ }
+
+ if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+ trigger_tokens_ht_element = zmalloc(sizeof(*trigger_tokens_ht_element));
+ if (!trigger_tokens_ht_element) {
+ ret = -1;
+ goto error;
+ }
+
+ /* Add trigger token to the trigger_tokens_ht. */
+ cds_lfht_node_init(&trigger_tokens_ht_element->node);
+ trigger_tokens_ht_element->token = trigger->key.value;
+ trigger_tokens_ht_element->trigger = trigger;
+
+ node = cds_lfht_add_unique(state->trigger_tokens_ht,
+ hash_key_u64(&trigger_tokens_ht_element->token, lttng_ht_seed),
+ match_trigger_token,
+ &trigger_tokens_ht_element->token,
+ &trigger_tokens_ht_element->node);
+ if (node != &trigger_tokens_ht_element->node) {
+ /* TODO: THIS IS A FATAL ERROR... should never happen */
+ /* Not a fatal error, simply report it to the client. */
+ *cmd_result = LTTNG_ERR_TRIGGER_EXISTS;
+ goto error_free_ht_element;
+ }
+ }
+
+ /*
+ * Ownership of the trigger and of its wrapper was transfered to
+ * the triggers_ht. Same for token ht element if necessary.
+ */
+ trigger_tokens_ht_element = NULL;
+ trigger_ht_element = NULL;
+ free_trigger = false;
+
+ if (action_is_notify(action)) {
+ ret = action_notify_register_trigger(state, trigger);
+ if (ret < 0) {
+ /* TODO should cmd_result be set here? */
+ ret = -1;
+ goto error_free_ht_element;
+ }
+ }
+
+ /* Increment the trigger unique id generator */
+ state->trigger_id.token_generator++;
+ *cmd_result = LTTNG_OK;
+
error_free_ht_element:
free(trigger_ht_element);
+ free(trigger_tokens_ht_element);
error:
if (free_trigger) {
- struct lttng_action *action = lttng_trigger_get_action(trigger);
-
- lttng_condition_destroy(condition);
- lttng_action_destroy(action);
lttng_trigger_destroy(trigger);
}
rcu_read_unlock();
}
static
-void free_notification_client_list_rcu(struct rcu_head *node)
+void free_lttng_trigger_ht_element_rcu(struct rcu_head *node)
{
- free(caa_container_of(node, struct notification_client_list,
+ free(caa_container_of(node, struct lttng_trigger_ht_element,
rcu_node));
}
static
-void free_lttng_trigger_ht_element_rcu(struct rcu_head *node)
+void free_notification_trigger_tokens_ht_element_rcu(struct rcu_head *node)
{
- free(caa_container_of(node, struct lttng_trigger_ht_element,
+ free(caa_container_of(node, struct notification_trigger_tokens_ht_element,
rcu_node));
}
struct cds_lfht_node *triggers_ht_node;
struct lttng_channel_trigger_list *trigger_list;
struct notification_client_list *client_list;
- struct notification_client_list_element *client_list_element, *tmp;
struct lttng_trigger_ht_element *trigger_ht_element = NULL;
struct lttng_condition *condition = lttng_trigger_get_condition(
trigger);
- struct lttng_action *action;
+ struct lttng_action *action = lttng_trigger_get_action(trigger);
enum lttng_error_code cmd_reply;
rcu_read_lock();
+ /* TODO change hashing for trigger */
+ /* TODO Disabling for the root user is not complete, for now the root
+ * user cannot disable the trigger from another user.
+ */
cds_lfht_lookup(state->triggers_ht,
lttng_condition_hash(condition),
- match_condition,
- condition,
+ match_trigger,
+ trigger,
&iter);
triggers_ht_node = cds_lfht_iter_get_node(&iter);
if (!triggers_ht_node) {
cds_list_for_each_entry_safe(trigger_element, tmp,
&trigger_list->list, node) {
- const struct lttng_condition *current_condition =
- lttng_trigger_get_const_condition(
- trigger_element->trigger);
-
- assert(current_condition);
- if (!lttng_condition_is_equal(condition,
- current_condition)) {
+ if (!lttng_trigger_is_equal(trigger, trigger_element->trigger)) {
continue;
}
}
}
- /*
- * Remove and release the client list from
- * notification_trigger_clients_ht.
- */
- client_list = get_client_list_from_condition(state, condition);
- assert(client_list);
+ if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT) {
+ struct notification_trigger_tokens_ht_element *trigger_tokens_ht_element;
+ cds_lfht_for_each_entry(state->trigger_tokens_ht, &iter, trigger_tokens_ht_element,
+ node) {
+ if (!lttng_trigger_is_equal(trigger, trigger_tokens_ht_element->trigger)) {
+ continue;
+ }
+
+ /* TODO talk to all app and remove it */
+ DBG("[notification-thread] Removed trigger from tokens_ht");
+ cds_lfht_del(state->trigger_tokens_ht,
+ &trigger_tokens_ht_element->node);
+ call_rcu(&trigger_tokens_ht_element->rcu_node, free_notification_trigger_tokens_ht_element_rcu);
+
+ break;
+ }
+ }
- cds_list_for_each_entry_safe(client_list_element, tmp,
- &client_list->list, node) {
- free(client_list_element);
+ if (action_is_notify(action)) {
+ /*
+ * Remove and release the client list from
+ * notification_trigger_clients_ht.
+ */
+ client_list = get_client_list_from_condition(state, condition);
+ assert(client_list);
+
+ /* Put new reference and the hashtable's reference. */
+ notification_client_list_put(client_list);
+ notification_client_list_put(client_list);
+ client_list = NULL;
}
- cds_lfht_del(state->notification_trigger_clients_ht,
- &client_list->notification_trigger_ht_node);
- call_rcu(&client_list->rcu_node, free_notification_client_list_rcu);
/* Remove trigger from triggers_ht. */
trigger_ht_element = caa_container_of(triggers_ht_node,
struct lttng_trigger_ht_element, node);
+ cds_lfht_del(state->triggers_by_name_ht, &trigger_ht_element->node_by_name);
cds_lfht_del(state->triggers_ht, triggers_ht_node);
- condition = lttng_trigger_get_condition(trigger_ht_element->trigger);
- lttng_condition_destroy(condition);
- action = lttng_trigger_get_action(trigger_ht_element->trigger);
- lttng_action_destroy(action);
+ /* Release the ownership of the trigger */
lttng_trigger_destroy(trigger_ht_element->trigger);
call_rcu(&trigger_ht_element->rcu_node, free_lttng_trigger_ht_element_rcu);
end:
pthread_mutex_lock(&handle->cmd_queue.lock);
cmd = cds_list_first_entry(&handle->cmd_queue.list,
struct notification_thread_command, cmd_list_node);
+ cds_list_del(&cmd->cmd_list_node);
+ pthread_mutex_unlock(&handle->cmd_queue.lock);
switch (cmd->type) {
case NOTIFICATION_COMMAND_TYPE_REGISTER_TRIGGER:
DBG("[notification-thread] Received register trigger command");
cmd->parameters.session_rotation.location,
&cmd->reply_code);
break;
+ case NOTIFICATION_COMMAND_TYPE_ADD_APPLICATION:
+ ret = handle_notification_thread_command_add_application(
+ handle,
+ state,
+ cmd->parameters.application.read_side_trigger_event_application_pipe,
+ &cmd->reply_code);
+ break;
+ case NOTIFICATION_COMMAND_TYPE_REMOVE_APPLICATION:
+ ret = handle_notification_thread_command_remove_application(
+ handle,
+ state,
+ cmd->parameters.application.read_side_trigger_event_application_pipe,
+ &cmd->reply_code);
+ break;
+ case NOTIFICATION_COMMAND_TYPE_GET_TOKENS:
+ {
+ struct lttng_triggers *triggers = NULL;
+ ret = handle_notification_thread_command_get_tokens(
+ handle, state, &triggers, &cmd->reply_code);
+ cmd->reply.get_tokens.triggers = triggers;
+ ret = 0;
+ break;
+ }
+ case NOTIFICATION_COMMAND_TYPE_LIST_TRIGGERS:
+ {
+ struct lttng_triggers *triggers = NULL;
+ ret = handle_notification_thread_command_list_triggers(
+ handle,
+ state,
+ cmd->parameters.list_triggers.uid,
+ cmd->parameters.list_triggers.gid,
+ &triggers,
+ &cmd->reply_code);
+ cmd->reply.list_triggers.triggers = triggers;
+ ret = 0;
+ break;
+ }
case NOTIFICATION_COMMAND_TYPE_QUIT:
DBG("[notification-thread] Received quit command");
cmd->reply_code = LTTNG_OK;
ret = 1;
goto end;
+ case NOTIFICATION_COMMAND_TYPE_CLIENT_COMMUNICATION_UPDATE:
+ {
+ const enum client_transmission_status client_status =
+ cmd->parameters.client_communication_update
+ .status;
+ const notification_client_id client_id =
+ cmd->parameters.client_communication_update.id;
+ struct notification_client *client;
+
+ rcu_read_lock();
+ client = get_client_from_id(client_id, state);
+
+ if (!client) {
+ /*
+ * Client error was probably already picked-up by the
+ * notification thread or it has disconnected
+ * gracefully while this command was queued.
+ */
+ DBG("Failed to find notification client to update communication status, client id = %" PRIu64,
+ client_id);
+ ret = 0;
+ } else {
+ pthread_mutex_lock(&client->lock);
+ ret = client_handle_transmission_status(
+ client, client_status, state);
+ pthread_mutex_unlock(&client->lock);
+ }
+ rcu_read_unlock();
+ break;
+ }
default:
ERR("[notification-thread] Unknown internal command received");
goto error_unlock;
goto error_unlock;
}
end:
- cds_list_del(&cmd->cmd_list_node);
- lttng_waiter_wake_up(&cmd->reply_waiter);
- pthread_mutex_unlock(&handle->cmd_queue.lock);
+ if (cmd->is_async) {
+ free(cmd);
+ cmd = NULL;
+ } else {
+ lttng_waiter_wake_up(&cmd->reply_waiter);
+ }
return ret;
error_unlock:
/* Wake-up and return a fatal error to the calling thread. */
lttng_waiter_wake_up(&cmd->reply_waiter);
- pthread_mutex_unlock(&handle->cmd_queue.lock);
cmd->reply_code = LTTNG_ERR_FATAL;
error:
/* Indicate a fatal error to the caller. */
return -1;
}
-static
-unsigned long hash_client_socket(int socket)
-{
- return hash_key_ulong((void *) (unsigned long) socket, lttng_ht_seed);
-}
-
static
int socket_set_non_blocking(int socket)
{
return ret;
}
+/* Client lock must be acquired by caller. */
static
int client_reset_inbound_state(struct notification_client *client)
{
int ret;
+ ASSERT_LOCKED(client->lock);
+
ret = lttng_dynamic_buffer_set_size(
&client->communication.inbound.buffer, 0);
assert(!ret);
ret = -1;
goto error;
}
+ pthread_mutex_init(&client->lock, NULL);
+ client->id = state->next_notification_client_id++;
CDS_INIT_LIST_HEAD(&client->condition_list);
lttng_dynamic_buffer_init(&client->communication.inbound.buffer);
lttng_dynamic_buffer_init(&client->communication.outbound.buffer);
client->communication.inbound.expect_creds = true;
+
+ pthread_mutex_lock(&client->lock);
ret = client_reset_inbound_state(client);
+ pthread_mutex_unlock(&client->lock);
if (ret) {
ERR("[notification-thread] Failed to reset client communication's inbound state");
ret = 0;
cds_lfht_add(state->client_socket_ht,
hash_client_socket(client->socket),
&client->client_socket_ht_node);
+ cds_lfht_add(state->client_id_ht,
+ hash_client_id(client->id),
+ &client->client_id_ht_node);
rcu_read_unlock();
return ret;
return ret;
}
-int handle_notification_thread_client_disconnect(
- int client_socket,
+/* RCU read-lock must be held by the caller. */
+/* Client lock must be held by the caller */
+static
+int notification_thread_client_disconnect(
+ struct notification_client *client,
struct notification_thread_state *state)
+{
+ int ret;
+ struct lttng_condition_list_element *condition_list_element, *tmp;
+
+ /* Acquire the client lock to disable its communication atomically. */
+ client->communication.active = false;
+ ret = lttng_poll_del(&state->events, client->socket);
+ if (ret) {
+ ERR("[notification-thread] Failed to remove client socket %d from poll set",
+ client->socket);
+ }
+
+ cds_lfht_del(state->client_socket_ht, &client->client_socket_ht_node);
+ cds_lfht_del(state->client_id_ht, &client->client_id_ht_node);
+
+ /* Release all conditions to which the client was subscribed. */
+ cds_list_for_each_entry_safe(condition_list_element, tmp,
+ &client->condition_list, node) {
+ (void) notification_thread_client_unsubscribe(client,
+ condition_list_element->condition, state, NULL);
+ }
+
+ /*
+ * Client no longer accessible to other threads (through the
+ * client lists).
+ */
+ notification_client_destroy(client, state);
+ return ret;
+}
+
+int handle_notification_thread_client_disconnect(
+ int client_socket, struct notification_thread_state *state)
{
int ret = 0;
struct notification_client *client;
goto end;
}
- ret = lttng_poll_del(&state->events, client_socket);
- if (ret) {
- ERR("[notification-thread] Failed to remove client socket from poll set");
- }
- cds_lfht_del(state->client_socket_ht,
- &client->client_socket_ht_node);
- notification_client_destroy(client, state);
+ pthread_mutex_lock(&client->lock);
+ ret = notification_thread_client_disconnect(client, state);
+ pthread_mutex_unlock(&client->lock);
end:
rcu_read_unlock();
return ret;
rcu_read_lock();
DBG("[notification-thread] Closing all client connections");
cds_lfht_for_each_entry(state->client_socket_ht, &iter, client,
- client_socket_ht_node) {
+ client_socket_ht_node) {
int ret;
- ret = handle_notification_thread_client_disconnect(
- client->socket, state);
+ pthread_mutex_lock(&client->lock);
+ ret = notification_thread_client_disconnect(
+ client, state);
+ pthread_mutex_unlock(&client->lock);
if (ret) {
error_encoutered = true;
}
}
static
-int client_flush_outgoing_queue(struct notification_client *client,
+int client_handle_transmission_status(
+ struct notification_client *client,
+ enum client_transmission_status transmission_status,
struct notification_thread_state *state)
+{
+ int ret = 0;
+
+ ASSERT_LOCKED(client->lock);
+
+ switch (transmission_status) {
+ case CLIENT_TRANSMISSION_STATUS_COMPLETE:
+ ret = lttng_poll_mod(&state->events, client->socket,
+ CLIENT_POLL_MASK_IN);
+ if (ret) {
+ goto end;
+ }
+
+ client->communication.outbound.queued_command_reply = false;
+ client->communication.outbound.dropped_notification = false;
+ break;
+ case CLIENT_TRANSMISSION_STATUS_QUEUED:
+ /*
+ * We want to be notified whenever there is buffer space
+ * available to send the rest of the payload.
+ */
+ ret = lttng_poll_mod(&state->events, client->socket,
+ CLIENT_POLL_MASK_IN_OUT);
+ if (ret) {
+ goto end;
+ }
+ break;
+ case CLIENT_TRANSMISSION_STATUS_FAIL:
+ ret = notification_thread_client_disconnect(client, state);
+ if (ret) {
+ goto end;
+ }
+ break;
+ case CLIENT_TRANSMISSION_STATUS_ERROR:
+ ret = -1;
+ goto end;
+ default:
+ abort();
+ }
+end:
+ return ret;
+}
+
+/* Client lock must be acquired by caller. */
+static
+enum client_transmission_status client_flush_outgoing_queue(
+ struct notification_client *client)
{
ssize_t ret;
size_t to_send_count;
+ enum client_transmission_status status;
+
+ ASSERT_LOCKED(client->lock);
+
+ if (!client->communication.active) {
+ status = CLIENT_TRANSMISSION_STATUS_FAIL;
+ goto end;
+ }
assert(client->communication.outbound.buffer.size != 0);
to_send_count = client->communication.outbound.buffer.size;
if (ret) {
goto error;
}
-
- /*
- * We want to be notified whenever there is buffer space
- * available to send the rest of the payload.
- */
- ret = lttng_poll_mod(&state->events, client->socket,
- CLIENT_POLL_MASK_IN_OUT);
- if (ret) {
- goto error;
- }
+ status = CLIENT_TRANSMISSION_STATUS_QUEUED;
} else if (ret < 0) {
/* Generic error, disconnect the client. */
- ERR("[notification-thread] Failed to send flush outgoing queue, disconnecting client (socket fd = %i)",
+ PERROR("[notification-thread] Failed to flush outgoing queue, disconnecting client (socket fd = %i)",
client->socket);
- ret = handle_notification_thread_client_disconnect(
- client->socket, state);
- if (ret) {
- goto error;
- }
+ status = CLIENT_TRANSMISSION_STATUS_FAIL;
} else {
/* No error and flushed the queue completely. */
ret = lttng_dynamic_buffer_set_size(
if (ret) {
goto error;
}
- ret = lttng_poll_mod(&state->events, client->socket,
- CLIENT_POLL_MASK_IN);
- if (ret) {
- goto error;
- }
-
- client->communication.outbound.queued_command_reply = false;
- client->communication.outbound.dropped_notification = false;
+ status = CLIENT_TRANSMISSION_STATUS_COMPLETE;
}
-
- return 0;
+end:
+ return status;
error:
- return -1;
+ return CLIENT_TRANSMISSION_STATUS_ERROR;
}
+/* Client lock must be acquired by caller. */
static
int client_send_command_reply(struct notification_client *client,
struct notification_thread_state *state,
.size = sizeof(reply),
};
char buffer[sizeof(msg) + sizeof(reply)];
+ enum client_transmission_status transmission_status;
+
+ ASSERT_LOCKED(client->lock);
+
+ if (client->communication.outbound.queued_command_reply) {
+ /* Protocol error. */
+ goto error;
+ }
+
+ memcpy(buffer, &msg, sizeof(msg));
+ memcpy(buffer + sizeof(msg), &reply, sizeof(reply));
+ DBG("[notification-thread] Send command reply (%i)", (int) status);
+
+ /* Enqueue buffer to outgoing queue and flush it. */
+ ret = lttng_dynamic_buffer_append(
+ &client->communication.outbound.buffer,
+ buffer, sizeof(buffer));
+ if (ret) {
+ goto error;
+ }
+
+ transmission_status = client_flush_outgoing_queue(client);
+ ret = client_handle_transmission_status(
+ client, transmission_status, state);
+ if (ret) {
+ goto error;
+ }
+
+ if (client->communication.outbound.buffer.size != 0) {
+ /* Queue could not be emptied. */
+ client->communication.outbound.queued_command_reply = true;
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+static
+int client_handle_message_unknown(struct notification_client *client,
+ struct notification_thread_state *state)
+{
+ int ret;
+
+ pthread_mutex_lock(&client->lock);
+
+ /*
+ * Receiving message header. The function will be called again
+ * once the rest of the message as been received and can be
+ * interpreted.
+ */
+ const struct lttng_notification_channel_message *msg;
+
+ assert(sizeof(*msg) == client->communication.inbound.buffer.size);
+ msg = (const struct lttng_notification_channel_message *)
+ client->communication.inbound.buffer.data;
+
+ if (msg->size == 0 ||
+ msg->size > DEFAULT_MAX_NOTIFICATION_CLIENT_MESSAGE_PAYLOAD_SIZE) {
+ ERR("[notification-thread] Invalid notification channel message: length = %u",
+ msg->size);
+ ret = -1;
+ goto end;
+ }
+
+ switch (msg->type) {
+ case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE:
+ case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE:
+ case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE:
+ break;
+ default:
+ ret = -1;
+ ERR("[notification-thread] Invalid notification channel message: unexpected message type");
+ goto end;
+ }
+
+ client->communication.inbound.bytes_to_receive = msg->size;
+ client->communication.inbound.msg_type =
+ (enum lttng_notification_channel_message_type) msg->type;
+ ret = lttng_dynamic_buffer_set_size(
+ &client->communication.inbound.buffer, msg->size);
+end:
+ pthread_mutex_unlock(&client->lock);
+ return ret;
+}
+
+static
+int client_handle_message_handshake(struct notification_client *client,
+ struct notification_thread_state *state)
+{
+ int ret;
+ struct lttng_notification_channel_command_handshake *handshake_client;
+ const struct lttng_notification_channel_command_handshake handshake_reply = {
+ .major = LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR,
+ .minor = LTTNG_NOTIFICATION_CHANNEL_VERSION_MINOR,
+ };
+ const struct lttng_notification_channel_message msg_header = {
+ .type = LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE,
+ .size = sizeof(handshake_reply),
+ };
+ enum lttng_notification_channel_status status =
+ LTTNG_NOTIFICATION_CHANNEL_STATUS_OK;
+ char send_buffer[sizeof(msg_header) + sizeof(handshake_reply)];
+ enum client_transmission_status transmission_status;
+
+ pthread_mutex_lock(&client->lock);
+
+ memcpy(send_buffer, &msg_header, sizeof(msg_header));
+ memcpy(send_buffer + sizeof(msg_header), &handshake_reply,
+ sizeof(handshake_reply));
+
+ handshake_client =
+ (struct lttng_notification_channel_command_handshake *)
+ client->communication.inbound.buffer
+ .data;
+ client->major = handshake_client->major;
+ client->minor = handshake_client->minor;
+ if (!client->communication.inbound.creds_received) {
+ ERR("[notification-thread] No credentials received from client");
+ ret = -1;
+ goto end;
+ }
+
+ client->uid = LTTNG_SOCK_GET_UID_CRED(
+ &client->communication.inbound.creds);
+ client->gid = LTTNG_SOCK_GET_GID_CRED(
+ &client->communication.inbound.creds);
+ DBG("[notification-thread] Received handshake from client (uid = %u, gid = %u) with version %i.%i",
+ client->uid, client->gid, (int) client->major,
+ (int) client->minor);
+
+ if (handshake_client->major !=
+ LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR) {
+ status = LTTNG_NOTIFICATION_CHANNEL_STATUS_UNSUPPORTED_VERSION;
+ }
+
+ ret = lttng_dynamic_buffer_append(
+ &client->communication.outbound.buffer, send_buffer,
+ sizeof(send_buffer));
+ if (ret) {
+ ERR("[notification-thread] Failed to send protocol version to notification channel client");
+ goto end;
+ }
+
+ client->validated = true;
+ client->communication.active = true;
+
+ transmission_status = client_flush_outgoing_queue(client);
+ ret = client_handle_transmission_status(
+ client, transmission_status, state);
+ if (ret) {
+ goto end;
+ }
+
+ ret = client_send_command_reply(client, state, status);
+ if (ret) {
+ ERR("[notification-thread] Failed to send reply to notification channel client");
+ goto end;
+ }
+
+ /* Set reception state to receive the next message header. */
+ ret = client_reset_inbound_state(client);
+ if (ret) {
+ ERR("[notification-thread] Failed to reset client communication's inbound state");
+ goto end;
+ }
+
+end:
+ pthread_mutex_unlock(&client->lock);
+ return ret;
+}
+
+static
+int client_handle_message_subscription(
+ struct notification_client *client,
+ enum lttng_notification_channel_message_type msg_type,
+ struct notification_thread_state *state)
+{
+ int ret;
+ struct lttng_condition *condition;
+ enum lttng_notification_channel_status status =
+ LTTNG_NOTIFICATION_CHANNEL_STATUS_OK;
+ const struct lttng_buffer_view condition_view =
+ lttng_buffer_view_from_dynamic_buffer(
+ &client->communication.inbound.buffer,
+ 0, -1);
+ size_t expected_condition_size;
- if (client->communication.outbound.queued_command_reply) {
- /* Protocol error. */
- goto error;
- }
+ pthread_mutex_lock(&client->lock);
+ expected_condition_size = client->communication.inbound.buffer.size;
+ pthread_mutex_unlock(&client->lock);
- memcpy(buffer, &msg, sizeof(msg));
- memcpy(buffer + sizeof(msg), &reply, sizeof(reply));
- DBG("[notification-thread] Send command reply (%i)", (int) status);
+ ret = lttng_condition_create_from_buffer(&condition_view, &condition);
+ if (ret != expected_condition_size) {
+ ERR("[notification-thread] Malformed condition received from client");
+ goto end;
+ }
- /* Enqueue buffer to outgoing queue and flush it. */
- ret = lttng_dynamic_buffer_append(
- &client->communication.outbound.buffer,
- buffer, sizeof(buffer));
+ if (msg_type == LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE) {
+ ret = notification_thread_client_subscribe(
+ client, condition, state, &status);
+ } else {
+ ret = notification_thread_client_unsubscribe(
+ client, condition, state, &status);
+ }
if (ret) {
- goto error;
+ goto end;
}
- ret = client_flush_outgoing_queue(client, state);
+ pthread_mutex_lock(&client->lock);
+ ret = client_send_command_reply(client, state, status);
if (ret) {
- goto error;
+ ERR("[notification-thread] Failed to send reply to notification channel client");
+ goto end_unlock;
}
- if (client->communication.outbound.buffer.size != 0) {
- /* Queue could not be emptied. */
- client->communication.outbound.queued_command_reply = true;
+ /* Set reception state to receive the next message header. */
+ ret = client_reset_inbound_state(client);
+ if (ret) {
+ ERR("[notification-thread] Failed to reset client communication's inbound state");
+ goto end_unlock;
}
- return 0;
-error:
- return -1;
+end_unlock:
+ pthread_mutex_unlock(&client->lock);
+end:
+ return ret;
}
static
switch (client->communication.inbound.msg_type) {
case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNKNOWN:
{
- /*
- * Receiving message header. The function will be called again
- * once the rest of the message as been received and can be
- * interpreted.
- */
- const struct lttng_notification_channel_message *msg;
-
- assert(sizeof(*msg) ==
- client->communication.inbound.buffer.size);
- msg = (const struct lttng_notification_channel_message *)
- client->communication.inbound.buffer.data;
-
- if (msg->size == 0 || msg->size > DEFAULT_MAX_NOTIFICATION_CLIENT_MESSAGE_PAYLOAD_SIZE) {
- ERR("[notification-thread] Invalid notification channel message: length = %u", msg->size);
- ret = -1;
- goto end;
- }
-
- switch (msg->type) {
- case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE:
- case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE:
- case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE:
- break;
- default:
- ret = -1;
- ERR("[notification-thread] Invalid notification channel message: unexpected message type");
- goto end;
- }
-
- client->communication.inbound.bytes_to_receive = msg->size;
- client->communication.inbound.msg_type =
- (enum lttng_notification_channel_message_type) msg->type;
- ret = lttng_dynamic_buffer_set_size(
- &client->communication.inbound.buffer, msg->size);
- if (ret) {
- goto end;
- }
+ ret = client_handle_message_unknown(client, state);
break;
}
case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE:
{
- struct lttng_notification_channel_command_handshake *handshake_client;
- struct lttng_notification_channel_command_handshake handshake_reply = {
- .major = LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR,
- .minor = LTTNG_NOTIFICATION_CHANNEL_VERSION_MINOR,
- };
- struct lttng_notification_channel_message msg_header = {
- .type = LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE,
- .size = sizeof(handshake_reply),
- };
- enum lttng_notification_channel_status status =
- LTTNG_NOTIFICATION_CHANNEL_STATUS_OK;
- char send_buffer[sizeof(msg_header) + sizeof(handshake_reply)];
-
- memcpy(send_buffer, &msg_header, sizeof(msg_header));
- memcpy(send_buffer + sizeof(msg_header), &handshake_reply,
- sizeof(handshake_reply));
-
- handshake_client =
- (struct lttng_notification_channel_command_handshake *)
- client->communication.inbound.buffer.data;
- client->major = handshake_client->major;
- client->minor = handshake_client->minor;
- if (!client->communication.inbound.creds_received) {
- ERR("[notification-thread] No credentials received from client");
- ret = -1;
- goto end;
- }
-
- client->uid = LTTNG_SOCK_GET_UID_CRED(
- &client->communication.inbound.creds);
- client->gid = LTTNG_SOCK_GET_GID_CRED(
- &client->communication.inbound.creds);
- DBG("[notification-thread] Received handshake from client (uid = %u, gid = %u) with version %i.%i",
- client->uid, client->gid, (int) client->major,
- (int) client->minor);
-
- if (handshake_client->major != LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR) {
- status = LTTNG_NOTIFICATION_CHANNEL_STATUS_UNSUPPORTED_VERSION;
- }
-
- ret = lttng_dynamic_buffer_append(&client->communication.outbound.buffer,
- send_buffer, sizeof(send_buffer));
- if (ret) {
- ERR("[notification-thread] Failed to send protocol version to notification channel client");
- goto end;
- }
-
- ret = client_flush_outgoing_queue(client, state);
- if (ret) {
- goto end;
- }
-
- ret = client_send_command_reply(client, state, status);
- if (ret) {
- ERR("[notification-thread] Failed to send reply to notification channel client");
- goto end;
- }
-
- /* Set reception state to receive the next message header. */
- ret = client_reset_inbound_state(client);
- if (ret) {
- ERR("[notification-thread] Failed to reset client communication's inbound state");
- goto end;
- }
- client->validated = true;
+ ret = client_handle_message_handshake(client, state);
break;
}
case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE:
case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE:
{
- struct lttng_condition *condition;
- enum lttng_notification_channel_status status =
- LTTNG_NOTIFICATION_CHANNEL_STATUS_OK;
- const struct lttng_buffer_view condition_view =
- lttng_buffer_view_from_dynamic_buffer(
- &client->communication.inbound.buffer,
- 0, -1);
- size_t expected_condition_size =
- client->communication.inbound.buffer.size;
-
- ret = lttng_condition_create_from_buffer(&condition_view,
- &condition);
- if (ret != expected_condition_size) {
- ERR("[notification-thread] Malformed condition received from client");
- goto end;
- }
-
- if (client->communication.inbound.msg_type ==
- LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE) {
- ret = notification_thread_client_subscribe(client,
- condition, state, &status);
- } else {
- ret = notification_thread_client_unsubscribe(client,
- condition, state, &status);
- }
- if (ret) {
- goto end;
- }
-
- ret = client_send_command_reply(client, state, status);
- if (ret) {
- ERR("[notification-thread] Failed to send reply to notification channel client");
- goto end;
- }
-
- /* Set reception state to receive the next message header. */
- ret = client_reset_inbound_state(client);
- if (ret) {
- ERR("[notification-thread] Failed to reset client communication's inbound state");
- goto end;
- }
+ ret = client_handle_message_subscription(client,
+ client->communication.inbound.msg_type, state);
break;
}
default:
struct notification_client *client;
ssize_t recv_ret;
size_t offset;
+ bool message_is_complete = false;
client = get_client_from_socket(socket, state);
if (!client) {
goto end;
}
+ pthread_mutex_lock(&client->lock);
offset = client->communication.inbound.buffer.size -
client->communication.inbound.bytes_to_receive;
if (client->communication.inbound.expect_creds) {
client->communication.inbound.buffer.data + offset,
client->communication.inbound.bytes_to_receive);
}
+ if (recv_ret >= 0) {
+ client->communication.inbound.bytes_to_receive -= recv_ret;
+ message_is_complete = client->communication.inbound
+ .bytes_to_receive == 0;
+ }
+ pthread_mutex_unlock(&client->lock);
if (recv_ret < 0) {
goto error_disconnect_client;
}
- client->communication.inbound.bytes_to_receive -= recv_ret;
- if (client->communication.inbound.bytes_to_receive == 0) {
+ if (message_is_complete) {
ret = client_dispatch_message(client, state);
if (ret) {
/*
*/
goto error_disconnect_client;
}
- } else {
- goto end;
}
end:
return ret;
error_disconnect_client:
- ret = handle_notification_thread_client_disconnect(socket, state);
+ pthread_mutex_lock(&client->lock);
+ ret = notification_thread_client_disconnect(client, state);
+ pthread_mutex_unlock(&client->lock);
return ret;
}
{
int ret;
struct notification_client *client;
+ enum client_transmission_status transmission_status;
client = get_client_from_socket(socket, state);
if (!client) {
goto end;
}
- ret = client_flush_outgoing_queue(client, state);
+ pthread_mutex_lock(&client->lock);
+ transmission_status = client_flush_outgoing_queue(client);
+ ret = client_handle_transmission_status(
+ client, transmission_status, state);
+ pthread_mutex_unlock(&client->lock);
if (ret) {
goto end;
}
}
static
-int client_enqueue_dropped_notification(struct notification_client *client,
- struct notification_thread_state *state)
+int client_notification_overflow(struct notification_client *client)
{
- int ret;
- struct lttng_notification_channel_message msg = {
+ int ret = 0;
+ const struct lttng_notification_channel_message msg = {
.type = (int8_t) LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION_DROPPED,
- .size = 0,
};
+ ASSERT_LOCKED(client->lock);
+
+ DBG("Dropping notification addressed to client (socket fd = %i)",
+ client->socket);
+ if (client->communication.outbound.dropped_notification) {
+ /*
+ * The client already has a "notification dropped" message
+ * in its outgoing queue. Nothing to do since all
+ * of those messages are coalesced.
+ */
+ goto end;
+ }
+
+ client->communication.outbound.dropped_notification = true;
ret = lttng_dynamic_buffer_append(
&client->communication.outbound.buffer, &msg,
sizeof(msg));
+ if (ret) {
+ PERROR("Failed to enqueue \"dropped notification\" message in client's (socket fd = %i) outgoing queue",
+ client->socket);
+ }
+end:
return ret;
}
+static int client_handle_transmission_status_wrapper(
+ struct notification_client *client,
+ enum client_transmission_status status,
+ void *user_data)
+{
+ return client_handle_transmission_status(client, status,
+ (struct notification_thread_state *) user_data);
+}
+
static
int send_evaluation_to_clients(const struct lttng_trigger *trigger,
const struct lttng_evaluation *evaluation,
struct notification_client_list* client_list,
struct notification_thread_state *state,
- uid_t channel_uid, gid_t channel_gid)
+ uid_t object_uid, gid_t object_gid)
+{
+ return notification_client_list_send_evaluation(client_list,
+ lttng_trigger_get_const_condition(trigger), evaluation,
+ lttng_trigger_get_credentials(trigger),
+ &(struct lttng_credentials){
+ .uid = object_uid, .gid = object_gid},
+ client_handle_transmission_status_wrapper, state);
+}
+
+LTTNG_HIDDEN
+int notification_client_list_send_evaluation(
+ struct notification_client_list *client_list,
+ const struct lttng_condition *condition,
+ const struct lttng_evaluation *evaluation,
+ const struct lttng_credentials *trigger_creds,
+ const struct lttng_credentials *source_object_creds,
+ report_client_transmission_result_cb client_report,
+ void *user_data)
{
int ret = 0;
struct lttng_dynamic_buffer msg_buffer;
struct notification_client_list_element *client_list_element, *tmp;
const struct lttng_notification notification = {
- .condition = (struct lttng_condition *) lttng_trigger_get_const_condition(trigger),
+ .condition = (struct lttng_condition *) condition,
.evaluation = (struct lttng_evaluation *) evaluation,
};
struct lttng_notification_channel_message msg_header = {
((struct lttng_notification_channel_message * ) msg_buffer.data)->size =
(uint32_t) (msg_buffer.size - sizeof(msg_header));
+ pthread_mutex_lock(&client_list->lock);
cds_list_for_each_entry_safe(client_list_element, tmp,
&client_list->list, node) {
+ enum client_transmission_status transmission_status;
struct notification_client *client =
client_list_element->client;
- if (client->uid != channel_uid && client->gid != channel_gid &&
- client->uid != 0) {
- /* Client is not allowed to monitor this channel. */
- DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this channel");
- continue;
+ ret = 0;
+ pthread_mutex_lock(&client->lock);
+ if (source_object_creds) {
+ if (client->uid != source_object_creds->uid &&
+ client->gid != source_object_creds->gid &&
+ client->uid != 0) {
+ /*
+ * Client is not allowed to monitor this
+ * object.
+ */
+ DBG("[notification-thread] Skipping client at it does not have the object permission to receive notification for this trigger");
+ goto unlock_client;
+ }
+ }
+
+ /* TODO: what is the behavior for root client on non root
+ * trigger? Since multiple triggers (different user) can have the same condition
+ * but with different action group that can have each a notify.
+ * Does the root client receive multiple notification for all
+ * those triggers with the same condition or only notification
+ * for triggers the root user configured?
+ * For now we do the later. All users including the root user
+ * can only receive notification from trigger it registered.
+ */
+ if (client->uid != trigger_creds->uid && client->gid != trigger_creds->gid) {
+ DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this trigger");
+ goto unlock_client;
}
DBG("[notification-thread] Sending notification to client (fd = %i, %zu bytes)",
* notification since the socket spilled-over to the
* queue.
*/
- DBG("[notification-thread] Dropping notification addressed to client (socket fd = %i)",
- client->socket);
- if (!client->communication.outbound.dropped_notification) {
- client->communication.outbound.dropped_notification = true;
- ret = client_enqueue_dropped_notification(
- client, state);
- if (ret) {
- goto end;
- }
+ ret = client_notification_overflow(client);
+ if (ret) {
+ goto unlock_client;
}
- continue;
}
ret = lttng_dynamic_buffer_append_buffer(
&client->communication.outbound.buffer,
&msg_buffer);
if (ret) {
- goto end;
+ goto unlock_client;
}
- ret = client_flush_outgoing_queue(client, state);
+ transmission_status = client_flush_outgoing_queue(client);
+ ret = client_report(client, transmission_status, user_data);
if (ret) {
- goto end;
+ goto unlock_client;
+ }
+unlock_client:
+ pthread_mutex_unlock(&client->lock);
+ if (ret) {
+ goto end_unlock_list;
}
}
ret = 0;
+
+end_unlock_list:
+ pthread_mutex_unlock(&client_list->lock);
end:
lttng_dynamic_buffer_reset(&msg_buffer);
return ret;
}
+static struct lttng_trigger_notification *receive_notification(int pipe,
+ enum lttng_domain_type domain)
+{
+ int ret;
+ uint64_t id;
+ struct lttng_trigger_notification *notification = NULL;
+ char *capture_buffer = NULL;
+ size_t capture_buffer_size;
+ void *reception_buffer;
+ size_t reception_size;
+
+ struct lttng_ust_trigger_notification ust_notification;
+ struct lttng_kernel_trigger_notification kernel_notification;
+
+ /* Init lttng_trigger_notification */
+
+ switch(domain) {
+ case LTTNG_DOMAIN_UST:
+ reception_buffer = (void *) &ust_notification;
+ reception_size = sizeof(ust_notification);
+ break;
+ case LTTNG_DOMAIN_KERNEL:
+ reception_buffer = (void *) &kernel_notification;
+ reception_size = sizeof(kernel_notification);
+ break;
+ default:
+ assert(0);
+ }
+
+ /*
+ * The monitoring pipe only holds messages smaller than PIPE_BUF,
+ * ensuring that read/write of sampling messages are atomic.
+ */
+ /* TODO: should we read as much as we can ? EWOULDBLOCK? */
+
+ ret = lttng_read(pipe, reception_buffer, reception_size);
+ if (ret != reception_size) {
+ ERR("[notification-thread] Failed to read from event source pipe (fd = %i)",
+ pipe);
+ /* TODO: Should this error out completly.
+ * This can happen when an app is killed as of today
+ * ret = -1 cause the whole thread to die and fuck up
+ * everything.
+ */
+ goto end;
+ }
+
+ switch(domain) {
+ case LTTNG_DOMAIN_UST:
+ id = ust_notification.id;
+ capture_buffer_size =
+ ust_notification.capture_buf_size;
+ break;
+ case LTTNG_DOMAIN_KERNEL:
+ id = kernel_notification.id;
+ capture_buffer_size =
+ kernel_notification.capture_buf_size;
+ break;
+ default:
+ assert(0);
+ }
+
+ if (capture_buffer_size == 0) {
+ capture_buffer = NULL;
+ goto skip_capture;
+ }
+
+ capture_buffer = malloc(capture_buffer_size);
+ if (!capture_buffer) {
+ ERR("[notification-thread] Failed to allocate capture buffer");
+ goto end;
+ }
+
+ /*
+ * Fetch additional payload (capture).
+ */
+ ret = lttng_read(pipe, capture_buffer, capture_buffer_size);
+ if (ret != capture_buffer_size) {
+ ERR("[notification-thread] Failed to read from event source pipe (fd = %i)",
+ pipe);
+ /* TODO: Should this error out completly.
+ * This can happen when an app is killed as of today
+ * ret = -1 cause the whole thread to die and fuck up
+ * everything.
+ */
+ goto end;
+ }
+
+skip_capture:
+ notification = lttng_trigger_notification_create(
+ id, domain, capture_buffer, capture_buffer_size);
+ if (notification == NULL) {
+ goto end;
+ }
+
+ /* Ownership transfered to the lttng_trigger_notification object */
+ capture_buffer = NULL;
+
+end:
+ free(capture_buffer);
+ return notification;
+}
+
+int handle_notification_thread_event(struct notification_thread_state *state,
+ int pipe,
+ enum lttng_domain_type domain)
+{
+ int ret;
+ struct cds_lfht_node *node;
+ struct cds_lfht_iter iter;
+ struct notification_trigger_tokens_ht_element *element;
+ struct lttng_trigger_notification *notification = NULL;
+ enum action_executor_status executor_status;
+ struct notification_client_list *client_list = NULL;
+
+ notification = receive_notification(pipe, domain);
+ if (notification == NULL) {
+ ERR("[notification-thread] Error receiving notification from tracer "
+ "(fd = %i, domain = %s)",
+ pipe, lttng_domain_type_str(domain));
+ ret = -1;
+ goto end;
+ }
+
+ /* Find triggers associated with this token. */
+ rcu_read_lock();
+ cds_lfht_lookup(state->trigger_tokens_ht,
+ hash_key_u64(¬ification->id, lttng_ht_seed),
+ match_trigger_token, ¬ification->id, &iter);
+ node = cds_lfht_iter_get_node(&iter);
+ if (caa_unlikely(!node)) {
+ /* TODO: is this an error? This might happen if the receive side
+ * is slow to process event from source and that the trigger was
+ * removed but the app still kicking. This yield another
+ * question on the trigger lifetime and when we can remove a
+ * trigger. How to guarantee that all event with the token idea
+ * have be processed? Do we want to provide this guarantee?
+ *
+ * Update: I have encountered this when using a trigger on
+ * sched_switch and then removing it. The frequency is quite
+ * high hence we en up exactly in the mentionned scenario.
+ * AFAIK this might be the best way to handle this.
+ */
+ ret = 0;
+ goto end_unlock;
+ }
+ element = caa_container_of(node,
+ struct notification_trigger_tokens_ht_element,
+ node);
+
+ if (!lttng_trigger_is_ready_to_fire(element->trigger)) {
+ ret = 0;
+ goto end_unlock;
+ }
+
+ client_list = get_client_list_from_condition(state,
+ lttng_trigger_get_const_condition(element->trigger));
+ executor_status = action_executor_enqueue(state->executor,
+ element->trigger, client_list, notification);
+
+ /* Notification ownership passed to the executor. */
+ notification = NULL;
+
+ switch (executor_status) {
+ case ACTION_EXECUTOR_STATUS_OK:
+ ret = 0;
+ break;
+ case ACTION_EXECUTOR_STATUS_OVERFLOW:
+ {
+ struct notification_client_list_element *client_list_element,
+ *tmp;
+
+ /*
+ * Not a fatal error; this is expected and simply means the
+ * executor has too much work queued already.
+ */
+ ret = 0;
+
+ if (!client_list) {
+ break;
+ }
+
+ /* Warn clients that a notification (or more) was dropped. */
+ pthread_mutex_lock(&client_list->lock);
+ cds_list_for_each_entry_safe(client_list_element, tmp,
+ &client_list->list, node) {
+ enum client_transmission_status transmission_status;
+ struct notification_client *client =
+ client_list_element->client;
+
+ pthread_mutex_lock(&client->lock);
+ ret = client_notification_overflow(client);
+ if (ret) {
+ /* Fatal error. */
+ goto next_client;
+ }
+
+ transmission_status =
+ client_flush_outgoing_queue(client);
+ ret = client_handle_transmission_status(
+ client, transmission_status, state);
+ if (ret) {
+ /* Fatal error. */
+ goto next_client;
+ }
+next_client:
+ pthread_mutex_unlock(&client->lock);
+ if (ret) {
+ break;
+ }
+ }
+ pthread_mutex_lock(&client_list->lock);
+ break;
+ }
+ case ACTION_EXECUTOR_STATUS_ERROR:
+ /* Fatal error, shut down everything. */
+ ERR("Fatal error encoutered while enqueuing action");
+ ret = -1;
+ goto end_unlock;
+ default:
+ /* Unhandled error. */
+ abort();
+ }
+
+end_unlock:
+ lttng_trigger_notification_destroy(notification);
+ notification_client_list_put(client_list);
+ rcu_read_unlock();
+end:
+ return ret;
+}
+
int handle_notification_thread_channel_sample(
struct notification_thread_state *state, int pipe,
enum lttng_domain_type domain)
* channel's destruction before we get a chance to process that
* sample.
*/
- DBG("[notification-thread] Received a sample for an unknown channel from consumerd, key = %" PRIu64 " in %s domain",
+ DBG("[notification-thread] Received a sample for an unknown channel from consumerd, key = %" PRIu64
+ " in %s domain",
latest_sample.key.key,
- domain == LTTNG_DOMAIN_KERNEL ? "kernel" :
- "user space");
+ lttng_domain_type_str(domain));
goto end_unlock;
}
channel_info = caa_container_of(node, struct channel_info,
node) {
const struct lttng_condition *condition;
const struct lttng_action *action;
- const struct lttng_trigger *trigger;
- struct notification_client_list *client_list;
+ struct lttng_trigger *trigger;
+ struct notification_client_list *client_list = NULL;
struct lttng_evaluation *evaluation = NULL;
+ bool client_list_is_empty;
+ ret = 0;
trigger = trigger_list_element->trigger;
condition = lttng_trigger_get_const_condition(trigger);
assert(condition);
action = lttng_trigger_get_const_action(trigger);
+
+ if (!lttng_trigger_is_ready_to_fire(trigger)) {
+ goto put_list;
+ }
/* Notify actions are the only type currently supported. */
+ /* TODO support other type of action */
assert(lttng_action_get_type_const(action) ==
LTTNG_ACTION_TYPE_NOTIFY);
*/
client_list = get_client_list_from_condition(state, condition);
assert(client_list);
- if (cds_list_empty(&client_list->list)) {
+ client_list_is_empty = cds_list_empty(&client_list->list);
+ if (client_list_is_empty) {
/*
* No clients interested in the evaluation's result,
* skip it.
*/
- continue;
+ goto put_list;
}
ret = evaluate_buffer_condition(condition, &evaluation, state,
latest_session_consumed_total,
channel_info);
if (caa_unlikely(ret)) {
- goto end_unlock;
+ goto put_list;
}
if (caa_likely(!evaluation)) {
- continue;
+ goto put_list;
}
/* Dispatch evaluation result to all clients. */
channel_info->session_info->uid,
channel_info->session_info->gid);
lttng_evaluation_destroy(evaluation);
+put_list:
+ notification_client_list_put(client_list);
if (caa_unlikely(ret)) {
- goto end_unlock;
+ break;
}
}
end_unlock:
struct notification_thread_state *state, int pipe,
enum lttng_domain_type domain);
+int handle_notification_thread_event(
+ struct notification_thread_state *state, int pipe,
+ enum lttng_domain_type domain);
+
#endif /* NOTIFICATION_THREAD_EVENTS_H */
#ifndef NOTIFICATION_THREAD_INTERNAL_H
#define NOTIFICATION_THREAD_INTERNAL_H
+#include <common/credentials.h>
+#include <lttng/notification/channel-internal.h>
#include <lttng/ref-internal.h>
-#include <urcu/rculfhash.h>
+#include <stdbool.h>
#include <unistd.h>
+#include <urcu/rculfhash.h>
+#include <urcu/ref.h>
+#include <urcu/call-rcu.h>
+#include "notification-thread.h"
+
+struct lttng_evaluation;
+struct notification_thread_handle;
struct channel_key {
uint64_t key;
struct rcu_head rcu_node;
};
+/*
+ * Facilities to carry the different notifications type in the action
+ * processing code path.
+ */
+struct lttng_trigger_notification {
+ uint64_t id;
+ enum lttng_domain_type type;
+ size_t capture_buf_size;
+ char *capture_buffer;
+};
+
+struct notification_client_list_element {
+ struct notification_client *client;
+ struct cds_list_head node;
+};
+
+/*
+ * Thread safety of notification_client and notification_client_list.
+ *
+ * The notification thread (main thread) and the action executor
+ * interact through client lists. Hence, when the action executor
+ * thread looks-up the list of clients subscribed to a given
+ * condition, it will acquire a reference to the list and lock it
+ * while attempting to communicate with the various clients.
+ *
+ * It is not necessary to reference-count clients as they are guaranteed
+ * to be 'alive' if they are present in a list and that list is locked. Indeed,
+ * removing references to the client from those subscription lists is part of
+ * the work performed on destruction of a client.
+ *
+ * No provision for other access scenarios are taken into account;
+ * this is the bare minimum to make these accesses safe and the
+ * notification thread's state is _not_ "thread-safe" in any general
+ * sense.
+ */
+struct notification_client_list {
+ pthread_mutex_t lock;
+ struct urcu_ref ref;
+ const struct lttng_trigger *trigger;
+ struct cds_list_head list;
+ /* Weak reference to container. */
+ struct cds_lfht *notification_trigger_clients_ht;
+ struct cds_lfht_node notification_trigger_clients_ht_node;
+ /* call_rcu delayed reclaim. */
+ struct rcu_head rcu_node;
+};
+
+struct notification_client {
+ /* Nests within the notification_client_list lock. */
+ pthread_mutex_t lock;
+ notification_client_id id;
+ int socket;
+ /* Client protocol version. */
+ uint8_t major, minor;
+ uid_t uid;
+ gid_t gid;
+ /*
+ * Indicates if the credentials and versions of the client have been
+ * checked.
+ */
+ bool validated;
+ /*
+ * Conditions to which the client's notification channel is subscribed.
+ * List of struct lttng_condition_list_node. The condition member is
+ * owned by the client.
+ */
+ struct cds_list_head condition_list;
+ struct cds_lfht_node client_socket_ht_node;
+ struct cds_lfht_node client_id_ht_node;
+ struct {
+ /*
+ * If a client's communication is inactive, it means that a
+ * fatal error has occurred (could be either a protocol error or
+ * the socket API returned a fatal error). No further
+ * communication should be attempted; the client is queued for
+ * clean-up.
+ */
+ bool active;
+ struct {
+ /*
+ * During the reception of a message, the reception
+ * buffers' "size" is set to contain the current
+ * message's complete payload.
+ */
+ struct lttng_dynamic_buffer buffer;
+ /* Bytes left to receive for the current message. */
+ size_t bytes_to_receive;
+ /* Type of the message being received. */
+ enum lttng_notification_channel_message_type msg_type;
+ /*
+ * Indicates whether or not credentials are expected
+ * from the client.
+ */
+ bool expect_creds;
+ /*
+ * Indicates whether or not credentials were received
+ * from the client.
+ */
+ bool creds_received;
+ /* Only used during credentials reception. */
+ lttng_sock_cred creds;
+ } inbound;
+ struct {
+ /*
+ * Indicates whether or not a notification addressed to
+ * this client was dropped because a command reply was
+ * already buffered.
+ *
+ * A notification is dropped whenever the buffer is not
+ * empty.
+ */
+ bool dropped_notification;
+ /*
+ * Indicates whether or not a command reply is already
+ * buffered. In this case, it means that the client is
+ * not consuming command replies before emitting a new
+ * one. This could be caused by a protocol error or a
+ * misbehaving/malicious client.
+ */
+ bool queued_command_reply;
+ struct lttng_dynamic_buffer buffer;
+ } outbound;
+ } communication;
+ /* call_rcu delayed reclaim. */
+ struct rcu_head rcu_node;
+};
+
+enum client_transmission_status {
+ CLIENT_TRANSMISSION_STATUS_COMPLETE,
+ CLIENT_TRANSMISSION_STATUS_QUEUED,
+ /* Communication failure. */
+ CLIENT_TRANSMISSION_STATUS_FAIL,
+ /* Fatal error. */
+ CLIENT_TRANSMISSION_STATUS_ERROR,
+};
+
+LTTNG_HIDDEN
+bool notification_client_list_get(struct notification_client_list *list);
+
+LTTNG_HIDDEN
+void notification_client_list_put(struct notification_client_list *list);
+
+typedef int (*report_client_transmission_result_cb)(
+ struct notification_client *client,
+ enum client_transmission_status status,
+ void *user_data);
+
+LTTNG_HIDDEN
+int notification_client_list_send_evaluation(
+ struct notification_client_list *list,
+ const struct lttng_condition *condition,
+ const struct lttng_evaluation *evaluation,
+ const struct lttng_credentials *trigger_creds,
+ const struct lttng_credentials *source_object_creds,
+ report_client_transmission_result_cb client_report,
+ void *user_data);
+
+LTTNG_HIDDEN
+int notification_thread_client_communication_update(
+ struct notification_thread_handle *handle,
+ notification_client_id id,
+ enum client_transmission_status transmission_status);
+/*
+ * Takes ownership of the payload if present.
+ */
+LTTNG_HIDDEN
+struct lttng_trigger_notification *lttng_trigger_notification_create(
+ uint64_t id,
+ enum lttng_domain_type domain,
+ char *payload,
+ size_t payload_size);
+
+LTTNG_HIDDEN
+void lttng_trigger_notification_destroy(
+ struct lttng_trigger_notification *trigger_notification);
+
#endif /* NOTIFICATION_THREAD_INTERNAL_H */
#include "health-sessiond.h"
#include "thread.h"
+#include "kernel.h"
+#include <common/kernel-ctl/kernel-ctl.h>
+
#include <urcu.h>
#include <urcu/list.h>
#include <urcu/rculfhash.h>
PERROR("close kernel consumer channel monitoring pipe");
}
}
+
+ /* TODO: refactor this if needed. Lifetime of the kernel notification
+ * event source.
+ * event_trigger_sources.kernel_tracer is owned by the main thread and
+ * is closed at this point.
+ */
+ handle->event_trigger_sources.kernel_tracer = -1;
end:
free(handle);
}
+/*
+ * TODO: refactor this if needed. Lifetime of the kernel notification event source.
+ * The kernel_notification_monitor_fd ownwership remain to the main thread.
+ * This is because we need to close this fd before removing the modules.
+ */
struct notification_thread_handle *notification_thread_handle_create(
struct lttng_pipe *ust32_channel_monitor_pipe,
struct lttng_pipe *ust64_channel_monitor_pipe,
- struct lttng_pipe *kernel_channel_monitor_pipe)
+ struct lttng_pipe *kernel_channel_monitor_pipe,
+ int kernel_notification_monitor_fd)
{
int ret;
struct notification_thread_handle *handle;
goto end;
}
+ handle->event_trigger_sources.kernel_tracer = -1;
+
sem_init(&handle->ready, 0, 0);
event_pipe = lttng_pipe_open(FD_CLOEXEC);
} else {
handle->channel_monitoring_pipes.kernel_consumer = -1;
}
+
+ handle->event_trigger_sources.kernel_tracer = kernel_notification_monitor_fd;
+
+ CDS_INIT_LIST_HEAD(&handle->event_trigger_sources.list);
+ ret = pthread_mutex_init(&handle->event_trigger_sources.lock, NULL);
+ if (ret) {
+ goto error;
+ }
end:
return handle;
error:
int ret;
/*
- * Create pollset with size 5:
+ * Create pollset with size 6:
* - notification channel socket (listen for new connections),
* - command queue event fd (internal sessiond commands),
* - consumerd (32-bit user space) channel monitor pipe,
* - consumerd (64-bit user space) channel monitor pipe,
* - consumerd (kernel) channel monitor pipe.
+ * - kernel trigger event pipe,
*/
- ret = lttng_poll_create(poll_set, 5, LTTNG_CLOEXEC);
+ ret = lttng_poll_create(poll_set, 6, LTTNG_CLOEXEC);
if (ret < 0) {
goto end;
}
goto error;
}
if (handle->channel_monitoring_pipes.kernel_consumer < 0) {
- goto end;
+ goto skip_kernel_consumer;
}
ret = lttng_poll_add(poll_set,
handle->channel_monitoring_pipes.kernel_consumer,
ERR("[notification-thread] Failed to add kernel channel monitoring pipe fd to pollset");
goto error;
}
+
+skip_kernel_consumer:
+ if (handle->event_trigger_sources.kernel_tracer < 0) {
+ goto end;
+ }
+
+ ret = lttng_poll_add(poll_set,
+ handle->event_trigger_sources.kernel_tracer,
+ LPOLLIN | LPOLLERR);
+ if (ret < 0) {
+ ERR("[notification-thread] Failed to add kernel trigger notification monitoring pipe fd to pollset");
+ goto error;
+ }
end:
return ret;
error:
ret = cds_lfht_destroy(state->client_socket_ht, NULL);
assert(!ret);
}
+ if (state->client_id_ht) {
+ ret = cds_lfht_destroy(state->client_id_ht, NULL);
+ assert(!ret);
+ }
if (state->triggers_ht) {
ret = handle_notification_thread_trigger_unregister_all(state);
assert(!ret);
ret = cds_lfht_destroy(state->sessions_ht, NULL);
assert(!ret);
}
+ if (state->triggers_by_name_ht) {
+ ret = cds_lfht_destroy(state->triggers_by_name_ht, NULL);
+ assert(!ret);
+ }
+ if (state->trigger_tokens_ht) {
+ ret = cds_lfht_destroy(state->trigger_tokens_ht, NULL);
+ assert(!ret);
+ }
/*
* Must be destroyed after all channels have been destroyed.
* See comment in struct lttng_session_trigger_list.
notification_channel_socket_destroy(
state->notification_channel_socket);
}
+ if (state->executor) {
+ action_executor_destroy(state->executor);
+ }
lttng_poll_clean(&state->events);
}
memset(state, 0, sizeof(*state));
state->notification_channel_socket = -1;
+ state->trigger_id.token_generator = 1;
lttng_poll_init(&state->events);
ret = notification_channel_socket_create();
goto error;
}
+ state->client_id_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
+ CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+ if (!state->client_id_ht) {
+ goto error;
+ }
+
state->channel_triggers_ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
if (!state->channel_triggers_ht) {
if (!state->triggers_ht) {
goto error;
}
+ state->triggers_by_name_ht = cds_lfht_new(DEFAULT_HT_SIZE,
+ 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+ if (!state->triggers_by_name_ht) {
+ goto error;
+ }
+ state->trigger_tokens_ht = cds_lfht_new(DEFAULT_HT_SIZE,
+ 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+ if (!state->trigger_tokens_ht) {
+ goto error;
+ }
+ state->executor = action_executor_create(handle);
+ if (!state->executor) {
+ goto error;
+ }
mark_thread_as_ready(handle);
end:
return 0;
return ret;
}
+static int handle_trigger_event_pipe(int fd,
+ uint32_t revents,
+ struct notification_thread_handle *handle,
+ struct notification_thread_state *state)
+{
+ int ret = 0;
+ enum lttng_domain_type domain;
+
+ if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+ ret = lttng_poll_del(&state->events, fd);
+ if (ret) {
+ ERR("[notification-thread] Failed to remove event monitoring pipe from poll set");
+ }
+ goto end;
+ }
+
+ if (fd == handle->event_trigger_sources.kernel_tracer) {
+ domain = LTTNG_DOMAIN_KERNEL;
+ } else {
+ domain = LTTNG_DOMAIN_UST;
+ }
+
+ ret = handle_notification_thread_event(state, fd, domain);
+ if (ret) {
+ ERR("[notification-thread] Event sample handling error occurred for fd: %d", fd);
+ ret = -1;
+ goto end;
+ }
+end:
+ return ret;
+}
+
+static bool fd_is_event_source(struct notification_thread_handle *handle, int fd)
+{
+ struct notification_event_trigger_source_element *source_element, *tmp;
+ cds_list_for_each_entry_safe(source_element, tmp,
+ &handle->event_trigger_sources.list, node) {
+ if (source_element->fd != fd) {
+ continue;
+ }
+ return true;
+ }
+ if (fd == handle->event_trigger_sources.kernel_tracer) {
+ return true;
+ }
+ return false;
+}
+
/*
* This thread services notification channel clients and commands received
* from various lttng-sessiond components over a command queue.
if (ret) {
goto error;
}
+ } else if (fd_is_event_source(handle, fd)) {
+ ret = handle_trigger_event_pipe(fd, revents, handle, &state);
+ if (ret) {
+ goto error;
+ }
} else {
/* Activity on a client's socket. */
if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
#ifndef NOTIFICATION_THREAD_H
#define NOTIFICATION_THREAD_H
-#include <urcu/list.h>
-#include <urcu.h>
-#include <urcu/rculfhash.h>
-#include <lttng/trigger/trigger.h>
-#include <common/pipe.h>
+#include "action-executor.h"
+#include "thread.h"
#include <common/compat/poll.h>
#include <common/hashtable/hashtable.h>
+#include <common/pipe.h>
+#include <lttng/trigger/trigger.h>
#include <pthread.h>
#include <semaphore.h>
-#include "thread.h"
+#include <urcu.h>
+#include <urcu/list.h>
+#include <urcu/rculfhash.h>
+
+typedef uint64_t notification_client_id;
+
+struct notification_event_trigger_source_element {
+ int fd;
+ struct cds_list_head node;
+};
+
+struct notification_trigger_tokens_ht_element {
+ uint64_t token;
+ struct lttng_trigger *trigger;
+ struct cds_lfht_node node;
+ /* call_rcu delayed reclaim. */
+ struct rcu_head rcu_node;
+};
struct notification_thread_handle {
/*
int ust64_consumer;
int kernel_consumer;
} channel_monitoring_pipes;
+ /*
+ * Read side of pipes used to reveice event trigger generetated by
+ * registered applications
+ */
+ struct {
+ /* List of notification_event_trigger_source_element */
+ struct cds_list_head list;
+ pthread_mutex_t lock;
+ int kernel_tracer;
+ } event_trigger_sources;
/* Used to wait for the launch of the notification thread. */
sem_t ready;
};
* In order to speed-up and simplify queries, hash tables providing the
* following associations are maintained:
*
- * - client_socket_ht: associate a client's socket (fd) to its "struct client"
- * This hash table owns the "struct client" which must thus be
- * disposed-of on removal from the hash table.
+ * - client_socket_ht: associate a client's socket (fd) to its
+ * "struct notification_client".
+ * This hash table owns the "struct notification_client" which must
+ * thus be disposed-of on removal from the hash table.
+ *
+ * - client_id_ht: associate a client's id to its "struct notification_client"
+ * This hash table holds a _weak_ reference to the
+ * "struct notification_client".
*
* - channel_triggers_ht:
* associates a channel key to a list of
* channels through their struct channel_info (ref-counting is used).
*
* - triggers_ht:
- * associates a condition to a struct lttng_trigger_ht_element.
+ * associates a trigger to a struct lttng_trigger_ht_element.
* The hash table holds the ownership of the
* lttng_trigger_ht_elements along with the triggers themselves.
+ * - triggers_by_name_ht:
+ * associates a trigger name to a struct lttng_trigger_ht_element.
+ * The hash table does not hold any ownership and is used strictly
+ * for lookup on registration.
*
* The thread reacts to the following internal events:
* 1) creation of a tracing channel,
* notification_trigger_clients_ht,
* - add trigger to channel_triggers_ht (if applicable),
* - add trigger to session_triggers_ht (if applicable),
+ * - add trigger to triggers_by_name_ht
* - add trigger to triggers_ht
* - evaluate the trigger's condition right away to react if that condition
* is true from the beginning.
* - 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 to triggers_by_name_ht
* - remove trigger from triggers_ht
*
* 5) Reception of a channel monitor sample from the consumer daemon
* 7) Session rotation completed
*
* 8) Connection of a client
- * - add client socket to the client_socket_ht.
+ * - add client socket to the client_socket_ht,
+ * - add client socket to the client_id_ht.
*
* 9) Disconnection of a client
+ * - remove client socket from the client_id_ht,
* - remove client socket from the client_socket_ht,
* - traverse all conditions to which the client is subscribed and remove
* the client from the notification_trigger_clients_ht.
int notification_channel_socket;
struct lttng_poll_event events;
struct cds_lfht *client_socket_ht;
+ struct cds_lfht *client_id_ht;
struct cds_lfht *channel_triggers_ht;
struct cds_lfht *session_triggers_ht;
struct cds_lfht *channel_state_ht;
struct cds_lfht *channels_ht;
struct cds_lfht *sessions_ht;
struct cds_lfht *triggers_ht;
+ struct cds_lfht *triggers_by_name_ht;
+ struct cds_lfht *trigger_tokens_ht;
+ struct {
+ uint64_t token_generator;
+ uint64_t name_offset;
+ } trigger_id;
+ notification_client_id next_notification_client_id;
+ struct action_executor *executor;
};
/* notification_thread_data takes ownership of the channel monitor pipes. */
struct notification_thread_handle *notification_thread_handle_create(
struct lttng_pipe *ust32_channel_monitor_pipe,
struct lttng_pipe *ust64_channel_monitor_pipe,
- struct lttng_pipe *kernel_channel_monitor_pipe);
+ struct lttng_pipe *kernel_channel_monitor_pipe,
+ int kernel_notification_monitor_fd);
void notification_thread_handle_destroy(
struct notification_thread_handle *handle);
struct lttng_thread *launch_notification_thread(
goto end;
}
+ lttng_trigger_set_credentials(
+ session->rotate_trigger, session->uid, session->gid);
+
nc_status = lttng_notification_channel_subscribe(
rotate_notification_channel, session->rotate_condition);
if (nc_status != LTTNG_NOTIFICATION_CHANNEL_STATUS_OK) {
void *ret;
struct lttng_thread *thread = (struct lttng_thread *) data;
- DBG("Launching \"%s\" thread", thread->name);
+ logger_set_thread_name(thread->name, true);
+ DBG("Entering thread entry point");
ret = thread->entry(thread->data);
- DBG("Thread \"%s\" has returned", thread->name);
+ DBG("Thread entry point has returned");
return ret;
}
result = false;
goto end;
}
- /* Release the list's reference to the thread. */
- cds_list_del(&thread->node);
- lttng_thread_put(thread);
+ DBG("Joined thread \"%s\"", thread->name);
end:
return result;
}
bool lttng_thread_shutdown(struct lttng_thread *thread)
{
- bool result;
-
- pthread_mutex_lock(&thread_list.lock);
- result = _lttng_thread_shutdown(thread);
- pthread_mutex_unlock(&thread_list.lock);
+ const bool result = _lttng_thread_shutdown(thread);
+
+ if (result) {
+ /* Release the list's reference to the thread. */
+ pthread_mutex_lock(&thread_list.lock);
+ cds_list_del(&thread->node);
+ lttng_thread_put(thread);
+ pthread_mutex_unlock(&thread_list.lock);
+ }
return result;
}
#include <lttng/lttng-error.h>
#include <lttng/userspace-probe.h>
#include <lttng/userspace-probe-internal.h>
-
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kprobe.h>
+#include <lttng/event-rule/kprobe-internal.h>
+#include <lttng/event-rule/kretprobe.h>
+#include <lttng/event-rule/kretprobe-internal.h>
+#include <lttng/event-rule/syscall.h>
+#include <lttng/event-rule/syscall-internal.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/event-rule/tracepoint-internal.h>
+#include <lttng/event-rule/uprobe-internal.h>
#include <common/common.h>
#include <common/defaults.h>
#include <common/trace-chunk.h>
struct ltt_kernel_event *trace_kernel_find_event(
char *name, struct ltt_kernel_channel *channel,
enum lttng_event_type type,
- struct lttng_filter_bytecode *filter)
+ struct lttng_bytecode *filter)
{
struct ltt_kernel_event *ev;
int found = 0;
}
}
+struct ltt_kernel_token_event_rule *trace_kernel_find_trigger_by_token(
+ struct ltt_kernel_token_event_rule_list *list,
+ uint64_t token)
+{
+ struct ltt_kernel_token_event_rule *token_event_rule;
+ int found = 0;
+
+ assert(list);
+
+ cds_list_for_each_entry(token_event_rule, &list->head, list) {
+ if (token_event_rule->token == token) {
+ found = 1;
+ }
+ break;
+ }
+ if (found) {
+ DBG("Found token event rule %" PRIu64, token);
+ return token_event_rule;
+ } else {
+ return NULL;
+ }
+}
+
/*
* Allocate and initialize a kernel session data structure.
*
*/
enum lttng_error_code trace_kernel_create_event(
struct lttng_event *ev, char *filter_expression,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
struct ltt_kernel_event **kernel_event)
{
enum lttng_error_code ret;
return ret;
}
+/*
+ * Allocate and initialize a kernel token event rule.
+ *
+ * Return pointer to structure or NULL.
+ */
+enum lttng_error_code trace_kernel_create_token_event_rule(
+ struct lttng_event_rule *event_rule,
+ uint64_t token,
+ struct ltt_kernel_token_event_rule **kernel_token_event_rule)
+{
+ enum lttng_error_code ret = LTTNG_OK;
+ struct ltt_kernel_token_event_rule *local_kernel_token_event_rule;
+
+ assert(kernel_token_event_rule);
+
+ local_kernel_token_event_rule = zmalloc(sizeof(struct ltt_kernel_token_event_rule));
+ if (local_kernel_token_event_rule == NULL) {
+ PERROR("Failed to allocate ltt_kernel_token_event_rule structure");
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+
+ local_kernel_token_event_rule->fd = -1;
+ local_kernel_token_event_rule->enabled = 1;
+ local_kernel_token_event_rule->token = token;
+
+ /* Get the reference of the event rule */
+ if (!lttng_event_rule_get(event_rule)) {
+ assert(0);
+ }
+
+ local_kernel_token_event_rule->event_rule = event_rule;
+ /* The event rule still own the filter and bytecode */
+ local_kernel_token_event_rule->filter = lttng_event_rule_get_filter_bytecode(event_rule);
+
+ DBG3("[trace] Kernel token event rule %" PRIu64 " allocated", local_kernel_token_event_rule->token);
+error:
+ *kernel_token_event_rule = local_kernel_token_event_rule;
+ return ret;
+
+}
+
+/*
+ * Initialize a kernel trigger from an event rule.
+ */
+enum lttng_error_code trace_kernel_init_trigger_from_event_rule(const struct lttng_event_rule *rule,
+ struct lttng_kernel_trigger *kernel_trigger)
+{
+ enum lttng_error_code ret;
+ enum lttng_event_rule_status status;
+ const char *name = NULL;
+
+ /* TODO: do this for now but have disucssion on if this could be the
+ * responsability of the event_rule itself ala
+ * "lttng_even_rule_generate_kernel_trigger"
+ */
+ switch (lttng_event_rule_get_type(rule)) {
+ case LTTNG_EVENT_RULE_TYPE_KPROBE:
+ kernel_trigger->instrumentation = LTTNG_KERNEL_KPROBE;
+ kernel_trigger->u.kprobe.addr = lttng_event_rule_kprobe_get_address(rule);
+ kernel_trigger->u.kprobe.offset = lttng_event_rule_kprobe_get_offset(rule);
+ strncpy(kernel_trigger->u.kprobe.symbol_name,
+ lttng_event_rule_kprobe_get_symbol_name(rule), LTTNG_KERNEL_SYM_NAME_LEN);
+ kernel_trigger->u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+ (void) lttng_event_rule_kprobe_get_name(rule, &name);
+ ret = LTTNG_OK;
+ break;
+ case LTTNG_EVENT_RULE_TYPE_UPROBE:
+ {
+ const struct lttng_userspace_probe_location* location = NULL;
+ const struct lttng_userspace_probe_location_lookup_method *lookup = NULL;
+
+ status = lttng_event_rule_uprobe_get_location(rule, &location);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+ goto error;
+ }
+
+ kernel_trigger->instrumentation = LTTNG_KERNEL_UPROBE;
+
+ /*
+ * Only the elf lookup method is supported at the moment.
+ */
+ lookup = lttng_userspace_probe_location_get_lookup_method(
+ location);
+ if (!lookup) {
+ ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+ goto error;
+ }
+
+ /*
+ * From the kernel tracer's perspective, all userspace probe
+ * event types are all the same: a file and an offset.
+ */
+ switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) {
+ case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+ /* Get the file descriptor on the target binary. */
+ kernel_trigger->u.uprobe.fd =
+ lttng_userspace_probe_location_function_get_binary_fd(location);
+
+ break;
+ case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+ /* Get the file descriptor on the target binary. */
+ kernel_trigger->u.uprobe.fd =
+ lttng_userspace_probe_location_tracepoint_get_binary_fd(location);
+ break;
+ default:
+ DBG("Unsupported lookup method type");
+ ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+ goto error;
+ }
+
+ (void) lttng_event_rule_uprobe_get_name(rule, &name);
+
+ ret = LTTNG_OK;
+ break;
+ }
+ case LTTNG_EVENT_RULE_TYPE_KRETPROBE:
+ assert("Not supported" && 0);
+ kernel_trigger->instrumentation = LTTNG_KERNEL_KRETPROBE;
+ kernel_trigger->u.kretprobe.addr = lttng_event_rule_kretprobe_get_address(rule);
+ kernel_trigger->u.kretprobe.offset = lttng_event_rule_kretprobe_get_offset(rule);
+ strncpy(kernel_trigger->u.kretprobe.symbol_name,
+ lttng_event_rule_kretprobe_get_symbol_name(rule), LTTNG_KERNEL_SYM_NAME_LEN);
+ kernel_trigger->u.kretprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+ (void) lttng_event_rule_kretprobe_get_name(rule, &name);
+ ret = LTTNG_OK;
+ break;
+ case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+ /* TODO: assert his is a kernel domain event-rule */
+ kernel_trigger->instrumentation = LTTNG_KERNEL_TRACEPOINT;
+ (void) lttng_event_rule_tracepoint_get_pattern(rule, &name);
+ ret = LTTNG_OK;
+ break;
+ case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+ kernel_trigger->instrumentation = LTTNG_KERNEL_SYSCALL;
+ (void) lttng_event_rule_syscall_get_pattern(rule, &name);
+ ret = LTTNG_OK;
+ break;
+ default:
+ ERR("Unknown kernel event rule instrumentation type (%d)", lttng_event_rule_get_type(rule));
+ ret = LTTNG_ERR_INVALID;
+ goto error;
+ }
+
+ /*
+ * WTF is LTTNG_EVENT_ALL??? and LTTNG_EVENT_FUNTION_ENTRY?????
+ */
+
+ /* Copy event name */
+ strncpy(kernel_trigger->name, name, LTTNG_KERNEL_SYM_NAME_LEN);
+ kernel_trigger->name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0';
+
+error:
+ return ret;
+}
/*
* Allocate and initialize a kernel metadata.
*
free(event);
}
+/*
+ * Cleanup kernel event structure.
+ */
+void trace_kernel_destroy_token_event_rule(struct ltt_kernel_token_event_rule *event)
+{
+ /* TODO: review in depth to ensure adequate disposing */
+ assert(event);
+
+ /* Remove from event list */
+ cds_list_del(&event->list);
+
+ if (event->fd >= 0) {
+ int ret;
+
+ DBG("[trace] Closing ,token event rule fd %d", event->fd);
+ /* Close kernel fd */
+ ret = close(event->fd);
+ if (ret) {
+ PERROR("close");
+ }
+ } else {
+ DBG("[trace] Tearing down token event rule (no associated fd)");
+ }
+
+ lttng_event_rule_put(event->event_rule);
+ free(event);
+}
/*
* Cleanup kernel context structure.
*/
struct cds_list_head head;
};
+/* Kernel event rule token list */
+struct ltt_kernel_token_event_rule_list {
+ struct cds_list_head head;
+};
+
/* Channel stream list */
struct ltt_kernel_stream_list {
struct cds_list_head head;
struct lttng_kernel_event *event;
struct cds_list_head list;
char *filter_expression;
- struct lttng_filter_bytecode *filter;
+ struct lttng_bytecode *filter;
+ struct lttng_userspace_probe_location *userspace_probe_location;
+};
+
+/* Kernel event */
+struct ltt_kernel_token_event_rule {
+ int fd;
+ int enabled;
+ enum lttng_event_type type;
+ struct lttng_event_rule *event_rule;
+ struct cds_list_head list;
+ uint64_t token;
+ const struct lttng_bytecode *filter;
struct lttng_userspace_probe_location *userspace_probe_location;
};
struct ltt_kernel_event *trace_kernel_find_event(
char *name, struct ltt_kernel_channel *channel,
enum lttng_event_type type,
- struct lttng_filter_bytecode *filter);
+ struct lttng_bytecode *filter);
struct ltt_kernel_channel *trace_kernel_get_channel_by_name(
const char *name, struct ltt_kernel_session *session);
+struct ltt_kernel_token_event_rule *trace_kernel_find_trigger_by_token(
+ struct ltt_kernel_token_event_rule_list *list,
+ uint64_t token);
+
/*
* Create functions malloc() the data structure.
*/
struct ltt_kernel_channel *trace_kernel_create_channel(
struct lttng_channel *chan);
enum lttng_error_code trace_kernel_create_event(struct lttng_event *ev,
- char *filter_expression, struct lttng_filter_bytecode *filter,
+ char *filter_expression, struct lttng_bytecode *filter,
struct ltt_kernel_event **kernel_event);
struct ltt_kernel_metadata *trace_kernel_create_metadata(void);
struct ltt_kernel_stream *trace_kernel_create_stream(const char *name,
unsigned int count);
struct ltt_kernel_context *trace_kernel_create_context(
struct lttng_kernel_context *ctx);
+enum lttng_error_code trace_kernel_create_token_event_rule(
+ struct lttng_event_rule *event_rule,
+ uint64_t token,
+ struct ltt_kernel_token_event_rule **kernel_token_event_rule);
struct ltt_kernel_context *trace_kernel_copy_context(
struct ltt_kernel_context *ctx);
+enum lttng_error_code trace_kernel_init_trigger_from_event_rule(const struct lttng_event_rule *rule,
+ struct lttng_kernel_trigger *kernel_trigger);
/*
* Destroy functions free() the data structure and remove from linked list if
void trace_kernel_destroy_event(struct ltt_kernel_event *event);
void trace_kernel_destroy_stream(struct ltt_kernel_stream *stream);
void trace_kernel_destroy_context(struct ltt_kernel_context *ctx);
+void trace_kernel_destroy_token_event_rule(struct ltt_kernel_token_event_rule *rule);
void trace_kernel_free_session(struct ltt_kernel_session *session);
#endif /* _LTT_TRACE_KERNEL_H */
* MUST be acquired before calling this.
*/
struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht,
- char *name, struct lttng_filter_bytecode *filter,
+ char *name, struct lttng_bytecode *filter,
enum lttng_ust_loglevel_type loglevel_type, int loglevel_value,
struct lttng_event_exclusion *exclusion)
{
*/
enum lttng_error_code trace_ust_create_event(struct lttng_event *ev,
char *filter_expression,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
struct lttng_event_exclusion *exclusion,
bool internal_event,
struct ltt_ust_event **ust_event)
struct ltt_ust_ht_key {
const char *name;
- const struct lttng_filter_bytecode *filter;
+ const struct lttng_bytecode *filter;
enum lttng_ust_loglevel_type loglevel_type;
int loglevel_value;
const struct lttng_event_exclusion *exclusion;
struct lttng_ust_event attr;
struct lttng_ht_node_str node;
char *filter_expression;
- struct lttng_filter_bytecode *filter;
+ struct lttng_bytecode *filter;
struct lttng_event_exclusion *exclusion;
/*
* An internal event is an event which was created by the session daemon
* Lookup functions. NULL is returned if not found.
*/
struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht,
- char *name, struct lttng_filter_bytecode *filter,
+ char *name, struct lttng_bytecode *filter,
enum lttng_ust_loglevel_type loglevel_type, int loglevel_value,
struct lttng_event_exclusion *exclusion);
struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht,
enum lttng_domain_type domain);
enum lttng_error_code trace_ust_create_event(struct lttng_event *ev,
char *filter_expression,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
struct lttng_event_exclusion *exclusion,
bool internal_event, struct ltt_ust_event **ust_event);
struct ltt_ust_context *trace_ust_create_context(
static inline
enum lttng_error_code trace_ust_create_event(struct lttng_event *ev,
const char *filter_expression,
- struct lttng_filter_bytecode *filter,
+ struct lttng_bytecode *filter,
struct lttng_event_exclusion *exclusion,
bool internal_event, struct ltt_ust_event **ust_event)
{
}
static inline
struct ltt_ust_event *trace_ust_find_event(struct lttng_ht *ht,
- char *name, struct lttng_filter_bytecode *filter,
+ char *name, struct lttng_bytecode *filter,
enum lttng_ust_loglevel_type loglevel_type, int loglevel_value,
struct lttng_event_exclusion *exclusion)
{
*/
} LTTNG_PACKED;
+#define LTTNG_UST_TRIGGER_PADDING1 16
+#define LTTNG_UST_TRIGGER_PADDING2 (LTTNG_UST_SYM_NAME_LEN + 32)
+struct lttng_ust_trigger {
+ uint64_t id;
+ enum lttng_ust_instrumentation instrumentation;
+ char name[LTTNG_UST_SYM_NAME_LEN]; /* event name */
+
+ enum lttng_ust_loglevel_type loglevel_type;
+ int loglevel; /* value, -1: all */
+ char padding[LTTNG_UST_TRIGGER_PADDING1];
+
+ /* Per instrumentation type configuration */
+ union {
+ char padding[LTTNG_UST_TRIGGER_PADDING2];
+ } u;
+} LTTNG_PACKED;
+
+#define LTTNG_TRIGGER_NOTIFICATION_PADDING 32
+struct lttng_ust_trigger_notification {
+ uint64_t id;
+ uint16_t capture_buf_size;
+ char padding[LTTNG_TRIGGER_NOTIFICATION_PADDING];
+} LTTNG_PACKED;
+
#define LTTNG_UST_EVENT_PADDING1 16
#define LTTNG_UST_EVENT_PADDING2 (LTTNG_UST_SYM_NAME_LEN + 32)
struct lttng_ust_event {
char data[0];
} LTTNG_PACKED;
+#define CAPTURE_BYTECODE_MAX_LEN 65536
+#define LTTNG_UST_CAPTURE_PADDING 32
+struct lttng_ust_capture_bytecode {
+ uint32_t len;
+ uint32_t reloc_offset;
+ uint64_t seqnum;
+ char padding[LTTNG_UST_CAPTURE_PADDING];
+ char data[0];
+} LTTNG_PACKED;
+
#define LTTNG_UST_EXCLUSION_PADDING 32
struct lttng_ust_event_exclusion {
uint32_t count;
#define LTTNG_UST_FILTER _UST_CMD(0xA0)
#define LTTNG_UST_EXCLUSION _UST_CMD(0xA1)
+#define LTTNG_UST_TRIGGER_SEND_FD _UST_CMD(0xB0)
+#define LTTNG_UST_TRIGGER_CREATE _UST_CMDW(0xB1, struct lttng_ust_trigger)
+
#define LTTNG_UST_ROOT_HANDLE 0
struct lttng_ust_obj;
#include <urcu/compiler.h>
#include <signal.h>
+#include <common/bytecode/bytecode.h>
#include <common/common.h>
+#include <common/hashtable/utils.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/trigger/trigger-internal.h>
#include <common/sessiond-comm/sessiond-comm.h>
#include "buffer-registry.h"
#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;
free(ua_event);
}
+/*
+ * Delete ust app token event_rule safely. RCU read lock must be held before calling
+ * this function. TODO: or does it????
+ */
+static
+void delete_ust_app_token_event_rule(int sock, struct ust_app_token_event_rule *ua_token,
+ struct ust_app *app)
+{
+ int ret;
+
+ assert(ua_token);
+
+ if (ua_token->exclusion != NULL)
+ free(ua_token->exclusion);
+ if (ua_token->obj != NULL) {
+ pthread_mutex_lock(&app->sock_lock);
+ ret = ustctl_release_object(sock, ua_token->obj);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app sock %d release event obj failed with ret %d",
+ sock, ret);
+ }
+ free(ua_token->obj);
+ }
+ lttng_trigger_put(ua_token->trigger);
+ free(ua_token);
+}
+
/*
* Release ust data object of the given stream.
*
{
int ret, sock;
struct ust_app_session *ua_sess, *tmp_ua_sess;
+ struct lttng_ht_iter iter;
+ struct ust_app_token_event_rule *token;
/*
* The session list lock must be held during this function to guarantee
rcu_read_unlock();
}
+ /* Wipe token associated with the app */
+ cds_lfht_for_each_entry(app->tokens_ht->ht, &iter.iter, token,
+ node.node) {
+ ret = lttng_ht_del(app->tokens_ht, &iter);
+ assert(!ret);
+ delete_ust_app_token_event_rule(app->sock, token, app);
+ }
+
ht_cleanup_push(app->sessions);
ht_cleanup_push(app->ust_sessions_objd);
ht_cleanup_push(app->ust_objd);
+ ht_cleanup_push(app->tokens_ht);
+
+ ustctl_release_object(sock, app->token_communication.handle);
+ free(app->token_communication.handle);
+
+ lttng_pipe_destroy(app->token_communication.trigger_event_pipe);
/*
* Wait until we have deleted the application from the sock hash table
return NULL;
}
+/*
+ * Alloc new UST app token event rule.
+ */
+static struct ust_app_token_event_rule *alloc_ust_app_token_event_rule(
+ struct lttng_trigger *trigger)
+{
+ struct ust_app_token_event_rule *ua_token;
+ struct lttng_condition *condition = NULL;
+ struct lttng_event_rule *event_rule = NULL;
+
+ ua_token = zmalloc(sizeof(struct ust_app_token_event_rule));
+ if (ua_token == NULL) {
+ PERROR("Failed to allocate ust_app_token_event_rule structure");
+ goto error;
+ }
+
+ /* Get reference of the trigger */
+ /* TODO should this be like lttng_event_rule_get with a returned bool? */
+ lttng_trigger_get(trigger);
+
+ ua_token->enabled = 1;
+ ua_token->token = lttng_trigger_get_key(trigger);
+ lttng_ht_node_init_u64(&ua_token->node, ua_token->token);
+
+ condition = lttng_trigger_get_condition(trigger);
+ assert(condition);
+ assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+ assert(LTTNG_CONDITION_STATUS_OK == lttng_condition_event_rule_get_rule_no_const(condition, &event_rule));
+ assert(event_rule);
+
+ ua_token->trigger = trigger;
+ ua_token->filter = lttng_event_rule_get_filter_bytecode(event_rule);
+ ua_token->exclusion = lttng_event_rule_generate_exclusions(event_rule);
+
+ /* TODO put capture here? or later*/
+
+ DBG3("UST app token event rule %" PRIu64 " allocated", ua_token->token);
+
+ return ua_token;
+
+error:
+ return NULL;
+}
+
/*
* Alloc new UST app context.
*/
}
/*
- * Allocate a filter and copy the given original filter.
+ * Create a liblttng-ust filter bytecode from given bytecode.
*
* Return allocated filter or NULL on error.
*/
-static struct lttng_filter_bytecode *copy_filter_bytecode(
- struct lttng_filter_bytecode *orig_f)
+static struct lttng_ust_filter_bytecode *
+create_ust_filter_bytecode_from_bytecode(const struct lttng_bytecode *orig_f)
{
- struct lttng_filter_bytecode *filter = NULL;
+ struct lttng_ust_filter_bytecode *filter = NULL;
/* Copy filter bytecode */
filter = zmalloc(sizeof(*filter) + orig_f->len);
if (!filter) {
- PERROR("zmalloc alloc filter bytecode");
+ PERROR("zmalloc alloc ust filter bytecode");
goto error;
}
+ assert(sizeof(struct lttng_bytecode) ==
+ sizeof(struct lttng_ust_filter_bytecode));
memcpy(filter, orig_f, sizeof(*filter) + orig_f->len);
-
error:
return filter;
}
/*
- * Create a liblttng-ust filter bytecode from given bytecode.
+ * Create a liblttng-ust capture bytecode from given bytecode.
*
* Return allocated filter or NULL on error.
*/
-static struct lttng_ust_filter_bytecode *create_ust_bytecode_from_bytecode(
- struct lttng_filter_bytecode *orig_f)
+static struct lttng_ust_capture_bytecode *
+create_ust_capture_bytecode_from_bytecode(const struct lttng_bytecode *orig_f)
{
- struct lttng_ust_filter_bytecode *filter = NULL;
+ struct lttng_ust_capture_bytecode *capture = NULL;
- /* Copy filter bytecode */
- filter = zmalloc(sizeof(*filter) + orig_f->len);
- if (!filter) {
- PERROR("zmalloc alloc ust filter bytecode");
+ /* Copy capture bytecode */
+ capture = zmalloc(sizeof(*capture) + orig_f->len);
+ if (!capture) {
+ PERROR("zmalloc alloc ust capture bytecode");
goto error;
}
- assert(sizeof(struct lttng_filter_bytecode) ==
- sizeof(struct lttng_ust_filter_bytecode));
- memcpy(filter, orig_f, sizeof(*filter) + orig_f->len);
+ assert(sizeof(struct lttng_bytecode) ==
+ sizeof(struct lttng_ust_capture_bytecode));
+ memcpy(capture, orig_f, sizeof(*capture) + orig_f->len);
error:
- return filter;
+ return capture;
}
/*
* Return an ust_app_event object or NULL on error.
*/
static struct ust_app_event *find_ust_app_event(struct lttng_ht *ht,
- const char *name, const struct lttng_filter_bytecode *filter,
+ const char *name, const struct lttng_bytecode *filter,
int loglevel_value,
const struct lttng_event_exclusion *exclusion)
{
return event;
}
+/*
+ * Lookup for an ust app tokens based on a token id.
+ *
+ * Return an ust_app_token_event_rule object or NULL on error.
+ */
+static struct ust_app_token_event_rule *find_ust_app_token_event_rule(struct lttng_ht *ht,
+ uint64_t token)
+{
+ struct lttng_ht_iter iter;
+ struct lttng_ht_node_u64 *node;
+ struct ust_app_token_event_rule *token_event_rule = NULL;
+
+ assert(ht);
+
+ lttng_ht_lookup(ht, &token, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
+ if (node == NULL) {
+ DBG2("UST app token %" PRIu64 " not found", token);
+ goto end;
+ }
+
+ token_event_rule = caa_container_of(node, struct ust_app_token_event_rule, node);
+end:
+ return token_event_rule;
+}
+
/*
* Create the channel context on the tracer.
*
/*
* Set the filter on the tracer.
*/
-static
-int set_ust_event_filter(struct ust_app_event *ua_event,
- struct ust_app *app)
+static int set_ust_filter(struct ust_app *app,
+ const struct lttng_bytecode *bytecode,
+ struct lttng_ust_object_data *ust_object)
{
int ret;
struct lttng_ust_filter_bytecode *ust_bytecode = NULL;
health_code_update();
- if (!ua_event->filter) {
- ret = 0;
+ ust_bytecode = create_ust_filter_bytecode_from_bytecode(bytecode);
+ if (!ust_bytecode) {
+ ret = -LTTNG_ERR_NOMEM;
goto error;
}
+ pthread_mutex_lock(&app->sock_lock);
+ ret = ustctl_set_filter(app->sock, ust_bytecode,
+ ust_object);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ ERR("UST app set filter failed for object %p of app (pid: %d) "
+ "with ret %d", ust_object, app->pid, ret);
+ } else {
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ ret = 0;
+ DBG3("UST app set filter. Application is dead.");
+ }
+ goto error;
+ }
+
+ DBG2("UST filter set for object %p successfully", ust_object);
+
+error:
+ health_code_update();
+ free(ust_bytecode);
+ return ret;
+}
- ust_bytecode = create_ust_bytecode_from_bytecode(ua_event->filter);
+/*
+ * Set a capture bytecode for the passed object.
+ * The seqnum enforce the ordering at runtime and on reception.
+ */
+static int set_ust_capture(struct ust_app *app,
+ const struct lttng_bytecode *bytecode,
+ unsigned int seqnum,
+ struct lttng_ust_object_data *ust_object)
+{
+ int ret;
+ struct lttng_ust_capture_bytecode *ust_bytecode = NULL;
+
+ health_code_update();
+
+ ust_bytecode = create_ust_capture_bytecode_from_bytecode(bytecode);
if (!ust_bytecode) {
ret = -LTTNG_ERR_NOMEM;
goto error;
}
+
+ /* Set the seqnum */
+ ust_bytecode->seqnum = seqnum;
+
pthread_mutex_lock(&app->sock_lock);
- ret = ustctl_set_filter(app->sock, ust_bytecode,
- ua_event->obj);
+ ret = ustctl_set_capture(app->sock, ust_bytecode,
+ ust_object);
pthread_mutex_unlock(&app->sock_lock);
if (ret < 0) {
if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
- ERR("UST app event %s filter failed for app (pid: %d) "
- "with ret %d", ua_event->attr.name, app->pid, ret);
+ ERR("UST app set capture failed for object %p of app (pid: %d) "
+ "with ret %d", ust_object, app->pid, ret);
} else {
/*
* This is normal behavior, an application can die during the
* continue normally.
*/
ret = 0;
- DBG3("UST app filter event failed. Application is dead.");
+ DBG3("UST app set capture. Application is dead.");
}
goto error;
}
- DBG2("UST filter set successfully for event %s", ua_event->name);
+ DBG2("UST capture set for object %p successfully", ust_object);
error:
health_code_update();
/*
* Set event exclusions on the tracer.
*/
-static
-int set_ust_event_exclusion(struct ust_app_event *ua_event,
- struct ust_app *app)
+static int set_ust_exclusions(struct ust_app *app,
+ struct lttng_event_exclusion *exclusions,
+ struct lttng_ust_object_data *ust_object)
{
int ret;
- struct lttng_ust_event_exclusion *ust_exclusion = NULL;
+ struct lttng_ust_event_exclusion *ust_exclusions = NULL;
- health_code_update();
+ assert(exclusions && exclusions->count > 0);
- if (!ua_event->exclusion || !ua_event->exclusion->count) {
- ret = 0;
- goto error;
- }
+ health_code_update();
- ust_exclusion = create_ust_exclusion_from_exclusion(
- ua_event->exclusion);
- if (!ust_exclusion) {
+ ust_exclusions = create_ust_exclusion_from_exclusion(
+ exclusions);
+ if (!ust_exclusions) {
ret = -LTTNG_ERR_NOMEM;
goto error;
}
pthread_mutex_lock(&app->sock_lock);
- ret = ustctl_set_exclusion(app->sock, ust_exclusion, ua_event->obj);
+ ret = ustctl_set_exclusion(app->sock, ust_exclusions, ust_object);
pthread_mutex_unlock(&app->sock_lock);
if (ret < 0) {
if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
- ERR("UST app event %s exclusions failed for app (pid: %d) "
- "with ret %d", ua_event->attr.name, app->pid, ret);
+ ERR("UST app exclusions failed for object %p of app (pid: %d) "
+ "with ret %d", ust_object, app->pid, ret);
} else {
/*
* This is normal behavior, an application can die during the
* continue normally.
*/
ret = 0;
- DBG3("UST app event exclusion failed. Application is dead.");
+ DBG3("UST app set exclusions failed. Application is dead.");
}
goto error;
}
- DBG2("UST exclusion set successfully for event %s", ua_event->name);
+ DBG2("UST exclusions set successfully for object %p", ust_object);
error:
health_code_update();
- free(ust_exclusion);
+ free(ust_exclusions);
return ret;
}
/*
* Disable the specified event on to UST tracer for the UST session.
*/
-static int disable_ust_event(struct ust_app *app,
- struct ust_app_session *ua_sess, struct ust_app_event *ua_event)
+static int disable_ust_object(struct ust_app *app,
+ struct lttng_ust_object_data *object)
{
int ret;
health_code_update();
pthread_mutex_lock(&app->sock_lock);
- ret = ustctl_disable(app->sock, ua_event->obj);
+ ret = ustctl_disable(app->sock, object);
pthread_mutex_unlock(&app->sock_lock);
if (ret < 0) {
if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
- ERR("UST app event %s disable failed for app (pid: %d) "
- "and session handle %d with ret %d",
- ua_event->attr.name, app->pid, ua_sess->handle, ret);
+ ERR("UST app disable failed for object %p app (pid: %d) with ret %d",
+ object, app->pid, ret);
} else {
/*
* This is normal behavior, an application can die during the
goto error;
}
- DBG2("UST app event %s disabled successfully for app (pid: %d)",
- ua_event->attr.name, app->pid);
+ DBG2("UST app object %p disabled successfully for app (pid: %d)",
+ object, app->pid);
error:
health_code_update();
/*
* Enable the specified event on to UST tracer for the UST session.
*/
-static int enable_ust_event(struct ust_app *app,
- struct ust_app_session *ua_sess, struct ust_app_event *ua_event)
+static int enable_ust_object(struct ust_app *app, struct lttng_ust_object_data *ust_object)
{
int ret;
health_code_update();
pthread_mutex_lock(&app->sock_lock);
- ret = ustctl_enable(app->sock, ua_event->obj);
+ ret = ustctl_enable(app->sock, ust_object);
pthread_mutex_unlock(&app->sock_lock);
if (ret < 0) {
if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
- ERR("UST app event %s enable failed for app (pid: %d) "
- "and session handle %d with ret %d",
- ua_event->attr.name, app->pid, ua_sess->handle, ret);
+ ERR("UST app enable failed for object %p app (pid: %d) with ret %d",
+ ust_object, app->pid, ret);
} else {
/*
* This is normal behavior, an application can die during the
* continue normally.
*/
ret = 0;
- DBG3("UST app enable event failed. Application is dead.");
+ DBG3("UST app enable failed. Application is dead.");
}
goto error;
}
- DBG2("UST app event %s enabled successfully for app (pid: %d)",
- ua_event->attr.name, app->pid);
+ DBG2("UST app object %p enabled successfully for app (pid: %d)",
+ ust_object, app->pid);
error:
health_code_update();
ua_event->handle = ua_event->obj->handle;
- DBG2("UST app event %s created successfully for pid:%d",
- ua_event->attr.name, app->pid);
+ DBG2("UST app event %s created successfully for pid:%d object: %p",
+ ua_event->attr.name, app->pid, ua_event->obj);
health_code_update();
/* Set filter if one is present. */
if (ua_event->filter) {
- ret = set_ust_event_filter(ua_event, app);
+ ret = set_ust_filter(app, ua_event->filter, ua_event->obj);
if (ret < 0) {
goto error;
}
/* Set exclusions for the event */
if (ua_event->exclusion) {
- ret = set_ust_event_exclusion(ua_event, app);
+ ret = set_ust_exclusions(app, ua_event->exclusion, ua_event->obj);
if (ret < 0) {
goto error;
}
* We now need to explicitly enable the event, since it
* is now disabled at creation.
*/
- ret = enable_ust_event(app, ua_sess, ua_event);
+ ret = enable_ust_object(app, ua_event->obj);
if (ret < 0) {
/*
* If we hit an EPERM, something is wrong with our enable call. If
return ret;
}
+static
+void init_ust_trigger_from_event_rule(const struct lttng_event_rule *rule, struct lttng_ust_trigger *trigger)
+{
+ enum lttng_event_rule_status status;
+ enum lttng_loglevel_type loglevel_type;
+ enum lttng_ust_loglevel_type ust_loglevel_type = LTTNG_UST_LOGLEVEL_ALL;
+ int loglevel = -1;
+ const char *pattern;
+
+ /* For now only LTTNG_EVENT_RULE_TYPE_TRACEPOINT are supported */
+ assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT);
+
+ memset(trigger, 0, sizeof(*trigger));
+
+ if (lttng_event_rule_is_agent(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 */
+ assert(0);
+ }
+
+ status = lttng_event_rule_tracepoint_get_loglevel_type(
+ rule, &loglevel_type);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ /* At this point this is a fatal error */
+ assert(0);
+ }
+
+ 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;
+ }
+
+ if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) {
+ status = lttng_event_rule_tracepoint_get_loglevel(
+ rule, &loglevel);
+ assert(status == LTTNG_EVENT_RULE_STATUS_OK);
+ }
+ }
+
+ trigger->instrumentation = LTTNG_UST_TRACEPOINT;
+ strncpy(trigger->name, pattern, LTTNG_UST_SYM_NAME_LEN - 1);
+ trigger->loglevel_type = ust_loglevel_type;
+ trigger->loglevel = loglevel;
+}
+
+/*
+ * Create the specified event rule token onto the UST tracer for a UST app.
+ */
+static
+int create_ust_token_event_rule(struct ust_app *app, struct ust_app_token_event_rule *ua_token)
+{
+ int ret = 0;
+ struct lttng_ust_trigger trigger;
+ struct lttng_condition *condition = NULL;
+ struct lttng_event_rule *event_rule = NULL;
+ unsigned int capture_bytecode_count = 0;
+
+ health_code_update();
+ assert(app->token_communication.handle);
+
+ condition = lttng_trigger_get_condition(ua_token->trigger);
+ assert(condition);
+ assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+
+ lttng_condition_event_rule_get_rule_no_const(condition, &event_rule);
+ assert(event_rule);
+ assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT);
+ /* Should we also test for UST at this point, or do we trust all the
+ * upper level? */
+
+ init_ust_trigger_from_event_rule(event_rule, &trigger);
+ trigger.id = ua_token->token;
+
+ /* Create UST trigger on tracer */
+ pthread_mutex_lock(&app->sock_lock);
+ ret = ustctl_create_trigger(app->sock, &trigger, app->token_communication.handle, &ua_token->obj);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (ret < 0) {
+ if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
+ abort();
+ ERR("Error ustctl create trigger %s for app pid: %d with ret %d",
+ trigger.name, app->pid, ret);
+ } else {
+ /*
+ * This is normal behavior, an application can die during the
+ * creation process. Don't report an error so the execution can
+ * continue normally.
+ */
+ ret = 0;
+ DBG3("UST app create event failed. Application is dead.");
+ }
+ goto error;
+ }
+
+ ua_token->handle = ua_token->obj->handle;
+
+ DBG2("UST app event %s created successfully for pid:%d object: %p",
+ trigger.name, app->pid, ua_token->obj);
+
+ health_code_update();
+
+ /* Set filter if one is present. */
+ if (ua_token->filter) {
+ ret = set_ust_filter(app, ua_token->filter, ua_token->obj);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+
+ /* Set exclusions for the event */
+ if (ua_token->exclusion) {
+ ret = set_ust_exclusions(app, ua_token->exclusion, ua_token->obj);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+
+ /* Set the capture bytecode
+ * TODO: do we want to emulate what is done with exclusion and provide
+ * and object with a count of capture bytecode? instead of multiple
+ * call?
+ * */
+ capture_bytecode_count = lttng_trigger_get_capture_bytecode_count(ua_token->trigger);
+ for (unsigned int i = 0; i < capture_bytecode_count; i++) {
+ const struct lttng_bytecode *capture_bytecode = lttng_trigger_get_capture_bytecode_at_index(ua_token->trigger, i);
+ ret = set_ust_capture(app, capture_bytecode, i, ua_token->obj);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+
+ /*
+ * We now need to explicitly enable the event, since it
+ * is disabled at creation.
+ */
+ ret = enable_ust_object(app, ua_token->obj);
+ if (ret < 0) {
+ /*
+ * If we hit an EPERM, something is wrong with our enable call. If
+ * we get an EEXIST, there is a problem on the tracer side since we
+ * just created it.
+ */
+ switch (ret) {
+ case -LTTNG_UST_ERR_PERM:
+ /* Code flow problem */
+ assert(0);
+ case -LTTNG_UST_ERR_EXIST:
+ /* It's OK for our use case. */
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+ goto error;
+ }
+ ua_token->enabled = true;
+
+error:
+ health_code_update();
+ return ret;
+}
+
/*
* Copy data between an UST app event and a LTT event.
*/
/* Copy filter bytecode */
if (uevent->filter) {
- ua_event->filter = copy_filter_bytecode(uevent->filter);
+ ua_event->filter = bytecode_copy(uevent->filter);
/* Filter might be NULL here in case of ENONEM. */
}
{
int ret;
- ret = enable_ust_event(app, ua_sess, ua_event);
+ ret = enable_ust_object(app, ua_event->obj);
if (ret < 0) {
goto error;
}
{
int ret;
- ret = disable_ust_event(app, ua_sess, ua_event);
+ ret = disable_ust_object(app, ua_event->obj);
if (ret < 0) {
goto error;
}
return ret;
}
+/*
+ * Create UST app event and create it on the tracer side.
+ *
+ * Called with ust app session mutex held.
+ */
+static
+int create_ust_app_token_event_rule(struct lttng_trigger *trigger,
+ struct ust_app *app)
+{
+ int ret = 0;
+ struct ust_app_token_event_rule *ua_token;
+
+ ua_token = alloc_ust_app_token_event_rule(trigger);
+ if (ua_token == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ /* Create it on the tracer side */
+ ret = create_ust_token_event_rule(app, ua_token);
+ if (ret < 0) {
+ /*
+ * Not found previously means that it does not exist on the
+ * tracer. If the application reports that the event existed,
+ * it means there is a bug in the sessiond or lttng-ust
+ * (or corruption, etc.)
+ */
+ if (ret == -LTTNG_UST_ERR_EXIST) {
+ ERR("Tracer for application reported that a token event rule being created already existed: "
+ "token = \"%" PRIu64 "\", pid = %d, ppid = %d, uid = %d, gid = %d",
+ lttng_trigger_get_key(trigger),
+ app->pid, app->ppid, app->uid,
+ app->gid);
+ }
+ goto error;
+ }
+
+ lttng_ht_add_unique_u64(app->tokens_ht, &ua_token->node);
+
+ DBG2("UST app create token event rule %" PRIu64 " for PID %d completed", lttng_trigger_get_key(trigger),
+ app->pid);
+
+end:
+ return ret;
+
+error:
+ /* Valid. Calling here is already in a read side lock */
+ delete_ust_app_token_event_rule(-1, ua_token, app);
+ return ret;
+}
+
/*
* Create UST metadata and open it on the tracer side.
*
struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock)
{
struct ust_app *lta = NULL;
+ struct lttng_pipe *trigger_event_source_pipe = NULL;
assert(msg);
assert(sock >= 0);
goto error;
}
+ trigger_event_source_pipe = lttng_pipe_open(FD_CLOEXEC);
+ if (!trigger_event_source_pipe) {
+ PERROR("Open trigger pipe");
+ goto error;
+ }
+
lta = zmalloc(sizeof(struct ust_app));
if (lta == NULL) {
PERROR("malloc");
goto error;
}
+ lta->token_communication.trigger_event_pipe = trigger_event_source_pipe;
+
lta->ppid = msg->ppid;
lta->uid = msg->uid;
lta->gid = msg->gid;
lta->ust_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
lta->ust_sessions_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
lta->notify_sock = -1;
+ lta->tokens_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
/* Copy name and make sure it's NULL terminated. */
strncpy(lta->name, msg->name, sizeof(lta->name));
return ret;
}
+/*
+ * Setup the base trigger group.
+ *
+ * Return 0 on success else a negative value either an errno code or a
+ * LTTng-UST error code.
+ */
+int ust_app_setup_trigger_group(struct ust_app *app)
+{
+ int ret;
+ int writefd;
+ struct lttng_ust_object_data *group = NULL;
+ enum lttng_error_code lttng_ret;
+
+ assert(app);
+
+ /* Get the write side of the pipe */
+ writefd = lttng_pipe_get_writefd(app->token_communication.trigger_event_pipe);
+
+ pthread_mutex_lock(&app->sock_lock);
+ ret = ustctl_create_trigger_group(app->sock, writefd, &group);
+ pthread_mutex_unlock(&app->sock_lock);
+ if (ret < 0) {
+ ERR("UST app %d create_trigger_group failed with ret %d", app->sock, ret);
+ goto end;
+ }
+
+ app->token_communication.handle = group;
+
+ lttng_ret = notification_thread_command_add_application(
+ notification_thread_handle, app->token_communication.trigger_event_pipe);
+ if (lttng_ret != LTTNG_OK) {
+ /* TODO: error */
+ ret = - 1;
+ ERR("Failed to add channel to notification thread");
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
/*
* Unregister app by removing it from the global traceable app list and freeing
* the data struct.
*/
void ust_app_unregister(int sock)
{
+ enum lttng_error_code ret_code;
struct ust_app *lta;
struct lttng_ht_node_ulong *node;
struct lttng_ht_iter ust_app_sock_iter;
lta->pid);
}
+ ret_code = notification_thread_command_remove_application(
+ notification_thread_handle,
+ lta->token_communication.trigger_event_pipe);
+ if (ret_code != LTTNG_OK) {
+ ERR("Failed to remove application from notification thread");
+ }
+
/* Free memory */
call_rcu(<a->pid_n.head, delete_ust_app_rcu);
return ret;
}
+static
+void ust_app_synchronize_tokens(struct ust_app *app)
+{
+ int ret = 0;
+ enum lttng_error_code ret_code;
+ enum lttng_trigger_status t_status;
+ struct lttng_ht_iter app_trigger_iter;
+ struct lttng_triggers *triggers;
+ struct ust_app_token_event_rule *token_event_rule_element;
+ unsigned int count;
+
+ rcu_read_lock();
+ /* TODO: is this necessary to protect against new trigger being added ?
+ * notification_trigger_tokens_ht is still the backing data structure
+ * for this listing. Leave it there for now.
+ */
+ pthread_mutex_lock(¬ification_trigger_tokens_ht_lock);
+ ret_code = notification_thread_command_get_tokens(
+ notification_thread_handle, &triggers);
+ if (ret_code != LTTNG_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ assert(triggers);
+
+ t_status = lttng_triggers_get_count(triggers, &count);
+ if (t_status != LTTNG_TRIGGER_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ for (unsigned int i = 0; i < count; i++) {
+ struct lttng_condition *condition;
+ struct lttng_event_rule *event_rule;
+ struct lttng_trigger *trigger;
+ struct ust_app_token_event_rule *ua_token;
+ uint64_t token;
+
+ trigger = lttng_triggers_get_pointer_of_index(triggers, i);
+ assert(trigger);
+
+ /* TODO: error checking and type checking */
+ token = lttng_trigger_get_key(trigger);
+ condition = lttng_trigger_get_condition(trigger);
+ (void) lttng_condition_event_rule_get_rule_no_const(condition, &event_rule);
+
+ if (lttng_event_rule_get_domain_type(event_rule) == LTTNG_DOMAIN_KERNEL) {
+ /* Skip kernel related trigger */
+ continue;
+ }
+
+ /* Iterate over all known token trigger */
+ ua_token = find_ust_app_token_event_rule(app->tokens_ht, token);
+ if (!ua_token) {
+ ret = create_ust_app_token_event_rule(trigger, app);
+ if (ret < 0) {
+ goto end;
+ }
+ }
+ }
+
+ /* Remove all unknown trigger from the app
+ * TODO find a way better way then this, do it on the unregister command
+ * and be specific on the token to remove instead of going over all
+ * trigger known to the app. This is sub optimal.
+ */
+ cds_lfht_for_each_entry (app->tokens_ht->ht, &app_trigger_iter.iter,
+ token_event_rule_element, node.node) {
+ uint64_t token;
+ bool found = false;
+
+ token = token_event_rule_element->token;
+
+ /*
+ * Check if the app event trigger still exists on the
+ * notification side.
+ * TODO: might want to change the backing data struct of the
+ * lttng_triggers object to allow quick lookup?
+ * For kernel mostly all of this can be removed once we delete
+ * on a per trigger basis.
+ */
+
+ for (unsigned int i = 0; i < count; i++) {
+ struct lttng_trigger *trigger;
+ uint64_t inner_token;
+
+ trigger = lttng_triggers_get_pointer_of_index(
+ triggers, i);
+ assert(trigger);
+
+ inner_token = lttng_trigger_get_key(trigger);
+
+ if (inner_token == token) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ /* Still valid */
+ continue;
+ }
+
+ /* TODO: This is fucking ugly API for fuck sake */
+ assert(!lttng_ht_del(app->tokens_ht, &app_trigger_iter));
+
+ (void) disable_ust_object(app, token_event_rule_element->obj);
+
+ delete_ust_app_token_event_rule(app->sock, token_event_rule_element, app);
+ }
+end:
+ lttng_triggers_destroy(triggers);
+ rcu_read_unlock();
+ pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock);
+ return;
+}
+
/*
* The caller must ensure that the application is compatible and is tracked
* by the process attribute trackers.
}
}
+void ust_app_global_update_tokens(struct ust_app *app)
+{
+ DBG2("UST app global update token for app sock %d", app->sock);
+
+ if (!app->compatible) {
+ return;
+ }
+ if (app->token_communication.handle == NULL) {
+ WARN("UST app global update token for app sock %d skipped since communcation handle is null", app->sock);
+ return;
+ }
+
+ ust_app_synchronize_tokens(app);
+}
+
/*
* Called with session lock held.
*/
rcu_read_unlock();
}
+void ust_app_global_update_all_tokens(void)
+{
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
+ ust_app_global_update_tokens(app);
+ }
+ rcu_read_unlock();
+}
+
/*
* Add context to a specific channel for global UST domain.
*/
/* Process name (short). */
#define UST_APP_PROCNAME_LEN 16
-struct lttng_filter_bytecode;
+struct lttng_bytecode;
struct lttng_ust_filter_bytecode;
extern int ust_consumerd64_fd, ust_consumerd32_fd;
struct ust_app_ht_key {
const char *name;
- const struct lttng_filter_bytecode *filter;
+ const struct lttng_bytecode *filter;
enum lttng_ust_loglevel_type loglevel_type;
const struct lttng_event_exclusion *exclusion;
};
struct lttng_ust_event attr;
char name[LTTNG_UST_SYM_NAME_LEN];
struct lttng_ht_node_str node;
- struct lttng_filter_bytecode *filter;
+ struct lttng_bytecode *filter;
+ struct lttng_event_exclusion *exclusion;
+};
+
+struct ust_app_token_event_rule {
+ int enabled;
+ int handle;
+ struct lttng_ust_object_data *obj;
+ struct lttng_trigger *trigger;
+ uint64_t token;
+ struct lttng_ht_node_u64 node;
+ /* The event_rule object own this pointer */
+ const struct lttng_bytecode *filter;
+ /* The event_rule object own this pointer */
struct lttng_event_exclusion *exclusion;
};
* Used for path creation
*/
time_t registration_time;
+ /*
+ * Trigger
+ */
+ struct {
+ struct lttng_ust_object_data *handle;
+ struct lttng_pipe *trigger_event_pipe;
+ } token_communication;
+ struct lttng_ht *tokens_ht;
};
#ifdef HAVE_LIBLTTNG_UST_CTL
struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx);
void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app);
void ust_app_global_update_all(struct ltt_ust_session *usess);
+void ust_app_global_update_tokens(struct ust_app *app);
+void ust_app_global_update_all_tokens(void);
void ust_app_clean_list(void);
int ust_app_ht_alloc(void);
struct lttng_ust_object_data *data);
enum lttng_error_code ust_app_clear_session(struct ltt_session *session);
+int ust_app_setup_trigger_group(struct ust_app *app);
+
static inline
int ust_app_supported(void)
{
void ust_app_global_update(struct ltt_ust_session *usess, struct ust_app *app)
{}
static inline
+void ust_app_global_update_tokens(struct ust_app *app)
+{}
+static inline
+void ust_app_global_update_all_tokens(void)
+{}
+static inline
+int ust_app_setup_trigger_group(struct ust_app *app)
+{
+ return 0;
+}
+static inline
int ust_app_disable_channel_glb(struct ltt_ust_session *usess,
struct ltt_ust_channel *uchan)
{
struct lttng_ust_object_data *obj_data);
int ustctl_set_exclusion(int sock, struct lttng_ust_event_exclusion *exclusion,
struct lttng_ust_object_data *obj_data);
+int ustctl_set_capture(int sock, struct lttng_ust_capture_bytecode *bytecode,
+ struct lttng_ust_object_data *obj_data);
int ustctl_enable(int sock, struct lttng_ust_object_data *object);
int ustctl_disable(int sock, struct lttng_ust_object_data *object);
int ustctl_start_session(int sock, int handle);
int ustctl_stop_session(int sock, int handle);
+int ustctl_create_trigger_group(int sock,
+ int pipe_fd,
+ struct lttng_ust_object_data **trigger_group_handle);
+int ustctl_create_trigger(int sock,
+ struct lttng_ust_trigger *trigger,
+ struct lttng_ust_object_data *trigger_group_handle,
+ struct lttng_ust_object_data **trigger_data);
+
/*
* ustctl_tracepoint_list returns a tracepoint list handle, or negative
* error value.
commands/enable_rotation.c \
commands/disable_rotation.c \
commands/clear.c \
- utils.c utils.h lttng.c
+ commands/add_trigger.c \
+ commands/list_triggers.c \
+ commands/remove_trigger.c \
+ utils.c utils.h lttng.c \
+ uprobe.c uprobe.h
lttng_CFLAGS = $(AM_CFLAGS) $(POPT_CFLAGS)
$(top_builddir)/src/common/libcommon.la \
$(top_builddir)/src/common/config/libconfig.la \
$(top_builddir)/src/common/string-utils/libstring-utils.la \
+ $(top_builddir)/src/common/filter/libfilter.la \
+ $(top_builddir)/src/common/argpar/libargpar.la \
$(POPT_LIBS)
DECL_COMMAND(enable_rotation);
DECL_COMMAND(disable_rotation);
DECL_COMMAND(clear);
+DECL_COMMAND(add_trigger);
+DECL_COMMAND(list_triggers);
+DECL_COMMAND(remove_trigger);
extern int cmd_help(int argc, const char **argv,
const struct cmd_struct commands[]);
--- /dev/null
+#include <stdio.h>
+
+#include "../command.h"
+#include "../uprobe.h"
+
+#include "common/argpar/argpar.h"
+#include "common/dynamic-array.h"
+#include "common/string-utils/string-utils.h"
+#include "common/utils.h"
+#include "lttng/condition/event-rule.h"
+#include "lttng/event-internal.h"
+#include "lttng/event-expr.h"
+#include <lttng/event-rule/event-rule-internal.h>
+#include "lttng/event-rule/kprobe.h"
+#include "lttng/event-rule/syscall.h"
+#include <lttng/event-rule/tracepoint.h>
+#include "lttng/event-rule/uprobe.h"
+#include "common/filter/filter-ast.h"
+#include "common/filter/filter-ir.h"
+#include "common/dynamic-array.h"
+
+#ifdef LTTNG_EMBED_HELP
+static const char help_msg[] =
+#include <lttng-add-trigger.1.h>
+;
+#endif
+
+enum {
+ OPT_HELP,
+ OPT_LIST_OPTIONS,
+
+ OPT_CONDITION,
+ OPT_ACTION,
+ OPT_ID,
+ OPT_FIRE_ONCE_AFTER,
+ OPT_FIRE_EVERY,
+
+ OPT_ALL,
+ OPT_FILTER,
+ OPT_EXCLUDE,
+ OPT_LOGLEVEL,
+ OPT_LOGLEVEL_ONLY,
+
+ OPT_USERSPACE,
+ OPT_KERNEL,
+ OPT_LOG4J,
+ OPT_JUL,
+ OPT_PYTHON,
+
+ OPT_FUNCTION,
+ OPT_PROBE,
+ OPT_USERSPACE_PROBE,
+ OPT_SYSCALL,
+ OPT_TRACEPOINT,
+
+ OPT_NAME,
+ OPT_MAX_SIZE,
+ OPT_DATA_URL,
+ OPT_CTRL_URL,
+
+ OPT_CAPTURE,
+};
+
+static const struct argpar_opt_descr event_rule_opt_descrs[] = {
+ { OPT_ALL, 'a', "all", false },
+ { OPT_FILTER, 'f', "filter", true },
+ { OPT_EXCLUDE, 'x', "exclude", true },
+ { OPT_LOGLEVEL, '\0', "loglevel", true },
+ { OPT_LOGLEVEL_ONLY, '\0', "loglevel-only", true },
+
+ /* Domains */
+ { OPT_USERSPACE, 'u', "userspace", false },
+ { OPT_KERNEL, 'k', "kernel", false },
+ { OPT_LOG4J, 'l', "log4j", false },
+ { OPT_JUL, 'j', "jul", false },
+ { OPT_PYTHON, 'p', "python", false },
+
+ /* Event rule types */
+ { OPT_FUNCTION, '\0', "function", true },
+ { OPT_PROBE, '\0', "probe", true },
+ { OPT_USERSPACE_PROBE, '\0', "userspace-probe", true },
+ { OPT_SYSCALL, '\0', "syscall" },
+ { OPT_TRACEPOINT, '\0', "tracepoint" },
+
+ /* Capture descriptor */
+ { OPT_CAPTURE, '\0', "capture", true },
+
+ ARGPAR_OPT_DESCR_SENTINEL
+};
+
+static
+bool assign_domain_type(enum lttng_domain_type *dest,
+ enum lttng_domain_type src)
+{
+ bool ret;
+
+ if (*dest == LTTNG_DOMAIN_NONE || *dest == src) {
+ *dest = src;
+ ret = true;
+ } else {
+ ERR("Multiple domains specified.");
+ ret = false;
+ }
+
+ return ret;
+}
+
+static
+bool assign_event_rule_type(enum lttng_event_rule_type *dest,
+ enum lttng_event_rule_type src)
+{
+ bool ret;
+
+ if (*dest == LTTNG_EVENT_RULE_TYPE_UNKNOWN || *dest == src) {
+ *dest = src;
+ ret = true;
+ } else {
+ ERR("Multiple event type not supported.");
+ ret = false;
+ }
+
+ return ret;
+}
+
+static
+bool assign_string(char **dest, const char *src, const char *opt_name)
+{
+ bool ret;
+
+ if (*dest) {
+ ERR(
+ "Duplicate %s given.", opt_name);
+ goto error;
+ }
+
+ *dest = strdup(src);
+ if (!*dest) {
+ ERR("Failed to allocate %s string.", opt_name);
+ goto error;
+ }
+
+ ret = true;
+ goto end;
+
+error:
+ ret = false;
+
+end:
+ return ret;
+}
+
+/* This is defined in enable_events.c. */
+LTTNG_HIDDEN
+int create_exclusion_list_and_validate(const char *event_name,
+ const char *exclusions_arg,
+ char ***exclusion_list);
+
+/*
+ * Parse `str` as a log level in domain `domain_type`. Return -1 if the string
+ * is not recognized as a valid log level.
+ */
+static
+int parse_loglevel_string(const char *str, enum lttng_domain_type domain_type)
+{
+
+ switch (domain_type) {
+ case LTTNG_DOMAIN_UST:
+ return loglevel_str_to_value(str);
+
+ case LTTNG_DOMAIN_LOG4J:
+ return loglevel_log4j_str_to_value(str);
+
+ case LTTNG_DOMAIN_JUL:
+ return loglevel_jul_str_to_value(str);
+
+ case LTTNG_DOMAIN_PYTHON:
+ return loglevel_python_str_to_value(str);
+
+ default:
+ /* Invalid domain type. */
+ abort();
+ }
+}
+
+static
+struct lttng_event_expr *ir_op_load_expr_to_event_expr(
+ struct ir_load_expression *load_exp, const char *capture_str)
+{
+ struct ir_load_expression_op *load_expr_op = load_exp->child;
+ struct lttng_event_expr *event_expr = NULL;
+ char *provider_name = NULL;
+
+ switch (load_expr_op->type) {
+ case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+ {
+ const char *field_name;
+
+ load_expr_op = load_expr_op->next;
+ assert(load_expr_op);
+ assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL);
+ field_name = load_expr_op->u.symbol;
+ assert(field_name);
+
+ event_expr = lttng_event_expr_event_payload_field_create(field_name);
+ if (!event_expr) {
+ fprintf(stderr, "Failed to create payload field event expression.\n");
+ goto error;
+ }
+
+ break;
+ }
+
+ case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+ {
+ const char *field_name;
+
+ load_expr_op = load_expr_op->next;
+ assert(load_expr_op);
+ assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL);
+ field_name = load_expr_op->u.symbol;
+ assert(field_name);
+
+ event_expr = lttng_event_expr_channel_context_field_create(field_name);
+ if (!event_expr) {
+ fprintf(stderr, "Failed to create channel context field event expression.\n");
+ goto error;
+ }
+
+ break;
+ }
+
+ case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+ {
+ const char *field_name;
+ const char *colon;
+ const char *type_name;
+
+ load_expr_op = load_expr_op->next;
+ assert(load_expr_op);
+ assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL);
+ field_name = load_expr_op->u.symbol;
+ assert(field_name);
+
+ /*
+ * The field name needs to be of the form PROVIDER:TYPE. We
+ * split it here.
+ */
+ colon = strchr(field_name, ':');
+ if (!colon) {
+ fprintf(stderr, "Invalid app-specific context field name: missing colon in `%s`.\n",
+ field_name);
+ goto error;
+ }
+
+ type_name = colon + 1;
+ if (*type_name == '\0') {
+ fprintf(stderr,
+ "Invalid app-specific context field name: missing type name after colon in `%s`.\n",
+ field_name);
+ goto error;
+ }
+
+ provider_name = strndup(field_name, colon - field_name);
+ if (!provider_name) {
+ fprintf(stderr, "Failed to allocate string.\n");
+ goto error;
+ }
+
+ event_expr = lttng_event_expr_app_specific_context_field_create(
+ provider_name, type_name);
+ if (!event_expr) {
+ fprintf(stderr,
+ "Failed to create app-specific context field event expression.\n");
+ goto error;
+ }
+
+ break;
+ }
+
+ default:
+ fprintf(stderr, "%s: unexpected load expr type %d.\n",
+ __func__, load_expr_op->type);
+ abort();
+ }
+
+ load_expr_op = load_expr_op->next;
+
+ /* There may be a single array index after that. */
+ if (load_expr_op->type == IR_LOAD_EXPRESSION_GET_INDEX) {
+ uint64_t index = load_expr_op->u.index;
+ struct lttng_event_expr *index_event_expr;
+
+ index_event_expr = lttng_event_expr_array_field_element_create(event_expr, index);
+ if (!index_event_expr) {
+ fprintf(stderr, "Failed to create array field element event expression.\n");
+ goto error;
+ }
+
+ event_expr = index_event_expr;
+ load_expr_op = load_expr_op->next;
+ }
+
+ switch (load_expr_op->type) {
+ case IR_LOAD_EXPRESSION_LOAD_FIELD:
+ /*
+ * This is what we expect, IR_LOAD_EXPRESSION_LOAD_FIELD is
+ * always found at the end of the chain.
+ */
+ break;
+ case IR_LOAD_EXPRESSION_GET_SYMBOL:
+ fprintf(stderr, "Error: While parsing expression `%s`: Capturing subfields is not supported.\n",
+ capture_str);
+ goto error;
+
+ default:
+ fprintf(stderr, "%s: unexpected load expression operator %s.\n",
+ __func__, ir_load_expression_type_str(load_expr_op->type));
+ abort();
+ }
+
+ goto end;
+
+error:
+ lttng_event_expr_destroy(event_expr);
+ event_expr = NULL;
+
+end:
+ free(provider_name);
+
+ return event_expr;
+}
+
+static
+struct lttng_event_expr *ir_op_load_to_event_expr(struct ir_op *ir,
+ const char *capture_str)
+{
+ struct lttng_event_expr *event_expr = NULL;
+
+ assert(ir->op == IR_OP_LOAD);
+
+ switch (ir->data_type) {
+ case IR_DATA_EXPRESSION:
+ {
+ struct ir_load_expression *ir_load_expr = ir->u.load.u.expression;
+ event_expr = ir_op_load_expr_to_event_expr(ir_load_expr, capture_str);
+ break;
+ }
+
+ default:
+ fprintf(stderr, "%s: unexpected data type: %s.\n", __func__,
+ ir_data_type_str(ir->data_type));
+ abort();
+ }
+
+ return event_expr;
+}
+
+static
+struct lttng_event_expr *ir_op_root_to_event_expr(struct ir_op *ir,
+ const char *capture_str)
+{
+ struct lttng_event_expr *event_expr = NULL;
+
+ assert(ir->op == IR_OP_ROOT);
+ ir = ir->u.root.child;
+
+ switch (ir->op) {
+ case IR_OP_LOAD:
+ event_expr = ir_op_load_to_event_expr(ir, capture_str);
+ break;
+
+ case IR_OP_BINARY:
+ fprintf(stderr, "Error: While parsing expression `%s`: Binary operators are not allowed in capture expressions.\n",
+ capture_str);
+ break;
+
+ case IR_OP_UNARY:
+ fprintf(stderr, "Error: While parsing expression `%s`: Unary operators are not allowed in capture expressions.\n",
+ capture_str);
+ break;
+
+ case IR_OP_LOGICAL:
+ fprintf(stderr, "Error: While parsing expression `%s`: Logical operators are not allowed in capture expressions.\n",
+ capture_str);
+ break;
+
+ default:
+ fprintf(stderr, "%s: unexpected IR op type: %s.\n", __func__,
+ ir_op_type_str(ir->op));
+ abort();
+ }
+
+ return event_expr;
+}
+
+static
+void destroy_event_expr(void *ptr)
+{
+ lttng_event_expr_destroy(ptr);
+}
+
+struct parse_event_rule_res {
+ /* Owned by this */
+ struct lttng_event_rule *er;
+
+ /* Array of `struct lttng_event_expr *` */
+ struct lttng_dynamic_pointer_array capture_descriptors;
+};
+
+static
+struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv)
+{
+ enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
+ enum lttng_event_rule_type event_rule_type = LTTNG_EVENT_RULE_TYPE_UNKNOWN;
+ struct argpar_state *state;
+ struct argpar_item *item = NULL;
+ char *error = NULL;
+ int consumed_args = -1;
+ struct lttng_userspace_probe_location *userspace_probe_location = NULL;
+ struct parse_event_rule_res res = { 0 };
+ struct lttng_event_expr *event_expr = NULL;
+ struct filter_parser_ctx *parser_ctx = NULL;
+
+ /* Was the -a/--all flag provided? */
+ bool all_events = false;
+
+ /* Tracepoint name (non-option argument) */
+ const char *tracepoint_name = NULL;
+
+ /* Holds the argument of --probe / --userspace-probe. */
+ char *source = NULL;
+
+ /* Filter */
+ char *filter = NULL;
+
+ /* Exclude */
+ char *exclude = NULL;
+ char **exclusion_list = NULL;
+
+ /* Log level */
+ char *loglevel_str = NULL;
+ bool loglevel_only = false;
+
+ lttng_dynamic_pointer_array_init(&res.capture_descriptors,
+ destroy_event_expr);
+ state = argpar_state_create(*argc, *argv, event_rule_opt_descrs);
+ if (!state) {
+ ERR("Failed to allocate an argpar state.");
+ goto error;
+ }
+
+ while (true) {
+ enum argpar_state_parse_next_status status;
+
+ ARGPAR_ITEM_DESTROY_AND_RESET(item);
+ status = argpar_state_parse_next(state, &item, &error);
+ if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+ ERR("%s", error);
+ goto error;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+ /* Just stop parsing here. */
+ break;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+ break;
+ }
+
+ assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+ if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+ struct argpar_item_opt *item_opt =
+ (struct argpar_item_opt *) item;
+
+ switch (item_opt->descr->id) {
+ /* Domains */
+ case OPT_USERSPACE:
+ if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_UST)) {
+ goto error;
+ }
+ break;
+
+ case OPT_KERNEL:
+ if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_KERNEL)) {
+ goto error;
+ }
+ break;
+
+ case OPT_LOG4J:
+ if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_LOG4J)) {
+ goto error;
+ }
+ break;
+
+ case OPT_JUL:
+ if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_JUL)) {
+ goto error;
+ }
+ break;
+
+ case OPT_PYTHON:
+ if (!assign_domain_type(&domain_type, LTTNG_DOMAIN_PYTHON)) {
+ goto error;
+ }
+ break;
+
+ /* Event rule types */
+ case OPT_FUNCTION:
+ if (!assign_event_rule_type(&event_rule_type,
+ LTTNG_EVENT_RULE_TYPE_KRETPROBE)) {
+ goto error;
+ }
+ break;
+
+ case OPT_PROBE:
+ if (!assign_event_rule_type(&event_rule_type,
+ LTTNG_EVENT_RULE_TYPE_KPROBE)) {
+ goto error;
+ }
+
+ if (!assign_string(&source, item_opt->arg, "source")) {
+ goto error;
+ }
+
+ break;
+
+ case OPT_USERSPACE_PROBE:
+ if (!assign_event_rule_type(&event_rule_type,
+ LTTNG_EVENT_RULE_TYPE_UPROBE)) {
+ goto error;
+ }
+
+ if (!assign_string(&source, item_opt->arg, "source")) {
+ goto error;
+ }
+ break;
+
+ case OPT_SYSCALL:
+ if (!assign_event_rule_type(&event_rule_type,
+ LTTNG_EVENT_RULE_TYPE_SYSCALL)) {
+ goto error;
+ }
+ break;
+
+ case OPT_TRACEPOINT:
+ if (!assign_event_rule_type(&event_rule_type,
+ LTTNG_EVENT_RULE_TYPE_TRACEPOINT)) {
+ goto error;
+ }
+ break;
+
+ case OPT_ALL:
+ all_events = true;
+ break;
+
+ case OPT_FILTER:
+ if (!assign_string(&filter, item_opt->arg, "--filter/-f")) {
+ goto error;
+ }
+ break;
+
+ case OPT_EXCLUDE:
+ if (!assign_string(&exclude, item_opt->arg, "--exclude/-x")) {
+ goto error;
+ }
+ break;
+
+ case OPT_LOGLEVEL:
+ case OPT_LOGLEVEL_ONLY:
+ if (!assign_string(&loglevel_str, item_opt->arg, "--loglevel/--loglevel-only")) {
+ goto error;
+ }
+
+ loglevel_only = item_opt->descr->id == OPT_LOGLEVEL_ONLY;
+ break;
+
+ case OPT_CAPTURE:
+ {
+ const char *capture_str = item_opt->arg;
+ int ret;
+
+ ret = filter_parser_ctx_create_from_filter_expression(
+ capture_str, &parser_ctx);
+ if (ret) {
+ fprintf(stderr, "Failed to parse capture expression `%s`.\n", capture_str);
+ goto error;
+ }
+
+ event_expr = ir_op_root_to_event_expr(parser_ctx->ir_root,
+ capture_str);
+ if (!event_expr) {
+ /* ir_op_root_to_event_expr has printed an error message. */
+ goto error;
+ }
+
+ ret = lttng_dynamic_pointer_array_add_pointer(
+ &res.capture_descriptors,
+ event_expr);
+ if (ret) {
+ goto error;
+ }
+
+ /* The ownership of event expression was transferred to the dynamic array. */
+ event_expr = NULL;
+
+ break;
+ }
+
+ default:
+ abort();
+ }
+ } else {
+ struct argpar_item_non_opt *item_non_opt =
+ (struct argpar_item_non_opt *) item;
+
+ /*
+ * Don't accept two non-option arguments/tracepoint
+ * names.
+ */
+ if (tracepoint_name) {
+ ERR(
+ "Unexpected argument: %s",
+ item_non_opt->arg);
+ goto error;
+ }
+
+ tracepoint_name = item_non_opt->arg;
+ }
+ }
+
+ if (event_rule_type == LTTNG_EVENT_RULE_TYPE_UNKNOWN) {
+ event_rule_type = LTTNG_EVENT_RULE_TYPE_TRACEPOINT;
+ }
+
+ /*
+ * Option -a is applicable to event rules of type tracepoint and
+ * syscall, and it is equivalent to using "*" as the tracepoint name.
+ */
+ if (all_events) {
+ switch (event_rule_type) {
+ case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+ case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+ break;
+ default:
+ ERR("Can't use -a/--all with event rule of type %s.",
+ lttng_event_rule_type_str(event_rule_type));
+ goto error;
+ }
+
+ if (tracepoint_name) {
+ ERR("Can't provide a tracepoint name with -a/--all.");
+ goto error;
+ }
+
+ /* In which case, it's equivalent to tracepoint name "*". */
+ tracepoint_name = "*";
+ }
+
+ /*
+ * A tracepoint name (or -a, for the event rule types that accept it)
+ * is required.
+ */
+ if (!tracepoint_name) {
+ ERR("Need to provide either a tracepoint name or -a/--all.");
+ goto error;
+ }
+
+ /*
+ * We don't support multiple tracepoint names for now.
+ */
+ if (strchr(tracepoint_name, ',')) {
+ ERR("multiple tracepoint names are not supported at the moment.");
+ goto error;
+ }
+
+ /*
+ * Update *argc and *argv so our caller can keep parsing what follows.
+ */
+ consumed_args = argpar_state_get_ingested_orig_args(state);
+ assert(consumed_args >= 0);
+ *argc -= consumed_args;
+ *argv += consumed_args;
+
+ /* Need to specify a domain. */
+ if (domain_type == LTTNG_DOMAIN_NONE) {
+ ERR("Please specify a domain (-k/-u/-j).");
+ goto error;
+ }
+
+ /* Validate event rule type against domain. */
+ switch (event_rule_type) {
+ case LTTNG_EVENT_RULE_TYPE_KPROBE:
+ case LTTNG_EVENT_RULE_TYPE_KRETPROBE:
+ case LTTNG_EVENT_RULE_TYPE_UPROBE:
+ case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+ if (domain_type != LTTNG_DOMAIN_KERNEL) {
+ ERR("Event type not available for user-space tracing");
+ goto error;
+ }
+ break;
+
+ case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+ break;
+
+ default:
+ abort();
+ }
+
+ /*
+ * Adding a filter to a probe, function or userspace-probe would be
+ * denied by the kernel tracer as it's not supported at the moment. We
+ * do an early check here to warn the user.
+ */
+ if (filter && domain_type == LTTNG_DOMAIN_KERNEL) {
+ switch (event_rule_type) {
+ case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+ case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+ break;
+ default:
+ ERR("Filter expressions are not supported for %s events",
+ lttng_event_rule_type_str(event_rule_type));
+ goto error;
+ }
+ }
+
+ /* If --exclude/-x was passed, split it into an exclusion list. */
+ if (exclude) {
+ if (domain_type != LTTNG_DOMAIN_UST) {
+ ERR("Event name exclusions are not yet implemented for %s events",
+ get_domain_str(domain_type));
+ goto error;
+ }
+
+
+ if (create_exclusion_list_and_validate(tracepoint_name, exclude,
+ &exclusion_list) != 0) {
+ ERR("Failed to create exclusion list.");
+ goto error;
+ }
+ }
+
+ if (loglevel_str && event_rule_type != LTTNG_EVENT_RULE_TYPE_TRACEPOINT) {
+ ERR("Log levels are only application to tracepoint event rules.");
+ goto error;
+ }
+
+ /* Finally, create the event rule object. */
+ switch (event_rule_type) {
+ case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+ {
+ enum lttng_event_rule_status event_rule_status;
+
+ res.er = lttng_event_rule_tracepoint_create(domain_type);
+ if (!res.er) {
+ ERR("Failed to create tracepoint event rule.");
+ goto error;
+ }
+
+ /* Set pattern. */
+ event_rule_status =
+ lttng_event_rule_tracepoint_set_pattern(res.er, tracepoint_name);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set tracepoint pattern.");
+ goto error;
+ }
+
+ /* Set filter. */
+ if (filter) {
+ event_rule_status =
+ lttng_event_rule_tracepoint_set_filter(
+ res.er, filter);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set tracepoint filter expression.");
+ goto error;
+ }
+ }
+
+ /* Set exclusion list. */
+ if (exclusion_list) {
+ int n;
+
+ /* Count number of items in exclusion list. */
+ for (n = 0; exclusion_list[n]; n++);
+
+ event_rule_status =
+ lttng_event_rule_tracepoint_set_exclusions(
+ res.er,
+ n, (const char **) exclusion_list);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set tracepoint exclusion list.");
+ goto error;
+ }
+ }
+
+ if (loglevel_str) {
+ int loglevel;
+
+ if (domain_type == LTTNG_DOMAIN_KERNEL) {
+ ERR("Log levels are not supported by the kernel tracer.");
+ goto error;
+ }
+
+ loglevel = parse_loglevel_string(
+ loglevel_str, domain_type);
+ if (loglevel < 0) {
+ ERR("Failed to parse `%s` as a log level.", loglevel_str);
+ goto error;
+ }
+
+ if (loglevel_only) {
+ event_rule_status =
+ lttng_event_rule_tracepoint_set_loglevel(
+ res.er, loglevel);
+ } else {
+ event_rule_status =
+ lttng_event_rule_tracepoint_set_loglevel_range(
+ res.er, loglevel);
+ }
+
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set log level.");
+ goto error;
+ }
+ }
+
+ break;
+ }
+
+ case LTTNG_EVENT_RULE_TYPE_KPROBE:
+ {
+ enum lttng_event_rule_status event_rule_status;
+
+ res.er = lttng_event_rule_kprobe_create();
+ if (!res.er) {
+ ERR("Failed to create kprobe event rule.");
+ goto error;
+ }
+
+ event_rule_status = lttng_event_rule_kprobe_set_name(res.er, tracepoint_name);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set kprobe event rule's name.");
+ goto error;
+ }
+
+ assert(source);
+ event_rule_status = lttng_event_rule_kprobe_set_source(res.er, source);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set kprobe event rule's source.");
+ goto error;
+ }
+
+ break;
+ }
+
+ case LTTNG_EVENT_RULE_TYPE_UPROBE:
+ {
+ int ret;
+ enum lttng_event_rule_status event_rule_status;
+
+ ret = parse_userspace_probe_opts(source, &userspace_probe_location);
+ if (ret) {
+ ERR("Failed to parse userspace probe location.");
+ goto error;
+ }
+
+ res.er = lttng_event_rule_uprobe_create();
+ if (!res.er) {
+ ERR("Failed to create userspace probe event rule.");
+ goto error;
+ }
+
+ event_rule_status = lttng_event_rule_uprobe_set_location(res.er, userspace_probe_location);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set userspace probe event rule's location.");
+ goto error;
+ }
+
+ event_rule_status = lttng_event_rule_uprobe_set_name(res.er, tracepoint_name);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set userspace probe event rule's name.");
+ goto error;
+ }
+
+ break;
+ }
+
+ case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+ {
+ enum lttng_event_rule_status event_rule_status;
+
+ res.er = lttng_event_rule_syscall_create();
+ if (!res.er) {
+ ERR("Failed to create syscall event rule.");
+ goto error;
+ }
+
+ event_rule_status = lttng_event_rule_syscall_set_pattern(res.er, tracepoint_name);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set syscall event rule's pattern.");
+ goto error;
+ }
+
+ if (filter) {
+ event_rule_status = lttng_event_rule_syscall_set_filter(
+ res.er, filter);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set syscall event rule's filter expression.");
+ goto error;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ ERR("%s: I don't support event rules of type `%s` at the moment.", __func__,
+ lttng_event_rule_type_str(event_rule_type));
+ goto error;
+ }
+
+ goto end;
+
+error:
+ lttng_event_rule_destroy(res.er);
+ res.er = NULL;
+ lttng_dynamic_pointer_array_reset(&res.capture_descriptors);
+
+end:
+ if (parser_ctx) {
+ filter_parser_ctx_free(parser_ctx);
+ }
+
+ lttng_event_expr_destroy(event_expr);
+ argpar_item_destroy(item);
+ free(error);
+ argpar_state_destroy(state);
+ free(filter);
+ free(exclude);
+ free(loglevel_str);
+ strutils_free_null_terminated_array_of_strings(exclusion_list);
+ lttng_userspace_probe_location_destroy(userspace_probe_location);
+ return res;
+}
+
+static
+struct lttng_condition *handle_condition_event(int *argc, const char ***argv)
+{
+ struct parse_event_rule_res res;
+ struct lttng_condition *c;
+ size_t i;
+
+ res = parse_event_rule(argc, argv);
+ if (!res.er) {
+ c = NULL;
+ goto error;
+ }
+
+ c = lttng_condition_event_rule_create(res.er);
+ if (!c) {
+ goto error;
+ }
+
+ /* Event rule ownership moved to `c` */
+ res.er = NULL;
+
+ for (i = 0; i < lttng_dynamic_pointer_array_get_count(&res.capture_descriptors);
+ i++) {
+ enum lttng_condition_status status;
+ struct lttng_event_expr **expr =
+ lttng_dynamic_array_get_element(
+ &res.capture_descriptors.array, i);
+
+ assert(expr);
+ assert(*expr);
+ status = lttng_condition_event_rule_append_capture_descriptor(
+ c, *expr);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ if (status == LTTNG_CONDITION_STATUS_UNSUPPORTED) {
+ ERR("The capture feature is unsupported by the event-rule type");
+ }
+ goto error;
+ }
+
+ /* Ownership of event expression moved to `c` */
+ *expr = NULL;
+ }
+
+ goto end;
+
+error:
+ lttng_condition_destroy(c);
+ c = NULL;
+
+end:
+ lttng_dynamic_pointer_array_reset(&res.capture_descriptors);
+ lttng_event_rule_destroy(res.er);
+ return c;
+}
+
+static
+struct lttng_condition *handle_condition_session_consumed_size(int *argc, const char ***argv)
+{
+ struct lttng_condition *cond = NULL;
+ struct argpar_state *state = NULL;
+ struct argpar_item *item = NULL;
+ const char *threshold_arg = NULL;
+ const char *session_name_arg = NULL;
+ uint64_t threshold;
+ char *error = NULL;
+ enum lttng_condition_status condition_status;
+
+ state = argpar_state_create(*argc, *argv, event_rule_opt_descrs);
+ if (!state) {
+ ERR("Failed to allocate an argpar state.");
+ goto error;
+ }
+
+ while (true) {
+ enum argpar_state_parse_next_status status;
+
+ ARGPAR_ITEM_DESTROY_AND_RESET(item);
+ status = argpar_state_parse_next(state, &item, &error);
+ if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+ ERR("%s", error);
+ goto error;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+ /* Just stop parsing here. */
+ break;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+ break;
+ }
+
+ assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+ if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+ struct argpar_item_opt *item_opt =
+ (struct argpar_item_opt *) item;
+
+ switch (item_opt->descr->id) {
+ default:
+ abort();
+ }
+ } else {
+ struct argpar_item_non_opt *item_non_opt;
+
+ assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
+
+ item_non_opt = (struct argpar_item_non_opt *) item;
+
+ switch (item_non_opt->non_opt_index) {
+ case 0:
+ session_name_arg = item_non_opt->arg;
+ break;
+ case 1:
+ threshold_arg = item_non_opt->arg;
+ break;
+ default:
+ ERR("Unexpected argument `%s`.",
+ item_non_opt->arg);
+ goto error;
+ }
+ }
+ }
+
+ *argc -= argpar_state_get_ingested_orig_args(state);
+ *argv += argpar_state_get_ingested_orig_args(state);
+
+ if (!session_name_arg) {
+ ERR("Missing session name argument.");
+ goto error;
+ }
+
+ if (!threshold_arg) {
+ ERR("Missing threshold argument.");
+ goto error;
+ }
+
+ if (utils_parse_size_suffix(threshold_arg, &threshold) != 0) {
+ ERR("Failed to parse `%s` as a size.", threshold_arg);
+ goto error;
+ }
+
+ cond = lttng_condition_session_consumed_size_create();
+ if (!cond) {
+ ERR("Failed to allocate a session consumed size condition.");
+ goto error;
+ }
+
+ condition_status = lttng_condition_session_consumed_size_set_session_name(
+ cond, session_name_arg);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to set session consumed size condition session name.");
+ goto error;
+ }
+
+
+ condition_status = lttng_condition_session_consumed_size_set_threshold(
+ cond, threshold);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to set session consumed size condition threshold.");
+ goto error;
+ }
+
+ goto end;
+
+error:
+ lttng_condition_destroy(cond);
+ cond = NULL;
+
+end:
+ argpar_state_destroy(state);
+ argpar_item_destroy(item);
+ free(error);
+ return cond;
+}
+
+static
+struct lttng_condition *handle_condition_buffer_usage_high(int *argc, const char ***argv)
+{
+ struct lttng_condition *cond = NULL;
+ struct argpar_state *state = NULL;
+ struct argpar_item *item = NULL;
+ const char *threshold_arg = NULL;
+ const char *session_name_arg = NULL;
+ uint64_t threshold;
+ char *error = NULL;
+ enum lttng_condition_status condition_status;
+
+ state = argpar_state_create(*argc, *argv, event_rule_opt_descrs);
+ if (!state) {
+ ERR("Failed to allocate an argpar state.");
+ goto error;
+ }
+
+ while (true) {
+ enum argpar_state_parse_next_status status;
+
+ ARGPAR_ITEM_DESTROY_AND_RESET(item);
+ status = argpar_state_parse_next(state, &item, &error);
+ if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+ ERR("%s", error);
+ goto error;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+ /* Just stop parsing here. */
+ break;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+ break;
+ }
+
+ assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+ if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+ struct argpar_item_opt *item_opt =
+ (struct argpar_item_opt *) item;
+
+ switch (item_opt->descr->id) {
+ default:
+ abort();
+ }
+ } else {
+ struct argpar_item_non_opt *item_non_opt;
+
+ assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
+
+ item_non_opt = (struct argpar_item_non_opt *) item;
+
+ switch (item_non_opt->non_opt_index) {
+ case 0:
+ session_name_arg = item_non_opt->arg;
+ break;
+ case 1:
+ threshold_arg = item_non_opt->arg;
+ break;
+ default:
+ ERR("Unexpected argument `%s`.",
+ item_non_opt->arg);
+ goto error;
+ }
+ }
+ }
+
+ *argc -= argpar_state_get_ingested_orig_args(state);
+ *argv += argpar_state_get_ingested_orig_args(state);
+
+ if (!session_name_arg) {
+ ERR("Missing session name argument.");
+ goto error;
+ }
+
+ if (!threshold_arg) {
+ ERR("Missing threshold argument.");
+ goto error;
+ }
+
+ if (utils_parse_size_suffix(threshold_arg, &threshold) != 0) {
+ ERR("Failed to parse `%s` as a size.", threshold_arg);
+ goto error;
+ }
+
+ cond = lttng_condition_session_consumed_size_create();
+ if (!cond) {
+ ERR("Failed to allocate a session consumed size condition.");
+ goto error;
+ }
+
+ condition_status = lttng_condition_session_consumed_size_set_session_name(
+ cond, session_name_arg);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to set session consumed size condition session name.");
+ goto error;
+ }
+
+ condition_status = lttng_condition_session_consumed_size_set_threshold(
+ cond, threshold);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to set session consumed size condition threshold.");
+ goto error;
+ }
+
+ goto end;
+
+error:
+ lttng_condition_destroy(cond);
+ cond = NULL;
+
+end:
+ argpar_state_destroy(state);
+ argpar_item_destroy(item);
+ free(error);
+ return cond;
+}
+
+static
+struct lttng_condition *handle_condition_buffer_usage_low(int *argc, const char ***argv)
+{
+ return NULL;
+}
+
+static
+struct lttng_condition *handle_condition_session_rotation_ongoing(int *argc, const char ***argv)
+{
+ return NULL;
+}
+
+static
+struct lttng_condition *handle_condition_session_rotation_completed(int *argc, const char ***argv)
+{
+ return NULL;
+}
+
+struct condition_descr {
+ const char *name;
+ struct lttng_condition *(*handler) (int *argc, const char ***argv);
+};
+
+static const
+struct condition_descr condition_descrs[] = {
+ { "on-event", handle_condition_event },
+ { "on-session-consumed-size", handle_condition_session_consumed_size },
+ { "on-buffer-usage-high", handle_condition_buffer_usage_high },
+ { "on-buffer-usage-low", handle_condition_buffer_usage_low },
+ { "on-session-rotation-ongoing", handle_condition_session_rotation_ongoing },
+ { "on-session-rotation-completed", handle_condition_session_rotation_completed },
+};
+
+static
+struct lttng_condition *parse_condition(int *argc, const char ***argv)
+{
+ int i;
+ struct lttng_condition *cond;
+ const char *condition_name;
+ const struct condition_descr *descr = NULL;
+
+ if (*argc == 0) {
+ ERR("Missing condition name.");
+ goto error;
+ }
+
+ condition_name = (*argv)[0];
+
+ (*argc)--;
+ (*argv)++;
+
+ for (i = 0; i < ARRAY_SIZE(condition_descrs); i++) {
+ if (strcmp(condition_name, condition_descrs[i].name) == 0) {
+ descr = &condition_descrs[i];
+ break;
+ }
+ }
+
+ if (!descr) {
+ ERR("Unknown condition name: %s", condition_name);
+ goto error;
+ }
+
+ cond = descr->handler(argc, argv);
+ if (!cond) {
+ /* The handler has already printed an error message. */
+ goto error;
+ }
+
+ goto end;
+error:
+ cond = NULL;
+end:
+ return cond;
+}
+
+
+static
+struct lttng_action *handle_action_notify(int *argc, const char ***argv)
+{
+ return lttng_action_notify_create();
+}
+
+static const struct argpar_opt_descr no_opt_descrs[] = {
+ ARGPAR_OPT_DESCR_SENTINEL
+};
+
+/*
+ * Generic handler for a kind of action that takes a session name as its sole
+ * argument.
+ */
+
+static
+struct lttng_action *handle_action_simple_session(
+ int *argc, const char ***argv,
+ struct lttng_action *(*create_action_cb)(void),
+ enum lttng_action_status (*set_session_name_cb)(struct lttng_action *, const char *),
+ const char *action_name)
+{
+ struct lttng_action *action = NULL;
+ struct argpar_state *state = NULL;
+ struct argpar_item *item = NULL;
+ const char *session_name_arg = NULL;
+ char *error = NULL;
+ enum lttng_action_status action_status;
+
+ state = argpar_state_create(*argc, *argv, no_opt_descrs);
+ if (!state) {
+ ERR("Failed to allocate an argpar state.");
+ goto error;
+ }
+
+ while (true) {
+ enum argpar_state_parse_next_status status;
+ struct argpar_item_non_opt *item_non_opt;
+
+ ARGPAR_ITEM_DESTROY_AND_RESET(item);
+ status = argpar_state_parse_next(state, &item, &error);
+ if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+ ERR("%s", error);
+ goto error;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+ /* Just stop parsing here. */
+ break;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+ break;
+ }
+
+ assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+ assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
+
+ item_non_opt = (struct argpar_item_non_opt *) item;
+
+ switch (item_non_opt->non_opt_index) {
+ case 0:
+ session_name_arg = item_non_opt->arg;
+ break;
+ default:
+ ERR("Unexpected argument `%s`.",
+ item_non_opt->arg);
+ goto error;
+ }
+ }
+
+ *argc -= argpar_state_get_ingested_orig_args(state);
+ *argv += argpar_state_get_ingested_orig_args(state);
+
+ if (!session_name_arg) {
+ ERR("Missing session name.");
+ goto error;
+ }
+
+ action = create_action_cb();
+ if (!action) {
+ ERR(
+ "Failed to allocate %s session action.", action_name);
+ goto error;
+ }
+
+ action_status = set_session_name_cb(action, session_name_arg);
+ if (action_status != LTTNG_ACTION_STATUS_OK) {
+ ERR(
+ "Failed to set action %s session's session name.",
+ action_name);
+ goto error;
+ }
+
+ goto end;
+
+error:
+ lttng_action_destroy(action);
+ action = NULL;
+
+end:
+ return action;
+}
+
+static
+struct lttng_action *handle_action_start_session(int *argc,
+ const char ***argv)
+{
+ return handle_action_simple_session(argc, argv,
+ lttng_action_start_session_create,
+ lttng_action_start_session_set_session_name,
+ "start");
+}
+
+static
+struct lttng_action *handle_action_stop_session(int *argc,
+ const char ***argv)
+{
+ return handle_action_simple_session(argc, argv,
+ lttng_action_stop_session_create,
+ lttng_action_stop_session_set_session_name,
+ "stop");
+}
+
+static
+struct lttng_action *handle_action_rotate_session(int *argc,
+ const char ***argv)
+{
+ return handle_action_simple_session(argc, argv,
+ lttng_action_rotate_session_create,
+ lttng_action_rotate_session_set_session_name,
+ "rotate");
+}
+
+static const struct argpar_opt_descr snapshot_action_opt_descrs[] = {
+ { OPT_NAME, 'n', "name", true },
+ { OPT_MAX_SIZE, 'm', "max-size", true },
+ { OPT_CTRL_URL, '\0', "ctrl-url", true },
+ { OPT_DATA_URL, '\0', "data-url", true },
+ ARGPAR_OPT_DESCR_SENTINEL
+};
+
+static
+struct lttng_action *handle_action_snapshot_session(int *argc,
+ const char ***argv)
+{
+ struct lttng_action *action = NULL;
+ struct argpar_state *state = NULL;
+ struct argpar_item *item = NULL;
+ const char *session_name_arg = NULL;
+ char *snapshot_name_arg = NULL;
+ char *ctrl_url_arg = NULL;
+ char *data_url_arg = NULL;
+ char *max_size_arg = NULL;
+ const char *url_arg = NULL;
+ char *error = NULL;
+ enum lttng_action_status action_status;
+ struct lttng_snapshot_output *snapshot_output = NULL;
+ int ret;
+
+ state = argpar_state_create(*argc, *argv, snapshot_action_opt_descrs);
+ if (!state) {
+ ERR("Failed to allocate an argpar state.");
+ goto error;
+ }
+
+ while (true) {
+ enum argpar_state_parse_next_status status;
+
+ ARGPAR_ITEM_DESTROY_AND_RESET(item);
+ status = argpar_state_parse_next(state, &item, &error);
+ if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+ ERR("%s", error);
+ goto error;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+ /* Just stop parsing here. */
+ break;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+ break;
+ }
+
+ assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+ if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+ struct argpar_item_opt *item_opt =
+ (struct argpar_item_opt *) item;
+
+ switch (item_opt->descr->id) {
+ case OPT_NAME:
+ if (!assign_string(&snapshot_name_arg, item_opt->arg, "--name/-n")) {
+ goto error;
+ }
+ break;
+
+ case OPT_MAX_SIZE:
+ if (!assign_string(&max_size_arg, item_opt->arg, "--max-size/-m")) {
+ goto error;
+ }
+ break;
+
+ case OPT_CTRL_URL:
+ if (!assign_string(&ctrl_url_arg, item_opt->arg, "--ctrl-url")) {
+ goto error;
+ }
+ break;
+
+ case OPT_DATA_URL:
+ if (!assign_string(&data_url_arg, item_opt->arg, "--data-url")) {
+ goto error;
+ }
+ break;
+
+ default:
+ abort();
+ }
+ } else {
+ struct argpar_item_non_opt *item_non_opt;
+
+ assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
+
+ item_non_opt = (struct argpar_item_non_opt *) item;
+
+ switch (item_non_opt->non_opt_index) {
+ case 0:
+ session_name_arg = item_non_opt->arg;
+ break;
+
+ // FIXME: the use of a non-option argument for this is to
+ // follow the syntax of `lttng snapshot record`. But otherwise,
+ // I think an option argument would be best.
+ case 1:
+ url_arg = item_non_opt->arg;
+ break;
+
+ default:
+ ERR("Unexpected argument `%s`.",
+ item_non_opt->arg);
+ goto error;
+ }
+ }
+ }
+
+ *argc -= argpar_state_get_ingested_orig_args(state);
+ *argv += argpar_state_get_ingested_orig_args(state);
+
+ if (!session_name_arg) {
+ ERR("Missing session name.");
+ goto error;
+ }
+
+ /* --ctrl-url and --data-url must come in pair. */
+ if (ctrl_url_arg && !data_url_arg) {
+ ERR("--ctrl-url is specified, but --data-url is missing.");
+ goto error;
+ }
+
+ if (!ctrl_url_arg && data_url_arg) {
+ ERR("--data-url is specified, but --ctrl-url is missing.");
+ goto error;
+ }
+
+ /* --ctrl-url/--data-url and the non-option URL are mutually exclusive. */
+ if (ctrl_url_arg && url_arg) {
+ ERR("Both --ctrl-url/--data-url and the non-option URL argument "
+ "can't be used together.");
+ goto error;
+ }
+
+ /*
+ * Did the user specify an option that implies using a
+ * custom/unregistered output?
+ */
+ if (url_arg || ctrl_url_arg) {
+ snapshot_output = lttng_snapshot_output_create();
+ if (!snapshot_output) {
+ ERR("Failed to allocate a snapshot output.");
+ goto error;
+ }
+ }
+
+ action = lttng_action_snapshot_session_create();
+ if (!action) {
+ ERR(
+ "Failed to allocate snapshot session action.");
+ goto error;
+ }
+
+ action_status = lttng_action_snapshot_session_set_session_name(
+ action, session_name_arg);
+ if (action_status != LTTNG_ACTION_STATUS_OK) {
+ ERR(
+ "Failed to set action snapshot session's session name.");
+ goto error;
+ }
+
+ if (snapshot_name_arg) {
+ if (!snapshot_output) {
+ ERR("Can't provide a snapshot output name without a snapshot output destination.");
+ goto error;
+ }
+
+ ret = lttng_snapshot_output_set_name(snapshot_name_arg, snapshot_output);
+ if (ret != 0) {
+ ERR("Failed to set name of snapshot output.");
+ goto error;
+ }
+ }
+
+ if (max_size_arg) {
+ uint64_t max_size;
+
+ if (!snapshot_output) {
+ ERR("Can't provide a snapshot output max size without a snapshot output destination.");
+ goto error;
+ }
+
+ ret = utils_parse_size_suffix(max_size_arg, &max_size);
+ if (ret != 0) {
+ ERR("Failed to parse `%s` as a size.", max_size_arg);
+ goto error;
+ }
+
+ ret = lttng_snapshot_output_set_size(max_size, snapshot_output);
+ if (ret != 0) {
+ ERR("Failed to set snapshot output's max size.");
+ goto error;
+ }
+ }
+
+ if (url_arg) {
+ /* One argument form, either net:// / net6:// or a local file path. */
+
+ if (strncmp(url_arg, "net://", strlen("net://")) == 0 ||
+ strncmp(url_arg, "net6://", strlen("net6://")) == 0) {
+ ret = lttng_snapshot_output_set_network_url(
+ url_arg, snapshot_output);
+ if (ret != 0) {
+ ERR("Failed to parse %s as a network URL.", url_arg);
+ goto error;
+ }
+ } else {
+ ret = lttng_snapshot_output_set_local_path(
+ url_arg, snapshot_output);
+ if (ret != 0) {
+ ERR("Failed to parse %s as a local path.", url_arg);
+ goto error;
+ }
+ }
+ }
+
+ if (ctrl_url_arg) {
+ /*
+ * Two argument form, network output with separate control and
+ * data URLs.
+ */
+ ret = lttng_snapshot_output_set_network_urls(
+ ctrl_url_arg, data_url_arg, snapshot_output);
+ if (ret != 0) {
+ ERR("Failed to parse `%s` and `%s` as control and data URLs.",
+ ctrl_url_arg, data_url_arg);
+ goto error;
+ }
+ }
+
+ if (snapshot_output) {
+ action_status = lttng_action_snapshot_session_set_output(
+ action, snapshot_output);
+ if (action_status != LTTNG_ACTION_STATUS_OK) {
+ ERR("Failed to set snapshot session action's output.");
+ goto error;
+ }
+
+ /* Ownership of `snapshot_output` has been transferred to the action. */
+ snapshot_output = NULL;
+ }
+
+ goto end;
+
+error:
+ lttng_action_destroy(action);
+ action = NULL;
+
+end:
+ free(snapshot_name_arg);
+ free(ctrl_url_arg);
+ free(data_url_arg);
+ free(snapshot_output);
+ return action;
+}
+
+struct action_descr {
+ const char *name;
+ struct lttng_action *(*handler) (int *argc, const char ***argv);
+};
+
+static const
+struct action_descr action_descrs[] = {
+ { "notify", handle_action_notify },
+ { "start-session", handle_action_start_session },
+ { "stop-session", handle_action_stop_session },
+ { "rotate-session", handle_action_rotate_session },
+ { "snapshot-session", handle_action_snapshot_session },
+};
+
+static
+struct lttng_action *parse_action(int *argc, const char ***argv)
+{
+ int i;
+ struct lttng_action *action;
+ const char *action_name;
+ const struct action_descr *descr = NULL;
+
+ if (*argc == 0) {
+ ERR("Missing action name.");
+ goto error;
+ }
+
+ action_name = (*argv)[0];
+
+ (*argc)--;
+ (*argv)++;
+
+ for (i = 0; i < ARRAY_SIZE(action_descrs); i++) {
+ if (strcmp(action_name, action_descrs[i].name) == 0) {
+ descr = &action_descrs[i];
+ break;
+ }
+ }
+
+ if (!descr) {
+ ERR("Unknown action name: %s", action_name);
+ goto error;
+ }
+
+ action = descr->handler(argc, argv);
+ if (!action) {
+ /* The handler has already printed an error message. */
+ goto error;
+ }
+
+ goto end;
+error:
+ action = NULL;
+end:
+ return action;
+}
+
+static const
+struct argpar_opt_descr add_trigger_options[] = {
+ { OPT_HELP, 'h', "help", false },
+ { OPT_LIST_OPTIONS, '\0', "list-options", false },
+ { OPT_CONDITION, '\0', "condition", false },
+ { OPT_ACTION, '\0', "action", false },
+ { OPT_ID, '\0', "id", true },
+ { OPT_FIRE_ONCE_AFTER, '\0', "fire-once-after", true },
+ { OPT_FIRE_EVERY, '\0', "fire-every", true },
+ ARGPAR_OPT_DESCR_SENTINEL,
+};
+
+static
+void lttng_actions_destructor(void *p)
+{
+ struct lttng_action *action = p;
+
+ lttng_action_destroy(action);
+}
+
+int cmd_add_trigger(int argc, const char **argv)
+{
+ int ret;
+ int my_argc = argc - 1;
+ const char **my_argv = argv + 1;
+ struct lttng_condition *condition = NULL;
+ struct lttng_dynamic_pointer_array actions;
+ struct argpar_state *argpar_state = NULL;
+ struct argpar_item *argpar_item = NULL;
+ struct lttng_action *action_group = NULL;
+ struct lttng_action *action = NULL;
+ struct lttng_trigger *trigger = NULL;
+ char *error = NULL;
+ char *id = NULL;
+ int i;
+ char *fire_once_after_str = NULL;
+ char *fire_every_str = NULL;
+
+ lttng_dynamic_pointer_array_init(&actions, lttng_actions_destructor);
+
+ while (true) {
+ enum argpar_state_parse_next_status status;
+ struct argpar_item_opt *item_opt;
+ int ingested_args;
+
+ argpar_state_destroy(argpar_state);
+ argpar_state = argpar_state_create(my_argc, my_argv,
+ add_trigger_options);
+ if (!argpar_state) {
+ ERR("Failed to create argpar state.");
+ goto error;
+ }
+
+ status = argpar_state_parse_next(argpar_state, &argpar_item, &error);
+
+ if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+ ERR("%s", error);
+ goto error;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+ ERR("%s", error);
+ goto error;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+ break;
+ }
+
+ assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+ if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) {
+ struct argpar_item_non_opt *item_non_opt =
+ (struct argpar_item_non_opt *) argpar_item;
+
+ ERR("Unexpected argument `%s`.",
+ item_non_opt->arg);
+ goto error;
+ }
+
+ item_opt = (struct argpar_item_opt *) argpar_item;
+
+ ingested_args = argpar_state_get_ingested_orig_args(
+ argpar_state);
+
+ my_argc -= ingested_args;
+ my_argv += ingested_args;
+
+ switch (item_opt->descr->id) {
+ case OPT_HELP:
+ SHOW_HELP();
+ ret = 0;
+ goto end;
+
+ case OPT_LIST_OPTIONS:
+ list_cmd_options_argpar(stdout, add_trigger_options);
+ ret = 0;
+ goto end;
+
+ case OPT_CONDITION:
+ {
+ if (condition) {
+ ERR("A --condition was already given.");
+ goto error;
+ }
+
+ condition = parse_condition(&my_argc, &my_argv);
+ if (!condition) {
+ /*
+ * An error message was already printed by
+ * parse_condition.
+ */
+ goto error;
+ }
+
+ break;
+ }
+
+ case OPT_ACTION:
+ {
+ action = parse_action(&my_argc, &my_argv);
+ if (!action) {
+ /*
+ * An error message was already printed by
+ * parse_condition.
+ */
+ goto error;
+ }
+
+ ret = lttng_dynamic_pointer_array_add_pointer(
+ &actions, action);
+ if (ret) {
+ ERR("Failed to add pointer to pointer array.");
+ goto error;
+ }
+
+ /* Ownership of the action was transferred to the group. */
+ action = NULL;
+
+ break;
+ }
+
+ case OPT_ID:
+ {
+ if (!assign_string(&id, item_opt->arg, "--id")) {
+ goto error;
+ }
+
+ break;
+ }
+
+ case OPT_FIRE_ONCE_AFTER:
+ {
+ if (!assign_string(&fire_once_after_str, item_opt->arg,
+ "--fire-once-after")) {
+ goto error;
+ }
+ break;
+ }
+
+ case OPT_FIRE_EVERY:
+ {
+ if (!assign_string(&fire_every_str, item_opt->arg,
+ "--fire-every")) {
+ goto error;
+ }
+ break;
+ }
+
+ default:
+ abort();
+ }
+ }
+
+ if (!condition) {
+ ERR("Missing --condition.");
+ goto error;
+ }
+
+ if (lttng_dynamic_pointer_array_get_count(&actions) == 0) {
+ ERR("Need at least one --action.");
+ goto error;
+ }
+
+ if (fire_every_str && fire_once_after_str) {
+ ERR("Can't specify both --fire-once-after and --fire-every.");
+ goto error;
+ }
+
+ action_group = lttng_action_group_create();
+ if (!action_group) {
+ goto error;
+ }
+
+ for (i = 0; i < lttng_dynamic_pointer_array_get_count(&actions); i++) {
+ enum lttng_action_status status;
+
+ action = lttng_dynamic_pointer_array_steal_pointer(&actions, i);
+
+ status = lttng_action_group_add_action(
+ action_group, action);
+ if (status != LTTNG_ACTION_STATUS_OK) {
+ goto error;
+ }
+
+ /* Ownership of the action was transferred to the group. */
+ action = NULL;
+ }
+
+
+ trigger = lttng_trigger_create(condition, action_group);
+ if (!trigger) {
+ goto error;
+ }
+
+ if (id) {
+ enum lttng_trigger_status trigger_status =
+ lttng_trigger_set_name(trigger, id);
+ if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+ ERR("Failed to set trigger id.");
+ goto error;
+ }
+ }
+
+ if (fire_once_after_str) {
+ unsigned long long threshold;
+ enum lttng_trigger_status trigger_status;
+
+ if (utils_parse_unsigned_long_long(fire_once_after_str, &threshold) != 0) {
+ ERR("Failed to parse `%s` as an integer.", fire_once_after_str);
+ goto error;
+ }
+
+ trigger_status = lttng_trigger_set_firing_policy(trigger,
+ LTTNG_TRIGGER_FIRE_ONCE_AFTER_N, threshold);
+ if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+ ERR("Failed to set trigger's firing policy.");
+ goto error;
+ }
+ }
+
+ if (fire_every_str) {
+ unsigned long long threshold;
+ enum lttng_trigger_status trigger_status;
+
+ if (utils_parse_unsigned_long_long(fire_every_str, &threshold) != 0) {
+ ERR("Failed to parse `%s` as an integer.", fire_every_str);
+ goto error;
+ }
+
+ trigger_status = lttng_trigger_set_firing_policy(trigger,
+ LTTNG_TRIGGER_FIRE_EVERY_N, threshold);
+ if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+ ERR("Failed to set trigger's firing policy.");
+ goto error;
+ }
+ }
+
+ ret = lttng_register_trigger(trigger);
+ if (ret) {
+ ERR("Failed to register trigger: %s.",
+ lttng_strerror(ret));
+ goto error;
+ }
+
+ MSG("Trigger registered successfully.");
+
+ goto end;
+
+error:
+ ret = 1;
+
+end:
+ argpar_state_destroy(argpar_state);
+ lttng_dynamic_pointer_array_reset(&actions);
+ lttng_condition_destroy(condition);
+ lttng_action_destroy(action_group);
+ lttng_trigger_destroy(trigger);
+ free(id);
+ free(fire_once_after_str);
+ free(fire_every_str);
+ // TODO: check what else to free
+
+ return ret;
+}
/* Mi dependancy */
#include <common/mi-lttng.h>
+#include <lttng/event-internal.h>
+
#include "../command.h"
+#include "../uprobe.h"
#if (LTTNG_SYMBOL_NAME_LEN == 256)
#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255"
return ret;
}
-/*
- * Walk the directories in the PATH environment variable to find the target
- * binary passed as parameter.
- *
- * On success, the full path of the binary is copied in binary_full_path out
- * parameter. This buffer is allocated by the caller and must be at least
- * LTTNG_PATH_MAX bytes long.
- * On failure, returns -1;
- */
-static int walk_command_search_path(const char *binary, char *binary_full_path)
-{
- char *tentative_binary_path = NULL;
- char *command_search_path = NULL;
- char *curr_search_dir_end = NULL;
- char *curr_search_dir = NULL;
- struct stat stat_output;
- int ret = 0;
-
- command_search_path = lttng_secure_getenv("PATH");
- if (!command_search_path) {
- ret = -1;
- goto end;
- }
-
- /*
- * Duplicate the $PATH string as the char pointer returned by getenv() should
- * not be modified.
- */
- command_search_path = strdup(command_search_path);
- if (!command_search_path) {
- ret = -1;
- goto end;
- }
-
- /*
- * This char array is used to concatenate path to binary to look for
- * the binary.
- */
- tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
- if (!tentative_binary_path) {
- ret = -1;
- goto alloc_error;
- }
-
- curr_search_dir = command_search_path;
- do {
- /*
- * Split on ':'. The return value of this call points to the
- * matching character.
- */
- curr_search_dir_end = strchr(curr_search_dir, ':');
- if (curr_search_dir_end != NULL) {
- /*
- * Add a NULL byte to the end of the first token so it
- * can be used as a string.
- */
- curr_search_dir_end[0] = '\0';
- }
-
- /* Empty the tentative path */
- memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char));
-
- /*
- * Build the tentative path to the binary using the current
- * search directory and the name of the binary.
- */
- ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s",
- curr_search_dir, binary);
- if (ret < 0) {
- goto free_binary_path;
- }
- if (ret < LTTNG_PATH_MAX) {
- /*
- * Use STAT(2) to see if the file exists.
- */
- ret = stat(tentative_binary_path, &stat_output);
- if (ret == 0) {
- /*
- * Verify that it is a regular file or a
- * symlink and not a special file (e.g.
- * device).
- */
- if (S_ISREG(stat_output.st_mode)
- || S_ISLNK(stat_output.st_mode)) {
- /*
- * Found a match, set the out parameter
- * and return success.
- */
- ret = lttng_strncpy(binary_full_path,
- tentative_binary_path,
- LTTNG_PATH_MAX);
- if (ret == -1) {
- ERR("Source path does not fit "
- "in destination buffer.");
- }
- goto free_binary_path;
- }
- }
- }
- /* Go to the next entry in the $PATH variable. */
- curr_search_dir = curr_search_dir_end + 1;
- } while (curr_search_dir_end != NULL);
-
-free_binary_path:
- free(tentative_binary_path);
-alloc_error:
- free(command_search_path);
-end:
- return ret;
-}
-
-/*
- * Check if the symbol field passed by the user is in fact an address or an
- * offset from a symbol. Those two instrumentation types are not supported yet.
- * It's expected to be a common mistake because of the existing --probe option
- * that does support these formats.
- *
- * Here are examples of these unsupported formats for the --userspace-probe
- * option:
- * elf:/path/to/binary:0x400430
- * elf:/path/to/binary:4194364
- * elf:/path/to/binary:my_symbol+0x323
- * elf:/path/to/binary:my_symbol+43
- */
-static int warn_userspace_probe_syntax(const char *symbol)
-{
- int ret;
-
- /* Check if the symbol field is an hex address. */
- ret = sscanf(symbol, "0x%*x");
- if (ret > 0) {
- /* If there is a match, print a warning and return an error. */
- ERR("Userspace probe on address not supported yet.");
- ret = CMD_UNSUPPORTED;
- goto error;
- }
-
- /* Check if the symbol field is an decimal address. */
- ret = sscanf(symbol, "%*u");
- if (ret > 0) {
- /* If there is a match, print a warning and return an error. */
- ERR("Userspace probe on address not supported yet.");
- ret = CMD_UNSUPPORTED;
- goto error;
- }
-
- /* Check if the symbol field is symbol+hex_offset. */
- ret = sscanf(symbol, "%*[^+]+0x%*x");
- if (ret > 0) {
- /* If there is a match, print a warning and return an error. */
- ERR("Userspace probe on symbol+offset not supported yet.");
- ret = CMD_UNSUPPORTED;
- goto error;
- }
-
- /* Check if the symbol field is symbol+decimal_offset. */
- ret = sscanf(symbol, "%*[^+]+%*u");
- if (ret > 0) {
- /* If there is a match, print a warning and return an error. */
- ERR("Userspace probe on symbol+offset not supported yet.");
- ret = CMD_UNSUPPORTED;
- goto error;
- }
-
- ret = 0;
-
-error:
- return ret;
-}
-
-/*
- * Parse userspace probe options
- * Set the userspace probe fields in the lttng_event struct and set the
- * target_path to the path to the binary.
- */
-static int parse_userspace_probe_opts(struct lttng_event *ev, char *opt)
-{
- int ret = CMD_SUCCESS;
- int num_token;
- char **tokens;
- char *target_path = NULL;
- char *unescaped_target_path = NULL;
- char *real_target_path = NULL;
- char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL;
- struct lttng_userspace_probe_location *probe_location = NULL;
- struct lttng_userspace_probe_location_lookup_method *lookup_method =
- NULL;
-
- if (opt == NULL) {
- ret = CMD_ERROR;
- goto end;
- }
-
- switch (ev->type) {
- case LTTNG_EVENT_USERSPACE_PROBE:
- break;
- default:
- assert(0);
- }
-
- /*
- * userspace probe fields are separated by ':'.
- */
- tokens = strutils_split(opt, ':', 1);
- num_token = strutils_array_of_strings_len(tokens);
-
- /*
- * Early sanity check that the number of parameter is between 2 and 4
- * inclusively.
- * elf:PATH:SYMBOL
- * std:PATH:PROVIDER_NAME:PROBE_NAME
- * PATH:SYMBOL (same behavior as ELF)
- */
- if (num_token < 2 || num_token > 4) {
- ret = CMD_ERROR;
- goto end_string;
- }
-
- /*
- * Looking up the first parameter will tell the technique to use to
- * interpret the userspace probe/function description.
- */
- switch (num_token) {
- case 2:
- /* When the probe type is omitted we assume ELF for now. */
- case 3:
- if (num_token == 3 && strcmp(tokens[0], "elf") == 0) {
- target_path = tokens[1];
- symbol_name = tokens[2];
- } else if (num_token == 2) {
- target_path = tokens[0];
- symbol_name = tokens[1];
- } else {
- ret = CMD_ERROR;
- goto end_string;
- }
- lookup_method =
- lttng_userspace_probe_location_lookup_method_function_elf_create();
- if (!lookup_method) {
- WARN("Failed to create ELF lookup method");
- ret = CMD_ERROR;
- goto end_string;
- }
- break;
- case 4:
- if (strcmp(tokens[0], "sdt") == 0) {
- target_path = tokens[1];
- provider_name = tokens[2];
- probe_name = tokens[3];
- } else {
- ret = CMD_ERROR;
- goto end_string;
- }
- lookup_method =
- lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
- if (!lookup_method) {
- WARN("Failed to create SDT lookup method");
- ret = CMD_ERROR;
- goto end_string;
- }
- break;
- default:
- ret = CMD_ERROR;
- goto end_string;
- }
-
- /* strutils_unescape_string allocates a new char *. */
- unescaped_target_path = strutils_unescape_string(target_path, 0);
- if (!unescaped_target_path) {
- ret = CMD_ERROR;
- goto end_destroy_lookup_method;
- }
-
- /*
- * If there is not forward slash in the path. Walk the $PATH else
- * expand.
- */
- if (strchr(unescaped_target_path, '/') == NULL) {
- /* Walk the $PATH variable to find the targeted binary. */
- real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
- if (!real_target_path) {
- PERROR("Error allocating path buffer");
- ret = CMD_ERROR;
- goto end_destroy_lookup_method;
- }
- ret = walk_command_search_path(unescaped_target_path, real_target_path);
- if (ret) {
- ERR("Binary not found.");
- ret = CMD_ERROR;
- goto end_destroy_lookup_method;
- }
- } else {
- /*
- * Expand references to `/./` and `/../`. This function does not check
- * if the file exists. This call returns an allocated buffer on
- * success.
- */
- real_target_path = utils_expand_path_keep_symlink(unescaped_target_path);
- if (!real_target_path) {
- ERR("Error expanding the path to binary.");
- ret = CMD_ERROR;
- goto end_destroy_lookup_method;
- }
-
- /*
- * Check if the file exists using access(2), If it does not,
- * return an error.
- */
- ret = access(real_target_path, F_OK);
- if (ret) {
- ERR("Cannot find binary at path: %s.", real_target_path);
- ret = CMD_ERROR;
- goto end_destroy_lookup_method;
- }
- }
-
- switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) {
- case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
- /*
- * Check for common mistakes in userspace probe description syntax.
- */
- ret = warn_userspace_probe_syntax(symbol_name);
- if (ret) {
- goto end_destroy_lookup_method;
- }
-
- probe_location = lttng_userspace_probe_location_function_create(
- real_target_path, symbol_name, lookup_method);
- if (!probe_location) {
- WARN("Failed to create function probe location");
- ret = CMD_ERROR;
- goto end_destroy_lookup_method;
- }
-
- /* Ownership transferred to probe_location. */
- lookup_method = NULL;
-
- ret = lttng_event_set_userspace_probe_location(ev, probe_location);
- if (ret) {
- WARN("Failed to set probe location on event");
- ret = CMD_ERROR;
- goto end_destroy_location;
- }
- break;
- case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
- probe_location = lttng_userspace_probe_location_tracepoint_create(
- real_target_path, provider_name, probe_name, lookup_method);
- if (!probe_location) {
- WARN("Failed to create function probe location");
- ret = CMD_ERROR;
- goto end_destroy_lookup_method;
- }
-
- /* Ownership transferred to probe_location. */
- lookup_method = NULL;
-
- ret = lttng_event_set_userspace_probe_location(ev, probe_location);
- if (ret) {
- WARN("Failed to set probe location on event");
- ret = CMD_ERROR;
- goto end_destroy_location;
- }
- break;
- default:
- ret = CMD_ERROR;
- goto end_destroy_lookup_method;
- }
-
- /* Successful parsing, now clean up everything and return. */
- goto end_string;
-
-end_destroy_location:
- lttng_userspace_probe_location_destroy(probe_location);
-end_destroy_lookup_method:
- lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
-end_string:
- strutils_free_null_terminated_array_of_strings(tokens);
- /*
- * Freeing both char * here makes the error handling simplier. free()
- * performs not action if the pointer is NULL.
- */
- free(real_target_path);
- free(unescaped_target_path);
-end:
- return ret;
-}
-
/*
* Maps LOG4j loglevel from string to value
*/
-static int loglevel_log4j_str_to_value(const char *inputstr)
+LTTNG_HIDDEN
+int loglevel_log4j_str_to_value(const char *inputstr)
{
int i = 0;
char str[LTTNG_SYMBOL_NAME_LEN];
/*
* Maps JUL loglevel from string to value
*/
-static int loglevel_jul_str_to_value(const char *inputstr)
+LTTNG_HIDDEN
+int loglevel_jul_str_to_value(const char *inputstr)
{
int i = 0;
char str[LTTNG_SYMBOL_NAME_LEN];
/*
* Maps Python loglevel from string to value
*/
-static int loglevel_python_str_to_value(const char *inputstr)
+LTTNG_HIDDEN
+int loglevel_python_str_to_value(const char *inputstr)
{
int i = 0;
char str[LTTNG_SYMBOL_NAME_LEN];
/*
* Maps loglevel from string to value
*/
-static
+LTTNG_HIDDEN
int loglevel_str_to_value(const char *inputstr)
{
int i = 0;
return ret;
}
-static
+/*
+ * FIXME: find a good place to declare this since add trigger also uses it
+ */
+LTTNG_HIDDEN
+int create_exclusion_list_and_validate(const char *event_name,
+ const char *exclusions_arg,
+ char ***exclusion_list);
+
+
+LTTNG_HIDDEN
int create_exclusion_list_and_validate(const char *event_name,
const char *exclusions_arg,
char ***exclusion_list)
struct lttng_event *ev;
struct lttng_domain dom;
char **exclusion_list = NULL;
+ struct lttng_userspace_probe_location *uprobe_loc = NULL;
memset(&dom, 0, sizeof(dom));
}
break;
case LTTNG_EVENT_USERSPACE_PROBE:
- ret = parse_userspace_probe_opts(ev, opt_userspace_probe);
+ assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE);
+
+ ret = parse_userspace_probe_opts(opt_userspace_probe, &uprobe_loc);
if (ret) {
switch (ret) {
case CMD_UNSUPPORTED:
}
goto error;
}
+
+ ret = lttng_event_set_userspace_probe_location(ev, uprobe_loc);
+ if (ret) {
+ WARN("Failed to set probe location on event");
+ ret = CMD_ERROR;
+ goto error;
+ }
+
+ /* Ownership of the uprobe location was transferred to the event. */
+ uprobe_loc = NULL;
break;
case LTTNG_EVENT_FUNCTION:
ret = parse_probe_opts(ev, opt_function);
}
lttng_destroy_handle(handle);
strutils_free_null_terminated_array_of_strings(exclusion_list);
+ lttng_userspace_probe_location_destroy(uprobe_loc);
/* Overwrite ret with error_holder if there was an actual error with
* enabling an event.
--- /dev/null
+#include <stdio.h>
+
+#include "../command.h"
+
+#include "common/argpar/argpar.h"
+#include "common/mi-lttng.h"
+#include "lttng/condition/condition-internal.h"
+#include "lttng/condition/event-rule.h"
+#include "lttng/domain-internal.h"
+#include "lttng/event-rule/event-rule-internal.h"
+#include "lttng/event-rule/kprobe.h"
+#include "lttng/event-rule/kprobe-internal.h"
+#include "lttng/event-rule/syscall.h"
+#include "lttng/event-rule/tracepoint.h"
+#include "lttng/event-rule/uprobe.h"
+#include "lttng/trigger/trigger-internal.h"
+
+#ifdef LTTNG_EMBED_HELP
+static const char help_msg[] =
+#include <lttng-list-trigger.1.h>
+;
+#endif
+
+enum {
+ OPT_HELP,
+ OPT_LIST_OPTIONS,
+};
+
+static const
+struct argpar_opt_descr list_trigger_options[] = {
+ { OPT_HELP, 'h', "help", false },
+ { OPT_LIST_OPTIONS, '\0', "list-options", false },
+ ARGPAR_OPT_DESCR_SENTINEL,
+};
+
+static
+void print_event_rule_tracepoint(const struct lttng_event_rule *event_rule)
+{
+ enum lttng_event_rule_status event_rule_status;
+ enum lttng_domain_type domain_type;
+ const char *pattern;
+ const char *filter;
+ int loglevel;
+ unsigned int exclusions_count;
+ int i;
+
+ event_rule_status = lttng_event_rule_tracepoint_get_pattern(
+ event_rule, &pattern);
+ assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+ event_rule_status = lttng_event_rule_tracepoint_get_domain_type(
+ event_rule, &domain_type);
+ assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+ _MSG(" rule: %s (type: tracepoint, domain: %s", pattern,
+ lttng_domain_type_str(domain_type));
+
+ event_rule_status = lttng_event_rule_tracepoint_get_filter(
+ event_rule, &filter);
+ if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) {
+ _MSG(", filter: %s", filter);
+ } else {
+ assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET);
+ }
+
+ event_rule_status = lttng_event_rule_tracepoint_get_loglevel(
+ event_rule, &loglevel);
+ if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) {
+ enum lttng_loglevel_type loglevel_type;
+ const char *loglevel_op;
+
+ event_rule_status = lttng_event_rule_tracepoint_get_loglevel_type(
+ event_rule, &loglevel_type);
+ assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+ assert(loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE ||
+ loglevel_type == LTTNG_EVENT_LOGLEVEL_SINGLE);
+
+ loglevel_op = (loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE ? "<=" : "==");
+
+ _MSG(", log level %s %s", loglevel_op,
+ mi_lttng_loglevel_string(loglevel, domain_type));
+ } else {
+ assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET);
+ }
+
+ event_rule_status = lttng_event_rule_tracepoint_get_exclusions_count(
+ event_rule, &exclusions_count);
+ assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+ if (exclusions_count > 0) {
+ _MSG(", exclusions: ");
+ for (i = 0; i < exclusions_count; i++) {
+ const char *exclusion;
+
+ event_rule_status = lttng_event_rule_tracepoint_get_exclusion_at_index(
+ event_rule, i, &exclusion);
+ assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+ _MSG("%s%s", i > 0 ? "," : "", exclusion);
+ }
+ }
+
+ MSG(")");
+}
+
+static
+void print_event_rule_kprobe(const struct lttng_event_rule *event_rule)
+{
+ enum lttng_event_rule_status event_rule_status;
+ const char *name, *symbol_name;
+ uint64_t offset;
+
+ event_rule_status = lttng_event_rule_kprobe_get_name(event_rule, &name);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to get kprobe event rule's name.");
+ goto end;
+ }
+
+ assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_KPROBE);
+
+ _MSG(" rule: %s (type: probe, location: ", name);
+
+ // FIXME: When the location has been specified by address, this field
+ // contains the address as a string. The only downside is that we are
+ // missing a `0x` prefix.
+ symbol_name = lttng_event_rule_kprobe_get_symbol_name(event_rule);
+ _MSG("%s", symbol_name);
+
+ offset = lttng_event_rule_kprobe_get_offset(event_rule);
+ if (offset > 0) {
+ _MSG("+0x%" PRIx64, offset);
+ }
+
+ MSG(")");
+
+end:
+ return;
+}
+
+static
+void print_event_rule_uprobe(const struct lttng_event_rule *event_rule)
+{
+ enum lttng_event_rule_status event_rule_status;
+ const char *name;
+ const struct lttng_userspace_probe_location *location;
+ enum lttng_userspace_probe_location_type userspace_probe_location_type;
+
+ assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_UPROBE);
+
+ event_rule_status = lttng_event_rule_uprobe_get_name(event_rule, &name);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to get uprobe event rule's name.");
+ goto end;
+ }
+
+ event_rule_status = lttng_event_rule_uprobe_get_location(event_rule, &location);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to get uprobe event rule's location.");
+ goto end;
+ }
+
+ _MSG(" rule: %s (type: userspace probe, location: ", name);
+
+ userspace_probe_location_type =
+ lttng_userspace_probe_location_get_type(location);
+
+ switch (userspace_probe_location_type) {
+ case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
+ {
+ const char *binary_path, *function_name;
+
+ binary_path = lttng_userspace_probe_location_function_get_binary_path(location);
+ function_name = lttng_userspace_probe_location_function_get_function_name(location);
+
+ _MSG("%s:%s", binary_path, function_name);
+ break;
+ }
+
+ case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
+ _MSG("SDT not implemented yet");
+ break;
+
+ default:
+ abort();
+ }
+
+ MSG(")");
+
+end:
+ return;
+}
+
+static
+void print_event_rule_syscall(const struct lttng_event_rule *event_rule)
+{
+ const char *pattern, *filter;
+ enum lttng_event_rule_status event_rule_status;
+
+ assert(lttng_event_rule_get_type(event_rule) == LTTNG_EVENT_RULE_TYPE_SYSCALL);
+
+ event_rule_status = lttng_event_rule_syscall_get_pattern(event_rule, &pattern);
+ assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+ _MSG(" - rule: %s (type: syscall", pattern);
+
+ event_rule_status = lttng_event_rule_syscall_get_filter(
+ event_rule, &filter);
+ if (event_rule_status == LTTNG_EVENT_RULE_STATUS_OK) {
+ _MSG(", filter: %s", filter);
+ } else {
+ assert(event_rule_status == LTTNG_EVENT_RULE_STATUS_UNSET);
+ }
+
+ MSG(")");
+}
+
+static
+void print_event_rule(const struct lttng_event_rule *event_rule)
+{
+ enum lttng_event_rule_type event_rule_type =
+ lttng_event_rule_get_type(event_rule);
+
+ switch (event_rule_type) {
+ case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+ print_event_rule_tracepoint(event_rule);
+ break;
+
+ case LTTNG_EVENT_RULE_TYPE_KPROBE:
+ print_event_rule_kprobe(event_rule);
+ break;
+
+ case LTTNG_EVENT_RULE_TYPE_UPROBE:
+ print_event_rule_uprobe(event_rule);
+ break;
+
+ case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+ print_event_rule_syscall(event_rule);
+ break;
+
+ default:
+ abort();
+ }
+}
+
+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);
+ _MSG("%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);
+ _MSG("$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);
+
+ _MSG("$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);
+
+ _MSG("[%u]", index);
+
+ break;
+ }
+
+ default:
+ abort();
+ }
+}
+
+static
+void print_condition_event_rule_hit(const struct lttng_condition *condition)
+{
+ const struct lttng_event_rule *event_rule;
+ enum lttng_condition_status condition_status;
+ unsigned int cap_desc_count, i;
+
+ condition_status =
+ lttng_condition_event_rule_get_rule(condition, &event_rule);
+ assert(condition_status == LTTNG_CONDITION_STATUS_OK);
+
+ print_event_rule(event_rule);
+
+ condition_status
+ = lttng_condition_event_rule_get_capture_descriptor_count(
+ condition, &cap_desc_count);
+ assert(condition_status == LTTNG_CONDITION_STATUS_OK);
+
+ if (cap_desc_count > 0) {
+ MSG(" captures:");
+
+ for (i = 0; i < cap_desc_count; i++) {
+ const struct lttng_event_expr *cap_desc =
+ lttng_condition_event_rule_get_capture_descriptor_at_index(
+ condition, i);
+
+ _MSG(" - ");
+ print_one_event_expr(cap_desc);
+ MSG("");
+ }
+ }
+}
+
+static
+void print_one_action(const struct lttng_action *action)
+{
+ enum lttng_action_type action_type;
+ enum lttng_action_status action_status;
+ const char *value;
+
+ action_type = lttng_action_get_type(action);
+ assert(action_type != LTTNG_ACTION_TYPE_GROUP);
+
+ switch (action_type) {
+ case LTTNG_ACTION_TYPE_NOTIFY:
+ MSG("notify");
+ break;
+
+ case LTTNG_ACTION_TYPE_START_SESSION:
+ action_status = lttng_action_start_session_get_session_name(
+ action, &value);
+ assert(action_status == LTTNG_ACTION_STATUS_OK);
+ MSG("start session `%s`", value);
+ break;
+
+ case LTTNG_ACTION_TYPE_STOP_SESSION:
+ action_status = lttng_action_stop_session_get_session_name(
+ action, &value);
+ assert(action_status == LTTNG_ACTION_STATUS_OK);
+ MSG("stop session `%s`", value);
+ break;
+
+ case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+ action_status = lttng_action_rotate_session_get_session_name(
+ action, &value);
+ assert(action_status == LTTNG_ACTION_STATUS_OK);
+ MSG("rotate session `%s`", value);
+ break;
+
+ case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+ {
+ const struct lttng_snapshot_output *output;
+
+ action_status = lttng_action_snapshot_session_get_session_name(
+ action, &value);
+ assert(action_status == LTTNG_ACTION_STATUS_OK);
+ _MSG("snapshot session `%s`", value);
+
+ action_status = lttng_action_snapshot_session_get_output_const(
+ action, &output);
+ if (action_status == LTTNG_ACTION_STATUS_OK) {
+ const char *name;
+ uint64_t max_size;
+ const char *ctrl_url, *data_url;
+ bool starts_with_file, starts_with_net, starts_with_net6;
+
+ ctrl_url = lttng_snapshot_output_get_ctrl_url(output);
+ assert(ctrl_url && strlen(ctrl_url) > 0);
+
+ data_url = lttng_snapshot_output_get_data_url(output);
+ assert(data_url);
+
+ starts_with_file = strncmp(ctrl_url, "file://", strlen("file://")) == 0;
+ starts_with_net = strncmp(ctrl_url, "net://", strlen("net://")) == 0;
+ starts_with_net6 = strncmp(ctrl_url, "net6://", strlen("net6://")) == 0;
+
+ if (ctrl_url[0] == '/' || starts_with_file) {
+ if (starts_with_file) {
+ ctrl_url += strlen("file://");
+ }
+
+ _MSG(", path: %s", ctrl_url);
+ } else if (starts_with_net || starts_with_net6) {
+ _MSG(", url: %s", ctrl_url);
+ } else {
+ assert(strlen(data_url) > 0);
+
+ _MSG(", control url: %s, data url: %s", ctrl_url, data_url);
+ }
+
+ name = lttng_snapshot_output_get_name(output);
+ assert(name);
+ if (strlen(name) > 0) {
+ _MSG(", name: %s", name);
+ }
+
+ max_size = lttng_snapshot_output_get_maxsize(output);
+ if (max_size != -1ULL) {
+ _MSG(", max size: %" PRIu64, max_size);
+ }
+ }
+
+ MSG("");
+ break;
+ }
+
+ default:
+ abort();
+ }
+}
+
+static
+void print_one_trigger(const struct lttng_trigger *trigger)
+{
+ const struct lttng_condition *condition;
+ enum lttng_condition_type condition_type;
+ const struct lttng_action *action;
+ enum lttng_action_type action_type;
+ enum lttng_trigger_status trigger_status;
+ const char *name;
+ enum lttng_trigger_firing_policy_type policy_type;
+ unsigned long long threshold;
+
+ trigger_status = lttng_trigger_get_name(trigger, &name);
+ assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+ MSG("- id: %s", name);
+
+ trigger_status = lttng_trigger_get_firing_policy(trigger,
+ &policy_type, &threshold);
+ if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+ ERR("Failed to get trigger's firing policy.");
+ goto end;
+ }
+
+ switch (policy_type) {
+ case LTTNG_TRIGGER_FIRE_EVERY_N:
+ if (threshold > 1) {
+ MSG(" firing policy: after every %llu occurences", threshold);
+ }
+ break;
+
+ case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N:
+ MSG(" firing policy: once after %llu occurences", threshold);
+ break;
+
+ default:
+ abort();
+ }
+
+ condition = lttng_trigger_get_const_condition(trigger);
+ condition_type = lttng_condition_get_type(condition);
+ MSG(" condition: %s",
+ lttng_condition_type_str(condition_type));
+
+ switch (condition_type) {
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ print_condition_event_rule_hit(condition);
+ break;
+
+ default:
+ MSG(" (condition type not handled in %s)", __func__);
+ break;
+ }
+
+ action = lttng_trigger_get_const_action(trigger);
+ action_type = lttng_action_get_type(action);
+ if (action_type == LTTNG_ACTION_TYPE_GROUP) {
+ enum lttng_action_status action_status;
+ unsigned int count, i;
+
+ MSG(" actions:");
+
+ action_status = lttng_action_group_get_count(action, &count);
+ assert(action_status == LTTNG_ACTION_STATUS_OK);
+
+ for (i = 0; i < count; i++) {
+ const struct lttng_action *subaction =
+ lttng_action_group_get_at_index_const(action, i);
+
+ _MSG(" ");
+ print_one_action(subaction);
+ }
+ } else {
+ _MSG(" action:");
+ print_one_action(action);
+ }
+
+end:
+ return;
+}
+
+static
+int compare_triggers_by_name(const void *a, const void *b)
+{
+ const struct lttng_trigger *trigger_a = *((const struct lttng_trigger **) a);
+ const struct lttng_trigger *trigger_b = *((const struct lttng_trigger **) b);
+ const char *name_a, *name_b;
+ enum lttng_trigger_status trigger_status;
+
+ trigger_status = lttng_trigger_get_name(trigger_a, &name_a);
+ assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+ trigger_status = lttng_trigger_get_name(trigger_b, &name_b);
+ assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+ return strcmp(name_a, name_b);
+}
+
+int cmd_list_triggers(int argc, const char **argv)
+{
+ int ret;
+ struct argpar_parse_ret argpar_parse_ret = { 0 };
+ struct lttng_triggers *triggers = NULL;
+ int i;
+ const struct lttng_trigger **sorted_triggers = NULL;
+ enum lttng_trigger_status trigger_status;
+ unsigned int num_triggers;
+
+ argpar_parse_ret = argpar_parse(argc - 1, argv + 1,
+ list_trigger_options, true);
+ if (!argpar_parse_ret.items) {
+ ERR("%s", argpar_parse_ret.error);
+ goto error;
+ }
+
+ for (i = 0; i < argpar_parse_ret.items->n_items; i++) {
+ struct argpar_item *item = argpar_parse_ret.items->items[i];
+
+ if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+ struct argpar_item_opt *item_opt =
+ (struct argpar_item_opt *) item;
+
+ switch (item_opt->descr->id) {
+ case OPT_HELP:
+ SHOW_HELP();
+ ret = 0;
+ goto end;
+
+ case OPT_LIST_OPTIONS:
+ list_cmd_options_argpar(stdout,
+ list_trigger_options);
+ ret = 0;
+ goto end;
+
+ default:
+ abort();
+ }
+
+ } else {
+ struct argpar_item_non_opt *item_non_opt =
+ (struct argpar_item_non_opt *) item;
+
+ ERR("Unexpected argument: %s", item_non_opt->arg);
+ }
+ }
+
+ ret = lttng_list_triggers(&triggers);
+ if (ret != 0) {
+ ERR("Error listing triggers: %s.",
+ lttng_strerror(ret));
+ goto error;
+ }
+
+ trigger_status = lttng_triggers_get_count(triggers, &num_triggers);
+ if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+ ERR("Failed to get trigger count.");
+ goto error;
+ }
+
+ sorted_triggers = calloc(num_triggers, sizeof(struct lttng_trigger *));
+ if (!sorted_triggers) {
+ ERR("Failed to allocate array of struct lttng_trigger *.");
+ goto error;
+ }
+
+ for (i = 0; i < num_triggers; i++) {
+ sorted_triggers[i] = lttng_triggers_get_at_index(triggers, i);
+ }
+
+ qsort(sorted_triggers, num_triggers, sizeof(struct lttng_trigger *),
+ compare_triggers_by_name);
+
+ for (i = 0; i < num_triggers; i++) {
+ print_one_trigger(sorted_triggers[i]);
+ }
+
+ goto end;
+
+error:
+ ret = 1;
+
+end:
+ argpar_parse_ret_fini(&argpar_parse_ret);
+ lttng_triggers_destroy(triggers);
+
+ return ret;
+}
--- /dev/null
+#include <stdio.h>
+
+#include "../command.h"
+
+#include "common/argpar/argpar.h"
+
+#ifdef LTTNG_EMBED_HELP
+static const char help_msg[] =
+#include <lttng-remove-trigger.1.h>
+;
+#endif
+
+enum {
+ OPT_HELP,
+ OPT_LIST_OPTIONS,
+};
+
+static const
+struct argpar_opt_descr remove_trigger_options[] = {
+ { OPT_HELP, 'h', "help", false },
+ { OPT_LIST_OPTIONS, '\0', "list-options", false },
+ ARGPAR_OPT_DESCR_SENTINEL,
+};
+
+int cmd_remove_trigger(int argc, const char **argv)
+{
+ int ret;
+ struct argpar_parse_ret argpar_parse_ret = { 0 };
+ const char *id = NULL;
+ int i;
+ struct lttng_triggers *triggers = NULL;
+ unsigned int triggers_count;
+ enum lttng_trigger_status trigger_status;
+ const struct lttng_trigger *trigger_to_remove = NULL;
+
+ argpar_parse_ret = argpar_parse(argc - 1, argv + 1,
+ remove_trigger_options, true);
+ if (!argpar_parse_ret.items) {
+ ERR("%s", argpar_parse_ret.error);
+ goto error;
+ }
+
+ for (i = 0; i < argpar_parse_ret.items->n_items; i++) {
+ struct argpar_item *item = argpar_parse_ret.items->items[i];
+
+ if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+ struct argpar_item_opt *item_opt =
+ (struct argpar_item_opt *) item;
+
+ switch (item_opt->descr->id) {
+ case OPT_HELP:
+ SHOW_HELP();
+ ret = 0;
+ goto end;
+
+ case OPT_LIST_OPTIONS:
+ list_cmd_options_argpar(stdout,
+ remove_trigger_options);
+ ret = 0;
+ goto end;
+
+ default:
+ abort();
+ }
+
+ } else {
+ struct argpar_item_non_opt *item_non_opt =
+ (struct argpar_item_non_opt *) item;
+
+ if (id) {
+ ERR("Unexpected argument: %s", item_non_opt->arg);
+ goto error;
+ }
+
+ id = item_non_opt->arg;
+ }
+ }
+
+ if (!id) {
+ ERR("Missing `id` argument.");
+ goto error;
+ }
+
+ ret = lttng_list_triggers(&triggers);
+ if (ret != 0) {
+ ERR("Failed to get the list of triggers.");
+ goto error;
+ }
+
+ trigger_status = lttng_triggers_get_count(triggers, &triggers_count);
+ assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+ for (i = 0; i < triggers_count; i++) {
+ const struct lttng_trigger *trigger;
+ const char *trigger_name;
+
+ trigger = lttng_triggers_get_at_index(triggers, i);
+ trigger_status = lttng_trigger_get_name(trigger, &trigger_name);
+ assert(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+ if (strcmp(trigger_name, id) == 0) {
+ trigger_to_remove = trigger;
+ break;
+ }
+ }
+
+ if (!trigger_to_remove) {
+ ERR("Couldn't find trigger with id `%s`.", id);
+ goto error;
+ }
+
+ ret = lttng_unregister_trigger(trigger_to_remove);
+ if (ret != 0) {
+ ERR("Failed to unregister trigger `%s`.", id);
+ goto error;
+ }
+
+ MSG("Removed trigger `%s`.", id);
+
+ ret = 0;
+ goto end;
+
+error:
+ ret = 1;
+
+end:
+ argpar_parse_ret_fini(&argpar_parse_ret);
+ lttng_triggers_destroy(triggers);
+
+ return ret;
+}
/* First level command */
static struct cmd_struct commands[] = {
{ "add-context", cmd_add_context},
+ { "add-trigger", cmd_add_trigger},
{ "create", cmd_create},
{ "clear", cmd_clear},
{ "destroy", cmd_destroy},
{ "enable-event", cmd_enable_events},
{ "help", NULL},
{ "list", cmd_list},
+ { "list-triggers", cmd_list_triggers},
{ "load", cmd_load},
{ "metadata", cmd_metadata},
{ "regenerate", cmd_regenerate},
+ { "remove-trigger", cmd_remove_trigger},
{ "rotate", cmd_rotate},
{ "enable-rotation", cmd_enable_rotation},
{ "disable-rotation", cmd_disable_rotation},
--- /dev/null
+/*
+ * Copyright (C) 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "uprobe.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "common/compat/getenv.h"
+#include "common/string-utils/string-utils.h"
+#include "common/utils.h"
+#include "lttng/constant.h"
+
+#include "command.h"
+
+/*
+ * Walk the directories in the PATH environment variable to find the target
+ * binary passed as parameter.
+ *
+ * On success, the full path of the binary is copied in binary_full_path out
+ * parameter. This buffer is allocated by the caller and must be at least
+ * LTTNG_PATH_MAX bytes long.
+ * On failure, returns -1;
+ */
+static
+int walk_command_search_path(const char *binary, char *binary_full_path)
+{
+ char *tentative_binary_path = NULL;
+ char *command_search_path = NULL;
+ char *curr_search_dir_end = NULL;
+ char *curr_search_dir = NULL;
+ struct stat stat_output;
+ int ret = 0;
+
+ command_search_path = lttng_secure_getenv("PATH");
+ if (!command_search_path) {
+ ret = -1;
+ goto end;
+ }
+
+ /*
+ * Duplicate the $PATH string as the char pointer returned by getenv() should
+ * not be modified.
+ */
+ command_search_path = strdup(command_search_path);
+ if (!command_search_path) {
+ ret = -1;
+ goto end;
+ }
+
+ /*
+ * This char array is used to concatenate path to binary to look for
+ * the binary.
+ */
+ tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
+ if (!tentative_binary_path) {
+ ret = -1;
+ goto alloc_error;
+ }
+
+ curr_search_dir = command_search_path;
+ do {
+ /*
+ * Split on ':'. The return value of this call points to the
+ * matching character.
+ */
+ curr_search_dir_end = strchr(curr_search_dir, ':');
+ if (curr_search_dir_end != NULL) {
+ /*
+ * Add a NULL byte to the end of the first token so it
+ * can be used as a string.
+ */
+ curr_search_dir_end[0] = '\0';
+ }
+
+ /* Empty the tentative path */
+ memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char));
+
+ /*
+ * Build the tentative path to the binary using the current
+ * search directory and the name of the binary.
+ */
+ ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s",
+ curr_search_dir, binary);
+ if (ret < 0) {
+ goto free_binary_path;
+ }
+ if (ret < LTTNG_PATH_MAX) {
+ /*
+ * Use STAT(2) to see if the file exists.
+ */
+ ret = stat(tentative_binary_path, &stat_output);
+ if (ret == 0) {
+ /*
+ * Verify that it is a regular file or a
+ * symlink and not a special file (e.g.
+ * device).
+ */
+ if (S_ISREG(stat_output.st_mode)
+ || S_ISLNK(stat_output.st_mode)) {
+ /*
+ * Found a match, set the out parameter
+ * and return success.
+ */
+ ret = lttng_strncpy(binary_full_path,
+ tentative_binary_path,
+ LTTNG_PATH_MAX);
+ if (ret == -1) {
+ ERR("Source path does not fit "
+ "in destination buffer.");
+ }
+ goto free_binary_path;
+ }
+ }
+ }
+ /* Go to the next entry in the $PATH variable. */
+ curr_search_dir = curr_search_dir_end + 1;
+ } while (curr_search_dir_end != NULL);
+
+free_binary_path:
+ free(tentative_binary_path);
+alloc_error:
+ free(command_search_path);
+end:
+ return ret;
+}
+
+/*
+ * Check if the symbol field passed by the user is in fact an address or an
+ * offset from a symbol. Those two instrumentation types are not supported yet.
+ * It's expected to be a common mistake because of the existing --probe option
+ * that does support these formats.
+ *
+ * Here are examples of these unsupported formats for the --userspace-probe
+ * option:
+ * elf:/path/to/binary:0x400430
+ * elf:/path/to/binary:4194364
+ * elf:/path/to/binary:my_symbol+0x323
+ * elf:/path/to/binary:my_symbol+43
+ */
+static
+int warn_userspace_probe_syntax(const char *symbol)
+{
+ int ret;
+
+ /* Check if the symbol field is an hex address. */
+ ret = sscanf(symbol, "0x%*x");
+ if (ret > 0) {
+ /* If there is a match, print a warning and return an error. */
+ ERR("Userspace probe on address not supported yet.");
+ ret = CMD_UNSUPPORTED;
+ goto error;
+ }
+
+ /* Check if the symbol field is an decimal address. */
+ ret = sscanf(symbol, "%*u");
+ if (ret > 0) {
+ /* If there is a match, print a warning and return an error. */
+ ERR("Userspace probe on address not supported yet.");
+ ret = CMD_UNSUPPORTED;
+ goto error;
+ }
+
+ /* Check if the symbol field is symbol+hex_offset. */
+ ret = sscanf(symbol, "%*[^+]+0x%*x");
+ if (ret > 0) {
+ /* If there is a match, print a warning and return an error. */
+ ERR("Userspace probe on symbol+offset not supported yet.");
+ ret = CMD_UNSUPPORTED;
+ goto error;
+ }
+
+ /* Check if the symbol field is symbol+decimal_offset. */
+ ret = sscanf(symbol, "%*[^+]+%*u");
+ if (ret > 0) {
+ /* If there is a match, print a warning and return an error. */
+ ERR("Userspace probe on symbol+offset not supported yet.");
+ ret = CMD_UNSUPPORTED;
+ goto error;
+ }
+
+ ret = 0;
+
+error:
+ return ret;
+}
+
+/*
+ * Parse userspace probe options
+ * Set the userspace probe fields in the lttng_event struct and set the
+ * target_path to the path to the binary.
+ */
+LTTNG_HIDDEN
+int parse_userspace_probe_opts(const char *opt,
+ struct lttng_userspace_probe_location **probe_location)
+{
+ int ret = CMD_SUCCESS;
+ int num_token;
+ char **tokens = NULL;
+ char *target_path = NULL;
+ char *unescaped_target_path = NULL;
+ char *real_target_path = NULL;
+ char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL;
+ struct lttng_userspace_probe_location *probe_location_local = NULL;
+ struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
+
+ assert(opt);
+
+ /*
+ * userspace probe fields are separated by ':'.
+ */
+ tokens = strutils_split(opt, ':', 1);
+ num_token = strutils_array_of_strings_len(tokens);
+
+ /*
+ * Early sanity check that the number of parameter is between 2 and 4
+ * inclusively.
+ * elf:PATH:SYMBOL
+ * std:PATH:PROVIDER_NAME:PROBE_NAME
+ * PATH:SYMBOL (same behavior as ELF)
+ */
+ if (num_token < 2 || num_token > 4) {
+ ret = CMD_ERROR;
+ goto end;
+ }
+
+ /*
+ * Looking up the first parameter will tell the technique to use to
+ * interpret the userspace probe/function description.
+ */
+ switch (num_token) {
+ case 2:
+ /* When the probe type is omitted we assume ELF for now. */
+ case 3:
+ if (num_token == 3 && strcmp(tokens[0], "elf") == 0) {
+ target_path = tokens[1];
+ symbol_name = tokens[2];
+ } else if (num_token == 2) {
+ target_path = tokens[0];
+ symbol_name = tokens[1];
+ } else {
+ ret = CMD_ERROR;
+ goto end;
+ }
+ lookup_method =
+ lttng_userspace_probe_location_lookup_method_function_elf_create();
+ if (!lookup_method) {
+ WARN("Failed to create ELF lookup method");
+ ret = CMD_ERROR;
+ goto end;
+ }
+ break;
+ case 4:
+ if (strcmp(tokens[0], "sdt") == 0) {
+ target_path = tokens[1];
+ provider_name = tokens[2];
+ probe_name = tokens[3];
+ } else {
+ ret = CMD_ERROR;
+ goto end;
+ }
+ lookup_method =
+ lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
+ if (!lookup_method) {
+ WARN("Failed to create SDT lookup method");
+ ret = CMD_ERROR;
+ goto end;
+ }
+ break;
+ default:
+ ret = CMD_ERROR;
+ goto end;
+ }
+
+ /* strutils_unescape_string allocates a new char *. */
+ unescaped_target_path = strutils_unescape_string(target_path, 0);
+ if (!unescaped_target_path) {
+ ret = CMD_ERROR;
+ goto end;
+ }
+
+ /*
+ * If there is not forward slash in the path. Walk the $PATH else
+ * expand.
+ */
+ if (strchr(unescaped_target_path, '/') == NULL) {
+ /* Walk the $PATH variable to find the targeted binary. */
+ real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
+ if (!real_target_path) {
+ PERROR("Error allocating path buffer");
+ ret = CMD_ERROR;
+ goto end;
+ }
+ ret = walk_command_search_path(unescaped_target_path, real_target_path);
+ if (ret) {
+ ERR("Binary not found.");
+ ret = CMD_ERROR;
+ goto end;
+ }
+ } else {
+ /*
+ * Expand references to `/./` and `/../`. This function does not check
+ * if the file exists. This call returns an allocated buffer on
+ * success.
+ */
+ real_target_path = utils_expand_path_keep_symlink(unescaped_target_path);
+ if (!real_target_path) {
+ ERR("Error expanding the path to binary.");
+ ret = CMD_ERROR;
+ goto end;
+ }
+
+ /*
+ * Check if the file exists using access(2), If it does not,
+ * return an error.
+ */
+ ret = access(real_target_path, F_OK);
+ if (ret) {
+ ERR("Cannot find binary at path: %s.", real_target_path);
+ ret = CMD_ERROR;
+ goto end;
+ }
+ }
+
+ switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) {
+ case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+ /*
+ * Check for common mistakes in userspace probe description syntax.
+ */
+ ret = warn_userspace_probe_syntax(symbol_name);
+ if (ret) {
+ goto end;
+ }
+
+ probe_location_local = lttng_userspace_probe_location_function_create(
+ real_target_path, symbol_name, lookup_method);
+ if (!probe_location_local) {
+ WARN("Failed to create function probe location");
+ ret = CMD_ERROR;
+ goto end;
+ }
+
+ /* Ownership transferred to probe_location. */
+ lookup_method = NULL;
+ break;
+ case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+ probe_location_local = lttng_userspace_probe_location_tracepoint_create(
+ real_target_path, provider_name, probe_name, lookup_method);
+ if (!probe_location_local) {
+ WARN("Failed to create function probe location");
+ ret = CMD_ERROR;
+ goto end;
+ }
+
+ /* Ownership transferred to probe_location. */
+ lookup_method = NULL;
+ break;
+ default:
+ ret = CMD_ERROR;
+ goto end;
+ }
+
+ /*
+ * Everything went fine, transfer ownership of probe location to
+ * caller.
+ */
+ *probe_location = probe_location_local;
+ probe_location_local = NULL;
+
+end:
+ lttng_userspace_probe_location_destroy(probe_location_local);
+ lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
+ strutils_free_null_terminated_array_of_strings(tokens);
+ /*
+ * Freeing both char * here makes the error handling simplier. free()
+ * performs not action if the pointer is NULL.
+ */
+ free(real_target_path);
+ free(unescaped_target_path);
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef SRC_BIN_LTTNG_UPROBE_H
+#define SRC_BIN_LTTNG_UPROBE_H
+
+#include <common/macros.h>
+
+struct lttng_userspace_probe_location;
+
+LTTNG_HIDDEN
+int parse_userspace_probe_opts(const char *opt,
+ struct lttng_userspace_probe_location **uprobe_loc);
+
+#endif /* SRC_BIN_LTTNG_UPROBE_H */
}
}
+/*
+ * Same as list_cmd_options, but for options specified for argpar.
+ */
+void list_cmd_options_argpar(FILE *ofp, const struct argpar_opt_descr *options)
+{
+ int i;
+ const struct argpar_opt_descr *option = NULL;
+
+ for (i = 0; options[i].long_name != NULL; i++) {
+ option = &options[i];
+
+ fprintf(ofp, "--%s\n", option->long_name);
+
+ if (isprint(option->short_name)) {
+ fprintf(ofp, "-%c\n", option->short_name);
+ }
+ }
+}
+
/*
* fls: returns the position of the most significant bit.
* Returns 0 if no bit is set, else returns the position of the most
#define _LTTNG_UTILS_H
#include <popt.h>
+#include "common/argpar/argpar.h"
#include <lttng/lttng.h>
char *get_session_name_quiet(void);
void list_commands(struct cmd_struct *commands, FILE *ofp);
void list_cmd_options(FILE *ofp, struct poptOption *options);
+void list_cmd_options_argpar(FILE *ofp, const struct argpar_opt_descr *options);
/*
* Return the minimum order for which x <= (1UL << order).
AUTOMAKE_OPTIONS = subdir-objects
-SUBDIRS = string-utils
+SUBDIRS = \
+ string-utils \
+ bytecode \
+ filter \
+ argpar
# Make sure to always distribute all folders
# since SUBDIRS is decided at configure time.
config \
consumer \
string-utils \
- fd-tracker
+ fd-tracker \
+ bytecode \
+ filter \
+ argpar
# Common library
noinst_LTLIBRARIES = libcommon.la
libcommon_la_SOURCES = \
actions/action.c \
+ actions/group.c \
actions/notify.c \
- buffer-usage.c \
+ actions/rotate-session.c \
+ actions/snapshot-session.c \
+ actions/start-session.c \
+ actions/stop-session.c \
buffer-view.h buffer-view.c \
common.h \
- condition.c \
+ conditions/buffer-usage.c \
+ conditions/condition.c \
+ conditions/event-rule.c \
+ conditions/session-consumed-size.c \
+ conditions/session-rotation.c \
context.c context.h \
- credentials.h \
+ credentials.c credentials.h \
daemonize.c daemonize.h \
defaults.c \
+ domain.c \
dynamic-array.c dynamic-array.h \
dynamic-buffer.c dynamic-buffer.h \
endpoint.c \
error.c error.h \
evaluation.c \
event.c \
+ event-expr.c \
+ event-expr-to-bytecode.c event-expr-to-bytecode.h \
+ event-field-value.c \
+ event-rule/event-rule.c \
+ event-rule/kprobe.c \
+ event-rule/kretprobe.c \
+ event-rule/syscall.c \
+ event-rule/uprobe.c \
+ event-rule/tracepoint.c \
filter.c filter.h \
fs-handle.c fs-handle.h fs-handle-internal.h \
futex.c futex.h \
pipe.c pipe.h \
readwrite.c readwrite.h \
runas.c runas.h \
- session-consumed-size.c \
session-descriptor.c \
- session-rotation.c \
+ snapshot.c snapshot.h \
spawn-viewer.c spawn-viewer.h \
time.c \
trace-chunk.c trace-chunk.h \
endif
libcommon_la_LIBADD = \
+ $(top_builddir)/src/common/bytecode/libbytecode.la \
$(top_builddir)/src/common/config/libconfig.la \
$(top_builddir)/src/common/compat/libcompat.la \
$(top_builddir)/src/common/hashtable/libhashtable.la \
- $(top_builddir)/src/common/fd-tracker/libfd-tracker.la
+ $(top_builddir)/src/common/fd-tracker/libfd-tracker.la \
+ $(top_builddir)/src/common/filter/libfilter.la \
+ -lmsgpackc
if BUILD_LIB_COMPAT
SUBDIRS += compat
*
*/
+#include <assert.h>
+#include <common/error.h>
#include <lttng/action/action-internal.h>
+#include <lttng/action/group-internal.h>
#include <lttng/action/notify-internal.h>
-#include <common/error.h>
-#include <assert.h>
+#include <lttng/action/rotate-session-internal.h>
+#include <lttng/action/snapshot-session-internal.h>
+#include <lttng/action/start-session-internal.h>
+#include <lttng/action/stop-session-internal.h>
-static const char *lttng_action_type_string(enum lttng_action_type action_type)
+LTTNG_HIDDEN
+const char *lttng_action_type_string(enum lttng_action_type action_type)
{
switch (action_type) {
case LTTNG_ACTION_TYPE_UNKNOWN:
return "UNKNOWN";
+ case LTTNG_ACTION_TYPE_GROUP:
+ return "GROUP";
case LTTNG_ACTION_TYPE_NOTIFY:
return "NOTIFY";
+ case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+ return "ROTATE_SESSION";
+ case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+ return "SNAPSHOT_SESSION";
+ case LTTNG_ACTION_TYPE_START_SESSION:
+ return "START_SESSION";
+ case LTTNG_ACTION_TYPE_STOP_SESSION:
+ return "STOP_SESSION";
default:
return "???";
}
}
-enum lttng_action_type lttng_action_get_type(struct lttng_action *action)
+enum lttng_action_type lttng_action_get_type(const struct lttng_action *action)
{
return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN;
}
enum lttng_action_type type,
action_validate_cb validate,
action_serialize_cb serialize,
+ action_equal_cb equal,
action_destroy_cb destroy)
{
+ urcu_ref_init(&action->ref);
action->type = type;
action->validate = validate;
action->serialize = serialize;
+ action->equal = equal;
action->destroy = destroy;
}
-void lttng_action_destroy(struct lttng_action *action)
+static
+void action_destroy_ref(struct urcu_ref *ref)
+{
+ struct lttng_action *action =
+ container_of(ref, struct lttng_action, ref);
+
+ action->destroy(action);
+}
+
+LTTNG_HIDDEN
+void lttng_action_get(struct lttng_action *action)
+{
+ urcu_ref_get(&action->ref);
+}
+
+LTTNG_HIDDEN
+void lttng_action_put(struct lttng_action *action)
{
if (!action) {
return;
}
assert(action->destroy);
- action->destroy(action);
+ urcu_ref_put(&action->ref, action_destroy_ref);
+}
+
+void lttng_action_destroy(struct lttng_action *action)
+{
+ lttng_action_put(action);
}
LTTNG_HIDDEN
case LTTNG_ACTION_TYPE_NOTIFY:
create_from_buffer_cb = lttng_action_notify_create_from_buffer;
break;
+ case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+ create_from_buffer_cb =
+ lttng_action_rotate_session_create_from_buffer;
+ break;
+ case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+ create_from_buffer_cb =
+ lttng_action_snapshot_session_create_from_buffer;
+ break;
+ case LTTNG_ACTION_TYPE_START_SESSION:
+ create_from_buffer_cb =
+ lttng_action_start_session_create_from_buffer;
+ break;
+ case LTTNG_ACTION_TYPE_STOP_SESSION:
+ create_from_buffer_cb =
+ lttng_action_stop_session_create_from_buffer;
+ break;
+ case LTTNG_ACTION_TYPE_GROUP:
+ create_from_buffer_cb = lttng_action_group_create_from_buffer;
+ break;
default:
ERR("Failed to create action from buffer, unhandled action type: action-type=%u (%s)",
action_comm->action_type,
end:
return consumed_len;
}
+
+LTTNG_HIDDEN
+bool lttng_action_is_equal(const struct lttng_action *a,
+ const struct lttng_action *b)
+{
+ bool is_equal = false;
+
+ if (!a || !b) {
+ goto end;
+ }
+
+ if (a->type != b->type) {
+ goto end;
+ }
+
+ if (a == b) {
+ is_equal = true;
+ goto end;
+ }
+
+ is_equal = a->equal ? a->equal(a, b) : true;
+end:
+ return is_equal;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/dynamic-array.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/group-internal.h>
+#include <lttng/action/group.h>
+
+struct lttng_action_group {
+ struct lttng_action parent;
+
+ /* The array own the action elements */
+ struct lttng_dynamic_pointer_array actions;
+};
+
+struct lttng_action_group_comm {
+ uint32_t action_count;
+
+ /*
+ * Variable data: each element serialized sequentially.
+ */
+ char data[];
+} LTTNG_PACKED;
+
+static void destroy_lttng_action_group_element(void *ptr)
+{
+ struct lttng_action *element = (struct lttng_action *) ptr;
+ lttng_action_destroy(element);
+}
+
+static struct lttng_action_group *action_group_from_action(
+ const struct lttng_action *action)
+{
+ assert(action);
+
+ return container_of(action, struct lttng_action_group, parent);
+}
+
+static const struct lttng_action_group *action_group_from_action_const(
+ const struct lttng_action *action)
+{
+ assert(action);
+
+ return container_of(action, struct lttng_action_group, parent);
+}
+
+static bool lttng_action_group_validate(struct lttng_action *action)
+{
+ unsigned int i, count;
+ struct lttng_action_group *action_group;
+ bool valid;
+
+ assert(lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP);
+
+ action_group = action_group_from_action(action);
+
+ count = lttng_dynamic_pointer_array_get_count(&action_group->actions);
+
+ for (i = 0; i < count; i++) {
+ struct lttng_action *child =
+ lttng_dynamic_pointer_array_get_pointer(
+ &action_group->actions, i);
+
+ assert(child);
+
+ if (!lttng_action_validate(child)) {
+ valid = false;
+ goto end;
+ }
+ }
+
+ valid = true;
+
+end:
+ return valid;
+}
+
+static bool lttng_action_group_is_equal(const struct lttng_action *_a, const struct lttng_action *_b)
+{
+ bool is_equal = false;
+ unsigned int i;
+ unsigned int a_count, b_count;
+
+ if (lttng_action_group_get_count(_a, &a_count) != LTTNG_ACTION_STATUS_OK) {
+ goto end;
+ }
+ if (lttng_action_group_get_count(_b, &b_count) != LTTNG_ACTION_STATUS_OK) {
+ goto end;
+ }
+
+
+ if (a_count != b_count) {
+ goto end;
+ }
+
+ for (i = 0; i < a_count; i++) {
+ const struct lttng_action *child_a =
+ lttng_action_group_get_at_index_const(_a, i);
+ const struct lttng_action *child_b =
+ lttng_action_group_get_at_index_const(_b, i);
+
+ assert(child_a);
+ assert(child_b);
+
+ if (!lttng_action_is_equal(child_a, child_b)) {
+ goto end;
+ }
+ }
+
+ is_equal = true;
+end:
+ return is_equal;
+}
+
+static int lttng_action_group_serialize(
+ struct lttng_action *action, struct lttng_dynamic_buffer *buf)
+{
+ struct lttng_action_group *action_group;
+ struct lttng_action_group_comm comm;
+ int ret;
+ unsigned int i, count;
+
+ assert(action);
+ assert(buf);
+ assert(lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP);
+
+ action_group = action_group_from_action(action);
+
+ DBG("Serializing action group");
+
+ count = lttng_dynamic_pointer_array_get_count(&action_group->actions);
+
+ comm.action_count = count;
+
+ ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+ if (ret) {
+ ret = -1;
+ goto end;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct lttng_action *child =
+ lttng_dynamic_pointer_array_get_pointer(
+ &action_group->actions, i);
+ assert(child);
+
+ ret = lttng_action_serialize(child, buf);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ ret = 0;
+
+end:
+ return ret;
+}
+
+static void lttng_action_group_destroy(struct lttng_action *action)
+{
+ struct lttng_action_group *action_group;
+
+ if (!action) {
+ goto end;
+ }
+
+ action_group = action_group_from_action(action);
+
+ lttng_dynamic_pointer_array_reset(&action_group->actions);
+
+ free(action_group);
+
+end:
+ return;
+}
+
+ssize_t lttng_action_group_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_action **p_action)
+{
+ ssize_t consumed_len;
+ struct lttng_action_group_comm *comm;
+ struct lttng_action *group;
+ struct lttng_action *child_action = NULL;
+ enum lttng_action_status status;
+ size_t i;
+
+ group = lttng_action_group_create();
+ if (!group) {
+ consumed_len = -1;
+ goto end;
+ }
+
+ comm = (struct lttng_action_group_comm *) view->data;
+
+ consumed_len = sizeof(struct lttng_action_group_comm);
+
+ for (i = 0; i < comm->action_count; i++) {
+ ssize_t consumed_len_child;
+ struct lttng_buffer_view child_view;
+
+ child_view = lttng_buffer_view_from_view(
+ view, consumed_len, view->size - consumed_len);
+ consumed_len_child = lttng_action_create_from_buffer(
+ &child_view, &child_action);
+
+ status = lttng_action_group_add_action(group, child_action);
+ if (status != LTTNG_ACTION_STATUS_OK) {
+ consumed_len = -1;
+ goto end;
+ }
+ child_action = NULL;
+
+ consumed_len += consumed_len_child;
+ }
+
+ *p_action = group;
+ group = NULL;
+
+end:
+ lttng_action_group_destroy(group);
+ lttng_action_destroy(child_action);
+
+ return consumed_len;
+}
+
+struct lttng_action *lttng_action_group_create(void)
+{
+ struct lttng_action_group *action_group;
+ struct lttng_action *action;
+
+ action_group = zmalloc(sizeof(struct lttng_action_group));
+ if (!action_group) {
+ action = NULL;
+ goto end;
+ }
+
+ action = &action_group->parent;
+
+ lttng_action_init(action, LTTNG_ACTION_TYPE_GROUP,
+ lttng_action_group_validate,
+ lttng_action_group_serialize,
+ lttng_action_group_is_equal,
+ lttng_action_group_destroy);
+
+ lttng_dynamic_pointer_array_init(&action_group->actions,
+ destroy_lttng_action_group_element);
+
+end:
+ return action;
+}
+
+enum lttng_action_status lttng_action_group_add_action(
+ struct lttng_action *group, struct lttng_action *action)
+{
+ struct lttng_action_group *action_group;
+ enum lttng_action_status status;
+ int ret;
+
+ if (!group ||
+ (lttng_action_get_type(group) !=
+ LTTNG_ACTION_TYPE_GROUP) ||
+ !action) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ /*
+ * Don't allow adding groups in groups for now, since we're afraid of
+ * cycles.
+ */
+ if (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ action_group = action_group_from_action(group);
+
+ ret = lttng_dynamic_pointer_array_add_pointer(&action_group->actions,
+ action);
+ if (ret < 0) {
+ status = LTTNG_ACTION_STATUS_ERROR;
+ goto end;
+ }
+
+ status = LTTNG_ACTION_STATUS_OK;
+end:
+ return status;
+}
+
+enum lttng_action_status lttng_action_group_get_count(
+ const struct lttng_action *group, unsigned int *count)
+{
+ const struct lttng_action_group *action_group;
+ enum lttng_action_status status = LTTNG_ACTION_STATUS_OK;
+
+ if (!group || (lttng_action_get_type_const(group) !=
+ LTTNG_ACTION_TYPE_GROUP)) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ *count = 0;
+ goto end;
+ }
+
+ action_group = action_group_from_action_const(group);
+
+ *count = lttng_dynamic_pointer_array_get_count(&action_group->actions);
+end:
+ return status;
+}
+
+const struct lttng_action *lttng_action_group_get_at_index_const(
+ const struct lttng_action *group, unsigned int index)
+{
+ unsigned int count;
+ const struct lttng_action_group *action_group;
+ const struct lttng_action * action = NULL;
+
+ if (lttng_action_group_get_count(group, &count) !=
+ LTTNG_ACTION_STATUS_OK) {
+ goto end;
+ }
+
+ if (index >= count) {
+ goto end;
+ }
+
+ action_group = action_group_from_action_const(group);
+ action = lttng_dynamic_pointer_array_get_pointer(&action_group->actions,
+ index);
+end:
+ return action;
+}
return 0;
}
+static
+bool lttng_action_notify_is_equal(const struct lttng_action *a,
+ const struct lttng_action *b)
+{
+ /* TODO check type ??? */
+ return true;
+}
+
struct lttng_action *lttng_action_notify_create(void)
{
struct lttng_action_notify *notify;
lttng_action_init(¬ify->parent, LTTNG_ACTION_TYPE_NOTIFY, NULL,
lttng_action_notify_serialize,
+ lttng_action_notify_is_equal,
lttng_action_notify_destroy);
end:
return ¬ify->parent;
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/rotate-session-internal.h>
+#include <lttng/action/rotate-session.h>
+
+struct lttng_action_rotate_session {
+ struct lttng_action parent;
+
+ /* Owned by this. */
+ char *session_name;
+};
+
+struct lttng_action_rotate_session_comm {
+ /* Includes the trailing \0. */
+ uint32_t session_name_len;
+
+ /*
+ * Variable data:
+ *
+ * - session name (null terminated)
+ */
+ char data[];
+} LTTNG_PACKED;
+
+static struct lttng_action_rotate_session *action_rotate_session_from_action(
+ struct lttng_action *action)
+{
+ assert(action);
+
+ return container_of(action, struct lttng_action_rotate_session, parent);
+}
+
+static const struct lttng_action_rotate_session *action_rotate_session_from_action_const(
+ const struct lttng_action *action)
+{
+ assert(action);
+
+ return container_of(action, struct lttng_action_rotate_session, parent);
+}
+
+static bool lttng_action_rotate_session_validate(struct lttng_action *action)
+{
+ bool valid;
+ struct lttng_action_rotate_session *action_rotate_session;
+
+ if (!action) {
+ valid = false;
+ goto end;
+ }
+
+ action_rotate_session = action_rotate_session_from_action(action);
+
+ /* A non-empty session name is mandatory. */
+ if (!action_rotate_session->session_name ||
+ strlen(action_rotate_session->session_name) == 0) {
+ valid = false;
+ goto end;
+ }
+
+ valid = true;
+end:
+ return valid;
+}
+
+static bool lttng_action_rotate_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b)
+{
+ bool is_equal = false;
+ const struct lttng_action_rotate_session *a, *b;
+
+ a = action_rotate_session_from_action_const(_a);
+ b = action_rotate_session_from_action_const(_b);
+
+ /* Action is not valid if this is not true. */
+ assert(a->session_name);
+ assert(b->session_name);
+ if (strcmp(a->session_name, b->session_name)) {
+ goto end;
+ }
+
+ is_equal = true;
+end:
+ return is_equal;
+}
+static int lttng_action_rotate_session_serialize(
+ struct lttng_action *action, struct lttng_dynamic_buffer *buf)
+{
+ struct lttng_action_rotate_session *action_rotate_session;
+ struct lttng_action_rotate_session_comm comm;
+ size_t session_name_len;
+ int ret;
+
+ assert(action);
+ assert(buf);
+
+ action_rotate_session = action_rotate_session_from_action(action);
+
+ assert(action_rotate_session->session_name);
+
+ DBG("Serializing rotate session action: session-name: %s",
+ action_rotate_session->session_name);
+
+ session_name_len = strlen(action_rotate_session->session_name) + 1;
+ comm.session_name_len = session_name_len;
+
+ ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+ if (ret) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = lttng_dynamic_buffer_append(buf,
+ action_rotate_session->session_name, session_name_len);
+ if (ret) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = 0;
+end:
+ return ret;
+}
+
+static void lttng_action_rotate_session_destroy(struct lttng_action *action)
+{
+ struct lttng_action_rotate_session *action_rotate_session;
+
+ if (!action) {
+ goto end;
+ }
+
+ action_rotate_session = action_rotate_session_from_action(action);
+
+ free(action_rotate_session->session_name);
+ free(action_rotate_session);
+
+end:
+ return;
+}
+
+ssize_t lttng_action_rotate_session_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_action **p_action)
+{
+ ssize_t consumed_len;
+ struct lttng_action_rotate_session_comm *comm;
+ const char *session_name;
+ struct lttng_action *action;
+ enum lttng_action_status status;
+
+ action = lttng_action_rotate_session_create();
+ if (!action) {
+ consumed_len = -1;
+ goto end;
+ }
+
+ comm = (struct lttng_action_rotate_session_comm *) view->data;
+ session_name = (const char *) &comm->data;
+
+ if (!lttng_buffer_view_contains_string(
+ view, session_name, comm->session_name_len)) {
+ consumed_len = -1;
+ goto end;
+ }
+
+ status = lttng_action_rotate_session_set_session_name(
+ action, session_name);
+ if (status != LTTNG_ACTION_STATUS_OK) {
+ consumed_len = -1;
+ goto end;
+ }
+
+ consumed_len = sizeof(struct lttng_action_rotate_session_comm) +
+ comm->session_name_len;
+ *p_action = action;
+ action = NULL;
+
+end:
+ lttng_action_rotate_session_destroy(action);
+
+ return consumed_len;
+}
+
+struct lttng_action *lttng_action_rotate_session_create(void)
+{
+ struct lttng_action *action;
+
+ action = zmalloc(sizeof(struct lttng_action_rotate_session));
+ if (!action) {
+ goto end;
+ }
+
+ lttng_action_init(action, LTTNG_ACTION_TYPE_ROTATE_SESSION,
+ lttng_action_rotate_session_validate,
+ lttng_action_rotate_session_serialize,
+ lttng_action_rotate_session_is_equal,
+ lttng_action_rotate_session_destroy);
+
+end:
+ return action;
+}
+
+enum lttng_action_status lttng_action_rotate_session_set_session_name(
+ struct lttng_action *action, const char *session_name)
+{
+ struct lttng_action_rotate_session *action_rotate_session;
+ enum lttng_action_status status;
+
+ if (!action || !session_name || strlen(session_name) == 0) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ action_rotate_session = action_rotate_session_from_action(action);
+
+ free(action_rotate_session->session_name);
+
+ action_rotate_session->session_name = strdup(session_name);
+ if (!action_rotate_session->session_name) {
+ status = LTTNG_ACTION_STATUS_ERROR;
+ goto end;
+ }
+
+ status = LTTNG_ACTION_STATUS_OK;
+end:
+ return status;
+}
+
+enum lttng_action_status lttng_action_rotate_session_get_session_name(
+ const struct lttng_action *action, const char **session_name)
+{
+ const struct lttng_action_rotate_session *action_rotate_session;
+ enum lttng_action_status status;
+
+ if (!action || !session_name) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ action_rotate_session = action_rotate_session_from_action_const(action);
+
+ *session_name = action_rotate_session->session_name;
+
+ status = LTTNG_ACTION_STATUS_OK;
+end:
+ return status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/snapshot.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/snapshot-session-internal.h>
+#include <lttng/action/snapshot-session.h>
+#include <lttng/snapshot.h>
+#include <lttng/snapshot-internal.h>
+#include <inttypes.h>
+
+struct lttng_action_snapshot_session {
+ struct lttng_action parent;
+
+ /* Owned by this. */
+ char *session_name;
+
+ /*
+ * When non-NULL, use this custom output when taking the snapshot,
+ * rather than the session's registered snapshot output.
+ *
+ * Owned by this.
+ */
+ struct lttng_snapshot_output *output;
+};
+
+struct lttng_action_snapshot_session_comm {
+ /* All string lengths include the trailing \0. */
+ uint32_t session_name_len;
+ uint32_t snapshot_output_len;
+
+ /*
+ * Variable data (all strings are null-terminated):
+ *
+ * - session name string
+ * - snapshot output object
+ *
+ */
+ char data[];
+} LTTNG_PACKED;
+
+static struct lttng_action_snapshot_session *
+action_snapshot_session_from_action(struct lttng_action *action)
+{
+ assert(action);
+
+ return container_of(
+ action, struct lttng_action_snapshot_session, parent);
+}
+
+static const struct lttng_action_snapshot_session *
+action_snapshot_session_from_action_const(const struct lttng_action *action)
+{
+ assert(action);
+
+ return container_of(
+ action, struct lttng_action_snapshot_session, parent);
+}
+
+static bool lttng_action_snapshot_session_validate(struct lttng_action *action)
+{
+ bool valid = false;
+ struct lttng_action_snapshot_session *action_snapshot_session;
+
+ if (!action) {
+ goto end;
+ }
+
+ action_snapshot_session = action_snapshot_session_from_action(action);
+
+ /* A non-empty session name is mandatory. */
+ if (!action_snapshot_session->session_name ||
+ strlen(action_snapshot_session->session_name) == 0) {
+ goto end;
+ }
+
+ if (action_snapshot_session->output &&
+ !lttng_snapshot_output_validate(action_snapshot_session->output)) {
+ goto end;
+ }
+
+ valid = true;
+end:
+ return valid;
+}
+
+static bool lttng_action_snapshot_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b)
+{
+ bool is_equal = false;
+ const struct lttng_action_snapshot_session *a, *b;
+
+ a = action_snapshot_session_from_action_const(_a);
+ b = action_snapshot_session_from_action_const(_b);
+
+ /* Action is not valid if this is not true. */
+ assert(a->session_name);
+ assert(b->session_name);
+ if (strcmp(a->session_name, b->session_name)) {
+ goto end;
+ }
+
+ if (a->output && b->output &&
+ !lttng_snapshot_output_is_equal(a->output, b->output)) {
+ goto end;
+ } else if (!!a->output != !!b->output) {
+ goto end;
+ }
+
+ is_equal = true;
+end:
+ return is_equal;
+}
+
+static size_t serialize_strlen(const char *s)
+{
+
+ size_t len = 0;
+
+ if (s) {
+ len = strlen(s) + 1;
+ }
+
+ return len;
+}
+
+static int lttng_action_snapshot_session_serialize(
+ struct lttng_action *action, struct lttng_dynamic_buffer *buf)
+{
+ struct lttng_action_snapshot_session *action_snapshot_session;
+ struct lttng_action_snapshot_session_comm comm;
+ struct lttng_dynamic_buffer snapshot_output_buf = { 0 };
+ int ret;
+
+ assert(action);
+ assert(buf);
+
+ lttng_dynamic_buffer_init(&snapshot_output_buf);
+
+ action_snapshot_session = action_snapshot_session_from_action(action);
+
+ assert(action_snapshot_session->session_name);
+ DBG("Serializing snapshot session action: session-name: %s",
+ action_snapshot_session->session_name);
+
+ /* Serialize the snapshot output object first, so we know its length. */
+ if (action_snapshot_session->output) {
+ ret = lttng_snapshot_output_serialize(
+ action_snapshot_session->output, &snapshot_output_buf);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ comm.session_name_len =
+ serialize_strlen(action_snapshot_session->session_name);
+ comm.snapshot_output_len = snapshot_output_buf.size;
+
+ ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+ if (ret) {
+ goto end;
+ }
+
+ ret = lttng_dynamic_buffer_append(buf,
+ action_snapshot_session->session_name,
+ comm.session_name_len);
+ if (ret) {
+ goto end;
+ }
+
+ ret = lttng_dynamic_buffer_append_buffer(buf, &snapshot_output_buf);
+ if (ret) {
+ goto end;
+ }
+
+end:
+ lttng_dynamic_buffer_reset(&snapshot_output_buf);
+ return ret;
+}
+
+static void lttng_action_snapshot_session_destroy(struct lttng_action *action)
+{
+ struct lttng_action_snapshot_session *action_snapshot_session;
+
+ if (!action) {
+ goto end;
+ }
+
+ action_snapshot_session = action_snapshot_session_from_action(action);
+
+ free(action_snapshot_session->session_name);
+ lttng_snapshot_output_destroy(action_snapshot_session->output);
+ free(action_snapshot_session);
+
+end:
+ return;
+}
+
+ssize_t lttng_action_snapshot_session_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_action **p_action)
+{
+ ssize_t consumed_len;
+ struct lttng_action_snapshot_session_comm *comm;
+ const char *variable_data;
+ struct lttng_action *action;
+ enum lttng_action_status status;
+ struct lttng_snapshot_output *snapshot_output = NULL;
+
+ action = lttng_action_snapshot_session_create();
+ if (!action) {
+ goto error;
+ }
+
+ comm = (struct lttng_action_snapshot_session_comm *) view->data;
+ variable_data = (const char *) &comm->data;
+
+ consumed_len = sizeof(struct lttng_action_snapshot_session_comm);
+
+ if (!lttng_buffer_view_contains_string(
+ view, variable_data, comm->session_name_len)) {
+ goto error;
+ }
+
+ status = lttng_action_snapshot_session_set_session_name(
+ action, variable_data);
+ if (status != LTTNG_ACTION_STATUS_OK) {
+ goto error;
+ }
+
+ variable_data += comm->session_name_len;
+ consumed_len += comm->session_name_len;
+
+ /* If there is a snapshot output object, deserialize it. */
+ if (comm->snapshot_output_len > 0) {
+ ssize_t snapshot_output_consumed_len;
+ enum lttng_action_status action_status;
+ struct lttng_buffer_view snapshot_output_buffer_view =
+ lttng_buffer_view_from_view(view, consumed_len,
+ comm->snapshot_output_len);
+ if (!snapshot_output_buffer_view.data) {
+ fprintf(stderr, "Failed to create buffer view for snapshot output.\n");
+ goto error;
+ }
+
+ snapshot_output_consumed_len =
+ lttng_snapshot_output_create_from_buffer(
+ &snapshot_output_buffer_view,
+ &snapshot_output);
+ if (snapshot_output_consumed_len != comm->snapshot_output_len) {
+ fprintf(stderr,
+ "Failed to deserialize snapshot output object: "
+ "consumed-len: %zd, expected-len: %" PRIu32,
+ snapshot_output_consumed_len,
+ comm->snapshot_output_len);
+ goto error;
+ }
+
+ action_status = lttng_action_snapshot_session_set_output(
+ action, snapshot_output);
+ if (action_status != LTTNG_ACTION_STATUS_OK) {
+ goto error;
+ }
+
+ /* Ownership has been transferred to the action. */
+ snapshot_output = NULL;
+ }
+
+ variable_data += comm->snapshot_output_len;
+ consumed_len += comm->snapshot_output_len;
+ *p_action = action;
+ action = NULL;
+
+ goto end;
+
+error:
+ consumed_len = -1;
+
+end:
+ lttng_action_snapshot_session_destroy(action);
+ lttng_snapshot_output_destroy(snapshot_output);
+
+ return consumed_len;
+}
+
+struct lttng_action *lttng_action_snapshot_session_create(void)
+{
+ struct lttng_action *action;
+
+ action = zmalloc(sizeof(struct lttng_action_snapshot_session));
+ if (!action) {
+ goto end;
+ }
+
+ lttng_action_init(action, LTTNG_ACTION_TYPE_SNAPSHOT_SESSION,
+ lttng_action_snapshot_session_validate,
+ lttng_action_snapshot_session_serialize,
+ lttng_action_snapshot_session_is_equal,
+ lttng_action_snapshot_session_destroy);
+
+end:
+ return action;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_set_session_name(
+ struct lttng_action *action, const char *session_name)
+{
+ struct lttng_action_snapshot_session *action_snapshot_session;
+ enum lttng_action_status status;
+
+ if (!action || !session_name || strlen(session_name) == 0) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ action_snapshot_session = action_snapshot_session_from_action(action);
+
+ free(action_snapshot_session->session_name);
+
+ action_snapshot_session->session_name = strdup(session_name);
+ if (!action_snapshot_session->session_name) {
+ status = LTTNG_ACTION_STATUS_ERROR;
+ goto end;
+ }
+
+ status = LTTNG_ACTION_STATUS_OK;
+end:
+ return status;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_get_session_name(
+ const struct lttng_action *action, const char **session_name)
+{
+ const struct lttng_action_snapshot_session *action_snapshot_session;
+ enum lttng_action_status status;
+
+ if (!action || !session_name) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ action_snapshot_session = action_snapshot_session_from_action_const(action);
+
+ if (action_snapshot_session->session_name) {
+ *session_name = action_snapshot_session->session_name;
+ status = LTTNG_ACTION_STATUS_OK;
+ } else {
+ status = LTTNG_ACTION_STATUS_UNSET;
+ }
+
+end:
+
+ return status;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_set_output(
+ struct lttng_action *action,
+ struct lttng_snapshot_output *output)
+{
+ struct lttng_action_snapshot_session *action_snapshot_session;
+ enum lttng_action_status status;
+
+ if (!action || !output) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ action_snapshot_session = action_snapshot_session_from_action(action);
+
+ lttng_snapshot_output_destroy(action_snapshot_session->output);
+ action_snapshot_session->output = output;
+
+ status = LTTNG_ACTION_STATUS_OK;
+
+end:
+ return status;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_get_output_const(
+ const struct lttng_action *action,
+ const struct lttng_snapshot_output **output)
+{
+ const struct lttng_action_snapshot_session *action_snapshot_session;
+ enum lttng_action_status status;
+
+ if (!action || !output) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ action_snapshot_session = action_snapshot_session_from_action_const(action);
+
+ if (action_snapshot_session->output) {
+ *output = action_snapshot_session->output;
+ status = LTTNG_ACTION_STATUS_OK;
+ } else {
+ status = LTTNG_ACTION_STATUS_UNSET;
+ }
+
+end:
+ return status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/start-session-internal.h>
+#include <lttng/action/start-session.h>
+
+struct lttng_action_start_session {
+ struct lttng_action parent;
+
+ /* Owned by this. */
+ char *session_name;
+};
+
+struct lttng_action_start_session_comm {
+ /* Includes the trailing \0. */
+ uint32_t session_name_len;
+
+ /*
+ * Variable data:
+ *
+ * - session name (null terminated)
+ */
+ char data[];
+} LTTNG_PACKED;
+
+static struct lttng_action_start_session *action_start_session_from_action(
+ struct lttng_action *action)
+{
+ assert(action);
+
+ return container_of(action, struct lttng_action_start_session, parent);
+}
+
+static const struct lttng_action_start_session *action_start_session_from_action_const(
+ const struct lttng_action *action)
+{
+ assert(action);
+
+ return container_of(action, struct lttng_action_start_session, parent);
+}
+
+static bool lttng_action_start_session_validate(struct lttng_action *action)
+{
+ bool valid;
+ struct lttng_action_start_session *action_start_session;
+
+ if (!action) {
+ valid = false;
+ goto end;
+ }
+
+ action_start_session = action_start_session_from_action(action);
+
+ /* A non-empty session name is mandatory. */
+ if (!action_start_session->session_name ||
+ strlen(action_start_session->session_name) == 0) {
+ valid = false;
+ goto end;
+ }
+
+ valid = true;
+end:
+ return valid;
+}
+
+static bool lttng_action_start_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b)
+{
+ bool is_equal = false;
+ struct lttng_action_start_session *a, *b;
+
+ a = container_of(_a, struct lttng_action_start_session, parent);
+ b = container_of(_b, struct lttng_action_start_session, parent);
+
+ /* Action is not valid if this is not true. */
+ assert(a->session_name);
+ assert(b->session_name);
+ if (strcmp(a->session_name, b->session_name)) {
+ goto end;
+ }
+
+ is_equal = true;
+end:
+ return is_equal;
+}
+
+static int lttng_action_start_session_serialize(
+ struct lttng_action *action, struct lttng_dynamic_buffer *buf)
+{
+ struct lttng_action_start_session *action_start_session;
+ struct lttng_action_start_session_comm comm;
+ size_t session_name_len;
+ int ret;
+
+ assert(action);
+ assert(buf);
+
+ action_start_session = action_start_session_from_action(action);
+
+ assert(action_start_session->session_name);
+
+ DBG("Serializing start session action: session-name: %s",
+ action_start_session->session_name);
+
+ session_name_len = strlen(action_start_session->session_name) + 1;
+ comm.session_name_len = session_name_len;
+
+ ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+ if (ret) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = lttng_dynamic_buffer_append(buf,
+ action_start_session->session_name, session_name_len);
+ if (ret) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = 0;
+end:
+ return ret;
+}
+
+static void lttng_action_start_session_destroy(struct lttng_action *action)
+{
+ struct lttng_action_start_session *action_start_session;
+
+ if (!action) {
+ goto end;
+ }
+
+ action_start_session = action_start_session_from_action(action);
+
+ free(action_start_session->session_name);
+ free(action_start_session);
+
+end:
+ return;
+}
+
+ssize_t lttng_action_start_session_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_action **p_action)
+{
+ ssize_t consumed_len;
+ struct lttng_action_start_session_comm *comm;
+ const char *session_name;
+ struct lttng_action *action;
+ enum lttng_action_status status;
+
+ action = lttng_action_start_session_create();
+ if (!action) {
+ consumed_len = -1;
+ goto end;
+ }
+
+ comm = (struct lttng_action_start_session_comm *) view->data;
+ session_name = (const char *) &comm->data;
+
+ if (!lttng_buffer_view_contains_string(
+ view, session_name, comm->session_name_len)) {
+ consumed_len = -1;
+ goto end;
+ }
+
+ status = lttng_action_start_session_set_session_name(
+ action, session_name);
+ if (status != LTTNG_ACTION_STATUS_OK) {
+ consumed_len = -1;
+ goto end;
+ }
+
+ consumed_len = sizeof(struct lttng_action_start_session_comm) +
+ comm->session_name_len;
+ *p_action = action;
+ action = NULL;
+
+end:
+ lttng_action_start_session_destroy(action);
+
+ return consumed_len;
+}
+
+struct lttng_action *lttng_action_start_session_create(void)
+{
+ struct lttng_action *action;
+
+ action = zmalloc(sizeof(struct lttng_action_start_session));
+ if (!action) {
+ goto end;
+ }
+
+ lttng_action_init(action, LTTNG_ACTION_TYPE_START_SESSION,
+ lttng_action_start_session_validate,
+ lttng_action_start_session_serialize,
+ lttng_action_start_session_is_equal,
+ lttng_action_start_session_destroy);
+
+end:
+ return action;
+}
+
+enum lttng_action_status lttng_action_start_session_set_session_name(
+ struct lttng_action *action, const char *session_name)
+{
+ struct lttng_action_start_session *action_start_session;
+ enum lttng_action_status status;
+
+ if (!action || !session_name || strlen(session_name) == 0) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ action_start_session = action_start_session_from_action(action);
+
+ free(action_start_session->session_name);
+
+ action_start_session->session_name = strdup(session_name);
+ if (!action_start_session->session_name) {
+ status = LTTNG_ACTION_STATUS_ERROR;
+ goto end;
+ }
+
+ status = LTTNG_ACTION_STATUS_OK;
+end:
+ return status;
+}
+
+enum lttng_action_status lttng_action_start_session_get_session_name(
+ const struct lttng_action *action, const char **session_name)
+{
+ const struct lttng_action_start_session *action_start_session;
+ enum lttng_action_status status;
+
+ if (!action || !session_name) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ action_start_session = action_start_session_from_action_const(action);
+
+ *session_name = action_start_session->session_name;
+
+ status = LTTNG_ACTION_STATUS_OK;
+end:
+ return status;
+}
--- /dev/null
+/*
+<<<<<<< HEAD
+ * Copyright (C) 2019 EfficiOS, Inc.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+=======
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+>>>>>>> 2e5f09656... actions: introduce stop session action
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/stop-session-internal.h>
+#include <lttng/action/stop-session.h>
+
+struct lttng_action_stop_session {
+ struct lttng_action parent;
+
+ /* Owned by this. */
+ char *session_name;
+};
+
+struct lttng_action_stop_session_comm {
+ /* Includes the trailing \0. */
+ uint32_t session_name_len;
+
+ /*
+ * Variable data:
+ *
+ * - session name (null terminated)
+ */
+ char data[];
+} LTTNG_PACKED;
+
+static struct lttng_action_stop_session *action_stop_session_from_action(
+ struct lttng_action *action)
+{
+ assert(action);
+
+ return container_of(action, struct lttng_action_stop_session, parent);
+}
+
+static const struct lttng_action_stop_session *action_stop_session_from_action_const(
+ const struct lttng_action *action)
+{
+ assert(action);
+
+ return container_of(action, struct lttng_action_stop_session, parent);
+}
+
+static bool lttng_action_stop_session_validate(struct lttng_action *action)
+{
+ bool valid;
+ struct lttng_action_stop_session *action_stop_session;
+
+ if (!action) {
+ valid = false;
+ goto end;
+ }
+
+ action_stop_session = action_stop_session_from_action(action);
+
+ /* A non-empty session name is mandatory. */
+ if (!action_stop_session->session_name ||
+ strlen(action_stop_session->session_name) == 0) {
+ valid = false;
+ goto end;
+ }
+
+ valid = true;
+end:
+ return valid;
+}
+
+static bool lttng_action_stop_session_is_equal(const struct lttng_action *_a, const struct lttng_action *_b)
+{
+ bool is_equal = false;
+ const struct lttng_action_stop_session *a, *b;
+
+ a = action_stop_session_from_action_const(_a);
+ b = action_stop_session_from_action_const(_b);
+
+ /* Action is not valid if this is not true. */
+ assert(a->session_name);
+ assert(b->session_name);
+ if (strcmp(a->session_name, b->session_name)) {
+ goto end;
+ }
+
+ is_equal = true;
+end:
+ return is_equal;
+}
+
+static int lttng_action_stop_session_serialize(
+ struct lttng_action *action, struct lttng_dynamic_buffer *buf)
+{
+ struct lttng_action_stop_session *action_stop_session;
+ struct lttng_action_stop_session_comm comm;
+ size_t session_name_len;
+ int ret;
+
+ assert(action);
+ assert(buf);
+
+ action_stop_session = action_stop_session_from_action(action);
+
+ assert(action_stop_session->session_name);
+
+ DBG("Serializing stop session action: session-name: %s",
+ action_stop_session->session_name);
+
+ session_name_len = strlen(action_stop_session->session_name) + 1;
+ comm.session_name_len = session_name_len;
+
+ ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+ if (ret) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = lttng_dynamic_buffer_append(buf,
+ action_stop_session->session_name, session_name_len);
+ if (ret) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = 0;
+end:
+ return ret;
+}
+
+static void lttng_action_stop_session_destroy(struct lttng_action *action)
+{
+ struct lttng_action_stop_session *action_stop_session;
+
+ if (!action) {
+ goto end;
+ }
+
+ action_stop_session = action_stop_session_from_action(action);
+
+ free(action_stop_session->session_name);
+ free(action_stop_session);
+
+end:
+ return;
+}
+
+ssize_t lttng_action_stop_session_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_action **p_action)
+{
+ ssize_t consumed_len;
+ struct lttng_action_stop_session_comm *comm;
+ const char *session_name;
+ struct lttng_action *action;
+ enum lttng_action_status status;
+
+ action = lttng_action_stop_session_create();
+ if (!action) {
+ consumed_len = -1;
+ goto end;
+ }
+
+ comm = (struct lttng_action_stop_session_comm *) view->data;
+ session_name = (const char *) &comm->data;
+
+ if (!lttng_buffer_view_contains_string(
+ view, session_name, comm->session_name_len)) {
+ consumed_len = -1;
+ goto end;
+ }
+
+ status = lttng_action_stop_session_set_session_name(
+ action, session_name);
+ if (status != LTTNG_ACTION_STATUS_OK) {
+ consumed_len = -1;
+ goto end;
+ }
+
+ consumed_len = sizeof(struct lttng_action_stop_session_comm) +
+ comm->session_name_len;
+ *p_action = action;
+ action = NULL;
+
+end:
+ lttng_action_stop_session_destroy(action);
+
+ return consumed_len;
+}
+
+struct lttng_action *lttng_action_stop_session_create(void)
+{
+ struct lttng_action *action;
+
+ action = zmalloc(sizeof(struct lttng_action_stop_session));
+ if (!action) {
+ goto end;
+ }
+
+ lttng_action_init(action, LTTNG_ACTION_TYPE_STOP_SESSION,
+ lttng_action_stop_session_validate,
+ lttng_action_stop_session_serialize,
+ lttng_action_stop_session_is_equal,
+ lttng_action_stop_session_destroy);
+
+end:
+ return action;
+}
+
+enum lttng_action_status lttng_action_stop_session_set_session_name(
+ struct lttng_action *action, const char *session_name)
+{
+ struct lttng_action_stop_session *action_stop_session;
+ enum lttng_action_status status;
+
+ if (!action || !session_name || strlen(session_name) == 0) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ action_stop_session = action_stop_session_from_action(action);
+
+ free(action_stop_session->session_name);
+
+ action_stop_session->session_name = strdup(session_name);
+ if (!action_stop_session->session_name) {
+ status = LTTNG_ACTION_STATUS_ERROR;
+ goto end;
+ }
+
+ status = LTTNG_ACTION_STATUS_OK;
+end:
+ return status;
+}
+
+enum lttng_action_status lttng_action_stop_session_get_session_name(
+ const struct lttng_action *action, const char **session_name)
+{
+ const struct lttng_action_stop_session *action_stop_session;
+ enum lttng_action_status status;
+
+ if (!action || !session_name) {
+ status = LTTNG_ACTION_STATUS_INVALID;
+ goto end;
+ }
+
+ action_stop_session = action_stop_session_from_action_const(action);
+
+ *session_name = action_stop_session->session_name;
+
+ status = LTTNG_ACTION_STATUS_OK;
+end:
+ return status;
+}
--- /dev/null
+noinst_LTLIBRARIES = libargpar.la
+
+libargpar_la_SOURCES = argpar.c argpar.h
--- /dev/null
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "argpar.h"
+
+#define argpar_realloc(_ptr, _type, _nmemb) ((_type *) realloc(_ptr, (_nmemb) * sizeof(_type)))
+#define argpar_calloc(_type, _nmemb) ((_type *) calloc((_nmemb), sizeof(_type)))
+#define argpar_zalloc(_type) argpar_calloc(_type, 1)
+
+#define ARGPAR_ASSERT(_cond) assert(_cond)
+
+/*
+ * Structure holding the argpar state between successive argpar_state_parse_next
+ * calls.
+ *
+ * Created with `argpar_state_create` and destroyed with `argpar_state_destroy`.
+ */
+struct argpar_state {
+ /*
+ * Data provided by the user in argpar_state_create, does not change
+ * afterwards.
+ */
+ unsigned int argc;
+ const char * const *argv;
+ const struct argpar_opt_descr *descrs;
+
+ /*
+ * Index of the argument to process in the next argpar_state_parse_next
+ * call.
+ */
+ unsigned int i;
+
+ /* Counter of non-option arguments. */
+ int non_opt_index;
+
+ /*
+ * Short option state: if set, we are in the middle of a short option
+ * group, so we should resume there at the next argpar_state_parse_next
+ * call.
+ */
+ const char *short_opt_ch;
+};
+
+static
+char *argpar_vasprintf(const char *fmt, va_list args)
+{
+ int len1, len2;
+ char *str;
+ va_list args2;
+
+ va_copy(args2, args);
+
+ len1 = vsnprintf(NULL, 0, fmt, args);
+ if (len1 < 0) {
+ str = NULL;
+ goto end;
+ }
+
+ str = malloc(len1 + 1);
+ if (!str) {
+ goto end;
+ }
+
+ len2 = vsnprintf(str, len1 + 1, fmt, args2);
+
+ ARGPAR_ASSERT(len1 == len2);
+
+end:
+ va_end(args2);
+ return str;
+}
+
+
+static
+char *argpar_asprintf(const char *fmt, ...)
+{
+ va_list args;
+ char *str;
+
+ va_start(args, fmt);
+ str = argpar_vasprintf(fmt, args);
+ va_end(args);
+
+ return str;
+}
+
+static
+bool argpar_string_append_printf(char **str, const char *fmt, ...)
+{
+ char *new_str = NULL;
+ char *addendum;
+ bool success;
+ va_list args;
+
+ ARGPAR_ASSERT(str);
+
+ va_start(args, fmt);
+ addendum = argpar_vasprintf(fmt, args);
+ va_end(args);
+
+ if (!addendum) {
+ success = false;
+ goto end;
+ }
+
+ new_str = argpar_asprintf("%s%s", *str ? *str : "", addendum);
+ if (!new_str) {
+ success = false;
+ goto end;
+ }
+
+ free(*str);
+ *str = new_str;
+
+ success = true;
+
+end:
+ free(addendum);
+
+ return success;
+}
+
+ARGPAR_HIDDEN
+void argpar_item_destroy(struct argpar_item *item)
+{
+ if (!item) {
+ goto end;
+ }
+
+ if (item->type == ARGPAR_ITEM_TYPE_OPT) {
+ struct argpar_item_opt * const opt_item = (void *) item;
+
+ free((void *) opt_item->arg);
+ }
+
+ free(item);
+
+end:
+ return;
+}
+
+static
+bool push_item(struct argpar_item_array * const array,
+ struct argpar_item * const item)
+{
+ bool success;
+
+ ARGPAR_ASSERT(array);
+ ARGPAR_ASSERT(item);
+
+ if (array->n_items == array->n_alloc) {
+ unsigned int new_n_alloc = array->n_alloc * 2;
+ struct argpar_item **new_items;
+
+ new_items = argpar_realloc(array->items,
+ struct argpar_item *, new_n_alloc);
+ if (!new_items) {
+ success = false;
+ goto end;
+ }
+
+ array->n_alloc = new_n_alloc;
+ array->items = new_items;
+ }
+
+ array->items[array->n_items] = item;
+ array->n_items++;
+
+ success = true;
+
+end:
+ return success;
+}
+
+static
+void destroy_item_array(struct argpar_item_array * const array)
+{
+ if (array) {
+ unsigned int i;
+
+ for (i = 0; i < array->n_items; i++) {
+ argpar_item_destroy(array->items[i]);
+ }
+
+ free(array->items);
+ free(array);
+ }
+}
+
+static
+struct argpar_item_array *new_item_array(void)
+{
+ struct argpar_item_array *ret;
+ const int initial_size = 10;
+
+ ret = argpar_zalloc(struct argpar_item_array);
+ if (!ret) {
+ goto end;
+ }
+
+ ret->items = argpar_calloc(struct argpar_item *, initial_size);
+ if (!ret->items) {
+ goto error;
+ }
+
+ ret->n_alloc = initial_size;
+
+ goto end;
+
+error:
+ destroy_item_array(ret);
+ ret = NULL;
+
+end:
+ return ret;
+}
+
+static
+struct argpar_item_opt *create_opt_item(
+ const struct argpar_opt_descr * const descr,
+ const char * const arg)
+{
+ struct argpar_item_opt *opt_item =
+ argpar_zalloc(struct argpar_item_opt);
+
+ if (!opt_item) {
+ goto end;
+ }
+
+ opt_item->base.type = ARGPAR_ITEM_TYPE_OPT;
+ opt_item->descr = descr;
+
+ if (arg) {
+ opt_item->arg = strdup(arg);
+ if (!opt_item->arg) {
+ goto error;
+ }
+ }
+
+ goto end;
+
+error:
+ argpar_item_destroy(&opt_item->base);
+ opt_item = NULL;
+
+end:
+ return opt_item;
+}
+
+static
+struct argpar_item_non_opt *create_non_opt_item(const char * const arg,
+ const unsigned int orig_index,
+ const unsigned int non_opt_index)
+{
+ struct argpar_item_non_opt * const non_opt_item =
+ argpar_zalloc(struct argpar_item_non_opt);
+
+ if (!non_opt_item) {
+ goto end;
+ }
+
+ non_opt_item->base.type = ARGPAR_ITEM_TYPE_NON_OPT;
+ non_opt_item->arg = arg;
+ non_opt_item->orig_index = orig_index;
+ non_opt_item->non_opt_index = non_opt_index;
+
+end:
+ return non_opt_item;
+}
+
+static
+const struct argpar_opt_descr *find_descr(
+ const struct argpar_opt_descr * const descrs,
+ const char short_name, const char * const long_name)
+{
+ const struct argpar_opt_descr *descr;
+
+ for (descr = descrs; descr->short_name || descr->long_name; descr++) {
+ if (short_name && descr->short_name &&
+ short_name == descr->short_name) {
+ goto end;
+ }
+
+ if (long_name && descr->long_name &&
+ strcmp(long_name, descr->long_name) == 0) {
+ goto end;
+ }
+ }
+
+end:
+ return !descr->short_name && !descr->long_name ? NULL : descr;
+}
+
+enum parse_orig_arg_opt_ret {
+ PARSE_ORIG_ARG_OPT_RET_OK,
+ PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT = -2,
+ PARSE_ORIG_ARG_OPT_RET_ERROR = -1,
+};
+
+static
+enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts,
+ const char * const next_orig_arg,
+ const struct argpar_opt_descr * const descrs,
+ struct argpar_state *state,
+ char **error,
+ struct argpar_item **item)
+{
+ enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
+ bool used_next_orig_arg = false;
+
+ if (strlen(short_opts) == 0) {
+ argpar_string_append_printf(error, "Invalid argument");
+ goto error;
+ }
+
+ if (!state->short_opt_ch) {
+ state->short_opt_ch = short_opts;
+ }
+
+ const char *opt_arg = NULL;
+ const struct argpar_opt_descr *descr;
+ struct argpar_item_opt *opt_item;
+
+ /* Find corresponding option descriptor */
+ descr = find_descr(descrs, *state->short_opt_ch, NULL);
+ if (!descr) {
+ ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT;
+ argpar_string_append_printf(error,
+ "Unknown option `-%c`", *state->short_opt_ch);
+ goto error;
+ }
+
+ if (descr->with_arg) {
+ if (state->short_opt_ch[1]) {
+ /* `-oarg` form */
+ opt_arg = &state->short_opt_ch[1];
+ } else {
+ /* `-o arg` form */
+ opt_arg = next_orig_arg;
+ used_next_orig_arg = true;
+ }
+
+ /*
+ * We accept `-o ''` (empty option's argument),
+ * but not `-o` alone if an option's argument is
+ * expected.
+ */
+ if (!opt_arg || (state->short_opt_ch[1] && strlen(opt_arg) == 0)) {
+ argpar_string_append_printf(error,
+ "Missing required argument for option `-%c`",
+ *state->short_opt_ch);
+ used_next_orig_arg = false;
+ goto error;
+ }
+ }
+
+ /* Create and append option argument */
+ opt_item = create_opt_item(descr, opt_arg);
+ if (!opt_item) {
+ goto error;
+ }
+
+ *item = &opt_item->base;
+
+ state->short_opt_ch++;
+
+ if (descr->with_arg || !*state->short_opt_ch) {
+ /* Option has an argument: no more options */
+ state->short_opt_ch = NULL;
+
+ if (used_next_orig_arg) {
+ state->i += 2;
+ } else {
+ state->i += 1;
+ }
+ }
+
+ goto end;
+
+error:
+ if (ret == PARSE_ORIG_ARG_OPT_RET_OK) {
+ ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
+ }
+
+end:
+ return ret;
+}
+
+static
+enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
+ const char * const next_orig_arg,
+ const struct argpar_opt_descr * const descrs,
+ struct argpar_state *state,
+ char **error,
+ struct argpar_item **item)
+{
+ const size_t max_len = 127;
+ enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
+ const struct argpar_opt_descr *descr;
+ struct argpar_item_opt *opt_item;
+ bool used_next_orig_arg = false;
+
+ /* Option's argument, if any */
+ const char *opt_arg = NULL;
+
+ /* Position of first `=`, if any */
+ const char *eq_pos;
+
+ /* Buffer holding option name when `long_opt_arg` contains `=` */
+ char buf[max_len + 1];
+
+ /* Option name */
+ const char *long_opt_name = long_opt_arg;
+
+ if (strlen(long_opt_arg) == 0) {
+ argpar_string_append_printf(error,
+ "Invalid argument");
+ goto error;
+ }
+
+ /* Find the first `=` in original argument */
+ eq_pos = strchr(long_opt_arg, '=');
+ if (eq_pos) {
+ const size_t long_opt_name_size = eq_pos - long_opt_arg;
+
+ /* Isolate the option name */
+ if (long_opt_name_size > max_len) {
+ argpar_string_append_printf(error,
+ "Invalid argument `--%s`", long_opt_arg);
+ goto error;
+ }
+
+ memcpy(buf, long_opt_arg, long_opt_name_size);
+ buf[long_opt_name_size] = '\0';
+ long_opt_name = buf;
+ }
+
+ /* Find corresponding option descriptor */
+ descr = find_descr(descrs, '\0', long_opt_name);
+ if (!descr) {
+ argpar_string_append_printf(error,
+ "Unknown option `--%s`", long_opt_name);
+ ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT;
+ goto error;
+ }
+
+ /* Find option's argument if any */
+ if (descr->with_arg) {
+ if (eq_pos) {
+ /* `--long-opt=arg` style */
+ opt_arg = eq_pos + 1;
+ } else {
+ /* `--long-opt arg` style */
+ if (!next_orig_arg) {
+ argpar_string_append_printf(error,
+ "Missing required argument for option `--%s`",
+ long_opt_name);
+ goto error;
+ }
+
+ opt_arg = next_orig_arg;
+ used_next_orig_arg = true;
+ }
+ }
+
+ /* Create and append option argument */
+ opt_item = create_opt_item(descr, opt_arg);
+ if (!opt_item) {
+ goto error;
+ }
+
+ if (used_next_orig_arg) {
+ state->i += 2;
+ } else {
+ state->i += 1;
+ }
+
+ *item = &opt_item->base;
+ goto end;
+
+error:
+ if (ret == PARSE_ORIG_ARG_OPT_RET_OK) {
+ ret = PARSE_ORIG_ARG_OPT_RET_ERROR;
+ }
+
+end:
+ return ret;
+}
+
+static
+enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg,
+ const char * const next_orig_arg,
+ const struct argpar_opt_descr * const descrs,
+ struct argpar_state *state,
+ char **error,
+ struct argpar_item **item)
+{
+ enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
+
+ ARGPAR_ASSERT(orig_arg[0] == '-');
+
+ if (orig_arg[1] == '-') {
+ /* Long option */
+ ret = parse_long_opt(&orig_arg[2],
+ next_orig_arg, descrs, state, error, item);
+ } else {
+ /* Short option */
+ ret = parse_short_opts(&orig_arg[1],
+ next_orig_arg, descrs, state, error, item);
+ }
+
+ return ret;
+}
+
+static
+bool prepend_while_parsing_arg_to_error(char **error,
+ const unsigned int i, const char * const arg)
+{
+ char *new_error;
+ bool success;
+
+ ARGPAR_ASSERT(error);
+ ARGPAR_ASSERT(*error);
+
+ new_error = argpar_asprintf("While parsing argument #%u (`%s`): %s",
+ i + 1, arg, *error);
+ if (!new_error) {
+ success = false;
+ goto end;
+ }
+
+ free(*error);
+ *error = new_error;
+ success = true;
+
+end:
+ return success;
+}
+
+ARGPAR_HIDDEN
+struct argpar_state *argpar_state_create(
+ unsigned int argc,
+ const char * const *argv,
+ const struct argpar_opt_descr * const descrs)
+{
+ struct argpar_state *state;
+
+ state = argpar_zalloc(struct argpar_state);
+ if (!state) {
+ goto end;
+ }
+
+ state->argc = argc;
+ state->argv = argv;
+ state->descrs = descrs;
+
+end:
+ return state;
+}
+
+ARGPAR_HIDDEN
+void argpar_state_destroy(struct argpar_state *state)
+{
+ free(state);
+}
+
+ARGPAR_HIDDEN
+enum argpar_state_parse_next_status argpar_state_parse_next(
+ struct argpar_state *state,
+ struct argpar_item **item,
+ char **error)
+{
+ enum argpar_state_parse_next_status status;
+
+ ARGPAR_ASSERT(state->i <= state->argc);
+
+ *error = NULL;
+
+ if (state->i == state->argc) {
+ status = ARGPAR_STATE_PARSE_NEXT_STATUS_END;
+ goto end;
+ }
+
+ enum parse_orig_arg_opt_ret parse_orig_arg_opt_ret;
+ const char * const orig_arg = state->argv[state->i];
+ const char * const next_orig_arg =
+ state->i < (state->argc - 1) ? state->argv[state->i + 1] : NULL;
+
+ if (orig_arg[0] != '-') {
+ /* Non-option argument */
+ struct argpar_item_non_opt *non_opt_item =
+ create_non_opt_item(orig_arg, state->i, state->non_opt_index);
+
+ if (!non_opt_item) {
+ status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR;
+ goto end;
+ }
+
+ state->non_opt_index++;
+ state->i++;
+
+ *item = &non_opt_item->base;
+ status = ARGPAR_STATE_PARSE_NEXT_STATUS_OK;
+ goto end;
+ }
+
+ /* Option argument */
+ parse_orig_arg_opt_ret = parse_orig_arg_opt(orig_arg,
+ next_orig_arg, state->descrs, state, error, item);
+ switch (parse_orig_arg_opt_ret) {
+ case PARSE_ORIG_ARG_OPT_RET_OK:
+ status = ARGPAR_STATE_PARSE_NEXT_STATUS_OK;
+ break;
+ case PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT:
+ status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT;
+ break;;
+ case PARSE_ORIG_ARG_OPT_RET_ERROR:
+ prepend_while_parsing_arg_to_error(
+ error, state->i, orig_arg);
+ status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR;
+ break;
+ default:
+ abort();
+ }
+
+end:
+ return status;
+}
+
+ARGPAR_HIDDEN
+int argpar_state_get_ingested_orig_args(struct argpar_state *state)
+{
+ return state->i;
+}
+
+ARGPAR_HIDDEN
+struct argpar_parse_ret argpar_parse(unsigned int argc,
+ const char * const *argv,
+ const struct argpar_opt_descr * const descrs,
+ bool fail_on_unknown_opt)
+{
+ struct argpar_parse_ret parse_ret = { 0 };
+ struct argpar_item *item = NULL;
+ struct argpar_state *state = NULL;
+
+ parse_ret.items = new_item_array();
+ if (!parse_ret.items) {
+ parse_ret.error = strdup("Failed to create items array.");
+ ARGPAR_ASSERT(parse_ret.error);
+ goto error;
+ }
+
+ state = argpar_state_create(argc, argv, descrs);
+ if (!state) {
+ parse_ret.error = strdup("Failed to create argpar state.");
+ ARGPAR_ASSERT(parse_ret.error);
+ goto error;
+ }
+
+ while (true) {
+ enum argpar_state_parse_next_status status;
+
+ status = argpar_state_parse_next(state, &item, &parse_ret.error);
+ if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
+ goto error;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
+ break;
+ } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
+ if (fail_on_unknown_opt) {
+ parse_ret.ingested_orig_args =
+ argpar_state_get_ingested_orig_args(state);
+ prepend_while_parsing_arg_to_error(
+ &parse_ret.error, parse_ret.ingested_orig_args,
+ argv[parse_ret.ingested_orig_args]);
+ status = ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR;
+ goto error;
+ }
+
+ free(parse_ret.error);
+ parse_ret.error = NULL;
+ break;
+ }
+
+ ARGPAR_ASSERT(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
+
+ if (!push_item(parse_ret.items, item)) {
+ goto error;
+ }
+ item = NULL;
+ }
+
+ ARGPAR_ASSERT(!parse_ret.error);
+ parse_ret.ingested_orig_args =
+ argpar_state_get_ingested_orig_args(state);
+ goto end;
+
+error:
+ ARGPAR_ASSERT(parse_ret.error);
+
+ /* That's how we indicate that an error occured */
+ destroy_item_array(parse_ret.items);
+ parse_ret.items = NULL;
+
+end:
+ argpar_state_destroy(state);
+ argpar_item_destroy(item);
+ return parse_ret;
+}
+
+ARGPAR_HIDDEN
+void argpar_parse_ret_fini(struct argpar_parse_ret *ret)
+{
+ ARGPAR_ASSERT(ret);
+
+ destroy_item_array(ret->items);
+ ret->items = NULL;
+
+ free(ret->error);
+ ret->error = NULL;
+}
--- /dev/null
+#ifndef BABELTRACE_ARGPAR_H
+#define BABELTRACE_ARGPAR_H
+
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * argpar is a library that provides facilities for argument parsing.
+ *
+ * Two APIs are available:
+ *
+ * - The iterator-style API, where you initialize a state object with
+ * `argpar_state_create`, then repeatedly call `argpar_state_parse_next` to
+ * get the arguments, until (1) there are no more arguments, (2) the parser
+ * encounters an error (e.g. unknown option) or (3) you get bored. This
+ * API gives you more control on when to stop parsing the arguments.
+ *
+ * - The parse-everything-in-one-shot-API, where you call `argpar_parse`,
+ * which parses the arguments until (1) there are not more arguments or
+ * (2) it encounters a parser error. It returns you a list of all the
+ * arguments it was able to parse, which you can consult at your leisure.
+ *
+ * The following describes how arguments are parsed, and applies to both APIs.
+ *
+ * argpar parses the arguments `argv` of which the count is `argc` using the
+ * sentinel-terminated (use `ARGPAR_OPT_DESCR_SENTINEL`) option
+ * descriptor array `descrs`.
+ *
+ * argpar considers ALL the elements of `argv`, including the* first one, so
+ * that you would typically pass `argc - 1` and `&argv[1]` from what main()
+ * receives.
+ *
+ * This argument parser supports:
+ *
+ * * Short options without an argument, possibly tied together:
+ *
+ * -f -auf -n
+ *
+ * * Short options with argument:
+ *
+ * -b 45 -f/mein/file -xyzhello
+ *
+ * * Long options without an argument:
+ *
+ * --five-guys --burger-king --pizza-hut --subway
+ *
+ * * Long options with arguments:
+ *
+ * --security enable --time=18.56
+ *
+ * * Non-option arguments (anything else).
+ *
+ * This parser does not accept `-` or `--` as arguments. The latter
+ * means "end of options" for many command-line tools, but this function
+ * is all about keeping the order of the arguments, so it does not mean
+ * much to put them at the end. This has the side effect that a
+ * non-option argument cannot have the form of an option, for example if
+ * you need to pass the exact relative path `--component`. In that case,
+ * you would need to pass `./--component`. There's no generic way to
+ * escape `-` for the moment.
+ *
+ * This parser accepts duplicate options (it will output one item for each
+ * instance).
+ *
+ * The returned items are of the type `struct argpar_item *`. Each item
+ * is to be casted to the appropriate type (`struct argpar_item_opt *` or
+ * `struct argpar_item_non_opt *`) depending on its type.
+ *
+ * The items are returned in the same order that the arguments were parsed,
+ * including non-option arguments. This means, for example, that for
+ *
+ * --hello --meow=23 /path/to/file -b
+ *
+ * found items are returned in this order: option item (--hello), option item
+ * (--meow=23), non-option item (/path/to/file) and option item (-b).
+ */
+
+#include <stdbool.h>
+
+/* Sentinel for an option descriptor array */
+#define ARGPAR_OPT_DESCR_SENTINEL { -1, '\0', NULL, false }
+
+/*
+ * ARGPAR_HIDDEN: if argpar is used in some shared library, we don't want them
+ * to be exported by that library, so mark them as "hidden".
+ *
+ * On Windows, symbols are local unless explicitly exported,
+ * see https://gcc.gnu.org/wiki/Visibility
+ */
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define ARGPAR_HIDDEN
+#else
+#define ARGPAR_HIDDEN __attribute__((visibility("hidden")))
+#endif
+
+/* Forward-declaration for the opaque type. */
+struct argpar_state;
+
+/* Option descriptor */
+struct argpar_opt_descr {
+ /* Numeric ID for this option */
+ const int id;
+
+ /* Short option character, or `\0` */
+ const char short_name;
+
+ /* Long option name (without `--`), or `NULL` */
+ const char * const long_name;
+
+ /* True if this option has an argument */
+ const bool with_arg;
+};
+
+/* Item type */
+enum argpar_item_type {
+ /* Option */
+ ARGPAR_ITEM_TYPE_OPT,
+
+ /* Non-option */
+ ARGPAR_ITEM_TYPE_NON_OPT,
+};
+
+/* Base item */
+struct argpar_item {
+ enum argpar_item_type type;
+};
+
+/* Option item */
+struct argpar_item_opt {
+ struct argpar_item base;
+
+ /* Corresponding descriptor */
+ const struct argpar_opt_descr *descr;
+
+ /* Argument, or `NULL` if none */
+ const char *arg;
+};
+
+/* Non-option item */
+struct argpar_item_non_opt {
+ struct argpar_item base;
+
+ /*
+ * Complete argument, pointing to one of the entries of the
+ * original arguments (`argv`).
+ */
+ const char *arg;
+
+ /* Index of this argument amongst all original arguments (`argv`) */
+ unsigned int orig_index;
+
+ /* Index of this argument amongst other non-option arguments */
+ unsigned int non_opt_index;
+};
+
+struct argpar_item_array {
+ /* Array of `struct argpar_item *`, or `NULL` on error */
+ struct argpar_item **items;
+
+ /* Number of used slots in `items`. */
+ unsigned int n_items;
+
+ /* Number of allocated slots in `items`. */
+ unsigned int n_alloc;
+};
+
+/* What is returned by argpar_parse() */
+struct argpar_parse_ret {
+ /* Array of `struct argpar_item *`, or `NULL` on error */
+ struct argpar_item_array *items;
+
+ /* Error string, or `NULL` if none */
+ char *error;
+
+ /* Number of original arguments (`argv`) ingested */
+ unsigned int ingested_orig_args;
+};
+
+/*
+ * Parses arguments in `argv` until the end is reached or an error is
+ * encountered.
+ *
+ * On success, this function returns an array of items
+ * (field `items` of `struct argpar_parse_ret`) corresponding to each parsed
+ * argument.
+ *
+ * In the returned structure, `ingested_orig_args` is the number of
+ * ingested arguments within `argv` to produce the resulting array of
+ * items.
+ *
+ * If `fail_on_unknown_opt` is true, then on success `ingested_orig_args` is
+ * equal to `argc`. Otherwise, `ingested_orig_args` contains the number of
+ * original arguments until an unknown _option_ occurs. For example, with
+ *
+ * --great --white contact nuance --shark nuclear
+ *
+ * if `--shark` is not described within `descrs` and
+ * `fail_on_unknown_opt` is false, then `ingested_orig_args` is 4 (two
+ * options, two non-options), whereas `argc` is 6.
+ *
+ * This makes it possible to know where a command name is, for example.
+ * With those arguments:
+ *
+ * --verbose --stuff=23 do-something --specific-opt -f -b
+ *
+ * and the descriptors for `--verbose` and `--stuff` only, the function
+ * returns the `--verbose` and `--stuff` option items, the
+ * `do-something` non-option item, and that three original arguments
+ * were ingested. This means you can start the next argument parsing
+ * stage, with option descriptors depending on the command name, at
+ * `&argv[3]`.
+ *
+ * Note that `ingested_orig_args` is not always equal to the number of
+ * returned items, as
+ *
+ * --hello -fdw
+ *
+ * for example contains two ingested original arguments, but four
+ * resulting items.
+ *
+ * On failure, the returned structure's `items` member is `NULL`, and
+ * the `error` string member contains details about the error.
+ *
+ * You can finalize the returned structure with
+ * argpar_parse_ret_fini().
+ */
+ARGPAR_HIDDEN
+struct argpar_parse_ret argpar_parse(unsigned int argc,
+ const char * const *argv,
+ const struct argpar_opt_descr *descrs,
+ bool fail_on_unknown_opt);
+
+/*
+ * Finalizes what is returned by argpar_parse().
+ *
+ * It is safe to call argpar_parse() multiple times with the same
+ * structure.
+ */
+ARGPAR_HIDDEN
+void argpar_parse_ret_fini(struct argpar_parse_ret *ret);
+
+/*
+ * Creates an instance of `struct argpar_state`.
+ *
+ * This sets up the argpar_state structure, but does not actually
+ * start parsing the arguments.
+ *
+ * When you are done with it, the state must be freed with
+ * `argpar_state_destroy`.
+ */
+ARGPAR_HIDDEN
+struct argpar_state *argpar_state_create(
+ unsigned int argc,
+ const char * const *argv,
+ const struct argpar_opt_descr * const descrs);
+
+/*
+ * Destroys an instance of `struct argpar_state`.
+ */
+ARGPAR_HIDDEN
+void argpar_state_destroy(struct argpar_state *state);
+
+
+enum argpar_state_parse_next_status {
+ ARGPAR_STATE_PARSE_NEXT_STATUS_OK,
+ ARGPAR_STATE_PARSE_NEXT_STATUS_END,
+ ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT,
+ ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR,
+};
+
+/*
+ * Parses and returns the next argument from `state`.
+ *
+ * On success, an item describing the argument is returned in `*item` and
+ * ARGPAR_STATE_PARSE_NEXT_STATUS_OK is returned. The item must be freed with
+ * `argpar_item_destroy`.
+ *
+ * If there are no more arguments to parse, ARGPAR_STATE_PARSE_NEXT_STATUS_END
+ * is returned.
+ *
+ * On failure (status codes ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT and
+ * ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR), an error string is returned in `*error`.
+ * This string must be freed with `free`.
+ */
+enum argpar_state_parse_next_status argpar_state_parse_next(
+ struct argpar_state *state,
+ struct argpar_item **item,
+ char **error);
+
+/*
+ * Return the number of ingested elements from argv that were required to
+ * produce the previously returned items.
+ */
+ARGPAR_HIDDEN
+int argpar_state_get_ingested_orig_args(struct argpar_state *state);
+
+/*
+ * Destroy an instance of `struct argpar_item`, as returned by
+ * argpar_state_parse_next.
+ */
+ARGPAR_HIDDEN
+void argpar_item_destroy(struct argpar_item *item);
+
+#define ARGPAR_ITEM_DESTROY_AND_RESET(_item) \
+ { \
+ argpar_item_destroy(_item); \
+ _item = NULL; \
+ }
+
+
+#endif /* BABELTRACE_ARGPAR_H */
+++ /dev/null
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/buffer-usage-internal.h>
-#include <common/macros.h>
-#include <common/error.h>
-#include <assert.h>
-#include <math.h>
-#include <float.h>
-#include <time.h>
-
-#define IS_USAGE_CONDITION(condition) ( \
- lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || \
- lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH \
- )
-
-static
-double fixed_to_double(uint32_t val)
-{
- return (double) val / (double) UINT32_MAX;
-}
-
-static
-uint64_t double_to_fixed(double val)
-{
- return (val * (double) UINT32_MAX);
-}
-
-static
-bool is_usage_evaluation(const struct lttng_evaluation *evaluation)
-{
- enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
-
- return type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW ||
- type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH;
-}
-
-static
-void lttng_condition_buffer_usage_destroy(struct lttng_condition *condition)
-{
- struct lttng_condition_buffer_usage *usage;
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
-
- free(usage->session_name);
- free(usage->channel_name);
- free(usage);
-}
-
-static
-bool lttng_condition_buffer_usage_validate(
- const struct lttng_condition *condition)
-{
- bool valid = false;
- struct lttng_condition_buffer_usage *usage;
-
- if (!condition) {
- goto end;
- }
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
- if (!usage->session_name) {
- ERR("Invalid buffer condition: a target session name must be set.");
- goto end;
- }
- if (!usage->channel_name) {
- ERR("Invalid buffer condition: a target channel name must be set.");
- goto end;
- }
- if (!usage->threshold_ratio.set && !usage->threshold_bytes.set) {
- ERR("Invalid buffer condition: a threshold must be set.");
- goto end;
- }
- if (!usage->domain.set) {
- ERR("Invalid buffer usage condition: a domain must be set.");
- goto end;
- }
-
- valid = true;
-end:
- return valid;
-}
-
-static
-int lttng_condition_buffer_usage_serialize(
- const struct lttng_condition *condition,
- struct lttng_dynamic_buffer *buf)
-{
- int ret;
- struct lttng_condition_buffer_usage *usage;
- size_t session_name_len, channel_name_len;
- struct lttng_condition_buffer_usage_comm usage_comm;
-
- if (!condition || !IS_USAGE_CONDITION(condition)) {
- ret = -1;
- goto end;
- }
-
- DBG("Serializing buffer usage condition");
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
-
- session_name_len = strlen(usage->session_name) + 1;
- channel_name_len = strlen(usage->channel_name) + 1;
- if (session_name_len > LTTNG_NAME_MAX ||
- channel_name_len > LTTNG_NAME_MAX) {
- ret = -1;
- goto end;
- }
-
- usage_comm.threshold_set_in_bytes = !!usage->threshold_bytes.set;
- usage_comm.session_name_len = session_name_len;
- usage_comm.channel_name_len = channel_name_len;
- usage_comm.domain_type = (int8_t) usage->domain.type;
-
- if (usage->threshold_bytes.set) {
- usage_comm.threshold = usage->threshold_bytes.value;
- } else {
- uint64_t val = double_to_fixed(
- usage->threshold_ratio.value);
-
- if (val > UINT32_MAX) {
- /* overflow. */
- ret = -1;
- goto end;
- }
- usage_comm.threshold = val;
- }
-
- ret = lttng_dynamic_buffer_append(buf, &usage_comm,
- sizeof(usage_comm));
- if (ret) {
- goto end;
- }
- ret = lttng_dynamic_buffer_append(buf, usage->session_name,
- session_name_len);
- if (ret) {
- goto end;
- }
- ret = lttng_dynamic_buffer_append(buf, usage->channel_name,
- channel_name_len);
- if (ret) {
- goto end;
- }
-end:
- return ret;
-}
-
-static
-bool lttng_condition_buffer_usage_is_equal(const struct lttng_condition *_a,
- const struct lttng_condition *_b)
-{
- bool is_equal = false;
- struct lttng_condition_buffer_usage *a, *b;
-
- a = container_of(_a, struct lttng_condition_buffer_usage, parent);
- b = container_of(_b, struct lttng_condition_buffer_usage, parent);
-
- if ((a->threshold_ratio.set && !b->threshold_ratio.set) ||
- (a->threshold_bytes.set && !b->threshold_bytes.set)) {
- goto end;
- }
-
- if (a->threshold_ratio.set && b->threshold_ratio.set) {
- double a_value, b_value, diff;
-
- a_value = a->threshold_ratio.value;
- b_value = b->threshold_ratio.value;
- diff = fabs(a_value - b_value);
-
- if (diff > DBL_EPSILON) {
- goto end;
- }
- } else if (a->threshold_bytes.set && b->threshold_bytes.set) {
- uint64_t a_value, b_value;
-
- a_value = a->threshold_bytes.value;
- b_value = b->threshold_bytes.value;
- if (a_value != b_value) {
- goto end;
- }
- }
-
- /* Condition is not valid if this is not true. */
- assert(a->session_name);
- assert(b->session_name);
- if (strcmp(a->session_name, b->session_name)) {
- goto end;
- }
-
- assert(a->channel_name);
- assert(b->channel_name);
- if (strcmp(a->channel_name, b->channel_name)) {
- goto end;
- }
-
- assert(a->domain.set);
- assert(b->domain.set);
- if (a->domain.type != b->domain.type) {
- goto end;
- }
- is_equal = true;
-end:
- return is_equal;
-}
-
-static
-struct lttng_condition *lttng_condition_buffer_usage_create(
- enum lttng_condition_type type)
-{
- struct lttng_condition_buffer_usage *condition;
-
- condition = zmalloc(sizeof(struct lttng_condition_buffer_usage));
- if (!condition) {
- return NULL;
- }
-
- lttng_condition_init(&condition->parent, type);
- condition->parent.validate = lttng_condition_buffer_usage_validate;
- condition->parent.serialize = lttng_condition_buffer_usage_serialize;
- condition->parent.equal = lttng_condition_buffer_usage_is_equal;
- condition->parent.destroy = lttng_condition_buffer_usage_destroy;
- return &condition->parent;
-}
-
-struct lttng_condition *lttng_condition_buffer_usage_low_create(void)
-{
- return lttng_condition_buffer_usage_create(
- LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW);
-}
-
-struct lttng_condition *lttng_condition_buffer_usage_high_create(void)
-{
- return lttng_condition_buffer_usage_create(
- LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH);
-}
-
-static
-ssize_t init_condition_from_buffer(struct lttng_condition *condition,
- const struct lttng_buffer_view *src_view)
-{
- ssize_t ret, condition_size;
- enum lttng_condition_status status;
- enum lttng_domain_type domain_type;
- const struct lttng_condition_buffer_usage_comm *condition_comm;
- const char *session_name, *channel_name;
- struct lttng_buffer_view names_view;
-
- if (src_view->size < sizeof(*condition_comm)) {
- ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
- ret = -1;
- goto end;
- }
-
- condition_comm = (const struct lttng_condition_buffer_usage_comm *) src_view->data;
- names_view = lttng_buffer_view_from_view(src_view,
- sizeof(*condition_comm), -1);
-
- if (condition_comm->session_name_len > LTTNG_NAME_MAX ||
- condition_comm->channel_name_len > LTTNG_NAME_MAX) {
- ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
- ret = -1;
- goto end;
- }
-
- if (names_view.size <
- (condition_comm->session_name_len +
- condition_comm->channel_name_len)) {
- ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
- ret = -1;
- goto end;
- }
-
- if (condition_comm->threshold_set_in_bytes) {
- status = lttng_condition_buffer_usage_set_threshold(condition,
- condition_comm->threshold);
- } else {
- status = lttng_condition_buffer_usage_set_threshold_ratio(
- condition,
- fixed_to_double(condition_comm->threshold));
- }
- if (status != LTTNG_CONDITION_STATUS_OK) {
- ERR("Failed to initialize buffer usage condition threshold");
- ret = -1;
- goto end;
- }
-
- if (condition_comm->domain_type <= LTTNG_DOMAIN_NONE ||
- condition_comm->domain_type > LTTNG_DOMAIN_PYTHON) {
- /* Invalid domain value. */
- ERR("Invalid domain type value (%i) found in condition buffer",
- (int) condition_comm->domain_type);
- ret = -1;
- goto end;
- }
-
- domain_type = (enum lttng_domain_type) condition_comm->domain_type;
- status = lttng_condition_buffer_usage_set_domain_type(condition,
- domain_type);
- if (status != LTTNG_CONDITION_STATUS_OK) {
- ERR("Failed to set buffer usage condition domain");
- ret = -1;
- goto end;
- }
-
- session_name = names_view.data;
- if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
- ERR("Malformed session name encountered in condition buffer");
- ret = -1;
- goto end;
- }
-
- channel_name = session_name + condition_comm->session_name_len;
- if (*(channel_name + condition_comm->channel_name_len - 1) != '\0') {
- ERR("Malformed channel name encountered in condition buffer");
- ret = -1;
- goto end;
- }
-
- status = lttng_condition_buffer_usage_set_session_name(condition,
- session_name);
- if (status != LTTNG_CONDITION_STATUS_OK) {
- ERR("Failed to set buffer usage session name");
- ret = -1;
- goto end;
- }
-
- status = lttng_condition_buffer_usage_set_channel_name(condition,
- channel_name);
- if (status != LTTNG_CONDITION_STATUS_OK) {
- ERR("Failed to set buffer usage channel name");
- ret = -1;
- goto end;
- }
-
- if (!lttng_condition_validate(condition)) {
- ret = -1;
- goto end;
- }
-
- condition_size = sizeof(*condition_comm) +
- (ssize_t) condition_comm->session_name_len +
- (ssize_t) condition_comm->channel_name_len;
- ret = condition_size;
-end:
- return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_buffer_usage_low_create_from_buffer(
- const struct lttng_buffer_view *view,
- struct lttng_condition **_condition)
-{
- ssize_t ret;
- struct lttng_condition *condition =
- lttng_condition_buffer_usage_low_create();
-
- if (!_condition || !condition) {
- ret = -1;
- goto error;
- }
-
- ret = init_condition_from_buffer(condition, view);
- if (ret < 0) {
- goto error;
- }
-
- *_condition = condition;
- return ret;
-error:
- lttng_condition_destroy(condition);
- return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_buffer_usage_high_create_from_buffer(
- const struct lttng_buffer_view *view,
- struct lttng_condition **_condition)
-{
- ssize_t ret;
- struct lttng_condition *condition =
- lttng_condition_buffer_usage_high_create();
-
- if (!_condition || !condition) {
- ret = -1;
- goto error;
- }
-
- ret = init_condition_from_buffer(condition, view);
- if (ret < 0) {
- goto error;
- }
-
- *_condition = condition;
- return ret;
-error:
- lttng_condition_destroy(condition);
- return ret;
-}
-
-static
-struct lttng_evaluation *create_evaluation_from_buffer(
- enum lttng_condition_type type,
- const struct lttng_buffer_view *view)
-{
- const struct lttng_evaluation_buffer_usage_comm *comm =
- (const struct lttng_evaluation_buffer_usage_comm *) view->data;
- struct lttng_evaluation *evaluation = NULL;
-
- if (view->size < sizeof(*comm)) {
- goto end;
- }
-
- evaluation = lttng_evaluation_buffer_usage_create(type,
- comm->buffer_use, comm->buffer_capacity);
-end:
- return evaluation;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_evaluation_buffer_usage_low_create_from_buffer(
- const struct lttng_buffer_view *view,
- struct lttng_evaluation **_evaluation)
-{
- ssize_t ret;
- struct lttng_evaluation *evaluation = NULL;
-
- if (!_evaluation) {
- ret = -1;
- goto error;
- }
-
- evaluation = create_evaluation_from_buffer(
- LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, view);
- if (!evaluation) {
- ret = -1;
- goto error;
- }
-
- *_evaluation = evaluation;
- ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
- return ret;
-error:
- lttng_evaluation_destroy(evaluation);
- return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_evaluation_buffer_usage_high_create_from_buffer(
- const struct lttng_buffer_view *view,
- struct lttng_evaluation **_evaluation)
-{
- ssize_t ret;
- struct lttng_evaluation *evaluation = NULL;
-
- if (!_evaluation) {
- ret = -1;
- goto error;
- }
-
- evaluation = create_evaluation_from_buffer(
- LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, view);
- if (!evaluation) {
- ret = -1;
- goto error;
- }
-
- *_evaluation = evaluation;
- ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
- return ret;
-error:
- lttng_evaluation_destroy(evaluation);
- return ret;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_threshold_ratio(
- const struct lttng_condition *condition,
- double *threshold_ratio)
-{
- struct lttng_condition_buffer_usage *usage;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_USAGE_CONDITION(condition) ||
- !threshold_ratio) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
- if (!usage->threshold_ratio.set) {
- status = LTTNG_CONDITION_STATUS_UNSET;
- goto end;
- }
- *threshold_ratio = usage->threshold_ratio.value;
-end:
- return status;
-}
-
-/* threshold_ratio expressed as [0.0, 1.0]. */
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_threshold_ratio(
- struct lttng_condition *condition, double threshold_ratio)
-{
- struct lttng_condition_buffer_usage *usage;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_USAGE_CONDITION(condition) ||
- threshold_ratio < 0.0 ||
- threshold_ratio > 1.0) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
- usage->threshold_ratio.set = true;
- usage->threshold_bytes.set = false;
- usage->threshold_ratio.value = threshold_ratio;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_threshold(
- const struct lttng_condition *condition,
- uint64_t *threshold_bytes)
-{
- struct lttng_condition_buffer_usage *usage;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_USAGE_CONDITION(condition) || !threshold_bytes) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
- if (!usage->threshold_bytes.set) {
- status = LTTNG_CONDITION_STATUS_UNSET;
- goto end;
- }
- *threshold_bytes = usage->threshold_bytes.value;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_threshold(
- struct lttng_condition *condition, uint64_t threshold_bytes)
-{
- struct lttng_condition_buffer_usage *usage;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_USAGE_CONDITION(condition)) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
- usage->threshold_ratio.set = false;
- usage->threshold_bytes.set = true;
- usage->threshold_bytes.value = threshold_bytes;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_session_name(
- const struct lttng_condition *condition,
- const char **session_name)
-{
- struct lttng_condition_buffer_usage *usage;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_USAGE_CONDITION(condition) || !session_name) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
- if (!usage->session_name) {
- status = LTTNG_CONDITION_STATUS_UNSET;
- goto end;
- }
- *session_name = usage->session_name;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_session_name(
- struct lttng_condition *condition, const char *session_name)
-{
- char *session_name_copy;
- struct lttng_condition_buffer_usage *usage;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_USAGE_CONDITION(condition) || !session_name ||
- strlen(session_name) == 0) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
- session_name_copy = strdup(session_name);
- if (!session_name_copy) {
- status = LTTNG_CONDITION_STATUS_ERROR;
- goto end;
- }
-
- if (usage->session_name) {
- free(usage->session_name);
- }
- usage->session_name = session_name_copy;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_channel_name(
- const struct lttng_condition *condition,
- const char **channel_name)
-{
- struct lttng_condition_buffer_usage *usage;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
- if (!usage->channel_name) {
- status = LTTNG_CONDITION_STATUS_UNSET;
- goto end;
- }
- *channel_name = usage->channel_name;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_channel_name(
- struct lttng_condition *condition, const char *channel_name)
-{
- char *channel_name_copy;
- struct lttng_condition_buffer_usage *usage;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name ||
- strlen(channel_name) == 0) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
- channel_name_copy = strdup(channel_name);
- if (!channel_name_copy) {
- status = LTTNG_CONDITION_STATUS_ERROR;
- goto end;
- }
-
- if (usage->channel_name) {
- free(usage->channel_name);
- }
- usage->channel_name = channel_name_copy;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_domain_type(
- const struct lttng_condition *condition,
- enum lttng_domain_type *type)
-{
- struct lttng_condition_buffer_usage *usage;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_USAGE_CONDITION(condition) || !type) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
- if (!usage->domain.set) {
- status = LTTNG_CONDITION_STATUS_UNSET;
- goto end;
- }
- *type = usage->domain.type;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_domain_type(
- struct lttng_condition *condition, enum lttng_domain_type type)
-{
- struct lttng_condition_buffer_usage *usage;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_USAGE_CONDITION(condition) ||
- type == LTTNG_DOMAIN_NONE) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(condition, struct lttng_condition_buffer_usage,
- parent);
- usage->domain.set = true;
- usage->domain.type = type;
-end:
- return status;
-}
-
-static
-int lttng_evaluation_buffer_usage_serialize(
- const struct lttng_evaluation *evaluation,
- struct lttng_dynamic_buffer *buf)
-{
- struct lttng_evaluation_buffer_usage *usage;
- struct lttng_evaluation_buffer_usage_comm comm;
-
- usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
- parent);
- comm.buffer_use = usage->buffer_use;
- comm.buffer_capacity = usage->buffer_capacity;
-
- return lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
-}
-
-static
-void lttng_evaluation_buffer_usage_destroy(
- struct lttng_evaluation *evaluation)
-{
- struct lttng_evaluation_buffer_usage *usage;
-
- usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
- parent);
- free(usage);
-}
-
-LTTNG_HIDDEN
-struct lttng_evaluation *lttng_evaluation_buffer_usage_create(
- enum lttng_condition_type type, uint64_t use, uint64_t capacity)
-{
- struct lttng_evaluation_buffer_usage *usage;
-
- usage = zmalloc(sizeof(struct lttng_evaluation_buffer_usage));
- if (!usage) {
- goto end;
- }
-
- usage->parent.type = type;
- usage->buffer_use = use;
- usage->buffer_capacity = capacity;
- usage->parent.serialize = lttng_evaluation_buffer_usage_serialize;
- usage->parent.destroy = lttng_evaluation_buffer_usage_destroy;
-end:
- return &usage->parent;
-}
-
-/*
- * Get the sampled buffer usage which caused the associated condition to
- * evaluate to "true".
- */
-enum lttng_evaluation_status
-lttng_evaluation_buffer_usage_get_usage_ratio(
- const struct lttng_evaluation *evaluation, double *usage_ratio)
-{
- struct lttng_evaluation_buffer_usage *usage;
- enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
- if (!evaluation || !is_usage_evaluation(evaluation) || !usage_ratio) {
- status = LTTNG_EVALUATION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
- parent);
- *usage_ratio = (double) usage->buffer_use /
- (double) usage->buffer_capacity;
-end:
- return status;
-}
-
-enum lttng_evaluation_status
-lttng_evaluation_buffer_usage_get_usage(
- const struct lttng_evaluation *evaluation,
- uint64_t *usage_bytes)
-{
- struct lttng_evaluation_buffer_usage *usage;
- enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
- if (!evaluation || !is_usage_evaluation(evaluation) || !usage_bytes) {
- status = LTTNG_EVALUATION_STATUS_INVALID;
- goto end;
- }
-
- usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
- parent);
- *usage_bytes = usage->buffer_use;
-end:
- return status;
-}
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+
+noinst_LTLIBRARIES = libbytecode.la
+
+libbytecode_la_SOURCES = \
+ bytecode.c bytecode.h
--- /dev/null
+/*
+ * Copyright 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "bytecode.h"
+
+#include <errno.h>
+
+#include "common/align.h"
+
+#define INIT_ALLOC_SIZE 4
+
+static inline int get_count_order(unsigned int count)
+{
+ int order;
+
+ order = lttng_fls(count) - 1;
+ if (count & (count - 1))
+ order++;
+ return order;
+}
+
+LTTNG_HIDDEN
+int bytecode_init(struct lttng_bytecode_alloc **fb)
+{
+ uint32_t alloc_len;
+
+ alloc_len = sizeof(struct lttng_bytecode_alloc) + INIT_ALLOC_SIZE;
+ *fb = calloc(alloc_len, 1);
+ if (!*fb) {
+ return -ENOMEM;
+ } else {
+ (*fb)->alloc_len = alloc_len;
+ return 0;
+ }
+}
+
+LTTNG_HIDDEN
+int32_t bytecode_reserve(struct lttng_bytecode_alloc **fb, uint32_t align, uint32_t len)
+{
+ int32_t ret;
+ uint32_t padding = offset_align((*fb)->b.len, align);
+ uint32_t new_len = (*fb)->b.len + padding + len;
+ uint32_t new_alloc_len = sizeof(struct lttng_bytecode_alloc) + new_len;
+ uint32_t old_alloc_len = (*fb)->alloc_len;
+
+ if (new_len > LTTNG_FILTER_MAX_LEN)
+ return -EINVAL;
+
+ if (new_alloc_len > old_alloc_len) {
+ struct lttng_bytecode_alloc *newptr;
+
+ new_alloc_len =
+ max_t(uint32_t, 1U << get_count_order(new_alloc_len), old_alloc_len << 1);
+ newptr = realloc(*fb, new_alloc_len);
+ if (!newptr)
+ return -ENOMEM;
+ *fb = newptr;
+ /* We zero directly the memory from start of allocation. */
+ memset(&((char *) *fb)[old_alloc_len], 0, new_alloc_len - old_alloc_len);
+ (*fb)->alloc_len = new_alloc_len;
+ }
+ (*fb)->b.len += padding;
+ ret = (*fb)->b.len;
+ (*fb)->b.len += len;
+ return ret;
+}
+
+LTTNG_HIDDEN
+int bytecode_push(struct lttng_bytecode_alloc **fb, const void *data,
+ uint32_t align, uint32_t len)
+{
+ int32_t offset;
+
+ offset = bytecode_reserve(fb, align, len);
+ if (offset < 0)
+ return offset;
+ memcpy(&(*fb)->b.data[offset], data, len);
+ return 0;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_logical(struct lttng_bytecode_alloc **fb,
+ struct logical_op *data,
+ uint32_t align, uint32_t len,
+ uint16_t *skip_offset)
+{
+ int32_t offset;
+
+ offset = bytecode_reserve(fb, align, len);
+ if (offset < 0)
+ return offset;
+ memcpy(&(*fb)->b.data[offset], data, len);
+ *skip_offset =
+ (void *) &((struct logical_op *) &(*fb)->b.data[offset])->skip_offset
+ - (void *) &(*fb)->b.data[0];
+ return 0;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_get_payload_root(struct lttng_bytecode_alloc **bytecode)
+{
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op);
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+
+ insn->op = BYTECODE_OP_GET_PAYLOAD_ROOT;
+ ret = bytecode_push(bytecode, insn, 1, insn_len);
+ free(insn);
+
+ return ret;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_get_context_root(struct lttng_bytecode_alloc **bytecode)
+{
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op);
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+
+ insn->op = BYTECODE_OP_GET_CONTEXT_ROOT;
+ ret = bytecode_push(bytecode, insn, 1, insn_len);
+ free(insn);
+
+ return ret;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_get_app_context_root(struct lttng_bytecode_alloc **bytecode)
+{
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op);
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+
+ insn->op = BYTECODE_OP_GET_APP_CONTEXT_ROOT;
+ ret = bytecode_push(bytecode, insn, 1, insn_len);
+ free(insn);
+
+ return ret;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_get_index_u64(struct lttng_bytecode_alloc **bytecode,
+ uint64_t index)
+{
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op)
+ + sizeof(struct get_index_u64);
+ struct get_index_u64 index_op_data;
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+
+ insn->op = BYTECODE_OP_GET_INDEX_U64;
+ index_op_data.index = index;
+ memcpy(insn->data, &index_op_data, sizeof(index));
+ ret = bytecode_push(bytecode, insn, 1, insn_len);
+
+ free(insn);
+
+ return ret;
+}
+
+LTTNG_HIDDEN
+int bytecode_push_get_symbol(struct lttng_bytecode_alloc **bytecode,
+ struct lttng_bytecode_alloc **bytecode_reloc,
+ const char *symbol)
+{
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op)
+ + sizeof(struct get_symbol);
+ struct get_symbol symbol_offset;
+ uint32_t reloc_offset_u32;
+ uint16_t reloc_offset;
+ uint32_t bytecode_reloc_offset_u32;
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ insn->op = BYTECODE_OP_GET_SYMBOL;
+
+ /*
+ * Get offset in the reloc portion at which the symbol name
+ * will end up at (GET_SYMBOL's operand points there).
+ */
+ bytecode_reloc_offset_u32 =
+ bytecode_get_len(&(*bytecode_reloc)->b)
+ + sizeof(reloc_offset);
+ symbol_offset.offset = (uint16_t) bytecode_reloc_offset_u32;
+ memcpy(insn->data, &symbol_offset, sizeof(symbol_offset));
+
+ /*
+ * Get offset in the bytecode where the opcode will end up at,
+ * the reloc offset points to it.
+ */
+ reloc_offset_u32 = bytecode_get_len(&(*bytecode)->b);
+ if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
+ ret = -EINVAL;
+ goto end;
+ }
+ reloc_offset = (uint16_t) reloc_offset_u32;
+
+ /* Append op in bytecode. */
+ ret = bytecode_push(bytecode, insn, 1, insn_len);
+ if (ret) {
+ goto end;
+ }
+
+ /* Append reloc offset. */
+ ret = bytecode_push(bytecode_reloc, &reloc_offset,
+ 1, sizeof(reloc_offset));
+ if (ret) {
+ goto end;
+ }
+
+ /* Append symbol name. */
+ ret = bytecode_push(bytecode_reloc, symbol, 1, strlen(symbol) + 1);
+
+end:
+ free(insn);
+ return ret;
+}
+
+/*
+ * Allocate an lttng_bytecode object and copy the given original bytecode.
+ *
+ * Return allocated bytecode or NULL on error.
+ */
+struct lttng_bytecode *bytecode_copy(
+ const struct lttng_bytecode *orig_f)
+{
+ struct lttng_bytecode *bytecode = NULL;
+
+ bytecode = zmalloc(sizeof(*bytecode) + orig_f->len);
+ if (!bytecode) {
+ goto error;
+ }
+
+ memcpy(bytecode, orig_f, sizeof(*bytecode) + orig_f->len);
+
+error:
+ return bytecode;
+}
--- /dev/null
+/*
+ * Copyright 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_COMMON_BYTECODE_H
+#define LTTNG_COMMON_BYTECODE_H
+
+#include <stdint.h>
+
+#include "common/macros.h"
+#include "common/sessiond-comm/sessiond-comm.h"
+
+/*
+ * offsets are absolute from start of bytecode.
+ */
+
+struct field_ref {
+ /* Initially, symbol offset. After link, field offset. */
+ uint16_t offset;
+} LTTNG_PACKED;
+
+struct get_symbol {
+ /* Symbol offset. */
+ uint16_t offset;
+} LTTNG_PACKED;
+
+struct get_index_u16 {
+ uint16_t index;
+} LTTNG_PACKED;
+
+struct get_index_u64 {
+ uint64_t index;
+} LTTNG_PACKED;
+
+struct literal_numeric {
+ int64_t v;
+} LTTNG_PACKED;
+
+struct literal_double {
+ double v;
+} LTTNG_PACKED;
+
+struct literal_string {
+ char string[0];
+} LTTNG_PACKED;
+
+enum bytecode_op {
+ BYTECODE_OP_UNKNOWN = 0,
+
+ BYTECODE_OP_RETURN = 1,
+
+ /* binary */
+ BYTECODE_OP_MUL = 2,
+ BYTECODE_OP_DIV = 3,
+ BYTECODE_OP_MOD = 4,
+ BYTECODE_OP_PLUS = 5,
+ BYTECODE_OP_MINUS = 6,
+ BYTECODE_OP_BIT_RSHIFT = 7,
+ BYTECODE_OP_BIT_LSHIFT = 8,
+ BYTECODE_OP_BIT_AND = 9,
+ BYTECODE_OP_BIT_OR = 10,
+ BYTECODE_OP_BIT_XOR = 11,
+
+ /* binary comparators */
+ BYTECODE_OP_EQ = 12,
+ BYTECODE_OP_NE = 13,
+ BYTECODE_OP_GT = 14,
+ BYTECODE_OP_LT = 15,
+ BYTECODE_OP_GE = 16,
+ BYTECODE_OP_LE = 17,
+
+ /* string binary comparator: apply to */
+ BYTECODE_OP_EQ_STRING = 18,
+ BYTECODE_OP_NE_STRING = 19,
+ BYTECODE_OP_GT_STRING = 20,
+ BYTECODE_OP_LT_STRING = 21,
+ BYTECODE_OP_GE_STRING = 22,
+ BYTECODE_OP_LE_STRING = 23,
+
+ /* s64 binary comparator */
+ BYTECODE_OP_EQ_S64 = 24,
+ BYTECODE_OP_NE_S64 = 25,
+ BYTECODE_OP_GT_S64 = 26,
+ BYTECODE_OP_LT_S64 = 27,
+ BYTECODE_OP_GE_S64 = 28,
+ BYTECODE_OP_LE_S64 = 29,
+
+ /* double binary comparator */
+ BYTECODE_OP_EQ_DOUBLE = 30,
+ BYTECODE_OP_NE_DOUBLE = 31,
+ BYTECODE_OP_GT_DOUBLE = 32,
+ BYTECODE_OP_LT_DOUBLE = 33,
+ BYTECODE_OP_GE_DOUBLE = 34,
+ BYTECODE_OP_LE_DOUBLE = 35,
+
+ /* Mixed S64-double binary comparators */
+ BYTECODE_OP_EQ_DOUBLE_S64 = 36,
+ BYTECODE_OP_NE_DOUBLE_S64 = 37,
+ BYTECODE_OP_GT_DOUBLE_S64 = 38,
+ BYTECODE_OP_LT_DOUBLE_S64 = 39,
+ BYTECODE_OP_GE_DOUBLE_S64 = 40,
+ BYTECODE_OP_LE_DOUBLE_S64 = 41,
+
+ BYTECODE_OP_EQ_S64_DOUBLE = 42,
+ BYTECODE_OP_NE_S64_DOUBLE = 43,
+ BYTECODE_OP_GT_S64_DOUBLE = 44,
+ BYTECODE_OP_LT_S64_DOUBLE = 45,
+ BYTECODE_OP_GE_S64_DOUBLE = 46,
+ BYTECODE_OP_LE_S64_DOUBLE = 47,
+
+ /* unary */
+ BYTECODE_OP_UNARY_PLUS = 48,
+ BYTECODE_OP_UNARY_MINUS = 49,
+ BYTECODE_OP_UNARY_NOT = 50,
+ BYTECODE_OP_UNARY_PLUS_S64 = 51,
+ BYTECODE_OP_UNARY_MINUS_S64 = 52,
+ BYTECODE_OP_UNARY_NOT_S64 = 53,
+ BYTECODE_OP_UNARY_PLUS_DOUBLE = 54,
+ BYTECODE_OP_UNARY_MINUS_DOUBLE = 55,
+ BYTECODE_OP_UNARY_NOT_DOUBLE = 56,
+
+ /* logical */
+ BYTECODE_OP_AND = 57,
+ BYTECODE_OP_OR = 58,
+
+ /* load field ref */
+ BYTECODE_OP_LOAD_FIELD_REF = 59,
+ BYTECODE_OP_LOAD_FIELD_REF_STRING = 60,
+ BYTECODE_OP_LOAD_FIELD_REF_SEQUENCE = 61,
+ BYTECODE_OP_LOAD_FIELD_REF_S64 = 62,
+ BYTECODE_OP_LOAD_FIELD_REF_DOUBLE = 63,
+
+ /* load immediate from operand */
+ BYTECODE_OP_LOAD_STRING = 64,
+ BYTECODE_OP_LOAD_S64 = 65,
+ BYTECODE_OP_LOAD_DOUBLE = 66,
+
+ /* cast */
+ BYTECODE_OP_CAST_TO_S64 = 67,
+ BYTECODE_OP_CAST_DOUBLE_TO_S64 = 68,
+ BYTECODE_OP_CAST_NOP = 69,
+
+ /* get context ref */
+ BYTECODE_OP_GET_CONTEXT_REF = 70,
+ BYTECODE_OP_GET_CONTEXT_REF_STRING = 71,
+ BYTECODE_OP_GET_CONTEXT_REF_S64 = 72,
+ BYTECODE_OP_GET_CONTEXT_REF_DOUBLE = 73,
+
+ /* load userspace field ref */
+ BYTECODE_OP_LOAD_FIELD_REF_USER_STRING = 74,
+ BYTECODE_OP_LOAD_FIELD_REF_USER_SEQUENCE = 75,
+
+ /*
+ * load immediate star globbing pattern (literal string)
+ * from immediate
+ */
+ BYTECODE_OP_LOAD_STAR_GLOB_STRING = 76,
+
+ /* globbing pattern binary operator: apply to */
+ BYTECODE_OP_EQ_STAR_GLOB_STRING = 77,
+ BYTECODE_OP_NE_STAR_GLOB_STRING = 78,
+
+ /*
+ * Instructions for recursive traversal through composed types.
+ */
+ BYTECODE_OP_GET_CONTEXT_ROOT = 79,
+ BYTECODE_OP_GET_APP_CONTEXT_ROOT = 80,
+ BYTECODE_OP_GET_PAYLOAD_ROOT = 81,
+
+ BYTECODE_OP_GET_SYMBOL = 82,
+ BYTECODE_OP_GET_SYMBOL_FIELD = 83,
+ BYTECODE_OP_GET_INDEX_U16 = 84,
+ BYTECODE_OP_GET_INDEX_U64 = 85,
+
+ BYTECODE_OP_LOAD_FIELD = 86,
+ BYTECODE_OP_LOAD_FIELD_S8 = 87,
+ BYTECODE_OP_LOAD_FIELD_S16 = 88,
+ BYTECODE_OP_LOAD_FIELD_S32 = 89,
+ BYTECODE_OP_LOAD_FIELD_S64 = 90,
+ BYTECODE_OP_LOAD_FIELD_U8 = 91,
+ BYTECODE_OP_LOAD_FIELD_U16 = 92,
+ BYTECODE_OP_LOAD_FIELD_U32 = 93,
+ BYTECODE_OP_LOAD_FIELD_U64 = 94,
+ BYTECODE_OP_LOAD_FIELD_STRING = 95,
+ BYTECODE_OP_LOAD_FIELD_SEQUENCE = 96,
+ BYTECODE_OP_LOAD_FIELD_DOUBLE = 97,
+
+ BYTECODE_OP_UNARY_BIT_NOT = 98,
+
+ BYTECODE_OP_RETURN_S64 = 99,
+
+ NR_BYTECODE_OPS,
+};
+
+typedef uint8_t bytecode_opcode_t;
+
+struct load_op {
+ bytecode_opcode_t op;
+
+ /*
+ * data to load. Size known by enum bytecode_opcode_t and null-term
+ * char.
+ */
+ char data[0];
+} LTTNG_PACKED;
+
+struct binary_op {
+ bytecode_opcode_t op;
+} LTTNG_PACKED;
+
+struct unary_op {
+ bytecode_opcode_t op;
+} LTTNG_PACKED;
+
+/* skip_offset is absolute from start of bytecode */
+struct logical_op {
+ bytecode_opcode_t op;
+ uint16_t skip_offset; /* bytecode insn, if skip second test */
+} LTTNG_PACKED;
+
+struct cast_op {
+ bytecode_opcode_t op;
+} LTTNG_PACKED;
+
+struct return_op {
+ bytecode_opcode_t op;
+} LTTNG_PACKED;
+
+struct lttng_bytecode_alloc {
+ uint32_t alloc_len;
+ struct lttng_bytecode b;
+};
+
+LTTNG_HIDDEN int bytecode_init(struct lttng_bytecode_alloc **fb);
+LTTNG_HIDDEN int32_t bytecode_reserve(struct lttng_bytecode_alloc **fb,
+ uint32_t align, uint32_t len);
+LTTNG_HIDDEN int bytecode_push(struct lttng_bytecode_alloc **fb,
+ const void *data, uint32_t align, uint32_t len);
+LTTNG_HIDDEN int bytecode_push_logical(struct lttng_bytecode_alloc **fb,
+ struct logical_op *data, uint32_t align, uint32_t len,
+ uint16_t *skip_offset);
+LTTNG_HIDDEN int bytecode_push_get_payload_root(
+ struct lttng_bytecode_alloc **bytecode);
+LTTNG_HIDDEN int bytecode_push_get_context_root(
+ struct lttng_bytecode_alloc **bytecode);
+LTTNG_HIDDEN int bytecode_push_get_app_context_root(
+ struct lttng_bytecode_alloc **bytecode);
+LTTNG_HIDDEN int bytecode_push_get_index_u64(
+ struct lttng_bytecode_alloc **bytecode, uint64_t index);
+LTTNG_HIDDEN int bytecode_push_get_symbol(
+ struct lttng_bytecode_alloc **bytecode,
+ struct lttng_bytecode_alloc **bytecode_reloc,
+ const char *symbol);
+LTTNG_HIDDEN struct lttng_bytecode *bytecode_copy(
+ const struct lttng_bytecode *orig_f);
+
+static inline
+unsigned int bytecode_get_len(struct lttng_bytecode *bytecode)
+{
+ return bytecode->len;
+}
+
+#endif /* LTTNG_COMMON_BYTECODE_H */
+++ /dev/null
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/buffer-usage-internal.h>
-#include <lttng/condition/session-consumed-size-internal.h>
-#include <lttng/condition/session-rotation-internal.h>
-#include <common/macros.h>
-#include <common/error.h>
-#include <common/dynamic-buffer.h>
-#include <common/buffer-view.h>
-#include <stdbool.h>
-#include <assert.h>
-
-enum lttng_condition_type lttng_condition_get_type(
- const struct lttng_condition *condition)
-{
- return condition ? condition->type : LTTNG_CONDITION_TYPE_UNKNOWN;
-}
-
-void lttng_condition_destroy(struct lttng_condition *condition)
-{
- if (!condition) {
- return;
- }
-
- assert(condition->destroy);
- condition->destroy(condition);
-}
-
-LTTNG_HIDDEN
-bool lttng_condition_validate(const struct lttng_condition *condition)
-{
- bool valid;
-
- if (!condition) {
- valid = false;
- goto end;
- }
-
- if (!condition->validate) {
- /* Sub-class guarantees that it can never be invalid. */
- valid = true;
- goto end;
- }
-
- valid = condition->validate(condition);
-end:
- return valid;
-}
-
-LTTNG_HIDDEN
-int lttng_condition_serialize(const struct lttng_condition *condition,
- struct lttng_dynamic_buffer *buf)
-{
- int ret;
- struct lttng_condition_comm condition_comm = { 0 };
-
- if (!condition) {
- ret = -1;
- goto end;
- }
-
- condition_comm.condition_type = (int8_t) condition->type;
-
- ret = lttng_dynamic_buffer_append(buf, &condition_comm,
- sizeof(condition_comm));
- if (ret) {
- goto end;
- }
-
- ret = condition->serialize(condition, buf);
- if (ret) {
- goto end;
- }
-end:
- return ret;
-}
-
-LTTNG_HIDDEN
-bool lttng_condition_is_equal(const struct lttng_condition *a,
- const struct lttng_condition *b)
-{
- bool is_equal = false;
-
- if (!a || !b) {
- goto end;
- }
-
- if (a->type != b->type) {
- goto end;
- }
-
- if (a == b) {
- is_equal = true;
- goto end;
- }
-
- is_equal = a->equal ? a->equal(a, b) : true;
-end:
- return is_equal;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_create_from_buffer(
- const struct lttng_buffer_view *buffer,
- struct lttng_condition **condition)
-{
- ssize_t ret, condition_size = 0;
- const struct lttng_condition_comm *condition_comm;
- condition_create_from_buffer_cb create_from_buffer = NULL;
-
- if (!buffer || !condition) {
- ret = -1;
- goto end;
- }
-
- DBG("Deserializing condition from buffer");
- condition_comm = (const struct lttng_condition_comm *) buffer->data;
- condition_size += sizeof(*condition_comm);
-
- switch ((enum lttng_condition_type) condition_comm->condition_type) {
- case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
- create_from_buffer = lttng_condition_buffer_usage_low_create_from_buffer;
- break;
- case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
- create_from_buffer = lttng_condition_buffer_usage_high_create_from_buffer;
- break;
- case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
- create_from_buffer = lttng_condition_session_consumed_size_create_from_buffer;
- break;
- case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
- create_from_buffer = lttng_condition_session_rotation_ongoing_create_from_buffer;
- break;
- case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
- create_from_buffer = lttng_condition_session_rotation_completed_create_from_buffer;
- break;
- default:
- ERR("Attempted to create condition of unknown type (%i)",
- (int) condition_comm->condition_type);
- ret = -1;
- goto end;
- }
-
- if (create_from_buffer) {
- const struct lttng_buffer_view view =
- lttng_buffer_view_from_view(buffer,
- sizeof(*condition_comm), -1);
-
- ret = create_from_buffer(&view, condition);
- if (ret < 0) {
- goto end;
- }
- condition_size += ret;
-
- } else {
- abort();
- }
-
- ret = condition_size;
-end:
- return ret;
-}
-
-LTTNG_HIDDEN
-void lttng_condition_init(struct lttng_condition *condition,
- enum lttng_condition_type type)
-{
- condition->type = type;
-}
--- /dev/null
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/buffer-usage-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+#include <time.h>
+
+#define IS_USAGE_CONDITION(condition) ( \
+ lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || \
+ lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH \
+ )
+
+static
+double fixed_to_double(uint32_t val)
+{
+ return (double) val / (double) UINT32_MAX;
+}
+
+static
+uint64_t double_to_fixed(double val)
+{
+ return (val * (double) UINT32_MAX);
+}
+
+static
+bool is_usage_evaluation(const struct lttng_evaluation *evaluation)
+{
+ enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
+
+ return type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW ||
+ type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH;
+}
+
+static
+void lttng_condition_buffer_usage_destroy(struct lttng_condition *condition)
+{
+ struct lttng_condition_buffer_usage *usage;
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+
+ free(usage->session_name);
+ free(usage->channel_name);
+ free(usage);
+}
+
+static
+bool lttng_condition_buffer_usage_validate(
+ const struct lttng_condition *condition)
+{
+ bool valid = false;
+ struct lttng_condition_buffer_usage *usage;
+
+ if (!condition) {
+ goto end;
+ }
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+ if (!usage->session_name) {
+ ERR("Invalid buffer condition: a target session name must be set.");
+ goto end;
+ }
+ if (!usage->channel_name) {
+ ERR("Invalid buffer condition: a target channel name must be set.");
+ goto end;
+ }
+ if (!usage->threshold_ratio.set && !usage->threshold_bytes.set) {
+ ERR("Invalid buffer condition: a threshold must be set.");
+ goto end;
+ }
+ if (!usage->domain.set) {
+ ERR("Invalid buffer usage condition: a domain must be set.");
+ goto end;
+ }
+
+ valid = true;
+end:
+ return valid;
+}
+
+static
+int lttng_condition_buffer_usage_serialize(
+ const struct lttng_condition *condition,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
+{
+ int ret;
+ struct lttng_condition_buffer_usage *usage;
+ size_t session_name_len, channel_name_len;
+ struct lttng_condition_buffer_usage_comm usage_comm;
+
+ if (!condition || !IS_USAGE_CONDITION(condition)) {
+ ret = -1;
+ goto end;
+ }
+
+ DBG("Serializing buffer usage condition");
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+
+ session_name_len = strlen(usage->session_name) + 1;
+ channel_name_len = strlen(usage->channel_name) + 1;
+ if (session_name_len > LTTNG_NAME_MAX ||
+ channel_name_len > LTTNG_NAME_MAX) {
+ ret = -1;
+ goto end;
+ }
+
+ usage_comm.threshold_set_in_bytes = !!usage->threshold_bytes.set;
+ usage_comm.session_name_len = session_name_len;
+ usage_comm.channel_name_len = channel_name_len;
+ usage_comm.domain_type = (int8_t) usage->domain.type;
+
+ if (usage->threshold_bytes.set) {
+ usage_comm.threshold = usage->threshold_bytes.value;
+ } else {
+ uint64_t val = double_to_fixed(
+ usage->threshold_ratio.value);
+
+ if (val > UINT32_MAX) {
+ /* overflow. */
+ ret = -1;
+ goto end;
+ }
+ usage_comm.threshold = val;
+ }
+
+ ret = lttng_dynamic_buffer_append(buf, &usage_comm,
+ sizeof(usage_comm));
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(buf, usage->session_name,
+ session_name_len);
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(buf, usage->channel_name,
+ channel_name_len);
+ if (ret) {
+ goto end;
+ }
+
+ if (fd_to_send) {
+ /* No fd to send */
+ *fd_to_send = -1;
+ }
+end:
+ return ret;
+}
+
+static
+bool lttng_condition_buffer_usage_is_equal(const struct lttng_condition *_a,
+ const struct lttng_condition *_b)
+{
+ bool is_equal = false;
+ struct lttng_condition_buffer_usage *a, *b;
+
+ a = container_of(_a, struct lttng_condition_buffer_usage, parent);
+ b = container_of(_b, struct lttng_condition_buffer_usage, parent);
+
+ if ((a->threshold_ratio.set && !b->threshold_ratio.set) ||
+ (a->threshold_bytes.set && !b->threshold_bytes.set)) {
+ goto end;
+ }
+
+ if (a->threshold_ratio.set && b->threshold_ratio.set) {
+ double a_value, b_value, diff;
+
+ a_value = a->threshold_ratio.value;
+ b_value = b->threshold_ratio.value;
+ diff = fabs(a_value - b_value);
+
+ if (diff > DBL_EPSILON) {
+ goto end;
+ }
+ } else if (a->threshold_bytes.set && b->threshold_bytes.set) {
+ uint64_t a_value, b_value;
+
+ a_value = a->threshold_bytes.value;
+ b_value = b->threshold_bytes.value;
+ if (a_value != b_value) {
+ goto end;
+ }
+ }
+
+ /* Condition is not valid if this is not true. */
+ assert(a->session_name);
+ assert(b->session_name);
+ if (strcmp(a->session_name, b->session_name)) {
+ goto end;
+ }
+
+ assert(a->channel_name);
+ assert(b->channel_name);
+ if (strcmp(a->channel_name, b->channel_name)) {
+ goto end;
+ }
+
+ assert(a->domain.set);
+ assert(b->domain.set);
+ if (a->domain.type != b->domain.type) {
+ goto end;
+ }
+ is_equal = true;
+end:
+ return is_equal;
+}
+
+static
+struct lttng_condition *lttng_condition_buffer_usage_create(
+ enum lttng_condition_type type)
+{
+ struct lttng_condition_buffer_usage *condition;
+
+ condition = zmalloc(sizeof(struct lttng_condition_buffer_usage));
+ if (!condition) {
+ return NULL;
+ }
+
+ lttng_condition_init(&condition->parent, type);
+ condition->parent.validate = lttng_condition_buffer_usage_validate;
+ condition->parent.serialize = lttng_condition_buffer_usage_serialize;
+ condition->parent.equal = lttng_condition_buffer_usage_is_equal;
+ condition->parent.destroy = lttng_condition_buffer_usage_destroy;
+ return &condition->parent;
+}
+
+struct lttng_condition *lttng_condition_buffer_usage_low_create(void)
+{
+ return lttng_condition_buffer_usage_create(
+ LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW);
+}
+
+struct lttng_condition *lttng_condition_buffer_usage_high_create(void)
+{
+ return lttng_condition_buffer_usage_create(
+ LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH);
+}
+
+static
+ssize_t init_condition_from_buffer(struct lttng_condition *condition,
+ const struct lttng_buffer_view *src_view)
+{
+ ssize_t ret, condition_size;
+ enum lttng_condition_status status;
+ enum lttng_domain_type domain_type;
+ const struct lttng_condition_buffer_usage_comm *condition_comm;
+ const char *session_name, *channel_name;
+ struct lttng_buffer_view names_view;
+
+ if (src_view->size < sizeof(*condition_comm)) {
+ ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+ ret = -1;
+ goto end;
+ }
+
+ condition_comm = (const struct lttng_condition_buffer_usage_comm *) src_view->data;
+ names_view = lttng_buffer_view_from_view(src_view,
+ sizeof(*condition_comm), -1);
+
+ if (condition_comm->session_name_len > LTTNG_NAME_MAX ||
+ condition_comm->channel_name_len > LTTNG_NAME_MAX) {
+ ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
+ ret = -1;
+ goto end;
+ }
+
+ if (names_view.size <
+ (condition_comm->session_name_len +
+ condition_comm->channel_name_len)) {
+ ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
+ ret = -1;
+ goto end;
+ }
+
+ if (condition_comm->threshold_set_in_bytes) {
+ status = lttng_condition_buffer_usage_set_threshold(condition,
+ condition_comm->threshold);
+ } else {
+ status = lttng_condition_buffer_usage_set_threshold_ratio(
+ condition,
+ fixed_to_double(condition_comm->threshold));
+ }
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to initialize buffer usage condition threshold");
+ ret = -1;
+ goto end;
+ }
+
+ if (condition_comm->domain_type <= LTTNG_DOMAIN_NONE ||
+ condition_comm->domain_type > LTTNG_DOMAIN_PYTHON) {
+ /* Invalid domain value. */
+ ERR("Invalid domain type value (%i) found in condition buffer",
+ (int) condition_comm->domain_type);
+ ret = -1;
+ goto end;
+ }
+
+ domain_type = (enum lttng_domain_type) condition_comm->domain_type;
+ status = lttng_condition_buffer_usage_set_domain_type(condition,
+ domain_type);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to set buffer usage condition domain");
+ ret = -1;
+ goto end;
+ }
+
+ session_name = names_view.data;
+ if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
+ ERR("Malformed session name encountered in condition buffer");
+ ret = -1;
+ goto end;
+ }
+
+ channel_name = session_name + condition_comm->session_name_len;
+ if (*(channel_name + condition_comm->channel_name_len - 1) != '\0') {
+ ERR("Malformed channel name encountered in condition buffer");
+ ret = -1;
+ goto end;
+ }
+
+ status = lttng_condition_buffer_usage_set_session_name(condition,
+ session_name);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to set buffer usage session name");
+ ret = -1;
+ goto end;
+ }
+
+ status = lttng_condition_buffer_usage_set_channel_name(condition,
+ channel_name);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to set buffer usage channel name");
+ ret = -1;
+ goto end;
+ }
+
+ if (!lttng_condition_validate(condition)) {
+ ret = -1;
+ goto end;
+ }
+
+ condition_size = sizeof(*condition_comm) +
+ (ssize_t) condition_comm->session_name_len +
+ (ssize_t) condition_comm->channel_name_len;
+ ret = condition_size;
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_buffer_usage_low_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_condition **_condition)
+{
+ ssize_t ret;
+ struct lttng_condition *condition =
+ lttng_condition_buffer_usage_low_create();
+
+ if (!_condition || !condition) {
+ ret = -1;
+ goto error;
+ }
+
+ ret = init_condition_from_buffer(condition, view);
+ if (ret < 0) {
+ goto error;
+ }
+
+ *_condition = condition;
+ return ret;
+error:
+ lttng_condition_destroy(condition);
+ return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_buffer_usage_high_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_condition **_condition)
+{
+ ssize_t ret;
+ struct lttng_condition *condition =
+ lttng_condition_buffer_usage_high_create();
+
+ if (!_condition || !condition) {
+ ret = -1;
+ goto error;
+ }
+
+ ret = init_condition_from_buffer(condition, view);
+ if (ret < 0) {
+ goto error;
+ }
+
+ *_condition = condition;
+ return ret;
+error:
+ lttng_condition_destroy(condition);
+ return ret;
+}
+
+static
+struct lttng_evaluation *create_evaluation_from_buffer(
+ enum lttng_condition_type type,
+ const struct lttng_buffer_view *view)
+{
+ const struct lttng_evaluation_buffer_usage_comm *comm =
+ (const struct lttng_evaluation_buffer_usage_comm *) view->data;
+ struct lttng_evaluation *evaluation = NULL;
+
+ if (view->size < sizeof(*comm)) {
+ goto end;
+ }
+
+ evaluation = lttng_evaluation_buffer_usage_create(type,
+ comm->buffer_use, comm->buffer_capacity);
+end:
+ return evaluation;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_buffer_usage_low_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_evaluation **_evaluation)
+{
+ ssize_t ret;
+ struct lttng_evaluation *evaluation = NULL;
+
+ if (!_evaluation) {
+ ret = -1;
+ goto error;
+ }
+
+ evaluation = create_evaluation_from_buffer(
+ LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, view);
+ if (!evaluation) {
+ ret = -1;
+ goto error;
+ }
+
+ *_evaluation = evaluation;
+ ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
+ return ret;
+error:
+ lttng_evaluation_destroy(evaluation);
+ return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_buffer_usage_high_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_evaluation **_evaluation)
+{
+ ssize_t ret;
+ struct lttng_evaluation *evaluation = NULL;
+
+ if (!_evaluation) {
+ ret = -1;
+ goto error;
+ }
+
+ evaluation = create_evaluation_from_buffer(
+ LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, view);
+ if (!evaluation) {
+ ret = -1;
+ goto error;
+ }
+
+ *_evaluation = evaluation;
+ ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
+ return ret;
+error:
+ lttng_evaluation_destroy(evaluation);
+ return ret;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_threshold_ratio(
+ const struct lttng_condition *condition,
+ double *threshold_ratio)
+{
+ struct lttng_condition_buffer_usage *usage;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_USAGE_CONDITION(condition) ||
+ !threshold_ratio) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+ if (!usage->threshold_ratio.set) {
+ status = LTTNG_CONDITION_STATUS_UNSET;
+ goto end;
+ }
+ *threshold_ratio = usage->threshold_ratio.value;
+end:
+ return status;
+}
+
+/* threshold_ratio expressed as [0.0, 1.0]. */
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_threshold_ratio(
+ struct lttng_condition *condition, double threshold_ratio)
+{
+ struct lttng_condition_buffer_usage *usage;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_USAGE_CONDITION(condition) ||
+ threshold_ratio < 0.0 ||
+ threshold_ratio > 1.0) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+ usage->threshold_ratio.set = true;
+ usage->threshold_bytes.set = false;
+ usage->threshold_ratio.value = threshold_ratio;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_threshold(
+ const struct lttng_condition *condition,
+ uint64_t *threshold_bytes)
+{
+ struct lttng_condition_buffer_usage *usage;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_USAGE_CONDITION(condition) || !threshold_bytes) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+ if (!usage->threshold_bytes.set) {
+ status = LTTNG_CONDITION_STATUS_UNSET;
+ goto end;
+ }
+ *threshold_bytes = usage->threshold_bytes.value;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_threshold(
+ struct lttng_condition *condition, uint64_t threshold_bytes)
+{
+ struct lttng_condition_buffer_usage *usage;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_USAGE_CONDITION(condition)) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+ usage->threshold_ratio.set = false;
+ usage->threshold_bytes.set = true;
+ usage->threshold_bytes.value = threshold_bytes;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_session_name(
+ const struct lttng_condition *condition,
+ const char **session_name)
+{
+ struct lttng_condition_buffer_usage *usage;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_USAGE_CONDITION(condition) || !session_name) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+ if (!usage->session_name) {
+ status = LTTNG_CONDITION_STATUS_UNSET;
+ goto end;
+ }
+ *session_name = usage->session_name;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_session_name(
+ struct lttng_condition *condition, const char *session_name)
+{
+ char *session_name_copy;
+ struct lttng_condition_buffer_usage *usage;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_USAGE_CONDITION(condition) || !session_name ||
+ strlen(session_name) == 0) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+ session_name_copy = strdup(session_name);
+ if (!session_name_copy) {
+ status = LTTNG_CONDITION_STATUS_ERROR;
+ goto end;
+ }
+
+ if (usage->session_name) {
+ free(usage->session_name);
+ }
+ usage->session_name = session_name_copy;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_channel_name(
+ const struct lttng_condition *condition,
+ const char **channel_name)
+{
+ struct lttng_condition_buffer_usage *usage;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+ if (!usage->channel_name) {
+ status = LTTNG_CONDITION_STATUS_UNSET;
+ goto end;
+ }
+ *channel_name = usage->channel_name;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_channel_name(
+ struct lttng_condition *condition, const char *channel_name)
+{
+ char *channel_name_copy;
+ struct lttng_condition_buffer_usage *usage;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name ||
+ strlen(channel_name) == 0) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+ channel_name_copy = strdup(channel_name);
+ if (!channel_name_copy) {
+ status = LTTNG_CONDITION_STATUS_ERROR;
+ goto end;
+ }
+
+ if (usage->channel_name) {
+ free(usage->channel_name);
+ }
+ usage->channel_name = channel_name_copy;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_domain_type(
+ const struct lttng_condition *condition,
+ enum lttng_domain_type *type)
+{
+ struct lttng_condition_buffer_usage *usage;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_USAGE_CONDITION(condition) || !type) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+ if (!usage->domain.set) {
+ status = LTTNG_CONDITION_STATUS_UNSET;
+ goto end;
+ }
+ *type = usage->domain.type;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_domain_type(
+ struct lttng_condition *condition, enum lttng_domain_type type)
+{
+ struct lttng_condition_buffer_usage *usage;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_USAGE_CONDITION(condition) ||
+ type == LTTNG_DOMAIN_NONE) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(condition, struct lttng_condition_buffer_usage,
+ parent);
+ usage->domain.set = true;
+ usage->domain.type = type;
+end:
+ return status;
+}
+
+static
+int lttng_evaluation_buffer_usage_serialize(
+ const struct lttng_evaluation *evaluation,
+ struct lttng_dynamic_buffer *buf)
+{
+ struct lttng_evaluation_buffer_usage *usage;
+ struct lttng_evaluation_buffer_usage_comm comm;
+
+ usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+ parent);
+ comm.buffer_use = usage->buffer_use;
+ comm.buffer_capacity = usage->buffer_capacity;
+
+ return lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+}
+
+static
+void lttng_evaluation_buffer_usage_destroy(
+ struct lttng_evaluation *evaluation)
+{
+ struct lttng_evaluation_buffer_usage *usage;
+
+ usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+ parent);
+ free(usage);
+}
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_buffer_usage_create(
+ enum lttng_condition_type type, uint64_t use, uint64_t capacity)
+{
+ struct lttng_evaluation_buffer_usage *usage;
+
+ usage = zmalloc(sizeof(struct lttng_evaluation_buffer_usage));
+ if (!usage) {
+ goto end;
+ }
+
+ usage->parent.type = type;
+ usage->buffer_use = use;
+ usage->buffer_capacity = capacity;
+ usage->parent.serialize = lttng_evaluation_buffer_usage_serialize;
+ usage->parent.destroy = lttng_evaluation_buffer_usage_destroy;
+end:
+ return &usage->parent;
+}
+
+/*
+ * Get the sampled buffer usage which caused the associated condition to
+ * evaluate to "true".
+ */
+enum lttng_evaluation_status
+lttng_evaluation_buffer_usage_get_usage_ratio(
+ const struct lttng_evaluation *evaluation, double *usage_ratio)
+{
+ struct lttng_evaluation_buffer_usage *usage;
+ enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+ if (!evaluation || !is_usage_evaluation(evaluation) || !usage_ratio) {
+ status = LTTNG_EVALUATION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+ parent);
+ *usage_ratio = (double) usage->buffer_use /
+ (double) usage->buffer_capacity;
+end:
+ return status;
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_buffer_usage_get_usage(
+ const struct lttng_evaluation *evaluation,
+ uint64_t *usage_bytes)
+{
+ struct lttng_evaluation_buffer_usage *usage;
+ enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+ if (!evaluation || !is_usage_evaluation(evaluation) || !usage_bytes) {
+ status = LTTNG_EVALUATION_STATUS_INVALID;
+ goto end;
+ }
+
+ usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+ parent);
+ *usage_bytes = usage->buffer_use;
+end:
+ return status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/buffer-usage-internal.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/condition/session-consumed-size-internal.h>
+#include <lttng/condition/session-rotation-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <common/dynamic-buffer.h>
+#include <common/buffer-view.h>
+#include <stdbool.h>
+#include <assert.h>
+
+enum lttng_condition_type lttng_condition_get_type(
+ const struct lttng_condition *condition)
+{
+ return condition ? condition->type : LTTNG_CONDITION_TYPE_UNKNOWN;
+}
+
+void lttng_condition_destroy(struct lttng_condition *condition)
+{
+ if (!condition) {
+ return;
+ }
+
+ assert(condition->destroy);
+ condition->destroy(condition);
+}
+
+LTTNG_HIDDEN
+bool lttng_condition_validate(const struct lttng_condition *condition)
+{
+ bool valid;
+
+ if (!condition) {
+ valid = false;
+ goto end;
+ }
+
+ if (!condition->validate) {
+ /* Sub-class guarantees that it can never be invalid. */
+ valid = true;
+ goto end;
+ }
+
+ valid = condition->validate(condition);
+end:
+ return valid;
+}
+
+LTTNG_HIDDEN
+int lttng_condition_serialize(const struct lttng_condition *condition,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
+{
+ int ret;
+ struct lttng_condition_comm condition_comm = { 0 };
+
+ if (!condition) {
+ ret = -1;
+ goto end;
+ }
+
+ condition_comm.condition_type = (int8_t) condition->type;
+
+ ret = lttng_dynamic_buffer_append(buf, &condition_comm,
+ sizeof(condition_comm));
+ if (ret) {
+ goto end;
+ }
+
+ ret = condition->serialize(condition, buf, fd_to_send);
+ if (ret) {
+ goto end;
+ }
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+bool lttng_condition_is_equal(const struct lttng_condition *a,
+ const struct lttng_condition *b)
+{
+ bool is_equal = false;
+
+ if (!a || !b) {
+ goto end;
+ }
+
+ if (a->type != b->type) {
+ goto end;
+ }
+
+ if (a == b) {
+ is_equal = true;
+ goto end;
+ }
+
+ is_equal = a->equal ? a->equal(a, b) : true;
+end:
+ return is_equal;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_create_from_buffer(
+ const struct lttng_buffer_view *buffer,
+ struct lttng_condition **condition)
+{
+ ssize_t ret, condition_size = 0;
+ const struct lttng_condition_comm *condition_comm;
+ condition_create_from_buffer_cb create_from_buffer = NULL;
+
+ if (!buffer || !condition) {
+ ret = -1;
+ goto end;
+ }
+
+ DBG("Deserializing condition from buffer");
+ condition_comm = (const struct lttng_condition_comm *) buffer->data;
+ condition_size += sizeof(*condition_comm);
+
+ switch ((enum lttng_condition_type) condition_comm->condition_type) {
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+ create_from_buffer = lttng_condition_buffer_usage_low_create_from_buffer;
+ break;
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+ create_from_buffer = lttng_condition_buffer_usage_high_create_from_buffer;
+ break;
+ case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+ create_from_buffer = lttng_condition_session_consumed_size_create_from_buffer;
+ break;
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+ create_from_buffer = lttng_condition_session_rotation_ongoing_create_from_buffer;
+ break;
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+ create_from_buffer = lttng_condition_session_rotation_completed_create_from_buffer;
+ break;
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ create_from_buffer = lttng_condition_event_rule_create_from_buffer;
+ break;
+ default:
+ ERR("Attempted to create condition of unknown type (%i)",
+ (int) condition_comm->condition_type);
+ ret = -1;
+ goto end;
+ }
+
+ if (create_from_buffer) {
+ const struct lttng_buffer_view view =
+ lttng_buffer_view_from_view(buffer,
+ sizeof(*condition_comm), -1);
+
+ ret = create_from_buffer(&view, condition);
+ if (ret < 0) {
+ goto end;
+ }
+ condition_size += ret;
+
+ } else {
+ abort();
+ }
+
+ ret = condition_size;
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+void lttng_condition_init(struct lttng_condition *condition,
+ enum lttng_condition_type type)
+{
+ condition->type = type;
+}
+
+LTTNG_HIDDEN
+const char *lttng_condition_type_str(enum lttng_condition_type type)
+{
+ switch (type) {
+ case LTTNG_CONDITION_TYPE_UNKNOWN:
+ return "unknown";
+
+ case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+ return "session consumed size";
+
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+ return "buffer usage high";
+
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+ return "buffer usage low";
+
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+ return "session rotation ongoing";
+
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+ return "session rotation completed";
+
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ return "event rule hit";
+
+ default:
+ return "???";
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2017 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lttng/condition/event-rule.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-field-value-internal.h>
+#include <lttng/event-expr-internal.h>
+#include <lttng/event-expr.h>
+#include <lttng/lttng-error.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <common/event-expr-to-bytecode.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <msgpack.h>
+
+#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_dynamic_buffer *buf,
+ int *fd_to_send);
+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 session event_rule condition: a rule must be set.");
+ goto end;
+ }
+
+ valid = lttng_event_rule_validate(event_rule->rule);
+end:
+ return valid;
+}
+
+/*
+ * Serializes the C string `str` into `buf`.
+ *
+ * Encoding is the length of `str` plus one (for the null character),
+ * and then the string, including its null character.
+ */
+static
+int serialize_cstr(const char *str, struct lttng_dynamic_buffer *buf)
+{
+ int ret;
+ uint32_t len = strlen(str) + 1;
+
+ /* Serialize the length, including the null character */
+ DBG("Serializing C string's length (including null character): "
+ "%" PRIu32, len);
+ ret = lttng_dynamic_buffer_append(buf, &len, sizeof(len));
+ if (ret) {
+ goto end;
+ }
+
+ /* Serialize the string */
+ DBG("Serializing C string: \"%s\"", str);
+ ret = lttng_dynamic_buffer_append(buf, str, len);
+ if (ret) {
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+/*
+ * Serializes the event expression `expr` into `buf`.
+ */
+static
+int serialize_event_expr(const struct lttng_event_expr *expr,
+ struct lttng_dynamic_buffer *buf)
+{
+ uint8_t type;
+ int ret;
+
+ /* Serialize the expression's type */
+ DBG("Serializing event expression's type: %d", expr->type);
+ type = expr->type;
+ ret = lttng_dynamic_buffer_append(buf, &type, sizeof(type));
+ if (ret) {
+ goto end;
+ }
+
+ /* Serialize the expression */
+ switch (expr->type) {
+ case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+ case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+ {
+ const struct lttng_event_expr_field *field_expr =
+ container_of(expr,
+ const struct lttng_event_expr_field,
+ parent);
+
+ /* Serialize the field name */
+ DBG("Serializing field event expression's field name: \"%s\"",
+ field_expr->name);
+ ret = serialize_cstr(field_expr->name, buf);
+ if (ret) {
+ goto end;
+ }
+
+ break;
+ }
+ case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+ {
+ const struct lttng_event_expr_app_specific_context_field *field_expr =
+ container_of(expr,
+ const struct lttng_event_expr_app_specific_context_field,
+ parent);
+
+ /* Serialize the provider name */
+ DBG("Serializing app-specific context field event expression's "
+ "provider name: \"%s\"",
+ field_expr->provider_name);
+ ret = serialize_cstr(field_expr->provider_name, buf);
+ if (ret) {
+ goto end;
+ }
+
+ /* Serialize the type name */
+ DBG("Serializing app-specific context field event expression's "
+ "type name: \"%s\"",
+ field_expr->provider_name);
+ ret = serialize_cstr(field_expr->type_name, buf);
+ if (ret) {
+ goto end;
+ }
+
+ break;
+ }
+ case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+ {
+ const struct lttng_event_expr_array_field_element *elem_expr =
+ container_of(expr,
+ const struct lttng_event_expr_array_field_element,
+ parent);
+ uint32_t index = elem_expr->index;
+
+ /* Serialize the index */
+ DBG("Serializing array field element event expression's "
+ "index: %u", elem_expr->index);
+ ret = lttng_dynamic_buffer_append(buf, &index, sizeof(index));
+ if (ret) {
+ goto end;
+ }
+
+ /* Serialize the parent array field expression */
+ DBG("Serializing array field element event expression's "
+ "parent array field event expression.");
+ ret = serialize_event_expr(elem_expr->array_field_expr, buf);
+ if (ret) {
+ goto end;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+end:
+ return ret;
+}
+
+static
+int lttng_condition_event_rule_serialize(
+ const struct lttng_condition *condition,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
+{
+ int ret;
+ struct lttng_condition_event_rule *event_rule;
+ enum lttng_condition_status status;
+ uint32_t capture_descr_count;
+ uint32_t i;
+
+ 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);
+
+ DBG("Serializing event rule condition's event rule");
+ ret = lttng_event_rule_serialize(event_rule->rule, buf, fd_to_send);
+ if (ret) {
+ goto end;
+ }
+
+ status = lttng_condition_event_rule_get_capture_descriptor_count(
+ condition, &capture_descr_count);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ ret = -1;
+ goto end;
+ };
+
+ DBG("Serializing event rule condition's capture descriptor count: %" PRIu32,
+ capture_descr_count);
+ ret = lttng_dynamic_buffer_append(buf, &capture_descr_count,
+ sizeof(capture_descr_count));
+ if (ret) {
+ goto end;
+ }
+
+ for (i = 0; i < capture_descr_count; i++) {
+ const struct lttng_capture_descriptor *desc =
+ lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+ condition, i);
+
+ DBG("Serializing event rule condition's capture descriptor %" PRIu32,
+ i);
+ ret = serialize_event_expr(desc->event_expression, buf);
+ if (ret) {
+ goto end;
+ }
+
+ /*
+ * Appending the internal index payload linked with the
+ * descriptor.
+ * TODO: might want to move to an englobing object to describe a
+ * capture descriptor publicly.
+ */
+ ret = lttng_dynamic_buffer_append(buf, &desc->capture_index,
+ sizeof(desc->capture_index));
+ if (ret) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static
+bool capture_descriptors_are_equal(
+ const struct lttng_condition *condition_a,
+ const struct lttng_condition *condition_b)
+{
+ bool is_equal = true;
+ unsigned int capture_descr_count_a;
+ unsigned int capture_descr_count_b;
+ size_t i;
+ enum lttng_condition_status status;
+
+ status = lttng_condition_event_rule_get_capture_descriptor_count(
+ condition_a, &capture_descr_count_a);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ goto not_equal;
+ }
+
+ status = lttng_condition_event_rule_get_capture_descriptor_count(
+ condition_b, &capture_descr_count_b);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ goto not_equal;
+ }
+
+ if (capture_descr_count_a != capture_descr_count_b) {
+ goto not_equal;
+ }
+
+ for (i = 0; i < capture_descr_count_a; i++) {
+ const struct lttng_event_expr *expr_a =
+ lttng_condition_event_rule_get_capture_descriptor_at_index(
+ condition_a,
+ i);
+ const struct lttng_event_expr *expr_b =
+ lttng_condition_event_rule_get_capture_descriptor_at_index(
+ condition_b,
+ i);
+
+ if (!lttng_event_expr_is_equal(expr_a, expr_b)) {
+ goto not_equal;
+ }
+ }
+
+ goto end;
+
+not_equal:
+ is_equal = false;
+
+end:
+ return is_equal;
+}
+
+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 session names must be set or both must be unset. */
+ if ((a->rule && !b->rule) ||
+ (!a->rule && b->rule)) {
+ WARN("Comparing session event_rule conditions with uninitialized rule.");
+ goto end;
+ }
+
+ is_equal = lttng_event_rule_is_equal(a->rule, b->rule);
+ if (!is_equal) {
+ goto end;
+ }
+
+ is_equal = capture_descriptors_are_equal(_a, _b);
+
+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_destroy(event_rule->rule);
+ lttng_dynamic_pointer_array_reset(&event_rule->capture_descriptors);
+ free(event_rule);
+}
+
+static
+void destroy_capture_descriptor(void *ptr)
+{
+ struct lttng_capture_descriptor *desc =
+ (struct lttng_capture_descriptor *) ptr;
+ lttng_event_expr_destroy(desc->event_expression);
+ free(desc);
+}
+
+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,
+
+ condition->rule = rule;
+ lttng_dynamic_pointer_array_init(&condition->capture_descriptors,
+ destroy_capture_descriptor);
+
+ parent = &condition->parent;
+end:
+ return parent;
+}
+
+static
+int64_t int_from_buffer(const struct lttng_buffer_view *view, size_t size,
+ size_t *offset)
+{
+ int64_t ret;
+
+ if (*offset + size > view->size) {
+ ret = -1;
+ goto end;
+ }
+
+ switch (size) {
+ case 1:
+ ret = (int64_t) view->data[*offset];
+ break;
+ case sizeof(int32_t):
+ {
+ int32_t s32;
+
+ memcpy(&s32, &view->data[*offset], sizeof(s32));
+ ret = (int64_t) s32;
+ break;
+ }
+ case sizeof(ret):
+ memcpy(&ret, &view->data[*offset], sizeof(ret));
+ break;
+ default:
+ abort();
+ }
+
+ *offset += size;
+
+end:
+ return ret;
+}
+static
+uint64_t uint_from_buffer(const struct lttng_buffer_view *view, size_t size,
+ size_t *offset)
+{
+ uint64_t ret;
+
+ if (*offset + size > view->size) {
+ ret = UINT64_C(-1);
+ goto end;
+ }
+
+ switch (size) {
+ case 1:
+ ret = (uint64_t) view->data[*offset];
+ break;
+ case sizeof(uint32_t):
+ {
+ uint32_t u32;
+
+ memcpy(&u32, &view->data[*offset], sizeof(u32));
+ ret = (uint64_t) u32;
+ break;
+ }
+ case sizeof(ret):
+ memcpy(&ret, &view->data[*offset], sizeof(ret));
+ break;
+ default:
+ abort();
+ }
+
+ *offset += size;
+
+end:
+ return ret;
+}
+
+static
+const char *str_from_buffer(const struct lttng_buffer_view *view,
+ size_t *offset)
+{
+ uint64_t len;
+ const char *ret;
+
+ len = uint_from_buffer(view, sizeof(uint32_t), offset);
+ if (len == UINT64_C(-1)) {
+ goto error;
+ }
+
+ ret = &view->data[*offset];
+
+ if (!lttng_buffer_view_contains_string(view, ret, len)) {
+ goto error;
+ }
+
+ *offset += len;
+ goto end;
+
+error:
+ ret = NULL;
+
+end:
+ return ret;
+}
+
+static
+struct lttng_event_expr *event_expr_from_buffer(
+ const struct lttng_buffer_view *view, size_t *offset)
+{
+ struct lttng_event_expr *expr = NULL;
+ const char *str;
+ uint64_t type;
+
+ type = uint_from_buffer(view, sizeof(uint8_t), offset);
+ if (type == UINT64_C(-1)) {
+ goto error;
+ }
+
+ switch (type) {
+ case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+ str = str_from_buffer(view, offset);
+ if (!str) {
+ goto error;
+ }
+
+ expr = lttng_event_expr_event_payload_field_create(str);
+ break;
+ case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+ str = str_from_buffer(view, offset);
+ if (!str) {
+ goto error;
+ }
+
+ expr = lttng_event_expr_channel_context_field_create(str);
+ break;
+ case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+ {
+ const char *provider_name;
+ const char *type_name;
+
+ provider_name = str_from_buffer(view, offset);
+ if (!provider_name) {
+ goto error;
+ }
+
+ type_name = str_from_buffer(view, offset);
+ if (!type_name) {
+ goto error;
+ }
+
+ expr = lttng_event_expr_app_specific_context_field_create(
+ provider_name, type_name);
+ break;
+ }
+ case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+ {
+ struct lttng_event_expr *array_field_expr;
+ uint64_t index;
+
+ index = uint_from_buffer(view, sizeof(uint32_t), offset);
+ if (index == UINT64_C(-1)) {
+ goto error;
+ }
+
+ /* Array field expression is the encoded after this */
+ array_field_expr = event_expr_from_buffer(view, offset);
+ if (!array_field_expr) {
+ goto error;
+ }
+
+ /* Move ownership of `array_field_expr` to new expression */
+ expr = lttng_event_expr_array_field_element_create(
+ array_field_expr, (unsigned int) index);
+ if (!expr) {
+ /* `array_field_expr` not moved: destroy it */
+ lttng_event_expr_destroy(array_field_expr);
+ }
+
+ break;
+ }
+ default:
+ abort();
+ }
+
+ goto end;
+
+error:
+ lttng_event_expr_destroy(expr);
+ expr = NULL;
+
+end:
+ return expr;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_event_rule_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_condition **_condition)
+{
+ ssize_t consumed_length;
+ size_t offset = 0;
+ ssize_t size;
+ uint64_t capture_descr_count;
+ uint64_t i;
+ struct lttng_condition *condition = NULL;
+ struct lttng_event_rule *event_rule = NULL;
+ struct lttng_buffer_view event_rule_view;
+
+ if (!view || !_condition) {
+ goto error;
+ }
+
+ /* Event rule */
+ event_rule_view = lttng_buffer_view_from_view(view, offset, -1);
+ size = lttng_event_rule_create_from_buffer(&event_rule_view,
+ &event_rule);
+ if (size < 0 || !event_rule) {
+ goto error;
+ }
+
+ /* Create condition (no capture descriptors yet) at this point */
+ condition = lttng_condition_event_rule_create(event_rule);
+ if (!condition) {
+ goto error;
+ }
+
+ /* Ownership moved to `condition` */
+ event_rule = NULL;
+
+ /* Capture descriptor count */
+ assert(size >= 0);
+ offset += (size_t) size;
+ capture_descr_count = uint_from_buffer(view, sizeof(uint32_t), &offset);
+ if (capture_descr_count == UINT64_C(-1)) {
+ goto error;
+ }
+
+ /* Capture descriptors */
+ for (i = 0; i < capture_descr_count; i++) {
+ enum lttng_condition_status status;
+ struct lttng_capture_descriptor *desc;
+
+ struct lttng_event_expr *expr = event_expr_from_buffer(
+ view, &offset);
+ int32_t payload_index = int_from_buffer(view, sizeof(int32_t),
+ &offset);
+
+ if (!expr) {
+ goto error;
+ }
+
+ /* Move ownership of `expr` to `condition` */
+ status = lttng_condition_event_rule_append_capture_descriptor(
+ condition, expr);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ /* `expr` not moved: destroy it */
+ lttng_event_expr_destroy(expr);
+ goto error;
+ }
+
+ /*
+ * Set the internal payload object for the descriptor. This can
+ * be used by liblttng-ctl to access capture msgpack payload on
+ * the client side.
+ */
+ desc = lttng_condition_event_rule_get_internal_capture_descriptor_at_index(condition, i);
+ if (desc == NULL) {
+ goto error;
+ }
+ desc->capture_index = payload_index;
+ }
+
+ consumed_length = (ssize_t) offset;
+ *_condition = condition;
+ condition = NULL;
+ goto end;
+
+error:
+ consumed_length = -1;
+
+end:
+ lttng_event_rule_destroy(event_rule);
+ lttng_condition_destroy(condition);
+ return consumed_length;
+}
+
+LTTNG_HIDDEN
+enum lttng_condition_status
+lttng_condition_event_rule_get_rule_no_const(
+ 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 *no_const_rule = NULL;
+ enum lttng_condition_status status;
+
+ status = lttng_condition_event_rule_get_rule_no_const(condition, &no_const_rule);
+ *rule = no_const_rule;
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_event_rule_append_capture_descriptor(
+ struct lttng_condition *condition,
+ struct lttng_event_expr *expr)
+{
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+ struct lttng_condition_event_rule *event_rule_cond =
+ container_of(condition,
+ struct lttng_condition_event_rule, parent);
+ int ret;
+ struct lttng_capture_descriptor *descriptor = NULL;
+ const struct lttng_event_rule *rule = NULL;
+
+ /* Only accept l-values */
+ if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !expr ||
+ !lttng_event_expr_is_lvalue(expr)) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ status = lttng_condition_event_rule_get_rule(condition, &rule);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ goto end;
+ }
+
+ switch(lttng_event_rule_get_type(rule)) {
+ case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+ case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+ /* Supported */
+ status = LTTNG_CONDITION_STATUS_OK;
+ break;
+ case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ break;
+ default:
+ status = LTTNG_CONDITION_STATUS_UNSUPPORTED;
+ break;
+ }
+
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ goto end;
+ }
+
+ descriptor = malloc(sizeof(*descriptor));
+ if (descriptor == NULL) {
+ status = LTTNG_CONDITION_STATUS_ERROR;
+ goto end;
+ }
+
+ descriptor->capture_index = -1;
+ descriptor->event_expression = expr;
+
+ ret = lttng_dynamic_pointer_array_add_pointer(
+ &event_rule_cond->capture_descriptors, descriptor);
+ if (ret) {
+ status = LTTNG_CONDITION_STATUS_ERROR;
+ goto end;
+ }
+
+ /* Ownership is transfered to the internal capture_descriptors array */
+ descriptor = NULL;
+end:
+ free(descriptor);
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_event_rule_get_capture_descriptor_count(
+ const struct lttng_condition *condition, unsigned int *count)
+{
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+ const struct lttng_condition_event_rule *event_rule_cond =
+ container_of(condition,
+ const struct lttng_condition_event_rule,
+ parent);
+
+ if (!condition || !IS_EVENT_RULE_CONDITION(condition) || !count) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ *count = lttng_dynamic_pointer_array_get_count(
+ &event_rule_cond->capture_descriptors);
+
+end:
+ return status;
+}
+
+LTTNG_HIDDEN
+struct lttng_capture_descriptor *
+lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+ const struct lttng_condition *condition, unsigned int index)
+{
+ const struct lttng_condition_event_rule *event_rule_cond =
+ container_of(condition,
+ const struct lttng_condition_event_rule,
+ parent);
+ struct lttng_capture_descriptor *desc = NULL;
+ unsigned int count;
+ enum lttng_condition_status status;
+
+ if (!condition || !IS_EVENT_RULE_CONDITION(condition)) {
+ goto end;
+ }
+
+ status = lttng_condition_event_rule_get_capture_descriptor_count(
+ condition, &count);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ goto end;
+ }
+
+ if (index >= count) {
+ goto end;
+ }
+
+ desc = lttng_dynamic_pointer_array_get_pointer(
+ &event_rule_cond->capture_descriptors, index);
+end:
+ return desc;
+}
+
+const struct lttng_event_expr *
+lttng_condition_event_rule_get_capture_descriptor_at_index(
+ const struct lttng_condition *condition, unsigned int index)
+{
+ const struct lttng_event_expr *expr = NULL;
+ const struct lttng_capture_descriptor *desc = NULL;
+
+ desc = lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+ condition, index);
+ if (desc == NULL) {
+ goto end;
+ }
+ expr = desc->event_expression;
+
+end:
+ return expr;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_event_rule_create_from_buffer(
+ const struct lttng_condition_event_rule *condition,
+ const struct lttng_buffer_view *view,
+ struct lttng_evaluation **_evaluation)
+{
+ ssize_t ret, offset = 0;
+ const char *name;
+ struct lttng_evaluation *evaluation = NULL;
+ const struct lttng_evaluation_event_rule_comm *comm =
+ (const struct lttng_evaluation_event_rule_comm *) view->data;
+ struct lttng_buffer_view current_view;
+ uint32_t capture_payload_size;
+ const char *capture_payload = NULL;
+
+ if (!_evaluation) {
+ ret = -1;
+ goto error;
+ }
+
+ if (view->size < sizeof(*comm)) {
+ ret = -1;
+ goto error;
+ }
+
+ /* Map the name, view of the payload */
+ offset += sizeof(*comm);
+ current_view = lttng_buffer_view_from_view(view, offset, comm->trigger_name_length);
+ name = current_view.data;
+ if (!name) {
+ ret = -1;
+ goto error;
+ }
+
+ if (comm->trigger_name_length == 1 ||
+ name[comm->trigger_name_length - 1] != '\0' ||
+ strlen(name) != comm->trigger_name_length - 1) {
+ /*
+ * Check that the name is not NULL, is NULL-terminated, and
+ * does not contain a NULL before the last byte.
+ */
+ ret = -1;
+ goto error;
+ }
+
+ offset += comm->trigger_name_length;
+ current_view = lttng_buffer_view_from_view(view, offset, -1);
+
+ if (current_view.size < sizeof(capture_payload_size)) {
+ ret = -1;
+ goto error;
+ }
+
+ memcpy(&capture_payload_size, current_view.data,
+ sizeof(capture_payload_size));
+ offset += sizeof(capture_payload_size);
+
+ if (capture_payload_size > 0) {
+ current_view = lttng_buffer_view_from_view(view, offset, -1);
+
+ if (current_view.size < capture_payload_size) {
+ ret = -1;
+ goto error;
+ }
+
+ capture_payload = current_view.data;
+ }
+
+ evaluation = lttng_evaluation_event_rule_create(condition, name,
+ capture_payload, capture_payload_size, true);
+ if (!evaluation) {
+ ret = -1;
+ goto error;
+ }
+
+ offset += capture_payload_size;
+ *_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_dynamic_buffer *buf)
+{
+ int ret = 0;
+ struct lttng_evaluation_event_rule *hit;
+ struct lttng_evaluation_event_rule_comm comm;
+ uint32_t capture_payload_size;
+
+ hit = container_of(evaluation, struct lttng_evaluation_event_rule,
+ parent);
+ comm.trigger_name_length = strlen(hit->name) + 1;
+ ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(buf, hit->name, comm.trigger_name_length);
+ if (ret) {
+ goto end;
+ }
+
+ capture_payload_size = (uint32_t) hit->capture_payload.size;
+ ret = lttng_dynamic_buffer_append(buf, &capture_payload_size,
+ sizeof(capture_payload_size));
+ if (ret) {
+ goto end;
+ }
+
+ ret = lttng_dynamic_buffer_append(buf, hit->capture_payload.data,
+ hit->capture_payload.size);
+ if (ret) {
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+static
+bool msgpack_str_is_equal(const struct msgpack_object *obj, const char *str)
+{
+ bool is_equal = true;
+
+ assert(obj->type == MSGPACK_OBJECT_STR);
+
+ if (obj->via.str.size != strlen(str)) {
+ is_equal = false;
+ goto end;
+ }
+
+ if (strncmp(obj->via.str.ptr, str, obj->via.str.size) != 0) {
+ is_equal = false;
+ goto end;
+ }
+
+end:
+ return is_equal;
+}
+
+static
+const msgpack_object *get_msgpack_map_obj(const struct msgpack_object *map_obj,
+ const char *name)
+{
+ const msgpack_object *ret = NULL;
+ size_t i;
+
+ assert(map_obj->type == MSGPACK_OBJECT_MAP);
+
+ for (i = 0; i < map_obj->via.map.size; i++) {
+ const struct msgpack_object_kv *kv = &map_obj->via.map.ptr[i];
+
+ assert(kv->key.type == MSGPACK_OBJECT_STR);
+
+ if (msgpack_str_is_equal(&kv->key, name)) {
+ ret = &kv->val;
+ goto end;
+ }
+ }
+
+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);
+ lttng_dynamic_buffer_reset(&hit->capture_payload);
+ if (hit->captured_values) {
+ lttng_event_field_value_destroy(hit->captured_values);
+ }
+ free(hit);
+}
+
+static
+int event_field_value_from_obj(const msgpack_object *obj,
+ struct lttng_event_field_value **field_val)
+{
+ assert(obj);
+ assert(field_val);
+ int ret = 0;
+
+ switch (obj->type) {
+ case MSGPACK_OBJECT_NIL:
+ /* Unavailable */
+ *field_val = NULL;
+ goto end;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ *field_val = lttng_event_field_value_uint_create(
+ obj->via.u64);
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ *field_val = lttng_event_field_value_int_create(
+ obj->via.i64);
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ *field_val = lttng_event_field_value_real_create(
+ obj->via.f64);
+ break;
+ case MSGPACK_OBJECT_STR:
+ *field_val = lttng_event_field_value_string_create_with_size(
+ obj->via.str.ptr, obj->via.str.size);
+ break;
+ case MSGPACK_OBJECT_ARRAY:
+ {
+ size_t i;
+
+ *field_val = lttng_event_field_value_array_create();
+ if (!*field_val) {
+ goto error;
+ }
+
+ for (i = 0; i < obj->via.array.size; i++) {
+ const msgpack_object *elem_obj = &obj->via.array.ptr[i];
+ struct lttng_event_field_value *elem_field_val;
+
+ ret = event_field_value_from_obj(elem_obj,
+ &elem_field_val);
+
+ if (ret) {
+ goto error;
+ }
+
+ if (elem_field_val) {
+ ret = lttng_event_field_value_array_append(
+ *field_val, elem_field_val);
+ } else {
+ ret = lttng_event_field_value_array_append_unavailable(
+ *field_val);
+ }
+
+ if (ret) {
+ lttng_event_field_value_destroy(elem_field_val);
+ goto error;
+ }
+ }
+
+ break;
+ }
+ case MSGPACK_OBJECT_MAP:
+ {
+ /*
+ * As of this version, the only valid map object is
+ * for an enumeration value, for example:
+ *
+ * type: enum
+ * value: 177
+ * labels:
+ * - Labatt 50
+ * - Molson Dry
+ * - Carling Black Label
+ */
+ const msgpack_object *inner_obj;
+ size_t label_i;
+
+ inner_obj = get_msgpack_map_obj(obj, "type");
+ if (!inner_obj) {
+ ERR("Missing `type` entry in map object.");
+ goto error;
+ }
+
+ if (inner_obj->type != MSGPACK_OBJECT_STR) {
+ ERR("Map object's `type` entry is not a string (it's a %d).",
+ inner_obj->type);
+ goto error;
+ }
+
+ if (!msgpack_str_is_equal(inner_obj, "enum")) {
+ ERR("Map object's `type` entry: expecting `enum`.");
+ goto error;
+ }
+
+ inner_obj = get_msgpack_map_obj(obj, "value");
+ if (!inner_obj) {
+ ERR("Missing `value` entry in map object.");
+ goto error;
+ }
+
+ if (inner_obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER) {
+ *field_val = lttng_event_field_value_enum_uint_create(
+ inner_obj->via.u64);
+ } else if (inner_obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER) {
+ *field_val = lttng_event_field_value_enum_int_create(
+ inner_obj->via.i64);
+ } else {
+ ERR("Map object's `value` entry is not an integer (it's a %d).",
+ inner_obj->type);
+ goto error;
+ }
+
+ if (!*field_val) {
+ goto error;
+ }
+
+ inner_obj = get_msgpack_map_obj(obj, "labels");
+ if (!inner_obj) {
+ /* No labels */
+ goto end;
+ }
+
+ if (inner_obj->type != MSGPACK_OBJECT_ARRAY) {
+ ERR("Map object's `labels` entry is not an array (it's a %d).",
+ inner_obj->type);
+ goto error;
+ }
+
+ for (label_i = 0; label_i < inner_obj->via.array.size;
+ label_i++) {
+ int iret;
+ const msgpack_object *elem_obj =
+ &inner_obj->via.array.ptr[label_i];
+
+ if (elem_obj->type != MSGPACK_OBJECT_STR) {
+ ERR("Map object's `labels` entry's type is not a string (it's a %d).",
+ elem_obj->type);
+ goto error;
+ }
+
+ iret = lttng_event_field_value_enum_append_label_with_size(
+ *field_val, elem_obj->via.str.ptr,
+ elem_obj->via.str.size);
+ if (iret) {
+ goto error;
+ }
+ }
+
+ break;
+ }
+ default:
+ ERR("Unexpected object type %d.", obj->type);
+ goto error;
+ }
+
+ if (!*field_val) {
+ goto error;
+ }
+
+ goto end;
+
+error:
+ lttng_event_field_value_destroy(*field_val);
+ *field_val = NULL;
+ ret = -1;
+
+end:
+ return ret;
+}
+
+static
+struct lttng_event_field_value *event_field_value_from_capture_payload(
+ const struct lttng_condition_event_rule *condition,
+ const char *capture_payload, size_t capture_payload_size)
+{
+ struct lttng_event_field_value *ret = NULL;
+ msgpack_unpacked unpacked;
+ msgpack_unpack_return unpack_return;
+ const msgpack_object *root_obj;
+ const msgpack_object_array *root_array_obj;
+ size_t i;
+ size_t count;
+
+ assert(condition);
+ assert(capture_payload);
+
+ /* Initialize value */
+ msgpack_unpacked_init(&unpacked);
+
+ /* Decode */
+ unpack_return = msgpack_unpack_next(&unpacked, capture_payload,
+ capture_payload_size, NULL);
+ if (unpack_return != MSGPACK_UNPACK_SUCCESS) {
+ ERR("msgpack_unpack_next() failed to decode the "
+ "MessagePack-encoded capture payload "
+ "(size %zu); returned %d.",
+ capture_payload_size, unpack_return);
+ goto error;
+ }
+
+ /* Get root array */
+ root_obj = &unpacked.data;
+
+ if (root_obj->type != MSGPACK_OBJECT_ARRAY) {
+ ERR("Expecting an array as the root object; got type %d.",
+ root_obj->type);
+ goto error;
+ }
+
+ root_array_obj = &root_obj->via.array;
+
+ /* Create an empty root array event field value */
+ ret = lttng_event_field_value_array_create();
+ if (!ret) {
+ goto error;
+ }
+
+ /*
+ * For each capture descriptor in the condition object:
+ *
+ * 1. Get its corresponding captured field value MessagePack
+ * object.
+ *
+ * 2. Create a corresponding event field value.
+ *
+ * 3. Append it to `ret` (the root array event field value).
+ */
+ count = lttng_dynamic_pointer_array_get_count(
+ &condition->capture_descriptors);
+ assert(count > 0);
+
+ for (i = 0; i < count; i++) {
+ const struct lttng_capture_descriptor *capture_descriptor =
+ lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+ &condition->parent, i);
+ const msgpack_object *elem_obj;
+ struct lttng_event_field_value *elem_field_val;
+ int iret;
+
+ assert(capture_descriptor);
+ assert(capture_descriptor->capture_index >= 0);
+
+ if (capture_descriptor->capture_index >= root_array_obj->size) {
+ ERR("Root array object of size %u does not have enough "
+ "elements for the capture index %u "
+ "(for capture descriptor #%zu).",
+ (unsigned int) root_array_obj->size,
+ (unsigned int) capture_descriptor->capture_index,
+ i);
+ goto error;
+ }
+
+ elem_obj = &root_array_obj->ptr[(size_t) capture_descriptor->capture_index];
+ iret = event_field_value_from_obj(elem_obj,
+ &elem_field_val);
+ if (iret) {
+ goto error;
+ }
+
+ if (elem_field_val) {
+ iret = lttng_event_field_value_array_append(ret,
+ elem_field_val);
+ } else {
+ iret = lttng_event_field_value_array_append_unavailable(
+ ret);
+ }
+
+ if (iret) {
+ lttng_event_field_value_destroy(elem_field_val);
+ goto error;
+ }
+ }
+
+ goto end;
+
+error:
+ lttng_event_field_value_destroy(ret);
+ ret = NULL;
+
+end:
+ msgpack_unpacked_destroy(&unpacked);
+ return ret;
+}
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_event_rule_create(
+ const struct lttng_condition_event_rule *condition,
+ const char *trigger_name,
+ const char *capture_payload, size_t capture_payload_size,
+ bool decode_capture_payload)
+{
+ struct lttng_evaluation_event_rule *hit;
+
+ hit = zmalloc(sizeof(struct lttng_evaluation_event_rule));
+ if (!hit) {
+ goto error;
+ }
+
+ /* TODO errir handling */
+ hit->name = strdup(trigger_name);
+ lttng_dynamic_buffer_init(&hit->capture_payload);
+
+ if (capture_payload) {
+ lttng_dynamic_buffer_append(&hit->capture_payload,
+ capture_payload, capture_payload_size);
+
+ if (decode_capture_payload) {
+ hit->captured_values =
+ event_field_value_from_capture_payload(
+ condition,
+ capture_payload,
+ capture_payload_size);
+ if (!hit->captured_values) {
+ ERR("Failed to decode the capture payload (size %zu).",
+ capture_payload_size);
+ goto error;
+ }
+ }
+ }
+
+ 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;
+ goto end;
+
+error:
+ lttng_evaluation_event_rule_destroy(&hit->parent);
+ hit = NULL;
+
+end:
+ return &hit->parent;
+}
+
+enum lttng_evaluation_status lttng_evaluation_get_captured_values(
+ const struct lttng_evaluation *evaluation,
+ const struct lttng_event_field_value **field_val)
+{
+ struct lttng_evaluation_event_rule *hit;
+ enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+ if (!evaluation || !is_event_rule_evaluation(evaluation) ||
+ !field_val) {
+ status = LTTNG_EVALUATION_STATUS_INVALID;
+ goto end;
+ }
+
+ hit = container_of(evaluation, struct lttng_evaluation_event_rule,
+ parent);
+ if (!hit->captured_values) {
+ status = LTTNG_EVALUATION_STATUS_INVALID;
+ goto end;
+ }
+
+ *field_val = hit->captured_values;
+
+end:
+ return status;
+}
+
+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;
+}
+
+LTTNG_HIDDEN
+enum lttng_error_code
+lttng_condition_event_rule_generate_capture_descriptor_bytecode_set(
+ struct lttng_condition *condition,
+ struct lttng_dynamic_pointer_array *bytecode_set)
+{
+ enum lttng_error_code ret;
+ enum lttng_condition_status status;
+ unsigned int capture_count;
+ const struct lttng_condition_event_rule_capture_bytecode_element *set_element;
+ struct lttng_capture_descriptor *local_capture_desc;
+ ssize_t set_count;
+ struct lttng_condition_event_rule_capture_bytecode_element *set_element_to_append =
+ NULL;
+ struct lttng_bytecode *bytecode = NULL;
+
+ if (!condition || !IS_EVENT_RULE_CONDITION(condition) ||
+ !bytecode_set) {
+ ret = LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ status = lttng_condition_event_rule_get_capture_descriptor_count(
+ condition, &capture_count);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ ret = LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ /*
+ * O(n^2), don't care. This code path is not hot.
+ * Before inserting into the set, validate that the expression is not
+ * already present in it.
+ */
+ for (unsigned int i = 0; i < capture_count; i++) {
+ int found_in_set = false;
+
+ set_count = lttng_dynamic_pointer_array_get_count(bytecode_set);
+
+ local_capture_desc =
+ lttng_condition_event_rule_get_internal_capture_descriptor_at_index(
+ condition, i);
+ if (local_capture_desc == NULL) {
+ ret = LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ /*
+ * Iterate over the set to check if already present in the set.
+ */
+ for (ssize_t j = 0; j < set_count; j++) {
+ set_element = (struct lttng_condition_event_rule_capture_bytecode_element
+ *)
+ lttng_dynamic_pointer_array_get_pointer(
+ bytecode_set, j);
+ if (set_element == NULL) {
+ ret = LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ if (!lttng_event_expr_is_equal(
+ local_capture_desc->event_expression,
+ set_element->expression)) {
+ /* Check against next set element */
+ continue;
+ }
+
+ /*
+ * Already present in the set, assign the
+ * capture index of the capture descriptor for
+ * future use.
+ */
+ found_in_set = true;
+ local_capture_desc->capture_index = j;
+ /* Exit inner loop */
+ break;
+ }
+
+ if (found_in_set) {
+ /* Process next local capture descriptor */
+ continue;
+ }
+
+ /*
+ * Not found in the set.
+ * Insert the capture descriptor in the set.
+ */
+ set_element_to_append = malloc(sizeof(*set_element_to_append));
+ if (set_element_to_append == NULL) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ /* Generate the bytecode */
+ status = lttng_event_expr_to_bytecode(
+ local_capture_desc->event_expression,
+ &bytecode);
+ if (status < 0 || bytecode == NULL) {
+ /* TODO: return pertinent capture related error code */
+ ret = LTTNG_ERR_FILTER_INVAL;
+ goto end;
+ }
+
+ set_element_to_append->bytecode = bytecode;
+
+ /*
+ * Ensure the lifetime of the event expression.
+ * Our reference will be put on condition destroy.
+ */
+ lttng_event_expr_get(local_capture_desc->event_expression);
+ set_element_to_append->expression =
+ local_capture_desc->event_expression;
+
+ ret = lttng_dynamic_pointer_array_add_pointer(
+ bytecode_set, set_element_to_append);
+ if (ret < 0) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ /* Ownership tranfered to the bytecode set */
+ set_element_to_append = NULL;
+ bytecode = NULL;
+
+ /* Assign the capture descriptor for future use */
+ local_capture_desc->capture_index = set_count;
+ }
+
+ /* Everything went better than expected */
+ ret = LTTNG_OK;
+
+end:
+ free(set_element_to_append);
+ free(bytecode);
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/session-consumed-size-internal.h>
+#include <lttng/constant.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+#include <time.h>
+
+#define IS_CONSUMED_SIZE_CONDITION(condition) ( \
+ lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
+ )
+
+#define IS_CONSUMED_SIZE_EVALUATION(evaluation) ( \
+ lttng_evaluation_get_type(evaluation) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
+ )
+
+static
+void lttng_condition_session_consumed_size_destroy(struct lttng_condition *condition)
+{
+ struct lttng_condition_session_consumed_size *consumed_size;
+
+ consumed_size = container_of(condition,
+ struct lttng_condition_session_consumed_size, parent);
+
+ free(consumed_size->session_name);
+ free(consumed_size);
+}
+
+static
+bool lttng_condition_session_consumed_size_validate(
+ const struct lttng_condition *condition)
+{
+ bool valid = false;
+ struct lttng_condition_session_consumed_size *consumed;
+
+ if (!condition) {
+ goto end;
+ }
+
+ consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+ parent);
+ if (!consumed->session_name) {
+ ERR("Invalid session consumed size condition: a target session name must be set.");
+ goto end;
+ }
+ if (!consumed->consumed_threshold_bytes.set) {
+ ERR("Invalid session consumed size condition: a threshold must be set.");
+ goto end;
+ }
+
+ valid = true;
+end:
+ return valid;
+}
+
+static
+int lttng_condition_session_consumed_size_serialize(
+ const struct lttng_condition *condition,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
+{
+ int ret;
+ size_t session_name_len;
+ struct lttng_condition_session_consumed_size *consumed;
+ struct lttng_condition_session_consumed_size_comm consumed_comm;
+
+ if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
+ ret = -1;
+ goto end;
+ }
+
+ DBG("Serializing session consumed size condition");
+ consumed = container_of(condition,
+ struct lttng_condition_session_consumed_size,
+ parent);
+
+ session_name_len = strlen(consumed->session_name) + 1;
+ if (session_name_len > LTTNG_NAME_MAX) {
+ ret = -1;
+ goto end;
+ }
+
+ consumed_comm.consumed_threshold_bytes =
+ consumed->consumed_threshold_bytes.value;
+ consumed_comm.session_name_len = (uint32_t) session_name_len;
+
+ ret = lttng_dynamic_buffer_append(buf, &consumed_comm,
+ sizeof(consumed_comm));
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(buf, consumed->session_name,
+ session_name_len);
+ if (ret) {
+ goto end;
+ }
+
+ if (fd_to_send) {
+ /* No fd to send */
+ *fd_to_send = -1;
+ }
+end:
+ return ret;
+}
+
+static
+bool lttng_condition_session_consumed_size_is_equal(const struct lttng_condition *_a,
+ const struct lttng_condition *_b)
+{
+ bool is_equal = false;
+ struct lttng_condition_session_consumed_size *a, *b;
+
+ a = container_of(_a, struct lttng_condition_session_consumed_size, parent);
+ b = container_of(_b, struct lttng_condition_session_consumed_size, parent);
+
+ if (a->consumed_threshold_bytes.set && b->consumed_threshold_bytes.set) {
+ uint64_t a_value, b_value;
+
+ a_value = a->consumed_threshold_bytes.value;
+ b_value = b->consumed_threshold_bytes.value;
+ if (a_value != b_value) {
+ goto end;
+ }
+ }
+
+ assert(a->session_name);
+ assert(b->session_name);
+ if (strcmp(a->session_name, b->session_name)) {
+ goto end;
+ }
+
+ is_equal = true;
+end:
+ return is_equal;
+}
+
+struct lttng_condition *lttng_condition_session_consumed_size_create(void)
+{
+ struct lttng_condition_session_consumed_size *condition;
+
+ condition = zmalloc(sizeof(struct lttng_condition_session_consumed_size));
+ if (!condition) {
+ return NULL;
+ }
+
+ lttng_condition_init(&condition->parent, LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE);
+ condition->parent.validate = lttng_condition_session_consumed_size_validate;
+ condition->parent.serialize = lttng_condition_session_consumed_size_serialize;
+ condition->parent.equal = lttng_condition_session_consumed_size_is_equal;
+ condition->parent.destroy = lttng_condition_session_consumed_size_destroy;
+ return &condition->parent;
+}
+
+static
+ssize_t init_condition_from_buffer(struct lttng_condition *condition,
+ const struct lttng_buffer_view *src_view)
+{
+ ssize_t ret, condition_size;
+ enum lttng_condition_status status;
+ const struct lttng_condition_session_consumed_size_comm *condition_comm;
+ const char *session_name;
+ struct lttng_buffer_view names_view;
+
+ if (src_view->size < sizeof(*condition_comm)) {
+ ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+ ret = -1;
+ goto end;
+ }
+
+ condition_comm = (const struct lttng_condition_session_consumed_size_comm *) src_view->data;
+ names_view = lttng_buffer_view_from_view(src_view,
+ sizeof(*condition_comm), -1);
+
+ if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
+ ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
+ ret = -1;
+ goto end;
+ }
+
+ if (names_view.size < condition_comm->session_name_len) {
+ ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
+ ret = -1;
+ goto end;
+ }
+
+ status = lttng_condition_session_consumed_size_set_threshold(condition,
+ condition_comm->consumed_threshold_bytes);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to initialize session consumed size condition threshold");
+ ret = -1;
+ goto end;
+ }
+
+ session_name = names_view.data;
+ if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
+ ERR("Malformed session name encountered in condition buffer");
+ ret = -1;
+ goto end;
+ }
+
+ status = lttng_condition_session_consumed_size_set_session_name(condition,
+ session_name);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to set session consumed size condition's session name");
+ ret = -1;
+ goto end;
+ }
+
+ if (!lttng_condition_validate(condition)) {
+ ret = -1;
+ goto end;
+ }
+
+ condition_size = sizeof(*condition_comm) +
+ (ssize_t) condition_comm->session_name_len;
+ ret = condition_size;
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_session_consumed_size_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_condition **_condition)
+{
+ ssize_t ret;
+ struct lttng_condition *condition =
+ lttng_condition_session_consumed_size_create();
+
+ if (!_condition || !condition) {
+ ret = -1;
+ goto error;
+ }
+
+ ret = init_condition_from_buffer(condition, view);
+ if (ret < 0) {
+ goto error;
+ }
+
+ *_condition = condition;
+ return ret;
+error:
+ lttng_condition_destroy(condition);
+ return ret;
+}
+
+static
+struct lttng_evaluation *create_evaluation_from_buffer(
+ const struct lttng_buffer_view *view)
+{
+ const struct lttng_evaluation_session_consumed_size_comm *comm =
+ (const struct lttng_evaluation_session_consumed_size_comm *) view->data;
+ struct lttng_evaluation *evaluation = NULL;
+
+ if (view->size < sizeof(*comm)) {
+ goto end;
+ }
+
+ evaluation = lttng_evaluation_session_consumed_size_create(
+ comm->session_consumed);
+end:
+ return evaluation;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_session_consumed_size_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_evaluation **_evaluation)
+{
+ ssize_t ret;
+ struct lttng_evaluation *evaluation = NULL;
+
+ if (!_evaluation) {
+ ret = -1;
+ goto error;
+ }
+
+ evaluation = create_evaluation_from_buffer(view);
+ if (!evaluation) {
+ ret = -1;
+ goto error;
+ }
+
+ *_evaluation = evaluation;
+ ret = sizeof(struct lttng_evaluation_session_consumed_size_comm);
+ return ret;
+error:
+ lttng_evaluation_destroy(evaluation);
+ return ret;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_get_threshold(
+ const struct lttng_condition *condition,
+ uint64_t *consumed_threshold_bytes)
+{
+ struct lttng_condition_session_consumed_size *consumed;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !consumed_threshold_bytes) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+ parent);
+ if (!consumed->consumed_threshold_bytes.set) {
+ status = LTTNG_CONDITION_STATUS_UNSET;
+ goto end;
+ }
+ *consumed_threshold_bytes = consumed->consumed_threshold_bytes.value;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_set_threshold(
+ struct lttng_condition *condition, uint64_t consumed_threshold_bytes)
+{
+ struct lttng_condition_session_consumed_size *consumed;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+ parent);
+ consumed->consumed_threshold_bytes.set = true;
+ consumed->consumed_threshold_bytes.value = consumed_threshold_bytes;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_get_session_name(
+ const struct lttng_condition *condition,
+ const char **session_name)
+{
+ struct lttng_condition_session_consumed_size *consumed;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !session_name) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+ parent);
+ if (!consumed->session_name) {
+ status = LTTNG_CONDITION_STATUS_UNSET;
+ goto end;
+ }
+ *session_name = consumed->session_name;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_set_session_name(
+ struct lttng_condition *condition, const char *session_name)
+{
+ char *session_name_copy;
+ struct lttng_condition_session_consumed_size *consumed;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) ||
+ !session_name || strlen(session_name) == 0) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+ parent);
+ session_name_copy = strdup(session_name);
+ if (!session_name_copy) {
+ status = LTTNG_CONDITION_STATUS_ERROR;
+ goto end;
+ }
+
+ if (consumed->session_name) {
+ free(consumed->session_name);
+ }
+ consumed->session_name = session_name_copy;
+end:
+ return status;
+}
+
+static
+int lttng_evaluation_session_consumed_size_serialize(
+ const struct lttng_evaluation *evaluation,
+ struct lttng_dynamic_buffer *buf)
+{
+ struct lttng_evaluation_session_consumed_size *consumed;
+ struct lttng_evaluation_session_consumed_size_comm comm;
+
+ consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
+ parent);
+ comm.session_consumed = consumed->session_consumed;
+ return lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+}
+
+static
+void lttng_evaluation_session_consumed_size_destroy(
+ struct lttng_evaluation *evaluation)
+{
+ struct lttng_evaluation_session_consumed_size *consumed;
+
+ consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
+ parent);
+ free(consumed);
+}
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_session_consumed_size_create(
+ uint64_t consumed)
+{
+ struct lttng_evaluation_session_consumed_size *consumed_eval;
+
+ consumed_eval = zmalloc(sizeof(struct lttng_evaluation_session_consumed_size));
+ if (!consumed_eval) {
+ goto end;
+ }
+
+ consumed_eval->parent.type = LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE;
+ consumed_eval->session_consumed = consumed;
+ consumed_eval->parent.serialize = lttng_evaluation_session_consumed_size_serialize;
+ consumed_eval->parent.destroy = lttng_evaluation_session_consumed_size_destroy;
+end:
+ return &consumed_eval->parent;
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_session_consumed_size_get_consumed_size(
+ const struct lttng_evaluation *evaluation,
+ uint64_t *session_consumed)
+{
+ struct lttng_evaluation_session_consumed_size *consumed;
+ enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+ if (!evaluation || !IS_CONSUMED_SIZE_EVALUATION(evaluation) ||
+ !session_consumed) {
+ status = LTTNG_EVALUATION_STATUS_INVALID;
+ goto end;
+ }
+
+ consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
+ parent);
+ *session_consumed = consumed->session_consumed;
+end:
+ return status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/session-rotation-internal.h>
+#include <lttng/location-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <assert.h>
+#include <stdbool.h>
+
+static
+bool lttng_condition_session_rotation_validate(
+ const struct lttng_condition *condition);
+static
+int lttng_condition_session_rotation_serialize(
+ const struct lttng_condition *condition,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send);
+static
+bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
+ const struct lttng_condition *_b);
+static
+void lttng_condition_session_rotation_destroy(
+ struct lttng_condition *condition);
+
+static const
+struct lttng_condition rotation_condition_template = {
+ /* .type omitted; shall be set on creation. */
+ .validate = lttng_condition_session_rotation_validate,
+ .serialize = lttng_condition_session_rotation_serialize,
+ .equal = lttng_condition_session_rotation_is_equal,
+ .destroy = lttng_condition_session_rotation_destroy,
+};
+
+static
+int lttng_evaluation_session_rotation_serialize(
+ const struct lttng_evaluation *evaluation,
+ struct lttng_dynamic_buffer *buf);
+static
+void lttng_evaluation_session_rotation_destroy(
+ struct lttng_evaluation *evaluation);
+
+static const
+struct lttng_evaluation rotation_evaluation_template = {
+ /* .type omitted; shall be set on creation. */
+ .serialize = lttng_evaluation_session_rotation_serialize,
+ .destroy = lttng_evaluation_session_rotation_destroy,
+};
+
+static
+bool is_rotation_condition(const struct lttng_condition *condition)
+{
+ enum lttng_condition_type type = lttng_condition_get_type(condition);
+
+ return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
+ type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
+}
+
+static
+bool is_rotation_evaluation(const struct lttng_evaluation *evaluation)
+{
+ enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
+
+ return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
+ type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
+}
+
+static
+bool lttng_condition_session_rotation_validate(
+ const struct lttng_condition *condition)
+{
+ bool valid = false;
+ struct lttng_condition_session_rotation *rotation;
+
+ if (!condition) {
+ goto end;
+ }
+
+ rotation = container_of(condition,
+ struct lttng_condition_session_rotation, parent);
+ if (!rotation->session_name) {
+ ERR("Invalid session rotation condition: a target session name must be set.");
+ goto end;
+ }
+
+ valid = true;
+end:
+ return valid;
+}
+
+static
+int lttng_condition_session_rotation_serialize(
+ const struct lttng_condition *condition,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
+{
+ int ret;
+ size_t session_name_len;
+ struct lttng_condition_session_rotation *rotation;
+ struct lttng_condition_session_rotation_comm rotation_comm;
+
+ if (!condition || !is_rotation_condition(condition)) {
+ ret = -1;
+ goto end;
+ }
+
+ DBG("Serializing session rotation condition");
+ rotation = container_of(condition, struct lttng_condition_session_rotation,
+ parent);
+
+ session_name_len = strlen(rotation->session_name) + 1;
+ if (session_name_len > LTTNG_NAME_MAX) {
+ ret = -1;
+ goto end;
+ }
+
+ rotation_comm.session_name_len = session_name_len;
+ ret = lttng_dynamic_buffer_append(buf, &rotation_comm,
+ sizeof(rotation_comm));
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(buf, rotation->session_name,
+ session_name_len);
+ if (ret) {
+ goto end;
+ }
+
+ if (fd_to_send) {
+ /* No fd to send */
+ *fd_to_send = -1;
+ }
+end:
+ return ret;
+}
+
+static
+bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
+ const struct lttng_condition *_b)
+{
+ bool is_equal = false;
+ struct lttng_condition_session_rotation *a, *b;
+
+ a = container_of(_a, struct lttng_condition_session_rotation, parent);
+ b = container_of(_b, struct lttng_condition_session_rotation, parent);
+
+ /* Both session names must be set or both must be unset. */
+ if ((a->session_name && !b->session_name) ||
+ (!a->session_name && b->session_name)) {
+ WARN("Comparing session rotation conditions with uninitialized session names.");
+ goto end;
+ }
+
+ if (a->session_name && b->session_name &&
+ strcmp(a->session_name, b->session_name)) {
+ goto end;
+ }
+
+ is_equal = true;
+end:
+ return is_equal;
+}
+
+static
+void lttng_condition_session_rotation_destroy(
+ struct lttng_condition *condition)
+{
+ struct lttng_condition_session_rotation *rotation;
+
+ rotation = container_of(condition,
+ struct lttng_condition_session_rotation, parent);
+
+ free(rotation->session_name);
+ free(rotation);
+}
+
+static
+struct lttng_condition *lttng_condition_session_rotation_create(
+ enum lttng_condition_type type)
+{
+ struct lttng_condition_session_rotation *condition;
+
+ condition = zmalloc(sizeof(struct lttng_condition_session_rotation));
+ if (!condition) {
+ return NULL;
+ }
+
+ memcpy(&condition->parent, &rotation_condition_template,
+ sizeof(condition->parent));
+ lttng_condition_init(&condition->parent, type);
+ return &condition->parent;
+}
+
+struct lttng_condition *lttng_condition_session_rotation_ongoing_create(void)
+{
+ return lttng_condition_session_rotation_create(
+ LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
+}
+
+struct lttng_condition *lttng_condition_session_rotation_completed_create(void)
+{
+ return lttng_condition_session_rotation_create(
+ LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
+}
+
+static
+ssize_t init_condition_from_buffer(struct lttng_condition *condition,
+ const struct lttng_buffer_view *src_view)
+{
+ ssize_t ret, condition_size;
+ enum lttng_condition_status status;
+ const struct lttng_condition_session_rotation_comm *condition_comm;
+ const char *session_name;
+ struct lttng_buffer_view name_view;
+
+ if (src_view->size < sizeof(*condition_comm)) {
+ ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+ ret = -1;
+ goto end;
+ }
+
+ condition_comm = (const struct lttng_condition_session_rotation_comm *) src_view->data;
+ name_view = lttng_buffer_view_from_view(src_view,
+ sizeof(*condition_comm), -1);
+
+ if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
+ ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
+ ret = -1;
+ goto end;
+ }
+
+ if (name_view.size < condition_comm->session_name_len) {
+ ERR("Failed to initialize from malformed condition buffer: buffer too short to contain session name");
+ ret = -1;
+ goto end;
+ }
+
+ session_name = name_view.data;
+ if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
+ ERR("Malformed session name encountered in condition buffer");
+ ret = -1;
+ goto end;
+ }
+
+ status = lttng_condition_session_rotation_set_session_name(condition,
+ session_name);
+ if (status != LTTNG_CONDITION_STATUS_OK) {
+ ERR("Failed to set buffer consumed session name");
+ ret = -1;
+ goto end;
+ }
+
+ if (!lttng_condition_validate(condition)) {
+ ret = -1;
+ goto end;
+ }
+
+ condition_size = sizeof(*condition_comm) +
+ (ssize_t) condition_comm->session_name_len;
+ ret = condition_size;
+end:
+ return ret;
+}
+
+static
+ssize_t lttng_condition_session_rotation_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_condition **_condition,
+ enum lttng_condition_type type)
+{
+ ssize_t ret;
+ struct lttng_condition *condition = NULL;
+
+ switch (type) {
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+ condition = lttng_condition_session_rotation_ongoing_create();
+ break;
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+ condition = lttng_condition_session_rotation_completed_create();
+ break;
+ default:
+ ret = -1;
+ goto error;
+ }
+
+ if (!_condition || !condition) {
+ ret = -1;
+ goto error;
+ }
+
+ ret = init_condition_from_buffer(condition, view);
+ if (ret < 0) {
+ goto error;
+ }
+
+ *_condition = condition;
+ return ret;
+error:
+ lttng_condition_destroy(condition);
+ return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_session_rotation_ongoing_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_condition **condition)
+{
+ return lttng_condition_session_rotation_create_from_buffer(view,
+ condition,
+ LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_condition_session_rotation_completed_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_condition **condition)
+{
+ return lttng_condition_session_rotation_create_from_buffer(view,
+ condition,
+ LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
+}
+
+static
+struct lttng_evaluation *lttng_evaluation_session_rotation_create(
+ enum lttng_condition_type type, uint64_t id,
+ struct lttng_trace_archive_location *location)
+{
+ struct lttng_evaluation_session_rotation *evaluation;
+
+ evaluation = zmalloc(sizeof(struct lttng_evaluation_session_rotation));
+ if (!evaluation) {
+ return NULL;
+ }
+
+ memcpy(&evaluation->parent, &rotation_evaluation_template,
+ sizeof(evaluation->parent));
+ lttng_evaluation_init(&evaluation->parent, type);
+ evaluation->id = id;
+ evaluation->location = location;
+ return &evaluation->parent;
+}
+
+static
+ssize_t create_evaluation_from_buffer(
+ enum lttng_condition_type type,
+ const struct lttng_buffer_view *view,
+ struct lttng_evaluation **_evaluation)
+{
+ ssize_t ret, size;
+ struct lttng_evaluation *evaluation = NULL;
+ struct lttng_trace_archive_location *location = NULL;
+ const struct lttng_evaluation_session_rotation_comm *comm =
+ (const struct lttng_evaluation_session_rotation_comm *) view->data;
+ struct lttng_buffer_view location_view;
+
+ if (view->size < sizeof(*comm)) {
+ goto error;
+ }
+
+ size = sizeof(*comm);
+ if (comm->has_location) {
+ location_view = lttng_buffer_view_from_view(view, sizeof(*comm),
+ -1);
+ if (!location_view.data) {
+ goto error;
+ }
+
+ ret = lttng_trace_archive_location_create_from_buffer(
+ &location_view, &location);
+ if (ret < 0) {
+ goto error;
+ }
+ size += ret;
+ }
+
+ evaluation = lttng_evaluation_session_rotation_create(type, comm->id,
+ location);
+ if (!evaluation) {
+ goto error;
+ }
+
+ ret = size;
+ *_evaluation = evaluation;
+ return ret;
+error:
+ lttng_trace_archive_location_destroy(location);
+ evaluation = NULL;
+ return -1;
+}
+
+static
+ssize_t lttng_evaluation_session_rotation_create_from_buffer(
+ enum lttng_condition_type type,
+ const struct lttng_buffer_view *view,
+ struct lttng_evaluation **_evaluation)
+{
+ ssize_t ret;
+ struct lttng_evaluation *evaluation = NULL;
+
+ if (!_evaluation) {
+ ret = -1;
+ goto error;
+ }
+
+ ret = create_evaluation_from_buffer(type, view, &evaluation);
+ if (ret < 0) {
+ goto error;
+ }
+
+ *_evaluation = evaluation;
+ return ret;
+error:
+ lttng_evaluation_destroy(evaluation);
+ return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_session_rotation_ongoing_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_evaluation **evaluation)
+{
+ return lttng_evaluation_session_rotation_create_from_buffer(
+ LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING,
+ view, evaluation);
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_evaluation_session_rotation_completed_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_evaluation **evaluation)
+{
+ return lttng_evaluation_session_rotation_create_from_buffer(
+ LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED,
+ view, evaluation);
+}
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_session_rotation_ongoing_create(
+ uint64_t id)
+{
+ return lttng_evaluation_session_rotation_create(
+ LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING, id,
+ NULL);
+}
+
+LTTNG_HIDDEN
+struct lttng_evaluation *lttng_evaluation_session_rotation_completed_create(
+ uint64_t id, struct lttng_trace_archive_location *location)
+{
+ return lttng_evaluation_session_rotation_create(
+ LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED, id,
+ location);
+}
+
+enum lttng_condition_status
+lttng_condition_session_rotation_get_session_name(
+ const struct lttng_condition *condition,
+ const char **session_name)
+{
+ struct lttng_condition_session_rotation *rotation;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !is_rotation_condition(condition) || !session_name) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ rotation = container_of(condition, struct lttng_condition_session_rotation,
+ parent);
+ if (!rotation->session_name) {
+ status = LTTNG_CONDITION_STATUS_UNSET;
+ goto end;
+ }
+ *session_name = rotation->session_name;
+end:
+ return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_rotation_set_session_name(
+ struct lttng_condition *condition, const char *session_name)
+{
+ char *session_name_copy;
+ struct lttng_condition_session_rotation *rotation;
+ enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+ if (!condition || !is_rotation_condition(condition) ||
+ !session_name || strlen(session_name) == 0) {
+ status = LTTNG_CONDITION_STATUS_INVALID;
+ goto end;
+ }
+
+ rotation = container_of(condition,
+ struct lttng_condition_session_rotation, parent);
+ session_name_copy = strdup(session_name);
+ if (!session_name_copy) {
+ status = LTTNG_CONDITION_STATUS_ERROR;
+ goto end;
+ }
+
+ free(rotation->session_name);
+ rotation->session_name = session_name_copy;
+end:
+ return status;
+}
+
+static
+int lttng_evaluation_session_rotation_serialize(
+ const struct lttng_evaluation *evaluation,
+ struct lttng_dynamic_buffer *buf)
+{
+ int ret;
+ struct lttng_evaluation_session_rotation *rotation;
+ struct lttng_evaluation_session_rotation_comm comm = { 0 };
+
+ rotation = container_of(evaluation,
+ struct lttng_evaluation_session_rotation, parent);
+ comm.id = rotation->id;
+ comm.has_location = !!rotation->location;
+ ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+ if (ret) {
+ goto end;
+ }
+ if (!rotation->location) {
+ goto end;
+ }
+ ret = lttng_trace_archive_location_serialize(rotation->location,
+ buf);
+end:
+ return ret;
+}
+
+static
+void lttng_evaluation_session_rotation_destroy(
+ struct lttng_evaluation *evaluation)
+{
+ struct lttng_evaluation_session_rotation *rotation;
+
+ rotation = container_of(evaluation,
+ struct lttng_evaluation_session_rotation, parent);
+ lttng_trace_archive_location_destroy(rotation->location);
+ free(rotation);
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_session_rotation_get_id(
+ const struct lttng_evaluation *evaluation, uint64_t *id)
+{
+ const struct lttng_evaluation_session_rotation *rotation;
+ enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+ if (!evaluation || !id || !is_rotation_evaluation(evaluation)) {
+ status = LTTNG_EVALUATION_STATUS_INVALID;
+ goto end;
+ }
+
+ rotation = container_of(evaluation,
+ struct lttng_evaluation_session_rotation, parent);
+ *id = rotation->id;
+end:
+ return status;
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_session_rotation_completed_get_location(
+ const struct lttng_evaluation *evaluation,
+ const struct lttng_trace_archive_location **location)
+{
+ const struct lttng_evaluation_session_rotation *rotation;
+ enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+ if (!evaluation || !location ||
+ evaluation->type != LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED) {
+ status = LTTNG_EVALUATION_STATUS_INVALID;
+ goto end;
+ }
+
+ rotation = container_of(evaluation,
+ struct lttng_evaluation_session_rotation, parent);
+ *location = rotation->location;
+end:
+ return status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2020 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include "credentials.h"
+
+bool lttng_credentials_is_equal(const struct lttng_credentials *a,
+ const struct lttng_credentials *b)
+{
+ assert(a);
+ assert(b);
+ if ((a->uid != b->uid) || (a->gid != b->gid)) {
+ return false;
+ }
+ return true;
+};
#define LTTNG_CREDENTIALS_H
#include <sys/types.h>
+#include <assert.h>
+#include <stdbool.h>
struct lttng_credentials {
uid_t uid;
gid_t gid;
};
+bool lttng_credentials_is_equal(const struct lttng_credentials *a,
+ const struct lttng_credentials *b);
+
#endif /* LTTNG_CREDENTIALS_H */
--- /dev/null
+/*
+ * Copyright (C) 2020 - EfficiOS, inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "lttng/domain-internal.h"
+#include "common/macros.h"
+
+LTTNG_HIDDEN
+const char *lttng_domain_type_str(enum lttng_domain_type domain_type)
+{
+ switch (domain_type) {
+ case LTTNG_DOMAIN_NONE:
+ return "none";
+
+ case LTTNG_DOMAIN_KERNEL:
+ return "kernel";
+
+ case LTTNG_DOMAIN_UST:
+ return "ust";
+
+ case LTTNG_DOMAIN_JUL:
+ return "jul";
+
+ case LTTNG_DOMAIN_LOG4J:
+ return "log4j";
+
+ case LTTNG_DOMAIN_PYTHON:
+ return "python";
+
+ default:
+ return "???";
+ }
+}
return *element;
}
+/*
+ * Returns the pointer at index `index`, sets the array slot to NULL. Does not
+ * run the destructor.
+ */
+
+static inline
+void *lttng_dynamic_pointer_array_steal_pointer(
+ struct lttng_dynamic_pointer_array *array, size_t index)
+{
+ void **p_element = lttng_dynamic_array_get_element(&array->array, index);
+ void *element = *p_element;
+
+ *p_element = NULL;
+
+ return element;
+}
+
/*
* Add a pointer to the end of a dynamic pointer array. The array's element
* count is increased by one and its underlying capacity is adjusted
#define _LGPL_SOURCE
#include <assert.h>
+#include <errno.h>
#include <inttypes.h>
+#include <pthread.h>
#include <stdlib.h>
#include <string.h>
-#include <errno.h>
-#include <lttng/lttng-error.h>
#include <common/common.h>
#include <common/compat/getenv.h>
+#include <lttng/lttng-error.h>
#include "error.h"
/* TLS variable that contains the time of one single log entry. */
DEFINE_URCU_TLS(struct log_time, error_log_time);
+DEFINE_URCU_TLS(const char *, logger_thread_name);
LTTNG_HIDDEN
const char *log_add_time(void)
return "";
}
+LTTNG_HIDDEN
+void logger_set_thread_name(const char *name, bool set_pthread_name)
+{
+ int ret;
+
+ assert(name);
+ URCU_TLS(logger_thread_name) = name;
+
+ if (set_pthread_name) {
+ char pthread_name[16];
+
+ /*
+ * Truncations are expected since pthread limits thread names to
+ * a generous 16 characters.
+ */
+ strncpy(pthread_name, name, sizeof(pthread_name));
+ pthread_name[sizeof(pthread_name) - 1] = '\0';
+ ret = pthread_setname_np(pthread_self(), pthread_name);
+ if (ret) {
+ DBG("Failed to set pthread name attribute");
+ }
+ }
+}
+
/*
* Human readable error message.
*/
#include <stdbool.h>
#include <urcu/tls-compat.h>
#include <common/compat/time.h>
+#include <common/string-utils/format.h>
+#include <common/macros.h>
#ifndef _GNU_SOURCE
#error "lttng-tools error.h needs _GNU_SOURCE"
char str[19];
};
extern DECLARE_URCU_TLS(struct log_time, error_log_time);
+extern DECLARE_URCU_TLS(const char *, logger_thread_name);
extern int lttng_opt_quiet;
extern int lttng_opt_verbose;
} while (0)
/* Three level of debug. Use -v, -vv or -vvv for the levels */
-#define _ERRMSG(msg, type, fmt, args...) __lttng_print(type, msg \
- " - %s [%ld/%ld]: " fmt " (in %s() at " __FILE__ ":" XSTR(__LINE__) ")\n", \
- log_add_time(), (long) getpid(), (long) lttng_gettid(), ## args, __func__)
+#define _ERRMSG(msg, type, fmt, args...) \
+ do { \
+ if (caa_unlikely(__lttng_print_check_opt(type))) { \
+ char generic_name[MAX_INT_DEC_LEN(long) + \
+ MAX_INT_DEC_LEN(long)]; \
+ \
+ snprintf(generic_name, sizeof(generic_name), \
+ "%ld/%ld", (long) getpid(), \
+ (long) lttng_gettid()); \
+ \
+ __lttng_print(type, \
+ msg " - %s [%s]: " fmt \
+ " (in %s() at " __FILE__ \
+ ":" XSTR(__LINE__) ")\n", \
+ log_add_time(), \
+ URCU_TLS(logger_thread_name) ?: \
+ generic_name, \
+ ##args, __func__); \
+ } \
+ } while (0)
-#define _ERRMSG_NO_LOC(msg, type, fmt, args...) __lttng_print(type, msg \
- " - %s [%ld/%ld]: " fmt "\n", \
- log_add_time(), (long) getpid(), (long) lttng_gettid(), ## args)
+#define _ERRMSG_NO_LOC(msg, type, fmt, args...) \
+ do { \
+ if (caa_unlikely(__lttng_print_check_opt(type))) { \
+ char generic_name[MAX_INT_DEC_LEN(long) + \
+ MAX_INT_DEC_LEN(long)]; \
+ \
+ snprintf(generic_name, sizeof(generic_name), \
+ "%ld/%ld", (long) getpid(), \
+ (long) lttng_gettid()); \
+ \
+ __lttng_print(type, msg " - %s [%s]: " fmt "\n", \
+ log_add_time(), \
+ URCU_TLS(logger_thread_name) ?: \
+ generic_name, \
+ ##args); \
+ } \
+ } while (0)
#define MSG(fmt, args...) \
__lttng_print(PRINT_MSG, fmt "\n", ## args)
#define BUG(fmt, args...) _ERRMSG("BUG", PRINT_BUG, fmt, ## args)
-#define DBG(fmt, args...) _ERRMSG("DEBUG1", PRINT_DBG, fmt, ## args)
-#define DBG_NO_LOC(fmt, args...) _ERRMSG_NO_LOC("DEBUG1", PRINT_DBG, fmt, ## args)
-#define DBG2(fmt, args...) _ERRMSG("DEBUG2", PRINT_DBG2, fmt, ## args)
-#define DBG3(fmt, args...) _ERRMSG("DEBUG3", PRINT_DBG3, fmt, ## args)
+#define DBG(fmt, args...) _ERRMSG("DBG1", PRINT_DBG, fmt, ## args)
+#define DBG_NO_LOC(fmt, args...) _ERRMSG_NO_LOC("DBG1", PRINT_DBG, fmt, ## args)
+#define DBG2(fmt, args...) _ERRMSG("DBG2", PRINT_DBG2, fmt, ## args)
+#define DBG3(fmt, args...) _ERRMSG("DBG3", PRINT_DBG3, fmt, ## args)
#define LOG(type, fmt, args...) \
do { \
switch (type) { \
*/
const char *log_add_time(void);
+/* Name must be a statically-allocated string. */
+LTTNG_HIDDEN
+void logger_set_thread_name(const char *name, bool set_pthread_name);
+
#endif /* _ERROR_H */
*
*/
+#include <lttng/condition/condition-internal.h>
#include <lttng/condition/evaluation-internal.h>
#include <lttng/condition/buffer-usage-internal.h>
#include <lttng/condition/session-consumed-size-internal.h>
#include <lttng/condition/session-rotation-internal.h>
+#include <lttng/condition/event-rule-internal.h>
#include <common/macros.h>
#include <common/error.h>
#include <stdbool.h>
LTTNG_HIDDEN
ssize_t lttng_evaluation_create_from_buffer(
+ const struct lttng_condition *condition,
const struct lttng_buffer_view *src_view,
struct lttng_evaluation **evaluation)
{
}
evaluation_size += ret;
break;
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ assert(condition);
+ assert(condition->type == LTTNG_CONDITION_TYPE_EVENT_RULE_HIT);
+ ret = lttng_evaluation_event_rule_create_from_buffer(
+ container_of(condition,
+ struct lttng_condition_event_rule,
+ parent),
+ &evaluation_view, evaluation);
+ if (ret < 0) {
+ goto end;
+ }
+ evaluation_size += ret;
+ break;
default:
ERR("Attempted to create evaluation of unknown type (%i)",
(int) evaluation_comm->type);
--- /dev/null
+/*
+ * Copyright 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "event-expr-to-bytecode.h"
+
+#include <stdio.h>
+#include <lttng/event-expr.h>
+#include <common/bytecode/bytecode.h>
+
+
+static
+int event_expr_to_bytecode_recursive(const struct lttng_event_expr *expr,
+ struct lttng_bytecode_alloc **bytecode,
+ struct lttng_bytecode_alloc **bytecode_reloc)
+{
+ enum lttng_event_expr_status event_expr_status;
+ int status;
+
+ switch (lttng_event_expr_get_type(expr)) {
+ case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+ {
+ const char *name;
+
+ status = bytecode_push_get_payload_root(bytecode);
+ if (status) {
+ goto end;
+ }
+
+ name = lttng_event_expr_event_payload_field_get_name(expr);
+ if (!name) {
+ status = -1;
+ goto end;
+ }
+
+ status = bytecode_push_get_symbol(bytecode, bytecode_reloc, name);
+ if (status) {
+ goto end;
+ }
+
+ break;
+ }
+
+ case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+ {
+ const char *name;
+
+ status = bytecode_push_get_context_root(bytecode);
+ if (status) {
+ goto end;
+ }
+
+ name = lttng_event_expr_channel_context_field_get_name(expr);
+ if (!name) {
+ status = -1;
+ goto end;
+ }
+
+ status = bytecode_push_get_symbol(bytecode, bytecode_reloc, name);
+ if (status) {
+ goto end;
+ }
+
+ break;
+ }
+
+ case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+ {
+ const char *provider_name, *type_name;
+ char *name = NULL;
+ int ret;
+
+ status = bytecode_push_get_app_context_root(bytecode);
+ if (status) {
+ goto end;
+ }
+
+ provider_name = lttng_event_expr_app_specific_context_field_get_provider_name(expr);
+ if (!provider_name) {
+ status = -1;
+ goto end;
+ }
+
+ type_name = lttng_event_expr_app_specific_context_field_get_type_name(expr);
+ if (!type_name) {
+ status = -1;
+ goto end;
+ }
+
+ /* Reconstitute the app context field name from its two parts. */
+ ret = asprintf(&name, "%s:%s", provider_name, type_name);
+ if (ret < 0) {
+ status = -1;
+ goto end;
+ }
+
+ status = bytecode_push_get_symbol(bytecode, bytecode_reloc, name);
+ free(name);
+ if (status) {
+ goto end;
+ }
+
+ break;
+ }
+
+ case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+ {
+ const struct lttng_event_expr *parent;
+ unsigned int index;
+
+ parent = lttng_event_expr_array_field_element_get_parent_expr(expr);
+ if (!parent) {
+ status = -1;
+ goto end;
+ }
+
+ status = event_expr_to_bytecode_recursive(parent, bytecode, bytecode_reloc);
+ if (status) {
+ goto end;
+ }
+
+ event_expr_status = lttng_event_expr_array_field_element_get_index(
+ expr, &index);
+ if (event_expr_status != LTTNG_EVENT_EXPR_STATUS_OK) {
+ status = -1;
+ goto end;
+ }
+
+ status = bytecode_push_get_index_u64(bytecode, index);
+ if (status) {
+ goto end;
+ }
+
+ break;
+ }
+
+ default:
+ abort();
+ }
+
+ status = 0;
+end:
+ return status;
+}
+
+LTTNG_HIDDEN
+int lttng_event_expr_to_bytecode(const struct lttng_event_expr *expr,
+ struct lttng_bytecode **bytecode_out)
+{
+ struct lttng_bytecode_alloc *bytecode = NULL;
+ struct lttng_bytecode_alloc *bytecode_reloc = NULL;
+ struct return_op ret_insn;
+ int status;
+
+ status = bytecode_init(&bytecode);
+ if (status) {
+ goto end;
+ }
+
+ status = bytecode_init(&bytecode_reloc);
+ if (status) {
+ goto end;
+ }
+
+ status = event_expr_to_bytecode_recursive (expr, &bytecode, &bytecode_reloc);
+ if (status) {
+ goto end;
+ }
+
+ ret_insn.op = BYTECODE_OP_RETURN;
+ bytecode_push(&bytecode, &ret_insn, 1, sizeof(ret_insn));
+
+ /* Append symbol table to bytecode. */
+ bytecode->b.reloc_table_offset = bytecode_get_len(&bytecode->b);
+ status = bytecode_push(&bytecode, bytecode_reloc->b.data,
+ 1, bytecode_get_len(&bytecode_reloc->b));
+ if (status) {
+ goto end;
+ }
+
+ /* Copy the `lttng_bytecode` out of the `lttng_bytecode_alloc`. */
+ *bytecode_out = bytecode_copy(&bytecode->b);
+ if (!*bytecode_out) {
+ status = -1;
+ goto end;
+ }
+
+end:
+ if (bytecode) {
+ free(bytecode);
+ }
+
+ if (bytecode_reloc) {
+ free(bytecode_reloc);
+ }
+
+ return status;
+}
--- /dev/null
+#ifndef SRC_COMMON_EVENT_EXPR_TO_BYTECODE_H
+#define SRC_COMMON_EVENT_EXPR_TO_BYTECODE_H
+
+/*
+ * Copyright 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/macros.h>
+
+struct lttng_bytecode;
+struct lttng_event_expr;
+
+LTTNG_HIDDEN
+int lttng_event_expr_to_bytecode (const struct lttng_event_expr *expr,
+ struct lttng_bytecode **bytecode_out);
+
+#endif /* SRC_COMMON_EVENT_EXPR_TO_BYTECODE_H */
--- /dev/null
+/*
+ * event-expr.c
+ *
+ * Linux Trace Toolkit Control Library
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <assert.h>
+#include <stddef.h>
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/event-expr-internal.h>
+
+enum lttng_event_expr_type lttng_event_expr_get_type(
+ const struct lttng_event_expr *expr)
+{
+ enum lttng_event_expr_type type;
+
+ if (!expr) {
+ type = LTTNG_EVENT_EXPR_TYPE_INVALID;
+ goto end;
+ }
+
+ type = expr->type;
+
+end:
+ return type;
+}
+
+static
+struct lttng_event_expr *create_empty_expr(enum lttng_event_expr_type type,
+ size_t size)
+{
+ struct lttng_event_expr *expr;
+
+ expr = zmalloc(size);
+ if (!expr) {
+ goto end;
+ }
+
+ urcu_ref_init(&expr->ref);
+ expr->type = type;
+
+end:
+ return expr;
+}
+
+static
+struct lttng_event_expr_field *create_field_event_expr(
+ enum lttng_event_expr_type type,
+ const char *name)
+{
+ struct lttng_event_expr_field *expr =
+ container_of(
+ create_empty_expr(type, sizeof(*expr)),
+ struct lttng_event_expr_field, parent);
+
+ if (!expr) {
+ goto error;
+ }
+
+ assert(name);
+ expr->name = strdup(name);
+ if (!expr->name) {
+ goto error;
+ }
+
+ goto end;
+
+error:
+ lttng_event_expr_destroy(&expr->parent);
+
+end:
+ return expr;
+}
+
+struct lttng_event_expr *lttng_event_expr_event_payload_field_create(
+ const char *field_name)
+{
+ struct lttng_event_expr *expr = NULL;
+
+ if (!field_name) {
+ goto end;
+ }
+
+ expr = &create_field_event_expr(
+ LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD,
+ field_name)->parent;
+
+end:
+ return expr;
+}
+
+struct lttng_event_expr *lttng_event_expr_channel_context_field_create(
+ const char *field_name)
+{
+ struct lttng_event_expr *expr = NULL;
+
+ if (!field_name) {
+ goto end;
+ }
+
+ expr = &create_field_event_expr(
+ LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD,
+ field_name)->parent;
+
+end:
+ return expr;
+}
+
+struct lttng_event_expr *lttng_event_expr_app_specific_context_field_create(
+ const char *provider_name, const char *type_name)
+{
+ struct lttng_event_expr_app_specific_context_field *expr = NULL;
+
+ if (!type_name || !provider_name) {
+ goto error;
+ }
+
+ expr = container_of(create_empty_expr(
+ LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD,
+ sizeof(*expr)),
+ struct lttng_event_expr_app_specific_context_field,
+ parent);
+ if (!expr) {
+ goto error;
+ }
+
+ expr->provider_name = strdup(provider_name);
+ if (!expr->provider_name) {
+ goto error;
+ }
+
+ expr->type_name = strdup(type_name);
+ if (!expr->type_name) {
+ goto error;
+ }
+
+ goto end;
+
+error:
+ lttng_event_expr_destroy(&expr->parent);
+
+end:
+ return &expr->parent;
+}
+
+struct lttng_event_expr *lttng_event_expr_array_field_element_create(
+ struct lttng_event_expr *array_field_expr,
+ unsigned int index)
+{
+ struct lttng_event_expr_array_field_element *expr = NULL;
+
+ /* The parent array field expression must be an l-value */
+ if (!array_field_expr ||
+ !lttng_event_expr_is_lvalue(array_field_expr)) {
+ goto error;
+ }
+
+ expr = container_of(create_empty_expr(
+ LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT,
+ sizeof(*expr)),
+ struct lttng_event_expr_array_field_element,
+ parent);
+ if (!expr) {
+ goto error;
+ }
+
+ expr->array_field_expr = array_field_expr;
+ expr->index = index;
+ goto end;
+
+error:
+ lttng_event_expr_destroy(&expr->parent);
+
+end:
+ return &expr->parent;
+}
+
+const char *lttng_event_expr_event_payload_field_get_name(
+ const struct lttng_event_expr *expr)
+{
+ const char *ret = NULL;
+
+ if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD) {
+ goto end;
+ }
+
+ ret = container_of(expr,
+ const struct lttng_event_expr_field, parent)->name;
+
+end:
+ return ret;
+}
+
+const char *lttng_event_expr_channel_context_field_get_name(
+ const struct lttng_event_expr *expr)
+{
+ const char *ret = NULL;
+
+ if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD) {
+ goto end;
+ }
+
+ ret = container_of(expr,
+ const struct lttng_event_expr_field, parent)->name;
+
+end:
+ return ret;
+}
+
+const char *lttng_event_expr_app_specific_context_field_get_provider_name(
+ const struct lttng_event_expr *expr)
+{
+ const char *ret = NULL;
+
+ if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) {
+ goto end;
+ }
+
+ ret = container_of(expr,
+ const struct lttng_event_expr_app_specific_context_field,
+ parent)->provider_name;
+
+end:
+ return ret;
+}
+
+const char *lttng_event_expr_app_specific_context_field_get_type_name(
+ const struct lttng_event_expr *expr)
+{
+ const char *ret = NULL;
+
+ if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) {
+ goto end;
+ }
+
+ ret = container_of(expr,
+ const struct lttng_event_expr_app_specific_context_field,
+ parent)->type_name;
+
+end:
+ return ret;
+}
+
+const struct lttng_event_expr *
+lttng_event_expr_array_field_element_get_parent_expr(
+ const struct lttng_event_expr *expr)
+{
+ const struct lttng_event_expr *ret = NULL;
+
+ if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT) {
+ goto end;
+ }
+
+ ret = container_of(expr,
+ const struct lttng_event_expr_array_field_element,
+ parent)->array_field_expr;
+
+end:
+ return ret;
+}
+
+enum lttng_event_expr_status lttng_event_expr_array_field_element_get_index(
+ const struct lttng_event_expr *expr, unsigned int *index)
+{
+ enum lttng_event_expr_status ret = LTTNG_EVENT_EXPR_STATUS_OK;
+
+ if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT ||
+ !index) {
+ ret = LTTNG_EVENT_EXPR_STATUS_INVALID;
+ goto end;
+ }
+
+ *index = container_of(expr,
+ const struct lttng_event_expr_array_field_element,
+ parent)->index;
+
+end:
+ return ret;
+}
+
+bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a,
+ const struct lttng_event_expr *expr_b)
+{
+ bool is_equal = true;
+
+ if (!expr_a && !expr_b) {
+ /* Both `NULL`: equal */
+ goto end;
+ }
+
+ if (!expr_a || !expr_b) {
+ /* Only one `NULL`: not equal */
+ goto not_equal;
+ }
+
+ if (expr_a->type != expr_b->type) {
+ /* Different types: not equal */
+ goto not_equal;
+ }
+
+ switch (expr_a->type) {
+ case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+ case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+ {
+ const struct lttng_event_expr_field *field_expr_a =
+ container_of(expr_a,
+ const struct lttng_event_expr_field,
+ parent);
+ const struct lttng_event_expr_field *field_expr_b =
+ container_of(expr_b,
+ const struct lttng_event_expr_field,
+ parent);
+
+ if (strcmp(field_expr_a->name, field_expr_b->name) != 0) {
+ goto not_equal;
+ }
+
+ break;
+ }
+ case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+ {
+ const struct lttng_event_expr_app_specific_context_field *field_expr_a =
+ container_of(expr_a,
+ const struct lttng_event_expr_app_specific_context_field,
+ parent);
+ const struct lttng_event_expr_app_specific_context_field *field_expr_b =
+ container_of(expr_b,
+ const struct lttng_event_expr_app_specific_context_field,
+ parent);
+
+ if (strcmp(field_expr_a->provider_name,
+ field_expr_b->provider_name) != 0) {
+ goto not_equal;
+ }
+
+ if (strcmp(field_expr_a->type_name,
+ field_expr_b->type_name) != 0) {
+ goto not_equal;
+ }
+
+ break;
+ }
+ case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+ {
+ const struct lttng_event_expr_array_field_element *elem_expr_a =
+ container_of(expr_a,
+ const struct lttng_event_expr_array_field_element,
+ parent);
+ const struct lttng_event_expr_array_field_element *elem_expr_b =
+ container_of(expr_b,
+ const struct lttng_event_expr_array_field_element,
+ parent);
+
+ if (!lttng_event_expr_is_equal(elem_expr_a->array_field_expr,
+ elem_expr_b->array_field_expr)) {
+ goto not_equal;
+ }
+
+ if (elem_expr_a->index != elem_expr_b->index) {
+ goto not_equal;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ goto end;
+
+not_equal:
+ is_equal = false;
+
+end:
+ return is_equal;
+}
+
+static
+void event_expr_destroy_ref(struct urcu_ref *ref)
+{
+ struct lttng_event_expr *expr =
+ container_of(ref, struct lttng_event_expr, ref);
+
+ switch (expr->type) {
+ case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+ case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+ {
+ struct lttng_event_expr_field *field_expr =
+ container_of(expr,
+ struct lttng_event_expr_field, parent);
+
+ free(field_expr->name);
+ break;
+ }
+ case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+ {
+ struct lttng_event_expr_app_specific_context_field *field_expr =
+ container_of(expr,
+ struct lttng_event_expr_app_specific_context_field,
+ parent);
+
+ free(field_expr->provider_name);
+ free(field_expr->type_name);
+ break;
+ }
+ case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+ {
+ struct lttng_event_expr_array_field_element *elem_expr =
+ container_of(expr,
+ struct lttng_event_expr_array_field_element,
+ parent);
+
+ lttng_event_expr_destroy(elem_expr->array_field_expr);
+ break;
+ }
+ default:
+ break;
+ }
+
+ free(expr);
+}
+
+LTTNG_HIDDEN
+void lttng_event_expr_get(struct lttng_event_expr *expr)
+{
+ urcu_ref_get(&expr->ref);
+}
+
+LTTNG_HIDDEN
+void lttng_event_expr_put(struct lttng_event_expr *expr) {
+ if(!expr) {
+ return;
+ }
+ urcu_ref_put(&expr->ref, event_expr_destroy_ref);
+}
+
+void lttng_event_expr_destroy(struct lttng_event_expr *expr)
+{
+ lttng_event_expr_put(expr);
+ return;
+}
--- /dev/null
+/*
+ * event-field-value.c
+ *
+ * Linux Trace Toolkit Control Library
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <assert.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/event-field-value-internal.h>
+
+static
+struct lttng_event_field_value *create_empty_field_val(
+ enum lttng_event_field_value_type type, size_t size)
+{
+ struct lttng_event_field_value *field_val;
+
+ field_val = zmalloc(size);
+ if (!field_val) {
+ goto end;
+ }
+
+ field_val->type = type;
+
+end:
+ return field_val;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_uint_create(
+ uint64_t val)
+{
+ struct lttng_event_field_value_uint *field_val;
+
+ field_val = container_of(create_empty_field_val(
+ LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT,
+ sizeof(*field_val)),
+ struct lttng_event_field_value_uint, parent);
+ if (!field_val) {
+ goto error;
+ }
+
+ field_val->val = val;
+ goto end;
+
+error:
+ lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+ return &field_val->parent;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_int_create(
+ int64_t val)
+{
+ struct lttng_event_field_value_int *field_val;
+
+ field_val = container_of(create_empty_field_val(
+ LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT,
+ sizeof(*field_val)),
+ struct lttng_event_field_value_int, parent);
+ if (!field_val) {
+ goto error;
+ }
+
+ field_val->val = val;
+ goto end;
+
+error:
+ lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+ return &field_val->parent;
+}
+
+static
+struct lttng_event_field_value_enum *create_enum_field_val(
+ enum lttng_event_field_value_type type, size_t size)
+{
+ struct lttng_event_field_value_enum *field_val;
+
+ field_val = container_of(create_empty_field_val(type, size),
+ struct lttng_event_field_value_enum, parent);
+ if (!field_val) {
+ goto error;
+ }
+
+ lttng_dynamic_pointer_array_init(&field_val->labels, free);
+ goto end;
+
+error:
+ lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+ return field_val;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_enum_uint_create(
+ uint64_t val)
+{
+ struct lttng_event_field_value_enum_uint *field_val;
+
+ field_val = container_of(create_enum_field_val(
+ LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM,
+ sizeof(*field_val)),
+ struct lttng_event_field_value_enum_uint, parent);
+ if (!field_val) {
+ goto error;
+ }
+
+ field_val->val = val;
+ goto end;
+
+error:
+ lttng_event_field_value_destroy(&field_val->parent.parent);
+
+end:
+ return &field_val->parent.parent;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_enum_int_create(
+ int64_t val)
+{
+ struct lttng_event_field_value_enum_int *field_val;
+
+ field_val = container_of(create_enum_field_val(
+ LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM,
+ sizeof(*field_val)),
+ struct lttng_event_field_value_enum_int, parent);
+ if (!field_val) {
+ goto error;
+ }
+
+ field_val->val = val;
+ goto end;
+
+error:
+ lttng_event_field_value_destroy(&field_val->parent.parent);
+
+end:
+ return &field_val->parent.parent;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_real_create(double val)
+{
+ struct lttng_event_field_value_real *field_val = container_of(
+ create_empty_field_val(
+ LTTNG_EVENT_FIELD_VALUE_TYPE_REAL,
+ sizeof(*field_val)),
+ struct lttng_event_field_value_real, parent);
+
+ if (!field_val) {
+ goto error;
+ }
+
+ field_val->val = val;
+ goto end;
+
+error:
+ lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+ return &field_val->parent;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_string_create_with_size(
+ const char *val, size_t size)
+{
+ struct lttng_event_field_value_string *field_val = container_of(
+ create_empty_field_val(
+ LTTNG_EVENT_FIELD_VALUE_TYPE_STRING,
+ sizeof(*field_val)),
+ struct lttng_event_field_value_string, parent);
+
+ if (!field_val) {
+ goto error;
+ }
+
+ assert(val);
+ field_val->val = strndup(val, size);
+ if (!field_val->val) {
+ goto error;
+ }
+
+ goto end;
+
+error:
+ lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+ return &field_val->parent;
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_string_create(
+ const char *val)
+{
+ assert(val);
+ return lttng_event_field_value_string_create_with_size(val,
+ strlen(val));
+}
+
+static
+void destroy_field_val(void *field_val)
+{
+ lttng_event_field_value_destroy(field_val);
+}
+
+LTTNG_HIDDEN
+struct lttng_event_field_value *lttng_event_field_value_array_create(void)
+{
+ struct lttng_event_field_value_array *field_val = container_of(
+ create_empty_field_val(
+ LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY,
+ sizeof(*field_val)),
+ struct lttng_event_field_value_array, parent);
+
+ if (!field_val) {
+ goto error;
+ }
+
+ lttng_dynamic_pointer_array_init(&field_val->elems, destroy_field_val);
+ goto end;
+
+error:
+ lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+ return &field_val->parent;
+}
+
+LTTNG_HIDDEN
+void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val)
+{
+ if (!field_val) {
+ goto end;
+ }
+
+ switch (field_val->type) {
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
+ {
+ struct lttng_event_field_value_enum *enum_field_val =
+ container_of(field_val,
+ struct lttng_event_field_value_enum, parent);
+
+ lttng_dynamic_pointer_array_reset(&enum_field_val->labels);
+ break;
+ }
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING:
+ {
+ struct lttng_event_field_value_string *str_field_val =
+ container_of(field_val,
+ struct lttng_event_field_value_string, parent);
+
+ free(str_field_val->val);
+ break;
+ }
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY:
+ {
+ struct lttng_event_field_value_array *array_field_expr =
+ container_of(field_val,
+ struct lttng_event_field_value_array,
+ parent);
+
+ lttng_dynamic_pointer_array_reset(&array_field_expr->elems);
+ break;
+ }
+ default:
+ break;
+ }
+
+ free(field_val);
+
+end:
+ return;
+}
+
+LTTNG_HIDDEN
+int lttng_event_field_value_enum_append_label_with_size(
+ struct lttng_event_field_value *field_val,
+ const char *label, size_t size)
+{
+ int ret;
+ char *mein_label;
+
+ assert(field_val);
+ assert(label);
+ mein_label = strndup(label, size);
+ if (!mein_label) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = lttng_dynamic_pointer_array_add_pointer(
+ &container_of(field_val,
+ struct lttng_event_field_value_enum, parent)->labels,
+ mein_label);
+ if (ret == 0) {
+ mein_label = NULL;
+ }
+
+end:
+ free(mein_label);
+ return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_event_field_value_enum_append_label(
+ struct lttng_event_field_value *field_val,
+ const char *label)
+{
+ assert(label);
+ return lttng_event_field_value_enum_append_label_with_size(field_val,
+ label, strlen(label));
+}
+
+LTTNG_HIDDEN
+int lttng_event_field_value_array_append(
+ struct lttng_event_field_value *array_field_val,
+ struct lttng_event_field_value *field_val)
+{
+ assert(array_field_val);
+ assert(field_val);
+ return lttng_dynamic_pointer_array_add_pointer(
+ &container_of(array_field_val,
+ struct lttng_event_field_value_array, parent)->elems,
+ field_val);
+}
+
+LTTNG_HIDDEN
+int lttng_event_field_value_array_append_unavailable(
+ struct lttng_event_field_value *array_field_val)
+{
+ assert(array_field_val);
+ return lttng_dynamic_pointer_array_add_pointer(
+ &container_of(array_field_val,
+ struct lttng_event_field_value_array, parent)->elems,
+ NULL);
+}
+
+enum lttng_event_field_value_type lttng_event_field_value_get_type(
+ const struct lttng_event_field_value *field_val)
+{
+ enum lttng_event_field_value_type type;
+
+ if (!field_val) {
+ type = LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID;
+ goto end;
+ }
+
+ type = field_val->type;
+
+end:
+ return type;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_unsigned_int_get_value(
+ const struct lttng_event_field_value *field_val, uint64_t *val)
+{
+ enum lttng_event_field_value_status status;
+
+ if (!field_val || !val) {
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+ goto end;
+ }
+
+ switch (field_val->type) {
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT:
+ *val = container_of(field_val,
+ const struct lttng_event_field_value_uint,
+ parent)->val;
+ break;
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
+ *val = container_of(
+ container_of(field_val,
+ const struct lttng_event_field_value_enum,
+ parent),
+ const struct lttng_event_field_value_enum_uint,
+ parent)->val;
+ break;
+ default:
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+ goto end;
+ }
+
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+ return status;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_signed_int_get_value(
+ const struct lttng_event_field_value *field_val, int64_t *val)
+{
+ enum lttng_event_field_value_status status;
+
+ if (!field_val || !val) {
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+ goto end;
+ }
+
+ switch (field_val->type) {
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT:
+ *val = container_of(field_val,
+ const struct lttng_event_field_value_int,
+ parent)->val;
+ break;
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
+ *val = container_of(
+ container_of(field_val,
+ const struct lttng_event_field_value_enum,
+ parent),
+ const struct lttng_event_field_value_enum_int,
+ parent)->val;
+ break;
+ default:
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+ goto end;
+ }
+
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+ return status;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_real_get_value(
+ const struct lttng_event_field_value *field_val, double *val)
+{
+ enum lttng_event_field_value_status status;
+
+ if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_REAL ||
+ !val) {
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+ goto end;
+ }
+
+ *val = container_of(field_val,
+ const struct lttng_event_field_value_real, parent)->val;
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+ return status;
+}
+
+static
+bool ist_enum_field_val(const struct lttng_event_field_value *field_val)
+{
+ return field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM ||
+ field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_enum_get_label_count(
+ const struct lttng_event_field_value *field_val,
+ unsigned int *count)
+{
+ enum lttng_event_field_value_status status;
+
+ if (!field_val || !ist_enum_field_val(field_val) || !count) {
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+ goto end;
+ }
+
+ *count = (unsigned int) lttng_dynamic_pointer_array_get_count(
+ &container_of(field_val,
+ const struct lttng_event_field_value_enum,
+ parent)->labels);
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+ return status;
+}
+
+const char *lttng_event_field_value_enum_get_label_at_index(
+ const struct lttng_event_field_value *field_val,
+ unsigned int index)
+{
+ const char *ret;
+ const struct lttng_event_field_value_enum *enum_field_val;
+
+ if (!field_val || !ist_enum_field_val(field_val)) {
+ ret = NULL;
+ goto end;
+ }
+
+ enum_field_val = container_of(field_val,
+ const struct lttng_event_field_value_enum, parent);
+
+ if (index >= lttng_dynamic_pointer_array_get_count(&enum_field_val->labels)) {
+ ret = NULL;
+ goto end;
+ }
+
+ ret = lttng_dynamic_pointer_array_get_pointer(&enum_field_val->labels,
+ index);
+
+end:
+ return ret;
+}
+
+const char *lttng_event_field_value_string_get_value(
+ const struct lttng_event_field_value *field_val)
+{
+ const char *ret;
+
+ if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_STRING) {
+ ret = NULL;
+ goto end;
+ }
+
+ ret = container_of(field_val,
+ const struct lttng_event_field_value_string, parent)->val;
+
+end:
+ return ret;
+}
+
+enum lttng_event_field_value_status lttng_event_field_value_array_get_length(
+ const struct lttng_event_field_value *field_val,
+ unsigned int *length)
+{
+ enum lttng_event_field_value_status status;
+
+ if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY ||
+ !length) {
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+ goto end;
+ }
+
+ *length = (unsigned int) lttng_dynamic_pointer_array_get_count(
+ &container_of(field_val,
+ const struct lttng_event_field_value_array,
+ parent)->elems);
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+ return status;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_array_get_element_at_index(
+ const struct lttng_event_field_value *field_val,
+ unsigned int index,
+ const struct lttng_event_field_value **elem_field_val)
+{
+ enum lttng_event_field_value_status status;
+ const struct lttng_event_field_value_array *array_field_val;
+
+ if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY ||
+ !elem_field_val) {
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+ goto end;
+ }
+
+ array_field_val = container_of(field_val,
+ const struct lttng_event_field_value_array, parent);
+
+ if (index >= lttng_dynamic_pointer_array_get_count(&array_field_val->elems)) {
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+ goto end;
+ }
+
+ *elem_field_val = lttng_dynamic_pointer_array_get_pointer(
+ &array_field_val->elems, index);
+ if (*elem_field_val) {
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+ } else {
+ status = LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE;
+ }
+
+end:
+ return status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte
+ * <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/buffer-view.h>
+#include <common/dynamic-buffer.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kprobe-internal.h>
+#include <lttng/event-rule/kretprobe-internal.h>
+#include <lttng/event-rule/syscall-internal.h>
+#include <lttng/event-rule/tracepoint-internal.h>
+#include <lttng/event-rule/uprobe-internal.h>
+#include <stdbool.h>
+
+enum lttng_event_rule_type lttng_event_rule_get_type(
+ const struct lttng_event_rule *event_rule)
+{
+ return event_rule ? event_rule->type : LTTNG_EVENT_RULE_TYPE_UNKNOWN;
+}
+
+LTTNG_HIDDEN
+enum lttng_domain_type lttng_event_rule_get_domain_type(
+ const struct lttng_event_rule *event_rule)
+{
+ enum lttng_domain_type domain_type;
+
+ switch (lttng_event_rule_get_type(event_rule)) {
+ case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+ (void) lttng_event_rule_tracepoint_get_domain_type(event_rule, &domain_type);
+ break;
+ case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+ case LTTNG_EVENT_RULE_TYPE_KPROBE:
+ case LTTNG_EVENT_RULE_TYPE_KRETPROBE:
+ case LTTNG_EVENT_RULE_TYPE_UPROBE:
+ domain_type = LTTNG_DOMAIN_KERNEL;
+ break;
+ case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
+ domain_type = LTTNG_DOMAIN_NONE;
+ break;
+ }
+
+ return domain_type;
+}
+
+static void lttng_event_rule_release(struct urcu_ref *ref)
+{
+ struct lttng_event_rule *event_rule =
+ container_of(ref, typeof(*event_rule), ref);
+
+ assert(event_rule->destroy);
+ event_rule->destroy(event_rule);
+}
+
+void lttng_event_rule_destroy(struct lttng_event_rule *event_rule)
+{
+ lttng_event_rule_put(event_rule);
+}
+
+LTTNG_HIDDEN
+bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule)
+{
+ bool valid;
+
+ if (!event_rule) {
+ valid = false;
+ goto end;
+ }
+
+ if (!event_rule->validate) {
+ /* Sub-class guarantees that it can never be invalid. */
+ valid = true;
+ goto end;
+ }
+
+ valid = event_rule->validate(event_rule);
+end:
+ return valid;
+}
+
+LTTNG_HIDDEN
+int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
+{
+ int ret;
+ struct lttng_event_rule_comm event_rule_comm = {0};
+
+ if (!event_rule) {
+ ret = -1;
+ goto end;
+ }
+
+ event_rule_comm.event_rule_type = (int8_t) event_rule->type;
+
+ ret = lttng_dynamic_buffer_append(
+ buf, &event_rule_comm, sizeof(event_rule_comm));
+ if (ret) {
+ goto end;
+ }
+
+ ret = event_rule->serialize(event_rule, buf, fd_to_send);
+ if (ret) {
+ goto end;
+ }
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+bool lttng_event_rule_is_equal(const struct lttng_event_rule *a,
+ const struct lttng_event_rule *b)
+{
+ bool is_equal = false;
+
+ if (!a || !b) {
+ goto end;
+ }
+
+ if (a->type != b->type) {
+ goto end;
+ }
+
+ if (a == b) {
+ is_equal = true;
+ goto end;
+ }
+
+ is_equal = a->equal ? a->equal(a, b) : true;
+end:
+ return is_equal;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_create_from_buffer(
+ const struct lttng_buffer_view *buffer,
+ struct lttng_event_rule **event_rule)
+{
+ ssize_t ret, event_rule_size = 0;
+ const struct lttng_event_rule_comm *event_rule_comm;
+ event_rule_create_from_buffer_cb create_from_buffer = NULL;
+
+ if (!buffer || !event_rule) {
+ ret = -1;
+ goto end;
+ }
+
+ DBG("Deserializing event_rule from buffer");
+ event_rule_comm = (const struct lttng_event_rule_comm *) buffer->data;
+ event_rule_size += sizeof(*event_rule_comm);
+
+ switch ((enum lttng_event_rule_type) event_rule_comm->event_rule_type) {
+ case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+ create_from_buffer =
+ lttng_event_rule_tracepoint_create_from_buffer;
+ break;
+ case LTTNG_EVENT_RULE_TYPE_KPROBE:
+ create_from_buffer = lttng_event_rule_kprobe_create_from_buffer;
+ break;
+ case LTTNG_EVENT_RULE_TYPE_KRETPROBE:
+ create_from_buffer =
+ lttng_event_rule_kretprobe_create_from_buffer;
+ break;
+ case LTTNG_EVENT_RULE_TYPE_UPROBE:
+ create_from_buffer = lttng_event_rule_uprobe_create_from_buffer;
+ break;
+ case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+ create_from_buffer =
+ lttng_event_rule_syscall_create_from_buffer;
+ break;
+ default:
+ ERR("Attempted to create event rule of unknown type (%i)",
+ (int) event_rule_comm->event_rule_type);
+ ret = -1;
+ goto end;
+ }
+
+ if (create_from_buffer) {
+ const struct lttng_buffer_view view =
+ lttng_buffer_view_from_view(buffer,
+ sizeof(*event_rule_comm), -1);
+
+ ret = create_from_buffer(&view, event_rule);
+ if (ret < 0) {
+ goto end;
+ }
+ event_rule_size += ret;
+
+ } else {
+ abort();
+ }
+
+ ret = event_rule_size;
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+void lttng_event_rule_init(struct lttng_event_rule *event_rule,
+ enum lttng_event_rule_type type)
+{
+ urcu_ref_init(&event_rule->ref);
+ event_rule->type = type;
+}
+
+LTTNG_HIDDEN
+bool lttng_event_rule_get(struct lttng_event_rule *event_rule)
+{
+ return urcu_ref_get_unless_zero(&event_rule->ref);
+}
+
+LTTNG_HIDDEN
+void lttng_event_rule_put(struct lttng_event_rule *event_rule)
+{
+ if (!event_rule) {
+ return;
+ }
+ assert(event_rule->ref.refcount);
+ urcu_ref_put(&event_rule->ref, lttng_event_rule_release);
+}
+
+LTTNG_HIDDEN
+enum lttng_error_code lttng_event_rule_populate(
+ struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+ assert(rule->populate);
+ return rule->populate(rule, uid, gid);
+}
+
+/* If not present return NULL
+ * Caller DO NOT own the returned object
+ */
+LTTNG_HIDDEN
+const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule)
+{
+ assert(rule->get_filter);
+ return rule->get_filter(rule);
+}
+
+/* If not present return NULL
+ * Caller DO NOT own the returned object
+ */
+LTTNG_HIDDEN
+const struct lttng_bytecode *lttng_event_rule_get_filter_bytecode(
+ const struct lttng_event_rule *rule)
+{
+ assert(rule->get_filter_bytecode);
+ return rule->get_filter_bytecode(rule);
+}
+
+/*
+ * If not present return NULL
+ * Caller OWN the returned object
+ * TODO: should this be done another way?
+ */
+LTTNG_HIDDEN
+struct lttng_event_exclusion *lttng_event_rule_generate_exclusions(
+ struct lttng_event_rule *rule)
+{
+ assert(rule->generate_exclusions);
+ return rule->generate_exclusions(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_is_agent(const struct lttng_event_rule *rule)
+{
+ bool ret = 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:
+ ret = true;
+ break;
+ case LTTNG_DOMAIN_UST:
+ case LTTNG_DOMAIN_KERNEL:
+ ret = false;
+ break;
+ default:
+ assert(0);
+ };
+
+ return ret;
+}
+
+const char *lttng_event_rule_type_str(enum lttng_event_rule_type type)
+{
+ switch (type) {
+ case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
+ return "unknown";
+
+ case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
+ return "tracepoint";
+
+ case LTTNG_EVENT_RULE_TYPE_SYSCALL:
+ return "syscall";
+
+ case LTTNG_EVENT_RULE_TYPE_KPROBE:
+ return "probe";
+
+ case LTTNG_EVENT_RULE_TYPE_KRETPROBE:
+ return "function";
+
+ case LTTNG_EVENT_RULE_TYPE_UPROBE:
+ return "userspace-probe";
+
+ default:
+ abort();
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/runas.h>
+#include <ctype.h>
+#include <lttng/constant.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kprobe-internal.h>
+#include <stdio.h>
+
+#define IS_KPROBE_EVENT_RULE(rule) \
+ (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KPROBE)
+
+#if (LTTNG_SYMBOL_NAME_LEN == 256)
+#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255"
+#endif
+
+static void lttng_event_rule_kprobe_destroy(struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_kprobe *kprobe;
+
+ kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+
+ free(kprobe->name);
+ free(kprobe->probe.symbol_name);
+ free(kprobe);
+}
+
+static bool lttng_event_rule_kprobe_validate(
+ const struct lttng_event_rule *rule)
+{
+ bool valid = false;
+ struct lttng_event_rule_kprobe *kprobe;
+
+ if (!rule) {
+ goto end;
+ }
+
+ kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+
+ /* Required field */
+ if (!kprobe->name) {
+ ERR("Invalid name event rule: a name must be set.");
+ goto end;
+ }
+ if (kprobe->probe.set == LTTNG_DOMAIN_NONE) {
+ ERR("Invalid kprobe event rule: a source must be set.");
+ goto end;
+ }
+
+ valid = true;
+end:
+ return valid;
+}
+
+static int lttng_event_rule_kprobe_serialize(
+ const struct lttng_event_rule *rule,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
+{
+ int ret;
+ size_t name_len, probe_symbol_name_len;
+ struct lttng_event_rule_kprobe *kprobe;
+ struct lttng_event_rule_kprobe_comm kprobe_comm;
+
+ if (!rule || !IS_KPROBE_EVENT_RULE(rule)) {
+ ret = -1;
+ goto end;
+ }
+
+ DBG("Serializing kprobe event rule");
+ kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+
+ name_len = strlen(kprobe->name) + 1;
+
+ if (kprobe->probe.symbol_name != NULL) {
+ probe_symbol_name_len = strlen(kprobe->probe.symbol_name) + 1;
+ } else {
+ probe_symbol_name_len = 0;
+ }
+
+ kprobe_comm.name_len = name_len;
+ kprobe_comm.probe_symbol_name_len = probe_symbol_name_len;
+ kprobe_comm.probe_address = kprobe->probe.address;
+ kprobe_comm.probe_offset = kprobe->probe.offset;
+
+ ret = lttng_dynamic_buffer_append(
+ buf, &kprobe_comm, sizeof(kprobe_comm));
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(buf, kprobe->name, name_len);
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(
+ buf, kprobe->probe.symbol_name, probe_symbol_name_len);
+ if (ret) {
+ goto end;
+ }
+
+ if (fd_to_send) {
+ /* Nothing to send */
+ *fd_to_send = -1;
+ }
+end:
+ return ret;
+}
+
+static bool lttng_event_rule_kprobe_is_equal(const struct lttng_event_rule *_a,
+ const struct lttng_event_rule *_b)
+{
+ bool is_equal = false;
+ struct lttng_event_rule_kprobe *a, *b;
+
+ a = container_of(_a, struct lttng_event_rule_kprobe, parent);
+ b = container_of(_b, struct lttng_event_rule_kprobe, parent);
+
+ /* Quick checks */
+ if (!!a->name != !!b->name) {
+ goto end;
+ }
+
+ if (!!a->probe.symbol_name != !!b->probe.symbol_name) {
+ goto end;
+ }
+
+ /* Long check */
+ /* kprobe is invalid if this is not true */
+ /* TODO: validate that a kprobe MUST have a name */
+ assert(a->name);
+ assert(b->name);
+ if (strcmp(a->name, b->name)) {
+ goto end;
+ }
+
+ if (a->probe.symbol_name) {
+ /* Both have symbol name due to previous checks */
+ if (strcmp(a->probe.symbol_name, b->probe.symbol_name)) {
+ goto end;
+ }
+ }
+
+ if (a->probe.offset != b->probe.offset) {
+ goto end;
+ }
+
+ if (a->probe.address != b->probe.address) {
+ goto end;
+ }
+
+ is_equal = true;
+end:
+ return is_equal;
+}
+
+static enum lttng_error_code lttng_event_rule_kprobe_populate(
+ struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+ /* Nothing to do */
+ return LTTNG_OK;
+}
+
+static const char *lttng_event_rule_kprobe_get_filter(
+ const struct lttng_event_rule *rule)
+{
+ /* Not supported */
+ return NULL;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_kprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
+{
+ /* Not supported */
+ return NULL;
+}
+
+static struct lttng_event_exclusion *
+lttng_event_rule_kprobe_generate_exclusions(struct lttng_event_rule *rule)
+{
+ /* Not supported */
+ return NULL;
+}
+
+struct lttng_event_rule *lttng_event_rule_kprobe_create()
+{
+ struct lttng_event_rule_kprobe *rule;
+
+ rule = zmalloc(sizeof(struct lttng_event_rule_kprobe));
+ if (!rule) {
+ return NULL;
+ }
+
+ lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_KPROBE);
+ rule->parent.validate = lttng_event_rule_kprobe_validate;
+ rule->parent.serialize = lttng_event_rule_kprobe_serialize;
+ rule->parent.equal = lttng_event_rule_kprobe_is_equal;
+ rule->parent.destroy = lttng_event_rule_kprobe_destroy;
+ rule->parent.populate = lttng_event_rule_kprobe_populate;
+ rule->parent.get_filter = lttng_event_rule_kprobe_get_filter;
+ rule->parent.get_filter_bytecode =
+ lttng_event_rule_kprobe_get_filter_bytecode;
+ rule->parent.generate_exclusions =
+ lttng_event_rule_kprobe_generate_exclusions;
+ return &rule->parent;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_kprobe_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_event_rule **_event_rule)
+{
+ ssize_t ret, offset = 0;
+ enum lttng_event_rule_status status;
+ const struct lttng_event_rule_kprobe_comm *kprobe_comm;
+ const char *name;
+ const char *probe_symbol_name = NULL;
+ struct lttng_buffer_view current_view;
+ struct lttng_event_rule *rule = NULL;
+ struct lttng_event_rule_kprobe *kprobe = NULL;
+
+ if (!_event_rule) {
+ ret = -1;
+ goto end;
+ }
+
+ if (view->size < sizeof(*kprobe_comm)) {
+ ERR("Failed to initialize from malformed event rule kprobe: buffer too short to contain header");
+ ret = -1;
+ goto end;
+ }
+
+ current_view = lttng_buffer_view_from_view(
+ view, offset, sizeof(*kprobe_comm));
+ kprobe_comm = (typeof(kprobe_comm)) current_view.data;
+ if (!kprobe_comm) {
+ ret = -1;
+ goto end;
+ }
+
+ rule = lttng_event_rule_kprobe_create();
+ if (!rule) {
+ ERR("Failed to create event rule kprobe");
+ ret = -1;
+ goto end;
+ }
+
+ kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+
+ /* Skip to payload */
+ offset += current_view.size;
+ /* Map the name */
+ current_view = lttng_buffer_view_from_view(
+ view, offset, kprobe_comm->name_len);
+ name = current_view.data;
+ if (!name) {
+ ret = -1;
+ goto end;
+ }
+
+ if (kprobe_comm->name_len == 1 ||
+ name[kprobe_comm->name_len - 1] != '\0' ||
+ strlen(name) != kprobe_comm->name_len - 1) {
+ /*
+ * Check that the name is not NULL, is NULL-terminated, and
+ * does not contain a NULL before the last byte.
+ */
+ ret = -1;
+ goto end;
+ }
+
+ /* Skip after the name */
+ offset += kprobe_comm->name_len;
+ if (!kprobe_comm->probe_symbol_name_len) {
+ goto skip_probe_symbol_name;
+ }
+
+ /* Map the probe_symbol_name */
+ current_view = lttng_buffer_view_from_view(
+ view, offset, kprobe_comm->probe_symbol_name_len);
+ probe_symbol_name = current_view.data;
+ if (!probe_symbol_name) {
+ ret = -1;
+ goto end;
+ }
+
+ if (kprobe_comm->probe_symbol_name_len == 1 ||
+ probe_symbol_name[kprobe_comm->probe_symbol_name_len -
+ 1] != '\0' ||
+ strlen(probe_symbol_name) !=
+ kprobe_comm->probe_symbol_name_len -
+ 1) {
+ /*
+ * Check that the filter expression is not NULL, is
+ * NULL-terminated, and does not contain a NULL before the last
+ * byte.
+ */
+ ret = -1;
+ goto end;
+ }
+
+ /* Skip after the pattern */
+ offset += kprobe_comm->probe_symbol_name_len;
+
+skip_probe_symbol_name:
+
+ status = lttng_event_rule_kprobe_set_name(rule, name);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set event rule kprobe name");
+ ret = -1;
+ goto end;
+ }
+ kprobe->probe.offset = kprobe_comm->probe_offset;
+ kprobe->probe.address = kprobe_comm->probe_address;
+ if (probe_symbol_name) {
+ kprobe->probe.symbol_name = strdup(probe_symbol_name);
+ if (!kprobe->probe.symbol_name) {
+ ERR("Failed to set event rule kprobe probe symbol name");
+ ret = -1;
+ goto end;
+ }
+ }
+
+ *_event_rule = rule;
+ rule = NULL;
+ ret = offset;
+end:
+ lttng_event_rule_destroy(rule);
+ return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kprobe_set_source(
+ struct lttng_event_rule *rule, const char *source)
+{
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+ int match;
+ char s_hex[19];
+ char name[LTTNG_SYMBOL_NAME_LEN];
+ struct lttng_event_rule_kprobe *kprobe;
+
+ /* TODO: support multiple call for this, we must free the symbol name if
+ * that happens !!!
+ */
+
+ if (!source || !IS_KPROBE_EVENT_RULE(rule) || !rule) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+
+ /* Check for symbol+offset */
+ match = sscanf(source, "%18s[^'+']+%18s", name, s_hex);
+ if (match == 2) {
+ /* TODO double validate termination handling of this */
+ kprobe->probe.symbol_name =
+ strndup(name, LTTNG_SYMBOL_NAME_LEN);
+ if (!kprobe->probe.symbol_name) {
+ status = LTTNG_EVENT_RULE_STATUS_ERROR;
+ goto end;
+ }
+ if (*s_hex == '\0') {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+ kprobe->probe.offset = strtoul(s_hex, NULL, 0);
+ kprobe->probe.address = 0;
+ kprobe->probe.set = true;
+ goto end;
+ }
+
+ /* Check for symbol */
+ if (isalpha(name[0]) || name[0] == '_') {
+ match = sscanf(source,
+ "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API
+ "s",
+ name);
+ if (match == 1) {
+ /* TODO double validate termination handling of this */
+ kprobe->probe.symbol_name =
+ strndup(name, LTTNG_SYMBOL_NAME_LEN);
+ if (!kprobe->probe.symbol_name) {
+ status = LTTNG_EVENT_RULE_STATUS_ERROR;
+ goto end;
+ }
+ kprobe->probe.offset = 0;
+ kprobe->probe.address = 0;
+ kprobe->probe.set = true;
+ goto end;
+ }
+ }
+
+ /* Check for address */
+ match = sscanf(source, "%18s", s_hex);
+ if (match > 0) {
+ if (*s_hex == '\0') {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+ kprobe->probe.address = strtoul(s_hex, NULL, 0);
+ kprobe->probe.offset = 0;
+ kprobe->probe.symbol_name = NULL;
+ kprobe->probe.set = true;
+ goto end;
+ }
+
+ /* No match */
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kprobe_set_name(
+ struct lttng_event_rule *rule, const char *name)
+{
+ char *name_copy = NULL;
+ struct lttng_event_rule_kprobe *kprobe;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name ||
+ strlen(name) == 0) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+ name_copy = strdup(name);
+ if (!name_copy) {
+ status = LTTNG_EVENT_RULE_STATUS_ERROR;
+ goto end;
+ }
+
+ if (kprobe->name) {
+ free(kprobe->name);
+ }
+
+ kprobe->name = name_copy;
+ name_copy = NULL;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kprobe_get_name(
+ const struct lttng_event_rule *rule, const char **name)
+{
+ struct lttng_event_rule_kprobe *kprobe;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+ if (!kprobe->name) {
+ status = LTTNG_EVENT_RULE_STATUS_UNSET;
+ goto end;
+ }
+
+ *name = kprobe->name;
+end:
+ return status;
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kprobe_get_address(
+ const struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_kprobe *kprobe;
+
+ assert(rule && IS_KPROBE_EVENT_RULE(rule));
+
+ kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+
+ return kprobe->probe.address;
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kprobe_get_offset(const struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_kprobe *kprobe;
+
+ assert(rule && IS_KPROBE_EVENT_RULE(rule));
+
+ kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+ return kprobe->probe.offset;
+}
+
+LTTNG_HIDDEN
+const char *lttng_event_rule_kprobe_get_symbol_name(
+ const struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_kprobe *kprobe;
+
+ assert(rule && IS_KPROBE_EVENT_RULE(rule));
+
+ kprobe = container_of(rule, struct lttng_event_rule_kprobe, parent);
+ return kprobe->probe.symbol_name;
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/runas.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kretprobe-internal.h>
+
+#define IS_KRETPROBE_EVENT_RULE(rule) \
+ (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KRETPROBE)
+
+static void lttng_event_rule_kretprobe_destroy(struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_kretprobe *kretprobe;
+
+ kretprobe = container_of(
+ rule, struct lttng_event_rule_kretprobe, parent);
+
+ /*
+ * TODO
+ */
+ free(kretprobe);
+}
+
+static bool lttng_event_rule_kretprobe_validate(
+ const struct lttng_event_rule *rule)
+{
+ /* TODO */
+ return false;
+}
+
+static int lttng_event_rule_kretprobe_serialize(
+ const struct lttng_event_rule *rule,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
+{
+ /* TODO */
+ return -1;
+}
+
+static bool lttng_event_rule_kretprobe_is_equal(
+ const struct lttng_event_rule *_a,
+ const struct lttng_event_rule *_b)
+{
+ /* TODO */
+ return false;
+}
+
+static enum lttng_error_code lttng_event_rule_kretprobe_populate(
+ struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+ /* Nothing to do */
+ return LTTNG_OK;
+}
+
+static const char *lttng_event_rule_kretprobe_get_filter(
+ const struct lttng_event_rule *rule)
+{
+ /* Not supported */
+ return NULL;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_kretprobe_get_filter_bytecode(
+ const struct lttng_event_rule *rule)
+{
+ /* Not supported */
+ return NULL;
+}
+
+static struct lttng_event_exclusion *
+lttng_event_rule_kretprobe_generate_exclusions(struct lttng_event_rule *rule)
+{
+ /* Not supported */
+ return NULL;
+}
+
+struct lttng_event_rule *lttng_event_rule_kretprobe_create()
+{
+ struct lttng_event_rule_kretprobe *rule;
+
+ rule = zmalloc(sizeof(struct lttng_event_rule_kretprobe));
+ if (!rule) {
+ return NULL;
+ }
+
+ lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_KRETPROBE);
+ rule->parent.validate = lttng_event_rule_kretprobe_validate;
+ rule->parent.serialize = lttng_event_rule_kretprobe_serialize;
+ rule->parent.equal = lttng_event_rule_kretprobe_is_equal;
+ rule->parent.destroy = lttng_event_rule_kretprobe_destroy;
+ rule->parent.populate = lttng_event_rule_kretprobe_populate;
+ rule->parent.get_filter = lttng_event_rule_kretprobe_get_filter;
+ rule->parent.get_filter_bytecode =
+ lttng_event_rule_kretprobe_get_filter_bytecode;
+ rule->parent.generate_exclusions =
+ lttng_event_rule_kretprobe_generate_exclusions;
+ return &rule->parent;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_kretprobe_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_event_rule **_event_rule)
+{
+ /* TODO */
+ return -1;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kretprobe_set_source(
+ struct lttng_event_rule *rule, const char *source)
+{
+ return LTTNG_EVENT_RULE_STATUS_UNSUPPORTED;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kretprobe_set_name(
+ struct lttng_event_rule *rule, const char *name)
+{
+ return LTTNG_EVENT_RULE_STATUS_UNSUPPORTED;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kretprobe_get_name(
+ const struct lttng_event_rule *rule, const char **name)
+{
+ return LTTNG_EVENT_RULE_STATUS_UNSUPPORTED;
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kretprobe_get_address(
+ const struct lttng_event_rule *rule)
+{
+ assert("Not implemented" && 0);
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_event_rule_kretprobe_get_offset(
+ const struct lttng_event_rule *rule)
+{
+ assert("Not implemented" && 0);
+}
+
+LTTNG_HIDDEN
+const char *lttng_event_rule_kretprobe_get_symbol_name(
+ const struct lttng_event_rule *rule)
+{
+ assert("Not implemented" && 0);
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/runas.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/syscall-internal.h>
+
+#define IS_SYSCALL_EVENT_RULE(rule) \
+ (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_SYSCALL)
+
+static void lttng_event_rule_syscall_destroy(struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_syscall *syscall;
+
+ if (rule == NULL) {
+ return;
+ }
+
+ syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+
+ free(syscall->pattern);
+ free(syscall->filter_expression);
+ free(syscall->internal_filter.filter);
+ free(syscall->internal_filter.bytecode);
+ free(syscall);
+}
+
+static bool lttng_event_rule_syscall_validate(
+ const struct lttng_event_rule *rule)
+{
+ bool valid = false;
+ struct lttng_event_rule_syscall *syscall;
+
+ if (!rule) {
+ goto end;
+ }
+
+ syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+
+ /* Required field */
+ if (!syscall->pattern) {
+ ERR("Invalid syscall event rule: a pattern must be set.");
+ goto end;
+ }
+
+ valid = true;
+end:
+ return valid;
+}
+
+static int lttng_event_rule_syscall_serialize(
+ const struct lttng_event_rule *rule,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
+{
+ int ret;
+ size_t pattern_len, filter_expression_len;
+ struct lttng_event_rule_syscall *syscall;
+ struct lttng_event_rule_syscall_comm syscall_comm;
+
+ if (!rule || !IS_SYSCALL_EVENT_RULE(rule)) {
+ ret = -1;
+ goto end;
+ }
+
+ DBG("Serializing syscall event rule");
+ syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+
+ pattern_len = strlen(syscall->pattern) + 1;
+
+ if (syscall->filter_expression != NULL) {
+ filter_expression_len = strlen(syscall->filter_expression) + 1;
+ } else {
+ filter_expression_len = 0;
+ }
+
+ syscall_comm.pattern_len = pattern_len;
+ syscall_comm.filter_expression_len = filter_expression_len;
+
+ ret = lttng_dynamic_buffer_append(
+ buf, &syscall_comm, sizeof(syscall_comm));
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(buf, syscall->pattern, pattern_len);
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(
+ buf, syscall->filter_expression, filter_expression_len);
+ if (ret) {
+ goto end;
+ }
+
+ if (fd_to_send) {
+ /* Nothing to send */
+ *fd_to_send = -1;
+ }
+end:
+ return ret;
+}
+
+static bool lttng_event_rule_syscall_is_equal(const struct lttng_event_rule *_a,
+ const struct lttng_event_rule *_b)
+{
+ bool is_equal = false;
+ struct lttng_event_rule_syscall *a, *b;
+
+ a = container_of(_a, struct lttng_event_rule_syscall, parent);
+ b = container_of(_b, struct lttng_event_rule_syscall, parent);
+
+ if (!!a->filter_expression != !!b->filter_expression) {
+ goto end;
+ }
+
+ /* Long check */
+ /* syscall is invalid if this is not true */
+ assert(a->pattern);
+ assert(b->pattern);
+ if (strcmp(a->pattern, b->pattern)) {
+ goto end;
+ }
+
+ if (a->filter_expression && b->filter_expression) {
+ if (strcmp(a->filter_expression, b->filter_expression)) {
+ goto end;
+ }
+ }
+
+ is_equal = true;
+end:
+ return is_equal;
+}
+
+static enum lttng_error_code lttng_event_rule_syscall_populate(
+ struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+ int ret;
+ enum lttng_error_code ret_code = LTTNG_OK;
+ struct lttng_event_rule_syscall *syscall;
+ enum lttng_event_rule_status status;
+ const char *filter;
+ struct lttng_bytecode *bytecode = NULL;
+
+ assert(rule);
+
+ syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+
+ /* Generate the filter bytecode */
+ status = lttng_event_rule_syscall_get_filter(rule, &filter);
+ if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+ filter = NULL;
+ } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ret_code = LTTNG_ERR_FILTER_INVAL;
+ goto end;
+ }
+
+ if (filter && filter[0] == '\0') {
+ ret_code = LTTNG_ERR_FILTER_INVAL;
+ goto error;
+ }
+
+ if (filter == NULL) {
+ /* Nothing to do */
+ ret = LTTNG_OK;
+ goto end;
+ }
+
+ syscall->internal_filter.filter = strdup(filter);
+ if (syscall->internal_filter.filter == NULL) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ ret = run_as_generate_filter_bytecode(
+ syscall->internal_filter.filter, uid, gid, &bytecode);
+ if (ret) {
+ ret_code = LTTNG_ERR_FILTER_INVAL;
+ }
+
+ syscall->internal_filter.bytecode = bytecode;
+ bytecode = NULL;
+
+error:
+end:
+ free(bytecode);
+ return ret_code;
+}
+
+static const char *lttng_event_rule_syscall_get_internal_filter(
+ const struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_syscall *syscall;
+ assert(rule);
+
+ syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+ return syscall->internal_filter.filter;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_syscall_get_internal_filter_bytecode(
+ const struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_syscall *syscall;
+ assert(rule);
+
+ syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+ return syscall->internal_filter.bytecode;
+}
+
+static struct lttng_event_exclusion *
+lttng_event_rule_syscall_generate_exclusions(struct lttng_event_rule *rule)
+{
+ /* Not supported */
+ return NULL;
+}
+
+struct lttng_event_rule *lttng_event_rule_syscall_create()
+{
+ struct lttng_event_rule_syscall *rule;
+
+ rule = zmalloc(sizeof(struct lttng_event_rule_syscall));
+ if (!rule) {
+ return NULL;
+ }
+
+ lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_SYSCALL);
+ rule->parent.validate = lttng_event_rule_syscall_validate;
+ rule->parent.serialize = lttng_event_rule_syscall_serialize;
+ rule->parent.equal = lttng_event_rule_syscall_is_equal;
+ rule->parent.destroy = lttng_event_rule_syscall_destroy;
+ rule->parent.populate = lttng_event_rule_syscall_populate;
+ rule->parent.get_filter = lttng_event_rule_syscall_get_internal_filter;
+ rule->parent.get_filter_bytecode =
+ lttng_event_rule_syscall_get_internal_filter_bytecode;
+ rule->parent.generate_exclusions =
+ lttng_event_rule_syscall_generate_exclusions;
+ return &rule->parent;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_syscall_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_event_rule **_event_rule)
+{
+ ssize_t ret, offset = 0;
+ enum lttng_event_rule_status status;
+ const struct lttng_event_rule_syscall_comm *syscall_comm;
+ const char *pattern;
+ const char *filter_expression = NULL;
+ struct lttng_buffer_view current_view;
+ struct lttng_event_rule *rule = NULL;
+
+ if (!_event_rule) {
+ ret = -1;
+ goto end;
+ }
+
+ if (view->size < sizeof(*syscall_comm)) {
+ ERR("Failed to initialize from malformed event rule syscall: buffer too short to contain header");
+ ret = -1;
+ goto end;
+ }
+
+ current_view = lttng_buffer_view_from_view(
+ view, offset, sizeof(*syscall_comm));
+ syscall_comm = (typeof(syscall_comm)) current_view.data;
+
+ if (!syscall_comm) {
+ ret = -1;
+ goto end;
+ }
+
+ rule = lttng_event_rule_syscall_create();
+ if (!rule) {
+ ERR("Failed to create event rule syscall");
+ ret = -1;
+ goto end;
+ }
+
+ /* Skip to payload */
+ offset += current_view.size;
+
+ /* Map the pattern */
+ current_view = lttng_buffer_view_from_view(
+ view, offset, syscall_comm->pattern_len);
+ pattern = current_view.data;
+ if (!pattern) {
+ ret = -1;
+ goto end;
+ }
+
+ if (syscall_comm->pattern_len == 1 ||
+ pattern[syscall_comm->pattern_len - 1] != '\0' ||
+ strlen(pattern) != syscall_comm->pattern_len - 1) {
+ /*
+ * Check that the pattern is not NULL, is NULL-terminated, and
+ * does not contain a NULL before the last byte.
+ */
+ ret = -1;
+ goto end;
+ }
+
+ /* Skip after the pattern */
+ offset += syscall_comm->pattern_len;
+
+ if (!syscall_comm->filter_expression_len) {
+ goto skip_filter_expression;
+ }
+
+ /* Map the filter_expression */
+ current_view = lttng_buffer_view_from_view(
+ view, offset, syscall_comm->filter_expression_len);
+ filter_expression = current_view.data;
+ if (!filter_expression) {
+ ret = -1;
+ goto end;
+ }
+
+ if (syscall_comm->filter_expression_len == 1 ||
+ filter_expression[syscall_comm->filter_expression_len -
+ 1] != '\0' ||
+ strlen(filter_expression) !=
+ syscall_comm->filter_expression_len -
+ 1) {
+ /*
+ * Check that the filter expression is not NULL, is
+ * NULL-terminated, and does not contain a NULL before the last
+ * byte.
+ */
+ ret = -1;
+ goto end;
+ }
+
+ /* Skip after the pattern */
+ offset += syscall_comm->filter_expression_len;
+
+skip_filter_expression:
+
+ status = lttng_event_rule_syscall_set_pattern(rule, pattern);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set event rule syscall pattern");
+ ret = -1;
+ goto end;
+ }
+
+ if (filter_expression) {
+ status = lttng_event_rule_syscall_set_filter(
+ rule, filter_expression);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set event rule syscall pattern");
+ ret = -1;
+ goto end;
+ }
+ }
+
+ *_event_rule = rule;
+ rule = NULL;
+ ret = offset;
+end:
+ lttng_event_rule_destroy(rule);
+ return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_syscall_set_pattern(
+ struct lttng_event_rule *rule, const char *pattern)
+{
+ char *pattern_copy = NULL;
+ struct lttng_event_rule_syscall *syscall;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern ||
+ strlen(pattern) == 0) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+ pattern_copy = strdup(pattern);
+ if (!pattern_copy) {
+ status = LTTNG_EVENT_RULE_STATUS_ERROR;
+ goto end;
+ }
+
+ if (syscall->pattern) {
+ free(syscall->pattern);
+ }
+
+ syscall->pattern = pattern_copy;
+ pattern_copy = NULL;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_syscall_get_pattern(
+ const struct lttng_event_rule *rule, const char **pattern)
+{
+ struct lttng_event_rule_syscall *syscall;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+ if (!syscall->pattern) {
+ status = LTTNG_EVENT_RULE_STATUS_UNSET;
+ goto end;
+ }
+
+ *pattern = syscall->pattern;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_syscall_set_filter(
+ struct lttng_event_rule *rule, const char *expression)
+{
+ char *expression_copy = NULL;
+ struct lttng_event_rule_syscall *syscall;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ /* TODO: validate that the passed expression is valid */
+
+ if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression ||
+ strlen(expression) == 0) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+ expression_copy = strdup(expression);
+ if (!expression_copy) {
+ status = LTTNG_EVENT_RULE_STATUS_ERROR;
+ goto end;
+ }
+
+ if (syscall->filter_expression) {
+ free(syscall->filter_expression);
+ }
+
+ syscall->filter_expression = expression_copy;
+ expression_copy = NULL;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_syscall_get_filter(
+ const struct lttng_event_rule *rule, const char **expression)
+{
+ struct lttng_event_rule_syscall *syscall;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ syscall = container_of(rule, struct lttng_event_rule_syscall, parent);
+ if (!syscall->filter_expression) {
+ status = LTTNG_EVENT_RULE_STATUS_UNSET;
+ goto end;
+ }
+
+ *expression = syscall->filter_expression;
+end:
+ return status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/runas.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/tracepoint-internal.h>
+#include <lttng/event.h>
+
+#define IS_TRACEPOINT_EVENT_RULE(rule) \
+ (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_TRACEPOINT)
+
+static void lttng_event_rule_tracepoint_destroy(struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+
+ if (rule == NULL) {
+ return;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+
+ free(tracepoint->pattern);
+ free(tracepoint->filter_expression);
+ for (int i = 0; i < tracepoint->exclusions.count; i++) {
+ free(tracepoint->exclusions.values[i]);
+ }
+ free(tracepoint->exclusions.values);
+ free(tracepoint->internal_filter.filter);
+ free(tracepoint->internal_filter.bytecode);
+ free(tracepoint);
+}
+
+static bool lttng_event_rule_tracepoint_validate(
+ const struct lttng_event_rule *rule)
+{
+ bool valid = false;
+ struct lttng_event_rule_tracepoint *tracepoint;
+
+ if (!rule) {
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+
+ /* Required field */
+ if (!tracepoint->pattern) {
+ ERR("Invalid tracepoint event rule: a pattern must be set.");
+ goto end;
+ }
+ if (tracepoint->domain == LTTNG_DOMAIN_NONE) {
+ ERR("Invalid tracepoint event rule: a domain must be set.");
+ goto end;
+ }
+
+ /* QUESTION: do we validate inside state on validate or during set of
+ * each component */
+
+ valid = true;
+end:
+ return valid;
+}
+
+static int lttng_event_rule_tracepoint_serialize(
+ const struct lttng_event_rule *rule,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
+{
+ int ret;
+ size_t pattern_len, filter_expression_len, exclusions_len;
+ struct lttng_event_rule_tracepoint *tracepoint;
+ struct lttng_event_rule_tracepoint_comm tracepoint_comm;
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) {
+ ret = -1;
+ goto end;
+ }
+
+ DBG("Serializing tracepoint event rule");
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+
+ pattern_len = strlen(tracepoint->pattern) + 1;
+
+ if (tracepoint->filter_expression != NULL) {
+ filter_expression_len =
+ strlen(tracepoint->filter_expression) + 1;
+ } else {
+ filter_expression_len = 0;
+ }
+
+ exclusions_len = 0;
+ for (int i = 0; i < tracepoint->exclusions.count; i++) {
+ /* Payload */
+ exclusions_len += strlen(tracepoint->exclusions.values[i]) + 1;
+ /* Bound check */
+ exclusions_len += sizeof(uint32_t);
+ }
+
+ tracepoint_comm.domain_type = (int8_t) tracepoint->domain;
+ tracepoint_comm.loglevel_type = (int8_t) tracepoint->loglevel.type;
+ tracepoint_comm.loglevel_value = tracepoint->loglevel.value;
+ tracepoint_comm.pattern_len = pattern_len;
+ tracepoint_comm.filter_expression_len = filter_expression_len;
+ tracepoint_comm.exclusions_count = tracepoint->exclusions.count;
+ tracepoint_comm.exclusions_len = exclusions_len;
+
+ ret = lttng_dynamic_buffer_append(
+ buf, &tracepoint_comm, sizeof(tracepoint_comm));
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(
+ buf, tracepoint->pattern, pattern_len);
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(buf, tracepoint->filter_expression,
+ filter_expression_len);
+ if (ret) {
+ goto end;
+ }
+
+ size_t exclusions_appended = 0;
+ for (int i = 0; i < tracepoint->exclusions.count; i++) {
+ size_t len;
+ len = strlen(tracepoint->exclusions.values[i]) + 1;
+ /* Append bound check, does not include the '\0' */
+ ret = lttng_dynamic_buffer_append(buf, &len, sizeof(uint32_t));
+ if (ret) {
+ goto end;
+ }
+ exclusions_appended += sizeof(uint32_t);
+
+ /* Include the '\0' in the payload */
+ ret = lttng_dynamic_buffer_append(
+ buf, tracepoint->exclusions.values[i], len);
+ if (ret) {
+ goto end;
+ }
+ exclusions_appended += len;
+ }
+
+ assert(exclusions_len == exclusions_appended);
+
+ if (fd_to_send) {
+ /* No fd to send */
+ *fd_to_send = -1;
+ }
+
+end:
+ return ret;
+}
+
+static bool lttng_event_rule_tracepoint_is_equal(
+ const struct lttng_event_rule *_a,
+ const struct lttng_event_rule *_b)
+{
+ bool is_equal = false;
+ struct lttng_event_rule_tracepoint *a, *b;
+
+ a = container_of(_a, struct lttng_event_rule_tracepoint, parent);
+ b = container_of(_b, struct lttng_event_rule_tracepoint, parent);
+
+ /* Quick checks */
+ if (a->domain != b->domain) {
+ goto end;
+ }
+
+ if (a->exclusions.count != b->exclusions.count) {
+ goto end;
+ }
+
+ if (!!a->filter_expression != !!b->filter_expression) {
+ goto end;
+ }
+
+ /* Long check */
+ /* Tracepoint is invalid if this is not true */
+ assert(a->pattern);
+ assert(b->pattern);
+ if (strcmp(a->pattern, b->pattern)) {
+ goto end;
+ }
+
+ if (a->filter_expression && b->filter_expression) {
+ if (strcmp(a->filter_expression, b->filter_expression)) {
+ goto end;
+ }
+ }
+
+ if (a->loglevel.type != b->loglevel.type) {
+ goto end;
+ }
+
+ if (a->loglevel.value != b->loglevel.value) {
+ goto end;
+ }
+
+ for (int i = 0; i < a->exclusions.count; i++) {
+ if (strcmp(a->exclusions.values[i], b->exclusions.values[i])) {
+ goto end;
+ }
+ }
+
+ is_equal = true;
+end:
+ return is_equal;
+}
+
+/*
+ * On success ret is 0;
+ *
+ * On error ret is negative.
+ *
+ * An event with NO loglevel and the name is * will return NULL.
+ */
+static int generate_agent_filter(
+ const struct lttng_event_rule *rule, char **_agent_filter)
+{
+ int err;
+ int ret = 0;
+ char *agent_filter = NULL;
+ const char *pattern;
+ const char *filter;
+ enum lttng_loglevel_type loglevel_type;
+ enum lttng_event_rule_status status;
+
+ assert(rule);
+ assert(_agent_filter);
+
+ status = lttng_event_rule_tracepoint_get_pattern(rule, &pattern);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ status = lttng_event_rule_tracepoint_get_filter(rule, &filter);
+ if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+ filter = NULL;
+ } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ status = lttng_event_rule_tracepoint_get_loglevel_type(
+ rule, &loglevel_type);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ /* Don't add filter for the '*' event. */
+ if (strcmp(pattern, "*") != 0) {
+ if (filter) {
+ err = asprintf(&agent_filter,
+ "(%s) && (logger_name == \"%s\")",
+ filter, pattern);
+ } else {
+ err = asprintf(&agent_filter, "logger_name == \"%s\"",
+ pattern);
+ }
+ if (err < 0) {
+ PERROR("asprintf");
+ ret = -1;
+ goto end;
+ }
+ }
+
+ if (loglevel_type != LTTNG_EVENT_LOGLEVEL_ALL) {
+ const char *op;
+ int loglevel_value;
+
+ status = lttng_event_rule_tracepoint_get_loglevel(
+ rule, &loglevel_value);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ if (loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE) {
+ op = ">=";
+ } else {
+ op = "==";
+ }
+
+ if (filter || agent_filter) {
+ char *new_filter;
+
+ err = asprintf(&new_filter,
+ "(%s) && (int_loglevel %s %d)",
+ agent_filter ? agent_filter : filter,
+ op, loglevel_value);
+ if (agent_filter) {
+ free(agent_filter);
+ }
+ agent_filter = new_filter;
+ } else {
+ err = asprintf(&agent_filter, "int_loglevel %s %d", op,
+ loglevel_value);
+ }
+ if (err < 0) {
+ PERROR("asprintf");
+ ret = -1;
+ goto end;
+ }
+ }
+
+ *_agent_filter = agent_filter;
+ agent_filter = NULL;
+
+end:
+ free(agent_filter);
+ return ret;
+}
+
+static enum lttng_error_code lttng_event_rule_tracepoint_populate(
+ struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+ int ret;
+ enum lttng_error_code ret_code;
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_domain_type domain_type;
+ enum lttng_event_rule_status status;
+ const char *filter;
+ struct lttng_bytecode *bytecode = NULL;
+
+ assert(rule);
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+
+ status = lttng_event_rule_tracepoint_get_filter(rule, &filter);
+ if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+ filter = NULL;
+ } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ret_code = LTTNG_ERR_FILTER_INVAL;
+ goto end;
+ }
+
+ if (filter && filter[0] == '\0') {
+ ret_code = LTTNG_ERR_FILTER_INVAL;
+ goto error;
+ }
+
+ status = lttng_event_rule_tracepoint_get_domain_type(
+ rule, &domain_type);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ret_code = LTTNG_ERR_UNK;
+ goto error;
+ }
+
+ switch (domain_type) {
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_PYTHON:
+ {
+ char *agent_filter;
+ ret = generate_agent_filter(rule, &agent_filter);
+ if (ret) {
+ ret_code = LTTNG_ERR_FILTER_INVAL;
+ goto error;
+ }
+ tracepoint->internal_filter.filter = agent_filter;
+ break;
+ }
+ default:
+ {
+ if (filter) {
+ tracepoint->internal_filter.filter = strdup(filter);
+ if (tracepoint->internal_filter.filter == NULL) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ } else {
+ tracepoint->internal_filter.filter = NULL;
+ }
+ break;
+ }
+ }
+
+ if (tracepoint->internal_filter.filter == NULL) {
+ ret_code = LTTNG_OK;
+ goto end;
+ }
+
+ ret = run_as_generate_filter_bytecode(
+ tracepoint->internal_filter.filter, uid, gid,
+ &bytecode);
+ if (ret) {
+ ret_code = LTTNG_ERR_FILTER_INVAL;
+ goto end;
+ }
+
+ tracepoint->internal_filter.bytecode = bytecode;
+ bytecode = NULL;
+ ret_code = LTTNG_OK;
+
+error:
+end:
+ free(bytecode);
+ return ret_code;
+}
+
+static const char *lttng_event_rule_tracepoint_get_internal_filter(
+ const struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ assert(rule);
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ return tracepoint->internal_filter.filter;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_tracepoint_get_internal_filter_bytecode(
+ const struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ assert(rule);
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ return tracepoint->internal_filter.bytecode;
+}
+
+/* TODO: review error handling, the function should be able to
+ * return error information.
+ */
+static struct lttng_event_exclusion *
+lttng_event_rule_tracepoint_generate_exclusions(struct lttng_event_rule *rule)
+{
+ enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
+ struct lttng_event_exclusion *local_exclusions = NULL;
+ struct lttng_event_exclusion *ret_exclusions = NULL;
+ unsigned int nb_exclusions = 0;
+
+ (void) lttng_event_rule_tracepoint_get_domain_type(rule, &domain_type);
+
+ switch (domain_type) {
+ case LTTNG_DOMAIN_KERNEL:
+ case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_PYTHON:
+ /* Not supported */
+ ret_exclusions = NULL;
+ goto end;
+ case LTTNG_DOMAIN_UST:
+ /* Exclusions supported */
+ break;
+ default:
+ assert(0);
+ }
+
+ (void) lttng_event_rule_tracepoint_get_exclusions_count(
+ rule, &nb_exclusions);
+ if (nb_exclusions == 0) {
+ /* Nothing to do */
+ ret_exclusions = NULL;
+ goto end;
+ }
+
+ local_exclusions = zmalloc(sizeof(struct lttng_event_exclusion) +
+ (LTTNG_SYMBOL_NAME_LEN * nb_exclusions));
+ if (!local_exclusions) {
+ ERR("local exclusion allocation");
+ ret_exclusions = NULL;
+ goto end;
+ }
+
+ local_exclusions->count = nb_exclusions;
+ for (unsigned int i = 0; i < nb_exclusions; i++) {
+ /* TODO: check for truncation.
+ * Part of this should be validated on set exclusion
+ */
+ const char *tmp;
+ (void) lttng_event_rule_tracepoint_get_exclusion_at_index(
+ rule, i, &tmp);
+ strncpy(local_exclusions->names[i], tmp, LTTNG_SYMBOL_NAME_LEN);
+ local_exclusions->names[i][LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
+ }
+
+ /* Pass ownership */
+ ret_exclusions = local_exclusions;
+ local_exclusions = NULL;
+end:
+ free(local_exclusions);
+ /* Not supported */
+ return ret_exclusions;
+}
+
+static struct lttng_event *lttng_event_rule_tracepoint_generate_lttng_event(
+ const struct lttng_event_rule *rule)
+{
+ 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;
+ (void) strncpy(local_event->name, tracepoint->pattern,
+ sizeof(local_event->name) - 1);
+ local_event->name[sizeof(local_event->name) - 1] = '\0';
+ 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)
+{
+ struct lttng_event_rule_tracepoint *rule;
+
+ if (domain_type == LTTNG_DOMAIN_NONE) {
+ return NULL;
+ }
+
+ rule = zmalloc(sizeof(struct lttng_event_rule_tracepoint));
+ if (!rule) {
+ return NULL;
+ }
+
+ lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_TRACEPOINT);
+ rule->parent.validate = lttng_event_rule_tracepoint_validate;
+ rule->parent.serialize = lttng_event_rule_tracepoint_serialize;
+ rule->parent.equal = lttng_event_rule_tracepoint_is_equal;
+ rule->parent.destroy = lttng_event_rule_tracepoint_destroy;
+ rule->parent.populate = lttng_event_rule_tracepoint_populate;
+ rule->parent.get_filter =
+ lttng_event_rule_tracepoint_get_internal_filter;
+ rule->parent.get_filter_bytecode =
+ lttng_event_rule_tracepoint_get_internal_filter_bytecode;
+ rule->parent.generate_exclusions =
+ lttng_event_rule_tracepoint_generate_exclusions;
+ rule->parent.generate_lttng_event =
+ lttng_event_rule_tracepoint_generate_lttng_event;
+
+ rule->domain = domain_type;
+ rule->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL;
+
+ return &rule->parent;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_tracepoint_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_event_rule **_event_rule)
+{
+ ssize_t ret, offset = 0;
+ enum lttng_event_rule_status status;
+ enum lttng_domain_type domain_type;
+ enum lttng_loglevel_type loglevel_type;
+ const struct lttng_event_rule_tracepoint_comm *tracepoint_comm;
+ const char *pattern;
+ const char *filter_expression = NULL;
+ const char **exclusions = NULL;
+ const uint32_t *exclusion_len;
+ const char *exclusion;
+ struct lttng_buffer_view current_view;
+ struct lttng_event_rule *rule = NULL;
+
+ if (!_event_rule) {
+ ret = -1;
+ goto end;
+ }
+
+ if (view->size < sizeof(*tracepoint_comm)) {
+ ERR("Failed to initialize from malformed event rule tracepoint: buffer too short to contain header");
+ ret = -1;
+ goto end;
+ }
+
+ current_view = lttng_buffer_view_from_view(
+ view, offset, sizeof(*tracepoint_comm));
+ tracepoint_comm = (typeof(tracepoint_comm)) current_view.data;
+
+ if (!tracepoint_comm) {
+ ret = -1;
+ goto end;
+ }
+
+ if (tracepoint_comm->domain_type <= LTTNG_DOMAIN_NONE ||
+ tracepoint_comm->domain_type > LTTNG_DOMAIN_PYTHON) {
+ /* Invalid domain value. */
+ ERR("Invalid domain type value (%i) found in tracepoint_comm buffer",
+ (int) tracepoint_comm->domain_type);
+ ret = -1;
+ goto end;
+ }
+
+ domain_type = (enum lttng_domain_type) tracepoint_comm->domain_type;
+ rule = lttng_event_rule_tracepoint_create(domain_type);
+ if (!rule) {
+ ERR("Failed to create event rule tracepoint");
+ ret = -1;
+ goto end;
+ }
+
+ loglevel_type = (enum lttng_loglevel_type)
+ tracepoint_comm->loglevel_type;
+ switch (loglevel_type) {
+ case LTTNG_EVENT_LOGLEVEL_ALL:
+ status = lttng_event_rule_tracepoint_set_loglevel_all(rule);
+ break;
+ case LTTNG_EVENT_LOGLEVEL_RANGE:
+ status = lttng_event_rule_tracepoint_set_loglevel_range(
+ rule, (enum lttng_loglevel_type) tracepoint_comm
+ ->loglevel_value);
+ break;
+ case LTTNG_EVENT_LOGLEVEL_SINGLE:
+ status = lttng_event_rule_tracepoint_set_loglevel(
+ rule, (enum lttng_loglevel_type) tracepoint_comm
+ ->loglevel_value);
+ break;
+ default:
+ ERR("Failed to set event rule tracepoint loglevel: unknown loglevel type");
+ ret = -1;
+ goto end;
+ }
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set event rule tracepoint loglevel");
+ }
+
+ /* Skip to payload */
+ offset += current_view.size;
+
+ /* Map the pattern */
+ current_view = lttng_buffer_view_from_view(
+ view, offset, tracepoint_comm->pattern_len);
+ pattern = current_view.data;
+ if (!pattern) {
+ ret = -1;
+ goto end;
+ }
+
+ if (tracepoint_comm->pattern_len == 1 ||
+ pattern[tracepoint_comm->pattern_len - 1] != '\0' ||
+ strlen(pattern) != tracepoint_comm->pattern_len - 1) {
+ /*
+ * Check that the pattern is not NULL, is NULL-terminated, and
+ * does not contain a NULL before the last byte.
+ */
+ ret = -1;
+ goto end;
+ }
+
+ /* Skip after the pattern */
+ offset += tracepoint_comm->pattern_len;
+
+ if (!tracepoint_comm->filter_expression_len) {
+ goto skip_filter_expression;
+ }
+
+ /* Map the filter_expression */
+ current_view = lttng_buffer_view_from_view(
+ view, offset, tracepoint_comm->filter_expression_len);
+ filter_expression = current_view.data;
+ if (!filter_expression) {
+ ret = -1;
+ goto end;
+ }
+
+ if (tracepoint_comm->filter_expression_len == 1 ||
+ filter_expression[tracepoint_comm->filter_expression_len -
+ 1] != '\0' ||
+ strlen(filter_expression) !=
+ tracepoint_comm->filter_expression_len -
+ 1) {
+ /*
+ * Check that the filter expression is not NULL, is
+ * NULL-terminated, and does not contain a NULL before the last
+ * byte.
+ */
+ ret = -1;
+ goto end;
+ }
+
+ /* Skip after the pattern */
+ offset += tracepoint_comm->filter_expression_len;
+
+skip_filter_expression:
+
+ if (!tracepoint_comm->exclusions_count) {
+ goto skip_exclusions;
+ }
+
+ exclusions = zmalloc(sizeof(*exclusions) *
+ tracepoint_comm->exclusions_count);
+ if (!exclusions) {
+ ret = -1;
+ goto end;
+ }
+
+ for (int i = 0; i < tracepoint_comm->exclusions_count; i++) {
+ current_view = lttng_buffer_view_from_view(
+ view, offset, sizeof(*exclusion_len));
+ exclusion_len = (typeof(exclusion_len)) current_view.data;
+ if (!exclusion_len) {
+ ret = -1;
+ goto end;
+ }
+
+ offset += sizeof(*exclusion_len);
+ current_view = lttng_buffer_view_from_view(
+ view, offset, *exclusion_len);
+ exclusion = current_view.data;
+ if (*exclusion_len == 1 ||
+ exclusion[*exclusion_len - 1] != '\0' ||
+ strlen(exclusion) != *exclusion_len - 1) {
+ /*
+ * Check that the exclusion is not NULL, is
+ * NULL-terminated, and does not contain a NULL before
+ * the last byte.
+ */
+ ret = -1;
+ goto end;
+ }
+ exclusions[i] = exclusion;
+ /* Skip to next exclusion */
+ offset += *exclusion_len;
+ }
+
+skip_exclusions:
+ status = lttng_event_rule_tracepoint_set_pattern(rule, pattern);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set event rule tracepoint pattern");
+ ret = -1;
+ goto end;
+ }
+
+ if (filter_expression) {
+ status = lttng_event_rule_tracepoint_set_filter(
+ rule, filter_expression);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set event rule tracepoint pattern");
+ ret = -1;
+ goto end;
+ }
+ }
+
+ if (exclusions) {
+ status = lttng_event_rule_tracepoint_set_exclusions(rule,
+ tracepoint_comm->exclusions_count, exclusions);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ ERR("Failed to set event rule tracepoint exclusions");
+ ret = -1;
+ goto end;
+ }
+ }
+
+ *_event_rule = rule;
+ rule = NULL;
+ ret = offset;
+end:
+ free(exclusions);
+ lttng_event_rule_destroy(rule);
+ return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_pattern(
+ struct lttng_event_rule *rule, const char *pattern)
+{
+ char *pattern_copy = NULL;
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !pattern ||
+ strlen(pattern) == 0) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ pattern_copy = strdup(pattern);
+ if (!pattern_copy) {
+ status = LTTNG_EVENT_RULE_STATUS_ERROR;
+ goto end;
+ }
+
+ if (tracepoint->pattern) {
+ free(tracepoint->pattern);
+ }
+
+ tracepoint->pattern = pattern_copy;
+ pattern_copy = NULL;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_pattern(
+ const struct lttng_event_rule *rule, const char **pattern)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !pattern) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ if (!tracepoint->pattern) {
+ status = LTTNG_EVENT_RULE_STATUS_UNSET;
+ goto end;
+ }
+
+ *pattern = tracepoint->pattern;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_domain_type(
+ const struct lttng_event_rule *rule,
+ enum lttng_domain_type *type)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !type) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ *type = tracepoint->domain;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_filter(
+ struct lttng_event_rule *rule, const char *expression)
+{
+ char *expression_copy = NULL;
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ /* TODO: validate that the passed expression is valid */
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !expression ||
+ strlen(expression) == 0) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ expression_copy = strdup(expression);
+ if (!expression_copy) {
+ status = LTTNG_EVENT_RULE_STATUS_ERROR;
+ goto end;
+ }
+
+ if (tracepoint->filter_expression) {
+ free(tracepoint->filter_expression);
+ }
+
+ tracepoint->filter_expression = expression_copy;
+ expression_copy = NULL;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_filter(
+ const struct lttng_event_rule *rule, const char **expression)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !expression) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ if (!tracepoint->filter_expression) {
+ status = LTTNG_EVENT_RULE_STATUS_UNSET;
+ goto end;
+ }
+
+ *expression = tracepoint->filter_expression;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel(
+ struct lttng_event_rule *rule, int level)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ /*
+ * TODO/QUESTION: do we validate the passed level based on the domain?
+ * What if no domain is set yet? Should we move the domain to the
+ * "create" api call to enforce the domain type?
+ */
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ tracepoint->loglevel.value = level;
+ tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_SINGLE;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel_range(
+ struct lttng_event_rule *rule, int level)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ /*
+ * TODO/QUESTION: do we validate the passed level based on the domain?
+ * What if no domain is set yet? Should we move the domain to the
+ * "create" api call to enforce the domain type?
+ */
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ tracepoint->loglevel.value = level;
+ tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_RANGE;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_loglevel_all(
+ struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule)) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ tracepoint->loglevel.type = LTTNG_EVENT_LOGLEVEL_ALL;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_loglevel_type(
+ const struct lttng_event_rule *rule,
+ enum lttng_loglevel_type *type)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !type) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ *type = tracepoint->loglevel.type;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_loglevel(
+ const struct lttng_event_rule *rule, int *level)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !level) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ if (tracepoint->loglevel.type == LTTNG_EVENT_LOGLEVEL_ALL) {
+ status = LTTNG_EVENT_RULE_STATUS_UNSET;
+ goto end;
+ }
+ *level = tracepoint->loglevel.value;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_set_exclusions(
+ struct lttng_event_rule *rule,
+ unsigned int count,
+ const char **exclusions)
+{
+ char **exclusions_copy = NULL;
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+ enum lttng_domain_type domain_type;
+
+ /* TODO: validate that the passed exclusions are valid? */
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || count == 0 ||
+ !exclusions) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+
+ status = lttng_event_rule_tracepoint_get_domain_type(
+ rule, &domain_type);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+ goto end;
+ }
+
+ switch (domain_type) {
+ case LTTNG_DOMAIN_KERNEL:
+ case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_PYTHON:
+ status = LTTNG_EVENT_RULE_STATUS_UNSUPPORTED;
+ goto end;
+ case LTTNG_DOMAIN_UST:
+ /* Exclusions supported */
+ break;
+ default:
+ assert(0);
+ }
+
+ exclusions_copy = zmalloc(sizeof(char *) * count);
+ if (!exclusions_copy) {
+ status = LTTNG_EVENT_RULE_STATUS_ERROR;
+ goto end;
+ }
+
+ /* Perform the copy locally */
+ for (int i = 0; i < count; i++) {
+ exclusions_copy[i] = strdup(exclusions[i]);
+ if (!exclusions_copy[i]) {
+ status = LTTNG_EVENT_RULE_STATUS_ERROR;
+ goto end;
+ }
+ }
+
+ if (tracepoint->exclusions.count != 0) {
+ for (int i = 0; i < tracepoint->exclusions.count; i++) {
+ free(tracepoint->exclusions.values[i]);
+ }
+ free(tracepoint->exclusions.values);
+ }
+
+ tracepoint->exclusions.values = exclusions_copy;
+ tracepoint->exclusions.count = count;
+ exclusions_copy = NULL;
+end:
+ if (exclusions_copy) {
+ for (int i = 0; i < count; i++) {
+ free(exclusions_copy[i]);
+ }
+ free(exclusions_copy);
+ }
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_exclusions_count(
+ const struct lttng_event_rule *rule, unsigned int *count)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !count) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ *count = tracepoint->exclusions.count;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_tracepoint_get_exclusion_at_index(
+ const struct lttng_event_rule *rule,
+ unsigned int index,
+ const char **exclusion)
+{
+ struct lttng_event_rule_tracepoint *tracepoint;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_TRACEPOINT_EVENT_RULE(rule) || !exclusion) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ tracepoint = container_of(
+ rule, struct lttng_event_rule_tracepoint, parent);
+ if (index >= tracepoint->exclusions.count) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+ *exclusion = tracepoint->exclusions.values[index];
+end:
+ return status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/runas.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/uprobe-internal.h>
+#include <lttng/userspace-probe-internal.h>
+
+#define IS_UPROBE_EVENT_RULE(rule) \
+ (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE)
+
+static void lttng_event_rule_uprobe_destroy(struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_uprobe *uprobe;
+
+ uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent);
+
+ lttng_userspace_probe_location_destroy(uprobe->location);
+ free(uprobe->name);
+ free(uprobe);
+}
+
+static bool lttng_event_rule_uprobe_validate(
+ const struct lttng_event_rule *rule)
+{
+ bool valid = false;
+ struct lttng_event_rule_uprobe *uprobe;
+
+ if (!rule) {
+ goto end;
+ }
+
+ uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent);
+
+ /* Required field */
+ if (!uprobe->name) {
+ ERR("Invalid uprobe event rule: a pattern must be set.");
+ goto end;
+ }
+
+ if (!uprobe->location) {
+ ERR("Invalid uprobe event rule: a location must be set.");
+ goto end;
+ }
+
+ /* TODO should we validate the probe location? */
+
+ valid = true;
+end:
+ return valid;
+}
+
+static int lttng_event_rule_uprobe_serialize(
+ const struct lttng_event_rule *rule,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
+{
+ int ret;
+ size_t name_len, header_offset, size_before_probe;
+ struct lttng_event_rule_uprobe *uprobe;
+ struct lttng_event_rule_uprobe_comm uprobe_comm = {0};
+ struct lttng_event_rule_uprobe_comm *header;
+
+ if (!rule || !IS_UPROBE_EVENT_RULE(rule)) {
+ ret = -1;
+ goto end;
+ }
+
+ header_offset = buf->size;
+
+ DBG("Serializing uprobe event rule");
+ uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent);
+
+ name_len = strlen(uprobe->name) + 1;
+
+ uprobe_comm.name_len = name_len;
+
+ ret = lttng_dynamic_buffer_append(
+ buf, &uprobe_comm, sizeof(uprobe_comm));
+ if (ret) {
+ goto end;
+ }
+ ret = lttng_dynamic_buffer_append(buf, uprobe->name, name_len);
+ if (ret) {
+ goto end;
+ }
+
+ size_before_probe = buf->size;
+
+ /* This serialize return the size taken in the buffer */
+ /* TODO: should all serialize standardise on this? */
+ ret = lttng_userspace_probe_location_serialize(
+ uprobe->location, buf, fd_to_send);
+ if (ret < 0) {
+ goto end;
+ }
+
+ /* Update the header regarding the probe size */
+ header = (struct lttng_event_rule_uprobe_comm *) ((char *) buf->data +
+ header_offset);
+ header->location_len = buf->size - size_before_probe;
+
+ ret = 0;
+
+end:
+ return ret;
+}
+
+static bool lttng_event_rule_uprobe_is_equal(const struct lttng_event_rule *_a,
+ const struct lttng_event_rule *_b)
+{
+ bool is_equal = false;
+ struct lttng_event_rule_uprobe *a, *b;
+
+ a = container_of(_a, struct lttng_event_rule_uprobe, parent);
+ b = container_of(_b, struct lttng_event_rule_uprobe, parent);
+
+ /* uprobe is invalid if this is not true */
+ assert(a->name);
+ assert(b->name);
+ if (strcmp(a->name, b->name)) {
+ goto end;
+ }
+
+ assert(a->location);
+ assert(b->location);
+ is_equal = lttng_userspace_probe_location_is_equal(
+ a->location, b->location);
+end:
+ return is_equal;
+}
+
+static enum lttng_error_code lttng_event_rule_uprobe_populate(
+ struct lttng_event_rule *rule, uid_t uid, gid_t gid)
+{
+ /* Nothing to do */
+ return LTTNG_OK;
+}
+
+static const char *lttng_event_rule_uprobe_get_filter(
+ const struct lttng_event_rule *rule)
+{
+ /* Unsupported */
+ return NULL;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
+{
+ /* Unsupported */
+ return NULL;
+}
+
+static struct lttng_event_exclusion *
+lttng_event_rule_uprobe_generate_exclusions(struct lttng_event_rule *rule)
+{
+ /* Unsupported */
+ return NULL;
+}
+
+struct lttng_event_rule *lttng_event_rule_uprobe_create()
+{
+ struct lttng_event_rule_uprobe *rule;
+
+ rule = zmalloc(sizeof(struct lttng_event_rule_uprobe));
+ if (!rule) {
+ return NULL;
+ }
+
+ lttng_event_rule_init(&rule->parent, LTTNG_EVENT_RULE_TYPE_UPROBE);
+ rule->parent.validate = lttng_event_rule_uprobe_validate;
+ rule->parent.serialize = lttng_event_rule_uprobe_serialize;
+ rule->parent.equal = lttng_event_rule_uprobe_is_equal;
+ rule->parent.destroy = lttng_event_rule_uprobe_destroy;
+ rule->parent.populate = lttng_event_rule_uprobe_populate;
+ rule->parent.get_filter = lttng_event_rule_uprobe_get_filter;
+ rule->parent.get_filter_bytecode =
+ lttng_event_rule_uprobe_get_filter_bytecode;
+ rule->parent.generate_exclusions =
+ lttng_event_rule_uprobe_generate_exclusions;
+ return &rule->parent;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_event_rule_uprobe_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_event_rule **_event_rule)
+{
+ ssize_t ret, offset = 0;
+ const struct lttng_event_rule_uprobe_comm *uprobe_comm;
+ const char *name;
+ struct lttng_buffer_view current_view;
+ struct lttng_event_rule *rule = NULL;
+ struct lttng_userspace_probe_location *location;
+ struct lttng_event_rule_uprobe *uprobe;
+
+ if (!_event_rule) {
+ ret = -1;
+ goto end;
+ }
+
+ if (view->size < sizeof(*uprobe_comm)) {
+ ERR("Failed to initialize from malformed event rule uprobe: buffer too short to contain header");
+ ret = -1;
+ goto end;
+ }
+
+ current_view = lttng_buffer_view_from_view(
+ view, offset, sizeof(*uprobe_comm));
+ uprobe_comm = (typeof(uprobe_comm)) current_view.data;
+
+ if (!uprobe_comm) {
+ ret = -1;
+ goto end;
+ }
+
+ rule = lttng_event_rule_uprobe_create();
+ if (!rule) {
+ ERR("Failed to create event rule uprobe");
+ ret = -1;
+ goto end;
+ }
+
+ /* Skip to payload */
+ offset += current_view.size;
+
+ /* Map the name */
+ current_view = lttng_buffer_view_from_view(
+ view, offset, uprobe_comm->name_len);
+ name = current_view.data;
+ if (!name) {
+ ret = -1;
+ goto end;
+ }
+
+ if (uprobe_comm->name_len == 1 ||
+ name[uprobe_comm->name_len - 1] != '\0' ||
+ strlen(name) != uprobe_comm->name_len - 1) {
+ /*
+ * Check that the name is not NULL, is NULL-terminated, and
+ * does not contain a NULL before the last byte.
+ */
+ ret = -1;
+ goto end;
+ }
+
+ /* Skip after the name */
+ offset += uprobe_comm->name_len;
+
+ /* Map the location */
+ current_view = lttng_buffer_view_from_view(
+ view, offset, uprobe_comm->location_len);
+ ret = lttng_userspace_probe_location_create_from_buffer(
+ ¤t_view, &location);
+ if (ret < 0) {
+ ret = -1;
+ goto end;
+ }
+
+ assert(ret == uprobe_comm->location_len);
+
+ /* Skip after the location */
+ offset += uprobe_comm->location_len;
+
+ uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent);
+ uprobe->location = location;
+
+ (void) lttng_event_rule_uprobe_set_name(rule, name);
+
+ if (!lttng_event_rule_uprobe_validate(rule)) {
+ ret = -1;
+ goto end;
+ }
+
+ *_event_rule = rule;
+ rule = NULL;
+ ret = offset;
+end:
+ lttng_event_rule_destroy(rule);
+ return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_uprobe_set_location(
+ struct lttng_event_rule *rule,
+ const struct lttng_userspace_probe_location *location)
+{
+ struct lttng_userspace_probe_location *location_copy = NULL;
+ struct lttng_event_rule_uprobe *uprobe;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent);
+ location_copy = lttng_userspace_probe_location_copy(location);
+ if (!location_copy) {
+ status = LTTNG_EVENT_RULE_STATUS_ERROR;
+ goto end;
+ }
+
+ if (uprobe->location) {
+ lttng_userspace_probe_location_destroy(uprobe->location);
+ }
+
+ uprobe->location = location_copy;
+ location_copy = NULL;
+end:
+ lttng_userspace_probe_location_destroy(location_copy);
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_uprobe_get_location(
+ const struct lttng_event_rule *rule,
+ const struct lttng_userspace_probe_location **location)
+{
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ *location = lttng_event_rule_uprobe_get_location_no_const(rule);
+ if (!*location) {
+ status = LTTNG_EVENT_RULE_STATUS_UNSET;
+ goto end;
+ }
+
+end:
+ return status;
+}
+
+LTTNG_HIDDEN
+struct lttng_userspace_probe_location *
+lttng_event_rule_uprobe_get_location_no_const(
+ const struct lttng_event_rule *rule)
+{
+ struct lttng_event_rule_uprobe *uprobe;
+ assert(rule);
+ uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent);
+
+ return uprobe->location;
+}
+
+enum lttng_event_rule_status lttng_event_rule_uprobe_set_name(
+ struct lttng_event_rule *rule, const char *name)
+{
+ char *name_copy = NULL;
+ struct lttng_event_rule_uprobe *uprobe;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name ||
+ strlen(name) == 0) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent);
+ name_copy = strdup(name);
+ if (!name_copy) {
+ status = LTTNG_EVENT_RULE_STATUS_ERROR;
+ goto end;
+ }
+
+ if (uprobe->name) {
+ free(uprobe->name);
+ }
+
+ uprobe->name = name_copy;
+ name_copy = NULL;
+end:
+ return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_uprobe_get_name(
+ const struct lttng_event_rule *rule, const char **name)
+{
+ struct lttng_event_rule_uprobe *uprobe;
+ enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+ if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name) {
+ status = LTTNG_EVENT_RULE_STATUS_INVALID;
+ goto end;
+ }
+
+ uprobe = container_of(rule, struct lttng_event_rule_uprobe, parent);
+ if (!uprobe->name) {
+ status = LTTNG_EVENT_RULE_STATUS_UNSET;
+ goto end;
+ }
+
+ *name = uprobe->name;
+end:
+ return status;
+}
LTTNG_HIDDEN
struct bytecode_symbol_iterator *bytecode_symbol_iterator_create(
- struct lttng_filter_bytecode *bytecode)
+ struct lttng_bytecode *bytecode)
{
struct bytecode_symbol_iterator *it = NULL;
*/
LTTNG_HIDDEN
struct bytecode_symbol_iterator *bytecode_symbol_iterator_create(
- struct lttng_filter_bytecode *bytecode);
+ struct lttng_bytecode *bytecode);
/*
* Advance iterator of one element.
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+
+AM_CPPFLAGS += -I$(srcdir) -I$(builddir)
+
+noinst_PROGRAMS = filter-grammar-test
+noinst_LTLIBRARIES = libfilter.la
+noinst_HEADERS = filter-ast.h \
+ filter-symbols.h
+
+BUILT_SOURCES = filter-parser.h
+
+libfilter_la_SOURCES = \
+ filter-parser.y filter-lexer.l \
+ filter-visitor-xml.c \
+ filter-visitor-generate-ir.c \
+ filter-visitor-ir-check-binary-op-nesting.c \
+ filter-visitor-ir-validate-string.c \
+ filter-visitor-ir-validate-globbing.c \
+ filter-visitor-ir-normalize-glob-patterns.c \
+ filter-visitor-generate-bytecode.c \
+ filter-ast.h \
+ filter-ir.h \
+ memstream.h
+libfilter_la_CFLAGS = -include filter-symbols.h $(AM_CFLAGS)
+libfilter_la_LIBADD = $(top_builddir)/src/common/string-utils/libstring-utils.la
+
+AM_YFLAGS = -t -d -v -Wno-yacc
+
+# start with empty files to clean
+CLEANFILES =
+
+if HAVE_BISON
+# we have bison: we can clean the generated parser files
+CLEANFILES += filter-parser.c filter-parser.h filter-parser.output
+else # HAVE_BISON
+# create target used to stop the build if we want to build the parser,
+# but we don't have the necessary tool to do so
+ERR_MSG = "Error: Cannot build target because bison is missing."
+ERR_MSG += "Make sure bison is installed and run the configure script again."
+
+filter-parser.c filter-parser.h: filter-parser.y
+ @echo $(ERR_MSG)
+ @false
+
+all-local: filter-parser.c filter-parser.h
+endif # HAVE_BISON
+
+if HAVE_FLEX
+# we have flex: we can clean the generated lexer files
+CLEANFILES += filter-lexer.c
+else # HAVE_FLEX
+# create target used to stop the build if we want to build the lexer,
+# but we don't have the necessary tool to do so
+ERR_MSG = "Error: Cannot build target because flex is missing."
+ERR_MSG += "Make sure flex is installed and run the configure script again."
+
+filter-lexer.c: filter-lexer.l
+ @echo $(ERR_MSG)
+ @false
+
+all-local: filter-lexer.c
+endif # HAVE_FLEX
+
+filter_grammar_test_SOURCES = filter-grammar-test.c
+filter_grammar_test_LDADD = \
+ libfilter.la \
+ ../bytecode/libbytecode.la
--- /dev/null
+#ifndef _FILTER_AST_H
+#define _FILTER_AST_H
+
+/*
+ * filter-ast.h
+ *
+ * LTTng filter AST
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+/*
+ * Note: filter-ast.h should be included before filter-parser.h.
+ */
+
+#include <urcu/list.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#define printf_debug(fmt, args...) \
+ do { \
+ if (filter_parser_debug) \
+ fprintf(stdout, "[debug] " fmt, ## args); \
+ } while (0)
+
+// the parameter name (of the reentrant 'yyparse' function)
+// data is a pointer to a 'SParserParam' structure
+//#define YYPARSE_PARAM parser_ctx
+
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+extern int filter_parser_debug;
+
+struct filter_node;
+struct filter_parser;
+
+enum node_type {
+ NODE_UNKNOWN = 0,
+ NODE_ROOT,
+
+ NODE_EXPRESSION,
+ NODE_OP,
+ NODE_UNARY_OP,
+
+ NR_NODE_TYPES,
+};
+
+enum op_type {
+ AST_OP_UNKNOWN = 0,
+ AST_OP_MUL,
+ AST_OP_DIV,
+ AST_OP_MOD,
+ AST_OP_PLUS,
+ AST_OP_MINUS,
+ AST_OP_BIT_RSHIFT,
+ AST_OP_BIT_LSHIFT,
+ AST_OP_AND,
+ AST_OP_OR,
+ AST_OP_BIT_AND,
+ AST_OP_BIT_OR,
+ AST_OP_BIT_XOR,
+
+ AST_OP_EQ,
+ AST_OP_NE,
+ AST_OP_GT,
+ AST_OP_LT,
+ AST_OP_GE,
+ AST_OP_LE,
+};
+
+enum unary_op_type {
+ AST_UNARY_UNKNOWN = 0,
+ AST_UNARY_PLUS,
+ AST_UNARY_MINUS,
+ AST_UNARY_NOT,
+ AST_UNARY_BIT_NOT,
+};
+
+enum ast_link_type {
+ AST_LINK_UNKNOWN = 0,
+ AST_LINK_DOT,
+ AST_LINK_RARROW,
+ AST_LINK_BRACKET,
+};
+
+struct filter_node {
+ /*
+ * Parent node is only set on demand by specific visitor.
+ */
+ struct filter_node *parent;
+ struct cds_list_head gc;
+
+ enum node_type type;
+ union {
+ struct {
+ } unknown;
+ struct {
+ struct filter_node *child;
+ } root;
+ struct {
+ enum {
+ AST_EXP_UNKNOWN = 0,
+ AST_EXP_STRING,
+ AST_EXP_CONSTANT,
+ AST_EXP_FLOAT_CONSTANT,
+ AST_EXP_IDENTIFIER,
+ AST_EXP_GLOBAL_IDENTIFIER,
+ AST_EXP_NESTED,
+ } type;
+ enum ast_link_type post_op; /* reverse */
+ enum ast_link_type pre_op; /* forward */
+ union {
+ const char *string;
+ uint64_t constant;
+ double float_constant;
+ const char *identifier;
+ /*
+ * child can be nested.
+ */
+ struct filter_node *child;
+ } u;
+ /* prev: backward dot/arrow chain (postfix expression) */
+ struct filter_node *prev;
+ /* next: forward dot/arrow chain, generated by a visitor. */
+ struct filter_node *next;
+ /* next_bracket: linked bracket chain (prefix expression) */
+ struct filter_node *next_bracket;
+ } expression;
+ struct {
+ enum op_type type;
+ struct filter_node *lchild;
+ struct filter_node *rchild;
+ } op;
+ struct {
+ enum unary_op_type type;
+ struct filter_node *child;
+ } unary_op;
+ } u;
+};
+
+struct filter_ast {
+ struct filter_node root;
+ struct cds_list_head allocated_nodes;
+};
+
+const char *node_type(struct filter_node *node);
+
+struct ir_op;
+
+struct filter_parser_ctx {
+ yyscan_t scanner;
+ struct filter_ast *ast;
+ struct cds_list_head allocated_strings;
+ struct ir_op *ir_root;
+ struct lttng_bytecode_alloc *bytecode;
+ struct lttng_bytecode_alloc *bytecode_reloc;
+};
+
+struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input);
+void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx);
+int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx);
+int filter_parser_ctx_create_from_filter_expression(
+ const char *filter_expression, struct filter_parser_ctx **ctxp);
+
+static inline
+struct filter_ast *filter_parser_get_ast(struct filter_parser_ctx *parser_ctx)
+{
+ return parser_ctx->ast;
+}
+
+int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream,
+ int indent);
+int filter_visitor_ir_generate(struct filter_parser_ctx *ctx);
+void filter_ir_free(struct filter_parser_ctx *ctx);
+int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx);
+void filter_bytecode_free(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx);
+int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx);
+
+#endif /* _FILTER_AST_H */
--- /dev/null
+/*
+ * filter-grammar-test.c
+ *
+ * LTTng filter grammar test
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "common/bytecode/bytecode.h"
+#include "filter-ast.h"
+#include "filter-parser.h"
+
+int main(int argc, char **argv)
+{
+ struct filter_parser_ctx *ctx;
+ int ret;
+ int print_xml = 0, generate_ir = 0, generate_bytecode = 0,
+ print_bytecode = 0;
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-p") == 0)
+ print_xml = 1;
+ else if (strcmp(argv[i], "-i") == 0)
+ generate_ir = 1;
+ else if (strcmp(argv[i], "-b") == 0)
+ generate_bytecode = 1;
+ else if (strcmp(argv[i], "-d") == 0)
+ filter_parser_debug = 1;
+ else if (strcmp(argv[i], "-B") == 0)
+ print_bytecode = 1;
+ }
+
+ /*
+ * Force generate the bytecode if the user asks to print the bytecode
+ * (can't print it without generating it first).
+ */
+ if (print_bytecode) {
+ generate_bytecode = 1;
+ }
+
+ /*
+ * Force generate the IR if the user asks to generate the bytecode
+ * (the bytecode is generated by visiting the IR).
+ */
+ if (generate_bytecode) {
+ generate_ir = 1;
+ }
+
+ ctx = filter_parser_ctx_alloc(stdin);
+ if (!ctx) {
+ fprintf(stderr, "Error allocating parser\n");
+ goto alloc_error;
+ }
+ ret = filter_parser_ctx_append_ast(ctx);
+ if (ret) {
+ fprintf(stderr, "Parse error\n");
+ goto parse_error;
+ }
+ if (print_xml) {
+ ret = filter_visitor_print_xml(ctx, stdout, 0);
+ if (ret) {
+ fflush(stdout);
+ fprintf(stderr, "XML print error\n");
+ goto parse_error;
+ }
+ }
+ if (generate_ir) {
+ printf("Generating IR... ");
+ fflush(stdout);
+ ret = filter_visitor_ir_generate(ctx);
+ if (ret) {
+ fprintf(stderr, "Generate IR error\n");
+ goto parse_error;
+ }
+ printf("done\n");
+
+ printf("Validating IR... ");
+ fflush(stdout);
+ ret = filter_visitor_ir_check_binary_op_nesting(ctx);
+ if (ret) {
+ goto parse_error;
+ }
+ printf("done\n");
+ }
+ if (generate_bytecode) {
+ printf("Generating bytecode... ");
+ fflush(stdout);
+ ret = filter_visitor_bytecode_generate(ctx);
+ if (ret) {
+ fprintf(stderr, "Generate bytecode error\n");
+ goto parse_error;
+ }
+ printf("done\n");
+ printf("Size of bytecode generated: %u bytes.\n",
+ bytecode_get_len(&ctx->bytecode->b));
+ }
+
+ if (print_bytecode) {
+ unsigned int bytecode_len, len, i;
+
+ len = bytecode_get_len(&ctx->bytecode->b);
+ bytecode_len = ctx->bytecode->b.reloc_table_offset;
+ printf("Bytecode:\n");
+ for (i = 0; i < bytecode_len; i++) {
+ printf("0x%X ",
+ ((uint8_t *) ctx->bytecode->b.data)[i]);
+ }
+ printf("\n");
+ printf("Reloc table:\n");
+ for (i = bytecode_len; i < len;) {
+ printf("{ 0x%X, ",
+ *(uint16_t *) &ctx->bytecode->b.data[i]);
+ i += sizeof(uint16_t);
+ printf("%s } ", &((char *) ctx->bytecode->b.data)[i]);
+ i += strlen(&((char *) ctx->bytecode->b.data)[i]) + 1;
+ }
+ printf("\n");
+ }
+
+ filter_bytecode_free(ctx);
+ filter_ir_free(ctx);
+ filter_parser_ctx_free(ctx);
+ return 0;
+
+parse_error:
+ filter_bytecode_free(ctx);
+ filter_ir_free(ctx);
+ filter_parser_ctx_free(ctx);
+alloc_error:
+ exit(EXIT_FAILURE);
+}
--- /dev/null
+#ifndef _FILTER_IR_H
+#define _FILTER_IR_H
+
+/*
+ * filter-ir.h
+ *
+ * LTTng filter ir
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "filter-ast.h"
+
+enum ir_op_signedness {
+ IR_SIGN_UNKNOWN = 0,
+ IR_SIGNED,
+ IR_UNSIGNED,
+ IR_SIGN_DYN, /* signedness determined dynamically */
+};
+
+enum ir_data_type {
+ IR_DATA_UNKNOWN = 0,
+ IR_DATA_STRING,
+ IR_DATA_NUMERIC, /* numeric and boolean */
+ IR_DATA_FLOAT,
+ IR_DATA_FIELD_REF,
+ IR_DATA_GET_CONTEXT_REF,
+ IR_DATA_EXPRESSION,
+};
+
+static inline
+const char *ir_data_type_str(enum ir_data_type type)
+{
+ switch (type) {
+ case IR_DATA_UNKNOWN:
+ return "IR_DATA_UNKNOWN";
+ case IR_DATA_STRING:
+ return "IR_DATA_STRING";
+ case IR_DATA_NUMERIC:
+ return "IR_DATA_NUMERIC";
+ case IR_DATA_FLOAT:
+ return "IR_DATA_FLOAT";
+ case IR_DATA_FIELD_REF:
+ return "IR_DATA_FIELD_REF";
+ case IR_DATA_GET_CONTEXT_REF:
+ return "IR_DATA_GET_CONTEXT_REF";
+ case IR_DATA_EXPRESSION:
+ return "IR_DATA_EXPRESSION";
+ default:
+ abort();
+ }
+}
+
+enum ir_op_type {
+ IR_OP_UNKNOWN = 0,
+ IR_OP_ROOT,
+ IR_OP_LOAD,
+ IR_OP_UNARY,
+ IR_OP_BINARY,
+ IR_OP_LOGICAL,
+};
+
+static inline
+const char *ir_op_type_str(enum ir_op_type type)
+{
+ switch (type) {
+ case IR_OP_UNKNOWN:
+ return "IR_OP_UNKNOWN";
+ case IR_OP_ROOT:
+ return "IR_OP_ROOT";
+ case IR_OP_LOAD:
+ return "IR_OP_LOAD";
+ case IR_OP_UNARY:
+ return "IR_OP_UNARY";
+ case IR_OP_BINARY:
+ return "IR_OP_BINARY";
+ case IR_OP_LOGICAL:
+ return "IR_OP_LOGICAL";
+ default:
+ abort();
+ }
+}
+
+/* left or right child */
+enum ir_side {
+ IR_SIDE_UNKNOWN = 0,
+ IR_LEFT,
+ IR_RIGHT,
+};
+
+enum ir_load_string_type {
+ /* Plain, no globbing at all: `hello world`. */
+ IR_LOAD_STRING_TYPE_PLAIN = 0,
+
+ /* Star at the end only: `hello *`. */
+ IR_LOAD_STRING_TYPE_GLOB_STAR_END,
+
+ /* At least one star, anywhere, but not at the end only: `he*wor*`. */
+ IR_LOAD_STRING_TYPE_GLOB_STAR,
+};
+
+struct ir_op_root {
+ struct ir_op *child;
+};
+
+enum ir_load_expression_type {
+ IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT,
+ IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT,
+ IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT,
+ IR_LOAD_EXPRESSION_GET_SYMBOL,
+ IR_LOAD_EXPRESSION_GET_INDEX,
+ IR_LOAD_EXPRESSION_LOAD_FIELD,
+};
+
+static inline
+const char *ir_load_expression_type_str(enum ir_load_expression_type type)
+{
+ switch (type) {
+ case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+ return "IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT";
+ case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+ return "IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT";
+ case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+ return "IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT";
+ case IR_LOAD_EXPRESSION_GET_SYMBOL:
+ return "IR_LOAD_EXPRESSION_GET_SYMBOL";
+ case IR_LOAD_EXPRESSION_GET_INDEX:
+ return "IR_LOAD_EXPRESSION_GET_INDEX";
+ case IR_LOAD_EXPRESSION_LOAD_FIELD:
+ return "IR_LOAD_EXPRESSION_LOAD_FIELD";
+ default:
+ abort();
+ }
+}
+
+struct ir_load_expression_op {
+ struct ir_load_expression_op *next;
+ enum ir_load_expression_type type;
+ union {
+ char *symbol;
+ uint64_t index;
+ } u;
+};
+
+struct ir_load_expression {
+ struct ir_load_expression_op *child;
+};
+
+struct ir_op_load {
+ union {
+ struct {
+ enum ir_load_string_type type;
+ char *value;
+ } string;
+ int64_t num;
+ double flt;
+ char *ref;
+ struct ir_load_expression *expression;
+ } u;
+};
+
+struct ir_op_unary {
+ enum unary_op_type type;
+ struct ir_op *child;
+};
+
+struct ir_op_binary {
+ enum op_type type;
+ struct ir_op *left;
+ struct ir_op *right;
+};
+
+struct ir_op_logical {
+ enum op_type type;
+ struct ir_op *left;
+ struct ir_op *right;
+};
+
+struct ir_op {
+ /* common to all ops */
+ enum ir_op_type op;
+ enum ir_data_type data_type;
+ enum ir_op_signedness signedness;
+ enum ir_side side;
+
+ union {
+ struct ir_op_root root;
+ struct ir_op_load load;
+ struct ir_op_unary unary;
+ struct ir_op_binary binary;
+ struct ir_op_logical logical;
+ } u;
+};
+
+#endif /* _FILTER_IR_H */
--- /dev/null
+%{
+/*
+ * filter-lexer.l
+ *
+ * LTTng filter lexer
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+
+static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+ __attribute__((unused));
+static int input (yyscan_t yyscanner) __attribute__((unused));
+
+%}
+
+%x comment_ml comment_sl string_lit char_const
+%option reentrant yylineno noyywrap bison-bridge
+%option extra-type="struct filter_parser_ctx *"
+ /* bison-locations */
+
+D [0-9]
+L [a-zA-Z_]
+H [a-fA-F0-9]
+E ([Ee][+-]?{D}+)
+P ([Pp][+-]?{D}+)
+FS (f|F|l|L)
+IS ((u|U)|(u|U)?(l|L|ll|LL)|(l|L|ll|LL)(u|U))
+
+INTEGER_SUFFIX [ \n\t]*(U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu)
+DIGIT [0-9]
+NONDIGIT [a-zA-Z_]
+HEXDIGIT [0-9A-Fa-f]
+OCTALDIGIT [0-7]
+UCHARLOWERCASE \\u{HEXDIGIT}{4}
+UCHARUPPERCASE \\U{HEXDIGIT}{8}
+ID_EXTRA_CHAR (":")
+ID_NONDIGIT {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE}|{ID_EXTRA_CHAR}
+IDENTIFIER {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})*
+ESCSEQ \\(\'|\"|\?|\\|a|b|f|n|r|t|v|{OCTALDIGIT}{1,3}|u{HEXDIGIT}{4}|U{HEXDIGIT}{8}|x{HEXDIGIT}+)
+%%
+
+ /*
+ * Using start conditions to deal with comments
+ * and strings.
+ */
+
+"/*" BEGIN(comment_ml);
+<comment_ml>[^*\n]* /* eat anything that's not a '*' */
+<comment_ml>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
+<comment_ml>\n ++yylineno;
+<comment_ml>"*"+"/" BEGIN(INITIAL);
+
+"//" BEGIN(comment_sl);
+<comment_sl>[^\n]*\n ++yylineno; BEGIN(INITIAL);
+
+L\' BEGIN(char_const); return CHARACTER_CONSTANT_START;
+\' BEGIN(char_const); return CHARACTER_CONSTANT_START;
+<char_const>\' BEGIN(INITIAL); return SQUOTE;
+
+L\" BEGIN(string_lit); return STRING_LITERAL_START;
+\" BEGIN(string_lit); return STRING_LITERAL_START;
+<string_lit>\" BEGIN(INITIAL); return DQUOTE;
+
+<char_const,string_lit>ESCSEQ return ESCSEQ;
+<char_const,string_lit>\n ; /* ignore */
+<char_const,string_lit>. setstring(yyextra, yylval, yytext); return CHAR_STRING_TOKEN;
+
+
+0[xX]{H}+{IS}? setstring(yyextra, yylval, yytext); return HEXADECIMAL_CONSTANT;
+0[0-7]*{IS}? setstring(yyextra, yylval, yytext); return OCTAL_CONSTANT;
+[1-9]{D}*{IS}? setstring(yyextra, yylval, yytext); return DECIMAL_CONSTANT;
+
+{D}+{E}{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+{D}*"."{D}+{E}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+{D}+"."{D}*{E}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+0[xX]{H}+{P}{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+0[xX]{H}*"."{H}+{P}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+0[xX]{H}+"."{H}*{P}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
+
+"[" return LSBRAC;
+"]" return RSBRAC;
+"(" return LPAREN;
+")" return RPAREN;
+"{" return LBRAC;
+"}" return RBRAC;
+"->" return RARROW;
+
+"*" return STAR;
+"+" return PLUS;
+"-" return MINUS;
+
+"%" return MOD_OP;
+"/" return DIV_OP;
+">>" return RIGHT_OP;
+"<<" return LEFT_OP;
+
+"==" return EQ_OP;
+"!=" return NE_OP;
+"<=" return LE_OP;
+">=" return GE_OP;
+"<" return LT_OP;
+">" return GT_OP;
+"&&" return AND_OP;
+"||" return OR_OP;
+"!" return NOT_OP;
+
+":=" return ASSIGN;
+":" return COLON;
+";" return SEMICOLON;
+"..." return DOTDOTDOT;
+"." return DOT;
+"=" return EQUAL;
+"," return COMMA;
+"^" return XOR_BIN;
+"&" return AND_BIN;
+"|" return OR_BIN;
+"~" return NOT_BIN;
+"$"{IDENTIFIER} printf_debug("<GLOBAL_IDENTIFIER %s>\n", yytext); setstring(yyextra, yylval, yytext); return GLOBAL_IDENTIFIER;
+{IDENTIFIER} printf_debug("<IDENTIFIER %s>\n", yytext); setstring(yyextra, yylval, yytext); return IDENTIFIER;
+[ \t\n]+ ; /* ignore */
+. return ERROR;
+%%
--- /dev/null
+%{
+/*
+ * filter-parser.y
+ *
+ * LTTng filter expression parser
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * Grammar inspired from http://www.quut.com/c/ANSI-C-grammar-y.html
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "common/bytecode/bytecode.h"
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "memstream.h"
+
+#include <common/macros.h>
+
+#define WIDTH_u64_SCANF_IS_A_BROKEN_API "20"
+#define WIDTH_o64_SCANF_IS_A_BROKEN_API "22"
+#define WIDTH_x64_SCANF_IS_A_BROKEN_API "17"
+#define WIDTH_lg_SCANF_IS_A_BROKEN_API "4096" /* Hugely optimistic approximation */
+
+#ifdef DEBUG
+static const int print_xml = 1;
+#define dbg_printf(fmt, args...) \
+ printf("[debug filter_parser] " fmt, ## args)
+#else
+static const int print_xml = 0;
+#define dbg_printf(fmt, args...) \
+do { \
+ /* do nothing but check printf format */ \
+ if (0) \
+ printf("[debug filter_parser] " fmt, ## args); \
+} while (0)
+#endif
+
+LTTNG_HIDDEN
+int yydebug;
+LTTNG_HIDDEN
+int filter_parser_debug = 0;
+
+LTTNG_HIDDEN
+int yyparse(struct filter_parser_ctx *parser_ctx, yyscan_t scanner);
+LTTNG_HIDDEN
+int yylex(union YYSTYPE *yyval, yyscan_t scanner);
+LTTNG_HIDDEN
+int yylex_init_extra(struct filter_parser_ctx *parser_ctx, yyscan_t * ptr_yy_globals);
+LTTNG_HIDDEN
+int yylex_destroy(yyscan_t yyparser_ctx);
+LTTNG_HIDDEN
+void yyrestart(FILE * in_str, yyscan_t parser_ctx);
+
+struct gc_string {
+ struct cds_list_head gc;
+ size_t alloclen;
+ char s[];
+};
+
+static const char *node_type_to_str[] = {
+ [ NODE_UNKNOWN ] = "NODE_UNKNOWN",
+ [ NODE_ROOT ] = "NODE_ROOT",
+ [ NODE_EXPRESSION ] = "NODE_EXPRESSION",
+ [ NODE_OP ] = "NODE_OP",
+ [ NODE_UNARY_OP ] = "NODE_UNARY_OP",
+};
+
+LTTNG_HIDDEN
+const char *node_type(struct filter_node *node)
+{
+ if (node->type < NR_NODE_TYPES)
+ return node_type_to_str[node->type];
+ else
+ return NULL;
+}
+
+static struct gc_string *gc_string_alloc(struct filter_parser_ctx *parser_ctx,
+ size_t len)
+{
+ struct gc_string *gstr;
+ size_t alloclen;
+
+ /* TODO: could be faster with find first bit or glib Gstring */
+ /* sizeof long to account for malloc header (int or long ?) */
+ for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + len;
+ alloclen *= 2);
+
+ gstr = zmalloc(alloclen);
+ if (!gstr) {
+ goto end;
+ }
+ cds_list_add(&gstr->gc, &parser_ctx->allocated_strings);
+ gstr->alloclen = alloclen;
+end:
+ return gstr;
+}
+
+/*
+ * note: never use gc_string_append on a string that has external references.
+ * gsrc will be garbage collected immediately, and gstr might be.
+ * Should only be used to append characters to a string literal or constant.
+ */
+static
+struct gc_string *gc_string_append(struct filter_parser_ctx *parser_ctx,
+ struct gc_string *gstr,
+ struct gc_string *gsrc)
+{
+ size_t newlen = strlen(gsrc->s) + strlen(gstr->s) + 1;
+ size_t alloclen;
+
+ /* TODO: could be faster with find first bit or glib Gstring */
+ /* sizeof long to account for malloc header (int or long ?) */
+ for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + newlen;
+ alloclen *= 2);
+
+ if (alloclen > gstr->alloclen) {
+ struct gc_string *newgstr;
+
+ newgstr = gc_string_alloc(parser_ctx, newlen);
+ strcpy(newgstr->s, gstr->s);
+ strcat(newgstr->s, gsrc->s);
+ cds_list_del(&gstr->gc);
+ free(gstr);
+ gstr = newgstr;
+ } else {
+ strcat(gstr->s, gsrc->s);
+ }
+ cds_list_del(&gsrc->gc);
+ free(gsrc);
+ return gstr;
+}
+
+LTTNG_HIDDEN
+void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src)
+{
+ lvalp->gs = gc_string_alloc(parser_ctx, strlen(src) + 1);
+ strcpy(lvalp->gs->s, src);
+}
+
+static struct filter_node *make_node(struct filter_parser_ctx *scanner,
+ enum node_type type)
+{
+ struct filter_ast *ast = filter_parser_get_ast(scanner);
+ struct filter_node *node;
+
+ node = zmalloc(sizeof(*node));
+ if (!node)
+ return NULL;
+ memset(node, 0, sizeof(*node));
+ node->type = type;
+ cds_list_add(&node->gc, &ast->allocated_nodes);
+
+ switch (type) {
+ case NODE_ROOT:
+ fprintf(stderr, "[error] %s: trying to create root node\n", __func__);
+ break;
+
+ case NODE_EXPRESSION:
+ break;
+ case NODE_OP:
+ break;
+ case NODE_UNARY_OP:
+ break;
+
+ case NODE_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown node type %d\n", __func__,
+ (int) type);
+ break;
+ }
+
+ return node;
+}
+
+static struct filter_node *make_op_node(struct filter_parser_ctx *scanner,
+ enum op_type type,
+ struct filter_node *lchild,
+ struct filter_node *rchild)
+{
+ struct filter_ast *ast = filter_parser_get_ast(scanner);
+ struct filter_node *node;
+
+ node = zmalloc(sizeof(*node));
+ if (!node)
+ return NULL;
+ memset(node, 0, sizeof(*node));
+ node->type = NODE_OP;
+ cds_list_add(&node->gc, &ast->allocated_nodes);
+ node->u.op.type = type;
+ node->u.op.lchild = lchild;
+ node->u.op.rchild = rchild;
+ return node;
+}
+
+static
+void yyerror(struct filter_parser_ctx *parser_ctx, yyscan_t scanner, const char *str)
+{
+ fprintf(stderr, "error %s\n", str);
+}
+
+#define parse_error(parser_ctx, str) \
+do { \
+ yyerror(parser_ctx, parser_ctx->scanner, YY_("parse error: " str "\n")); \
+ YYERROR; \
+} while (0)
+
+static void free_strings(struct cds_list_head *list)
+{
+ struct gc_string *gstr, *tmp;
+
+ cds_list_for_each_entry_safe(gstr, tmp, list, gc)
+ free(gstr);
+}
+
+static struct filter_ast *filter_ast_alloc(void)
+{
+ struct filter_ast *ast;
+
+ ast = zmalloc(sizeof(*ast));
+ if (!ast)
+ return NULL;
+ memset(ast, 0, sizeof(*ast));
+ CDS_INIT_LIST_HEAD(&ast->allocated_nodes);
+ ast->root.type = NODE_ROOT;
+ return ast;
+}
+
+static void filter_ast_free(struct filter_ast *ast)
+{
+ struct filter_node *node, *tmp;
+
+ cds_list_for_each_entry_safe(node, tmp, &ast->allocated_nodes, gc)
+ free(node);
+ free(ast);
+}
+
+LTTNG_HIDDEN
+int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx)
+{
+ return yyparse(parser_ctx, parser_ctx->scanner);
+}
+
+LTTNG_HIDDEN
+struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input)
+{
+ struct filter_parser_ctx *parser_ctx;
+ int ret;
+
+ yydebug = filter_parser_debug;
+
+ parser_ctx = zmalloc(sizeof(*parser_ctx));
+ if (!parser_ctx)
+ return NULL;
+ memset(parser_ctx, 0, sizeof(*parser_ctx));
+
+ ret = yylex_init_extra(parser_ctx, &parser_ctx->scanner);
+ if (ret) {
+ fprintf(stderr, "yylex_init error\n");
+ goto cleanup_parser_ctx;
+ }
+ /* Start processing new stream */
+ yyrestart(input, parser_ctx->scanner);
+
+ parser_ctx->ast = filter_ast_alloc();
+ if (!parser_ctx->ast)
+ goto cleanup_lexer;
+ CDS_INIT_LIST_HEAD(&parser_ctx->allocated_strings);
+
+ if (yydebug)
+ fprintf(stdout, "parser_ctx input is a%s.\n",
+ isatty(fileno(input)) ? "n interactive tty" :
+ " noninteractive file");
+
+ return parser_ctx;
+
+cleanup_lexer:
+ ret = yylex_destroy(parser_ctx->scanner);
+ if (!ret)
+ fprintf(stderr, "yylex_destroy error\n");
+cleanup_parser_ctx:
+ free(parser_ctx);
+ return NULL;
+}
+
+LTTNG_HIDDEN
+void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx)
+{
+ int ret;
+
+ free_strings(&parser_ctx->allocated_strings);
+ filter_ast_free(parser_ctx->ast);
+ ret = yylex_destroy(parser_ctx->scanner);
+ if (ret)
+ fprintf(stderr, "yylex_destroy error\n");
+ free(parser_ctx);
+}
+
+LTTNG_HIDDEN
+int filter_parser_ctx_create_from_filter_expression(
+ const char *filter_expression, struct filter_parser_ctx **ctxp)
+{
+ int ret;
+ struct filter_parser_ctx *ctx = NULL;
+ FILE *fmem = NULL;
+
+ assert(filter_expression);
+ assert(ctxp);
+
+ /*
+ * Casting const to non-const, as the underlying function will use it in
+ * read-only mode.
+ */
+ fmem = lttng_fmemopen((void *) filter_expression,
+ strlen(filter_expression), "r");
+ if (!fmem) {
+ fprintf(stderr, "Error opening memory as stream\n");
+ ret = -LTTNG_ERR_FILTER_NOMEM;
+ goto error;
+ }
+ ctx = filter_parser_ctx_alloc(fmem);
+ if (!ctx) {
+ fprintf(stderr, "Error allocating parser\n");
+ ret = -LTTNG_ERR_FILTER_NOMEM;
+ goto filter_alloc_error;
+ }
+ ret = filter_parser_ctx_append_ast(ctx);
+ if (ret) {
+ fprintf(stderr, "Parse error\n");
+ ret = -LTTNG_ERR_FILTER_INVAL;
+ goto parse_error;
+ }
+ if (print_xml) {
+ ret = filter_visitor_print_xml(ctx, stdout, 0);
+ if (ret) {
+ fflush(stdout);
+ fprintf(stderr, "XML print error\n");
+ ret = -LTTNG_ERR_FILTER_INVAL;
+ goto parse_error;
+ }
+ }
+
+ dbg_printf("Generating IR... ");
+ fflush(stdout);
+ ret = filter_visitor_ir_generate(ctx);
+ if (ret) {
+ fprintf(stderr, "Generate IR error\n");
+ ret = -LTTNG_ERR_FILTER_INVAL;
+ goto parse_error;
+ }
+ dbg_printf("done\n");
+
+ dbg_printf("Validating IR... ");
+ fflush(stdout);
+ ret = filter_visitor_ir_check_binary_op_nesting(ctx);
+ if (ret) {
+ ret = -LTTNG_ERR_FILTER_INVAL;
+ goto parse_error;
+ }
+
+ /* Normalize globbing patterns in the expression. */
+ ret = filter_visitor_ir_normalize_glob_patterns(ctx);
+ if (ret) {
+ ret = -LTTNG_ERR_FILTER_INVAL;
+ goto parse_error;
+ }
+
+ /* Validate strings used as literals in the expression. */
+ ret = filter_visitor_ir_validate_string(ctx);
+ if (ret) {
+ ret = -LTTNG_ERR_FILTER_INVAL;
+ goto parse_error;
+ }
+
+ /* Validate globbing patterns in the expression. */
+ ret = filter_visitor_ir_validate_globbing(ctx);
+ if (ret) {
+ ret = -LTTNG_ERR_FILTER_INVAL;
+ goto parse_error;
+ }
+
+ dbg_printf("done\n");
+
+ dbg_printf("Generating bytecode... ");
+ fflush(stdout);
+ ret = filter_visitor_bytecode_generate(ctx);
+ if (ret) {
+ fprintf(stderr, "Generate bytecode error\n");
+ ret = -LTTNG_ERR_FILTER_INVAL;
+ goto parse_error;
+ }
+ dbg_printf("done\n");
+ dbg_printf("Size of bytecode generated: %u bytes.\n",
+ bytecode_get_len(&ctx->bytecode->b));
+
+ /* No need to keep the memory stream. */
+ if (fclose(fmem) != 0) {
+ fprintf(stderr, "fclose (%d) \n", errno);
+ ret = -LTTNG_ERR_FILTER_INVAL;
+ }
+
+ *ctxp = ctx;
+ return 0;
+
+parse_error:
+ filter_ir_free(ctx);
+ filter_parser_ctx_free(ctx);
+filter_alloc_error:
+ if (fclose(fmem) != 0) {
+ fprintf(stderr, "fclose (%d) \n", errno);
+ }
+error:
+ return ret;
+}
+
+%}
+
+%code provides
+{
+#include "common/macros.h"
+
+LTTNG_HIDDEN
+void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src);
+}
+
+%define api.pure
+ /* %locations */
+%parse-param {struct filter_parser_ctx *parser_ctx}
+%parse-param {yyscan_t scanner}
+%lex-param {yyscan_t scanner}
+%start translation_unit
+%token CHARACTER_CONSTANT_START SQUOTE STRING_LITERAL_START DQUOTE
+%token ESCSEQ CHAR_STRING_TOKEN
+%token DECIMAL_CONSTANT OCTAL_CONSTANT HEXADECIMAL_CONSTANT FLOAT_CONSTANT
+%token LSBRAC RSBRAC LPAREN RPAREN LBRAC RBRAC RARROW
+%token STAR PLUS MINUS
+%token MOD_OP DIV_OP RIGHT_OP LEFT_OP
+%token EQ_OP NE_OP LE_OP GE_OP LT_OP GT_OP AND_OP OR_OP NOT_OP
+%token ASSIGN COLON SEMICOLON DOTDOTDOT DOT EQUAL COMMA
+%token XOR_BIN AND_BIN OR_BIN NOT_BIN
+
+%token <gs> IDENTIFIER GLOBAL_IDENTIFIER
+%token ERROR
+%union
+{
+ long long ll;
+ char c;
+ struct gc_string *gs;
+ struct filter_node *n;
+}
+
+%type <gs> s_char s_char_sequence c_char c_char_sequence
+
+%type <n> primary_expression
+%type <n> prefix_expression
+%type <n> prefix_expression_rec
+%type <n> postfix_expression
+%type <n> unary_expression
+%type <n> unary_operator
+%type <n> multiplicative_expression
+%type <n> additive_expression
+%type <n> shift_expression
+%type <n> relational_expression
+%type <n> equality_expression
+%type <n> and_expression
+%type <n> exclusive_or_expression
+%type <n> inclusive_or_expression
+%type <n> logical_and_expression
+%type <n> logical_or_expression
+%type <n> expression
+%type <n> identifiers
+
+%%
+
+
+/* 1.5 Constants */
+
+c_char_sequence:
+ c_char
+ { $$ = $1; }
+ | c_char_sequence c_char
+ { $$ = gc_string_append(parser_ctx, $1, $2); }
+ ;
+
+c_char:
+ CHAR_STRING_TOKEN
+ { $$ = yylval.gs; }
+ | ESCSEQ
+ {
+ parse_error(parser_ctx, "escape sequences not supported yet");
+ }
+ ;
+
+/* 1.6 String literals */
+
+s_char_sequence:
+ s_char
+ { $$ = $1; }
+ | s_char_sequence s_char
+ { $$ = gc_string_append(parser_ctx, $1, $2); }
+ ;
+
+s_char:
+ CHAR_STRING_TOKEN
+ { $$ = yylval.gs; }
+ | ESCSEQ
+ {
+ parse_error(parser_ctx, "escape sequences not supported yet");
+ }
+ ;
+
+primary_expression:
+ DECIMAL_CONSTANT
+ {
+ $$ = make_node(parser_ctx, NODE_EXPRESSION);
+ $$->u.expression.type = AST_EXP_CONSTANT;
+ if (sscanf(yylval.gs->s, "%" WIDTH_u64_SCANF_IS_A_BROKEN_API SCNu64,
+ &$$->u.expression.u.constant) != 1) {
+ parse_error(parser_ctx, "cannot scanf decimal constant");
+ }
+ }
+ | OCTAL_CONSTANT
+ {
+ $$ = make_node(parser_ctx, NODE_EXPRESSION);
+ $$->u.expression.type = AST_EXP_CONSTANT;
+ if (!strcmp(yylval.gs->s, "0")) {
+ $$->u.expression.u.constant = 0;
+ } else if (sscanf(yylval.gs->s, "0%" WIDTH_o64_SCANF_IS_A_BROKEN_API SCNo64,
+ &$$->u.expression.u.constant) != 1) {
+ parse_error(parser_ctx, "cannot scanf octal constant");
+ }
+ }
+ | HEXADECIMAL_CONSTANT
+ {
+ $$ = make_node(parser_ctx, NODE_EXPRESSION);
+ $$->u.expression.type = AST_EXP_CONSTANT;
+ if (sscanf(yylval.gs->s, "0x%" WIDTH_x64_SCANF_IS_A_BROKEN_API SCNx64,
+ &$$->u.expression.u.constant) != 1) {
+ parse_error(parser_ctx, "cannot scanf hexadecimal constant");
+ }
+ }
+ | FLOAT_CONSTANT
+ {
+ $$ = make_node(parser_ctx, NODE_EXPRESSION);
+ $$->u.expression.type = AST_EXP_FLOAT_CONSTANT;
+ if (sscanf(yylval.gs->s, "%" WIDTH_lg_SCANF_IS_A_BROKEN_API "lg",
+ &$$->u.expression.u.float_constant) != 1) {
+ parse_error(parser_ctx, "cannot scanf float constant");
+ }
+ }
+ | STRING_LITERAL_START DQUOTE
+ {
+ $$ = make_node(parser_ctx, NODE_EXPRESSION);
+ $$->u.expression.type = AST_EXP_STRING;
+ $$->u.expression.u.string = "";
+ }
+ | STRING_LITERAL_START s_char_sequence DQUOTE
+ {
+ $$ = make_node(parser_ctx, NODE_EXPRESSION);
+ $$->u.expression.type = AST_EXP_STRING;
+ $$->u.expression.u.string = $2->s;
+ }
+ | CHARACTER_CONSTANT_START c_char_sequence SQUOTE
+ {
+ $$ = make_node(parser_ctx, NODE_EXPRESSION);
+ $$->u.expression.type = AST_EXP_STRING;
+ $$->u.expression.u.string = $2->s;
+ }
+ | LPAREN expression RPAREN
+ {
+ $$ = make_node(parser_ctx, NODE_EXPRESSION);
+ $$->u.expression.type = AST_EXP_NESTED;
+ $$->u.expression.u.child = $2;
+ }
+ ;
+
+identifiers
+ : IDENTIFIER
+ {
+ $$ = make_node(parser_ctx, NODE_EXPRESSION);
+ $$->u.expression.type = AST_EXP_IDENTIFIER;
+ $$->u.expression.u.identifier = yylval.gs->s;
+ }
+ | GLOBAL_IDENTIFIER
+ {
+ $$ = make_node(parser_ctx, NODE_EXPRESSION);
+ $$->u.expression.type = AST_EXP_GLOBAL_IDENTIFIER;
+ $$->u.expression.u.identifier = yylval.gs->s;
+ }
+ ;
+
+prefix_expression_rec
+ : LSBRAC unary_expression RSBRAC
+ {
+ $$ = $2;
+ }
+ | LSBRAC unary_expression RSBRAC prefix_expression_rec
+ {
+ $$ = $2;
+ $$->u.expression.pre_op = AST_LINK_BRACKET;
+ $$->u.expression.prev = $4;
+ }
+ ;
+
+prefix_expression
+ : identifiers
+ {
+ $$ = $1;
+ }
+ | identifiers prefix_expression_rec
+ {
+ $$ = $1;
+ $$->u.expression.pre_op = AST_LINK_BRACKET;
+ $$->u.expression.next_bracket = $2;
+ }
+ ;
+
+postfix_expression
+ : prefix_expression
+ {
+ $$ = $1;
+ }
+ | postfix_expression DOT prefix_expression
+ {
+ $$ = $3;
+ $$->u.expression.post_op = AST_LINK_DOT;
+ $$->u.expression.prev = $1;
+ }
+ | postfix_expression RARROW prefix_expression
+ {
+ $$ = $3;
+ $$->u.expression.post_op = AST_LINK_RARROW;
+ $$->u.expression.prev = $1;
+ }
+ ;
+
+unary_expression
+ : postfix_expression
+ { $$ = $1; }
+ | primary_expression
+ { $$ = $1; }
+ | unary_operator unary_expression
+ {
+ $$ = $1;
+ $$->u.unary_op.child = $2;
+ }
+ ;
+
+unary_operator
+ : PLUS
+ {
+ $$ = make_node(parser_ctx, NODE_UNARY_OP);
+ $$->u.unary_op.type = AST_UNARY_PLUS;
+ }
+ | MINUS
+ {
+ $$ = make_node(parser_ctx, NODE_UNARY_OP);
+ $$->u.unary_op.type = AST_UNARY_MINUS;
+ }
+ | NOT_OP
+ {
+ $$ = make_node(parser_ctx, NODE_UNARY_OP);
+ $$->u.unary_op.type = AST_UNARY_NOT;
+ }
+ | NOT_BIN
+ {
+ $$ = make_node(parser_ctx, NODE_UNARY_OP);
+ $$->u.unary_op.type = AST_UNARY_BIT_NOT;
+ }
+ ;
+
+multiplicative_expression
+ : unary_expression
+ { $$ = $1; }
+ | multiplicative_expression STAR unary_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_MUL, $1, $3);
+ }
+ | multiplicative_expression DIV_OP unary_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_DIV, $1, $3);
+ }
+ | multiplicative_expression MOD_OP unary_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_MOD, $1, $3);
+ }
+ ;
+
+additive_expression
+ : multiplicative_expression
+ { $$ = $1; }
+ | additive_expression PLUS multiplicative_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_PLUS, $1, $3);
+ }
+ | additive_expression MINUS multiplicative_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_MINUS, $1, $3);
+ }
+ ;
+
+shift_expression
+ : additive_expression
+ { $$ = $1; }
+ | shift_expression LEFT_OP additive_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_BIT_LSHIFT, $1, $3);
+ }
+ | shift_expression RIGHT_OP additive_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_BIT_RSHIFT, $1, $3);
+ }
+ ;
+
+and_expression
+ : shift_expression
+ { $$ = $1; }
+ | and_expression AND_BIN shift_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_BIT_AND, $1, $3);
+ }
+ ;
+
+exclusive_or_expression
+ : and_expression
+ { $$ = $1; }
+ | exclusive_or_expression XOR_BIN and_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_BIT_XOR, $1, $3);
+ }
+ ;
+
+inclusive_or_expression
+ : exclusive_or_expression
+ { $$ = $1; }
+ | inclusive_or_expression OR_BIN exclusive_or_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_BIT_OR, $1, $3);
+ }
+ ;
+
+relational_expression
+ : inclusive_or_expression
+ { $$ = $1; }
+ | relational_expression LT_OP inclusive_or_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_LT, $1, $3);
+ }
+ | relational_expression GT_OP inclusive_or_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_GT, $1, $3);
+ }
+ | relational_expression LE_OP inclusive_or_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_LE, $1, $3);
+ }
+ | relational_expression GE_OP inclusive_or_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_GE, $1, $3);
+ }
+ ;
+
+equality_expression
+ : relational_expression
+ { $$ = $1; }
+ | equality_expression EQ_OP relational_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_EQ, $1, $3);
+ }
+ | equality_expression NE_OP relational_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_NE, $1, $3);
+ }
+ ;
+
+logical_and_expression
+ : equality_expression
+ { $$ = $1; }
+ | logical_and_expression AND_OP equality_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_AND, $1, $3);
+ }
+ ;
+
+logical_or_expression
+ : logical_and_expression
+ { $$ = $1; }
+ | logical_or_expression OR_OP logical_and_expression
+ {
+ $$ = make_op_node(parser_ctx, AST_OP_OR, $1, $3);
+ }
+ ;
+
+expression
+ : logical_or_expression
+ { $$ = $1; }
+ ;
+
+translation_unit
+ : expression
+ {
+ parser_ctx->ast->root.u.root.child = $1;
+ }
+ ;
--- /dev/null
+#ifndef _FILTER_SYMBOLS_H
+#define _FILTER_SYMBOLS_H
+
+/*
+ * filter-symbols.h
+ *
+ * LTTng filter flex/bison symbol prefixes
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#define yy_create_buffer lttng_yy_create_buffer
+#define yy_delete_buffer lttng_yy_delete_buffer
+#define yy_flush_buffer lttng_yy_flush_buffer
+#define yy_scan_buffer lttng_yy_scan_buffer
+#define yy_scan_bytes lttng_yy_scan_bytes
+#define yy_scan_string lttng_yy_scan_string
+#define yy_switch_to_buffer lttng_yy_switch_to_buffer
+#define yyalloc lttng_yyalloc
+#define yyfree lttng_yyfree
+#define yyget_column lttng_yyget_column
+#define yyget_debug lttng_yyget_debug
+#define yyget_extra lttng_yyget_extra
+#define yyget_in lttng_yyget_in
+#define yyget_leng lttng_yyget_leng
+#define yyget_lineno lttng_yyget_lineno
+#define yyget_lval lttng_yyget_lval
+#define yyget_out lttng_yyget_out
+#define yyget_text lttng_yyget_text
+#define yylex_init lttng_yylex_init
+#define yypop_buffer_state lttng_yypop_buffer_state
+#define yypush_buffer_state lttng_yypush_buffer_state
+#define yyrealloc lttng_yyrealloc
+#define yyset_column lttng_yyset_column
+#define yyset_debug lttng_yyset_debug
+#define yyset_extra lttng_yyset_extra
+#define yyset_in lttng_yyset_in
+#define yyset_lineno lttng_yyset_lineno
+#define yyset_lval lttng_yyset_lval
+#define yyset_out lttng_yyset_out
+
+#endif /* _FILTER_SYMBOLS_H */
--- /dev/null
+/*
+ * filter-visitor-generate-bytecode.c
+ *
+ * LTTng filter bytecode generation
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "common/align.h"
+#include "common/bytecode/bytecode.h"
+#include "common/compat/string.h"
+#include "common/macros.h"
+#include "filter-ast.h"
+#include "filter-ir.h"
+
+#ifndef max_t
+#define max_t(type, a, b) ((type) ((a) > (b) ? (a) : (b)))
+#endif
+
+static
+int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx,
+ struct ir_op *node);
+
+static
+int bytecode_patch(struct lttng_bytecode_alloc **fb,
+ const void *data,
+ uint16_t offset,
+ uint32_t len)
+{
+ if (offset >= (*fb)->b.len) {
+ return -EINVAL;
+ }
+ memcpy(&(*fb)->b.data[offset], data, len);
+ return 0;
+}
+
+static
+int visit_node_root(struct filter_parser_ctx *ctx, struct ir_op *node)
+{
+ int ret;
+ struct return_op insn;
+
+ /* Visit child */
+ ret = recursive_visit_gen_bytecode(ctx, node->u.root.child);
+ if (ret)
+ return ret;
+
+ /* Generate end of bytecode instruction */
+ insn.op = BYTECODE_OP_RETURN;
+ return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
+}
+
+static
+int append_str(char **s, const char *append)
+{
+ char *old = *s;
+ char *new;
+ size_t oldlen = (old == NULL) ? 0 : strlen(old);
+ size_t appendlen = strlen(append);
+
+ new = calloc(oldlen + appendlen + 1, 1);
+ if (!new) {
+ return -ENOMEM;
+ }
+ if (oldlen) {
+ strcpy(new, old);
+ }
+ strcat(new, append);
+ *s = new;
+ free(old);
+ return 0;
+}
+
+/*
+ * 1: match
+ * 0: no match
+ * < 0: error
+ */
+static
+int load_expression_legacy_match(const struct ir_load_expression *exp,
+ enum bytecode_op *op_type,
+ char **symbol)
+{
+ const struct ir_load_expression_op *op;
+ bool need_dot = false;
+
+ op = exp->child;
+ switch (op->type) {
+ case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+ *op_type = BYTECODE_OP_GET_CONTEXT_REF;
+ if (append_str(symbol, "$ctx.")) {
+ return -ENOMEM;
+ }
+ need_dot = false;
+ break;
+ case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+ *op_type = BYTECODE_OP_GET_CONTEXT_REF;
+ if (append_str(symbol, "$app.")) {
+ return -ENOMEM;
+ }
+ need_dot = false;
+ break;
+ case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+ *op_type = BYTECODE_OP_LOAD_FIELD_REF;
+ need_dot = false;
+ break;
+
+ case IR_LOAD_EXPRESSION_GET_SYMBOL:
+ case IR_LOAD_EXPRESSION_GET_INDEX:
+ case IR_LOAD_EXPRESSION_LOAD_FIELD:
+ default:
+ return 0; /* no match */
+ }
+
+ for (;;) {
+ op = op->next;
+ if (!op) {
+ return 0; /* no match */
+ }
+ switch (op->type) {
+ case IR_LOAD_EXPRESSION_LOAD_FIELD:
+ goto end;
+ case IR_LOAD_EXPRESSION_GET_SYMBOL:
+ if (need_dot && append_str(symbol, ".")) {
+ return -ENOMEM;
+ }
+ if (append_str(symbol, op->u.symbol)) {
+ return -ENOMEM;
+ }
+ break;
+ default:
+ return 0; /* no match */
+ }
+ need_dot = true;
+ }
+end:
+ return 1; /* Legacy match */
+}
+
+/*
+ * 1: legacy match
+ * 0: no legacy match
+ * < 0: error
+ */
+static
+int visit_node_load_expression_legacy(struct filter_parser_ctx *ctx,
+ const struct ir_load_expression *exp,
+ const struct ir_load_expression_op *op)
+{
+ struct load_op *insn = NULL;
+ uint32_t insn_len = sizeof(struct load_op)
+ + sizeof(struct field_ref);
+ struct field_ref ref_offset;
+ uint32_t reloc_offset_u32;
+ uint16_t reloc_offset;
+ enum bytecode_op op_type;
+ char *symbol = NULL;
+ int ret;
+
+ ret = load_expression_legacy_match(exp, &op_type, &symbol);
+ if (ret <= 0) {
+ goto end;
+ }
+ insn = calloc(insn_len, 1);
+ if (!insn) {
+ ret = -ENOMEM;
+ goto end;
+ }
+ insn->op = op_type;
+ ref_offset.offset = (uint16_t) -1U;
+ memcpy(insn->data, &ref_offset, sizeof(ref_offset));
+ /* reloc_offset points to struct load_op */
+ reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b);
+ if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
+ ret = -EINVAL;
+ goto end;
+ }
+ reloc_offset = (uint16_t) reloc_offset_u32;
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ if (ret) {
+ goto end;
+ }
+ /* append reloc */
+ ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset,
+ 1, sizeof(reloc_offset));
+ if (ret) {
+ goto end;
+ }
+ ret = bytecode_push(&ctx->bytecode_reloc, symbol,
+ 1, strlen(symbol) + 1);
+ if (ret) {
+ goto end;
+ }
+ ret = 1; /* legacy */
+end:
+ free(insn);
+ free(symbol);
+ return ret;
+}
+
+static
+int visit_node_load_expression(struct filter_parser_ctx *ctx,
+ const struct ir_op *node)
+{
+ struct ir_load_expression *exp;
+ struct ir_load_expression_op *op;
+ int ret;
+
+ exp = node->u.load.u.expression;
+ if (!exp) {
+ return -EINVAL;
+ }
+ op = exp->child;
+ if (!op) {
+ return -EINVAL;
+ }
+
+ /*
+ * TODO: if we remove legacy load for application contexts, we
+ * need to update session bytecode parser as well.
+ */
+ ret = visit_node_load_expression_legacy(ctx, exp, op);
+ if (ret < 0) {
+ return ret;
+ }
+ if (ret > 0) {
+ return 0; /* legacy */
+ }
+
+ for (; op != NULL; op = op->next) {
+ switch (op->type) {
+ case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+ {
+ int ret = bytecode_push_get_context_root(&ctx->bytecode);
+ if (ret) {
+ return ret;
+ }
+
+ break;
+ }
+ case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+ {
+ int ret = bytecode_push_get_app_context_root(&ctx->bytecode);
+ if (ret) {
+ return ret;
+ }
+
+ break;
+ }
+ case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+ {
+ int ret = bytecode_push_get_payload_root(&ctx->bytecode);
+ if (ret) {
+ return ret;
+ }
+
+ break;
+ }
+ case IR_LOAD_EXPRESSION_GET_SYMBOL:
+ {
+ int ret = bytecode_push_get_symbol(
+ &ctx->bytecode,
+ &ctx->bytecode_reloc,
+ op->u.symbol);
+ if (ret) {
+ return ret;
+ }
+ break;
+ }
+ case IR_LOAD_EXPRESSION_GET_INDEX:
+ {
+ int ret = bytecode_push_get_index_u64(&ctx->bytecode, op->u.index);
+ if (ret) {
+ return ret;
+ }
+ break;
+ }
+ case IR_LOAD_EXPRESSION_LOAD_FIELD:
+ {
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op);
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+ insn->op = BYTECODE_OP_LOAD_FIELD;
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ free(insn);
+ if (ret) {
+ return ret;
+ }
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static
+int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node)
+{
+ int ret;
+
+ switch (node->data_type) {
+ case IR_DATA_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] Unknown data type in %s\n",
+ __func__);
+ return -EINVAL;
+
+ case IR_DATA_STRING:
+ {
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op)
+ + strlen(node->u.load.u.string.value) + 1;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+
+ switch (node->u.load.u.string.type) {
+ case IR_LOAD_STRING_TYPE_GLOB_STAR:
+ /*
+ * We explicitly tell the interpreter here that
+ * this load is a full star globbing pattern so
+ * that the appropriate matching function can be
+ * called. Also, see comment below.
+ */
+ insn->op = BYTECODE_OP_LOAD_STAR_GLOB_STRING;
+ break;
+ default:
+ /*
+ * This is the "legacy" string, which includes
+ * star globbing patterns with a star only at
+ * the end. Both "plain" and "star at the end"
+ * literal strings are handled at the same place
+ * by the tracer's filter bytecode interpreter,
+ * whereas full star globbing patterns (stars
+ * can be anywhere in the string) is a special
+ * case.
+ */
+ insn->op = BYTECODE_OP_LOAD_STRING;
+ break;
+ }
+
+ strcpy(insn->data, node->u.load.u.string.value);
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ free(insn);
+ return ret;
+ }
+ case IR_DATA_NUMERIC:
+ {
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op)
+ + sizeof(struct literal_numeric);
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+ insn->op = BYTECODE_OP_LOAD_S64;
+ memcpy(insn->data, &node->u.load.u.num, sizeof(int64_t));
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ free(insn);
+ return ret;
+ }
+ case IR_DATA_FLOAT:
+ {
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op)
+ + sizeof(struct literal_double);
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+ insn->op = BYTECODE_OP_LOAD_DOUBLE;
+ memcpy(insn->data, &node->u.load.u.flt, sizeof(double));
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ free(insn);
+ return ret;
+ }
+ case IR_DATA_EXPRESSION:
+ return visit_node_load_expression(ctx, node);
+ }
+}
+
+static
+int visit_node_unary(struct filter_parser_ctx *ctx, struct ir_op *node)
+{
+ int ret;
+ struct unary_op insn;
+
+ /* Visit child */
+ ret = recursive_visit_gen_bytecode(ctx, node->u.unary.child);
+ if (ret)
+ return ret;
+
+ /* Generate end of bytecode instruction */
+ switch (node->u.unary.type) {
+ case AST_UNARY_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] Unknown unary node type in %s\n",
+ __func__);
+ return -EINVAL;
+ case AST_UNARY_PLUS:
+ /* Nothing to do. */
+ return 0;
+ case AST_UNARY_MINUS:
+ insn.op = BYTECODE_OP_UNARY_MINUS;
+ return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
+ case AST_UNARY_NOT:
+ insn.op = BYTECODE_OP_UNARY_NOT;
+ return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
+ case AST_UNARY_BIT_NOT:
+ insn.op = BYTECODE_OP_UNARY_BIT_NOT;
+ return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
+ }
+}
+
+/*
+ * Binary comparator nesting is disallowed. This allows fitting into
+ * only 2 registers.
+ */
+static
+int visit_node_binary(struct filter_parser_ctx *ctx, struct ir_op *node)
+{
+ int ret;
+ struct binary_op insn;
+
+ /* Visit child */
+ ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left);
+ if (ret)
+ return ret;
+ ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right);
+ if (ret)
+ return ret;
+
+ switch (node->u.binary.type) {
+ case AST_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] Unknown unary node type in %s\n",
+ __func__);
+ return -EINVAL;
+
+ case AST_OP_AND:
+ case AST_OP_OR:
+ fprintf(stderr, "[error] Unexpected logical node type in %s\n",
+ __func__);
+ return -EINVAL;
+
+ case AST_OP_MUL:
+ insn.op = BYTECODE_OP_MUL;
+ break;
+ case AST_OP_DIV:
+ insn.op = BYTECODE_OP_DIV;
+ break;
+ case AST_OP_MOD:
+ insn.op = BYTECODE_OP_MOD;
+ break;
+ case AST_OP_PLUS:
+ insn.op = BYTECODE_OP_PLUS;
+ break;
+ case AST_OP_MINUS:
+ insn.op = BYTECODE_OP_MINUS;
+ break;
+ case AST_OP_BIT_RSHIFT:
+ insn.op = BYTECODE_OP_BIT_RSHIFT;
+ break;
+ case AST_OP_BIT_LSHIFT:
+ insn.op = BYTECODE_OP_BIT_LSHIFT;
+ break;
+ case AST_OP_BIT_AND:
+ insn.op = BYTECODE_OP_BIT_AND;
+ break;
+ case AST_OP_BIT_OR:
+ insn.op = BYTECODE_OP_BIT_OR;
+ break;
+ case AST_OP_BIT_XOR:
+ insn.op = BYTECODE_OP_BIT_XOR;
+ break;
+
+ case AST_OP_EQ:
+ insn.op = BYTECODE_OP_EQ;
+ break;
+ case AST_OP_NE:
+ insn.op = BYTECODE_OP_NE;
+ break;
+ case AST_OP_GT:
+ insn.op = BYTECODE_OP_GT;
+ break;
+ case AST_OP_LT:
+ insn.op = BYTECODE_OP_LT;
+ break;
+ case AST_OP_GE:
+ insn.op = BYTECODE_OP_GE;
+ break;
+ case AST_OP_LE:
+ insn.op = BYTECODE_OP_LE;
+ break;
+ }
+ return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
+}
+
+/*
+ * A logical op always return a s64 (1 or 0).
+ */
+static
+int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node)
+{
+ int ret;
+ struct logical_op insn;
+ uint16_t skip_offset_loc;
+ uint16_t target_loc;
+
+ /* Visit left child */
+ ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left);
+ if (ret)
+ return ret;
+ /* Cast to s64 if float or field ref */
+ if ((node->u.binary.left->data_type == IR_DATA_FIELD_REF
+ || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF
+ || node->u.binary.left->data_type == IR_DATA_EXPRESSION)
+ || node->u.binary.left->data_type == IR_DATA_FLOAT) {
+ struct cast_op cast_insn;
+
+ if (node->u.binary.left->data_type == IR_DATA_FIELD_REF
+ || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF
+ || node->u.binary.left->data_type == IR_DATA_EXPRESSION) {
+ cast_insn.op = BYTECODE_OP_CAST_TO_S64;
+ } else {
+ cast_insn.op = BYTECODE_OP_CAST_DOUBLE_TO_S64;
+ }
+ ret = bytecode_push(&ctx->bytecode, &cast_insn,
+ 1, sizeof(cast_insn));
+ if (ret)
+ return ret;
+ }
+ switch (node->u.logical.type) {
+ default:
+ fprintf(stderr, "[error] Unknown node type in %s\n",
+ __func__);
+ return -EINVAL;
+
+ case AST_OP_AND:
+ insn.op = BYTECODE_OP_AND;
+ break;
+ case AST_OP_OR:
+ insn.op = BYTECODE_OP_OR;
+ break;
+ }
+ insn.skip_offset = (uint16_t) -1UL; /* Temporary */
+ ret = bytecode_push_logical(&ctx->bytecode, &insn, 1, sizeof(insn),
+ &skip_offset_loc);
+ if (ret)
+ return ret;
+ /* Visit right child */
+ ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right);
+ if (ret)
+ return ret;
+ /* Cast to s64 if float or field ref */
+ if ((node->u.binary.right->data_type == IR_DATA_FIELD_REF
+ || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF
+ || node->u.binary.right->data_type == IR_DATA_EXPRESSION)
+ || node->u.binary.right->data_type == IR_DATA_FLOAT) {
+ struct cast_op cast_insn;
+
+ if (node->u.binary.right->data_type == IR_DATA_FIELD_REF
+ || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF
+ || node->u.binary.right->data_type == IR_DATA_EXPRESSION) {
+ cast_insn.op = BYTECODE_OP_CAST_TO_S64;
+ } else {
+ cast_insn.op = BYTECODE_OP_CAST_DOUBLE_TO_S64;
+ }
+ ret = bytecode_push(&ctx->bytecode, &cast_insn,
+ 1, sizeof(cast_insn));
+ if (ret)
+ return ret;
+ }
+ /* We now know where the logical op can skip. */
+ target_loc = (uint16_t) bytecode_get_len(&ctx->bytecode->b);
+ ret = bytecode_patch(&ctx->bytecode,
+ &target_loc, /* Offset to jump to */
+ skip_offset_loc, /* Where to patch */
+ sizeof(uint16_t));
+ return ret;
+}
+
+/*
+ * Postorder traversal of the tree. We need the children result before
+ * we can evaluate the parent.
+ */
+static
+int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx,
+ struct ir_op *node)
+{
+ switch (node->op) {
+ case IR_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] Unknown node type in %s\n",
+ __func__);
+ return -EINVAL;
+
+ case IR_OP_ROOT:
+ return visit_node_root(ctx, node);
+ case IR_OP_LOAD:
+ return visit_node_load(ctx, node);
+ case IR_OP_UNARY:
+ return visit_node_unary(ctx, node);
+ case IR_OP_BINARY:
+ return visit_node_binary(ctx, node);
+ case IR_OP_LOGICAL:
+ return visit_node_logical(ctx, node);
+ }
+}
+
+LTTNG_HIDDEN
+void filter_bytecode_free(struct filter_parser_ctx *ctx)
+{
+ if (!ctx) {
+ return;
+ }
+
+ if (ctx->bytecode) {
+ free(ctx->bytecode);
+ ctx->bytecode = NULL;
+ }
+
+ if (ctx->bytecode_reloc) {
+ free(ctx->bytecode_reloc);
+ ctx->bytecode_reloc = NULL;
+ }
+}
+
+LTTNG_HIDDEN
+int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx)
+{
+ int ret;
+
+ ret = bytecode_init(&ctx->bytecode);
+ if (ret)
+ return ret;
+ ret = bytecode_init(&ctx->bytecode_reloc);
+ if (ret)
+ goto error;
+ ret = recursive_visit_gen_bytecode(ctx, ctx->ir_root);
+ if (ret)
+ goto error;
+
+ /* Finally, append symbol table to bytecode */
+ ctx->bytecode->b.reloc_table_offset = bytecode_get_len(&ctx->bytecode->b);
+ return bytecode_push(&ctx->bytecode, ctx->bytecode_reloc->b.data,
+ 1, bytecode_get_len(&ctx->bytecode_reloc->b));
+
+error:
+ filter_bytecode_free(ctx);
+ return ret;
+}
--- /dev/null
+/*
+ * filter-visitor-generate-ir.c
+ *
+ * LTTng filter generate intermediate representation
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+#include <common/macros.h>
+#include <common/string-utils/string-utils.h>
+
+static
+struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx,
+ struct filter_node *node, enum ir_side side);
+
+static
+struct ir_op *make_op_root(struct ir_op *child, enum ir_side side)
+{
+ struct ir_op *op;
+
+ op = calloc(sizeof(struct ir_op), 1);
+ if (!op)
+ return NULL;
+ switch (child->data_type) {
+ case IR_DATA_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] Unknown root child data type\n");
+ free(op);
+ return NULL;
+ case IR_DATA_STRING:
+ fprintf(stderr, "[error] String cannot be root data type\n");
+ free(op);
+ return NULL;
+ case IR_DATA_NUMERIC:
+ case IR_DATA_FIELD_REF:
+ case IR_DATA_GET_CONTEXT_REF:
+ case IR_DATA_EXPRESSION:
+ /* ok */
+ break;
+ }
+ op->op = IR_OP_ROOT;
+ op->side = side;
+ op->data_type = child->data_type;
+ op->signedness = child->signedness;
+ op->u.root.child = child;
+ return op;
+}
+
+static
+enum ir_load_string_type get_literal_string_type(const char *string)
+{
+ assert(string);
+
+ if (strutils_is_star_glob_pattern(string)) {
+ if (strutils_is_star_at_the_end_only_glob_pattern(string)) {
+ return IR_LOAD_STRING_TYPE_GLOB_STAR_END;
+ }
+
+ return IR_LOAD_STRING_TYPE_GLOB_STAR;
+ }
+
+ return IR_LOAD_STRING_TYPE_PLAIN;
+}
+
+static
+struct ir_op *make_op_load_string(const char *string, enum ir_side side)
+{
+ struct ir_op *op;
+
+ op = calloc(sizeof(struct ir_op), 1);
+ if (!op)
+ return NULL;
+ op->op = IR_OP_LOAD;
+ op->data_type = IR_DATA_STRING;
+ op->signedness = IR_SIGN_UNKNOWN;
+ op->side = side;
+ op->u.load.u.string.type = get_literal_string_type(string);
+ op->u.load.u.string.value = strdup(string);
+ if (!op->u.load.u.string.value) {
+ free(op);
+ return NULL;
+ }
+ return op;
+}
+
+static
+struct ir_op *make_op_load_numeric(int64_t v, enum ir_side side)
+{
+ struct ir_op *op;
+
+ op = calloc(sizeof(struct ir_op), 1);
+ if (!op)
+ return NULL;
+ op->op = IR_OP_LOAD;
+ op->data_type = IR_DATA_NUMERIC;
+ /* TODO: for now, all numeric values are signed */
+ op->signedness = IR_SIGNED;
+ op->side = side;
+ op->u.load.u.num = v;
+ return op;
+}
+
+static
+struct ir_op *make_op_load_float(double v, enum ir_side side)
+{
+ struct ir_op *op;
+
+ op = calloc(sizeof(struct ir_op), 1);
+ if (!op)
+ return NULL;
+ op->op = IR_OP_LOAD;
+ op->data_type = IR_DATA_FLOAT;
+ op->signedness = IR_SIGN_UNKNOWN;
+ op->side = side;
+ op->u.load.u.flt = v;
+ return op;
+}
+
+static
+void free_load_expression(struct ir_load_expression *load_expression)
+{
+ struct ir_load_expression_op *exp_op;
+
+ if (!load_expression)
+ return;
+ exp_op = load_expression->child;
+ for (;;) {
+ struct ir_load_expression_op *prev_exp_op;
+
+ if (!exp_op)
+ break;
+ switch (exp_op->type) {
+ case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+ case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+ case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+ case IR_LOAD_EXPRESSION_GET_INDEX:
+ case IR_LOAD_EXPRESSION_LOAD_FIELD:
+ break;
+ case IR_LOAD_EXPRESSION_GET_SYMBOL:
+ free(exp_op->u.symbol);
+ break;
+ }
+ prev_exp_op = exp_op;
+ exp_op = exp_op->next;
+ free(prev_exp_op);
+ }
+ free(load_expression);
+}
+
+/*
+ * Returns the first node of the chain, after initializing the next
+ * pointers.
+ */
+static
+struct filter_node *load_expression_get_forward_chain(struct filter_node *node)
+{
+ struct filter_node *prev_node;
+
+ for (;;) {
+ assert(node->type == NODE_EXPRESSION);
+ prev_node = node;
+ node = node->u.expression.prev;
+ if (!node) {
+ break;
+ }
+ node->u.expression.next = prev_node;
+ }
+ return prev_node;
+}
+
+static
+struct ir_load_expression *create_load_expression(struct filter_node *node)
+{
+ struct ir_load_expression *load_exp;
+ struct ir_load_expression_op *load_exp_op, *prev_op;
+ const char *str;
+
+ /* Get forward chain. */
+ node = load_expression_get_forward_chain(node);
+ if (!node)
+ return NULL;
+ load_exp = calloc(sizeof(struct ir_load_expression), 1);
+ if (!load_exp)
+ return NULL;
+
+ /* Root */
+ load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
+ if (!load_exp_op)
+ goto error;
+ load_exp->child = load_exp_op;
+ str = node->u.expression.u.string;
+ if (!strcmp(str, "$ctx")) {
+ load_exp_op->type = IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT;
+ node = node->u.expression.next;
+ if (!node) {
+ fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str);
+ goto error;
+ }
+ str = node->u.expression.u.string;
+ } else if (!strcmp(str, "$app")) {
+ load_exp_op->type = IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT;
+ node = node->u.expression.next;
+ if (!node) {
+ fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str);
+ goto error;
+ }
+ str = node->u.expression.u.string;
+ } else if (str[0] == '$') {
+ fprintf(stderr, "[error] Unexpected identifier \'%s\'\n", str);
+ goto error;
+ } else {
+ load_exp_op->type = IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT;
+ }
+
+ for (;;) {
+ struct filter_node *bracket_node;
+
+ prev_op = load_exp_op;
+ load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
+ if (!load_exp_op)
+ goto error;
+ prev_op->next = load_exp_op;
+ load_exp_op->type = IR_LOAD_EXPRESSION_GET_SYMBOL;
+ load_exp_op->u.symbol = strdup(str);
+ if (!load_exp_op->u.symbol)
+ goto error;
+
+ /* Explore brackets from current node. */
+ for (bracket_node = node->u.expression.next_bracket;
+ bracket_node != NULL;
+ bracket_node = bracket_node->u.expression.next_bracket) {
+ prev_op = load_exp_op;
+ load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
+ if (!load_exp_op)
+ goto error;
+ prev_op->next = load_exp_op;
+ load_exp_op->type = IR_LOAD_EXPRESSION_GET_INDEX;
+ load_exp_op->u.index = bracket_node->u.expression.u.constant;
+ }
+ /* Go to next chain element. */
+ node = node->u.expression.next;
+ if (!node)
+ break;
+ str = node->u.expression.u.string;
+ }
+ /* Add final load field */
+ prev_op = load_exp_op;
+ load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
+ if (!load_exp_op)
+ goto error;
+ prev_op->next = load_exp_op;
+ load_exp_op->type = IR_LOAD_EXPRESSION_LOAD_FIELD;
+ return load_exp;
+
+error:
+ free_load_expression(load_exp);
+ return NULL;
+}
+
+static
+struct ir_op *make_op_load_expression(struct filter_node *node,
+ enum ir_side side)
+{
+ struct ir_op *op;
+
+ op = calloc(sizeof(struct ir_op), 1);
+ if (!op)
+ return NULL;
+ op->op = IR_OP_LOAD;
+ op->data_type = IR_DATA_EXPRESSION;
+ op->signedness = IR_SIGN_DYN;
+ op->side = side;
+ op->u.load.u.expression = create_load_expression(node);
+ if (!op->u.load.u.expression) {
+ goto error;
+ }
+ return op;
+
+error:
+ free_load_expression(op->u.load.u.expression);
+ free(op);
+ return NULL;
+}
+
+static
+struct ir_op *make_op_unary(enum unary_op_type unary_op_type,
+ const char *op_str, enum ir_op_signedness signedness,
+ struct ir_op *child, enum ir_side side)
+{
+ struct ir_op *op = NULL;
+
+ if (child->data_type == IR_DATA_STRING) {
+ fprintf(stderr, "[error] unary operation '%s' not allowed on string literal\n", op_str);
+ goto error;
+ }
+
+ op = calloc(sizeof(struct ir_op), 1);
+ if (!op)
+ return NULL;
+ op->op = IR_OP_UNARY;
+ op->data_type = child->data_type;
+ op->signedness = signedness;
+ op->side = side;
+ op->u.unary.type = unary_op_type;
+ op->u.unary.child = child;
+ return op;
+
+error:
+ free(op);
+ return NULL;
+}
+
+/*
+ * unary + is pretty much useless.
+ */
+static
+struct ir_op *make_op_unary_plus(struct ir_op *child, enum ir_side side)
+{
+ return make_op_unary(AST_UNARY_PLUS, "+", child->signedness,
+ child, side);
+}
+
+static
+struct ir_op *make_op_unary_minus(struct ir_op *child, enum ir_side side)
+{
+ return make_op_unary(AST_UNARY_MINUS, "-", child->signedness,
+ child, side);
+}
+
+static
+struct ir_op *make_op_unary_not(struct ir_op *child, enum ir_side side)
+{
+ return make_op_unary(AST_UNARY_NOT, "!", child->signedness,
+ child, side);
+}
+
+static
+struct ir_op *make_op_unary_bit_not(struct ir_op *child, enum ir_side side)
+{
+ return make_op_unary(AST_UNARY_BIT_NOT, "~", child->signedness,
+ child, side);
+}
+
+static
+struct ir_op *make_op_binary_compare(enum op_type bin_op_type,
+ const char *op_str, struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ struct ir_op *op = NULL;
+
+ if (left->data_type == IR_DATA_UNKNOWN
+ || right->data_type == IR_DATA_UNKNOWN) {
+ fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str);
+ goto error;
+
+ }
+ if ((left->data_type == IR_DATA_STRING
+ && (right->data_type == IR_DATA_NUMERIC || right->data_type == IR_DATA_FLOAT))
+ || ((left->data_type == IR_DATA_NUMERIC || left->data_type == IR_DATA_FLOAT) &&
+ right->data_type == IR_DATA_STRING)) {
+ fprintf(stderr, "[error] binary operation '%s' operand type mismatch\n", op_str);
+ goto error;
+ }
+
+ op = calloc(sizeof(struct ir_op), 1);
+ if (!op)
+ return NULL;
+ op->op = IR_OP_BINARY;
+ op->u.binary.type = bin_op_type;
+ op->u.binary.left = left;
+ op->u.binary.right = right;
+
+ /* we return a boolean, represented as signed numeric */
+ op->data_type = IR_DATA_NUMERIC;
+ op->signedness = IR_SIGNED;
+ op->side = side;
+
+ return op;
+
+error:
+ free(op);
+ return NULL;
+}
+
+static
+struct ir_op *make_op_binary_eq(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_compare(AST_OP_EQ, "==", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_ne(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_compare(AST_OP_NE, "!=", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_gt(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_compare(AST_OP_GT, ">", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_lt(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_compare(AST_OP_LT, "<", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_ge(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_compare(AST_OP_GE, ">=", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_le(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_compare(AST_OP_LE, "<=", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_logical(enum op_type bin_op_type,
+ const char *op_str, struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ struct ir_op *op = NULL;
+
+ if (left->data_type == IR_DATA_UNKNOWN
+ || right->data_type == IR_DATA_UNKNOWN) {
+ fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str);
+ goto error;
+
+ }
+ if (left->data_type == IR_DATA_STRING
+ || right->data_type == IR_DATA_STRING) {
+ fprintf(stderr, "[error] logical binary operation '%s' cannot have string operand\n", op_str);
+ goto error;
+ }
+
+ op = calloc(sizeof(struct ir_op), 1);
+ if (!op)
+ return NULL;
+ op->op = IR_OP_LOGICAL;
+ op->u.binary.type = bin_op_type;
+ op->u.binary.left = left;
+ op->u.binary.right = right;
+
+ /* we return a boolean, represented as signed numeric */
+ op->data_type = IR_DATA_NUMERIC;
+ op->signedness = IR_SIGNED;
+ op->side = side;
+
+ return op;
+
+error:
+ free(op);
+ return NULL;
+}
+
+static
+struct ir_op *make_op_binary_bitwise(enum op_type bin_op_type,
+ const char *op_str, struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ struct ir_op *op = NULL;
+
+ if (left->data_type == IR_DATA_UNKNOWN
+ || right->data_type == IR_DATA_UNKNOWN) {
+ fprintf(stderr, "[error] bitwise binary operation '%s' has unknown operand type\n", op_str);
+ goto error;
+
+ }
+ if (left->data_type == IR_DATA_STRING
+ || right->data_type == IR_DATA_STRING) {
+ fprintf(stderr, "[error] bitwise binary operation '%s' cannot have string operand\n", op_str);
+ goto error;
+ }
+ if (left->data_type == IR_DATA_FLOAT
+ || right->data_type == IR_DATA_FLOAT) {
+ fprintf(stderr, "[error] bitwise binary operation '%s' cannot have floating point operand\n", op_str);
+ goto error;
+ }
+
+ op = calloc(sizeof(struct ir_op), 1);
+ if (!op)
+ return NULL;
+ op->op = IR_OP_BINARY;
+ op->u.binary.type = bin_op_type;
+ op->u.binary.left = left;
+ op->u.binary.right = right;
+
+ /* we return a signed numeric */
+ op->data_type = IR_DATA_NUMERIC;
+ op->signedness = IR_SIGNED;
+ op->side = side;
+
+ return op;
+
+error:
+ free(op);
+ return NULL;
+}
+
+static
+struct ir_op *make_op_binary_logical_and(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_logical(AST_OP_AND, "&&", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_logical_or(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_logical(AST_OP_OR, "||", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_bitwise_rshift(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_bitwise(AST_OP_BIT_RSHIFT, ">>", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_bitwise_lshift(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_bitwise(AST_OP_BIT_LSHIFT, "<<", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_bitwise_and(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_bitwise(AST_OP_BIT_AND, "&", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_bitwise_or(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_bitwise(AST_OP_BIT_OR, "|", left, right, side);
+}
+
+static
+struct ir_op *make_op_binary_bitwise_xor(struct ir_op *left, struct ir_op *right,
+ enum ir_side side)
+{
+ return make_op_binary_bitwise(AST_OP_BIT_XOR, "^", left, right, side);
+}
+
+static
+void filter_free_ir_recursive(struct ir_op *op)
+{
+ if (!op)
+ return;
+ switch (op->op) {
+ case IR_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] Unknown op type in %s\n",
+ __func__);
+ break;
+ case IR_OP_ROOT:
+ filter_free_ir_recursive(op->u.root.child);
+ break;
+ case IR_OP_LOAD:
+ switch (op->data_type) {
+ case IR_DATA_STRING:
+ free(op->u.load.u.string.value);
+ break;
+ case IR_DATA_FIELD_REF: /* fall-through */
+ case IR_DATA_GET_CONTEXT_REF:
+ free(op->u.load.u.ref);
+ break;
+ case IR_DATA_EXPRESSION:
+ free_load_expression(op->u.load.u.expression);
+ default:
+ break;
+ }
+ break;
+ case IR_OP_UNARY:
+ filter_free_ir_recursive(op->u.unary.child);
+ break;
+ case IR_OP_BINARY:
+ filter_free_ir_recursive(op->u.binary.left);
+ filter_free_ir_recursive(op->u.binary.right);
+ break;
+ case IR_OP_LOGICAL:
+ filter_free_ir_recursive(op->u.logical.left);
+ filter_free_ir_recursive(op->u.logical.right);
+ break;
+ }
+ free(op);
+}
+
+static
+struct ir_op *make_expression(struct filter_parser_ctx *ctx,
+ struct filter_node *node, enum ir_side side)
+{
+ switch (node->u.expression.type) {
+ case AST_EXP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown expression type\n", __func__);
+ return NULL;
+
+ case AST_EXP_STRING:
+ return make_op_load_string(node->u.expression.u.string, side);
+ case AST_EXP_CONSTANT:
+ return make_op_load_numeric(node->u.expression.u.constant,
+ side);
+ case AST_EXP_FLOAT_CONSTANT:
+ return make_op_load_float(node->u.expression.u.float_constant,
+ side);
+ case AST_EXP_IDENTIFIER:
+ case AST_EXP_GLOBAL_IDENTIFIER:
+ return make_op_load_expression(node, side);
+ case AST_EXP_NESTED:
+ return generate_ir_recursive(ctx, node->u.expression.u.child,
+ side);
+ }
+}
+
+static
+struct ir_op *make_op(struct filter_parser_ctx *ctx,
+ struct filter_node *node, enum ir_side side)
+{
+ struct ir_op *op = NULL, *lchild, *rchild;
+ const char *op_str = "?";
+
+ switch (node->u.op.type) {
+ case AST_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown binary op type\n", __func__);
+ return NULL;
+
+ /*
+ * The following binary operators other than comparators and
+ * logical and/or are not supported yet.
+ */
+ case AST_OP_MUL:
+ op_str = "*";
+ goto error_not_supported;
+ case AST_OP_DIV:
+ op_str = "/";
+ goto error_not_supported;
+ case AST_OP_MOD:
+ op_str = "%";
+ goto error_not_supported;
+ case AST_OP_PLUS:
+ op_str = "+";
+ goto error_not_supported;
+ case AST_OP_MINUS:
+ op_str = "-";
+ goto error_not_supported;
+
+ case AST_OP_BIT_RSHIFT:
+ case AST_OP_BIT_LSHIFT:
+ case AST_OP_BIT_AND:
+ case AST_OP_BIT_OR:
+ case AST_OP_BIT_XOR:
+ lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
+ if (!lchild)
+ return NULL;
+ rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT);
+ if (!rchild) {
+ filter_free_ir_recursive(lchild);
+ return NULL;
+ }
+ break;
+
+ case AST_OP_EQ:
+ case AST_OP_NE:
+ case AST_OP_GT:
+ case AST_OP_LT:
+ case AST_OP_GE:
+ case AST_OP_LE:
+ lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
+ if (!lchild)
+ return NULL;
+ rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT);
+ if (!rchild) {
+ filter_free_ir_recursive(lchild);
+ return NULL;
+ }
+ break;
+
+ case AST_OP_AND:
+ case AST_OP_OR:
+ /*
+ * Both children considered as left, since we need to
+ * populate R0.
+ */
+ lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
+ if (!lchild)
+ return NULL;
+ rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_LEFT);
+ if (!rchild) {
+ filter_free_ir_recursive(lchild);
+ return NULL;
+ }
+ break;
+ }
+
+ switch (node->u.op.type) {
+ case AST_OP_AND:
+ op = make_op_binary_logical_and(lchild, rchild, side);
+ break;
+ case AST_OP_OR:
+ op = make_op_binary_logical_or(lchild, rchild, side);
+ break;
+ case AST_OP_EQ:
+ op = make_op_binary_eq(lchild, rchild, side);
+ break;
+ case AST_OP_NE:
+ op = make_op_binary_ne(lchild, rchild, side);
+ break;
+ case AST_OP_GT:
+ op = make_op_binary_gt(lchild, rchild, side);
+ break;
+ case AST_OP_LT:
+ op = make_op_binary_lt(lchild, rchild, side);
+ break;
+ case AST_OP_GE:
+ op = make_op_binary_ge(lchild, rchild, side);
+ break;
+ case AST_OP_LE:
+ op = make_op_binary_le(lchild, rchild, side);
+ break;
+ case AST_OP_BIT_RSHIFT:
+ op = make_op_binary_bitwise_rshift(lchild, rchild, side);
+ break;
+ case AST_OP_BIT_LSHIFT:
+ op = make_op_binary_bitwise_lshift(lchild, rchild, side);
+ break;
+ case AST_OP_BIT_AND:
+ op = make_op_binary_bitwise_and(lchild, rchild, side);
+ break;
+ case AST_OP_BIT_OR:
+ op = make_op_binary_bitwise_or(lchild, rchild, side);
+ break;
+ case AST_OP_BIT_XOR:
+ op = make_op_binary_bitwise_xor(lchild, rchild, side);
+ break;
+ default:
+ break;
+ }
+
+ if (!op) {
+ filter_free_ir_recursive(rchild);
+ filter_free_ir_recursive(lchild);
+ }
+ return op;
+
+error_not_supported:
+ fprintf(stderr, "[error] %s: binary operation '%s' not supported\n",
+ __func__, op_str);
+ return NULL;
+}
+
+static
+struct ir_op *make_unary_op(struct filter_parser_ctx *ctx,
+ struct filter_node *node, enum ir_side side)
+{
+ switch (node->u.unary_op.type) {
+ case AST_UNARY_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown unary op type\n", __func__);
+ return NULL;
+
+ case AST_UNARY_PLUS:
+ {
+ struct ir_op *op, *child;
+
+ child = generate_ir_recursive(ctx, node->u.unary_op.child,
+ side);
+ if (!child)
+ return NULL;
+ op = make_op_unary_plus(child, side);
+ if (!op) {
+ filter_free_ir_recursive(child);
+ return NULL;
+ }
+ return op;
+ }
+ case AST_UNARY_MINUS:
+ {
+ struct ir_op *op, *child;
+
+ child = generate_ir_recursive(ctx, node->u.unary_op.child,
+ side);
+ if (!child)
+ return NULL;
+ op = make_op_unary_minus(child, side);
+ if (!op) {
+ filter_free_ir_recursive(child);
+ return NULL;
+ }
+ return op;
+ }
+ case AST_UNARY_NOT:
+ {
+ struct ir_op *op, *child;
+
+ child = generate_ir_recursive(ctx, node->u.unary_op.child,
+ side);
+ if (!child)
+ return NULL;
+ op = make_op_unary_not(child, side);
+ if (!op) {
+ filter_free_ir_recursive(child);
+ return NULL;
+ }
+ return op;
+ }
+ case AST_UNARY_BIT_NOT:
+ {
+ struct ir_op *op, *child;
+
+ child = generate_ir_recursive(ctx, node->u.unary_op.child,
+ side);
+ if (!child)
+ return NULL;
+ op = make_op_unary_bit_not(child, side);
+ if (!op) {
+ filter_free_ir_recursive(child);
+ return NULL;
+ }
+ return op;
+ }
+ }
+
+ return NULL;
+}
+
+static
+struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx,
+ struct filter_node *node, enum ir_side side)
+{
+ switch (node->type) {
+ case NODE_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown node type\n", __func__);
+ return NULL;
+
+ case NODE_ROOT:
+ {
+ struct ir_op *op, *child;
+
+ child = generate_ir_recursive(ctx, node->u.root.child,
+ side);
+ if (!child)
+ return NULL;
+ op = make_op_root(child, side);
+ if (!op) {
+ filter_free_ir_recursive(child);
+ return NULL;
+ }
+ return op;
+ }
+ case NODE_EXPRESSION:
+ return make_expression(ctx, node, side);
+ case NODE_OP:
+ return make_op(ctx, node, side);
+ case NODE_UNARY_OP:
+ return make_unary_op(ctx, node, side);
+ }
+ return 0;
+}
+
+LTTNG_HIDDEN
+void filter_ir_free(struct filter_parser_ctx *ctx)
+{
+ filter_free_ir_recursive(ctx->ir_root);
+ ctx->ir_root = NULL;
+}
+
+LTTNG_HIDDEN
+int filter_visitor_ir_generate(struct filter_parser_ctx *ctx)
+{
+ struct ir_op *op;
+
+ op = generate_ir_recursive(ctx, &ctx->ast->root, IR_LEFT);
+ if (!op) {
+ return -EINVAL;
+ }
+ ctx->ir_root = op;
+ return 0;
+}
--- /dev/null
+/*
+ * filter-visitor-ir-check-binary-comparator.c
+ *
+ * LTTng filter IR check binary comparator
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+static
+int check_bin_comparator(struct ir_op *node)
+{
+ switch (node->op) {
+ case IR_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+ return -EINVAL;
+
+ case IR_OP_ROOT:
+ return check_bin_comparator(node->u.root.child);
+ case IR_OP_LOAD:
+ return 0;
+ case IR_OP_UNARY:
+ return check_bin_comparator(node->u.unary.child);
+ case IR_OP_BINARY:
+ {
+ int ret;
+
+ if (node->u.binary.left->data_type == IR_DATA_STRING
+ || node->u.binary.right->data_type
+ == IR_DATA_STRING) {
+ if (node->u.binary.type != AST_OP_EQ
+ && node->u.binary.type != AST_OP_NE) {
+ fprintf(stderr, "[error] Only '==' and '!=' comparators are allowed for strings\n");
+ return -EINVAL;
+ }
+ }
+
+ ret = check_bin_comparator(node->u.binary.left);
+ if (ret)
+ return ret;
+ return check_bin_comparator(node->u.binary.right);
+ }
+ case IR_OP_LOGICAL:
+ {
+ int ret;
+
+ ret = check_bin_comparator(node->u.logical.left);
+ if (ret)
+ return ret;
+ return check_bin_comparator(node->u.logical.right);
+ }
+ }
+}
+
+int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx)
+{
+ return check_bin_comparator(ctx->ir_root);
+}
--- /dev/null
+/*
+ * filter-visitor-ir-check-binary-op-nesting.c
+ *
+ * LTTng filter IR check binary op nesting
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+#include <common/macros.h>
+
+static
+int check_bin_op_nesting_recursive(struct ir_op *node, int nesting)
+{
+ switch (node->op) {
+ case IR_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+ return -EINVAL;
+
+ case IR_OP_ROOT:
+ return check_bin_op_nesting_recursive(node->u.root.child,
+ nesting);
+ case IR_OP_LOAD:
+ return 0;
+ case IR_OP_UNARY:
+ return check_bin_op_nesting_recursive(node->u.unary.child,
+ nesting);
+ case IR_OP_BINARY:
+ {
+ int ret;
+
+ ret = check_bin_op_nesting_recursive(node->u.binary.left,
+ nesting + 1);
+ if (ret)
+ return ret;
+ return check_bin_op_nesting_recursive(node->u.binary.right,
+ nesting + 1);
+ }
+ case IR_OP_LOGICAL:
+ {
+ int ret;
+
+ ret = check_bin_op_nesting_recursive(node->u.logical.left,
+ nesting);
+ if (ret)
+ return ret;
+ return check_bin_op_nesting_recursive(node->u.logical.right,
+ nesting);
+ }
+ }
+}
+
+LTTNG_HIDDEN
+int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx)
+{
+ return check_bin_op_nesting_recursive(ctx->ir_root, 0);
+}
--- /dev/null
+/*
+ * filter-visitor-ir-normalize-glob-patterns.c
+ *
+ * LTTng filter IR normalize string
+ *
+ * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <common/macros.h>
+#include <common/string-utils/string-utils.h>
+
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+static
+int normalize_glob_patterns(struct ir_op *node)
+{
+ switch (node->op) {
+ case IR_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+ return -EINVAL;
+
+ case IR_OP_ROOT:
+ return normalize_glob_patterns(node->u.root.child);
+ case IR_OP_LOAD:
+ {
+ if (node->data_type == IR_DATA_STRING) {
+ enum ir_load_string_type type =
+ node->u.load.u.string.type;
+ if (type == IR_LOAD_STRING_TYPE_GLOB_STAR_END ||
+ type == IR_LOAD_STRING_TYPE_GLOB_STAR) {
+ assert(node->u.load.u.string.value);
+ strutils_normalize_star_glob_pattern(
+ node->u.load.u.string.value);
+ }
+ }
+
+ return 0;
+ }
+ case IR_OP_UNARY:
+ return normalize_glob_patterns(node->u.unary.child);
+ case IR_OP_BINARY:
+ {
+ int ret = normalize_glob_patterns(node->u.binary.left);
+
+ if (ret)
+ return ret;
+ return normalize_glob_patterns(node->u.binary.right);
+ }
+ case IR_OP_LOGICAL:
+ {
+ int ret;
+
+ ret = normalize_glob_patterns(node->u.logical.left);
+ if (ret)
+ return ret;
+ return normalize_glob_patterns(node->u.logical.right);
+ }
+ }
+}
+
+/*
+ * This function normalizes all the globbing literal strings with
+ * utils_normalize_glob_pattern(). See the documentation of
+ * utils_normalize_glob_pattern() for more details.
+ */
+LTTNG_HIDDEN
+int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx)
+{
+ return normalize_glob_patterns(ctx->ir_root);
+}
--- /dev/null
+/*
+ * filter-visitor-ir-validate-globbing.c
+ *
+ * LTTng filter IR validate globbing
+ *
+ * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <common/macros.h>
+
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+static
+int validate_globbing(struct ir_op *node)
+{
+ int ret;
+
+ switch (node->op) {
+ case IR_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+ return -EINVAL;
+
+ case IR_OP_ROOT:
+ return validate_globbing(node->u.root.child);
+ case IR_OP_LOAD:
+ return 0;
+ case IR_OP_UNARY:
+ return validate_globbing(node->u.unary.child);
+ case IR_OP_BINARY:
+ {
+ struct ir_op *left = node->u.binary.left;
+ struct ir_op *right = node->u.binary.right;
+
+ if (left->op == IR_OP_LOAD && right->op == IR_OP_LOAD &&
+ left->data_type == IR_DATA_STRING &&
+ right->data_type == IR_DATA_STRING) {
+ /* Test 1. */
+ if (left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR &&
+ right->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) {
+ fprintf(stderr, "[error] Cannot compare two globbing patterns\n");
+ return -1;
+ }
+
+ if (right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR &&
+ left->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) {
+ fprintf(stderr, "[error] Cannot compare two globbing patterns\n");
+ return -1;
+ }
+ }
+
+ if ((left->op == IR_OP_LOAD && left->data_type == IR_DATA_STRING) ||
+ (right->op == IR_OP_LOAD && right->data_type == IR_DATA_STRING)) {
+ if ((left->op == IR_OP_LOAD && left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR) ||
+ (right->op == IR_OP_LOAD && right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR)) {
+ /* Test 2. */
+ if (node->u.binary.type != AST_OP_EQ &&
+ node->u.binary.type != AST_OP_NE) {
+ fprintf(stderr, "[error] Only the `==` and `!=` operators are allowed with a globbing pattern\n");
+ return -1;
+ }
+ }
+ }
+
+ ret = validate_globbing(left);
+ if (ret) {
+ return ret;
+ }
+
+ return validate_globbing(right);
+ }
+ case IR_OP_LOGICAL:
+ ret = validate_globbing(node->u.logical.left);
+ if (ret)
+ return ret;
+ return validate_globbing(node->u.logical.right);
+ }
+}
+
+/*
+ * This function recursively validates that:
+ *
+ * 1. When there's a binary operation between two literal strings,
+ * if one of them has the IR_LOAD_STRING_TYPE_GLOB_STAR type,
+ * the other one has the IR_LOAD_STRING_TYPE_PLAIN type.
+ *
+ * In other words, you cannot compare two globbing patterns, except
+ * for two globbing patterns with only a star at the end for backward
+ * compatibility reasons.
+ *
+ * 2. When there's a binary operation between two literal strings, if
+ * one of them is a (full) star globbing pattern, the binary
+ * operation is either == or !=.
+ */
+LTTNG_HIDDEN
+int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx)
+{
+ return validate_globbing(ctx->ir_root);
+}
--- /dev/null
+/*
+ * filter-visitor-ir-validate-string.c
+ *
+ * LTTng filter IR validate string
+ *
+ * Copyright 2014 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <common/macros.h>
+
+#include "filter-ast.h"
+#include "filter-parser.h"
+#include "filter-ir.h"
+
+enum parse_char_result {
+ PARSE_CHAR_UNKNOWN = -2,
+ PARSE_CHAR_WILDCARD = -1,
+ PARSE_CHAR_NORMAL = 0,
+};
+
+static
+enum parse_char_result parse_char(const char **p)
+{
+ switch (**p) {
+ case '\\':
+ (*p)++;
+ switch (**p) {
+ case '\\':
+ case '*':
+ return PARSE_CHAR_NORMAL;
+ default:
+ return PARSE_CHAR_UNKNOWN;
+ }
+ case '*':
+ return PARSE_CHAR_WILDCARD;
+ default:
+ return PARSE_CHAR_NORMAL;
+ }
+}
+
+static
+int validate_string(struct ir_op *node)
+{
+ switch (node->op) {
+ case IR_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown op type\n", __func__);
+ return -EINVAL;
+
+ case IR_OP_ROOT:
+ return validate_string(node->u.root.child);
+ case IR_OP_LOAD:
+ {
+ int ret = 0;
+
+ if (node->data_type == IR_DATA_STRING) {
+ const char *str;
+
+ assert(node->u.load.u.string.value);
+ str = node->u.load.u.string.value;
+
+ for (;;) {
+ enum parse_char_result res;
+
+ if (!(*str)) {
+ break;
+ }
+
+ res = parse_char(&str);
+ str++;
+
+ switch (res) {
+ case PARSE_CHAR_UNKNOWN:
+ ret = -EINVAL;
+ fprintf(stderr,
+ "Unsupported escape character detected.\n");
+ goto end_load;
+ case PARSE_CHAR_NORMAL:
+ default:
+ break;
+ }
+ }
+ }
+end_load:
+ return ret;
+ }
+ case IR_OP_UNARY:
+ return validate_string(node->u.unary.child);
+ case IR_OP_BINARY:
+ {
+ int ret = validate_string(node->u.binary.left);
+
+ if (ret)
+ return ret;
+ return validate_string(node->u.binary.right);
+ }
+ case IR_OP_LOGICAL:
+ {
+ int ret;
+
+ ret = validate_string(node->u.logical.left);
+ if (ret)
+ return ret;
+ return validate_string(node->u.logical.right);
+ }
+ }
+}
+
+LTTNG_HIDDEN
+int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx)
+{
+ return validate_string(ctx->ir_root);
+}
--- /dev/null
+/*
+ * filter-visitor-xml.c
+ *
+ * LTTng filter XML pretty printer visitor
+ *
+ * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "filter-ast.h"
+#include "filter-parser.h"
+
+#include <common/macros.h>
+
+#define fprintf_dbg(fd, fmt, args...) fprintf(fd, "%s: " fmt, __func__, ## args)
+
+static
+int recursive_visit_print(struct filter_node *node, FILE *stream, int indent);
+
+static
+void print_tabs(FILE *fd, int depth)
+{
+ int i;
+
+ for (i = 0; i < depth; i++)
+ fprintf(fd, "\t");
+}
+
+static
+int recursive_visit_print_expression(struct filter_node *node,
+ FILE *stream, int indent)
+{
+ struct filter_node *iter_node;
+
+ if (!node) {
+ fprintf(stderr, "[error] %s: NULL child\n", __func__);
+ return -EINVAL;
+ }
+ switch (node->u.expression.type) {
+ case AST_EXP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown expression\n", __func__);
+ return -EINVAL;
+ case AST_EXP_STRING:
+ print_tabs(stream, indent);
+ fprintf(stream, "<string value=\"%s\"/>\n",
+ node->u.expression.u.string);
+ break;
+ case AST_EXP_CONSTANT:
+ print_tabs(stream, indent);
+ fprintf(stream, "<constant value=\"%" PRIu64 "\"/>\n",
+ node->u.expression.u.constant);
+ break;
+ case AST_EXP_FLOAT_CONSTANT:
+ print_tabs(stream, indent);
+ fprintf(stream, "<float_constant value=\"%lg\"/>\n",
+ node->u.expression.u.float_constant);
+ break;
+ case AST_EXP_IDENTIFIER: /* fall-through */
+ case AST_EXP_GLOBAL_IDENTIFIER:
+ print_tabs(stream, indent);
+ fprintf(stream, "<%s value=\"%s\"/>\n",
+ node->u.expression.type == AST_EXP_IDENTIFIER ?
+ "identifier" : "global_identifier",
+ node->u.expression.u.identifier);
+ iter_node = node->u.expression.next;
+ while (iter_node) {
+ print_tabs(stream, indent);
+ fprintf(stream, "<bracket>\n");
+ if (recursive_visit_print_expression(iter_node,
+ stream, indent + 1)) {
+ return -EINVAL;
+ }
+ print_tabs(stream, indent);
+ fprintf(stream, "</bracket>\n");
+ iter_node = iter_node->u.expression.next;
+
+ }
+ break;
+ case AST_EXP_NESTED:
+ return recursive_visit_print(node->u.expression.u.child,
+ stream, indent + 1);
+ }
+ return 0;
+}
+
+
+static
+int recursive_visit_print(struct filter_node *node, FILE *stream, int indent)
+{
+ int ret;
+
+ if (!node) {
+ fprintf(stderr, "[error] %s: NULL child\n", __func__);
+ return -EINVAL;
+ }
+ switch (node->type) {
+ case NODE_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown node type\n", __func__);
+ return -EINVAL;
+ case NODE_ROOT:
+ print_tabs(stream, indent);
+ fprintf(stream, "<root>\n");
+ ret = recursive_visit_print(node->u.root.child, stream,
+ indent + 1);
+ print_tabs(stream, indent);
+ fprintf(stream, "</root>\n");
+ return ret;
+ case NODE_EXPRESSION:
+ print_tabs(stream, indent);
+ fprintf(stream, "<expression>\n");
+ ret = recursive_visit_print_expression(node, stream,
+ indent + 1);
+ print_tabs(stream, indent);
+ fprintf(stream, "</expression>\n");
+ return ret;
+ case NODE_OP:
+ print_tabs(stream, indent);
+ fprintf(stream, "<op type=");
+ switch (node->u.op.type) {
+ case AST_OP_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown op\n", __func__);
+ return -EINVAL;
+ case AST_OP_MUL:
+ fprintf(stream, "\"*\"");
+ break;
+ case AST_OP_DIV:
+ fprintf(stream, "\"/\"");
+ break;
+ case AST_OP_MOD:
+ fprintf(stream, "\"%%\"");
+ break;
+ case AST_OP_PLUS:
+ fprintf(stream, "\"+\"");
+ break;
+ case AST_OP_MINUS:
+ fprintf(stream, "\"-\"");
+ break;
+ case AST_OP_BIT_RSHIFT:
+ fprintf(stream, "\">>\"");
+ break;
+ case AST_OP_BIT_LSHIFT:
+ fprintf(stream, "\"<<\"");
+ break;
+ case AST_OP_AND:
+ fprintf(stream, "\"&&\"");
+ break;
+ case AST_OP_OR:
+ fprintf(stream, "\"||\"");
+ break;
+ case AST_OP_BIT_AND:
+ fprintf(stream, "\"&\"");
+ break;
+ case AST_OP_BIT_OR:
+ fprintf(stream, "\"|\"");
+ break;
+ case AST_OP_BIT_XOR:
+ fprintf(stream, "\"^\"");
+ break;
+
+ case AST_OP_EQ:
+ fprintf(stream, "\"==\"");
+ break;
+ case AST_OP_NE:
+ fprintf(stream, "\"!=\"");
+ break;
+ case AST_OP_GT:
+ fprintf(stream, "\">\"");
+ break;
+ case AST_OP_LT:
+ fprintf(stream, "\"<\"");
+ break;
+ case AST_OP_GE:
+ fprintf(stream, "\">=\"");
+ break;
+ case AST_OP_LE:
+ fprintf(stream, "\"<=\"");
+ break;
+ }
+ fprintf(stream, ">\n");
+ ret = recursive_visit_print(node->u.op.lchild,
+ stream, indent + 1);
+ if (ret)
+ return ret;
+ ret = recursive_visit_print(node->u.op.rchild,
+ stream, indent + 1);
+ if (ret)
+ return ret;
+ print_tabs(stream, indent);
+ fprintf(stream, "</op>\n");
+ return ret;
+ case NODE_UNARY_OP:
+ print_tabs(stream, indent);
+ fprintf(stream, "<unary_op type=");
+ switch (node->u.unary_op.type) {
+ case AST_UNARY_UNKNOWN:
+ default:
+ fprintf(stderr, "[error] %s: unknown unary_op\n", __func__);
+ return -EINVAL;
+ case AST_UNARY_PLUS:
+ fprintf(stream, "\"+\"");
+ break;
+ case AST_UNARY_MINUS:
+ fprintf(stream, "\"-\"");
+ break;
+ case AST_UNARY_NOT:
+ fprintf(stream, "\"!\"");
+ break;
+ case AST_UNARY_BIT_NOT:
+ fprintf(stream, "\"~\"");
+ break;
+ }
+ fprintf(stream, ">\n");
+ ret = recursive_visit_print(node->u.unary_op.child,
+ stream, indent + 1);
+ print_tabs(stream, indent);
+ fprintf(stream, "</unary_op>\n");
+ return ret;
+ }
+ return 0;
+}
+
+LTTNG_HIDDEN
+int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream,
+ int indent)
+{
+ return recursive_visit_print(&ctx->ast->root, stream, indent);
+}
--- /dev/null
+/*
+ * Copyright 2012 (C) Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#ifndef _LTTNG_CTL_MEMSTREAM_H
+#define _LTTNG_CTL_MEMSTREAM_H
+
+#ifdef LTTNG_HAVE_FMEMOPEN
+#include <stdio.h>
+
+static inline
+FILE *lttng_fmemopen(void *buf, size_t size, const char *mode)
+{
+ return fmemopen(buf, size, mode);
+}
+
+#else /* LTTNG_HAVE_FMEMOPEN */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * Fallback for systems which don't have fmemopen. Copy buffer to a
+ * temporary file, and use that file as FILE * input.
+ */
+static inline
+FILE *lttng_fmemopen(void *buf, size_t size, const char *mode)
+{
+ char tmpname[PATH_MAX];
+ size_t len;
+ FILE *fp;
+ int ret;
+
+ /*
+ * Support reading only.
+ */
+ if (strcmp(mode, "rb") != 0) {
+ return NULL;
+ }
+ strncpy(tmpname, "/tmp/lttng-tmp-XXXXXX", PATH_MAX);
+ ret = mkstemp(tmpname);
+ if (ret < 0) {
+ return NULL;
+ }
+ /*
+ * We need to write to the file.
+ */
+ fp = fdopen(ret, "w+");
+ if (!fp) {
+ goto error_unlink;
+ }
+ /* Copy the entire buffer to the file */
+ len = fwrite(buf, sizeof(char), size, fp);
+ if (len != size) {
+ goto error_close;
+ }
+ ret = fseek(fp, 0L, SEEK_SET);
+ if (ret < 0) {
+ PERROR("fseek");
+ goto error_close;
+ }
+ /* We keep the handle open, but can unlink the file on the VFS. */
+ ret = unlink(tmpname);
+ if (ret < 0) {
+ PERROR("unlink");
+ }
+ return fp;
+
+error_close:
+ ret = fclose(fp);
+ if (ret < 0) {
+ PERROR("close");
+ }
+error_unlink:
+ ret = unlink(tmpname);
+ if (ret < 0) {
+ PERROR("unlink");
+ }
+ return NULL;
+}
+
+#endif /* LTTNG_HAVE_FMEMOPEN */
+
+#endif /* _LTTNG_CTL_MEMSTREAM_H */
LTTNG_KERNEL_SESSION_STOP);
}
-int kernctl_filter(int fd, struct lttng_filter_bytecode *filter)
+int kernctl_create_trigger_group(int fd)
+{
+ return LTTNG_IOCTL_NO_CHECK(fd, LTTNG_KERNEL_TRIGGER_GROUP_CREATE);
+}
+
+int kernctl_create_trigger_group_notification_fd(int group_fd)
+{
+ return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_TRIGGER_GROUP_NOTIFICATION_FD);
+}
+
+int kernctl_create_trigger(int group_fd, struct lttng_kernel_trigger *trigger)
+{
+ return LTTNG_IOCTL_NO_CHECK(group_fd, LTTNG_KERNEL_TRIGGER_CREATE, trigger);
+}
+
+int kernctl_capture(int fd, const struct lttng_bytecode *capture)
+{
+ struct lttng_kernel_capture_bytecode *kb;
+ uint32_t len;
+ int ret;
+
+ /* Translate bytecode to kernel bytecode */
+ kb = zmalloc(sizeof(*kb) + capture->len);
+ if (!kb)
+ return -ENOMEM;
+ kb->len = len = capture->len;
+ kb->reloc_offset = capture->reloc_table_offset;
+ kb->seqnum = capture->seqnum;
+ memcpy(kb->data, capture->data, len);
+ ret = LTTNG_IOCTL_CHECK(fd, LTTNG_KERNEL_CAPTURE, kb);
+ free(kb);
+ return ret;
+}
+
+int kernctl_filter(int fd, const struct lttng_bytecode *filter)
{
struct lttng_kernel_filter_bytecode *kb;
uint32_t len;
int kernctl_start_session(int fd);
int kernctl_stop_session(int fd);
+int kernctl_create_trigger_group(int fd);
+
+/* Apply on trigger_group FD*/
+int kernctl_create_trigger_group_notification_fd(int fd);
+int kernctl_create_trigger(int fd, struct lttng_kernel_trigger *trigger);
+
/* Apply on event FD */
-int kernctl_filter(int fd, struct lttng_filter_bytecode *filter);
+int kernctl_filter(int fd, const struct lttng_bytecode *filter);
int kernctl_add_callsite(int fd, struct lttng_kernel_event_callsite *callsite);
+int kernctl_capture(int fd, const struct lttng_bytecode *capture);
int kernctl_tracepoint_list(int fd);
int kernctl_syscall_list(int fd);
#define LTTNG_KERNEL_TRACER_ABI_VERSION \
_IOR(0xF6, 0x4B, struct lttng_kernel_tracer_abi_version)
+#define LTTNG_KERNEL_TRIGGER_GROUP_CREATE _IO(0xF6, 0x4C)
+
+/* Trigger group file descriptor ioctl */
+#define LTTNG_KERNEL_TRIGGER_GROUP_NOTIFICATION_FD _IO(0xF6, 0x30)
+#define LTTNG_KERNEL_TRIGGER_CREATE \
+ _IOW(0xF6, 0x31, struct lttng_kernel_trigger)
+#define LTTNG_KERNEL_CAPTURE _IO(0xF6, 0x32)
+
/* Session FD ioctl */
#define LTTNG_KERNEL_METADATA \
_IOW(0xF6, 0x54, struct lttng_kernel_channel)
} u;
} LTTNG_PACKED;
+#define LTTNG_KERNEL_TRIGGER_PADDING1 16
+#define LTTNG_KERNEL_TRIGGER_PADDING2 LTTNG_KERNEL_SYM_NAME_LEN + 32
+struct lttng_kernel_trigger {
+ uint64_t id;
+ char name[LTTNG_KERNEL_SYM_NAME_LEN]; /* event name */
+ enum lttng_kernel_instrumentation instrumentation;
+ char padding[LTTNG_KERNEL_TRIGGER_PADDING1];
+
+ /* Per instrumentation type configuration */
+ union {
+ struct lttng_kernel_kretprobe kretprobe;
+ struct lttng_kernel_kprobe kprobe;
+ struct lttng_kernel_uprobe uprobe;
+ struct lttng_kernel_function ftrace;
+ char padding[LTTNG_KERNEL_TRIGGER_PADDING2];
+ } u;
+} LTTNG_PACKED;
+
+#define LTTNG_KERNEL_TRIGGER_NOTIFICATION_PADDING 32
+struct lttng_kernel_trigger_notification {
+ uint64_t id;
+ uint16_t capture_buf_size;
+ char padding[LTTNG_KERNEL_TRIGGER_NOTIFICATION_PADDING];
+} LTTNG_PACKED;
+
+#define LTTNG_KERNEL_CAPTURE_BYTECODE_MAX_LEN 65536
+struct lttng_kernel_capture_bytecode {
+ uint32_t len;
+ uint32_t reloc_offset;
+ uint64_t seqnum;
+ char data[0];
+} LTTNG_PACKED;
+
struct lttng_kernel_tracer_version {
uint32_t major;
uint32_t minor;
}
size_before_payload = buf->size;
- ret = lttng_condition_serialize(notification->condition,
- buf);
+ ret = lttng_condition_serialize(notification->condition, buf, NULL);
if (ret) {
goto end;
}
/* struct lttng_evaluation */
evaluation_view = lttng_buffer_view_from_view(&condition_view,
condition_size, -1);
- evaluation_size = lttng_evaluation_create_from_buffer(&evaluation_view,
- &evaluation);
+ evaluation_size = lttng_evaluation_create_from_buffer(condition,
+ &evaluation_view, &evaluation);
if (evaluation_size < 0) {
ret = evaluation_size;
goto end;
#include <assert.h>
#include <signal.h>
+#include <common/bytecode/bytecode.h>
#include <common/lttng-kernel.h>
#include <common/common.h>
#include <common/utils.h>
#include <common/compat/getenv.h>
#include <common/compat/prctl.h>
+#include <common/compat/string.h>
#include <common/unix.h>
#include <common/defaults.h>
#include <common/lttng-elf.h>
#include <lttng/constant.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/filter/filter-ast.h>
+
#include "runas.h"
struct run_as_data;
RUN_AS_RENAMEAT,
RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET,
RUN_AS_EXTRACT_SDT_PROBE_OFFSETS,
+ RUN_AS_GENERATE_FILTER_BYTECODE,
};
struct run_as_mkdir_data {
char provider_name[LTTNG_SYMBOL_NAME_LEN];
} LTTNG_PACKED;
+struct run_as_generate_filter_bytecode_data {
+ char filter_expression[LTTNG_FILTER_MAX_LEN];
+} LTTNG_PATCKED;
+
struct run_as_rename_data {
/*
* [0] = old_dirfd
uint64_t offsets[LTTNG_KERNEL_MAX_UPROBE_NUM];
} LTTNG_PACKED;
+struct run_as_generate_filter_bytecode_ret {
+ /* A lttng_bytecode_filter strcut with "dynamic" payload */
+ char bytecode[LTTNG_FILTER_MAX_LEN];
+} LTTNG_PACKED;
+
struct run_as_data {
enum run_as_cmd cmd;
union {
struct run_as_rename_data rename;
struct run_as_extract_elf_symbol_offset_data extract_elf_symbol_offset;
struct run_as_extract_sdt_probe_offsets_data extract_sdt_probe_offsets;
+ struct run_as_generate_filter_bytecode_data generate_filter_bytecode;
} u;
uid_t uid;
gid_t gid;
struct run_as_open_ret open;
struct run_as_extract_elf_symbol_offset_ret extract_elf_symbol_offset;
struct run_as_extract_sdt_probe_offsets_ret extract_sdt_probe_offsets;
+ struct run_as_generate_filter_bytecode_ret generate_filter_bytecode;
} u;
int _errno;
bool _error;
.out_fd_count = 0,
.use_cwd_fd = false,
},
+ [RUN_AS_GENERATE_FILTER_BYTECODE] = {
+ .in_fds_offset = -1,
+ .in_fd_count = 0,
+ .out_fds_offset = -1,
+ .out_fd_count = 0,
+ .use_cwd_fd = false,
+ },
};
struct run_as_worker {
}
#endif
+static
+int _generate_filter_bytecode(struct run_as_data *data,
+ struct run_as_ret *ret_value) {
+ int ret = 0;
+ const char *filter_expression = NULL;
+ struct filter_parser_ctx *ctx = NULL;
+
+ ret_value->_error = false;
+
+ filter_expression = data->u.generate_filter_bytecode.filter_expression;
+
+ if (lttng_strnlen(filter_expression, LTTNG_FILTER_MAX_LEN - 1) == LTTNG_FILTER_MAX_LEN - 1) {
+ ret_value->_error = true;
+ ret = -1;
+ goto end;
+ }
+
+ ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx);
+ if (ret < 0) {
+ ret_value->_error = true;
+ ret = -1;
+ goto end;
+ }
+
+ DBG("Size of bytecode generated: %u bytes.",
+ bytecode_get_len(&ctx->bytecode->b));
+
+ /* Copy the lttng_bytecode_filter object to the return structure */
+ memcpy(ret_value->u.generate_filter_bytecode.bytecode,
+ &ctx->bytecode->b,
+ sizeof(ctx->bytecode->b) +
+ bytecode_get_len(&ctx->bytecode->b));
+
+end:
+ if (ctx) {
+ filter_bytecode_free(ctx);
+ filter_ir_free(ctx);
+ filter_parser_ctx_free(ctx);
+ }
+ return ret;
+}
static
run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd)
{
return _extract_elf_symbol_offset;
case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
return _extract_sdt_probe_offsets;
+ case RUN_AS_GENERATE_FILTER_BYTECODE:
+ return _generate_filter_bytecode;
default:
ERR("Unknown command %d", (int) cmd);
return NULL;
ssize_t len;
unsigned int i;
+ if (fd_count == 0) {
+ /* Nothing to send */
+ return 0;
+ }
+
for (i = 0; i < fd_count; i++) {
if (fds[i] < 0) {
ERR("Attempt to send invalid file descriptor to master (fd = %i)",
unsigned int i;
ssize_t len;
+ if (fd_count == 0) {
+ /* Nothing to receive */
+ goto end;
+ }
+
len = lttcomm_recv_fds_unix_sock(sock, fds, fd_count);
if (len == 0) {
ret = -1;
return ret;
}
+LTTNG_HIDDEN
+int run_as_generate_filter_bytecode(const char *filter_expression,
+ uid_t uid,
+ gid_t gid,
+ struct lttng_bytecode **bytecode)
+{
+ int ret;
+ struct run_as_data data = {};
+ struct run_as_ret run_as_ret = {};
+ const struct lttng_bytecode *view_bytecode = NULL;
+ struct lttng_bytecode *local_bytecode = NULL;
+
+ DBG3("generate_filter_bytecode() from expression=\"%s\" for uid %d and gid %d",
+ filter_expression, (int) uid, (int) gid);
+
+ ret = lttng_strncpy(data.u.generate_filter_bytecode.filter_expression, filter_expression,
+ sizeof(data.u.generate_filter_bytecode.filter_expression));
+ if (ret) {
+ goto error;
+ }
+
+ run_as(RUN_AS_GENERATE_FILTER_BYTECODE, &data, &run_as_ret, uid, gid);
+ errno = run_as_ret._errno;
+ if (run_as_ret._error) {
+ ret = -1;
+ goto error;
+ }
+
+ view_bytecode = (const struct lttng_bytecode *) run_as_ret.u.generate_filter_bytecode.bytecode;
+
+ local_bytecode = zmalloc(sizeof(*local_bytecode) + view_bytecode->len);
+ if (!local_bytecode) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ memcpy(local_bytecode, run_as_ret.u.generate_filter_bytecode.bytecode,
+ sizeof(*local_bytecode) + view_bytecode->len);
+ *bytecode = local_bytecode;
+error:
+ return ret;
+
+}
+
LTTNG_HIDDEN
int run_as_create_worker(const char *procname,
post_fork_cleanup_cb clean_up_func,
#include <unistd.h>
#include <common/macros.h>
+#include <common/sessiond-comm/sessiond-comm.h>
/*
* The run-as process is launched by forking without an exec*() call. This means
const char* probe_name, uid_t uid, gid_t gid,
uint64_t **offsets, uint32_t *num_offset);
LTTNG_HIDDEN
+int run_as_generate_filter_bytecode(const char *filter_expression,
+ uid_t uid,
+ gid_t gid,
+ struct lttng_bytecode **bytecode);
+LTTNG_HIDDEN
int run_as_create_worker(const char *procname,
post_fork_cleanup_cb clean_up_func, void *clean_up_user_data);
LTTNG_HIDDEN
+++ /dev/null
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/session-consumed-size-internal.h>
-#include <lttng/constant.h>
-#include <common/macros.h>
-#include <common/error.h>
-#include <assert.h>
-#include <math.h>
-#include <float.h>
-#include <time.h>
-
-#define IS_CONSUMED_SIZE_CONDITION(condition) ( \
- lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
- )
-
-#define IS_CONSUMED_SIZE_EVALUATION(evaluation) ( \
- lttng_evaluation_get_type(evaluation) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
- )
-
-static
-void lttng_condition_session_consumed_size_destroy(struct lttng_condition *condition)
-{
- struct lttng_condition_session_consumed_size *consumed_size;
-
- consumed_size = container_of(condition,
- struct lttng_condition_session_consumed_size, parent);
-
- free(consumed_size->session_name);
- free(consumed_size);
-}
-
-static
-bool lttng_condition_session_consumed_size_validate(
- const struct lttng_condition *condition)
-{
- bool valid = false;
- struct lttng_condition_session_consumed_size *consumed;
-
- if (!condition) {
- goto end;
- }
-
- consumed = container_of(condition, struct lttng_condition_session_consumed_size,
- parent);
- if (!consumed->session_name) {
- ERR("Invalid session consumed size condition: a target session name must be set.");
- goto end;
- }
- if (!consumed->consumed_threshold_bytes.set) {
- ERR("Invalid session consumed size condition: a threshold must be set.");
- goto end;
- }
-
- valid = true;
-end:
- return valid;
-}
-
-static
-int lttng_condition_session_consumed_size_serialize(
- const struct lttng_condition *condition,
- struct lttng_dynamic_buffer *buf)
-{
- int ret;
- size_t session_name_len;
- struct lttng_condition_session_consumed_size *consumed;
- struct lttng_condition_session_consumed_size_comm consumed_comm;
-
- if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
- ret = -1;
- goto end;
- }
-
- DBG("Serializing session consumed size condition");
- consumed = container_of(condition,
- struct lttng_condition_session_consumed_size,
- parent);
-
- session_name_len = strlen(consumed->session_name) + 1;
- if (session_name_len > LTTNG_NAME_MAX) {
- ret = -1;
- goto end;
- }
-
- consumed_comm.consumed_threshold_bytes =
- consumed->consumed_threshold_bytes.value;
- consumed_comm.session_name_len = (uint32_t) session_name_len;
-
- ret = lttng_dynamic_buffer_append(buf, &consumed_comm,
- sizeof(consumed_comm));
- if (ret) {
- goto end;
- }
- ret = lttng_dynamic_buffer_append(buf, consumed->session_name,
- session_name_len);
- if (ret) {
- goto end;
- }
-end:
- return ret;
-}
-
-static
-bool lttng_condition_session_consumed_size_is_equal(const struct lttng_condition *_a,
- const struct lttng_condition *_b)
-{
- bool is_equal = false;
- struct lttng_condition_session_consumed_size *a, *b;
-
- a = container_of(_a, struct lttng_condition_session_consumed_size, parent);
- b = container_of(_b, struct lttng_condition_session_consumed_size, parent);
-
- if (a->consumed_threshold_bytes.set && b->consumed_threshold_bytes.set) {
- uint64_t a_value, b_value;
-
- a_value = a->consumed_threshold_bytes.value;
- b_value = b->consumed_threshold_bytes.value;
- if (a_value != b_value) {
- goto end;
- }
- }
-
- assert(a->session_name);
- assert(b->session_name);
- if (strcmp(a->session_name, b->session_name)) {
- goto end;
- }
-
- is_equal = true;
-end:
- return is_equal;
-}
-
-struct lttng_condition *lttng_condition_session_consumed_size_create(void)
-{
- struct lttng_condition_session_consumed_size *condition;
-
- condition = zmalloc(sizeof(struct lttng_condition_session_consumed_size));
- if (!condition) {
- return NULL;
- }
-
- lttng_condition_init(&condition->parent, LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE);
- condition->parent.validate = lttng_condition_session_consumed_size_validate;
- condition->parent.serialize = lttng_condition_session_consumed_size_serialize;
- condition->parent.equal = lttng_condition_session_consumed_size_is_equal;
- condition->parent.destroy = lttng_condition_session_consumed_size_destroy;
- return &condition->parent;
-}
-
-static
-ssize_t init_condition_from_buffer(struct lttng_condition *condition,
- const struct lttng_buffer_view *src_view)
-{
- ssize_t ret, condition_size;
- enum lttng_condition_status status;
- const struct lttng_condition_session_consumed_size_comm *condition_comm;
- const char *session_name;
- struct lttng_buffer_view names_view;
-
- if (src_view->size < sizeof(*condition_comm)) {
- ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
- ret = -1;
- goto end;
- }
-
- condition_comm = (const struct lttng_condition_session_consumed_size_comm *) src_view->data;
- names_view = lttng_buffer_view_from_view(src_view,
- sizeof(*condition_comm), -1);
-
- if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
- ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
- ret = -1;
- goto end;
- }
-
- if (names_view.size < condition_comm->session_name_len) {
- ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
- ret = -1;
- goto end;
- }
-
- status = lttng_condition_session_consumed_size_set_threshold(condition,
- condition_comm->consumed_threshold_bytes);
- if (status != LTTNG_CONDITION_STATUS_OK) {
- ERR("Failed to initialize session consumed size condition threshold");
- ret = -1;
- goto end;
- }
-
- session_name = names_view.data;
- if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
- ERR("Malformed session name encountered in condition buffer");
- ret = -1;
- goto end;
- }
-
- status = lttng_condition_session_consumed_size_set_session_name(condition,
- session_name);
- if (status != LTTNG_CONDITION_STATUS_OK) {
- ERR("Failed to set session consumed size condition's session name");
- ret = -1;
- goto end;
- }
-
- if (!lttng_condition_validate(condition)) {
- ret = -1;
- goto end;
- }
-
- condition_size = sizeof(*condition_comm) +
- (ssize_t) condition_comm->session_name_len;
- ret = condition_size;
-end:
- return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_session_consumed_size_create_from_buffer(
- const struct lttng_buffer_view *view,
- struct lttng_condition **_condition)
-{
- ssize_t ret;
- struct lttng_condition *condition =
- lttng_condition_session_consumed_size_create();
-
- if (!_condition || !condition) {
- ret = -1;
- goto error;
- }
-
- ret = init_condition_from_buffer(condition, view);
- if (ret < 0) {
- goto error;
- }
-
- *_condition = condition;
- return ret;
-error:
- lttng_condition_destroy(condition);
- return ret;
-}
-
-static
-struct lttng_evaluation *create_evaluation_from_buffer(
- const struct lttng_buffer_view *view)
-{
- const struct lttng_evaluation_session_consumed_size_comm *comm =
- (const struct lttng_evaluation_session_consumed_size_comm *) view->data;
- struct lttng_evaluation *evaluation = NULL;
-
- if (view->size < sizeof(*comm)) {
- goto end;
- }
-
- evaluation = lttng_evaluation_session_consumed_size_create(
- comm->session_consumed);
-end:
- return evaluation;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_evaluation_session_consumed_size_create_from_buffer(
- const struct lttng_buffer_view *view,
- struct lttng_evaluation **_evaluation)
-{
- ssize_t ret;
- struct lttng_evaluation *evaluation = NULL;
-
- if (!_evaluation) {
- ret = -1;
- goto error;
- }
-
- evaluation = create_evaluation_from_buffer(view);
- if (!evaluation) {
- ret = -1;
- goto error;
- }
-
- *_evaluation = evaluation;
- ret = sizeof(struct lttng_evaluation_session_consumed_size_comm);
- return ret;
-error:
- lttng_evaluation_destroy(evaluation);
- return ret;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_get_threshold(
- const struct lttng_condition *condition,
- uint64_t *consumed_threshold_bytes)
-{
- struct lttng_condition_session_consumed_size *consumed;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !consumed_threshold_bytes) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- consumed = container_of(condition, struct lttng_condition_session_consumed_size,
- parent);
- if (!consumed->consumed_threshold_bytes.set) {
- status = LTTNG_CONDITION_STATUS_UNSET;
- goto end;
- }
- *consumed_threshold_bytes = consumed->consumed_threshold_bytes.value;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_set_threshold(
- struct lttng_condition *condition, uint64_t consumed_threshold_bytes)
-{
- struct lttng_condition_session_consumed_size *consumed;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- consumed = container_of(condition, struct lttng_condition_session_consumed_size,
- parent);
- consumed->consumed_threshold_bytes.set = true;
- consumed->consumed_threshold_bytes.value = consumed_threshold_bytes;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_get_session_name(
- const struct lttng_condition *condition,
- const char **session_name)
-{
- struct lttng_condition_session_consumed_size *consumed;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !session_name) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- consumed = container_of(condition, struct lttng_condition_session_consumed_size,
- parent);
- if (!consumed->session_name) {
- status = LTTNG_CONDITION_STATUS_UNSET;
- goto end;
- }
- *session_name = consumed->session_name;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_set_session_name(
- struct lttng_condition *condition, const char *session_name)
-{
- char *session_name_copy;
- struct lttng_condition_session_consumed_size *consumed;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) ||
- !session_name || strlen(session_name) == 0) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- consumed = container_of(condition, struct lttng_condition_session_consumed_size,
- parent);
- session_name_copy = strdup(session_name);
- if (!session_name_copy) {
- status = LTTNG_CONDITION_STATUS_ERROR;
- goto end;
- }
-
- if (consumed->session_name) {
- free(consumed->session_name);
- }
- consumed->session_name = session_name_copy;
-end:
- return status;
-}
-
-static
-int lttng_evaluation_session_consumed_size_serialize(
- const struct lttng_evaluation *evaluation,
- struct lttng_dynamic_buffer *buf)
-{
- struct lttng_evaluation_session_consumed_size *consumed;
- struct lttng_evaluation_session_consumed_size_comm comm;
-
- consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
- parent);
- comm.session_consumed = consumed->session_consumed;
- return lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
-}
-
-static
-void lttng_evaluation_session_consumed_size_destroy(
- struct lttng_evaluation *evaluation)
-{
- struct lttng_evaluation_session_consumed_size *consumed;
-
- consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
- parent);
- free(consumed);
-}
-
-LTTNG_HIDDEN
-struct lttng_evaluation *lttng_evaluation_session_consumed_size_create(
- uint64_t consumed)
-{
- struct lttng_evaluation_session_consumed_size *consumed_eval;
-
- consumed_eval = zmalloc(sizeof(struct lttng_evaluation_session_consumed_size));
- if (!consumed_eval) {
- goto end;
- }
-
- consumed_eval->parent.type = LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE;
- consumed_eval->session_consumed = consumed;
- consumed_eval->parent.serialize = lttng_evaluation_session_consumed_size_serialize;
- consumed_eval->parent.destroy = lttng_evaluation_session_consumed_size_destroy;
-end:
- return &consumed_eval->parent;
-}
-
-enum lttng_evaluation_status
-lttng_evaluation_session_consumed_size_get_consumed_size(
- const struct lttng_evaluation *evaluation,
- uint64_t *session_consumed)
-{
- struct lttng_evaluation_session_consumed_size *consumed;
- enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
- if (!evaluation || !IS_CONSUMED_SIZE_EVALUATION(evaluation) ||
- !session_consumed) {
- status = LTTNG_EVALUATION_STATUS_INVALID;
- goto end;
- }
-
- consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
- parent);
- *session_consumed = consumed->session_consumed;
-end:
- return status;
-}
+++ /dev/null
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/session-rotation-internal.h>
-#include <lttng/location-internal.h>
-#include <common/macros.h>
-#include <common/error.h>
-#include <assert.h>
-#include <stdbool.h>
-
-static
-bool lttng_condition_session_rotation_validate(
- const struct lttng_condition *condition);
-static
-int lttng_condition_session_rotation_serialize(
- const struct lttng_condition *condition,
- struct lttng_dynamic_buffer *buf);
-static
-bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
- const struct lttng_condition *_b);
-static
-void lttng_condition_session_rotation_destroy(
- struct lttng_condition *condition);
-
-static const
-struct lttng_condition rotation_condition_template = {
- /* .type omitted; shall be set on creation. */
- .validate = lttng_condition_session_rotation_validate,
- .serialize = lttng_condition_session_rotation_serialize,
- .equal = lttng_condition_session_rotation_is_equal,
- .destroy = lttng_condition_session_rotation_destroy,
-};
-
-static
-int lttng_evaluation_session_rotation_serialize(
- const struct lttng_evaluation *evaluation,
- struct lttng_dynamic_buffer *buf);
-static
-void lttng_evaluation_session_rotation_destroy(
- struct lttng_evaluation *evaluation);
-
-static const
-struct lttng_evaluation rotation_evaluation_template = {
- /* .type omitted; shall be set on creation. */
- .serialize = lttng_evaluation_session_rotation_serialize,
- .destroy = lttng_evaluation_session_rotation_destroy,
-};
-
-static
-bool is_rotation_condition(const struct lttng_condition *condition)
-{
- enum lttng_condition_type type = lttng_condition_get_type(condition);
-
- return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
- type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
-}
-
-static
-bool is_rotation_evaluation(const struct lttng_evaluation *evaluation)
-{
- enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
-
- return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
- type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
-}
-
-static
-bool lttng_condition_session_rotation_validate(
- const struct lttng_condition *condition)
-{
- bool valid = false;
- struct lttng_condition_session_rotation *rotation;
-
- if (!condition) {
- goto end;
- }
-
- rotation = container_of(condition,
- struct lttng_condition_session_rotation, parent);
- if (!rotation->session_name) {
- ERR("Invalid session rotation condition: a target session name must be set.");
- goto end;
- }
-
- valid = true;
-end:
- return valid;
-}
-
-static
-int lttng_condition_session_rotation_serialize(
- const struct lttng_condition *condition,
- struct lttng_dynamic_buffer *buf)
-{
- int ret;
- size_t session_name_len;
- struct lttng_condition_session_rotation *rotation;
- struct lttng_condition_session_rotation_comm rotation_comm;
-
- if (!condition || !is_rotation_condition(condition)) {
- ret = -1;
- goto end;
- }
-
- DBG("Serializing session rotation condition");
- rotation = container_of(condition, struct lttng_condition_session_rotation,
- parent);
-
- session_name_len = strlen(rotation->session_name) + 1;
- if (session_name_len > LTTNG_NAME_MAX) {
- ret = -1;
- goto end;
- }
-
- rotation_comm.session_name_len = session_name_len;
- ret = lttng_dynamic_buffer_append(buf, &rotation_comm,
- sizeof(rotation_comm));
- if (ret) {
- goto end;
- }
- ret = lttng_dynamic_buffer_append(buf, rotation->session_name,
- session_name_len);
- if (ret) {
- goto end;
- }
-end:
- return ret;
-}
-
-static
-bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
- const struct lttng_condition *_b)
-{
- bool is_equal = false;
- struct lttng_condition_session_rotation *a, *b;
-
- a = container_of(_a, struct lttng_condition_session_rotation, parent);
- b = container_of(_b, struct lttng_condition_session_rotation, parent);
-
- /* Both session names must be set or both must be unset. */
- if ((a->session_name && !b->session_name) ||
- (!a->session_name && b->session_name)) {
- WARN("Comparing session rotation conditions with uninitialized session names.");
- goto end;
- }
-
- if (a->session_name && b->session_name &&
- strcmp(a->session_name, b->session_name)) {
- goto end;
- }
-
- is_equal = true;
-end:
- return is_equal;
-}
-
-static
-void lttng_condition_session_rotation_destroy(
- struct lttng_condition *condition)
-{
- struct lttng_condition_session_rotation *rotation;
-
- rotation = container_of(condition,
- struct lttng_condition_session_rotation, parent);
-
- free(rotation->session_name);
- free(rotation);
-}
-
-static
-struct lttng_condition *lttng_condition_session_rotation_create(
- enum lttng_condition_type type)
-{
- struct lttng_condition_session_rotation *condition;
-
- condition = zmalloc(sizeof(struct lttng_condition_session_rotation));
- if (!condition) {
- return NULL;
- }
-
- memcpy(&condition->parent, &rotation_condition_template,
- sizeof(condition->parent));
- lttng_condition_init(&condition->parent, type);
- return &condition->parent;
-}
-
-struct lttng_condition *lttng_condition_session_rotation_ongoing_create(void)
-{
- return lttng_condition_session_rotation_create(
- LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
-}
-
-struct lttng_condition *lttng_condition_session_rotation_completed_create(void)
-{
- return lttng_condition_session_rotation_create(
- LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
-}
-
-static
-ssize_t init_condition_from_buffer(struct lttng_condition *condition,
- const struct lttng_buffer_view *src_view)
-{
- ssize_t ret, condition_size;
- enum lttng_condition_status status;
- const struct lttng_condition_session_rotation_comm *condition_comm;
- const char *session_name;
- struct lttng_buffer_view name_view;
-
- if (src_view->size < sizeof(*condition_comm)) {
- ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
- ret = -1;
- goto end;
- }
-
- condition_comm = (const struct lttng_condition_session_rotation_comm *) src_view->data;
- name_view = lttng_buffer_view_from_view(src_view,
- sizeof(*condition_comm), -1);
-
- if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
- ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
- ret = -1;
- goto end;
- }
-
- if (name_view.size < condition_comm->session_name_len) {
- ERR("Failed to initialize from malformed condition buffer: buffer too short to contain session name");
- ret = -1;
- goto end;
- }
-
- session_name = name_view.data;
- if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
- ERR("Malformed session name encountered in condition buffer");
- ret = -1;
- goto end;
- }
-
- status = lttng_condition_session_rotation_set_session_name(condition,
- session_name);
- if (status != LTTNG_CONDITION_STATUS_OK) {
- ERR("Failed to set buffer consumed session name");
- ret = -1;
- goto end;
- }
-
- if (!lttng_condition_validate(condition)) {
- ret = -1;
- goto end;
- }
-
- condition_size = sizeof(*condition_comm) +
- (ssize_t) condition_comm->session_name_len;
- ret = condition_size;
-end:
- return ret;
-}
-
-static
-ssize_t lttng_condition_session_rotation_create_from_buffer(
- const struct lttng_buffer_view *view,
- struct lttng_condition **_condition,
- enum lttng_condition_type type)
-{
- ssize_t ret;
- struct lttng_condition *condition = NULL;
-
- switch (type) {
- case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
- condition = lttng_condition_session_rotation_ongoing_create();
- break;
- case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
- condition = lttng_condition_session_rotation_completed_create();
- break;
- default:
- ret = -1;
- goto error;
- }
-
- if (!_condition || !condition) {
- ret = -1;
- goto error;
- }
-
- ret = init_condition_from_buffer(condition, view);
- if (ret < 0) {
- goto error;
- }
-
- *_condition = condition;
- return ret;
-error:
- lttng_condition_destroy(condition);
- return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_session_rotation_ongoing_create_from_buffer(
- const struct lttng_buffer_view *view,
- struct lttng_condition **condition)
-{
- return lttng_condition_session_rotation_create_from_buffer(view,
- condition,
- LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_condition_session_rotation_completed_create_from_buffer(
- const struct lttng_buffer_view *view,
- struct lttng_condition **condition)
-{
- return lttng_condition_session_rotation_create_from_buffer(view,
- condition,
- LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
-}
-
-static
-struct lttng_evaluation *lttng_evaluation_session_rotation_create(
- enum lttng_condition_type type, uint64_t id,
- struct lttng_trace_archive_location *location)
-{
- struct lttng_evaluation_session_rotation *evaluation;
-
- evaluation = zmalloc(sizeof(struct lttng_evaluation_session_rotation));
- if (!evaluation) {
- return NULL;
- }
-
- memcpy(&evaluation->parent, &rotation_evaluation_template,
- sizeof(evaluation->parent));
- lttng_evaluation_init(&evaluation->parent, type);
- evaluation->id = id;
- evaluation->location = location;
- return &evaluation->parent;
-}
-
-static
-ssize_t create_evaluation_from_buffer(
- enum lttng_condition_type type,
- const struct lttng_buffer_view *view,
- struct lttng_evaluation **_evaluation)
-{
- ssize_t ret, size;
- struct lttng_evaluation *evaluation = NULL;
- struct lttng_trace_archive_location *location = NULL;
- const struct lttng_evaluation_session_rotation_comm *comm =
- (const struct lttng_evaluation_session_rotation_comm *) view->data;
- struct lttng_buffer_view location_view;
-
- if (view->size < sizeof(*comm)) {
- goto error;
- }
-
- size = sizeof(*comm);
- if (comm->has_location) {
- location_view = lttng_buffer_view_from_view(view, sizeof(*comm),
- -1);
- if (!location_view.data) {
- goto error;
- }
-
- ret = lttng_trace_archive_location_create_from_buffer(
- &location_view, &location);
- if (ret < 0) {
- goto error;
- }
- size += ret;
- }
-
- evaluation = lttng_evaluation_session_rotation_create(type, comm->id,
- location);
- if (!evaluation) {
- goto error;
- }
-
- ret = size;
- *_evaluation = evaluation;
- return ret;
-error:
- lttng_trace_archive_location_destroy(location);
- evaluation = NULL;
- return -1;
-}
-
-static
-ssize_t lttng_evaluation_session_rotation_create_from_buffer(
- enum lttng_condition_type type,
- const struct lttng_buffer_view *view,
- struct lttng_evaluation **_evaluation)
-{
- ssize_t ret;
- struct lttng_evaluation *evaluation = NULL;
-
- if (!_evaluation) {
- ret = -1;
- goto error;
- }
-
- ret = create_evaluation_from_buffer(type, view, &evaluation);
- if (ret < 0) {
- goto error;
- }
-
- *_evaluation = evaluation;
- return ret;
-error:
- lttng_evaluation_destroy(evaluation);
- return ret;
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_evaluation_session_rotation_ongoing_create_from_buffer(
- const struct lttng_buffer_view *view,
- struct lttng_evaluation **evaluation)
-{
- return lttng_evaluation_session_rotation_create_from_buffer(
- LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING,
- view, evaluation);
-}
-
-LTTNG_HIDDEN
-ssize_t lttng_evaluation_session_rotation_completed_create_from_buffer(
- const struct lttng_buffer_view *view,
- struct lttng_evaluation **evaluation)
-{
- return lttng_evaluation_session_rotation_create_from_buffer(
- LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED,
- view, evaluation);
-}
-
-LTTNG_HIDDEN
-struct lttng_evaluation *lttng_evaluation_session_rotation_ongoing_create(
- uint64_t id)
-{
- return lttng_evaluation_session_rotation_create(
- LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING, id,
- NULL);
-}
-
-LTTNG_HIDDEN
-struct lttng_evaluation *lttng_evaluation_session_rotation_completed_create(
- uint64_t id, struct lttng_trace_archive_location *location)
-{
- return lttng_evaluation_session_rotation_create(
- LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED, id,
- location);
-}
-
-enum lttng_condition_status
-lttng_condition_session_rotation_get_session_name(
- const struct lttng_condition *condition,
- const char **session_name)
-{
- struct lttng_condition_session_rotation *rotation;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !is_rotation_condition(condition) || !session_name) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- rotation = container_of(condition, struct lttng_condition_session_rotation,
- parent);
- if (!rotation->session_name) {
- status = LTTNG_CONDITION_STATUS_UNSET;
- goto end;
- }
- *session_name = rotation->session_name;
-end:
- return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_rotation_set_session_name(
- struct lttng_condition *condition, const char *session_name)
-{
- char *session_name_copy;
- struct lttng_condition_session_rotation *rotation;
- enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
- if (!condition || !is_rotation_condition(condition) ||
- !session_name || strlen(session_name) == 0) {
- status = LTTNG_CONDITION_STATUS_INVALID;
- goto end;
- }
-
- rotation = container_of(condition,
- struct lttng_condition_session_rotation, parent);
- session_name_copy = strdup(session_name);
- if (!session_name_copy) {
- status = LTTNG_CONDITION_STATUS_ERROR;
- goto end;
- }
-
- free(rotation->session_name);
- rotation->session_name = session_name_copy;
-end:
- return status;
-}
-
-static
-int lttng_evaluation_session_rotation_serialize(
- const struct lttng_evaluation *evaluation,
- struct lttng_dynamic_buffer *buf)
-{
- int ret;
- struct lttng_evaluation_session_rotation *rotation;
- struct lttng_evaluation_session_rotation_comm comm = { 0 };
-
- rotation = container_of(evaluation,
- struct lttng_evaluation_session_rotation, parent);
- comm.id = rotation->id;
- comm.has_location = !!rotation->location;
- ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
- if (ret) {
- goto end;
- }
- if (!rotation->location) {
- goto end;
- }
- ret = lttng_trace_archive_location_serialize(rotation->location,
- buf);
-end:
- return ret;
-}
-
-static
-void lttng_evaluation_session_rotation_destroy(
- struct lttng_evaluation *evaluation)
-{
- struct lttng_evaluation_session_rotation *rotation;
-
- rotation = container_of(evaluation,
- struct lttng_evaluation_session_rotation, parent);
- lttng_trace_archive_location_destroy(rotation->location);
- free(rotation);
-}
-
-enum lttng_evaluation_status
-lttng_evaluation_session_rotation_get_id(
- const struct lttng_evaluation *evaluation, uint64_t *id)
-{
- const struct lttng_evaluation_session_rotation *rotation;
- enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
- if (!evaluation || !id || !is_rotation_evaluation(evaluation)) {
- status = LTTNG_EVALUATION_STATUS_INVALID;
- goto end;
- }
-
- rotation = container_of(evaluation,
- struct lttng_evaluation_session_rotation, parent);
- *id = rotation->id;
-end:
- return status;
-}
-
-enum lttng_evaluation_status
-lttng_evaluation_session_rotation_completed_get_location(
- const struct lttng_evaluation *evaluation,
- const struct lttng_trace_archive_location **location)
-{
- const struct lttng_evaluation_session_rotation *rotation;
- enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
- if (!evaluation || !location ||
- evaluation->type != LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED) {
- status = LTTNG_EVALUATION_STATUS_INVALID;
- goto end;
- }
-
- rotation = container_of(evaluation,
- struct lttng_evaluation_session_rotation, parent);
- *location = rotation->location;
-end:
- return status;
-}
LTTNG_SESSION_LIST_ROTATION_SCHEDULES = 48,
LTTNG_CREATE_SESSION_EXT = 49,
LTTNG_CLEAR_SESSION = 50,
+ LTTNG_LIST_TRIGGERS = 51,
};
enum lttcomm_relayd_command {
* starts at reloc_table_offset.
*/
#define LTTNG_FILTER_PADDING 32
-struct lttng_filter_bytecode {
+struct lttng_bytecode {
uint32_t len; /* len of data */
uint32_t reloc_table_offset;
uint64_t seqnum;
--- /dev/null
+/*
+ * Copyright (C) 2020 - EfficiOS, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "buffer-view.h"
+#include "dynamic-buffer.h"
+#include "lttng/snapshot.h"
+#include "lttng/snapshot-internal.h"
+#include "snapshot.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+LTTNG_HIDDEN
+bool lttng_snapshot_output_validate(const struct lttng_snapshot_output *output)
+{
+ bool valid = false;
+ size_t len;
+
+ /*
+ * It is mandatory to have a ctrl_url. If there is only one output
+ * URL (in the net://, net6:// or file:// form), it will be in this
+ * field.
+ */
+ len = lttng_strnlen(output->ctrl_url, sizeof(output->ctrl_url));
+ if (len == 0 || len >= sizeof(output->ctrl_url)) {
+ goto end;
+ }
+
+ len = lttng_strnlen(output->data_url, sizeof(output->data_url));
+ if (len >= sizeof(output->data_url)) {
+ goto end;
+ }
+
+ len = lttng_strnlen(output->name, sizeof(output->name));
+ if (len >= sizeof(output->name)) {
+ goto end;
+ }
+
+ valid = true;
+
+end:
+ return valid;
+}
+
+LTTNG_HIDDEN
+bool lttng_snapshot_output_is_equal(
+ const struct lttng_snapshot_output *a,
+ const struct lttng_snapshot_output *b)
+{
+ bool equal = false;
+
+ assert(a);
+ assert(b);
+
+ if (a->max_size != b->max_size) {
+ goto end;
+ }
+
+ if (strcmp(a->name, b->name) != 0) {
+ goto end;
+ }
+
+ if (strcmp(a->ctrl_url, b->ctrl_url) != 0) {
+ goto end;
+ }
+
+ if (strcmp(a->data_url, b->data_url) != 0) {
+ goto end;
+ }
+
+ equal = true;
+
+end:
+ return equal;
+}
+
+/*
+ * This is essentially the same as `struct lttng_snapshot_output`, but packed.
+ */
+struct LTTNG_PACKED lttng_snapshot_output_comm {
+ uint32_t id;
+ uint64_t max_size;
+ char name[LTTNG_NAME_MAX];
+ char ctrl_url[PATH_MAX];
+ char data_url[PATH_MAX];
+};
+
+LTTNG_HIDDEN
+int lttng_snapshot_output_serialize(
+ const struct lttng_snapshot_output *output,
+ struct lttng_dynamic_buffer *buf)
+{
+ struct lttng_snapshot_output_comm comm;
+ int ret;
+
+ comm.id = output->id;
+ comm.max_size = output->max_size;
+
+ ret = lttng_strncpy(comm.name, output->name, sizeof(comm.name));
+ if (ret) {
+ goto end;
+ }
+
+ ret = lttng_strncpy(comm.ctrl_url, output->ctrl_url, sizeof(comm.ctrl_url));
+ if (ret) {
+ goto end;
+ }
+
+ ret = lttng_strncpy(comm.data_url, output->data_url, sizeof(comm.data_url));
+ if (ret) {
+ goto end;
+ }
+
+ ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm));
+ if (ret) {
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_snapshot_output_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_snapshot_output **output_p)
+{
+ const struct lttng_snapshot_output_comm *comm;
+ struct lttng_snapshot_output *output = NULL;
+ int ret;
+
+ if (view->size != sizeof(*comm)) {
+ ret = -1;
+ goto end;
+ }
+
+ output = lttng_snapshot_output_create();
+ if (!output) {
+ ret = -1;
+ goto end;
+ }
+
+ comm = (struct lttng_snapshot_output_comm *) view->data;
+
+ output->id = comm->id;
+ output->max_size = comm->max_size;
+
+ ret = lttng_strncpy(output->name, comm->name, sizeof(output->name));
+ if (ret) {
+ goto end;
+ }
+
+ ret = lttng_strncpy(output->ctrl_url, comm->ctrl_url, sizeof(output->ctrl_url));
+ if (ret) {
+ goto end;
+ }
+
+ ret = lttng_strncpy(output->data_url, comm->data_url, sizeof(output->data_url));
+ if (ret) {
+ goto end;
+ }
+
+ *output_p = output;
+ output = NULL;
+ ret = sizeof(*comm);
+
+end:
+ lttng_snapshot_output_destroy(output);
+ return ret;
+}
--- /dev/null
+#ifndef COMMON_SNAPSHOT_H
+#define COMMON_SNAPSHOT_H
+
+/*
+ * Copyright (C) 2020 - EfficiOS, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "common/macros.h"
+
+#include <stdbool.h>
+
+struct lttng_buffer_view;
+struct lttng_dynamic_buffer;
+struct lttng_snapshot_output;
+
+LTTNG_HIDDEN
+bool lttng_snapshot_output_validate(const struct lttng_snapshot_output *output);
+
+LTTNG_HIDDEN
+bool lttng_snapshot_output_is_equal(
+ const struct lttng_snapshot_output *a,
+ const struct lttng_snapshot_output *b);
+
+LTTNG_HIDDEN
+int lttng_snapshot_output_serialize(
+ const struct lttng_snapshot_output *output,
+ struct lttng_dynamic_buffer *buf);
+
+LTTNG_HIDDEN
+ssize_t lttng_snapshot_output_create_from_buffer(
+ const struct lttng_buffer_view *view,
+ struct lttng_snapshot_output **output_p);
+
+#endif /* COMMON_SNAPSHOT_H */
#include <lttng/trigger/trigger-internal.h>
#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/condition/buffer-usage.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-expr-internal.h>
#include <lttng/action/action-internal.h>
+#include <lttng/domain.h>
#include <common/error.h>
+#include <common/dynamic-array.h>
#include <assert.h>
+#include <inttypes.h>
+
+static void lttng_trigger_set_internal_object_ownership(
+ struct lttng_trigger *trigger)
+{
+ /*
+ * This is necessary to faciliate the object destroy phase. A trigger
+ * created by a client does not OWN the internal objects (condition and
+ * action) but when a trigger object is created on the sessiond side or
+ * for listing triggers (mostly via create_from_buffer) the object is
+ * the owner of the internal objects. Hence, we set the ownership bool
+ * to true, in such case, to facilitate object lifetime management and
+ * internal ownership.
+ *
+ * TODO: I'm open to any other solution
+ */
+ assert(trigger);
+ trigger->owns_internal_objects = true;
+}
+
+static void destroy_lttng_condition_event_rule_capture_bytecode_element(void *ptr)
+{
+ struct lttng_condition_event_rule_capture_bytecode_element *element =
+ ptr;
+ lttng_event_expr_destroy(element->expression);
+ free(element->bytecode);
+ free(element);
+}
LTTNG_HIDDEN
-bool lttng_trigger_validate(struct lttng_trigger *trigger)
+bool lttng_trigger_validate(const struct lttng_trigger *trigger)
{
bool valid;
goto end;
}
+ urcu_ref_init(&trigger->ref);
+ trigger->owns_internal_objects = false;
+ trigger->firing_policy.type = LTTNG_TRIGGER_FIRE_EVERY_N;
+ trigger->firing_policy.threshold = 1;
trigger->condition = condition;
trigger->action = action;
+
+ trigger->creds.set = false;
+
+ lttng_dynamic_pointer_array_init(&trigger->capture_bytecode_set,
+ destroy_lttng_condition_event_rule_capture_bytecode_element);
+
end:
return trigger;
}
return trigger ? trigger->condition : NULL;
}
-LTTNG_HIDDEN
const struct lttng_condition *lttng_trigger_get_const_condition(
const struct lttng_trigger *trigger)
{
- return trigger->condition;
+ return trigger ? trigger->condition : NULL;
}
struct lttng_action *lttng_trigger_get_action(
return trigger ? trigger->action : NULL;
}
-LTTNG_HIDDEN
const struct lttng_action *lttng_trigger_get_const_action(
const struct lttng_trigger *trigger)
{
- return trigger->action;
+ return trigger ? trigger->action : NULL;
}
-void lttng_trigger_destroy(struct lttng_trigger *trigger)
+static void trigger_destroy_ref(struct urcu_ref *ref)
{
- if (!trigger) {
- return;
+ struct lttng_trigger *trigger =
+ container_of(ref, struct lttng_trigger, ref);
+
+ lttng_dynamic_pointer_array_reset(&trigger->capture_bytecode_set);
+
+ if (trigger->owns_internal_objects) {
+ struct lttng_action *action = lttng_trigger_get_action(trigger);
+ struct lttng_condition *condition =
+ lttng_trigger_get_condition(trigger);
+
+ assert(action);
+ assert(condition);
+ lttng_action_destroy(action);
+ lttng_condition_destroy(condition);
}
+ free(trigger->name);
free(trigger);
}
+void lttng_trigger_destroy(struct lttng_trigger *trigger)
+{
+ lttng_trigger_put(trigger);
+}
+
LTTNG_HIDDEN
ssize_t lttng_trigger_create_from_buffer(
const struct lttng_buffer_view *src_view,
struct lttng_trigger **trigger)
{
- ssize_t ret, offset = 0, condition_size, action_size;
+ ssize_t ret, offset = 0, condition_size, action_size, name_size = 0;
+ enum lttng_trigger_status status;
struct lttng_condition *condition = NULL;
struct lttng_action *action = NULL;
const struct lttng_trigger_comm *trigger_comm;
struct lttng_buffer_view condition_view;
struct lttng_buffer_view action_view;
+ struct lttng_buffer_view name_view;
+ const char *name = NULL;
+ unsigned long long firing_threshold;
+ enum lttng_trigger_firing_policy_type firing_policy;
if (!src_view || !trigger) {
ret = -1;
trigger_comm = (const struct lttng_trigger_comm *) src_view->data;
offset += sizeof(*trigger_comm);
+ firing_policy = trigger_comm->policy_type;
+ firing_threshold = trigger_comm->policy_threshold;
+ if (trigger_comm->name_length != 0) {
+ name_view = lttng_buffer_view_from_view(
+ src_view, offset, trigger_comm->name_length);
+ name = name_view.data;
+ if (trigger_comm->name_length == 1 ||
+ name[trigger_comm->name_length - 1] != '\0' ||
+ strlen(name) != trigger_comm->name_length - 1) {
+ ret = -1;
+ goto end;
+ }
+ offset += trigger_comm->name_length;
+ name_size = trigger_comm->name_length;
+ }
+
condition_view = lttng_buffer_view_from_view(src_view, offset, -1);
/* struct lttng_condition */
offset += action_size;
/* Unexpected size of inner-elements; the buffer is corrupted. */
- if ((ssize_t) trigger_comm->length != condition_size + action_size) {
+ if ((ssize_t) trigger_comm->length != condition_size + action_size + name_size) {
ret = -1;
goto error;
}
ret = -1;
goto error;
}
+
+ /* Take ownership of the internal object from there */
+ lttng_trigger_set_internal_object_ownership(*trigger);
+ condition = NULL;
+ action = NULL;
+
+ if (name) {
+ status = lttng_trigger_set_name(*trigger, name);
+ if (status != LTTNG_TRIGGER_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+ }
+
+ status = lttng_trigger_set_firing_policy(*trigger, firing_policy, firing_threshold);
+ if (status != LTTNG_TRIGGER_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
ret = offset;
end:
return ret;
* for the detailed format.
*/
LTTNG_HIDDEN
-int lttng_trigger_serialize(struct lttng_trigger *trigger,
- struct lttng_dynamic_buffer *buf)
+int lttng_trigger_serialize(const struct lttng_trigger *trigger,
+ struct lttng_dynamic_buffer *buf,
+ int *fd_to_send)
{
int ret;
- size_t header_offset, size_before_payload;
+ size_t header_offset, size_before_payload, size_name;
struct lttng_trigger_comm trigger_comm = { 0 };
struct lttng_trigger_comm *header;
header_offset = buf->size;
+
+ if (trigger->name != NULL) {
+ size_name = strlen(trigger->name) + 1;
+ } else {
+ size_name = 0;
+ }
+
+ trigger_comm.name_length = size_name;
+ trigger_comm.policy_type = (uint8_t) trigger->firing_policy.type;
+ trigger_comm.policy_threshold = (uint64_t) trigger->firing_policy.threshold;
+
ret = lttng_dynamic_buffer_append(buf, &trigger_comm,
sizeof(trigger_comm));
if (ret) {
}
size_before_payload = buf->size;
- ret = lttng_condition_serialize(trigger->condition, buf);
+
+ /* Trigger name */
+ ret = lttng_dynamic_buffer_append(buf, trigger->name, size_name);
+ if (ret) {
+ goto end;
+ }
+
+ ret = lttng_condition_serialize(trigger->condition, buf, fd_to_send);
if (ret) {
goto end;
}
end:
return ret;
}
+
+enum lttng_trigger_status lttng_trigger_set_name(struct lttng_trigger *trigger, const char* name)
+{
+ char *name_copy = NULL;
+ enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
+
+ if (!trigger || !name ||
+ strlen(name) == 0) {
+ status = LTTNG_TRIGGER_STATUS_INVALID;
+ goto end;
+ }
+
+ name_copy = strdup(name);
+ if (!name_copy) {
+ status = LTTNG_TRIGGER_STATUS_ERROR;
+ goto end;
+ }
+
+ if (trigger->name) {
+ free(trigger->name);
+ }
+
+ trigger->name = name_copy;
+ name_copy = NULL;
+end:
+ return status;
+}
+
+enum lttng_trigger_status lttng_trigger_get_name(const struct lttng_trigger *trigger, const char **name)
+{
+ enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
+
+ if (!trigger || !name) {
+ status = LTTNG_TRIGGER_STATUS_INVALID;
+ goto end;
+ }
+
+ if (!trigger->name) {
+ status = LTTNG_TRIGGER_STATUS_UNSET;
+ }
+
+ *name = trigger->name;
+end:
+ return status;
+}
+
+LTTNG_HIDDEN
+int lttng_trigger_assign(struct lttng_trigger *dst,
+ const struct lttng_trigger *src)
+{
+ int ret = 0;
+ enum lttng_trigger_status status;
+ /* todo some validation */
+
+ status = lttng_trigger_set_name(dst, src->name);
+ if (status != LTTNG_TRIGGER_STATUS_OK) {
+ ret = -1;
+ ERR("Failed to set name for trigger");
+ goto end;
+ }
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_set_key(struct lttng_trigger *trigger, uint64_t key)
+{
+ assert(trigger);
+ trigger->key.value = key;
+ trigger->key.set = true;
+}
+
+LTTNG_HIDDEN
+uint64_t lttng_trigger_get_key(const struct lttng_trigger *trigger)
+{
+ assert(trigger);
+
+ assert(trigger->key.set == true);
+ return trigger->key.value;
+}
+
+LTTNG_HIDDEN
+int lttng_trigger_generate_name(struct lttng_trigger *trigger, uint64_t offset)
+{
+ int ret = 0;
+ char *generated_name = NULL;
+ assert(trigger->key.set);
+
+ ret = asprintf(&generated_name, "T%" PRIu64 "", trigger->key.value + offset);
+ if (ret < 0) {
+ ERR("Failed to generate trigger name");
+ ret = -1;
+ goto end;
+ }
+
+ if (trigger->name) {
+ free(trigger->name);
+ }
+ trigger->name = generated_name;
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_get(struct lttng_trigger *trigger)
+{
+ urcu_ref_get(&trigger->ref);
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_put(struct lttng_trigger *trigger)
+{
+ if (!trigger) {
+ return;
+ }
+
+ urcu_ref_put(&trigger->ref , trigger_destroy_ref);
+}
+
+static void delete_trigger_array_element(void *ptr)
+{
+ struct lttng_trigger *trigger = ptr;
+ lttng_trigger_destroy(trigger);
+}
+
+LTTNG_HIDDEN
+bool lttng_trigger_is_equal(
+ const struct lttng_trigger *a, const struct lttng_trigger *b)
+{
+ /* TODO: Optimization: for now a trigger with a firing policy that is
+ * not the same even if the conditions and actions is the same is
+ * treated as a "completely" different trigger. In a perfect world we
+ * would simply add a supplemental counter internally (sessiond side) to
+ * remove overhead on the tracer side.
+ */
+ 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
+ */
+ if (!lttng_condition_is_equal(a->condition, b->condition)) {
+ return false;
+ }
+ if (!lttng_action_is_equal(a->action, b->action)) {
+ return false;
+ }
+
+ return true;
+}
+
+LTTNG_HIDDEN
+struct lttng_triggers *lttng_triggers_create(void)
+{
+ struct lttng_triggers *triggers = NULL;
+
+ triggers = zmalloc(sizeof(*triggers));
+ if (!triggers) {
+ goto error;
+ }
+
+ lttng_dynamic_pointer_array_init(&triggers->array, delete_trigger_array_element);
+
+ return triggers;
+error:
+ free(triggers);
+ return NULL;
+}
+
+LTTNG_HIDDEN
+struct lttng_trigger *lttng_triggers_get_pointer_of_index(
+ const struct lttng_triggers *triggers, unsigned int index)
+{
+ assert(triggers);
+ if (index >= lttng_dynamic_pointer_array_get_count(&triggers->array)) {
+ return NULL;
+ }
+ return lttng_dynamic_pointer_array_get_pointer(&triggers->array, index);
+}
+
+LTTNG_HIDDEN
+int lttng_triggers_add(
+ struct lttng_triggers *triggers, struct lttng_trigger *trigger)
+{
+ assert(triggers);
+ assert(trigger);
+
+ return lttng_dynamic_pointer_array_add_pointer(&triggers->array, trigger);
+}
+
+const struct lttng_trigger *lttng_triggers_get_at_index(
+ const struct lttng_triggers *triggers, unsigned int index)
+{
+ assert(triggers);
+ return lttng_triggers_get_pointer_of_index(triggers, index);
+}
+
+enum lttng_trigger_status lttng_triggers_get_count(const struct lttng_triggers *triggers, unsigned int *count)
+{
+ enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
+
+ if (!triggers || !count) {
+ status = LTTNG_TRIGGER_STATUS_INVALID;
+ goto end;
+ }
+
+ *count = lttng_dynamic_pointer_array_get_count(&triggers->array);
+end:
+ return status;
+}
+
+void lttng_triggers_destroy(struct lttng_triggers *triggers)
+{
+ if (!triggers) {
+ return;
+ }
+
+ lttng_dynamic_pointer_array_reset(&triggers->array);
+ free(triggers);
+}
+
+int lttng_triggers_serialize(const struct lttng_triggers *triggers,
+ struct lttng_dynamic_buffer *buffer)
+{
+ int ret;
+ unsigned int count;
+ size_t header_offset, size_before_payload;
+ struct lttng_triggers_comm triggers_comm = { 0 };
+ struct lttng_triggers_comm *header;
+ struct lttng_trigger *trigger;
+ enum lttng_trigger_status status;
+
+ header_offset = buffer->size;
+
+ status = lttng_triggers_get_count(triggers, &count);
+ if (status != LTTNG_TRIGGER_STATUS_OK) {
+ ret = LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ triggers_comm.count = count;
+
+ ret = lttng_dynamic_buffer_append(buffer, &triggers_comm,
+ sizeof(triggers_comm));
+ if (ret) {
+ goto end;
+ }
+
+ size_before_payload = buffer->size;
+
+ for (int i = 0; i < count; i++) {
+ trigger = lttng_triggers_get_pointer_of_index(triggers, i);
+ if (!trigger) {
+ assert(0);
+ }
+
+ ret = lttng_trigger_serialize(trigger, buffer, NULL);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ /* Update payload size. */
+ header = (struct lttng_triggers_comm *) ((char *) buffer->data + header_offset);
+ header->length = buffer->size - size_before_payload;
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_triggers_create_from_buffer(
+ const struct lttng_buffer_view *src_view,
+ struct lttng_triggers **triggers)
+{
+ ssize_t ret, offset = 0, trigger_size, triggers_size = 0;
+ const struct lttng_triggers_comm *triggers_comm;
+ struct lttng_buffer_view trigger_view;
+ struct lttng_triggers *local_triggers = NULL;
+
+ if (!src_view || !triggers) {
+ ret = -1;
+ goto error;
+ }
+
+ /* lttng_trigger_comms header */
+ triggers_comm = (const struct lttng_triggers_comm *) src_view->data;
+ offset += sizeof(*triggers_comm);
+
+ local_triggers = lttng_triggers_create();
+ if (!local_triggers) {
+ ret = -1;
+ goto error;
+ }
+
+ for (int i = 0; i < triggers_comm->count; i++) {
+ struct lttng_trigger *trigger = NULL;
+ trigger_view = lttng_buffer_view_from_view(src_view, offset, -1);
+ trigger_size = lttng_trigger_create_from_buffer(&trigger_view,
+ &trigger);
+ if (trigger_size < 0) {
+ ret = trigger_size;
+ goto error;
+ }
+
+ /* Pass ownership of the trigger to the collection */
+ ret = lttng_triggers_add(local_triggers, trigger);
+ if (ret < 0) {
+ assert(0);
+ }
+ trigger = NULL;
+
+ offset += trigger_size;
+ triggers_size += trigger_size;
+ }
+
+ /* Unexpected size of inner-elements; the buffer is corrupted. */
+ if ((ssize_t) triggers_comm->length != triggers_size) {
+ ret = -1;
+ goto error;
+ }
+
+ /* Pass ownership to caller */
+ *triggers = local_triggers;
+ local_triggers = NULL;
+
+ ret = offset;
+error:
+
+ lttng_triggers_destroy(local_triggers);
+ return ret;
+}
+
+LTTNG_HIDDEN
+const struct lttng_credentials *lttng_trigger_get_credentials(
+ const struct lttng_trigger *trigger)
+{
+ assert(trigger->creds.set);
+ return &(trigger->creds.credentials);
+}
+
+LTTNG_HIDDEN
+void lttng_trigger_set_credentials(
+ struct lttng_trigger *trigger, uid_t uid, gid_t gid)
+{
+ trigger->creds.credentials.uid = uid;
+ trigger->creds.credentials.gid = gid;
+ trigger->creds.set = true;
+}
+
+enum lttng_trigger_status lttng_trigger_set_firing_policy(
+ struct lttng_trigger *trigger,
+ enum lttng_trigger_firing_policy_type policy_type,
+ unsigned long long 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_type *policy_type,
+ unsigned long long *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_is_ready_to_fire(struct lttng_trigger *trigger)
+{
+ assert(trigger);
+ bool ready_to_fire = false;
+
+ trigger->firing_policy.current_count++;
+
+ switch (trigger->firing_policy.type) {
+ case LTTNG_TRIGGER_FIRE_EVERY_N:
+ if (trigger->firing_policy.current_count == trigger->firing_policy.threshold) {
+ trigger->firing_policy.current_count = 0;
+ ready_to_fire = true;
+ }
+ break;
+ case LTTNG_TRIGGER_FIRE_ONCE_AFTER_N:
+ if (trigger->firing_policy.current_count == trigger->firing_policy.threshold) {
+ /* TODO: remove the trigger of at least deactivate it on
+ * the tracers side to remove any work overhead on the
+ * traced application or kernel since the trigger will
+ * never fire again.
+ * Still this branch should be left here since event
+ * could still be in the pipe. These will be discarded.
+ */
+ ready_to_fire = true;
+ }
+ break;
+ default:
+ assert(0);
+ };
+
+ return ready_to_fire;
+}
+
+LTTNG_HIDDEN
+enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction(
+ const struct lttng_trigger *trigger)
+{
+ enum lttng_domain_type type = LTTNG_DOMAIN_NONE;
+ const struct lttng_event_rule *event_rule;
+ enum lttng_condition_status c_status;
+ enum lttng_condition_type c_type;
+
+ assert(trigger);
+ assert(trigger->condition);
+ c_type = lttng_condition_get_type(trigger->condition);
+ if (c_type == LTTNG_CONDITION_TYPE_UNKNOWN) {
+ assert(0);
+ }
+
+ switch (c_type) {
+ case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+ type = LTTNG_DOMAIN_NONE;
+ break;
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ c_status = lttng_condition_event_rule_get_rule(
+ trigger->condition, &event_rule);
+ if (c_status != LTTNG_CONDITION_STATUS_OK) {
+ /* The condition object is invalid */
+ assert(0);
+ }
+
+ type = lttng_event_rule_get_domain_type(event_rule);
+ break;
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+ c_status = lttng_condition_buffer_usage_get_domain_type(
+ trigger->condition, &type);
+ if (c_status != LTTNG_CONDITION_STATUS_OK) {
+ /* The condition object is invalid */
+ assert(0);
+ }
+ break;
+ default:
+ type = LTTNG_DOMAIN_NONE;
+ break;
+ }
+
+ return type;
+}
+
+LTTNG_HIDDEN
+unsigned int lttng_trigger_get_capture_bytecode_count(
+ const struct lttng_trigger *trigger)
+{
+ unsigned int count = 0;
+ if (!trigger) {
+ goto end;
+ }
+
+ count = lttng_dynamic_pointer_array_get_count(
+ &trigger->capture_bytecode_set);
+
+end:
+ return count;
+}
+
+LTTNG_HIDDEN
+const struct lttng_bytecode *
+lttng_trigger_get_capture_bytecode_at_index(
+ const struct lttng_trigger *trigger, unsigned int index)
+{
+ struct lttng_condition_event_rule_capture_bytecode_element *element = NULL;
+ struct lttng_bytecode *bytecode = NULL;
+
+ element = lttng_dynamic_pointer_array_get_pointer(
+ &trigger->capture_bytecode_set, index);
+
+ if (element == NULL) {
+ goto end;
+ }
+ bytecode = element->bytecode;
+end:
+ return bytecode;
+}
if (errno == EINTR) {
goto retry;
} else {
- /*
- * Only warn about EPIPE when quiet mode is
- * deactivated.
- * We consider EPIPE as expected.
- */
- if (errno != EPIPE || !lttng_opt_quiet) {
+ /* We consider EPIPE and EAGAIN as expected. */
+ if (!lttng_opt_quiet &&
+ (errno != EPIPE && errno != EAGAIN)) {
PERROR("recvmsg");
}
goto end;
if (errno == EINTR) {
goto retry;
} else {
- /*
- * Only warn about EPIPE when quiet mode is
- * deactivated.
- * We consider EPIPE as expected.
- */
- if (errno != EPIPE || !lttng_opt_quiet) {
+ /* We consider EPIPE and EAGAIN as expected. */
+ if (!lttng_opt_quiet &&
+ (errno != EPIPE && errno != EAGAIN)) {
PERROR("sendmsg");
}
goto end;
*/
#include <assert.h>
+#include <common/compat/string.h>
#include <common/error.h>
#include <common/macros.h>
-#include <common/compat/string.h>
#include <fcntl.h>
#include <lttng/constant.h>
#include <lttng/userspace-probe-internal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/unistd.h>
enum lttng_userspace_probe_location_lookup_method_type
lttng_userspace_probe_location_lookup_method_get_type(
}
}
+/*
+ * Compare two FDs.
+ * For now only the Inode number is compared.
+ */
+static bool fd_is_equal(int a, int b)
+{
+ int ret;
+ bool is_equal = false;
+ struct stat a_stat, b_stat;
+
+ if (a < 0 && b >= 0) {
+ goto end;
+ }
+
+ if (b < 0 && a >= 0) {
+ goto end;
+ }
+
+ if (a < 0 && b < 0) {
+ if (a == -1 && b == -1) {
+ is_equal = true;
+ goto end;
+ }
+ /* Invalid state, assert */
+ assert(0);
+ goto end;
+ }
+
+ /* Both are valid FD numbers */
+ ret = fstat(a, &a_stat);
+ if (ret) {
+ PERROR("fstat on fd a: %d", a);
+ goto end;
+ }
+
+ ret = fstat(b, &b_stat);
+ if (ret) {
+ PERROR("fstat on fd b: %d", b);
+ goto end;
+ }
+
+ is_equal = (a_stat.st_ino == b_stat.st_ino);
+
+end:
+ return is_equal;
+}
+
+static bool lttng_userspace_probe_location_function_is_equal(
+ const struct lttng_userspace_probe_location *_a,
+ const struct lttng_userspace_probe_location *_b)
+{
+ bool is_equal = false;
+ struct lttng_userspace_probe_location_function *a, *b;
+
+ a = container_of(_a, struct lttng_userspace_probe_location_function,
+ parent);
+ b = container_of(_b, struct lttng_userspace_probe_location_function,
+ parent);
+
+ if (a->instrumentation_type != b->instrumentation_type) {
+ goto end;
+ }
+
+ assert(a->function_name);
+ assert(b->function_name);
+ if (strcmp(a->function_name, b->function_name)) {
+ goto end;
+ }
+
+ assert(a->binary_path);
+ assert(b->binary_path);
+ if (strcmp(a->binary_path, b->binary_path)) {
+ goto end;
+ }
+
+ // TODO: see gerrit comment: https://review.lttng.org/c/lttng-tools/+/2924/27/src/common/userspace-probe.c#255
+ //is_equal = fd_is_equal(a->binary_fd, b->binary_fd);
+ is_equal = true;
+end:
+ return is_equal;
+}
+
static struct lttng_userspace_probe_location *
lttng_userspace_probe_location_function_create_no_check(const char *binary_path,
const char *function_name,
ret = &location->parent;
ret->lookup_method = lookup_method;
ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION;
+ ret->equal = lttng_userspace_probe_location_function_is_equal;
goto end;
error:
return ret;
}
+static bool lttng_userspace_probe_location_tracepoint_is_equal(
+ const struct lttng_userspace_probe_location *_a,
+ const struct lttng_userspace_probe_location *_b)
+{
+ bool is_equal = false;
+ struct lttng_userspace_probe_location_tracepoint *a, *b;
+
+ a = container_of(_a, struct lttng_userspace_probe_location_tracepoint,
+ parent);
+ b = container_of(_b, struct lttng_userspace_probe_location_tracepoint,
+ parent);
+
+ assert(a->probe_name);
+ assert(b->probe_name);
+ if (strcmp(a->probe_name, b->probe_name)) {
+ goto end;
+ }
+
+ assert(a->provider_name);
+ assert(b->provider_name);
+ if (strcmp(a->provider_name, b->provider_name)) {
+ goto end;
+ }
+
+ assert(a->binary_path);
+ assert(b->binary_path);
+ if (strcmp(a->binary_path, b->binary_path)) {
+ goto end;
+ }
+
+ is_equal = fd_is_equal(a->binary_fd, b->binary_fd);
+
+end:
+ return is_equal;
+}
+
static struct lttng_userspace_probe_location *
lttng_userspace_probe_location_tracepoint_create_no_check(const char *binary_path,
const char *provider_name, const char *probe_name,
ret = &location->parent;
ret->lookup_method = lookup_method;
ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT;
+ ret->equal = lttng_userspace_probe_location_tracepoint_is_equal;
goto end;
error:
err:
return new_location;
}
+
+LTTNG_HIDDEN
+bool lttng_userspace_probe_location_lookup_method_is_equal(
+ const struct lttng_userspace_probe_location_lookup_method *a,
+ const struct lttng_userspace_probe_location_lookup_method *b)
+{
+ bool is_equal = false;
+
+ if (!a || !b) {
+ goto end;
+ }
+
+ if (a == b) {
+ is_equal = true;
+ goto end;
+ }
+
+ /* Should we use the getter here? */
+ if (a->type != b->type) {
+ goto end;
+ }
+
+ /* Not much else to compare except the type for now */
+ is_equal = true;
+end:
+ return is_equal;
+}
+
+LTTNG_HIDDEN
+bool lttng_userspace_probe_location_is_equal(
+ const struct lttng_userspace_probe_location *a,
+ const struct lttng_userspace_probe_location *b)
+{
+ bool is_equal = false;
+
+ if (!a || !b) {
+ goto end;
+ }
+
+ if (a == b) {
+ is_equal = true;
+ goto end;
+ }
+
+ /* Should we use the getter here? */
+ if (!lttng_userspace_probe_location_lookup_method_is_equal(
+ a->lookup_method, b->lookup_method)) {
+ goto end;
+ }
+
+ /* Should we use the getter here? */
+ if (a->type != b->type) {
+ goto end;
+ }
+
+ is_equal = a->equal ? a->equal(a, b) : true;
+end:
+ return is_equal;
+}
+
+LTTNG_HIDDEN
+int lttng_userspace_probe_location_set_binary_fd(
+ struct lttng_userspace_probe_location *location, int fd)
+{
+ int ret = 0;
+ const struct lttng_userspace_probe_location_lookup_method *lookup = NULL;
+ /*
+ * Set the file descriptor received from the client through the unix
+ * socket in the probe location.
+ */
+ lookup = lttng_userspace_probe_location_get_lookup_method(location);
+ if (!lookup) {
+ ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+ goto end;
+ }
+
+ /*
+ * From the kernel tracer's perspective, all userspace probe event types
+ * are all the same: a file and an offset.
+ */
+ switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) {
+ case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+ ret = lttng_userspace_probe_location_function_set_binary_fd(
+ location, fd);
+ break;
+ case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+ ret = lttng_userspace_probe_location_tracepoint_set_binary_fd(
+ location, fd);
+ break;
+ default:
+ ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+ goto end;
+ }
+
+ if (ret) {
+ ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+ goto end;
+ }
+end:
+ return ret;
+}
free(buf);
return ret_val;
}
+
+LTTNG_HIDDEN
+int utils_parse_unsigned_long_long(const char *str,
+ unsigned long long *value)
+{
+ int ret;
+ char *endptr;
+
+ assert(str);
+ assert(value);
+
+ errno = 0;
+ *value = strtoull(str, &endptr, 10);
+
+ /* Conversion failed. Out of range? */
+ if (errno != 0) {
+ ret = -1;
+ goto end;
+ }
+
+ /* Not the end of the string? */
+ if (*endptr) {
+ ret = -1;
+ goto end;
+ }
+
+ /* Empty string? */
+ if (endptr == str) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = 0;
+
+end:
+ return ret;
+}
enum lttng_error_code utils_group_id_from_name(
const char *group_name, gid_t *group_id);
+/*
+ * Parse `str` as an unsigned long long value.
+ *
+ * Return 0 on success. Return -1 on failure which can be because:
+ *
+ * - `str` is zero length
+ * - `str` contains invalid
+ */
+LTTNG_HIDDEN
+int utils_parse_unsigned_long_long(const char *str,
+ unsigned long long *value);
+
#endif /* _COMMON_UTILS_H */
# SPDX-License-Identifier: GPL-2.0-only
-SUBDIRS = filter
+SUBDIRS =
AM_CPPFLAGS += -I$(srcdir) -I$(builddir)
liblttng_ctl_la_LIBADD = \
$(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \
- $(top_builddir)/src/common/libcommon.la \
- $(top_builddir)/src/lib/lttng-ctl/filter/libfilter.la
+ $(top_builddir)/src/common/libcommon.la
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = lttng-ctl.pc
goto end_unlock;
}
- ret = lttng_condition_serialize(condition, &buffer);
+ ret = lttng_condition_serialize(condition, &buffer, NULL);
if (ret) {
status = LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID;
goto end_unlock;
+++ /dev/null
-# SPDX-License-Identifier: GPL-2.0-only
-
-AM_CPPFLAGS += -I$(srcdir) -I$(builddir)
-
-noinst_PROGRAMS = filter-grammar-test
-noinst_LTLIBRARIES = libfilter.la
-noinst_HEADERS = filter-ast.h \
- filter-symbols.h
-
-BUILT_SOURCES = filter-parser.h
-
-libfilter_la_SOURCES = \
- filter-parser.y filter-lexer.l \
- filter-visitor-xml.c \
- filter-visitor-generate-ir.c \
- filter-visitor-ir-check-binary-op-nesting.c \
- filter-visitor-ir-validate-string.c \
- filter-visitor-ir-validate-globbing.c \
- filter-visitor-ir-normalize-glob-patterns.c \
- filter-visitor-generate-bytecode.c \
- filter-ast.h \
- filter-bytecode.h \
- filter-ir.h \
- memstream.h
-libfilter_la_CFLAGS = -include filter-symbols.h $(AM_CFLAGS)
-libfilter_la_LIBADD = $(top_builddir)/src/common/string-utils/libstring-utils.la
-
-AM_YFLAGS = -t -d -v -Wno-yacc
-
-# start with empty files to clean
-CLEANFILES =
-
-if HAVE_BISON
-# we have bison: we can clean the generated parser files
-CLEANFILES += filter-parser.c filter-parser.h filter-parser.output
-else # HAVE_BISON
-# create target used to stop the build if we want to build the parser,
-# but we don't have the necessary tool to do so
-ERR_MSG = "Error: Cannot build target because bison is missing."
-ERR_MSG += "Make sure bison is installed and run the configure script again."
-
-filter-parser.c filter-parser.h: filter-parser.y
- @echo $(ERR_MSG)
- @false
-
-all-local: filter-parser.c filter-parser.h
-endif # HAVE_BISON
-
-if HAVE_FLEX
-# we have flex: we can clean the generated lexer files
-CLEANFILES += filter-lexer.c
-else # HAVE_FLEX
-# create target used to stop the build if we want to build the lexer,
-# but we don't have the necessary tool to do so
-ERR_MSG = "Error: Cannot build target because flex is missing."
-ERR_MSG += "Make sure flex is installed and run the configure script again."
-
-filter-lexer.c: filter-lexer.l
- @echo $(ERR_MSG)
- @false
-
-all-local: filter-lexer.c
-endif # HAVE_FLEX
-
-filter_grammar_test_SOURCES = filter-grammar-test.c
-filter_grammar_test_LDADD = libfilter.la
+++ /dev/null
-#ifndef _FILTER_AST_H
-#define _FILTER_AST_H
-
-/*
- * filter-ast.h
- *
- * LTTng filter AST
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-/*
- * Note: filter-ast.h should be included before filter-parser.h.
- */
-
-#include <urcu/list.h>
-#include <stdint.h>
-
-#define printf_debug(fmt, args...) \
- do { \
- if (filter_parser_debug) \
- fprintf(stdout, "[debug] " fmt, ## args); \
- } while (0)
-
-// the parameter name (of the reentrant 'yyparse' function)
-// data is a pointer to a 'SParserParam' structure
-//#define YYPARSE_PARAM parser_ctx
-
-#ifndef YY_TYPEDEF_YY_SCANNER_T
-#define YY_TYPEDEF_YY_SCANNER_T
-typedef void* yyscan_t;
-#endif
-
-extern int filter_parser_debug;
-
-struct filter_node;
-struct filter_parser;
-
-enum node_type {
- NODE_UNKNOWN = 0,
- NODE_ROOT,
-
- NODE_EXPRESSION,
- NODE_OP,
- NODE_UNARY_OP,
-
- NR_NODE_TYPES,
-};
-
-enum op_type {
- AST_OP_UNKNOWN = 0,
- AST_OP_MUL,
- AST_OP_DIV,
- AST_OP_MOD,
- AST_OP_PLUS,
- AST_OP_MINUS,
- AST_OP_BIT_RSHIFT,
- AST_OP_BIT_LSHIFT,
- AST_OP_AND,
- AST_OP_OR,
- AST_OP_BIT_AND,
- AST_OP_BIT_OR,
- AST_OP_BIT_XOR,
-
- AST_OP_EQ,
- AST_OP_NE,
- AST_OP_GT,
- AST_OP_LT,
- AST_OP_GE,
- AST_OP_LE,
-};
-
-enum unary_op_type {
- AST_UNARY_UNKNOWN = 0,
- AST_UNARY_PLUS,
- AST_UNARY_MINUS,
- AST_UNARY_NOT,
- AST_UNARY_BIT_NOT,
-};
-
-enum ast_link_type {
- AST_LINK_UNKNOWN = 0,
- AST_LINK_DOT,
- AST_LINK_RARROW,
- AST_LINK_BRACKET,
-};
-
-struct filter_node {
- /*
- * Parent node is only set on demand by specific visitor.
- */
- struct filter_node *parent;
- struct cds_list_head gc;
-
- enum node_type type;
- union {
- struct {
- } unknown;
- struct {
- struct filter_node *child;
- } root;
- struct {
- enum {
- AST_EXP_UNKNOWN = 0,
- AST_EXP_STRING,
- AST_EXP_CONSTANT,
- AST_EXP_FLOAT_CONSTANT,
- AST_EXP_IDENTIFIER,
- AST_EXP_GLOBAL_IDENTIFIER,
- AST_EXP_NESTED,
- } type;
- enum ast_link_type post_op; /* reverse */
- enum ast_link_type pre_op; /* forward */
- union {
- const char *string;
- uint64_t constant;
- double float_constant;
- const char *identifier;
- /*
- * child can be nested.
- */
- struct filter_node *child;
- } u;
- /* prev: backward dot/arrow chain (postfix expression) */
- struct filter_node *prev;
- /* next: forward dot/arrow chain, generated by a visitor. */
- struct filter_node *next;
- /* next_bracket: linked bracket chain (prefix expression) */
- struct filter_node *next_bracket;
- } expression;
- struct {
- enum op_type type;
- struct filter_node *lchild;
- struct filter_node *rchild;
- } op;
- struct {
- enum unary_op_type type;
- struct filter_node *child;
- } unary_op;
- } u;
-};
-
-struct filter_ast {
- struct filter_node root;
- struct cds_list_head allocated_nodes;
-};
-
-const char *node_type(struct filter_node *node);
-
-struct ir_op;
-
-struct filter_parser_ctx {
- yyscan_t scanner;
- struct filter_ast *ast;
- struct cds_list_head allocated_strings;
- struct ir_op *ir_root;
- struct lttng_filter_bytecode_alloc *bytecode;
- struct lttng_filter_bytecode_alloc *bytecode_reloc;
-};
-
-struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input);
-void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx);
-int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx);
-
-static inline
-struct filter_ast *filter_parser_get_ast(struct filter_parser_ctx *parser_ctx)
-{
- return parser_ctx->ast;
-}
-
-int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream,
- int indent);
-int filter_visitor_ir_generate(struct filter_parser_ctx *ctx);
-void filter_ir_free(struct filter_parser_ctx *ctx);
-int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx);
-void filter_bytecode_free(struct filter_parser_ctx *ctx);
-int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx);
-int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx);
-int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx);
-int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx);
-int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx);
-
-#endif /* _FILTER_AST_H */
+++ /dev/null
-#ifndef _FILTER_BYTECODE_H
-#define _FILTER_BYTECODE_H
-
-/*
- * filter-bytecode.h
- *
- * LTTng filter bytecode
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/sessiond-comm/sessiond-comm.h>
-#include <common/macros.h>
-
-#include "filter-ast.h"
-
-/*
- * offsets are absolute from start of bytecode.
- */
-
-struct field_ref {
- /* Initially, symbol offset. After link, field offset. */
- uint16_t offset;
-} LTTNG_PACKED;
-
-struct get_symbol {
- /* Symbol offset. */
- uint16_t offset;
-} LTTNG_PACKED;
-
-struct get_index_u16 {
- uint16_t index;
-} LTTNG_PACKED;
-
-struct get_index_u64 {
- uint64_t index;
-} LTTNG_PACKED;
-
-struct literal_numeric {
- int64_t v;
-} LTTNG_PACKED;
-
-struct literal_double {
- double v;
-} LTTNG_PACKED;
-
-struct literal_string {
- char string[0];
-} LTTNG_PACKED;
-
-enum filter_op {
- FILTER_OP_UNKNOWN = 0,
-
- FILTER_OP_RETURN = 1,
-
- /* binary */
- FILTER_OP_MUL = 2,
- FILTER_OP_DIV = 3,
- FILTER_OP_MOD = 4,
- FILTER_OP_PLUS = 5,
- FILTER_OP_MINUS = 6,
- FILTER_OP_BIT_RSHIFT = 7,
- FILTER_OP_BIT_LSHIFT = 8,
- FILTER_OP_BIT_AND = 9,
- FILTER_OP_BIT_OR = 10,
- FILTER_OP_BIT_XOR = 11,
-
- /* binary comparators */
- FILTER_OP_EQ = 12,
- FILTER_OP_NE = 13,
- FILTER_OP_GT = 14,
- FILTER_OP_LT = 15,
- FILTER_OP_GE = 16,
- FILTER_OP_LE = 17,
-
- /* string binary comparator: apply to */
- FILTER_OP_EQ_STRING = 18,
- FILTER_OP_NE_STRING = 19,
- FILTER_OP_GT_STRING = 20,
- FILTER_OP_LT_STRING = 21,
- FILTER_OP_GE_STRING = 22,
- FILTER_OP_LE_STRING = 23,
-
- /* s64 binary comparator */
- FILTER_OP_EQ_S64 = 24,
- FILTER_OP_NE_S64 = 25,
- FILTER_OP_GT_S64 = 26,
- FILTER_OP_LT_S64 = 27,
- FILTER_OP_GE_S64 = 28,
- FILTER_OP_LE_S64 = 29,
-
- /* double binary comparator */
- FILTER_OP_EQ_DOUBLE = 30,
- FILTER_OP_NE_DOUBLE = 31,
- FILTER_OP_GT_DOUBLE = 32,
- FILTER_OP_LT_DOUBLE = 33,
- FILTER_OP_GE_DOUBLE = 34,
- FILTER_OP_LE_DOUBLE = 35,
-
- /* Mixed S64-double binary comparators */
- FILTER_OP_EQ_DOUBLE_S64 = 36,
- FILTER_OP_NE_DOUBLE_S64 = 37,
- FILTER_OP_GT_DOUBLE_S64 = 38,
- FILTER_OP_LT_DOUBLE_S64 = 39,
- FILTER_OP_GE_DOUBLE_S64 = 40,
- FILTER_OP_LE_DOUBLE_S64 = 41,
-
- FILTER_OP_EQ_S64_DOUBLE = 42,
- FILTER_OP_NE_S64_DOUBLE = 43,
- FILTER_OP_GT_S64_DOUBLE = 44,
- FILTER_OP_LT_S64_DOUBLE = 45,
- FILTER_OP_GE_S64_DOUBLE = 46,
- FILTER_OP_LE_S64_DOUBLE = 47,
-
- /* unary */
- FILTER_OP_UNARY_PLUS = 48,
- FILTER_OP_UNARY_MINUS = 49,
- FILTER_OP_UNARY_NOT = 50,
- FILTER_OP_UNARY_PLUS_S64 = 51,
- FILTER_OP_UNARY_MINUS_S64 = 52,
- FILTER_OP_UNARY_NOT_S64 = 53,
- FILTER_OP_UNARY_PLUS_DOUBLE = 54,
- FILTER_OP_UNARY_MINUS_DOUBLE = 55,
- FILTER_OP_UNARY_NOT_DOUBLE = 56,
-
- /* logical */
- FILTER_OP_AND = 57,
- FILTER_OP_OR = 58,
-
- /* load field ref */
- FILTER_OP_LOAD_FIELD_REF = 59,
- FILTER_OP_LOAD_FIELD_REF_STRING = 60,
- FILTER_OP_LOAD_FIELD_REF_SEQUENCE = 61,
- FILTER_OP_LOAD_FIELD_REF_S64 = 62,
- FILTER_OP_LOAD_FIELD_REF_DOUBLE = 63,
-
- /* load immediate from operand */
- FILTER_OP_LOAD_STRING = 64,
- FILTER_OP_LOAD_S64 = 65,
- FILTER_OP_LOAD_DOUBLE = 66,
-
- /* cast */
- FILTER_OP_CAST_TO_S64 = 67,
- FILTER_OP_CAST_DOUBLE_TO_S64 = 68,
- FILTER_OP_CAST_NOP = 69,
-
- /* get context ref */
- FILTER_OP_GET_CONTEXT_REF = 70,
- FILTER_OP_GET_CONTEXT_REF_STRING = 71,
- FILTER_OP_GET_CONTEXT_REF_S64 = 72,
- FILTER_OP_GET_CONTEXT_REF_DOUBLE = 73,
-
- /* load userspace field ref */
- FILTER_OP_LOAD_FIELD_REF_USER_STRING = 74,
- FILTER_OP_LOAD_FIELD_REF_USER_SEQUENCE = 75,
-
- /*
- * load immediate star globbing pattern (literal string)
- * from immediate
- */
- FILTER_OP_LOAD_STAR_GLOB_STRING = 76,
-
- /* globbing pattern binary operator: apply to */
- FILTER_OP_EQ_STAR_GLOB_STRING = 77,
- FILTER_OP_NE_STAR_GLOB_STRING = 78,
-
- /*
- * Instructions for recursive traversal through composed types.
- */
- FILTER_OP_GET_CONTEXT_ROOT = 79,
- FILTER_OP_GET_APP_CONTEXT_ROOT = 80,
- FILTER_OP_GET_PAYLOAD_ROOT = 81,
-
- FILTER_OP_GET_SYMBOL = 82,
- FILTER_OP_GET_SYMBOL_FIELD = 83,
- FILTER_OP_GET_INDEX_U16 = 84,
- FILTER_OP_GET_INDEX_U64 = 85,
-
- FILTER_OP_LOAD_FIELD = 86,
- FILTER_OP_LOAD_FIELD_S8 = 87,
- FILTER_OP_LOAD_FIELD_S16 = 88,
- FILTER_OP_LOAD_FIELD_S32 = 89,
- FILTER_OP_LOAD_FIELD_S64 = 90,
- FILTER_OP_LOAD_FIELD_U8 = 91,
- FILTER_OP_LOAD_FIELD_U16 = 92,
- FILTER_OP_LOAD_FIELD_U32 = 93,
- FILTER_OP_LOAD_FIELD_U64 = 94,
- FILTER_OP_LOAD_FIELD_STRING = 95,
- FILTER_OP_LOAD_FIELD_SEQUENCE = 96,
- FILTER_OP_LOAD_FIELD_DOUBLE = 97,
-
- FILTER_OP_UNARY_BIT_NOT = 98,
-
- FILTER_OP_RETURN_S64 = 99,
-
- NR_FILTER_OPS,
-};
-
-typedef uint8_t filter_opcode_t;
-
-struct load_op {
- filter_opcode_t op;
- char data[0];
- /* data to load. Size known by enum filter_opcode and null-term char. */
-} LTTNG_PACKED;
-
-struct binary_op {
- filter_opcode_t op;
-} LTTNG_PACKED;
-
-struct unary_op {
- filter_opcode_t op;
-} LTTNG_PACKED;
-
-/* skip_offset is absolute from start of bytecode */
-struct logical_op {
- filter_opcode_t op;
- uint16_t skip_offset; /* bytecode insn, if skip second test */
-} LTTNG_PACKED;
-
-struct cast_op {
- filter_opcode_t op;
-} LTTNG_PACKED;
-
-struct return_op {
- filter_opcode_t op;
-} LTTNG_PACKED;
-
-struct lttng_filter_bytecode_alloc {
- uint32_t alloc_len;
- struct lttng_filter_bytecode b;
-};
-
-static inline
-unsigned int bytecode_get_len(struct lttng_filter_bytecode *bytecode)
-{
- return bytecode->len;
-}
-
-#endif /* _FILTER_BYTECODE_H */
+++ /dev/null
-/*
- * filter-grammar-test.c
- *
- * LTTng filter grammar test
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-bytecode.h"
-
-int main(int argc, char **argv)
-{
- struct filter_parser_ctx *ctx;
- int ret;
- int print_xml = 0, generate_ir = 0, generate_bytecode = 0,
- print_bytecode = 0;
- int i;
-
- for (i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-p") == 0)
- print_xml = 1;
- else if (strcmp(argv[i], "-i") == 0)
- generate_ir = 1;
- else if (strcmp(argv[i], "-b") == 0)
- generate_bytecode = 1;
- else if (strcmp(argv[i], "-d") == 0)
- filter_parser_debug = 1;
- else if (strcmp(argv[i], "-B") == 0)
- print_bytecode = 1;
- }
-
- /*
- * Force generate the bytecode if the user asks to print the bytecode
- * (can't print it without generating it first).
- */
- if (print_bytecode) {
- generate_bytecode = 1;
- }
-
- /*
- * Force generate the IR if the user asks to generate the bytecode
- * (the bytecode is generated by visiting the IR).
- */
- if (generate_bytecode) {
- generate_ir = 1;
- }
-
- ctx = filter_parser_ctx_alloc(stdin);
- if (!ctx) {
- fprintf(stderr, "Error allocating parser\n");
- goto alloc_error;
- }
- ret = filter_parser_ctx_append_ast(ctx);
- if (ret) {
- fprintf(stderr, "Parse error\n");
- goto parse_error;
- }
- if (print_xml) {
- ret = filter_visitor_print_xml(ctx, stdout, 0);
- if (ret) {
- fflush(stdout);
- fprintf(stderr, "XML print error\n");
- goto parse_error;
- }
- }
- if (generate_ir) {
- printf("Generating IR... ");
- fflush(stdout);
- ret = filter_visitor_ir_generate(ctx);
- if (ret) {
- fprintf(stderr, "Generate IR error\n");
- goto parse_error;
- }
- printf("done\n");
-
- printf("Validating IR... ");
- fflush(stdout);
- ret = filter_visitor_ir_check_binary_op_nesting(ctx);
- if (ret) {
- goto parse_error;
- }
- printf("done\n");
- }
- if (generate_bytecode) {
- printf("Generating bytecode... ");
- fflush(stdout);
- ret = filter_visitor_bytecode_generate(ctx);
- if (ret) {
- fprintf(stderr, "Generate bytecode error\n");
- goto parse_error;
- }
- printf("done\n");
- printf("Size of bytecode generated: %u bytes.\n",
- bytecode_get_len(&ctx->bytecode->b));
- }
-
- if (print_bytecode) {
- unsigned int bytecode_len, len, i;
-
- len = bytecode_get_len(&ctx->bytecode->b);
- bytecode_len = ctx->bytecode->b.reloc_table_offset;
- printf("Bytecode:\n");
- for (i = 0; i < bytecode_len; i++) {
- printf("0x%X ",
- ((uint8_t *) ctx->bytecode->b.data)[i]);
- }
- printf("\n");
- printf("Reloc table:\n");
- for (i = bytecode_len; i < len;) {
- printf("{ 0x%X, ",
- *(uint16_t *) &ctx->bytecode->b.data[i]);
- i += sizeof(uint16_t);
- printf("%s } ", &((char *) ctx->bytecode->b.data)[i]);
- i += strlen(&((char *) ctx->bytecode->b.data)[i]) + 1;
- }
- printf("\n");
- }
-
- filter_bytecode_free(ctx);
- filter_ir_free(ctx);
- filter_parser_ctx_free(ctx);
- return 0;
-
-parse_error:
- filter_bytecode_free(ctx);
- filter_ir_free(ctx);
- filter_parser_ctx_free(ctx);
-alloc_error:
- exit(EXIT_FAILURE);
-}
+++ /dev/null
-#ifndef _FILTER_IR_H
-#define _FILTER_IR_H
-
-/*
- * filter-ir.h
- *
- * LTTng filter ir
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include "filter-ast.h"
-
-enum ir_op_signedness {
- IR_SIGN_UNKNOWN = 0,
- IR_SIGNED,
- IR_UNSIGNED,
- IR_SIGN_DYN, /* signedness determined dynamically */
-};
-
-enum ir_data_type {
- IR_DATA_UNKNOWN = 0,
- IR_DATA_STRING,
- IR_DATA_NUMERIC, /* numeric and boolean */
- IR_DATA_FLOAT,
- IR_DATA_FIELD_REF,
- IR_DATA_GET_CONTEXT_REF,
- IR_DATA_EXPRESSION,
-};
-
-enum ir_op_type {
- IR_OP_UNKNOWN = 0,
- IR_OP_ROOT,
- IR_OP_LOAD,
- IR_OP_UNARY,
- IR_OP_BINARY,
- IR_OP_LOGICAL,
-};
-
-/* left or right child */
-enum ir_side {
- IR_SIDE_UNKNOWN = 0,
- IR_LEFT,
- IR_RIGHT,
-};
-
-enum ir_load_string_type {
- /* Plain, no globbing at all: `hello world`. */
- IR_LOAD_STRING_TYPE_PLAIN = 0,
-
- /* Star at the end only: `hello *`. */
- IR_LOAD_STRING_TYPE_GLOB_STAR_END,
-
- /* At least one star, anywhere, but not at the end only: `he*wor*`. */
- IR_LOAD_STRING_TYPE_GLOB_STAR,
-};
-
-struct ir_op_root {
- struct ir_op *child;
-};
-
-enum ir_load_expression_type {
- IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT,
- IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT,
- IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT,
- IR_LOAD_EXPRESSION_GET_SYMBOL,
- IR_LOAD_EXPRESSION_GET_INDEX,
- IR_LOAD_EXPRESSION_LOAD_FIELD,
-};
-
-struct ir_load_expression_op {
- struct ir_load_expression_op *next;
- enum ir_load_expression_type type;
- union {
- char *symbol;
- uint64_t index;
- } u;
-};
-
-struct ir_load_expression {
- struct ir_load_expression_op *child;
-};
-
-struct ir_op_load {
- union {
- struct {
- enum ir_load_string_type type;
- char *value;
- } string;
- int64_t num;
- double flt;
- char *ref;
- struct ir_load_expression *expression;
- } u;
-};
-
-struct ir_op_unary {
- enum unary_op_type type;
- struct ir_op *child;
-};
-
-struct ir_op_binary {
- enum op_type type;
- struct ir_op *left;
- struct ir_op *right;
-};
-
-struct ir_op_logical {
- enum op_type type;
- struct ir_op *left;
- struct ir_op *right;
-};
-
-struct ir_op {
- /* common to all ops */
- enum ir_op_type op;
- enum ir_data_type data_type;
- enum ir_op_signedness signedness;
- enum ir_side side;
-
- union {
- struct ir_op_root root;
- struct ir_op_load load;
- struct ir_op_unary unary;
- struct ir_op_binary binary;
- struct ir_op_logical logical;
- } u;
-};
-
-#endif /* _FILTER_IR_H */
+++ /dev/null
-%{
-/*
- * filter-lexer.l
- *
- * LTTng filter lexer
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-
-static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
- __attribute__((unused));
-static int input (yyscan_t yyscanner) __attribute__((unused));
-
-%}
-
-%x comment_ml comment_sl string_lit char_const
-%option reentrant yylineno noyywrap bison-bridge
-%option extra-type="struct filter_parser_ctx *"
- /* bison-locations */
-
-D [0-9]
-L [a-zA-Z_]
-H [a-fA-F0-9]
-E ([Ee][+-]?{D}+)
-P ([Pp][+-]?{D}+)
-FS (f|F|l|L)
-IS ((u|U)|(u|U)?(l|L|ll|LL)|(l|L|ll|LL)(u|U))
-
-INTEGER_SUFFIX [ \n\t]*(U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu)
-DIGIT [0-9]
-NONDIGIT [a-zA-Z_]
-HEXDIGIT [0-9A-Fa-f]
-OCTALDIGIT [0-7]
-UCHARLOWERCASE \\u{HEXDIGIT}{4}
-UCHARUPPERCASE \\U{HEXDIGIT}{8}
-ID_EXTRA_CHAR (":")
-ID_NONDIGIT {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE}|{ID_EXTRA_CHAR}
-IDENTIFIER {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})*
-ESCSEQ \\(\'|\"|\?|\\|a|b|f|n|r|t|v|{OCTALDIGIT}{1,3}|u{HEXDIGIT}{4}|U{HEXDIGIT}{8}|x{HEXDIGIT}+)
-%%
-
- /*
- * Using start conditions to deal with comments
- * and strings.
- */
-
-"/*" BEGIN(comment_ml);
-<comment_ml>[^*\n]* /* eat anything that's not a '*' */
-<comment_ml>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
-<comment_ml>\n ++yylineno;
-<comment_ml>"*"+"/" BEGIN(INITIAL);
-
-"//" BEGIN(comment_sl);
-<comment_sl>[^\n]*\n ++yylineno; BEGIN(INITIAL);
-
-L\' BEGIN(char_const); return CHARACTER_CONSTANT_START;
-\' BEGIN(char_const); return CHARACTER_CONSTANT_START;
-<char_const>\' BEGIN(INITIAL); return SQUOTE;
-
-L\" BEGIN(string_lit); return STRING_LITERAL_START;
-\" BEGIN(string_lit); return STRING_LITERAL_START;
-<string_lit>\" BEGIN(INITIAL); return DQUOTE;
-
-<char_const,string_lit>ESCSEQ return ESCSEQ;
-<char_const,string_lit>\n ; /* ignore */
-<char_const,string_lit>. setstring(yyextra, yylval, yytext); return CHAR_STRING_TOKEN;
-
-
-0[xX]{H}+{IS}? setstring(yyextra, yylval, yytext); return HEXADECIMAL_CONSTANT;
-0[0-7]*{IS}? setstring(yyextra, yylval, yytext); return OCTAL_CONSTANT;
-[1-9]{D}*{IS}? setstring(yyextra, yylval, yytext); return DECIMAL_CONSTANT;
-
-{D}+{E}{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-{D}*"."{D}+{E}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-{D}+"."{D}*{E}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-0[xX]{H}+{P}{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-0[xX]{H}*"."{H}+{P}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-0[xX]{H}+"."{H}*{P}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT;
-
-"[" return LSBRAC;
-"]" return RSBRAC;
-"(" return LPAREN;
-")" return RPAREN;
-"{" return LBRAC;
-"}" return RBRAC;
-"->" return RARROW;
-
-"*" return STAR;
-"+" return PLUS;
-"-" return MINUS;
-
-"%" return MOD_OP;
-"/" return DIV_OP;
-">>" return RIGHT_OP;
-"<<" return LEFT_OP;
-
-"==" return EQ_OP;
-"!=" return NE_OP;
-"<=" return LE_OP;
-">=" return GE_OP;
-"<" return LT_OP;
-">" return GT_OP;
-"&&" return AND_OP;
-"||" return OR_OP;
-"!" return NOT_OP;
-
-":=" return ASSIGN;
-":" return COLON;
-";" return SEMICOLON;
-"..." return DOTDOTDOT;
-"." return DOT;
-"=" return EQUAL;
-"," return COMMA;
-"^" return XOR_BIN;
-"&" return AND_BIN;
-"|" return OR_BIN;
-"~" return NOT_BIN;
-"$"{IDENTIFIER} printf_debug("<GLOBAL_IDENTIFIER %s>\n", yytext); setstring(yyextra, yylval, yytext); return GLOBAL_IDENTIFIER;
-{IDENTIFIER} printf_debug("<IDENTIFIER %s>\n", yytext); setstring(yyextra, yylval, yytext); return IDENTIFIER;
-[ \t\n]+ ; /* ignore */
-. return ERROR;
-%%
+++ /dev/null
-%{
-/*
- * filter-parser.y
- *
- * LTTng filter expression parser
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- * Grammar inspired from http://www.quut.com/c/ANSI-C-grammar-y.html
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-
-#include <common/macros.h>
-
-#define WIDTH_u64_SCANF_IS_A_BROKEN_API "20"
-#define WIDTH_o64_SCANF_IS_A_BROKEN_API "22"
-#define WIDTH_x64_SCANF_IS_A_BROKEN_API "17"
-#define WIDTH_lg_SCANF_IS_A_BROKEN_API "4096" /* Hugely optimistic approximation */
-
-LTTNG_HIDDEN
-int yydebug;
-LTTNG_HIDDEN
-int filter_parser_debug = 0;
-
-LTTNG_HIDDEN
-int yyparse(struct filter_parser_ctx *parser_ctx, yyscan_t scanner);
-LTTNG_HIDDEN
-int yylex(union YYSTYPE *yyval, yyscan_t scanner);
-LTTNG_HIDDEN
-int yylex_init_extra(struct filter_parser_ctx *parser_ctx, yyscan_t * ptr_yy_globals);
-LTTNG_HIDDEN
-int yylex_destroy(yyscan_t yyparser_ctx);
-LTTNG_HIDDEN
-void yyrestart(FILE * in_str, yyscan_t parser_ctx);
-
-struct gc_string {
- struct cds_list_head gc;
- size_t alloclen;
- char s[];
-};
-
-static const char *node_type_to_str[] = {
- [ NODE_UNKNOWN ] = "NODE_UNKNOWN",
- [ NODE_ROOT ] = "NODE_ROOT",
- [ NODE_EXPRESSION ] = "NODE_EXPRESSION",
- [ NODE_OP ] = "NODE_OP",
- [ NODE_UNARY_OP ] = "NODE_UNARY_OP",
-};
-
-LTTNG_HIDDEN
-const char *node_type(struct filter_node *node)
-{
- if (node->type < NR_NODE_TYPES)
- return node_type_to_str[node->type];
- else
- return NULL;
-}
-
-static struct gc_string *gc_string_alloc(struct filter_parser_ctx *parser_ctx,
- size_t len)
-{
- struct gc_string *gstr;
- size_t alloclen;
-
- /* TODO: could be faster with find first bit or glib Gstring */
- /* sizeof long to account for malloc header (int or long ?) */
- for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + len;
- alloclen *= 2);
-
- gstr = zmalloc(alloclen);
- if (!gstr) {
- goto end;
- }
- cds_list_add(&gstr->gc, &parser_ctx->allocated_strings);
- gstr->alloclen = alloclen;
-end:
- return gstr;
-}
-
-/*
- * note: never use gc_string_append on a string that has external references.
- * gsrc will be garbage collected immediately, and gstr might be.
- * Should only be used to append characters to a string literal or constant.
- */
-static
-struct gc_string *gc_string_append(struct filter_parser_ctx *parser_ctx,
- struct gc_string *gstr,
- struct gc_string *gsrc)
-{
- size_t newlen = strlen(gsrc->s) + strlen(gstr->s) + 1;
- size_t alloclen;
-
- /* TODO: could be faster with find first bit or glib Gstring */
- /* sizeof long to account for malloc header (int or long ?) */
- for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + newlen;
- alloclen *= 2);
-
- if (alloclen > gstr->alloclen) {
- struct gc_string *newgstr;
-
- newgstr = gc_string_alloc(parser_ctx, newlen);
- strcpy(newgstr->s, gstr->s);
- strcat(newgstr->s, gsrc->s);
- cds_list_del(&gstr->gc);
- free(gstr);
- gstr = newgstr;
- } else {
- strcat(gstr->s, gsrc->s);
- }
- cds_list_del(&gsrc->gc);
- free(gsrc);
- return gstr;
-}
-
-LTTNG_HIDDEN
-void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src)
-{
- lvalp->gs = gc_string_alloc(parser_ctx, strlen(src) + 1);
- strcpy(lvalp->gs->s, src);
-}
-
-static struct filter_node *make_node(struct filter_parser_ctx *scanner,
- enum node_type type)
-{
- struct filter_ast *ast = filter_parser_get_ast(scanner);
- struct filter_node *node;
-
- node = zmalloc(sizeof(*node));
- if (!node)
- return NULL;
- memset(node, 0, sizeof(*node));
- node->type = type;
- cds_list_add(&node->gc, &ast->allocated_nodes);
-
- switch (type) {
- case NODE_ROOT:
- fprintf(stderr, "[error] %s: trying to create root node\n", __func__);
- break;
-
- case NODE_EXPRESSION:
- break;
- case NODE_OP:
- break;
- case NODE_UNARY_OP:
- break;
-
- case NODE_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown node type %d\n", __func__,
- (int) type);
- break;
- }
-
- return node;
-}
-
-static struct filter_node *make_op_node(struct filter_parser_ctx *scanner,
- enum op_type type,
- struct filter_node *lchild,
- struct filter_node *rchild)
-{
- struct filter_ast *ast = filter_parser_get_ast(scanner);
- struct filter_node *node;
-
- node = zmalloc(sizeof(*node));
- if (!node)
- return NULL;
- memset(node, 0, sizeof(*node));
- node->type = NODE_OP;
- cds_list_add(&node->gc, &ast->allocated_nodes);
- node->u.op.type = type;
- node->u.op.lchild = lchild;
- node->u.op.rchild = rchild;
- return node;
-}
-
-static
-void yyerror(struct filter_parser_ctx *parser_ctx, yyscan_t scanner, const char *str)
-{
- fprintf(stderr, "error %s\n", str);
-}
-
-#define parse_error(parser_ctx, str) \
-do { \
- yyerror(parser_ctx, parser_ctx->scanner, YY_("parse error: " str "\n")); \
- YYERROR; \
-} while (0)
-
-static void free_strings(struct cds_list_head *list)
-{
- struct gc_string *gstr, *tmp;
-
- cds_list_for_each_entry_safe(gstr, tmp, list, gc)
- free(gstr);
-}
-
-static struct filter_ast *filter_ast_alloc(void)
-{
- struct filter_ast *ast;
-
- ast = zmalloc(sizeof(*ast));
- if (!ast)
- return NULL;
- memset(ast, 0, sizeof(*ast));
- CDS_INIT_LIST_HEAD(&ast->allocated_nodes);
- ast->root.type = NODE_ROOT;
- return ast;
-}
-
-static void filter_ast_free(struct filter_ast *ast)
-{
- struct filter_node *node, *tmp;
-
- cds_list_for_each_entry_safe(node, tmp, &ast->allocated_nodes, gc)
- free(node);
- free(ast);
-}
-
-LTTNG_HIDDEN
-int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx)
-{
- return yyparse(parser_ctx, parser_ctx->scanner);
-}
-
-LTTNG_HIDDEN
-struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input)
-{
- struct filter_parser_ctx *parser_ctx;
- int ret;
-
- yydebug = filter_parser_debug;
-
- parser_ctx = zmalloc(sizeof(*parser_ctx));
- if (!parser_ctx)
- return NULL;
- memset(parser_ctx, 0, sizeof(*parser_ctx));
-
- ret = yylex_init_extra(parser_ctx, &parser_ctx->scanner);
- if (ret) {
- fprintf(stderr, "yylex_init error\n");
- goto cleanup_parser_ctx;
- }
- /* Start processing new stream */
- yyrestart(input, parser_ctx->scanner);
-
- parser_ctx->ast = filter_ast_alloc();
- if (!parser_ctx->ast)
- goto cleanup_lexer;
- CDS_INIT_LIST_HEAD(&parser_ctx->allocated_strings);
-
- if (yydebug)
- fprintf(stdout, "parser_ctx input is a%s.\n",
- isatty(fileno(input)) ? "n interactive tty" :
- " noninteractive file");
-
- return parser_ctx;
-
-cleanup_lexer:
- ret = yylex_destroy(parser_ctx->scanner);
- if (!ret)
- fprintf(stderr, "yylex_destroy error\n");
-cleanup_parser_ctx:
- free(parser_ctx);
- return NULL;
-}
-
-LTTNG_HIDDEN
-void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx)
-{
- int ret;
-
- free_strings(&parser_ctx->allocated_strings);
- filter_ast_free(parser_ctx->ast);
- ret = yylex_destroy(parser_ctx->scanner);
- if (ret)
- fprintf(stderr, "yylex_destroy error\n");
- free(parser_ctx);
-}
-
-%}
-
-%code provides
-{
-#include "common/macros.h"
-
-LTTNG_HIDDEN
-void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src);
-}
-
-%define api.pure
- /* %locations */
-%parse-param {struct filter_parser_ctx *parser_ctx}
-%parse-param {yyscan_t scanner}
-%lex-param {yyscan_t scanner}
-%start translation_unit
-%token CHARACTER_CONSTANT_START SQUOTE STRING_LITERAL_START DQUOTE
-%token ESCSEQ CHAR_STRING_TOKEN
-%token DECIMAL_CONSTANT OCTAL_CONSTANT HEXADECIMAL_CONSTANT FLOAT_CONSTANT
-%token LSBRAC RSBRAC LPAREN RPAREN LBRAC RBRAC RARROW
-%token STAR PLUS MINUS
-%token MOD_OP DIV_OP RIGHT_OP LEFT_OP
-%token EQ_OP NE_OP LE_OP GE_OP LT_OP GT_OP AND_OP OR_OP NOT_OP
-%token ASSIGN COLON SEMICOLON DOTDOTDOT DOT EQUAL COMMA
-%token XOR_BIN AND_BIN OR_BIN NOT_BIN
-
-%token <gs> IDENTIFIER GLOBAL_IDENTIFIER
-%token ERROR
-%union
-{
- long long ll;
- char c;
- struct gc_string *gs;
- struct filter_node *n;
-}
-
-%type <gs> s_char s_char_sequence c_char c_char_sequence
-
-%type <n> primary_expression
-%type <n> prefix_expression
-%type <n> prefix_expression_rec
-%type <n> postfix_expression
-%type <n> unary_expression
-%type <n> unary_operator
-%type <n> multiplicative_expression
-%type <n> additive_expression
-%type <n> shift_expression
-%type <n> relational_expression
-%type <n> equality_expression
-%type <n> and_expression
-%type <n> exclusive_or_expression
-%type <n> inclusive_or_expression
-%type <n> logical_and_expression
-%type <n> logical_or_expression
-%type <n> expression
-%type <n> identifiers
-
-%%
-
-
-/* 1.5 Constants */
-
-c_char_sequence:
- c_char
- { $$ = $1; }
- | c_char_sequence c_char
- { $$ = gc_string_append(parser_ctx, $1, $2); }
- ;
-
-c_char:
- CHAR_STRING_TOKEN
- { $$ = yylval.gs; }
- | ESCSEQ
- {
- parse_error(parser_ctx, "escape sequences not supported yet");
- }
- ;
-
-/* 1.6 String literals */
-
-s_char_sequence:
- s_char
- { $$ = $1; }
- | s_char_sequence s_char
- { $$ = gc_string_append(parser_ctx, $1, $2); }
- ;
-
-s_char:
- CHAR_STRING_TOKEN
- { $$ = yylval.gs; }
- | ESCSEQ
- {
- parse_error(parser_ctx, "escape sequences not supported yet");
- }
- ;
-
-primary_expression:
- DECIMAL_CONSTANT
- {
- $$ = make_node(parser_ctx, NODE_EXPRESSION);
- $$->u.expression.type = AST_EXP_CONSTANT;
- if (sscanf(yylval.gs->s, "%" WIDTH_u64_SCANF_IS_A_BROKEN_API SCNu64,
- &$$->u.expression.u.constant) != 1) {
- parse_error(parser_ctx, "cannot scanf decimal constant");
- }
- }
- | OCTAL_CONSTANT
- {
- $$ = make_node(parser_ctx, NODE_EXPRESSION);
- $$->u.expression.type = AST_EXP_CONSTANT;
- if (!strcmp(yylval.gs->s, "0")) {
- $$->u.expression.u.constant = 0;
- } else if (sscanf(yylval.gs->s, "0%" WIDTH_o64_SCANF_IS_A_BROKEN_API SCNo64,
- &$$->u.expression.u.constant) != 1) {
- parse_error(parser_ctx, "cannot scanf octal constant");
- }
- }
- | HEXADECIMAL_CONSTANT
- {
- $$ = make_node(parser_ctx, NODE_EXPRESSION);
- $$->u.expression.type = AST_EXP_CONSTANT;
- if (sscanf(yylval.gs->s, "0x%" WIDTH_x64_SCANF_IS_A_BROKEN_API SCNx64,
- &$$->u.expression.u.constant) != 1) {
- parse_error(parser_ctx, "cannot scanf hexadecimal constant");
- }
- }
- | FLOAT_CONSTANT
- {
- $$ = make_node(parser_ctx, NODE_EXPRESSION);
- $$->u.expression.type = AST_EXP_FLOAT_CONSTANT;
- if (sscanf(yylval.gs->s, "%" WIDTH_lg_SCANF_IS_A_BROKEN_API "lg",
- &$$->u.expression.u.float_constant) != 1) {
- parse_error(parser_ctx, "cannot scanf float constant");
- }
- }
- | STRING_LITERAL_START DQUOTE
- {
- $$ = make_node(parser_ctx, NODE_EXPRESSION);
- $$->u.expression.type = AST_EXP_STRING;
- $$->u.expression.u.string = "";
- }
- | STRING_LITERAL_START s_char_sequence DQUOTE
- {
- $$ = make_node(parser_ctx, NODE_EXPRESSION);
- $$->u.expression.type = AST_EXP_STRING;
- $$->u.expression.u.string = $2->s;
- }
- | CHARACTER_CONSTANT_START c_char_sequence SQUOTE
- {
- $$ = make_node(parser_ctx, NODE_EXPRESSION);
- $$->u.expression.type = AST_EXP_STRING;
- $$->u.expression.u.string = $2->s;
- }
- | LPAREN expression RPAREN
- {
- $$ = make_node(parser_ctx, NODE_EXPRESSION);
- $$->u.expression.type = AST_EXP_NESTED;
- $$->u.expression.u.child = $2;
- }
- ;
-
-identifiers
- : IDENTIFIER
- {
- $$ = make_node(parser_ctx, NODE_EXPRESSION);
- $$->u.expression.type = AST_EXP_IDENTIFIER;
- $$->u.expression.u.identifier = yylval.gs->s;
- }
- | GLOBAL_IDENTIFIER
- {
- $$ = make_node(parser_ctx, NODE_EXPRESSION);
- $$->u.expression.type = AST_EXP_GLOBAL_IDENTIFIER;
- $$->u.expression.u.identifier = yylval.gs->s;
- }
- ;
-
-prefix_expression_rec
- : LSBRAC unary_expression RSBRAC
- {
- $$ = $2;
- }
- | LSBRAC unary_expression RSBRAC prefix_expression_rec
- {
- $$ = $2;
- $$->u.expression.pre_op = AST_LINK_BRACKET;
- $$->u.expression.prev = $4;
- }
- ;
-
-prefix_expression
- : identifiers
- {
- $$ = $1;
- }
- | identifiers prefix_expression_rec
- {
- $$ = $1;
- $$->u.expression.pre_op = AST_LINK_BRACKET;
- $$->u.expression.next_bracket = $2;
- }
- ;
-
-postfix_expression
- : prefix_expression
- {
- $$ = $1;
- }
- | postfix_expression DOT prefix_expression
- {
- $$ = $3;
- $$->u.expression.post_op = AST_LINK_DOT;
- $$->u.expression.prev = $1;
- }
- | postfix_expression RARROW prefix_expression
- {
- $$ = $3;
- $$->u.expression.post_op = AST_LINK_RARROW;
- $$->u.expression.prev = $1;
- }
- ;
-
-unary_expression
- : postfix_expression
- { $$ = $1; }
- | primary_expression
- { $$ = $1; }
- | unary_operator unary_expression
- {
- $$ = $1;
- $$->u.unary_op.child = $2;
- }
- ;
-
-unary_operator
- : PLUS
- {
- $$ = make_node(parser_ctx, NODE_UNARY_OP);
- $$->u.unary_op.type = AST_UNARY_PLUS;
- }
- | MINUS
- {
- $$ = make_node(parser_ctx, NODE_UNARY_OP);
- $$->u.unary_op.type = AST_UNARY_MINUS;
- }
- | NOT_OP
- {
- $$ = make_node(parser_ctx, NODE_UNARY_OP);
- $$->u.unary_op.type = AST_UNARY_NOT;
- }
- | NOT_BIN
- {
- $$ = make_node(parser_ctx, NODE_UNARY_OP);
- $$->u.unary_op.type = AST_UNARY_BIT_NOT;
- }
- ;
-
-multiplicative_expression
- : unary_expression
- { $$ = $1; }
- | multiplicative_expression STAR unary_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_MUL, $1, $3);
- }
- | multiplicative_expression DIV_OP unary_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_DIV, $1, $3);
- }
- | multiplicative_expression MOD_OP unary_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_MOD, $1, $3);
- }
- ;
-
-additive_expression
- : multiplicative_expression
- { $$ = $1; }
- | additive_expression PLUS multiplicative_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_PLUS, $1, $3);
- }
- | additive_expression MINUS multiplicative_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_MINUS, $1, $3);
- }
- ;
-
-shift_expression
- : additive_expression
- { $$ = $1; }
- | shift_expression LEFT_OP additive_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_BIT_LSHIFT, $1, $3);
- }
- | shift_expression RIGHT_OP additive_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_BIT_RSHIFT, $1, $3);
- }
- ;
-
-and_expression
- : shift_expression
- { $$ = $1; }
- | and_expression AND_BIN shift_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_BIT_AND, $1, $3);
- }
- ;
-
-exclusive_or_expression
- : and_expression
- { $$ = $1; }
- | exclusive_or_expression XOR_BIN and_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_BIT_XOR, $1, $3);
- }
- ;
-
-inclusive_or_expression
- : exclusive_or_expression
- { $$ = $1; }
- | inclusive_or_expression OR_BIN exclusive_or_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_BIT_OR, $1, $3);
- }
- ;
-
-relational_expression
- : inclusive_or_expression
- { $$ = $1; }
- | relational_expression LT_OP inclusive_or_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_LT, $1, $3);
- }
- | relational_expression GT_OP inclusive_or_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_GT, $1, $3);
- }
- | relational_expression LE_OP inclusive_or_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_LE, $1, $3);
- }
- | relational_expression GE_OP inclusive_or_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_GE, $1, $3);
- }
- ;
-
-equality_expression
- : relational_expression
- { $$ = $1; }
- | equality_expression EQ_OP relational_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_EQ, $1, $3);
- }
- | equality_expression NE_OP relational_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_NE, $1, $3);
- }
- ;
-
-logical_and_expression
- : equality_expression
- { $$ = $1; }
- | logical_and_expression AND_OP equality_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_AND, $1, $3);
- }
- ;
-
-logical_or_expression
- : logical_and_expression
- { $$ = $1; }
- | logical_or_expression OR_OP logical_and_expression
- {
- $$ = make_op_node(parser_ctx, AST_OP_OR, $1, $3);
- }
- ;
-
-expression
- : logical_or_expression
- { $$ = $1; }
- ;
-
-translation_unit
- : expression
- {
- parser_ctx->ast->root.u.root.child = $1;
- }
- ;
+++ /dev/null
-#ifndef _FILTER_SYMBOLS_H
-#define _FILTER_SYMBOLS_H
-
-/*
- * filter-symbols.h
- *
- * LTTng filter flex/bison symbol prefixes
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#define yy_create_buffer lttng_yy_create_buffer
-#define yy_delete_buffer lttng_yy_delete_buffer
-#define yy_flush_buffer lttng_yy_flush_buffer
-#define yy_scan_buffer lttng_yy_scan_buffer
-#define yy_scan_bytes lttng_yy_scan_bytes
-#define yy_scan_string lttng_yy_scan_string
-#define yy_switch_to_buffer lttng_yy_switch_to_buffer
-#define yyalloc lttng_yyalloc
-#define yyfree lttng_yyfree
-#define yyget_column lttng_yyget_column
-#define yyget_debug lttng_yyget_debug
-#define yyget_extra lttng_yyget_extra
-#define yyget_in lttng_yyget_in
-#define yyget_leng lttng_yyget_leng
-#define yyget_lineno lttng_yyget_lineno
-#define yyget_lval lttng_yyget_lval
-#define yyget_out lttng_yyget_out
-#define yyget_text lttng_yyget_text
-#define yylex_init lttng_yylex_init
-#define yypop_buffer_state lttng_yypop_buffer_state
-#define yypush_buffer_state lttng_yypush_buffer_state
-#define yyrealloc lttng_yyrealloc
-#define yyset_column lttng_yyset_column
-#define yyset_debug lttng_yyset_debug
-#define yyset_extra lttng_yyset_extra
-#define yyset_in lttng_yyset_in
-#define yyset_lineno lttng_yyset_lineno
-#define yyset_lval lttng_yyset_lval
-#define yyset_out lttng_yyset_out
-
-#endif /* _FILTER_SYMBOLS_H */
+++ /dev/null
-/*
- * filter-visitor-generate-bytecode.c
- *
- * LTTng filter bytecode generation
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <common/align.h>
-#include <common/compat/string.h>
-
-#include "filter-bytecode.h"
-#include "filter-ir.h"
-#include "filter-ast.h"
-
-#include <common/macros.h>
-
-#ifndef max_t
-#define max_t(type, a, b) ((type) ((a) > (b) ? (a) : (b)))
-#endif
-
-#define INIT_ALLOC_SIZE 4
-
-static
-int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx,
- struct ir_op *node);
-
-static inline int get_count_order(unsigned int count)
-{
- int order;
-
- order = lttng_fls(count) - 1;
- if (count & (count - 1))
- order++;
- return order;
-}
-
-static
-int bytecode_init(struct lttng_filter_bytecode_alloc **fb)
-{
- uint32_t alloc_len;
-
- alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + INIT_ALLOC_SIZE;
- *fb = calloc(alloc_len, 1);
- if (!*fb) {
- return -ENOMEM;
- } else {
- (*fb)->alloc_len = alloc_len;
- return 0;
- }
-}
-
-static
-int32_t bytecode_reserve(struct lttng_filter_bytecode_alloc **fb, uint32_t align, uint32_t len)
-{
- int32_t ret;
- uint32_t padding = offset_align((*fb)->b.len, align);
- uint32_t new_len = (*fb)->b.len + padding + len;
- uint32_t new_alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + new_len;
- uint32_t old_alloc_len = (*fb)->alloc_len;
-
- if (new_len > LTTNG_FILTER_MAX_LEN)
- return -EINVAL;
-
- if (new_alloc_len > old_alloc_len) {
- struct lttng_filter_bytecode_alloc *newptr;
-
- new_alloc_len =
- max_t(uint32_t, 1U << get_count_order(new_alloc_len), old_alloc_len << 1);
- newptr = realloc(*fb, new_alloc_len);
- if (!newptr)
- return -ENOMEM;
- *fb = newptr;
- /* We zero directly the memory from start of allocation. */
- memset(&((char *) *fb)[old_alloc_len], 0, new_alloc_len - old_alloc_len);
- (*fb)->alloc_len = new_alloc_len;
- }
- (*fb)->b.len += padding;
- ret = (*fb)->b.len;
- (*fb)->b.len += len;
- return ret;
-}
-
-static
-int bytecode_push(struct lttng_filter_bytecode_alloc **fb, const void *data,
- uint32_t align, uint32_t len)
-{
- int32_t offset;
-
- offset = bytecode_reserve(fb, align, len);
- if (offset < 0)
- return offset;
- memcpy(&(*fb)->b.data[offset], data, len);
- return 0;
-}
-
-static
-int bytecode_push_logical(struct lttng_filter_bytecode_alloc **fb,
- struct logical_op *data,
- uint32_t align, uint32_t len,
- uint16_t *skip_offset)
-{
- int32_t offset;
-
- offset = bytecode_reserve(fb, align, len);
- if (offset < 0)
- return offset;
- memcpy(&(*fb)->b.data[offset], data, len);
- *skip_offset =
- (void *) &((struct logical_op *) &(*fb)->b.data[offset])->skip_offset
- - (void *) &(*fb)->b.data[0];
- return 0;
-}
-
-static
-int bytecode_patch(struct lttng_filter_bytecode_alloc **fb,
- const void *data,
- uint16_t offset,
- uint32_t len)
-{
- if (offset >= (*fb)->b.len) {
- return -EINVAL;
- }
- memcpy(&(*fb)->b.data[offset], data, len);
- return 0;
-}
-
-static
-int visit_node_root(struct filter_parser_ctx *ctx, struct ir_op *node)
-{
- int ret;
- struct return_op insn;
-
- /* Visit child */
- ret = recursive_visit_gen_bytecode(ctx, node->u.root.child);
- if (ret)
- return ret;
-
- /* Generate end of bytecode instruction */
- insn.op = FILTER_OP_RETURN;
- return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
-}
-
-static
-int append_str(char **s, const char *append)
-{
- char *old = *s;
- char *new;
- size_t oldlen = (old == NULL) ? 0 : strlen(old);
- size_t appendlen = strlen(append);
-
- new = calloc(oldlen + appendlen + 1, 1);
- if (!new) {
- return -ENOMEM;
- }
- if (oldlen) {
- strcpy(new, old);
- }
- strcat(new, append);
- *s = new;
- free(old);
- return 0;
-}
-
-/*
- * 1: match
- * 0: no match
- * < 0: error
- */
-static
-int load_expression_legacy_match(const struct ir_load_expression *exp,
- enum filter_op *op_type,
- char **symbol)
-{
- const struct ir_load_expression_op *op;
- bool need_dot = false;
-
- op = exp->child;
- switch (op->type) {
- case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
- *op_type = FILTER_OP_GET_CONTEXT_REF;
- if (append_str(symbol, "$ctx.")) {
- return -ENOMEM;
- }
- need_dot = false;
- break;
- case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
- *op_type = FILTER_OP_GET_CONTEXT_REF;
- if (append_str(symbol, "$app.")) {
- return -ENOMEM;
- }
- need_dot = false;
- break;
- case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
- *op_type = FILTER_OP_LOAD_FIELD_REF;
- need_dot = false;
- break;
-
- case IR_LOAD_EXPRESSION_GET_SYMBOL:
- case IR_LOAD_EXPRESSION_GET_INDEX:
- case IR_LOAD_EXPRESSION_LOAD_FIELD:
- default:
- return 0; /* no match */
- }
-
- for (;;) {
- op = op->next;
- if (!op) {
- return 0; /* no match */
- }
- switch (op->type) {
- case IR_LOAD_EXPRESSION_LOAD_FIELD:
- goto end;
- case IR_LOAD_EXPRESSION_GET_SYMBOL:
- if (need_dot && append_str(symbol, ".")) {
- return -ENOMEM;
- }
- if (append_str(symbol, op->u.symbol)) {
- return -ENOMEM;
- }
- break;
- default:
- return 0; /* no match */
- }
- need_dot = true;
- }
-end:
- return 1; /* Legacy match */
-}
-
-/*
- * 1: legacy match
- * 0: no legacy match
- * < 0: error
- */
-static
-int visit_node_load_expression_legacy(struct filter_parser_ctx *ctx,
- const struct ir_load_expression *exp,
- const struct ir_load_expression_op *op)
-{
- struct load_op *insn = NULL;
- uint32_t insn_len = sizeof(struct load_op)
- + sizeof(struct field_ref);
- struct field_ref ref_offset;
- uint32_t reloc_offset_u32;
- uint16_t reloc_offset;
- enum filter_op op_type;
- char *symbol = NULL;
- int ret;
-
- ret = load_expression_legacy_match(exp, &op_type, &symbol);
- if (ret <= 0) {
- goto end;
- }
- insn = calloc(insn_len, 1);
- if (!insn) {
- ret = -ENOMEM;
- goto end;
- }
- insn->op = op_type;
- ref_offset.offset = (uint16_t) -1U;
- memcpy(insn->data, &ref_offset, sizeof(ref_offset));
- /* reloc_offset points to struct load_op */
- reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b);
- if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
- ret = -EINVAL;
- goto end;
- }
- reloc_offset = (uint16_t) reloc_offset_u32;
- ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
- if (ret) {
- goto end;
- }
- /* append reloc */
- ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset,
- 1, sizeof(reloc_offset));
- if (ret) {
- goto end;
- }
- ret = bytecode_push(&ctx->bytecode_reloc, symbol,
- 1, strlen(symbol) + 1);
- if (ret) {
- goto end;
- }
- ret = 1; /* legacy */
-end:
- free(insn);
- free(symbol);
- return ret;
-}
-
-static
-int visit_node_load_expression(struct filter_parser_ctx *ctx,
- const struct ir_op *node)
-{
- struct ir_load_expression *exp;
- struct ir_load_expression_op *op;
- int ret;
-
- exp = node->u.load.u.expression;
- if (!exp) {
- return -EINVAL;
- }
- op = exp->child;
- if (!op) {
- return -EINVAL;
- }
-
- /*
- * TODO: if we remove legacy load for application contexts, we
- * need to update session bytecode parser as well.
- */
- ret = visit_node_load_expression_legacy(ctx, exp, op);
- if (ret < 0) {
- return ret;
- }
- if (ret > 0) {
- return 0; /* legacy */
- }
-
- for (; op != NULL; op = op->next) {
- switch (op->type) {
- case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
- {
- struct load_op *insn;
- uint32_t insn_len = sizeof(struct load_op);
- int ret;
-
- insn = calloc(insn_len, 1);
- if (!insn)
- return -ENOMEM;
- insn->op = FILTER_OP_GET_CONTEXT_ROOT;
- ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
- free(insn);
- if (ret) {
- return ret;
- }
- break;
- }
- case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
- {
- struct load_op *insn;
- uint32_t insn_len = sizeof(struct load_op);
- int ret;
-
- insn = calloc(insn_len, 1);
- if (!insn)
- return -ENOMEM;
- insn->op = FILTER_OP_GET_APP_CONTEXT_ROOT;
- ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
- free(insn);
- if (ret) {
- return ret;
- }
- break;
- }
- case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
- {
- struct load_op *insn;
- uint32_t insn_len = sizeof(struct load_op);
- int ret;
-
- insn = calloc(insn_len, 1);
- if (!insn)
- return -ENOMEM;
- insn->op = FILTER_OP_GET_PAYLOAD_ROOT;
- ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
- free(insn);
- if (ret) {
- return ret;
- }
- break;
- }
- case IR_LOAD_EXPRESSION_GET_SYMBOL:
- {
- struct load_op *insn;
- uint32_t insn_len = sizeof(struct load_op)
- + sizeof(struct get_symbol);
- struct get_symbol symbol_offset;
- uint32_t reloc_offset_u32;
- uint16_t reloc_offset;
- uint32_t bytecode_reloc_offset_u32;
- int ret;
-
- insn = calloc(insn_len, 1);
- if (!insn)
- return -ENOMEM;
- insn->op = FILTER_OP_GET_SYMBOL;
- bytecode_reloc_offset_u32 =
- bytecode_get_len(&ctx->bytecode_reloc->b)
- + sizeof(reloc_offset);
- symbol_offset.offset =
- (uint16_t) bytecode_reloc_offset_u32;
- memcpy(insn->data, &symbol_offset,
- sizeof(symbol_offset));
- /* reloc_offset points to struct load_op */
- reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b);
- if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
- free(insn);
- return -EINVAL;
- }
- reloc_offset = (uint16_t) reloc_offset_u32;
- ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
- if (ret) {
- free(insn);
- return ret;
- }
- /* append reloc */
- ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset,
- 1, sizeof(reloc_offset));
- if (ret) {
- free(insn);
- return ret;
- }
- ret = bytecode_push(&ctx->bytecode_reloc,
- op->u.symbol,
- 1, strlen(op->u.symbol) + 1);
- free(insn);
- if (ret) {
- return ret;
- }
- break;
- }
- case IR_LOAD_EXPRESSION_GET_INDEX:
- {
- struct load_op *insn;
- uint32_t insn_len = sizeof(struct load_op)
- + sizeof(struct get_index_u64);
- struct get_index_u64 index;
- int ret;
-
- insn = calloc(insn_len, 1);
- if (!insn)
- return -ENOMEM;
- insn->op = FILTER_OP_GET_INDEX_U64;
- index.index = op->u.index;
- memcpy(insn->data, &index, sizeof(index));
- ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
- free(insn);
- if (ret) {
- return ret;
- }
- break;
- }
- case IR_LOAD_EXPRESSION_LOAD_FIELD:
- {
- struct load_op *insn;
- uint32_t insn_len = sizeof(struct load_op);
- int ret;
-
- insn = calloc(insn_len, 1);
- if (!insn)
- return -ENOMEM;
- insn->op = FILTER_OP_LOAD_FIELD;
- ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
- free(insn);
- if (ret) {
- return ret;
- }
- break;
- }
- }
- }
- return 0;
-}
-
-static
-int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node)
-{
- int ret;
-
- switch (node->data_type) {
- case IR_DATA_UNKNOWN:
- default:
- fprintf(stderr, "[error] Unknown data type in %s\n",
- __func__);
- return -EINVAL;
-
- case IR_DATA_STRING:
- {
- struct load_op *insn;
- uint32_t insn_len = sizeof(struct load_op)
- + strlen(node->u.load.u.string.value) + 1;
-
- insn = calloc(insn_len, 1);
- if (!insn)
- return -ENOMEM;
-
- switch (node->u.load.u.string.type) {
- case IR_LOAD_STRING_TYPE_GLOB_STAR:
- /*
- * We explicitly tell the interpreter here that
- * this load is a full star globbing pattern so
- * that the appropriate matching function can be
- * called. Also, see comment below.
- */
- insn->op = FILTER_OP_LOAD_STAR_GLOB_STRING;
- break;
- default:
- /*
- * This is the "legacy" string, which includes
- * star globbing patterns with a star only at
- * the end. Both "plain" and "star at the end"
- * literal strings are handled at the same place
- * by the tracer's filter bytecode interpreter,
- * whereas full star globbing patterns (stars
- * can be anywhere in the string) is a special
- * case.
- */
- insn->op = FILTER_OP_LOAD_STRING;
- break;
- }
-
- strcpy(insn->data, node->u.load.u.string.value);
- ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
- free(insn);
- return ret;
- }
- case IR_DATA_NUMERIC:
- {
- struct load_op *insn;
- uint32_t insn_len = sizeof(struct load_op)
- + sizeof(struct literal_numeric);
-
- insn = calloc(insn_len, 1);
- if (!insn)
- return -ENOMEM;
- insn->op = FILTER_OP_LOAD_S64;
- memcpy(insn->data, &node->u.load.u.num, sizeof(int64_t));
- ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
- free(insn);
- return ret;
- }
- case IR_DATA_FLOAT:
- {
- struct load_op *insn;
- uint32_t insn_len = sizeof(struct load_op)
- + sizeof(struct literal_double);
-
- insn = calloc(insn_len, 1);
- if (!insn)
- return -ENOMEM;
- insn->op = FILTER_OP_LOAD_DOUBLE;
- memcpy(insn->data, &node->u.load.u.flt, sizeof(double));
- ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
- free(insn);
- return ret;
- }
- case IR_DATA_EXPRESSION:
- return visit_node_load_expression(ctx, node);
- }
-}
-
-static
-int visit_node_unary(struct filter_parser_ctx *ctx, struct ir_op *node)
-{
- int ret;
- struct unary_op insn;
-
- /* Visit child */
- ret = recursive_visit_gen_bytecode(ctx, node->u.unary.child);
- if (ret)
- return ret;
-
- /* Generate end of bytecode instruction */
- switch (node->u.unary.type) {
- case AST_UNARY_UNKNOWN:
- default:
- fprintf(stderr, "[error] Unknown unary node type in %s\n",
- __func__);
- return -EINVAL;
- case AST_UNARY_PLUS:
- /* Nothing to do. */
- return 0;
- case AST_UNARY_MINUS:
- insn.op = FILTER_OP_UNARY_MINUS;
- return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
- case AST_UNARY_NOT:
- insn.op = FILTER_OP_UNARY_NOT;
- return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
- case AST_UNARY_BIT_NOT:
- insn.op = FILTER_OP_UNARY_BIT_NOT;
- return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
- }
-}
-
-/*
- * Binary comparator nesting is disallowed. This allows fitting into
- * only 2 registers.
- */
-static
-int visit_node_binary(struct filter_parser_ctx *ctx, struct ir_op *node)
-{
- int ret;
- struct binary_op insn;
-
- /* Visit child */
- ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left);
- if (ret)
- return ret;
- ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right);
- if (ret)
- return ret;
-
- switch (node->u.binary.type) {
- case AST_OP_UNKNOWN:
- default:
- fprintf(stderr, "[error] Unknown unary node type in %s\n",
- __func__);
- return -EINVAL;
-
- case AST_OP_AND:
- case AST_OP_OR:
- fprintf(stderr, "[error] Unexpected logical node type in %s\n",
- __func__);
- return -EINVAL;
-
- case AST_OP_MUL:
- insn.op = FILTER_OP_MUL;
- break;
- case AST_OP_DIV:
- insn.op = FILTER_OP_DIV;
- break;
- case AST_OP_MOD:
- insn.op = FILTER_OP_MOD;
- break;
- case AST_OP_PLUS:
- insn.op = FILTER_OP_PLUS;
- break;
- case AST_OP_MINUS:
- insn.op = FILTER_OP_MINUS;
- break;
- case AST_OP_BIT_RSHIFT:
- insn.op = FILTER_OP_BIT_RSHIFT;
- break;
- case AST_OP_BIT_LSHIFT:
- insn.op = FILTER_OP_BIT_LSHIFT;
- break;
- case AST_OP_BIT_AND:
- insn.op = FILTER_OP_BIT_AND;
- break;
- case AST_OP_BIT_OR:
- insn.op = FILTER_OP_BIT_OR;
- break;
- case AST_OP_BIT_XOR:
- insn.op = FILTER_OP_BIT_XOR;
- break;
-
- case AST_OP_EQ:
- insn.op = FILTER_OP_EQ;
- break;
- case AST_OP_NE:
- insn.op = FILTER_OP_NE;
- break;
- case AST_OP_GT:
- insn.op = FILTER_OP_GT;
- break;
- case AST_OP_LT:
- insn.op = FILTER_OP_LT;
- break;
- case AST_OP_GE:
- insn.op = FILTER_OP_GE;
- break;
- case AST_OP_LE:
- insn.op = FILTER_OP_LE;
- break;
- }
- return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
-}
-
-/*
- * A logical op always return a s64 (1 or 0).
- */
-static
-int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node)
-{
- int ret;
- struct logical_op insn;
- uint16_t skip_offset_loc;
- uint16_t target_loc;
-
- /* Visit left child */
- ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left);
- if (ret)
- return ret;
- /* Cast to s64 if float or field ref */
- if ((node->u.binary.left->data_type == IR_DATA_FIELD_REF
- || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF
- || node->u.binary.left->data_type == IR_DATA_EXPRESSION)
- || node->u.binary.left->data_type == IR_DATA_FLOAT) {
- struct cast_op cast_insn;
-
- if (node->u.binary.left->data_type == IR_DATA_FIELD_REF
- || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF
- || node->u.binary.left->data_type == IR_DATA_EXPRESSION) {
- cast_insn.op = FILTER_OP_CAST_TO_S64;
- } else {
- cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64;
- }
- ret = bytecode_push(&ctx->bytecode, &cast_insn,
- 1, sizeof(cast_insn));
- if (ret)
- return ret;
- }
- switch (node->u.logical.type) {
- default:
- fprintf(stderr, "[error] Unknown node type in %s\n",
- __func__);
- return -EINVAL;
-
- case AST_OP_AND:
- insn.op = FILTER_OP_AND;
- break;
- case AST_OP_OR:
- insn.op = FILTER_OP_OR;
- break;
- }
- insn.skip_offset = (uint16_t) -1UL; /* Temporary */
- ret = bytecode_push_logical(&ctx->bytecode, &insn, 1, sizeof(insn),
- &skip_offset_loc);
- if (ret)
- return ret;
- /* Visit right child */
- ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right);
- if (ret)
- return ret;
- /* Cast to s64 if float or field ref */
- if ((node->u.binary.right->data_type == IR_DATA_FIELD_REF
- || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF
- || node->u.binary.right->data_type == IR_DATA_EXPRESSION)
- || node->u.binary.right->data_type == IR_DATA_FLOAT) {
- struct cast_op cast_insn;
-
- if (node->u.binary.right->data_type == IR_DATA_FIELD_REF
- || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF
- || node->u.binary.right->data_type == IR_DATA_EXPRESSION) {
- cast_insn.op = FILTER_OP_CAST_TO_S64;
- } else {
- cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64;
- }
- ret = bytecode_push(&ctx->bytecode, &cast_insn,
- 1, sizeof(cast_insn));
- if (ret)
- return ret;
- }
- /* We now know where the logical op can skip. */
- target_loc = (uint16_t) bytecode_get_len(&ctx->bytecode->b);
- ret = bytecode_patch(&ctx->bytecode,
- &target_loc, /* Offset to jump to */
- skip_offset_loc, /* Where to patch */
- sizeof(uint16_t));
- return ret;
-}
-
-/*
- * Postorder traversal of the tree. We need the children result before
- * we can evaluate the parent.
- */
-static
-int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx,
- struct ir_op *node)
-{
- switch (node->op) {
- case IR_OP_UNKNOWN:
- default:
- fprintf(stderr, "[error] Unknown node type in %s\n",
- __func__);
- return -EINVAL;
-
- case IR_OP_ROOT:
- return visit_node_root(ctx, node);
- case IR_OP_LOAD:
- return visit_node_load(ctx, node);
- case IR_OP_UNARY:
- return visit_node_unary(ctx, node);
- case IR_OP_BINARY:
- return visit_node_binary(ctx, node);
- case IR_OP_LOGICAL:
- return visit_node_logical(ctx, node);
- }
-}
-
-LTTNG_HIDDEN
-void filter_bytecode_free(struct filter_parser_ctx *ctx)
-{
- if (!ctx) {
- return;
- }
-
- if (ctx->bytecode) {
- free(ctx->bytecode);
- ctx->bytecode = NULL;
- }
-
- if (ctx->bytecode_reloc) {
- free(ctx->bytecode_reloc);
- ctx->bytecode_reloc = NULL;
- }
-}
-
-LTTNG_HIDDEN
-int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx)
-{
- int ret;
-
- ret = bytecode_init(&ctx->bytecode);
- if (ret)
- return ret;
- ret = bytecode_init(&ctx->bytecode_reloc);
- if (ret)
- goto error;
- ret = recursive_visit_gen_bytecode(ctx, ctx->ir_root);
- if (ret)
- goto error;
-
- /* Finally, append symbol table to bytecode */
- ctx->bytecode->b.reloc_table_offset = bytecode_get_len(&ctx->bytecode->b);
- return bytecode_push(&ctx->bytecode, ctx->bytecode_reloc->b.data,
- 1, bytecode_get_len(&ctx->bytecode_reloc->b));
-
-error:
- filter_bytecode_free(ctx);
- return ret;
-}
+++ /dev/null
-/*
- * filter-visitor-generate-ir.c
- *
- * LTTng filter generate intermediate representation
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-#include <common/macros.h>
-#include <common/string-utils/string-utils.h>
-
-static
-struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx,
- struct filter_node *node, enum ir_side side);
-
-static
-struct ir_op *make_op_root(struct ir_op *child, enum ir_side side)
-{
- struct ir_op *op;
-
- op = calloc(sizeof(struct ir_op), 1);
- if (!op)
- return NULL;
- switch (child->data_type) {
- case IR_DATA_UNKNOWN:
- default:
- fprintf(stderr, "[error] Unknown root child data type\n");
- free(op);
- return NULL;
- case IR_DATA_STRING:
- fprintf(stderr, "[error] String cannot be root data type\n");
- free(op);
- return NULL;
- case IR_DATA_NUMERIC:
- case IR_DATA_FIELD_REF:
- case IR_DATA_GET_CONTEXT_REF:
- case IR_DATA_EXPRESSION:
- /* ok */
- break;
- }
- op->op = IR_OP_ROOT;
- op->side = side;
- op->data_type = child->data_type;
- op->signedness = child->signedness;
- op->u.root.child = child;
- return op;
-}
-
-static
-enum ir_load_string_type get_literal_string_type(const char *string)
-{
- assert(string);
-
- if (strutils_is_star_glob_pattern(string)) {
- if (strutils_is_star_at_the_end_only_glob_pattern(string)) {
- return IR_LOAD_STRING_TYPE_GLOB_STAR_END;
- }
-
- return IR_LOAD_STRING_TYPE_GLOB_STAR;
- }
-
- return IR_LOAD_STRING_TYPE_PLAIN;
-}
-
-static
-struct ir_op *make_op_load_string(const char *string, enum ir_side side)
-{
- struct ir_op *op;
-
- op = calloc(sizeof(struct ir_op), 1);
- if (!op)
- return NULL;
- op->op = IR_OP_LOAD;
- op->data_type = IR_DATA_STRING;
- op->signedness = IR_SIGN_UNKNOWN;
- op->side = side;
- op->u.load.u.string.type = get_literal_string_type(string);
- op->u.load.u.string.value = strdup(string);
- if (!op->u.load.u.string.value) {
- free(op);
- return NULL;
- }
- return op;
-}
-
-static
-struct ir_op *make_op_load_numeric(int64_t v, enum ir_side side)
-{
- struct ir_op *op;
-
- op = calloc(sizeof(struct ir_op), 1);
- if (!op)
- return NULL;
- op->op = IR_OP_LOAD;
- op->data_type = IR_DATA_NUMERIC;
- /* TODO: for now, all numeric values are signed */
- op->signedness = IR_SIGNED;
- op->side = side;
- op->u.load.u.num = v;
- return op;
-}
-
-static
-struct ir_op *make_op_load_float(double v, enum ir_side side)
-{
- struct ir_op *op;
-
- op = calloc(sizeof(struct ir_op), 1);
- if (!op)
- return NULL;
- op->op = IR_OP_LOAD;
- op->data_type = IR_DATA_FLOAT;
- op->signedness = IR_SIGN_UNKNOWN;
- op->side = side;
- op->u.load.u.flt = v;
- return op;
-}
-
-static
-void free_load_expression(struct ir_load_expression *load_expression)
-{
- struct ir_load_expression_op *exp_op;
-
- if (!load_expression)
- return;
- exp_op = load_expression->child;
- for (;;) {
- struct ir_load_expression_op *prev_exp_op;
-
- if (!exp_op)
- break;
- switch (exp_op->type) {
- case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
- case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
- case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
- case IR_LOAD_EXPRESSION_GET_INDEX:
- case IR_LOAD_EXPRESSION_LOAD_FIELD:
- break;
- case IR_LOAD_EXPRESSION_GET_SYMBOL:
- free(exp_op->u.symbol);
- break;
- }
- prev_exp_op = exp_op;
- exp_op = exp_op->next;
- free(prev_exp_op);
- }
- free(load_expression);
-}
-
-/*
- * Returns the first node of the chain, after initializing the next
- * pointers.
- */
-static
-struct filter_node *load_expression_get_forward_chain(struct filter_node *node)
-{
- struct filter_node *prev_node;
-
- for (;;) {
- assert(node->type == NODE_EXPRESSION);
- prev_node = node;
- node = node->u.expression.prev;
- if (!node) {
- break;
- }
- node->u.expression.next = prev_node;
- }
- return prev_node;
-}
-
-static
-struct ir_load_expression *create_load_expression(struct filter_node *node)
-{
- struct ir_load_expression *load_exp;
- struct ir_load_expression_op *load_exp_op, *prev_op;
- const char *str;
-
- /* Get forward chain. */
- node = load_expression_get_forward_chain(node);
- if (!node)
- return NULL;
- load_exp = calloc(sizeof(struct ir_load_expression), 1);
- if (!load_exp)
- return NULL;
-
- /* Root */
- load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
- if (!load_exp_op)
- goto error;
- load_exp->child = load_exp_op;
- str = node->u.expression.u.string;
- if (!strcmp(str, "$ctx")) {
- load_exp_op->type = IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT;
- node = node->u.expression.next;
- if (!node) {
- fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str);
- goto error;
- }
- str = node->u.expression.u.string;
- } else if (!strcmp(str, "$app")) {
- load_exp_op->type = IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT;
- node = node->u.expression.next;
- if (!node) {
- fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str);
- goto error;
- }
- str = node->u.expression.u.string;
- } else if (str[0] == '$') {
- fprintf(stderr, "[error] Unexpected identifier \'%s\'\n", str);
- goto error;
- } else {
- load_exp_op->type = IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT;
- }
-
- for (;;) {
- struct filter_node *bracket_node;
-
- prev_op = load_exp_op;
- load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
- if (!load_exp_op)
- goto error;
- prev_op->next = load_exp_op;
- load_exp_op->type = IR_LOAD_EXPRESSION_GET_SYMBOL;
- load_exp_op->u.symbol = strdup(str);
- if (!load_exp_op->u.symbol)
- goto error;
-
- /* Explore brackets from current node. */
- for (bracket_node = node->u.expression.next_bracket;
- bracket_node != NULL;
- bracket_node = bracket_node->u.expression.next_bracket) {
- prev_op = load_exp_op;
- load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
- if (!load_exp_op)
- goto error;
- prev_op->next = load_exp_op;
- load_exp_op->type = IR_LOAD_EXPRESSION_GET_INDEX;
- load_exp_op->u.index = bracket_node->u.expression.u.constant;
- }
- /* Go to next chain element. */
- node = node->u.expression.next;
- if (!node)
- break;
- str = node->u.expression.u.string;
- }
- /* Add final load field */
- prev_op = load_exp_op;
- load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1);
- if (!load_exp_op)
- goto error;
- prev_op->next = load_exp_op;
- load_exp_op->type = IR_LOAD_EXPRESSION_LOAD_FIELD;
- return load_exp;
-
-error:
- free_load_expression(load_exp);
- return NULL;
-}
-
-static
-struct ir_op *make_op_load_expression(struct filter_node *node,
- enum ir_side side)
-{
- struct ir_op *op;
-
- op = calloc(sizeof(struct ir_op), 1);
- if (!op)
- return NULL;
- op->op = IR_OP_LOAD;
- op->data_type = IR_DATA_EXPRESSION;
- op->signedness = IR_SIGN_DYN;
- op->side = side;
- op->u.load.u.expression = create_load_expression(node);
- if (!op->u.load.u.expression) {
- goto error;
- }
- return op;
-
-error:
- free_load_expression(op->u.load.u.expression);
- free(op);
- return NULL;
-}
-
-static
-struct ir_op *make_op_unary(enum unary_op_type unary_op_type,
- const char *op_str, enum ir_op_signedness signedness,
- struct ir_op *child, enum ir_side side)
-{
- struct ir_op *op = NULL;
-
- if (child->data_type == IR_DATA_STRING) {
- fprintf(stderr, "[error] unary operation '%s' not allowed on string literal\n", op_str);
- goto error;
- }
-
- op = calloc(sizeof(struct ir_op), 1);
- if (!op)
- return NULL;
- op->op = IR_OP_UNARY;
- op->data_type = child->data_type;
- op->signedness = signedness;
- op->side = side;
- op->u.unary.type = unary_op_type;
- op->u.unary.child = child;
- return op;
-
-error:
- free(op);
- return NULL;
-}
-
-/*
- * unary + is pretty much useless.
- */
-static
-struct ir_op *make_op_unary_plus(struct ir_op *child, enum ir_side side)
-{
- return make_op_unary(AST_UNARY_PLUS, "+", child->signedness,
- child, side);
-}
-
-static
-struct ir_op *make_op_unary_minus(struct ir_op *child, enum ir_side side)
-{
- return make_op_unary(AST_UNARY_MINUS, "-", child->signedness,
- child, side);
-}
-
-static
-struct ir_op *make_op_unary_not(struct ir_op *child, enum ir_side side)
-{
- return make_op_unary(AST_UNARY_NOT, "!", child->signedness,
- child, side);
-}
-
-static
-struct ir_op *make_op_unary_bit_not(struct ir_op *child, enum ir_side side)
-{
- return make_op_unary(AST_UNARY_BIT_NOT, "~", child->signedness,
- child, side);
-}
-
-static
-struct ir_op *make_op_binary_compare(enum op_type bin_op_type,
- const char *op_str, struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- struct ir_op *op = NULL;
-
- if (left->data_type == IR_DATA_UNKNOWN
- || right->data_type == IR_DATA_UNKNOWN) {
- fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str);
- goto error;
-
- }
- if ((left->data_type == IR_DATA_STRING
- && (right->data_type == IR_DATA_NUMERIC || right->data_type == IR_DATA_FLOAT))
- || ((left->data_type == IR_DATA_NUMERIC || left->data_type == IR_DATA_FLOAT) &&
- right->data_type == IR_DATA_STRING)) {
- fprintf(stderr, "[error] binary operation '%s' operand type mismatch\n", op_str);
- goto error;
- }
-
- op = calloc(sizeof(struct ir_op), 1);
- if (!op)
- return NULL;
- op->op = IR_OP_BINARY;
- op->u.binary.type = bin_op_type;
- op->u.binary.left = left;
- op->u.binary.right = right;
-
- /* we return a boolean, represented as signed numeric */
- op->data_type = IR_DATA_NUMERIC;
- op->signedness = IR_SIGNED;
- op->side = side;
-
- return op;
-
-error:
- free(op);
- return NULL;
-}
-
-static
-struct ir_op *make_op_binary_eq(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_compare(AST_OP_EQ, "==", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_ne(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_compare(AST_OP_NE, "!=", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_gt(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_compare(AST_OP_GT, ">", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_lt(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_compare(AST_OP_LT, "<", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_ge(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_compare(AST_OP_GE, ">=", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_le(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_compare(AST_OP_LE, "<=", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_logical(enum op_type bin_op_type,
- const char *op_str, struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- struct ir_op *op = NULL;
-
- if (left->data_type == IR_DATA_UNKNOWN
- || right->data_type == IR_DATA_UNKNOWN) {
- fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str);
- goto error;
-
- }
- if (left->data_type == IR_DATA_STRING
- || right->data_type == IR_DATA_STRING) {
- fprintf(stderr, "[error] logical binary operation '%s' cannot have string operand\n", op_str);
- goto error;
- }
-
- op = calloc(sizeof(struct ir_op), 1);
- if (!op)
- return NULL;
- op->op = IR_OP_LOGICAL;
- op->u.binary.type = bin_op_type;
- op->u.binary.left = left;
- op->u.binary.right = right;
-
- /* we return a boolean, represented as signed numeric */
- op->data_type = IR_DATA_NUMERIC;
- op->signedness = IR_SIGNED;
- op->side = side;
-
- return op;
-
-error:
- free(op);
- return NULL;
-}
-
-static
-struct ir_op *make_op_binary_bitwise(enum op_type bin_op_type,
- const char *op_str, struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- struct ir_op *op = NULL;
-
- if (left->data_type == IR_DATA_UNKNOWN
- || right->data_type == IR_DATA_UNKNOWN) {
- fprintf(stderr, "[error] bitwise binary operation '%s' has unknown operand type\n", op_str);
- goto error;
-
- }
- if (left->data_type == IR_DATA_STRING
- || right->data_type == IR_DATA_STRING) {
- fprintf(stderr, "[error] bitwise binary operation '%s' cannot have string operand\n", op_str);
- goto error;
- }
- if (left->data_type == IR_DATA_FLOAT
- || right->data_type == IR_DATA_FLOAT) {
- fprintf(stderr, "[error] bitwise binary operation '%s' cannot have floating point operand\n", op_str);
- goto error;
- }
-
- op = calloc(sizeof(struct ir_op), 1);
- if (!op)
- return NULL;
- op->op = IR_OP_BINARY;
- op->u.binary.type = bin_op_type;
- op->u.binary.left = left;
- op->u.binary.right = right;
-
- /* we return a signed numeric */
- op->data_type = IR_DATA_NUMERIC;
- op->signedness = IR_SIGNED;
- op->side = side;
-
- return op;
-
-error:
- free(op);
- return NULL;
-}
-
-static
-struct ir_op *make_op_binary_logical_and(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_logical(AST_OP_AND, "&&", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_logical_or(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_logical(AST_OP_OR, "||", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_bitwise_rshift(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_bitwise(AST_OP_BIT_RSHIFT, ">>", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_bitwise_lshift(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_bitwise(AST_OP_BIT_LSHIFT, "<<", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_bitwise_and(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_bitwise(AST_OP_BIT_AND, "&", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_bitwise_or(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_bitwise(AST_OP_BIT_OR, "|", left, right, side);
-}
-
-static
-struct ir_op *make_op_binary_bitwise_xor(struct ir_op *left, struct ir_op *right,
- enum ir_side side)
-{
- return make_op_binary_bitwise(AST_OP_BIT_XOR, "^", left, right, side);
-}
-
-static
-void filter_free_ir_recursive(struct ir_op *op)
-{
- if (!op)
- return;
- switch (op->op) {
- case IR_OP_UNKNOWN:
- default:
- fprintf(stderr, "[error] Unknown op type in %s\n",
- __func__);
- break;
- case IR_OP_ROOT:
- filter_free_ir_recursive(op->u.root.child);
- break;
- case IR_OP_LOAD:
- switch (op->data_type) {
- case IR_DATA_STRING:
- free(op->u.load.u.string.value);
- break;
- case IR_DATA_FIELD_REF: /* fall-through */
- case IR_DATA_GET_CONTEXT_REF:
- free(op->u.load.u.ref);
- break;
- case IR_DATA_EXPRESSION:
- free_load_expression(op->u.load.u.expression);
- default:
- break;
- }
- break;
- case IR_OP_UNARY:
- filter_free_ir_recursive(op->u.unary.child);
- break;
- case IR_OP_BINARY:
- filter_free_ir_recursive(op->u.binary.left);
- filter_free_ir_recursive(op->u.binary.right);
- break;
- case IR_OP_LOGICAL:
- filter_free_ir_recursive(op->u.logical.left);
- filter_free_ir_recursive(op->u.logical.right);
- break;
- }
- free(op);
-}
-
-static
-struct ir_op *make_expression(struct filter_parser_ctx *ctx,
- struct filter_node *node, enum ir_side side)
-{
- switch (node->u.expression.type) {
- case AST_EXP_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown expression type\n", __func__);
- return NULL;
-
- case AST_EXP_STRING:
- return make_op_load_string(node->u.expression.u.string, side);
- case AST_EXP_CONSTANT:
- return make_op_load_numeric(node->u.expression.u.constant,
- side);
- case AST_EXP_FLOAT_CONSTANT:
- return make_op_load_float(node->u.expression.u.float_constant,
- side);
- case AST_EXP_IDENTIFIER:
- case AST_EXP_GLOBAL_IDENTIFIER:
- return make_op_load_expression(node, side);
- case AST_EXP_NESTED:
- return generate_ir_recursive(ctx, node->u.expression.u.child,
- side);
- }
-}
-
-static
-struct ir_op *make_op(struct filter_parser_ctx *ctx,
- struct filter_node *node, enum ir_side side)
-{
- struct ir_op *op = NULL, *lchild, *rchild;
- const char *op_str = "?";
-
- switch (node->u.op.type) {
- case AST_OP_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown binary op type\n", __func__);
- return NULL;
-
- /*
- * The following binary operators other than comparators and
- * logical and/or are not supported yet.
- */
- case AST_OP_MUL:
- op_str = "*";
- goto error_not_supported;
- case AST_OP_DIV:
- op_str = "/";
- goto error_not_supported;
- case AST_OP_MOD:
- op_str = "%";
- goto error_not_supported;
- case AST_OP_PLUS:
- op_str = "+";
- goto error_not_supported;
- case AST_OP_MINUS:
- op_str = "-";
- goto error_not_supported;
-
- case AST_OP_BIT_RSHIFT:
- case AST_OP_BIT_LSHIFT:
- case AST_OP_BIT_AND:
- case AST_OP_BIT_OR:
- case AST_OP_BIT_XOR:
- lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
- if (!lchild)
- return NULL;
- rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT);
- if (!rchild) {
- filter_free_ir_recursive(lchild);
- return NULL;
- }
- break;
-
- case AST_OP_EQ:
- case AST_OP_NE:
- case AST_OP_GT:
- case AST_OP_LT:
- case AST_OP_GE:
- case AST_OP_LE:
- lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
- if (!lchild)
- return NULL;
- rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT);
- if (!rchild) {
- filter_free_ir_recursive(lchild);
- return NULL;
- }
- break;
-
- case AST_OP_AND:
- case AST_OP_OR:
- /*
- * Both children considered as left, since we need to
- * populate R0.
- */
- lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT);
- if (!lchild)
- return NULL;
- rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_LEFT);
- if (!rchild) {
- filter_free_ir_recursive(lchild);
- return NULL;
- }
- break;
- }
-
- switch (node->u.op.type) {
- case AST_OP_AND:
- op = make_op_binary_logical_and(lchild, rchild, side);
- break;
- case AST_OP_OR:
- op = make_op_binary_logical_or(lchild, rchild, side);
- break;
- case AST_OP_EQ:
- op = make_op_binary_eq(lchild, rchild, side);
- break;
- case AST_OP_NE:
- op = make_op_binary_ne(lchild, rchild, side);
- break;
- case AST_OP_GT:
- op = make_op_binary_gt(lchild, rchild, side);
- break;
- case AST_OP_LT:
- op = make_op_binary_lt(lchild, rchild, side);
- break;
- case AST_OP_GE:
- op = make_op_binary_ge(lchild, rchild, side);
- break;
- case AST_OP_LE:
- op = make_op_binary_le(lchild, rchild, side);
- break;
- case AST_OP_BIT_RSHIFT:
- op = make_op_binary_bitwise_rshift(lchild, rchild, side);
- break;
- case AST_OP_BIT_LSHIFT:
- op = make_op_binary_bitwise_lshift(lchild, rchild, side);
- break;
- case AST_OP_BIT_AND:
- op = make_op_binary_bitwise_and(lchild, rchild, side);
- break;
- case AST_OP_BIT_OR:
- op = make_op_binary_bitwise_or(lchild, rchild, side);
- break;
- case AST_OP_BIT_XOR:
- op = make_op_binary_bitwise_xor(lchild, rchild, side);
- break;
- default:
- break;
- }
-
- if (!op) {
- filter_free_ir_recursive(rchild);
- filter_free_ir_recursive(lchild);
- }
- return op;
-
-error_not_supported:
- fprintf(stderr, "[error] %s: binary operation '%s' not supported\n",
- __func__, op_str);
- return NULL;
-}
-
-static
-struct ir_op *make_unary_op(struct filter_parser_ctx *ctx,
- struct filter_node *node, enum ir_side side)
-{
- switch (node->u.unary_op.type) {
- case AST_UNARY_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown unary op type\n", __func__);
- return NULL;
-
- case AST_UNARY_PLUS:
- {
- struct ir_op *op, *child;
-
- child = generate_ir_recursive(ctx, node->u.unary_op.child,
- side);
- if (!child)
- return NULL;
- op = make_op_unary_plus(child, side);
- if (!op) {
- filter_free_ir_recursive(child);
- return NULL;
- }
- return op;
- }
- case AST_UNARY_MINUS:
- {
- struct ir_op *op, *child;
-
- child = generate_ir_recursive(ctx, node->u.unary_op.child,
- side);
- if (!child)
- return NULL;
- op = make_op_unary_minus(child, side);
- if (!op) {
- filter_free_ir_recursive(child);
- return NULL;
- }
- return op;
- }
- case AST_UNARY_NOT:
- {
- struct ir_op *op, *child;
-
- child = generate_ir_recursive(ctx, node->u.unary_op.child,
- side);
- if (!child)
- return NULL;
- op = make_op_unary_not(child, side);
- if (!op) {
- filter_free_ir_recursive(child);
- return NULL;
- }
- return op;
- }
- case AST_UNARY_BIT_NOT:
- {
- struct ir_op *op, *child;
-
- child = generate_ir_recursive(ctx, node->u.unary_op.child,
- side);
- if (!child)
- return NULL;
- op = make_op_unary_bit_not(child, side);
- if (!op) {
- filter_free_ir_recursive(child);
- return NULL;
- }
- return op;
- }
- }
-
- return NULL;
-}
-
-static
-struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx,
- struct filter_node *node, enum ir_side side)
-{
- switch (node->type) {
- case NODE_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown node type\n", __func__);
- return NULL;
-
- case NODE_ROOT:
- {
- struct ir_op *op, *child;
-
- child = generate_ir_recursive(ctx, node->u.root.child,
- side);
- if (!child)
- return NULL;
- op = make_op_root(child, side);
- if (!op) {
- filter_free_ir_recursive(child);
- return NULL;
- }
- return op;
- }
- case NODE_EXPRESSION:
- return make_expression(ctx, node, side);
- case NODE_OP:
- return make_op(ctx, node, side);
- case NODE_UNARY_OP:
- return make_unary_op(ctx, node, side);
- }
- return 0;
-}
-
-LTTNG_HIDDEN
-void filter_ir_free(struct filter_parser_ctx *ctx)
-{
- filter_free_ir_recursive(ctx->ir_root);
- ctx->ir_root = NULL;
-}
-
-LTTNG_HIDDEN
-int filter_visitor_ir_generate(struct filter_parser_ctx *ctx)
-{
- struct ir_op *op;
-
- op = generate_ir_recursive(ctx, &ctx->ast->root, IR_LEFT);
- if (!op) {
- return -EINVAL;
- }
- ctx->ir_root = op;
- return 0;
-}
+++ /dev/null
-/*
- * filter-visitor-ir-check-binary-comparator.c
- *
- * LTTng filter IR check binary comparator
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-static
-int check_bin_comparator(struct ir_op *node)
-{
- switch (node->op) {
- case IR_OP_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown op type\n", __func__);
- return -EINVAL;
-
- case IR_OP_ROOT:
- return check_bin_comparator(node->u.root.child);
- case IR_OP_LOAD:
- return 0;
- case IR_OP_UNARY:
- return check_bin_comparator(node->u.unary.child);
- case IR_OP_BINARY:
- {
- int ret;
-
- if (node->u.binary.left->data_type == IR_DATA_STRING
- || node->u.binary.right->data_type
- == IR_DATA_STRING) {
- if (node->u.binary.type != AST_OP_EQ
- && node->u.binary.type != AST_OP_NE) {
- fprintf(stderr, "[error] Only '==' and '!=' comparators are allowed for strings\n");
- return -EINVAL;
- }
- }
-
- ret = check_bin_comparator(node->u.binary.left);
- if (ret)
- return ret;
- return check_bin_comparator(node->u.binary.right);
- }
- case IR_OP_LOGICAL:
- {
- int ret;
-
- ret = check_bin_comparator(node->u.logical.left);
- if (ret)
- return ret;
- return check_bin_comparator(node->u.logical.right);
- }
- }
-}
-
-int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx)
-{
- return check_bin_comparator(ctx->ir_root);
-}
+++ /dev/null
-/*
- * filter-visitor-ir-check-binary-op-nesting.c
- *
- * LTTng filter IR check binary op nesting
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-#include <common/macros.h>
-
-static
-int check_bin_op_nesting_recursive(struct ir_op *node, int nesting)
-{
- switch (node->op) {
- case IR_OP_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown op type\n", __func__);
- return -EINVAL;
-
- case IR_OP_ROOT:
- return check_bin_op_nesting_recursive(node->u.root.child,
- nesting);
- case IR_OP_LOAD:
- return 0;
- case IR_OP_UNARY:
- return check_bin_op_nesting_recursive(node->u.unary.child,
- nesting);
- case IR_OP_BINARY:
- {
- int ret;
-
- ret = check_bin_op_nesting_recursive(node->u.binary.left,
- nesting + 1);
- if (ret)
- return ret;
- return check_bin_op_nesting_recursive(node->u.binary.right,
- nesting + 1);
- }
- case IR_OP_LOGICAL:
- {
- int ret;
-
- ret = check_bin_op_nesting_recursive(node->u.logical.left,
- nesting);
- if (ret)
- return ret;
- return check_bin_op_nesting_recursive(node->u.logical.right,
- nesting);
- }
- }
-}
-
-LTTNG_HIDDEN
-int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx)
-{
- return check_bin_op_nesting_recursive(ctx->ir_root, 0);
-}
+++ /dev/null
-/*
- * filter-visitor-ir-normalize-glob-patterns.c
- *
- * LTTng filter IR normalize string
- *
- * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-
-#include <common/macros.h>
-#include <common/string-utils/string-utils.h>
-
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-static
-int normalize_glob_patterns(struct ir_op *node)
-{
- switch (node->op) {
- case IR_OP_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown op type\n", __func__);
- return -EINVAL;
-
- case IR_OP_ROOT:
- return normalize_glob_patterns(node->u.root.child);
- case IR_OP_LOAD:
- {
- if (node->data_type == IR_DATA_STRING) {
- enum ir_load_string_type type =
- node->u.load.u.string.type;
- if (type == IR_LOAD_STRING_TYPE_GLOB_STAR_END ||
- type == IR_LOAD_STRING_TYPE_GLOB_STAR) {
- assert(node->u.load.u.string.value);
- strutils_normalize_star_glob_pattern(
- node->u.load.u.string.value);
- }
- }
-
- return 0;
- }
- case IR_OP_UNARY:
- return normalize_glob_patterns(node->u.unary.child);
- case IR_OP_BINARY:
- {
- int ret = normalize_glob_patterns(node->u.binary.left);
-
- if (ret)
- return ret;
- return normalize_glob_patterns(node->u.binary.right);
- }
- case IR_OP_LOGICAL:
- {
- int ret;
-
- ret = normalize_glob_patterns(node->u.logical.left);
- if (ret)
- return ret;
- return normalize_glob_patterns(node->u.logical.right);
- }
- }
-}
-
-/*
- * This function normalizes all the globbing literal strings with
- * utils_normalize_glob_pattern(). See the documentation of
- * utils_normalize_glob_pattern() for more details.
- */
-LTTNG_HIDDEN
-int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx)
-{
- return normalize_glob_patterns(ctx->ir_root);
-}
+++ /dev/null
-/*
- * filter-visitor-ir-validate-globbing.c
- *
- * LTTng filter IR validate globbing
- *
- * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-
-#include <common/macros.h>
-
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-static
-int validate_globbing(struct ir_op *node)
-{
- int ret;
-
- switch (node->op) {
- case IR_OP_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown op type\n", __func__);
- return -EINVAL;
-
- case IR_OP_ROOT:
- return validate_globbing(node->u.root.child);
- case IR_OP_LOAD:
- return 0;
- case IR_OP_UNARY:
- return validate_globbing(node->u.unary.child);
- case IR_OP_BINARY:
- {
- struct ir_op *left = node->u.binary.left;
- struct ir_op *right = node->u.binary.right;
-
- if (left->op == IR_OP_LOAD && right->op == IR_OP_LOAD &&
- left->data_type == IR_DATA_STRING &&
- right->data_type == IR_DATA_STRING) {
- /* Test 1. */
- if (left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR &&
- right->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) {
- fprintf(stderr, "[error] Cannot compare two globbing patterns\n");
- return -1;
- }
-
- if (right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR &&
- left->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) {
- fprintf(stderr, "[error] Cannot compare two globbing patterns\n");
- return -1;
- }
- }
-
- if ((left->op == IR_OP_LOAD && left->data_type == IR_DATA_STRING) ||
- (right->op == IR_OP_LOAD && right->data_type == IR_DATA_STRING)) {
- if ((left->op == IR_OP_LOAD && left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR) ||
- (right->op == IR_OP_LOAD && right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR)) {
- /* Test 2. */
- if (node->u.binary.type != AST_OP_EQ &&
- node->u.binary.type != AST_OP_NE) {
- fprintf(stderr, "[error] Only the `==` and `!=` operators are allowed with a globbing pattern\n");
- return -1;
- }
- }
- }
-
- ret = validate_globbing(left);
- if (ret) {
- return ret;
- }
-
- return validate_globbing(right);
- }
- case IR_OP_LOGICAL:
- ret = validate_globbing(node->u.logical.left);
- if (ret)
- return ret;
- return validate_globbing(node->u.logical.right);
- }
-}
-
-/*
- * This function recursively validates that:
- *
- * 1. When there's a binary operation between two literal strings,
- * if one of them has the IR_LOAD_STRING_TYPE_GLOB_STAR type,
- * the other one has the IR_LOAD_STRING_TYPE_PLAIN type.
- *
- * In other words, you cannot compare two globbing patterns, except
- * for two globbing patterns with only a star at the end for backward
- * compatibility reasons.
- *
- * 2. When there's a binary operation between two literal strings, if
- * one of them is a (full) star globbing pattern, the binary
- * operation is either == or !=.
- */
-LTTNG_HIDDEN
-int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx)
-{
- return validate_globbing(ctx->ir_root);
-}
+++ /dev/null
-/*
- * filter-visitor-ir-validate-string.c
- *
- * LTTng filter IR validate string
- *
- * Copyright 2014 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-
-#include <common/macros.h>
-
-#include "filter-ast.h"
-#include "filter-parser.h"
-#include "filter-ir.h"
-
-enum parse_char_result {
- PARSE_CHAR_UNKNOWN = -2,
- PARSE_CHAR_WILDCARD = -1,
- PARSE_CHAR_NORMAL = 0,
-};
-
-static
-enum parse_char_result parse_char(const char **p)
-{
- switch (**p) {
- case '\\':
- (*p)++;
- switch (**p) {
- case '\\':
- case '*':
- return PARSE_CHAR_NORMAL;
- default:
- return PARSE_CHAR_UNKNOWN;
- }
- case '*':
- return PARSE_CHAR_WILDCARD;
- default:
- return PARSE_CHAR_NORMAL;
- }
-}
-
-static
-int validate_string(struct ir_op *node)
-{
- switch (node->op) {
- case IR_OP_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown op type\n", __func__);
- return -EINVAL;
-
- case IR_OP_ROOT:
- return validate_string(node->u.root.child);
- case IR_OP_LOAD:
- {
- int ret = 0;
-
- if (node->data_type == IR_DATA_STRING) {
- const char *str;
-
- assert(node->u.load.u.string.value);
- str = node->u.load.u.string.value;
-
- for (;;) {
- enum parse_char_result res;
-
- if (!(*str)) {
- break;
- }
-
- res = parse_char(&str);
- str++;
-
- switch (res) {
- case PARSE_CHAR_UNKNOWN:
- ret = -EINVAL;
- fprintf(stderr,
- "Unsupported escape character detected.\n");
- goto end_load;
- case PARSE_CHAR_NORMAL:
- default:
- break;
- }
- }
- }
-end_load:
- return ret;
- }
- case IR_OP_UNARY:
- return validate_string(node->u.unary.child);
- case IR_OP_BINARY:
- {
- int ret = validate_string(node->u.binary.left);
-
- if (ret)
- return ret;
- return validate_string(node->u.binary.right);
- }
- case IR_OP_LOGICAL:
- {
- int ret;
-
- ret = validate_string(node->u.logical.left);
- if (ret)
- return ret;
- return validate_string(node->u.logical.right);
- }
- }
-}
-
-LTTNG_HIDDEN
-int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx)
-{
- return validate_string(ctx->ir_root);
-}
+++ /dev/null
-/*
- * filter-visitor-xml.c
- *
- * LTTng filter XML pretty printer visitor
- *
- * Copyright 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "filter-ast.h"
-#include "filter-parser.h"
-
-#include <common/macros.h>
-
-#define fprintf_dbg(fd, fmt, args...) fprintf(fd, "%s: " fmt, __func__, ## args)
-
-static
-int recursive_visit_print(struct filter_node *node, FILE *stream, int indent);
-
-static
-void print_tabs(FILE *fd, int depth)
-{
- int i;
-
- for (i = 0; i < depth; i++)
- fprintf(fd, "\t");
-}
-
-static
-int recursive_visit_print_expression(struct filter_node *node,
- FILE *stream, int indent)
-{
- struct filter_node *iter_node;
-
- if (!node) {
- fprintf(stderr, "[error] %s: NULL child\n", __func__);
- return -EINVAL;
- }
- switch (node->u.expression.type) {
- case AST_EXP_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown expression\n", __func__);
- return -EINVAL;
- case AST_EXP_STRING:
- print_tabs(stream, indent);
- fprintf(stream, "<string value=\"%s\"/>\n",
- node->u.expression.u.string);
- break;
- case AST_EXP_CONSTANT:
- print_tabs(stream, indent);
- fprintf(stream, "<constant value=\"%" PRIu64 "\"/>\n",
- node->u.expression.u.constant);
- break;
- case AST_EXP_FLOAT_CONSTANT:
- print_tabs(stream, indent);
- fprintf(stream, "<float_constant value=\"%lg\"/>\n",
- node->u.expression.u.float_constant);
- break;
- case AST_EXP_IDENTIFIER: /* fall-through */
- case AST_EXP_GLOBAL_IDENTIFIER:
- print_tabs(stream, indent);
- fprintf(stream, "<%s value=\"%s\"/>\n",
- node->u.expression.type == AST_EXP_IDENTIFIER ?
- "identifier" : "global_identifier",
- node->u.expression.u.identifier);
- iter_node = node->u.expression.next;
- while (iter_node) {
- print_tabs(stream, indent);
- fprintf(stream, "<bracket>\n");
- if (recursive_visit_print_expression(iter_node,
- stream, indent + 1)) {
- return -EINVAL;
- }
- print_tabs(stream, indent);
- fprintf(stream, "</bracket>\n");
- iter_node = iter_node->u.expression.next;
-
- }
- break;
- case AST_EXP_NESTED:
- return recursive_visit_print(node->u.expression.u.child,
- stream, indent + 1);
- }
- return 0;
-}
-
-
-static
-int recursive_visit_print(struct filter_node *node, FILE *stream, int indent)
-{
- int ret;
-
- if (!node) {
- fprintf(stderr, "[error] %s: NULL child\n", __func__);
- return -EINVAL;
- }
- switch (node->type) {
- case NODE_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown node type\n", __func__);
- return -EINVAL;
- case NODE_ROOT:
- print_tabs(stream, indent);
- fprintf(stream, "<root>\n");
- ret = recursive_visit_print(node->u.root.child, stream,
- indent + 1);
- print_tabs(stream, indent);
- fprintf(stream, "</root>\n");
- return ret;
- case NODE_EXPRESSION:
- print_tabs(stream, indent);
- fprintf(stream, "<expression>\n");
- ret = recursive_visit_print_expression(node, stream,
- indent + 1);
- print_tabs(stream, indent);
- fprintf(stream, "</expression>\n");
- return ret;
- case NODE_OP:
- print_tabs(stream, indent);
- fprintf(stream, "<op type=");
- switch (node->u.op.type) {
- case AST_OP_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown op\n", __func__);
- return -EINVAL;
- case AST_OP_MUL:
- fprintf(stream, "\"*\"");
- break;
- case AST_OP_DIV:
- fprintf(stream, "\"/\"");
- break;
- case AST_OP_MOD:
- fprintf(stream, "\"%%\"");
- break;
- case AST_OP_PLUS:
- fprintf(stream, "\"+\"");
- break;
- case AST_OP_MINUS:
- fprintf(stream, "\"-\"");
- break;
- case AST_OP_BIT_RSHIFT:
- fprintf(stream, "\">>\"");
- break;
- case AST_OP_BIT_LSHIFT:
- fprintf(stream, "\"<<\"");
- break;
- case AST_OP_AND:
- fprintf(stream, "\"&&\"");
- break;
- case AST_OP_OR:
- fprintf(stream, "\"||\"");
- break;
- case AST_OP_BIT_AND:
- fprintf(stream, "\"&\"");
- break;
- case AST_OP_BIT_OR:
- fprintf(stream, "\"|\"");
- break;
- case AST_OP_BIT_XOR:
- fprintf(stream, "\"^\"");
- break;
-
- case AST_OP_EQ:
- fprintf(stream, "\"==\"");
- break;
- case AST_OP_NE:
- fprintf(stream, "\"!=\"");
- break;
- case AST_OP_GT:
- fprintf(stream, "\">\"");
- break;
- case AST_OP_LT:
- fprintf(stream, "\"<\"");
- break;
- case AST_OP_GE:
- fprintf(stream, "\">=\"");
- break;
- case AST_OP_LE:
- fprintf(stream, "\"<=\"");
- break;
- }
- fprintf(stream, ">\n");
- ret = recursive_visit_print(node->u.op.lchild,
- stream, indent + 1);
- if (ret)
- return ret;
- ret = recursive_visit_print(node->u.op.rchild,
- stream, indent + 1);
- if (ret)
- return ret;
- print_tabs(stream, indent);
- fprintf(stream, "</op>\n");
- return ret;
- case NODE_UNARY_OP:
- print_tabs(stream, indent);
- fprintf(stream, "<unary_op type=");
- switch (node->u.unary_op.type) {
- case AST_UNARY_UNKNOWN:
- default:
- fprintf(stderr, "[error] %s: unknown unary_op\n", __func__);
- return -EINVAL;
- case AST_UNARY_PLUS:
- fprintf(stream, "\"+\"");
- break;
- case AST_UNARY_MINUS:
- fprintf(stream, "\"-\"");
- break;
- case AST_UNARY_NOT:
- fprintf(stream, "\"!\"");
- break;
- case AST_UNARY_BIT_NOT:
- fprintf(stream, "\"~\"");
- break;
- }
- fprintf(stream, ">\n");
- ret = recursive_visit_print(node->u.unary_op.child,
- stream, indent + 1);
- print_tabs(stream, indent);
- fprintf(stream, "</unary_op>\n");
- return ret;
- }
- return 0;
-}
-
-LTTNG_HIDDEN
-int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream,
- int indent)
-{
- return recursive_visit_print(&ctx->ast->root, stream, indent);
-}
+++ /dev/null
-/*
- * Copyright 2012 (C) Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: MIT
- *
- */
-
-#ifndef _LTTNG_CTL_MEMSTREAM_H
-#define _LTTNG_CTL_MEMSTREAM_H
-
-#ifdef LTTNG_HAVE_FMEMOPEN
-#include <stdio.h>
-
-static inline
-FILE *lttng_fmemopen(void *buf, size_t size, const char *mode)
-{
- return fmemopen(buf, size, mode);
-}
-
-#else /* LTTNG_HAVE_FMEMOPEN */
-
-#include <stdlib.h>
-#include <stdio.h>
-
-/*
- * Fallback for systems which don't have fmemopen. Copy buffer to a
- * temporary file, and use that file as FILE * input.
- */
-static inline
-FILE *lttng_fmemopen(void *buf, size_t size, const char *mode)
-{
- char tmpname[PATH_MAX];
- size_t len;
- FILE *fp;
- int ret;
-
- /*
- * Support reading only.
- */
- if (strcmp(mode, "rb") != 0) {
- return NULL;
- }
- strncpy(tmpname, "/tmp/lttng-tmp-XXXXXX", PATH_MAX);
- ret = mkstemp(tmpname);
- if (ret < 0) {
- return NULL;
- }
- /*
- * We need to write to the file.
- */
- fp = fdopen(ret, "w+");
- if (!fp) {
- goto error_unlink;
- }
- /* Copy the entire buffer to the file */
- len = fwrite(buf, sizeof(char), size, fp);
- if (len != size) {
- goto error_close;
- }
- ret = fseek(fp, 0L, SEEK_SET);
- if (ret < 0) {
- PERROR("fseek");
- goto error_close;
- }
- /* We keep the handle open, but can unlink the file on the VFS. */
- ret = unlink(tmpname);
- if (ret < 0) {
- PERROR("unlink");
- }
- return fp;
-
-error_close:
- ret = fclose(fp);
- if (ret < 0) {
- PERROR("close");
- }
-error_unlink:
- ret = unlink(tmpname);
- if (ret < 0) {
- PERROR("unlink");
- }
- return NULL;
-}
-
-#endif /* LTTNG_HAVE_FMEMOPEN */
-
-#endif /* _LTTNG_CTL_MEMSTREAM_H */
[ HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH ] = "Session daemon application registration dispatcher",
[ HEALTH_SESSIOND_TYPE_ROTATION ] = "Session daemon rotation manager",
[ HEALTH_SESSIOND_TYPE_TIMER ] = "Session daemon timer manager",
+ [ HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR ] = "Session daemon trigger action executor",
};
static
return lttng_ctl_ask_sessiond_fds_varlen(lsm, fds, nb_fd, NULL,
0, NULL, NULL, NULL);
}
+
+/*
+ * Calls lttng_ctl_ask_sessiond_fds_varlen() with fds with no expected command
+ * header and with varlen data.
+ */
+static inline int lttng_ctl_ask_sessiond_fds_varlen_no_cmd_header(
+ struct lttcomm_session_msg *lsm,
+ const int *fds,
+ size_t nb_fd,
+ void *vardata,
+ size_t vardata_len,
+ void **user_payload_buf)
+{
+ return lttng_ctl_ask_sessiond_fds_varlen(lsm, fds, nb_fd, vardata,
+ vardata_len, user_payload_buf, NULL, NULL);
+}
+
/*
* Use this if no variable length data needs to be sent.
*/
#include <string.h>
#include <unistd.h>
+#include <common/bytecode/bytecode.h>
#include <common/common.h>
#include <common/compat/string.h>
#include <common/defaults.h>
#include <lttng/trigger/trigger-internal.h>
#include <lttng/userspace-probe-internal.h>
-#include "filter/filter-ast.h"
-#include "filter/filter-parser.h"
-#include "filter/filter-bytecode.h"
-#include "filter/memstream.h"
+#include <common/filter/filter-ast.h>
+#include <common/filter/filter-parser.h>
+#include <common/filter/memstream.h>
#include "lttng-ctl-helper.h"
-#ifdef DEBUG
-static const int print_xml = 1;
-#define dbg_printf(fmt, args...) \
- printf("[debug liblttng-ctl] " fmt, ## args)
-#else
-static const int print_xml = 0;
-#define dbg_printf(fmt, args...) \
-do { \
- /* do nothing but check printf format */ \
- if (0) \
- printf("[debug liblttnctl] " fmt, ## args); \
-} while (0)
-#endif
-
#define COPY_DOMAIN_PACKED(dst, src) \
do { \
struct lttng_domain _tmp_domain; \
return NULL;
}
-/*
- * Generate the filter bytecode from a given filter expression string. Put the
- * newly allocated parser context in ctxp and populate the lsm object with the
- * expression len.
- *
- * Return 0 on success else a LTTNG_ERR_* code and ctxp is untouched.
- */
-static int generate_filter(char *filter_expression,
- struct lttcomm_session_msg *lsm, struct filter_parser_ctx **ctxp)
-{
- int ret;
- struct filter_parser_ctx *ctx = NULL;
- FILE *fmem = NULL;
-
- assert(filter_expression);
- assert(lsm);
- assert(ctxp);
-
- /*
- * Casting const to non-const, as the underlying function will use it in
- * read-only mode.
- */
- fmem = lttng_fmemopen((void *) filter_expression,
- strlen(filter_expression), "r");
- if (!fmem) {
- fprintf(stderr, "Error opening memory as stream\n");
- ret = -LTTNG_ERR_FILTER_NOMEM;
- goto error;
- }
- ctx = filter_parser_ctx_alloc(fmem);
- if (!ctx) {
- fprintf(stderr, "Error allocating parser\n");
- ret = -LTTNG_ERR_FILTER_NOMEM;
- goto filter_alloc_error;
- }
- ret = filter_parser_ctx_append_ast(ctx);
- if (ret) {
- fprintf(stderr, "Parse error\n");
- ret = -LTTNG_ERR_FILTER_INVAL;
- goto parse_error;
- }
- if (print_xml) {
- ret = filter_visitor_print_xml(ctx, stdout, 0);
- if (ret) {
- fflush(stdout);
- fprintf(stderr, "XML print error\n");
- ret = -LTTNG_ERR_FILTER_INVAL;
- goto parse_error;
- }
- }
-
- dbg_printf("Generating IR... ");
- fflush(stdout);
- ret = filter_visitor_ir_generate(ctx);
- if (ret) {
- fprintf(stderr, "Generate IR error\n");
- ret = -LTTNG_ERR_FILTER_INVAL;
- goto parse_error;
- }
- dbg_printf("done\n");
-
- dbg_printf("Validating IR... ");
- fflush(stdout);
- ret = filter_visitor_ir_check_binary_op_nesting(ctx);
- if (ret) {
- ret = -LTTNG_ERR_FILTER_INVAL;
- goto parse_error;
- }
-
- /* Normalize globbing patterns in the expression. */
- ret = filter_visitor_ir_normalize_glob_patterns(ctx);
- if (ret) {
- ret = -LTTNG_ERR_FILTER_INVAL;
- goto parse_error;
- }
-
- /* Validate strings used as literals in the expression. */
- ret = filter_visitor_ir_validate_string(ctx);
- if (ret) {
- ret = -LTTNG_ERR_FILTER_INVAL;
- goto parse_error;
- }
-
- /* Validate globbing patterns in the expression. */
- ret = filter_visitor_ir_validate_globbing(ctx);
- if (ret) {
- ret = -LTTNG_ERR_FILTER_INVAL;
- goto parse_error;
- }
-
- dbg_printf("done\n");
-
- dbg_printf("Generating bytecode... ");
- fflush(stdout);
- ret = filter_visitor_bytecode_generate(ctx);
- if (ret) {
- fprintf(stderr, "Generate bytecode error\n");
- ret = -LTTNG_ERR_FILTER_INVAL;
- goto parse_error;
- }
- dbg_printf("done\n");
- dbg_printf("Size of bytecode generated: %u bytes.\n",
- bytecode_get_len(&ctx->bytecode->b));
-
- lsm->u.enable.bytecode_len = sizeof(ctx->bytecode->b)
- + bytecode_get_len(&ctx->bytecode->b);
- lsm->u.enable.expression_len = strlen(filter_expression) + 1;
-
- /* No need to keep the memory stream. */
- if (fclose(fmem) != 0) {
- PERROR("fclose");
- }
-
- *ctxp = ctx;
- return 0;
-
-parse_error:
- filter_ir_free(ctx);
- filter_parser_ctx_free(ctx);
-filter_alloc_error:
- if (fclose(fmem) != 0) {
- PERROR("fclose");
- }
-error:
- return ret;
-}
-
/*
* Enable event(s) for a channel, possibly with exclusions and a filter.
* If no event name is specified, all events are enabled.
}
}
- ret = generate_filter(filter_expression, &lsm, &ctx);
+ ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx);
if (ret) {
goto filter_error;
}
+
+ lsm.u.enable.bytecode_len = sizeof(ctx->bytecode->b)
+ + bytecode_get_len(&ctx->bytecode->b);
+ lsm.u.enable.expression_len = strlen(filter_expression) + 1;
}
ret = lttng_dynamic_buffer_set_capacity(&send_buffer,
}
}
- ret = generate_filter(filter_expression, &lsm, &ctx);
+ ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx);
if (ret) {
goto filter_error;
}
+
+ lsm.u.enable.bytecode_len = sizeof(ctx->bytecode->b)
+ + bytecode_get_len(&ctx->bytecode->b);
+ lsm.u.enable.expression_len = strlen(filter_expression) + 1;
}
varlen_data = zmalloc(lsm.u.disable.bytecode_len
int lttng_register_trigger(struct lttng_trigger *trigger)
{
int ret;
+ int reply_ret;
struct lttcomm_session_msg lsm;
struct lttng_dynamic_buffer buffer;
+ void *reply = NULL;
+ struct lttng_buffer_view reply_view;
+ struct lttng_trigger *reply_trigger = NULL;
+ bool send_fd = false;
+ int fd_to_send;
+ enum lttng_domain_type domain_type;
lttng_dynamic_buffer_init(&buffer);
if (!trigger) {
goto end;
}
- ret = lttng_trigger_serialize(trigger, &buffer);
+ domain_type = lttng_trigger_get_underlying_domain_type_restriction(
+ trigger);
+
+ ret = lttng_trigger_serialize(trigger, &buffer, &fd_to_send);
if (ret < 0) {
ret = -LTTNG_ERR_UNK;
goto end;
}
+ if (getenv("LTTNG_REGISTER_TRIGGER_DRY_RUN")) {
+ /*
+ * Don't really send the request, just deserialize, validate
+ * that it is equal to the original trigger (to test
+ * serialization and deserialization), and return.
+ */
+ struct lttng_buffer_view bv;
+ ssize_t sz;
+
+ bv = lttng_buffer_view_from_dynamic_buffer(&buffer, 0, -1);
+ sz = lttng_trigger_create_from_buffer(&bv, &reply_trigger);
+ if (sz != bv.size) {
+ ret = -LTTNG_ERR_UNK;
+ goto end;
+ }
+
+ if (!reply_trigger) {
+ ret = -LTTNG_ERR_UNK;
+ goto end;
+ }
+
+ if (!lttng_trigger_is_equal(trigger, reply_trigger)) {
+ ret = -LTTNG_ERR_UNK;
+ goto end;
+ }
+
+ /* Give it a dummy name. */
+ lttng_trigger_set_name(trigger, "yop");
+
+ ret = 0;
+ goto end;
+ }
+
+ send_fd = fd_to_send >= 0;
+
memset(&lsm, 0, sizeof(lsm));
lsm.cmd_type = LTTNG_REGISTER_TRIGGER;
+ lsm.domain.type = domain_type;
lsm.u.trigger.length = (uint32_t) buffer.size;
- ret = lttng_ctl_ask_sessiond_varlen_no_cmd_header(&lsm, buffer.data,
- buffer.size, NULL);
+ reply_ret = lttng_ctl_ask_sessiond_fds_varlen_no_cmd_header(&lsm,
+ send_fd ? &fd_to_send : NULL,
+ send_fd ? 1 : 0,
+ buffer.data,
+ buffer.size,
+ &reply);
+ if (reply_ret < 0) {
+ ret = reply_ret;
+ goto end;
+ } else if (reply_ret == 0) {
+ /* Socket unexpectedly closed by the session daemon. */
+ ret = -LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ reply_view = lttng_buffer_view_init(reply, 0, reply_ret);
+ ret = lttng_trigger_create_from_buffer(&reply_view, &reply_trigger);
+ if (ret < 0) {
+ ret = -LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ ret = lttng_trigger_assign(trigger, reply_trigger);
+ if (ret < 0) {
+ ret = -LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ ret = 0;
end:
+ free(reply);
lttng_dynamic_buffer_reset(&buffer);
+ lttng_trigger_destroy(reply_trigger);
return ret;
}
-int lttng_unregister_trigger(struct lttng_trigger *trigger)
+int lttng_unregister_trigger(const struct lttng_trigger *trigger)
{
int ret;
struct lttcomm_session_msg lsm;
goto end;
}
- ret = lttng_trigger_serialize(trigger, &buffer);
+ ret = lttng_trigger_serialize(trigger, &buffer, NULL);
if (ret < 0) {
ret = -LTTNG_ERR_UNK;
goto end;
return ret;
}
+/*
+ * Ask the session daemon for all registered triggers.
+ * Allocate a lttng_triggers collection.
+ * On error, returns a negative value.
+ */
+int lttng_list_triggers(struct lttng_triggers **triggers)
+{
+ int ret;
+ int reply_ret;
+ struct lttcomm_session_msg lsm;
+ struct lttng_buffer_view reply_view;
+ struct lttng_triggers *local_triggers = NULL;
+ void *reply = NULL;
+
+ memset(&lsm, 0, sizeof(lsm));
+ lsm.cmd_type = LTTNG_LIST_TRIGGERS;
+
+ reply_ret = lttng_ctl_ask_sessiond(&lsm, &reply);
+ if (reply_ret < 0) {
+ ret = reply_ret;
+ goto end;
+ } else if (reply_ret == 0) {
+ /* Socket unexpectedly closed by the session daemon. */
+ ret = -LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ reply_view = lttng_buffer_view_init(reply, 0, reply_ret);
+ ret = lttng_triggers_create_from_buffer(&reply_view, &local_triggers);
+ if (ret < 0) {
+ ret = -LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ *triggers = local_triggers;
+ local_triggers = NULL;
+ ret = 0;
+end:
+ free(reply);
+ free(local_triggers);
+ return ret;
+}
+
/*
* lib constructor.
*/
* Getter family functions of snapshot output.
*/
-uint32_t lttng_snapshot_output_get_id(struct lttng_snapshot_output *output)
+uint32_t lttng_snapshot_output_get_id(const struct lttng_snapshot_output *output)
{
return output->id;
}
const char *lttng_snapshot_output_get_name(
- struct lttng_snapshot_output *output)
+ const struct lttng_snapshot_output *output)
{
return output->name;
}
-const char *lttng_snapshot_output_get_data_url(struct lttng_snapshot_output *output)
+const char *lttng_snapshot_output_get_data_url(const struct lttng_snapshot_output *output)
{
return output->data_url;
}
-const char *lttng_snapshot_output_get_ctrl_url(struct lttng_snapshot_output *output)
+const char *lttng_snapshot_output_get_ctrl_url(const struct lttng_snapshot_output *output)
{
return output->ctrl_url;
}
uint64_t lttng_snapshot_output_get_maxsize(
- struct lttng_snapshot_output *output)
+ const struct lttng_snapshot_output *output)
{
return output->max_size;
}
lttng_ctl_copy_string(output->data_url, url, sizeof(output->data_url));
return 0;
}
+
+int lttng_snapshot_output_set_local_path(const char *path,
+ struct lttng_snapshot_output *output)
+{
+ int ret;
+ struct lttng_uri *uris = NULL;
+ ssize_t num_uris;
+
+ if (!path || !output) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ num_uris = uri_parse_str_urls(path, NULL, &uris);
+ if (num_uris != 1) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ if (uris[0].dtype != LTTNG_DST_PATH) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ ret = lttng_strncpy(output->ctrl_url, path, sizeof(output->ctrl_url));
+ if (ret != 0) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+end:
+ free(uris);
+ return ret;
+}
+
+int lttng_snapshot_output_set_network_url(const char *url,
+ struct lttng_snapshot_output *output)
+{
+ int ret;
+ struct lttng_uri *uris = NULL;
+ ssize_t num_uris;
+
+ if (!url || !output) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ num_uris = uri_parse_str_urls(url, NULL, &uris);
+ if (num_uris != 2) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ if (uris[0].dtype != LTTNG_DST_IPV4 &&
+ uris[0].dtype != LTTNG_DST_IPV6) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ if (uris[1].dtype != LTTNG_DST_IPV4 &&
+ uris[1].dtype != LTTNG_DST_IPV6) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ ret = lttng_strncpy(output->ctrl_url, url, sizeof(output->ctrl_url));
+ if (ret != 0) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+end:
+ free(uris);
+ return ret;
+}
+
+int lttng_snapshot_output_set_network_urls(
+ const char *ctrl_url, const char *data_url,
+ struct lttng_snapshot_output *output)
+{
+ int ret;
+ struct lttng_uri *uris = NULL;
+ ssize_t num_uris;
+
+ if (!ctrl_url || !data_url || !output) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ num_uris = uri_parse_str_urls(ctrl_url, data_url, &uris);
+ if (num_uris != 2) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ if (uris[0].dtype != LTTNG_DST_IPV4 &&
+ uris[0].dtype != LTTNG_DST_IPV6) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ if (uris[1].dtype != LTTNG_DST_IPV4 &&
+ uris[1].dtype != LTTNG_DST_IPV6) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ ret = lttng_strncpy(output->ctrl_url, ctrl_url, sizeof(output->ctrl_url));
+ if (ret != 0) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ ret = lttng_strncpy(output->data_url, data_url, sizeof(output->data_url));
+ if (ret != 0) {
+ ret = -LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+end:
+ free(uris);
+ return ret;
+}
tools/crash/test_crash \
tools/regen-metadata/test_ust \
tools/regen-statedump/test_ust \
- tools/notification/test_notification_ust \
- tools/notification/test_notification_kernel \
+ tools/notification/test_notification_ust_error \
+ tools/notification/test_notification_ust_buffer_usage \
+ tools/notification/test_notification_ust_capture \
+ tools/notification/test_notification_ust_event_rule_condition_exclusion \
+ tools/notification/test_notification_kernel_error \
+ tools/notification/test_notification_kernel_buffer_usage \
+ tools/notification/test_notification_kernel_capture \
+ tools/notification/test_notification_kernel_instrumentation \
+ tools/notification/test_notification_kernel_syscall \
+ tools/notification/test_notification_kernel_userspace_probe \
tools/notification/test_notification_multi_app \
tools/rotation/test_ust \
tools/rotation/test_kernel \
tools/rotation/test_schedule_api \
tools/metadata/test_kernel \
tools/working-directory/test_relayd_working_directory \
- tools/notification/test_notification_multi_app \
tools/clear/test_ust \
tools/clear/test_kernel \
- tools/tracker/test_event_tracker
+ tools/tracker/test_event_tracker \
+ tools/trigger/start-stop/test_start_stop \
+ tools/trigger/test_add_trigger_cli \
+ tools/trigger/test_list_triggers_cli \
+ tools/trigger/test_remove_trigger_cli
if HAVE_LIBLTTNG_UST_CTL
SUBDIRS += ust
function run_workload()
{
local TEST_APP=$1
+ # shift the first argument, passing along the other args if any to the
+ # test app.
+ shift
local start_file_sync
start_file_sync=$(mktemp -u)
lttng_untrack_all
- ./"$TEST_APP" "$start_file_sync" &
+ ./"$TEST_APP" "$start_file_sync" "$@" &
PID=$!
lttng_track_pid $PID
lttng_enable_kernel_syscall_ok "$SESSION_NAME" "$EVENT_NAME" "$CHANNEL_NAME"
add_context_kernel_ok "$SESSION_NAME" "$CHANNEL_NAME" "callstack-kernel"
- run_workload $TEST_APP_KERNELSPACE
+ run_workload "$TEST_APP_KERNELSPACE" "/proc/cpuinfo" "/proc/cmdline"
destroy_lttng_session_ok "$SESSION_NAME"
lttng_untrack_kernel_all_ok
# Launch the testapp and save its Process ID
- ./"$TESTCMD" "$start_file_sync" &
+ ./"$TESTCMD" "$start_file_sync" "/proc/cpuinfo" "/proc/cmdline" &
PID=$!
# Set LTTng to track this PID and start the tracing
SUBDIRS = streaming filtering health tracefile-limits snapshots live exclusion save-load mi \
wildcard crash regen-metadata regen-statedump notification rotation \
- base-path metadata working-directory relayd-grouping clear tracker
+ base-path metadata working-directory relayd-grouping clear tracker trigger
if NO_SHARED
CLEANFILES = libpause_consumer.so libpause_consumer.so.debug
-EXTRA_DIST = test_notification_ust test_notification_kernel test_notification_multi_app base_client.c notification.c consumer_testpoints.c
-
+EXTRA_DIST = \
+ base_client.c \
+ consumer_testpoints.c \
+ notification.c \
+ test_notification_kernel_buffer_usage \
+ test_notification_kernel_capture \
+ test_notification_kernel_error \
+ test_notification_kernel_instrumentation \
+ test_notification_kernel_syscall \
+ test_notification_kernel_userspace_probe \
+ test_notification_multi_app \
+ test_notification_ust_buffer_usage \
+ test_notification_ust_capture \
+ test_notification_ust_error \
+ test_notification_ust_event_rule_condition_exclusion\
+ util_event_generator.sh
else
# In order to test the health check feature, the helper library
rotation_SOURCES = rotation.c
rotation_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm
-noinst_SCRIPTS = test_notification_ust test_notification_kernel test_notification_multi_app test_rotation
-EXTRA_DIST = test_notification_ust test_notification_kernel test_notification_multi_app test_rotation
+noinst_SCRIPTS = \
+ test_notification_kernel_buffer_usage \
+ test_notification_kernel_error \
+ test_notification_kernel_instrumentation \
+ test_notification_kernel_syscall \
+ test_notification_kernel_userspace_probe \
+ test_notification_multi_app \
+ test_notification_ust_buffer_usage \
+ test_notification_ust_error \
+ test_notification_ust_event_rule_condition_exclusion \
+ test_rotation
+EXTRA_DIST = \
+ test_notification_kernel_buffer_usage \
+ test_notification_kernel_capture \
+ test_notification_kernel_error \
+ test_notification_kernel_instrumentation \
+ test_notification_kernel_syscall \
+ test_notification_kernel_userspace_probe \
+ test_notification_multi_app \
+ test_notification_ust_buffer_usage \
+ test_notification_ust_capture \
+ test_notification_ust_error \
+ test_notification_ust_event_rule_condition_exclusion \
+ test_rotation \
+ util_event_generator.sh
all-local:
@if [ x"$(srcdir)" != x"$(builddir)" ]; then \
#include <lttng/condition/buffer-usage.h>
#include <lttng/condition/condition.h>
#include <lttng/condition/evaluation.h>
+#include <lttng/condition/event-rule.h>
#include <lttng/domain.h>
#include <lttng/endpoint.h>
+#include <lttng/event-field-value.h>
+#include <lttng/event-rule/kprobe.h>
+#include <lttng/event-rule/syscall.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/event-rule/uprobe.h>
#include <lttng/lttng-error.h>
+#include <lttng/lttng.h>
#include <lttng/notification/channel.h>
#include <lttng/notification/notification.h>
+#include <lttng/condition/evaluation.h>
#include <lttng/trigger/trigger.h>
-#include <lttng/lttng.h>
+#include <lttng/userspace-probe.h>
#include <tap/tap.h>
-#define NUM_TESTS 104
+/* A callback to populate the condition capture descriptor */
+typedef int (*condition_capture_desc_cb)(struct lttng_condition *condition);
+
+/* A callback for captured field validation */
+typedef int (*validate_cb)(const struct lttng_event_field_value *event_field, unsigned iteration);
int nb_args = 0;
int named_pipe_args_start = 0;
-pid_t app_pid = -1;
+pid_t app_pid = 0;
const char *app_state_file = NULL;
+enum field_type {
+ FIELD_TYPE_PAYLOAD,
+ FIELD_TYPE_CONTEXT,
+ FIELD_TYPE_APP_CONTEXT,
+ FIELD_TYPE_ARRAY_FIELD,
+};
+
+struct capture_base_field_tuple {
+ char* field_name;
+ enum field_type field_type;
+ bool expected_ust; // Do we expect a capture?
+ bool expected_kernel; // Do we expect a capture?
+ validate_cb validate_ust;
+ validate_cb validate_kernel;
+};
+
static
-void wait_on_file(const char *path, bool file_exist)
+const char *field_value_type_to_str(enum lttng_event_field_value_type type)
{
- if (!path) {
- return;
+ switch (type) {
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_UNKNOWN:
+ return "UNKNOWN";
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID:
+ return "INVALID";
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT:
+ return "UNSIGNED INT";
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT:
+ return "SIGNED INT";
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
+ return "UNSIGNED ENUM";
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
+ return "SIGNED ENUM";
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_REAL:
+ return "REAL";
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING:
+ return "STRING";
+ case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY:
+ return "ARRAY";
+ default:
+ abort();
}
- for (;;) {
- int ret;
- struct stat buf;
+}
- ret = stat(path, &buf);
- if (ret == -1 && errno == ENOENT) {
- if (file_exist) {
- /*
- * The file does not exist. wait a bit and
- * continue looping until it does.
- */
- (void) poll(NULL, 0, 10);
- continue;
- }
+static int validate_type(
+ const struct lttng_event_field_value *event_field,
+ enum lttng_event_field_value_type expect)
+{
+ int ret;
+ enum lttng_event_field_value_type value;
- /*
- * File does not exist and the exit condition we want.
- * Break from the loop and return.
- */
- break;
- }
- if (ret) {
- perror("stat");
- exit(EXIT_FAILURE);
- }
- /*
- * stat() returned 0, so the file exists. break now only if
- * that's the exit condition we want.
- */
- if (file_exist) {
- break;
- }
+ value = lttng_event_field_value_get_type(event_field);
+ if (value == LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID) {
+ ret = 1;
+ goto end;
}
+
+ ret = (expect == value);
+ ok(ret, "Expected field type: %s got %s",
+ field_value_type_to_str(expect),
+ field_value_type_to_str(value));
+
+ ret = !ret;
+
+end:
+ return ret;
}
-static
-int write_pipe(const char *path, uint8_t data)
+/*
+ * Validate unsigned captured field against the iteration number.
+ * The iteration number is always unsigned and will always be compared to value
+ * under MAX_UINT.
+ */
+static int validate_unsigned_int_field(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
{
- int ret = 0;
- int fd = 0;
+ int ret;
+ uint64_t value;
+ enum lttng_event_field_value_status status;
- fd = open(path, O_WRONLY | O_NONBLOCK);
- if (fd < 0) {
- perror("Could not open consumer control named pipe");
+ ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT);
+ if (ret) {
goto end;
}
- ret = write(fd, &data , sizeof(data));
- if (ret < 1) {
- perror("Named pipe write failed");
- if (close(fd)) {
- perror("Named pipe close failed");
- }
- ret = -1;
+ status = lttng_event_field_value_unsigned_int_get_value(
+ event_field , &value);
+ if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ fail("lttng_event_field_value_unsigned_int_get_value");
+ ret = 1;
goto end;
}
- ret = close(fd);
- if (ret < 0) {
- perror("Name pipe closing failed");
- ret = -1;
- goto end;
- }
+ ret = (value == (uint64_t) iteration);
+ ok (ret, "Expected unsigned int of value: %u got %" PRIu64, iteration, value);
+
+ ret = !ret;
+
end:
+
return ret;
}
-static
-int stop_consumer(const char **argv)
+/*
+ * Validate signed captured field.
+ * Value should be -1.
+ */
+static int validate_signed_int_field(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
{
- int ret = 0, i;
+ int ret;
+ int64_t expected = -1;
+ int64_t value;
+ enum lttng_event_field_value_status status;
- for (i = named_pipe_args_start; i < nb_args; i++) {
- ret = write_pipe(argv[i], 49);
+ /* Unused */
+ (void) iteration;
+
+ ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT);
+ if (ret) {
+ goto end;
+ }
+
+ status = lttng_event_field_value_signed_int_get_value(
+ event_field , &value);
+ if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ fail("lttng_event_field_value_signed_int_get_value");
+ ret = 1;
+ goto end;
}
+
+ ret = (value == expected);
+ ok(ret, "Expected signed int of value: %" PRId64 " got %" PRId64, expected, value);
+
+ ret = !ret;
+
+end:
+
return ret;
}
-static
-int resume_consumer(const char **argv)
+/*
+ * Validate array of unsigned int.
+ */
+static int validate_array_unsigned_int_field(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
{
- int ret = 0, i;
+ int ret;
+ enum lttng_event_field_value_status status;
+ unsigned int expected = 3;
+ unsigned int count;
- for (i = named_pipe_args_start; i < nb_args; i++) {
- ret = write_pipe(argv[i], 0);
+ /* Unused */
+ (void) iteration;
+
+ ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY);
+ if (ret) {
+ goto end;
+ }
+
+ status = lttng_event_field_value_array_get_length(event_field, &count);
+ if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ fail("lttng_event_field_value_array_get_length");
+ ret = 1;
+ goto end;
+ }
+
+ ret = (count == expected);
+ ok(ret, "Expected %d subelements got %d", expected, count);
+ if (!ret) {
+ ret = 1;
+ goto end;
+ }
+
+ for (unsigned int i = 1; i < count + 1; i++) {
+ const struct lttng_event_field_value *value;
+ status = lttng_event_field_value_array_get_element_at_index(
+ event_field, i - 1, &value);
+ if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ fail("lttng_event_field_value_array_get_element_at_index");
+ ret = 1;
+ goto end;
+ }
+ ret = validate_unsigned_int_field(value, i);
+ if (ret) {
+ goto end;
+ }
}
+
+ ret = 0;
+end:
+
return ret;
}
-
-static
-int suspend_application(void)
+static int validate_array_unsigned_int_field_at_index(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
{
int ret;
- struct stat buf;
+ uint64_t expected_value = 2;
+ enum lttng_event_field_value_status status;
+ uint64_t value;
- if (!stat(app_state_file, &buf)) {
- fail("App is already in a suspended state.");
- ret = -1;
- goto error;
- }
+ /* Unused */
+ (void) iteration;
- /*
- * Send SIGUSR1 to application instructing it to bypass tracepoint.
- */
- ret = kill(app_pid, SIGUSR1);
+ ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT);
if (ret) {
- fail("SIGUSR1 failed. errno %d", errno);
- ret = -1;
- goto error;
+ goto end;
}
- wait_on_file(app_state_file, true);
+ status = lttng_event_field_value_unsigned_int_get_value(
+ event_field , &value);
+ if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ fail("lttng_event_field_value_unsigned_int_get_value");
+ ret = 1;
+ goto end;
+ }
-error:
- return ret;
+ ret = (value == expected_value);
+ ok (ret, "Expected unsigned int of value: %u got %" PRIu64,
+ expected_value, value);
+ ret = 0;
+end:
+ return ret;
}
-static
-int resume_application()
+/*
+ * Validate sequence for a string (seqfield1):
+ *
+ * Value: "test" in utf8 [116, 101, 115, 116]
+ */
+static int validate_seqfield1(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
{
int ret;
- struct stat buf;
+ enum lttng_event_field_value_status status;
+ unsigned int count;
+ unsigned int expect[4] = {116, 101, 115, 116};
- ret = stat(app_state_file, &buf);
- if (ret == -1 && errno == ENOENT) {
- fail("State file does not exist");
- goto error;
- }
+ /* Unused */
+ (void) iteration;
+
+ ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY);
if (ret) {
- perror("stat");
- goto error;
+ goto end;
}
- ret = kill(app_pid, SIGUSR1);
- if (ret) {
- fail("SIGUSR1 failed. errno %d", errno);
- ret = -1;
- goto error;
+ status = lttng_event_field_value_array_get_length(event_field, &count);
+ if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ fail("lttng_event_field_value_array_get_length");
+ ret = 1;
+ goto end;
}
- wait_on_file(app_state_file, false);
+ ret = (count == 4);
+ ok(ret, "Expected 4 subelement got %d", count);
+ if (!ret) {
+ ret = 1;
+ goto end;
+ }
-error:
- return ret;
+ for (unsigned int i = 0; i < count ; i++) {
+ const struct lttng_event_field_value *value;
+ status = lttng_event_field_value_array_get_element_at_index(
+ event_field, i, &value);
+ if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ fail("lttng_event_field_value_array_get_element_at_index");
+ ret = 1;
+ goto end;
+ }
+ ret = validate_unsigned_int_field(value, expect[i]);
+ if (ret) {
+ goto end;
+ }
+ }
-}
+ ret = 0;
+end:
+ return ret;
+}
-static
-void test_triggers_buffer_usage_condition(const char *session_name,
- const char *channel_name,
- enum lttng_domain_type domain_type,
- enum lttng_condition_type condition_type)
+static int validate_string(
+ const struct lttng_event_field_value *event_field,
+ const char *expect)
{
- unsigned int test_vector_size = 5, i;
- enum lttng_condition_status condition_status;
- struct lttng_action *action;
+ int ret;
+ const char *value = NULL;
- /* Set-up */
- action = lttng_action_notify_create();
- if (!action) {
- fail("Setup error on action creation");
+ ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_STRING);
+ if (ret) {
goto end;
}
- /* Test lttng_register_trigger with null value */
- ok(lttng_register_trigger(NULL) == -LTTNG_ERR_INVALID, "Registering a NULL trigger fails as expected");
+ value = lttng_event_field_value_string_get_value(event_field);
+ if (!value) {
+ fail("lttng_event_field_value_array_get_length");
+ ret = 1;
+ goto end;
+ }
- /* Test: register a trigger */
+ ok(!strcmp(value, expect), "Expected string: \"%s\" got \"%s\"", expect, value);
- for (i = 0; i < pow(2,test_vector_size); i++) {
- int loop_ret = 0;
- char *test_tuple_string = NULL;
- unsigned int mask_position = 0;
- bool session_name_set = false;
- bool channel_name_set = false;
- bool threshold_ratio_set = false;
- bool threshold_byte_set = false;
- bool domain_type_set = false;
+ ret = 0;
+end:
- struct lttng_trigger *trigger = NULL;
- struct lttng_condition *condition = NULL;
+ return ret;
+}
- /* Create base condition */
- switch (condition_type) {
- case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
- condition = lttng_condition_buffer_usage_low_create();
- break;
- case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
- condition = lttng_condition_buffer_usage_high_create();
- break;
- default:
- loop_ret = 1;
- goto loop_end;
- }
+/*
+ * Validate string. Expected value is "test".
+ */
+static int validate_string_test(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
+{
+ int ret;
+ const char *expect = "test";
- if (!condition) {
- loop_ret = 1;
- goto loop_end;
+ /* Unused */
+ (void) iteration;
- }
+ ret = validate_string(event_field, expect);
+ return ret;
+}
- /* Prepare the condition for trigger registration test */
+/*
+ * Validate escaped string. Expected value is "\*".
+ */
+static int validate_string_escaped(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
+{
+ int ret;
+ const char *expect = "\\*";
- /* Set session name */
- if ((1 << mask_position) & i) {
- condition_status = lttng_condition_buffer_usage_set_session_name(
- condition, session_name);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- loop_ret = 1;
- goto loop_end;
- }
- session_name_set = true;
- }
- mask_position++;
+ /* Unused */
+ (void) iteration;
- /* Set channel name */
- if ((1 << mask_position) & i) {
- condition_status = lttng_condition_buffer_usage_set_channel_name(
- condition, channel_name);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- loop_ret = 1;
- goto loop_end;
- }
- channel_name_set = true;
+ ret = validate_string(event_field, expect);
+ return ret;
+}
+
+/*
+ * Validate real field.
+ */
+static int validate_real(
+ const struct lttng_event_field_value *event_field,
+ double expect)
+{
+ int ret;
+ double value;
+ enum lttng_event_field_value_status status;
+
+ ret = validate_type(event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_REAL);
+ if (ret) {
+ goto end;
+ }
+
+ status = lttng_event_field_value_real_get_value(event_field, &value);
+ if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ fail("lttng_event_field_value_real_get_value");
+ ret = 1;
+ goto end;
+ }
+
+ ret = (value == expect);
+ ok(ret, "Real expected: %f got: %f", expect, value);
+
+ ret = !ret;
+end:
+ return ret;
+}
+
+/*
+ * Validate floatfield.
+ */
+static int validate_floatfield(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
+{
+ int ret;
+ double expect = 2222.0;
+
+ /* Unused */
+ (void) iteration;
+
+ ret = validate_real(event_field, expect);
+ return ret;
+}
+
+/*
+ * Validate doublefield.
+ */
+static int validate_doublefield(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
+{
+ int ret;
+ double expect = 2.0;
+
+ /* Unused */
+ (void) iteration;
+
+ ret = validate_real(event_field, expect);
+ return ret;
+}
+
+/*
+ * Validate enum0: enum0 = ( "AUTO: EXPECT 0" : container = 0 )
+ */
+static int validate_enum0(const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
+{
+ int ret;
+ enum lttng_event_field_value_status status;
+ uint64_t value;
+ uint64_t expected_value = 0;
+
+ /* Unused */
+ (void) iteration;
+
+ ret = validate_type(event_field,
+ LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM);
+ if (ret) {
+ goto end;
+ }
+
+ status = lttng_event_field_value_unsigned_int_get_value(
+ event_field, &value);
+ if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ fail("lttng_event_field_value_unsigned_int_get_value");
+ ret = 1;
+ goto end;
+ }
+
+ ok(value == expected_value,
+ "Enum value expected: %" PRIu64 " got %" PRIu64,
+ expected_value, value);
+
+end:
+ return ret;
+}
+
+/*
+ * Validate enumnegative: enumnegative = ( "AUTO: EXPECT 0" : container = 0 )
+ *
+ * We expect 2 labels here.
+ */
+static int validate_enumnegative(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
+{
+ int ret;
+ enum lttng_event_field_value_status status;
+ int64_t value;
+ int64_t expected_value = -1;
+
+ /* Unused */
+ (void) iteration;
+
+ ret = validate_type(
+ event_field, LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM);
+ if (ret) {
+ goto end;
+ }
+
+ status = lttng_event_field_value_signed_int_get_value(
+ event_field, &value);
+ if (status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ fail("lttng_event_field_value_unsigned_int_get_value");
+ ret = 1;
+ goto end;
+ }
+
+ ok(value == expected_value,
+ "Enum value expected: %" PRId64 " got %" PRId64,
+ expected_value, value);
+
+end:
+ return ret;
+}
+
+static int validate_context_procname_ust(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
+{
+ int ret;
+
+ /* Unused */
+ (void) iteration;
+
+ ret = validate_string(event_field, "gen-ust-events");
+ return ret;
+}
+
+static int validate_context_procname_kernel(
+ const struct lttng_event_field_value *event_field,
+ unsigned int iteration)
+{
+ int ret;
+
+ /* Unused */
+ (void) iteration;
+
+ ret = validate_string(event_field, "echo");
+ return ret;
+}
+
+struct capture_base_field_tuple test_capture_base_fields[] = {
+ {"DOESNOTEXIST", FIELD_TYPE_PAYLOAD, false, false, NULL, NULL},
+ {"intfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field},
+ {"longfield", FIELD_TYPE_PAYLOAD, true, true, validate_unsigned_int_field, validate_unsigned_int_field},
+ {"signedfield", FIELD_TYPE_PAYLOAD, true, true, validate_signed_int_field, validate_signed_int_field},
+ {"arrfield1", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field},
+ {"arrfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test},
+ {"arrfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field},
+ {"seqfield1", FIELD_TYPE_PAYLOAD, true, true, validate_seqfield1, validate_seqfield1},
+ {"seqfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test},
+ {"seqfield3", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field},
+ {"seqfield4", FIELD_TYPE_PAYLOAD, true, true, validate_array_unsigned_int_field, validate_array_unsigned_int_field},
+ {"arrfield1[1]", FIELD_TYPE_ARRAY_FIELD, true, true, validate_array_unsigned_int_field_at_index, validate_array_unsigned_int_field_at_index},
+ {"stringfield", FIELD_TYPE_PAYLOAD, true, true, validate_string_test, validate_string_test},
+ {"stringfield2", FIELD_TYPE_PAYLOAD, true, true, validate_string_escaped, validate_string_escaped},
+ {"floatfield", FIELD_TYPE_PAYLOAD, true, false, validate_floatfield, validate_floatfield},
+ {"doublefield", FIELD_TYPE_PAYLOAD, true, false, validate_doublefield, validate_doublefield},
+ {"enum0", FIELD_TYPE_PAYLOAD, true, true, validate_enum0, validate_enum0},
+ {"enumnegative", FIELD_TYPE_PAYLOAD, true, true, validate_enumnegative, validate_enumnegative},
+ {"$ctx.procname", FIELD_TYPE_CONTEXT, true, true, validate_context_procname_ust, validate_context_procname_kernel},
+};
+
+static const char *get_notification_trigger_name(
+ struct lttng_notification *notification)
+{
+ const char *name = NULL;
+ enum lttng_evaluation_status status;
+ const struct lttng_evaluation *evaluation;
+ evaluation = lttng_notification_get_evaluation(notification);
+ if (evaluation == NULL) {
+ fail("lttng_notification_get_evaluation");
+ goto end;
+ }
+
+ switch (lttng_evaluation_get_type(evaluation)) {
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ {
+ status = lttng_evaluation_event_rule_get_trigger_name(
+ evaluation, &name);
+ if (status != LTTNG_EVALUATION_STATUS_OK) {
+ fail("lttng_evaluation_event_rule_get_trigger_name");
+ name = NULL;
+ goto end;
+ }
+ break;
+ }
+ default:
+ fail("Wrong notification evaluation type \n");
+ goto end;
+ }
+end:
+ return name;
+}
+
+static int validator_notification_trigger_name(
+ struct lttng_notification *notification,
+ const char *trigger_name)
+{
+ int ret;
+ bool name_is_equal;
+ const char *name;
+
+ assert(notification);
+ assert(trigger_name);
+
+ name = get_notification_trigger_name(notification);
+ if (name == NULL) {
+ ret = 1;
+ goto end;
+ }
+
+ name_is_equal = (strcmp(trigger_name, name) == 0);
+ ok(name_is_equal, "Expected trigger name: %s got %s", trigger_name,
+ name);
+
+ ret = !name_is_equal;
+
+end:
+ return ret;
+}
+
+static
+void wait_on_file(const char *path, bool file_exist)
+{
+ if (!path) {
+ return;
+ }
+ for (;;) {
+ int ret;
+ struct stat buf;
+
+ ret = stat(path, &buf);
+ if (ret == -1 && errno == ENOENT) {
+ if (file_exist) {
+ /*
+ * The file does not exist. wait a bit and
+ * continue looping until it does.
+ */
+ (void) poll(NULL, 0, 10);
+ continue;
+ }
+
+ /*
+ * File does not exist and the exit condition we want.
+ * Break from the loop and return.
+ */
+ break;
+ }
+ if (ret) {
+ perror("stat");
+ exit(EXIT_FAILURE);
+ }
+ /*
+ * stat() returned 0, so the file exists. break now only if
+ * that's the exit condition we want.
+ */
+ if (file_exist) {
+ break;
+ }
+ }
+}
+
+static
+int write_pipe(const char *path, uint8_t data)
+{
+ int ret = 0;
+ int fd = 0;
+
+ fd = open(path, O_WRONLY | O_NONBLOCK);
+ if (fd < 0) {
+ perror("Could not open consumer control named pipe");
+ goto end;
+ }
+
+ ret = write(fd, &data , sizeof(data));
+ if (ret < 1) {
+ perror("Named pipe write failed");
+ if (close(fd)) {
+ perror("Named pipe close failed");
+ }
+ ret = -1;
+ goto end;
+ }
+
+ ret = close(fd);
+ if (ret < 0) {
+ perror("Name pipe closing failed");
+ ret = -1;
+ goto end;
+ }
+end:
+ return ret;
+}
+
+static
+int stop_consumer(const char **argv)
+{
+ int ret = 0, i;
+
+ for (i = named_pipe_args_start; i < nb_args; i++) {
+ ret = write_pipe(argv[i], 49);
+ }
+ return ret;
+}
+
+static
+int resume_consumer(const char **argv)
+{
+ int ret = 0, i;
+
+ for (i = named_pipe_args_start; i < nb_args; i++) {
+ ret = write_pipe(argv[i], 0);
+ }
+ return ret;
+}
+
+static
+int suspend_application(void)
+{
+ int ret;
+ struct stat buf;
+
+ if (!stat(app_state_file, &buf)) {
+ fail("App is already in a suspended state.");
+ ret = -1;
+ goto error;
+ }
+
+ /*
+ * Send SIGUSR1 to application instructing it to bypass tracepoint.
+ */
+ assert(app_pid > 1);
+
+ ret = kill(app_pid, SIGUSR1);
+ if (ret) {
+ fail("SIGUSR1 failed. errno %d", errno);
+ ret = -1;
+ goto error;
+ }
+
+ wait_on_file(app_state_file, true);
+
+error:
+ return ret;
+
+}
+
+static
+int resume_application()
+{
+ int ret;
+ struct stat buf;
+
+ ret = stat(app_state_file, &buf);
+ if (ret == -1 && errno == ENOENT) {
+ fail("State file does not exist");
+ goto error;
+ }
+ if (ret) {
+ perror("stat");
+ goto error;
+ }
+
+ assert(app_pid > 1);
+
+ ret = kill(app_pid, SIGUSR1);
+ if (ret) {
+ fail("SIGUSR1 failed. errno %d", errno);
+ ret = -1;
+ goto error;
+ }
+
+ wait_on_file(app_state_file, false);
+
+error:
+ return ret;
+
+}
+
+
+static
+void test_triggers_buffer_usage_condition(const char *session_name,
+ const char *channel_name,
+ enum lttng_domain_type domain_type,
+ enum lttng_condition_type condition_type)
+{
+ unsigned int test_vector_size = 5, i;
+ enum lttng_condition_status condition_status;
+ struct lttng_action *action;
+
+ /* Set-up */
+ action = lttng_action_notify_create();
+ if (!action) {
+ fail("Setup error on action creation");
+ goto end;
+ }
+
+ /* Test lttng_register_trigger with null value */
+ ok(lttng_register_trigger(NULL) == -LTTNG_ERR_INVALID, "Registering a NULL trigger fails as expected");
+
+ /* Test: register a trigger */
+
+ for (i = 0; i < pow(2,test_vector_size); i++) {
+ int loop_ret = 0;
+ char *test_tuple_string = NULL;
+ unsigned int mask_position = 0;
+ bool session_name_set = false;
+ bool channel_name_set = false;
+ bool threshold_ratio_set = false;
+ bool threshold_byte_set = false;
+ bool domain_type_set = false;
+
+ struct lttng_trigger *trigger = NULL;
+ struct lttng_condition *condition = NULL;
+
+ /* Create base condition */
+ switch (condition_type) {
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+ condition = lttng_condition_buffer_usage_low_create();
+ break;
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+ condition = lttng_condition_buffer_usage_high_create();
+ break;
+ default:
+ loop_ret = 1;
+ goto loop_end;
+ }
+
+ if (!condition) {
+ loop_ret = 1;
+ goto loop_end;
+
+ }
+
+ /* Prepare the condition for trigger registration test */
+
+ /* Set session name */
+ if ((1 << mask_position) & i) {
+ condition_status = lttng_condition_buffer_usage_set_session_name(
+ condition, session_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+ session_name_set = true;
+ }
+ mask_position++;
+
+ /* Set channel name */
+ if ((1 << mask_position) & i) {
+ condition_status = lttng_condition_buffer_usage_set_channel_name(
+ condition, channel_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+ channel_name_set = true;
+ }
+ mask_position++;
+
+ /* Set threshold ratio */
+ if ((1 << mask_position) & i) {
+ condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
+ condition, 0.0);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+ threshold_ratio_set = true;
+ }
+ mask_position++;
+
+ /* Set threshold byte */
+ if ((1 << mask_position) & i) {
+ condition_status = lttng_condition_buffer_usage_set_threshold(
+ condition, 0);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+ threshold_byte_set = true;
+ }
+ mask_position++;
+
+ /* Set domain type */
+ if ((1 << mask_position) & i) {
+ condition_status = lttng_condition_buffer_usage_set_domain_type(
+ condition, LTTNG_DOMAIN_UST);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+ domain_type_set = true;
+ }
+
+ /* Safety check */
+ if (mask_position != test_vector_size -1) {
+ assert("Logic error for test vector generation");
+ }
+
+ loop_ret = asprintf(&test_tuple_string, "session name %s, channel name %s, threshold ratio %s, threshold byte %s, domain type %s",
+ session_name_set ? "set" : "unset",
+ channel_name_set ? "set" : "unset",
+ threshold_ratio_set ? "set" : "unset",
+ threshold_byte_set ? "set" : "unset",
+ domain_type_set? "set" : "unset");
+ if (!test_tuple_string || loop_ret < 0) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+
+ /* Create trigger */
+ trigger = lttng_trigger_create(condition, action);
+ if (!trigger) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+
+ loop_ret = lttng_register_trigger(trigger);
+
+loop_end:
+ if (loop_ret == 1) {
+ fail("Setup error occurred for tuple: %s", test_tuple_string);
+ goto loop_cleanup;
+ }
+
+ /* This combination happens three times */
+ if (session_name_set && channel_name_set
+ && (threshold_ratio_set || threshold_byte_set)
+ && domain_type_set) {
+ ok(loop_ret == 0, "Trigger is registered: %s", test_tuple_string);
+
+ /*
+ * Test that a trigger cannot be registered
+ * multiple time.
+ */
+ loop_ret = lttng_register_trigger(trigger);
+ ok(loop_ret == -LTTNG_ERR_TRIGGER_EXISTS, "Re-register trigger fails as expected: %s", test_tuple_string);
+
+ /* Test that a trigger can be unregistered */
+ loop_ret = lttng_unregister_trigger(trigger);
+ ok(loop_ret == 0, "Unregister trigger: %s", test_tuple_string);
+
+ /*
+ * Test that unregistration of a non-previously
+ * registered trigger fail.
+ */
+ loop_ret = lttng_unregister_trigger(trigger);
+ ok(loop_ret == -LTTNG_ERR_TRIGGER_NOT_FOUND, "Unregister of a non-registered trigger fails as expected: %s", test_tuple_string);
+ } else {
+ ok(loop_ret == -LTTNG_ERR_INVALID_TRIGGER, "Trigger is invalid as expected and cannot be registered: %s", test_tuple_string);
+ }
+
+loop_cleanup:
+ free(test_tuple_string);
+ lttng_trigger_destroy(trigger);
+ lttng_condition_destroy(condition);
+ }
+
+end:
+ lttng_action_destroy(action);
+}
+
+static
+void wait_data_pending(const char *session_name)
+{
+ int ret;
+
+ do {
+ ret = lttng_data_pending(session_name);
+ assert(ret >= 0);
+ } while (ret != 0);
+}
+
+static
+int setup_buffer_usage_condition(struct lttng_condition *condition,
+ const char *condition_name,
+ const char *session_name,
+ const char *channel_name,
+ const enum lttng_domain_type domain_type)
+{
+ enum lttng_condition_status condition_status;
+ int ret = 0;
+
+ condition_status = lttng_condition_buffer_usage_set_session_name(
+ condition, session_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Error setting session name on %s creation", condition_name);
+ ret = -1;
+ goto end;
+ }
+
+ condition_status = lttng_condition_buffer_usage_set_channel_name(
+ condition, channel_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Error setting channel name on %s creation", condition_name);
+ ret = -1;
+ goto end;
+ }
+
+ condition_status = lttng_condition_buffer_usage_set_domain_type(
+ condition, domain_type);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Error setting domain type on %s creation", condition_name);
+ ret = -1;
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+static
+void test_invalid_channel_subscription(
+ const enum lttng_domain_type domain_type)
+{
+ enum lttng_condition_status condition_status;
+ enum lttng_notification_channel_status nc_status;
+ struct lttng_condition *dummy_condition = NULL;
+ struct lttng_condition *dummy_invalid_condition = NULL;
+ struct lttng_notification_channel *notification_channel = NULL;
+ int ret = 0;
+
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
+ if (!notification_channel) {
+ goto end;
+ }
+
+ /*
+ * Create a dummy, empty (thus invalid) condition to test error paths.
+ */
+ dummy_invalid_condition = lttng_condition_buffer_usage_low_create();
+ if (!dummy_invalid_condition) {
+ fail("Setup error on condition creation");
+ goto end;
+ }
+
+ /*
+ * Test subscription and unsubscription of an invalid condition to/from
+ * a channel.
+ */
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, dummy_invalid_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID,
+ "Subscribing to an invalid condition");
+
+ nc_status = lttng_notification_channel_unsubscribe(
+ notification_channel, dummy_invalid_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID,
+ "Unsubscribing from an invalid condition");
+
+ /* Create a valid dummy condition with a ratio of 0.5 */
+ dummy_condition = lttng_condition_buffer_usage_low_create();
+ if (!dummy_condition) {
+ fail("Setup error on dummy_condition creation");
+ goto end;
+ }
+
+ condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
+ dummy_condition, 0.5);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on condition creation");
+ goto end;
+ }
+
+ ret = setup_buffer_usage_condition(dummy_condition, "dummy_condition",
+ "dummy_session", "dummy_channel", domain_type);
+ if (ret) {
+ fail("Setup error on dummy condition creation");
+ goto end;
+ }
+
+ /*
+ * Test subscription and unsubscription to/from a channel with invalid
+ * parameters.
+ */
+ nc_status = lttng_notification_channel_subscribe(NULL, NULL);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID,
+ "Notification channel subscription is invalid: NULL, NULL");
+
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, NULL);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID,
+ "Notification channel subscription is invalid: NON-NULL, NULL");
+
+ nc_status = lttng_notification_channel_subscribe(NULL, dummy_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID,
+ "Notification channel subscription is invalid: NULL, NON-NULL");
+
+ nc_status = lttng_notification_channel_unsubscribe(
+ notification_channel, dummy_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION,
+ "Unsubscribing from a valid unknown condition");
+
+end:
+ lttng_notification_channel_destroy(notification_channel);
+ lttng_condition_destroy(dummy_invalid_condition);
+ lttng_condition_destroy(dummy_condition);
+ return;
+}
+
+enum buffer_usage_type {
+ BUFFER_USAGE_TYPE_LOW,
+ BUFFER_USAGE_TYPE_HIGH,
+};
+
+static int register_buffer_usage_notify_trigger(const char *session_name,
+ const char *channel_name,
+ const enum lttng_domain_type domain_type,
+ enum buffer_usage_type buffer_usage_type,
+ double ratio,
+ struct lttng_condition **condition,
+ struct lttng_action **action,
+ struct lttng_trigger **trigger)
+{
+ enum lttng_condition_status condition_status;
+ struct lttng_action *tmp_action = NULL;
+ struct lttng_condition *tmp_condition = NULL;
+ struct lttng_trigger *tmp_trigger = NULL;
+ int ret = 0;
+
+ /* Set-up */
+ tmp_action = lttng_action_notify_create();
+ if (!action) {
+ fail("Setup error on action creation");
+ ret = -1;
+ goto error;
+ }
+
+ if (buffer_usage_type == BUFFER_USAGE_TYPE_LOW) {
+ tmp_condition = lttng_condition_buffer_usage_low_create();
+ } else {
+ tmp_condition = lttng_condition_buffer_usage_high_create();
+ }
+
+ if (!tmp_condition) {
+ fail("Setup error on condition creation");
+ ret = -1;
+ goto error;
+ }
+
+ /* Set the buffer usage threashold */
+ condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
+ tmp_condition, ratio);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on condition creation");
+ ret = -1;
+ goto error;
+ }
+
+ ret = setup_buffer_usage_condition(tmp_condition, "condition_name",
+ session_name, channel_name, domain_type);
+ if (ret) {
+ fail("Setup error on condition creation");
+ ret = -1;
+ goto error;
+ }
+
+ /* Register the triggers for condition */
+ tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action);
+ if (!tmp_trigger) {
+ fail("Setup error on trigger creation");
+ ret = -1;
+ goto error;
+ }
+
+ ret = lttng_register_trigger(tmp_trigger);
+ if (ret) {
+ fail("Setup error on trigger registration");
+ ret = -1;
+ goto error;
+ }
+
+ *condition = tmp_condition;
+ *trigger = tmp_trigger;
+ *action = tmp_action;
+ goto end;
+
+error:
+ lttng_action_destroy(tmp_action);
+ lttng_condition_destroy(tmp_condition);
+ lttng_trigger_destroy(tmp_trigger);
+
+end:
+ return ret;
+}
+
+static void test_subscription_twice(const char *session_name,
+ const char *channel_name,
+ const enum lttng_domain_type domain_type)
+{
+ int ret = 0;
+ enum lttng_notification_channel_status nc_status;
+
+ struct lttng_action *action = NULL;
+ struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_trigger *trigger = NULL;
+
+ struct lttng_condition *condition = NULL;
+
+ ret = register_buffer_usage_notify_trigger(session_name, channel_name,
+ domain_type, BUFFER_USAGE_TYPE_LOW, 0.99, &condition,
+ &action, &trigger);
+ if (ret) {
+ fail("Setup error on trigger registration");
+ goto end;
+ }
+
+ /* Begin testing. */
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
+ if (!notification_channel) {
+ goto end;
+ }
+
+ /* Subscribe a valid condition. */
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to condition");
+
+ /* Subscribing again should fail. */
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED,
+ "Subscribe to a condition for which subscription was already done");
+
+end:
+ lttng_unregister_trigger(trigger);
+ lttng_trigger_destroy(trigger);
+ lttng_notification_channel_destroy(notification_channel);
+ lttng_action_destroy(action);
+ lttng_condition_destroy(condition);
+}
+
+static void test_buffer_usage_notification_channel(const char *session_name,
+ const char *channel_name,
+ const enum lttng_domain_type domain_type,
+ const char **argv)
+{
+ int ret = 0;
+ enum lttng_notification_channel_status nc_status;
+
+ struct lttng_action *low_action = NULL;
+ struct lttng_action *high_action = NULL;
+ struct lttng_notification *notification = NULL;
+ struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_trigger *low_trigger = NULL;
+ struct lttng_trigger *high_trigger = NULL;
+
+ struct lttng_condition *low_condition = NULL;
+ struct lttng_condition *high_condition = NULL;
+
+ double low_ratio = 0.0;
+ /* This is not 99 since we can end up in scenario where an event is
+ * bigger than 1% of the buffer and hence the buffer ratio will never
+ * trigger since the event will always be discarder by the tracer.
+ */
+
+ double high_ratio = 0.90;
+
+ ret = register_buffer_usage_notify_trigger(session_name, channel_name,
+ domain_type, BUFFER_USAGE_TYPE_LOW, low_ratio,
+ &low_condition, &low_action, &low_trigger);
+ if (ret) {
+ fail("Setup error on low trigger registration");
+ goto end;
+ }
+
+ ret = register_buffer_usage_notify_trigger(session_name, channel_name,
+ domain_type, BUFFER_USAGE_TYPE_HIGH, high_ratio,
+ &high_condition, &high_action, &high_trigger);
+ if (ret) {
+ fail("Setup error on high trigger registration");
+ goto end;
+ }
+
+ /* Begin testing */
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
+ if (!notification_channel) {
+ goto end;
+ }
+
+ /* Subscribe a valid low condition */
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, low_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to low condition");
+
+ /* Subscribe a valid high condition */
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, high_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to high condition");
+
+ resume_application();
+
+ /* Wait for notification to happen */
+ stop_consumer(argv);
+ lttng_start_tracing(session_name);
+
+ /* Wait for high notification */
+ nc_status = lttng_notification_channel_get_next_notification(
+ notification_channel, ¬ification);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification &&
+ lttng_condition_get_type(lttng_notification_get_condition(
+ notification)) ==
+ LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH,
+ "High notification received after intermediary communication");
+ lttng_notification_destroy(notification);
+ notification = NULL;
+
+ suspend_application();
+ lttng_stop_tracing_no_wait(session_name);
+ resume_consumer(argv);
+ wait_data_pending(session_name);
+
+ /*
+ * Test that communication still work even if there is notification
+ * waiting for consumption.
+ */
+
+ nc_status = lttng_notification_channel_unsubscribe(
+ notification_channel, low_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Unsubscribe with pending notification");
+
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, low_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe with pending notification");
+
+ nc_status = lttng_notification_channel_get_next_notification(
+ notification_channel, ¬ification);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification &&
+ lttng_condition_get_type(lttng_notification_get_condition(
+ notification)) ==
+ LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW,
+ "Low notification received after intermediary communication");
+ lttng_notification_destroy(notification);
+ notification = NULL;
+
+ /* Stop consumer to force a high notification */
+ stop_consumer(argv);
+ resume_application();
+ lttng_start_tracing(session_name);
+
+ nc_status = lttng_notification_channel_get_next_notification(
+ notification_channel, ¬ification);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification &&
+ lttng_condition_get_type(lttng_notification_get_condition(
+ notification)) ==
+ LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH,
+ "High notification received after intermediary communication");
+ lttng_notification_destroy(notification);
+ notification = NULL;
+
+ suspend_application();
+ lttng_stop_tracing_no_wait(session_name);
+ resume_consumer(argv);
+ wait_data_pending(session_name);
+
+ nc_status = lttng_notification_channel_get_next_notification(
+ notification_channel, ¬ification);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification &&
+ lttng_condition_get_type(lttng_notification_get_condition(
+ notification)) ==
+ LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW,
+ "Low notification received after re-subscription");
+ lttng_notification_destroy(notification);
+ notification = NULL;
+
+ stop_consumer(argv);
+ resume_application();
+ /* Stop consumer to force a high notification */
+ lttng_start_tracing(session_name);
+
+ nc_status = lttng_notification_channel_get_next_notification(
+ notification_channel, ¬ification);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification &&
+ lttng_condition_get_type(lttng_notification_get_condition(
+ notification)) ==
+ LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH,
+ "High notification");
+ lttng_notification_destroy(notification);
+ notification = NULL;
+
+ suspend_application();
+
+ /* Resume consumer to allow event consumption */
+ lttng_stop_tracing_no_wait(session_name);
+ resume_consumer(argv);
+ wait_data_pending(session_name);
+
+ nc_status = lttng_notification_channel_unsubscribe(
+ notification_channel, low_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Unsubscribe low condition with pending notification");
+
+ nc_status = lttng_notification_channel_unsubscribe(
+ notification_channel, high_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Unsubscribe high condition with pending notification");
+
+end:
+ lttng_notification_channel_destroy(notification_channel);
+ lttng_trigger_destroy(low_trigger);
+ lttng_trigger_destroy(high_trigger);
+ lttng_action_destroy(low_action);
+ lttng_action_destroy(high_action);
+ lttng_condition_destroy(low_condition);
+ lttng_condition_destroy(high_condition);
+}
+
+static void create_tracepoint_event_rule_trigger(const char *event_pattern,
+ const char *trigger_name,
+ const char *filter,
+ unsigned int exclusion_count,
+ const char **exclusions,
+ enum lttng_domain_type domain_type,
+ condition_capture_desc_cb capture_desc_cb,
+ struct lttng_condition **condition,
+ struct lttng_action **action,
+ struct lttng_trigger **trigger)
+{
+ enum lttng_event_rule_status event_rule_status;
+ enum lttng_trigger_status trigger_status;
+
+ struct lttng_action *tmp_action = NULL;
+ struct lttng_event_rule *event_rule = NULL;
+ struct lttng_condition *tmp_condition = NULL;
+ struct lttng_trigger *tmp_trigger = NULL;
+ int ret;
+
+ assert(event_pattern);
+ assert(trigger_name);
+ assert(condition);
+ assert(trigger);
+
+ event_rule = lttng_event_rule_tracepoint_create(domain_type);
+ ok(event_rule, "Tracepoint event rule object creation");
+
+ event_rule_status = lttng_event_rule_tracepoint_set_pattern(
+ event_rule, event_pattern);
+ ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+ "Setting tracepoint event rule pattern: %s",
+ event_pattern);
+
+ if (filter) {
+ event_rule_status = lttng_event_rule_tracepoint_set_filter(
+ event_rule, filter);
+ ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+ "Setting tracepoint event rule filter: %s",
+ filter);
+ }
+
+ if (exclusions) {
+ assert(domain_type == LTTNG_DOMAIN_UST);
+ assert(exclusion_count > 0);
+
+ event_rule_status = lttng_event_rule_tracepoint_set_exclusions(
+ event_rule, exclusion_count, exclusions);
+ ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+ "Setting tracepoint event rule exclusions");
+ }
+
+ tmp_condition = lttng_condition_event_rule_create(event_rule);
+ ok(tmp_condition, "Condition event rule object creation");
+ /* Ownership was passed to condition */
+ event_rule = NULL;
+
+ if (capture_desc_cb) {
+ ret = capture_desc_cb(tmp_condition);
+ if (ret) {
+ assert("Generating the condition capture descriptor");
}
- mask_position++;
+ }
+
+ tmp_action = lttng_action_notify_create();
+ ok(tmp_action, "Action event rule object creation");
+
+ tmp_trigger = lttng_trigger_create(tmp_condition, tmp_action);
+ ok(tmp_trigger, "Trigger object creation %s", trigger_name);
+
+ trigger_status = lttng_trigger_set_name(tmp_trigger, trigger_name);
+ ok(trigger_status == LTTNG_TRIGGER_STATUS_OK,
+ "Setting name to trigger %s", trigger_name);
+
+ ret = lttng_register_trigger(tmp_trigger);
+ ok(ret == 0, "Trigger registration %s", trigger_name);
+
+ *condition = tmp_condition;
+ *action = tmp_action;
+ *trigger = tmp_trigger;
+
+ return;
+}
+
+static struct lttng_notification *get_next_notification(
+ struct lttng_notification_channel *notification_channel)
+{
+ struct lttng_notification *local_notification = NULL;
+ enum lttng_notification_channel_status status;
+
+ /* Receive the next notification. */
+ status = lttng_notification_channel_get_next_notification(
+ notification_channel, &local_notification);
+
+ switch (status) {
+ case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK:
+ break;
+ case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED:
+ fail("Notifications have been dropped");
+ local_notification = NULL;
+ break;
+ default:
+ /* Unhandled conditions / errors. */
+ fail("error: Unknown notification channel status\n");
+ local_notification = NULL;
+ break;
+ }
+
+ return local_notification;
+}
+
+static void test_tracepoint_event_rule_notification(
+ enum lttng_domain_type domain_type)
+{
+ int i;
+ int ret;
+ enum lttng_notification_channel_status nc_status;
+
+ struct lttng_action *action = NULL;
+ struct lttng_condition *condition = NULL;
+ struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_trigger *trigger = NULL;
+ const char *trigger_name = "my_precious";
+ const char *pattern;
+
+ if (domain_type == LTTNG_DOMAIN_UST) {
+ pattern = "tp:tptest";
+ } else {
+ pattern = "lttng_test_filter_event";
+ }
+
+ create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0,
+ NULL, domain_type, NULL, &condition, &action, &trigger);
+
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
+
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to tracepoint event rule condition");
+
+ resume_application();
- /* Set threshold ratio */
- if ((1 << mask_position) & i) {
- condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
- condition, 0.0);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- loop_ret = 1;
- goto loop_end;
- }
- threshold_ratio_set = true;
- }
- mask_position++;
+ /* Get 3 notifications */
+ for (i = 0; i < 3; i++) {
+ struct lttng_notification *notification = get_next_notification(
+ notification_channel);
+ ok(notification, "Received notification");
- /* Set threshold byte */
- if ((1 << mask_position) & i) {
- condition_status = lttng_condition_buffer_usage_set_threshold(
- condition, 0);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- loop_ret = 1;
- goto loop_end;
- }
- threshold_byte_set = true;
+ /* Error */
+ if (notification == NULL) {
+ goto end;
}
- mask_position++;
- /* Set domain type */
- if ((1 << mask_position) & i) {
- condition_status = lttng_condition_buffer_usage_set_domain_type(
- condition, LTTNG_DOMAIN_UST);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- loop_ret = 1;
- goto loop_end;
- }
- domain_type_set = true;
+ ret = validator_notification_trigger_name(notification, trigger_name);
+ lttng_notification_destroy(notification);
+ if (ret) {
+ goto end;
}
+ }
- /* Safety check */
- if (mask_position != test_vector_size -1) {
- assert("Logic error for test vector generation");
+end:
+ suspend_application();
+ lttng_notification_channel_destroy(notification_channel);
+ lttng_unregister_trigger(trigger);
+ lttng_trigger_destroy(trigger);
+ lttng_action_destroy(action);
+ lttng_condition_destroy(condition);
+ return;
+}
+
+static void test_tracepoint_event_rule_notification_filter(
+ enum lttng_domain_type domain_type)
+{
+ int i;
+ enum lttng_notification_channel_status nc_status;
+
+ struct lttng_condition *ctrl_condition = NULL, *condition = NULL;
+ struct lttng_action *ctrl_action = NULL, *action = NULL;
+ struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL;
+ const char *ctrl_trigger_name = "control_trigger";
+ const char *trigger_name = "trigger";
+ const char *pattern;
+ int ctrl_count = 0, count = 0;
+
+ if (domain_type == LTTNG_DOMAIN_UST) {
+ pattern = "tp:tptest";
+ } else {
+ pattern = "lttng_test_filter_event";
+ }
+
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
+
+ create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL,
+ 0, NULL, domain_type, NULL, &ctrl_condition,
+ &ctrl_action, &ctrl_trigger);
+
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, ctrl_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to tracepoint event rule condition");
+
+ /*
+ * Attach a filter expression to get notification only if the
+ * `intfield` is even.
+ */
+ create_tracepoint_event_rule_trigger(pattern, trigger_name,
+ "(intfield & 1) == 0", 0, NULL, domain_type, NULL,
+ &condition, &action, &trigger);
+
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to tracepoint event rule condition");
+
+ /*
+ * We registered 2 notifications triggers, one with a filter and one
+ * without (control). The one with a filter will only fired when the
+ * `intfield` is a multiple of 2. We should get two times as many
+ * control notifications as filter notifications.
+ */
+ resume_application();
+
+ /*
+ * Get 3 notifications. We should get 1 for the regular trigger (with
+ * the filter) and 2 from the control trigger. This works whatever
+ * the order we receive the notifications.
+ */
+ for (i = 0; i < 3; i++) {
+ const char *name;
+ struct lttng_notification *notification = get_next_notification(
+ notification_channel);
+ ok(notification, "Received notification");
+
+ /* Error */
+ if (notification == NULL) {
+ goto end;
}
- loop_ret = asprintf(&test_tuple_string, "session name %s, channel name %s, threshold ratio %s, threshold byte %s, domain type %s",
- session_name_set ? "set" : "unset",
- channel_name_set ? "set" : "unset",
- threshold_ratio_set ? "set" : "unset",
- threshold_byte_set ? "set" : "unset",
- domain_type_set? "set" : "unset");
- if (!test_tuple_string || loop_ret < 0) {
- loop_ret = 1;
- goto loop_end;
+ name = get_notification_trigger_name(notification);
+ if (name == NULL) {
+ lttng_notification_destroy(notification);
+ goto end;
}
- /* Create trigger */
- trigger = lttng_trigger_create(condition, action);
- if (!trigger) {
- loop_ret = 1;
- goto loop_end;
+ if (strcmp(ctrl_trigger_name, name) == 0) {
+ ctrl_count++;
+ } else if (strcmp(trigger_name, name) == 0) {
+ count++;
}
+ lttng_notification_destroy(notification);
+ }
+ ok(ctrl_count / 2 == count,
+ "Get twice as many control notif as of regular notif");
- loop_ret = lttng_register_trigger(trigger);
+end:
+ suspend_application();
+ lttng_unregister_trigger(trigger);
+ lttng_unregister_trigger(ctrl_trigger);
+ lttng_notification_channel_destroy(notification_channel);
+ lttng_trigger_destroy(trigger);
+ lttng_trigger_destroy(ctrl_trigger);
+ lttng_condition_destroy(condition);
+ lttng_condition_destroy(ctrl_condition);
+ lttng_action_destroy(action);
+ lttng_action_destroy(ctrl_action);
+ return;
+}
-loop_end:
- if (loop_ret == 1) {
- fail("Setup error occurred for tuple: %s", test_tuple_string);
- goto loop_cleanup;
- }
+static void test_tracepoint_event_rule_notification_exclusion(
+ enum lttng_domain_type domain_type)
+{
+ enum lttng_notification_channel_status nc_status;
+ struct lttng_condition *ctrl_condition = NULL, *condition = NULL;
+ struct lttng_action *ctrl_action = NULL, *action = NULL;
+ struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_trigger *ctrl_trigger = NULL, *trigger = NULL;
+ const char *ctrl_trigger_name = "control_exclusion_trigger";
+ const char *trigger_name = "exclusion_trigger";
+ const char *pattern = "tp:tptest*";
+ const char *exclusions[4] = {
+ "tp:tptest2", "tp:tptest3", "tp:tptest4", "tp:tptest5"};
+ int ctrl_count = 0, count = 0;
+ int i;
+
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
- /* This combination happens three times */
- if (session_name_set && channel_name_set
- && (threshold_ratio_set || threshold_byte_set)
- && domain_type_set) {
- ok(loop_ret == 0, "Trigger is registered: %s", test_tuple_string);
+ create_tracepoint_event_rule_trigger(pattern, ctrl_trigger_name, NULL,
+ 0, NULL, domain_type, NULL, &ctrl_condition,
+ &ctrl_action, &ctrl_trigger);
- /*
- * Test that a trigger cannot be registered
- * multiple time.
- */
- loop_ret = lttng_register_trigger(trigger);
- ok(loop_ret == -LTTNG_ERR_TRIGGER_EXISTS, "Re-register trigger fails as expected: %s", test_tuple_string);
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, ctrl_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to tracepoint event rule condition");
- /* Test that a trigger can be unregistered */
- loop_ret = lttng_unregister_trigger(trigger);
- ok(loop_ret == 0, "Unregister trigger: %s", test_tuple_string);
+ create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 4,
+ exclusions, domain_type, NULL, &condition, &action,
+ &trigger);
- /*
- * Test that unregistration of a non-previously
- * registered trigger fail.
- */
- loop_ret = lttng_unregister_trigger(trigger);
- ok(loop_ret == -LTTNG_ERR_TRIGGER_NOT_FOUND, "Unregister of a non-registered trigger fails as expected: %s", test_tuple_string);
- } else {
- ok(loop_ret == -LTTNG_ERR_INVALID_TRIGGER, "Trigger is invalid as expected and cannot be registered: %s", test_tuple_string);
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to tracepoint event rule condition");
+
+ /*
+ * We registered 2 notifications triggers, one with an exclusion and
+ * one without (control).
+ * - The trigger with an exclusion will fire once every iteration.
+ * - The trigger without an exclusion will fire 5 times every
+ * iteration.
+ *
+ * We should get 5 times as many notifications from the control
+ * trigger.
+ */
+ resume_application();
+
+ /*
+ * Get 6 notifications. We should get 1 for the regular trigger (with
+ * the exclusion) and 5 from the control trigger. This works whatever
+ * the order we receive the notifications.
+ */
+ for (i = 0; i < 6; i++) {
+ const char *name;
+ struct lttng_notification *notification = get_next_notification(
+ notification_channel);
+ ok(notification, "Received notification");
+
+ /* Error */
+ if (notification == NULL) {
+ goto end;
}
-loop_cleanup:
- free(test_tuple_string);
- lttng_trigger_destroy(trigger);
- lttng_condition_destroy(condition);
+ name = get_notification_trigger_name(notification);
+ if (name == NULL) {
+ lttng_notification_destroy(notification);
+ goto end;
+ }
+
+ if (strcmp(ctrl_trigger_name, name) == 0) {
+ ctrl_count++;
+ } else if (strcmp(trigger_name, name) == 0) {
+ count++;
+ }
+ lttng_notification_destroy(notification);
}
+ ok(ctrl_count / 5 == count,
+ "Got 5 times as many control notif as of regular notif");
end:
+ suspend_application();
+ lttng_unregister_trigger(trigger);
+ lttng_unregister_trigger(ctrl_trigger);
+ lttng_notification_channel_destroy(notification_channel);
+ lttng_trigger_destroy(trigger);
+ lttng_trigger_destroy(ctrl_trigger);
+ lttng_condition_destroy(condition);
+ lttng_condition_destroy(ctrl_condition);
lttng_action_destroy(action);
+ lttng_action_destroy(ctrl_action);
+ return;
}
-static
-void wait_data_pending(const char *session_name)
+static void test_kprobe_event_rule_notification(
+ enum lttng_domain_type domain_type)
{
- int ret;
+ enum lttng_notification_channel_status nc_status;
+ enum lttng_event_rule_status event_rule_status;
+ enum lttng_trigger_status trigger_status;
- do {
- ret = lttng_data_pending(session_name);
- assert(ret >= 0);
- } while (ret != 0);
+ struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_condition *condition = NULL;
+ struct lttng_event_rule *event_rule = NULL;
+ struct lttng_action *action = NULL;
+ struct lttng_trigger *trigger = NULL;
+ const char *trigger_name = "kprobe_trigger";
+ const char *symbol_name = "_do_fork";
+ int i, ret;
+
+ action = lttng_action_notify_create();
+ if (!action) {
+ fail("Setup error on action creation");
+ goto end;
+ }
+
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
+
+ event_rule = lttng_event_rule_kprobe_create();
+ ok(event_rule, "kprobe event rule object creation");
+
+ event_rule_status = lttng_event_rule_kprobe_set_source(
+ event_rule, symbol_name);
+ ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+ "Setting kprobe event rule source: %s", symbol_name);
+
+ event_rule_status = lttng_event_rule_kprobe_set_name(
+ event_rule, trigger_name);
+ ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+ "Setting kprobe event rule name: %s", trigger_name);
+
+ condition = lttng_condition_event_rule_create(event_rule);
+ ok(condition, "Condition event rule object creation");
+
+ /* Register the triggers for condition */
+ trigger = lttng_trigger_create(condition, action);
+ if (!trigger) {
+ fail("Setup error on trigger creation");
+ goto end;
+ }
+
+ trigger_status = lttng_trigger_set_name(trigger, trigger_name);
+ ok(trigger_status == LTTNG_TRIGGER_STATUS_OK,
+ "Setting name to trigger %s", trigger_name);
+
+ ret = lttng_register_trigger(trigger);
+ if (ret) {
+ fail("Setup error on trigger registration");
+ goto end;
+ }
+
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to tracepoint event rule condition");
+
+ resume_application();
+
+ for (i = 0; i < 3; i++) {
+ struct lttng_notification *notification = get_next_notification(
+ notification_channel);
+ ok(notification, "Received notification");
+
+ /* Error */
+ if (notification == NULL) {
+ goto end;
+ }
+
+ ret = validator_notification_trigger_name(notification, trigger_name);
+ lttng_notification_destroy(notification);
+ if (ret) {
+ goto end;
+ }
+ }
+
+end:
+ suspend_application();
+ lttng_notification_channel_destroy(notification_channel);
+ lttng_unregister_trigger(trigger);
+ lttng_trigger_destroy(trigger);
+ lttng_action_destroy(action);
+ lttng_condition_destroy(condition);
+ return;
}
-static
-void test_notification_channel(const char *session_name, const char *channel_name, const enum lttng_domain_type domain_type, const char **argv)
+static void test_uprobe_event_rule_notification(
+ enum lttng_domain_type domain_type,
+ const char *testapp_path,
+ const char *test_symbol_name)
{
- int ret = 0;
- enum lttng_condition_status condition_status;
enum lttng_notification_channel_status nc_status;
+ enum lttng_event_rule_status event_rule_status;
+ enum lttng_trigger_status trigger_status;
- struct lttng_action *action = NULL;
- struct lttng_notification *notification = NULL;
struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_userspace_probe_location *probe_location = NULL;
+ struct lttng_userspace_probe_location_lookup_method *lookup_method =
+ NULL;
+ struct lttng_condition *condition = NULL;
+ struct lttng_event_rule *event_rule = NULL;
+ struct lttng_action *action = NULL;
struct lttng_trigger *trigger = NULL;
+ const char *trigger_name = "uprobe_trigger";
+ int i, ret;
- struct lttng_condition *low_condition = NULL;
- struct lttng_condition *high_condition = NULL;
- struct lttng_condition *dummy_invalid_condition = NULL;
- struct lttng_condition *dummy_condition = NULL;
-
- double low_ratio = 0.0;
- double high_ratio = 0.99;
-
- /* Set-up */
action = lttng_action_notify_create();
if (!action) {
fail("Setup error on action creation");
goto end;
}
- /* Create a dummy, empty condition for later test */
- dummy_invalid_condition = lttng_condition_buffer_usage_low_create();
- if (!dummy_invalid_condition) {
- fail("Setup error on condition creation");
+ lookup_method = lttng_userspace_probe_location_lookup_method_function_elf_create();
+ if (!lookup_method) {
+ fail("Setup error on userspace probe lookup method creation");
goto end;
}
- /* Create a valid dummy condition with a ratio of 0.5 */
- dummy_condition = lttng_condition_buffer_usage_low_create();
- if (!dummy_condition) {
- fail("Setup error on dummy_condition creation");
+ probe_location = lttng_userspace_probe_location_function_create(
+ testapp_path, test_symbol_name, lookup_method);
+ if (!probe_location) {
+ fail("Setup error on userspace probe location creation");
goto end;
-
}
- condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
- dummy_condition, 0.5);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on condition creation");
+
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
+
+ event_rule = lttng_event_rule_uprobe_create();
+ ok(event_rule, "kprobe event rule object creation");
+
+ event_rule_status = lttng_event_rule_uprobe_set_location(
+ event_rule, probe_location);
+ ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+ "Setting uprobe event rule location");
+
+ event_rule_status = lttng_event_rule_uprobe_set_name(
+ event_rule, trigger_name);
+ ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+ "Setting uprobe event rule name: %s", trigger_name);
+
+ condition = lttng_condition_event_rule_create(event_rule);
+ ok(condition, "Condition event rule object creation");
+
+ /* Register the triggers for condition */
+ trigger = lttng_trigger_create(condition, action);
+ if (!trigger) {
+ fail("Setup error on trigger creation");
goto end;
}
- condition_status = lttng_condition_buffer_usage_set_session_name(
- dummy_condition, session_name);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on dummy_condition creation");
+ trigger_status = lttng_trigger_set_name(trigger, trigger_name);
+ ok(trigger_status == LTTNG_TRIGGER_STATUS_OK,
+ "Setting name to trigger %s", trigger_name);
+
+ ret = lttng_register_trigger(trigger);
+ if (ret) {
+ fail("Setup error on trigger registration");
goto end;
}
- condition_status = lttng_condition_buffer_usage_set_channel_name(
- dummy_condition, channel_name);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on dummy_condition creation");
- goto end;
+
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to tracepoint event rule condition");
+
+ resume_application();
+
+ for (i = 0; i < 3; i++) {
+ struct lttng_notification *notification = get_next_notification(
+ notification_channel);
+ ok(notification, "Received notification");
+
+ /* Error */
+ if (notification == NULL) {
+ goto end;
+ }
+
+ ret = validator_notification_trigger_name(notification, trigger_name);
+ lttng_notification_destroy(notification);
+ if (ret) {
+ goto end;
+ }
}
- condition_status = lttng_condition_buffer_usage_set_domain_type(
- dummy_condition, domain_type);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on dummy_condition creation");
+end:
+ suspend_application();
+ lttng_notification_channel_destroy(notification_channel);
+ lttng_unregister_trigger(trigger);
+ lttng_trigger_destroy(trigger);
+ lttng_action_destroy(action);
+ lttng_condition_destroy(condition);
+ return;
+}
+
+static void test_syscall_event_rule_notification(
+ enum lttng_domain_type domain_type)
+{
+ enum lttng_notification_channel_status nc_status;
+ enum lttng_event_rule_status event_rule_status;
+ enum lttng_trigger_status trigger_status;
+
+ struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_condition *condition = NULL;
+ struct lttng_event_rule *event_rule = NULL;
+ struct lttng_action *action = NULL;
+ struct lttng_trigger *trigger = NULL;
+ const char *trigger_name = "syscall_trigger";
+ const char *syscall_name = "openat";
+ int i, ret;
+
+ action = lttng_action_notify_create();
+ if (!action) {
+ fail("Setup error on action creation");
goto end;
}
- /* Register a low condition with a ratio */
- low_condition = lttng_condition_buffer_usage_low_create();
- if (!low_condition) {
- fail("Setup error on low_condition creation");
- goto end;
- }
- condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
- low_condition, low_ratio);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on low_condition creation");
- goto end;
- }
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
- condition_status = lttng_condition_buffer_usage_set_session_name(
- low_condition, session_name);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on low_condition creation");
- goto end;
- }
- condition_status = lttng_condition_buffer_usage_set_channel_name(
- low_condition, channel_name);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on low_condition creation");
- goto end;
- }
- condition_status = lttng_condition_buffer_usage_set_domain_type(
- low_condition, domain_type);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on low_condition creation");
- goto end;
+ event_rule = lttng_event_rule_syscall_create();
+ ok(event_rule, "syscall event rule object creation");
- }
+ event_rule_status = lttng_event_rule_syscall_set_pattern(
+ event_rule, syscall_name);
+ ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+ "Setting syscall event rule pattern: %s", syscall_name);
- /* Register a high condition with a ratio */
- high_condition = lttng_condition_buffer_usage_high_create();
- if (!high_condition) {
- fail("Setup error on high_condition creation");
- goto end;
- }
+ condition = lttng_condition_event_rule_create(event_rule);
+ ok(condition, "Condition event rule object creation");
- condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
- high_condition, high_ratio);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on high_condition creation");
+ /* Register the triggers for condition */
+ trigger = lttng_trigger_create(condition, action);
+ if (!trigger) {
+ fail("Setup error on trigger creation");
goto end;
}
- condition_status = lttng_condition_buffer_usage_set_session_name(
- high_condition, session_name);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on high_condition creation");
+ trigger_status = lttng_trigger_set_name(trigger, trigger_name);
+ ok(trigger_status == LTTNG_TRIGGER_STATUS_OK,
+ "Setting name to trigger %s", trigger_name);
+
+ ret = lttng_register_trigger(trigger);
+ if (ret) {
+ fail("Setup error on trigger registration");
goto end;
}
- condition_status = lttng_condition_buffer_usage_set_channel_name(
- high_condition, channel_name);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on high_condition creation");
- goto end;
+
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to tracepoint event rule condition");
+
+ resume_application();
+
+ for (i = 0; i < 3; i++) {
+ struct lttng_notification *notification = get_next_notification(
+ notification_channel);
+ ok(notification, "Received notification");
+
+ /* Error */
+ if (notification == NULL) {
+ goto end;
+ }
+
+ ret = validator_notification_trigger_name(notification, trigger_name);
+ lttng_notification_destroy(notification);
+ if (ret) {
+ goto end;
+ }
}
- condition_status = lttng_condition_buffer_usage_set_domain_type(
- high_condition, domain_type);
- if (condition_status != LTTNG_CONDITION_STATUS_OK) {
- fail("Setup error on high_condition creation");
+end:
+ suspend_application();
+ lttng_notification_channel_destroy(notification_channel);
+ lttng_unregister_trigger(trigger);
+ lttng_trigger_destroy(trigger);
+ lttng_action_destroy(action);
+ lttng_condition_destroy(condition);
+ return;
+}
+
+static void test_syscall_event_rule_notification_filter(
+ enum lttng_domain_type domain_type)
+{
+ enum lttng_notification_channel_status nc_status;
+ enum lttng_event_rule_status event_rule_status;
+ enum lttng_trigger_status trigger_status;
+
+ struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_condition *condition = NULL;
+ struct lttng_event_rule *event_rule = NULL;
+ struct lttng_action *action = NULL;
+ struct lttng_trigger *trigger = NULL;
+ const char *trigger_name = "syscall_trigger";
+ const char *syscall_name = "openat";
+ int i, ret;
+
+ action = lttng_action_notify_create();
+ if (!action) {
+ fail("Setup error on action creation");
goto end;
}
- /* Register the triggers for low and high condition */
- trigger = lttng_trigger_create(low_condition, action);
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
+
+ event_rule = lttng_event_rule_syscall_create();
+ ok(event_rule, "syscall event rule object creation");
+
+ event_rule_status = lttng_event_rule_syscall_set_pattern(
+ event_rule, syscall_name);
+ ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+ "Setting syscall event rule pattern: %s", syscall_name);
+
+ event_rule_status = lttng_event_rule_syscall_set_filter(
+ event_rule, "filename==\"/proc/cpuinfo\"");
+ ok(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK,
+ "Setting syscall event rule pattern: %s", syscall_name);
+
+ condition = lttng_condition_event_rule_create(event_rule);
+ ok(condition, "Condition event rule object creation");
+
+ /* Register the triggers for condition */
+ trigger = lttng_trigger_create(condition, action);
if (!trigger) {
- fail("Setup error on low trigger creation");
+ fail("Setup error on trigger creation");
goto end;
}
+ trigger_status = lttng_trigger_set_name(trigger, trigger_name);
+ ok(trigger_status == LTTNG_TRIGGER_STATUS_OK,
+ "Setting name to trigger %s", trigger_name);
+
ret = lttng_register_trigger(trigger);
if (ret) {
- fail("Setup error on low trigger registration");
+ fail("Setup error on trigger registration");
goto end;
}
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to tracepoint event rule condition");
+
+ resume_application();
+
+ for (i = 0; i < 3; i++) {
+ struct lttng_notification *notification = get_next_notification(
+ notification_channel);
+ ok(notification, "Received notification");
+
+ /* Error */
+ if (notification == NULL) {
+ goto end;
+ }
+
+ ret = validator_notification_trigger_name(notification, trigger_name);
+ lttng_notification_destroy(notification);
+ if (ret) {
+ goto end;
+ }
+ }
+
+end:
+ suspend_application();
+ lttng_unregister_trigger(trigger);
+ lttng_notification_channel_destroy(notification_channel);
lttng_trigger_destroy(trigger);
- trigger = NULL;
+ lttng_condition_destroy(condition);
+ return;
+}
- trigger = lttng_trigger_create(high_condition, action);
- if (!trigger) {
- fail("Setup error on high trigger creation");
- goto end;
+static int generate_capture_descr(struct lttng_condition *condition)
+{
+ int ret;
+ struct lttng_event_expr *expr = NULL;
+ unsigned int basic_field_size;
+ enum lttng_condition_status cond_status;
+
+ basic_field_size = sizeof(test_capture_base_fields) / sizeof(*test_capture_base_fields);
+ for (int i = 0; i < basic_field_size; i++) {
+
+ diag("Adding capture descriptor \"%s\"", test_capture_base_fields[i].field_name);
+
+ switch (test_capture_base_fields[i].field_type) {
+ case FIELD_TYPE_PAYLOAD:
+ expr = lttng_event_expr_event_payload_field_create(
+ test_capture_base_fields[i].field_name);
+ break;
+ case FIELD_TYPE_CONTEXT:
+ expr = lttng_event_expr_channel_context_field_create(
+ test_capture_base_fields[i].field_name);
+ break;
+ case FIELD_TYPE_ARRAY_FIELD:
+ {
+ int nb_matches;
+ unsigned int index;
+ char field_name[256];
+ struct lttng_event_expr *array_expr = NULL;
+ nb_matches = sscanf(test_capture_base_fields[i].field_name,
+ "%[^[][%u]", field_name, &index);
+ if (nb_matches != 2) {
+ ret = 1;
+ goto end;
+ }
+ array_expr = lttng_event_expr_event_payload_field_create(
+ field_name);
+
+ expr = lttng_event_expr_array_field_element_create(
+ array_expr, index);
+ break;
+ }
+ case FIELD_TYPE_APP_CONTEXT:
+ fail("Application context not tested yet.");
+ default:
+ ret = 1;
+ goto end;
+ }
+ if (expr == NULL) {
+ fail("Creating capture expression");
+ ret = -1;
+ goto end;
+ }
+ cond_status = lttng_condition_event_rule_append_capture_descriptor(
+ condition, expr);
+ if (cond_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Appending capture_descriptor");
+ ret = -1;
+ lttng_event_expr_destroy(expr);
+ goto end;
+ }
}
- ret = lttng_register_trigger(trigger);
- if (ret) {
- fail("Setup error on high trigger registration");
+ ret = 0;
+
+end:
+ return ret;
+}
+
+static int validator_notification_trigger_capture(
+ enum lttng_domain_type domain,
+ struct lttng_notification *notification,
+ const int iteration)
+{
+ int ret;
+ unsigned int capture_count, i;
+ enum lttng_evaluation_status evaluation_status;
+ enum lttng_event_field_value_status event_field_value_status;
+ const struct lttng_evaluation *evaluation;
+ const struct lttng_event_field_value *captured_fields;
+ bool at_least_one_error = false;
+
+ evaluation = lttng_notification_get_evaluation(notification);
+ if (evaluation == NULL) {
+ fail("lttng_notification_get_evaluation");
+ ret = 1;
goto end;
}
- /* Begin testing */
- notification_channel = lttng_notification_channel_create(lttng_session_daemon_notification_endpoint);
- ok(notification_channel, "Notification channel object creation");
- if (!notification_channel) {
+ /* TODO: it seems weird that lttng_evaluation_get_captured_values return
+ * INVALID if no capture were present. might be better to return
+ * something with more meaning. Another question is how we link the
+ * notion of capture and the descriptor from the perspective of a
+ * client. Is it really invalid to ask for captured value when there might
+ * not be any?
+ */
+ evaluation_status = lttng_evaluation_get_captured_values(evaluation, &captured_fields);
+ if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) {
+ diag("lttng_evaluation_get_captured_values");
+ ret = 1;
goto end;
}
- /* Basic error path check */
- nc_status = lttng_notification_channel_subscribe(NULL, NULL);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NULL");
-
- nc_status = lttng_notification_channel_subscribe(notification_channel, NULL);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NON-NULL, NULL");
+ event_field_value_status =
+ lttng_event_field_value_array_get_length(captured_fields,
+ &capture_count);
+ if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ assert(0 && "get count of captured field");
+ }
- nc_status = lttng_notification_channel_subscribe(NULL, low_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NON-NULL");
+ for (i = 0; i < capture_count; i++) {
+ const struct lttng_event_field_value *captured_field = NULL;
+ validate_cb validate;
+ bool expected;
- nc_status = lttng_notification_channel_subscribe(notification_channel, dummy_invalid_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Subscribing to an invalid condition");
+ diag("Validating capture \"%s\"", test_capture_base_fields[i].field_name);
+ event_field_value_status = lttng_event_field_value_array_get_element_at_index(captured_fields, i, &captured_field);
- nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_invalid_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Unsubscribing from an invalid condition");
+ switch(domain) {
+ case LTTNG_DOMAIN_UST:
+ expected = test_capture_base_fields[i].expected_ust;
+ break;
+ case LTTNG_DOMAIN_KERNEL:
+ expected = test_capture_base_fields[i].expected_kernel;
+ break;
+ default:
+ assert(0 && "Domain invalid for this test");
+ }
- nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, "Unsubscribing from a valid unknown condition");
+ if (!expected) {
+ ok(event_field_value_status == LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE, "No payload captured");
+ continue;
+ }
- /* Subscribe a valid low condition */
- nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition");
+ if (domain == LTTNG_DOMAIN_UST) {
+ validate = test_capture_base_fields[i].validate_ust;
+ } else {
+ validate = test_capture_base_fields[i].validate_kernel;
+ }
- /* Subscribe a valid high condition */
- nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition");
+ if (event_field_value_status != LTTNG_EVENT_FIELD_VALUE_STATUS_OK) {
+ const char *reason;
+ if (event_field_value_status ==
+ LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE) {
+ reason = "Expected a capture but it is unavailable";
+ } else {
+ reason = "lttng_event_field_value_array_get_element_at_index";
+ }
+ fail(reason);
+ ret = 1;
+ goto end;
+ }
+ diag("Captured field of type %s",
+ field_value_type_to_str(
+ lttng_event_field_value_get_type(captured_field)));
- nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done");
+ assert(validate);
+ ret = validate(captured_field, iteration);
+ if (ret) {
+ at_least_one_error = true;
+ }
+ }
- nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done");
+ ret = at_least_one_error;
- /* Wait for notification to happen */
- stop_consumer(argv);
- lttng_start_tracing(session_name);
+end:
+ return ret;
+}
- /* Wait for high notification */
- nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK
- && notification
- && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH,
- "High notification received after intermediary communication");
- lttng_notification_destroy(notification);
- notification = NULL;
+static void test_tracepoint_event_rule_notification_capture(
+ enum lttng_domain_type domain_type)
+{
+ enum lttng_notification_channel_status nc_status;
- suspend_application();
- lttng_stop_tracing_no_wait(session_name);
- resume_consumer(argv);
- wait_data_pending(session_name);
+ int i, ret;
+ struct lttng_action *action = NULL;
+ struct lttng_condition *condition = NULL;
+ struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_trigger *trigger = NULL;
+ const char *trigger_name = "my_precious";
+ const char *pattern;
- /*
- * Test that communication still work even if there is notification
- * waiting for consumption.
- */
+ if (domain_type == LTTNG_DOMAIN_UST) {
+ pattern = "tp:tptest";
+ } else {
+ pattern = "lttng_test_filter_event";
+ }
- nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe with pending notification");
+ create_tracepoint_event_rule_trigger(pattern, trigger_name, NULL, 0,
+ NULL, domain_type, generate_capture_descr, &condition,
+ &action, &trigger);
- nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "subscribe with pending notification");
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
- nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK
- && notification
- && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW,
- "Low notification received after intermediary communication");
- lttng_notification_destroy(notification);
- notification = NULL;
+ nc_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK,
+ "Subscribe to tracepoint event rule condition");
- /* Stop consumer to force a high notification */
- stop_consumer(argv);
resume_application();
- lttng_start_tracing(session_name);
-
- nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification &&
- lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH,
- "High notification received after intermediary communication");
- lttng_notification_destroy(notification);
- notification = NULL;
-
- suspend_application();
- lttng_stop_tracing_no_wait(session_name);
- resume_consumer(argv);
- wait_data_pending(session_name);
- nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification &&
- lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW,
- "Low notification received after re-subscription");
- lttng_notification_destroy(notification);
- notification = NULL;
+ /* Get 3 notifications */
+ for (i = 0; i < 3; i++) {
+ struct lttng_notification *notification = get_next_notification(
+ notification_channel);
+ ok(notification, "Received notification");
- stop_consumer(argv);
- resume_application();
- /* Stop consumer to force a high notification */
- lttng_start_tracing(session_name);
+ /* Error */
+ if (notification == NULL) {
+ goto end;
+ }
- nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification &&
- lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH,
- "High notification");
- lttng_notification_destroy(notification);
- notification = NULL;
+ ret = validator_notification_trigger_name(notification, trigger_name);
+ if (ret) {
+ lttng_notification_destroy(notification);
+ goto end;
+ }
- /* Resume consumer to allow event consumption */
- suspend_application();
- lttng_stop_tracing_no_wait(session_name);
- resume_consumer(argv);
- wait_data_pending(session_name);
+ ret = validator_notification_trigger_capture(domain_type, notification, i);
+ if (ret) {
+ lttng_notification_destroy(notification);
+ goto end;
+ }
- nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe low condition with pending notification");
- nc_status = lttng_notification_channel_unsubscribe(notification_channel, high_condition);
- ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe high condition with pending notification");
+ lttng_notification_destroy(notification);
+ }
end:
+ suspend_application();
lttng_notification_channel_destroy(notification_channel);
+ lttng_unregister_trigger(trigger);
lttng_trigger_destroy(trigger);
lttng_action_destroy(action);
- lttng_condition_destroy(low_condition);
- lttng_condition_destroy(high_condition);
- lttng_condition_destroy(dummy_invalid_condition);
- lttng_condition_destroy(dummy_condition);
+ lttng_condition_destroy(condition);
+ return;
}
int main(int argc, const char *argv[])
{
- const char *session_name = NULL;
- const char *channel_name = NULL;
+ int test_scenario;
const char *domain_type_string = NULL;
enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
- plan_tests(NUM_TESTS);
-
- /* Argument 6 and upward are named pipe location for consumerd control */
- named_pipe_args_start = 6;
-
- if (argc < 7) {
- fail("Missing parameter for tests to run %d", argc);
+ if (argc < 5) {
+ fail("Missing test scenario, domain type, pid, or application state file argument(s)");
goto error;
}
- nb_args = argc;
-
- domain_type_string = argv[1];
- session_name = argv[2];
- channel_name = argv[3];
- app_pid = (pid_t) atoi(argv[4]);
- app_state_file = argv[5];
+ test_scenario = atoi(argv[1]);
+ domain_type_string = argv[2];
+ app_pid = (pid_t) atoi(argv[3]);
+ app_state_file = argv[4];
if (!strcmp("LTTNG_DOMAIN_UST", domain_type_string)) {
domain_type = LTTNG_DOMAIN_UST;
goto error;
}
- diag("Test trigger for domain %s with buffer_usage_low condition", domain_type_string);
- test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW);
- diag("Test trigger for domain %s with buffer_usage_high condition", domain_type_string);
- test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH);
+ /*
+ * Test cases are responsible for resuming the app when needed
+ * and making sure it's suspended when returning.
+ */
+ suspend_application();
+
+ switch (test_scenario) {
+ case 1:
+ {
+ plan_tests(44);
+
+ /* Test cases that need gen-ust-event testapp. */
+ diag("Test basic notification error paths for domain %s",
+ domain_type_string);
+ test_invalid_channel_subscription(domain_type);
+
+ diag("Test tracepoint event rule notifications for domain %s",
+ domain_type_string);
+ test_tracepoint_event_rule_notification(domain_type);
+
+ diag("Test tracepoint event rule notifications with filter for domain %s",
+ domain_type_string);
+ test_tracepoint_event_rule_notification_filter(domain_type);
+ break;
+ }
+ case 2:
+ {
+ const char *session_name, *channel_name;
+ /* Test cases that need a tracing session enabled. */
+ plan_tests(99);
+
+ /*
+ * Argument 7 and upward are named pipe location for consumerd
+ * control.
+ */
+ named_pipe_args_start = 7;
+
+ if (argc < 8) {
+ fail("Missing parameter for tests to run %d", argc);
+ goto error;
+ }
+
+ nb_args = argc;
+
+ session_name = argv[5];
+ channel_name = argv[6];
+
+ test_subscription_twice(session_name, channel_name,
+ domain_type);
+
+ diag("Test trigger for domain %s with buffer_usage_low condition",
+ domain_type_string);
+ test_triggers_buffer_usage_condition(session_name, channel_name,
+ domain_type,
+ LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW);
+
+ diag("Test trigger for domain %s with buffer_usage_high condition",
+ domain_type_string);
+ test_triggers_buffer_usage_condition(session_name, channel_name,
+ domain_type,
+ LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH);
+
+ diag("Test buffer usage notification channel api for domain %s",
+ domain_type_string);
+ test_buffer_usage_notification_channel(session_name, channel_name,
+ domain_type, argv);
+ break;
+ }
+ case 3:
+ {
+ /*
+ * Test cases that need a test app with more than one event
+ * type.
+ */
+ plan_tests(25);
+
+ /*
+ * At the moment, the only test case of this scenario is
+ * exclusion which is only supported by UST
+ */
+ assert(domain_type == LTTNG_DOMAIN_UST);
+ diag("Test tracepoint event rule notifications with exclusion for domain %s",
+ domain_type_string);
+ test_tracepoint_event_rule_notification_exclusion(domain_type);
+
+ break;
+ }
+ case 4:
+ {
+ plan_tests(13);
+ /* Test cases that need the kernel tracer. */
+ assert(domain_type == LTTNG_DOMAIN_KERNEL);
+
+ diag("Test kprobe event rule notifications for domain %s",
+ domain_type_string);
+
+ test_kprobe_event_rule_notification(domain_type);
+
+ break;
+ }
+ case 5:
+ {
+ plan_tests(25);
+ /* Test cases that need the kernel tracer. */
+ assert(domain_type == LTTNG_DOMAIN_KERNEL);
+
+ diag("Test syscall event rule notifications for domain %s",
+ domain_type_string);
+
+ test_syscall_event_rule_notification(domain_type);
+
+ diag("Test syscall filtering event rule notifications for domain %s",
+ domain_type_string);
+
+ test_syscall_event_rule_notification_filter(domain_type);
+
+ break;
+ }
+ case 6:
+ {
+ const char *testapp_path, *test_symbol_name;
+
+ plan_tests(13);
+
+ if (argc < 7) {
+ fail("Missing parameter for tests to run %d", argc);
+ goto error;
+ }
+
+ testapp_path = argv[5];
+ test_symbol_name = argv[6];
+ /* Test cases that need the kernel tracer. */
+ assert(domain_type == LTTNG_DOMAIN_KERNEL);
+
+ diag("Test userspace-probe event rule notifications for domain %s",
+ domain_type_string);
+
+ test_uprobe_event_rule_notification(
+ domain_type, testapp_path, test_symbol_name);
+
+ break;
+ }
+ case 7:
+ {
+ switch(domain_type) {
+ case LTTNG_DOMAIN_UST:
+ plan_tests(222);
+ break;
+ case LTTNG_DOMAIN_KERNEL:
+ plan_tests(216);
+ break;
+ default:
+ assert(0);
+ }
+
+ diag("Test tracepoint event rule notification captures for domain %s",
+ domain_type_string);
+ test_tracepoint_event_rule_notification_capture(domain_type);
+
+ break;
+ }
+
+ default:
+ abort();
+ }
- diag("Test notification channel api for domain %s", domain_type_string);
- test_notification_channel(session_name, channel_name, domain_type, argv);
error:
return exit_status();
}
+++ /dev/null
-#!/bin/bash
-#
-# Copyright (C) 2017 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
-#
-# SPDX-License-Identifier: LGPL-2.1-only
-
-CURDIR=$(dirname $0)/
-TESTDIR=$CURDIR/../../../
-
-TMPDIR=$(mktemp -d)
-
-#This is needed since the testpoint create a pipe with the consumerd type suffixed
-TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
-TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
-TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so)
-
-
-TESTAPP_PATH="$TESTDIR/utils/testapp"
-TESTAPP_NAME="gen-ust-events"
-TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
-TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
-
-NR_ITER=-1
-NR_USEC_WAIT=5
-
-SESSION_NAME="my_session"
-CHANNEL_NAME="my_channel"
-
-TRACE_PATH=$(mktemp -d)
-PAGE_SIZE=$(getconf PAGE_SIZE)
-
-DIR=$(readlink -f $TESTDIR)
-NUM_TESTS=104
-
-source $TESTDIR/utils/utils.sh
-
-function kernel_event_generator_toggle_state
-{
- kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
-
-}
-function kernel_event_generator
-{
- state_file=$1
- kernel_event_generator_suspended=0
- trap kernel_event_generator_toggle_state SIGUSR1
-
- while (true); do
- if [[ $kernel_event_generator_suspended -eq "1" ]]; then
- touch $state_file
- sleep 0.5
- else
- if [[ -f $state_file ]]; then
- rm $state_file 2> /dev/null
- fi
- echo -n "1000" > /proc/lttng-test-filter-event 2> /dev/null
- fi
- done
-}
-
-function kernel_test
-{
- local consumerd_pipe=()
- local event_name="lttng_test_filter_event"
-
- modprobe lttng-test
-
- LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}"
- start_lttng_sessiond_notap
-
- create_lttng_session_notap $SESSION_NAME $TRACE_PATH
-
- lttng_enable_kernel_channel_notap $SESSION_NAME $CHANNEL_NAME --subbuf-size=$PAGE_SIZE
- enable_kernel_lttng_event_notap $SESSION_NAME $event_name $CHANNEL_NAME
-
- #This is needed since the testpoint create a pipe with the consumer type suffixed
- for f in "$TESTPOINT_BASE_PATH"*; do
- consumerd_pipe+=("$f")
- done
-
- kernel_event_generator $TESTAPP_STATE_PATH &
- APP_PID=$!
-
- $CURDIR/notification LTTNG_DOMAIN_KERNEL $SESSION_NAME $CHANNEL_NAME $APP_PID $TESTAPP_STATE_PATH ${consumerd_pipe[@]}
-
- destroy_lttng_session_notap $SESSION_NAME
- stop_lttng_sessiond_notap
-
- kill -9 $APP_PID
- wait $APP_PID 2> /dev/null
-
-
- rmmod lttng-test
-
- rm -rf ${consumerd_pipe[@]} 2> /dev/null
-}
-
-if [ "$(id -u)" == "0" ]; then
- validate_lttng_modules_present
- kernel_test
-else
- # Kernel tests are skipped.
- plan_tests $NUM_TESTS
- skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
-fi
-
-# Just in case cleanup
-rm -rf $TRACE_PATH
-rm -rf $TMPDIR
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname "$0")/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+TESTPOINT=$(readlink -f "${CURDIR}/.libs/libpause_consumer.so")
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_buffer_usage_notification
+{
+ local event_name="lttng_test_filter_event"
+ local trace_path
+ local page_size
+ local consumerd_pipe=()
+
+ trace_path=$(mktemp -d)
+ page_size=$(getconf PAGE_SIZE)
+
+ create_lttng_session_notap $SESSION_NAME "$trace_path"
+
+ lttng_enable_kernel_channel_notap $SESSION_NAME $CHANNEL_NAME \
+ --subbuf-size="$page_size"
+ enable_kernel_lttng_event_notap $SESSION_NAME $event_name $CHANNEL_NAME
+
+ kernel_event_generator generate_filter_events "$TESTAPP_STATE_PATH" &
+ APP_PID=$!
+
+ # This is needed since the testpoint create a pipe with the consumer
+ # type suffixed.
+ for f in "$TESTPOINT_BASE_PATH"*; do
+ consumerd_pipe+=("$f")
+ done
+
+ "$CURDIR/notification" 2 LTTNG_DOMAIN_KERNEL $APP_PID "$TESTAPP_STATE_PATH" \
+ $SESSION_NAME $CHANNEL_NAME "${consumerd_pipe[@]}"
+
+ destroy_lttng_session_notap $SESSION_NAME
+
+ kill -SIGUSR2 $APP_PID
+ wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+
+ validate_lttng_modules_present
+
+
+ modprobe lttng-test
+
+ # Used on sessiond launch.
+ LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 \
+ CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} \
+ LD_PRELOAD=${TESTPOINT}"
+ start_lttng_sessiond_notap
+
+ test_buffer_usage_notification
+
+ stop_lttng_sessiond_notap
+ rmmod lttng-test
+
+ rm -rf "${consumerd_pipe[@]}" 2> /dev/null
+else
+ # Kernel tests are skipped.
+ plan_tests $NUM_TESTS
+ skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+rm -rf "$TMPDIR"
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname "$0")/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_basic_error_path
+{
+ kernel_event_generator_run_once_per_transition generate_filter_events \
+ "$TESTAPP_STATE_PATH" 10 &
+ APP_PID=$!
+
+ "$CURDIR/notification" 7 LTTNG_DOMAIN_KERNEL $APP_PID \
+ "$TESTAPP_STATE_PATH"
+
+ kill -SIGUSR2 $APP_PID
+ wait $APP_PID 2> /dev/null
+}
+
+
+if [ "$(id -u)" == "0" ]; then
+ validate_lttng_modules_present
+
+ modprobe lttng-test
+
+ start_lttng_sessiond_notap
+
+ test_basic_error_path
+
+ stop_lttng_sessiond_notap
+ rmmod lttng-test
+
+else
+ # Kernel tests are skipped.
+ plan_tests $NUM_TESTS
+ skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+# Just in case cleanup
+rm -rf "$TMPDIR"
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname "$0")/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_basic_error_path
+{
+ kernel_event_generator_run_once_per_transition generate_filter_events \
+ "$TESTAPP_STATE_PATH" 10 &
+ APP_PID=$!
+
+ "$CURDIR/notification" 1 LTTNG_DOMAIN_KERNEL $APP_PID \
+ "$TESTAPP_STATE_PATH"
+
+ kill -SIGUSR2 $APP_PID
+ wait $APP_PID 2> /dev/null
+}
+
+
+if [ "$(id -u)" == "0" ]; then
+ validate_lttng_modules_present
+
+ modprobe lttng-test
+
+ start_lttng_sessiond_notap
+
+ test_basic_error_path
+
+ stop_lttng_sessiond_notap
+ rmmod lttng-test
+
+else
+ # Kernel tests are skipped.
+ plan_tests $NUM_TESTS
+ skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+# Just in case cleanup
+rm -rf "$TMPDIR"
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname "$0")/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_kernel_instrumentation_notification
+{
+ kernel_event_generator generate_filter_events "$TESTAPP_STATE_PATH" &
+ APP_PID=$!
+
+ "$CURDIR/notification" 4 LTTNG_DOMAIN_KERNEL $APP_PID \
+ "$TESTAPP_STATE_PATH"
+
+ kill -SIGUSR2 $APP_PID
+ wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+ validate_lttng_modules_present
+
+ modprobe lttng-test
+
+ start_lttng_sessiond_notap
+
+ test_kernel_instrumentation_notification
+
+ stop_lttng_sessiond_notap
+ rmmod lttng-test
+
+else
+ # Kernel tests are skipped.
+ plan_tests $NUM_TESTS
+ skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+rm -rf "$TMPDIR"
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname "$0")/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_kernel_syscall_notification
+{
+ kernel_event_generator_run_once_per_transition generate_syscalls \
+ "$TESTAPP_STATE_PATH" 10 /proc/cpuinfo /proc/cmdline &
+ APP_PID=$!
+
+ # Pass the syscall_generator_file for filtering
+ "$CURDIR/notification" 5 LTTNG_DOMAIN_KERNEL $APP_PID \
+ "$TESTAPP_STATE_PATH"
+
+
+ kill -SIGUSR2 $APP_PID
+ wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+ validate_lttng_modules_present
+
+ start_lttng_sessiond_notap
+
+ test_kernel_syscall_notification
+
+ stop_lttng_sessiond_notap
+
+else
+ # Kernel tests are skipped.
+ plan_tests $NUM_TESTS
+ skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+# Just in case cleanup
+rm -rf "$TMPDIR"
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname "$0")/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NUM_TESTS=104
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_kernel_userspace_probe_notification
+{
+ kernel_event_generator_run_once_per_transition userspace_probe_testapp "$TESTAPP_STATE_PATH" 10 &
+ APP_PID=$!
+
+ "$CURDIR/notification" 6 LTTNG_DOMAIN_KERNEL \
+ $APP_PID "$TESTAPP_STATE_PATH" \
+ "$USERSPACE_PROBE_ELF_TESTAPP_BIN" "test_function"
+
+ kill -SIGUSR2 $APP_PID
+ wait $APP_PID 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+ validate_lttng_modules_present
+
+ start_lttng_sessiond_notap
+
+ test_kernel_userspace_probe_notification
+
+ stop_lttng_sessiond_notap
+else
+ # Kernel tests are skipped.
+ plan_tests $NUM_TESTS
+ skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+# Just in case cleanup
+rm -rf "$TMPDIR"
NUM_TESTS=$(($NUM_TEST_UST + $NUM_TEST_KERNEL))
source $TESTDIR/utils/utils.sh
+source $CURDIR/util_event_generator.sh
consumerd_pipe=()
file_sync_after_first_event=$(mktemp -u)
app_pids=()
-function kernel_event_generator_toggle_state
-{
- kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
-
-}
-function kernel_event_generator
-{
- state_file=$1
- kernel_event_generator_suspended=0
- trap kernel_event_generator_toggle_state SIGUSR1
-
- while (true); do
- if [[ $kernel_event_generator_suspended -eq "1" ]]; then
- touch $state_file
- sleep 0.5
- else
- if [[ -f $state_file ]]; then
- rm $state_file 2> /dev/null
- fi
- echo -n "1000" > /proc/lttng-test-filter-event
- fi
- done
-}
-
-function ust_event_generator_toggle_state
-{
- ust_event_generator_suspended=$((ust_event_generator_suspended==0))
-
-}
-function ust_event_generator
-{
- state_file=$1
- ust_event_generator_suspended=0
- trap ust_event_generator_toggle_state SIGUSR1
- trap "exit" SIGTERM SIGINT
- while (true); do
- if [[ $ust_event_generator_suspended -eq "1" ]]; then
- touch $state_file
- sleep 0.5
- else
- if [[ -f $state_file ]]; then
- rm $state_file 2> /dev/null
- fi
- taskset -c 0 $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1
- fi
- done
-}
-
function start_client {
local pid=-1
local output_file=$1
print_errors $output_dir "${high_output_file_pattern}"
fi
- rm -rf $output_dir
destroy_lttng_session_ok $SESSION_NAME
stop_lttng_sessiond
for pipe in "${consumerd_pipe[@]}"; do
rm -rf "${pipe}"
done
+
+ rm -rf $output_dir
}
function test_multi_app_ust ()
{
diag "Multi client app UST notification"
- ust_event_generator $TESTAPP_STATE_FILE &
+ ust_event_generator "$TESTAPP_BIN" "$TESTAPP_STATE_FILE" &
local generator_pid=$!
test_multi_app ust $generator_pid
- kill -s SIGTERM $generator_pid 2> /dev/null
+ kill -s SIGUSR2 $generator_pid 2> /dev/null
wait $generator_pid 2> /dev/null
rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
}
diag "Multi client app kernel notification"
modprobe lttng-test
- kernel_event_generator $TESTAPP_STATE_FILE &
+ kernel_event_generator generate_filter_events $TESTAPP_STATE_FILE &
local generator_pid=$!
test_multi_app kernel $generator_pid
- kill -s SIGTERM $generator_pid 2> /dev/null
+ kill -s SIGUSR2 $generator_pid 2> /dev/null
wait $generator_pid 2> /dev/null
rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
diag "On register notification UST"
# Start app in infinite loop
- ust_event_generator $TESTAPP_STATE_FILE &
+ ust_event_generator "$TESTAPP_BIN" "$TESTAPP_STATE_FILE" &
local generator_pid=$!
test_on_register_evaluation ust $generator_pid
- kill -s SIGTERM $generator_pid 2> /dev/null
+ kill -s SIGUSR2 $generator_pid 2> /dev/null
wait $generator_pid 2> /dev/null
rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
modprobe lttng-test
- kernel_event_generator $TESTAPP_STATE_FILE &
+ kernel_event_generator generate_filter_events $TESTAPP_STATE_FILE &
local generator_pid=$!
test_on_register_evaluation kernel $generator_pid
- kill -s SIGTERM $generator_pid 2> /dev/null
+ kill -s SIGUSR2 $generator_pid 2> /dev/null
wait $generator_pid 2> /dev/null
rm -rf ${TESTAPP_STATE_FILE} 2> /dev/null
print_errors "${high_output_file_pattern}"
fi
- rm -rf $output_dir
destroy_lttng_session_ok $SESSION_NAME
stop_lttng_sessiond
- kill -s SIGTERM $generator_pid 2> /dev/null
+ kill -s SIGUSR2 $generator_pid 2> /dev/null
wait $generator_pid 2> /dev/null
for pipe in "${consumerd_pipe[@]}"; do
rm -rf "${pipe}"
done
+ rm -rf "$output_dir"
}
+++ /dev/null
-#!/bin/bash
-#
-# Copyright (C) 2017 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
-#
-# SPDX-License-Identifier: LGPL-2.1-only
-
-CURDIR=$(dirname $0)/
-TESTDIR=$CURDIR/../../../
-
-TMPDIR=$(mktemp -d)
-
-#This is needed since the testpoint create a pipe with the consumerd type suffixed
-TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
-TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
-TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so)
-
-
-TESTAPP_PATH="$TESTDIR/utils/testapp"
-TESTAPP_NAME="gen-ust-events"
-TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
-TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
-
-NR_ITER=1000
-NR_USEC_WAIT=5
-
-SESSION_NAME="my_session"
-CHANNEL_NAME="my_channel"
-
-TRACE_PATH=$(mktemp -d)
-PAGE_SIZE=$(getconf PAGE_SIZE)
-
-DIR=$(readlink -f $TESTDIR)
-
-
-source $TESTDIR/utils/utils.sh
-
-function ust_event_generator_toggle_state
-{
- ust_event_generator_suspended=$((ust_event_generator_suspended==0))
-
-}
-function ust_event_generator
-{
- state_file=$1
- ust_event_generator_suspended=0
- trap ust_event_generator_toggle_state SIGUSR1
-
- while (true); do
- if [[ $ust_event_generator_suspended -eq "1" ]]; then
- touch $state_file
- sleep 0.5
- else
- if [[ -f $state_file ]]; then
- rm -rf $state_file 2> /dev/null
- fi
- taskset -c 0 $TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1
- fi
- done
-}
-
-consumerd_pipe=()
-file_sync_after_first_event=$(mktemp -u)
-event_name="tp:tptest"
-
-LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}"
-start_lttng_sessiond_notap
-
-create_lttng_session_notap $SESSION_NAME $TRACE_PATH
-
-enable_ust_lttng_channel_notap $SESSION_NAME $CHANNEL_NAME --subbuf-size=$PAGE_SIZE
-enable_ust_lttng_event_notap $SESSION_NAME $event_name $CHANNEL_NAME
-
-#This is needed since the testpoint create a pipe with the consumer type suffixed
-for f in "$TESTPOINT_BASE_PATH"*; do
- consumerd_pipe+=("$f")
-done
-
-
-ust_event_generator $TESTAPP_STATE_PATH &
-APP_PID=$!
-
-$CURDIR/notification LTTNG_DOMAIN_UST $SESSION_NAME $CHANNEL_NAME $APP_PID $TESTAPP_STATE_PATH ${consumerd_pipe[@]}
-
-destroy_lttng_session_notap $SESSION_NAME
-stop_lttng_sessiond_notap
-
-# On ungraceful kill the app is cleaned up via the full_cleanup call
-# Suppress kill message
-kill -9 $APP_PID
-wait $APP_PID 2> /dev/null
-
-rm -rf $TMPDIR
-
-# Just in case cleanup
-rm -rf $TRACE_PATH
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname "$0")/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+TESTPOINT=$(readlink -f "${CURDIR}/.libs/libpause_consumer.so")
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+
+GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events"
+GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME"
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_buffer_usage_notification
+{
+ consumerd_pipe=()
+ event_name="tp:tptest"
+
+ create_lttng_session_notap $SESSION_NAME "$TRACE_PATH"
+
+ enable_ust_lttng_channel_notap $SESSION_NAME $CHANNEL_NAME --subbuf-size="$PAGE_SIZE"
+ enable_ust_lttng_event_notap $SESSION_NAME $event_name $CHANNEL_NAME
+
+ # This is needed since the testpoint create a pipe with the consumer type suffixed
+ for f in "$TESTPOINT_BASE_PATH"*; do
+ consumerd_pipe+=("$f")
+ done
+
+ ust_event_generator "$GEN_UST_EVENTS_TESTAPP_BIN" "$TESTAPP_STATE_PATH" &
+ APP_PID=$!
+
+ "$CURDIR/notification" 2 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH" \
+ $SESSION_NAME $CHANNEL_NAME "${consumerd_pipe[@]}"
+
+ destroy_lttng_session_notap $SESSION_NAME
+
+ # On ungraceful kill the app is cleaned up via the full_cleanup call
+ # Suppress kill message
+ kill -SIGUSR2 $APP_PID
+ wait $APP_PID 2> /dev/null
+
+ # Just in case cleanup
+ rm -rf "$TRACE_PATH"
+}
+
+LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}"
+start_lttng_sessiond_notap
+
+test_buffer_usage_notification
+
+stop_lttng_sessiond_notap
+
+rm -rf "$TMPDIR"
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname "$0")/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+
+GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events"
+GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME"
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_basic_error_path
+{
+ ust_event_generator_run_once_per_transition \
+ "$GEN_UST_EVENTS_TESTAPP_BIN" "$TESTAPP_STATE_PATH" 3 10 &
+ APP_PID=$!
+
+ "$CURDIR/notification" 7 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH"
+
+ kill -SIGUSR2 $APP_PID
+ wait $APP_PID 2> /dev/null
+}
+
+start_lttng_sessiond_notap
+
+test_basic_error_path
+
+stop_lttng_sessiond_notap
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname "$0")/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+
+GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events"
+GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME"
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_basic_error_path
+{
+ ust_event_generator_run_once_per_transition "$GEN_UST_EVENTS_TESTAPP_BIN" "$TESTAPP_STATE_PATH" 5 5 &
+ APP_PID=$!
+
+ "$CURDIR/notification" 1 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH"
+
+ kill -SIGUSR2 $APP_PID
+ wait $APP_PID 2> /dev/null
+}
+
+start_lttng_sessiond_notap
+
+test_basic_error_path
+
+stop_lttng_sessiond_notap
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+CURDIR=$(dirname "$0")/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+
+GEN_UST_NEVENTS_TESTAPP_NAME="gen-ust-nevents"
+GEN_UST_NEVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_NEVENTS_TESTAPP_NAME/$GEN_UST_NEVENTS_TESTAPP_NAME"
+
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+# shellcheck source=./util_event_generator.sh
+source "$CURDIR/util_event_generator.sh"
+
+function test_event_rule_condition_exclusion_notification
+{
+ ust_event_generator_run_once_per_transition "$GEN_UST_NEVENTS_TESTAPP_BIN" "$TESTAPP_STATE_PATH" 5 5 &
+ APP_PID=$!
+
+ "$CURDIR/notification" 3 LTTNG_DOMAIN_UST $APP_PID "$TESTAPP_STATE_PATH"
+
+ kill -SIGUSR2 $APP_PID
+ wait $APP_PID 2> /dev/null
+}
+
+start_lttng_sessiond_notap
+
+test_event_rule_condition_exclusion_notification
+
+stop_lttng_sessiond_notap
+
+rm -rf "$TMPDIR"
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2020 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+GENERATOR_CURDIR=$(dirname "$0")/
+GENERATOR_TESTDIR=$GENERATOR_CURDIR/../../../
+TESTAPP_PATH=${TESTAPP_PATH:-"$GENERATOR_TESTDIR/utils/testapp"}
+
+SYSCALL_TESTAPP_NAME=${SYSCALL_TESTAPP_NAME:-"gen-syscall-events"}
+SYSCALL_TESTAPP_BIN=${SYSCALL_TESTAPP_BIN:-"$TESTAPP_PATH/$SYSCALL_TESTAPP_NAME/$SYSCALL_TESTAPP_NAME"}
+
+USERSPACE_PROBE_ELF_TESTAPP_NAME=${USERSPACE_PROBE_ELF_TESTAPP_NAME:-"userspace-probe-elf-binary"}
+USERSPACE_PROBE_ELF_TESTAPP_BIN=${USERSPACE_PROBE_ELF_TESTAPP_BIN:-"$TESTAPP_PATH/$USERSPACE_PROBE_ELF_TESTAPP_NAME/.libs/$USERSPACE_PROBE_ELF_TESTAPP_NAME"}
+
+function generate_filter_events
+{
+ local nr=$1
+ /bin/echo -n "$nr" > /proc/lttng-test-filter-event 2> /dev/null
+}
+
+function generate_syscalls
+{
+ local nr=$1
+ shift
+
+ for i in $(seq 1 "$nr"); do
+ # Pass /dev/null so to generate the syscall right away.
+ $SYSCALL_TESTAPP_BIN /dev/null "$@"
+ done
+}
+
+function userspace_probe_testapp
+{
+ local nr=$1
+ shift
+
+ for i in $(seq 1 "$nr"); do
+ $USERSPACE_PROBE_ELF_TESTAPP_BIN "$@"
+ done
+}
+
+function ust_event_generator_toggle_state
+{
+ ust_event_generator_suspended=$((ust_event_generator_suspended==0))
+}
+
+function generator_quit
+{
+ generator_quit=0
+}
+
+# Note: Only one generator can be used at a time per domain type
+function ust_event_generator_run_once_per_transition
+{
+ # Used by the signal trap
+ ust_event_generator_suspended=0
+ # Used by the signal trap for SIGUSR2 to end the generator
+ generator_quit=1
+
+ local test_app=$1
+ local state_file=$2
+ local nr_iter=$3
+ local nr_usec_wait=$4
+ local run=false
+
+ # Pass any of the remaining arguments to the generator.
+ shift 4
+
+ trap ust_event_generator_toggle_state SIGUSR1
+ trap generator_quit SIGUSR2
+
+ while [ $generator_quit -ne 0 ]; do
+ if [[ $ust_event_generator_suspended -eq "1" ]]; then
+ touch "$state_file"
+ # Reset the "run" state
+ run=true
+ sleep 0.5
+ elif [ "$run" = true ]; then
+ taskset -c 0 "$test_app" -i "$nr_iter" -w "$nr_usec_wait" "$@"> /dev/null 2>&1
+ run=false;
+ if [[ -f $state_file ]]; then
+ rm -rf "$state_file" 2> /dev/null
+ fi
+ else
+ # Wait for a "suspend" to reset the run state
+ sleep 0.1
+ fi
+ done
+}
+
+# Note: Only one generator can be used at a time per domain type
+function ust_event_generator
+{
+ # Used by the signal trap
+ ust_event_generator_suspended=0
+ # Used by the signal trap for SIGUSR2 to end the generator
+ generator_quit=1
+
+ local test_app=$1
+ local state_file=$2
+ local nr_iter=1000
+ local nr_usec_wait=5
+
+ # Pass any of the remaining arguments to the generator.
+ shift 2
+
+ trap ust_event_generator_toggle_state SIGUSR1
+ trap generator_quit SIGUSR2
+
+ while [ $generator_quit -ne 0 ]; do
+ if [[ $ust_event_generator_suspended -eq "1" ]]; then
+ touch "$state_file"
+ # Reset the "run" state
+ sleep 0.5
+ else
+ taskset -c 0 "$test_app" -i $nr_iter -w $nr_usec_wait "$@" > /dev/null 2>&1
+ if [[ -f $state_file ]]; then
+ rm -rf "$state_file" 2> /dev/null
+ fi
+ fi
+ done
+}
+
+function kernel_event_generator_toggle_state
+{
+ kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
+
+}
+
+function kernel_event_generator_run_once_per_transition
+{
+ # Used by the signal trap
+ kernel_event_generator_suspended=0
+ # Used by the signal trap for SIGUSR2 to end the generator
+ generator_quit=1
+
+ local generator=$1
+ local state_file=$2
+ local nr_iter=$3
+
+ # Pass any of the remaining arguments to the generator.
+ shift 3
+
+ local run=false
+ trap kernel_event_generator_toggle_state SIGUSR1
+ trap generator_quit SIGUSR2
+
+ while [ $generator_quit -ne 0 ]; do
+ if [[ $kernel_event_generator_suspended -eq "1" ]]; then
+ touch "$state_file"
+ run=true
+ sleep 0.5
+ elif [ "$run" = true ]; then
+ $generator "$nr_iter" "$@"
+ run=false
+ if [[ -f $state_file ]]; then
+ rm "$state_file" 2> /dev/null
+ fi
+ else
+ # Wait for a "suspend" to reset the run state
+ sleep 0.1
+ fi
+ done
+}
+
+function kernel_event_generator
+{
+ # Used by the signal trap
+ kernel_event_generator_suspended=0
+ # Used by the signal trap for SIGUSR2 to end the generator
+ generator_quit=1
+
+ local generator=$1
+ local state_file=$2
+
+ # Pass any of the remaining arguments to the generator.
+ shift 2
+
+ trap kernel_event_generator_toggle_state SIGUSR1
+ trap generator_quit SIGUSR2
+
+ while [ $generator_quit -ne 0 ]; do
+ if [[ $kernel_event_generator_suspended -eq "1" ]]; then
+ touch "$state_file"
+ sleep 0.5
+ else
+ $generator "10" "$@"
+ if [[ -f $state_file ]]; then
+ rm "$state_file" 2> /dev/null
+ fi
+ fi
+ done
+}
--- /dev/null
+AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_srcdir)/tests -I$(top_srcdir)/tests/utils/ -I$(srcdir)
+AM_LDFLAGS =
+
+SUBDIRS=utils start-stop
+
+LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la
+LIB_LTTNG_CTL = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la
+
+noinst_PROGRAMS = base_client trigger
+
+if NO_SHARED
+
+CLEANFILES = libpause_consumer.so libpause_consumer.so.debug
+EXTRA_DIST = test_trigger_ust test_trigger_kernel base_client.c trigger.c consumer_testpoints.c
+
+else
+
+# In order to use testpoint, the helper library
+# must be built as .so to be able to LD_PRELOAD it.
+FORCE_SHARED_LIB_OPTIONS = -module -shared -avoid-version \
+ -rpath $(abs_builddir)
+
+libpause_consumer_la_SOURCES = consumer_testpoints.c
+libpause_consumer_la_LIBADD = \
+ $(top_builddir)/src/common/libcommon.la \
+ $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \
+ $(DL_LIBS)
+libpause_consumer_la_LDFLAGS = $(FORCE_SHARED_LIB_OPTIONS)
+noinst_LTLIBRARIES = libpause_consumer.la
+
+base_client_SOURCES = base_client.c
+base_client_LDADD = $(LIB_LTTNG_CTL)
+
+trigger_SOURCES = trigger.c
+trigger_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm
+
+noinst_SCRIPTS = test_trigger_ust test_trigger_kernel
+EXTRA_DIST = test_trigger_ust test_trigger_kernel \
+ test_add_trigger_cli \
+ test_list_triggers_cli \
+ test_remove_trigger_cli
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
+endif
--- /dev/null
+/*
+ * base_client.c
+ *
+ * Base client application for testing of LTTng trigger API
+ *
+ * Copyright 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <lttng/action/action.h>
+#include <lttng/action/start-session.h>
+#include <lttng/action/notify.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/domain.h>
+#include <lttng/trigger/trigger.h>
+#include <lttng/lttng-error.h>
+#include <lttng/endpoint.h>
+#include <lttng/notification/channel.h>
+#include <lttng/notification/notification.h>
+
+const char *session_name = NULL;
+enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
+const char *pattern = NULL;
+
+int parse_arguments(char **argv) {
+ const char *domain_type_string = NULL;
+
+ session_name = argv[1];
+ domain_type_string = argv[2];
+ pattern = argv[3];
+
+ /* Parse arguments */
+ /* Domain type */
+ if (!strcasecmp("LTTNG_DOMAIN_UST", domain_type_string)) {
+ domain_type = LTTNG_DOMAIN_UST;
+ }
+ if (!strcasecmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) {
+ domain_type = LTTNG_DOMAIN_KERNEL;
+ }
+ if (!strcasecmp("LTTNG_DOMAIN_JUL", domain_type_string)) {
+ domain_type = LTTNG_DOMAIN_JUL;
+ }
+ if (!strcasecmp("LTTNG_DOMAIN_PYTHON", domain_type_string)) {
+ domain_type = LTTNG_DOMAIN_PYTHON;
+ }
+ if (!strcasecmp("LTTNG_DOMAIN_LOG4J", domain_type_string)) {
+ domain_type = LTTNG_DOMAIN_LOG4J;
+ }
+ if (domain_type == LTTNG_DOMAIN_NONE) {
+ printf("error: Unknown domain type\n");
+ goto error;
+ }
+
+ return 0;
+error:
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ int ret = 0;
+ struct lttng_condition *condition = NULL;
+
+ enum lttng_event_rule_status event_rule_status;
+ struct lttng_event_rule *event_rule = NULL;
+
+ enum lttng_action_status action_status;
+ struct lttng_action *action = NULL;
+
+ enum lttng_notification_channel_status nc_status;
+ struct lttng_notification_channel *notification_channel = NULL;
+
+ const char* exclusions[] = { "sample_component:message2"};
+
+ struct lttng_trigger *trigger = NULL;
+
+ if (argc < 4) {
+ printf("error: Missing arguments for tests\n");
+ ret = 1;
+ goto end;
+ }
+
+ ret = parse_arguments(argv);
+ if (ret) {
+ printf("error: Could not parse arguments\n");
+ goto end;
+ }
+
+ event_rule = lttng_event_rule_tracepoint_create(domain_type);
+ if (!event_rule) {
+ printf("error: Could not create condition object\n");
+ ret = 1;
+ goto end;
+ }
+
+ event_rule_status = lttng_event_rule_tracepoint_set_pattern(event_rule, pattern);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ printf("error: Could not set pattern\n");
+ ret = 1;
+ goto end;
+ }
+
+ event_rule_status = lttng_event_rule_tracepoint_set_filter(event_rule, "message=='Hello World'");
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ printf("error: Could not set pattern\n");
+ ret = 1;
+ goto end;
+ }
+
+ event_rule_status = lttng_event_rule_tracepoint_set_exclusions(event_rule, 1, exclusions);
+ if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
+ printf("error: Could not set exclusions\n");
+ ret = 1;
+ goto end;
+ }
+
+ condition = lttng_condition_event_rule_create(event_rule);
+ if (!condition) {
+ printf("error: Could not create condition\n");
+ ret = 1;
+ goto end;
+ }
+ /* Ownership was passed to condition */
+ event_rule = NULL;
+
+ action = lttng_action_notify_create();
+ if (!action) {
+ printf("error: Could not create action notify\n");
+ ret = 1;
+ goto end;
+ }
+
+ trigger = lttng_trigger_create(condition, action);
+ if (!trigger) {
+ printf("error: Could not create trigger\n");
+ ret = 1;
+ goto end;
+ }
+
+ ret = lttng_register_trigger(trigger);
+
+ notification_channel = lttng_notification_channel_create(
+ lttng_session_daemon_notification_endpoint);
+ if (!notification_channel) {
+ printf("error: Could not create notification channel\n");
+ ret = 1;
+ goto end;
+ }
+ /*
+ * An equivalent trigger might already be registered if an other app
+ * registered an equivalent trigger.
+ */
+ if (ret < 0 && ret != -LTTNG_ERR_TRIGGER_EXISTS) {
+ printf("error: %s\n", lttng_strerror(ret));
+ ret = 1;
+ goto end;
+ }
+
+ nc_status = lttng_notification_channel_subscribe(notification_channel, condition);
+ if (nc_status != LTTNG_NOTIFICATION_CHANNEL_STATUS_OK) {
+ printf("error: Could not subscribe\n");
+ ret = 1;
+ goto end;
+ }
+
+ for (;;) {
+ struct lttng_notification *notification;
+ enum lttng_notification_channel_status status;
+ const struct lttng_evaluation *notification_evaluation;
+ const struct lttng_condition *notification_condition;
+ const char *name;
+
+ /* Receive the next notification. */
+ status = lttng_notification_channel_get_next_notification(
+ notification_channel,
+ ¬ification);
+
+ switch (status) {
+ case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK:
+ break;
+ case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED:
+ ret = 1;
+ printf("error: No drop should be observed during this test app\n");
+ goto end;
+ case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED:
+ /*
+ * The notification channel has been closed by the
+ * session daemon. This is typically caused by a session
+ * daemon shutting down (cleanly or because of a crash).
+ */
+ printf("error: Notification channel was closed\n");
+ ret = 1;
+ goto end;
+ default:
+ /* Unhandled conditions / errors. */
+ printf("error: Unknown notification channel status\n");
+ ret = 1;
+ goto end;
+ }
+
+ notification_condition = lttng_notification_get_condition(notification);
+ notification_evaluation = lttng_notification_get_evaluation(notification);
+ switch (lttng_evaluation_get_type(notification_evaluation)) {
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ lttng_evaluation_event_rule_get_trigger_name(notification_evaluation, &name);
+ printf("Received nootification from trigger \"%s\"\n", name);
+ break;
+ default:
+ printf("error: Wrong notification evaluation type \n");
+ break;
+
+ }
+
+ lttng_notification_destroy(notification);
+ }
+end:
+ if (trigger) {
+ lttng_unregister_trigger(trigger);
+ }
+ lttng_event_rule_destroy(event_rule);
+ lttng_trigger_destroy(trigger);
+ lttng_condition_destroy(condition);
+ lttng_action_destroy(action);
+ printf("exit: %d\n", ret);
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2017 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <common/compat/getenv.h>
+#include <common/consumer/consumer.h>
+#include <common/pipe.h>
+#include <common/error.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <lttng/constant.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <assert.h>
+#include <stdio.h>
+
+static char *pause_pipe_path;
+static struct lttng_pipe *pause_pipe;
+static int *data_consumption_state;
+static enum lttng_consumer_type (*lttng_consumer_get_type)(void);
+
+int lttng_opt_verbose;
+int lttng_opt_mi;
+int lttng_opt_quiet;
+
+static
+void __attribute__((destructor)) pause_pipe_fini(void)
+{
+ int ret;
+
+ if (pause_pipe_path) {
+ ret = unlink(pause_pipe_path);
+ if (ret) {
+ PERROR("unlink pause pipe");
+ }
+ }
+
+ free(pause_pipe_path);
+ lttng_pipe_destroy(pause_pipe);
+}
+
+/*
+ * We use this testpoint, invoked at the start of the consumerd's data handling
+ * thread to create a named pipe/FIFO which a test application can use to either
+ * pause or resume the consumption of data.
+ */
+int __testpoint_consumerd_thread_data(void)
+{
+ int ret = 0;
+ const char *pause_pipe_path_prefix, *domain;
+
+ pause_pipe_path_prefix = lttng_secure_getenv(
+ "CONSUMER_PAUSE_PIPE_PATH");
+ if (!pause_pipe_path_prefix) {
+ ret = -1;
+ goto end;
+ }
+
+ /*
+ * These symbols are exclusive to the consumerd process, hence we can't
+ * rely on their presence in the sessiond. Not looking-up these symbols
+ * dynamically would not allow this shared object to be LD_PRELOAD-ed
+ * when launching the session daemon.
+ */
+ data_consumption_state = dlsym(NULL, "data_consumption_paused");
+ assert(data_consumption_state);
+ lttng_consumer_get_type = dlsym(NULL, "lttng_consumer_get_type");
+ assert(lttng_consumer_get_type);
+
+ switch (lttng_consumer_get_type()) {
+ case LTTNG_CONSUMER_KERNEL:
+ domain = "kernel";
+ break;
+ case LTTNG_CONSUMER32_UST:
+ domain = "ust32";
+ break;
+ case LTTNG_CONSUMER64_UST:
+ domain = "ust64";
+ break;
+ default:
+ abort();
+ }
+
+ ret = asprintf(&pause_pipe_path, "%s-%s", pause_pipe_path_prefix,
+ domain);
+ if (ret < 1) {
+ ERR("Failed to allocate pause pipe path");
+ goto end;
+ }
+
+ DBG("Creating pause pipe at %s", pause_pipe_path);
+ pause_pipe = lttng_pipe_named_open(pause_pipe_path,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, O_NONBLOCK);
+ if (!pause_pipe) {
+ ERR("Failed to create pause pipe at %s", pause_pipe_path);
+ ret = -1;
+ goto end;
+ }
+
+ /* Only the read end of the pipe is useful to us. */
+ ret = lttng_pipe_write_close(pause_pipe);
+end:
+ return ret;
+}
+
+int __testpoint_consumerd_thread_data_poll(void)
+{
+ int ret = 0;
+ uint8_t value;
+ bool value_read = false;
+
+ if (!pause_pipe) {
+ ret = -1;
+ goto end;
+ }
+
+ /* Purge pipe and only consider the freshest value. */
+ do {
+ ret = lttng_pipe_read(pause_pipe, &value, sizeof(value));
+ if (ret == sizeof(value)) {
+ value_read = true;
+ }
+ } while (ret == sizeof(value));
+
+ ret = (errno == EAGAIN) ? 0 : -errno;
+
+ if (value_read) {
+ *data_consumption_state = !!value;
+ DBG("Message received on pause pipe: %s data consumption",
+ *data_consumption_state ? "paused" : "resumed");
+ }
+end:
+ return ret;
+}
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+
+noinst_SCRIPTS = test_start_stop
+
+EXTRA_DIST = test_start_stop
+
+all-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ cp -f $(srcdir)/$$script $(builddir); \
+ done; \
+ fi
+
+clean-local:
+ @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
+ for script in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$script; \
+ done; \
+ fi
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+#
+# SPDX-License-Identifier: LGPL-2.1-only
+
+TEST_DESC="Triggers - Start and stop actions"
+
+CURDIR=$(dirname "$0")/
+TESTDIR=${CURDIR}/../../../..
+
+# shellcheck source=../../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+GEN_UST_EVENTS_TESTAPP_NAME="gen-ust-events"
+GEN_UST_EVENTS_TESTAPP_BIN="$TESTAPP_PATH/$GEN_UST_EVENTS_TESTAPP_NAME/$GEN_UST_EVENTS_TESTAPP_NAME"
+FULL_LTTNG_BIN="$TESTDIR/../src/bin/lttng/$LTTNG_BIN"
+NOTIFICATION_CLIENT_BIN="$CURDIR/../utils/notification-client"
+NUM_TESTS=18
+
+NR_ITER=5
+NR_USEC_WAIT=5
+
+function lttng_add_trigger_ust()
+{
+ local expected_to_fail="$1"
+ local trigger_name="$2"
+ shift 2
+
+ "$FULL_LTTNG_BIN" add-trigger --id "$trigger_name" "$@" 1> /dev/null 2> /dev/null
+ ret=$?
+ if [[ $expected_to_fail -eq "1" ]]; then
+ test "$ret" -ne "0"
+ ok $? "Add trigger $trigger_name failed as expected"
+ else
+ ok $ret "Add trigger $trigger_name"
+ fi
+}
+
+function lttng_remove_trigger_ust()
+{
+ local expected_to_fail="$1"
+ local trigger_name="$2"
+
+ "$FULL_LTTNG_BIN" remove-trigger "$trigger_name" 1> /dev/null 2> /dev/null
+ ret=$?
+ if [[ $expected_to_fail -eq "1" ]]; then
+ test "$ret" -ne "0"
+ ok $? "Remove trigger $trigger_name failed as expected"
+ else
+ ok $ret "Remove trigger $trigger_name"
+ fi
+}
+
+function lttng_add_trigger_ust_ok()
+{
+ lttng_add_trigger_ust 0 "$@"
+}
+
+function lttng_remove_trigger_ust_ok()
+{
+ lttng_remove_trigger_ust 0 "$@"
+}
+
+function lttng_session_is_active()
+{
+ local SESSION_NAME="$1"
+ "$FULL_LTTNG_BIN" list "$SESSION_NAME" | grep "Tracing session" | grep -q "\[active\]"
+
+ ok $ret "Session \"$SESSION_NAME\" is active"
+}
+
+function lttng_session_is_inactive()
+{
+ local SESSION_NAME="$1"
+ "$FULL_LTTNG_BIN" list "$SESSION_NAME" | grep "Tracing session" | grep -q "\[inactive\]"
+
+ ok $ret "Session \"$SESSION_NAME\" is inactive"
+}
+
+function test_start_session_action()
+{
+ local SESSION_NAME="my_triggered_session"
+ local TRIGGER_NAME="trigger1"
+ local TRACE_PATH=$(mktemp -d test-start-action-trace.XXXXXX)
+ local SYNC_AFTER_NOTIF_REGISTER_PATH=$(mktemp test-notif-register.XXXXXX)
+
+ diag "Start session action"
+
+ create_lttng_session_ok $SESSION_NAME "$TRACE_PATH"
+
+ enable_ust_lttng_event_ok $SESSION_NAME "tp:tptest"
+
+ lttng_session_is_inactive $SESSION_NAME
+
+ # Add `start-session` action to an event-rule condition _followed_ by
+ # a `notify` action.
+ lttng_add_trigger_ust_ok \
+ $TRIGGER_NAME \
+ --condition on-event -u "tp:tptest" \
+ --action start-session $SESSION_NAME \
+ --action notify
+
+ # Launch notification listener.
+ $NOTIFICATION_CLIENT_BIN \
+ --trigger $TRIGGER_NAME \
+ --sync-after-notif-register "$SYNC_AFTER_NOTIF_REGISTER_PATH"
+ notif_client_pid=$!
+
+ while [ ! -f "${SYNC_AFTER_NOTIF_REGISTER_PATH}" ]; do
+ sleep 0.5
+ done
+
+ # Artificially produce the desired event-rule condition.
+ $GEN_UST_EVENTS_TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1
+
+ # notification-client will exit once it receives a notification.
+ wait $notif_client_pid
+ test "$?" -eq "0"
+ ok $? "notification client exited successfully"
+
+ # Test that the session as started.
+ lttng_session_is_active $SESSION_NAME
+
+ # Tearing down.
+ lttng_remove_trigger_ust_ok $TRIGGER_NAME
+ stop_lttng_tracing_ok $SESSION_NAME
+ destroy_lttng_session_ok $SESSION_NAME
+
+ rm -f "$SYNC_AFTER_NOTIF_REGISTER_PATH"
+ rm -rf "$TRACE_PATH"
+}
+
+function test_stop_session_action()
+{
+ local SESSION_NAME="my_triggered_session"
+ local TRIGGER_NAME="trigger1"
+ local TRACE_PATH=$(mktemp -d test-stop-action-trace.XXXXXX)
+ local SYNC_AFTER_NOTIF_REGISTER_PATH=$(mktemp test-notif-register.XXXXXX)
+
+ diag "Stop session action"
+ create_lttng_session_ok $SESSION_NAME "$TRACE_PATH"
+
+ enable_ust_lttng_event_ok $SESSION_NAME "tp:tptest"
+
+ start_lttng_tracing_ok $SESSION_NAME
+
+ lttng_session_is_active $SESSION_NAME
+
+ # Add `stop-session` action to an event-rule condition _followed_ by
+ # a `notify` action.
+ lttng_add_trigger_ust_ok \
+ $TRIGGER_NAME \
+ --condition on-event -u "tp:tptest" \
+ --action stop-session $SESSION_NAME \
+ --action notify
+
+ # Launch notification listener.
+ $NOTIFICATION_CLIENT_BIN \
+ --trigger $TRIGGER_NAME \
+ --sync-after-notif-register "$SYNC_AFTER_NOTIF_REGISTER_PATH"
+ notif_client_pid=$!
+
+ while [ ! -f "${SYNC_AFTER_NOTIF_REGISTER_PATH}" ]; do
+ sleep 0.5
+ done
+
+ # Artificially produce the desired event-rule condition.
+ $GEN_UST_EVENTS_TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT > /dev/null 2>&1
+
+ # notification-client will exit once it receives a notification.
+ wait $notif_client_pid
+ test "$?" -eq "0"
+ ok $? "notification client exited successfully"
+
+ # Test that the session as started.
+ lttng_session_is_inactive $SESSION_NAME
+
+ # Tearing down.
+ lttng_remove_trigger_ust_ok $TRIGGER_NAME
+ destroy_lttng_session_ok $SESSION_NAME
+
+ rm -f "$SYNC_AFTER_NOTIF_REGISTER_PATH"
+ rm -rf "$TRACE_PATH"
+}
+
+ # MUST set TESTDIR before calling those functions
+plan_tests $NUM_TESTS
+
+print_test_banner "$TEST_DESC"
+
+start_lttng_sessiond_notap
+
+test_start_session_action
+test_stop_session_action
+
+stop_lttng_sessiond_notap
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) - 2020 EfficiOS, inc
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+# Test the `lttng add-trigger` command line interface.
+
+CURDIR="$(dirname "$0")"
+TESTDIR="$CURDIR/../../.."
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+
+plan_tests 219
+
+FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}"
+
+# shellcheck disable=SC2119
+start_lttng_sessiond_notap
+
+tmp_stdout=$(mktemp -t test_parse_cli_trigger_stdout.XXXXXX)
+tmp_stderr=$(mktemp -t test_parse_cli_trigger_stderr.XXXXXX)
+uprobe_elf_binary="${TESTDIR}/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary"
+
+if [ "$(id -u)" == "0" ]; then
+ ist_root=1
+else
+ ist_root=0
+fi
+
+function test_success ()
+{
+ local test_name="$1"
+ shift
+
+ diag "${FULL_LTTNG_BIN} add-trigger $*"
+ "${FULL_LTTNG_BIN}" add-trigger "$@" > "${tmp_stdout}" 2> "${tmp_stderr}"
+ ok $? "${test_name}: exit code is 0"
+
+ diff -u "${tmp_stdout}" <(echo "Trigger registered successfully.")
+ ok $? "${test_name}: expected stdout"
+
+ diff -u "${tmp_stderr}" /dev/null
+ ok $? "${test_name}: expected stderr"
+}
+
+function test_failure ()
+{
+ local test_name="$1"
+ local error_msg="$2"
+
+ shift 2
+
+ diag "${FULL_LTTNG_BIN} add-trigger $*"
+ "${FULL_LTTNG_BIN}" add-trigger "$@" > "${tmp_stdout}" 2> "${tmp_stderr}"
+ isnt $? 0 "${test_name}: exit code is not 0"
+
+ diff -u "${tmp_stdout}" /dev/null
+ ok $? "${test_name}: expected stdout"
+
+ diff -u "${tmp_stderr}" <(echo "${error_msg}")
+ ok $? "${test_name}: expected stderr"
+}
+
+# top-level options
+test_success "explicit id" \
+ --id hohoho \
+ --condition on-event some-event-id -u \
+ --action notify
+
+# `--condition on-event` successes
+test_success "--condition on-event some-event -u" \
+ --condition on-event some-event -u \
+ --action notify
+
+test_success "--condition on-event -a -u" \
+ --condition on-event -a -u \
+ --action notify
+
+test_success "--fire-once-after" \
+ --condition on-event -u test-fire-once-after \
+ --action notify \
+ --fire-once-after=55
+
+test_success "--fire-every" \
+ --condition on-event -u test-fire-every \
+ --action notify \
+ --fire-every=55
+
+skip $ist_root "non-root user: skipping kprobe tests" 12 || {
+ test_success "--condition on-event probe by symbol" \
+ --condition on-event -k --probe=do_sys_open my_sys_open \
+ --action notify
+
+ test_success "--condition on-event probe by symbol with offset" \
+ --condition on-event -k --probe=do_sys_open+10 my_sys_open \
+ --action notify
+
+ sys_open_addr=$(grep ' T do_sys_open$' /proc/kallsyms | cut -f 1 -d ' ')
+
+ test_success "--condition on-event probe by address" \
+ --condition on-event -k "--probe=${sys_open_addr}" my_sys_open \
+ --action notify
+
+ test_success "--condition on-event probe by address with offset" \
+ --condition on-event -k "--probe=${sys_open_addr}+10" my_sys_open \
+ --action notify
+}
+
+skip $ist_root "non-root user: skipping uprobe tests" 6 || {
+ test_success "--condition on-event uprobe" \
+ --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function ma-probe \
+ --action notify
+
+ test_success "--condition on-event uprobe with elf prefix" \
+ --condition on-event -k --userspace-probe=elf:${uprobe_elf_binary}:test_function ma-probe-2 \
+ --action notify
+}
+
+skip $ist_root "non-root user: skipping syscall tests" 9 || {
+ test_success "--condition on-event syscall" \
+ --condition on-event -k --syscall open \
+ --action notify
+
+ test_success "--condition on-event syscall -a" \
+ --condition on-event -k --syscall -a \
+ --action notify
+
+ test_success "--condition on-event syscall with filter" \
+ --condition on-event -k --syscall --filter 'a > 2' open \
+ --action notify
+}
+
+# `--action notify` successes
+test_success "--action notify" \
+ --condition on-event some-event-notify -u \
+ --action notify
+
+test_success "--action notify --capture foo" \
+ --condition on-event some-event-notify-foo -u \
+ --capture foo --action notify
+
+test_success "--action notify --capture foo[2]" \
+ --condition on-event some-event-notify-foo2 -u \
+ --capture 'foo[2]' --action notify
+
+test_success '--action notify --capture $ctx.foo' \
+ --condition on-event some-event-notify-ctx-foo -u \
+ --capture '$ctx.foo' --action notify
+
+test_success '--action notify --capture $ctx.foo[2]' \
+ --condition on-event some-event-notify-ctx-foo2 -u \
+ --capture '$ctx.foo[2]' --action notify
+
+test_success '--action notify --capture $app.prov:type' \
+ --condition on-event some-event-notify-app-prov-type -u \
+ --capture '$app.prov:type' --action notify
+
+test_success '--action notify --capture $app.prov:type[2]' \
+ --condition on-event some-event-notify-app-prov-type-2 -u \
+ --capture '$app.prov:type[2]' --action notify
+
+test_success '--action notify multiple captures' \
+ --condition on-event some-event-notify-multiple-captures -u \
+ --capture foo --capture '$app.hello:world' --action notify
+
+# `--action start-session` successes
+test_success "--action start-session" \
+ --condition on-event some-event-start-session -u \
+ --action start-session ze-session
+
+# `--action stop-session` successes
+test_success "--action stop-session foo" \
+ --condition on-event some-event-stop-session -u \
+ --action stop-session ze-session
+
+# `--action rotate-session` successes
+test_success "--action rotate-session foo" \
+ --condition on-event some-event-rotate-session -u \
+ --action rotate-session ze-session
+
+# `--action snapshot-session` successes
+test_success "--action snapshot-session foo" \
+ --condition on-event some-event-snapshot-session -u \
+ --action snapshot-session ze-session
+
+test_success "--action snapshot-session with file URI" \
+ --condition on-event some-event-snapshot-session2 -u \
+ --action snapshot-session ze-session /hello
+
+test_success "--action snapshot-session with net URI" \
+ --condition on-event some-event-snapshot-session3 -u \
+ --action snapshot-session ze-session net://1.2.3.4
+
+test_success "--action snapshot-session with ctrl/data URIs" \
+ --condition on-event some-event-snapshot-session4 -u \
+ --action snapshot-session ze-session --ctrl-url=tcp://1.2.3.4:1234 --data-url=tcp://1.2.3.4:1235
+
+# top-level failures
+test_failure "no args" "Error: Missing --condition."
+
+test_failure "unknown option" \
+ "Error: Unknown option \`--hello\`" \
+ --hello
+
+test_failure "missing --action" \
+ "Error: Need at least one --action." \
+ --condition on-event hello -u
+
+test_failure "two --condition" \
+ "Error: A --condition was already given." \
+ --condition on-event aaa -u \
+ --condition on-event bbb -u \
+ --action notify
+
+test_failure "missing argument to --id" \
+ "Error: While parsing argument #1 (\`--id\`): Missing required argument for option \`--id\`" \
+ --id
+
+for cmd in fire-once-after fire-every; do
+ test_failure "missing argument to --${cmd}" \
+ "Error: While parsing argument #1 (\`--${cmd}\`): Missing required argument for option \`--${cmd}\`" \
+ --condition on-event -u -a --action notify \
+ --${cmd}
+
+ test_failure "invalid argument to --${cmd}: non-digit character" \
+ "Error: Failed to parse \`123bob\` as an integer." \
+ --condition on-event -u -a --action notify \
+ --${cmd} 123bob
+
+ test_failure "invalid argument to --${cmd}: empty string" \
+ "Error: Failed to parse \`\` as an integer." \
+ --condition on-event -u -a --action notify \
+ --${cmd} ""
+done
+
+# `--condition` failures
+test_failure "missing args after --condition" \
+ "Error: Missing condition name." \
+ --condition
+test_failure "unknown --condition" \
+ "Error: Unknown condition name: zoofest" \
+ --condition zoofest
+
+# `--condition on-event` failures
+test_failure "missing args after --condition on-event" \
+ "Error: Need to provide either a tracepoint name or -a/--all." \
+ --condition on-event
+test_failure "missing domain in --condition on-event" \
+ "Error: Please specify a domain (-k/-u/-j)." \
+ --condition on-event -a
+test_failure "extra args after --condition on-event" \
+ "Error: Unexpected argument: bozo" \
+ --condition on-event foo -u bozo
+test_failure "--condition on-event: --all with --probe" \
+ "Error: Can't use -a/--all with event rule of type probe." \
+ --condition on-event --probe=do_sys_open --all
+test_failure "--condition on-event: missing tracepoint name with --probe" \
+ "Error: Need to provide either a tracepoint name or -a/--all." \
+ --condition on-event -k --probe do_sys_open
+
+test_failure "--condition on-event: missing tracepoint name with --userspace-probe" \
+ "Error: Need to provide either a tracepoint name or -a/--all." \
+ --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function
+
+test_failure "--condition on-event: extra argument with --userspace-probe" \
+ "Error: Unexpected argument: world" \
+ --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_failure hello world
+
+test_failure "--condition on-event: missing tracepoint name with --syscall" \
+ "Error: Need to provide either a tracepoint name or -a/--all." \
+ --condition on-event -k --syscall
+
+test_failure "--condition on-event: extra argument with --syscall" \
+ "Error: Unexpected argument: open" \
+ --condition on-event -k --syscall open open
+
+test_failure "--condition on-event: both -a and a tracepoint name with --syscall" \
+ "Error: Can't provide a tracepoint name with -a/--all." \
+ --condition on-event -k --syscall -a open
+
+test_failure "--condition on-event --capture: missing argument (end of arg list)" \
+ 'Error: While parsing argument #3 (`--capture`): Missing required argument for option `--capture`' \
+ --action notify \
+ --condition on-event -u -a --capture
+
+test_failure "--condition on-event --capture: missing argument (before another option)" \
+ 'Error: While parsing expression `--action`: Unary operators are not allowed in capture expressions.' \
+ --condition on-event -u -a --capture \
+ --action notify \
+
+test_failure "--condition on-event --capture: binary operator" \
+ 'Error: While parsing expression `foo == 2`: Binary operators are not allowed in capture expressions.' \
+ --condition on-event -u -a \
+ --capture 'foo == 2' --action notify
+
+test_failure "--condition on-event --capture: unary operator" \
+ 'Error: While parsing expression `!foo`: Unary operators are not allowed in capture expressions.' \
+ --condition on-event -u -a \
+ --capture '!foo' --action notify
+
+test_failure "--condition on-event --capture: logical operator" \
+ 'Error: While parsing expression `foo || bar`: Logical operators are not allowed in capture expressions.' \
+ --condition on-event -u -a \
+ --capture 'foo || bar' --action notify
+
+test_failure "--condition on-event --capture: accessing a sub-field" \
+ 'Error: While parsing expression `foo.bar`: Capturing subfields is not supported.' \
+ --condition on-event -u -a \
+ --capture 'foo.bar' --action notify
+
+test_failure "--condition on-event --capture: accessing the sub-field of an array element" \
+ 'Error: While parsing expression `foo[3].bar`: Capturing subfields is not supported.' \
+ --condition on-event -u -a \
+ --capture 'foo[3].bar' --action notify
+
+test_failure "--condition on-event --capture: missing colon in app-specific context field" \
+ 'Invalid app-specific context field name: missing colon in `foo`.' \
+ --condition on-event -u -a \
+ --capture '$app.foo' --action notify
+
+test_failure "--condition on-event --capture: missing colon in app-specific context field" \
+ 'Invalid app-specific context field name: missing type name after colon in `foo:`.' \
+ --condition on-event -u -a \
+ --capture '$app.foo:' --action notify
+
+# `--action` failures
+test_failure "missing args after --action" \
+ "Error: Missing action name." \
+ --condition on-event -u -a \
+ --action
+
+# `--action notify` failures
+test_failure "extra arg after --action notify" \
+ "Error: Unexpected argument \`bob\`." \
+ --condition on-event -u -a \
+ --action notify bob
+
+# `--action start-session` failures
+test_failure "missing arg after --action start-session" \
+ "Error: Missing session name." \
+ --condition on-event some-event-start-session -u \
+ --action start-session
+test_failure "extra arg after --action start-session" \
+ "Error: Unexpected argument \`bob\`." \
+ --condition on-event some-event-start-session -u \
+ --action start-session ze-session bob
+
+# `--action stop-session` failures
+test_failure "missing arg after --action stop-session" \
+ "Error: Missing session name." \
+ --condition on-event some-event-stop-session -u \
+ --action stop-session
+test_failure "extra arg after --action stop-session" \
+ "Error: Unexpected argument \`bob\`." \
+ --condition on-event some-event-stop-session -u \
+ --action stop-session ze-session bob
+
+# `--action rotate-session` failures
+test_failure "missing arg after --action rotate-session" \
+ "Error: Missing session name." \
+ --condition on-event some-event-rotate-session -u \
+ --action rotate-session
+test_failure "extra arg after --action rotate-session" \
+ "Error: Unexpected argument \`bob\`." \
+ --condition on-event some-event-rotate-session -u \
+ --action rotate-session ze-session bob
+
+# `--action snapshot-session` failures
+test_failure "missing arg after --action snapshot-session" \
+ "Error: Missing session name." \
+ --condition on-event some-event-snapshot-session -u \
+ --action snapshot-session
+test_failure "extra arg after --action snapshot-session" \
+ "Error: Unexpected argument \`bob\`." \
+ --condition on-event some-event-snapshot-session -u \
+ --action snapshot-session ze-session /dest bob
+test_failure "snapshot-session action, --max-size without destination" \
+ "Error: Can't provide a snapshot output max size without a snapshot output destination." \
+ --condition on-event some-event-snapshot-session -u \
+ --action snapshot-session ze-session --max-size 10M
+test_failure "snapshot-session action, --name without destination" \
+ "Error: Can't provide a snapshot output name without a snapshot output destination." \
+ --condition on-event some-event-snapshot-session -u \
+ --action snapshot-session ze-session --name hallo
+
+
+# Cleanup
+stop_lttng_sessiond_notap
+rm -f "${tmp_stdout}"
+rm -f "${tmp_stderr}"
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) - 2020 EfficiOS, inc
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+# Test the `lttng list-trigger` command line interface.
+
+CURDIR="$(dirname "$0")"
+TESTDIR="$CURDIR/../../.."
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+
+plan_tests 45
+
+FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}"
+
+tmp_stdout=$(mktemp -t test_list_triggers_cli_stdout.XXXXXX)
+tmp_stderr=$(mktemp -t test_list_triggers_cli_stderr.XXXXXX)
+tmp_expected_stdout=$(mktemp -t test_list_triggers_cli_expected_stdout.XXXXXX)
+uprobe_elf_binary=$(realpath "${TESTDIR}/utils/testapp/userspace-probe-elf-binary/userspace-probe-elf-binary")
+
+if [ "$(id -u)" == "0" ]; then
+ ist_root=1
+else
+ ist_root=0
+fi
+
+function add_trigger ()
+{
+ "${FULL_LTTNG_BIN}" add-trigger "$@"
+ ok $? "add trigger \`$*\`: exit code is 0"
+}
+
+function list_triggers ()
+{
+ local test_name="$1"
+ local expected_stdout_file="$2"
+
+ "${FULL_LTTNG_BIN}" list-triggers > "${tmp_stdout}" 2> "${tmp_stderr}"
+ ok $? "${test_name}: exit code is 0"
+
+ diff -u "${expected_stdout_file}" "${tmp_stdout}"
+ ok $? "${test_name}: expected stdout"
+
+ diff -u /dev/null "${tmp_stderr}"
+ ok $? "${test_name}: expected stderr"
+}
+
+test_top_level_options ()
+{
+ # shellcheck disable=SC2119
+ start_lttng_sessiond_notap
+
+
+ add_trigger --id hello --condition on-event -u test-id --action notify
+ add_trigger --fire-once-after 123 --condition on-event -u test-fire-once-after --action notify
+ add_trigger --fire-every 124 --condition on-event -u test-fire-every --action notify
+
+ cat > "${tmp_expected_stdout}" <<- EOF
+ - id: T2
+ firing policy: once after 123 occurences
+ condition: event rule hit
+ rule: test-fire-once-after (type: tracepoint, domain: ust)
+ actions:
+ notify
+ - id: T3
+ firing policy: after every 124 occurences
+ condition: event rule hit
+ rule: test-fire-every (type: tracepoint, domain: ust)
+ actions:
+ notify
+ - id: hello
+ condition: event rule hit
+ rule: test-id (type: tracepoint, domain: ust)
+ actions:
+ notify
+ EOF
+
+ list_triggers "top level options" "${tmp_expected_stdout}"
+
+ stop_lttng_sessiond_notap
+}
+
+test_on_event_tracepoint ()
+{
+ # shellcheck disable=SC2119
+ start_lttng_sessiond_notap
+
+ add_trigger --condition on-event -u -a --action notify
+ add_trigger --id ABC --condition on-event aaa -u --filter 'p == 2' --action notify
+ add_trigger --condition on-event 'hello*' -u -x 'hello2,hello3,hello4' --action notify
+ add_trigger --id BCD --condition on-event -u gerboise --loglevel INFO --action notify
+ add_trigger --condition on-event -u lemming --loglevel-only WARNING --action notify
+ add_trigger --condition on-event -u capture-payload-field --capture a --action notify
+ add_trigger --condition on-event -u capture-array --capture 'a[2]' --capture '$ctx.tourlou[18]' --action notify
+ add_trigger --condition on-event -u capture-chan-ctx --capture '$ctx.vpid' --action notify
+ add_trigger --condition on-event -u capture-app-ctx --capture '$app.iga:active_clients' --action notify
+
+
+ cat > "${tmp_expected_stdout}" <<- EOF
+ - id: ABC
+ condition: event rule hit
+ rule: aaa (type: tracepoint, domain: ust, filter: p == 2)
+ actions:
+ notify
+ - id: BCD
+ condition: event rule hit
+ rule: gerboise (type: tracepoint, domain: ust, log level <= TRACE_INFO)
+ actions:
+ notify
+ - id: T1
+ condition: event rule hit
+ rule: * (type: tracepoint, domain: ust)
+ actions:
+ notify
+ - id: T3
+ condition: event rule hit
+ rule: hello* (type: tracepoint, domain: ust, exclusions: hello2,hello3,hello4)
+ actions:
+ notify
+ - id: T5
+ condition: event rule hit
+ rule: lemming (type: tracepoint, domain: ust, log level == TRACE_WARNING)
+ actions:
+ notify
+ - id: T6
+ condition: event rule hit
+ rule: capture-payload-field (type: tracepoint, domain: ust)
+ captures:
+ - a
+ actions:
+ notify
+ - id: T7
+ condition: event rule hit
+ rule: capture-array (type: tracepoint, domain: ust)
+ captures:
+ - a[2]
+ - \$ctx.tourlou[18]
+ actions:
+ notify
+ - id: T8
+ condition: event rule hit
+ rule: capture-chan-ctx (type: tracepoint, domain: ust)
+ captures:
+ - \$ctx.vpid
+ actions:
+ notify
+ - id: T9
+ condition: event rule hit
+ rule: capture-app-ctx (type: tracepoint, domain: ust)
+ captures:
+ - \$app.iga:active_clients
+ actions:
+ notify
+ EOF
+
+ list_triggers "on-event, tracepoint event rule" "${tmp_expected_stdout}"
+
+ stop_lttng_sessiond_notap
+}
+
+test_on_event_probe ()
+{
+ local sys_open_addr
+
+ # shellcheck disable=SC2119
+ start_lttng_sessiond_notap
+
+ sys_open_addr=$(grep ' T do_sys_open$' /proc/kallsyms | cut -f 1 -d ' ')
+
+ add_trigger --condition on-event -k --probe=do_sys_open my_sys_open --action notify
+ add_trigger --condition on-event -k --probe=do_sys_open+10 my_sys_open --action notify
+ add_trigger --condition on-event -k --probe="${sys_open_addr}" my_sys_open --action notify
+ add_trigger --condition on-event -k --probe="${sys_open_addr}+10" my_sys_open --action notify
+
+ cat > "${tmp_expected_stdout}" <<- EOF
+ - id: T1
+ condition: event rule hit
+ rule: my_sys_open (type: probe, location: do_sys_open)
+ actions:
+ notify
+ - id: T2
+ condition: event rule hit
+ rule: my_sys_open (type: probe, location: do_sys_open+10)
+ actions:
+ notify
+ - id: T3
+ condition: event rule hit
+ rule: my_sys_open (type: probe, location: ${sys_open_addr})
+ actions:
+ notify
+ - id: T4
+ condition: event rule hit
+ rule: my_sys_open (type: probe, location: ${sys_open_addr}+10)
+ actions:
+ notify
+ EOF
+
+ list_triggers "on-event, probe event rule" "${tmp_expected_stdout}"
+
+ stop_lttng_sessiond_notap
+}
+
+test_on_event_userspace_probe ()
+{
+ # shellcheck disable=SC2119
+ start_lttng_sessiond_notap
+
+ add_trigger --condition on-event -k --userspace-probe=${uprobe_elf_binary}:test_function ma-probe --action notify
+
+ cat > "${tmp_expected_stdout}" <<- EOF
+ - id: T1
+ condition: event rule hit
+ rule: ma-probe (type: userspace probe, location: ${uprobe_elf_binary}:test_function)
+ actions:
+ notify
+ EOF
+
+ list_triggers "on-event, userspace-probe event rule" "${tmp_expected_stdout}"
+
+ stop_lttng_sessiond_notap
+}
+
+test_on_event_syscall ()
+{
+ # shellcheck disable=SC2119
+ start_lttng_sessiond_notap
+
+ add_trigger --condition on-event -k --syscall open --action notify
+ add_trigger --condition on-event -k --syscall ptrace --filter 'a > 2' --action notify
+
+ cat > "${tmp_expected_stdout}" <<- EOF
+ - id: T1
+ condition: event rule hit
+ - rule: open (type: syscall)
+ actions:
+ notify
+ - id: T2
+ condition: event rule hit
+ - rule: ptrace (type: syscall, filter: a > 2)
+ actions:
+ notify
+ EOF
+
+ list_triggers "on-event, syscall event rule" "${tmp_expected_stdout}"
+
+ stop_lttng_sessiond_notap
+}
+
+test_snapshot_action ()
+{
+ start_lttng_sessiond_notap
+
+ add_trigger --condition on-event -u some-event --action snapshot-session ze-session
+ add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path
+ add_trigger --condition on-event -u some-event --action snapshot-session ze-session file:///some/other/path
+ add_trigger --condition on-event -u some-event --action snapshot-session ze-session net://1.2.3.4
+ add_trigger --condition on-event -u some-event --action snapshot-session ze-session net://1.2.3.4:1234:1235
+ add_trigger --condition on-event -u some-event --action snapshot-session ze-session --ctrl-url=tcp://1.2.3.4:1111 --data-url=tcp://1.2.3.4:1112
+ add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path --max-size=1234
+ add_trigger --condition on-event -u some-event --action snapshot-session ze-session /some/path --name=meh
+
+
+ cat > "${tmp_expected_stdout}" <<- EOF
+ - id: T1
+ condition: event rule hit
+ rule: some-event (type: tracepoint, domain: ust)
+ actions:
+ snapshot session \`ze-session\`
+ - id: T2
+ condition: event rule hit
+ rule: some-event (type: tracepoint, domain: ust)
+ actions:
+ snapshot session \`ze-session\`, path: /some/path
+ - id: T3
+ condition: event rule hit
+ rule: some-event (type: tracepoint, domain: ust)
+ actions:
+ snapshot session \`ze-session\`, path: /some/other/path
+ - id: T4
+ condition: event rule hit
+ rule: some-event (type: tracepoint, domain: ust)
+ actions:
+ snapshot session \`ze-session\`, url: net://1.2.3.4
+ - id: T5
+ condition: event rule hit
+ rule: some-event (type: tracepoint, domain: ust)
+ actions:
+ snapshot session \`ze-session\`, url: net://1.2.3.4:1234:1235
+ - id: T6
+ condition: event rule hit
+ rule: some-event (type: tracepoint, domain: ust)
+ actions:
+ snapshot session \`ze-session\`, control url: tcp://1.2.3.4:1111, data url: tcp://1.2.3.4:1112
+ - id: T7
+ condition: event rule hit
+ rule: some-event (type: tracepoint, domain: ust)
+ actions:
+ snapshot session \`ze-session\`, path: /some/path, max size: 1234
+ - id: T8
+ condition: event rule hit
+ rule: some-event (type: tracepoint, domain: ust)
+ actions:
+ snapshot session \`ze-session\`, path: /some/path, name: meh
+ EOF
+
+ list_triggers "snapshot action" "${tmp_expected_stdout}"
+
+ stop_lttng_sessiond_notap
+}
+
+test_top_level_options
+test_on_event_tracepoint
+skip $ist_root "non-root user: skipping kprobe tests" 7 || test_on_event_probe
+skip $ist_root "non-root user: skipping uprobe tests" 4 || test_on_event_userspace_probe
+skip $ist_root "non-root user: skipping syscall tests" 5 || test_on_event_syscall
+test_snapshot_action
+
+# Cleanup
+rm -f "${tmp_stdout}"
+rm -f "${tmp_stderr}"
+rm -f "${tmp_expected_stdout}"
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) - 2020 EfficiOS, inc
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+# Test the `lttng remove-trigger` command line interface.
+
+CURDIR="$(dirname "$0")"
+TESTDIR="$CURDIR/../../.."
+
+# shellcheck source=../../../utils/utils.sh
+source "$TESTDIR/utils/utils.sh"
+
+plan_tests 17
+
+FULL_LTTNG_BIN="${TESTDIR}/../src/bin/lttng/${LTTNG_BIN}"
+
+tmp_stdout=$(mktemp -t test_list_triggers_cli_stdout.XXXXXX)
+tmp_stderr=$(mktemp -t test_list_triggers_cli_stderr.XXXXXX)
+tmp_expected_stdout=$(mktemp -t test_list_triggers_cli_expected_stdout.XXXXXX)
+
+function add_trigger ()
+{
+ "${FULL_LTTNG_BIN}" add-trigger "$@"
+ ok $? "add trigger \`$*\`: exit code is 0"
+}
+
+function list_triggers ()
+{
+ local test_name="$1"
+ local expected_stdout_file="$2"
+
+ "${FULL_LTTNG_BIN}" list-triggers > "${tmp_stdout}" 2> "${tmp_stderr}"
+ ok $? "${test_name}: exit code is 0"
+
+ diff -u "${expected_stdout_file}" "${tmp_stdout}"
+ ok $? "${test_name}: expected stdout"
+
+ diff -u /dev/null "${tmp_stderr}"
+ ok $? "${test_name}: expected stderr"
+}
+
+function remove_trigger ()
+{
+ local id="$1"
+ local test_name="remove trigger ${id}"
+
+ "${FULL_LTTNG_BIN}" remove-trigger "${id}" > "${tmp_stdout}" 2> "${tmp_stderr}"
+ ok $? "${test_name}: exit code is 0"
+
+ diff -u <(echo "Removed trigger \`${id}\`.") "${tmp_stdout}"
+ ok $? "${test_name}: expected stdout"
+
+ diff -u /dev/null "${tmp_stderr}"
+ ok $? "${test_name}: expected stderr"
+}
+
+# shellcheck disable=SC2119
+start_lttng_sessiond_notap
+
+# Add a few triggers
+add_trigger --condition on-event -u -a --action notify
+add_trigger --id ABC --condition on-event aaa -u --filter 'p == 2' --action notify
+
+cat > "${tmp_expected_stdout}" <<- EOF
+- id: ABC
+ condition: event rule hit
+ rule: aaa (type: tracepoint, domain: ust, filter: p == 2)
+ actions:
+ notify
+- id: T1
+ condition: event rule hit
+ rule: * (type: tracepoint, domain: ust)
+ actions:
+ notify
+EOF
+list_triggers "two triggers left" "${tmp_expected_stdout}"
+
+remove_trigger "ABC"
+
+cat > "${tmp_expected_stdout}" <<- EOF
+- id: T1
+ condition: event rule hit
+ rule: * (type: tracepoint, domain: ust)
+ actions:
+ notify
+EOF
+list_triggers "one trigger left" "${tmp_expected_stdout}"
+
+remove_trigger "T1"
+
+list_triggers "no triggers left" "/dev/null"
+
+# Cleanup
+stop_lttng_sessiond_notap
+rm -f "${tmp_stdout}"
+rm -f "${tmp_stderr}"
+rm -f "${tmp_expected_stdout}"
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) - 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+TESTPOIT_ARGS="CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LTTNG_TESTPOINT_ENABLE=1"
+TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so)
+
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+TESTAPP_NAME="gen-ust-events"
+TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NR_ITER=-1
+NR_USEC_WAIT=5
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+NUM_TESTS=104
+
+source $TESTDIR/utils/utils.sh
+
+function kernel_event_generator_toogle_state
+{
+ kernel_event_generator_suspended=$((kernel_event_generator_suspended==0))
+
+}
+function kernel_event_generator
+{
+ state_file=$1
+ kernel_event_generator_suspended=0
+ trap kernel_event_generator_toogle_state SIGUSR1
+
+ while (true); do
+ if [[ $kernel_event_generator_suspended -eq "1" ]]; then
+ touch $state_file
+ sleep 0.5
+ else
+ if [[ -f $state_file ]]; then
+ rm $state_file 2> /dev/null
+ fi
+ echo -n "1000" > /proc/lttng-test-filter-event 2> /dev/null
+ fi
+ done
+}
+
+function kernel_test
+{
+ local consumerd_pipe=()
+ local event_name="lttng_test_filter_event"
+
+ modprobe lttng-test
+
+ LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}"
+ start_lttng_sessiond_notap
+
+ create_lttng_session_notap $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_channel_notap $SESSION_NAME $CHANNEL_NAME --subbuf-size=$PAGE_SIZE
+ enable_kernel_lttng_event_notap $SESSION_NAME $event_name $CHANNEL_NAME
+
+ #This is needed since the testpoint create a pipe with the consumer type suffixed
+ for f in "$TESTPOINT_BASE_PATH"*; do
+ consumerd_pipe+=("$f")
+ done
+
+ kernel_event_generator $TESTAPP_STATE_PATH &
+ APP_PID=$!
+
+ $CURDIR/notification LTTNG_DOMAIN_KERNEL $SESSION_NAME $CHANNEL_NAME $APP_PID $TESTAPP_STATE_PATH ${consumerd_pipe[@]}
+
+ destroy_lttng_session_notap $SESSION_NAME
+ stop_lttng_sessiond_notap
+
+ kill -9 $APP_PID
+ wait $APP_PID 2> /dev/null
+
+
+ rmmod lttng-test
+
+ rm -rf ${consumerd_pipe[@]} 2> /dev/null
+}
+
+if [ "$(id -u)" == "0" ]; then
+ validate_lttng_modules_present
+ kernel_test
+else
+ # Kernel tests are skipped.
+ plan_tests $NUM_TESTS
+ skip 0 "Root access is needed. Skipping all kernel notification tests." $NUM_TESTS
+fi
+
+# Just in case cleanup
+rm -rf $TRACE_PATH
+rm -rf $TMPDIR
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) - 2017 Jonathan Rajotte-Julien <jonathan.rajotte-julien@efficios.com>
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../../../
+
+TMPDIR=$(mktemp -d)
+
+#This is needed since the testpoint create a pipe with the consumerd type suffixed
+TESTPOINT_BASE_PATH=$(readlink -f "$TMPDIR/lttng.t_p_n")
+TESTPOINT_PIPE_PATH=$(mktemp -u "${TESTPOINT_BASE_PATH}.XXXXXX")
+TESTPOIT_ARGS="CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LTTNG_TESTPOINT_ENABLE=1"
+TESTPOINT=$(readlink -f ${CURDIR}/.libs/libpause_consumer.so)
+
+
+TESTAPP_PATH="$TESTDIR/utils/testapp"
+TESTAPP_NAME="gen-ust-events"
+TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
+TESTAPP_STATE_PATH=$(mktemp -u "$TMPDIR/application_state.XXXXXXXXXX")
+
+NR_ITER=1000
+NR_USEC_WAIT=5
+
+SESSION_NAME="my_session"
+CHANNEL_NAME="my_channel"
+
+TRACE_PATH=$(mktemp -d)
+PAGE_SIZE=$(getconf PAGE_SIZE)
+
+DIR=$(readlink -f $TESTDIR)
+
+
+source $TESTDIR/utils/utils.sh
+
+function ust_event_generator_toogle_state
+{
+ ust_event_generator_suspended=$((ust_event_generator_suspended==0))
+
+}
+function ust_event_generator
+{
+ state_file=$1
+ ust_event_generator_suspended=0
+ trap ust_event_generator_toogle_state SIGUSR1
+
+ while (true); do
+ if [[ $ust_event_generator_suspended -eq "1" ]]; then
+ touch $state_file
+ sleep 0.5
+ else
+ if [[ -f $state_file ]]; then
+ rm -rf $state_file 2> /dev/null
+ fi
+ taskset -c 0 $TESTAPP_BIN $NR_ITER $NR_USEC_WAIT > /dev/null 2>&1
+ fi
+ done
+}
+
+consumerd_pipe=()
+file_sync_after_first_event=$(mktemp -u)
+event_name="tp:tptest"
+
+LTTNG_SESSIOND_ENV_VARS="LTTNG_TESTPOINT_ENABLE=1 CONSUMER_PAUSE_PIPE_PATH=${TESTPOINT_PIPE_PATH} LD_PRELOAD=${TESTPOINT}"
+start_lttng_sessiond_notap
+
+create_lttng_session_notap $SESSION_NAME $TRACE_PATH
+
+enable_ust_lttng_channel_notap $SESSION_NAME $CHANNEL_NAME --subbuf-size=$PAGE_SIZE
+enable_ust_lttng_event_notap $SESSION_NAME $event_name $CHANNEL_NAME
+
+#This is needed since the testpoint create a pipe with the consumer type suffixed
+for f in "$TESTPOINT_BASE_PATH"*; do
+ consumerd_pipe+=("$f")
+done
+
+
+ust_event_generator $TESTAPP_STATE_PATH &
+APP_PID=$!
+
+$CURDIR/notification LTTNG_DOMAIN_UST $SESSION_NAME $CHANNEL_NAME $APP_PID $TESTAPP_STATE_PATH ${consumerd_pipe[@]}
+
+destroy_lttng_session_notap $SESSION_NAME
+stop_lttng_sessiond_notap
+
+# On ungraceful kill the app is cleaned up via the full_cleanup call
+# Suppress kill message
+kill -9 $APP_PID
+wait $APP_PID 2> /dev/null
+
+rm -rf $TMPDIR
+
+# Just in case cleanup
+rm -rf $TRACE_PATH
--- /dev/null
+/*
+ * notification.c
+ *
+ * Tests suite for LTTng notification API
+ *
+ * Copyright (C) 2017 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <assert.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <lttng/action/action.h>
+#include <lttng/action/notify.h>
+#include <lttng/condition/buffer-usage.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/evaluation.h>
+#include <lttng/domain.h>
+#include <lttng/endpoint.h>
+#include <lttng/lttng-error.h>
+#include <lttng/notification/channel.h>
+#include <lttng/notification/notification.h>
+#include <lttng/trigger/trigger.h>
+#include <lttng/lttng.h>
+
+#include <tap/tap.h>
+
+#define NUM_TESTS 104
+
+int nb_args = 0;
+int named_pipe_args_start = 0;
+pid_t app_pid = -1;
+const char *app_state_file = NULL;
+
+static
+void wait_on_file(const char *path, bool file_exist)
+{
+ if (!path) {
+ return;
+ }
+ for (;;) {
+ int ret;
+ struct stat buf;
+
+ ret = stat(path, &buf);
+ if (ret == -1 && errno == ENOENT) {
+ if (file_exist) {
+ (void) poll(NULL, 0, 10); /* 10 ms delay */
+ continue; /* retry */
+ }
+ break; /* File does not exist */
+ }
+ if (ret) {
+ perror("stat");
+ exit(EXIT_FAILURE);
+ }
+ break; /* found */
+ }
+}
+
+int write_pipe(const char *path, uint8_t data)
+{
+ int ret = 0;
+ int fd = 0;
+
+ fd = open(path, O_WRONLY | O_NONBLOCK);
+ if (fd < 0) {
+ perror("Could not open consumer control named pipe");
+ goto end;
+ }
+
+ ret = write(fd, &data , sizeof(data));
+ if (ret < 1) {
+ perror("Named pipe write failed");
+ if (close(fd)) {
+ perror("Named pipe close failed");
+ }
+ ret = -1;
+ goto end;
+ }
+
+ ret = close(fd);
+ if (ret < 0) {
+ perror("Name pipe closing failed");
+ ret = -1;
+ goto end;
+ }
+end:
+ return ret;
+}
+
+int stop_consumer(const char **argv)
+{
+ int ret = 0;
+ for (int i = named_pipe_args_start; i < nb_args; i++) {
+ ret = write_pipe(argv[i], 49);
+ }
+ return ret;
+}
+
+int resume_consumer(const char **argv)
+{
+ int ret = 0;
+ for (int i = named_pipe_args_start; i < nb_args; i++) {
+ ret = write_pipe(argv[i], 0);
+ }
+ return ret;
+}
+
+int suspend_application()
+{
+ int ret;
+ struct stat buf;
+
+ if (!stat(app_state_file, &buf)) {
+ fail("App is already in a suspended state.");
+ ret = -1;
+ goto error;
+ }
+
+ /*
+ * Send SIGUSR1 to application instructing it to bypass tracepoint.
+ */
+ ret = kill(app_pid, SIGUSR1);
+ if (ret) {
+ fail("SIGUSR1 failed. errno %d", errno);
+ ret = -1;
+ goto error;
+ }
+
+ wait_on_file(app_state_file, true);
+
+error:
+ return ret;
+
+}
+
+int resume_application()
+{
+ int ret;
+ struct stat buf;
+
+ ret = stat(app_state_file, &buf);
+ if (ret == -1 && errno == ENOENT) {
+ fail("State file does not exist");
+ goto error;
+ }
+ if (ret) {
+ perror("stat");
+ goto error;
+ }
+
+ ret = kill(app_pid, SIGUSR1);
+ if (ret) {
+ fail("SIGUSR1 failed. errno %d", errno);
+ ret = -1;
+ goto error;
+ }
+
+ wait_on_file(app_state_file, false);
+
+error:
+ return ret;
+
+}
+
+
+void test_triggers_buffer_usage_condition(const char *session_name,
+ const char *channel_name,
+ enum lttng_domain_type domain_type,
+ enum lttng_condition_type condition_type)
+{
+ enum lttng_condition_status condition_status;
+ struct lttng_action *action;
+
+ /* Set-up */
+ action = lttng_action_notify_create();
+ if (!action) {
+ fail("Setup error on action creation");
+ goto end;
+ }
+
+ /* Test lttng_register_trigger with null value */
+ ok(lttng_register_trigger(NULL) == -LTTNG_ERR_INVALID, "Registering a NULL trigger fails as expected");
+
+ /* Test: register a trigger */
+ unsigned int test_vector_size = 5;
+ for (unsigned int i = 0; i < pow(2,test_vector_size); i++) {
+ int loop_ret = 0;
+ char *test_tuple_string = NULL;
+ unsigned int mask_position = 0;
+ bool session_name_set = false;
+ bool channel_name_set = false;
+ bool threshold_ratio_set = false;
+ bool threshold_byte_set = false;
+ bool domain_type_set = false;
+
+ struct lttng_trigger *trigger = NULL;
+ struct lttng_condition *condition = NULL;
+
+ /* Create base condition */
+ switch (condition_type) {
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+ condition = lttng_condition_buffer_usage_low_create();
+ break;
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+ condition = lttng_condition_buffer_usage_high_create();
+ break;
+ default:
+ loop_ret = 1;
+ goto loop_end;
+ }
+
+ if (!condition) {
+ loop_ret = 1;
+ goto loop_end;
+
+ }
+
+ /* Prepare the condition for trigger registration test */
+
+ /* Set session name */
+ if ((1 << mask_position) & i) {
+ condition_status = lttng_condition_buffer_usage_set_session_name(
+ condition, session_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+ session_name_set = true;
+ }
+ mask_position++;
+
+ /* Set channel name */
+ if ((1 << mask_position) & i) {
+ condition_status = lttng_condition_buffer_usage_set_channel_name(
+ condition, channel_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+ channel_name_set = true;
+ }
+ mask_position++;
+
+ /* Set threshold ratio */
+ if ((1 << mask_position) & i) {
+ condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
+ condition, 0.0);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+ threshold_ratio_set = true;
+ }
+ mask_position++;
+
+ /* Set threshold byte */
+ if ((1 << mask_position) & i) {
+ condition_status = lttng_condition_buffer_usage_set_threshold(
+ condition, 0);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+ threshold_byte_set = true;
+ }
+ mask_position++;
+
+ /* Set domain type */
+ if ((1 << mask_position) & i) {
+ condition_status = lttng_condition_buffer_usage_set_domain_type(
+ condition, LTTNG_DOMAIN_UST);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+ domain_type_set = true;
+ }
+
+ /* Safety check */
+ if (mask_position != test_vector_size -1) {
+ assert("Logic error for test vector generation");
+ }
+
+ loop_ret = asprintf(&test_tuple_string, "session name %s, channel name %s, threshold ratio %s, threshold byte %s, domain type %s",
+ session_name_set ? "set" : "unset",
+ channel_name_set ? "set" : "unset",
+ threshold_ratio_set ? "set" : "unset",
+ threshold_byte_set ? "set" : "unset",
+ domain_type_set? "set" : "unset");
+ if (!test_tuple_string || loop_ret < 0) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+
+ /* Create trigger */
+ trigger = lttng_trigger_create(condition, action);
+ if (!trigger) {
+ loop_ret = 1;
+ goto loop_end;
+ }
+
+ loop_ret = lttng_register_trigger(trigger);
+
+loop_end:
+ if (loop_ret == 1) {
+ fail("Setup error occurred for tuple: %s", test_tuple_string);
+ goto loop_cleanup;
+ }
+
+ /* This combination happens three times */
+ if (session_name_set && channel_name_set
+ && (threshold_ratio_set || threshold_byte_set)
+ && domain_type_set) {
+ ok(loop_ret == 0, "Trigger is registered: %s", test_tuple_string);
+
+ /*
+ * Test that a trigger cannot be registered
+ * multiple time.
+ */
+ loop_ret = lttng_register_trigger(trigger);
+ ok(loop_ret == -LTTNG_ERR_TRIGGER_EXISTS, "Re-register trigger fails as expected: %s", test_tuple_string);
+
+ /* Test that a trigger can be unregistered */
+ loop_ret = lttng_unregister_trigger(trigger);
+ ok(loop_ret == 0, "Unregister trigger: %s", test_tuple_string);
+
+ /*
+ * Test that unregistration of a non-previously
+ * registered trigger fail.
+ */
+ loop_ret = lttng_unregister_trigger(trigger);
+ ok(loop_ret == -LTTNG_ERR_TRIGGER_NOT_FOUND, "Unregister of a non-registerd trigger fails as expected: %s", test_tuple_string);
+ } else {
+ ok(loop_ret == -LTTNG_ERR_INVALID_TRIGGER, "Trigger is invalid as expected and cannot be registered: %s", test_tuple_string);
+ }
+
+loop_cleanup:
+ free(test_tuple_string);
+ lttng_trigger_destroy(trigger);
+ lttng_condition_destroy(condition);
+ }
+
+end:
+ lttng_action_destroy(action);
+}
+
+static
+void wait_data_pending(const char *session_name)
+{
+ int ret;
+
+ do {
+ ret = lttng_data_pending(session_name);
+ assert(ret >= 0);
+ } while (ret != 0);
+}
+
+void test_notification_channel(const char *session_name, const char *channel_name, const enum lttng_domain_type domain_type, const char **argv)
+{
+ int ret = 0;
+ enum lttng_condition_status condition_status;
+ enum lttng_notification_channel_status nc_status;
+
+ struct lttng_action *action = NULL;
+ struct lttng_notification *notification = NULL;
+ struct lttng_notification_channel *notification_channel = NULL;
+ struct lttng_trigger *trigger = NULL;
+
+ struct lttng_condition *low_condition = NULL;
+ struct lttng_condition *high_condition = NULL;
+ struct lttng_condition *dummy_invalid_condition = NULL;
+ struct lttng_condition *dummy_condition = NULL;
+
+ double low_ratio = 0.0;
+ double high_ratio = 0.99;
+
+ /* Set-up */
+ action = lttng_action_notify_create();
+ if (!action) {
+ fail("Setup error on action creation");
+ goto end;
+ }
+
+ /* Create a dummy, empty condition for later test */
+ dummy_invalid_condition = lttng_condition_buffer_usage_low_create();
+ if (!dummy_invalid_condition) {
+ fail("Setup error on condition creation");
+ goto end;
+ }
+
+ /* Create a valid dummy condition with a ratio of 0.5 */
+ dummy_condition = lttng_condition_buffer_usage_low_create();
+ if (!dummy_condition) {
+ fail("Setup error on dummy_condition creation");
+ goto end;
+
+ }
+ condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
+ dummy_condition, 0.5);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on condition creation");
+ goto end;
+ }
+
+ condition_status = lttng_condition_buffer_usage_set_session_name(
+ dummy_condition, session_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on dummy_condition creation");
+ goto end;
+ }
+ condition_status = lttng_condition_buffer_usage_set_channel_name(
+ dummy_condition, channel_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on dummy_condition creation");
+ goto end;
+ }
+ condition_status = lttng_condition_buffer_usage_set_domain_type(
+ dummy_condition, domain_type);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on dummy_condition creation");
+ goto end;
+ }
+
+ /* Register a low condition with a ratio */
+ low_condition = lttng_condition_buffer_usage_low_create();
+ if (!low_condition) {
+ fail("Setup error on low_condition creation");
+ goto end;
+ }
+ condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
+ low_condition, low_ratio);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on low_condition creation");
+ goto end;
+ }
+
+ condition_status = lttng_condition_buffer_usage_set_session_name(
+ low_condition, session_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on low_condition creation");
+ goto end;
+ }
+ condition_status = lttng_condition_buffer_usage_set_channel_name(
+ low_condition, channel_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on low_condition creation");
+ goto end;
+ }
+ condition_status = lttng_condition_buffer_usage_set_domain_type(
+ low_condition, domain_type);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on low_condition creation");
+ goto end;
+
+ }
+
+ /* Register a high condition with a ratio */
+ high_condition = lttng_condition_buffer_usage_high_create();
+ if (!high_condition) {
+ fail("Setup error on high_condition creation");
+ goto end;
+ }
+
+ condition_status = lttng_condition_buffer_usage_set_threshold_ratio(
+ high_condition, high_ratio);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on high_condition creation");
+ goto end;
+ }
+
+ condition_status = lttng_condition_buffer_usage_set_session_name(
+ high_condition, session_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on high_condition creation");
+ goto end;
+ }
+ condition_status = lttng_condition_buffer_usage_set_channel_name(
+ high_condition, channel_name);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on high_condition creation");
+ goto end;
+ }
+ condition_status = lttng_condition_buffer_usage_set_domain_type(
+ high_condition, domain_type);
+ if (condition_status != LTTNG_CONDITION_STATUS_OK) {
+ fail("Setup error on high_condition creation");
+ goto end;
+ }
+
+ /* Register the triggers for low and high condition */
+ trigger = lttng_trigger_create(low_condition, action);
+ if (!trigger) {
+ fail("Setup error on low trigger creation");
+ goto end;
+ }
+
+ ret = lttng_register_trigger(trigger);
+ if (ret) {
+ fail("Setup error on low trigger registration");
+ goto end;
+ }
+
+ lttng_trigger_destroy(trigger);
+ trigger = NULL;
+
+ trigger = lttng_trigger_create(high_condition, action);
+ if (!trigger) {
+ fail("Setup error on high trigger creation");
+ goto end;
+ }
+
+ ret = lttng_register_trigger(trigger);
+ if (ret) {
+ fail("Setup error on high trigger registration");
+ goto end;
+ }
+
+ /* Begin testing */
+ notification_channel = lttng_notification_channel_create(lttng_session_daemon_notification_endpoint);
+ ok(notification_channel, "Notification channel object creation");
+ if (!notification_channel) {
+ goto end;
+ }
+
+ /* Basic error path check */
+ nc_status = lttng_notification_channel_subscribe(NULL, NULL);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NULL");
+
+ nc_status = lttng_notification_channel_subscribe(notification_channel, NULL);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NON-NULL, NULL");
+
+ nc_status = lttng_notification_channel_subscribe(NULL, low_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Notification channel subscription is invalid: NULL, NON-NULL");
+
+ nc_status = lttng_notification_channel_subscribe(notification_channel, dummy_invalid_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Subscribing to an invalid condition");
+
+ nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_invalid_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID, "Unsubscribing from an invalid condition");
+
+ nc_status = lttng_notification_channel_unsubscribe(notification_channel, dummy_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION, "Unsubscribing from a valid unknown condition");
+
+ /* Subscribe a valid low condition */
+ nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition");
+
+ /* Subscribe a valid high condition */
+ nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Subscribe to condition");
+
+ nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done");
+
+ nc_status = lttng_notification_channel_subscribe(notification_channel, high_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED, "Subscribe to a condition for which subscription was already done");
+
+ /* Wait for notification to happen */
+ stop_consumer(argv);
+ lttng_start_tracing(session_name);
+
+ /* Wait for high notification */
+ nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK
+ && notification
+ && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH,
+ "High notification received after intermediary communication");
+ lttng_notification_destroy(notification);
+ notification = NULL;
+
+ suspend_application();
+ lttng_stop_tracing_no_wait(session_name);
+ resume_consumer(argv);
+ wait_data_pending(session_name);
+
+ /*
+ * Test that communication still work even if there is notification
+ * waiting for consumption.
+ */
+
+ nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe with pending notification");
+
+ nc_status = lttng_notification_channel_subscribe(notification_channel, low_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "subscribe with pending notification");
+
+ nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK
+ && notification
+ && lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW,
+ "Low notification received after intermediary communication");
+ lttng_notification_destroy(notification);
+ notification = NULL;
+
+ /* Stop consumer to force a high notification */
+ stop_consumer(argv);
+ resume_application();
+ lttng_start_tracing(session_name);
+
+ nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification &&
+ lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH,
+ "High notification received after intermediary communication");
+ lttng_notification_destroy(notification);
+ notification = NULL;
+
+ suspend_application();
+ lttng_stop_tracing_no_wait(session_name);
+ resume_consumer(argv);
+ wait_data_pending(session_name);
+
+ nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification &&
+ lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW,
+ "Low notification received after re-subscription");
+ lttng_notification_destroy(notification);
+ notification = NULL;
+
+ stop_consumer(argv);
+ resume_application();
+ /* Stop consumer to force a high notification */
+ lttng_start_tracing(session_name);
+
+ nc_status = lttng_notification_channel_get_next_notification(notification_channel, ¬ification);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK && notification &&
+ lttng_condition_get_type(lttng_notification_get_condition(notification)) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH,
+ "High notification");
+ lttng_notification_destroy(notification);
+ notification = NULL;
+
+ /* Resume consumer to allow event consumption */
+ suspend_application();
+ lttng_stop_tracing_no_wait(session_name);
+ resume_consumer(argv);
+ wait_data_pending(session_name);
+
+ nc_status = lttng_notification_channel_unsubscribe(notification_channel, low_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe low condition with pending notification");
+ nc_status = lttng_notification_channel_unsubscribe(notification_channel, high_condition);
+ ok(nc_status == LTTNG_NOTIFICATION_CHANNEL_STATUS_OK, "Unsubscribe high condition with pending notification");
+
+end:
+ lttng_notification_channel_destroy(notification_channel);
+ lttng_trigger_destroy(trigger);
+ lttng_action_destroy(action);
+ lttng_condition_destroy(low_condition);
+ lttng_condition_destroy(high_condition);
+ lttng_condition_destroy(dummy_invalid_condition);
+ lttng_condition_destroy(dummy_condition);
+}
+
+int main(int argc, const char *argv[])
+{
+ const char *session_name = NULL;
+ const char *channel_name = NULL;
+ const char *domain_type_string = NULL;
+ enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
+
+ plan_tests(NUM_TESTS);
+
+ /* Argument 6 and upward are named pipe location for consumerd control */
+ named_pipe_args_start = 6;
+
+ if (argc < 7) {
+ fail("Missing parameter for tests to run %d", argc);
+ goto error;
+ }
+
+ nb_args = argc;
+
+ domain_type_string = argv[1];
+ session_name = argv[2];
+ channel_name = argv[3];
+ app_pid = (pid_t) atoi(argv[4]);
+ app_state_file = argv[5];
+
+ if (!strcmp("LTTNG_DOMAIN_UST", domain_type_string)) {
+ domain_type = LTTNG_DOMAIN_UST;
+ }
+ if (!strcmp("LTTNG_DOMAIN_KERNEL", domain_type_string)) {
+ domain_type = LTTNG_DOMAIN_KERNEL;
+ }
+ if (domain_type == LTTNG_DOMAIN_NONE) {
+ fail("Unknown domain type");
+ goto error;
+ }
+
+ diag("Test trigger for domain %s with buffer_usage_low condition", domain_type_string);
+ test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW);
+ diag("Test trigger for domain %s with buffer_usage_high condition", domain_type_string);
+ test_triggers_buffer_usage_condition(session_name, channel_name, domain_type, LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH);
+
+ diag("Test notification channel api for domain %s", domain_type_string);
+ test_notification_channel(session_name, channel_name, domain_type, argv);
+error:
+ return exit_status();
+}
+
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+
+AM_CPPFLAGS += -I$(srcdir) -I$(top_srcdir)/tests/utils
+LIBLTTNG_CTL=$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la
+
+noinst_PROGRAMS = notification-client
+notification_client_SOURCES = notification-client.c
+notification_client_LDADD = $(UST_LIBS) $(DL_LIBS) $(LIBLTTNG_CTL) \
+ $(top_builddir)/tests/utils/libtestutils.la
--- /dev/null
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <lttng/condition/event-rule.h>
+#include <lttng/lttng.h>
+
+#include "utils.h"
+
+static struct option long_options[] =
+{
+ /* These options set a flag. */
+ {"trigger", required_argument, 0, 'i'},
+ {"sync-after-notif-register", required_argument, 0, 'a'},
+ {0, 0, 0, 0}
+};
+
+static bool action_group_contains_notify(
+ const struct lttng_action *action_group)
+{
+ unsigned int i, count;
+ enum lttng_action_status status =
+ lttng_action_group_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_group_get_at_index_const(
+ 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 bool is_expected_trigger_name(const char *expected_trigger_name,
+ struct lttng_notification *notification)
+{
+ int ret = false;
+ 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:
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+ case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+ case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+ break;
+ case LTTNG_CONDITION_TYPE_EVENT_RULE_HIT:
+ {
+ const char *trigger_name;
+ enum lttng_evaluation_status evaluation_status;
+
+ evaluation_status =
+ lttng_evaluation_event_rule_get_trigger_name(
+ evaluation, &trigger_name);
+ if (evaluation_status != LTTNG_EVALUATION_STATUS_OK) {
+ fprintf(stderr, "Failed to get trigger name of event rule notification\n");
+ ret = -1;
+ break;
+ }
+
+ ret = true;
+ break;
+ }
+ default:
+ fprintf(stderr, "Unknown notification type (%d)\n", type);
+ }
+
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ int option;
+ int option_index;
+ const char *expected_trigger_name = NULL;
+ struct lttng_triggers *triggers = NULL;
+ unsigned int count, i, subcription_count = 0;
+ enum lttng_trigger_status trigger_status;
+ char *after_notif_register_file_path = NULL;
+ struct lttng_notification_channel *notification_channel = NULL;
+
+ while ((option = getopt_long(argc, argv, "a:t:",
+ long_options, &option_index)) != -1) {
+ switch (option) {
+ case 'a':
+ after_notif_register_file_path = strdup(optarg);
+ break;
+ case 't':
+ expected_trigger_name = strdup(optarg);
+ break;
+ case '?':
+ /* getopt_long already printed an error message. */
+ default:
+ ret = -1;
+ goto end;
+ }
+ }
+
+ if (optind != argc) {
+ ret = -1;
+ goto end;
+ }
+
+
+ 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) {
+ 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;
+
+ lttng_trigger_get_name(trigger, &trigger_name);
+ if (strcmp(trigger_name, expected_trigger_name)) {
+ continue;
+ }
+
+ if (!((action_type == LTTNG_ACTION_TYPE_GROUP &&
+ action_group_contains_notify(action)) ||
+ action_type == LTTNG_ACTION_TYPE_NOTIFY)) {
+ /* "The action of trigger is not notify, skipping. */
+ continue;
+ }
+
+ channel_status = lttng_notification_channel_subscribe(
+ notification_channel, condition);
+ if (channel_status) {
+ fprintf(stderr, "Failed to subscribe to notifications of trigger \"%s\"\n",
+ trigger_name);
+ ret = -1;
+ goto end;
+ }
+
+ subcription_count++;
+ }
+
+ if (subcription_count == 0) {
+ printf("No matching trigger with a notify action found.\n");
+ ret = 0;
+ goto end;
+ }
+
+
+ /*
+ * We registered to the notification of our target trigger. We can now
+ * create the sync file to signify that we are ready.
+ */
+ ret = create_file(after_notif_register_file_path);
+ if (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 = is_expected_trigger_name(expected_trigger_name,
+ notification);
+ lttng_notification_destroy(notification);
+ if (ret) {
+ ret = 0;
+ goto end;
+ }
+ }
+end:
+ lttng_triggers_destroy(triggers);
+ lttng_notification_channel_destroy(notification_channel);
+ return !!ret;
+}
start_lttng_relayd_opt 1 "-d" "--working-directory $working_dir"
- pid=$(pgrep "$RELAYD_MATCH")
+ pid=$(lttng_pgrep "$RELAYD_MATCH")
ok $? "Found lttng-relayd"
cwd=$(readlink "/proc/${pid}/cwd")
start_lttng_relayd_opt 1 "-d" ""
- pid=$(pgrep "$RELAYD_MATCH")
+ pid=$(lttng_pgrep "$RELAYD_MATCH")
ok $? "Found lttng-relayd"
cwd=$(readlink "/proc/${pid}/cwd")
start_lttng_relayd_opt 1 "-b" "--working-directory $working_dir"
- pid=$(pgrep "$RELAYD_MATCH")
+ pid=$(lttng_pgrep "$RELAYD_MATCH")
ok $? "Found lttng-relayd"
cwd=$(readlink "/proc/${pid}/cwd")
start_lttng_relayd_opt 1 "-b" ""
- pid=$(pgrep "$RELAYD_MATCH")
+ pid=$(lttng_pgrep "$RELAYD_MATCH")
ok $? "Found lttng-relayd"
cwd=$(readlink "/proc/${pid}/cwd")
ERROR_OUTPUT_DEST=$(mktemp)
start_lttng_relayd_opt 1 "-b" "-v --working-dir $working_dir"
- pid=$(pgrep "$RELAYD_MATCH")
+ pid=$(lttng_pgrep "$RELAYD_MATCH")
ok $? "Found lttng-relayd"
cwd=$(readlink "/proc/${pid}/cwd")
test $? -eq "1"
ok $? "Expect failure to start lttng-relayd for non-existent working directory"
- pid=$(pgrep "$RELAYD_MATCH")
+ pid=$(lttng_pgrep "$RELAYD_MATCH")
if [ -z "$pid" ]; then
pass "No lttng-relayd present"
else
export LTTNG_RELAYD_WORKING_DIRECTORY=${working_dir}
start_lttng_relayd_opt 1 "-b" ""
- pid=$(pgrep "$RELAYD_MATCH")
+ pid=$(lttng_pgrep "$RELAYD_MATCH")
ok $? "Found lttng-relayd"
cwd=$(readlink "/proc/$pid/cwd")
export LTTNG_RELAYD_WORKING_DIRECTORY=${working_dir_env}
start_lttng_relayd_opt 1 "-b" "--working-dir ${working_dir_cmdline}"
- pid=$(pgrep "$RELAYD_MATCH")
+ pid=$(lttng_pgrep "$RELAYD_MATCH")
ok $? "Found lttng-relayd"
cwd=$(readlink "/proc/$pid/cwd")
function check_sessiond()
{
- if [ -z "$(pgrep --full lt-lttng-sessiond)" ]; then
+ if [ -z "$(lttng_pgrep lt-lttng-sessiond)" ]; then
local str_date=$(date +%H%M%S-%d%m%Y)
diag "!!!The session daemon died unexpectedly!!!"
BAIL_OUT "*** Kernel too old for session daemon tests ***"
fi
- if [ -z $(pgrep --full lt-$SESSIOND_BIN) ]; then
+ if [ -z $(lttng_pgrep lt-$SESSIOND_BIN) ]; then
# We have to start it like this so the ulimit -c is used by this
# process. Also, we collect any error message printed out.
$TESTDIR/../src/bin/lttng-sessiond/$SESSIOND_BIN --quiet --background --consumerd32-path="$TESTDIR/../src/bin/lttng-consumerd/lttng-consumerd" --consumerd64-path="$TESTDIR/../src/bin/lttng-consumerd/lttng-consumerd" >$LOG_FILE 2>&1
function check_sessiond()
{
- if [ -z "$(pgrep --full lt-lttng-sessiond)" ]; then
+ if [ -z "$(lttng_pgrep lt-lttng-sessiond)" ]; then
local str_date=$(date +%H%M%S-%d%m%Y)
diag "!!!The session daemon died unexpectedly!!!"
function check_relayd()
{
- if [ -z "$(pgrep --full lt-lttng-relayd)" ]; then
+ if [ -z "$(lttng_pgrep lt-lttng-relayd)" ]; then
local str_date=$(date +%H%M%S-%d%m%Y)
diag "!!!The relay daemon died unexpectedly!!!"
BAIL_OUT "*** Kernel too old for session daemon tests ***"
fi
- if [ -z $(pgrep --full lt-$SESSIOND_BIN) ]; then
+ if [ -z $(lttng_pgrep lt-$SESSIOND_BIN) ]; then
# We have to start it like this so the ulimit -c is used by this
# process. Also, we collect any error message printed out.
$TESTDIR/../src/bin/lttng-sessiond/$SESSIOND_BIN --quiet --background --consumerd32-path="$TESTDIR/../src/bin/lttng-consumerd/lttng-consumerd" --consumerd64-path="$TESTDIR/../src/bin/lttng-consumerd/lttng-consumerd" >$LOG_FILE_SESSIOND 2>&1
{
local opt=$1
- if [ -z $(pgrep --full lt-$RELAYD_BIN) ]; then
+ if [ -z $(lttng_pgrep lt-$RELAYD_BIN) ]; then
$TESTDIR/../src/bin/lttng-relayd/$RELAYD_BIN $opt >$LOG_FILE_RELAYD 2>&1 &
ok $? "Start lttng-relayd (opt: \"$opt\")"
fi
function check_sessiond()
{
- if [ -z "$(pgrep --full lt-lttng-sessiond)" ]; then
+ if [ -z "$(lttng_pgrep lt-lttng-sessiond)" ]; then
local str_date=$(date +%H%M%S-%d%m%Y)
diag "!!!The session daemon died unexpectedly!!!"
BAIL_OUT "*** Kernel too old for session daemon tests ***"
fi
- if [ -z $(pgrep --full lt-$SESSIOND_BIN) ]; then
+ if [ -z $(lttng_pgrep lt-$SESSIOND_BIN) ]; then
# We have to start it like this so the ulimit -c is used by this
# process. Also, we collect any error message printed out.
#$TESTDIR/../src/bin/lttng-sessiond/$SESSIOND_BIN --quiet --background --consumerd32-path="$TESTDIR/../src/bin/lttng-consumerd/lttng-consumerd" --consumerd64-path="$TESTDIR/../src/bin/lttng-consumerd/lttng-consumerd" >$LOG_FILE_SESSIOND 2>&1
{
local opt=$1
- if [ -z $(pgrep --full lt-$RELAYD_BIN) ]; then
+ if [ -z $(lttng_pgrep lt-$RELAYD_BIN) ]; then
$TESTDIR/../src/bin/lttng-relayd/$RELAYD_BIN $opt >$LOG_FILE_RELAYD 2>&1 &
ok $? "Start lttng-relayd (opt: \"$opt\")"
fi
function check_relayd()
{
- if [ -z "$(pgrep --full lt-lttng-relayd)" ]; then
+ if [ -z "$(lttng_pgrep lt-lttng-relayd)" ]; then
local str_date=$(date +%H%M%S-%d%m%Y)
#diag "Relay daemon died. Starting it again"
test_utils_compat_poll \
test_string_utils \
test_notification \
+ test_event_rule \
test_directory_handle \
test_relayd_backward_compat_group_by_session \
ini_config/test_ini_config \
test_fd_tracker \
test_uuid \
- test_buffer_view
+ test_buffer_view \
+ test_event_expr_to_bytecode
LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la
test_string_utils test_notification test_directory_handle \
test_relayd_backward_compat_group_by_session \
test_fd_tracker test_uuid \
- test_buffer_view
+ test_buffer_view test_fd_tracker test_event_rule test_condition \
+ test_event_expr_to_bytecode
if HAVE_LIBLTTNG_UST_CTL
noinst_PROGRAMS += test_ust_data
$(top_builddir)/src/bin/lttng-sessiond/kernel.$(OBJEXT) \
$(top_builddir)/src/bin/lttng-sessiond/ht-cleanup.$(OBJEXT) \
$(top_builddir)/src/bin/lttng-sessiond/notification-thread.$(OBJEXT) \
+ $(top_builddir)/src/bin/lttng-sessiond/action-executor.$(OBJEXT) \
$(top_builddir)/src/bin/lttng-sessiond/lttng-syscall.$(OBJEXT) \
$(top_builddir)/src/bin/lttng-sessiond/channel.$(OBJEXT) \
$(top_builddir)/src/bin/lttng-sessiond/agent.$(OBJEXT) \
test_session_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBRELAYD) $(LIBSESSIOND_COMM) \
$(LIBHASHTABLE) $(DL_LIBS) -lrt -lurcu-common -lurcu \
$(KMOD_LIBS) \
+ -lmsgpackc \
$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \
$(top_builddir)/src/common/kernel-ctl/libkernel-ctl.la \
$(top_builddir)/src/common/compat/libcompat.la \
$(LIBHASHTABLE) $(DL_LIBS) -lrt -lurcu-common -lurcu \
$(UST_CTL_LIBS) \
$(KMOD_LIBS) \
+ -lmsgpackc \
$(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \
$(top_builddir)/src/common/kernel-ctl/libkernel-ctl.la \
$(top_builddir)/src/common/compat/libcompat.la \
# Notification api
test_notification_SOURCES = test_notification.c
test_notification_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(DL_LIBS)
+#
+# Event rule api
+test_event_rule_SOURCES = test_event_rule.c
+test_event_rule_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS)
+
+# Condition api
+test_condition_SOURCES = test_condition.c
+test_condition_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBLTTNG_CTL) $(DL_LIBS)
# relayd backward compat for groou-by-session utilities
test_relayd_backward_compat_group_by_session_SOURCES = test_relayd_backward_compat_group_by_session.c
# buffer view unit test
test_buffer_view_SOURCES = test_buffer_view.c
test_buffer_view_LDADD = $(LIBTAP) $(LIBCOMMON)
+
+# Event expression to bytecode test
+test_event_expr_to_bytecode_SOURCES = test_event_expr_to_bytecode.c
+test_event_expr_to_bytecode_LDADD = $(LIBTAP) $(LIBLTTNG_CTL) $(LIBCOMMON)
--- /dev/null
+/*
+ * test_condition.c
+ *
+ * Unit tests for the condition API.
+ *
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <tap/tap.h>
+
+#include <lttng/event.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule.h>
+#include <lttng/domain.h>
+#include <common/dynamic-buffer.h>
+#include <common/buffer-view.h>
+
+/* For error.h */
+int lttng_opt_quiet = 1;
+int lttng_opt_verbose;
+int lttng_opt_mi;
+
+#define NUM_TESTS 3
+
+static
+void test_condition_event_rule(void)
+{
+ int ret;
+ struct lttng_event_rule *tracepoint = NULL;
+ const struct lttng_event_rule *tracepoint_tmp = NULL;
+ enum lttng_event_rule_status status;
+ struct lttng_condition *condition = NULL;
+ struct lttng_condition *condition_from_buffer = NULL;
+ enum lttng_condition_status condition_status;
+ const char *pattern="my_event_*";
+ const char *filter="msg_id == 23 && size >= 2048";
+ const char *exclusions[] = {"my_event_test1", "my_event_test2" ,"my_event_test3"};
+ struct lttng_dynamic_buffer buffer;
+ struct lttng_buffer_view view;
+
+ lttng_dynamic_buffer_init(&buffer);
+
+ tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_UST);
+ ok(tracepoint, "tracepoint UST_DOMAIN");
+
+ status = lttng_event_rule_tracepoint_set_pattern(tracepoint, pattern);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern");
+
+ status = lttng_event_rule_tracepoint_set_filter(tracepoint, filter);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter");
+
+ status = lttng_event_rule_tracepoint_set_loglevel_range(tracepoint, LTTNG_LOGLEVEL_WARNING);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting range loglevel");
+
+ status = lttng_event_rule_tracepoint_set_exclusions(tracepoint, 3, exclusions);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting exclusions");
+
+ condition = lttng_condition_event_rule_create(tracepoint);
+ ok(condition, "created condition");
+
+ condition_status = lttng_condition_event_rule_get_rule(condition, &tracepoint_tmp);
+ ok(condition_status == LTTNG_CONDITION_STATUS_OK, "getting event rule");
+ ok(tracepoint == tracepoint_tmp, "Ownership transfer is good");
+
+ ret = lttng_condition_serialize(condition, &buffer, NULL);
+ ok(ret == 0, "Condition serialized");
+
+ view = lttng_buffer_view_from_dynamic_buffer(&buffer, 0, -1);
+ (void) lttng_condition_create_from_buffer(&view, &condition_from_buffer);
+ ok(condition_from_buffer, "condition from buffer is non null");
+
+ ok(lttng_condition_is_equal(condition, condition_from_buffer), "serialized and from buffer are equal");
+
+ lttng_dynamic_buffer_reset(&buffer);
+ lttng_condition_destroy(condition);
+ lttng_condition_destroy(condition_from_buffer);
+}
+
+int main(int argc, const char *argv[])
+{
+ plan_tests(NUM_TESTS);
+ test_condition_event_rule();
+ return exit_status();
+}
--- /dev/null
+/*
+ * Copyright 2020 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include <lttng/event-expr.h>
+#include <common/event-expr-to-bytecode.h>
+#include <common/bytecode/bytecode.h>
+#include <tap/tap.h>
+
+#define NR_TESTS 4
+
+static
+void test_event_payload_field(void)
+{
+ struct lttng_event_expr *event_expr;
+ struct lttng_bytecode *bytecode = NULL;
+ int ret;
+
+ event_expr = lttng_event_expr_event_payload_field_create("tourlou");
+ ret = lttng_event_expr_to_bytecode(event_expr, &bytecode);
+
+ ok(ret == 0, "event payload field");
+
+ lttng_event_expr_destroy(event_expr);
+ free(bytecode);
+}
+
+static
+void test_channel_context_field(void)
+{
+ struct lttng_event_expr *event_expr;
+ struct lttng_bytecode *bytecode = NULL;
+ int ret;
+
+ event_expr = lttng_event_expr_channel_context_field_create("tourlou");
+ ret = lttng_event_expr_to_bytecode(event_expr, &bytecode);
+
+ ok(ret == 0, "channel context field");
+
+ lttng_event_expr_destroy(event_expr);
+ free(bytecode);
+}
+
+static
+void test_app_specific_context_field(void)
+{
+ struct lttng_event_expr *event_expr;
+ struct lttng_bytecode *bytecode = NULL;
+ int ret;
+
+ event_expr = lttng_event_expr_app_specific_context_field_create("Bob", "Leponge");
+ ret = lttng_event_expr_to_bytecode(event_expr, &bytecode);
+
+ ok(ret == 0, "app-specific context field");
+
+ lttng_event_expr_destroy(event_expr);
+ free(bytecode);
+}
+
+static
+void test_array_field_element(void)
+{
+ struct lttng_event_expr *event_expr;
+ struct lttng_bytecode *bytecode = NULL;
+ int ret;
+
+ event_expr = lttng_event_expr_event_payload_field_create("allo");
+ event_expr = lttng_event_expr_array_field_element_create(event_expr, 168);
+ ret = lttng_event_expr_to_bytecode(event_expr, &bytecode);
+
+ ok(ret == 0, "array field element");
+
+ lttng_event_expr_destroy(event_expr);
+ free(bytecode);
+}
+
+int main(void)
+{
+ plan_tests(NR_TESTS);
+
+ test_event_payload_field();
+ test_channel_context_field();
+ test_app_specific_context_field();
+ test_array_field_element();
+
+ return exit_status();
+}
--- /dev/null
+/*
+ * test_event_rule.c
+ *
+ * Unit tests for the notification API.
+ *
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <tap/tap.h>
+
+#include <lttng/event.h>
+#include <lttng/event-rule/tracepoint.h>
+#include <lttng/event-rule/tracepoint-internal.h>
+#include <lttng/domain.h>
+#include <common/dynamic-buffer.h>
+#include <common/buffer-view.h>
+
+/* For error.h */
+int lttng_opt_quiet = 1;
+int lttng_opt_verbose;
+int lttng_opt_mi;
+
+#define NUM_TESTS 31
+
+static
+void test_event_rule_tracepoint_ust(void)
+{
+ int ret;
+ unsigned int count;
+ struct lttng_event_rule *tracepoint = NULL;
+ struct lttng_event_rule *tracepoint_from_buffer = NULL;
+ enum lttng_event_rule_status status;
+ enum lttng_domain_type domain_type;
+ enum lttng_loglevel_type loglevel_type;
+ const char *pattern="my_event_*";
+ const char *filter="msg_id == 23 && size >= 2048";
+ const char *tmp;
+ const char *exclusions[] = {"my_event_test1", "my_event_test2" ,"my_event_test3"};
+ struct lttng_dynamic_buffer buffer;
+ struct lttng_buffer_view view;
+
+ lttng_dynamic_buffer_init(&buffer);
+
+ tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_UST);
+ ok(tracepoint, "tracepoint UST_DOMAIN");
+
+ status = lttng_event_rule_tracepoint_get_domain_type(tracepoint, &domain_type);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get tracepoint domain");
+ ok(domain_type == LTTNG_DOMAIN_UST, "domain is UST");
+
+ status = lttng_event_rule_tracepoint_set_pattern(tracepoint, pattern);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting pattern");
+ status = lttng_event_rule_tracepoint_get_pattern(tracepoint, &tmp);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting pattern");
+ ok(!strncmp(pattern, tmp, strlen(pattern)), "pattern is equal");
+
+ status = lttng_event_rule_tracepoint_set_filter(tracepoint, filter);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting filter");
+ status = lttng_event_rule_tracepoint_get_filter(tracepoint, &tmp);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting filter");
+ ok(!strncmp(filter, tmp, strlen(filter)), "filter is equal");
+
+ status = lttng_event_rule_tracepoint_set_loglevel_all(tracepoint);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting all loglevel");
+ status = lttng_event_rule_tracepoint_get_loglevel_type(tracepoint, &loglevel_type);
+ ok(loglevel_type == LTTNG_EVENT_LOGLEVEL_ALL, "getting loglevel type all");
+ status = lttng_event_rule_tracepoint_get_loglevel(tracepoint, &ret);
+ ok(status == LTTNG_EVENT_RULE_STATUS_UNSET, "get unset loglevel value");
+
+ status = lttng_event_rule_tracepoint_set_loglevel(tracepoint, LTTNG_LOGLEVEL_INFO);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting single loglevel");
+ status = lttng_event_rule_tracepoint_get_loglevel_type(tracepoint, &loglevel_type);
+ ok(loglevel_type == LTTNG_EVENT_LOGLEVEL_SINGLE, "getting loglevel type single");
+ status = lttng_event_rule_tracepoint_get_loglevel(tracepoint, &ret);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get loglevel value");
+ ok(ret == LTTNG_LOGLEVEL_INFO, "loglevel value is equal");
+
+ status = lttng_event_rule_tracepoint_set_loglevel_range(tracepoint, LTTNG_LOGLEVEL_WARNING);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting range loglevel");
+ status = lttng_event_rule_tracepoint_get_loglevel_type(tracepoint, &loglevel_type);
+ ok(loglevel_type == LTTNG_EVENT_LOGLEVEL_RANGE, "getting loglevel type range");
+ status = lttng_event_rule_tracepoint_get_loglevel(tracepoint, &ret);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "get loglevel value");
+ ok(ret == LTTNG_LOGLEVEL_WARNING, "loglevel valuei is equal");
+
+ status = lttng_event_rule_tracepoint_set_exclusions(tracepoint, 3, exclusions);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "setting exclusions");
+
+ status = lttng_event_rule_tracepoint_get_exclusions_count(tracepoint, &count);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting exclusion count");
+ ok(count == 3, "count is %d/3", count);
+
+ for (int i = 0; i < count; i++) {
+ status = lttng_event_rule_tracepoint_get_exclusion_at_index(tracepoint, i, &tmp);
+ ok(status == LTTNG_EVENT_RULE_STATUS_OK, "getting exclusion at index %d", i);
+ ok(!strncmp(exclusions[i], tmp, strlen(exclusions[i])), "%s == %s", tmp, exclusions[i]);
+ }
+
+ lttng_event_rule_serialize(tracepoint, &buffer, NULL);
+ view = lttng_buffer_view_from_dynamic_buffer(&buffer, 0, -1);
+ lttng_event_rule_create_from_buffer(&view, &tracepoint_from_buffer);
+
+ ok(lttng_event_rule_is_equal(tracepoint, tracepoint_from_buffer), "serialized and from buffer are equal");
+
+
+
+
+ lttng_dynamic_buffer_reset(&buffer);
+ lttng_event_rule_destroy(tracepoint);
+ lttng_event_rule_destroy(tracepoint_from_buffer);
+}
+
+static
+void test_event_rule_tracepoint(void)
+{
+ struct lttng_event_rule *tracepoint = NULL;
+
+ diag("Testing lttng_event_rule_tracepoint");
+ tracepoint = lttng_event_rule_tracepoint_create(LTTNG_DOMAIN_NONE);
+ ok(!tracepoint, "Domain type restriction on create");
+
+ test_event_rule_tracepoint_ust();
+}
+
+int main(int argc, const char *argv[])
+{
+ plan_tests(NUM_TESTS);
+ test_event_rule_tracepoint();
+ return exit_status();
+}
#include "utils.h"
#define MAX_LEN 16
+
+static
+int open_read_close(const char *path)
+{
+ int fd, ret;
+ char buf[MAX_LEN];
+ /*
+ * Start generating syscalls. We use syscall(2) to prevent libc to change
+ * the underlying syscall. e.g. calling openat(2) instead of open(2).
+ */
+ fd = syscall(SYS_openat, AT_FDCWD, path, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ ret = -1;
+ goto error;
+ }
+
+ ret = syscall(SYS_read, fd, buf, MAX_LEN);
+ if (ret < 0) {
+ perror("read");
+ ret = -1;
+ goto error;
+ }
+
+ ret = syscall(SYS_close, fd);
+ if (ret == -1) {
+ perror("close");
+ ret = -1;
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
/*
* The process waits for the creation of a file passed as argument from an
* external processes to execute a syscall and exiting. This is useful for tests
*/
int main(int argc, char **argv)
{
- int fd, ret;
- char buf[MAX_LEN];
- char *start_file;
+ int ret;
+ const char *start_file, *path1, *path2;
- if (argc != 2) {
+ if (argc != 4) {
fprintf(stderr, "Error: Missing argument\n");
+ fprintf(stderr, "USAGE: %s PATH_WAIT_FILE PATH1_TO_OPEN PATH2_TO_OPEN\n", argv[0]);
fprintf(stderr, "USAGE: %s PATH_WAIT_FILE\n", argv[0]);
ret = -1;
goto error;
}
start_file = argv[1];
+ path1 = argv[2];
+ path2 = argv[3];
/*
* Wait for the start_file to be created by an external process
* Start generating syscalls. We use syscall(2) to prevent libc to change
* the underlying syscall. e.g. calling openat(2) instead of open(2).
*/
- fd = syscall(SYS_openat, AT_FDCWD, "/proc/cpuinfo", O_RDONLY);
- if (fd < 0) {
- perror("open");
- ret = -1;
- goto error;
- }
-
- ret = syscall(SYS_read, fd, buf, MAX_LEN);
- if (ret < 0) {
- perror("read");
+ ret = open_read_close(path1);
+ if (ret == -1) {
ret = -1;
goto error;
}
- ret = syscall(SYS_close, fd);
+ ret = open_read_close(path2);
if (ret == -1) {
- perror("close");
ret = -1;
goto error;
}
int option;
long values[] = { 1, 2, 3 };
char text[10] = "test";
+ char escape[10] = "\\*";
double dbl = 2.0;
float flt = 2222.0;
+ uint32_t net_values[] = { 1, 2, 3 };
int nr_iter = 100, ret = 0, first_event_file_created = 0;
useconds_t nr_usec = 0;
char *after_first_event_file_path = NULL;
/* Wait on file before exiting */
char *before_exit_file_path = NULL;
+ for (i = 0; i < 3; i++) {
+ net_values[i] = htonl(net_values[i]);
+ }
+
while ((option = getopt_long(argc, argv, "i:w:a:b:c:d:",
long_options, &option_index)) != -1) {
switch (option) {
}
netint = htonl(i);
tracepoint(tp, tptest, i, netint, values, text,
- strlen(text), dbl, flt);
+ strlen(text), escape, net_values, dbl, flt);
/*
* First loop we create the file if asked to indicate
#define _TRACEPOINT_TP_H
#include <lttng/tracepoint.h>
+#include <stdint.h>
+
+TRACEPOINT_ENUM(
+ tp, tptest_enum,
+ TP_ENUM_VALUES(
+ ctf_enum_auto("AUTO: EXPECT 0")
+ ctf_enum_value("VALUE: 23", 23)
+ ctf_enum_value("VALUE: 27", 27)
+ ctf_enum_auto("AUTO: EXPECT 28")
+ ctf_enum_range("RANGE: 101 TO 303", 101, 303)
+ ctf_enum_auto("AUTO: EXPECT 304")
+ ctf_enum_value("VALUE: -1", -1)
+ )
+)
TRACEPOINT_EVENT(tp, tptest,
TP_ARGS(int, anint, int, netint, long *, values,
char *, text, size_t, textlen,
+ char *, etext, uint32_t * , net_values,
double, doublearg, float, floatarg),
TP_FIELDS(
ctf_integer(int, intfield, anint)
ctf_integer_hex(int, intfield2, anint)
ctf_integer(long, longfield, anint)
+ ctf_integer(int, signedfield, -1)
ctf_integer_network(int, netintfield, netint)
ctf_integer_network_hex(int, netintfieldhex, netint)
ctf_array(long, arrfield1, values, 3)
ctf_array_text(char, arrfield2, text, 10)
+ ctf_array_network(uint32_t, arrfield3, net_values, 3)
ctf_sequence(char, seqfield1, text, size_t, textlen)
ctf_sequence_text(char, seqfield2, text, size_t, textlen)
+ ctf_sequence_network(uint32_t, seqfield3, net_values, size_t, 3)
+ ctf_sequence(long, seqfield4, values, size_t, 3)
ctf_string(stringfield, text)
+ ctf_string(stringfield2, etext)
ctf_float(float, floatfield, floatarg)
ctf_float(double, doublefield, doublearg)
+ ctf_enum(tp, tptest_enum, int, enum0, 0)
+ ctf_enum(tp, tptest_enum, int, enum23, 23)
+ ctf_enum(tp, tptest_enum, int, enum27, 27)
+ ctf_enum(tp, tptest_enum, int, enum28, 28)
+ ctf_enum(tp, tptest_enum, int, enum202, 202)
+ ctf_enum(tp, tptest_enum, int, enum304, 304)
+ ctf_enum(tp, tptest_enum, int, enumnegative, -1)
)
)
trap null_pipes SIGPIPE
+# Check pgrep from env, default to pgrep if none
+if [ -z "$PGREP" ]; then
+ PGREP=pgrep
+fi
+
+# Due to the renaming of threads we need to use the full command (pgrep -f) to
+# identify the pids for multiple lttng related processes. The problem with "pgrep
+# -f" is that it ends up also looking at the arguments. We use a two stage
+# lookup. The first one is using "pgrep -f" yielding potential candidate.
+# The second on perform grep on the basename of the first field of the
+# /proc/pid/cmdline of the previously identified pids. The first field
+# correspond to the actual command.
+function lttng_pgrep ()
+{
+ local pattern=$1
+ local possible_pids
+ local full_command_no_argument
+ local command_basename
+
+ possible_pids=$($PGREP -f "$pattern")
+ if [ -z "$possible_pids" ]; then
+ return 0
+ fi
+
+ while IFS= read -r pid ; do
+ # /proc/pid/cmdline is null separated.
+ if full_command_no_argument=$(cut -d '' -f 1 < /proc/"$pid"/cmdline); then
+ command_basename=$(basename "$full_command_no_argument")
+ if grep -q "$pattern" <<< "$command_basename"; then
+ echo "$pid"
+ fi
+ fi
+ done <<< "$possible_pids"
+ return 0
+}
+
function print_ok ()
{
# Check if we are a terminal
DIR=$(readlink -f "$TESTDIR")
- if [ -z $(pgrep $RELAYD_MATCH) ]; then
+ if [ -z $(lttng_pgrep "$RELAYD_MATCH") ]; then
# shellcheck disable=SC2086
$DIR/../src/bin/lttng-relayd/$RELAYD_BIN $process_mode $opt 1> $OUTPUT_DEST 2> $ERROR_OUTPUT_DEST
#$DIR/../src/bin/lttng-relayd/$RELAYD_BIN $opt -vvv >>/tmp/relayd.log 2>&1 &
local retval=0
local pids=
- pids=$(pgrep "$RELAYD_MATCH")
+ pids=$(lttng_pgrep "$RELAYD_MATCH")
if [ -z "$pids" ]; then
if [ "$withtap" -eq "1" ]; then
pass "No relay daemon to kill"
else
out=1
while [ -n "$out" ]; do
- out=$(pgrep "$RELAYD_MATCH")
+ out=$(lttng_pgrep "$RELAYD_MATCH")
if [ -n "$dtimeleft_s" ]; then
if [ $dtimeleft_s -lt 0 ]; then
out=
: "${LTTNG_SESSION_CONFIG_XSD_PATH="${DIR}/../src/common/config/"}"
export LTTNG_SESSION_CONFIG_XSD_PATH
- if [ -z "$(pgrep "${SESSIOND_MATCH}")" ]; then
+ if [ -z "$(lttng_pgrep "${SESSIOND_MATCH}")" ]; then
# Have a load path ?
if [ -n "$load_path" ]; then
# shellcheck disable=SC2086
local retval=0
local runas_pids=
- runas_pids=$(pgrep "$RUNAS_MATCH")
+ runas_pids=$(lttng_pgrep "$RUNAS_MATCH")
local pids=
- pids=$(pgrep "$SESSIOND_MATCH")
+ pids=$(lttng_pgrep "$SESSIOND_MATCH")
if [ -n "$runas_pids" ]; then
pids="$pids $runas_pids"
else
out=1
while [ -n "$out" ]; do
- out=$(pgrep "${SESSIOND_MATCH}")
+ out=$(lttng_pgrep "${SESSIOND_MATCH}")
if [ -n "$dtimeleft_s" ]; then
if [ $dtimeleft_s -lt 0 ]; then
out=
done
out=1
while [ -n "$out" ]; do
- out=$(pgrep "$CONSUMERD_MATCH")
+ out=$(lttng_pgrep "$CONSUMERD_MATCH")
if [ -n "$dtimeleft_s" ]; then
if [ $dtimeleft_s -lt 0 ]; then
out=
return
fi
- PID_SESSIOND="$(pgrep "${SESSIOND_MATCH}") $(pgrep "$RUNAS_MATCH")"
+ PID_SESSIOND="$(lttng_pgrep "${SESSIOND_MATCH}") $(lttng_pgrep "$RUNAS_MATCH")"
if [ "$withtap" -eq "1" ]; then
diag "Sending SIGSTOP to lt-$SESSIOND_BIN and $SESSIOND_BIN pids: $(echo "$PID_SESSIOND" | tr '\n' ' ')"
else
out=1
while [ $out -ne 0 ]; do
- pid="$(pgrep "$SESSIOND_MATCH")"
+ pid="$(lttng_pgrep "$SESSIOND_MATCH")"
# Wait until state becomes stopped for session
# daemon(s).
local retval=0
- PID_CONSUMERD="$(pgrep "$CONSUMERD_MATCH")"
+ PID_CONSUMERD="$(lttng_pgrep "$CONSUMERD_MATCH")"
if [ -z "$PID_CONSUMERD" ]; then
if [ "$withtap" -eq "1" ]; then
else
out=1
while [ $out -ne 0 ]; do
- pid="$(pgrep "$CONSUMERD_MATCH")"
+ pid="$(lttng_pgrep "$CONSUMERD_MATCH")"
# If consumerds are still present check their status.
# A zombie status qualifies the consumerd as *killed*
local withtap=$1
local signal=SIGSTOP
- PID_CONSUMERD="$(pgrep "$CONSUMERD_MATCH")"
+ PID_CONSUMERD="$(lttng_pgrep "$CONSUMERD_MATCH")"
diag "Sending SIGSTOP to $CONSUMERD_BIN pids: $(echo "$PID_CONSUMERD" | tr '\n' ' ')"
else
out=1
while [ $out -ne 0 ]; do
- pid="$(pgrep "$CONSUMERD_MATCH")"
+ pid="$(lttng_pgrep "$CONSUMERD_MATCH")"
# Wait until state becomes stopped for all
# consumers.