See CodingStyle for guidelines style and design guidelines.
-Although the LTTng-tools code base is primarily written in C, it does
-contain shell, Perl, and Python code as well. There is no official coding
+Although the LTTng-tools code base is primarily written in C++, it does
+contain C, shell, and Python code as well. There is no official coding
standard for these languages. However, using a style consistent with the
rest of the code written in that language is strongly encouraged.
of the https://lttng.org/man/1/lttng-view/[`lttng view`] command,
`make{nbsp}check` and tests.
+
-Debian/Ubuntu package: `babeltrace2`
+Debian/Ubuntu package: `babeltrace2 libbabeltrace2-dev`
* **https://lttng.org/[LTTng{nbh}UST]** (same minor version as {lt}):
LTTng user space tracing (applications and libraries).
+
Debian/Ubuntu package: `liblttng{nbh}ust{nbh}dev`
-* **Perl**: `make{nbsp}check` and tests.
-
* **https://www.python.org/[Python]{nbsp}≥{nbsp}3.4**:
`make{nbsp}check` and tests.
+
)
AM_CONDITIONAL([BUILD_TESTS], [test x$build_tests = xyes])
+
+AS_IF([test "x$build_tests" = "xyes"],
+ [
+ PKG_CHECK_MODULES([babeltrace2], [babeltrace2])
+ ]
+)
+
AM_CONDITIONAL([BUILD_EXTRAS], [test x$enable_extras != xno])
# Export libraries build conditions.
tests/unit/ini_config/Makefile
tests/perf/Makefile
tests/utils/Makefile
+ tests/utils/bt2_plugins/Makefile
+ tests/utils/bt2_plugins/event_name/Makefile
+ tests/utils/bt2_plugins/field_stats/Makefile
tests/utils/lttngtest/Makefile
tests/utils/tap/Makefile
tests/utils/testapp/Makefile
CURDIR=$(dirname $0)/
TESTDIR=$CURDIR/../../..
-STATS_BIN="$TESTDIR/utils/babelstats.pl"
+BT2_PLUGINS_DIR="${TESTDIR}/utils/bt2_plugins"
+
SESSION_NAME="test-exclusion"
TESTAPP_PATH="$TESTDIR/utils/testapp"
TESTAPP_NAME="gen-ust-nevents"
# Destroy session
destroy_lttng_session_ok $SESSION_NAME
- stats=`"$BABELTRACE_BIN" $trace_path | $STATS_BIN --tracepoint "$event_name_expected_to_be_missing" | grep -v index 2> /dev/null`
+ stats=$("$BABELTRACE_BIN" --plugin-path "${BT2_PLUGINS_DIR}" "${trace_path}" -c filter.lttngtest.event_name -p "names=[\"${event_name_expected_to_be_missing}\"]" -c sink.lttngtest.field_stats | grep -v index 2> /dev/null)
if [ ! -z "$stats" ]; then
fail "Excluded event \"$event_name_expected_to_be_missing\" was found in trace!"
else
# Destroy session
destroy_lttng_session_ok $SESSION_NAME
- stats=$("$BABELTRACE_BIN" $trace_path | $STATS_BIN --tracepoint "$event_name_expected_to_be_missing" | grep -v index 2> /dev/null)
+ stats=$("$BABELTRACE_BIN" --plugin-path "${BT2_PLUGINS_DIR}" "${trace_path}" -c filter.lttngtest.event_name -p "names=[\"${event_name_expected_to_be_missing}\"]" -c sink.lttngtest.field_stats | grep -v index 2> /dev/null)
if [ ! -z "$stats" ]; then
fail "Excluded event \"$event_name_expected_to_be_missing\" was found in trace!"
else
CURDIR=$(dirname $0)/
TESTDIR=$CURDIR/../../..
-STATS_BIN="$TESTDIR/utils/babelstats.pl"
+BT2_PLUGINS_DIR="${TESTDIR}/utils/bt2_plugins"
+
SESSION_NAME="valid_filter"
NR_ITER=100
NUM_GLOBAL_TESTS=2
# Destroy session
destroy_lttng_session_ok $SESSION_NAME --no-wait
- stats=`"$BABELTRACE_BIN" $trace_path | $STATS_BIN --tracepoint $event_name`
-
- rm -rf $trace_path
+ stats=$("${BABELTRACE_BIN}" --plugin-path "${BT2_PLUGINS_DIR}" "${trace_path}" -c filter.lttngtest.event_name -p "names=[\"${event_name}\"]" -c sink.lttngtest.field_stats)
$validator "$stats"
-
ok $? "Validate trace filter output"
-
rm -rf $trace_path
}
CURDIR=$(dirname "$0")/
TESTDIR=$CURDIR/../../..
+BT2_PLUGINS_DIR="${TESTDIR}/utils/bt2_plugins"
TESTAPP_PATH="$TESTDIR/utils/testapp"
TESTAPP_NAME="gen-ust-events"
TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
-STATS_BIN="$TESTDIR/utils/babelstats.pl"
NUM_TESTS=74
PAGE_SIZE=$(getconf PAGE_SIZE)
[ "$(get_stream_file_count "$trace_path" "$stream_pattern")" -eq "$count_limit" ]
ok $? "Stream meets the trace file limit of $count_limit"
- stats=`"$BABELTRACE_BIN" $trace_path | $STATS_BIN --tracepoint $event_name`
+ stats=$("$BABELTRACE_BIN" --plugin-path "${BT2_PLUGINS_DIR}" convert $trace_path -c filter.lttngtest.event_name -p "names=[\"${event_name}\"]" -c sink.lttngtest.field_stats)
validate_min_max "$stats" "intfield" "[0-9]+" "$expected_max"
ok $? "Trace validation - intfield"
# SPDX-License-Identifier: GPL-2.0-only
-SUBDIRS = . tap testapp xml-utils lttngtest
+SUBDIRS = . tap testapp xml-utils lttngtest bt2_plugins
-EXTRA_DIST = utils.sh test_utils.py babelstats.pl warn_processes.sh \
+EXTRA_DIST = utils.sh test_utils.py warn_processes.sh \
parse-callstack.py
-dist_noinst_SCRIPTS = utils.sh test_utils.py babelstats.pl tap-driver.sh
+dist_noinst_SCRIPTS = utils.sh test_utils.py tap-driver.sh
noinst_LTLIBRARIES = libtestutils.la
libtestutils_la_SOURCES = \
+++ /dev/null
-#!/usr/bin/env perl
-
-# Copyright (C) 2012 Christian Babeux <christian.babeux@efficios.com>
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-
-use strict;
-use warnings;
-
-use Getopt::Long;
-
-my $opt_tracepoint;
-
-GetOptions('tracepoint=s' => \$opt_tracepoint)
- or die("Invalid command-line option\n");
-
-defined($opt_tracepoint)
- or die("Missing tracepoint, use --tracepoint <name>");
-
-# Parse an array string.
-# The format is as follow: [ [index] = value, ... ]
-sub parse_array
-{
- my ($arr_str) = @_;
- my @array = ();
-
- # Strip leading and ending brackets, remove whitespace
- $arr_str =~ s/^\[//;
- $arr_str =~ s/\]$//;
- $arr_str =~ s/\s//g;
-
- my @entries = split(',', $arr_str);
-
- foreach my $entry (@entries) {
- if ($entry =~ /^\[(\d+)\]=(\d+)$/) {
- my $index = $1;
- my $value = $2;
- splice @array, $index, 0, $value;
- }
- }
-
- return \@array;
-}
-
-# Parse fields values.
-# Format can either be a name = array or a name = value pair.
-sub parse_fields
-{
- my ($fields_str) = @_;
- my %fields_hash;
-
- my $field_name = '[\w\d_]+';
- my $field_value = '[\w\d_\\\*"]+';
- my $array = '\[(?:\s\[\d+\]\s=\s\d+,)*\s\[\d+\]\s=\s\d+\s\]';
-
- # Split the various fields
- my @fields = ($fields_str =~ /$field_name\s=\s(?:$array|$field_value)/g);
-
- foreach my $field (@fields) {
- if ($field =~ /($field_name)\s=\s($array)/) {
- my $name = $1;
- my $value = parse_array($2);
- $fields_hash{$name} = $value;
- }
-
- if ($field =~ /($field_name)\s=\s($field_value)/) {
- my $name = $1;
- my $value = $2;
- $fields_hash{$name} = $value;
- }
- }
-
- return \%fields_hash;
-}
-
-# Using an event array, merge all the fields
-# of a particular tracepoint.
-sub merge_fields
-{
- my ($events_ref) = @_;
- my %merged;
-
- foreach my $event (@{$events_ref}) {
- my $tp_event = $event->{'tp_event'};
- my $tracepoint = "${tp_event}";
-
- foreach my $key (keys %{$event->{'fields'}}) {
- my $val = $event->{'fields'}->{$key};
-
- # TODO: Merge of array is not implemented.
- next if (ref($val) eq 'ARRAY');
- $merged{$tracepoint}{$key}{$val} = undef;
- }
- }
-
- return \%merged;
-}
-
-# Print the minimum and maximum of each fields
-# for a particular tracepoint.
-sub print_fields_stats
-{
- my ($merged_ref, $tracepoint) = @_;
-
- return unless ($tracepoint && exists $merged_ref->{$tracepoint});
-
- foreach my $field (keys %{$merged_ref->{$tracepoint}}) {
- my @sorted;
- my @val = keys %{$merged_ref->{$tracepoint}->{$field}};
-
- if ($val[0] =~ /^\d+$/) {
- # Sort numerically
- @sorted = sort { $a <=> $b } @val;
- } elsif ($val[0] =~ /^0x[\da-f]+$/i) {
- # Convert the hex values and sort numerically
- @sorted = sort { hex($a) <=> hex($b) } @val;
- } else {
- # Fallback, alphabetical sort
- @sorted = sort { lc($a) cmp lc($b) } @val;
- }
-
- my $min = $sorted[0];
- my $max = $sorted[-1];
-
- print "$field $min $max\n";
- }
-}
-
-my @events;
-
-while (<>)
-{
- my $timestamp = '\[(?:.*)\]';
- my $elapsed = '\((?:.*)\)';
- my $hostname = '(?:.*)';
- my $tp_event = '(.*)';
- my $pkt_context = '(?:\{[^}]*\},\s)*';
- my $fields = '\{(.*)\}$';
-
- # Parse babeltrace text output format
- if (/$timestamp\s$elapsed\s$hostname\s$tp_event:\s$pkt_context$fields/) {
- my %event_hash;
- $event_hash{'tp_event'} = $1;
- $event_hash{'fields'} = parse_fields($2);
-
- push @events, \%event_hash;
- }
-}
-
-my %merged_fields = %{merge_fields(\@{events})};
-print_fields_stats(\%merged_fields, $opt_tracepoint);
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+
+SUBDIRS = event_name field_stats
+
+AM_CPPFLAGS += -I$(srcdir)
+AM_CXXFLAGS += $(babeltrace2_CFLAGS) \
+ $(WARN_FLAGS)
+
+noinst_LTLIBRARIES = lttngtest.la
+
+# lttng-tools uses -fvisibility=hidden by default, but to
+# produce a loadable plugin some of the symbols must not be
+# hidden. Override the `-fvisibility` for this shared object.
+lttngtest_la_CXXFLAGS = \
+ $(AM_CXXFLAGS) \
+ -fvisibility=default
+
+lttngtest_la_SOURCES = \
+ lttngtest-plugin.cpp
+
+lttngtest_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ $(LD_NO_UNDEFINED) \
+ -rpath / -avoid-version -module $(LD_NOTEXT)
+
+lttngtest_la_LIBADD = \
+ event_name/event_name.la \
+ field_stats/field_stats.la
--- /dev/null
+The `filter.lttngtest.event_name` plugin only has a single input and output port.
+This means that it cannot be connected directly to a `source.ctf.fs` plugin, as
+those have multiple output ports for the different event streams.
+
+A `filter.utils.muxer` plugin must be placed between any multi-output port plugin
+and the `filter.lttngtest.event_name` plugin. This is done automatically with in
+the architecture created by `babeltrace2 convert`.
+
+Example with `babeltrace2 convert`:
+
+```
+SOURCE_PATH=/tmp/tmp.1J5DueCziG
+EVENT_NAME=tp:the_string
+babeltrace2 --plugin-path=.libs/ convert "${SOURCE_PATH}" -c filter.lttngtest.event_name -p "names=[\"$EVENT_NAME\"]" -c sink.lttngtest.field_stats
+```
+
+Example with `babeltrace2 run`:
+
+```
+SOURCE_PATH=/tmp/tmp.1J5DueCziG
+EVENT_NAME=tp:the_string
+babeltrace2 --plugin-path=.libs/ run -c A:source.ctf.fs -p "inputs=[\"$SOURCE_PATH\"]" -c muxer:filter.utils.muxer -c B:filter.lttngtest.event_name -p "names=[\"$EVENT_NAME\"]" -c C:sink.lttngtest.field_stats -x A:muxer -x muxer:B -x B:C
+```
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+
+AM_CXXFLAGS += $(babeltrace2_CFLAGS) \
+ $(WARN_FLAGS)
+
+noinst_LTLIBRARIES = event_name.la
+event_name_la_SOURCES = \
+ event_name.cpp \
+ event_name.hpp
+event_name_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ $(LD_NO_UNDEFINED) \
+ -avoid-version -module $(LD_NOTEXT)
--- /dev/null
+/*
+ * Copyright (C) 2023 Kienan Stewart <kstewart@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "event_name.hpp"
+
+#include <assert.h>
+#include <babeltrace2/babeltrace.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#include <unordered_set>
+
+struct event_name {
+ std::unordered_set<std::string> names;
+ const bt_value *names_value;
+ /* weak reference */
+ bt_self_component_port_input *input_port;
+};
+
+struct event_name_iterator_data {
+ struct event_name *event_name;
+ bt_message_iterator *iterator;
+};
+
+bt_component_class_initialize_method_status
+event_name_initialize(bt_self_component_filter *self_comp,
+ bt_self_component_filter_configuration *,
+ const bt_value *params,
+ void *)
+{
+ bt_component_class_initialize_method_status status;
+ bt_self_component_port_input *input_port;
+ struct event_name *event_name;
+ auto self = bt_self_component_filter_as_self_component(self_comp);
+ if (bt_self_component_filter_add_input_port(self_comp, "in", nullptr, &input_port) !=
+ BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(self,
+ "Failed to add input port");
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+ goto end;
+ }
+
+ if (bt_self_component_filter_add_output_port(self_comp, "out", nullptr, nullptr) !=
+ BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(self,
+ "Failed to add output port");
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+ goto end;
+ }
+
+ event_name = new (std::nothrow) struct event_name;
+ if (event_name == nullptr) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ self, "Failed to allocate memory for private component data");
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+ goto end;
+ }
+
+ event_name->input_port = input_port;
+ event_name->names_value = bt_value_map_borrow_entry_value_const(params, "names");
+ if (event_name->names_value == nullptr) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ self, "'names' parameter is required");
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+ goto err_free;
+ }
+ if (bt_value_get_type(event_name->names_value) != BT_VALUE_TYPE_ARRAY) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ self, "'names' parameter must be an array");
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+ goto err_free;
+ }
+ if (bt_value_array_is_empty(event_name->names_value)) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ bt_self_component_filter_as_self_component(self_comp),
+ "'names' parameter must not be empty");
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+ goto err_free;
+ }
+ for (uint64_t index = 0; index < bt_value_array_get_length(event_name->names_value);
+ index++) {
+ const bt_value *names_entry = bt_value_array_borrow_element_by_index_const(
+ event_name->names_value, index);
+ if (bt_value_get_type(names_entry) != BT_VALUE_TYPE_STRING) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ self, "All members of the 'names' parameter array must be strings");
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+ goto err_free;
+ }
+ event_name->names.emplace(bt_value_string_get(names_entry));
+ }
+ bt_value_get_ref(event_name->names_value);
+ bt_self_component_set_data(self, event_name);
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+ goto end;
+
+err_free:
+ delete event_name;
+end:
+ return status;
+}
+
+void event_name_finalize(bt_self_component_filter *self_comp)
+{
+ struct event_name *event_name = (struct event_name *) bt_self_component_get_data(
+ bt_self_component_filter_as_self_component(self_comp));
+ bt_value_put_ref(event_name->names_value);
+ delete event_name;
+}
+
+bt_message_iterator_class_initialize_method_status
+event_name_message_iterator_initialize(bt_self_message_iterator *self_message_iterator,
+ bt_self_message_iterator_configuration *,
+ bt_self_component_port_output *)
+{
+ struct event_name *event_name = (struct event_name *) bt_self_component_get_data(
+ bt_self_message_iterator_borrow_component(self_message_iterator));
+ assert(event_name);
+
+ struct event_name_iterator_data *iter_data =
+ (struct event_name_iterator_data *) malloc(sizeof(struct event_name_iterator_data));
+
+ if (iter_data == nullptr) {
+ return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+ }
+ iter_data->event_name = event_name;
+
+ if (bt_message_iterator_create_from_message_iterator(
+ self_message_iterator, event_name->input_port, &iter_data->iterator) !=
+ BT_MESSAGE_ITERATOR_CREATE_FROM_MESSAGE_ITERATOR_STATUS_OK) {
+ free(iter_data);
+ return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+ }
+
+ bt_self_message_iterator_set_data(self_message_iterator, iter_data);
+
+ return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
+}
+
+void event_name_message_iterator_finalize(bt_self_message_iterator *self_message)
+{
+ struct event_name_iterator_data *iter_data =
+ (struct event_name_iterator_data *) bt_self_message_iterator_get_data(self_message);
+
+ assert(iter_data);
+ bt_message_iterator_put_ref(iter_data->iterator);
+ free(iter_data);
+}
+
+static bool message_passes(const bt_message *message, const std::unordered_set<std::string>& names)
+{
+ if (bt_message_get_type(message) != BT_MESSAGE_TYPE_EVENT) {
+ return true;
+ }
+
+ const bt_event *event = bt_message_event_borrow_event_const(message);
+ const bt_event_class *event_class = bt_event_borrow_class_const(event);
+ const char *event_name = bt_event_class_get_name(event_class);
+
+ if (event_name == nullptr) {
+ return false;
+ }
+
+ if (names.find(event_name) != names.end()) {
+ return true;
+ }
+
+ return false;
+}
+
+bt_message_iterator_class_next_method_status
+event_name_message_iterator_next(bt_self_message_iterator *self_message_iterator,
+ bt_message_array_const messages,
+ uint64_t,
+ uint64_t *count)
+{
+ bt_message_array_const upstream_messages;
+ uint64_t upstream_message_count;
+ uint64_t index = 0;
+ bt_message_iterator_next_status next_status;
+ bt_message_iterator_class_next_method_status status =
+ BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
+ struct event_name_iterator_data *iter_data =
+ (struct event_name_iterator_data *) bt_self_message_iterator_get_data(
+ self_message_iterator);
+ struct event_name *event_name = (struct event_name *) bt_self_component_get_data(
+ bt_self_message_iterator_borrow_component(self_message_iterator));
+
+ assert(event_name);
+ assert(iter_data);
+
+ while (index == 0) {
+ next_status = bt_message_iterator_next(
+ iter_data->iterator, &upstream_messages, &upstream_message_count);
+ if (next_status != BT_MESSAGE_ITERATOR_NEXT_STATUS_OK) {
+ status = static_cast<bt_message_iterator_class_next_method_status>(
+ next_status);
+ goto end;
+ }
+
+ for (uint64_t upstream_index = 0; upstream_index < upstream_message_count;
+ upstream_index++) {
+ const bt_message *upstream_message = upstream_messages[upstream_index];
+ if (message_passes(upstream_message, event_name->names)) {
+ messages[index] = upstream_message;
+ index++;
+ } else {
+ bt_message_put_ref(upstream_message);
+ }
+ }
+ }
+
+ *count = index;
+end:
+ return status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2023 Kienan Stewart <kstewart@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef EVENT_NAME_H
+#define EVENT_NAME_H
+
+#include <babeltrace2/babeltrace.h>
+
+bt_component_class_initialize_method_status
+event_name_initialize(bt_self_component_filter *self_comp,
+ bt_self_component_filter_configuration *config,
+ const bt_value *params,
+ void *init_data);
+
+void event_name_finalize(bt_self_component_filter *self_comp);
+
+bt_message_iterator_class_initialize_method_status
+event_name_message_iterator_initialize(bt_self_message_iterator *self_message_iterator,
+ bt_self_message_iterator_configuration *config,
+ bt_self_component_port_output *self_port);
+
+void event_name_message_iterator_finalize(bt_self_message_iterator *self_message_iterator);
+
+bt_message_iterator_class_next_method_status
+event_name_message_iterator_next(bt_self_message_iterator *self_message_iterator,
+ bt_message_array_const messages,
+ uint64_t capacity,
+ uint64_t *count);
+
+#endif /* EVENT_NAME_H */
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+
+AM_CXXFLAGS += $(babeltrace2_CFLAGS) \
+ $(WARN_FLAGS)
+
+noinst_LTLIBRARIES = field_stats.la
+field_stats_la_SOURCES = \
+ field_stats.cpp \
+ field_stats.hpp
+field_stats_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ $(LD_NO_UNDEFINED) \
+ -avoid-version -module $(LD_NOTEXT)
--- /dev/null
+/**
+ * Copyright (C) 2023 Kienan Stewart <kstewart@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "../fmt.hpp"
+#include "field_stats.hpp"
+
+#include <assert.h>
+#include <babeltrace2/babeltrace.h>
+#include <cstring>
+#include <stdio.h>
+#include <stdlib.h>
+
+struct field_stats {
+ bt_message_iterator *iterator;
+ bt_value *stats_value;
+ const bt_event_class *event_class;
+};
+
+bt_component_class_initialize_method_status
+field_stats_initialize(bt_self_component_sink *self_component_sink,
+ bt_self_component_sink_configuration *,
+ const bt_value *,
+ void *)
+{
+ bt_component_class_initialize_method_status status;
+ struct field_stats *field_stats = nullptr;
+
+ if (bt_self_component_sink_add_input_port(self_component_sink, "in", nullptr, nullptr) !=
+ BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ bt_self_component_sink_as_self_component(self_component_sink),
+ "Failed to add input port");
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+ goto error;
+ }
+
+ field_stats = (struct field_stats *) malloc(sizeof(*field_stats));
+ if (field_stats == nullptr) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ bt_self_component_sink_as_self_component(self_component_sink),
+ "Failed to allocate memory for private data");
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+ goto error;
+ }
+
+ field_stats->iterator = nullptr;
+ field_stats->stats_value = bt_value_map_create();
+ field_stats->event_class = nullptr;
+ if (field_stats->stats_value == nullptr) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ bt_self_component_sink_as_self_component(self_component_sink),
+ "Failed to allocate memory for field_stats.stats map");
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+ goto error;
+ }
+ bt_self_component_set_data(bt_self_component_sink_as_self_component(self_component_sink),
+ field_stats);
+ status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+ goto end;
+
+error:
+ if (field_stats) {
+ free(field_stats);
+ }
+end:
+ return status;
+}
+
+static bt_value_map_foreach_entry_const_func_status
+stats_value_print_summary(const char *key, const bt_value *value, void *)
+{
+ assert(bt_value_is_map(value));
+
+ const bt_value *min = bt_value_map_borrow_entry_value_const(value, "min");
+ const bt_value *max = bt_value_map_borrow_entry_value_const(value, "max");
+ const bt_value *display_base = bt_value_map_borrow_entry_value_const(value, "display_base");
+ enum bt_field_class_integer_preferred_display_base display_base_value =
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
+
+ if (display_base != nullptr) {
+ display_base_value = (enum bt_field_class_integer_preferred_display_base)
+ bt_value_integer_unsigned_get(display_base);
+ }
+ assert(min != nullptr);
+ assert(max != nullptr);
+
+ if (bt_value_is_string(min)) {
+ fmt::print("{} \"{}\" \"{}\"\n",
+ key,
+ bt_value_string_get(min),
+ bt_value_string_get(max));
+ } else if (bt_value_is_unsigned_integer(min)) {
+ switch (display_base_value) {
+ case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL:
+ fmt::print("{} 0x{:X} 0x{:X}\n",
+ key,
+ bt_value_integer_unsigned_get(min),
+ bt_value_integer_unsigned_get(max));
+ break;
+ default:
+ fmt::print("{} {} {}\n",
+ key,
+ bt_value_integer_unsigned_get(min),
+ bt_value_integer_unsigned_get(max));
+ break;
+ }
+ } else if (bt_value_is_signed_integer(min)) {
+ switch (display_base_value) {
+ case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL:
+ fmt::print("{} 0x{:X} 0x{:X}\n",
+ key,
+ (uint64_t) bt_value_integer_signed_get(min),
+ (uint64_t) bt_value_integer_signed_get(max));
+ break;
+ default:
+ fmt::print("{} {} {}\n",
+ key,
+ bt_value_integer_signed_get(min),
+ bt_value_integer_signed_get(max));
+ break;
+ }
+ } else if (bt_value_is_real(min)) {
+ fmt::print("{} {:0g} {:0g}\n", key, bt_value_real_get(min), bt_value_real_get(max));
+ } else {
+ assert(BT_FALSE);
+ }
+ return BT_VALUE_MAP_FOREACH_ENTRY_CONST_FUNC_STATUS_OK;
+}
+
+void field_stats_finalize(bt_self_component_sink *self_component_sink)
+{
+ struct field_stats *field_stats = (struct field_stats *) bt_self_component_get_data(
+ bt_self_component_sink_as_self_component(self_component_sink));
+ bt_value_put_ref(field_stats->stats_value);
+ free(field_stats);
+}
+
+bt_component_class_sink_graph_is_configured_method_status
+field_stats_graph_is_configured(bt_self_component_sink *self_component_sink)
+{
+ struct field_stats *field_stats = (struct field_stats *) bt_self_component_get_data(
+ bt_self_component_sink_as_self_component(self_component_sink));
+ bt_self_component_port_input *input_port =
+ bt_self_component_sink_borrow_input_port_by_index(self_component_sink, 0);
+ if (bt_message_iterator_create_from_sink_component(
+ self_component_sink, input_port, &field_stats->iterator) !=
+ BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_OK) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ bt_self_component_sink_as_self_component(self_component_sink),
+ "input port message iterator creation failed");
+ return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_ERROR;
+ }
+
+ return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK;
+}
+
+static bt_component_class_sink_consume_method_status
+member_stats_set_min_max(bt_value *member_map,
+ const bt_field_class_structure_member *member,
+ const bt_field *member_field,
+ const bt_field_class *member_class,
+ const bt_field_class_type *member_class_type,
+ bt_self_component_sink *self_component_sink)
+{
+ bt_value *min, *max, *display_base = bt_value_null;
+ const char *name = bt_field_class_structure_member_get_name(member);
+
+ if (bt_field_class_type_is(*member_class_type, BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER)) {
+ min = bt_value_integer_unsigned_create_init(
+ bt_field_integer_unsigned_get_value(member_field));
+ max = bt_value_integer_unsigned_create_init(
+ bt_field_integer_unsigned_get_value(member_field));
+ display_base = bt_value_integer_unsigned_create_init(
+ bt_field_class_integer_get_preferred_display_base(member_class));
+ } else if (bt_field_class_type_is(*member_class_type, BT_FIELD_CLASS_TYPE_SIGNED_INTEGER)) {
+ min = bt_value_integer_signed_create_init(
+ bt_field_integer_signed_get_value(member_field));
+ max = bt_value_integer_signed_create_init(
+ bt_field_integer_signed_get_value(member_field));
+ display_base = bt_value_integer_unsigned_create_init(
+ bt_field_class_integer_get_preferred_display_base(member_class));
+ } else if (bt_field_class_type_is(*member_class_type, BT_FIELD_CLASS_TYPE_STRING)) {
+ min = bt_value_string_create_init(bt_field_string_get_value(member_field));
+ max = bt_value_string_create_init(bt_field_string_get_value(member_field));
+ } else if (bt_field_class_type_is(*member_class_type,
+ BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL)) {
+ min = bt_value_real_create_init(
+ bt_field_real_double_precision_get_value(member_field));
+ max = bt_value_real_create_init(
+ bt_field_real_double_precision_get_value(member_field));
+ } else if (bt_field_class_type_is(*member_class_type,
+ BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL)) {
+ min = bt_value_real_create_init(
+ bt_field_real_single_precision_get_value(member_field));
+ max = bt_value_real_create_init(
+ bt_field_real_single_precision_get_value(member_field));
+ } else if (bt_field_class_type_is(*member_class_type, BT_FIELD_CLASS_TYPE_BIT_ARRAY)) {
+ min = bt_value_integer_unsigned_create_init(
+ bt_field_bit_array_get_value_as_integer(member_field));
+ max = bt_value_integer_unsigned_create_init(
+ bt_field_bit_array_get_value_as_integer(member_field));
+ } else {
+ const auto field_class_type_name = fmt::to_string(*member_class_type);
+
+ fmt::print("Unsupported field type for '{}': {}\n", name, field_class_type_name);
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ bt_self_component_sink_as_self_component(self_component_sink),
+ "Unsupported field type '%s' for member '%s'",
+ field_class_type_name.c_str(),
+ name);
+
+ return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+ }
+
+ if (min != nullptr) {
+ bt_value_map_insert_entry(member_map, "min", min);
+ bt_value_put_ref(min);
+ } else {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ bt_self_component_sink_as_self_component(self_component_sink),
+ "No minimum value for member '%s'",
+ name);
+ return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+ }
+ if (max != nullptr) {
+ bt_value_map_insert_entry(member_map, "max", max);
+ bt_value_put_ref(max);
+ } else {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ bt_self_component_sink_as_self_component(self_component_sink),
+ "No maximum value for member '%s'",
+ name);
+ return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+ }
+ if (display_base != bt_value_null) {
+ bt_value_map_insert_entry(member_map, "display_base", display_base);
+ bt_value_put_ref(display_base);
+ }
+ return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+}
+
+static bt_component_class_sink_consume_method_status
+member_stats_update_min_max(bt_value *member_map,
+ const bt_field_class_structure_member *member,
+ const bt_field *member_field,
+ const bt_field_class_type *member_class_type,
+ bt_self_component_sink *self_component_sink)
+{
+ const char *name = bt_field_class_structure_member_get_name(member);
+ bt_value *min = bt_value_map_borrow_entry_value(member_map, "min");
+ bt_value *max = bt_value_map_borrow_entry_value(member_map, "max");
+
+ if (min == nullptr || max == nullptr) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ bt_self_component_sink_as_self_component(self_component_sink),
+ "Missing min or max value for member '%s'",
+ name);
+ return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+ }
+
+ if (bt_field_class_type_is(*member_class_type, BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER)) {
+ bt_value *value = bt_value_integer_unsigned_create_init(
+ bt_field_integer_unsigned_get_value(member_field));
+ if (bt_value_integer_unsigned_get(value) < bt_value_integer_unsigned_get(min)) {
+ bt_value_integer_unsigned_set(min, bt_value_integer_unsigned_get(value));
+ }
+ if (bt_value_integer_unsigned_get(value) > bt_value_integer_unsigned_get(max)) {
+ bt_value_integer_unsigned_set(max, bt_value_integer_unsigned_get(value));
+ }
+ bt_value_put_ref(value);
+ } else if (bt_field_class_type_is(*member_class_type, BT_FIELD_CLASS_TYPE_SIGNED_INTEGER)) {
+ bt_value *value = bt_value_integer_signed_create_init(
+ bt_field_integer_signed_get_value(member_field));
+ if (bt_value_integer_signed_get(value) < bt_value_integer_signed_get(min)) {
+ bt_value_integer_signed_set(min, bt_value_integer_signed_get(value));
+ }
+ if (bt_value_integer_signed_get(value) > bt_value_integer_signed_get(max)) {
+ bt_value_integer_signed_set(max, bt_value_integer_signed_get(value));
+ }
+ bt_value_put_ref(value);
+ } else if (bt_field_class_type_is(*member_class_type, BT_FIELD_CLASS_TYPE_STRING)) {
+ bt_value *value =
+ bt_value_string_create_init(bt_field_string_get_value(member_field));
+ if (strcmp(bt_value_string_get(value), bt_value_string_get(min)) < 0) {
+ bt_value_string_set(min, bt_value_string_get(value));
+ }
+ if (strcmp(bt_value_string_get(value), bt_value_string_get(max)) > 0) {
+ bt_value_string_set(max, bt_value_string_get(value));
+ }
+ bt_value_put_ref(value);
+ } else if (bt_field_class_type_is(*member_class_type,
+ BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL)) {
+ bt_value *value = bt_value_real_create_init(
+ bt_field_real_double_precision_get_value(member_field));
+ if (bt_value_real_get(value) < bt_value_real_get(min)) {
+ bt_value_real_set(min, bt_value_real_get(value));
+ }
+ if (bt_value_real_get(value) > bt_value_real_get(max)) {
+ bt_value_real_set(max, bt_value_real_get(value));
+ }
+ bt_value_put_ref(value);
+ } else if (bt_field_class_type_is(*member_class_type,
+ BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL)) {
+ bt_value *value = bt_value_real_create_init(
+ (double) bt_field_real_single_precision_get_value(member_field));
+ if (bt_value_real_get(value) < bt_value_real_get(min)) {
+ bt_value_real_set(min, bt_value_real_get(value));
+ }
+ if (bt_value_real_get(value) > bt_value_real_get(max)) {
+ bt_value_real_set(max, bt_value_real_get(value));
+ }
+ bt_value_put_ref(value);
+ } else if (bt_field_class_type_is(*member_class_type, BT_FIELD_CLASS_TYPE_BIT_ARRAY)) {
+ bt_value *value = bt_value_integer_unsigned_create_init(
+ bt_field_bit_array_get_value_as_integer(member_field));
+ if (bt_value_integer_unsigned_get(value) < bt_value_integer_unsigned_get(min)) {
+ bt_value_integer_unsigned_set(min, bt_value_integer_unsigned_get(value));
+ }
+ if (bt_value_integer_unsigned_get(value) > bt_value_integer_unsigned_get(max)) {
+ bt_value_integer_unsigned_set(max, bt_value_integer_unsigned_get(value));
+ }
+ bt_value_put_ref(value);
+ } else {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ bt_self_component_sink_as_self_component(self_component_sink),
+ "Unsupported field type '%ld' for member '%s'",
+ *member_class_type,
+ name);
+ return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+ }
+ return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+}
+
+static bt_component_class_sink_consume_method_status
+update_stats(const bt_message *message,
+ field_stats *field_stats,
+ bt_self_component_sink *self_component_sink)
+{
+ if (bt_message_get_type(message) != BT_MESSAGE_TYPE_EVENT) {
+ /* It's not an error to get non-EVENT messages */
+ return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+ }
+
+ bt_component_class_sink_consume_method_status status;
+ const bt_event *event = bt_message_event_borrow_event_const(message);
+ const bt_field *event_payload = bt_event_borrow_payload_field_const(event);
+ const bt_event_class *event_class = bt_event_borrow_class_const(event);
+ const bt_field_class *event_payload_class =
+ bt_event_class_borrow_payload_field_class_const(event_class);
+
+ if (field_stats->event_class != nullptr) {
+ assert(event_class == field_stats->event_class);
+ } else {
+ field_stats->event_class = event_class;
+ }
+
+ /* Iterate over each field in the event payload */
+ for (uint64_t index = 0;
+ index < bt_field_class_structure_get_member_count(event_payload_class);
+ index++) {
+ const bt_field_class_structure_member *member =
+ bt_field_class_structure_borrow_member_by_index_const(event_payload_class,
+ index);
+ const char *name = bt_field_class_structure_member_get_name(member);
+ const bt_field *member_field =
+ bt_field_structure_borrow_member_field_by_name_const(event_payload, name);
+ const bt_field_class *member_class =
+ bt_field_class_structure_member_borrow_field_class_const(member);
+ const bt_field_class_type member_class_type = bt_field_class_get_type(member_class);
+
+ /* Ignore array and structure field types. */
+ if (bt_field_class_type_is(member_class_type, BT_FIELD_CLASS_TYPE_ARRAY) ||
+ bt_field_class_type_is(member_class_type, BT_FIELD_CLASS_TYPE_STRUCTURE)) {
+ continue;
+ }
+
+ bt_value *member_map =
+ bt_value_map_borrow_entry_value(field_stats->stats_value, name);
+ if (member_map == nullptr) {
+ if (bt_value_map_insert_empty_map_entry(
+ field_stats->stats_value, name, &member_map) !=
+ BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+ BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+ bt_self_component_sink_as_self_component(
+ self_component_sink),
+ "Failed to insert new empty map entry for field '%s'",
+ name);
+ return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+ }
+
+ status = member_stats_set_min_max(member_map,
+ member,
+ member_field,
+ member_class,
+ &member_class_type,
+ self_component_sink);
+ if (status != BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK) {
+ return status;
+ }
+ } else {
+ status = member_stats_update_min_max(member_map,
+ member,
+ member_field,
+ &member_class_type,
+ self_component_sink);
+ if (status != BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK) {
+ return status;
+ }
+ }
+ }
+ return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+}
+
+bt_component_class_sink_consume_method_status
+field_stats_consume(bt_self_component_sink *self_component_sink)
+{
+ bt_component_class_sink_consume_method_status status;
+ struct field_stats *field_stats = (struct field_stats *) bt_self_component_get_data(
+ bt_self_component_sink_as_self_component(self_component_sink));
+ bt_message_array_const messages;
+ uint64_t message_count;
+ bt_message_iterator_next_status next_status;
+
+ assert(field_stats);
+ next_status = bt_message_iterator_next(field_stats->iterator, &messages, &message_count);
+
+ if (next_status != BT_MESSAGE_ITERATOR_NEXT_STATUS_OK) {
+ if (next_status == BT_MESSAGE_ITERATOR_NEXT_STATUS_END) {
+ bt_value_map_foreach_entry_const(
+ field_stats->stats_value, stats_value_print_summary, nullptr);
+ bt_message_iterator_put_ref(field_stats->iterator);
+ }
+ status = static_cast<bt_component_class_sink_consume_method_status>(next_status);
+ goto end;
+ }
+
+ for (uint64_t index = 0; index < message_count; index++) {
+ const bt_message *message = messages[index];
+ status = update_stats(message, field_stats, self_component_sink);
+ bt_message_put_ref(message);
+ if (status != BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK) {
+ goto end;
+ }
+ }
+ status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+end:
+ return status;
+}
--- /dev/null
+/**
+ * Copyright (C) 2023 Kienan Stewart <kstewart@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef FIELD_STATS_H
+#define FIELD_STATS_H
+
+#include <babeltrace2/babeltrace.h>
+
+bt_component_class_initialize_method_status
+field_stats_initialize(bt_self_component_sink *self_component_sink,
+ bt_self_component_sink_configuration *config,
+ const bt_value *params,
+ void *initialize_method_data);
+
+void field_stats_finalize(bt_self_component_sink *self_component_sink);
+
+bt_component_class_sink_graph_is_configured_method_status
+field_stats_graph_is_configured(bt_self_component_sink *self_component_sink);
+
+bt_component_class_sink_consume_method_status
+field_stats_consume(bt_self_component_sink *self_component_sink);
+
+#endif /* FIELD_STATS_H */
--- /dev/null
+/*
+ * Copyright (C) 2024 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_TESTS_UTILS_BT2_PLUGINS_FMT_H
+#define LTTNG_TESTS_UTILS_BT2_PLUGINS_FMT_H
+
+#include <common/format.hpp>
+
+#include <babeltrace2/babeltrace.h>
+
+/*
+ * Due to a bug in g++ < 7.1, this specialization must be enclosed in the fmt namespace,
+ * see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480.
+ */
+namespace fmt {
+template <>
+struct formatter<bt_field_class_type> : formatter<std::string> {
+ template <typename FormatContextType>
+ typename FormatContextType::iterator format(const bt_field_class_type field_class_type,
+ FormatContextType& ctx)
+ {
+ const char *name;
+
+ switch (field_class_type) {
+ case BT_FIELD_CLASS_TYPE_BOOL:
+ name = "BOOL";
+ break;
+ case BT_FIELD_CLASS_TYPE_BIT_ARRAY:
+ name = "BIT_ARRAY";
+ break;
+ case BT_FIELD_CLASS_TYPE_INTEGER:
+ name = "INTEGER";
+ break;
+ case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER:
+ name = "UNSIGNED_INTEGER";
+ break;
+ case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER:
+ name = "SIGNED_INTEGER";
+ break;
+ case BT_FIELD_CLASS_TYPE_ENUMERATION:
+ name = "ENUMERATION";
+ break;
+ case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION:
+ name = "UNSIGNED_ENUMERATION";
+ break;
+ case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION:
+ name = "SIGNED_ENUMERATION";
+ break;
+ case BT_FIELD_CLASS_TYPE_REAL:
+ name = "REAL";
+ break;
+ case BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL:
+ name = "SINGLE_PRECISION_REAL";
+ break;
+ case BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL:
+ name = "DOUBLE_PRECISION_REAL";
+ break;
+ case BT_FIELD_CLASS_TYPE_STRING:
+ name = "STRING";
+ break;
+ case BT_FIELD_CLASS_TYPE_STRUCTURE:
+ name = "STRUCTURE";
+ break;
+ case BT_FIELD_CLASS_TYPE_ARRAY:
+ name = "ARRAY";
+ break;
+ case BT_FIELD_CLASS_TYPE_STATIC_ARRAY:
+ name = "STATIC_ARRAY";
+ break;
+ case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY:
+ name = "DYNAMIC_ARRAY";
+ break;
+ case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITHOUT_LENGTH_FIELD:
+ name = "DYNAMIC_ARRAY_WITHOUT_LENGTH_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITH_LENGTH_FIELD:
+ name = "DYNAMIC_ARRAY_WITH_LENGTH_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_OPTION:
+ name = "OPTION";
+ break;
+ case BT_FIELD_CLASS_TYPE_OPTION_WITHOUT_SELECTOR_FIELD:
+ name = "OPTION_WITHOUT_SELECTOR_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_OPTION_WITH_SELECTOR_FIELD:
+ name = "OPTION_WITH_SELECTOR_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_OPTION_WITH_BOOL_SELECTOR_FIELD:
+ name = "OPTION_WITH_BOOL_SELECTOR_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_OPTION_WITH_INTEGER_SELECTOR_FIELD:
+ name = "OPTION_WITH_INTEGER_SELECTOR_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_OPTION_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD:
+ name = "OPTION_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_OPTION_WITH_SIGNED_INTEGER_SELECTOR_FIELD:
+ name = "OPTION_WITH_SIGNED_INTEGER_SELECTOR_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_VARIANT:
+ name = "VARIANT";
+ break;
+ case BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR_FIELD:
+ name = "VARIANT_WITHOUT_SELECTOR_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_VARIANT_WITH_SELECTOR_FIELD:
+ name = "VARIANT_WITH_SELECTOR_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_VARIANT_WITH_INTEGER_SELECTOR_FIELD:
+ name = "VARIANT_WITH_INTEGER_SELECTOR_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD:
+ name = "VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD";
+ break;
+ case BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_INTEGER_SELECTOR_FIELD:
+ name = "VARIANT_WITH_SIGNED_INTEGER_SELECTOR_FIELD";
+ break;
+ default:
+ abort();
+ }
+
+ return format_to(ctx.out(), name);
+ }
+};
+} /* namespace fmt */
+
+#endif /* LTTNG_TESTS_UTILS_BT2_PLUGINS_FMT_H */
--- /dev/null
+/*
+ * Copyright (C) 2023 Kienan Stewart <kstewart@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "event_name/event_name.hpp"
+#include "field_stats/field_stats.hpp"
+
+#include <babeltrace2/babeltrace.h>
+
+BT_PLUGIN_MODULE();
+
+BT_PLUGIN(lttngtest);
+BT_PLUGIN_DESCRIPTION("Filter and sink used in lttng-tools test suite");
+BT_PLUGIN_AUTHOR("Kienan Stewart");
+BT_PLUGIN_LICENSE("LGPL-2.1-only");
+
+/* flt.lttngtest.event_name */
+/* Filter class to pass events matching given names */
+BT_PLUGIN_FILTER_COMPONENT_CLASS(event_name, event_name_message_iterator_next);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION(event_name, "Filter events by tracepoint name(s)");
+BT_PLUGIN_FILTER_COMPONENT_CLASS_INITIALIZE_METHOD(event_name, event_name_initialize);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_FINALIZE_METHOD(event_name, event_name_finalize);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD(
+ event_name, event_name_message_iterator_initialize);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD(
+ event_name, event_name_message_iterator_finalize);
+
+/* sink.lttngtest.field_stats */
+/* Sink class to produce certain statistics for seen fields */
+BT_PLUGIN_SINK_COMPONENT_CLASS(field_stats, field_stats_consume);
+BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(field_stats,
+ "Track minimum and maxiumum values of seen fields");
+BT_PLUGIN_SINK_COMPONENT_CLASS_INITIALIZE_METHOD(field_stats, field_stats_initialize);
+BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(field_stats, field_stats_finalize);
+BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(field_stats,
+ field_stats_graph_is_configured);