if HAVE_LIBLTTNG_UST_CTL
liblttng_sessiond_common_la_SOURCES += trace-ust.cpp ust-registry.cpp ust-app.cpp \
ust-consumer.cpp ust-consumer.hpp notify-apps.cpp \
- ust-metadata.cpp ust-clock.hpp ust-clock.cpp \
+ ust-clock-class.hpp ust-clock-class.cpp \
agent-thread.cpp agent-thread.hpp \
- ust-field-utils.hpp ust-field-utils.cpp \
+ ust-field-convert.cpp ust-field-convert.hpp \
ust-sigbus.cpp \
ust-registry-session.cpp \
+ ust-registry-event.cpp ust-registry-event.hpp \
+ ust-registry-channel.cpp ust-registry-channel.hpp \
ust-registry-session-uid.cpp \
- ust-registry-session-pid.cpp
-
+ ust-registry-session-pid.cpp \
+ tsdl-trace-class-visitor.hpp tsdl-trace-class-visitor.cpp
endif
# link on liblttngctl for check if sessiond is already alive.
/* Sleep for 100ms between each check for the shm path's deletion. */
#define SESSION_DESTROY_SHM_PATH_CHECK_DELAY_US 100000
+namespace lsu = lttng::sessiond::ust;
+
static enum lttng_error_code wait_on_path(void *path);
namespace {
return ret;
}
-static
-int clear_metadata_file(int fd)
-{
- int ret;
- off_t lseek_ret;
-
- lseek_ret = lseek(fd, 0, SEEK_SET);
- if (lseek_ret < 0) {
- PERROR("lseek");
- ret = -1;
- goto end;
- }
-
- ret = ftruncate(fd, 0);
- if (ret < 0) {
- PERROR("ftruncate");
- goto end;
- }
-
-end:
- return ret;
-}
-
-static
-int ust_regenerate_metadata(struct ltt_ust_session *usess)
-{
- int ret = 0;
- struct buffer_reg_uid *uid_reg = NULL;
- struct buffer_reg_session *session_reg = NULL;
-
- rcu_read_lock();
- cds_list_for_each_entry(uid_reg, &usess->buffer_reg_uid_list, lnode) {
- ust_registry_session *registry;
- struct ust_registry_channel *chan;
- struct lttng_ht_iter iter_chan;
-
- session_reg = uid_reg->registry;
- registry = session_reg->reg.ust;
-
- pthread_mutex_lock(®istry->_lock);
- registry->_metadata_len_sent = 0;
- memset(registry->_metadata, 0, registry->_metadata_alloc_len);
- registry->_metadata_len = 0;
- registry->_metadata_version++;
- if (registry->_metadata_fd > 0) {
- /* Clear the metadata file's content. */
- ret = clear_metadata_file(registry->_metadata_fd);
- if (ret) {
- pthread_mutex_unlock(®istry->_lock);
- goto end;
- }
- }
-
- ret = ust_metadata_session_statedump(registry);
- if (ret) {
- pthread_mutex_unlock(®istry->_lock);
- ERR("Failed to generate session metadata (err = %d)",
- ret);
- goto end;
- }
- cds_lfht_for_each_entry(registry->_channels->ht, &iter_chan.iter,
- chan, node.node) {
- struct ust_registry_event *event;
- struct lttng_ht_iter iter_event;
-
- chan->metadata_dumped = 0;
-
- ret = ust_metadata_channel_statedump(registry, chan);
- if (ret) {
- pthread_mutex_unlock(®istry->_lock);
- ERR("Failed to generate channel metadata "
- "(err = %d)", ret);
- goto end;
- }
- cds_lfht_for_each_entry(chan->events->ht, &iter_event.iter,
- event, node.node) {
- event->metadata_dumped = 0;
- ret = ust_metadata_event_statedump(registry,
- chan, event);
- if (ret) {
- pthread_mutex_unlock(®istry->_lock);
- ERR("Failed to generate event metadata "
- "(err = %d)", ret);
- goto end;
- }
- }
- }
- pthread_mutex_unlock(®istry->_lock);
- }
-
-end:
- rcu_read_unlock();
- return ret;
-}
-
/*
* Command LTTNG_REGENERATE_METADATA from the lttng-ctl library.
*
}
if (session->ust_session) {
- ret = ust_regenerate_metadata(session->ust_session);
+ ret = trace_ust_regenerate_metadata(session->ust_session);
if (ret < 0) {
ERR("Failed to regenerate the UST metadata");
goto end;
#include <common/exception.hpp>
#include <common/format.hpp>
+#include <set>
+
namespace lst = lttng::sessiond::trace;
namespace {
mantissa_digits(in_mantissa_digits)
{
/* Allowed (exponent, mantissa) pairs. */
- static const std::vector<std::pair<unsigned int, unsigned int>> allowed_pairs{
+ static const std::set<std::pair<unsigned int, unsigned int>> allowed_pairs{
{5, 11}, /* binary16 */
{8, 24}, /* binary32 */
{11, 53}, /* binary64 */
{15, 113}, /* binary128 */
};
- const auto input_pair = decltype(allowed_pairs)::value_type(exponent_digits, mantissa_digits);
- for (const auto& pair : allowed_pairs) {
- if (input_pair == pair) {
- /* mantissa and exponent digits is a valid pair. */
- return;
- }
+ if (allowed_pairs.find({exponent_digits, mantissa_digits}) != allowed_pairs.end()) {
+ /* mantissa and exponent digits is a valid pair. */
+ return;
}
LTTNG_THROW_INVALID_ARGUMENT_ERROR(
void lst::variant_type::accept(type_visitor& visitor) const
{
visitor.visit(*this);
-}
\ No newline at end of file
+}
public:
array_type(unsigned int alignment, type::cuptr element_type);
- const type::cuptr element_type;
+ const type::cuptr element_type;
protected:
virtual bool _is_equal(const type& base_other) const noexcept override;
#include <common/hashtable/hashtable.hpp>
#include <common/dynamic-array.hpp>
#include <common/make-unique-wrapper.hpp>
+#include <common/pthread-lock.hpp>
#include <lttng/rotation.h>
#include <lttng/location.h>
#include <lttng/lttng-error.h>
LTTNG_ASSERT(!ret);
}
+int trace_ust_regenerate_metadata(struct ltt_ust_session *usess)
+{
+ int ret = 0;
+ struct buffer_reg_uid *uid_reg = NULL;
+ struct buffer_reg_session *session_reg = NULL;
+
+ rcu_read_lock();
+ cds_list_for_each_entry(uid_reg, &usess->buffer_reg_uid_list, lnode) {
+ ust_registry_session *registry;
+
+ session_reg = uid_reg->registry;
+ registry = session_reg->reg.ust;
+
+ try {
+ registry->regenerate_metadata();
+ } catch (const std::exception& ex) {
+ ERR("Failed to regenerate user space session metadata: %s", ex.what());
+ ret = -1;
+ goto end;
+ }
+ }
+
+end:
+ rcu_read_unlock();
+ return ret;
+}
+
/*
* Iterate over a hash table containing channels and cleanup safely.
*/
void trace_ust_delete_channel(struct lttng_ht *ht,
struct ltt_ust_channel *channel);
+int trace_ust_regenerate_metadata(struct ltt_ust_session *usess);
+
/*
* Destroy functions free() the data structure and remove from linked list if
* it's applies.
return;
}
+static inline int trace_ust_regenerate_metadata(
+ struct ltt_ust_session *usess __attribute__((unused)))
+{
+ return 0;
+}
+
static inline
struct agent *trace_ust_find_agent(
struct ltt_ust_session *session __attribute__((unused)),
--- /dev/null
+/*
+ * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "clock-class.hpp"
+#include "tsdl-trace-class-visitor.hpp"
+
+#include <common/exception.hpp>
+#include <common/format.hpp>
+#include <common/make-unique.hpp>
+#include <common/uuid.hpp>
+
+#include <array>
+#include <queue>
+#include <locale>
+
+namespace lst = lttng::sessiond::trace;
+namespace tsdl = lttng::sessiond::tsdl;
+
+namespace {
+const auto ctf_spec_major = 1;
+const auto ctf_spec_minor = 8;
+
+/*
+ * A previous implementation always prepended '_' to the identifiers in order to
+ * side-step the problem of escaping TSDL keywords and ensuring identifiers
+ * started with an alphabetic character.
+ *
+ * Changing this behaviour to a smarter algorithm would break readers that have
+ * come to expect this initial underscore.
+ */
+std::string escape_tsdl_identifier(const std::string& original_identifier)
+{
+ if (original_identifier.size() == 0) {
+ LTTNG_THROW_ERROR("Invalid 0-length identifier used in trace description");
+ }
+
+ std::string new_identifier;
+ /* Optimisticly assume most identifiers are valid and allocate the same length. */
+ new_identifier.reserve(original_identifier.size());
+ new_identifier = "_";
+
+ /* Replace illegal characters by '_'. */
+ std::locale c_locale{"C"};
+ for (const auto current_char : original_identifier) {
+ if (!std::isalnum(current_char, c_locale) && current_char != '_') {
+ new_identifier += '_';
+ } else {
+ new_identifier += current_char;
+ }
+ }
+
+ return new_identifier;
+}
+
+std::string escape_tsdl_env_string_value(const std::string& original_string)
+{
+ std::string escaped_string;
+
+ escaped_string.reserve(original_string.size());
+
+ for (const auto c : original_string) {
+ switch (c) {
+ case '\n':
+ escaped_string += "\\n";
+ break;
+ case '\\':
+ escaped_string += "\\\\";
+ break;
+ case '"':
+ escaped_string += "\"";
+ break;
+ default:
+ escaped_string += c;
+ break;
+ }
+ }
+
+ return escaped_string;
+}
+
+class tsdl_field_visitor : public lttng::sessiond::trace::field_visitor,
+ public lttng::sessiond::trace::type_visitor {
+public:
+ tsdl_field_visitor(const lst::abi& abi, unsigned int indentation_level) :
+ _indentation_level{indentation_level}, _trace_abi{abi}
+ {
+ }
+
+ std::string& get_description()
+ {
+ return _description;
+ }
+
+private:
+ virtual void visit(const lst::field& field) override final
+ {
+ /*
+ * Hack: keep the name of the field being visited since
+ * the tracers can express sequences, variants, and arrays with an alignment
+ * constraint, which is not expressible in TSDL. To work around this limitation, an
+ * empty structure declaration is inserted when needed to express the aligment
+ * constraint. The name of this structure is generated using the field's name.
+ */
+ _escaped_current_field_name = escape_tsdl_identifier(field.name);
+
+ field._type->accept(*this);
+ _description += " ";
+ _description += _escaped_current_field_name;
+
+ /*
+ * Some types requires suffixes to be appended (e.g. the length of arrays
+ * and sequences, the mappings of enumerations).
+ */
+ while (!_type_suffixes.empty()) {
+ _description += _type_suffixes.front();
+ _type_suffixes.pop();
+ }
+
+ _description += ";";
+ _escaped_current_field_name.clear();
+ }
+
+ virtual void visit(const lst::integer_type& type) override final
+ {
+ _description += "integer { ";
+
+ /* Mandatory properties (no defaults). */
+ _description += fmt::format("size = {size}; align = {alignment};",
+ fmt::arg("size", type.size),
+ fmt::arg("alignment", type.alignment));
+
+ /* Defaults to unsigned. */
+ if (type.signedness == lst::integer_type::signedness::SIGNED) {
+ _description += " signed = true;";
+ }
+
+ /* Defaults to 10. */
+ if (type.base != lst::integer_type::base::DECIMAL) {
+ unsigned int base;
+
+ switch (type.base) {
+ case lst::integer_type::base::BINARY:
+ base = 2;
+ break;
+ case lst::integer_type::base::OCTAL:
+ base = 8;
+ break;
+ case lst::integer_type::base::HEXADECIMAL:
+ base = 16;
+ break;
+ default:
+ LTTNG_THROW_ERROR(fmt::format(
+ "Unexpected base encountered while serializing integer type to TSDL: base = {}",
+ (int) type.base));
+ }
+
+ _description += fmt::format(" base = {};", base);
+ }
+
+ /* Defaults to the trace's native byte order. */
+ if (type.byte_order != _trace_abi.byte_order) {
+ const auto byte_order_str = type.byte_order == lst::byte_order::BIG_ENDIAN_ ? "be" : "le";
+
+ _description += fmt::format(" byte_order = {};", byte_order_str);
+ }
+
+ if (_current_integer_encoding_override) {
+ const char *encoding_str;
+
+ switch (*_current_integer_encoding_override) {
+ case lst::string_type::encoding::ASCII:
+ encoding_str = "ASCII";
+ break;
+ case lst::string_type::encoding::UTF8:
+ encoding_str = "UTF8";
+ break;
+ default:
+ LTTNG_THROW_ERROR(fmt::format(
+ "Unexpected encoding encountered while serializing integer type to TSDL: encoding = {}",
+ (int) *_current_integer_encoding_override));
+ }
+
+ _description += fmt::format(" encoding = {};", encoding_str);
+ _current_integer_encoding_override.reset();
+ }
+
+ _description += " }";
+ }
+
+ virtual void visit(const lst::floating_point_type& type) override final
+ {
+ _description += fmt::format(
+ "floating_point {{ align = {alignment}; mant_dig = {mantissa_digits}; exp_dig = {exponent_digits};",
+ fmt::arg("alignment", type.alignment),
+ fmt::arg("mantissa_digits", type.mantissa_digits),
+ fmt::arg("exponent_digits", type.exponent_digits));
+
+ /* Defaults to the trace's native byte order. */
+ if (type.byte_order != _trace_abi.byte_order) {
+ const auto byte_order_str = type.byte_order == lst::byte_order::BIG_ENDIAN_ ? "be" : "le";
+
+ _description += fmt::format(" byte_order = {};", byte_order_str);
+ }
+
+ _description += " }";
+ }
+
+ template <class EnumerationType>
+ void visit_enumeration(const EnumerationType& type)
+ {
+ /* name follows, when applicable. */
+ _description += "enum : ";
+
+ tsdl_field_visitor integer_visitor{_trace_abi, _indentation_level};
+
+ integer_visitor.visit(static_cast<const lst::integer_type&>(type));
+ _description += integer_visitor.get_description() + " {\n";
+
+ const auto mappings_indentation_level = _indentation_level + 1;
+
+ bool first_mapping = true;
+ for (const auto& mapping : *type._mappings) {
+ if (!first_mapping) {
+ _description += ",\n";
+ }
+
+ _description.resize(_description.size() + mappings_indentation_level, '\t');
+ if (!mapping.range) {
+ _description += fmt::format("\"{}\"", mapping.name);
+ } else if (mapping.range->begin == mapping.range->end) {
+ _description += fmt::format(
+ "\"{mapping_name}\" = {mapping_value}",
+ fmt::arg("mapping_name", mapping.name),
+ fmt::arg("mapping_value", mapping.range->begin));
+ } else {
+ _description += fmt::format(
+ "\"{mapping_name}\" = {mapping_range_begin} ... {mapping_range_end}",
+ fmt::arg("mapping_name", mapping.name),
+ fmt::arg("mapping_range_begin",
+ mapping.range->begin),
+ fmt::arg("mapping_range_end", mapping.range->end));
+ }
+
+ first_mapping = false;
+ }
+
+ _description += "\n";
+ _description.resize(_description.size() + _indentation_level, '\t');
+ _description += "}";
+ }
+
+ virtual void visit(const lst::signed_enumeration_type& type) override final
+ {
+ visit_enumeration(type);
+ }
+
+ virtual void visit(const lst::unsigned_enumeration_type& type) override final
+ {
+ visit_enumeration(type);
+ }
+
+ virtual void visit(const lst::static_length_array_type& type) override final
+ {
+ if (type.alignment != 0) {
+ LTTNG_ASSERT(_escaped_current_field_name.size() > 0);
+ _description += fmt::format(
+ "struct {{ }} align({alignment}) {field_name}_padding;\n",
+ fmt::arg("alignment", type.alignment),
+ fmt::arg("field_name", _escaped_current_field_name));
+ _description.resize(_description.size() + _indentation_level, '\t');
+ }
+
+ type.element_type->accept(*this);
+ _type_suffixes.emplace(fmt::format("[{}]", type.length));
+ }
+
+ virtual void visit(const lst::dynamic_length_array_type& type) override final
+ {
+ if (type.alignment != 0) {
+ /*
+ * Note that this doesn't support nested sequences. For
+ * the moment, tracers can't express those. However, we
+ * could wrap nested sequences in structures, which
+ * would allow us to express alignment constraints.
+ */
+ LTTNG_ASSERT(_escaped_current_field_name.size() > 0);
+ _description += fmt::format(
+ "struct {{ }} align({alignment}) {field_name}_padding;\n",
+ fmt::arg("alignment", type.alignment),
+ fmt::arg("field_name", _escaped_current_field_name));
+ _description.resize(_description.size() + _indentation_level, '\t');
+ }
+
+ type.element_type->accept(*this);
+ _type_suffixes.emplace(fmt::format(
+ "[{}]", escape_tsdl_identifier(type.length_field_name)));
+ }
+
+ virtual void visit(const lst::null_terminated_string_type& type) override final
+ {
+ /* Defaults to UTF-8. */
+ if (type.encoding == lst::null_terminated_string_type::encoding::ASCII) {
+ _description += "string { encoding = ASCII }";
+ } else {
+ _description += "string";
+ }
+ }
+
+ virtual void visit(const lst::structure_type& type) override final
+ {
+ _indentation_level++;
+ _description += "struct {";
+
+ for (const auto& field : type._fields) {
+ _description += "\n";
+ _description.resize(_description.size() + _indentation_level, '\t');
+ field->accept(*this);
+ }
+
+ _indentation_level--;
+ if (type._fields.size() != 0) {
+ _description += "\n";
+ _description.resize(_description.size() + _indentation_level, '\t');
+ }
+
+ _description += "};";
+ }
+
+ virtual void visit(const lst::variant_type& type) override final
+ {
+ if (type.alignment != 0) {
+ LTTNG_ASSERT(_escaped_current_field_name.size() > 0);
+ _description += fmt::format(
+ "struct {{ }} align({alignment}) {field_name}_padding;\n",
+ fmt::arg("alignment", type.alignment),
+ fmt::arg("field_name", _escaped_current_field_name));
+ _description.resize(_description.size() + _indentation_level, '\t');
+ }
+
+ _indentation_level++;
+ _description += fmt::format("variant <{}> {\n", escape_tsdl_identifier(type.tag_name));
+
+ bool first_field = true;
+ for (const auto& field : type._choices) {
+ if (!first_field) {
+ _description += ",\n";
+ }
+
+ _description.resize(_description.size() + _indentation_level, '\t');
+ field->accept(*this);
+ first_field = false;
+ }
+
+ _description += "\n";
+ _description.resize(_description.size() + _indentation_level, '\t');
+ _description += "};";
+ _indentation_level--;
+ }
+
+ lst::type::cuptr create_character_type(enum lst::string_type::encoding encoding)
+ {
+ _current_integer_encoding_override = encoding;
+ return lttng::make_unique<lst::integer_type>(8, _trace_abi.byte_order, 8,
+ lst::integer_type::signedness::UNSIGNED,
+ lst::integer_type::base::DECIMAL);
+ }
+
+ virtual void visit(const lst::static_length_string_type& type) override final
+ {
+ /*
+ * TSDL expresses static-length strings as arrays of 8-bit integer with
+ * an encoding specified.
+ */
+ const auto char_array = lttng::make_unique<lst::static_length_array_type>(
+ type.alignment, create_character_type(type.encoding), type.length);
+
+ visit(*char_array);
+ }
+
+ virtual void visit(const lst::dynamic_length_string_type& type) override final
+ {
+ /*
+ * TSDL expresses dynamic-length strings as arrays of 8-bit integer with
+ * an encoding specified.
+ */
+ const auto char_sequence = lttng::make_unique<lst::dynamic_length_array_type>(
+ type.alignment, create_character_type(type.encoding),
+ type.length_field_name);
+
+ visit(*char_sequence);
+ }
+
+ std::string _escaped_current_field_name;
+ /*
+ * Encoding to specify for the next serialized integer type.
+ * Since the integer_type does not allow an encoding to be specified (it is a TSDL-specific
+ * concept), this attribute is used when expressing static or dynamic length strings as
+ * arrays/sequences of bytes with an encoding.
+ */
+ nonstd::optional<enum lst::string_type::encoding> _current_integer_encoding_override;
+
+ unsigned int _indentation_level;
+ const lst::abi& _trace_abi;
+
+ std::queue<std::string> _type_suffixes;
+
+ /* Description in TSDL format. */
+ std::string _description;
+};
+} /* namespace */
+
+tsdl::trace_class_visitor::trace_class_visitor(const lst::abi& trace_abi,
+ tsdl::append_metadata_fragment_function append_metadata_fragment) :
+ _trace_abi{trace_abi}, _append_metadata_fragment(append_metadata_fragment)
+{
+}
+
+void tsdl::trace_class_visitor::append_metadata_fragment(const std::string& fragment) const
+{
+ _append_metadata_fragment(fragment);
+}
+
+void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::trace_class& trace_class)
+{
+ /* Declare type aliases, trace class, and packet header. */
+ auto trace_class_tsdl = fmt::format(
+ "/* CTF {ctf_major}.{ctf_minor} */\n\n"
+ "typealias integer {{ size = 8; align = {uint8_t_alignment}; signed = false; }} := uint8_t;\n"
+ "typealias integer {{ size = 16; align = {uint16_t_alignment}; signed = false; }} := uint16_t;\n"
+ "typealias integer {{ size = 32; align = {uint32_t_alignment}; signed = false; }} := uint32_t;\n"
+ "typealias integer {{ size = 64; align = {uint64_t_alignment}; signed = false; }} := uint64_t;\n"
+ "typealias integer {{ size = {bits_per_long}; align = {long_alignment}; signed = false; }} := unsigned long;\n"
+ "typealias integer {{ size = 5; align = 1; signed = false; }} := uint5_t;\n"
+ "typealias integer {{ size = 27; align = 1; signed = false; }} := uint27_t;\n"
+ "\n"
+ "trace {{\n"
+ " major = {ctf_major};\n"
+ " minor = {ctf_minor};\n"
+ " uuid = \"{uuid}\";\n"
+ " byte_order = {byte_order};\n"
+ " packet.header := struct {{\n"
+ " uint32_t magic;\n"
+ " uint8_t uuid[16];\n"
+ " uint32_t stream_id;\n"
+ " uint64_t stream_instance_id;\n"
+ " }};\n"
+ "}};\n\n",
+ fmt::arg("ctf_major", ctf_spec_major),
+ fmt::arg("ctf_minor", ctf_spec_minor),
+ fmt::arg("uint8_t_alignment", trace_class.abi.uint8_t_alignment),
+ fmt::arg("uint16_t_alignment", trace_class.abi.uint16_t_alignment),
+ fmt::arg("uint32_t_alignment", trace_class.abi.uint32_t_alignment),
+ fmt::arg("uint64_t_alignment", trace_class.abi.uint64_t_alignment),
+ fmt::arg("long_alignment", trace_class.abi.long_alignment),
+ fmt::arg("long_size", trace_class.abi.long_alignment),
+ fmt::arg("bits_per_long", trace_class.abi.bits_per_long),
+ fmt::arg("uuid", lttng::utils::uuid_to_str(trace_class.uuid)),
+ fmt::arg("byte_order",
+ trace_class.abi.byte_order == lst::byte_order::BIG_ENDIAN_ ?
+ "be" :
+ "le"));
+
+ /* Declare trace scope and type aliases. */
+ append_metadata_fragment(std::move(trace_class_tsdl));
+}
+
+void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::clock_class& clock_class)
+{
+ auto uuid_str = clock_class.uuid ?
+ fmt::format(" uuid = \"{}\";\n",
+ lttng::utils::uuid_to_str(*clock_class.uuid)) :
+ "";
+
+ /* Assumes a single clock that maps to specific stream class fields/roles. */
+ auto clock_class_str = fmt::format(
+ "clock {{\n"
+ " name = \"{name}\";\n"
+ /* Optional uuid. */
+ "{uuid}"
+ " description = \"{description}\";\n"
+ " freq = {frequency};\n"
+ " offset = {offset};\n"
+ "}};\n"
+ "\n"
+ "typealias integer {{\n"
+ " size = 27; align = 1; signed = false;\n"
+ " map = clock.{name}.value;\n"
+ "}} := uint27_clock_{name}_t;\n"
+ "\n"
+ "typealias integer {{\n"
+ " size = 32; align = {uint32_t_alignment}; signed = false;\n"
+ " map = clock.{name}.value;\n"
+ "}} := uint32_clock_{name}_t;\n"
+ "\n"
+ "typealias integer {{\n"
+ " size = 64; align = {uint64_t_alignment}; signed = false;\n"
+ " map = clock.{name}.value;\n"
+ "}} := uint64_clock_{name}_t;\n"
+ "\n"
+ "struct packet_context {{\n"
+ " uint64_clock_{name}_t timestamp_begin;\n"
+ " uint64_clock_{name}_t timestamp_end;\n"
+ " uint64_t content_size;\n"
+ " uint64_t packet_size;\n"
+ " uint64_t packet_seq_num;\n"
+ " unsigned long events_discarded;\n"
+ " uint32_t cpu_id;\n"
+ "}};\n"
+ "\n"
+ "struct event_header_compact {{\n"
+ " enum : uint5_t {{ compact = 0 ... 30, extended = 31 }} id;\n"
+ " variant <id> {{\n"
+ " struct {{\n"
+ " uint27_clock_{name}_t timestamp;\n"
+ " }} compact;\n"
+ " struct {{\n"
+ " uint32_t id;\n"
+ " uint64_clock_{name}_t timestamp;\n"
+ " }} extended;\n"
+ " }} v;\n"
+ "}} align({uint32_t_alignment});\n"
+ "\n"
+ "struct event_header_large {{\n"
+ " enum : uint16_t {{ compact = 0 ... 65534, extended = 65535 }} id;\n"
+ " variant <id> {{\n"
+ " struct {{\n"
+ " uint32_clock_{name}_t timestamp;\n"
+ " }} compact;\n"
+ " struct {{\n"
+ " uint32_t id;\n"
+ " uint64_clock_{name}_t timestamp;\n"
+ " }} extended;\n"
+ " }} v;\n"
+ "}} align({uint16_t_alignment});\n\n",
+ fmt::arg("name", clock_class.name),
+ fmt::arg("uuid", uuid_str),
+ fmt::arg("description", clock_class.description),
+ fmt::arg("frequency", clock_class.frequency),
+ fmt::arg("offset", clock_class.offset),
+ fmt::arg("uint16_t_alignment", _trace_abi.uint16_t_alignment),
+ fmt::arg("uint32_t_alignment", _trace_abi.uint32_t_alignment),
+ fmt::arg("uint64_t_alignment", _trace_abi.uint64_t_alignment));
+
+ append_metadata_fragment(std::move(clock_class_str));
+}
+
+void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::stream_class& stream_class)
+{
+ /* Declare stream. */
+ auto stream_class_str = fmt::format("stream {{\n"
+ " id = {id};\n"
+ " event.header := {header_type};\n"
+ " packet.context := struct packet_context;\n",
+ fmt::arg("id", stream_class.id),
+ fmt::arg("header_type", stream_class.header_type == lst::stream_class::header_type::COMPACT ?
+ "struct event_header_compact" :
+ "struct event_header_large"));
+
+ auto context_field_visitor = tsdl_field_visitor(_trace_abi, 1);
+
+ stream_class.get_context().accept(static_cast<lst::type_visitor&>(context_field_visitor));
+
+ stream_class_str += fmt::format(" event.context := {}\n}};\n\n",
+ context_field_visitor.get_description());
+
+ append_metadata_fragment(stream_class_str);
+}
+
+void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::event_class& event_class)
+{
+ auto event_class_str = fmt::format("event {{\n"
+ " name = \"{name}\";\n"
+ " id = {id};\n"
+ " stream_id = {stream_class_id};\n"
+ " loglevel = {log_level};\n",
+ fmt::arg("name", event_class.name),
+ fmt::arg("id", event_class.id),
+ fmt::arg("stream_class_id", event_class.stream_class_id),
+ fmt::arg("log_level", event_class.log_level));
+
+ if (event_class.model_emf_uri) {
+ event_class_str += fmt::format(
+ " model.emf.uri = \"{}\";\n", *event_class.model_emf_uri);
+ }
+
+ auto payload_visitor = tsdl_field_visitor(_trace_abi, 1);
+
+ event_class.payload->accept(static_cast<lst::type_visitor&>(payload_visitor));
+
+ event_class_str += fmt::format(
+ " fields := {}\n}};\n\n", payload_visitor.get_description());
+
+ append_metadata_fragment(event_class_str);
+}
+
+void tsdl::trace_class_visitor::environment_begin()
+{
+ _environment += "env {\n";
+}
+
+void tsdl::trace_class_visitor::visit(
+ const lttng::sessiond::trace::environment_field<int64_t>& field)
+{
+ _environment += fmt::format(" {} = {};\n", field.name, field.value);
+}
+
+void tsdl::trace_class_visitor::visit(
+ const lttng::sessiond::trace::environment_field<const char *>& field)
+{
+ _environment += fmt::format(
+ " {} = \"{}\";\n", field.name, escape_tsdl_env_string_value(field.value));
+}
+
+void tsdl::trace_class_visitor::environment_end()
+{
+ _environment += "};\n\n";
+ append_metadata_fragment(_environment);
+ _environment.clear();
+}
--- /dev/null
+/*
+ * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef LTTNG_TSDL_TRACE_CLASS_VISITOR_H
+#define LTTNG_TSDL_TRACE_CLASS_VISITOR_H
+
+#include "trace-class.hpp"
+#include "stream-class.hpp"
+#include "event-class.hpp"
+
+#include <vendor/optional.hpp>
+
+#include <functional>
+
+namespace lttng {
+namespace sessiond {
+namespace tsdl {
+
+using append_metadata_fragment_function = std::function<void(const std::string& fragment)>;
+
+class trace_class_visitor : public lttng::sessiond::trace::trace_class_visitor {
+public:
+ trace_class_visitor(const lttng::sessiond::trace::abi& trace_abi,
+ append_metadata_fragment_function append_metadata);
+
+ /* trace class visitor interface. */
+ virtual void visit(const lttng::sessiond::trace::trace_class& trace_class) override final;
+
+ /* clock class visitor interface. */
+ virtual void visit(const lttng::sessiond::trace::clock_class& clock_class) override final;
+
+ /* environment visitor interface. */
+ virtual void environment_begin() override final;
+ virtual void visit(const lttng::sessiond::trace::environment_field<int64_t>& field) override final;
+ virtual void visit(const lttng::sessiond::trace::environment_field<const char *>& field) override final;
+ virtual void environment_end() override final;
+
+ /* stream class visitor interface. */
+ virtual void visit(const lttng::sessiond::trace::stream_class& stream_class) override final;
+
+ /* event class visitor interface. */
+ virtual void visit(const lttng::sessiond::trace::event_class& event_class) override final;
+
+private:
+ /* Coherent (parseable) fragments must be appended. */
+ void append_metadata_fragment(const std::string& fragment) const;
+
+ const lttng::sessiond::trace::abi& _trace_abi;
+ const append_metadata_fragment_function _append_metadata_fragment;
+ std::string _environment;
+};
+
+} /* namespace tsdl */
+} /* namespace sessiond */
+} /* namespace lttng */
+
+#endif /* LTTNG_TSDL_TRACE_CLASS_VISITOR_H */
*/
#define _LGPL_SOURCE
+
+#include "buffer-registry.hpp"
+#include "condition-internal.hpp"
+#include "event-notifier-error-accounting.hpp"
+#include "event.hpp"
+#include "fd-limit.hpp"
+#include "field.hpp"
+#include "health-sessiond.hpp"
+#include "lttng-sessiond.hpp"
+#include "lttng-ust-ctl.hpp"
+#include "lttng-ust-error.hpp"
+#include "notification-thread-commands.hpp"
+#include "rotate.hpp"
+#include "session.hpp"
+#include "ust-app.hpp"
+#include "ust-consumer.hpp"
+#include "ust-field-convert.hpp"
+#include "utils.hpp"
+
+#include <common/bytecode/bytecode.hpp>
+#include <common/common.hpp>
+#include <common/compat/errno.hpp>
+#include <common/exception.hpp>
+#include <common/format.hpp>
+#include <common/hashtable/utils.hpp>
+#include <common/make-unique.hpp>
+#include <common/sessiond-comm/sessiond-comm.hpp>
+#include <common/urcu.hpp>
+
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule-matches-internal.hpp>
+#include <lttng/condition/event-rule-matches.h>
+#include <lttng/event-rule/event-rule-internal.hpp>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/user-tracepoint.h>
+#include <lttng/trigger/trigger-internal.hpp>
+
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <pthread.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <urcu/compiler.h>
-#include <signal.h>
+#include <vector>
-#include <common/bytecode/bytecode.hpp>
-#include <common/compat/errno.hpp>
-#include <common/common.hpp>
-#include <common/hashtable/utils.hpp>
-#include <lttng/event-rule/event-rule.h>
-#include <lttng/event-rule/event-rule-internal.hpp>
-#include <lttng/event-rule/user-tracepoint.h>
-#include <lttng/condition/condition.h>
-#include <lttng/condition/event-rule-matches-internal.hpp>
-#include <lttng/condition/event-rule-matches.h>
-#include <lttng/trigger/trigger-internal.hpp>
-#include <common/sessiond-comm/sessiond-comm.hpp>
-
-#include "buffer-registry.hpp"
-#include "condition-internal.hpp"
-#include "fd-limit.hpp"
-#include "health-sessiond.hpp"
-#include "ust-app.hpp"
-#include "ust-consumer.hpp"
-#include "lttng-ust-ctl.hpp"
-#include "lttng-ust-error.hpp"
-#include "utils.hpp"
-#include "session.hpp"
-#include "lttng-sessiond.hpp"
-#include "notification-thread-commands.hpp"
-#include "rotate.hpp"
-#include "event.hpp"
-#include "event-notifier-error-accounting.hpp"
-#include "ust-field-utils.hpp"
+namespace lsu = lttng::sessiond::ust;
+namespace lst = lttng::sessiond::trace;
struct lttng_ht *ust_app_ht;
struct lttng_ht *ust_app_ht_by_sock;
static uint64_t _next_session_id;
static pthread_mutex_t next_session_id_lock = PTHREAD_MUTEX_INITIALIZER;
+namespace {
+
+/*
+ * Return the session registry according to the buffer type of the given
+ * session.
+ *
+ * A registry per UID object MUST exists before calling this function or else
+ * it LTTNG_ASSERT() if not found. RCU read side lock must be acquired.
+ */
+static ust_registry_session *get_session_registry(
+ const struct ust_app_session *ua_sess)
+{
+ ust_registry_session *registry = NULL;
+
+ LTTNG_ASSERT(ua_sess);
+
+ switch (ua_sess->buffer_type) {
+ case LTTNG_BUFFER_PER_PID:
+ {
+ struct buffer_reg_pid *reg_pid = buffer_reg_pid_find(ua_sess->id);
+ if (!reg_pid) {
+ goto error;
+ }
+ registry = reg_pid->registry->reg.ust;
+ break;
+ }
+ case LTTNG_BUFFER_PER_UID:
+ {
+ struct buffer_reg_uid *reg_uid = buffer_reg_uid_find(
+ ua_sess->tracing_id, ua_sess->bits_per_long,
+ lttng_credentials_get_uid(&ua_sess->real_credentials));
+ if (!reg_uid) {
+ goto error;
+ }
+ registry = reg_uid->registry->reg.ust;
+ break;
+ }
+ default:
+ abort();
+ };
+
+error:
+ return registry;
+}
+
+ust_registry_session::locked_ptr
+get_locked_session_registry(const struct ust_app_session *ua_sess)
+{
+ auto session = get_session_registry(ua_sess);
+ if (session) {
+ pthread_mutex_lock(&session->_lock);
+ }
+
+ return ust_registry_session::locked_ptr{session};
+}
+} /* namespace */
+
/*
* Return the incremented value of next_channel_key.
*/
free(obj);
}
-/*
- * Return the session registry according to the buffer type of the given
- * session.
- *
- * A registry per UID object MUST exists before calling this function or else
- * it LTTNG_ASSERT() if not found. RCU read side lock must be acquired.
- */
-static ust_registry_session *get_session_registry(
- struct ust_app_session *ua_sess)
-{
- ust_registry_session *registry = NULL;
-
- LTTNG_ASSERT(ua_sess);
-
- switch (ua_sess->buffer_type) {
- case LTTNG_BUFFER_PER_PID:
- {
- struct buffer_reg_pid *reg_pid = buffer_reg_pid_find(ua_sess->id);
- if (!reg_pid) {
- goto error;
- }
- registry = reg_pid->registry->reg.ust;
- break;
- }
- case LTTNG_BUFFER_PER_UID:
- {
- struct buffer_reg_uid *reg_uid = buffer_reg_uid_find(
- ua_sess->tracing_id, ua_sess->bits_per_long,
- lttng_credentials_get_uid(&ua_sess->real_credentials));
- if (!reg_uid) {
- goto error;
- }
- registry = reg_uid->registry->reg.ust;
- break;
- }
- default:
- abort();
- };
-
-error:
- return registry;
-}
-
/*
* Delete ust context safely. RCU read lock must be held before calling
* this function.
*
* The session list lock must be held by the caller.
*/
-static
-void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan,
- struct ust_app *app)
+static void delete_ust_app_channel(int sock,
+ struct ust_app_channel *ua_chan,
+ struct ust_app *app,
+ const ust_registry_session::locked_ptr& locked_registry)
{
int ret;
struct lttng_ht_iter iter;
struct ust_app_event *ua_event;
struct ust_app_ctx *ua_ctx;
struct ust_app_stream *stream, *stmp;
- ust_registry_session *registry;
LTTNG_ASSERT(ua_chan);
ASSERT_RCU_READ_LOCKED();
if (ua_chan->session->buffer_type == LTTNG_BUFFER_PER_PID) {
/* Wipe and free registry from session registry. */
- registry = get_session_registry(ua_chan->session);
- if (registry) {
- ust_registry_channel_del_free(registry, ua_chan->key,
- sock >= 0);
+ if (locked_registry) {
+ try {
+ locked_registry->remove_channel(ua_chan->key, sock >= 0);
+ } catch (const std::exception &ex) {
+ DBG("Could not find channel for removal: %s", ex.what());
+ }
}
+
/*
* A negative socket can be used by the caller when
* cleaning-up a ua_chan in an error path. Skip the
* but it can be caused by recoverable errors (e.g. the application has
* terminated concurrently).
*/
-ssize_t ust_app_push_metadata(ust_registry_session *registry,
- struct consumer_socket *socket, int send_zero_data)
+ssize_t ust_app_push_metadata(const ust_registry_session::locked_ptr& locked_registry,
+ struct consumer_socket *socket,
+ int send_zero_data)
{
int ret;
char *metadata_str = NULL;
ssize_t ret_val;
uint64_t metadata_key, metadata_version;
- LTTNG_ASSERT(registry);
+ LTTNG_ASSERT(locked_registry);
LTTNG_ASSERT(socket);
ASSERT_RCU_READ_LOCKED();
- metadata_key = registry->_metadata_key;
+ metadata_key = locked_registry->_metadata_key;
/*
* Means that no metadata was assigned to the session. This can
return 0;
}
- offset = registry->_metadata_len_sent;
- len = registry->_metadata_len - registry->_metadata_len_sent;
- new_metadata_len_sent = registry->_metadata_len;
- metadata_version = registry->_metadata_version;
+ offset = locked_registry->_metadata_len_sent;
+ len = locked_registry->_metadata_len - locked_registry->_metadata_len_sent;
+ new_metadata_len_sent = locked_registry->_metadata_len;
+ metadata_version = locked_registry->_metadata_version;
if (len == 0) {
DBG3("No metadata to push for metadata key %" PRIu64,
- registry->_metadata_key);
+ locked_registry->_metadata_key);
ret_val = len;
if (send_zero_data) {
DBG("No metadata to push");
goto error;
}
/* Copy what we haven't sent out. */
- memcpy(metadata_str, registry->_metadata + offset, len);
+ memcpy(metadata_str, locked_registry->_metadata + offset, len);
push_data:
- pthread_mutex_unlock(®istry->_lock);
+ pthread_mutex_unlock(&locked_registry->_lock);
/*
* We need to unlock the registry while we push metadata to
* break a circular dependency between the consumerd metadata
*/
ret = consumer_push_metadata(socket, metadata_key,
metadata_str, len, offset, metadata_version);
- pthread_mutex_lock(®istry->_lock);
+ pthread_mutex_lock(&locked_registry->_lock);
if (ret < 0) {
/*
* There is an acceptable race here between the registry
* largest metadata_len_sent value of the concurrent
* send.
*/
- registry->_metadata_len_sent =
- std::max(registry->_metadata_len_sent,
+ locked_registry->_metadata_len_sent =
+ std::max(locked_registry->_metadata_len_sent,
new_metadata_len_sent);
}
free(metadata_str);
* the metadata cache has been destroyed on the
* consumer.
*/
- registry->_metadata_closed = true;
+ locked_registry->_metadata_closed = true;
}
error_push:
free(metadata_str);
* but it can be caused by recoverable errors (e.g. the application has
* terminated concurrently).
*/
-static int push_metadata(ust_registry_session *registry,
+static int push_metadata(const ust_registry_session::locked_ptr& locked_registry,
struct consumer_output *consumer)
{
int ret_val;
ssize_t ret;
struct consumer_socket *socket;
- LTTNG_ASSERT(registry);
+ LTTNG_ASSERT(locked_registry);
LTTNG_ASSERT(consumer);
ASSERT_RCU_READ_LOCKED();
- pthread_mutex_lock(®istry->_lock);
- if (registry->_metadata_closed) {
+ if (locked_registry->_metadata_closed) {
ret_val = -EPIPE;
goto error;
}
/* Get consumer socket to use to push the metadata.*/
- socket = consumer_find_socket_by_bitness(registry->_bits_per_long,
+ socket = consumer_find_socket_by_bitness(locked_registry->abi.bits_per_long,
consumer);
if (!socket) {
ret_val = -1;
goto error;
}
- ret = ust_app_push_metadata(registry, socket, 0);
+ ret = ust_app_push_metadata(locked_registry, socket, 0);
if (ret < 0) {
ret_val = ret;
goto error;
}
- pthread_mutex_unlock(®istry->_lock);
return 0;
error:
- pthread_mutex_unlock(®istry->_lock);
return ret_val;
}
*
* Return 0 on success else a negative value.
*/
-static int close_metadata(ust_registry_session *registry,
+static int close_metadata(uint64_t metadata_key, unsigned int consumer_bitness,
struct consumer_output *consumer)
{
int ret;
struct consumer_socket *socket;
- uint64_t metadata_key;
- bool registry_was_already_closed;
+ lttng::urcu::read_lock_guard read_lock_guard;
- LTTNG_ASSERT(registry);
LTTNG_ASSERT(consumer);
- rcu_read_lock();
-
- pthread_mutex_lock(®istry->_lock);
- metadata_key = registry->_metadata_key;
- registry_was_already_closed = registry->_metadata_closed;
- if (metadata_key != 0) {
- /*
- * Metadata closed. Even on error this means that the consumer
- * is not responding or not found so either way a second close
- * should NOT be emit for this registry.
- */
- registry->_metadata_closed = true;
- }
- pthread_mutex_unlock(®istry->_lock);
-
- if (metadata_key == 0 || registry_was_already_closed) {
- ret = 0;
- goto end;
- }
-
- /* Get consumer socket to use to push the metadata.*/
- socket = consumer_find_socket_by_bitness(registry->_bits_per_long,
+ /* Get consumer socket to use to push the metadata. */
+ socket = consumer_find_socket_by_bitness(consumer_bitness,
consumer);
if (!socket) {
ret = -1;
}
end:
- rcu_read_unlock();
return ret;
}
int ret;
struct lttng_ht_iter iter;
struct ust_app_channel *ua_chan;
- ust_registry_session *registry;
LTTNG_ASSERT(ua_sess);
ASSERT_RCU_READ_LOCKED();
LTTNG_ASSERT(!ua_sess->deleted);
ua_sess->deleted = true;
- registry = get_session_registry(ua_sess);
+ auto locked_registry = get_locked_session_registry(ua_sess);
/* Registry can be null on error path during initialization. */
- if (registry) {
+ if (locked_registry) {
/* Push metadata for application before freeing the application. */
- (void) push_metadata(registry, ua_sess->consumer);
+ (void) push_metadata(locked_registry, ua_sess->consumer);
+ }
+ cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan,
+ node.node) {
+ ret = lttng_ht_del(ua_sess->channels, &iter);
+ LTTNG_ASSERT(!ret);
+ delete_ust_app_channel(sock, ua_chan, app, locked_registry);
+ }
+
+ if (locked_registry) {
/*
* Don't ask to close metadata for global per UID buffers. Close
* metadata only on destroy trace session in this case. Also, the
* close so don't send a close command if closed.
*/
if (ua_sess->buffer_type != LTTNG_BUFFER_PER_UID) {
- /* And ask to close it for this session registry. */
- (void) close_metadata(registry, ua_sess->consumer);
- }
- }
+ const auto metadata_key = locked_registry->_metadata_key;
+ const auto consumer_bitness = locked_registry->abi.bits_per_long;
- cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan,
- node.node) {
- ret = lttng_ht_del(ua_sess->channels, &iter);
- LTTNG_ASSERT(!ret);
- delete_ust_app_channel(sock, ua_chan, app);
+ if (!locked_registry->_metadata_closed && metadata_key != 0) {
+ locked_registry->_metadata_closed = true;
+ }
+
+ /* Release lock before communication, see comments in close_metadata(). */
+ locked_registry.reset();
+ (void) close_metadata(metadata_key, consumer_bitness, ua_sess->consumer);
+ }
}
/* In case of per PID, the registry is kept in the session. */
LTTNG_OPTIONAL_SET(&ua_sess->effective_credentials.uid, usess->uid);
LTTNG_OPTIONAL_SET(&ua_sess->effective_credentials.gid, usess->gid);
ua_sess->buffer_type = usess->buffer_type;
- ua_sess->bits_per_long = app->bits_per_long;
+ ua_sess->bits_per_long = app->abi.bits_per_long;
/* There is only one consumer object per session possible. */
consumer_output_get(usess->consumer);
ret = snprintf(ua_sess->path, sizeof(ua_sess->path),
DEFAULT_UST_TRACE_UID_PATH,
lttng_credentials_get_uid(&ua_sess->real_credentials),
- app->bits_per_long);
+ app->abi.bits_per_long);
break;
default:
abort();
case LTTNG_BUFFER_PER_UID:
ret = snprintf(tmp_shm_path, sizeof(tmp_shm_path),
"/" DEFAULT_UST_TRACE_UID_PATH,
- app->uid, app->bits_per_long);
+ app->uid, app->abi.bits_per_long);
break;
default:
abort();
}
/* Initialize registry. */
- reg_pid->registry->reg.ust = ust_registry_session_per_pid_create(app,
- app->bits_per_long, app->uint8_t_alignment,
- app->uint16_t_alignment, app->uint32_t_alignment,
- app->uint64_t_alignment, app->long_alignment,
- app->byte_order, app->version.major, app->version.minor,
- reg_pid->root_shm_path, reg_pid->shm_path,
+ reg_pid->registry->reg.ust = ust_registry_session_per_pid_create(app, app->abi,
+ app->version.major, app->version.minor, reg_pid->root_shm_path,
+ reg_pid->shm_path,
lttng_credentials_get_uid(&ua_sess->effective_credentials),
lttng_credentials_get_gid(&ua_sess->effective_credentials),
ua_sess->tracing_id);
rcu_read_lock();
- reg_uid = buffer_reg_uid_find(usess->id, app->bits_per_long, app->uid);
+ reg_uid = buffer_reg_uid_find(usess->id, app->abi.bits_per_long, app->uid);
if (!reg_uid) {
/*
* This is the create channel path meaning that if there is NO
* registry available, we have to create one for this session.
*/
- ret = buffer_reg_uid_create(usess->id, app->bits_per_long, app->uid,
- LTTNG_DOMAIN_UST, ®_uid,
- ua_sess->root_shm_path, ua_sess->shm_path);
+ ret = buffer_reg_uid_create(usess->id, app->abi.bits_per_long, app->uid,
+ LTTNG_DOMAIN_UST, ®_uid, ua_sess->root_shm_path,
+ ua_sess->shm_path);
if (ret < 0) {
goto error;
}
}
/* Initialize registry. */
- reg_uid->registry->reg.ust = ust_registry_session_per_uid_create(
- app->bits_per_long, app->uint8_t_alignment,
- app->uint16_t_alignment, app->uint32_t_alignment,
- app->uint64_t_alignment, app->long_alignment,
- app->byte_order, app->version.major,
- app->version.minor, reg_uid->root_shm_path,
- reg_uid->shm_path, usess->uid, usess->gid,
- ua_sess->tracing_id, app->uid);
+ reg_uid->registry->reg.ust = ust_registry_session_per_uid_create(app->abi,
+ app->version.major, app->version.minor, reg_uid->root_shm_path,
+ reg_uid->shm_path, usess->uid, usess->gid, ua_sess->tracing_id, app->uid);
if (!reg_uid->registry->reg.ust) {
/*
* reg_uid->registry->reg.ust is NULL upon error, so we need to
buf_reg_chan->num_subbuf = ua_chan->attr.num_subbuf;
/* Create and add a channel registry to session. */
- ret = ust_registry_channel_add(reg_sess->reg.ust,
- ua_chan->tracing_channel_id);
- if (ret < 0) {
+ try {
+ reg_sess->reg.ust->add_channel(ua_chan->tracing_channel_id);
+ } catch (const std::exception& ex) {
+ ERR("Failed to add a channel registry to userspace registry session: %s", ex.what());
+ ret = -1;
goto error;
}
+
buffer_reg_channel_add(reg_sess, buf_reg_chan);
if (regp) {
struct buffer_reg_channel *buf_reg_chan;
struct ltt_session *session = NULL;
enum lttng_error_code notification_ret;
- struct ust_registry_channel *ust_reg_chan;
LTTNG_ASSERT(app);
LTTNG_ASSERT(usess);
DBG("UST app creating channel %s with per UID buffers", ua_chan->name);
- reg_uid = buffer_reg_uid_find(usess->id, app->bits_per_long, app->uid);
+ reg_uid = buffer_reg_uid_find(usess->id, app->abi.bits_per_long, app->uid);
/*
* The session creation handles the creation of this global registry
* object. If none can be find, there is a code flow problem or a
* ust app channel object with all streams and data object.
*/
ret = do_consumer_create_channel(usess, ua_sess, ua_chan,
- app->bits_per_long, reg_uid->registry->reg.ust);
+ app->abi.bits_per_long, reg_uid->registry->reg.ust);
if (ret < 0) {
ERR("Error creating UST channel \"%s\" on the consumer daemon",
ua_chan->name);
* Let's remove the previously created buffer registry channel so
* it's not visible anymore in the session registry.
*/
- ust_registry_channel_del_free(reg_uid->registry->reg.ust,
- ua_chan->tracing_channel_id, false);
+ auto locked_registry = reg_uid->registry->reg.ust->lock();
+ try {
+ locked_registry->remove_channel(ua_chan->tracing_channel_id, false);
+ } catch (const std::exception &ex) {
+ DBG("Could not find channel for removal: %s", ex.what());
+ }
buffer_reg_channel_remove(reg_uid->registry, buf_reg_chan);
buffer_reg_channel_destroy(buf_reg_chan, LTTNG_DOMAIN_UST);
goto error;
goto error;
}
- /* Notify the notification subsystem of the channel's creation. */
- pthread_mutex_lock(®_uid->registry->reg.ust->_lock);
- ust_reg_chan = ust_registry_channel_find(reg_uid->registry->reg.ust,
- ua_chan->tracing_channel_id);
- LTTNG_ASSERT(ust_reg_chan);
- ust_reg_chan->consumer_key = ua_chan->key;
- ust_reg_chan = NULL;
- pthread_mutex_unlock(®_uid->registry->reg.ust->_lock);
+ {
+ auto locked_registry = reg_uid->registry->reg.ust->lock();
+ auto& ust_reg_chan = locked_registry->get_channel(ua_chan->tracing_channel_id);
+ ust_reg_chan._consumer_key = ua_chan->key;
+ }
+
+ /* Notify the notification subsystem of the channel's creation. */
notification_ret = notification_thread_command_add_channel(
the_notification_thread_handle, session->name,
lttng_credentials_get_uid(
enum lttng_error_code cmd_ret;
struct ltt_session *session = NULL;
uint64_t chan_reg_key;
- struct ust_registry_channel *ust_reg_chan;
LTTNG_ASSERT(app);
LTTNG_ASSERT(usess);
LTTNG_ASSERT(registry);
/* Create and add a new channel registry to session. */
- ret = ust_registry_channel_add(registry, ua_chan->key);
- if (ret < 0) {
- ERR("Error creating the UST channel \"%s\" registry instance",
- ua_chan->name);
+ try {
+ registry->add_channel(ua_chan->key);
+ } catch (const std::exception& ex) {
+ ERR("Error creating the UST channel \"%s\" registry instance: %s", ua_chan->name,
+ ex.what());
+ ret = -1;
goto error;
}
/* Create and get channel on the consumer side. */
ret = do_consumer_create_channel(usess, ua_sess, ua_chan,
- app->bits_per_long, registry);
+ app->abi.bits_per_long, registry);
if (ret < 0) {
ERR("Error creating UST channel \"%s\" on the consumer daemon",
ua_chan->name);
}
chan_reg_key = ua_chan->key;
- pthread_mutex_lock(®istry->_lock);
- ust_reg_chan = ust_registry_channel_find(registry, chan_reg_key);
- LTTNG_ASSERT(ust_reg_chan);
- ust_reg_chan->consumer_key = ua_chan->key;
- pthread_mutex_unlock(®istry->_lock);
+ {
+ auto locked_registry = registry->lock();
+
+ auto& ust_reg_chan = locked_registry->get_channel(chan_reg_key);
+ ust_reg_chan._consumer_key = ua_chan->key;
+ }
cmd_ret = notification_thread_command_add_channel(
the_notification_thread_handle, session->name,
error_remove_from_registry:
if (ret) {
- ust_registry_channel_del_free(registry, ua_chan->key, false);
+ try {
+ auto locked_registry = registry->lock();
+ locked_registry->remove_channel(ua_chan->key, false);
+ } catch (const std::exception& ex) {
+ DBG("Could not find channel for removal: %s", ex.what());
+ }
}
error:
rcu_read_unlock();
int ret = 0;
struct ust_app_channel *metadata;
struct consumer_socket *socket;
- ust_registry_session *registry;
struct ltt_session *session = NULL;
LTTNG_ASSERT(ua_sess);
LTTNG_ASSERT(consumer);
ASSERT_RCU_READ_LOCKED();
- registry = get_session_registry(ua_sess);
+ auto locked_registry = get_locked_session_registry(ua_sess);
/* The UST app session is held registry shall not be null. */
- LTTNG_ASSERT(registry);
-
- pthread_mutex_lock(®istry->_lock);
+ LTTNG_ASSERT(locked_registry);
/* Metadata already exists for this registry or it was closed previously */
- if (registry->_metadata_key || registry->_metadata_closed) {
+ if (locked_registry->_metadata_key || locked_registry->_metadata_closed) {
ret = 0;
goto error;
}
}
/* Get the right consumer socket for the application. */
- socket = consumer_find_socket_by_bitness(app->bits_per_long, consumer);
+ socket = consumer_find_socket_by_bitness(app->abi.bits_per_long, consumer);
if (!socket) {
ret = -EINVAL;
goto error_consumer;
* consumer requesting the metadata and the ask_channel call on our side
* did not returned yet.
*/
- registry->_metadata_key = metadata->key;
+ locked_registry->_metadata_key = metadata->key;
session = session_find_by_id(ua_sess->tracing_id);
LTTNG_ASSERT(session);
* consumer.
*/
ret = ust_consumer_ask_channel(ua_sess, metadata, consumer, socket,
- registry, session->current_trace_chunk);
+ locked_registry.get(), session->current_trace_chunk);
if (ret < 0) {
/* Nullify the metadata key so we don't try to close it later on. */
- registry->_metadata_key = 0;
+ locked_registry->_metadata_key = 0;
goto error_consumer;
}
ret = consumer_setup_metadata(socket, metadata->key);
if (ret < 0) {
/* Nullify the metadata key so we don't try to close it later on. */
- registry->_metadata_key = 0;
+ locked_registry->_metadata_key = 0;
goto error_consumer;
}
error_consumer:
lttng_fd_put(LTTNG_FD_APPS, 1);
- delete_ust_app_channel(-1, metadata, app);
+ delete_ust_app_channel(-1, metadata, app, locked_registry);
error:
- pthread_mutex_unlock(®istry->_lock);
if (session) {
session_put(session);
}
lta->uid = msg->uid;
lta->gid = msg->gid;
- lta->bits_per_long = msg->bits_per_long;
- lta->uint8_t_alignment = msg->uint8_t_alignment;
- lta->uint16_t_alignment = msg->uint16_t_alignment;
- lta->uint32_t_alignment = msg->uint32_t_alignment;
- lta->uint64_t_alignment = msg->uint64_t_alignment;
- lta->long_alignment = msg->long_alignment;
- lta->byte_order = msg->byte_order;
+ lta->abi = {
+ .bits_per_long = msg->bits_per_long,
+ .long_alignment = msg->long_alignment,
+ .uint8_t_alignment = msg->uint8_t_alignment,
+ .uint16_t_alignment = msg->uint16_t_alignment,
+ .uint32_t_alignment = msg->uint32_t_alignment,
+ .uint64_t_alignment = msg->uint64_t_alignment,
+ .byte_order = msg->byte_order == LITTLE_ENDIAN ?
+ lttng::sessiond::trace::byte_order::LITTLE_ENDIAN_ :
+ lttng::sessiond::trace::byte_order::BIG_ENDIAN_,
+ };
lta->v_major = msg->major;
lta->v_minor = msg->minor;
*/
cds_lfht_for_each_entry(lta->sessions->ht, &iter.iter, ua_sess,
node.node) {
- ust_registry_session *registry;
-
ret = lttng_ht_del(lta->sessions, &iter);
if (ret) {
/* The session was already removed so scheduled for teardown. */
* The close metadata below nullifies the metadata pointer in the
* session so the delete session will NOT push/close a second time.
*/
- registry = get_session_registry(ua_sess);
- if (registry) {
+ auto locked_registry = get_locked_session_registry(ua_sess);
+ if (locked_registry) {
/* Push metadata for application before freeing the application. */
- (void) push_metadata(registry, ua_sess->consumer);
+ (void) push_metadata(locked_registry, ua_sess->consumer);
/*
* Don't ask to close metadata for global per UID buffers. Close
* close so don't send a close command if closed.
*/
if (ua_sess->buffer_type != LTTNG_BUFFER_PER_UID) {
- /* And ask to close it for this session registry. */
- (void) close_metadata(registry, ua_sess->consumer);
+ const auto metadata_key = locked_registry->_metadata_key;
+ const auto consumer_bitness = locked_registry->abi.bits_per_long;
+
+ if (!locked_registry->_metadata_closed && metadata_key != 0) {
+ locked_registry->_metadata_closed = true;
+ }
+
+ /* Release lock before communication, see comments in close_metadata(). */
+ locked_registry.reset();
+ (void) close_metadata(metadata_key, consumer_bitness, ua_sess->consumer);
+ } else {
+ locked_registry.reset();
}
}
cds_list_add(&ua_sess->teardown_node, <a->teardown_head);
{
int ret = 0;
struct ust_app_session *ua_sess;
- ust_registry_session *registry;
DBG("Stopping tracing for ust app pid %d", app->pid);
health_code_update();
- registry = get_session_registry(ua_sess);
+ {
+ auto locked_registry = get_locked_session_registry(ua_sess);
- /* The UST app session is held registry shall not be null. */
- LTTNG_ASSERT(registry);
+ /* The UST app session is held registry shall not be null. */
+ LTTNG_ASSERT(locked_registry);
- /* Push metadata for application before freeing the application. */
- (void) push_metadata(registry, ua_sess->consumer);
+ /* Push metadata for application before freeing the application. */
+ (void) push_metadata(locked_registry, ua_sess->consumer);
+ }
end_unlock:
pthread_mutex_unlock(&ua_sess->lock);
health_code_update();
/* Flushing buffers */
- socket = consumer_find_socket_by_bitness(app->bits_per_long,
+ socket = consumer_find_socket_by_bitness(app->abi.bits_per_long,
ua_sess->consumer);
/* Flush buffers and push metadata. */
ust_session_reg = reg->registry->reg.ust;
/* Push metadata. */
- (void) push_metadata(ust_session_reg, usess->consumer);
+ auto locked_registry = ust_session_reg->lock();
+ (void) push_metadata(locked_registry, usess->consumer);
}
break;
}
health_code_update();
- socket = consumer_find_socket_by_bitness(app->bits_per_long,
+ socket = consumer_find_socket_by_bitness(app->abi.bits_per_long,
ua_sess->consumer);
if (!socket) {
ERR("Failed to find consumer (%" PRIu32 ") socket",
- app->bits_per_long);
+ app->abi.bits_per_long);
ret = -1;
goto end_unlock;
}
return ua_chan;
}
-/*
- * Fixup legacy context fields for comparison:
- * - legacy array becomes array_nestable,
- * - legacy struct becomes struct_nestable,
- * - legacy variant becomes variant_nestable,
- * legacy sequences are not emitted in LTTng-UST contexts.
- */
-static int ust_app_fixup_legacy_context_fields(size_t *_nr_fields,
- struct lttng_ust_ctl_field **_fields)
-{
- struct lttng_ust_ctl_field *fields = *_fields, *new_fields = NULL;
- size_t nr_fields = *_nr_fields, new_nr_fields = 0, i, j;
- bool found = false;
- int ret = 0;
-
- for (i = 0; i < nr_fields; i++) {
- const struct lttng_ust_ctl_field *field = &fields[i];
-
- switch (field->type.atype) {
- case lttng_ust_ctl_atype_sequence:
- ERR("Unexpected legacy sequence context.");
- ret = -EINVAL;
- goto end;
- case lttng_ust_ctl_atype_array:
- switch (field->type.u.legacy.array.elem_type.atype) {
- case lttng_ust_ctl_atype_integer:
- break;
- default:
- ERR("Unexpected legacy array element type in context.");
- ret = -EINVAL;
- goto end;
- }
- found = true;
- /* One field for array_nested, one field for elem type. */
- new_nr_fields += 2;
- break;
-
- case lttng_ust_ctl_atype_struct: /* Fallthrough */
- case lttng_ust_ctl_atype_variant:
- found = true;
- new_nr_fields++;
- break;
- default:
- new_nr_fields++;
- break;
- }
- }
- if (!found) {
- goto end;
- }
-
- new_fields = calloc<lttng_ust_ctl_field>(new_nr_fields);
- if (!new_fields) {
- ret = -ENOMEM;
- goto end;
- }
-
- for (i = 0, j = 0; i < nr_fields; i++, j++) {
- const struct lttng_ust_ctl_field *field = &fields[i];
- struct lttng_ust_ctl_field *new_field = &new_fields[j];
-
- switch (field->type.atype) {
- case lttng_ust_ctl_atype_array:
- /* One field for array_nested, one field for elem type. */
- strncpy(new_field->name, field->name, LTTNG_UST_ABI_SYM_NAME_LEN - 1);
- new_field->type.atype = lttng_ust_ctl_atype_array_nestable;
- new_field->type.u.array_nestable.length = field->type.u.legacy.array.length;
- new_field->type.u.array_nestable.alignment = 0;
- new_field = &new_fields[++j]; /* elem type */
- new_field->type.atype = field->type.u.legacy.array.elem_type.atype;
- assert(new_field->type.atype == lttng_ust_ctl_atype_integer);
- new_field->type.u.integer = field->type.u.legacy.array.elem_type.u.basic.integer;
- break;
- case lttng_ust_ctl_atype_struct:
- strncpy(new_field->name, field->name, LTTNG_UST_ABI_SYM_NAME_LEN - 1);
- new_field->type.atype = lttng_ust_ctl_atype_struct_nestable;
- new_field->type.u.struct_nestable.nr_fields = field->type.u.legacy._struct.nr_fields;
- new_field->type.u.struct_nestable.alignment = 0;
- break;
- case lttng_ust_ctl_atype_variant:
- strncpy(new_field->name, field->name, LTTNG_UST_ABI_SYM_NAME_LEN - 1);
- new_field->type.atype = lttng_ust_ctl_atype_variant_nestable;
- new_field->type.u.variant_nestable.nr_choices = field->type.u.legacy.variant.nr_choices;
- strncpy(new_field->type.u.variant_nestable.tag_name,
- field->type.u.legacy.variant.tag_name,
- LTTNG_UST_ABI_SYM_NAME_LEN - 1);
- new_field->type.u.variant_nestable.alignment = 0;
- break;
- default:
- *new_field = *field;
- break;
- }
- }
- free(fields);
- *_fields = new_fields;
- *_nr_fields = new_nr_fields;
-end:
- return ret;
-}
-
/*
* Reply to a register channel notification from an application on the notify
* socket. The channel metadata is also created.
*
* On success 0 is returned else a negative value.
*/
-static int reply_ust_register_channel(int sock, int cobjd,
- size_t nr_fields, struct lttng_ust_ctl_field *fields)
+static int handle_app_register_channel_notification(int sock,
+ int cobjd,
+ struct lttng_ust_ctl_field *raw_context_fields,
+ size_t context_field_count)
{
int ret, ret_code = 0;
uint32_t chan_id;
uint64_t chan_reg_key;
- enum lttng_ust_ctl_channel_header type = LTTNG_UST_CTL_CHANNEL_HEADER_UNKNOWN;
struct ust_app *app;
struct ust_app_channel *ua_chan;
struct ust_app_session *ua_sess;
- ust_registry_session *registry;
- struct ust_registry_channel *ust_reg_chan;
+ auto ust_ctl_context_fields = lttng::make_unique_wrapper<lttng_ust_ctl_field, lttng::free>(
+ raw_context_fields);
- rcu_read_lock();
+ lttng::urcu::read_lock_guard read_lock_guard;
/* Lookup application. If not found, there is a code flow error. */
app = find_app_by_notify_sock(sock);
if (!app) {
DBG("Application socket %d is being torn down. Abort event notify",
sock);
- ret = -1;
- goto error_rcu_unlock;
+ return -1;
}
/* Lookup channel by UST object descriptor. */
ua_chan = find_channel_by_objd(app, cobjd);
if (!ua_chan) {
DBG("Application channel is being torn down. Abort event notify");
- ret = 0;
- goto error_rcu_unlock;
+ return 0;
}
LTTNG_ASSERT(ua_chan->session);
ua_sess = ua_chan->session;
/* Get right session registry depending on the session buffer type. */
- registry = get_session_registry(ua_sess);
- if (!registry) {
+ auto locked_registry_session = get_locked_session_registry(ua_sess);
+ if (!locked_registry_session) {
DBG("Application session is being torn down. Abort event notify");
- ret = 0;
- goto error_rcu_unlock;
+ return 0;
};
/* Depending on the buffer type, a different channel key is used. */
chan_reg_key = ua_chan->key;
}
- pthread_mutex_lock(®istry->_lock);
-
- ust_reg_chan = ust_registry_channel_find(registry, chan_reg_key);
- LTTNG_ASSERT(ust_reg_chan);
+ auto& ust_reg_chan = locked_registry_session->get_channel(chan_reg_key);
/* Channel id is set during the object creation. */
- chan_id = ust_reg_chan->chan_id;
+ chan_id = ust_reg_chan.id;
- ret = ust_app_fixup_legacy_context_fields(&nr_fields, &fields);
- if (ret < 0) {
- ERR("Registering application channel due to legacy context fields fixup error: pid = %d, sock = %d",
- app->pid, app->sock);
- ret_code = -EINVAL;
- goto reply;
- }
- if (!ust_reg_chan->register_done) {
- /*
- * TODO: eventually use the registry event count for
- * this channel to better guess header type for per-pid
- * buffers.
- */
- type = LTTNG_UST_CTL_CHANNEL_HEADER_LARGE;
- ust_reg_chan->nr_ctx_fields = nr_fields;
- ust_reg_chan->ctx_fields = fields;
- fields = NULL;
- ust_reg_chan->header_type = type;
+ /*
+ * The application returns the typing information of the channel's
+ * context fields. In per-PID buffering mode, this is the first and only
+ * time we get this information. It is our chance to finalize the
+ * initialiation of the channel and serialize it's layout's description
+ * to the trace's metadata.
+ *
+ * However, in per-UID buffering mode, every application will provide
+ * this information (redundantly). The first time will allow us to
+ * complete the initialization. The following times, we simply validate
+ * that all apps provide the same typing for the context fields as a
+ * sanity check.
+ */
+ lst::type::cuptr context_fields = lttng::make_unique<lst::structure_type>(0,
+ lsu::create_trace_fields_from_ust_ctl_fields(*locked_registry_session,
+ ust_ctl_context_fields.get(), context_field_count));
+
+ if (!ust_reg_chan.is_registered()) {
+ ust_reg_chan.set_context(std::move(context_fields));
} else {
- /* Get current already assigned values. */
- type = ust_reg_chan->header_type;
/*
* Validate that the context fields match between
* registry and newcoming application.
*/
- if (!match_lttng_ust_ctl_field_array(ust_reg_chan->ctx_fields,
- ust_reg_chan->nr_ctx_fields,
- fields, nr_fields)) {
+ if (ust_reg_chan.get_context() != *context_fields) {
ERR("Registering application channel due to context field mismatch: pid = %d, sock = %d",
app->pid, app->sock);
ret_code = -EINVAL;
}
/* Append to metadata */
- if (!ust_reg_chan->metadata_dumped) {
- ret_code = ust_metadata_channel_statedump(registry, ust_reg_chan);
+ if (!ust_reg_chan._metadata_dumped) {
+ /*ret_code = ust_metadata_channel_statedump(registry, ust_reg_chan);*/
if (ret_code) {
ERR("Error appending channel metadata (errno = %d)", ret_code);
goto reply;
reply:
DBG3("UST app replying to register channel key %" PRIu64
- " with id %u, type = %d, ret = %d", chan_reg_key, chan_id, type,
+ " with id %u, ret = %d", chan_reg_key, chan_id,
ret_code);
- ret = lttng_ust_ctl_reply_register_channel(sock, chan_id, type, ret_code);
+ ret = lttng_ust_ctl_reply_register_channel(sock, chan_id,
+ ust_reg_chan.header_type == lst::stream_class::header_type::COMPACT ?
+ LTTNG_UST_CTL_CHANNEL_HEADER_COMPACT :
+ LTTNG_UST_CTL_CHANNEL_HEADER_LARGE,
+ ret_code);
if (ret < 0) {
if (ret == -EPIPE || ret == -LTTNG_UST_ERR_EXITING) {
DBG3("UST app reply channel failed. Application died: pid = %d, sock = %d",
ERR("UST app reply channel failed with ret %d: pid = %d, sock = %d",
ret, app->pid, app->sock);
}
- goto error;
+
+ return ret;
}
- /* This channel registry registration is completed. */
- ust_reg_chan->register_done = 1;
+ /* This channel registry's registration is completed. */
+ ust_reg_chan.set_as_registered();
-error:
- pthread_mutex_unlock(®istry->_lock);
-error_rcu_unlock:
- rcu_read_unlock();
- free(fields);
return ret;
}
*
* On success 0 is returned else a negative value.
*/
-static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name,
- char *sig, size_t nr_fields, struct lttng_ust_ctl_field *fields,
- int loglevel_value, char *model_emf_uri)
+static int add_event_ust_registry(int sock, int sobjd, int cobjd, const char *name,
+ char *raw_signature, size_t nr_fields, struct lttng_ust_ctl_field *raw_fields,
+ int loglevel_value, char *raw_model_emf_uri)
{
int ret, ret_code;
uint32_t event_id = 0;
struct ust_app *app;
struct ust_app_channel *ua_chan;
struct ust_app_session *ua_sess;
- ust_registry_session *registry;
-
- rcu_read_lock();
+ lttng::urcu::read_lock_guard rcu_lock;
+ auto signature = lttng::make_unique_wrapper<char, lttng::free>(raw_signature);
+ auto fields = lttng::make_unique_wrapper<lttng_ust_ctl_field, lttng::free>(raw_fields);
+ auto model_emf_uri = lttng::make_unique_wrapper<char, lttng::free>(raw_model_emf_uri);
/* Lookup application. If not found, there is a code flow error. */
app = find_app_by_notify_sock(sock);
if (!app) {
DBG("Application socket %d is being torn down. Abort event notify",
sock);
- ret = -1;
- goto error_rcu_unlock;
+ return -1;
}
/* Lookup channel by UST object descriptor. */
ua_chan = find_channel_by_objd(app, cobjd);
if (!ua_chan) {
DBG("Application channel is being torn down. Abort event notify");
- ret = 0;
- goto error_rcu_unlock;
+ return 0;
}
LTTNG_ASSERT(ua_chan->session);
ua_sess = ua_chan->session;
- registry = get_session_registry(ua_sess);
- if (!registry) {
- DBG("Application session is being torn down. Abort event notify");
- ret = 0;
- goto error_rcu_unlock;
- }
-
if (ua_sess->buffer_type == LTTNG_BUFFER_PER_UID) {
chan_reg_key = ua_chan->tracing_channel_id;
} else {
chan_reg_key = ua_chan->key;
}
- pthread_mutex_lock(®istry->_lock);
-
- /*
- * From this point on, this call acquires the ownership of the sig, fields
- * and model_emf_uri meaning any free are done inside it if needed. These
- * three variables MUST NOT be read/write after this.
- */
- ret_code = ust_registry_create_event(registry, chan_reg_key,
- sobjd, cobjd, name, sig, nr_fields, fields,
- loglevel_value, model_emf_uri, ua_sess->buffer_type,
- &event_id, app);
- sig = NULL;
- fields = NULL;
- model_emf_uri = NULL;
+ {
+ auto locked_registry = get_locked_session_registry(ua_sess);
+ if (locked_registry) {
+ /*
+ * From this point on, this call acquires the ownership of the signature,
+ * fields and model_emf_uri meaning any free are done inside it if needed.
+ * These three variables MUST NOT be read/write after this.
+ */
+ try {
+ auto& channel = locked_registry->get_channel(chan_reg_key);
+
+ /* event_id is set on success. */
+ channel.add_event(sobjd, cobjd, name, signature.get(),
+ lsu::create_trace_fields_from_ust_ctl_fields(
+ *locked_registry, fields.get(),
+ nr_fields),
+ loglevel_value,
+ model_emf_uri.get() ?
+ nonstd::optional<std::string>(
+ model_emf_uri.get()) :
+ nonstd::nullopt,
+ ua_sess->buffer_type, *app, event_id);
+ ret_code = 0;
+ } catch (const std::exception& ex) {
+ ERR("Failed to add event `%s` to registry session: %s", name,
+ ex.what());
+ /* Inform the application of the error; don't return directly. */
+ ret_code = -EINVAL;
+ }
+ } else {
+ DBG("Application session is being torn down. Abort event notify");
+ return 0;
+ }
+ }
/*
* The return value is returned to ustctl so in case of an error, the
* No need to wipe the create event since the application socket will
* get close on error hence cleaning up everything by itself.
*/
- goto error;
+ return ret;
}
DBG3("UST registry event %s with id %" PRId32 " added successfully",
name, event_id);
-
-error:
- pthread_mutex_unlock(®istry->_lock);
-error_rcu_unlock:
- rcu_read_unlock();
- free(sig);
- free(fields);
- free(model_emf_uri);
return ret;
}
DBG2("UST app ustctl register event received");
- ret = lttng_ust_ctl_recv_register_event(sock, &sobjd, &cobjd, name,
- &loglevel_value, &sig, &nr_fields, &fields,
- &model_emf_uri);
+ ret = lttng_ust_ctl_recv_register_event(sock, &sobjd, &cobjd, name, &loglevel_value,
+ &sig, &nr_fields, &fields, &model_emf_uri);
if (ret < 0) {
if (ret == -EPIPE || ret == -LTTNG_UST_ERR_EXITING) {
DBG3("UST app recv event failed. Application died: sock = %d",
goto error;
}
+ {
+ lttng::urcu::read_lock_guard rcu_lock;
+ const struct ust_app *app = find_app_by_notify_sock(sock);
+ if (!app) {
+ DBG("Application socket %d is being torn down. Abort event notify", sock);
+ ret = -1;
+ goto error;
+ }
+ }
+
+ if ((!fields && nr_fields > 0) || (fields && nr_fields == 0)) {
+ ERR("Invalid return value from lttng_ust_ctl_recv_register_event: fields = %p, nr_fields = %zu",
+ fields, nr_fields);
+ ret = -1;
+ free(fields);
+ goto error;
+ }
+
/*
* Add event to the UST registry coming from the notify socket. This
* call will free if needed the sig, fields and model_emf_uri. This
case LTTNG_UST_CTL_NOTIFY_CMD_CHANNEL:
{
int sobjd, cobjd;
- size_t nr_fields;
- struct lttng_ust_ctl_field *fields;
+ size_t field_count;
+ struct lttng_ust_ctl_field *context_fields;
DBG2("UST app ustctl register channel received");
- ret = lttng_ust_ctl_recv_register_channel(sock, &sobjd, &cobjd, &nr_fields,
- &fields);
+ ret = lttng_ust_ctl_recv_register_channel(
+ sock, &sobjd, &cobjd, &field_count, &context_fields);
if (ret < 0) {
if (ret == -EPIPE || ret == -LTTNG_UST_ERR_EXITING) {
DBG3("UST app recv channel failed. Application died: sock = %d",
WARN("UST app recv channel failed. Communication time out: sock = %d",
sock);
} else {
- ERR("UST app recv channel failed with ret %d: sock = %d",
- ret, sock);
+ ERR("UST app recv channel failed with ret %d: sock = %d", ret,
+ sock);
}
goto error;
}
/*
* The fields ownership are transfered to this function call meaning
* that if needed it will be freed. After this, it's invalid to access
- * fields or clean it up.
+ * fields or clean them up.
*/
- ret = reply_ust_register_channel(sock, cobjd, nr_fields,
- fields);
+ ret = handle_app_register_channel_notification(sock, cobjd, context_fields, field_count);
if (ret < 0) {
goto error;
}
goto error;
}
- /* Callee assumes ownership of entries */
+ /* Callee assumes ownership of entries. */
ret = add_enum_ust_registry(sock, sobjd, name,
entries, nr_entries);
if (ret < 0) {
}
/* Get the right consumer socket for the application. */
- socket = consumer_find_socket_by_bitness(app->bits_per_long,
+ socket = consumer_find_socket_by_bitness(app->abi.bits_per_long,
output);
if (!socket) {
status = LTTNG_ERR_INVALID;
continue;
}
- (void) push_metadata(reg->registry->reg.ust, usess->consumer);
+ {
+ auto locked_registry = reg->registry->reg.ust->lock();
+ (void) push_metadata(locked_registry, usess->consumer);
+ }
ret = consumer_rotate_channel(socket,
reg->registry->reg.ust->_metadata_key,
}
/* Get the right consumer socket for the application. */
- socket = consumer_find_socket_by_bitness(app->bits_per_long,
+ socket = consumer_find_socket_by_bitness(app->abi.bits_per_long,
usess->consumer);
if (!socket) {
cmd_ret = LTTNG_ERR_INVALID;
}
/* Rotate the metadata channel. */
- (void) push_metadata(registry, usess->consumer);
+ {
+ auto locked_registry = registry->lock();
+
+ (void) push_metadata(locked_registry, usess->consumer);
+ }
ret = consumer_rotate_channel(socket,
registry->_metadata_key,
ua_sess->consumer,
}
}
- (void) push_metadata(reg->registry->reg.ust, usess->consumer);
+ {
+ auto locked_registry = reg->registry->reg.ust->lock();
+ (void) push_metadata(locked_registry, usess->consumer);
+ }
/*
* Clear the metadata channel.
}
/* Get the right consumer socket for the application. */
- socket = consumer_find_socket_by_bitness(app->bits_per_long,
+ socket = consumer_find_socket_by_bitness(app->abi.bits_per_long,
usess->consumer);
if (!socket) {
cmd_ret = LTTNG_ERR_INVALID;
}
}
- (void) push_metadata(registry, usess->consumer);
+ {
+ auto locked_registry = registry->lock();
+ (void) push_metadata(locked_registry, usess->consumer);
+ }
/*
* Clear the metadata channel.
/* Get the right consumer socket for the application. */
socket = consumer_find_socket_by_bitness(
- app->bits_per_long, usess->consumer);
+ app->abi.bits_per_long, usess->consumer);
if (!socket) {
ret = LTTNG_ERR_FATAL;
goto error;
#include <stdint.h>
#include <common/index-allocator.hpp>
+#include <common/format.hpp>
#include <common/uuid.hpp>
#include "trace-ust.hpp"
uid_t uid; /* User ID that owns the apps */
gid_t gid; /* Group ID that owns the apps */
- /* App ABI */
- uint32_t bits_per_long;
- uint32_t uint8_t_alignment;
- uint32_t uint16_t_alignment;
- uint32_t uint32_t_alignment;
- uint32_t uint64_t_alignment;
- uint32_t long_alignment;
- int byte_order; /* BIG_ENDIAN or LITTLE_ENDIAN */
+ /* App ABI. */
+ lttng::sessiond::trace::abi abi;
int compatible; /* If the lttng-ust tracer version does not match the
supported version of the session daemon, this flag is
struct lttng_ht *token_to_event_notifier_rule_ht;
};
+template <>
+struct fmt::formatter<ust_app> : fmt::formatter<std::string> {
+ template <typename FormatCtx>
+ typename FormatCtx::iterator format(const ust_app& app, FormatCtx& ctx)
+ {
+ return fmt::format_to(ctx.out(),
+ "{{ procname = `{}`, ppid = {}, pid = {}, uid = {}, gid = {}, version = {}.{}, registration time = {} }}",
+ app.name, app.ppid, app.pid, app.uid, app.gid, app.v_major,
+ app.v_minor,
+ lttng::utils::time_to_iso8601_str(app.registration_time));
+ }
+};
+
+
#ifdef HAVE_LIBLTTNG_UST_CTL
int ust_app_register(struct ust_register_msg *msg, int sock);
void ust_app_add(struct ust_app *app);
struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock);
void ust_app_notify_sock_unregister(int sock);
-ssize_t ust_app_push_metadata(ust_registry_session *registry,
- struct consumer_socket *socket, int send_zero_data);
+ssize_t ust_app_push_metadata(const ust_registry_session::locked_ptr& registry,
+ struct consumer_socket *socket,
+ int send_zero_data);
void ust_app_destroy(struct ust_app *app);
enum lttng_error_code ust_app_snapshot_record(
const struct ltt_ust_session *usess,
--- /dev/null
+/*
+ * Copyright (C) 2010 Pierre-Marc Fournier
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "ust-clock-class.hpp"
+
+#include <common/time.hpp>
+#include <common/exception.hpp>
+
+#include <lttng/ust-clock.h>
+
+#define CLOCK_OFFSET_SAMPLE_COUNT 10
+
+namespace lst = lttng::sessiond::trace;
+
+namespace {
+struct offset_sample {
+ /* correlation offset */
+ lst::clock_class::scycles_t offset;
+ /* lower is better */
+ lst::clock_class::cycles_t measure_delta;
+};
+
+lst::clock_class::cycles_t sample_clock_read64()
+{
+ lttng_ust_clock_read64_function read64_cb;
+
+ if (lttng_ust_trace_clock_get_read64_cb(&read64_cb)) {
+ LTTNG_THROW_ERROR("Failed to get clock sample callback");
+ }
+
+ return read64_cb();
+}
+
+lst::clock_class::cycles_t sample_clock_frequency()
+{
+ lttng_ust_clock_freq_function get_freq_cb;
+
+ if (lttng_ust_trace_clock_get_freq_cb(&get_freq_cb)) {
+ LTTNG_THROW_ERROR("Failed to get clock frequency callback");
+ }
+
+ return get_freq_cb();
+}
+
+nonstd::optional<lttng_uuid> sample_clock_uuid()
+{
+ lttng_ust_clock_uuid_function get_uuid_cb;
+
+ if (lttng_ust_trace_clock_get_uuid_cb(&get_uuid_cb)) {
+ return nonstd::nullopt;
+ }
+
+ char uuid_str[LTTNG_UUID_STR_LEN];
+ if (get_uuid_cb(uuid_str)) {
+ return nonstd::nullopt;
+ }
+
+ lttng_uuid uuid;
+ if (lttng_uuid_from_str(uuid_str, uuid)) {
+ LTTNG_THROW_ERROR("Failed to parse UUID from string");
+ }
+
+ return nonstd::optional<lttng_uuid>{uuid};
+}
+
+const char *sample_clock_name()
+{
+ lttng_ust_clock_name_function get_name_cb;
+
+ if (lttng_ust_trace_clock_get_name_cb(&get_name_cb)) {
+ LTTNG_THROW_ERROR("Failed to get clock name callback");
+ }
+
+ const auto name = get_name_cb();
+ if (!name) {
+ LTTNG_THROW_ERROR("Invalid clock name returned by LTTng-UST `lttng_ust_clock_name_function`");
+ }
+
+ return name;
+}
+
+const char *sample_clock_description()
+{
+ lttng_ust_clock_description_function get_description_cb;
+
+ if (lttng_ust_trace_clock_get_description_cb(&get_description_cb)) {
+ LTTNG_THROW_ERROR("Failed to get clock description callback");
+ }
+
+ const auto description = get_description_cb();
+ if (!description) {
+ LTTNG_THROW_ERROR("Invalid clock description returned by LTTng-UST `lttng_ust_clock_description_function`");
+ }
+
+ return description;
+}
+
+/*
+ * The offset between monotonic and realtime clock can be negative if
+ * the system sets the REALTIME clock to 0 after boot.
+ */
+void measure_single_clock_offset(struct offset_sample *sample)
+{
+ lst::clock_class::cycles_t monotonic_avg, monotonic[2], measure_delta,
+ realtime;
+ const auto tcf = sample_clock_frequency();
+ struct timespec rts = { 0, 0 };
+
+ monotonic[0] = sample_clock_read64();
+ if (lttng_clock_gettime(CLOCK_REALTIME, &rts)) {
+ LTTNG_THROW_POSIX("Failed to sample time from clock", errno);
+ }
+
+ monotonic[1] = sample_clock_read64();
+ measure_delta = monotonic[1] - monotonic[0];
+ if (measure_delta > sample->measure_delta) {
+ /*
+ * Discard value if it took longer to read than the best
+ * sample so far.
+ */
+ return;
+ }
+
+ monotonic_avg = (monotonic[0] + monotonic[1]) >> 1;
+ realtime = (lst::clock_class::cycles_t) rts.tv_sec * tcf;
+ if (tcf == NSEC_PER_SEC) {
+ realtime += rts.tv_nsec;
+ } else {
+ realtime += (lst::clock_class::cycles_t) rts.tv_nsec * tcf /
+ NSEC_PER_SEC;
+ }
+
+ sample->offset = (lst::clock_class::scycles_t) realtime - monotonic_avg;
+ sample->measure_delta = measure_delta;
+}
+
+/*
+ * Approximation of NTP time of day to clock monotonic correlation,
+ * taken at start of trace. Keep the measurement that took the less time
+ * to complete, thus removing imprecision caused by preemption.
+ * May return a negative offset.
+ */
+lst::clock_class::scycles_t measure_clock_offset(void)
+{
+ struct offset_sample offset_best_sample = {
+ .offset = 0,
+ .measure_delta = UINT64_MAX,
+ };
+
+ for (auto i = 0; i < CLOCK_OFFSET_SAMPLE_COUNT; i++) {
+ measure_single_clock_offset(&offset_best_sample);
+ }
+
+ return offset_best_sample.offset;
+}
+} /* namespace */
+
+lttng::sessiond::ust::clock_class::clock_class() :
+ lst::clock_class(sample_clock_name(),
+ sample_clock_description(),
+ sample_clock_uuid(),
+ measure_clock_offset(),
+ sample_clock_frequency())
+{
+}
--- /dev/null
+/*
+ * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef LTTNG_UST_CLOCK_CLASS_H
+#define LTTNG_UST_CLOCK_CLASS_H
+
+#include "clock-class.hpp"
+
+namespace lttng {
+namespace sessiond {
+namespace ust {
+
+class clock_class : public lttng::sessiond::trace::clock_class {
+public:
+ clock_class();
+};
+
+} /* namespace ust */
+} /* namespace sessiond */
+} /* namespace lttng */
+
+#endif /* LTTNG_UST_CLOCK_CLASS_H */
+++ /dev/null
-/*
- * Copyright (C) 2010 Pierre-Marc Fournier
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#include "ust-clock.hpp"
-
-#include <common/time.hpp>
-#include <common/exception.hpp>
-
-#define CLOCK_OFFSET_SAMPLE_COUNT 10
-
-namespace {
-struct offset_sample {
- /* correlation offset */
- lttng::ust::clock_attributes_sample::scycles_t offset;
- /* lower is better */
- lttng::ust::clock_attributes_sample::cycles_t measure_delta;
-};
-
-lttng::ust::clock_attributes_sample::cycles_t sample_clock_read64()
-{
- lttng_ust_clock_read64_function read64_cb;
-
- if (lttng_ust_trace_clock_get_read64_cb(&read64_cb)) {
- LTTNG_THROW_ERROR("Failed to get clock sample callback");
- }
-
- return read64_cb();
-}
-
-lttng::ust::clock_attributes_sample::cycles_t sample_clock_frequency()
-{
- lttng_ust_clock_freq_function get_freq_cb;
-
- if (lttng_ust_trace_clock_get_freq_cb(&get_freq_cb)) {
- LTTNG_THROW_ERROR("Failed to get clock frequency callback");
- }
-
- return get_freq_cb();
-}
-
-nonstd::optional<lttng_uuid> sample_clock_uuid()
-{
- lttng_ust_clock_uuid_function get_uuid_cb;
-
- if (lttng_ust_trace_clock_get_uuid_cb(&get_uuid_cb)) {
- return nonstd::nullopt;
- }
-
- char uuid_str[LTTNG_UUID_STR_LEN];
- if (get_uuid_cb(uuid_str)) {
- return nonstd::nullopt;
- }
-
- lttng_uuid uuid;
- if (lttng_uuid_from_str(uuid_str, uuid)) {
- LTTNG_THROW_ERROR("Failed to parse UUID from string");
- }
-
- return nonstd::optional<lttng_uuid>{uuid};
-}
-
-const char *sample_clock_name()
-{
- lttng_ust_clock_name_function get_name_cb;
-
- if (lttng_ust_trace_clock_get_name_cb(&get_name_cb)) {
- LTTNG_THROW_ERROR("Failed to get clock name callback");
- }
-
- const auto name = get_name_cb();
- if (!name) {
- LTTNG_THROW_ERROR("Invalid clock name returned by LTTng-UST `lttng_ust_clock_name_function`");
- }
-
- return name;
-}
-
-const char *sample_clock_description()
-{
- lttng_ust_clock_description_function get_description_cb;
-
- if (lttng_ust_trace_clock_get_description_cb(&get_description_cb)) {
- LTTNG_THROW_ERROR("Failed to get clock description callback");
- }
-
- const auto description = get_description_cb();
- if (!description) {
- LTTNG_THROW_ERROR("Invalid clock description returned by LTTng-UST `lttng_ust_clock_description_function`");
- }
-
- return description;
-}
-
-/*
- * The offset between monotonic and realtime clock can be negative if
- * the system sets the REALTIME clock to 0 after boot.
- */
-void measure_single_clock_offset(struct offset_sample *sample)
-{
- lttng::ust::clock_attributes_sample::cycles_t monotonic_avg, monotonic[2], measure_delta,
- realtime;
- const auto tcf = sample_clock_frequency();
- struct timespec rts = { 0, 0 };
-
- monotonic[0] = sample_clock_read64();
- if (lttng_clock_gettime(CLOCK_REALTIME, &rts)) {
- LTTNG_THROW_POSIX("Failed to sample time from clock", errno);
- }
-
- monotonic[1] = sample_clock_read64();
- measure_delta = monotonic[1] - monotonic[0];
- if (measure_delta > sample->measure_delta) {
- /*
- * Discard value if it took longer to read than the best
- * sample so far.
- */
- return;
- }
-
- monotonic_avg = (monotonic[0] + monotonic[1]) >> 1;
- realtime = (lttng::ust::clock_attributes_sample::cycles_t) rts.tv_sec * tcf;
- if (tcf == NSEC_PER_SEC) {
- realtime += rts.tv_nsec;
- } else {
- realtime += (lttng::ust::clock_attributes_sample::cycles_t) rts.tv_nsec * tcf /
- NSEC_PER_SEC;
- }
-
- sample->offset = (lttng::ust::clock_attributes_sample::scycles_t) realtime - monotonic_avg;
- sample->measure_delta = measure_delta;
-}
-
-/*
- * Approximation of NTP time of day to clock monotonic correlation,
- * taken at start of trace. Keep the measurement that took the less time
- * to complete, thus removing imprecision caused by preemption.
- * May return a negative offset.
- */
-lttng::ust::clock_attributes_sample::scycles_t measure_clock_offset(void)
-{
- struct offset_sample offset_best_sample = {
- .offset = 0,
- .measure_delta = UINT64_MAX,
- };
-
- for (auto i = 0; i < CLOCK_OFFSET_SAMPLE_COUNT; i++) {
- measure_single_clock_offset(&offset_best_sample);
- }
-
- return offset_best_sample.offset;
-}
-}
-
-lttng::ust::clock_attributes_sample::clock_attributes_sample() :
- _name{sample_clock_name()},
- _description{sample_clock_description()},
- _uuid{sample_clock_uuid()},
- _offset{measure_clock_offset()},
- _frequency{sample_clock_frequency()}
-{
-}
+++ /dev/null
-/*
- * Copyright (C) 2010 Pierre-Marc Fournier
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#ifndef _UST_CLOCK_H
-#define _UST_CLOCK_H
-
-#include <common/compat/time.hpp>
-#include <common/uuid.hpp>
-#include <vendor/optional.hpp>
-
-#include <lttng/ust-clock.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string>
-#include <sys/time.h>
-#include <urcu/arch.h>
-#include <urcu/system.h>
-
-namespace lttng {
-namespace ust {
-
-class clock_attributes_sample {
-public:
- using cycles_t = uint64_t;
- using scycles_t = int64_t;
-
- clock_attributes_sample();
-
- const std::string _name;
- const std::string _description;
- const nonstd::optional<lttng_uuid> _uuid;
- const scycles_t _offset;
- const cycles_t _frequency;
-};
-
-} /* namespace ust */
-} /* namespace lttng */
-
-#endif /* _UST_CLOCK_H */
#include "session.hpp"
#include "lttng-sessiond.hpp"
+namespace lsu = lttng::sessiond::ust;
+
/*
* Send a single channel to the consumer using command ASK_CHANNEL_CREATION.
*
uint64_t key, chan_reg_key;
char *pathname = NULL;
struct lttcomm_consumer_msg msg;
- struct ust_registry_channel *ust_reg_chan;
char shm_path[PATH_MAX] = "";
char root_shm_path[PATH_MAX] = "";
bool is_local_trace;
* those buffer files.
*/
} else {
- ust_reg_chan = ust_registry_channel_find(registry, chan_reg_key);
- LTTNG_ASSERT(ust_reg_chan);
- chan_id = ust_reg_chan->chan_id;
+ {
+ auto locked_registry = registry->lock();
+ auto& ust_reg_chan = registry->get_channel(chan_reg_key);
+
+ chan_id = ust_reg_chan.id;
+ }
+
if (ua_sess->shm_path[0]) {
strncpy(shm_path, ua_sess->shm_path, sizeof(shm_path));
shm_path[sizeof(shm_path) - 1] = '\0';
ua_chan->name,
consumer->net_seq_index,
ua_chan->key,
- registry->_uuid,
+ registry->uuid,
chan_id,
ua_chan->tracefile_size,
ua_chan->tracefile_count,
}
LTTNG_ASSERT(ust_reg);
- pthread_mutex_lock(&ust_reg->_lock);
- ret_push = ust_app_push_metadata(ust_reg, socket, 1);
- pthread_mutex_unlock(&ust_reg->_lock);
+ {
+ auto locked_ust_reg = ust_reg->lock();
+ ret_push = ust_app_push_metadata(locked_ust_reg, socket, 1);
+ }
if (ret_push == -EPIPE) {
DBG("Application or relay closed while pushing metadata");
} else if (ret_push < 0) {
--- /dev/null
+/*
+ * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "ust-field-convert.hpp"
+
+#include <common/make-unique.hpp>
+
+#include <unordered_map>
+
+namespace lst = lttng::sessiond::trace;
+namespace lsu = lttng::sessiond::ust;
+namespace {
+
+/*
+ * Type enclosing the session information that may be required during the decoding
+ * of the lttng_ust_ctl_field array provided by applications on registration of
+ * an event.
+ */
+class session_attributes {
+public:
+ using registry_enum_getter_fn =
+ std::function<lsu::registry_enum::const_rcu_protected_reference(
+ const char *name, uint64_t id)>;
+
+ session_attributes(registry_enum_getter_fn reg_enum_getter,
+ lst::byte_order native_trace_byte_order) :
+ get_registry_enum{reg_enum_getter}, _native_trace_byte_order{native_trace_byte_order}
+ {
+ }
+
+ const registry_enum_getter_fn get_registry_enum;
+ const lst::byte_order _native_trace_byte_order;
+};
+
+/* Used to publish fields on which a field being decoded has an implicit dependency. */
+using publish_field_fn = std::function<void(lst::field::cuptr)>;
+
+lst::type::cuptr create_type_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field,
+ publish_field_fn publish_field);
+
+void create_field_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field,
+ publish_field_fn publish_field);
+
+template <class UstCtlEncodingType>
+enum lst::null_terminated_string_type::encoding ust_ctl_encoding_to_string_field_encoding(UstCtlEncodingType encoding)
+{
+ static const std::unordered_map<UstCtlEncodingType, enum lst::null_terminated_string_type::encoding>
+ encoding_conversion_map = {
+ {(UstCtlEncodingType) lttng_ust_ctl_encode_ASCII,
+ lst::null_terminated_string_type::encoding::ASCII},
+ {(UstCtlEncodingType) lttng_ust_ctl_encode_UTF8,
+ lst::null_terminated_string_type::encoding::UTF8},
+ };
+
+ const auto encoding_it = encoding_conversion_map.find(encoding);
+ if (encoding_it == encoding_conversion_map.end()) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Unknown lttng_ust_ctl_string_encodings value `{}` encountered when decoding integer field",
+ encoding));
+ }
+
+ return encoding_it->second;
+}
+
+template <class UstCtlBaseType>
+enum lst::integer_type::base ust_ctl_base_to_integer_field_base(UstCtlBaseType base)
+{
+ static const std::unordered_map<UstCtlBaseType, enum lst::integer_type::base>
+ base_conversion_map = {{2, lst::integer_type::base::BINARY},
+ {8, lst::integer_type::base::OCTAL},
+ {10, lst::integer_type::base::DECIMAL},
+ {16, lst::integer_type::base::HEXADECIMAL}};
+
+ const auto base_it = base_conversion_map.find(base);
+ if (base_it == base_conversion_map.end()) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Unknown integer base value `{}` encountered when decoding integer field",
+ base));
+ }
+
+ return base_it->second;
+}
+
+lst::type::cuptr create_integer_type_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field)
+{
+ if (current >= end) {
+ LTTNG_THROW_PROTOCOL_ERROR(
+ fmt::format("End of {} array reached unexpectedly during decoding",
+ typeid(*current)));
+ }
+
+ const auto base = ust_ctl_base_to_integer_field_base(current->type.u.integer.base);
+ const auto signedness = current->type.u.integer.signedness ?
+ lst::integer_type::signedness::SIGNED :
+ lst::integer_type::signedness::UNSIGNED;
+ const auto byte_order = current->type.u.integer.reverse_byte_order ?
+ lst::type::reverse_byte_order(session_attributes._native_trace_byte_order) :
+ session_attributes._native_trace_byte_order;
+
+ *next_ust_ctl_field = current + 1;
+
+ return lttng::make_unique<const lst::integer_type>(current->type.u.integer.alignment,
+ byte_order, current->type.u.integer.size, signedness, base);
+}
+
+lst::type::cuptr create_floating_point_type_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field)
+{
+ if (current >= end) {
+ LTTNG_THROW_PROTOCOL_ERROR(
+ fmt::format("End of {} array reached unexpectedly during decoding",
+ typeid(*current)));
+ }
+
+ *next_ust_ctl_field = current + 1;
+
+ const auto byte_order = current->type.u._float.reverse_byte_order ?
+ lst::type::reverse_byte_order(session_attributes._native_trace_byte_order) :
+ session_attributes._native_trace_byte_order;
+
+ try {
+ return lttng::make_unique<const lst::floating_point_type>(
+ current->type.u._float.alignment, byte_order,
+ current->type.u._float.exp_dig, current->type.u._float.mant_dig);
+ } catch (lttng::invalid_argument_error& ex) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format("Invalid floating point attribute in {}: {}",
+ typeid(*current), ex.what()));
+ }
+}
+
+lst::type::cuptr create_enumeration_type_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field)
+{
+ if (current >= end) {
+ LTTNG_THROW_PROTOCOL_ERROR(
+ fmt::format("End of {} array reached unexpectedly during decoding",
+ typeid(*current)));
+ }
+
+ uint64_t enumeration_id;
+ const auto& enum_uctl_field = *current;
+ const char *enumeration_name;
+ const auto *enum_container_uctl_type =
+ ¤t->type.u.legacy.basic.enumeration.container_type;
+
+ if (enum_uctl_field.type.atype == lttng_ust_ctl_atype_enum_nestable) {
+ /* Nestable enumeration fields are followed by their container type. */
+ ++current;
+ if (current >= end) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Array of {} is too short to contain nestable enumeration's container",
+ typeid(*current)));
+ }
+
+ if (current->type.atype != lttng_ust_ctl_atype_integer) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Invalid type of nestable enum container: type id = {}",
+ current->type.atype));
+ }
+
+ enum_container_uctl_type = ¤t->type.u.integer;
+ enumeration_id = enum_uctl_field.type.u.enum_nestable.id;
+ enumeration_name = enum_uctl_field.type.u.enum_nestable.name;
+ } else {
+ enumeration_id = enum_uctl_field.type.u.legacy.basic.enumeration.id;
+ enumeration_name = enum_uctl_field.type.u.legacy.basic.enumeration.name;
+ }
+
+ *next_ust_ctl_field = current + 1;
+
+ const auto base = ust_ctl_base_to_integer_field_base(enum_container_uctl_type->base);
+ const auto byte_order = enum_container_uctl_type->reverse_byte_order ?
+ lst::integer_type::reverse_byte_order(
+ session_attributes._native_trace_byte_order) :
+ session_attributes._native_trace_byte_order;
+ const auto signedness = enum_container_uctl_type->signedness ?
+ lst::integer_type::signedness::SIGNED :
+ lst::integer_type::signedness::UNSIGNED;
+
+ if (enum_container_uctl_type->signedness) {
+ const auto& enum_registry = static_cast<const lsu::registry_signed_enum&>(
+ *session_attributes.get_registry_enum(
+ enumeration_name, enumeration_id));
+
+ return lttng::make_unique<const lst::signed_enumeration_type>(
+ enum_container_uctl_type->alignment, byte_order,
+ enum_container_uctl_type->size, signedness, base,
+ enum_registry._mappings);
+ } else {
+ const auto& enum_registry = static_cast<const lsu::registry_unsigned_enum&>(
+ *session_attributes.get_registry_enum(
+ enumeration_name, enumeration_id));
+
+ return lttng::make_unique<const lst::unsigned_enumeration_type>(
+ enum_container_uctl_type->alignment, byte_order,
+ enum_container_uctl_type->size, signedness, base,
+ enum_registry._mappings);
+ }
+}
+
+lst::type::cuptr create_string_type_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes __attribute__((unused)),
+ const lttng_ust_ctl_field **next_ust_ctl_field)
+{
+ if (current >= end) {
+ LTTNG_THROW_PROTOCOL_ERROR(
+ fmt::format("End of {} array reached unexpectedly during decoding",
+ typeid(*current)));
+ }
+
+ const auto& string_uctl_field = *current;
+ *next_ust_ctl_field = current + 1;
+
+ const auto encoding = ust_ctl_encoding_to_string_field_encoding(
+ string_uctl_field.type.u.string.encoding);
+
+ return lttng::make_unique<const lst::null_terminated_string_type>(1, encoding);
+}
+
+lst::type::cuptr create_integer_type_from_ust_ctl_basic_type(
+ const lttng_ust_ctl_basic_type& type, const session_attributes& session_attributes)
+{
+ /* Checked by caller. */
+ LTTNG_ASSERT(type.atype == lttng_ust_ctl_atype_integer);
+
+ const auto byte_order = type.u.basic.integer.reverse_byte_order ?
+ lst::integer_type::reverse_byte_order(
+ session_attributes._native_trace_byte_order) :
+ session_attributes._native_trace_byte_order;
+ const auto signedness = type.u.basic.integer.signedness ?
+ lst::integer_type::signedness::SIGNED :
+ lst::integer_type::signedness::UNSIGNED;
+ const auto base = ust_ctl_base_to_integer_field_base(type.u.basic.integer.base);
+ const auto size = type.u.basic.integer.size;
+ const auto alignment = type.u.basic.integer.alignment;
+
+ return lttng::make_unique<const lst::integer_type>(
+ alignment, byte_order, size, signedness, base);
+}
+
+lst::type::cuptr create_array_type_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field)
+{
+ if (current >= end) {
+ LTTNG_THROW_PROTOCOL_ERROR(
+ fmt::format("End of {} array reached unexpectedly during decoding",
+ typeid(*current)));
+ }
+
+ const auto& array_uctl_field = *current;
+ uint32_t array_alignment, array_length;
+ lst::type::cuptr element_type;
+ nonstd::optional<enum lst::string_type::encoding> element_encoding;
+
+ array_length = array_uctl_field.type.u.legacy.array.length;
+ array_alignment = 0;
+
+ const auto& element_uctl_type = array_uctl_field.type.u.legacy.array.elem_type;
+ if (element_uctl_type.atype != lttng_ust_ctl_atype_integer) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Unexpected legacy array element type: atype = {}, expected atype = lttng_ust_ctl_atype_integer ({})",
+ element_uctl_type.atype, lttng_ust_ctl_atype_integer));
+ }
+
+ element_type = create_integer_type_from_ust_ctl_basic_type(
+ element_uctl_type, session_attributes);
+ if (element_uctl_type.atype == lttng_ust_ctl_atype_integer &&
+ element_uctl_type.u.basic.integer.encoding != lttng_ust_ctl_encode_none) {
+ /* Element represents a text character. */
+ element_encoding = ust_ctl_encoding_to_string_field_encoding(
+ element_uctl_type.u.basic.integer.encoding);
+ }
+
+ *next_ust_ctl_field = current + 1;
+
+ if (element_encoding) {
+ const auto integer_element_size =
+ static_cast<const lst::integer_type&>(*element_type).size;
+
+ if (integer_element_size != 8) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Unexpected legacy array element type: integer has encoding but size is not 8: size = {}",
+ integer_element_size));
+ }
+
+ /* Array is a static-length string. */
+ return lttng::make_unique<lst::static_length_string_type>(
+ array_alignment, *element_encoding, array_length);
+ }
+
+ return lttng::make_unique<lst::static_length_array_type>(
+ array_alignment, std::move(element_type), array_length);
+}
+
+lst::type::cuptr create_array_nestable_type_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field,
+ publish_field_fn publish_field)
+{
+ if (current >= end) {
+ LTTNG_THROW_PROTOCOL_ERROR(
+ fmt::format("End of {} array reached unexpectedly during decoding",
+ typeid(*current)));
+ }
+
+ const auto& array_uctl_field = *current;
+ uint32_t array_alignment, array_length;
+ lst::type::cuptr element_type;
+ nonstd::optional<enum lst::string_type::encoding> element_encoding;
+
+ array_length = array_uctl_field.type.u.array_nestable.length;
+ array_alignment = array_uctl_field.type.u.array_nestable.alignment;
+
+ /* Nestable array fields are followed by their element type. */
+ const auto& element_uctl_field = *(current + 1);
+
+ /* next_ust_ctl_field is updated as needed. */
+ element_type = create_type_from_ust_ctl_fields(&element_uctl_field, end, session_attributes,
+ next_ust_ctl_field, publish_field);
+ if (element_uctl_field.type.atype == lttng_ust_ctl_atype_integer &&
+ element_uctl_field.type.u.integer.encoding != lttng_ust_ctl_encode_none) {
+ /* Element represents a text character. */
+ element_encoding = ust_ctl_encoding_to_string_field_encoding(
+ element_uctl_field.type.u.integer.encoding);
+ }
+
+ if (element_encoding) {
+ const auto integer_element_size =
+ static_cast<const lst::integer_type&>(*element_type).size;
+
+ if (integer_element_size != 8) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Unexpected array element type: integer has encoding but size is not 8: size = {}",
+ integer_element_size));
+ }
+
+ /* Array is a static-length string. */
+ return lttng::make_unique<lst::static_length_string_type>(
+ array_alignment, *element_encoding, array_length);
+ }
+
+ return lttng::make_unique<lst::static_length_array_type>(
+ array_alignment, std::move(element_type), array_length);
+}
+
+/*
+ * For legacy sequence types, LTTng-UST expresses both the sequence and sequence
+ * length as part of the same lttng_ust_ctl_field entry.
+ */
+lst::type::cuptr create_sequence_type_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field,
+ publish_field_fn publish_field)
+{
+ if (current >= end) {
+ LTTNG_THROW_PROTOCOL_ERROR(
+ fmt::format("End of {} array reached unexpectedly during decoding",
+ typeid(*current)));
+ }
+
+ const auto& sequence_uctl_field = *current;
+ const auto& element_uctl_type = sequence_uctl_field.type.u.legacy.sequence.elem_type;
+ const auto& length_uctl_type = sequence_uctl_field.type.u.legacy.sequence.length_type;
+ const auto sequence_alignment = 0U;
+
+ if (element_uctl_type.atype != lttng_ust_ctl_atype_integer) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Unexpected legacy sequence element type: atype = {}, expected atype = lttng_ust_ctl_atype_integer ({})",
+ element_uctl_type.atype, lttng_ust_ctl_atype_integer));
+ }
+
+ if (length_uctl_type.atype != lttng_ust_ctl_atype_integer) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Unexpected legacy sequence length field type: atype = {}, expected atype = lttng_ust_ctl_atype_integer ({})",
+ length_uctl_type.atype, lttng_ust_ctl_atype_integer));
+ }
+
+ nonstd::optional<enum lst::string_type::encoding> element_encoding;
+ if (element_uctl_type.atype == lttng_ust_ctl_atype_integer &&
+ element_uctl_type.u.basic.integer.encoding != lttng_ust_ctl_encode_none) {
+ /* Element represents a text character. */
+ element_encoding = ust_ctl_encoding_to_string_field_encoding(
+ element_uctl_type.u.basic.integer.encoding);
+ }
+
+ const auto length_field_name = fmt::format("_{}_length", sequence_uctl_field.name);
+ auto element_type = create_integer_type_from_ust_ctl_basic_type(
+ element_uctl_type, session_attributes);
+ auto length_type = create_integer_type_from_ust_ctl_basic_type(
+ length_uctl_type, session_attributes);
+
+ /* Publish an implicit length field _before_ the sequence field. */
+ publish_field(lttng::make_unique<lst::field>(length_field_name, std::move(length_type)));
+
+ *next_ust_ctl_field = current + 1;
+
+ if (element_encoding) {
+ const auto integer_element_size =
+ static_cast<const lst::integer_type&>(*element_type).size;
+
+ if (integer_element_size != 8) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Unexpected legacy array element type: integer has encoding but size is not 8: size = {}",
+ integer_element_size));
+ }
+
+ /* Sequence is a dynamic-length string. */
+ return lttng::make_unique<lst::dynamic_length_string_type>(sequence_alignment,
+ *element_encoding, std::move(length_field_name));
+ }
+
+ return lttng::make_unique<lst::dynamic_length_array_type>(
+ sequence_alignment, std::move(element_type), std::move(length_field_name));
+}
+
+lst::type::cuptr create_sequence_nestable_type_from_ust_ctl_fields(
+ const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field,
+ publish_field_fn publish_field)
+{
+ if (current >= end) {
+ LTTNG_THROW_PROTOCOL_ERROR(
+ fmt::format("End of {} array reached unexpectedly during decoding",
+ typeid(*current)));
+ }
+
+ const auto& sequence_uctl_field = *current;
+ const auto sequence_alignment = sequence_uctl_field.type.u.sequence_nestable.alignment;
+ const auto *length_field_name = sequence_uctl_field.type.u.sequence_nestable.length_name;
+
+ /* Nestable sequence fields are followed by their element type. */
+ const auto& element_uctl_field = *(current + 1);
+
+ nonstd::optional<enum lst::string_type::encoding> element_encoding;
+ if (element_uctl_field.type.atype == lttng_ust_ctl_atype_integer &&
+ element_uctl_field.type.u.integer.encoding != lttng_ust_ctl_encode_none) {
+ /* Element represents a text character. */
+ element_encoding = ust_ctl_encoding_to_string_field_encoding(
+ element_uctl_field.type.u.integer.encoding);
+ }
+
+ /* next_ust_ctl_field is updated as needed. */
+ auto element_type = create_type_from_ust_ctl_fields(&element_uctl_field, end,
+ session_attributes, next_ust_ctl_field, publish_field);
+
+ if (lttng_strnlen(sequence_uctl_field.type.u.sequence_nestable.length_name,
+ sizeof(sequence_uctl_field.type.u.sequence_nestable.length_name)) ==
+ sizeof(sequence_uctl_field.type.u.sequence_nestable.length_name)) {
+ LTTNG_THROW_PROTOCOL_ERROR("Sequence length field name is not null terminated");
+ }
+
+ if (element_encoding) {
+ const auto integer_element_size =
+ static_cast<const lst::integer_type&>(*element_type).size;
+
+ if (integer_element_size != 8) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Unexpected array element type: integer has encoding but size is not 8: size = {}",
+ integer_element_size));
+ }
+
+ /* Sqeuence is a dynamic-length string. */
+ return lttng::make_unique<lst::dynamic_length_string_type>(sequence_alignment,
+ *element_encoding, std::move(length_field_name));
+ }
+
+ return lttng::make_unique<lst::dynamic_length_array_type>(
+ sequence_alignment, std::move(element_type), std::move(length_field_name));
+}
+
+lst::type::cuptr create_structure_field_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes __attribute__((unused)),
+ const lttng_ust_ctl_field **next_ust_ctl_field)
+{
+ if (current >= end) {
+ LTTNG_THROW_PROTOCOL_ERROR(
+ fmt::format("End of {} array reached unexpectedly during decoding",
+ typeid(*current)));
+ }
+
+ uint32_t field_count;
+ uint32_t alignment;
+ const auto& structure_uctl_field = *current;
+
+ if (structure_uctl_field.type.atype == lttng_ust_ctl_atype_struct) {
+ field_count = structure_uctl_field.type.u.legacy._struct.nr_fields;
+ alignment = 0;
+ } else {
+ field_count = structure_uctl_field.type.u.struct_nestable.nr_fields;
+ alignment = structure_uctl_field.type.u.struct_nestable.alignment;
+ }
+
+ if (field_count != 0) {
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Only empty structures are supported by LTTng-UST: nr_fields = {}",
+ field_count));
+ }
+
+ *next_ust_ctl_field = current + 1;
+ return lttng::make_unique<lst::structure_type>(alignment, lst::structure_type::fields());
+}
+
+lst::type::cuptr create_variant_field_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field)
+{
+ if (current >= end) {
+ LTTNG_THROW_PROTOCOL_ERROR(
+ fmt::format("End of {} array reached unexpectedly during decoding",
+ typeid(*current)));
+ }
+
+ const auto& variant_uctl_field = *current;
+ current++;
+
+ uint32_t alignment;
+ uint32_t choice_count;
+ const char *tag_name;
+
+ if (variant_uctl_field.type.atype == lttng_ust_ctl_atype_variant) {
+ alignment = 0;
+ choice_count = variant_uctl_field.type.u.legacy.variant.nr_choices;
+ tag_name = variant_uctl_field.type.u.legacy.variant.tag_name;
+ } else {
+ alignment = variant_uctl_field.type.u.variant_nestable.alignment;
+ choice_count = variant_uctl_field.type.u.variant_nestable.nr_choices;
+ tag_name = variant_uctl_field.type.u.variant_nestable.tag_name;
+ }
+
+ /* Choices follow. next_ust_ctl_field is updated as needed. */
+ lst::variant_type::choices choices;
+ for (unsigned int i = 0; i < choice_count; i++) {
+ create_field_from_ust_ctl_fields(current, end, session_attributes,
+ next_ust_ctl_field, [&choices](lst::field::cuptr field) {
+ choices.emplace_back(std::move(field));
+ });
+
+ current = *next_ust_ctl_field;
+ }
+
+ return lttng::make_unique<lst::variant_type>(alignment, tag_name, std::move(choices));
+}
+
+lst::type::cuptr create_type_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field,
+ publish_field_fn publish_field)
+{
+ switch (current->type.atype) {
+ case lttng_ust_ctl_atype_integer:
+ return create_integer_type_from_ust_ctl_fields(
+ current, end, session_attributes, next_ust_ctl_field);
+ case lttng_ust_ctl_atype_enum:
+ case lttng_ust_ctl_atype_enum_nestable:
+ return create_enumeration_type_from_ust_ctl_fields(
+ current, end, session_attributes, next_ust_ctl_field);
+ case lttng_ust_ctl_atype_float:
+ return create_floating_point_type_from_ust_ctl_fields(
+ current, end, session_attributes, next_ust_ctl_field);
+ case lttng_ust_ctl_atype_string:
+ return create_string_type_from_ust_ctl_fields(
+ current, end, session_attributes, next_ust_ctl_field);
+ case lttng_ust_ctl_atype_array:
+ return create_array_type_from_ust_ctl_fields(current, end, session_attributes,
+ next_ust_ctl_field);
+ case lttng_ust_ctl_atype_array_nestable:
+ return create_array_nestable_type_from_ust_ctl_fields(current, end,
+ session_attributes, next_ust_ctl_field, publish_field);
+ case lttng_ust_ctl_atype_sequence:
+ return create_sequence_type_from_ust_ctl_fields(current, end, session_attributes,
+ next_ust_ctl_field, publish_field);
+ case lttng_ust_ctl_atype_sequence_nestable:
+ return create_sequence_nestable_type_from_ust_ctl_fields(current, end,
+ session_attributes, next_ust_ctl_field, publish_field);
+ case lttng_ust_ctl_atype_struct:
+ case lttng_ust_ctl_atype_struct_nestable:
+ return create_structure_field_from_ust_ctl_fields(
+ current, end, session_attributes, next_ust_ctl_field);
+ case lttng_ust_ctl_atype_variant:
+ case lttng_ust_ctl_atype_variant_nestable:
+ return create_variant_field_from_ust_ctl_fields(
+ current, end, session_attributes, next_ust_ctl_field);
+ default:
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Unknown {} value `{}` encountered while converting {} to {}",
+ typeid(current->type.atype), current->type.atype, typeid(*current),
+ typeid(lst::type::cuptr::element_type)));
+ }
+}
+
+void create_field_from_ust_ctl_fields(const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end,
+ const session_attributes& session_attributes,
+ const lttng_ust_ctl_field **next_ust_ctl_field,
+ publish_field_fn publish_field)
+{
+ LTTNG_ASSERT(current < end);
+
+ if (lttng_strnlen(current->name, sizeof(current->name)) == sizeof(current->name)) {
+ LTTNG_THROW_PROTOCOL_ERROR(
+ fmt::format("Name of {} is not null-terminated", typeid(*current)));
+ }
+
+ publish_field(lttng::make_unique<lst::field>(current->name,
+ create_type_from_ust_ctl_fields(current, end, session_attributes,
+ next_ust_ctl_field, publish_field)));
+}
+
+/*
+ * `lttng_ust_ctl_field`s can be nested, in which case creating a field will consume
+ * more than one lttng_ust_ctl_field. create_field_from_ust_ctl_fields returns the
+ * position of the next lttng_ust_ctl_field to consume or `end` when the last field
+ * is consumed.
+ *
+ * Always returns a new field, throws on error.
+ */
+std::vector<lst::field::cuptr> create_fields_from_ust_ctl_fields(
+ const ust_registry_session& session,
+ const lttng_ust_ctl_field *current,
+ const lttng_ust_ctl_field *end)
+{
+ std::vector<lst::field::cuptr> fields;
+ const auto trace_native_byte_order = session.abi.byte_order;
+ const session_attributes session_attributes{
+ [&session](const char *enum_name, uint64_t enum_id) {
+ return ust_registry_lookup_enum_by_id(&session, enum_name, enum_id);
+ },
+ trace_native_byte_order};
+
+ while (current < end) {
+ auto *next_field = current;
+
+ /*
+ * create_field_from_ust_ctl_fields will consume one field at a time.
+ * However, some fields expressed by LTTng-UST's protocol are expended
+ * to multiple event fields (legacy sequence fields implicitly define
+ * their length field).
+ *
+ * The lambda allows the factory functions to push as many fields as
+ * needed depending on the decoded field's type.
+ */
+ create_field_from_ust_ctl_fields(current, end, session_attributes, &next_field,
+ [&fields](lst::field::cuptr field) {
+ fields.emplace_back(std::move(field));
+ });
+
+ current = next_field;
+ }
+
+ return fields;
+}
+} /* namespace */
+
+std::vector<lst::field::cuptr> lsu::create_trace_fields_from_ust_ctl_fields(
+ const ust_registry_session& session,
+ const lttng_ust_ctl_field *fields,
+ std::size_t field_count)
+{
+ return create_fields_from_ust_ctl_fields(session, fields, fields + field_count);
+}
--- /dev/null
+/*
+ * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef LTTNG_UST_FIELD_CONVERT_H
+#define LTTNG_UST_FIELD_CONVERT_H
+
+#include "field.hpp"
+#include "ust-registry.hpp"
+
+#include <cstddef>
+#include <vector>
+
+namespace lttng {
+namespace sessiond {
+namespace ust {
+
+std::vector<trace::field::cuptr> create_trace_fields_from_ust_ctl_fields(
+ const ust_registry_session& session,
+ const lttng_ust_ctl_field *fields,
+ std::size_t field_count);
+
+} /* namespace ust */
+} /* namespace sessiond */
+} /* namespace lttng */
+
+#endif /* LTTNG_UST_FIELD_CONVERT_H */
+++ /dev/null
-/*
- * Copyright (C) 2018 Francis Deslauriers <francis.deslauriers@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#include <stdbool.h>
-#include <string.h>
-
-#include "ust-field-utils.hpp"
-
-/*
- * The lttng_ust_ctl_field is made of a combination of C basic types
- * lttng_ust_ctl_basic_type and _lttng_ust_ctl_basic_type.
- *
- * lttng_ust_ctl_basic_type contains an enumeration describing the abstract type.
- * _lttng_ust_ctl_basic_type does _NOT_ contain an enumeration describing the
- * abstract type.
- *
- * A layer is needed to use the same code for both structures.
- * When dealing with _lttng_ust_ctl_basic_type, we need to use the abstract type of
- * the lttng_ust_ctl_type struct.
- */
-
-/*
- * Compare two lttng_ust_ctl_integer_type fields.
- * Returns 1 if both are identical.
- */
-static bool match_lttng_ust_ctl_field_integer(const struct lttng_ust_ctl_integer_type *first,
- const struct lttng_ust_ctl_integer_type *second)
-{
- if (first->size != second->size) {
- goto no_match;
- }
- if (first->alignment != second->alignment) {
- goto no_match;
- }
- if (first->signedness != second->signedness) {
- goto no_match;
- }
- if (first->encoding != second->encoding) {
- goto no_match;
- }
- if (first->base != second->base) {
- goto no_match;
- }
- if (first->reverse_byte_order != second->reverse_byte_order) {
- goto no_match;
- }
-
- return true;
-
-no_match:
- return false;
-}
-
-/*
- * Compare two _lttng_ust_ctl_basic_type fields known to be of type integer.
- * Returns 1 if both are identical.
- */
-static bool match_lttng_ust_ctl_field_integer_from_raw_basic_type(
- const union _lttng_ust_ctl_basic_type *first,
- const union _lttng_ust_ctl_basic_type *second)
-{
- return match_lttng_ust_ctl_field_integer(&first->integer, &second->integer);
-}
-
-/*
- * Compare two _lttng_ust_ctl_basic_type fields known to be of type enum.
- * Returns 1 if both are identical.
- */
-static bool match_lttng_ust_ctl_field_enum_from_raw_basic_type(
- const union _lttng_ust_ctl_basic_type *first,
- const union _lttng_ust_ctl_basic_type *second)
-{
- /*
- * Compare enumeration ID. Enumeration ID is provided to the application by
- * the session daemon before event registration.
- */
- if (first->enumeration.id != second->enumeration.id) {
- goto no_match;
- }
-
- /*
- * Sanity check of the name and container type. Those were already checked
- * during enum registration.
- */
- if (strncmp(first->enumeration.name, second->enumeration.name,
- LTTNG_UST_ABI_SYM_NAME_LEN)) {
- goto no_match;
- }
- if (!match_lttng_ust_ctl_field_integer(&first->enumeration.container_type,
- &second->enumeration.container_type)) {
- goto no_match;
- }
-
- return true;
-
-no_match:
- return false;
-}
-
-/*
- * Compare two _lttng_ust_ctl_basic_type fields known to be of type string.
- * Returns 1 if both are identical.
- */
-static bool match_lttng_ust_ctl_field_string_from_raw_basic_type(
- const union _lttng_ust_ctl_basic_type *first,
- const union _lttng_ust_ctl_basic_type *second)
-{
- return first->string.encoding == second->string.encoding;
-}
-
-/*
- * Compare two _lttng_ust_ctl_basic_type fields known to be of type float.
- * Returns 1 if both are identical.
- */
-static bool match_lttng_ust_ctl_field_float_from_raw_basic_type(
- const union _lttng_ust_ctl_basic_type *first,
- const union _lttng_ust_ctl_basic_type *second)
-{
- if (first->_float.exp_dig != second->_float.exp_dig) {
- goto no_match;
- }
-
- if (first->_float.mant_dig != second->_float.mant_dig) {
- goto no_match;
- }
-
- if (first->_float.reverse_byte_order !=
- second->_float.reverse_byte_order) {
- goto no_match;
- }
-
- if (first->_float.alignment != second->_float.alignment) {
- goto no_match;
- }
-
- return true;
-
-no_match:
- return false;
-}
-
-/*
- * Compare two _lttng_ust_ctl_basic_type fields given their respective abstract types.
- * Returns 1 if both are identical.
- */
-static bool match_lttng_ust_ctl_field_raw_basic_type(
- enum lttng_ust_ctl_abstract_types first_atype,
- const union _lttng_ust_ctl_basic_type *first,
- enum lttng_ust_ctl_abstract_types second_atype,
- const union _lttng_ust_ctl_basic_type *second)
-{
- if (first_atype != second_atype) {
- goto no_match;
- }
-
- switch (first_atype) {
- case lttng_ust_ctl_atype_integer:
- if (!match_lttng_ust_ctl_field_integer_from_raw_basic_type(first, second)) {
- goto no_match;
- }
- break;
- case lttng_ust_ctl_atype_enum:
- if (!match_lttng_ust_ctl_field_enum_from_raw_basic_type(first, second)) {
- goto no_match;
- }
- break;
- case lttng_ust_ctl_atype_string:
- if (!match_lttng_ust_ctl_field_string_from_raw_basic_type(first, second)) {
- goto no_match;
- }
- break;
- case lttng_ust_ctl_atype_float:
- if (!match_lttng_ust_ctl_field_float_from_raw_basic_type(first, second)) {
- goto no_match;
- }
- break;
- default:
- goto no_match;
- }
-
- return true;
-
-no_match:
- return false;
-}
-
-/*
- * Compatibility layer between the lttng_ust_ctl_basic_type struct and
- * _lttng_ust_ctl_basic_type union.
- */
-static bool match_lttng_ust_ctl_field_basic_type(const struct lttng_ust_ctl_basic_type *first,
- const struct lttng_ust_ctl_basic_type *second)
-{
- return match_lttng_ust_ctl_field_raw_basic_type(first->atype, &first->u.basic,
- second->atype, &second->u.basic);
-}
-
-int match_lttng_ust_ctl_field(const struct lttng_ust_ctl_field *first,
- const struct lttng_ust_ctl_field *second)
-{
- /* Check the name of the field is identical. */
- if (strncmp(first->name, second->name, LTTNG_UST_ABI_SYM_NAME_LEN)) {
- goto no_match;
- }
-
- /* Check the field type is identical. */
- if (first->type.atype != second->type.atype) {
- goto no_match;
- }
-
- /* Check the field layout. */
- switch (first->type.atype) {
- case lttng_ust_ctl_atype_integer:
- case lttng_ust_ctl_atype_enum:
- case lttng_ust_ctl_atype_string:
- case lttng_ust_ctl_atype_float:
- if (!match_lttng_ust_ctl_field_raw_basic_type(first->type.atype,
- &first->type.u.legacy.basic, second->type.atype,
- &second->type.u.legacy.basic)) {
- goto no_match;
- }
- break;
- case lttng_ust_ctl_atype_sequence:
- /* Match element type of the sequence. */
- if (!match_lttng_ust_ctl_field_basic_type(&first->type.u.legacy.sequence.elem_type,
- &second->type.u.legacy.sequence.elem_type)) {
- goto no_match;
- }
-
- /* Match length type of the sequence. */
- if (!match_lttng_ust_ctl_field_basic_type(&first->type.u.legacy.sequence.length_type,
- &second->type.u.legacy.sequence.length_type)) {
- goto no_match;
- }
- break;
- case lttng_ust_ctl_atype_array:
- /* Match element type of the array. */
- if (!match_lttng_ust_ctl_field_basic_type(&first->type.u.legacy.array.elem_type,
- &second->type.u.legacy.array.elem_type)) {
- goto no_match;
- }
-
- /* Match length of the array. */
- if (first->type.u.legacy.array.length != second->type.u.legacy.array.length) {
- goto no_match;
- }
- break;
- case lttng_ust_ctl_atype_variant:
- /* Compare number of choice of the variants. */
- if (first->type.u.legacy.variant.nr_choices !=
- second->type.u.legacy.variant.nr_choices) {
- goto no_match;
- }
-
- /* Compare tag name of the variants. */
- if (strncmp(first->type.u.legacy.variant.tag_name,
- second->type.u.legacy.variant.tag_name,
- LTTNG_UST_ABI_SYM_NAME_LEN)) {
- goto no_match;
- }
- break;
- case lttng_ust_ctl_atype_struct:
- /* Compare number of fields of the structs. */
- if (first->type.u.legacy._struct.nr_fields != second->type.u.legacy._struct.nr_fields) {
- goto no_match;
- }
- break;
- case lttng_ust_ctl_atype_sequence_nestable:
- if (first->type.u.sequence_nestable.alignment != second->type.u.sequence_nestable.alignment) {
- goto no_match;
- }
- /* Compare length_name of the sequences. */
- if (strncmp(first->type.u.sequence_nestable.length_name,
- second->type.u.sequence_nestable.length_name,
- LTTNG_UST_ABI_SYM_NAME_LEN)) {
- goto no_match;
- }
- /* Comparison will be done when marshalling following items. */
- break;
- case lttng_ust_ctl_atype_array_nestable:
- if (first->type.u.array_nestable.alignment != second->type.u.array_nestable.alignment) {
- goto no_match;
- }
- /* Match length of the array. */
- if (first->type.u.array_nestable.length != second->type.u.array_nestable.length) {
- goto no_match;
- }
- /* Comparison of element type will be done when marshalling following item. */
- break;
- case lttng_ust_ctl_atype_enum_nestable:
- if (first->type.u.enum_nestable.id != second->type.u.enum_nestable.id) {
- goto no_match;
- }
- /* Compare name of the enums. */
- if (strncmp(first->type.u.enum_nestable.name,
- second->type.u.enum_nestable.name,
- LTTNG_UST_ABI_SYM_NAME_LEN)) {
- goto no_match;
- }
- /* Comparison of element type will be done when marshalling following item. */
- break;
- case lttng_ust_ctl_atype_struct_nestable:
- if (first->type.u.struct_nestable.alignment != second->type.u.struct_nestable.alignment) {
- goto no_match;
- }
- /* Compare number of fields of the structs. */
- if (first->type.u.struct_nestable.nr_fields != second->type.u.struct_nestable.nr_fields) {
- goto no_match;
- }
- break;
- case lttng_ust_ctl_atype_variant_nestable:
- if (first->type.u.variant_nestable.alignment != second->type.u.variant_nestable.alignment) {
- goto no_match;
- }
- /* Compare number of choice of the variants. */
- if (first->type.u.variant_nestable.nr_choices !=
- second->type.u.variant_nestable.nr_choices) {
- goto no_match;
- }
-
- /* Compare tag name of the variants. */
- if (strncmp(first->type.u.variant_nestable.tag_name,
- second->type.u.variant_nestable.tag_name,
- LTTNG_UST_ABI_SYM_NAME_LEN)) {
- goto no_match;
- }
- break;
- default:
- goto no_match;
- }
-
- return true;
-
-no_match:
- return false;
-}
-
-/*
- * Compare two arrays of UST fields.
- * Return true if both arrays have identical field definitions, false otherwise.
- */
-bool match_lttng_ust_ctl_field_array(const struct lttng_ust_ctl_field *first,
- size_t nr_first,
- const struct lttng_ust_ctl_field *second,
- size_t nr_second)
-{
- size_t i;
- const size_t nr_fields = nr_first;
-
- /* Compare the array lengths. */
- if (nr_first != nr_second) {
- goto no_match;
- }
-
- /* Compare each field individually. */
- for (i = 0; i < nr_fields; i++) {
- if (!match_lttng_ust_ctl_field(&first[i], &second[i])) {
- goto no_match;
- }
- }
-
- return true;
-
-no_match:
- return false;
-}
+++ /dev/null
-/*
- * Copyright (C) 2018 Francis Deslauriers francis.deslauriers@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#ifndef LTTNG_UST_FIELD_UTILS_H
-#define LTTNG_UST_FIELD_UTILS_H
-
-#include "lttng-ust-ctl.hpp"
-
-/*
- * Compare two UST fields.
- * Return 1 if both fields have identical definition, 0 otherwise.
- */
-int match_lttng_ust_ctl_field(const struct lttng_ust_ctl_field *first,
- const struct lttng_ust_ctl_field *second);
-
-/*
- * Compare two arrays of UST fields.
- * Return true if both arrays have identical field definitions, false otherwise.
- */
-bool match_lttng_ust_ctl_field_array(const struct lttng_ust_ctl_field *first,
- size_t nr_first,
- const struct lttng_ust_ctl_field *second,
- size_t nr_second);
-
-#endif /* LTTNG_UST_FIELD_UTILS_H */
#include <common/common.hpp>
#include <common/exception.hpp>
+#include <common/format.hpp>
#include <common/time.hpp>
#include <common/uuid.hpp>
#include "ust-app.hpp"
-#include "ust-clock.hpp"
+#include "ust-clock-class.hpp"
#include "ust-registry.hpp"
+#include "tsdl-environment-visitor.hpp"
-static
-int _lttng_field_statedump(ust_registry_session *session,
- const struct lttng_ust_ctl_field *fields, size_t nr_fields,
- size_t *iter_field, size_t nesting);
-
-static inline
-int get_count_order(unsigned int count)
-{
- int order;
-
- order = lttng_fls(count) - 1;
- if (count & (count - 1)) {
- order++;
- }
- LTTNG_ASSERT(order >= 0);
- return order;
-}
-
-/*
- * Returns offset where to write in metadata array, or negative error value on error.
- */
-static
-ssize_t metadata_reserve(ust_registry_session *session, size_t len)
-{
- size_t new_len = session->_metadata_len + len;
- size_t new_alloc_len = new_len;
- size_t old_alloc_len = session->_metadata_alloc_len;
- ssize_t ret;
-
- if (new_alloc_len > (UINT32_MAX >> 1))
- return -EINVAL;
- if ((old_alloc_len << 1) > (UINT32_MAX >> 1))
- return -EINVAL;
-
- if (new_alloc_len > old_alloc_len) {
- char *newptr;
-
- new_alloc_len =
- std::max<size_t>(1U << get_count_order(new_alloc_len), old_alloc_len << 1);
- newptr = (char *) realloc(session->_metadata, new_alloc_len);
- if (!newptr)
- return -ENOMEM;
- session->_metadata = newptr;
- /* We zero directly the memory from start of allocation. */
- memset(&session->_metadata[old_alloc_len], 0, new_alloc_len - old_alloc_len);
- session->_metadata_alloc_len = new_alloc_len;
- }
- ret = session->_metadata_len;
- session->_metadata_len += len;
- return ret;
-}
-
-static
-int metadata_file_append(ust_registry_session *session,
- const char *str, size_t len)
-{
- ssize_t written;
-
- if (session->_metadata_fd < 0) {
- return 0;
- }
- /* Write to metadata file */
- written = lttng_write(session->_metadata_fd, str, len);
- if (written != len) {
- return -1;
- }
- return 0;
-}
-
-/*
- * We have exclusive access to our metadata buffer (protected by the
- * ust_lock), so we can do racy operations such as looking for
- * remaining space left in packet and write, since mutual exclusion
- * protects us from concurrent writes.
- */
-static ATTR_FORMAT_PRINTF(2, 3)
-int lttng_metadata_printf(ust_registry_session *session,
- const char *fmt, ...)
-{
- char *str = NULL;
- size_t len;
- va_list ap;
- ssize_t offset;
- int ret;
-
- va_start(ap, fmt);
- ret = vasprintf(&str, fmt, ap);
- va_end(ap);
- if (ret < 0)
- return -ENOMEM;
-
- len = strlen(str);
- offset = metadata_reserve(session, len);
- if (offset < 0) {
- ret = offset;
- goto end;
- }
- memcpy(&session->_metadata[offset], str, len);
- ret = metadata_file_append(session, str, len);
- if (ret) {
- PERROR("Error appending to metadata file");
- goto end;
- }
- DBG3("Append to metadata: \"%s\"", str);
- ret = 0;
-
-end:
- free(str);
- return ret;
-}
-
-static
-int print_tabs(ust_registry_session *session, size_t nesting)
-{
- size_t i;
-
- for (i = 0; i < nesting; i++) {
- int ret;
-
- ret = lttng_metadata_printf(session, " ");
- if (ret) {
- return ret;
- }
- }
- return 0;
-}
-
-static
-void sanitize_ctf_identifier(char *out, const char *in)
-{
- size_t i;
-
- for (i = 0; i < LTTNG_UST_ABI_SYM_NAME_LEN; i++) {
- switch (in[i]) {
- case '.':
- case '$':
- case ':':
- out[i] = '_';
- break;
- default:
- out[i] = in[i];
- }
- }
-}
-
-static
-int print_escaped_ctf_string(ust_registry_session *session, const char *string)
-{
- int ret = 0;
- size_t i;
- char cur;
-
- i = 0;
- cur = string[i];
- while (cur != '\0') {
- switch (cur) {
- case '\n':
- ret = lttng_metadata_printf(session, "%s", "\\n");
- break;
- case '\\':
- case '"':
- ret = lttng_metadata_printf(session, "%c", '\\');
- if (ret) {
- goto error;
- }
- /* We still print the current char */
- /* Fallthrough */
- default:
- ret = lttng_metadata_printf(session, "%c", cur);
- break;
- }
-
- if (ret) {
- goto error;
- }
-
- cur = string[++i];
- }
-error:
- return ret;
-}
-
-/* Called with session registry mutex held. */
-static
-int ust_metadata_enum_statedump(ust_registry_session *session,
- const char *enum_name,
- uint64_t enum_id,
- const struct lttng_ust_ctl_integer_type *container_type,
- const char *field_name, size_t *iter_field, size_t nesting)
-{
- struct ust_registry_enum *reg_enum;
- const struct lttng_ust_ctl_enum_entry *entries;
- size_t nr_entries;
- int ret = 0;
- size_t i;
- char identifier[LTTNG_UST_ABI_SYM_NAME_LEN];
-
- rcu_read_lock();
- reg_enum = ust_registry_lookup_enum_by_id(session, enum_name, enum_id);
- rcu_read_unlock();
- /* reg_enum can still be used because session registry mutex is held. */
- if (!reg_enum) {
- ret = -ENOENT;
- goto end;
- }
- entries = reg_enum->entries;
- nr_entries = reg_enum->nr_entries;
-
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "enum : integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u; } {\n",
- container_type->size,
- container_type->alignment,
- container_type->signedness,
- (container_type->encoding == lttng_ust_ctl_encode_none)
- ? "none"
- : (container_type->encoding == lttng_ust_ctl_encode_UTF8)
- ? "UTF8"
- : "ASCII",
- container_type->base);
- if (ret) {
- goto end;
- }
- nesting++;
- /* Dump all entries */
- for (i = 0; i < nr_entries; i++) {
- const struct lttng_ust_ctl_enum_entry *entry = &entries[i];
- int j, len;
-
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "\"");
- if (ret) {
- goto end;
- }
- len = strlen(entry->string);
- /* Escape the character '"' */
- for (j = 0; j < len; j++) {
- char c = entry->string[j];
-
- switch (c) {
- case '"':
- ret = lttng_metadata_printf(session,
- "\\\"");
- break;
- case '\\':
- ret = lttng_metadata_printf(session,
- "\\\\");
- break;
- default:
- ret = lttng_metadata_printf(session,
- "%c", c);
- break;
- }
- if (ret) {
- goto end;
- }
- }
- ret = lttng_metadata_printf(session, "\"");
- if (ret) {
- goto end;
- }
-
- if (entry->u.extra.options &
- LTTNG_UST_CTL_UST_ENUM_ENTRY_OPTION_IS_AUTO) {
- ret = lttng_metadata_printf(session, ",\n");
- if (ret) {
- goto end;
- }
- } else {
- ret = lttng_metadata_printf(session,
- " = ");
- if (ret) {
- goto end;
- }
-
- if (entry->start.signedness) {
- ret = lttng_metadata_printf(session,
- "%" PRId64, entry->start.value);
- } else {
- ret = lttng_metadata_printf(session,
- "%" PRIu64, entry->start.value);
- }
- if (ret) {
- goto end;
- }
-
- if (entry->start.signedness == entry->end.signedness &&
- entry->start.value ==
- entry->end.value) {
- ret = lttng_metadata_printf(session, ",\n");
- } else {
- if (entry->end.signedness) {
- ret = lttng_metadata_printf(session,
- " ... %" PRId64 ",\n",
- entry->end.value);
- } else {
- ret = lttng_metadata_printf(session,
- " ... %" PRIu64 ",\n",
- entry->end.value);
- }
- }
- if (ret) {
- goto end;
- }
- }
- }
- nesting--;
- sanitize_ctf_identifier(identifier, field_name);
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session, "} _%s;\n",
- identifier);
-end:
- (*iter_field)++;
- return ret;
-}
-
-static
-int _lttng_variant_statedump(ust_registry_session *session,
- uint32_t nr_choices, const char *tag_name,
- uint32_t alignment,
- const struct lttng_ust_ctl_field *fields, size_t nr_fields,
- size_t *iter_field, size_t nesting)
-{
- const struct lttng_ust_ctl_field *variant = &fields[*iter_field];
- uint32_t i;
- int ret;
- char identifier[LTTNG_UST_ABI_SYM_NAME_LEN];
-
- (*iter_field)++;
- sanitize_ctf_identifier(identifier, tag_name);
- if (alignment) {
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "struct { } align(%u) _%s_padding;\n",
- alignment * CHAR_BIT,
- variant->name);
- if (ret) {
- goto end;
- }
- }
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "variant <_%s> {\n",
- identifier);
- if (ret) {
- goto end;
- }
-
- for (i = 0; i < nr_choices; i++) {
- if (*iter_field >= nr_fields) {
- ret = -EOVERFLOW;
- goto end;
- }
- ret = _lttng_field_statedump(session,
- fields, nr_fields,
- iter_field, nesting + 1);
- if (ret) {
- goto end;
- }
- }
- sanitize_ctf_identifier(identifier, variant->name);
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "} _%s;\n",
- identifier);
- if (ret) {
- goto end;
- }
-end:
- return ret;
-}
-
-static
-int _lttng_field_statedump(ust_registry_session *session,
- const struct lttng_ust_ctl_field *fields, size_t nr_fields,
- size_t *iter_field, size_t nesting)
-{
- int ret = 0;
- const char *bo_be = " byte_order = be;";
- const char *bo_le = " byte_order = le;";
- const char *bo_native = "";
- const char *bo_reverse;
- const struct lttng_ust_ctl_field *field;
-
- if (*iter_field >= nr_fields) {
- ret = -EOVERFLOW;
- goto end;
- }
- field = &fields[*iter_field];
-
- if (session->_byte_order == BIG_ENDIAN) {
- bo_reverse = bo_le;
- } else {
- bo_reverse = bo_be;
- }
-
- switch (field->type.atype) {
- case lttng_ust_ctl_atype_integer:
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s;\n",
- field->type.u.integer.size,
- field->type.u.integer.alignment,
- field->type.u.integer.signedness,
- (field->type.u.integer.encoding == lttng_ust_ctl_encode_none)
- ? "none"
- : (field->type.u.integer.encoding == lttng_ust_ctl_encode_UTF8)
- ? "UTF8"
- : "ASCII",
- field->type.u.integer.base,
- field->type.u.integer.reverse_byte_order ? bo_reverse : bo_native,
- field->name);
- (*iter_field)++;
- break;
- case lttng_ust_ctl_atype_enum:
- ret = ust_metadata_enum_statedump(session,
- field->type.u.legacy.basic.enumeration.name,
- field->type.u.legacy.basic.enumeration.id,
- &field->type.u.legacy.basic.enumeration.container_type,
- field->name, iter_field, nesting);
- break;
- case lttng_ust_ctl_atype_float:
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "floating_point { exp_dig = %u; mant_dig = %u; align = %u;%s } _%s;\n",
- field->type.u._float.exp_dig,
- field->type.u._float.mant_dig,
- field->type.u._float.alignment,
- field->type.u._float.reverse_byte_order ? bo_reverse : bo_native,
- field->name);
- (*iter_field)++;
- break;
- case lttng_ust_ctl_atype_array:
- {
- const struct lttng_ust_ctl_basic_type *elem_type;
-
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- elem_type = &field->type.u.legacy.array.elem_type;
- /* Only integers are currently supported in arrays. */
- if (elem_type->atype != lttng_ust_ctl_atype_integer) {
- ret = -EINVAL;
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s[%u];\n",
- elem_type->u.basic.integer.size,
- elem_type->u.basic.integer.alignment,
- elem_type->u.basic.integer.signedness,
- (elem_type->u.basic.integer.encoding == lttng_ust_ctl_encode_none)
- ? "none"
- : (elem_type->u.basic.integer.encoding == lttng_ust_ctl_encode_UTF8)
- ? "UTF8"
- : "ASCII",
- elem_type->u.basic.integer.base,
- elem_type->u.basic.integer.reverse_byte_order ? bo_reverse : bo_native,
- field->name, field->type.u.legacy.array.length);
- (*iter_field)++;
- break;
- }
- case lttng_ust_ctl_atype_array_nestable:
- {
- uint32_t array_length;
- const struct lttng_ust_ctl_field *array_nestable;
- const struct lttng_ust_ctl_type *elem_type;
-
- array_length = field->type.u.array_nestable.length;
- (*iter_field)++;
-
- if (*iter_field >= nr_fields) {
- ret = -EOVERFLOW;
- goto end;
- }
- array_nestable = &fields[*iter_field];
- elem_type = &array_nestable->type;
-
- /* Only integers are currently supported in arrays. */
- if (elem_type->atype != lttng_ust_ctl_atype_integer) {
- ret = -EINVAL;
- goto end;
- }
-
- if (field->type.u.array_nestable.alignment) {
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "struct { } align(%u) _%s_padding;\n",
- field->type.u.array_nestable.alignment * CHAR_BIT,
- field->name);
- if (ret) {
- goto end;
- }
- }
-
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s[%u];\n",
- elem_type->u.integer.size,
- elem_type->u.integer.alignment,
- elem_type->u.integer.signedness,
- (elem_type->u.integer.encoding == lttng_ust_ctl_encode_none)
- ? "none"
- : (elem_type->u.integer.encoding == lttng_ust_ctl_encode_UTF8)
- ? "UTF8"
- : "ASCII",
- elem_type->u.integer.base,
- elem_type->u.integer.reverse_byte_order ? bo_reverse : bo_native,
- field->name, array_length);
- (*iter_field)++;
- break;
- }
- case lttng_ust_ctl_atype_sequence:
- {
- const struct lttng_ust_ctl_basic_type *elem_type;
- const struct lttng_ust_ctl_basic_type *length_type;
-
- elem_type = &field->type.u.legacy.sequence.elem_type;
- length_type = &field->type.u.legacy.sequence.length_type;
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
-
- /* Only integers are currently supported in sequences. */
- if (elem_type->atype != lttng_ust_ctl_atype_integer) {
- ret = -EINVAL;
- goto end;
- }
-
- ret = lttng_metadata_printf(session,
- "integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } __%s_length;\n",
- length_type->u.basic.integer.size,
- (unsigned int) length_type->u.basic.integer.alignment,
- length_type->u.basic.integer.signedness,
- (length_type->u.basic.integer.encoding == lttng_ust_ctl_encode_none)
- ? "none"
- : ((length_type->u.basic.integer.encoding == lttng_ust_ctl_encode_UTF8)
- ? "UTF8"
- : "ASCII"),
- length_type->u.basic.integer.base,
- length_type->u.basic.integer.reverse_byte_order ? bo_reverse : bo_native,
- field->name);
- if (ret) {
- goto end;
- }
-
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s[ __%s_length ];\n",
- elem_type->u.basic.integer.size,
- (unsigned int) elem_type->u.basic.integer.alignment,
- elem_type->u.basic.integer.signedness,
- (elem_type->u.basic.integer.encoding == lttng_ust_ctl_encode_none)
- ? "none"
- : ((elem_type->u.basic.integer.encoding == lttng_ust_ctl_encode_UTF8)
- ? "UTF8"
- : "ASCII"),
- elem_type->u.basic.integer.base,
- elem_type->u.basic.integer.reverse_byte_order ? bo_reverse : bo_native,
- field->name,
- field->name);
- (*iter_field)++;
- break;
- }
- case lttng_ust_ctl_atype_sequence_nestable:
- {
- const struct lttng_ust_ctl_field *sequence_nestable;
- const struct lttng_ust_ctl_type *elem_type;
-
- (*iter_field)++;
- if (*iter_field >= nr_fields) {
- ret = -EOVERFLOW;
- goto end;
- }
- sequence_nestable = &fields[*iter_field];
- elem_type = &sequence_nestable->type;
-
- /* Only integers are currently supported in sequences. */
- if (elem_type->atype != lttng_ust_ctl_atype_integer) {
- ret = -EINVAL;
- goto end;
- }
-
- if (field->type.u.sequence_nestable.alignment) {
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "struct { } align(%u) _%s_padding;\n",
- field->type.u.sequence_nestable.alignment * CHAR_BIT,
- field->name);
- if (ret) {
- goto end;
- }
- }
-
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s[ _%s ];\n",
- elem_type->u.integer.size,
- (unsigned int) elem_type->u.integer.alignment,
- elem_type->u.integer.signedness,
- (elem_type->u.integer.encoding == lttng_ust_ctl_encode_none)
- ? "none"
- : ((elem_type->u.integer.encoding == lttng_ust_ctl_encode_UTF8)
- ? "UTF8"
- : "ASCII"),
- elem_type->u.integer.base,
- elem_type->u.integer.reverse_byte_order ? bo_reverse : bo_native,
- field->name,
- field->type.u.sequence_nestable.length_name);
- (*iter_field)++;
- break;
- }
- case lttng_ust_ctl_atype_string:
- /* Default encoding is UTF8 */
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "string%s _%s;\n",
- field->type.u.string.encoding == lttng_ust_ctl_encode_ASCII ?
- " { encoding = ASCII; }" : "",
- field->name);
- (*iter_field)++;
- break;
- case lttng_ust_ctl_atype_variant:
- ret = _lttng_variant_statedump(session,
- field->type.u.legacy.variant.nr_choices,
- field->type.u.legacy.variant.tag_name,
- 0,
- fields, nr_fields, iter_field, nesting);
- if (ret) {
- goto end;
- }
- break;
- case lttng_ust_ctl_atype_variant_nestable:
- ret = _lttng_variant_statedump(session,
- field->type.u.variant_nestable.nr_choices,
- field->type.u.variant_nestable.tag_name,
- field->type.u.variant_nestable.alignment,
- fields, nr_fields, iter_field, nesting);
- if (ret) {
- goto end;
- }
- break;
- case lttng_ust_ctl_atype_struct:
- if (field->type.u.legacy._struct.nr_fields != 0) {
- /* Currently only 0-length structures are supported. */
- ret = -EINVAL;
- goto end;
- }
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- ret = lttng_metadata_printf(session,
- "struct {} _%s;\n",
- field->name);
- (*iter_field)++;
- break;
- case lttng_ust_ctl_atype_struct_nestable:
- if (field->type.u.struct_nestable.nr_fields != 0) {
- /* Currently only 0-length structures are supported. */
- ret = -EINVAL;
- goto end;
- }
- ret = print_tabs(session, nesting);
- if (ret) {
- goto end;
- }
- if (field->type.u.struct_nestable.alignment) {
- ret = lttng_metadata_printf(session,
- "struct {} align(%u) _%s;\n",
- field->type.u.struct_nestable.alignment * CHAR_BIT,
- field->name);
- if (ret) {
- goto end;
- }
- } else {
- ret = lttng_metadata_printf(session,
- "struct {} _%s;\n",
- field->name);
- }
- (*iter_field)++;
- break;
- case lttng_ust_ctl_atype_enum_nestable:
- {
- const struct lttng_ust_ctl_field *container_field;
- const struct lttng_ust_ctl_type *container_type;
-
- (*iter_field)++;
- if (*iter_field >= nr_fields) {
- ret = -EOVERFLOW;
- goto end;
- }
- container_field = &fields[*iter_field];
- container_type = &container_field->type;
-
- /* Only integers are supported as container types. */
- if (container_type->atype != lttng_ust_ctl_atype_integer) {
- ret = -EINVAL;
- goto end;
- }
- ret = ust_metadata_enum_statedump(session,
- field->type.u.enum_nestable.name,
- field->type.u.enum_nestable.id,
- &container_type->u.integer,
- field->name, iter_field, nesting);
- break;
- }
- default:
- ret = -EINVAL;
- }
-end:
- return ret;
-}
-
-static
-int _lttng_context_metadata_statedump(ust_registry_session *session,
- size_t nr_ctx_fields,
- struct lttng_ust_ctl_field *ctx)
-{
- int ret = 0;
- size_t i = 0;
-
- if (!ctx)
- return 0;
- for (;;) {
- if (i >= nr_ctx_fields) {
- break;
- }
- ret = _lttng_field_statedump(session, ctx,
- nr_ctx_fields, &i, 2);
- if (ret) {
- break;
- }
- }
- return ret;
-}
-
-static
-int _lttng_fields_metadata_statedump(ust_registry_session *session,
- struct ust_registry_event *event)
-{
- int ret = 0;
- size_t i = 0;
-
- for (;;) {
- if (i >= event->nr_fields) {
- break;
- }
- ret = _lttng_field_statedump(session, event->fields,
- event->nr_fields, &i, 2);
- if (ret) {
- break;
- }
- }
- return ret;
-}
-
-/*
- * Should be called with session registry mutex held.
- */
-int ust_metadata_event_statedump(ust_registry_session *session,
- struct ust_registry_channel *chan,
- struct ust_registry_event *event)
-{
- int ret = 0;
-
- /* Don't dump metadata events */
- if (chan->chan_id == -1U)
- return 0;
-
- /*
- * We don't want to output an event's metadata before its parent
- * stream's metadata. If the stream's metadata hasn't been output yet,
- * skip this event. Its metadata will be output when we output the
- * stream's metadata.
- */
- if (!chan->metadata_dumped || event->metadata_dumped) {
- return 0;
- }
-
- ret = lttng_metadata_printf(session,
- "event {\n"
- " name = \"%s\";\n"
- " id = %u;\n"
- " stream_id = %u;\n",
- event->name,
- event->id,
- chan->chan_id);
- if (ret) {
- goto end;
- }
-
- ret = lttng_metadata_printf(session,
- " loglevel = %d;\n",
- event->loglevel_value);
- if (ret) {
- goto end;
- }
-
- if (event->model_emf_uri) {
- ret = lttng_metadata_printf(session,
- " model.emf.uri = \"%s\";\n",
- event->model_emf_uri);
- if (ret) {
- goto end;
- }
- }
-
- ret = lttng_metadata_printf(session,
- " fields := struct {\n"
- );
- if (ret) {
- goto end;
- }
-
- ret = _lttng_fields_metadata_statedump(session, event);
- if (ret) {
- goto end;
- }
-
- ret = lttng_metadata_printf(session,
- " };\n"
- "};\n\n");
- if (ret) {
- goto end;
- }
- event->metadata_dumped = 1;
-
-end:
- return ret;
-}
-
-/*
- * Should be called with session registry mutex held.
- *
- * RCU read lock must be held by the caller.
- */
-int ust_metadata_channel_statedump(ust_registry_session *session,
- struct ust_registry_channel *chan)
-{
- int ret;
-
- ASSERT_RCU_READ_LOCKED();
-
- /* Don't dump metadata events */
- if (chan->chan_id == -1U)
- return 0;
-
- if (!chan->header_type)
- return -EINVAL;
-
- ret = lttng_metadata_printf(session,
- "stream {\n"
- " id = %u;\n"
- " event.header := %s;\n"
- " packet.context := struct packet_context;\n",
- chan->chan_id,
- chan->header_type == LTTNG_UST_CTL_CHANNEL_HEADER_COMPACT ?
- "struct event_header_compact" :
- "struct event_header_large");
- if (ret) {
- return ret;
- }
-
- if (chan->ctx_fields) {
- ret = lttng_metadata_printf(session,
- " event.context := struct {\n");
- if (ret) {
- return ret;
- }
- }
- ret = _lttng_context_metadata_statedump(session,
- chan->nr_ctx_fields,
- chan->ctx_fields);
- if (ret) {
- return ret;
- }
- if (chan->ctx_fields) {
- ret = lttng_metadata_printf(session,
- " };\n");
- if (ret) {
- return ret;
- }
- }
-
- ret = lttng_metadata_printf(session,
- "};\n\n");
- if (ret) {
- return ret;
- }
-
- /* Flag success of metadata dump. */
- chan->metadata_dumped = 1;
-
- /*
- * Output the metadata of any existing event.
- *
- * Sort the events by id. This is not necessary, but it's nice to have
- * a more predictable order in the metadata file.
- */
- std::vector<ust_registry_event *> events;
- {
- cds_lfht_iter event_iter;
- ust_registry_event *event;
- cds_lfht_for_each_entry(chan->events->ht, &event_iter, event,
- node.node) {
- events.push_back(event);
- }
- }
-
- std::sort(events.begin(), events.end(),
- [] (ust_registry_event *a, ust_registry_event *b) {
- return a->id < b->id;
- });
-
- for (ust_registry_event *event : events) {
- ust_metadata_event_statedump(session, chan, event);
- }
-
- return 0;
-}
-
-static
-int _lttng_stream_packet_context_declare(ust_registry_session *session)
-{
- return lttng_metadata_printf(session,
- "struct packet_context {\n"
- " uint64_clock_monotonic_t timestamp_begin;\n"
- " uint64_clock_monotonic_t timestamp_end;\n"
- " uint64_t content_size;\n"
- " uint64_t packet_size;\n"
- " uint64_t packet_seq_num;\n"
- " unsigned long events_discarded;\n"
- " uint32_t cpu_id;\n"
- "};\n\n"
- );
-}
-
-/*
- * Compact header:
- * id: range: 0 - 30.
- * id 31 is reserved to indicate an extended header.
- *
- * Large header:
- * id: range: 0 - 65534.
- * id 65535 is reserved to indicate an extended header.
- */
-static
-int _lttng_event_header_declare(ust_registry_session *session)
-{
- return lttng_metadata_printf(session,
- "struct event_header_compact {\n"
- " enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n"
- " variant <id> {\n"
- " struct {\n"
- " uint27_clock_monotonic_t timestamp;\n"
- " } compact;\n"
- " struct {\n"
- " uint32_t id;\n"
- " uint64_clock_monotonic_t timestamp;\n"
- " } extended;\n"
- " } v;\n"
- "} align(%u);\n"
- "\n"
- "struct event_header_large {\n"
- " enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n"
- " variant <id> {\n"
- " struct {\n"
- " uint32_clock_monotonic_t timestamp;\n"
- " } compact;\n"
- " struct {\n"
- " uint32_t id;\n"
- " uint64_clock_monotonic_t timestamp;\n"
- " } extended;\n"
- " } v;\n"
- "} align(%u);\n\n",
- session->_uint32_t_alignment,
- session->_uint16_t_alignment
- );
-}
-
-static
-int print_metadata_session_information(ust_registry_session *registry)
-{
- int ret;
- struct ltt_session *session = NULL;
- char creation_datetime[ISO8601_STR_LEN];
-
- rcu_read_lock();
- session = session_find_by_id(registry->_tracing_id);
- if (!session) {
- ret = -1;
- goto error;
- }
-
- /* Print the trace name */
- ret = lttng_metadata_printf(registry, " trace_name = \"");
- if (ret) {
- goto error;
- }
-
- /*
- * This is necessary since the creation time is present in the session
- * name when it is generated.
- */
- if (session->has_auto_generated_name) {
- ret = print_escaped_ctf_string(registry, DEFAULT_SESSION_NAME);
- } else {
- ret = print_escaped_ctf_string(registry, session->name);
- }
- if (ret) {
- goto error;
- }
-
- ret = lttng_metadata_printf(registry, "\";\n");
- if (ret) {
- goto error;
- }
-
- /* Prepare creation time */
- ret = time_to_iso8601_str(session->creation_time, creation_datetime,
- sizeof(creation_datetime));
- if (ret) {
- goto error;
- }
-
- /* Output the reste of the information */
- ret = lttng_metadata_printf(registry,
- " trace_creation_datetime = \"%s\";\n"
- " hostname = \"%s\";\n",
- creation_datetime, session->hostname);
- if (ret) {
- goto error;
- }
-
-error:
- if (session) {
- session_put(session);
- }
- rcu_read_unlock();
- return ret;
-}
-
-static
-int print_metadata_app_information(ust_registry_session *registry)
-{
- if (registry->get_buffering_scheme() != LTTNG_BUFFER_PER_PID) {
- return 0;
- }
-
- const auto *per_pid_session = static_cast<const ust_registry_session_per_pid *>(registry);
-
- char datetime[ISO8601_STR_LEN];
- int ret = time_to_iso8601_str(
- per_pid_session->_app_creation_time, datetime, sizeof(datetime));
- if (ret) {
- return ret;
- }
-
- ret = lttng_metadata_printf(registry,
- " tracer_patchlevel = %u;\n"
- " vpid = %d;\n"
- " procname = \"%s\";\n"
- " vpid_datetime = \"%s\";\n",
- per_pid_session->_tracer_patch_level_version, (int) per_pid_session->_vpid,
- per_pid_session->_procname.c_str(), datetime);
- return ret;
-}
-
-/*
- * Should be called with session registry mutex held.
- */
-int ust_metadata_session_statedump(ust_registry_session *session)
-{
- int ret = 0;
- char trace_uuid_str[LTTNG_UUID_STR_LEN];
-
- LTTNG_ASSERT(session);
-
- lttng_uuid_to_str(session->_uuid, trace_uuid_str);
-
- /* For crash ABI */
- ret = lttng_metadata_printf(session,
- "/* CTF %u.%u */\n\n",
- CTF_SPEC_MAJOR,
- CTF_SPEC_MINOR);
- if (ret) {
- goto end;
- }
-
- ret = lttng_metadata_printf(session,
- "typealias integer { size = 8; align = %u; signed = false; } := uint8_t;\n"
- "typealias integer { size = 16; align = %u; signed = false; } := uint16_t;\n"
- "typealias integer { size = 32; align = %u; signed = false; } := uint32_t;\n"
- "typealias integer { size = 64; align = %u; signed = false; } := uint64_t;\n"
- "typealias integer { size = %u; align = %u; signed = false; } := unsigned long;\n"
- "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n"
- "typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\n"
- "\n"
- "trace {\n"
- " major = %u;\n"
- " minor = %u;\n"
- " uuid = \"%s\";\n"
- " byte_order = %s;\n"
- " packet.header := struct {\n"
- " uint32_t magic;\n"
- " uint8_t uuid[16];\n"
- " uint32_t stream_id;\n"
- " uint64_t stream_instance_id;\n"
- " };\n"
- "};\n\n",
- session->_uint8_t_alignment,
- session->_uint16_t_alignment,
- session->_uint32_t_alignment,
- session->_uint64_t_alignment,
- session->_bits_per_long,
- session->_long_alignment,
- CTF_SPEC_MAJOR,
- CTF_SPEC_MINOR,
- trace_uuid_str,
- session->_byte_order == BIG_ENDIAN ? "be" : "le"
- );
- if (ret) {
- goto end;
- }
-
- ret = lttng_metadata_printf(session,
- "env {\n"
- " domain = \"ust\";\n"
- " tracer_name = \"lttng-ust\";\n"
- " tracer_major = %u;\n"
- " tracer_minor = %u;\n"
- " tracer_buffering_scheme = \"%s\";\n"
- " tracer_buffering_id = %u;\n"
- " architecture_bit_width = %u;\n",
- session->_app_tracer_version_major, session->_app_tracer_version_minor,
- session->get_buffering_scheme() == LTTNG_BUFFER_PER_PID ? "pid" : "uid",
- session->get_buffering_scheme() == LTTNG_BUFFER_PER_PID ?
- (int) static_cast<ust_registry_session_per_pid *>(session)->_vpid :
- (int) static_cast<ust_registry_session_per_uid *>(session)->_tracing_uid,
- session->_bits_per_long);
- if (ret) {
- goto end;
- }
-
- ret = print_metadata_session_information(session);
- if (ret) {
- goto end;
- }
-
- /*
- * If per-application registry, we can output extra information
- * about the application.
- */
- ret = print_metadata_app_information(session);
- if (ret) {
- goto end;
- }
-
- ret = lttng_metadata_printf(session,
- "};\n\n"
- );
- if (ret) {
- goto end;
- }
-
- try {
- const lttng::ust::clock_attributes_sample clock;
-
- ret = lttng_metadata_printf(session,
- "clock {\n"
- " name = \"%s\";\n",
- clock._name.c_str());
- if (ret) {
- goto end;
- }
-
- if (clock._uuid) {
- char clock_uuid_str[LTTNG_UUID_STR_LEN];
-
- lttng_uuid_to_str(*clock._uuid, clock_uuid_str);
- ret = lttng_metadata_printf(
- session, " uuid = \"%s\";\n", clock_uuid_str);
- if (ret) {
- goto end;
- }
- }
-
- ret = lttng_metadata_printf(session,
- " description = \"%s\";\n"
- " freq = %" PRIu64 "; /* Frequency, in Hz */\n"
- " /* clock value offset from Epoch is: offset * (1/freq) */\n"
- " offset = %" PRId64 ";\n"
- "};\n\n",
- clock._description.c_str(), clock._frequency, clock._offset);
- if (ret) {
- goto end;
- }
-
- ret = lttng_metadata_printf(session,
- "typealias integer {\n"
- " size = 27; align = 1; signed = false;\n"
- " map = clock.%s.value;\n"
- "} := uint27_clock_monotonic_t;\n"
- "\n"
- "typealias integer {\n"
- " size = 32; align = %u; signed = false;\n"
- " map = clock.%s.value;\n"
- "} := uint32_clock_monotonic_t;\n"
- "\n"
- "typealias integer {\n"
- " size = 64; align = %u; signed = false;\n"
- " map = clock.%s.value;\n"
- "} := uint64_clock_monotonic_t;\n\n",
- clock._name.c_str(), session->_uint32_t_alignment,
- clock._name.c_str(), session->_uint64_t_alignment,
- clock._name.c_str());
- if (ret) {
- goto end;
- }
- } catch (const std::exception &ex) {
- ERR("Failed to serialize clock description: %s", ex.what());
- ret = -1;
- goto end;
- }
-
- ret = _lttng_stream_packet_context_declare(session);
- if (ret) {
- goto end;
- }
-
- ret = _lttng_event_header_declare(session);
- if (ret) {
- goto end;
- }
-
-end:
- return ret;
-}
+namespace ls = lttng::sessiond;
+namespace lsu = lttng::sessiond::ust;
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef LTTNG_UST_REGISTRY_CHANNEL_H
+#define LTTNG_UST_REGISTRY_CHANNEL_H
+
+#include "stream-class.hpp"
+
+#include <common/hashtable/hashtable.hpp>
+
+#include <lttng/lttng.h>
+
+#include <urcu.h>
+#include <functional>
+
+struct ust_app;
+
+namespace lttng {
+namespace sessiond {
+namespace ust {
+
+class registry_event;
+
+class registry_channel : public lttng::sessiond::trace::stream_class {
+public:
+ using registered_listener_fn = std::function<void(const registry_channel&)>;
+ using event_added_listener_fn = std::function<void(const registry_channel&, const registry_event &)>;
+
+ registry_channel(uint32_t channel_id,
+ registered_listener_fn channel_registered_listener,
+ event_added_listener_fn new_event_listener);
+ void add_event(int session_objd,
+ int channel_objd,
+ std::string name,
+ std::string signature,
+ std::vector<lttng::sessiond::trace::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);
+ virtual ~registry_channel();
+
+ virtual const lttng::sessiond::trace::type& get_context() const override final;
+ void set_context(lttng::sessiond::trace::type::cuptr context);
+
+ /* Channel was registered to at least one application. */
+ bool is_registered() const;
+ void set_as_registered();
+
+ uint64_t _key;
+ uint64_t _consumer_key;
+
+ /*
+ * Flag for this channel if the metadata was dumped once during
+ * registration. 0 means no, 1 yes.
+ */
+ unsigned int _metadata_dumped;
+
+ /*
+ * Hash table containing events sent by the UST tracer. MUST be accessed
+ * with a RCU read side lock acquired.
+ */
+ struct lttng_ht *_events;
+ struct lttng_ht_node_u64 _node;
+ /* For delayed reclaim */
+ struct rcu_head _rcu_head;
+ /* Once this value reaches UINT32_MAX, no more id can be allocated. */
+ uint32_t _next_event_id;
+
+private:
+ virtual void _accept_on_event_classes(
+ lttng::sessiond::trace::trace_class_visitor& trace_class_visitor) const override final;
+
+ registered_listener_fn _is_registered_listener;
+ event_added_listener_fn _event_added_listener;
+ /* Indicates if this channel registry has already been registered. */
+ bool _is_registered;
+};
+
+} /* namespace ust */
+} /* namespace sessiond */
+} /* namespace lttng */
+
+#endif /* LTTNG_UST_REGISTRY_CHANNEL_H */
--- /dev/null
+/*
+ * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "ust-registry-event.hpp"
+
+#include <common/make-unique.hpp>
+
+#include <urcu/rculfhash.h>
+
+namespace lst = lttng::sessiond::trace;
+namespace lsu = lttng::sessiond::ust;
+
+lsu::registry_event::registry_event(unsigned int in_id,
+ unsigned int in_stream_class_id,
+ int in_session_objd,
+ int in_channel_objd,
+ std::string in_name,
+ std::string in_signature,
+ std::vector<lttng::sessiond::trace::field::cuptr> in_fields,
+ int in_loglevel_value,
+ nonstd::optional<std::string> in_model_emf_uri) :
+ lst::event_class(in_id,
+ in_stream_class_id,
+ in_loglevel_value,
+ std::move(in_name),
+ std::move(in_model_emf_uri),
+ lttng::make_unique<lst::structure_type>(0, std::move(in_fields))),
+ session_objd{in_session_objd},
+ channel_objd{in_channel_objd},
+ signature{std::move(in_signature)},
+ _metadata_dumped{false}
+{
+ cds_lfht_node_init(&this->_node.node);
+}
+
+/*
+ * Free event data structure. This does NOT delete it from any hash table. It's
+ * safe to pass a NULL pointer. This should be called inside a call RCU if the
+ * event is previously deleted from a rcu hash table.
+ */
+void lsu::registry_event_destroy(lsu::registry_event *event)
+{
+ delete event;
+}
--- /dev/null
+/*
+ * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#ifndef LTTNG_UST_REGISTRY_EVENT_H
+#define LTTNG_UST_REGISTRY_EVENT_H
+
+#include "event-class.hpp"
+#include "field.hpp"
+
+#include <common/format.hpp>
+#include <common/hashtable/hashtable.hpp>
+#include <vendor/optional.hpp>
+
+#include <typeinfo>
+
+namespace lttng {
+namespace sessiond {
+namespace ust {
+
+/*
+ * Event registered from a UST tracer sent to the session daemon. This is
+ * indexed and matched by <event_name/signature>.
+ */
+class registry_event : public lttng::sessiond::trace::event_class {
+public:
+ registry_event(unsigned int id,
+ unsigned int stream_class_id,
+ int session_objd,
+ int channel_objd,
+ std::string name,
+ std::string signature,
+ std::vector<lttng::sessiond::trace::field::cuptr> fields,
+ int loglevel_value,
+ nonstd::optional<std::string> model_emf_uri);
+ virtual ~registry_event() = default;
+
+ /* Both objd are set by the tracer. */
+ const int session_objd;
+ const int channel_objd;
+ const std::string signature;
+
+ /*
+ * Flag for this channel if the metadata was dumped once during
+ * registration.
+ */
+ bool _metadata_dumped;
+
+ /*
+ * Node in the ust-registry hash table. The event name is used to
+ * initialize the node and the event_name/signature for the match function.
+ */
+ struct lttng_ht_node_u64 _node;
+};
+
+void registry_event_destroy(registry_event *event);
+
+} /* namespace ust */
+} /* namespace sessiond */
+} /* namespace lttng */
+
+template <>
+struct fmt::formatter<lttng::sessiond::ust::registry_event> : fmt::formatter<std::string> {
+ template <typename FormatCtx>
+ typename FormatCtx::iterator format(
+ const lttng::sessiond::ust::registry_event& event, FormatCtx& ctx)
+ {
+ return fmt::format_to(ctx.out(),
+ "{{ name = `{}`, signature = `{}`, id = {}, session objd = {}, channel objd = {} }}",
+ event.name, event.signature, event.id, event.session_objd,
+ event.channel_objd);
+ }
+};
+
+#endif /* LTTNG_UST_REGISTRY_EVENT_H */
#include "ust-app.hpp"
#include "ust-registry.hpp"
-ust_registry_session_per_pid::ust_registry_session_per_pid(const struct ust_app &app,
- uint32_t bits_per_long,
- uint32_t uint8_t_alignment,
- uint32_t uint16_t_alignment,
- uint32_t uint32_t_alignment,
- uint32_t uint64_t_alignment,
- uint32_t long_alignment,
- int byte_order,
+#include <common/exception.hpp>
+
+namespace lst = lttng::sessiond::trace;
+
+ust_registry_session_per_pid::ust_registry_session_per_pid(const struct ust_app& app,
+ const struct lst::abi& in_abi,
uint32_t major,
uint32_t minor,
const char *root_shm_path,
uid_t euid,
gid_t egid,
uint64_t tracing_id) :
- ust_registry_session{bits_per_long, uint8_t_alignment, uint16_t_alignment,
- uint32_t_alignment, uint64_t_alignment, long_alignment, byte_order, major,
- minor, root_shm_path, shm_path, euid, egid, tracing_id},
+ ust_registry_session{in_abi, major, minor, root_shm_path, shm_path, euid, egid, tracing_id},
_tracer_patch_level_version{app.version.patchlevel},
_vpid{app.pid},
_procname{app.name},
_app_creation_time{app.registration_time}
{
- statedump();
+ lttng::pthread::lock_guard registry_lock(_lock);
+ _generate_metadata();
}
lttng_buffer_type ust_registry_session_per_pid::get_buffering_scheme() const noexcept
{
return LTTNG_BUFFER_PER_PID;
}
+
+void ust_registry_session_per_pid::_visit_environment(lst::trace_class_visitor& visitor) const
+{
+ ust_registry_session::_visit_environment(visitor);
+ visitor.visit(lst::environment_field<int64_t>("tracer_buffering_id", _vpid));
+ visitor.visit(lst::environment_field<int64_t>(
+ "tracer_patchlevel", _tracer_patch_level_version));
+ visitor.visit(lst::environment_field<int64_t>("vpid", _vpid));
+ visitor.visit(lst::environment_field<std::string>("procname", _procname));
+ visitor.visit(lst::environment_field<std::string>(
+ "vpid_datetime", lttng::utils::time_to_iso8601_str(_app_creation_time)));
+}
#include "ust-registry.hpp"
-ust_registry_session_per_uid::ust_registry_session_per_uid(uint32_t bits_per_long,
- uint32_t uint8_t_alignment,
- uint32_t uint16_t_alignment,
- uint32_t uint32_t_alignment,
- uint32_t uint64_t_alignment,
- uint32_t long_alignment,
- int byte_order,
+namespace lst = lttng::sessiond::trace;
+
+ust_registry_session_per_uid::ust_registry_session_per_uid(
+ const struct lst::abi& in_abi,
uint32_t major,
uint32_t minor,
const char *root_shm_path,
gid_t egid,
uint64_t tracing_id,
uid_t tracing_uid) :
- ust_registry_session{bits_per_long, uint8_t_alignment, uint16_t_alignment,
- uint32_t_alignment, uint64_t_alignment, long_alignment, byte_order, major,
- minor, root_shm_path, shm_path, euid, egid, tracing_id},
+ ust_registry_session{in_abi, major, minor, root_shm_path, shm_path, euid, egid, tracing_id},
_tracing_uid{tracing_uid}
{
- statedump();
+ lttng::pthread::lock_guard registry_lock(_lock);
+ _generate_metadata();
}
lttng_buffer_type ust_registry_session_per_uid::get_buffering_scheme() const noexcept
{
return LTTNG_BUFFER_PER_UID;
}
+
+void ust_registry_session_per_uid::_visit_environment(lst::trace_class_visitor& visitor) const
+{
+ ust_registry_session::_visit_environment(visitor);
+ visitor.visit(lst::environment_field<int64_t>("tracer_buffering_id", _tracing_uid));
+}
*
*/
+#include "field.hpp"
+#include "lttng-sessiond.hpp"
+#include "notification-thread-commands.hpp"
+#include "session.hpp"
+#include "trace-class.hpp"
+#include "tsdl-trace-class-visitor.hpp"
+#include "ust-app.hpp"
+#include "ust-field-convert.hpp"
#include "ust-registry.hpp"
#include <common/compat/directory-handle.hpp>
#include <common/error.hpp>
#include <common/exception.hpp>
+#include <common/format.hpp>
#include <common/macros.hpp>
+#include <common/make-unique.hpp>
#include <common/pthread-lock.hpp>
#include <common/runas.hpp>
+#include <common/time.hpp>
+#include <common/urcu.hpp>
#include <fcntl.h>
+#include <functional>
+#include <mutex>
#include <sstream>
#include <string>
-ust_registry_session::ust_registry_session(uint32_t bits_per_long,
- uint32_t uint8_t_alignment,
- uint32_t uint16_t_alignment,
- uint32_t uint32_t_alignment,
- uint32_t uint64_t_alignment,
- uint32_t long_alignment,
- int byte_order,
+namespace ls = lttng::sessiond;
+namespace lst = lttng::sessiond::trace;
+namespace lsu = lttng::sessiond::ust;
+
+namespace {
+lttng_uuid generate_uuid_or_throw()
+{
+ lttng_uuid new_uuid;
+
+ if (lttng_uuid_generate(new_uuid)) {
+ LTTNG_THROW_POSIX("Failed to generate UST uuid", errno);
+ }
+
+ return new_uuid;
+}
+
+int get_count_order(unsigned int count)
+{
+ int order;
+
+ order = lttng_fls(count) - 1;
+ if (count & (count - 1)) {
+ order++;
+ }
+
+ LTTNG_ASSERT(order >= 0);
+ return order;
+}
+
+void clear_metadata_file(int fd)
+{
+ const auto lseek_ret = lseek(fd, 0, SEEK_SET);
+ if (lseek_ret < 0) {
+ LTTNG_THROW_POSIX("Failed to seek to the beginning of the metadata file while clearing it", errno);
+ }
+
+ const auto ret = ftruncate(fd, 0);
+ if (ret < 0) {
+ LTTNG_THROW_POSIX("Failed to truncate the metadata file while clearing it", errno);
+ }
+}
+
+/*
+ * Validate that the id has reached the maximum allowed or not.
+ */
+bool is_max_channel_id(uint32_t id)
+{
+ return id == UINT32_MAX;
+}
+
+void destroy_channel_rcu(struct rcu_head *head)
+{
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ lsu::registry_channel *chan =
+ caa_container_of(head, lsu::registry_channel, _rcu_head);
+ DIAGNOSTIC_POP
+
+ delete chan;
+}
+
+/*
+ * Destroy every element of the registry and free the memory. This does NOT
+ * free the registry pointer since it might not have been allocated before so
+ * it's the caller responsability.
+ */
+void destroy_channel(lsu::registry_channel *chan, bool notify)
+{
+ struct lttng_ht_iter iter;
+ lttng::sessiond::ust::registry_event *event;
+ enum lttng_error_code cmd_ret;
+
+ LTTNG_ASSERT(chan);
+
+ if (notify) {
+ cmd_ret = notification_thread_command_remove_channel(
+ the_notification_thread_handle,
+ chan->_consumer_key, LTTNG_DOMAIN_UST);
+ if (cmd_ret != LTTNG_OK) {
+ ERR("Failed to remove channel from notification thread");
+ }
+ }
+
+ if (chan->_events) {
+ lttng::urcu::read_lock_guard read_lock_guard;
+
+ /* Destroy all event associated with this registry. */
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ cds_lfht_for_each_entry(
+ chan->_events->ht, &iter.iter, event, _node.node) {
+ /* Delete the node from the ht and free it. */
+ ust_registry_channel_destroy_event(chan, event);
+ }
+ DIAGNOSTIC_POP
+ }
+
+ call_rcu(&chan->_rcu_head, destroy_channel_rcu);
+}
+} /* namespace */
+
+void ls::details::locked_ust_registry_session_release(ust_registry_session *session)
+{
+ pthread_mutex_unlock(&session->_lock);
+}
+
+ust_registry_session::ust_registry_session(const struct lst::abi& in_abi,
uint32_t major,
uint32_t minor,
const char *root_shm_path,
uid_t euid,
gid_t egid,
uint64_t tracing_id) :
- _bits_per_long{bits_per_long},
- _uint8_t_alignment{uint8_t_alignment},
- _uint16_t_alignment{uint16_t_alignment},
- _uint32_t_alignment{uint32_t_alignment},
- _uint64_t_alignment{uint64_t_alignment},
- _long_alignment{long_alignment},
- _byte_order{byte_order},
+ lst::trace_class(in_abi, generate_uuid_or_throw()),
_uid{euid},
_gid{egid},
_app_tracer_version_major{major},
_app_tracer_version_minor{minor},
- _tracing_id{tracing_id}
+ _tracing_id{tracing_id},
+ _metadata_generating_visitor{lttng::make_unique<ls::tsdl::trace_class_visitor>(
+ abi, [this](const std::string& fragment) {
+ _append_metadata_fragment(fragment);
+ })}
{
pthread_mutex_init(&_lock, NULL);
strncpy(_root_shm_path, root_shm_path, sizeof(_root_shm_path));
if (!_channels) {
LTTNG_THROW_POSIX("Failed to create channels hash table", ENOMEM);
}
-
- if (lttng_uuid_generate(_uuid)) {
- LTTNG_THROW_POSIX("Failed to generate UST uuid", errno);
- }
}
ust_registry_session::~ust_registry_session()
{
int ret;
struct lttng_ht_iter iter;
- struct ust_registry_channel *chan;
- struct ust_registry_enum *reg_enum;
+ lsu::registry_channel *chan;
+ lsu::registry_enum *reg_enum;
/* On error, EBUSY can be returned if lock. Code flow error. */
ret = pthread_mutex_destroy(&_lock);
LTTNG_ASSERT(!ret);
if (_channels) {
- rcu_read_lock();
+ lttng::urcu::read_lock_guard read_lock_guard;
+
/* Destroy all event associated with this registry. */
- cds_lfht_for_each_entry (_channels->ht, &iter.iter, chan, node.node) {
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ cds_lfht_for_each_entry(_channels->ht, &iter.iter, chan, _node.node) {
/* Delete the node from the ht and free it. */
ret = lttng_ht_del(_channels.get(), &iter);
LTTNG_ASSERT(!ret);
- ust_registry_channel_destroy(chan, true);
+ destroy_channel(chan, true);
}
-
- rcu_read_unlock();
+ DIAGNOSTIC_POP
}
free(_metadata);
if (_enums) {
rcu_read_lock();
/* Destroy all enum entries associated with this registry. */
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
cds_lfht_for_each_entry (_enums->ht, &iter.iter, reg_enum, node.node) {
ust_registry_destroy_enum(this, reg_enum);
}
+ DIAGNOSTIC_POP
rcu_read_unlock();
}
}
-void ust_registry_session::statedump()
+ust_registry_session::locked_ptr ust_registry_session::lock()
{
- lttng::pthread::lock_guard registry_lock(_lock);
+ pthread_mutex_lock(&_lock);
+ return locked_ptr(this);
+}
+
+/*
+ * Initialize registry with default values.
+ */
+void ust_registry_session::add_channel(uint64_t key)
+{
+ lttng::pthread::lock_guard session_lock_guard(_lock);
+
+ /*
+ * Assign a channel ID right now since the event notification comes
+ * *before* the channel notify so the ID needs to be set at this point so
+ * the metadata can be dumped for that event.
+ */
+ if (is_max_channel_id(_used_channel_id)) {
+ LTTNG_THROW_ERROR(fmt::format("Failed to allocate unique id for channel under session while adding channel"));
+ }
+
+ auto chan = new lsu::registry_channel(
+ _get_next_channel_id(),
+ /* Registered channel listener. */
+ [this](const lsu::registry_channel& registered_channel) {
+ /*
+ * Channel registration completed, serialize it's layout's
+ * description.
+ */
+ registered_channel.accept(*_metadata_generating_visitor);
+ },
+ /* Added event listener. */
+ [this](const lsu::registry_channel& channel,
+ const lsu::registry_event& added_event) {
+ /*
+ * The channel and its event classes will be dumped at once when
+ * it is registered. This check prevents event classes from being
+ * declared before their stream class.
+ */
+ if (channel.is_registered()) {
+ added_event.accept(*_metadata_generating_visitor);
+ }
+ });
+
+ lttng::urcu::read_lock_guard rcu_read_lock_guard;
+ lttng_ht_node_init_u64(&chan->_node, key);
+ lttng_ht_add_unique_u64(_channels.get(), &chan->_node);
+}
+
+lttng::sessiond::ust::registry_channel& ust_registry_session::get_channel(
+ uint64_t channel_key) const
+{
+ lttng::urcu::read_lock_guard read_lock_guard;
+ struct lttng_ht_node_u64 *node;
+ struct lttng_ht_iter iter;
+
+ ASSERT_LOCKED(_lock);
+
+ lttng_ht_lookup(_channels.get(), &channel_key, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
+ if (!node) {
+ LTTNG_THROW_INVALID_ARGUMENT_ERROR(fmt::format(
+ "Invalid channel key provided: channel key = {}", channel_key));
+ }
- const int ret = ust_metadata_session_statedump(this);
- if (ret) {
- LTTNG_THROW_ERROR(
- "Failed to generate session metadata during registry session creation");
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ auto chan = caa_container_of(node, lsu::registry_channel, _node);
+ DIAGNOSTIC_POP
+ return *chan;
+}
+
+void ust_registry_session::remove_channel(uint64_t channel_key, bool notify)
+{
+ struct lttng_ht_iter iter;
+ int ret;
+ lttng::urcu::read_lock_guard read_lock_guard;
+
+ ASSERT_LOCKED(_lock);
+ auto& channel = get_channel(channel_key);
+
+ iter.iter.node = &channel._node.node;
+ ret = lttng_ht_del(_channels.get(), &iter);
+ LTTNG_ASSERT(!ret);
+ destroy_channel(&channel, notify);
+}
+
+void ust_registry_session::_visit_environment(
+ lttng::sessiond::trace::trace_class_visitor& visitor) const
+{
+ ASSERT_LOCKED(_lock);
+
+ visitor.visit(lst::environment_field<const char *>("domain", "ust"));
+ visitor.visit(lst::environment_field<const char *>("tracer_name", "lttng-ust"));
+ visitor.visit(lst::environment_field<int64_t>("tracer_major", _app_tracer_version_major));
+ visitor.visit(lst::environment_field<int64_t>("tracer_minor", _app_tracer_version_minor));
+ visitor.visit(lst::environment_field<const char *>("tracer_buffering_scheme",
+ get_buffering_scheme() == LTTNG_BUFFER_PER_PID ? "pid" : "uid"));
+ visitor.visit(lst::environment_field<int64_t>("architecture_bit_width", abi.bits_per_long));
+
+ {
+ /* The caller already holds the session and session list locks. */
+ ASSERT_SESSION_LIST_LOCKED();
+ const auto session = lttng::sessiond::find_session_by_id(_tracing_id);
+
+ LTTNG_ASSERT(session);
+ ASSERT_LOCKED(session->lock);
+
+ visitor.visit(lst::environment_field<const char *>("trace_name",
+ session->has_auto_generated_name ? DEFAULT_SESSION_NAME :
+ session->name));
+ visitor.visit(lst::environment_field<std::string>("trace_creation_datetime",
+ lttng::utils::time_to_iso8601_str(session->creation_time)));
+ visitor.visit(lst::environment_field<const char *>("hostname", session->hostname));
}
}
+
+void ust_registry_session::_accept_on_clock_classes(lst::trace_class_visitor& visitor) const
+{
+ ASSERT_LOCKED(_lock);
+ _clock.accept(visitor);
+}
+
+void ust_registry_session::_accept_on_stream_classes(lst::trace_class_visitor& visitor) const
+{
+ ASSERT_LOCKED(_lock);
+
+ std::vector<const lttng::sessiond::ust::registry_channel *> sorted_stream_classes;
+
+ {
+ lttng::urcu::read_lock_guard rcu_lock_guard;
+ const lsu::registry_channel *channel;
+ lttng_ht_iter channel_it;
+
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ cds_lfht_for_each_entry(_channels->ht, &channel_it.iter, channel, _node.node) {
+ sorted_stream_classes.emplace_back(channel);
+ }
+ DIAGNOSTIC_POP
+ }
+
+ std::sort(sorted_stream_classes.begin(), sorted_stream_classes.end(),
+ [](const lttng::sessiond::ust::registry_channel *a,
+ const lttng::sessiond::ust::registry_channel *b) {
+ return a->id < b->id;
+ });
+
+ for (const auto stream_class : sorted_stream_classes) {
+ stream_class->accept(visitor);
+ }
+}
+
+/*
+ * Return next available channel id and increment the used counter. The
+ * is_max_channel_id function MUST be called before in order to validate
+ * if the maximum number of IDs have been reached. If not, it is safe to call
+ * this function.
+ *
+ * Return a unique channel ID. If max is reached, the used_channel_id counter
+ * is returned.
+ */
+uint32_t ust_registry_session::_get_next_channel_id()
+{
+ if (is_max_channel_id(_used_channel_id)) {
+ return _used_channel_id;
+ }
+
+ _used_channel_id++;
+ return _next_channel_id++;
+}
+
+void ust_registry_session::_increase_metadata_size(size_t reservation_length)
+{
+ const auto new_len = _metadata_len + reservation_length;
+ auto new_alloc_len = new_len;
+ const auto old_alloc_len = _metadata_alloc_len;
+
+ /* Rounding the new allocation length to the next power of 2 would overflow. */
+ if (new_alloc_len > (UINT32_MAX >> 1)) {
+ LTTNG_THROW_ERROR("Failed to reserve trace metadata storage as the new size would overflow");
+ }
+
+ /* 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 ust_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 ust_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 ust_registry_session::_generate_metadata()
+{
+ accept(*_metadata_generating_visitor);
+}
+
+void ust_registry_session::regenerate_metadata()
+{
+ lttng::pthread::lock_guard registry_lock(_lock);
+
+ _metadata_version++;
+ _reset_metadata();
+ _generate_metadata();
+}
*/
#define _LGPL_SOURCE
-#include <inttypes.h>
-
-#include <common/common.hpp>
-#include <common/hashtable/utils.hpp>
-#include <common/exception.hpp>
-#include <lttng/lttng.h>
#include "ust-registry.hpp"
-#include "ust-app.hpp"
-#include "ust-field-utils.hpp"
-#include "utils.hpp"
#include "lttng-sessiond.hpp"
#include "notification-thread-commands.hpp"
+#include "ust-app.hpp"
+#include "utils.hpp"
-/*
- * Hash table match function for event in the registry.
- */
-static int ht_match_event(struct cds_lfht_node *node, const void *_key)
-{
- const struct ust_registry_event *key;
- struct ust_registry_event *event;
-
- LTTNG_ASSERT(node);
- LTTNG_ASSERT(_key);
-
- event = caa_container_of(node, struct ust_registry_event, node.node);
- LTTNG_ASSERT(event);
- key = (ust_registry_event *) _key;
-
- /* It has to be a perfect match. First, compare the event names. */
- if (strncmp(event->name, key->name, sizeof(event->name))) {
- goto no_match;
- }
-
- /* Compare log levels. */
- if (event->loglevel_value != key->loglevel_value) {
- goto no_match;
- }
-
- /* Compare the arrays of fields. */
- if (!match_lttng_ust_ctl_field_array(event->fields, event->nr_fields,
- key->fields, key->nr_fields)) {
- goto no_match;
- }
-
- /* Compare model URI. */
- if (event->model_emf_uri != NULL && key->model_emf_uri == NULL) {
- goto no_match;
- } else if(event->model_emf_uri == NULL && key->model_emf_uri != NULL) {
- goto no_match;
- } else if (event->model_emf_uri != NULL && key->model_emf_uri != NULL) {
- if (strcmp(event->model_emf_uri, key->model_emf_uri)) {
- goto no_match;
- }
- }
-
- /* Match */
- return 1;
-
-no_match:
- return 0;
-}
-
-static unsigned long ht_hash_event(const void *_key, unsigned long seed)
-{
- uint64_t hashed_key;
- const struct ust_registry_event *key = (ust_registry_event *) _key;
-
- LTTNG_ASSERT(key);
-
- hashed_key = (uint64_t) hash_key_str(key->name, seed);
-
- return hash_key_u64(&hashed_key, seed);
-}
-
-static int compare_enums(const struct ust_registry_enum *reg_enum_a,
- const struct ust_registry_enum *reg_enum_b)
-{
- int ret = 0;
- size_t i;
-
- LTTNG_ASSERT(strcmp(reg_enum_a->name, reg_enum_b->name) == 0);
- if (reg_enum_a->nr_entries != reg_enum_b->nr_entries) {
- ret = -1;
- goto end;
- }
- for (i = 0; i < reg_enum_a->nr_entries; i++) {
- const struct lttng_ust_ctl_enum_entry *entries_a, *entries_b;
+#include <common/common.hpp>
+#include <common/exception.hpp>
+#include <common/format.hpp>
+#include <common/hashtable/utils.hpp>
+#include <common/make-unique-wrapper.hpp>
+#include <lttng/lttng.h>
- entries_a = ®_enum_a->entries[i];
- entries_b = ®_enum_b->entries[i];
- if (entries_a->start.value != entries_b->start.value) {
- ret = -1;
- goto end;
- }
- if (entries_a->end.value != entries_b->end.value) {
- ret = -1;
- goto end;
- }
- if (entries_a->start.signedness != entries_b->start.signedness) {
- ret = -1;
- goto end;
- }
- if (entries_a->end.signedness != entries_b->end.signedness) {
- ret = -1;
- goto end;
- }
+#include <inttypes.h>
- if (strcmp(entries_a->string, entries_b->string)) {
- ret = -1;
- goto end;
- }
- }
-end:
- return ret;
-}
+namespace ls = lttng::sessiond;
+namespace lst = lttng::sessiond::trace;
+namespace lsu = lttng::sessiond::ust;
/*
* Hash table match function for enumerations in the session. Match is
*/
static int ht_match_enum(struct cds_lfht_node *node, const void *_key)
{
- struct ust_registry_enum *_enum;
- const struct ust_registry_enum *key;
+ lsu::registry_enum *_enum;
+ const lsu::registry_enum *key;
LTTNG_ASSERT(node);
LTTNG_ASSERT(_key);
- _enum = caa_container_of(node, struct ust_registry_enum,
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ _enum = caa_container_of(node, lsu::registry_enum,
node.node);
- LTTNG_ASSERT(_enum);
- key = (ust_registry_enum *) _key;
+ DIAGNOSTIC_POP
- if (strncmp(_enum->name, key->name, LTTNG_UST_ABI_SYM_NAME_LEN)) {
- goto no_match;
- }
- if (compare_enums(_enum, key)) {
- goto no_match;
- }
-
- /* Match. */
- return 1;
+ LTTNG_ASSERT(_enum);
+ key = (lsu::registry_enum *) _key;
-no_match:
- return 0;
+ return *_enum == *key;
}
/*
*/
static int ht_match_enum_id(struct cds_lfht_node *node, const void *_key)
{
- struct ust_registry_enum *_enum;
- const struct ust_registry_enum *key = (ust_registry_enum *) _key;
+ lsu::registry_enum *_enum;
+ const lsu::registry_enum *key = (lsu::registry_enum *) _key;
LTTNG_ASSERT(node);
LTTNG_ASSERT(_key);
- _enum = caa_container_of(node, struct ust_registry_enum, node.node);
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ _enum = caa_container_of(node, lsu::registry_enum, node.node);
+ DIAGNOSTIC_POP
+
LTTNG_ASSERT(_enum);
if (_enum->id != key->id) {
*/
static unsigned long ht_hash_enum(void *_key, unsigned long seed)
{
- struct ust_registry_enum *key = (ust_registry_enum *) _key;
+ lsu::registry_enum *key = (lsu::registry_enum *) _key;
LTTNG_ASSERT(key);
- return hash_key_str(key->name, seed);
-}
-
-/*
- * Return negative value on error, 0 if OK.
- *
- * TODO: we could add stricter verification of more types to catch
- * errors in liblttng-ust implementation earlier than consumption by the
- * trace reader.
- */
-static
-int validate_event_field(struct lttng_ust_ctl_field *field,
- const char *event_name,
- struct ust_app *app)
-{
- int ret = 0;
-
- switch(field->type.atype) {
- case lttng_ust_ctl_atype_integer:
- case lttng_ust_ctl_atype_enum:
- case lttng_ust_ctl_atype_array:
- case lttng_ust_ctl_atype_sequence:
- case lttng_ust_ctl_atype_string:
- case lttng_ust_ctl_atype_variant:
- case lttng_ust_ctl_atype_array_nestable:
- case lttng_ust_ctl_atype_sequence_nestable:
- case lttng_ust_ctl_atype_enum_nestable:
- case lttng_ust_ctl_atype_variant_nestable:
- break;
- case lttng_ust_ctl_atype_struct:
- if (field->type.u.legacy._struct.nr_fields != 0) {
- WARN("Unsupported non-empty struct field.");
- ret = -EINVAL;
- goto end;
- }
- break;
- case lttng_ust_ctl_atype_struct_nestable:
- if (field->type.u.struct_nestable.nr_fields != 0) {
- WARN("Unsupported non-empty struct field.");
- ret = -EINVAL;
- goto end;
- }
- break;
-
- case lttng_ust_ctl_atype_float:
- switch (field->type.u._float.mant_dig) {
- case 0:
- WARN("UST application '%s' (pid: %d) has unknown float mantissa '%u' "
- "in field '%s', rejecting event '%s'",
- app->name, app->pid,
- field->type.u._float.mant_dig,
- field->name,
- event_name);
- ret = -EINVAL;
- goto end;
- default:
- break;
- }
- break;
-
- default:
- ret = -ENOENT;
- goto end;
- }
-end:
- return ret;
-}
-
-static
-int validate_event_fields(size_t nr_fields, struct lttng_ust_ctl_field *fields,
- const char *event_name, struct ust_app *app)
-{
- unsigned int i;
-
- for (i = 0; i < nr_fields; i++) {
- if (validate_event_field(&fields[i], event_name, app) < 0)
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * Allocate event and initialize it. This does NOT set a valid event id from a
- * registry.
- */
-static struct ust_registry_event *alloc_event(int session_objd,
- int channel_objd, char *name, char *sig, size_t nr_fields,
- struct lttng_ust_ctl_field *fields, int loglevel_value,
- char *model_emf_uri, struct ust_app *app)
-{
- struct ust_registry_event *event = NULL;
-
- /*
- * Ensure that the field content is valid.
- */
- if (validate_event_fields(nr_fields, fields, name, app) < 0) {
- return NULL;
- }
-
- event = zmalloc<ust_registry_event>();
- if (!event) {
- PERROR("zmalloc ust registry event");
- goto error;
- }
-
- event->session_objd = session_objd;
- event->channel_objd = channel_objd;
- /* Allocated by ustctl. */
- event->signature = sig;
- event->nr_fields = nr_fields;
- event->fields = fields;
- event->loglevel_value = loglevel_value;
- event->model_emf_uri = model_emf_uri;
- if (name) {
- /* Copy event name and force NULL byte. */
- strncpy(event->name, name, sizeof(event->name));
- event->name[sizeof(event->name) - 1] = '\0';
- }
- cds_lfht_node_init(&event->node.node);
-
-error:
- return event;
-}
-
-/*
- * Free event data structure. This does NOT delete it from any hash table. It's
- * safe to pass a NULL pointer. This should be called inside a call RCU if the
- * event is previously deleted from a rcu hash table.
- */
-static void destroy_event(struct ust_registry_event *event)
-{
- if (!event) {
- return;
- }
-
- free(event->fields);
- free(event->model_emf_uri);
- free(event->signature);
- free(event);
+ return hash_key_str(key->name.c_str(), seed);
}
/*
* Destroy event function call of the call RCU.
*/
-static void destroy_event_rcu(struct rcu_head *head)
-{
- struct lttng_ht_node_u64 *node =
- caa_container_of(head, struct lttng_ht_node_u64, head);
- struct ust_registry_event *event =
- caa_container_of(node, struct ust_registry_event, node);
-
- destroy_event(event);
-}
-
-/*
- * Find an event using the name and signature in the given registry. RCU read
- * side lock MUST be acquired before calling this function and as long as the
- * event reference is kept by the caller.
- *
- * On success, the event pointer is returned else NULL.
- */
-struct ust_registry_event *ust_registry_find_event(
- struct ust_registry_channel *chan, char *name, char *sig)
-{
- struct lttng_ht_node_u64 *node;
- struct lttng_ht_iter iter;
- struct ust_registry_event *event = NULL;
- struct ust_registry_event key;
-
- LTTNG_ASSERT(chan);
- LTTNG_ASSERT(name);
- LTTNG_ASSERT(sig);
- ASSERT_RCU_READ_LOCKED();
-
- /* Setup key for the match function. */
- strncpy(key.name, name, sizeof(key.name));
- key.name[sizeof(key.name) - 1] = '\0';
- key.signature = sig;
-
- cds_lfht_lookup(chan->events->ht, chan->events->hash_fct(&key, lttng_ht_seed),
- chan->events->match_fct, &key, &iter.iter);
- node = lttng_ht_iter_get_node_u64(&iter);
- if (!node) {
- goto end;
- }
- event = caa_container_of(node, struct ust_registry_event, node);
-
-end:
- return event;
-}
-
-/*
- * Create a ust_registry_event from the given parameters and add it to the
- * registry hash table. If event_id is valid, it is set with the newly created
- * event id.
- *
- * On success, return 0 else a negative value. The created event MUST be unique
- * so on duplicate entry -EINVAL is returned. On error, event_id is untouched.
- *
- * Should be called with session registry mutex held.
- */
-int ust_registry_create_event(ust_registry_session *session,
- uint64_t chan_key, int session_objd, int channel_objd, char *name,
- char *sig, size_t nr_fields, struct lttng_ust_ctl_field *fields,
- int loglevel_value, char *model_emf_uri, int buffer_type,
- uint32_t *event_id_p, struct ust_app *app)
+static void ust_registry_event_destroy_rcu(struct rcu_head *head)
{
- int ret;
- uint32_t event_id;
- struct cds_lfht_node *nptr;
- struct ust_registry_event *event = NULL;
- struct ust_registry_channel *chan;
-
- LTTNG_ASSERT(session);
- LTTNG_ASSERT(name);
- LTTNG_ASSERT(sig);
- LTTNG_ASSERT(event_id_p);
-
- rcu_read_lock();
-
- /*
- * 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 || channel_objd < 0) {
- ret = -EINVAL;
- goto error_free;
- }
-
- chan = ust_registry_channel_find(session, chan_key);
- if (!chan) {
- ret = -EINVAL;
- goto error_free;
- }
-
- /* Check if we've reached the maximum possible id. */
- if (ust_registry_is_max_id(chan->used_event_id)) {
- ret = -ENOENT;
- goto error_free;
- }
-
- event = alloc_event(session_objd, channel_objd, name, sig, nr_fields,
- fields, loglevel_value, model_emf_uri, app);
- if (!event) {
- ret = -ENOMEM;
- goto error_free;
- }
-
- DBG3("UST registry creating event with event: %s, sig: %s, id: %u, "
- "chan_objd: %u, sess_objd: %u, chan_id: %u", event->name,
- event->signature, event->id, event->channel_objd,
- event->session_objd, chan->chan_id);
-
- /*
- * 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(chan->events->ht, chan->events->hash_fct(event,
- lttng_ht_seed), chan->events->match_fct, event, &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 and make sure we destroy the previously allocated
- * event object.
- */
- destroy_event(event);
- event = caa_container_of(nptr, struct ust_registry_event,
- node.node);
- LTTNG_ASSERT(event);
- event_id = event->id;
- } else {
- ERR("UST registry create event add unique failed for event: %s, "
- "sig: %s, id: %u, chan_objd: %u, sess_objd: %u",
- event->name, event->signature, event->id,
- event->channel_objd, event->session_objd);
- ret = -EINVAL;
- goto error_unlock;
- }
- } else {
- /* Request next event id if the node was successfully added. */
- event_id = event->id = ust_registry_get_next_event_id(chan);
- }
-
- *event_id_p = event_id;
-
- if (!event->metadata_dumped) {
- /* Append to metadata */
- ret = ust_metadata_event_statedump(session, chan, event);
- if (ret) {
- ERR("Error appending event metadata (errno = %d)", ret);
- rcu_read_unlock();
- return ret;
- }
- }
-
- rcu_read_unlock();
- return 0;
-
-error_free:
- free(sig);
- free(fields);
- free(model_emf_uri);
-error_unlock:
- rcu_read_unlock();
- destroy_event(event);
- return ret;
+ struct lttng_ht_node_u64 *node = caa_container_of(head, struct lttng_ht_node_u64, head);
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ lttng::sessiond::ust::registry_event *event =
+ caa_container_of(node, lttng::sessiond::ust::registry_event, _node);
+ DIAGNOSTIC_POP
+
+ lttng::sessiond::ust::registry_event_destroy(event);
}
/*
* For a given event in a registry, delete the entry and destroy the event.
* This MUST be called within a RCU read side lock section.
*/
-void ust_registry_destroy_event(struct ust_registry_channel *chan,
- struct ust_registry_event *event)
+void ust_registry_channel_destroy_event(lsu::registry_channel *chan,
+ lttng::sessiond::ust::registry_event *event)
{
int ret;
struct lttng_ht_iter iter;
ASSERT_RCU_READ_LOCKED();
/* Delete the node first. */
- iter.iter.node = &event->node.node;
- ret = lttng_ht_del(chan->events, &iter);
+ iter.iter.node = &event->_node.node;
+ ret = lttng_ht_del(chan->_events, &iter);
LTTNG_ASSERT(!ret);
- call_rcu(&event->node.head, destroy_event_rcu);
+ call_rcu(&event->_node.head, ust_registry_event_destroy_rcu);
return;
}
-static void destroy_enum(struct ust_registry_enum *reg_enum)
+static void destroy_enum(lsu::registry_enum *reg_enum)
{
if (!reg_enum) {
return;
}
- free(reg_enum->entries);
- free(reg_enum);
+
+ delete reg_enum;
}
static void destroy_enum_rcu(struct rcu_head *head)
{
- struct ust_registry_enum *reg_enum =
- caa_container_of(head, struct ust_registry_enum, rcu_head);
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ lsu::registry_enum *reg_enum =
+ caa_container_of(head, lsu::registry_enum, rcu_head);
+ DIAGNOSTIC_POP
destroy_enum(reg_enum);
}
* Lookup enumeration by name and comparing enumeration entries.
* Needs to be called from RCU read-side critical section.
*/
-static struct ust_registry_enum *ust_registry_lookup_enum(
+static lsu::registry_enum *ust_registry_lookup_enum(
ust_registry_session *session,
- const struct ust_registry_enum *reg_enum_lookup)
+ const lsu::registry_enum *reg_enum_lookup)
{
- struct ust_registry_enum *reg_enum = NULL;
+ lsu::registry_enum *reg_enum = NULL;
struct lttng_ht_node_str *node;
struct lttng_ht_iter iter;
if (!node) {
goto end;
}
- reg_enum = caa_container_of(node, struct ust_registry_enum, node);
+
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ reg_enum = caa_container_of(node, lsu::registry_enum, node);
+ DIAGNOSTIC_POP
+
end:
return reg_enum;
}
/*
* Lookup enumeration by enum ID.
- * Needs to be called from RCU read-side critical section.
*/
-struct ust_registry_enum *
- ust_registry_lookup_enum_by_id(ust_registry_session *session,
+lsu::registry_enum::const_rcu_protected_reference
+ust_registry_lookup_enum_by_id(const ust_registry_session *session,
const char *enum_name, uint64_t enum_id)
{
- struct ust_registry_enum *reg_enum = NULL;
+ lsu::registry_enum *reg_enum = NULL;
struct lttng_ht_node_str *node;
struct lttng_ht_iter iter;
- struct ust_registry_enum reg_enum_lookup;
+ 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();
- memset(®_enum_lookup, 0, sizeof(reg_enum_lookup));
- strncpy(reg_enum_lookup.name, enum_name, LTTNG_UST_ABI_SYM_NAME_LEN);
- reg_enum_lookup.name[LTTNG_UST_ABI_SYM_NAME_LEN - 1] = '\0';
reg_enum_lookup.id = enum_id;
cds_lfht_lookup(session->_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_str(&iter);
if (!node) {
- goto end;
+ LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
+ "Unknown enumeration referenced by application event field: enum name = `{}`, enum id = {}",
+ enum_name, enum_id));
}
- reg_enum = caa_container_of(node, struct ust_registry_enum, node);
-end:
- return reg_enum;
+
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
+ reg_enum = caa_container_of(node, lsu::registry_enum, node);
+ DIAGNOSTIC_POP
+
+ return lsu::registry_enum::const_rcu_protected_reference{*reg_enum, std::move(rcu_lock)};
}
/*
- * Create a ust_registry_enum from the given parameters and add it to the
+ * Create a lsu::registry_enum from the given parameters and add it to the
* registry hash table, or find it if already there.
*
* On success, return 0 else a negative value.
*/
int ust_registry_create_or_find_enum(ust_registry_session *session,
int session_objd, char *enum_name,
- struct lttng_ust_ctl_enum_entry *entries, size_t nr_entries,
+ struct lttng_ust_ctl_enum_entry *raw_entries, size_t nr_entries,
uint64_t *enum_id)
{
int ret = 0;
struct cds_lfht_node *nodep;
- struct ust_registry_enum *reg_enum = NULL, *old_reg_enum;
+ lsu::registry_enum *reg_enum = NULL, *old_reg_enum;
+ auto entries = lttng::make_unique_wrapper<lttng_ust_ctl_enum_entry, lttng::free>(raw_entries);
LTTNG_ASSERT(session);
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) {
+ if (session_objd < 0 || nr_entries == 0 ||
+ lttng_strnlen(enum_name, LTTNG_UST_ABI_SYM_NAME_LEN) ==
+ LTTNG_UST_ABI_SYM_NAME_LEN) {
ret = -EINVAL;
goto end;
}
- /* Check if the enumeration was already dumped */
- reg_enum = zmalloc<ust_registry_enum>();
- if (!reg_enum) {
- PERROR("zmalloc ust registry enumeration");
+ try {
+ 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);
+ }
+ } catch (const std::exception& ex) {
+ ERR("Failed to create ust registry enumeration: %s", ex.what());
ret = -ENOMEM;
goto end;
}
- strncpy(reg_enum->name, enum_name, LTTNG_UST_ABI_SYM_NAME_LEN);
- reg_enum->name[LTTNG_UST_ABI_SYM_NAME_LEN - 1] = '\0';
- /* entries will be owned by reg_enum. */
- reg_enum->entries = entries;
- reg_enum->nr_entries = nr_entries;
- entries = NULL;
old_reg_enum = ust_registry_lookup_enum(session, reg_enum);
if (old_reg_enum) {
goto end;
}
reg_enum->id = session->_next_enum_id++;
- cds_lfht_node_init(®_enum->node.node);
nodep = cds_lfht_add_unique(session->_enums->ht,
ht_hash_enum(reg_enum, lttng_ht_seed),
ht_match_enum_id, reg_enum,
enum_name, reg_enum->id, session_objd);
*enum_id = reg_enum->id;
end:
- free(entries);
rcu_read_unlock();
return ret;
}
* This MUST be called within a RCU read side lock section.
*/
void ust_registry_destroy_enum(ust_registry_session *reg_session,
- struct ust_registry_enum *reg_enum)
+ lsu::registry_enum *reg_enum)
{
int ret;
struct lttng_ht_iter iter;
call_rcu(®_enum->rcu_head, destroy_enum_rcu);
}
-static
-void destroy_channel_rcu(struct rcu_head *head)
-{
- struct ust_registry_channel *chan =
- caa_container_of(head, struct ust_registry_channel, rcu_head);
-
- if (chan->events) {
- lttng_ht_destroy(chan->events);
- }
-
- free(chan->ctx_fields);
- free(chan);
-}
-
-/*
- * Destroy every element of the registry and free the memory. This does NOT
- * free the registry pointer since it might not have been allocated before so
- * it's the caller responsability.
- */
-void ust_registry_channel_destroy(struct ust_registry_channel *chan, bool notify)
-{
- struct lttng_ht_iter iter;
- struct ust_registry_event *event;
- enum lttng_error_code cmd_ret;
-
- LTTNG_ASSERT(chan);
-
- if (notify) {
- cmd_ret = notification_thread_command_remove_channel(
- the_notification_thread_handle,
- chan->consumer_key, LTTNG_DOMAIN_UST);
- if (cmd_ret != LTTNG_OK) {
- ERR("Failed to remove channel from notification thread");
- }
- }
-
- if (chan->events) {
- rcu_read_lock();
- /* Destroy all event associated with this registry. */
- cds_lfht_for_each_entry(
- chan->events->ht, &iter.iter, event, node.node) {
- /* Delete the node from the ht and free it. */
- ust_registry_destroy_event(chan, event);
- }
- rcu_read_unlock();
- }
- call_rcu(&chan->rcu_head, destroy_channel_rcu);
-}
-
-/*
- * Initialize registry with default values.
- */
-int ust_registry_channel_add(ust_registry_session *session,
- uint64_t key)
-{
- int ret = 0;
- struct ust_registry_channel *chan;
-
- LTTNG_ASSERT(session);
-
- chan = zmalloc<ust_registry_channel>();
- if (!chan) {
- PERROR("zmalloc ust registry channel");
- ret = -ENOMEM;
- goto error_alloc;
- }
-
- chan->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
- if (!chan->events) {
- ret = -ENOMEM;
- goto error;
- }
-
- /* Set custom match function. */
- chan->events->match_fct = ht_match_event;
- chan->events->hash_fct = ht_hash_event;
-
- /*
- * Assign a channel ID right now since the event notification comes
- * *before* the channel notify so the ID needs to be set at this point so
- * the metadata can be dumped for that event.
- */
- if (ust_registry_is_max_id(session->_used_channel_id)) {
- ret = -1;
- goto error;
- }
- chan->chan_id = ust_registry_get_next_chan_id(session);
-
- rcu_read_lock();
- lttng_ht_node_init_u64(&chan->node, key);
- lttng_ht_add_unique_u64(session->_channels.get(), &chan->node);
- rcu_read_unlock();
-
- return 0;
-
-error:
- ust_registry_channel_destroy(chan, false);
-error_alloc:
- return ret;
-}
-
-/*
- * Find a channel in the given registry. RCU read side lock MUST be acquired
- * before calling this function and as long as the event reference is kept by
- * the caller.
- *
- * On success, the pointer is returned else NULL.
- */
-struct ust_registry_channel *ust_registry_channel_find(
- ust_registry_session *session, uint64_t key)
-{
- struct lttng_ht_node_u64 *node;
- struct lttng_ht_iter iter;
- struct ust_registry_channel *chan = NULL;
-
- LTTNG_ASSERT(session);
- LTTNG_ASSERT(session->_channels);
- ASSERT_RCU_READ_LOCKED();
-
- DBG3("UST registry channel finding key %" PRIu64, key);
-
- lttng_ht_lookup(session->_channels.get(), &key, &iter);
- node = lttng_ht_iter_get_node_u64(&iter);
- if (!node) {
- goto end;
- }
- chan = caa_container_of(node, struct ust_registry_channel, node);
-
-end:
- return chan;
-}
-
-/*
- * Remove channel using key from registry and free memory.
- */
-void ust_registry_channel_del_free(ust_registry_session *session,
- uint64_t key, bool notif)
-{
- struct lttng_ht_iter iter;
- struct ust_registry_channel *chan;
- int ret;
-
- LTTNG_ASSERT(session);
-
- rcu_read_lock();
- chan = ust_registry_channel_find(session, key);
- if (!chan) {
- rcu_read_unlock();
- goto end;
- }
-
- iter.iter.node = &chan->node.node;
- ret = lttng_ht_del(session->_channels.get(), &iter);
- LTTNG_ASSERT(!ret);
- rcu_read_unlock();
- ust_registry_channel_destroy(chan, notif);
-
-end:
- return;
-}
-
-ust_registry_session *ust_registry_session_per_uid_create(uint32_t bits_per_long,
- uint32_t uint8_t_alignment,
- uint32_t uint16_t_alignment,
- uint32_t uint32_t_alignment,
- uint32_t uint64_t_alignment,
- uint32_t long_alignment,
- int byte_order,
+ust_registry_session *ust_registry_session_per_uid_create(const lttng::sessiond::trace::abi& abi,
uint32_t major,
uint32_t minor,
const char *root_shm_path,
uid_t tracing_uid)
{
try {
- return new ust_registry_session_per_uid(bits_per_long, uint8_t_alignment,
- uint16_t_alignment, uint32_t_alignment, uint64_t_alignment,
- long_alignment, byte_order, major, minor, root_shm_path, shm_path,
+ return new ust_registry_session_per_uid(abi, major, minor, root_shm_path, shm_path,
euid, egid, tracing_id, tracing_uid);
- } catch (const std::exception &ex) {
+ } catch (const std::exception& ex) {
ERR("Failed to create per-uid registry session: %s", ex.what());
return nullptr;
}
}
ust_registry_session *ust_registry_session_per_pid_create(struct ust_app *app,
- uint32_t bits_per_long,
- uint32_t uint8_t_alignment,
- uint32_t uint16_t_alignment,
- uint32_t uint32_t_alignment,
- uint32_t uint64_t_alignment,
- uint32_t long_alignment,
- int byte_order,
+ const lttng::sessiond::trace::abi& abi,
uint32_t major,
uint32_t minor,
const char *root_shm_path,
uint64_t tracing_id)
{
try {
- return new ust_registry_session_per_pid(*app, bits_per_long, uint8_t_alignment,
- uint16_t_alignment, uint32_t_alignment, uint64_t_alignment,
- long_alignment, byte_order, major, minor, root_shm_path, shm_path,
- euid, egid, tracing_id);
- } catch (const std::exception &ex) {
+ return new ust_registry_session_per_pid(*app, abi, major, minor, root_shm_path,
+ shm_path, euid, egid, tracing_id);
+ } catch (const std::exception& ex) {
ERR("Failed to create per-pid registry session: %s", ex.what());
return nullptr;
}
{
delete reg;
}
+
+lsu::registry_enum::registry_enum(
+ std::string in_name, enum lst::integer_type::signedness in_signedness) :
+ name{std::move(in_name)}, signedness{in_signedness}
+{
+ cds_lfht_node_init(&this->node.node);
+ this->rcu_head = {};
+}
+
+bool lsu::operator==(const lsu::registry_enum& lhs, const lsu::registry_enum& rhs) noexcept
+{
+ if (lhs.signedness != rhs.signedness) {
+ return false;
+ }
+
+ return lhs._is_equal(rhs);
+}
#ifndef LTTNG_UST_REGISTRY_H
#define LTTNG_UST_REGISTRY_H
-#include <pthread.h>
-#include <stdint.h>
-#include <ctime>
-#include <string>
-#include <memory>
-
+#include "event-class.hpp"
+#include "field.hpp"
+#include "lttng-ust-ctl.hpp"
+#include "session.hpp"
+#include "stream-class.hpp"
+#include "trace-class.hpp"
+#include "ust-clock-class.hpp"
+#include "ust-registry-channel.hpp"
+#include "ust-registry-event.hpp"
+
+#include <common/format.hpp>
#include <common/hashtable/hashtable.hpp>
+#include <common/locked-reference.hpp>
+#include <common/urcu.hpp>
#include <common/uuid.hpp>
#include <lttng/domain.h>
-#include "lttng-ust-ctl.hpp"
+#include <ctime>
+#include <memory>
+#include <pthread.h>
+#include <stdint.h>
+#include <string>
+#include <type_traits>
#define CTF_SPEC_MAJOR 1
#define CTF_SPEC_MINOR 8
struct ust_app;
+class ust_registry_session;
+
+namespace lttng {
+namespace sessiond {
+namespace details {
+void locked_ust_registry_session_release(ust_registry_session *session);
+} /* namespace details */
+} /* namespace sessiond */
+} /* namespace lttng */
-class ust_registry_session {
+class ust_registry_session : public lttng::sessiond::trace::trace_class {
public:
- virtual lttng_buffer_type get_buffering_scheme() const = 0;
- virtual ~ust_registry_session();
+ using locked_ptr = std::unique_ptr<ust_registry_session,
+ lttng::details::create_unique_class<ust_registry_session,
+ lttng::sessiond::details::locked_ust_registry_session_release>::
+ deleter>;
-protected:
- /* Prevent instanciation of this base class. */
- ust_registry_session(unsigned int bits_per_long,
- unsigned int uint8_t_alignment,
- unsigned int uint16_t_alignment,
- unsigned int uint32_t_alignment,
- unsigned int uint64_t_alignment,
- unsigned int long_alignment,
- int byte_order,
- unsigned int app_tracer_version_major,
- unsigned int app_tracer_version_minor,
- const char *root_shm_path,
- const char *shm_path,
- uid_t euid,
- gid_t egid,
- uint64_t tracing_id);
+ virtual lttng_buffer_type get_buffering_scheme() const noexcept = 0;
+ locked_ptr lock();
- void statedump();
+ void add_channel(uint64_t channel_key);
+ lttng::sessiond::ust::registry_channel& get_channel(uint64_t channel_key) const;
+ void remove_channel(uint64_t channel_key, bool notify);
+
+ void regenerate_metadata();
+ virtual ~ust_registry_session();
-public:
/*
* With multiple writers and readers, use this lock to access
* the registry. Can nest within the ust app session lock.
* sessiond to the consumerd.
* The consumer socket lock nests within this lock.
*/
- pthread_mutex_t _lock;
+ mutable pthread_mutex_t _lock;
/* Next channel ID available for a newly registered channel. */
uint32_t _next_channel_id = 0;
/* Once this value reaches UINT32_MAX, no more id can be allocated. */
uint32_t _used_channel_id = 0;
/* Next enumeration ID available. */
uint64_t _next_enum_id = 0;
- /* Universal unique identifier used by the tracer. */
- lttng_uuid _uuid = {};
-
- /* session ABI description */
-
- /* Size of long, in bits */
- unsigned int _bits_per_long;
- /* Alignment, in bits */
- unsigned int _uint8_t_alignment, _uint16_t_alignment, _uint32_t_alignment,
- _uint64_t_alignment, _long_alignment;
- /* endianness: BIG_ENDIAN or LITTLE_ENDIAN */
- int _byte_order;
/* Generated metadata. */
char *_metadata = nullptr; /* NOT null-terminated ! Use memcpy. */
uint32_t _app_tracer_version_minor = 0;
/* The id of the parent session */
- uint64_t _tracing_id = -1ULL;
+ ltt_session::id_t _tracing_id = -1ULL;
+
+protected:
+ /* Prevent instanciation of this base class. */
+ ust_registry_session(const struct lttng::sessiond::trace::abi& abi,
+ unsigned int app_tracer_version_major,
+ unsigned int app_tracer_version_minor,
+ const char *root_shm_path,
+ const char *shm_path,
+ uid_t euid,
+ gid_t egid,
+ uint64_t tracing_id);
+ virtual void _visit_environment(
+ lttng::sessiond::trace::trace_class_visitor& trace_class_visitor)
+ const override;
+ void _generate_metadata();
+
+private:
+ uint32_t _get_next_channel_id();
+ void _increase_metadata_size(size_t reservation_length);
+ void _append_metadata_fragment(const std::string& fragment);
+ void _reset_metadata();
+
+ virtual void _accept_on_clock_classes(
+ lttng::sessiond::trace::trace_class_visitor& trace_class_visitor)
+ const override final;
+ virtual void _accept_on_stream_classes(
+ lttng::sessiond::trace::trace_class_visitor& trace_class_visitor)
+ const override final;
+
+ lttng::sessiond::ust::clock_class _clock;
+ const lttng::sessiond::trace::trace_class_visitor::cuptr _metadata_generating_visitor;
};
class ust_registry_session_per_uid : public ust_registry_session {
public:
- ust_registry_session_per_uid(uint32_t bits_per_long,
- uint32_t uint8_t_alignment,
- uint32_t uint16_t_alignment,
- uint32_t uint32_t_alignment,
- uint32_t uint64_t_alignment,
- uint32_t long_alignment,
- int byte_order,
+ ust_registry_session_per_uid(const struct lttng::sessiond::trace::abi& trace_abi,
uint32_t major,
uint32_t minor,
const char *root_shm_path,
virtual lttng_buffer_type get_buffering_scheme() const noexcept override final;
+private:
+ virtual void _visit_environment(
+ lttng::sessiond::trace::trace_class_visitor& trace_class_visitor)
+ const override final;
+
const uid_t _tracing_uid;
};
class ust_registry_session_per_pid : public ust_registry_session {
public:
- ust_registry_session_per_pid(const struct ust_app &app,
- uint32_t bits_per_long,
- uint32_t uint8_t_alignment,
- uint32_t uint16_t_alignment,
- uint32_t uint32_t_alignment,
- uint32_t uint64_t_alignment,
- uint32_t long_alignment,
- int byte_order,
+ ust_registry_session_per_pid(const struct ust_app& app,
+ const struct lttng::sessiond::trace::abi&
+ trace_abi,
uint32_t major,
uint32_t minor,
const char *root_shm_path,
virtual lttng_buffer_type get_buffering_scheme() const noexcept override final;
- pid_t get_vpid() const
- {
- return _vpid;
- }
+private:
+ virtual void _visit_environment(
+ lttng::sessiond::trace::trace_class_visitor& trace_class_visitor)
+ const override final;
const unsigned int _tracer_patch_level_version;
const pid_t _vpid;
const std::time_t _app_creation_time;
};
-struct ust_registry_channel {
- uint64_t key;
- uint64_t consumer_key;
- /* Id set when replying to a register channel. */
- uint32_t chan_id;
- enum lttng_ust_ctl_channel_header header_type;
- /*
- * Flag for this channel if the metadata was dumped once during
- * registration. 0 means no, 1 yes.
- */
- unsigned int metadata_dumped;
- /* Indicates if this channel registry has already been registered. */
- unsigned int register_done;
+namespace lttng {
+namespace sessiond {
+namespace ust {
- /*
- * Hash table containing events sent by the UST tracer. MUST be accessed
- * with a RCU read side lock acquired.
- */
- struct lttng_ht *events;
- /* Next event ID available for a newly registered event. */
- uint32_t next_event_id;
- /* Once this value reaches UINT32_MAX, no more id can be allocated. */
- uint32_t used_event_id;
- /*
- * Context fields of the registry. Context are per channel. Allocated by a
- * register channel notification from the UST tracer.
- */
- size_t nr_ctx_fields;
- struct lttng_ust_ctl_field *ctx_fields;
- struct lttng_ht_node_u64 node;
- /* For delayed reclaim */
- struct rcu_head rcu_head;
-};
+class registry_enum {
+public:
+ using const_rcu_protected_reference = lttng::locked_reference<const registry_enum, lttng::urcu::unique_read_lock>;
-/*
- * Event registered from a UST tracer sent to the session daemon. This is
- * indexed and matched by <event_name/signature>.
- */
-struct ust_registry_event {
- int id;
- /* Both objd are set by the tracer. */
- int session_objd;
- int channel_objd;
- /* Name of the event returned by the tracer. */
- char name[LTTNG_UST_ABI_SYM_NAME_LEN];
- char *signature;
- int loglevel_value;
- size_t nr_fields;
- struct lttng_ust_ctl_field *fields;
- char *model_emf_uri;
- /*
- * Flag for this channel if the metadata was dumped once during
- * registration. 0 means no, 1 yes.
- */
- unsigned int metadata_dumped;
- /*
- * Node in the ust-registry hash table. The event name is used to
- * initialize the node and the event_name/signature for the match function.
- */
- struct lttng_ht_node_u64 node;
-};
+ registry_enum(std::string name, enum lttng::sessiond::trace::integer_type::signedness signedness);
+ virtual ~registry_enum() = default;
-struct ust_registry_enum {
- char name[LTTNG_UST_ABI_SYM_NAME_LEN];
- struct lttng_ust_ctl_enum_entry *entries;
- size_t nr_entries;
- uint64_t id; /* enum id in session */
+ std::string name;
+ enum lttng::sessiond::trace::integer_type::signedness signedness;
+ /* enum id in session */
+ uint64_t id = -1ULL;
/* Enumeration node in session hash table. */
struct lttng_ht_node_str node;
/* For delayed reclaim. */
struct rcu_head rcu_head;
+
+ friend bool operator==(const registry_enum& lhs, const registry_enum& rhs) noexcept;
+protected:
+ virtual bool _is_equal(const registry_enum& other) const noexcept = 0;
};
-/*
- * Validate that the id has reached the maximum allowed or not.
- *
- * Return 0 if NOT else 1.
- */
-static inline int ust_registry_is_max_id(uint32_t id)
+bool operator==(const registry_enum& lhs, const registry_enum& rhs) noexcept;
+
+namespace details {
+template <class MappingIntegerType>
+typename trace::typed_enumeration_type<MappingIntegerType>::mapping mapping_from_ust_ctl_entry(
+ const lttng_ust_ctl_enum_entry& entry)
{
- return (id == UINT32_MAX) ? 1 : 0;
+ if (entry.u.extra.options & LTTNG_UST_CTL_UST_ENUM_ENTRY_OPTION_IS_AUTO) {
+ return {entry.string};
+
+ } else {
+ return {entry.string,
+ {(MappingIntegerType) entry.start.value,
+ (MappingIntegerType) entry.end.value}};
+ }
}
-/*
- * Return next available event id and increment the used counter. The
- * ust_registry_is_max_id function MUST be called before in order to validate
- * if the maximum number of IDs have been reached. If not, it is safe to call
- * this function.
- *
- * Return a unique channel ID. If max is reached, the used_event_id counter is
- * returned.
- */
-static inline uint32_t ust_registry_get_next_event_id(
- struct ust_registry_channel *r)
+template <class MappingIntegerType>
+typename trace::typed_enumeration_type<MappingIntegerType>::mappings mappings_from_ust_ctl_entries(
+ const lttng_ust_ctl_enum_entry *in_entries, size_t in_entry_count)
{
- if (ust_registry_is_max_id(r->used_event_id)) {
- return r->used_event_id;
+ typename trace::typed_enumeration_type<MappingIntegerType>::mappings mappings;
+
+ for (size_t entry_idx = 0; entry_idx < in_entry_count; entry_idx++) {
+ const auto& entry = in_entries[entry_idx];
+
+ mappings.emplace_back(mapping_from_ust_ctl_entry<MappingIntegerType>(entry));
}
- r->used_event_id++;
- return r->next_event_id++;
+ return mappings;
}
+} /* namespace details */
-/*
- * Return next available channel id and increment the used counter. The
- * ust_registry_is_max_id function MUST be called before in order to validate
- * if the maximum number of IDs have been reached. If not, it is safe to call
- * this function.
- *
- * Return a unique channel ID. If max is reached, the used_channel_id counter
- * is returned.
- */
-static inline uint32_t ust_registry_get_next_chan_id(
- ust_registry_session *r)
-{
- if (ust_registry_is_max_id(r->_used_channel_id)) {
- return r->_used_channel_id;
+template <class MappingIntegerType>
+class registry_typed_enum : public registry_enum {
+public:
+ registry_typed_enum(const char *in_name,
+ const lttng_ust_ctl_enum_entry *entries,
+ size_t entry_count) :
+ registry_enum(in_name,
+ std::is_signed<MappingIntegerType>::value ?
+ lttng::sessiond::trace::integer_type::signedness::SIGNED :
+ lttng::sessiond::trace::integer_type::signedness::UNSIGNED),
+ _mappings{std::make_shared<
+ typename trace::typed_enumeration_type<MappingIntegerType>::mappings>(
+ details::mappings_from_ust_ctl_entries<MappingIntegerType>(
+ entries, entry_count))}
+ {
}
- r->_used_channel_id++;
- return r->_next_channel_id++;
-}
+ const typename std::shared_ptr<const typename lttng::sessiond::trace::typed_enumeration_type<
+ MappingIntegerType>::mappings>
+ _mappings;
-/*
- * Return registry event count. This is read atomically.
- */
-static inline uint32_t ust_registry_get_event_count(
- struct ust_registry_channel *r)
-{
- return (uint32_t) uatomic_read(&r->used_event_id);
-}
+protected:
+ virtual bool _is_equal(const registry_enum& base_other) const noexcept
+ {
+ const auto &other = static_cast<decltype(*this)&>(base_other);
-#ifdef HAVE_LIBLTTNG_UST_CTL
+ /* Don't compare IDs as some comparisons are performed before an id is assigned. */
+ return this->name == other.name && *this->_mappings == *other._mappings;
+ }
+};
-void ust_registry_channel_destroy(ust_registry_session *session,
- struct ust_registry_channel *chan);
-struct ust_registry_channel *ust_registry_channel_find(
- ust_registry_session *session, uint64_t key);
-int ust_registry_channel_add(ust_registry_session *session,
- uint64_t key);
-void ust_registry_channel_del_free(ust_registry_session *session,
- uint64_t key, bool notif);
-void ust_registry_channel_destroy(struct ust_registry_channel *chan, bool notify);
+using registry_signed_enum = registry_typed_enum<int64_t>;
+using registry_unsigned_enum = registry_typed_enum<uint64_t>;
+
+} /* namespace ust */
+} /* namespace sessiond */
+} /* namespace lttng */
+
+#ifdef HAVE_LIBLTTNG_UST_CTL
/*
* Create per-uid registry with default values.
* Return new instance on success, nullptr on error.
*/
ust_registry_session *ust_registry_session_per_uid_create(
- uint32_t bits_per_long,
- uint32_t uint8_t_alignment,
- uint32_t uint16_t_alignment,
- uint32_t uint32_t_alignment,
- uint32_t uint64_t_alignment,
- uint32_t long_alignment,
- int byte_order,
+ const lttng::sessiond::trace::abi& abi,
uint32_t major,
uint32_t minor,
const char *root_shm_path,
* Return new instance on success, nullptr on error.
*/
ust_registry_session *ust_registry_session_per_pid_create(struct ust_app *app,
- uint32_t bits_per_long,
- uint32_t uint8_t_alignment,
- uint32_t uint16_t_alignment,
- uint32_t uint32_t_alignment,
- uint32_t uint64_t_alignment,
- uint32_t long_alignment,
- int byte_order,
+ const lttng::sessiond::trace::abi& abi,
uint32_t major,
uint32_t minor,
const char *root_shm_path,
uint64_t tracing_id);
void ust_registry_session_destroy(ust_registry_session *session);
-int ust_registry_create_event(ust_registry_session *session,
- uint64_t chan_key, int session_objd, int channel_objd, char *name,
- char *sig, size_t nr_fields, struct lttng_ust_ctl_field *fields,
- int loglevel_value, char *model_emf_uri, int buffer_type,
- uint32_t *event_id_p, struct ust_app *app);
-struct ust_registry_event *ust_registry_find_event(
- struct ust_registry_channel *chan, char *name, char *sig);
-void ust_registry_destroy_event(struct ust_registry_channel *chan,
- struct ust_registry_event *event);
-
-/* app can be NULL for registry shared across applications. */
-int ust_metadata_session_statedump(ust_registry_session *session);
-int ust_metadata_channel_statedump(ust_registry_session *session,
- struct ust_registry_channel *chan);
-int ust_metadata_event_statedump(ust_registry_session *session,
- struct ust_registry_channel *chan,
- struct ust_registry_event *event);
+void ust_registry_channel_destroy_event(lttng::sessiond::ust::registry_channel *chan,
+ lttng::sessiond::ust::registry_event *event);
+
int ust_registry_create_or_find_enum(ust_registry_session *session,
int session_objd, char *name,
struct lttng_ust_ctl_enum_entry *entries, size_t nr_entries,
uint64_t *enum_id);
-struct ust_registry_enum *
- ust_registry_lookup_enum_by_id(ust_registry_session *session,
+lttng::sessiond::ust::registry_enum::const_rcu_protected_reference
+ust_registry_lookup_enum_by_id(const ust_registry_session *session,
const char *name, uint64_t id);
void ust_registry_destroy_enum(ust_registry_session *reg_session,
- struct ust_registry_enum *reg_enum);
-
+ lttng::sessiond::ust::registry_enum *reg_enum);
#else /* HAVE_LIBLTTNG_UST_CTL */
-static inline void ust_registry_channel_destroy(
- struct ust_registry_channel *chan __attribute__((unused)),
- bool notify __attribute__((unused)))
-{
-}
-
-static inline
-struct ust_registry_channel *ust_registry_channel_find(
- ust_registry_session *session __attribute__((unused)),
- uint64_t key __attribute__((unused)))
-{
- return NULL;
-}
-
-static inline
-int ust_registry_channel_add(
- ust_registry_session *session __attribute__((unused)),
- uint64_t key __attribute__((unused)))
-{
- return 0;
-}
-
-static inline
-void ust_registry_channel_del_free(
- ust_registry_session *session __attribute__((unused)),
- uint64_t key __attribute__((unused)),
- bool notif __attribute__((unused)))
-{}
-
static inline
ust_registry_session *ust_registry_session_per_uid_create(
uint32_t bits_per_long __attribute__((unused)),
ust_registry_session *session __attribute__((unused)))
{}
-static inline
-int ust_registry_create_event(
- ust_registry_session *session __attribute__((unused)),
- uint64_t chan_key __attribute__((unused)),
- int session_objd __attribute__((unused)),
- int channel_objd __attribute__((unused)),
- char *name __attribute__((unused)),
- char *sig __attribute__((unused)),
- size_t nr_fields __attribute__((unused)),
- struct lttng_ust_ctl_field *fields __attribute__((unused)),
- int loglevel_value __attribute__((unused)),
- char *model_emf_uri __attribute__((unused)),
- int buffer_type __attribute__((unused)),
- uint32_t *event_id_p __attribute__((unused)))
-{
- return 0;
-}
-static inline
-struct ust_registry_event *ust_registry_find_event(
- struct ust_registry_channel *chan __attribute__((unused)),
- char *name __attribute__((unused)),
- char *sig __attribute__((unused)))
-{
- return NULL;
-}
-
static inline
void ust_registry_destroy_event(
- struct ust_registry_channel *chan __attribute__((unused)),
- struct ust_registry_event *event __attribute__((unused)))
+ lttng::sessiond::ust::registry_channel *chan __attribute__((unused)),
+ lttng::sessiond::ust::registry_event *event __attribute__((unused)))
{}
/* The app object can be NULL for registry shared across applications. */
static inline
int ust_metadata_channel_statedump(
ust_registry_session *session __attribute__((unused)),
- struct ust_registry_channel *chan __attribute__((unused)))
+ lttng::sessiond::ust::registry_channel *chan __attribute__((unused)))
{
return 0;
}
static inline
int ust_metadata_event_statedump(
ust_registry_session *session __attribute__((unused)),
- struct ust_registry_channel *chan __attribute__((unused)),
- struct ust_registry_event *event __attribute__((unused)))
+ lttng::sessiond::ust::registry_channel *chan __attribute__((unused)),
+ lttng::sessiond::ust::registry_event *event __attribute__((unused)))
{
return 0;
}
static inline
struct ust_registry_enum *
ust_registry_lookup_enum_by_id(
- ust_registry_session *session __attribute__((unused)),
+ const ust_registry_session *session __attribute__((unused)),
const char *name __attribute__((unused)),
uint64_t id __attribute__((unused)))
{
std::lock_guard<details::read_lock> _guard;
};
+using unique_read_lock = std::unique_lock<details::read_lock>;
+
} /* namespace urcu */
} /* namespace lttng */