diff --git a/contrib/jenkins_tests/gtest.sh b/contrib/jenkins_tests/gtest.sh index fa33771d1..c9a9a0715 100755 --- a/contrib/jenkins_tests/gtest.sh +++ b/contrib/jenkins_tests/gtest.sh @@ -72,20 +72,20 @@ fi eval "${sudo_cmd} pkill -9 ${prj_service} 2>/dev/null || true" eval "${sudo_cmd} ${install_dir}/sbin/${prj_service} --console -v5 &" -# Test with full coverage of the new config -eval "${sudo_cmd} $timeout_exe env XLIO_USE_NEW_CONFIG=1 XLIO_CONFIG_FILE=${WORKSPACE}/tests/gtest/xlio_config_full_coverage.json GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt --gtest_filter=-xlio_*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-basic.xml" +# Test with full coverage of the config-file feature +eval "${sudo_cmd} $timeout_exe env WORKSPACE=${WORKSPACE} XLIO_USE_NEW_CONFIG=1 XLIO_CONFIG_FILE=${WORKSPACE}/tests/gtest/xlio_config_full_coverage.json GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt --gtest_filter=-xlio_*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-basic.xml" rc=$(($rc+$?)) # Exclude EXTRA API tests IPv6 -eval "${sudo_cmd} $timeout_exe env GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt_ipv6 --gtest_filter=-xlio_*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-basic-ipv6.xml" +eval "${sudo_cmd} $timeout_exe env WORKSPACE=${WORKSPACE} GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt_ipv6 --gtest_filter=-xlio_*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-basic-ipv6.xml" rc=$(($rc+$?)) # Verify Delegated TCP Timers tests -eval "${sudo_cmd} $timeout_exe env XLIO_RX_POLL_ON_TX_TCP=1 XLIO_TCP_ABORT_ON_CLOSE=1 XLIO_TCP_CTL_THREAD=delegate GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt --gtest_filter=-xlio*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-delegate.xml" +eval "${sudo_cmd} $timeout_exe env WORKSPACE=${WORKSPACE} XLIO_RX_POLL_ON_TX_TCP=1 XLIO_TCP_ABORT_ON_CLOSE=1 XLIO_TCP_CTL_THREAD=delegate GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt --gtest_filter=-xlio*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-delegate.xml" rc=$(($rc+$?)) # Verify Delegated TCP Timers tests IPv6 -eval "${sudo_cmd} $timeout_exe env XLIO_RX_POLL_ON_TX_TCP=1 XLIO_TCP_ABORT_ON_CLOSE=1 XLIO_TCP_CTL_THREAD=delegate GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt_ipv6 --gtest_filter=-xlio*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-delegate-ipv6.xml" +eval "${sudo_cmd} $timeout_exe env WORKSPACE=${WORKSPACE} XLIO_RX_POLL_ON_TX_TCP=1 XLIO_TCP_ABORT_ON_CLOSE=1 XLIO_TCP_CTL_THREAD=delegate GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt_ipv6 --gtest_filter=-xlio*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-delegate-ipv6.xml" rc=$(($rc+$?)) if [[ -z "${MANUAL_RUN}" ]]; then diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 4820762a1..c8810920c 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -197,6 +197,7 @@ libxlio_la_SOURCES := \ \ $(top_builddir)/src/core/config/descriptor_providers/xlio_config_schema.h \ \ + config_printer.cpp \ libxlio.c \ main.cpp \ \ @@ -344,6 +345,7 @@ libxlio_la_SOURCES := \ util/data_updater.h \ \ $(top_builddir)/third_party/legacy_config_parser/config_parser.h \ + config_printer.h \ main.h \ xlio.h \ xlio_extra.h \ diff --git a/src/core/config/config_registry.cpp b/src/core/config/config_registry.cpp index 330157715..7f627005f 100644 --- a/src/core/config/config_registry.cpp +++ b/src/core/config/config_registry.cpp @@ -117,6 +117,8 @@ void config_registry::initialize_registry(std::queue> && key + "': expected " + get_user_friendly_type_name(param_desc.type()) + ", got " + get_user_friendly_type_name(value.type())); + } catch (const std::runtime_error &e) { + throw_xlio_exception("In '" + loader->source() + "': " + e.what()); } } diff --git a/src/core/config/config_registry.h b/src/core/config/config_registry.h index d1b02e586..7c7cffe60 100644 --- a/src/core/config/config_registry.h +++ b/src/core/config/config_registry.h @@ -69,64 +69,41 @@ class config_registry { std::vector get_sources() const; /** - * @brief Gets default value for non-integer types + * @brief Gets default value * @tparam T Value type * @param key Configuration parameter key * @return Default value for the parameter */ - template - typename std::enable_if::value, T>::type get_default_value( - const std::string &key) const - { - return get_value_impl( - key, [this](const std::string &k) { return get_default_value_as_any(k); }); - } - - /** - * @brief Gets default value for integer types with bounds checking - * @tparam T Integer type - * @param key Configuration parameter key - * @return Default value for the parameter - * @note For enums, use int instead since C++14 doesn't support bound checking for enums - */ - template - typename std::enable_if::value, T>::type get_default_value( - const std::string &key) const + template T get_default_value(const std::string &key) const { return get_value_impl( key, [this](const std::string &k) { return get_default_value_as_any(k); }); } /** - * @brief Gets configured value for non-integer types + * @brief Gets configured value. For integer types, also does bounds checking * @tparam T Value type * @param key Configuration parameter key * @return Current value for the parameter */ - template - typename std::enable_if::value, T>::type get_value(const std::string &key) const + template T get_value(const std::string &key) const { return get_value_impl(key, [this](const std::string &k) { return get_value_as_any(k); }); } /** - * @brief Gets configured value for integer types with bounds checking - * @tparam T Integer type + * @brief Gets configured value as any * @param key Configuration parameter key - * @return Current value for the parameter - * @note For enums, use int instead since C++14 doesn't support bound checking for enums + * @return Current value for the parameter, as any */ - template - typename std::enable_if::value, T>::type get_value(const std::string &key) const - { - return get_value_impl(key, [this](const std::string &k) { return get_value_as_any(k); }); - } + std::experimental::any get_value_as_any(const std::string &key) const; + + const config_descriptor &get_config_descriptor() const { return m_config_descriptor; } private: std::map m_config_data; config_descriptor m_config_descriptor; std::vector m_sources; - std::experimental::any get_value_as_any(const std::string &key) const; std::experimental::any get_default_value_as_any(const std::string &key) const; void initialize_registry(std::queue> &&value_loaders, std::unique_ptr descriptor_provider); diff --git a/src/core/config/descriptor_providers/json_descriptor_provider.cpp b/src/core/config/descriptor_providers/json_descriptor_provider.cpp index 9b951cc9c..02e978999 100644 --- a/src/core/config/descriptor_providers/json_descriptor_provider.cpp +++ b/src/core/config/descriptor_providers/json_descriptor_provider.cpp @@ -152,6 +152,8 @@ std::unique_ptr json_descriptor_provider::create_descripto // Create parameter descriptor with default value auto descriptor = std::make_unique(*analysis.default_value); + descriptor->set_title(analysis.title); + // Apply constraints if present if (analysis.needs_constraint_validation()) { apply_constraints(descriptor.get(), analysis.constraint_cfg); diff --git a/src/core/config/descriptor_providers/schema_analyzer.cpp b/src/core/config/descriptor_providers/schema_analyzer.cpp index 87b2c8281..a926e0f17 100644 --- a/src/core/config/descriptor_providers/schema_analyzer.cpp +++ b/src/core/config/descriptor_providers/schema_analyzer.cpp @@ -11,6 +11,7 @@ #include #include #include +#include "vlogger/vlogger.h" static void for_each_oneof_option(json_object *one_of_field, std::function func) @@ -161,6 +162,25 @@ std::experimental::optional schema_analyzer::determine_d return json_utils::to_any_value(default_field); } +std::experimental::optional schema_analyzer::determine_title() +{ + json_object *title_field = + json_utils::try_get_field(m_property_obj, config_strings::schema::JSON_TITLE); + if (!title_field) { + // Enforce title definition only for leafs and arrays. Objects are exempt. + if (determine_value_type() != typeid(json_object *)) { + throw_xlio_exception("Title must be a defined for: " + m_path + " - " + + std::to_string(json_object_get_type(title_field))); + } + return std::experimental::nullopt; + } + if (json_object_get_type(title_field) != json_type_string) { + throw_xlio_exception("Title must be a string for: " + m_path + " - " + + std::to_string(json_object_get_type(title_field))); + } + return std::experimental::optional(json_object_get_string(title_field)); +} + memory_size_extension_config_t schema_analyzer::analyze_memory_size_extension_config() { if (!has_memory_size_flag()) { @@ -397,6 +417,7 @@ schema_analyzer::analysis_result::analysis_result(schema_analyzer &analyzer) : json_property_type(analyzer.determine_property_type()) , value_type(analyzer.determine_value_type()) , default_value(analyzer.determine_default_value(value_type)) + , title(analyzer.determine_title()) , memory_cfg(analyzer.analyze_memory_size_extension_config()) , constraint_cfg(analyzer.analyze_constraint_config()) , enum_cfg(analyzer.analyze_enum_mapping_config()) diff --git a/src/core/config/descriptor_providers/schema_analyzer.h b/src/core/config/descriptor_providers/schema_analyzer.h index 35b6830ef..34ed5d0a9 100644 --- a/src/core/config/descriptor_providers/schema_analyzer.h +++ b/src/core/config/descriptor_providers/schema_analyzer.h @@ -70,6 +70,8 @@ class schema_analyzer { std::type_index value_type; /**< C++ type for the parameter value */ std::experimental::optional default_value; /**< Default value ready for use */ + std::experimental::optional + title; /**< Title of the parameter as defined in schema */ // Pre-parsed component configurations memory_size_extension_config_t memory_cfg; /**< Memory size transformation configuration */ @@ -120,6 +122,7 @@ class schema_analyzer { std::type_index determine_value_type(); std::experimental::optional determine_default_value( std::type_index type); + std::experimental::optional determine_title(); // Component configuration methods memory_size_extension_config_t analyze_memory_size_extension_config(); diff --git a/src/core/config/descriptors/config_descriptor.cpp b/src/core/config/descriptors/config_descriptor.cpp index 25a8e62a4..41d721590 100644 --- a/src/core/config/descriptors/config_descriptor.cpp +++ b/src/core/config/descriptors/config_descriptor.cpp @@ -80,6 +80,11 @@ std::type_index config_descriptor::get_parent_expected_type(const std::string &k return typeid(std::map); } +const config_descriptor::parameter_map_t &config_descriptor::get_parameter_map() const +{ + return parameter_map; +} + /** * @brief Calculates the Levenshtein distance between two strings. * diff --git a/src/core/config/descriptors/config_descriptor.h b/src/core/config/descriptors/config_descriptor.h index 873228681..ad5789813 100644 --- a/src/core/config/descriptors/config_descriptor.h +++ b/src/core/config/descriptors/config_descriptor.h @@ -20,6 +20,8 @@ */ class config_descriptor { public: + typedef std::map parameter_map_t; + /** * @brief Default constructor */ @@ -55,11 +57,18 @@ class config_descriptor { */ std::type_index get_parent_expected_type(const std::string &key) const; + /** + * @brief Gets the parameter map, allowing external users to iterate + * over all parameters + * @return The parameter map + */ + const parameter_map_t &get_parameter_map() const; + private: /** * @brief Map from parameter name to its descriptor */ - std::map parameter_map; + parameter_map_t parameter_map; /** * @brief Set of all parameter keys for efficient prefix-based lookups diff --git a/src/core/config/descriptors/parameter_descriptor.cpp b/src/core/config/descriptors/parameter_descriptor.cpp index acfafefc8..17150bc69 100644 --- a/src/core/config/descriptors/parameter_descriptor.cpp +++ b/src/core/config/descriptors/parameter_descriptor.cpp @@ -12,6 +12,17 @@ #include #include #include +#include + +void parameter_descriptor::set_title(const std::experimental::optional &title) +{ + m_title = title; +} + +const std::experimental::optional ¶meter_descriptor::get_title() const +{ + return m_title; +} /** * @brief Parse memory size string with suffixes (e.g., "4GB", "512MB", "1024KB", "1024B", "5G") @@ -151,6 +162,7 @@ parameter_descriptor::parameter_descriptor(const parameter_descriptor &pd) , m_string_mapping(pd.m_string_mapping) , m_value_transformer(pd.m_value_transformer) , m_type(pd.m_type) + , m_title(pd.m_title) { } @@ -164,6 +176,11 @@ void parameter_descriptor::set_string_mappings(const std::mapsecond; } - throw std::experimental::bad_any_cast(); + // If no string mappings are defined, throw an exception which has no further information + if (m_string_mapping.empty()) { + throw std::experimental::bad_any_cast(); + } + + // We have string mappings but value is not one of them - make an effort and create a nice error + // message + std::string valid_values = std::accumulate( + next(m_string_mapping.begin()), m_string_mapping.end(), m_string_mapping.begin()->first, + [](const std::string &a, const auto &b) { return a + "," + b.first; }); + + throw std::runtime_error("Invalid value for " + m_title.value_or("") + ": " + val + + ", not one of: [" + valid_values + "]"); } std::experimental::any parameter_descriptor::get_value(bool val) const @@ -226,6 +255,23 @@ std::experimental::any parameter_descriptor::get_value(const std::string &val) c throw std::experimental::bad_any_cast(); } +std::string parameter_descriptor::convert_int64_to_mapped_string_or( + int64_t val, const std::string &default_value) const +{ + if (m_string_mapping.empty()) { + return std::to_string(val); + } + + for (const auto &mapping : m_string_mapping) { + if ((mapping.second.type() == typeid(int64_t)) && + (std::experimental::any_cast(mapping.second) == val)) { + return mapping.first; + } + } + + return default_value; +} + std::experimental::any parameter_descriptor::get_value(int64_t val) const { if (m_type != typeid(int64_t)) { diff --git a/src/core/config/descriptors/parameter_descriptor.h b/src/core/config/descriptors/parameter_descriptor.h index 2e967301e..32189a1de 100644 --- a/src/core/config/descriptors/parameter_descriptor.h +++ b/src/core/config/descriptors/parameter_descriptor.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -78,12 +79,30 @@ class parameter_descriptor { */ void set_string_mappings(const std::map &mappings); + /** + * @brief returns true if string-to-value mappings were set by set_string_mappings() + * @return true if string-to-value mappings were set by set_string_mappings(), false otherwise + */ + bool has_string_mappings() const; + /** * @brief Sets a value transformer function * @param transformer Function to transform input values */ void set_value_transformer(value_transformer_t transformer); + /** + * @brief Sets the title of the parameter + * @param title Title of the parameter + */ + + void set_title(const std::experimental::optional &title); + /** + * @brief Gets the title of the parameter. Note that it may be nullopt if not set + * @return Title of the parameter + */ + const std::experimental::optional &get_title() const; + /** * @brief Validates a value against all constraints * @param value Value to validate @@ -138,6 +157,17 @@ class parameter_descriptor { */ std::experimental::any get_value(const std::vector &val) const; + /** + * @brief Converts an integer to a string. If string mappings were set by set_string_mappings(), + * use them. + * @param val Integer value to convert + * @param default_value Default value to return if string mappings were set but the value is not + * in the mappings + * @return Converted value. + */ + std::string convert_int64_to_mapped_string_or(int64_t val, + const std::string &default_value) const; + /** * @brief Gets the type of the parameter * @return The type of the parameter @@ -162,6 +192,8 @@ class parameter_descriptor { std::map m_string_mapping; /**< String-to-value mappings */ value_transformer_t m_value_transformer; /**< Value transformation function */ std::type_index m_type; + std::experimental::optional + m_title; /**< Title of the parameter as defined in schema */ /** * @brief Parses a memory size string with suffixes (KB, MB, GB) diff --git a/src/core/config_printer.cpp b/src/core/config_printer.cpp new file mode 100644 index 000000000..d515ef288 --- /dev/null +++ b/src/core/config_printer.cpp @@ -0,0 +1,426 @@ +/* + * SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES + * Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause + */ + +#include "config_printer.h" + +#include +#include + +#include "config/descriptors/config_descriptor.h" +#include "config/config_registry.h" +#include "util/sys_vars.h" +#include "vlogger/vlogger.h" + +#define FORMAT_NUMBER "%-30s %-26d [%s]\n" +#define FORMAT_STRING "%-30s %-26s [%s]\n" +#define FORMAT_NUMSTR "%-30s %-2d%-24s [%s]\n" + +#define VLOG_STR_PARAM_DETAILS(param_val, param_def_val, args...) \ + do { \ + if (param_val && strcmp(param_val, param_def_val)) { \ + vlog_printf(VLOG_INFO, ##args); \ + } else { \ + vlog_printf(VLOG_DETAILS, ##args); \ + } \ + } while (0); + +#define VLOG_NUM_PARAM_DETAILS(param_val, param_def_val, args...) \ + do { \ + if (param_val != param_def_val) { \ + vlog_printf(VLOG_INFO, ##args); \ + } else { \ + vlog_printf(VLOG_DETAILS, ##args); \ + } \ + } while (0); + +#define LOG_STR_PARAM_AS(param_desc, param_val, param_def_val, param_name, val_desc_str) \ + VLOG_STR_PARAM_DETAILS(param_val, param_def_val, FORMAT_STRING, param_desc, val_desc_str, \ + param_name) + +#define LOG_NUM_PARAM(param_desc, param_val, param_def_val, param_name) \ + VLOG_NUM_PARAM_DETAILS(param_val, param_def_val, FORMAT_NUMBER, param_desc, param_val, \ + param_name) + +#define LOG_NUM_PARAM_AS(param_desc, param_val, param_def_val, param_name, val_desc_str) \ + VLOG_NUM_PARAM_DETAILS(param_val, param_def_val, FORMAT_STRING, param_desc, val_desc_str, \ + param_name) + +#define LOG_BOOL_PARAM(param_desc, param_val, param_def_val, param_name) \ + VLOG_NUM_PARAM_DETAILS(param_val, param_def_val, FORMAT_STRING, param_desc, \ + param_val ? "true" : "false", param_name) + +#define LOG_NUM_PARAM_AS_NUMSTR(param_desc, param_val, param_def_val, param_name, val_desc_str) \ + VLOG_NUM_PARAM_DETAILS(param_val, param_def_val, FORMAT_NUMSTR, param_desc, param_val, \ + val_desc_str, param_name) + +/** + * @brief Constructor + * @param mce_sys_var Reference to the mce_sys_var object + */ +config_printer::config_printer(const mce_sys_var &mce_sys_var) + : m_mce_sys_var(mce_sys_var) +{ +} + +void config_printer::print_to_log() +{ + // Scan all config parameters and print them. + // The VLOG_PARAM_XXX macros show: + // - When logging level is INFO: non-default parameters only + // - When logging level is DETAILS: all parameters + const config_registry ®istry = m_mce_sys_var.get_registry().value(); + const config_descriptor &descriptor = registry.get_config_descriptor(); + const config_descriptor::parameter_map_t ¶meter_map = descriptor.get_parameter_map(); + for (const auto &it : parameter_map) { + const std::string &key = it.first; + const parameter_descriptor ¶m_descriptor = it.second; + + const std::string &title = param_descriptor.get_title().value_or(""); + + // Use function pointer dispatch for better performance and maintainability + static const std::map< + std::string, + std::function> + special_treatments = { + {CONFIG_VAR_LOG_LEVEL, &config_printer::print_log_level}, + {CONFIG_VAR_RX_NUM_WRE, &config_printer::print_rx_num_wre}, + {CONFIG_VAR_RX_NUM_WRE_TO_POST_RECV, + &config_printer::print_rx_num_wre_to_post_recv}, + {CONFIG_VAR_PROGRESS_ENGINE_INTERVAL, + &config_printer::print_progress_engine_interval}, + // NOP - handled by prev case CONFIG_VAR_PROGRESS_ENGINE_INTERVAL + {CONFIG_VAR_PROGRESS_ENGINE_WCE_MAX, &config_printer::print_nothing}, + {CONFIG_VAR_QP_COMPENSATION_LEVEL, &config_printer::print_qp_compensation_level} +#if defined(DEFINED_NGINX) + , + {CONFIG_VAR_NGINX_WORKERS_NUM, &config_printer::print_nginx_workers_num} +#endif +#if defined(DEFINED_ENVOY) + , + {CONFIG_VAR_ENVOY_WORKERS_NUM, &config_printer::print_envoy_workers_num} +#endif + }; + + const auto special_treatment = special_treatments.find(key); + + if (special_treatment != special_treatments.end()) { + special_treatment->second(this, key, title); + } else { + // Generic handling by type of all other params + const std::experimental::any element = registry.get_value_as_any(key); + print_config_element(key, ¶m_descriptor, element, param_descriptor.default_value()); + } + } +} + +void config_printer::print_log_level(const std::string &key, const std::string &title) +{ + // Specific treatment to ensure log level is always shown + LOG_NUM_PARAM_AS(title.c_str(), m_mce_sys_var.log_level, + // VLOG_INIT is never == m_mce_sys_var.log_level, so we always show log level + VLOG_INIT, key.c_str(), log_level::to_str(m_mce_sys_var.log_level)); +} + +void config_printer::print_rx_num_wre(const std::string &key, const std::string &title) +{ + LOG_NUM_PARAM( + title.c_str(), m_mce_sys_var.rx_num_wr, + (m_mce_sys_var.enable_striding_rq ? MCE_DEFAULT_STRQ_NUM_WRE : MCE_DEFAULT_RX_NUM_WRE), + key.c_str()); +} + +void config_printer::print_rx_num_wre_to_post_recv(const std::string &key, const std::string &title) +{ + LOG_NUM_PARAM(title.c_str(), m_mce_sys_var.rx_num_wr_to_post_recv, + (m_mce_sys_var.enable_striding_rq ? MCE_DEFAULT_STRQ_NUM_WRE_TO_POST_RECV + : MCE_DEFAULT_RX_NUM_WRE_TO_POST_RECV), + key.c_str()); +} + +void config_printer::print_progress_engine_interval(const std::string &key, + const std::string &title) +{ + if (m_mce_sys_var.progress_engine_interval_msec == MCE_CQ_DRAIN_INTERVAL_DISABLED || + m_mce_sys_var.progress_engine_wce_max == 0) { + LOG_NUM_PARAM_AS_NUMSTR(title.c_str(), m_mce_sys_var.progress_engine_interval_msec, + INT_MAX, // Ensure it is always shown - we want the user to + // know it is disabled + key.c_str(), "(Disabled)"); + } else { + LOG_NUM_PARAM(title.c_str(), m_mce_sys_var.progress_engine_interval_msec, + MCE_DEFAULT_PROGRESS_ENGINE_INTERVAL_MSEC, key.c_str()); + LOG_NUM_PARAM("Max CQEs per periodic drain", m_mce_sys_var.progress_engine_wce_max, + MCE_DEFAULT_PROGRESS_ENGINE_WCE_MAX, CONFIG_VAR_PROGRESS_ENGINE_WCE_MAX); + } +} + +void config_printer::print_nothing(const std::string & /*key*/, const std::string & /*title*/) +{ +} + +void config_printer::print_qp_compensation_level(const std::string &key, const std::string &title) +{ + LOG_NUM_PARAM(title.c_str(), m_mce_sys_var.qp_compensation_level, m_mce_sys_var.rx_num_wr / 2U, + key.c_str()); +} + +#if defined(DEFINED_NGINX) +void config_printer::print_nginx_workers_num(const std::string &key, const std::string &title) +{ + LOG_NUM_PARAM(title.c_str(), + (m_mce_sys_var.app.type == APP_NGINX ? m_mce_sys_var.app.workers_num + : MCE_DEFAULT_APP_WORKERS_NUM), + MCE_DEFAULT_APP_WORKERS_NUM, key.c_str()); +} +#endif + +#if defined(DEFINED_ENVOY) +void config_printer::print_envoy_workers_num(const std::string &key, const std::string &title) +{ + LOG_NUM_PARAM(title.c_str(), + (m_mce_sys_var.app.type == APP_ENVOY ? m_mce_sys_var.app.workers_num + : MCE_DEFAULT_APP_WORKERS_NUM), + MCE_DEFAULT_APP_WORKERS_NUM, key.c_str()); +} +#endif + +std::string config_printer::to_str_accurate(size_t size) +{ + static const char *suffixes[] = {"", "K", "M", "G", nullptr}; + int sfx_idx = 0; + + while ((size > 0 && size % 1024U == 0) && suffixes[sfx_idx + 1]) { + ++sfx_idx; + size /= 1024U; + } + + return std::to_string(size) + suffixes[sfx_idx]; +} + +/** + * @brief Shows a configured int64 value using log messages + * @param key Configuration parameter name + * @param title Configuration parameter title + * @param param_descriptor Configuration parameter descriptor. nullptr for sub-objects. + * @param element Current value for the parameter, as any + * @param def_value_any Default value for the parameter, as std::any. + When there is no default value defined, it is empty + */ +void config_printer::print_int64_config_element(const std::string &key, const std::string &title, + const parameter_descriptor *param_descriptor, + const std::experimental::any &element, + const std::experimental::any &def_value_any) +{ + // def_value is of type void when not given + + int64_t def_value = (def_value_any.type() == typeid(int64_t)) + ? std::experimental::any_cast(def_value_any) + : 0; + int64_t cur_value = std::experimental::any_cast(element); + // Check if this int has a string mapping + std::string def_value_str; + std::string cur_value_str; + if (param_descriptor && param_descriptor->has_string_mappings()) { + def_value_str = param_descriptor->convert_int64_to_mapped_string_or( + def_value, std::to_string(def_value) + "(Invalid value)"); + cur_value_str = param_descriptor->convert_int64_to_mapped_string_or( + cur_value, std::to_string(cur_value) + "(Invalid value)"); + } + // No string ? Show the number as-is (But use K/M/G suffixes if it is a power of 1K, for + // clarity) + if (cur_value_str.empty()) { + LOG_NUM_PARAM_AS(title.c_str(), cur_value, def_value, key.c_str(), + to_str_accurate(cur_value).c_str()); + } else { + LOG_STR_PARAM_AS(title.c_str(), cur_value_str.c_str(), def_value_str.c_str(), key.c_str(), + cur_value_str.c_str()); + } +} + +/** + * @brief Shows a configured boolean value using log messages + * @param key Configuration parameter name + * @param title Configuration parameter title + * @param param_descriptor Configuration parameter descriptor. nullptr for sub-objects. + * @param element Current value for the parameter, as any + * @param def_value_any Default value for the parameter, as std::any. + When there is no default value defined, it is empty + */ +void config_printer::print_bool_config_element(const std::string &key, const std::string &title, + const std::experimental::any &element, + const std::experimental::any &def_value_any) +{ + // def_value is of type void when not given + + bool def_value = (def_value_any.type() == typeid(bool)) + ? std::experimental::any_cast(def_value_any) + : false; + bool cur_value = std::experimental::any_cast(element); + LOG_BOOL_PARAM(title.c_str(), cur_value, def_value, key.c_str()); +} + +/** + * @brief Shows a configured string value using log messages + * @param key Configuration parameter name + * @param title Configuration parameter title + * @param param_descriptor Configuration parameter descriptor. nullptr for sub-objects. + * @param element Current value for the parameter, as any + * @param def_value_any Default value for the parameter, as std::any. + When there is no default value defined, it is empty + */ +void config_printer::print_string_config_element(const std::string &key, const std::string &title, + const std::experimental::any &element, + const std::experimental::any &def_value_any) +{ + // def_value is of type void when not given + std::string def_value = (def_value_any.type() == typeid(std::string)) + ? std::experimental::any_cast(def_value_any) + : std::string(); + std::string cur_value = std::experimental::any_cast(element); + LOG_NUM_PARAM_AS(title.c_str(), cur_value, def_value, key.c_str(), cur_value.c_str()); +} + +/* + * This part of the code shows the value of all non-default config variables + * using log messages. + * Since configuration parameters may be nested, and not only in a simple hierarchy of scalars but + * also as vectors or maps, we need to dig into vectors and maps and show them using the same method + * as we use for scalars. + * Here is how we show scalars: + + XLIO INFO : TX ring allocation logic per_socket [performance.rings.tx.allocation_logic] + XLIO INFO : RX poll duration (µsec) 2049 [performance.polling.blocking_rx_poll_usec] + + * And here is how we show a vector of maps: + + XLIO INFO : Acceleration control rules [acceleration_control.rules] XLIO INFO : Action 0 of + rule 1 [acceleration_control.rules[0].actions[0]] XLIO INFO : 1 + [acceleration_control.rules[0].id] XLIO INFO : a + [acceleration_control.rules[0].name] XLIO INFO : Action 0 of rule + 2 [acceleration_control.rules[1].actions[0]] XLIO INFO : xx + [acceleration_control.rules[1].actions[1]] + + */ + +/* + * Helper functions to print configured values using log messages + */ + +/** + * @brief Shows a configured vector of values using log messages + * @param key Configuration parameter name + * @param title Configuration parameter title + * @param element Current value for the parameter, as any (It is actually std::vector) + */ +void config_printer::print_config_vector(const std::string &key, const std::string &title, + const std::experimental::any &element) +{ + auto cur_value_vector = + std::experimental::any_cast>(element); + // When a vector is not empty and has a title, show the title + if (cur_value_vector.size() > 0 && !title.empty()) { + // "Because "dummy" is not an empty string, this line will be shown the moment the vector is + // not empty + LOG_NUM_PARAM_AS(title.c_str(), std::string(), std::string("dummy"), key.c_str(), ""); + } + + for (auto it = cur_value_vector.cbegin(); it != cur_value_vector.cend(); ++it) { + const auto &cur_value_any = *it; + + // If there are more than 4 elements, show only the first two and the last 2, + // as we do not want to flood the output in case there are + // many elements in the vector. + + /* + Here is how it looks when there are 8 elements in the vector: + + XLIO INFO : Action-0-0 + [acceleration_control.rules[0].actions[0]] XLIO INFO : 0 + [acceleration_control.rules[0].id] XLIO INFO : Zero + [acceleration_control.rules[0].name] XLIO INFO : Action-1-0 + [acceleration_control.rules[1].actions[0]] XLIO INFO : 1 + [acceleration_control.rules[1].id] XLIO INFO : One + [acceleration_control.rules[1].name] XLIO INFO : 4 element(s) not shown + [acceleration_control.rules[2-5]] XLIO INFO : Action-6-0 + [acceleration_control.rules[5].actions[0]] XLIO INFO : 6 + [acceleration_control.rules[6].id] XLIO INFO : Six + [acceleration_control.rules[6].name] XLIO INFO : Action-7-0 + [acceleration_control.rules[7].actions[0]] XLIO INFO : 7 + [acceleration_control.rules[7].id] XLIO INFO : Seven + [acceleration_control.rules[7].name] + + */ + const int amount_to_show = 2; + unsigned int index = it - cur_value_vector.begin(); + // show the first 2 and the last 2 elements + if ((index < amount_to_show) || (index >= cur_value_vector.size() - amount_to_show)) { + print_config_element(key + "[" + std::to_string(index) + "]", nullptr, cur_value_any, + std::experimental::any()); + } + // If we are here, there are elements which are not shown. Show which elements are not + // shown. + else if (index == amount_to_show) { + std::string msg = key + "[" + std::to_string(amount_to_show); + if (cur_value_vector.size() > amount_to_show * 2 + 1) { + msg += "-" + std::to_string(cur_value_vector.size() - amount_to_show - 1); + } + msg += "]"; + std::string title1 = std::to_string(cur_value_vector.size() - amount_to_show * 2) + + " element(s) not shown"; + LOG_NUM_PARAM_AS(title1.c_str(), std::string(), std::string("dummy"), msg.c_str(), ""); + } + } +} + +/** + * @brief Shows a configured map of values using log messages + * @param key Configuration parameter name + * @param title Configuration parameter title + * @param element Current value for the parameter, as any (It is actually std::map) + */ +void config_printer::print_config_map(const std::string &key, const std::experimental::any &element) +{ + auto cur_value_map = + std::experimental::any_cast>(element); + for (const auto &it : cur_value_map) { + const auto &cur_value_any = it.second; + print_config_element(key + "." + it.first, nullptr, cur_value_any, + std::experimental::any()); + } +} + +/** + * @brief Shows configured values using log messages + * @param key Configuration parameter name + * @param param_descriptor Configuration parameter descriptor. nullptr for sub-objects. + * @param element Current value for the parameter, as any + * @param def_value_any Default value for the parameter, as std::any. + When there is no default value defined, it is empty + */ +void config_printer::print_config_element(const std::string &key, + const parameter_descriptor *param_descriptor, + const std::experimental::any &element, + const std::experimental::any &def_value_any) +{ + const std::string title = param_descriptor ? param_descriptor->get_title().value_or("") : ""; + const std::type_info &type = element.type(); + + if (type == typeid(bool)) { + print_bool_config_element(key, title, element, def_value_any); + } else if (type == typeid(std::string)) { + print_string_config_element(key, title, element, def_value_any); + } else if (type == typeid(int64_t)) { + print_int64_config_element(key, title, param_descriptor, element, def_value_any); + } else if (type == typeid(std::vector)) { + print_config_vector(key, title, element); + } else if (type == typeid(std::map)) { + print_config_map(key, element); + } else { + vlog_printf(VLOG_ERROR, "%s : Unsupported type: %s\n", key.c_str(), type.name()); + throw_xlio_exception("Unsupported type - See error output for details\n"); + } +} diff --git a/src/core/config_printer.h b/src/core/config_printer.h new file mode 100644 index 000000000..34a41f55e --- /dev/null +++ b/src/core/config_printer.h @@ -0,0 +1,100 @@ +/* + * SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES + * Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause + */ + +#pragma once + +#include +#include + +#include "config.h" + +// fwd declarations +struct mce_sys_var; +class parameter_descriptor; + +/** + * This class shows configured values using log messages. + * Normally, only non-default valules are shown. + * When the debug level is set to DETAILS, all values are shown. + */ + +class config_printer { +public: + config_printer(const mce_sys_var &mce_sys_var); + + /** + * @brief Shows configured values using log messages. Normally, only non-default values + * are shown. When the debug level is set to DETAILS, all values are shown. + */ + void print_to_log(); + +private: + /** + * @brief Translates a number to a string, using K/M/G suffix as appropriate, without rounding. + * Example: 1024 -> "1K", 2048 -> "2K", 1024*1024 -> "1M", 7*1024*1024 -> "7M", + * 1024*1024*1024 -> "1G" + * NOTE unlike option_size::to_str(): 10241 -> "10241" and not "10K" + * @param size The number to translate + * @return The translated string + */ + std::string to_str_accurate(size_t size); + + /** + * @brief Shows configured values using log messages + * @param key Configuration parameter name + * @param param_descriptor Configuration parameter descriptor. nullptr for sub-objects. + * @param element Current value for the parameter, as any + * @param def_value_any Default value for the parameter, as std::any. + When there is no default value defined, it is empty + */ + void print_config_element(const std::string &key, const parameter_descriptor *param_descriptor, + const std::experimental::any &element, + const std::experimental::any &def_value_any); + + // Helpers to print_config_element() + // Each halper handles an element of a specific type + void print_int64_config_element(const std::string &key, const std::string &title, + const parameter_descriptor *param_descriptor, + const std::experimental::any &element, + const std::experimental::any &def_value_any); + + void print_bool_config_element(const std::string &key, const std::string &title, + const std::experimental::any &element, + const std::experimental::any &def_value_any); + + void print_string_config_element(const std::string &key, const std::string &title, + const std::experimental::any &element, + const std::experimental::any &def_value_any); + + void print_config_vector(const std::string &key, const std::string &title, + const std::experimental::any &element); + + void print_config_map(const std::string &key, const std::experimental::any &element); + + // Print functions for specific fields which we handle in a non-standard way + void print_log_level(const std::string &key, const std::string &title); + + void print_rx_num_wre(const std::string &key, const std::string &title); + + void print_rx_num_wre_to_post_recv(const std::string &key, const std::string &title); + + void print_progress_engine_interval(const std::string &key, const std::string &title); + + void print_nothing(const std::string & /*key*/, const std::string & /*title*/); + + void print_qp_compensation_level(const std::string &key, const std::string &title); + +#if defined(DEFINED_NGINX) + void print_nginx_workers_num(const std::string &key, const std::string &title); +#endif + +#if defined(DEFINED_ENVOY) + void print_envoy_workers_num(const std::string &key, const std::string &title); +#endif + + // Members + const mce_sys_var &m_mce_sys_var; +}; diff --git a/src/core/main.cpp b/src/core/main.cpp index aa0db4711..22b399107 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -9,7 +9,7 @@ #endif #include "main.h" - +#include "config_printer.h" #include #include #include @@ -54,6 +54,7 @@ #include "sock/sockinfo_udp.h" #include "sock/bind_no_port.h" #include "iomux/io_mux_call.h" +#include "config/descriptors/config_descriptor.h" #include "util/instrumentation.h" #include "util/agent.h" @@ -879,48 +880,11 @@ void print_xlio_global_settings() vlog_printf(VLOG_INFO, "---------------------------------------------------------------------------\n"); - if (safe_mce_sys().mce_spec != MCE_SPEC_NONE) { - vlog_printf(VLOG_INFO, FORMAT_STRING, "Spec", - xlio_spec::to_str((xlio_spec_t)safe_mce_sys().mce_spec), CONFIG_VAR_SPEC); - } - - VLOG_STR_PARAM_STRING("Log Level", log_level::to_str(safe_mce_sys().log_level), "", - CONFIG_VAR_LOG_LEVEL, log_level::to_str(safe_mce_sys().log_level)); - VLOG_PARAM_NUMBER("Log Details", safe_mce_sys().log_details, MCE_DEFAULT_LOG_DETAILS, - CONFIG_VAR_LOG_DETAILS); - VLOG_PARAM_BOOL("Log Colors", safe_mce_sys().log_colors, MCE_DEFAULT_LOG_COLORS, - CONFIG_VAR_LOG_COLORS); - VLOG_STR_PARAM_STRING("Log File", safe_mce_sys().log_filename, MCE_DEFAULT_LOG_FILE, - CONFIG_VAR_LOG_FILENAME, safe_mce_sys().log_filename); - VLOG_STR_PARAM_STRING("Stats File", safe_mce_sys().stats_filename, MCE_DEFAULT_STATS_FILE, - CONFIG_VAR_STATS_FILENAME, safe_mce_sys().stats_filename); - VLOG_STR_PARAM_STRING("Stats shared memory directory", safe_mce_sys().stats_shmem_dirname, - MCE_DEFAULT_STATS_SHMEM_DIR, CONFIG_VAR_STATS_SHMEM_DIRNAME, - safe_mce_sys().stats_shmem_dirname); - VLOG_STR_PARAM_STRING("SERVICE output directory", safe_mce_sys().service_notify_dir, - MCE_DEFAULT_SERVICE_FOLDER, CONFIG_VAR_SERVICE_DIR, - safe_mce_sys().service_notify_dir); - VLOG_PARAM_NUMBER("Stats FD Num (max)", safe_mce_sys().stats_fd_num_max, - MCE_DEFAULT_STATS_FD_NUM, CONFIG_VAR_STATS_FD_NUM); - VLOG_STR_PARAM_STRING("Application ID", safe_mce_sys().app_id, MCE_DEFAULT_APP_ID, - CONFIG_VAR_APPLICATION_ID, safe_mce_sys().app_id); - VLOG_PARAM_BOOL("Polling CPU idle usage", safe_mce_sys().select_handle_cpu_usage_stats, - MCE_DEFAULT_SELECT_CPU_USAGE_STATS, CONFIG_VAR_SELECT_CPU_USAGE_STATS); - VLOG_PARAM_BOOL("SigIntr Ctrl-C Handle", safe_mce_sys().handle_sigintr, - MCE_DEFAULT_HANDLE_SIGINTR, CONFIG_VAR_HANDLE_SIGINTR); - VLOG_PARAM_BOOL("SegFault Backtrace", safe_mce_sys().handle_segfault, - MCE_DEFAULT_HANDLE_SIGFAULT, CONFIG_VAR_HANDLE_SIGSEGV); - VLOG_PARAM_STRING("Print a report", safe_mce_sys().print_report, MCE_DEFAULT_PRINT_REPORT, - CONFIG_VAR_PRINT_REPORT, option_3::to_str(safe_mce_sys().print_report)); - VLOG_PARAM_BOOL("Quick start", safe_mce_sys().quick_start, MCE_DEFAULT_QUICK_START, - CONFIG_VAR_QUICK_START); + config_printer printer = config_printer(safe_mce_sys()); + printer.print_to_log(); + // Once we are done with the registry, destroy it to free the memory + safe_mce_sys().destroy_registry(); - VLOG_PARAM_NUMSTR("Ring allocation logic TX", safe_mce_sys().ring_allocation_logic_tx, - MCE_DEFAULT_RING_ALLOCATION_LOGIC_TX, CONFIG_VAR_RING_ALLOCATION_LOGIC_TX, - ring_logic_str(safe_mce_sys().ring_allocation_logic_tx)); - VLOG_PARAM_NUMSTR("Ring allocation logic RX", safe_mce_sys().ring_allocation_logic_rx, - MCE_DEFAULT_RING_ALLOCATION_LOGIC_RX, CONFIG_VAR_RING_ALLOCATION_LOGIC_RX, - ring_logic_str(safe_mce_sys().ring_allocation_logic_rx)); if (safe_mce_sys().ring_allocation_logic_rx == RING_LOGIC_PER_USER_ID) { vlog_printf(VLOG_WARNING, "user_id is not supported using " @@ -935,328 +899,6 @@ void print_xlio_global_settings() safe_mce_sys().ring_allocation_logic_tx = MCE_DEFAULT_RING_ALLOCATION_LOGIC_TX; } - VLOG_PARAM_NUMBER("Ring migration ratio TX", safe_mce_sys().ring_migration_ratio_tx, - (safe_mce_sys().enable_tso ? -1 : MCE_DEFAULT_RING_MIGRATION_RATIO_TX), - CONFIG_VAR_RING_MIGRATION_RATIO_TX); - VLOG_PARAM_NUMBER("Ring migration ratio RX", safe_mce_sys().ring_migration_ratio_rx, - MCE_DEFAULT_RING_MIGRATION_RATIO_RX, CONFIG_VAR_RING_MIGRATION_RATIO_RX); - - if (safe_mce_sys().ring_limit_per_interface) { - VLOG_PARAM_NUMBER("Ring limit per interface", safe_mce_sys().ring_limit_per_interface, - MCE_DEFAULT_RING_LIMIT_PER_INTERFACE, - CONFIG_VAR_RING_LIMIT_PER_INTERFACE); - } else { - VLOG_PARAM_NUMSTR("Ring limit per interface", safe_mce_sys().ring_limit_per_interface, - MCE_DEFAULT_RING_LIMIT_PER_INTERFACE, CONFIG_VAR_RING_LIMIT_PER_INTERFACE, - "(no limit)"); - } - - VLOG_PARAM_NUMBER("Ring On Device Memory TX", safe_mce_sys().ring_dev_mem_tx, - MCE_DEFAULT_RING_DEV_MEM_TX, CONFIG_VAR_RING_DEV_MEM_TX); - - VLOG_PARAM_STRING("Zerocopy Cache Threshold", safe_mce_sys().zc_cache_threshold, - MCE_DEFAULT_ZC_CACHE_THRESHOLD, CONFIG_VAR_ZC_CACHE_THRESHOLD, - option_size::to_str(safe_mce_sys().zc_cache_threshold)); - VLOG_PARAM_STRING("Tx Mem Buf size", safe_mce_sys().tx_buf_size, MCE_DEFAULT_TX_BUF_SIZE, - CONFIG_VAR_TX_BUF_SIZE, option_size::to_str(safe_mce_sys().tx_buf_size)); - VLOG_PARAM_NUMBER("Tx QP WRE", safe_mce_sys().tx_num_wr, MCE_DEFAULT_TX_NUM_WRE, - CONFIG_VAR_TX_NUM_WRE); - VLOG_PARAM_NUMBER("Tx QP WRE Batching", safe_mce_sys().tx_num_wr_to_signal, - MCE_DEFAULT_TX_NUM_WRE_TO_SIGNAL, CONFIG_VAR_TX_NUM_WRE_TO_SIGNAL); - VLOG_PARAM_NUMBER("Tx Max QP INLINE", safe_mce_sys().tx_max_inline, MCE_DEFAULT_TX_MAX_INLINE, - CONFIG_VAR_TX_MAX_INLINE); - VLOG_PARAM_BOOL("Tx MC Loopback", safe_mce_sys().tx_mc_loopback_default, - MCE_DEFAULT_TX_MC_LOOPBACK, CONFIG_VAR_TX_MC_LOOPBACK); - VLOG_PARAM_BOOL("Tx non-blocked eagains", safe_mce_sys().tx_nonblocked_eagains, - MCE_DEFAULT_TX_NONBLOCKED_EAGAINS, CONFIG_VAR_TX_NONBLOCKED_EAGAINS); - VLOG_PARAM_NUMBER("Tx Prefetch Bytes", safe_mce_sys().tx_prefetch_bytes, - MCE_DEFAULT_TX_PREFETCH_BYTES, CONFIG_VAR_TX_PREFETCH_BYTES); - VLOG_PARAM_NUMBER("Tx Bufs Batch TCP", safe_mce_sys().tx_bufs_batch_tcp, - MCE_DEFAULT_TX_BUFS_BATCH_TCP, CONFIG_VAR_TX_BUFS_BATCH_TCP); - VLOG_PARAM_NUMBER("Tx Segs Batch TCP", safe_mce_sys().tx_segs_batch_tcp, - MCE_DEFAULT_TX_SEGS_BATCH_TCP, CONFIG_VAR_TX_SEGS_BATCH_TCP); - VLOG_PARAM_NUMBER("Tx Segs Ring Batch TCP", safe_mce_sys().tx_segs_ring_batch_tcp, - MCE_DEFAULT_TX_SEGS_RING_BATCH_TCP, CONFIG_VAR_TX_SEGS_RING_BATCH_TCP); - VLOG_PARAM_STRING("TCP Send Buffer size", safe_mce_sys().tcp_send_buffer_size, - MCE_DEFAULT_TCP_SEND_BUFFER_SIZE, CONFIG_VAR_TCP_SEND_BUFFER_SIZE, - option_size::to_str(safe_mce_sys().tcp_send_buffer_size)); - VLOG_PARAM_NUMBER( - "Rx QP WRE", safe_mce_sys().rx_num_wr, - (safe_mce_sys().enable_striding_rq ? MCE_DEFAULT_STRQ_NUM_WRE : MCE_DEFAULT_RX_NUM_WRE), - CONFIG_VAR_RX_NUM_WRE); - VLOG_PARAM_NUMBER("Rx QP WRE Batching", safe_mce_sys().rx_num_wr_to_post_recv, - (safe_mce_sys().enable_striding_rq ? MCE_DEFAULT_STRQ_NUM_WRE_TO_POST_RECV - : MCE_DEFAULT_RX_NUM_WRE_TO_POST_RECV), - CONFIG_VAR_RX_NUM_WRE_TO_POST_RECV); - VLOG_PARAM_NUMBER("Rx Byte Min Limit", safe_mce_sys().rx_ready_byte_min_limit, - MCE_DEFAULT_RX_BYTE_MIN_LIMIT, CONFIG_VAR_RX_BYTE_MIN_LIMIT); - VLOG_PARAM_NUMBER("Rx Poll Loops", safe_mce_sys().rx_poll_num, MCE_DEFAULT_RX_NUM_POLLS, - CONFIG_VAR_RX_NUM_POLLS); - VLOG_PARAM_NUMBER("Rx Poll Init Loops", safe_mce_sys().rx_poll_num_init, - MCE_DEFAULT_RX_NUM_POLLS_INIT, CONFIG_VAR_RX_NUM_POLLS_INIT); - if (safe_mce_sys().rx_udp_poll_os_ratio) { - VLOG_PARAM_NUMBER("Rx UDP Poll OS Ratio", safe_mce_sys().rx_udp_poll_os_ratio, - MCE_DEFAULT_RX_UDP_POLL_OS_RATIO, CONFIG_VAR_RX_UDP_POLL_OS_RATIO); - } else { - VLOG_PARAM_NUMSTR("Rx UDP Poll OS Ratio", safe_mce_sys().rx_udp_poll_os_ratio, - MCE_DEFAULT_RX_UDP_POLL_OS_RATIO, CONFIG_VAR_RX_UDP_POLL_OS_RATIO, - "(Disabled)"); - } - - VLOG_PARAM_NUMBER("HW TS Conversion", safe_mce_sys().hw_ts_conversion_mode, - MCE_DEFAULT_HW_TS_CONVERSION_MODE, CONFIG_VAR_HW_TS_CONVERSION_MODE); - - if (safe_mce_sys().rx_poll_yield_loops) { - VLOG_PARAM_NUMBER("Rx Poll Yield", safe_mce_sys().rx_poll_yield_loops, - MCE_DEFAULT_RX_POLL_YIELD, CONFIG_VAR_RX_POLL_YIELD); - } else { - VLOG_PARAM_STRING("Rx Poll Yield", safe_mce_sys().rx_poll_yield_loops, - MCE_DEFAULT_RX_POLL_YIELD, CONFIG_VAR_RX_POLL_YIELD, "Disabled"); - } - VLOG_PARAM_NUMBER("Rx Prefetch Bytes", safe_mce_sys().rx_prefetch_bytes, - MCE_DEFAULT_RX_PREFETCH_BYTES, CONFIG_VAR_RX_PREFETCH_BYTES); - - VLOG_PARAM_NUMBER("Rx Prefetch Bytes Before Poll", safe_mce_sys().rx_prefetch_bytes_before_poll, - MCE_DEFAULT_RX_PREFETCH_BYTES_BEFORE_POLL, - CONFIG_VAR_RX_PREFETCH_BYTES_BEFORE_POLL); - - if (safe_mce_sys().rx_cq_drain_rate_nsec == MCE_RX_CQ_DRAIN_RATE_DISABLED) { - VLOG_PARAM_STRING("Rx CQ Drain Rate", safe_mce_sys().rx_cq_drain_rate_nsec, - MCE_DEFAULT_RX_CQ_DRAIN_RATE, CONFIG_VAR_RX_CQ_DRAIN_RATE_NSEC, - "Disabled"); - } else { - VLOG_PARAM_NUMBER("Rx CQ Drain Rate (nsec)", safe_mce_sys().rx_cq_drain_rate_nsec, - MCE_DEFAULT_RX_CQ_DRAIN_RATE, CONFIG_VAR_RX_CQ_DRAIN_RATE_NSEC); - } - - VLOG_PARAM_NUMBER("GRO max streams", safe_mce_sys().gro_streams_max, - MCE_DEFAULT_GRO_STREAMS_MAX, CONFIG_VAR_GRO_STREAMS_MAX); - VLOG_PARAM_BOOL("Disable flow tag", safe_mce_sys().disable_flow_tag, - MCE_DEFAULT_DISABLE_FLOW_TAG, CONFIG_VAR_DISABLE_FLOW_TAG); - - VLOG_PARAM_BOOL("TCP 2T rules", safe_mce_sys().tcp_2t_rules, MCE_DEFAULT_TCP_2T_RULES, - CONFIG_VAR_TCP_2T_RULES); - VLOG_PARAM_BOOL("TCP 3T rules", safe_mce_sys().tcp_3t_rules, MCE_DEFAULT_TCP_3T_RULES, - CONFIG_VAR_TCP_3T_RULES); - VLOG_PARAM_BOOL("UDP 3T rules", safe_mce_sys().udp_3t_rules, MCE_DEFAULT_UDP_3T_RULES, - CONFIG_VAR_UDP_3T_RULES); - VLOG_PARAM_BOOL("ETH MC L2 only rules", safe_mce_sys().eth_mc_l2_only_rules, - MCE_DEFAULT_ETH_MC_L2_ONLY_RULES, CONFIG_VAR_ETH_MC_L2_ONLY_RULES); - VLOG_PARAM_BOOL("Force Flowtag for MC", safe_mce_sys().mc_force_flowtag, - MCE_DEFAULT_MC_FORCE_FLOWTAG, CONFIG_VAR_MC_FORCE_FLOWTAG); - VLOG_PARAM_BOOL("Striding RQ", safe_mce_sys().enable_strq_env, MCE_DEFAULT_STRQ, - CONFIG_VAR_STRQ); - VLOG_PARAM_NUMBER("STRQ Strides per RWQE", safe_mce_sys().strq_stride_num_per_rwqe, - MCE_DEFAULT_STRQ_NUM_STRIDES, CONFIG_VAR_STRQ_NUM_STRIDES); - VLOG_PARAM_NUMBER("STRQ Stride Size (Bytes)", safe_mce_sys().strq_stride_size_bytes, - MCE_DEFAULT_STRQ_STRIDE_SIZE_BYTES, CONFIG_VAR_STRQ_STRIDE_SIZE_BYTES); - VLOG_PARAM_NUMBER( - "STRQ Strides Compensation Level", safe_mce_sys().strq_strides_compensation_level, - MCE_DEFAULT_STRQ_STRIDES_COMPENSATION_LEVEL, CONFIG_VAR_STRQ_STRIDES_COMPENSATION_LEVEL); - VLOG_PARAM_NUMBER("Select Poll (usec)", safe_mce_sys().select_poll_num, - MCE_DEFAULT_SELECT_NUM_POLLS, CONFIG_VAR_SELECT_NUM_POLLS); - - if (safe_mce_sys().select_poll_os_ratio) { - VLOG_PARAM_NUMBER("Select Poll OS Ratio", safe_mce_sys().select_poll_os_ratio, - MCE_DEFAULT_SELECT_POLL_OS_RATIO, CONFIG_VAR_SELECT_POLL_OS_RATIO); - } else { - VLOG_PARAM_NUMSTR("Select Poll OS Ratio", safe_mce_sys().select_poll_os_ratio, - MCE_DEFAULT_SELECT_POLL_OS_RATIO, CONFIG_VAR_SELECT_POLL_OS_RATIO, - "(Disabled)"); - } - - if (safe_mce_sys().select_skip_os_fd_check) { - VLOG_PARAM_NUMBER("Select Skip OS", safe_mce_sys().select_skip_os_fd_check, - MCE_DEFAULT_SELECT_SKIP_OS, CONFIG_VAR_SELECT_SKIP_OS); - } else { - VLOG_PARAM_NUMSTR("Select Skip OS", safe_mce_sys().select_skip_os_fd_check, - MCE_DEFAULT_SELECT_SKIP_OS, CONFIG_VAR_SELECT_SKIP_OS, "(Disabled)"); - } - - if (safe_mce_sys().progress_engine_interval_msec == MCE_CQ_DRAIN_INTERVAL_DISABLED || - safe_mce_sys().progress_engine_wce_max == 0) { - vlog_printf(VLOG_INFO, FORMAT_NUMSTR, "CQ Drain Interval (msec)", - safe_mce_sys().progress_engine_interval_msec, "(Disabled)", - CONFIG_VAR_PROGRESS_ENGINE_INTERVAL); - } else { - VLOG_PARAM_NUMBER("CQ Drain Interval (msec)", safe_mce_sys().progress_engine_interval_msec, - MCE_DEFAULT_PROGRESS_ENGINE_INTERVAL_MSEC, - CONFIG_VAR_PROGRESS_ENGINE_INTERVAL); - VLOG_PARAM_NUMBER("CQ Drain WCE (max)", safe_mce_sys().progress_engine_wce_max, - MCE_DEFAULT_PROGRESS_ENGINE_WCE_MAX, CONFIG_VAR_PROGRESS_ENGINE_WCE_MAX); - } - - VLOG_PARAM_BOOL("CQ Interrupts Moderation", safe_mce_sys().cq_moderation_enable, - MCE_DEFAULT_CQ_MODERATION_ENABLE, CONFIG_VAR_CQ_MODERATION_ENABLE); - VLOG_PARAM_NUMBER("CQ Moderation Count", safe_mce_sys().cq_moderation_count, - MCE_DEFAULT_CQ_MODERATION_COUNT, CONFIG_VAR_CQ_MODERATION_COUNT); - VLOG_PARAM_NUMBER("CQ Moderation Period (usec)", safe_mce_sys().cq_moderation_period_usec, - MCE_DEFAULT_CQ_MODERATION_PERIOD_USEC, CONFIG_VAR_CQ_MODERATION_PERIOD_USEC); - VLOG_PARAM_NUMBER("CQ AIM Max Count", safe_mce_sys().cq_aim_max_count, - MCE_DEFAULT_CQ_AIM_MAX_COUNT, CONFIG_VAR_CQ_AIM_MAX_COUNT); - VLOG_PARAM_NUMBER("CQ AIM Max Period (usec)", safe_mce_sys().cq_aim_max_period_usec, - MCE_DEFAULT_CQ_AIM_MAX_PERIOD_USEC, CONFIG_VAR_CQ_AIM_MAX_PERIOD_USEC); - if (safe_mce_sys().cq_aim_interval_msec == MCE_CQ_ADAPTIVE_MODERATION_DISABLED) { - vlog_printf(VLOG_INFO, FORMAT_STRING, "CQ Adaptive Moderation", "Disabled", - CONFIG_VAR_CQ_AIM_INTERVAL_MSEC); - } else { - VLOG_PARAM_NUMBER("CQ AIM Interval (msec)", safe_mce_sys().cq_aim_interval_msec, - MCE_DEFAULT_CQ_AIM_INTERVAL_MSEC, CONFIG_VAR_CQ_AIM_INTERVAL_MSEC); - } - VLOG_PARAM_NUMBER( - "CQ AIM Interrupts Rate (per sec)", safe_mce_sys().cq_aim_interrupts_rate_per_sec, - MCE_DEFAULT_CQ_AIM_INTERRUPTS_RATE_PER_SEC, CONFIG_VAR_CQ_AIM_INTERRUPTS_RATE_PER_SEC); - - VLOG_PARAM_NUMBER("CQ Poll Batch (max)", safe_mce_sys().cq_poll_batch_max, - MCE_DEFAULT_CQ_POLL_BATCH, CONFIG_VAR_CQ_POLL_BATCH_MAX); - VLOG_PARAM_BOOL("CQ Keeps QP Full", safe_mce_sys().cq_keep_qp_full, MCE_DEFAULT_CQ_KEEP_QP_FULL, - CONFIG_VAR_CQ_KEEP_QP_FULL); - VLOG_PARAM_NUMBER("QP Compensation Level", safe_mce_sys().qp_compensation_level, - safe_mce_sys().rx_num_wr / 2U, CONFIG_VAR_QP_COMPENSATION_LEVEL); - VLOG_PARAM_BOOL("Offloaded Sockets", safe_mce_sys().offloaded_sockets, - MCE_DEFAULT_OFFLOADED_SOCKETS, CONFIG_VAR_OFFLOADED_SOCKETS); - VLOG_PARAM_NUMBER("Timer Resolution (msec)", safe_mce_sys().timer_resolution_msec, - MCE_DEFAULT_TIMER_RESOLUTION_MSEC, CONFIG_VAR_TIMER_RESOLUTION_MSEC); - VLOG_PARAM_NUMBER("TCP Timer Resolution (msec)", safe_mce_sys().tcp_timer_resolution_msec, - MCE_DEFAULT_TCP_TIMER_RESOLUTION_MSEC, CONFIG_VAR_TCP_TIMER_RESOLUTION_MSEC); - VLOG_PARAM_STRING( - "TCP control thread", option_tcp_ctl_thread::to_str(safe_mce_sys().tcp_ctl_thread), - option_tcp_ctl_thread::to_str(MCE_DEFAULT_TCP_CTL_THREAD), CONFIG_VAR_TCP_CTL_THREAD, - option_tcp_ctl_thread::to_str(safe_mce_sys().tcp_ctl_thread)); - VLOG_PARAM_NUMBER("TCP timestamp option", safe_mce_sys().tcp_ts_opt, - MCE_DEFAULT_TCP_TIMESTAMP_OPTION, CONFIG_VAR_TCP_TIMESTAMP_OPTION); - VLOG_PARAM_BOOL("TCP nodelay", safe_mce_sys().tcp_nodelay, MCE_DEFAULT_TCP_NODELAY, - CONFIG_VAR_TCP_NODELAY); - VLOG_PARAM_NUMBER("TCP nodelay treshold", safe_mce_sys().tcp_nodelay_treshold, - MCE_DEFAULT_TCP_NODELAY_TRESHOLD, CONFIG_VAR_TCP_NODELAY_TRESHOLD); - VLOG_PARAM_BOOL("TCP quickack", safe_mce_sys().tcp_quickack, MCE_DEFAULT_TCP_QUICKACK, - CONFIG_VAR_TCP_QUICKACK); - VLOG_PARAM_NUMSTR(xlio_exception_handling::getName(), (int)safe_mce_sys().exception_handling, - xlio_exception_handling::MODE_DEFAULT, xlio_exception_handling::getSysVar(), - safe_mce_sys().exception_handling.to_str()); - VLOG_PARAM_BOOL("Avoid sys-calls on tcp fd", safe_mce_sys().avoid_sys_calls_on_tcp_fd, - MCE_DEFAULT_AVOID_SYS_CALLS_ON_TCP_FD, CONFIG_VAR_AVOID_SYS_CALLS_ON_TCP_FD); - VLOG_PARAM_BOOL("Allow privileged sock opt", safe_mce_sys().allow_privileged_sock_opt, - MCE_DEFAULT_ALLOW_PRIVILEGED_SOCK_OPT, CONFIG_VAR_ALLOW_PRIVILEGED_SOCK_OPT); - VLOG_PARAM_NUMBER("Delay after join (msec)", safe_mce_sys().wait_after_join_msec, - MCE_DEFAULT_WAIT_AFTER_JOIN_MSEC, CONFIG_VAR_WAIT_AFTER_JOIN_MSEC); - VLOG_STR_PARAM_STRING("Internal Thread Affinity", safe_mce_sys().internal_thread_affinity_str, - MCE_DEFAULT_INTERNAL_THREAD_AFFINITY_STR, - CONFIG_VAR_INTERNAL_THREAD_AFFINITY, - safe_mce_sys().internal_thread_affinity_str); - VLOG_STR_PARAM_STRING("Internal Thread Cpuset", safe_mce_sys().internal_thread_cpuset, - MCE_DEFAULT_INTERNAL_THREAD_CPUSET, CONFIG_VAR_INTERNAL_THREAD_CPUSET, - safe_mce_sys().internal_thread_cpuset); - VLOG_PARAM_NUMSTR("Buffer batching mode", safe_mce_sys().buffer_batching_mode, - MCE_DEFAULT_BUFFER_BATCHING_MODE, CONFIG_VAR_BUFFER_BATCHING_MODE, - buffer_batching_mode_str(safe_mce_sys().buffer_batching_mode)); - VLOG_PARAM_STRING( - "Use hugepages", safe_mce_sys().mem_alloc_type, MCE_DEFAULT_MEM_ALLOC_TYPE, - CONFIG_VAR_MEM_ALLOC_TYPE, - (safe_mce_sys().mem_alloc_type == option_alloc_type::HUGE) ? "true" : "false"); - VLOG_PARAM_STRING("Memory limit", safe_mce_sys().memory_limit, MCE_DEFAULT_MEMORY_LIMIT, - CONFIG_VAR_MEMORY_LIMIT, option_size::to_str(safe_mce_sys().memory_limit)); - VLOG_PARAM_STRING("Memory limit (user allocator)", safe_mce_sys().memory_limit_user, - MCE_DEFAULT_MEMORY_LIMIT_USER, CONFIG_VAR_MEMORY_LIMIT_USER, - option_size::to_str(safe_mce_sys().memory_limit_user)); - VLOG_PARAM_STRING("Hugepage size", safe_mce_sys().hugepage_size, MCE_DEFAULT_HUGEPAGE_SIZE, - CONFIG_VAR_HUGEPAGE_SIZE, option_size::to_str(safe_mce_sys().hugepage_size)); - - VLOG_PARAM_NUMBER("Num of UC ARPs", safe_mce_sys().neigh_uc_arp_quata, - MCE_DEFAULT_NEIGH_UC_ARP_QUATA, CONFIG_VAR_NEIGH_UC_ARP_QUATA); - VLOG_PARAM_NUMBER("UC ARP delay (msec)", safe_mce_sys().neigh_wait_till_send_arp_msec, - MCE_DEFAULT_NEIGH_UC_ARP_DELAY_MSEC, CONFIG_VAR_NEIGH_UC_ARP_DELAY_MSEC); - VLOG_PARAM_NUMBER("Num of neigh restart retries", safe_mce_sys().neigh_num_err_retries, - MCE_DEFAULT_NEIGH_NUM_ERR_RETRIES, CONFIG_VAR_NEIGH_NUM_ERR_RETRIES); - VLOG_STR_PARAM_STRING("TSO support", option_3::to_str(safe_mce_sys().enable_tso), - option_3::to_str(MCE_DEFAULT_TSO), CONFIG_VAR_TSO, - option_3::to_str(safe_mce_sys().enable_tso)); - VLOG_PARAM_STRING("TSO max size", safe_mce_sys().max_tso_sz, MCE_DEFAULT_MAX_TSO_SIZE, - CONFIG_VAR_MAX_TSO_SIZE, option_size::to_str(safe_mce_sys().max_tso_sz)); - VLOG_STR_PARAM_STRING("LRO support", option_3::to_str(safe_mce_sys().enable_lro), - option_3::to_str(MCE_DEFAULT_LRO), CONFIG_VAR_LRO, - option_3::to_str(safe_mce_sys().enable_lro)); -#ifdef DEFINED_UTLS - VLOG_PARAM_BOOL("UTLS RX support", safe_mce_sys().enable_utls_rx, MCE_DEFAULT_UTLS_RX, - CONFIG_VAR_UTLS_RX); - VLOG_PARAM_BOOL("UTLS TX support", safe_mce_sys().enable_utls_tx, MCE_DEFAULT_UTLS_TX, - CONFIG_VAR_UTLS_TX); - VLOG_PARAM_NUMBER("UTLS high watermark DEK cache size", - static_cast(safe_mce_sys().utls_high_wmark_dek_cache_size), - MCE_DEFAULT_UTLS_HIGH_WMARK_DEK_CACHE_SIZE, - CONFIG_VAR_UTLS_HIGH_WMARK_DEK_CACHE_SIZE); - VLOG_PARAM_NUMBER("UTLS low watermark DEK cache size", - static_cast(safe_mce_sys().utls_low_wmark_dek_cache_size), - MCE_DEFAULT_UTLS_LOW_WMARK_DEK_CACHE_SIZE, - CONFIG_VAR_UTLS_LOW_WMARK_DEK_CACHE_SIZE); -#endif /* DEFINED_UTLS */ -#if defined(DEFINED_NGINX) - VLOG_PARAM_NUMBER("Number of Nginx workers", - (safe_mce_sys().app.type == APP_NGINX ? safe_mce_sys().app.workers_num - : MCE_DEFAULT_APP_WORKERS_NUM), - MCE_DEFAULT_APP_WORKERS_NUM, CONFIG_VAR_NGINX_WORKERS_NUM); - VLOG_PARAM_NUMBER("Size of UDP socket pool", safe_mce_sys().nginx_udp_socket_pool_size, - MCE_DEFAULT_NGINX_UDP_POOL_SIZE, CONFIG_VAR_NGINX_UDP_POOL_SIZE); - VLOG_PARAM_NUMBER("Max RX reuse buffs UDP pool", - safe_mce_sys().nginx_udp_socket_pool_rx_num_buffs_reuse, - MCE_DEFAULT_NGINX_UDP_POOL_RX_NUM_BUFFS_REUSE, - CONFIG_VAR_NGINX_UDP_POOL_RX_NUM_BUFFS_REUSE); -#endif -#if defined(DEFINED_ENVOY) - VLOG_PARAM_NUMBER("Number of Envoy workers", - (safe_mce_sys().app.type == APP_ENVOY ? safe_mce_sys().app.workers_num - : MCE_DEFAULT_APP_WORKERS_NUM), - MCE_DEFAULT_APP_WORKERS_NUM, CONFIG_VAR_ENVOY_WORKERS_NUM); -#endif /* DEFINED_ENVOY */ -#if defined(DEFINED_NGINX) || defined(DEFINED_ENVOY) - VLOG_PARAM_NUMBER("Src port stirde", safe_mce_sys().app.src_port_stride, - MCE_DEFAULT_SRC_PORT_STRIDE, CONFIG_VAR_SRC_PORT_STRIDE); -#endif - VLOG_PARAM_BOOL("fork() support", safe_mce_sys().handle_fork, MCE_DEFAULT_FORK_SUPPORT, - CONFIG_VAR_FORK); - VLOG_PARAM_BOOL("close on dup2()", safe_mce_sys().close_on_dup2, MCE_DEFAULT_CLOSE_ON_DUP2, - CONFIG_VAR_CLOSE_ON_DUP2); - switch (safe_mce_sys().mtu) { - case MTU_FOLLOW_INTERFACE: - VLOG_PARAM_NUMSTR("MTU", safe_mce_sys().mtu, MCE_DEFAULT_MTU, CONFIG_VAR_MTU, - "(follow actual MTU)"); - break; - default: - VLOG_PARAM_NUMBER("MTU", safe_mce_sys().mtu, MCE_DEFAULT_MTU, CONFIG_VAR_MTU); - break; - } - switch (safe_mce_sys().lwip_mss) { - case MSS_FOLLOW_MTU: - VLOG_PARAM_NUMSTR("MSS", safe_mce_sys().lwip_mss, MCE_DEFAULT_MSS, CONFIG_VAR_MSS, - "(follow XLIO_MTU)"); - break; - default: - VLOG_PARAM_NUMBER("MSS", safe_mce_sys().lwip_mss, MCE_DEFAULT_MSS, CONFIG_VAR_MSS); - break; - } - VLOG_PARAM_NUMSTR("TCP CC Algorithm", safe_mce_sys().lwip_cc_algo_mod, - MCE_DEFAULT_LWIP_CC_ALGO_MOD, CONFIG_VAR_TCP_CC_ALGO, - lwip_cc_algo_str(safe_mce_sys().lwip_cc_algo_mod)); - VLOG_PARAM_BOOL("Deferred close", safe_mce_sys().deferred_close, MCE_DEFAULT_DEFERRED_CLOSE, - CONFIG_VAR_DEFERRED_CLOSE); - VLOG_PARAM_BOOL("TCP abort on close", safe_mce_sys().tcp_abort_on_close, - MCE_DEFAULT_TCP_ABORT_ON_CLOSE, CONFIG_VAR_TCP_ABORT_ON_CLOSE); - VLOG_PARAM_BOOL("Polling Rx on Tx TCP", safe_mce_sys().rx_poll_on_tx_tcp, - MCE_DEFAULT_RX_POLL_ON_TX_TCP, CONFIG_VAR_RX_POLL_ON_TX_TCP); - VLOG_PARAM_BOOL("RX CQ wait control", safe_mce_sys().rx_cq_wait_ctrl, - MCE_DEFAULT_RX_CQ_WAIT_CTRL, CONFIG_VAR_RX_CQ_WAIT_CTRL); - VLOG_PARAM_STRING( - "Skip CQ polling in rx", safe_mce_sys().skip_poll_in_rx, MCE_DEFAULT_SKIP_POLL_IN_RX, - CONFIG_VAR_SKIP_POLL_IN_RX, - safe_mce_sys().skip_poll_in_rx - ? (safe_mce_sys().skip_poll_in_rx == SKIP_POLL_IN_RX_ENABLE ? "enable" - : "enable_epoll_only") - : "disable"); - VLOG_PARAM_STRING("Mutex over spinlock", safe_mce_sys().multilock, MCE_DEFAULT_MULTILOCK, - CONFIG_VAR_MULTILOCK, - (safe_mce_sys().multilock != MULTILOCK_SPIN) ? "true" : "false"); - VLOG_PARAM_NUMBER("Worker Threads", safe_mce_sys().worker_threads, MCE_DEFAULT_WORKER_THREADS, - CONFIG_VAR_WORKER_THREADS); vlog_printf(VLOG_INFO, "---------------------------------------------------------------------------\n"); } diff --git a/src/core/util/sys_vars.cpp b/src/core/util/sys_vars.cpp index 9a42e525c..e2b4b1d7a 100644 --- a/src/core/util/sys_vars.cpp +++ b/src/core/util/sys_vars.cpp @@ -240,6 +240,7 @@ const char *to_str(size_t size) static char str[64]; return to_str(size, str, sizeof(str)); } + } // namespace option_size namespace option_x { @@ -2927,9 +2928,9 @@ void mce_sys_var::configure_network_timing(const config_registry ®istry) void mce_sys_var::apply_config_from_registry() { - config_registry registry; - std::string sources; + m_registry = config_registry(); + config_registry ®istry = m_registry.value(); const auto &source_list = registry.get_sources(); if (!source_list.empty()) { sources = std::accumulate( diff --git a/src/core/util/sys_vars.h b/src/core/util/sys_vars.h index 5eb1446bf..9b805e581 100644 --- a/src/core/util/sys_vars.h +++ b/src/core/util/sys_vars.h @@ -7,6 +7,7 @@ #ifndef SYS_VARS_H #define SYS_VARS_H +#include #include #include #include @@ -299,6 +300,11 @@ struct mce_sys_var { void get_params(); bool is_threads_mode() const { return worker_threads > 0; } + // Returns the config registry + const std::experimental::optional &get_registry() const { return m_registry; } + // Once we are done with the registry, we can destroy it to free the memory + void destroy_registry() { m_registry = std::experimental::nullopt; } + // Update parameters for multi-process applications void update_multi_process_params(); void fixup_params(); @@ -518,6 +524,12 @@ struct mce_sys_var { void legacy_read_strq_strides_num(); void legacy_read_strq_stride_size_bytes(); + // The configuration registry + // Defined after all other members to not to disturb alignment and division to cache-lines + // of previous members, some of which are accessed at run-time. + // Once the main code is done with it + std::experimental::optional m_registry; + // prevent unautothrized creation of objects // coverity[CTOR_DTOR_LEAK] suppression for app_name member is needed because Coverity // incorrectly flags it as a memory leak diff --git a/tests/gtest/Makefile.am b/tests/gtest/Makefile.am index dbed8398c..3e985a2d6 100644 --- a/tests/gtest/Makefile.am +++ b/tests/gtest/Makefile.am @@ -95,6 +95,8 @@ gtest_SOURCES = \ mix/ip_address.cc \ mix/mix_list.cc \ \ + output/config.cc \ + \ tcp/tcp_accept.cc \ tcp/tcp_bind.cc \ tcp/tcp_connect.cc \ diff --git a/tests/gtest/output/config-sample-2rules.json b/tests/gtest/output/config-sample-2rules.json new file mode 100644 index 000000000..7fd3c77e6 --- /dev/null +++ b/tests/gtest/output/config-sample-2rules.json @@ -0,0 +1,21 @@ +{ + "acceleration_control": { + "rules": [ + { + "id": "0", + "name": "a", + "actions": [ + "Action 0 of rule a" + ] + }, + { + "id": "1", + "name": "b", + "actions": [ + "Action 0 of rule b", + "xx" + ] + } + ] + } +} diff --git a/tests/gtest/output/config-sample-4rules.json b/tests/gtest/output/config-sample-4rules.json new file mode 100644 index 000000000..140cc5300 --- /dev/null +++ b/tests/gtest/output/config-sample-4rules.json @@ -0,0 +1,35 @@ +{ + "acceleration_control": { + "rules": [ + { + "id": "0", + "name": "a", + "actions": [ + "Action 0 of rule a" + ] + }, + { + "id": "1", + "name": "b", + "actions": [ + "Action 0 of rule b", + "xx" + ] + }, + { + "id": "2", + "name": "c", + "actions": [ + "Action 0 of rule c" + ] + }, + { + "id": "3", + "name": "d", + "actions": [ + "Action 0 of rule d" + ] + } + ] + } +} \ No newline at end of file diff --git a/tests/gtest/output/config-sample-5rules.json b/tests/gtest/output/config-sample-5rules.json new file mode 100644 index 000000000..81527bba9 --- /dev/null +++ b/tests/gtest/output/config-sample-5rules.json @@ -0,0 +1,42 @@ +{ + "acceleration_control": { + "rules": [ + { + "id": "0", + "name": "a", + "actions": [ + "Action 0 of rule a" + ] + }, + { + "id": "1", + "name": "b", + "actions": [ + "Action 0 of rule b", + "xx" + ] + }, + { + "id": "2", + "name": "c", + "actions": [ + "Action 0 of rule c" + ] + }, + { + "id": "3", + "name": "d", + "actions": [ + "Action 0 of rule d" + ] + }, + { + "id": "4", + "name": "e", + "actions": [ + "Action 0 of rule e" + ] + } + ] + } +} \ No newline at end of file diff --git a/tests/gtest/output/config-sample.json b/tests/gtest/output/config-sample.json new file mode 100644 index 000000000..62903cdcc --- /dev/null +++ b/tests/gtest/output/config-sample.json @@ -0,0 +1,112 @@ +{ + "monitor": { + "log": { + "level": 3 + }, + "exit_report": 1 + }, + "core": { + "daemon": { + "enable": true, + "dir": "/funny-dir" + }, + "resources": { + "hugepages": { + "enable": true + } + } + }, + "hardware_features": { + "tcp": { + "tls_offload": { + "dek_cache_max_size": 4096, + "rx_enable": true, + "tx_enable": true + }, + "lro": "enable" + } + }, + "applications": { + "nginx": { + "src_port_stride": 3 + } + }, + "acceleration_control": { + "rules": [ + { + "id": "0", + "name": "a", + "actions": [ + "Action 0 of rule a" + ] + }, + { + "id": "1", + "name": "b", + "actions": [ + "Action 0 of rule b", + "xx" + ] + }, + { + "id": "2", + "name": "c", + "actions": [ + "Action 0 of rule c" + ] + }, + { + "id": "3", + "name": "d", + "actions": [ + "Action 0 of rule d" + ] + }, + { + "id": "4", + "name": "e", + "actions": [ + "Action 0 of rule e" + ] + }, + { + "id": "5", + "name": "f", + "actions": [ + "Action 0 of rule f" + ] + } + ] + }, + "performance": { + "buffers": { + "tx": { + "buf_size": "1K" + } + }, + "rings": { + "tx": { + "allocation_logic": 10 + } + }, + "threading": { + "cpu_affinity": "1,2,3", + "internal_handler": { + "behavior": "delegate" + } + }, + "polling": { + "skip_cq_on_rx": 1, + "blocking_rx_poll_usec": 2049 + }, + "completion_queue": { + "interrupt_moderation": { + "adaptive_change_frequency_msec": 0 + }, + "periodic_drain_msec": 75 + } + }, + "profiles": { + "spec": "nginx_dpu" + } +} \ No newline at end of file diff --git a/tests/gtest/output/config.cc b/tests/gtest/output/config.cc new file mode 100644 index 000000000..414989c60 --- /dev/null +++ b/tests/gtest/output/config.cc @@ -0,0 +1,250 @@ +/* + * SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES + * Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause + */ + +#include +#include + +#include "common/def.h" + +/** + * Base class for output tests + */ +class output : virtual public testing::Test { +public: + void SetUp() override + { + m_workspace = std::getenv("WORKSPACE"); + if (m_workspace) { + std::cout << "WORKSPACE: '" << m_workspace << "'" << std::endl; + } else { + std::cout << "WORKSPACE is not set" << std::endl; + } + } + +protected: + void exec_cmd(const std::string &cmd) + { + int rc = system(cmd.c_str()); + if (rc != 0) { + throw std::runtime_error("system('" + cmd + "') failed!"); + } + } + + void check_file(const std::string &filename, const std::vector &expected_strings, + const std::vector &unexpected_strings = {}) + { + std::string full_filename = filename; + + std::string prefix; + if (m_workspace) { + prefix = std::string(m_workspace) + "/tests/gtest/output"; + } else { + // Assume we are running from gtest/ + prefix = "output"; + } + full_filename = prefix + "/" + filename; + std::string output_file = prefix + "/output.txt"; + + // LD_PRELOAD is already set for the gtest, it is inherited by the new process, no need + // to do anything + // The syntax '[command] > [file] 2>&1' works for both bash and dash + std::string cmd = "XLIO_USE_NEW_CONFIG=1 XLIO_CONFIG_FILE=" + full_filename + " ls > " + + output_file + " 2>&1"; + exec_cmd(cmd); + + // Read the output file + std::ifstream ifs(output_file); + std::string text(std::istreambuf_iterator(ifs), {}); + ifs.close(); + + for (const auto &expected : expected_strings) { + // ASSERT and not EXPECT because if there are multiple failures the test output + // becomes flooded, so we want to stop after the first failure + ASSERT_TRUE(text.find(expected) != std::string::npos) + << "Did not find '" << expected << "' in xlio output when using file '" << filename + << "':" << std::endl + << "START OF TEXT:" << std::endl + << text << std::endl + << "END OF TEXT"; + }; + for (const auto &unexpected : unexpected_strings) { + ASSERT_TRUE(text.find(unexpected) == std::string::npos) + << "Found unexpected '" << unexpected << "' in xlio output when using file '" + << filename << "':" << std::endl + << "START OF TEXT:" << std::endl + << text << std::endl + << "END OF TEXT"; + } + } + + // nullptr if WORKSPACE env var is not set + char *m_workspace {nullptr}; +}; + +/** + * @test misc.config_show_sample_values + * @brief + * Tests that various config values are shown at xlio start + * + * @details + * This test run a new process with LD_PRELOAD and captures the text output from xlio : + * - Various config values are set in a json config file + * - The test makes sure they are shown properly + */ +TEST_F(output, config_show_sample_values) +{ + check_file( + "config-sample.json", + { + // We want there extra-wide lines for readability, tell clang-format to accept them + // clang-format off + + // The config sources are shown - we check only the fixed part of the path, + // because the full path changes depending on the WORKSPACE env var + "output/config-sample.json", + + // Log level is always shown + "XLIO INFO : Log level INFO [monitor.log.level]", + // Simple number + "XLIO INFO : Source port stride 3 [applications.nginx.src_port_stride]", + // Simple string + "Daemon working directory /funny-dir [core.daemon.dir]", + // Simple boolean + "Enable TLS RX offload true [hardware_features.tcp.tls_offload.rx_enable]", + // 4096 is shown as 4K + "DEK max cache size 4K [hardware_features.tcp.tls_offload.dek_cache_max_size]", + // CONFIG_VAR_PROGRESS_ENGINE_INTERVAL is a special case, 0 is shown as '0 (Disabled)' + "Periodic drain interval (msec) 0 (Disabled) [performance.completion_queue.periodic_drain_msec]", + // Another simple string + "CPU affinity 1,2,3 [performance.threading.cpu_affinity]", + // Enum value is shown as a string + "TX ring allocation logic per_socket [performance.rings.tx.allocation_logic]", + // Not a power of 1024, hence shown in full accuracy + "XLIO INFO : RX poll duration (µsec) 2049 [performance.polling.blocking_rx_poll_usec]", + // The accelaration rules array - Yey, recursion ! + "XLIO INFO : Acceleration control rules [acceleration_control.rules]", + "XLIO INFO : 0 [acceleration_control.rules[0].id]", + "XLIO INFO : a [acceleration_control.rules[0].name]", + "XLIO INFO : Action 0 of rule a [acceleration_control.rules[0].actions[0]]", + "XLIO INFO : 1 [acceleration_control.rules[1].id]", + "XLIO INFO : b [acceleration_control.rules[1].name]", + "XLIO INFO : Action 0 of rule b [acceleration_control.rules[1].actions[0]]", + "XLIO INFO : xx [acceleration_control.rules[1].actions[1]]", + "XLIO INFO : 2 element(s) not shown [acceleration_control.rules[2-3]]", + "XLIO INFO : 4 [acceleration_control.rules[4].id]", + "XLIO INFO : e [acceleration_control.rules[4].name]", + "XLIO INFO : Action 0 of rule e [acceleration_control.rules[4].actions[0]]", + "XLIO INFO : 5 [acceleration_control.rules[5].id]", + "XLIO INFO : f [acceleration_control.rules[5].name]", + "XLIO INFO : Action 0 of rule f [acceleration_control.rules[5].actions[0]]" + // clang-format on + }); +} + +/** + * @test misc.config_show_sample_values_2rules + * @brief + * With only 2 acceleration control rules, we should not see the "element(s) not shown" message + */ +TEST_F(output, config_show_sample_values_2rules) +{ + check_file( + "config-sample-2rules.json", + { + // We want there extra-wide lines for readability, tell clang-format to accept them + // clang-format off + + // The config sources are shown + "output/config-sample-2rules.json", + + // The accelaration rules array with only 2 rules + "XLIO INFO : Acceleration control rules [acceleration_control.rules]", + "XLIO INFO : 0 [acceleration_control.rules[0].id]", + "XLIO INFO : a [acceleration_control.rules[0].name]", + "XLIO INFO : Action 0 of rule a [acceleration_control.rules[0].actions[0]]", + "XLIO INFO : 1 [acceleration_control.rules[1].id]", + "XLIO INFO : b [acceleration_control.rules[1].name]", + "XLIO INFO : Action 0 of rule b [acceleration_control.rules[1].actions[0]]", + "XLIO INFO : xx [acceleration_control.rules[1].actions[1]]" + // clang-format on + }, + {// This is NOT expected + "element(s) not shown"}); +} + +/** + * @test misc.config_show_sample_values_4rules + * @brief + * With only 4 acceleration control rules, we should not see the "element(s) not shown" message + */ +TEST_F(output, config_show_sample_values_4rules) +{ + check_file( + "config-sample-4rules.json", + { + // We want there extra-wide lines for readability, tell clang-format to accept them + // clang-format off + + // The config sources are shown + "output/config-sample-4rules.json", + + // The accelaration rules array with only 4 rules + "XLIO INFO : Acceleration control rules [acceleration_control.rules]", + "XLIO INFO : 0 [acceleration_control.rules[0].id]", + "XLIO INFO : a [acceleration_control.rules[0].name]", + "XLIO INFO : Action 0 of rule a [acceleration_control.rules[0].actions[0]]", + "XLIO INFO : 1 [acceleration_control.rules[1].id]", + "XLIO INFO : b [acceleration_control.rules[1].name]", + "XLIO INFO : Action 0 of rule b [acceleration_control.rules[1].actions[0]]", + "XLIO INFO : xx [acceleration_control.rules[1].actions[1]]", + "XLIO INFO : 2 [acceleration_control.rules[2].id]", + "XLIO INFO : c [acceleration_control.rules[2].name]", + "XLIO INFO : Action 0 of rule c [acceleration_control.rules[2].actions[0]]", + "XLIO INFO : 3 [acceleration_control.rules[3].id]", + "XLIO INFO : d [acceleration_control.rules[3].name]", + "XLIO INFO : Action 0 of rule d [acceleration_control.rules[3].actions[0]]" + // clang-format on + }, + {// This is NOT expected + "element(s) not shown"}); +} + +/** + * @test misc.config_show_sample_values_5rules + * @brief + * With 5 acceleration control rules, we should see the "element(s) not shown" message with a + * single element [2] + */ +TEST_F(output, config_show_sample_values_5rules) +{ + check_file( + "config-sample-5rules.json", + { + // We want there extra-wide lines for readability, tell clang-format to accept them + // clang-format off + + // The config sources are shown + "output/config-sample-5rules.json", + + // The accelaration rules array with 5 rules + "XLIO INFO : Acceleration control rules [acceleration_control.rules]", + "XLIO INFO : 0 [acceleration_control.rules[0].id]", + "XLIO INFO : a [acceleration_control.rules[0].name]", + "XLIO INFO : Action 0 of rule a [acceleration_control.rules[0].actions[0]]", + "XLIO INFO : 1 [acceleration_control.rules[1].id]", + "XLIO INFO : b [acceleration_control.rules[1].name]", + "XLIO INFO : Action 0 of rule b [acceleration_control.rules[1].actions[0]]", + "XLIO INFO : xx [acceleration_control.rules[1].actions[1]]", + "XLIO INFO : 1 element(s) not shown [acceleration_control.rules[2]]", + "XLIO INFO : 3 [acceleration_control.rules[3].id]", + "XLIO INFO : d [acceleration_control.rules[3].name]", + "XLIO INFO : Action 0 of rule d [acceleration_control.rules[3].actions[0]]", + "XLIO INFO : 4 [acceleration_control.rules[4].id]", + "XLIO INFO : e [acceleration_control.rules[4].name]", + "XLIO INFO : Action 0 of rule e [acceleration_control.rules[4].actions[0]]" + // clang-format on + }); +} diff --git a/tests/unit_tests/config/config_registry.cpp b/tests/unit_tests/config/config_registry.cpp index 84c8fa963..113c6271b 100644 --- a/tests/unit_tests/config/config_registry.cpp +++ b/tests/unit_tests/config/config_registry.cpp @@ -770,3 +770,28 @@ TEST(config, config_registry_power_of_2_validation_zero_values) ASSERT_EQ(512LL, registry.get_value("hardware_features.striding_rq.strides_num")); ASSERT_EQ(64LL, registry.get_value("hardware_features.striding_rq.stride_size")); } + +TEST(config, config_registry_bad_enum_string) +{ + conf_file_writer json_config(R"({ + "monitor":{ + "log":{ + "level":"invalid-value" + } + } + })"); + + env_setter config_file_setter("XLIO_CONFIG_FILE", json_config.get()); + + try { + config_registry registry; + FAIL() << "Expected exception was not thrown"; + } catch (const xlio_exception &e) { + std::string msg = e.what(); + // Make sure exception contains proper error message + EXPECT_NE(msg.find("Invalid value for Log level: invalid-value, not one of: " + "[all,debug,details,error,fine,finer,info,init,none,panic,warn]"), + std::string::npos) + << msg; + } +} diff --git a/tests/unit_tests/config/parameter_descriptor.cpp b/tests/unit_tests/config/parameter_descriptor.cpp index 67eb42336..73e3f6600 100644 --- a/tests/unit_tests/config/parameter_descriptor.cpp +++ b/tests/unit_tests/config/parameter_descriptor.cpp @@ -448,6 +448,32 @@ TEST(config, parameter_descriptor_boolean_rejects_integer) std::experimental::any int_value = int64_t(1); ASSERT_THROW(bool_param_descriptor.get_value(int_value), std::experimental::bad_any_cast); } + +TEST(config, parameter_descriptor_convert_int64_to_string_with_mapping) +{ + // Test behavior when string mappints are defined + constexpr int64_t DEFAULT_INT_VALUE = 16; + parameter_descriptor int_param_descriptor {std::experimental::any(int64_t(DEFAULT_INT_VALUE))}; + int_param_descriptor.set_string_mappings({{"a", 1}, {"b", 2}, {"c", 3}}); + + // Values in the map are converted + ASSERT_EQ("a", int_param_descriptor.convert_int64_to_mapped_string_or(1, "x")); + ASSERT_EQ("b", int_param_descriptor.convert_int64_to_mapped_string_or(2, "x")); + // Values not in the map are converted to the default value supplied + ASSERT_EQ("x", int_param_descriptor.convert_int64_to_mapped_string_or(4, "x")); +} + +TEST(config, parameter_descriptor_convert_int64_to_string_without_mapping) +{ + // Test that without mapping, integers are converted trivially + constexpr int64_t DEFAULT_INT_VALUE = 16; + parameter_descriptor int_param_descriptor {std::experimental::any(int64_t(DEFAULT_INT_VALUE))}; + + ASSERT_EQ("1", int_param_descriptor.convert_int64_to_mapped_string_or(1, "x")); + ASSERT_EQ("2", int_param_descriptor.convert_int64_to_mapped_string_or(2, "x")); + ASSERT_EQ("4", int_param_descriptor.convert_int64_to_mapped_string_or(4, "x")); +} + // Test suite for power-of-2 constraint validation class power_of_2_or_zero_constraint_test : public ::testing::Test { protected: diff --git a/tests/unit_tests/config/schema_analyzer.cpp b/tests/unit_tests/config/schema_analyzer.cpp index 880b883a8..05075e559 100644 --- a/tests/unit_tests/config/schema_analyzer.cpp +++ b/tests/unit_tests/config/schema_analyzer.cpp @@ -76,6 +76,12 @@ class schema_analyzer_test : public ::testing::Test { json_object_object_add(items, "type", json_object_new_string("string")); json_object_object_add(array_property, "items", items); json_object_object_add(array_property, "default", json_object_new_array()); + + simple_property_no_title = json_object_new_object(); + json_object_object_add(simple_property_no_title, "type", json_object_new_string("integer")); + json_object_object_add(simple_property_no_title, "description", + json_object_new_string("A test property")); + json_object_object_add(simple_property_no_title, "default", json_object_new_int(42)); } void TearDown() override @@ -85,6 +91,7 @@ class schema_analyzer_test : public ::testing::Test { json_object_put(one_of_property); json_object_put(object_property); json_object_put(array_property); + json_object_put(simple_property_no_title); } json_object *simple_property; @@ -92,6 +99,7 @@ class schema_analyzer_test : public ::testing::Test { json_object *one_of_property; json_object *object_property; json_object *array_property; + json_object *simple_property_no_title; }; TEST_F(schema_analyzer_test, analyze_simple_property) @@ -106,6 +114,21 @@ TEST_F(schema_analyzer_test, analyze_simple_property) // Test default value ASSERT_EQ(std::experimental::any_cast(*analysis.default_value), 42); + ASSERT_EQ(*analysis.title, "Test Property"); +} + +TEST_F(schema_analyzer_test, analyze_simple_property_no_title_throws) +{ + // We want to verify the error message, so not using ASSERT_THROW + //ASSERT_THROW(schema_analyzer::analyze(simple_property_no_title, "test.simple_no_title"), xlio_exception); + try { + schema_analyzer::analyze(simple_property_no_title, "test.simple_no_title"); + FAIL() << "Expected xlio_exception"; + } catch (const xlio_exception& e) { + EXPECT_NE(std::string(e.what()).find("Title must be a defined for"), std::string::npos); + } catch (...) { + FAIL() << "Expected xlio_exception"; + } } TEST_F(schema_analyzer_test, analyze_extended_property)