Clean-up: tests: bt2 plug-ins: modernize the plug-ins
authorJérémie Galarneau <jeremie.galarneau@efficios.com>
Fri, 8 Mar 2024 21:18:29 +0000 (16:18 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Tue, 12 Mar 2024 15:09:06 +0000 (11:09 -0400)
By virtue of their use of the C Babeltrace 2 APIs, the test plug-ins
perform a fair amount of manual resource management.

To make it possible to adopt a more modern C++ style in those plug-ins,
a number of helpers are introduced.

Introduce reference wrappers for the Babeltrace 2 interface:
  - value_ref: wraps a bt_value reference using std::unique_ptr
  - message_const_ref: wraps a constant message reference using a
    unique_ptr
  - message_iterator_ref: wraps a message iterator reference using a
    unique_ptr
  - event_class_const_ref: wraps a constant event class reference using
    a unique_ptr

A specialized random_access_container_wrapper is specialized to wrap
bt_value arrays of strings.

In doing so, it is possible to eliminate the use of gotos and manual
reference management on error paths. Some struct/classes are renamed to
eliminate ambiguities that arose over the refactoring.

The changes allow some simplifications of the code flow in places which
are applied directly.

Change-Id: I25c148d7970cb89add55a86f2c162973d3d27e4a
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
src/common/container-wrapper.hpp
tests/utils/bt2_plugins/Makefile.am
tests/utils/bt2_plugins/event_name/event_name.cpp
tests/utils/bt2_plugins/event_name/event_name.hpp
tests/utils/bt2_plugins/field_stats/field_stats.cpp
tests/utils/bt2_plugins/field_stats/field_stats.hpp
tests/utils/bt2_plugins/utils.hpp [new file with mode: 0644]

index 7f9678d08d3cb8252de645bd3731434d73b4e52a..20642a8d294213a5a914bd6d3a0cbedcb2c4767c 100644 (file)
@@ -117,6 +117,11 @@ public:
                return ContainerOperations::size(_container);
        }
 
