X-Git-Url: https://git.liburcu.org/?a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fust-registry-session.cpp;fp=src%2Fbin%2Flttng-sessiond%2Fust-registry-session.cpp;h=652a3fbc9624c57f95456d39dce77d30ea9096d1;hb=d7bfb9b0fa35679d3e728b9165699d9faf905539;hp=20dbc41a55a02cb60ea5c05ba4cd43562abaef2d;hpb=0220be14254fac4f7af642fd6630282b29776a70;p=lttng-tools.git 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(); +}