From: Jérémie Galarneau Date: Mon, 13 Jun 2022 16:01:21 +0000 (-0400) Subject: sessiond: transition from lttng-ust to tracer agnostic API X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=d7bfb9b0fa35679d3e728b9165699d9faf905539;p=lttng-tools.git sessiond: transition from lttng-ust to tracer agnostic API Refactor the session daemon's user space tracer management to use the tracer-agnostic trace hierarchy description API. Since the API introduced under lttng::sessiond::trace is closer to idiomatic C++, some changes are needed to make use of it. The biggest changes make the ust_registry* structures inherit from the trace descriptions classes (trace_class, stream_class, event_class, clock_class). This effectively isolates the members of the `ust_registry` structures that describe their corresponding CTF class to a base class and leaves only the implementation guts in the `ust_registry` objects (moved under lttng::sessiond::ust). Since the generation of TSDL metadata directly used the lttng_ust_ctl API, it is replaced by a new implementation that is based around a trace class visitor that serializes the tracer agnostic trace description objects. The TSDL environment visitor is moved under the TSDL implementation of the trace class visitor. Some little changes are also made to the existing code to make it exception-safe, as needed. Signed-off-by: Jérémie Galarneau Change-Id: I6562ef12e85afa91780337e8c6fdbb13568d9ece --- diff --git a/src/bin/lttng-sessiond/Makefile.am b/src/bin/lttng-sessiond/Makefile.am index 78e769ef6..86e356752 100644 --- a/src/bin/lttng-sessiond/Makefile.am +++ b/src/bin/lttng-sessiond/Makefile.am @@ -67,14 +67,16 @@ liblttng_sessiond_common_la_SOURCES = utils.cpp utils.hpp \ 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. diff --git a/src/bin/lttng-sessiond/cmd.cpp b/src/bin/lttng-sessiond/cmd.cpp index 659713c23..fe9656a89 100644 --- a/src/bin/lttng-sessiond/cmd.cpp +++ b/src/bin/lttng-sessiond/cmd.cpp @@ -74,6 +74,8 @@ /* 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 { @@ -4296,101 +4298,6 @@ end: 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. * @@ -4421,7 +4328,7 @@ int cmd_regenerate_metadata(struct ltt_session *session) } 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; diff --git a/src/bin/lttng-sessiond/field.cpp b/src/bin/lttng-sessiond/field.cpp index 25f0e4676..66087cfae 100644 --- a/src/bin/lttng-sessiond/field.cpp +++ b/src/bin/lttng-sessiond/field.cpp @@ -10,6 +10,8 @@ #include #include +#include + namespace lst = lttng::sessiond::trace; namespace { @@ -111,19 +113,16 @@ lst::floating_point_type::floating_point_type(unsigned int in_alignment, mantissa_digits(in_mantissa_digits) { /* Allowed (exponent, mantissa) pairs. */ - static const std::vector> allowed_pairs{ + static const std::set> 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( @@ -317,4 +316,4 @@ bool lst::variant_type::_is_equal(const type& base_other) const noexcept void lst::variant_type::accept(type_visitor& visitor) const { visitor.visit(*this); -} \ No newline at end of file +} diff --git a/src/bin/lttng-sessiond/field.hpp b/src/bin/lttng-sessiond/field.hpp index bad66a39a..23c67b04c 100644 --- a/src/bin/lttng-sessiond/field.hpp +++ b/src/bin/lttng-sessiond/field.hpp @@ -221,7 +221,7 @@ class array_type : public type { 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; diff --git a/src/bin/lttng-sessiond/session.hpp b/src/bin/lttng-sessiond/session.hpp index 17f3ca139..12ac3eb73 100644 --- a/src/bin/lttng-sessiond/session.hpp +++ b/src/bin/lttng-sessiond/session.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/src/bin/lttng-sessiond/trace-ust.cpp b/src/bin/lttng-sessiond/trace-ust.cpp index de368a3ba..1a98d3ff7 100644 --- a/src/bin/lttng-sessiond/trace-ust.cpp +++ b/src/bin/lttng-sessiond/trace-ust.cpp @@ -1353,6 +1353,33 @@ void trace_ust_delete_channel(struct lttng_ht *ht, 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. */ diff --git a/src/bin/lttng-sessiond/trace-ust.hpp b/src/bin/lttng-sessiond/trace-ust.hpp index 99995373a..38662ec58 100644 --- a/src/bin/lttng-sessiond/trace-ust.hpp +++ b/src/bin/lttng-sessiond/trace-ust.hpp @@ -209,6 +209,8 @@ int trace_ust_match_context(const struct ltt_ust_context *uctx, 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. @@ -348,6 +350,12 @@ void trace_ust_delete_channel( 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)), diff --git a/src/bin/lttng-sessiond/tsdl-trace-class-visitor.cpp b/src/bin/lttng-sessiond/tsdl-trace-class-visitor.cpp new file mode 100644 index 000000000..733c05a45 --- /dev/null +++ b/src/bin/lttng-sessiond/tsdl-trace-class-visitor.cpp @@ -0,0 +1,623 @@ +/* + * Copyright (C) 2022 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "clock-class.hpp" +#include "tsdl-trace-class-visitor.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +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 + 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(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(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( + 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( + 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 _current_integer_encoding_override; + + unsigned int _indentation_level; + const lst::abi& _trace_abi; + + std::queue _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 {{\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 {{\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(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(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& field) +{ + _environment += fmt::format(" {} = {};\n", field.name, field.value); +} + +void tsdl::trace_class_visitor::visit( + const lttng::sessiond::trace::environment_field& 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(); +} diff --git a/src/bin/lttng-sessiond/tsdl-trace-class-visitor.hpp b/src/bin/lttng-sessiond/tsdl-trace-class-visitor.hpp new file mode 100644 index 000000000..6d629e2be --- /dev/null +++ b/src/bin/lttng-sessiond/tsdl-trace-class-visitor.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 Jérémie Galarneau + * + * 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 + +#include + +namespace lttng { +namespace sessiond { +namespace tsdl { + +using append_metadata_fragment_function = std::function; + +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& field) override final; + virtual void visit(const lttng::sessiond::trace::environment_field& 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 */ diff --git a/src/bin/lttng-sessiond/ust-app.cpp b/src/bin/lttng-sessiond/ust-app.cpp index b4192398b..dd51d61f2 100644 --- a/src/bin/lttng-sessiond/ust-app.cpp +++ b/src/bin/lttng-sessiond/ust-app.cpp @@ -7,10 +7,48 @@ */ #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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + #include #include #include #include +#include #include #include #include @@ -19,37 +57,10 @@ #include #include #include -#include +#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; @@ -66,6 +77,63 @@ static pthread_mutex_t next_channel_key_lock = PTHREAD_MUTEX_INITIALIZER; 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. */ @@ -233,49 +301,6 @@ static void close_notify_sock_rcu(struct rcu_head *head) 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. @@ -534,16 +559,16 @@ end: * * 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(); @@ -574,11 +599,14 @@ void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan, 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 @@ -657,8 +685,9 @@ int ust_app_release_object(struct ust_app *app, struct lttng_ust_abi_object_data * 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; @@ -666,11 +695,11 @@ ssize_t ust_app_push_metadata(ust_registry_session *registry, 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 @@ -680,13 +709,13 @@ ssize_t ust_app_push_metadata(ust_registry_session *registry, 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"); @@ -703,10 +732,10 @@ ssize_t ust_app_push_metadata(ust_registry_session *registry, 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 @@ -721,7 +750,7 @@ push_data: */ 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 @@ -758,8 +787,8 @@ push_data: * 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); @@ -775,7 +804,7 @@ error: * the metadata cache has been destroyed on the * consumer. */ - registry->_metadata_closed = true; + locked_registry->_metadata_closed = true; } error_push: free(metadata_str); @@ -796,41 +825,38 @@ error_push: * 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; } @@ -846,39 +872,17 @@ error: * * 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; @@ -891,7 +895,6 @@ static int close_metadata(ust_registry_session *registry, } end: - rcu_read_unlock(); return ret; } @@ -918,7 +921,6 @@ void delete_ust_app_session(int sock, struct ust_app_session *ua_sess, 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(); @@ -928,12 +930,21 @@ void delete_ust_app_session(int sock, struct ust_app_session *ua_sess, 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 @@ -941,16 +952,17 @@ void delete_ust_app_session(int sock, struct ust_app_session *ua_sess, * 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. */ @@ -2397,7 +2409,7 @@ static void shadow_copy_session(struct ust_app_session *ua_sess, 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); @@ -2418,7 +2430,7 @@ static void shadow_copy_session(struct ust_app_session *ua_sess, 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(); @@ -2446,7 +2458,7 @@ static void shadow_copy_session(struct ust_app_session *ua_sess, 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(); @@ -2534,12 +2546,9 @@ static int setup_buffer_reg_pid(struct ust_app_session *ua_sess, } /* 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); @@ -2586,15 +2595,15 @@ static int setup_buffer_reg_uid(struct ltt_ust_session *usess, 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; } @@ -2603,14 +2612,9 @@ static int setup_buffer_reg_uid(struct ltt_ust_session *usess, } /* 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 @@ -3216,11 +3220,14 @@ static int create_buffer_reg_channel(struct buffer_reg_session *reg_sess, 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) { @@ -3374,7 +3381,6 @@ static int create_channel_per_uid(struct ust_app *app, 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); @@ -3384,7 +3390,7 @@ static int create_channel_per_uid(struct ust_app *app, 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 @@ -3416,7 +3422,7 @@ static int create_channel_per_uid(struct ust_app *app, * 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); @@ -3425,8 +3431,12 @@ static int create_channel_per_uid(struct ust_app *app, * 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; @@ -3442,15 +3452,14 @@ static int create_channel_per_uid(struct ust_app *app, 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( @@ -3499,7 +3508,6 @@ static int create_channel_per_pid(struct ust_app *app, 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); @@ -3515,10 +3523,12 @@ static int create_channel_per_pid(struct ust_app *app, 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; } @@ -3529,7 +3539,7 @@ static int create_channel_per_pid(struct ust_app *app, /* 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); @@ -3545,11 +3555,12 @@ static int create_channel_per_pid(struct ust_app *app, } 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, @@ -3567,7 +3578,12 @@ static int create_channel_per_pid(struct ust_app *app, 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(); @@ -3816,7 +3832,6 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess, 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); @@ -3824,14 +3839,12 @@ static int create_ust_app_metadata(struct ust_app_session *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; } @@ -3854,7 +3867,7 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess, } /* 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; @@ -3866,7 +3879,7 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess, * 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); @@ -3880,10 +3893,10 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess, * 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; } @@ -3896,7 +3909,7 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess, 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; } @@ -3905,9 +3918,8 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess, 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); } @@ -4000,13 +4012,17 @@ struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock) 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; @@ -4265,8 +4281,6 @@ void ust_app_unregister(int sock) */ 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. */ @@ -4299,10 +4313,10 @@ void ust_app_unregister(int sock) * 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 @@ -4311,8 +4325,18 @@ void ust_app_unregister(int sock) * 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); @@ -5207,7 +5231,6 @@ int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app) { int ret = 0; struct ust_app_session *ua_sess; - ust_registry_session *registry; DBG("Stopping tracing for ust app pid %d", app->pid); @@ -5284,13 +5307,15 @@ int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app) 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); @@ -5332,7 +5357,7 @@ int ust_app_flush_app_session(struct ust_app *app, 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. */ @@ -5413,7 +5438,8 @@ int ust_app_flush_session(struct ltt_ust_session *usess) 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; } @@ -5468,11 +5494,11 @@ int ust_app_clear_quiescent_app_session(struct ust_app *app, 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; } @@ -6334,106 +6360,6 @@ error: 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(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. @@ -6442,47 +6368,45 @@ end: * * 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( + 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. */ @@ -6492,42 +6416,36 @@ static int reply_ust_register_channel(int sock, int cobjd, 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(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; @@ -6536,8 +6454,8 @@ static int reply_ust_register_channel(int sock, int cobjd, } /* 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; @@ -6546,10 +6464,14 @@ static int reply_ust_register_channel(int sock, int cobjd, 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", @@ -6561,17 +6483,13 @@ reply: 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; } @@ -6584,9 +6502,9 @@ error_rcu_unlock: * * 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; @@ -6594,57 +6512,69 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, 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(raw_signature); + auto fields = lttng::make_unique_wrapper(raw_fields); + auto model_emf_uri = lttng::make_unique_wrapper(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( + 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 @@ -6667,19 +6597,11 @@ static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, * 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; } @@ -6810,9 +6732,8 @@ int ust_app_recv_notify(int sock) 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", @@ -6827,6 +6748,24 @@ int ust_app_recv_notify(int sock) 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 @@ -6844,13 +6783,13 @@ int ust_app_recv_notify(int sock) 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", @@ -6859,8 +6798,8 @@ int ust_app_recv_notify(int sock) 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; } @@ -6868,10 +6807,9 @@ int ust_app_recv_notify(int sock) /* * 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; } @@ -6903,7 +6841,7 @@ int ust_app_recv_notify(int sock) 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) { @@ -7111,7 +7049,7 @@ enum lttng_error_code ust_app_snapshot_record( } /* 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; @@ -7472,7 +7410,10 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session) 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, @@ -7501,7 +7442,7 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session) } /* 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; @@ -7531,7 +7472,11 @@ enum lttng_error_code ust_app_rotate_session(struct ltt_session *session) } /* 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, @@ -7715,7 +7660,10 @@ enum lttng_error_code ust_app_clear_session(struct ltt_session *session) } } - (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. @@ -7746,7 +7694,7 @@ enum lttng_error_code ust_app_clear_session(struct ltt_session *session) } /* 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; @@ -7772,7 +7720,10 @@ enum lttng_error_code ust_app_clear_session(struct ltt_session *session) } } - (void) push_metadata(registry, usess->consumer); + { + auto locked_registry = registry->lock(); + (void) push_metadata(locked_registry, usess->consumer); + } /* * Clear the metadata channel. @@ -7891,7 +7842,7 @@ enum lttng_error_code ust_app_open_packets(struct ltt_session *session) /* 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; diff --git a/src/bin/lttng-sessiond/ust-app.hpp b/src/bin/lttng-sessiond/ust-app.hpp index 48dc7b64e..f9238edc3 100644 --- a/src/bin/lttng-sessiond/ust-app.hpp +++ b/src/bin/lttng-sessiond/ust-app.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include "trace-ust.hpp" @@ -255,14 +256,8 @@ struct ust_app { 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 @@ -330,6 +325,20 @@ struct ust_app { struct lttng_ht *token_to_event_notifier_rule_ht; }; +template <> +struct fmt::formatter : fmt::formatter { + template + 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); @@ -367,8 +376,9 @@ int ust_app_recv_notify(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, diff --git a/src/bin/lttng-sessiond/ust-clock-class.cpp b/src/bin/lttng-sessiond/ust-clock-class.cpp new file mode 100644 index 000000000..34b466f0a --- /dev/null +++ b/src/bin/lttng-sessiond/ust-clock-class.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010 Pierre-Marc Fournier + * Copyright (C) 2011 Mathieu Desnoyers + * Copyright (C) 2022 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "ust-clock-class.hpp" + +#include +#include + +#include + +#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 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{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()) +{ +} diff --git a/src/bin/lttng-sessiond/ust-clock-class.hpp b/src/bin/lttng-sessiond/ust-clock-class.hpp new file mode 100644 index 000000000..70160a8c1 --- /dev/null +++ b/src/bin/lttng-sessiond/ust-clock-class.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Jérémie Galarneau + * + * 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 */ diff --git a/src/bin/lttng-sessiond/ust-clock.cpp b/src/bin/lttng-sessiond/ust-clock.cpp deleted file mode 100644 index 3d853ad50..000000000 --- a/src/bin/lttng-sessiond/ust-clock.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2010 Pierre-Marc Fournier - * Copyright (C) 2011 Mathieu Desnoyers - * Copyright (C) 2022 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include "ust-clock.hpp" - -#include -#include - -#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 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{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()} -{ -} diff --git a/src/bin/lttng-sessiond/ust-clock.hpp b/src/bin/lttng-sessiond/ust-clock.hpp deleted file mode 100644 index 7b9034173..000000000 --- a/src/bin/lttng-sessiond/ust-clock.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2010 Pierre-Marc Fournier - * Copyright (C) 2011 Mathieu Desnoyers - * Copyright (C) 2022 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#ifndef _UST_CLOCK_H -#define _UST_CLOCK_H - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -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 _uuid; - const scycles_t _offset; - const cycles_t _frequency; -}; - -} /* namespace ust */ -} /* namespace lttng */ - -#endif /* _UST_CLOCK_H */ diff --git a/src/bin/lttng-sessiond/ust-consumer.cpp b/src/bin/lttng-sessiond/ust-consumer.cpp index 3a1673329..9098f9cd3 100644 --- a/src/bin/lttng-sessiond/ust-consumer.cpp +++ b/src/bin/lttng-sessiond/ust-consumer.cpp @@ -25,6 +25,8 @@ #include "session.hpp" #include "lttng-sessiond.hpp" +namespace lsu = lttng::sessiond::ust; + /* * Send a single channel to the consumer using command ASK_CHANNEL_CREATION. * @@ -42,7 +44,6 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, 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; @@ -105,9 +106,13 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, * 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'; @@ -145,7 +150,7 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, ua_chan->name, consumer->net_seq_index, ua_chan->key, - registry->_uuid, + registry->uuid, chan_id, ua_chan->tracefile_size, ua_chan->tracefile_count, @@ -493,9 +498,10 @@ int ust_consumer_metadata_request(struct consumer_socket *socket) } 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) { diff --git a/src/bin/lttng-sessiond/ust-field-convert.cpp b/src/bin/lttng-sessiond/ust-field-convert.cpp new file mode 100644 index 000000000..06ade1e40 --- /dev/null +++ b/src/bin/lttng-sessiond/ust-field-convert.cpp @@ -0,0 +1,687 @@ +/* + * Copyright (C) 2022 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "ust-field-convert.hpp" + +#include + +#include + +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; + + 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; + +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 +enum lst::null_terminated_string_type::encoding ust_ctl_encoding_to_string_field_encoding(UstCtlEncodingType encoding) +{ + static const std::unordered_map + 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 +enum lst::integer_type::base ust_ctl_base_to_integer_field_base(UstCtlBaseType base) +{ + static const std::unordered_map + 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(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( + 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( + *session_attributes.get_registry_enum( + enumeration_name, enumeration_id)); + + return lttng::make_unique( + enum_container_uctl_type->alignment, byte_order, + enum_container_uctl_type->size, signedness, base, + enum_registry._mappings); + } else { + const auto& enum_registry = static_cast( + *session_attributes.get_registry_enum( + enumeration_name, enumeration_id)); + + return lttng::make_unique( + 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(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( + 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 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(*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( + array_alignment, *element_encoding, array_length); + } + + return lttng::make_unique( + 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 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(*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( + array_alignment, *element_encoding, array_length); + } + + return lttng::make_unique( + 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 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(length_field_name, std::move(length_type))); + + *next_ust_ctl_field = current + 1; + + if (element_encoding) { + const auto integer_element_size = + static_cast(*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(sequence_alignment, + *element_encoding, std::move(length_field_name)); + } + + return lttng::make_unique( + 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 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(*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(sequence_alignment, + *element_encoding, std::move(length_field_name)); + } + + return lttng::make_unique( + 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(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(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(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 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 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 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); +} diff --git a/src/bin/lttng-sessiond/ust-field-convert.hpp b/src/bin/lttng-sessiond/ust-field-convert.hpp new file mode 100644 index 000000000..f570e65ed --- /dev/null +++ b/src/bin/lttng-sessiond/ust-field-convert.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 Jérémie Galarneau + * + * 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 +#include + +namespace lttng { +namespace sessiond { +namespace ust { + +std::vector 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 */ diff --git a/src/bin/lttng-sessiond/ust-field-utils.cpp b/src/bin/lttng-sessiond/ust-field-utils.cpp deleted file mode 100644 index 27d3070d2..000000000 --- a/src/bin/lttng-sessiond/ust-field-utils.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (C) 2018 Francis Deslauriers - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include - -#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; -} diff --git a/src/bin/lttng-sessiond/ust-field-utils.hpp b/src/bin/lttng-sessiond/ust-field-utils.hpp deleted file mode 100644 index 886ae5232..000000000 --- a/src/bin/lttng-sessiond/ust-field-utils.hpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 */ diff --git a/src/bin/lttng-sessiond/ust-metadata.cpp b/src/bin/lttng-sessiond/ust-metadata.cpp index edbad7783..6a5d5ff52 100644 --- a/src/bin/lttng-sessiond/ust-metadata.cpp +++ b/src/bin/lttng-sessiond/ust-metadata.cpp @@ -17,1293 +17,14 @@ #include #include +#include #include #include #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(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 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 {\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 {\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(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(session)->_vpid : - (int) static_cast(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; diff --git a/src/bin/lttng-sessiond/ust-registry-channel.cpp b/src/bin/lttng-sessiond/ust-registry-channel.cpp new file mode 100644 index 000000000..d03c89c6e --- /dev/null +++ b/src/bin/lttng-sessiond/ust-registry-channel.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2022 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "ust-registry-channel.hpp" +#include "ust-app.hpp" +#include "ust-registry-event.hpp" + +#include +#include +#include +#include +#include + +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 event_fields, + int loglevel_value, + nonstd::optional 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( + 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 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); + } +} diff --git a/src/bin/lttng-sessiond/ust-registry-channel.hpp b/src/bin/lttng-sessiond/ust-registry-channel.hpp new file mode 100644 index 000000000..e8819812e --- /dev/null +++ b/src/bin/lttng-sessiond/ust-registry-channel.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2022 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#ifndef LTTNG_UST_REGISTRY_CHANNEL_H +#define LTTNG_UST_REGISTRY_CHANNEL_H + +#include "stream-class.hpp" + +#include + +#include + +#include +#include + +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; + using event_added_listener_fn = std::function; + + 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 event_fields, + int loglevel_value, + nonstd::optional 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 */ diff --git a/src/bin/lttng-sessiond/ust-registry-event.cpp b/src/bin/lttng-sessiond/ust-registry-event.cpp new file mode 100644 index 000000000..891e9e5b0 --- /dev/null +++ b/src/bin/lttng-sessiond/ust-registry-event.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "ust-registry-event.hpp" + +#include + +#include + +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 in_fields, + int in_loglevel_value, + nonstd::optional 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(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; +} diff --git a/src/bin/lttng-sessiond/ust-registry-event.hpp b/src/bin/lttng-sessiond/ust-registry-event.hpp new file mode 100644 index 000000000..08b18a5c7 --- /dev/null +++ b/src/bin/lttng-sessiond/ust-registry-event.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 Jérémie Galarneau + * + * 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 +#include +#include + +#include + +namespace lttng { +namespace sessiond { +namespace ust { + +/* + * Event registered from a UST tracer sent to the session daemon. This is + * indexed and matched by . + */ +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 fields, + int loglevel_value, + nonstd::optional 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 : fmt::formatter { + template + 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 */ diff --git a/src/bin/lttng-sessiond/ust-registry-session-pid.cpp b/src/bin/lttng-sessiond/ust-registry-session-pid.cpp index b2e8d9444..f8251106f 100644 --- a/src/bin/lttng-sessiond/ust-registry-session-pid.cpp +++ b/src/bin/lttng-sessiond/ust-registry-session-pid.cpp @@ -8,14 +8,12 @@ #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 + +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, @@ -23,18 +21,29 @@ ust_registry_session_per_pid::ust_registry_session_per_pid(const struct ust_app 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("tracer_buffering_id", _vpid)); + visitor.visit(lst::environment_field( + "tracer_patchlevel", _tracer_patch_level_version)); + visitor.visit(lst::environment_field("vpid", _vpid)); + visitor.visit(lst::environment_field("procname", _procname)); + visitor.visit(lst::environment_field( + "vpid_datetime", lttng::utils::time_to_iso8601_str(_app_creation_time))); +} diff --git a/src/bin/lttng-sessiond/ust-registry-session-uid.cpp b/src/bin/lttng-sessiond/ust-registry-session-uid.cpp index 9bfa8442a..b7630b2a5 100644 --- a/src/bin/lttng-sessiond/ust-registry-session-uid.cpp +++ b/src/bin/lttng-sessiond/ust-registry-session-uid.cpp @@ -7,13 +7,10 @@ #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, @@ -22,15 +19,20 @@ ust_registry_session_per_uid::ust_registry_session_per_uid(uint32_t bits_per_lon 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("tracer_buffering_id", _tracing_uid)); +} diff --git a/src/bin/lttng-sessiond/ust-registry-session.cpp b/src/bin/lttng-sessiond/ust-registry-session.cpp index 20dbc41a5..652a3fbc9 100644 --- a/src/bin/lttng-sessiond/ust-registry-session.cpp +++ b/src/bin/lttng-sessiond/ust-registry-session.cpp @@ -5,26 +5,140 @@ * */ +#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 #include #include +#include #include +#include #include #include +#include +#include #include +#include +#include #include #include -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, @@ -32,18 +146,16 @@ ust_registry_session::ust_registry_session(uint32_t bits_per_long, 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( + 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)); @@ -91,34 +203,32 @@ ust_registry_session::ust_registry_session(uint32_t bits_per_long, 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); @@ -144,21 +254,265 @@ ust_registry_session::~ust_registry_session() 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("domain", "ust")); + visitor.visit(lst::environment_field("tracer_name", "lttng-ust")); + visitor.visit(lst::environment_field("tracer_major", _app_tracer_version_major)); + visitor.visit(lst::environment_field("tracer_minor", _app_tracer_version_minor)); + visitor.visit(lst::environment_field("tracer_buffering_scheme", + get_buffering_scheme() == LTTNG_BUFFER_PER_PID ? "pid" : "uid")); + visitor.visit(lst::environment_field("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("trace_name", + session->has_auto_generated_name ? DEFAULT_SESSION_NAME : + session->name)); + visitor.visit(lst::environment_field("trace_creation_datetime", + lttng::utils::time_to_iso8601_str(session->creation_time))); + visitor.visit(lst::environment_field("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 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( + 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(); +} diff --git a/src/bin/lttng-sessiond/ust-registry.cpp b/src/bin/lttng-sessiond/ust-registry.cpp index 838b51afe..570f367a8 100644 --- a/src/bin/lttng-sessiond/ust-registry.cpp +++ b/src/bin/lttng-sessiond/ust-registry.cpp @@ -6,122 +6,25 @@ */ #define _LGPL_SOURCE -#include - -#include -#include -#include -#include #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 +#include +#include +#include +#include +#include - 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 - 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 @@ -130,29 +33,22 @@ end: */ 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; } /* @@ -161,13 +57,17 @@ no_match: */ 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) { @@ -187,324 +87,33 @@ no_match: */ 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(); - 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; @@ -514,28 +123,31 @@ void ust_registry_destroy_event(struct ust_registry_channel *chan, 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); } @@ -544,11 +156,11 @@ static void destroy_enum_rcu(struct rcu_head *head) * 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; @@ -561,44 +173,56 @@ static struct ust_registry_enum *ust_registry_lookup_enum( 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. @@ -609,12 +233,13 @@ end: */ 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(raw_entries); LTTNG_ASSERT(session); LTTNG_ASSERT(enum_name); @@ -625,24 +250,26 @@ int ust_registry_create_or_find_enum(ust_registry_session *session, * 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(); - 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) { @@ -659,7 +286,6 @@ int ust_registry_create_or_find_enum(ust_registry_session *session, 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, @@ -670,7 +296,6 @@ int ust_registry_create_or_find_enum(ust_registry_session *session, enum_name, reg_enum->id, session_objd); *enum_id = reg_enum->id; end: - free(entries); rcu_read_unlock(); return ret; } @@ -681,7 +306,7 @@ end: * 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; @@ -697,174 +322,7 @@ void ust_registry_destroy_enum(ust_registry_session *reg_session, 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(); - 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, @@ -875,24 +333,16 @@ ust_registry_session *ust_registry_session_per_uid_create(uint32_t bits_per_long 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, @@ -902,11 +352,9 @@ ust_registry_session *ust_registry_session_per_pid_create(struct ust_app *app, 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; } @@ -920,3 +368,20 @@ void ust_registry_session_destroy(ust_registry_session *reg) { 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); +} diff --git a/src/bin/lttng-sessiond/ust-registry.hpp b/src/bin/lttng-sessiond/ust-registry.hpp index 88bd502db..acfc92404 100644 --- a/src/bin/lttng-sessiond/ust-registry.hpp +++ b/src/bin/lttng-sessiond/ust-registry.hpp @@ -9,49 +9,62 @@ #ifndef LTTNG_UST_REGISTRY_H #define LTTNG_UST_REGISTRY_H -#include -#include -#include -#include -#include - +#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 #include +#include +#include #include #include -#include "lttng-ust-ctl.hpp" +#include +#include +#include +#include +#include +#include #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:: + 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. @@ -60,25 +73,13 @@ public: * 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. */ @@ -153,18 +154,43 @@ public: 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, @@ -176,19 +202,19 @@ public: 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, @@ -199,10 +225,10 @@ public: 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; @@ -210,150 +236,104 @@ public: 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; -/* - * Event registered from a UST tracer sent to the session daemon. This is - * indexed and matched by . - */ -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 +typename trace::typed_enumeration_type::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 +typename trace::typed_enumeration_type::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::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(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 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::value ? + lttng::sessiond::trace::integer_type::signedness::SIGNED : + lttng::sessiond::trace::integer_type::signedness::UNSIGNED), + _mappings{std::make_shared< + typename trace::typed_enumeration_type::mappings>( + details::mappings_from_ust_ctl_entries( + entries, entry_count))} + { } - r->_used_channel_id++; - return r->_next_channel_id++; -} + const typename std::shared_ptr::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(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; +using registry_unsigned_enum = registry_typed_enum; + +} /* namespace ust */ +} /* namespace sessiond */ +} /* namespace lttng */ + +#ifdef HAVE_LIBLTTNG_UST_CTL /* * Create per-uid registry with default values. @@ -361,13 +341,7 @@ void ust_registry_channel_destroy(struct ust_registry_channel *chan, bool notify * 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, @@ -383,13 +357,7 @@ ust_registry_session *ust_registry_session_per_uid_create( * 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, @@ -399,64 +367,20 @@ ust_registry_session *ust_registry_session_per_pid_create(struct ust_app *app, 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)), @@ -504,36 +428,10 @@ void ust_registry_session_destroy( 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. */ @@ -547,7 +445,7 @@ int ust_metadata_session_statedump( 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; } @@ -555,8 +453,8 @@ int ust_metadata_channel_statedump( 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; } @@ -576,7 +474,7 @@ int ust_registry_create_or_find_enum( 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))) { diff --git a/src/common/urcu.hpp b/src/common/urcu.hpp index 2ea2001cd..d121ecd9f 100644 --- a/src/common/urcu.hpp +++ b/src/common/urcu.hpp @@ -68,6 +68,8 @@ private: std::lock_guard _guard; }; +using unique_read_lock = std::unique_lock; + } /* namespace urcu */ } /* namespace lttng */