+       bool empty() const
+       {
+               return size() == 0;
+       }
+
        typename std::conditional<std::is_pointer<ElementType>::value, ElementType, ElementType&>::type
        operator[](std::size_t index)
        {
@@ -144,7 +149,7 @@ public:
        operator[](std::size_t index) const
        {
                if (index >= ContainerOperations::size(_container)) {
-                       LTTNG_THROW_INVALID_ARGUMENT_ERROR(lttng::format(
+                       throw std::invalid_argument(lttng::format(
                                "Out of bound access through random_access_container_wrapper: index={}, size={}",
                                index,
                                size()));
index 59fdbcb83dd54f9a0d7a35694f9f9a1b9b44e547..ce6697e27bc7e4e25e8f3047602d08e33e40ea54 100644 (file)
@@ -16,7 +16,9 @@ lttngtest_la_CXXFLAGS = \
        -fvisibility=default
 
 lttngtest_la_SOURCES = \
-       lttngtest-plugin.cpp
+       lttngtest-plugin.cpp \
+       fmt.hpp \
+       utils.hpp
 
 lttngtest_la_LDFLAGS = \
        $(AM_LDFLAGS) \
index 6c8e1c5b2b133b826098eb62b642e6a6ee008896..170e375924b5a72e8abee303d2a684323cded093 100644 (file)
  *
  */
 
+#include "../utils.hpp"
 #include "event_name.hpp"
 
+#include <common/container-wrapper.hpp>
+#include <common/macros.hpp>
+#include <common/make-unique.hpp>
+
 #include <assert.h>
 #include <babeltrace2/babeltrace.h>
+#include <cstdint>
+#include <exception>
 #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;
+class event_name_set_operations {
+public:
+       static const char *get(const bt_value *array, std::size_t index)
+       {
+               const auto *names_entry =
+                       bt_value_array_borrow_element_by_index_const(array, index);
+
+               if (bt_value_get_type(names_entry) != BT_VALUE_TYPE_STRING) {
+                       throw std::runtime_error(
+                               "All members of the 'names' parameter array must be strings");
+               }
+
+               return bt_value_string_get(names_entry);
+       }
+
+       static std::size_t size(const bt_value *array)
+       {
+               return bt_value_array_get_length(array);
+       }
+};
+
+class event_name_set
+       : public lttng::utils::random_access_container_wrapper<const bt_value *,
+                                                              const char *,
+                                                              event_name_set_operations> {
+public:
+       friend event_name_set_operations;
+
+       event_name_set() :
+               lttng::utils::random_access_container_wrapper<const bt_value *,
+                                                             const char *,
+                                                             event_name_set_operations>(nullptr)
+       {
+       }
+
+       event_name_set(event_name_set&& original) :
+               lttng::utils::random_access_container_wrapper<const bt_value *,
+                                                             const char *,
+                                                             event_name_set_operations>(
+                       std::move(original._container))
+       {
+       }
+
+       event_name_set(const bt_value *names) :
+               lttng::utils::random_access_container_wrapper<const bt_value *,
+                                                             const char *,
+                                                             event_name_set_operations>(names)
+       {
+               if (bt_value_get_type(names) != BT_VALUE_TYPE_ARRAY) {
+                       throw std::invalid_argument("'names' parameter must be an array");
+               }
+       }
+};
+
+class event_name_filter {
+public:
+       event_name_filter(bt_self_component_port_input *input_port_,
+                         const event_name_set& name_set) :
+               input_port{ input_port_ }, _names{ name_set.begin(), name_set.end() }
+       {
+       }
+
+       bool event_name_is_allowed(const char *event_name) const noexcept
+       {
+               return _names.find(event_name) != _names.end();
+       }
+
        /* weak reference */
-       bt_self_component_port_input *input_port;
+       bt_self_component_port_input *const input_port;
+
+private:
+       const std::unordered_set<std::string> _names;
 };
 
 struct event_name_iterator_data {
-       struct event_name *event_name;
-       bt_message_iterator *iterator;
+       event_name_iterator_data(lttng::bt2::message_iterator_ref iterator_,
+                                const class event_name_filter& event_name_filter_) :
+               upstream_iterator{ std::move(iterator_) }, event_name_filter{ event_name_filter_ }
+       {
+       }
+
+       ~event_name_iterator_data()
+       {
+       }
+
+       const lttng::bt2::message_iterator_ref upstream_iterator;
+       const class event_name_filter& event_name_filter;
 };
 
+namespace {
+bool message_passes(const bt_message *message, const event_name_filter& event_name_filter)
+{
+       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;
+       }
+
+       return event_name_filter.event_name_is_allowed(event_name);
+}
+} /* namespace */
+
 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;
+       std::unique_ptr<class event_name_filter> event_name_filter;
+
        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;
+               return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
        }
 
        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;
+               return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
        }
 
-       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) {
+       const auto names_param = bt_value_map_borrow_entry_value_const(params, "names");
+       if (names_param == 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;
+               return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
        }
-       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) {
+
+       try {
+               event_name_set event_names{ names_param };
+               if (event_names.empty()) {
                        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;
+                               bt_self_component_filter_as_self_component(self_comp),
+                               "'names' parameter must not be empty");
+                       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
                }
-               event_name->names.emplace(bt_value_string_get(names_entry));
+
+               event_name_filter =
+                       lttng::make_unique<class event_name_filter>(input_port, event_names);
+       } catch (const std::bad_alloc&) {
+               BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+                       self, "Failed to allocate memory for private component data");
+               return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+       } catch (const std::exception& ex) {
+               BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(self, "%s", ex.what());
+               return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
        }
-       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;
+       /* Ownership of event_name is transferred to the component. */
+       bt_self_component_set_data(self, event_name_filter.release());
+       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
 }
 
 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;
+       class event_name_filter *event_name_filter =
+               (class event_name_filter *) bt_self_component_get_data(
+                       bt_self_component_filter_as_self_component(self_comp));
+
+       delete event_name_filter;
 }
 
 bt_message_iterator_class_initialize_method_status
