+
+ /* The current allocation length is already the largest we can afford. */
+ if ((old_alloc_len << 1) > (UINT32_MAX >> 1)) {
+ LTTNG_THROW_ERROR(
+ "Failed to reserve trace metadata storage as the max size was already reached");
+ }
+
+ if (new_alloc_len > old_alloc_len) {
+ new_alloc_len =
+ std::max<size_t>(1U << get_count_order(new_alloc_len), old_alloc_len << 1);
+
+ auto newptr = (char *) realloc(_metadata, new_alloc_len);
+ if (!newptr) {
+ LTTNG_THROW_POSIX("Failed to allocate trace metadata storage", errno);
+ }
+
+ _metadata = newptr;
+
+ /* We zero directly the memory from start of allocation. */
+ memset(&_metadata[old_alloc_len], 0, new_alloc_len - old_alloc_len);
+ _metadata_alloc_len = new_alloc_len;
+ }
+
+ _metadata_len += reservation_length;
+}
+
+void lsu::registry_session::_append_metadata_fragment(const std::string& fragment)
+{
+ const auto offset = _metadata_len;
+
+ _increase_metadata_size(fragment.size());
+ memcpy(&_metadata[offset], fragment.c_str(), fragment.size());
+
+ if (_metadata_fd >= 0) {
+ const auto bytes_written =
+ lttng_write(_metadata_fd, fragment.c_str(), fragment.size());
+
+ if (bytes_written != fragment.size()) {
+ LTTNG_THROW_POSIX("Failed to write trace metadata fragment to file", errno);
+ }
+ }
+}
+
+void lsu::registry_session::_reset_metadata()
+{
+ _metadata_len_sent = 0;
+ memset(_metadata, 0, _metadata_alloc_len);
+ _metadata_len = 0;
+
+ if (_metadata_fd > 0) {
+ /* Clear the metadata file's content. */
+ clear_metadata_file(_metadata_fd);
+ }
+}
+
+void lsu::registry_session::_generate_metadata()
+{
+ trace_class::accept(*_metadata_generating_visitor);
+}
+
+void lsu::registry_session::regenerate_metadata()
+{
+ const lttng::pthread::lock_guard registry_lock(_lock);
+
+ /* Resample the clock */
+ _clock = lttng::make_unique<lsu::clock_class>();
+
+ _metadata_version++;
+ _reset_metadata();
+ _generate_metadata();
+}
+
+/*
+ * Lookup enumeration by enum ID.
+ *
+ * Note that there is no need to lock the registry session as this only
+ * performs an RCU-protected look-up. The function also return an rcu-protected
+ * reference, which ensures that the caller keeps the RCU read lock until it
+ * disposes of the object.
+ */
+lsu::registry_enum::const_rcu_protected_reference
+lsu::registry_session::enumeration(const char *enum_name, uint64_t enum_id) const
+{
+ lsu::registry_enum *reg_enum = nullptr;
+ struct lttng_ht_node_str *node;
+ struct lttng_ht_iter iter;
+ lttng::urcu::unique_read_lock rcu_lock;
+ /*
+ * Hack: only the name is used for hashing; the rest of the attributes
+ * can be fudged.
+ */
+ lsu::registry_signed_enum reg_enum_lookup(enum_name, nullptr, 0);
+
+ ASSERT_RCU_READ_LOCKED();
+
+ reg_enum_lookup.id = enum_id;
+ cds_lfht_lookup(_enums->ht,
+ ht_hash_enum((void *) ®_enum_lookup, lttng_ht_seed),
+ ht_match_enum_id,
+ ®_enum_lookup,
+ &iter.iter);
+ node = lttng_ht_iter_get_node<lttng_ht_node_str>(&iter);
+ if (!node) {
+ LTTNG_THROW_PROTOCOL_ERROR(lttng::format(
+ "Unknown enumeration referenced by application event field: enum name = `{}`, enum id = {}",
+ enum_name,
+ enum_id));
+ }
+
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ reg_enum = lttng::utils::container_of(node, &lsu::registry_enum::node);
+ DIAGNOSTIC_POP
+
+ return lsu::registry_enum::const_rcu_protected_reference{ *reg_enum, std::move(rcu_lock) };
+}
+
+/*
+ * Lookup enumeration by name and comparing enumeration entries.
+ * Needs to be called from RCU read-side critical section.
+ */
+lsu::registry_enum *
+lsu::registry_session::_lookup_enum(const lsu::registry_enum *reg_enum_lookup) const
+{
+ lsu::registry_enum *reg_enum = nullptr;
+ struct lttng_ht_node_str *node;
+ struct lttng_ht_iter iter;
+
+ ASSERT_RCU_READ_LOCKED();
+
+ cds_lfht_lookup(_enums->ht,
+ ht_hash_enum((void *) reg_enum_lookup, lttng_ht_seed),
+ ht_match_enum,
+ reg_enum_lookup,
+ &iter.iter);
+ node = lttng_ht_iter_get_node<lttng_ht_node_str>(&iter);
+ if (!node) {
+ goto end;
+ }
+
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ reg_enum = lttng::utils::container_of(node, &lsu::registry_enum::node);
+ DIAGNOSTIC_POP
+
+end:
+ return reg_enum;
+}
+
+/*
+ * Create a lsu::registry_enum from the given parameters and add it to the
+ * registry hash table, or find it if already there.
+ *
+ * Should be called with session registry mutex held.
+ *
+ * We receive ownership of entries.
+ */
+void lsu::registry_session::create_or_find_enum(int session_objd,
+ const char *enum_name,
+ struct lttng_ust_ctl_enum_entry *raw_entries,
+ size_t nr_entries,
+ uint64_t *enum_id)
+{
+ struct cds_lfht_node *nodep;
+ lsu::registry_enum *reg_enum = nullptr, *old_reg_enum;
+ const lttng::urcu::read_lock_guard read_lock_guard;
+ auto entries = lttng::make_unique_wrapper<lttng_ust_ctl_enum_entry, lttng::memory::free>(
+ raw_entries);
+
+ LTTNG_ASSERT(enum_name);
+
+ /*
+ * 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(lttng::format(
+ "Invalid parameters used to create or look-up enumeration from registry session: session_objd = {}",
+ session_objd));
+ }
+ if (nr_entries == 0) {
+ LTTNG_THROW_INVALID_ARGUMENT_ERROR(lttng::format(
+ "Invalid parameters used to create or look-up enumeration from registry session: nr_entries = {}",
+ nr_entries));
+ }
+ if (lttng_strnlen(enum_name, LTTNG_UST_ABI_SYM_NAME_LEN) == LTTNG_UST_ABI_SYM_NAME_LEN) {
+ LTTNG_THROW_INVALID_ARGUMENT_ERROR(
+ "Invalid parameters used to create or look-up enumeration from registry session: enumeration name is not null terminated");
+ }
+
+ if (entries->start.signedness) {
+ reg_enum = new lsu::registry_signed_enum(enum_name, entries.get(), nr_entries);
+ } else {
+ reg_enum = new lsu::registry_unsigned_enum(enum_name, entries.get(), nr_entries);
+ }
+
+ old_reg_enum = _lookup_enum(reg_enum);
+ if (old_reg_enum) {
+ DBG("enum %s already in sess_objd: %u", enum_name, session_objd);
+ /* Fall through. Use prior enum. */
+ destroy_enum(reg_enum);
+ reg_enum = old_reg_enum;
+ } else {
+ DBG("UST registry creating enum: %s, sess_objd: %u", enum_name, session_objd);
+ if (_next_enum_id == -1ULL) {
+ destroy_enum(reg_enum);
+ LTTNG_THROW_ERROR(
+ "Failed to allocate unique enumeration ID as it would overflow");
+ }
+
+ reg_enum->id = _next_enum_id++;
+ nodep = cds_lfht_add_unique(_enums->ht,
+ ht_hash_enum(reg_enum, lttng_ht_seed),
+ ht_match_enum_id,
+ reg_enum,
+ ®_enum->node.node);
+ LTTNG_ASSERT(nodep == ®_enum->node.node);
+ }
+
+ DBG("UST registry reply with enum %s with id %" PRIu64 " in sess_objd: %u",
+ enum_name,
+ reg_enum->id,
+ session_objd);
+ *enum_id = reg_enum->id;