From 923a8b2176d42a849b8c325607f1713e153302b6 Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Wed, 13 Nov 2024 15:37:36 -0500 Subject: [PATCH] lttng-ctl/lttng: Move enable-channel memory check to client MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Change-Id: I855284c3f2a6910363a075e6a7bb4c6d55b22685 Signed-off-by: Kienan Stewart Signed-off-by: Jérémie Galarneau --- src/bin/lttng/commands/enable_channels.cpp | 52 +++++++++++++- src/common/utils.cpp | 50 ++++++++++++++ src/lib/lttng-ctl/lttng-ctl.cpp | 79 ---------------------- 3 files changed, 101 insertions(+), 80 deletions(-) diff --git a/src/bin/lttng/commands/enable_channels.cpp b/src/bin/lttng/commands/enable_channels.cpp index 4e5f2eb15..bbce3e4b6 100644 --- a/src/bin/lttng/commands/enable_channels.cpp +++ b/src/bin/lttng/commands/enable_channels.cpp @@ -135,6 +135,45 @@ static void set_default_attr(struct lttng_domain *dom) } } +static bool system_has_memory_for_channel_buffers(char *session_name, struct lttng_channel *channel, uint64_t *bytes_required, uint64_t *bytes_available) { + /* + * Verify that the amount of memory required to create the requested + * buffer is available on the system at the moment. + */ + unsigned long total_buffer_size_needed_per_cpu {0}; + const auto spec = lttng::cli::session_spec(lttng::cli::session_spec::type::NAME, session_name); + const auto sessions = list_sessions(spec); + int ncpus {0}; + + if (sessions.size() <= 0) { + /* Session not found */ + ERR_FMT("Session not found, name='{}'", session_name); + return false; + } + + if (channel->attr.num_subbuf > UINT64_MAX / channel->attr.subbuf_size) { + /* Overflow */ + ERR_FMT("Integer overflow calculating total buffer size per CPU on channel '{}': num_subbuf={}, subbuf_size={}", channel->name, channel->attr.num_subbuf, channel->attr.subbuf_size) + return false; + } + + total_buffer_size_needed_per_cpu = channel->attr.num_subbuf * channel->attr.subbuf_size; + try { + ncpus = utils_get_cpu_count(); + } catch (const std::exception &ex) { + ERR_FMT("Exception when getting CPU count: {}", ex.what()); + return false; + } + + /* In snapshot mode, an extra set of buffers is required. */ + const auto _bytes_required = static_cast(total_buffer_size_needed_per_cpu * ncpus + sessions[0].snapshot_mode); + if (bytes_required != nullptr) { + *bytes_required = _bytes_required; + } + + return utils_check_enough_available_memory(_bytes_required, bytes_available) == LTTNG_OK; +} + /* * Adding channel using the lttng API. */ @@ -142,6 +181,8 @@ static int enable_channel(char *session_name, char *channel_list) { struct lttng_channel *channel = nullptr; int ret = CMD_SUCCESS, warn = 0, error = 0, success = 0; + auto bytes_required = static_cast(0); + auto bytes_available = static_cast(0); char *channel_name; struct lttng_domain dom; @@ -280,8 +321,17 @@ static int enable_channel(char *session_name, char *channel_list) } } - DBG("Enabling channel %s", channel_name); + if (!system_has_memory_for_channel_buffers( + session_name, channel, &bytes_required, &bytes_available)) { + ERR_FMT("Not enough system memory available for channel '{}'. At least {}MiB required, {}MiB available", + channel->name, + bytes_required / 1024 / 1024, + bytes_available / 1024 / 1024); + error = 1; + goto error; + } + DBG("Enabling channel %s", channel_name); ret = lttng_enable_channel(handle, channel); if (ret < 0) { bool msg_already_printed = false; diff --git a/src/common/utils.cpp b/src/common/utils.cpp index b1478c80a..78d7beb5a 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -1469,3 +1469,53 @@ unsigned int utils_get_cpu_count() LTTNG_MAY_THROW { return get_max_possible_cpu_id() + 1; } + +/** + * Returns LTTNG_OK if the system as num_bytes available. + * + * If bytes_available is not NULL, the best estimate of the available memory + * will be recorded at that address. + */ +enum lttng_error_code utils_check_enough_available_memory(uint64_t num_bytes, uint64_t *bytes_available) +{ + int ret; + enum lttng_error_code ret_code; + auto best_mem_info = static_cast(0); + + /* + * Try to get the `MemAvail` field of `/proc/meminfo`. This is the most + * reliable estimate we can get but it is only exposed by the kernel + * since 3.14. (See Linux kernel commit: + * 34e431b0ae398fc54ea69ff85ec700722c9da773) + */ + ret = utils_get_memory_available(&best_mem_info); + if (ret >= 0) { + goto success; + } + + /* + * As a backup plan, use `MemTotal` field of `/proc/meminfo`. This + * is a sanity check for obvious user error. + */ + ret = utils_get_memory_total(&best_mem_info); + if (ret >= 0) { + goto success; + } + + /* No valid source of information. */ + ret_code = LTTNG_ERR_NOMEM; + goto end; + +success: + if (bytes_available != nullptr) { + *bytes_available = best_mem_info; + } + + if (best_mem_info >= num_bytes) { + ret_code = LTTNG_OK; + } else { + ret_code = LTTNG_ERR_NOMEM; + } +end: + return ret_code; +} diff --git a/src/lib/lttng-ctl/lttng-ctl.cpp b/src/lib/lttng-ctl/lttng-ctl.cpp index 8ba7d657a..9af768fb1 100644 --- a/src/lib/lttng-ctl/lttng-ctl.cpp +++ b/src/lib/lttng-ctl/lttng-ctl.cpp @@ -297,67 +297,6 @@ end: return ret; } -static enum lttng_error_code check_enough_available_memory(uint64_t num_bytes_requested_per_cpu) -{ - int ret; - enum lttng_error_code ret_code; - long num_cpu; - uint64_t best_mem_info; - uint64_t num_bytes_requested_total; - - /* - * Get the number of CPU currently online to compute the amount of - * memory needed to create a buffer for every CPU. - */ - try { - num_cpu = long(utils_get_cpu_count()); - } catch (const std::exception& ex) { - ret_code = LTTNG_ERR_FATAL; - goto end; - } - - if (num_bytes_requested_per_cpu > UINT64_MAX / (uint64_t) num_cpu) { - /* Overflow */ - ret_code = LTTNG_ERR_OVERFLOW; - goto end; - } - - num_bytes_requested_total = num_bytes_requested_per_cpu * (uint64_t) num_cpu; - - /* - * Try to get the `MemAvail` field of `/proc/meminfo`. This is the most - * reliable estimate we can get but it is only exposed by the kernel - * since 3.14. (See Linux kernel commit: - * 34e431b0ae398fc54ea69ff85ec700722c9da773) - */ - ret = utils_get_memory_available(&best_mem_info); - if (ret >= 0) { - goto success; - } - - /* - * As a backup plan, use `MemTotal` field of `/proc/meminfo`. This - * is a sanity check for obvious user error. - */ - ret = utils_get_memory_total(&best_mem_info); - if (ret >= 0) { - goto success; - } - - /* No valid source of information. */ - ret_code = LTTNG_ERR_NOMEM; - goto end; - -success: - if (best_mem_info >= num_bytes_requested_total) { - ret_code = LTTNG_OK; - } else { - ret_code = LTTNG_ERR_NOMEM; - } -end: - return ret_code; -} - /* * Try connect to session daemon with sock_path. * @@ -1611,7 +1550,6 @@ int lttng_enable_channel(struct lttng_handle *handle, struct lttng_channel *in_c int ret; struct lttng_dynamic_buffer buffer; struct lttcomm_session_msg lsm; - uint64_t total_buffer_size_needed_per_cpu = 0; struct lttng_channel *channel = nullptr; lttng_dynamic_buffer_init(&buffer); @@ -1622,23 +1560,6 @@ int lttng_enable_channel(struct lttng_handle *handle, struct lttng_channel *in_c goto end; } - /* - * Verify that the amount of memory required to create the requested - * buffer is available on the system at the moment. - */ - if (in_chan->attr.num_subbuf > UINT64_MAX / in_chan->attr.subbuf_size) { - /* Overflow */ - ret = -LTTNG_ERR_OVERFLOW; - goto end; - } - - total_buffer_size_needed_per_cpu = in_chan->attr.num_subbuf * in_chan->attr.subbuf_size; - ret_code = check_enough_available_memory(total_buffer_size_needed_per_cpu); - if (ret_code != LTTNG_OK) { - ret = -ret_code; - goto end; - } - /* Copy the channel for easier manipulation. */ channel = lttng_channel_copy(in_chan); if (!channel) { -- 2.39.5