@@ -117,104 +199,87 @@ event_name_message_iterator_initialize(bt_self_message_iterator *self_message_it
                                       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;
+       const auto& event_name_filter =
+               *static_cast<class event_name_filter *>(bt_self_component_get_data(
+                       bt_self_message_iterator_borrow_component(self_message_iterator)));
 
+       bt_message_iterator *raw_iterator;
        if (bt_message_iterator_create_from_message_iterator(
-                   self_message_iterator, event_name->input_port, &iter_data->iterator) !=
+                   self_message_iterator, event_name_filter.input_port, &raw_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);
+       lttng::bt2::message_iterator_ref iterator(raw_iterator);
+       raw_iterator = nullptr;
+
+       std::unique_ptr<event_name_iterator_data> iter_data;
+       try {
+               iter_data = lttng::make_unique<event_name_iterator_data>(std::move(iterator),
+                                                                        event_name_filter);
+       } catch (const std::bad_alloc&) {
+               BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_MESSAGE_ITERATOR(
+                       self_message_iterator, "Failed to allocate event_name iterator data");
+               return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+       }
 
+       /* Transfer the ownership of iter_data to the iterator. */
+       bt_self_message_iterator_set_data(self_message_iterator, iter_data.release());
        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);
+       event_name_iterator_data *iter_data = static_cast<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;
+       LTTNG_ASSERT(iter_data);
+       delete iter_data;
 }
 
 bt_message_iterator_class_next_method_status
 event_name_message_iterator_next(bt_self_message_iterator *self_message_iterator,
-                                bt_message_array_const messages,
+                                bt_message_array_const messages_to_deliver_downstream,
                                 uint64_t,
-                                uint64_t *count)
+                                uint64_t *_messages_to_deliver_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);
+       std::uint64_t messages_to_deliver_count = 0;
+       auto *iter_data = static_cast<event_name_iterator_data *>(
+               bt_self_message_iterator_get_data(self_message_iterator));
+       const auto& event_name_filter =
+               *static_cast<class event_name_filter *>(bt_self_component_get_data(
+                       bt_self_message_iterator_borrow_component(self_message_iterator)));
+
+       LTTNG_ASSERT(iter_data);
+
+       /* Retry until we have at least one message to deliver downstream. */
+       while (messages_to_deliver_count == 0) {
+               bt_message_array_const upstream_messages;
+               bt_message_iterator_next_status next_status;
+               uint64_t upstream_message_count;
+
+               next_status = bt_message_iterator_next(iter_data->upstream_iterator.get(),
+                                                      &upstream_messages,
+                                                      &upstream_message_count);
                if (next_status != BT_MESSAGE_ITERATOR_NEXT_STATUS_OK) {
-                       status = static_cast<bt_message_iterator_class_next_method_status>(
+                       return static_cast<bt_message_iterator_class_next_method_status>(
                                next_status);
-                       goto end;
                }
 
-               for (uint64_t upstream_index = 0; upstream_index < upstream_message_count;
+               for (std::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);
+                       lttng::bt2::message_const_ref upstream_message(
+                               upstream_messages[upstream_index]);
+
+                       if (message_passes(upstream_message.get(), event_name_filter)) {
+                               /* Reference transferred to downstream message batch. */
+                               messages_to_deliver_downstream[messages_to_deliver_count] =
+                                       upstream_message.release();
+                               messages_to_deliver_count++;
                        }
                }
        }
 
-       *count = index;
-end:
-       return status;
+       *_messages_to_deliver_count = messages_to_deliver_count;
+       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
 }
index 65513b2868f9cd9ea82c302525c2bcb8ad6c0917..9798b66f533dbc4e23d608dfe4a25ac3a9209893 100644 (file)
@@ -5,8 +5,8 @@
  *
  */
 
-#ifndef EVENT_NAME_H
-#define EVENT_NAME_H
+#ifndef LTTNG_TEST_UTILS_BT2_PLUGIN_EVENT_NAME_HPP
+#define LTTNG_TEST_UTILS_BT2_PLUGIN_EVENT_NAME_HPP
 
 #include <babeltrace2/babeltrace.h>
 
@@ -31,4 +31,4 @@ event_name_message_iterator_next(bt_self_message_iterator *self_message_iterator
                                 uint64_t capacity,
                                 uint64_t *count);
 
-#endif /* EVENT_NAME_H */
+#endif /* LTTNG_TEST_UTILS_BT2_PLUGIN_EVENT_NAME_HPP */
index 0dc59fcacd35323ad7bdffd1aa13371627ca9728..dc8894fd8a0b2ac594407d7c64f7480b086f6bc3 100644 (file)
  */
 
 #include "../fmt.hpp"
