}
}
+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<uint64_t>(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.
*/
{
struct lttng_channel *channel = nullptr;
int ret = CMD_SUCCESS, warn = 0, error = 0, success = 0;
+ auto bytes_required = static_cast<uint64_t>(0);
+ auto bytes_available = static_cast<uint64_t>(0);
char *channel_name;
struct lttng_domain dom;
}
}
- 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;
{
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<uint64_t>(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;
+}
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.
*
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);
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) {