sessiond: transition from lttng-ust to tracer agnostic API
[lttng-tools.git] / src / bin / lttng-sessiond / ust-registry-channel.cpp
diff --git a/src/bin/lttng-sessiond/ust-registry-channel.cpp b/src/bin/lttng-sessiond/ust-registry-channel.cpp
new file mode 100644 (file)
index 0000000..d03c89c
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "ust-registry-channel.hpp"
+#include "ust-app.hpp"
+#include "ust-registry-event.hpp"
+
+#include <common/error.hpp>
+#include <common/exception.hpp>
+#include <common/hashtable/utils.hpp>
+#include <common/make-unique-wrapper.hpp>
+#include <common/urcu.hpp>
+
+namespace lst = lttng::sessiond::trace;
+namespace lsu = lttng::sessiond::ust;
+
+namespace {
+bool is_max_event_id(uint32_t id)
+{
+       return id == UINT32_MAX;
+}
+
+unsigned long ht_hash_event(const void *_key, unsigned long seed)
+{
+       uint64_t hashed_key;
+       const lttng::sessiond::ust::registry_event *key =
+                       (lttng::sessiond::ust::registry_event *) _key;
+
+       LTTNG_ASSERT(key);
+
+       hashed_key = (uint64_t) hash_key_str(key->name.c_str(), seed);
+
+       return hash_key_u64(&hashed_key, seed);
+}
+
+/*
+ * Hash table match function for event in the registry.
+ */
+int ht_match_event(struct cds_lfht_node *node, const void *_key)
+{
+       const lttng::sessiond::ust::registry_event *key;
+       lttng::sessiond::ust::registry_event *event;
+
+       LTTNG_ASSERT(node);
+       LTTNG_ASSERT(_key);
+
+       DIAGNOSTIC_PUSH
+       DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+       event = caa_container_of(node, lttng::sessiond::ust::registry_event, _node.node);
+       DIAGNOSTIC_POP
+
+       LTTNG_ASSERT(event);
+       key = (lttng::sessiond::ust::registry_event *) _key;
+
+       /* It has to be a perfect match. First, compare the event names. */
+       if (event->name != key->name) {
+               goto no_match;
+       }
+
+       /* Compare log levels. */
+       if (event->log_level != key->log_level) {
+               goto no_match;
+       }
+
+       /* Compare the arrays of fields. */
+       if (*event->payload != *key->payload) {
+               goto no_match;
+       }
+
+       /* Compare model URI. */
+       if (event->model_emf_uri != key->model_emf_uri) {
+               goto no_match;
+       }
+
+       /* Match */
+       return 1;
+
+no_match:
+       return 0;
+}
+}; /* namespace */
+
+lsu::registry_channel::registry_channel(unsigned int channel_id,
+               lsu::registry_channel::registered_listener_fn channel_registered_listener,
+               lsu::registry_channel::event_added_listener_fn event_added_listener) :
+       lst::stream_class(channel_id, lst::stream_class::header_type::LARGE),
+       _key{-1ULL},
+       _consumer_key{-1ULL},
+       _metadata_dumped{false},
+       _next_event_id{0},
+       _is_registered_listener{channel_registered_listener},
+       _event_added_listener{event_added_listener},
+       _is_registered{false}
+{
+       _events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+       if (!_events) {
+               LTTNG_THROW_POSIX("Failed to allocate urcu events hash table", ENOMEM);
+       }
+
+       /* Set custom match function. */
+       _events->match_fct = ht_match_event;
+       _events->hash_fct = ht_hash_event;
+}
+
+void lsu::registry_channel::add_event(
+               int session_objd,
+               int channel_objd,
+               std::string name,
+               std::string signature,
+               std::vector<lst::field::cuptr> event_fields,
+               int loglevel_value,
+               nonstd::optional<std::string> model_emf_uri,
+               lttng_buffer_type buffer_type,
+               const ust_app& app,
+               uint32_t& out_event_id)
+{
+       uint32_t event_id;
+       struct cds_lfht_node *nptr;
+       lttng::urcu::read_lock_guard read_lock_guard;
+
+       /*
+        * This should not happen but since it comes from the UST tracer, an
+        * external party, don't assert and simply validate values.
+        */
+       if (session_objd < 0) {
+               LTTNG_THROW_INVALID_ARGUMENT_ERROR(fmt::format(
+                               "Invalid session object descriptor provided by application: session descriptor = {}, app = {}",
+                               session_objd, app));
+       }
+
+       if (channel_objd < 0) {
+               LTTNG_THROW_INVALID_ARGUMENT_ERROR(fmt::format(
+                               "Invalid channel object descriptor provided by application: channel descriptor = {}, app = {}",
+                               channel_objd, app));
+       }
+
+       /* Check if we've reached the maximum possible id. */
+       if (is_max_event_id(_next_event_id)) {
+               LTTNG_THROW_ERROR(fmt::format(
+                               "Failed to allocate new event id (id would overflow): app = {}",
+                               app));
+       }
+
+       auto event = lttng::make_unique_wrapper<lsu::registry_event, registry_event_destroy>(
+                       new lsu::registry_event(_next_event_id, id, session_objd, channel_objd,
+                                       std::move(name), std::move(signature),
+                                       std::move(event_fields), loglevel_value,
+                                       std::move(model_emf_uri)));
+
+       DBG3("%s", fmt::format("UST registry creating event: event = {}", *event).c_str());
+
+       /*
+        * This is an add unique with a custom match function for event. The node
+        * are matched using the event name and signature.
+        */
+       nptr = cds_lfht_add_unique(_events->ht, _events->hash_fct(event.get(), lttng_ht_seed),
+                       _events->match_fct, event.get(), &event->_node.node);
+       if (nptr != &event->_node.node) {
+               if (buffer_type == LTTNG_BUFFER_PER_UID) {
+                       /*
+                        * This is normal, we just have to send the event id of the
+                        * returned node.
+                        */
+                       DIAGNOSTIC_PUSH
+                       DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+                       const auto existing_event = caa_container_of(
+                                       nptr, lttng::sessiond::ust::registry_event, _node.node);
+                       DIAGNOSTIC_POP
+                       event_id = existing_event->id;
+               } else {
+                       LTTNG_THROW_INVALID_ARGUMENT_ERROR(fmt::format(
+                                       "UST registry create event add unique failed for event: event = {}",
+                                       *event));
+               }
+       } else {
+               const auto& event_ref = *event;
+
+               /* Ownership transferred to _events hash table. */
+               event.release();
+
+               /* Request next event id if the node was successfully added. */
+               event_id = event_ref.id;
+
+               /*
+                * Only increment the next id here since we don't want to waste an ID when the event
+                * matches an existing one.
+                */
+               _next_event_id++;
+               _event_added_listener(*this, event_ref);
+       }
+
+       out_event_id = event_id;
+}
+
+lsu::registry_channel::~registry_channel()
+{
+       lttng_ht_destroy(_events);
+}
+
+const lttng::sessiond::trace::type& lsu::registry_channel::get_context() const
+{
+       LTTNG_ASSERT(_is_registered);
+       return lst::stream_class::get_context();
+}
+
+void lsu::registry_channel::set_context(lttng::sessiond::trace::type::cuptr context)
+{
+       /* Must only be set once, on the first channel registration provided by an application. */
+       LTTNG_ASSERT(!_context);
+       _context = std::move(context);
+}
+
+bool lsu::registry_channel::is_registered() const
+{
+       return _is_registered;
+}
+
+void lsu::registry_channel::set_as_registered()
+{
+       if (!_is_registered) {
+               _is_registered = true;
+               _is_registered_listener(*this);
+       }
+}
+
+void lsu::registry_channel::_accept_on_event_classes(
+               lttng::sessiond::trace::trace_class_visitor& visitor) const
+{
+       std::vector<const lttng::sessiond::ust::registry_event *> sorted_event_classes;
+
+       {
+               lttng::urcu::read_lock_guard read_lock_guard;
+               struct lttng_ht_iter iter;
+               const lttng::sessiond::ust::registry_event *event;
+
+               DIAGNOSTIC_PUSH
+               DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+               cds_lfht_for_each_entry(_events->ht, &iter.iter, event, _node.node) {
+                       sorted_event_classes.emplace_back(event);
+               }
+               DIAGNOSTIC_POP
+       }
+
+       std::sort(sorted_event_classes.begin(), sorted_event_classes.end(),
+                       [](const lttng::sessiond::ust::registry_event *a,
+                                       const lttng::sessiond::ust::registry_event *b) {
+                               return a->id < b->id;
+                       });
+
+       for (const auto event : sorted_event_classes) {
+               event->accept(visitor);
+       }
+}
This page took 0.024616 seconds and 4 git commands to generate.