+#include "../utils.hpp"
 #include "field_stats.hpp"
 
+#include <common/make-unique-wrapper.hpp>
+#include <common/make-unique.hpp>
+
 #include <assert.h>
 #include <babeltrace2/babeltrace.h>
+#include <cstdint>
 #include <cstring>
+#include <iostream>
 #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;
+class bad_alloc_with_msg : public std::bad_alloc {
+public:
+       explicit bad_alloc_with_msg(const std::string& msg) : _msg(msg)
+       {
        }
 
-       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;
+       virtual const char *what() const noexcept override
+       {
+               return _msg.c_str();
        }
 
-       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;
+private:
+       std::string _msg;
+};
+
+struct field_stats {
+public:
+       field_stats() : stats_value{ lttng::bt2::make_value_ref(bt_value_map_create()) }
+       {
+               if (!stats_value) {
+                       throw bad_alloc_with_msg(
+                               "Failed to allocate memory for field_stats.stats map");
+               }
        }
-       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);
+       ~field_stats()
+       {
        }
-end:
-       return status;
-}
 
-static bt_value_map_foreach_entry_const_func_status
+       lttng::bt2::message_iterator_ref upstream_iterator;
+       lttng::bt2::event_class_const_ref event_class;
+       const lttng::bt2::value_ref stats_value;
+};
+
+namespace {
+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));
+       LTTNG_ASSERT(bt_value_is_map(value));
+
+       const auto *min = bt_value_map_borrow_entry_value_const(value, "min");
+       LTTNG_ASSERT(min != nullptr);
+       const auto *max = bt_value_map_borrow_entry_value_const(value, "max");
+       LTTNG_ASSERT(max != nullptr);
 
-       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;
+       const auto *display_base = bt_value_map_borrow_entry_value_const(value, "display_base");
+       auto 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)) {
+       LTTNG_ASSERT(bt_value_get_type(min) == bt_value_get_type(max));
+
+       switch (bt_value_get_type(min)) {
+       case BT_VALUE_TYPE_STRING:
                fmt::print("{} \"{}\" \"{}\"\n",
                           key,
                           bt_value_string_get(min),
                           bt_value_string_get(max));
-       } else if (bt_value_is_unsigned_integer(min)) {
+               break;
+       case BT_VALUE_TYPE_UNSIGNED_INTEGER:
                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));
+                       std::cout << lttng::format("{} 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));
+                       std::cout << lttng::format("{} {} {}\n",
+                                                  key,
+                                                  bt_value_integer_unsigned_get(min),
+                                                  bt_value_integer_unsigned_get(max));
                        break;
                }
-       } else if (bt_value_is_signed_integer(min)) {
+
+               break;
+       case BT_VALUE_TYPE_SIGNED_INTEGER:
                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));
+                       std::cout << lttng::format("{} 0x{:X} 0x{:X}\n",
+                                                  key,
+                                                  std::uint64_t(bt_value_integer_signed_get(min)),
+                                                  std::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));
+                       std::cout << lttng::format("{} {} {}\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;
+               break;
+       case BT_VALUE_TYPE_REAL:
+               std::cout << lttng::format(
+                       "{} {:0g} {:0g}\n", key, bt_value_real_get(min), bt_value_real_get(max));
+               break;
+       default:
+               abort();
        }
 
-       return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK;
+       return BT_VALUE_MAP_FOREACH_ENTRY_CONST_FUNC_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)
+void 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_value *min, *max, *display_base = bt_value_null;
+       lttng::bt2::value_ref min, max, display_base;
        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));
+               min = lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
+                       bt_field_integer_unsigned_get_value(member_field)));
+               max = lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
+                       bt_field_integer_unsigned_get_value(member_field)));
+               display_base = lttng::bt2::make_value_ref(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));
+               min = lttng::bt2::make_value_ref(bt_value_integer_signed_create_init(
+                       bt_field_integer_signed_get_value(member_field)));
+               max = lttng::bt2::make_value_ref(bt_value_integer_signed_create_init(
+                       bt_field_integer_signed_get_value(member_field)));
+               display_base = lttng::bt2::make_value_ref(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));
+               min = lttng::bt2::make_value_ref(
+                       bt_value_string_create_init(bt_field_string_get_value(member_field)));
+               max = lttng::bt2::make_value_ref(
+                       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));
+               min = lttng::bt2::make_value_ref(bt_value_real_create_init(
+                       bt_field_real_double_precision_get_value(member_field)));
+               max = lttng::bt2::make_value_ref(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));
+               min = lttng::bt2::make_value_ref(bt_value_real_create_init(
+                       bt_field_real_single_precision_get_value(member_field)));
+               max = lttng::bt2::make_value_ref(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));
+               min = lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
+                       bt_field_bit_array_get_value_as_integer(member_field)));
+               max = lttng::bt2::make_value_ref(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;
+               throw std::runtime_error(lttng::format(
+                       "Unsupported field type '{}' for member '{}'", *member_class_type, name));
        }
 
-       if (min != nullptr) {
-               bt_value_map_insert_entry(member_map, "min", min);
-               bt_value_put_ref(min);
+       if (min) {
+               bt_value_map_insert_entry(member_map, "min", min.get());
        } 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;
+               throw std::runtime_error(lttng::format("No minimum value for member '{}'", name));
        }
-       if (max != nullptr) {
-               bt_value_map_insert_entry(member_map, "max", max);
-               bt_value_put_ref(max);
+
+       if (max) {
+               bt_value_map_insert_entry(member_map, "max", max.get());
        } 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;
+               throw std::runtime_error(lttng::format("No maximum value for member '{}'", name));
        }
-       if (display_base != bt_value_null) {
-               bt_value_map_insert_entry(member_map, "display_base", display_base);
-               bt_value_put_ref(display_base);
+
+       if (display_base) {
+               bt_value_map_insert_entry(member_map, "display_base", display_base.get());
        }
-       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)
+void 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)
 {
        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;
+               throw std::runtime_error(
+                       lttng::format("Missing min or max value for member '{}'", name));
        }
 
        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));
+               const auto value = bt_field_integer_unsigned_get_value(member_field);
+
+               if (value < bt_value_integer_unsigned_get(min)) {
+                       bt_value_integer_unsigned_set(min, 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));
+
+               if (value > bt_value_integer_unsigned_get(max)) {
+                       bt_value_integer_unsigned_set(max, 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));
+               const auto value = bt_field_integer_signed_get_value(member_field);
+
+               if (value < bt_value_integer_signed_get(min)) {
+                       bt_value_integer_signed_set(min, 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));
+
+               if (value > bt_value_integer_signed_get(max)) {
+                       bt_value_integer_signed_set(max, 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));
+               const auto value = bt_field_string_get_value(member_field);
+
+               if (strcmp(value, bt_value_string_get(min)) < 0) {
+                       bt_value_string_set(min, value);
                }
-               if (strcmp(bt_value_string_get(value), bt_value_string_get(max)) > 0) {
-                       bt_value_string_set(max, bt_value_string_get(value));
+
+               if (strcmp(value, bt_value_string_get(max)) > 0) {
+                       bt_value_string_set(max, 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));
+               const auto value = bt_field_real_double_precision_get_value(member_field);
+
+               if (value < bt_value_real_get(min)) {
+                       bt_value_real_set(min, value);
                }
-               if (bt_value_real_get(value) > bt_value_real_get(max)) {
-                       bt_value_real_set(max, bt_value_real_get(value));
+
+               if (value > bt_value_real_get(max)) {
+                       bt_value_real_set(max, 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));
+               const auto value = double(bt_field_real_single_precision_get_value(member_field));
+
+               if (value < bt_value_real_get(min)) {
+                       bt_value_real_set(min, value);
                }
-               if (bt_value_real_get(value) > bt_value_real_get(max)) {
-                       bt_value_real_set(max, bt_value_real_get(value));
+
+               if (value > bt_value_real_get(max)) {
+                       bt_value_real_set(max, 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));
+               const auto value = bt_field_bit_array_get_value_as_integer(member_field);
+
+               if (value < bt_value_integer_unsigned_get(min)) {
+                       bt_value_integer_unsigned_set(min, 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));
+
+               if (value > bt_value_integer_unsigned_get(max)) {
+                       bt_value_integer_unsigned_set(max, 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;
+               throw std::runtime_error(lttng::format(
+                       "Unsupported field type '%{}' for member '{}'", *member_class_type, name));
        }
-       return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
 }
 
-static bt_component_class_sink_consume_method_status
+bt_component_class_sink_consume_method_status
 update_stats(const bt_message *message,
-            field_stats *field_stats,
+            field_statsfield_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 */
+               /* 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 =
+       const auto *event = bt_message_event_borrow_event_const(message);
+       const auto *event_payload = bt_event_borrow_payload_field_const(event);
+       const auto *event_class = bt_event_borrow_class_const(event);
+       const auto *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);
+       if (field_stats.event_class != nullptr) {
+               LTTNG_ASSERT(event_class == field_stats.event_class.get());
        } else {
-               field_stats->event_class = event_class;
+               bt_event_class_get_ref(event_class);
+               field_stats.event_class.reset(event_class);
        }
 
        /* Iterate over each field in the event payload */
-       for (uint64_t index = 0;
+       for (std::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 =
+               const auto *name = bt_field_class_structure_member_get_name(member);
+               const auto *member_field =
                        bt_field_structure_borrow_member_field_by_name_const(event_payload, name);
-               const bt_field_class *member_class =
+               const auto *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);
+               const auto 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)) {
+                       /* Ignore array and structure field types. */
                        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;
+               try {
+                       auto *member_map = bt_value_map_borrow_entry_value(
+                               field_stats.stats_value.get(), name);
+                       if (member_map == nullptr) {
+                               /* Initial creation of the value. */
+                               if (bt_value_map_insert_empty_map_entry(
+                                           field_stats.stats_value.get(), name, &member_map) !=
+                                   BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+                                       throw std::runtime_error(lttng::format(
+                                               "Failed to insert new empty map entry for field '{}'",
+                                               name));
+                               }
+
+                               member_stats_set_min_max(member_map,
+                                                        member,
+                                                        member_field,
+                                                        member_class,
+                                                        &member_class_type);
+                       } else {
+                               /* Update the value with min/max values. */
+                               member_stats_update_min_max(
+                                       member_map, member, member_field, &member_class_type);
                        }
+               } catch (const std::exception& ex) {
+                       BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+                               bt_self_component_sink_as_self_component(self_component_sink),
+                               "%s",
+                               ex.what());
+                       return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
                }
        }
+
        return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
 }
+} /* namespace */
+
+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 *)
+{
+       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");
+               return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+       }
+
+       std::unique_ptr<struct field_stats> field_stats;
+       try {
+               field_stats = lttng::make_unique<struct field_stats>();
+       } catch (const bad_alloc_with_msg& ex) {
+               BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+                       bt_self_component_sink_as_self_component(self_component_sink),
+                       "%s",
+                       ex.what());
+               return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+       } catch (const std::bad_alloc&) {
+               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");
+               return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+       }
+
+       /* Transfer ownership to the component. */
+       bt_self_component_set_data(bt_self_component_sink_as_self_component(self_component_sink),
+                                  field_stats.release());
+       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+}
+
+void field_stats_finalize(bt_self_component_sink *self_component_sink)
+{
+       auto *field_stats = static_cast<struct field_stats *>(bt_self_component_get_data(
+               bt_self_component_sink_as_self_component(self_component_sink)));
+
+       delete field_stats;
+}
+
+bt_component_class_sink_graph_is_configured_method_status
+field_stats_graph_is_configured(bt_self_component_sink *self_component_sink)
+{
+       auto& field_stats = *static_cast<struct field_stats *>(bt_self_component_get_data(
+               bt_self_component_sink_as_self_component(self_component_sink)));
+       auto *input_port =
+               bt_self_component_sink_borrow_input_port_by_index(self_component_sink, 0);
+
+       bt_message_iterator *raw_iterator;
+       if (bt_message_iterator_create_from_sink_component(
+                   self_component_sink, input_port, &raw_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;
+       }
+
+       field_stats.upstream_iterator.reset(raw_iterator);
+       return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_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;
+       auto& field_stats = *static_cast<struct field_stats *>(bt_self_component_get_data(
+               bt_self_component_sink_as_self_component(self_component_sink)));
 
-       assert(field_stats);
-       next_status = bt_message_iterator_next(field_stats->iterator, &messages, &message_count);
+       std::uint64_t message_count;
+       bt_message_array_const messages;
+       const auto next_status = bt_message_iterator_next(
+               field_stats.upstream_iterator.get(), &messages, &message_count);
 
        if (next_status != BT_MESSAGE_ITERATOR_NEXT_STATUS_OK) {
                if (next_status == BT_MESSAGE_ITERATOR_NEXT_STATUS_END) {
+                       /* End reached, print the summary. */
                        bt_value_map_foreach_entry_const(
-                               field_stats->stats_value, stats_value_print_summary, nullptr);
-                       bt_message_iterator_put_ref(field_stats->iterator);
+                               field_stats.stats_value.get(), stats_value_print_summary, nullptr);
                }
-               status = static_cast<bt_component_class_sink_consume_method_status>(next_status);
-               goto end;
+
+               return static_cast<bt_component_class_sink_consume_method_status>(next_status);
        }
 
-       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);
+       for (std::uint64_t index = 0; index < message_count; index++) {
+               const auto message = lttng::bt2::message_const_ref(messages[index]);
+
+               const auto status = update_stats(message.get(), field_stats, self_component_sink);
                if (status != BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK) {
-                       goto end;
+                       return status;
                }
        }
-       status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
-end:
-       return status;
+
+       return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
 }
index 833a4cfafde462d1d8802896eaca6b0890c8a912..b1977af049a95060fb984316d104c1df344c8203 100644 (file)
@@ -5,8 +5,8 @@
  *
  */
 
-#ifndef FIELD_STATS_H
-#define FIELD_STATS_H
+#ifndef LTTNG_TEST_UTILS_BT2_PLUGIN_FIELD_STATS_HPP
+#define LTTNG_TEST_UTILS_BT2_PLUGIN_FIELD_STATS_HPP
 
 #include <babeltrace2/babeltrace.h>
 
@@ -24,4 +24,4 @@ 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 */
+#endif /* LTTNG_TEST_UTILS_BT2_PLUGIN_FIELD_STATS_HPP */
diff --git a/tests/utils/bt2_plugins/utils.hpp b/tests/utils/bt2_plugins/utils.hpp
new file mode 100644 (file)
index 0000000..fcf7637
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#ifndef LTTNG_TEST_UTILS_BT2_PLUGIN_UTILS_HPP
+#define LTTNG_TEST_UTILS_BT2_PLUGIN_UTILS_HPP
+
+#include <common/make-unique-wrapper.hpp>
+
+#include <babeltrace2/babeltrace.h>
+
+namespace lttng {
+namespace bt2 {
+namespace internal {
+static inline void bt_value_put_ref(bt_value *value)
+{
+       bt_value_put_ref(const_cast<const bt_value *>(value));
+}
+} /* namespace internal */
+
+using value_ref = std::unique_ptr<
+       bt_value,
+       lttng::memory::create_deleter_class<bt_value, internal::bt_value_put_ref>::deleter>;
+
+using event_class_const_ref = std::unique_ptr<
+       const bt_event_class,
+       lttng::memory::create_deleter_class<const bt_event_class, bt_event_class_put_ref>::deleter>;
+
+static inline value_ref make_value_ref(bt_value *instance)
+{
+       const memory::create_deleter_class<bt_value, internal::bt_value_put_ref> unique_deleter;
+       return unique_deleter(instance);
+}
+
+using message_const_ref = std::unique_ptr<
+       const bt_message,
+       lttng::memory::create_deleter_class<const bt_message, bt_message_put_ref>::deleter>;
+
+using message_iterator_ref =
+       std::unique_ptr<bt_message_iterator,
+                       lttng::memory::create_deleter_class<const bt_message_iterator,
+                                                           bt_message_iterator_put_ref>::deleter>;
+} /* namespace bt2 */
+} /* namespace lttng */
+
+#endif /* LTTNG_TEST_UTILS_BT2_PLUGIN_UTILS_HPP */
This page took 0.044911 seconds and 4 git commands to generate.