From bb835ff10a62ec53c97c15163f86e19b178c5d86 Mon Sep 17 00:00:00 2001 From: Nathan Hjelm Date: Tue, 24 Sep 2024 13:49:24 -0600 Subject: [PATCH] mca/base: add a new MCA variable type for include lists It is not uncommon in Open MPI to register a string variable to allow specifying an exclude or exclude list (every framework registers one). Given this common patten it is worthwhile to formalize an MCA include list variable type: MCA_BASE_VAR_TYPE_INCLUDE_LIST. Variables of this type use a new opal object (mca_base_var_include_list_t) that stores and argv-style array and an exclude- list flag. To register an include list variable the caller must first either OBJ_CONSTRUCT the object or allocate it with OBJ_NEW. Variables of this type are exposed as strings to MPI_T. Signed-off-by: Nathan Hjelm --- configure.ac | 1 + ompi/mpi/tool/cvar_read.c | 12 ++ ompi/mpi/tool/mpit_common.c | 2 + opal/class/Makefile.am | 9 +- opal/class/opal_include_list.c | 146 ++++++++++++++++++ opal/class/opal_include_list.h | 50 +++++++ opal/class/opal_serializable.c | 53 +++++++ opal/class/opal_serializable.h | 36 +++++ opal/mca/base/mca_base_var.c | 78 +++++++--- opal/mca/base/mca_base_var.h | 17 +++ test/Makefile.am | 3 +- test/class/Makefile.am | 9 +- test/class/opal_include_list.c | 263 +++++++++++++++++++++++++++++++++ test/mca/Makefile.am | 41 +++++ test/mca/mca_var.c | 216 +++++++++++++++++++++++++++ 15 files changed, 910 insertions(+), 26 deletions(-) create mode 100644 opal/class/opal_include_list.c create mode 100644 opal/class/opal_include_list.h create mode 100644 opal/class/opal_serializable.c create mode 100644 opal/class/opal_serializable.h create mode 100644 test/class/opal_include_list.c create mode 100644 test/mca/Makefile.am create mode 100644 test/mca/mca_var.c diff --git a/configure.ac b/configure.ac index 2057a103712..5cd5ef821c0 100644 --- a/configure.ac +++ b/configure.ac @@ -1505,6 +1505,7 @@ AC_CONFIG_FILES([ test/asm/Makefile test/datatype/Makefile test/class/Makefile + test/mca/Makefile test/mpool/Makefile test/support/Makefile test/threads/Makefile diff --git a/ompi/mpi/tool/cvar_read.c b/ompi/mpi/tool/cvar_read.c index b46db2c99c4..48bd7c92313 100644 --- a/ompi/mpi/tool/cvar_read.c +++ b/ompi/mpi/tool/cvar_read.c @@ -10,6 +10,7 @@ * reserved. * Copyright (c) 2021 Amazon.com, Inc. or its affiliates. All Rights * reserved. + * Copyright (c) 2024 Google, LLC. All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow @@ -88,6 +89,17 @@ int MPI_T_cvar_read (MPI_T_cvar_handle handle, void *buf) } break; + case MCA_BASE_VAR_TYPE_SERIALIZABLE: { + char *tmp = value->serializable.serialize(&value->serializable); + if (strlen(tmp) == 0) { + ((char *)buf)[0] = '\0'; + } else { + strcpy ((char *) buf, tmp); + } + free (tmp); + + break; + } default: rc = MPI_T_ERR_INVALID; } diff --git a/ompi/mpi/tool/mpit_common.c b/ompi/mpi/tool/mpit_common.c index e47da814f7a..3006dd9d35b 100644 --- a/ompi/mpi/tool/mpit_common.c +++ b/ompi/mpi/tool/mpit_common.c @@ -8,6 +8,7 @@ * Copyright (c) 2020 The University of Tennessee and The University * of Tennessee Research Foundation. All rights * reserved. + * Copyright (c) 2024 Google, LLC. All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow @@ -56,6 +57,7 @@ static MPI_Datatype mca_to_mpi_datatypes[MCA_BASE_VAR_TYPE_MAX] = { [MCA_BASE_VAR_TYPE_UINT32_T] = MPI_UINT32_T, [MCA_BASE_VAR_TYPE_INT64_T] = MPI_INT64_T, [MCA_BASE_VAR_TYPE_UINT64_T] = MPI_UINT64_T, + [MCA_BASE_VAR_TYPE_SERIALIZABLE] = MPI_CHAR, }; int ompit_var_type_to_datatype (mca_base_var_type_t type, MPI_Datatype *datatype) diff --git a/opal/class/Makefile.am b/opal/class/Makefile.am index 1d3beb50a78..e75f60a7dc6 100644 --- a/opal/class/Makefile.am +++ b/opal/class/Makefile.am @@ -15,6 +15,7 @@ # reserved. # Copyright (c) 2021 Nanook Consulting. All rights reserved. # Copyright (c) 2022 Amazon.com, Inc. or its affiliates. All Rights reserved. +# Copyright (c) 2024 Google, LLC. All rights reserved. # $COPYRIGHT$ # # Additional copyrights may follow @@ -41,7 +42,9 @@ headers += \ class/opal_value_array.h \ class/opal_ring_buffer.h \ class/opal_rb_tree.h \ - class/opal_interval_tree.h + class/opal_interval_tree.h \ + class/opal_include_list.h \ + class/opal_serializable.h libopen_pal_core_la_SOURCES += \ class/opal_bitmap.c \ @@ -58,4 +61,6 @@ libopen_pal_core_la_SOURCES += \ class/opal_value_array.c \ class/opal_ring_buffer.c \ class/opal_rb_tree.c \ - class/opal_interval_tree.c + class/opal_interval_tree.c \ + class/opal_include_list.c \ + class/opal_serializable.c diff --git a/opal/class/opal_include_list.c b/opal/class/opal_include_list.c new file mode 100644 index 00000000000..9db001c0c55 --- /dev/null +++ b/opal/class/opal_include_list.c @@ -0,0 +1,146 @@ +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ +/* + * Copyright (c) 2024 Google, LLC. All rights reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#include + +#include "opal_config.h" + +#include "opal/class/opal_include_list.h" +#include "opal/class/opal_object.h" +#include "opal/include/opal/constants.h" +#include "opal/mca/base/base.h" +#include "opal/util/argv.h" +#include "opal/util/output.h" +#include "opal/util/printf.h" + +static void include_list_destructor(opal_include_list_t *p); + +static int opal_include_list_deserialize(opal_include_list_t *object, const char *value) +{ + /* release any current value and set to defaults */ + include_list_destructor(object); + + if (NULL == value || 0 == strlen(value)) { + return OPAL_SUCCESS; + } + + if ('^' == value[0]) { + object->is_exclude = true; + ++value; + } + + object->items = opal_argv_split(value, ','); + return OPAL_SUCCESS; +} + +static char *opal_include_list_serialize(const opal_include_list_t *object) +{ + if (NULL == object->items) { + return strdup(""); + } + + char *tmp = opal_argv_join(object->items, ','); + if (object->is_exclude) { + char *value_str = NULL; + (void) opal_asprintf(&value_str, "^%s", tmp); + free(tmp); + return value_str; + } + + return tmp; +} + +static bool opal_include_list_is_set(const opal_include_list_t *object) +{ + return (object->items != NULL); +} + +static void include_list_contructor(opal_include_list_t *p) +{ + p->super.deserialize = (opal_serializable_deserialize_fn_t)opal_include_list_deserialize; + p->super.serialize = (opal_serializable_serialize_fn_t)opal_include_list_serialize; + p->super.is_set = (opal_serializable_is_set_fn_t)opal_include_list_is_set; + p->items = NULL; + p->is_exclude = false; +} + +static void include_list_destructor(opal_include_list_t *p) +{ + opal_argv_free(p->items); + include_list_contructor(p); +} + +OBJ_CLASS_INSTANCE(opal_include_list_t, opal_object_t, include_list_contructor, + include_list_destructor); + +static int include_list_match_regex(opal_include_list_t *include_list, const char *value, + bool case_sensitive) +{ + int regex_flags = REG_EXTENDED | REG_NOSUB; + regex_t regex; + + if (!case_sensitive) { + regex_flags |= REG_ICASE; + } + + for (int i = 0 ; include_list->items[i] ; ++i) { + int rc = regcomp(®ex, include_list->items[i], regex_flags); + if (rc != 0) { + /* incorrectly formatted regular expression */ + opal_output_verbose(MCA_BASE_VERBOSE_WARN, 0, "error compiling regular expression: %s, " + "ignoring", include_list->items[i]); + continue; + } + + rc = regexec(®ex, value, /*nmatch=*/0, /*pmatch=*/NULL, /*eflags=*/0); + regfree(®ex); + if (0 == rc) { + return (include_list->is_exclude ? -1 : 1) * (i + 1); + } + } + + return include_list->is_exclude ? 1 : -1; +} + +static int include_list_match(opal_include_list_t *include_list, const char *value, + bool case_sensitive) +{ + for (int i = 0 ; include_list->items[i] ; ++i) { + bool match = false; + if (case_sensitive) { + if (0 == strcmp(include_list->items[i], value)) { + match = true; + } + } else if (0 == strcasecmp(include_list->items[i], value)) { + match = true; + } + + if (match) { + return (include_list->is_exclude ? -1 : 1) * (i + 1); + } + } + + return include_list->is_exclude ? 1 : -1; +} + +int opal_include_list_match(opal_include_list_t *include_list, const char *value, + bool regex_match, bool case_sensitive) +{ + if (include_list == NULL || value == NULL || include_list->items == NULL) { + opal_output_verbose(MCA_BASE_VERBOSE_ERROR, 0, "error matching in include list"); + return -1; + } + + if (regex_match) { + return include_list_match_regex(include_list, value, case_sensitive); + } + + return include_list_match(include_list, value, case_sensitive); +} diff --git a/opal/class/opal_include_list.h b/opal/class/opal_include_list.h new file mode 100644 index 00000000000..4c95b4c15d7 --- /dev/null +++ b/opal/class/opal_include_list.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ +/* + * Copyright (c) 2024 Google, LLC. All rights reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#if !defined(OPAL_INCLUDE_LIST_H) +#define OPAL_INCLUDE_LIST_H + +#include "opal_config.h" +#include "opal/class/opal_object.h" +#include "opal/class/opal_serializable.h" + +/** + * An include list. These are strings of the form: + * foo,bar,baz (include) + * ^foo,bar,baz (exclude) + */ +struct opal_include_list { + opal_serializable_t super; + /** argv array of items */ + char **items; + /** is this an exclude list */ + bool is_exclude; +}; +typedef struct opal_include_list opal_include_list_t; + +OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_include_list_t); + +/** + * Match a string against the include list and return the list rank. + * + * @param[in] include_list Include list + * @param[in] value Value to match + * @param[in] regex_match Treat the entries in the include list as regular expressions + * @param[in] case_sensitive Make matching case sensitive + * + * This method searches the include list for value. If an entry matches then the rank of the + * include list match is returned. A negative number is returned if the list is an exclude + * list. + */ +OPAL_DECLSPEC int opal_include_list_match(opal_include_list_t *include_list, const char *value, + bool regex_match, bool case_sensitive); + + +#endif /* OPAL_INCLUDE_LIST_H */ diff --git a/opal/class/opal_serializable.c b/opal/class/opal_serializable.c new file mode 100644 index 00000000000..1b2d892d04f --- /dev/null +++ b/opal/class/opal_serializable.c @@ -0,0 +1,53 @@ +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ +/* + * Copyright (c) 2024 Google, LLC. All rights reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#include + +#include "opal_config.h" + +#include "opal/class/opal_include_list.h" +#include "opal/class/opal_object.h" +#include "opal/include/opal/constants.h" +#include "opal/mca/base/base.h" +#include "opal/util/argv.h" +#include "opal/util/output.h" + +static int opal_serializable_deserialize(opal_serializable_t *object, const char *value) +{ + (void)object; + (void)value; + return OPAL_ERR_NOT_IMPLEMENTED; +} + +static char *opal_serializable_serialize(const opal_serializable_t *object) +{ + (void)object; + return NULL; +} + +static bool opal_serializable_is_set(const opal_serializable_t *object) +{ + (void)object; + return false; +} + +static void opal_serializable_constructor(opal_serializable_t *p) +{ + p->deserialize = opal_serializable_deserialize; + p->serialize = opal_serializable_serialize; + p->is_set = opal_serializable_is_set; +} + +static void opal_serializable_destructor(opal_serializable_t *p) +{ +} + +OBJ_CLASS_INSTANCE(opal_serializable_t, opal_object_t, opal_serializable_constructor, + opal_serializable_destructor); diff --git a/opal/class/opal_serializable.h b/opal/class/opal_serializable.h new file mode 100644 index 00000000000..0c7007f8340 --- /dev/null +++ b/opal/class/opal_serializable.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ +/* + * Copyright (c) 2024 Google, LLC. All rights reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#if !defined(OPAL_SERIALIZABLE_H) +#define OPAL_SERIALIZABLE_H + +#include "opal_config.h" +#include "opal/class/opal_object.h" + +struct opal_serializable; +typedef struct opal_serializable opal_serializable_t; + +typedef int (*opal_serializable_deserialize_fn_t)(opal_serializable_t *object, const char *value); +typedef char *(*opal_serializable_serialize_fn_t)(const opal_serializable_t *object); +typedef bool (*opal_serializable_is_set_fn_t)(const opal_serializable_t *object); + +struct opal_serializable { + opal_object_t super; + /** de-serialize the object from a string */ + opal_serializable_deserialize_fn_t deserialize; + /** serialize the object into a string */ + opal_serializable_serialize_fn_t serialize; + /** object has a value set */ + opal_serializable_is_set_fn_t is_set; +}; + +OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_serializable_t); + +#endif /* OPAL_SERIALIZABLE_H */ diff --git a/opal/mca/base/mca_base_var.c b/opal/mca/base/mca_base_var.c index b9860c6ed1e..a33661c0762 100644 --- a/opal/mca/base/mca_base_var.c +++ b/opal/mca/base/mca_base_var.c @@ -20,7 +20,7 @@ * Copyright (c) 2018 Amazon.com, Inc. or its affiliates. All Rights reserved. * Copyright (c) 2018 Triad National Security, LLC. All rights * reserved. - * Copyright (c) 2020 Google, LLC. All rights reserved. + * Copyright (c) 2020-2024 Google, LLC. All rights reserved. * Copyright (c) 2021 Nanook Consulting. All rights reserved. * Copyright (c) 2022 Computer Architecture and VLSI Systems (CARV) * Laboratory, ICS Forth. All rights reserved. @@ -45,6 +45,7 @@ #endif #include +#include "opal/class/opal_serializable.h" #include "opal/constants.h" #include "opal/include/opal_stdint.h" #include "opal/mca/base/mca_base_alias.h" @@ -766,6 +767,9 @@ static int var_set_from_string(mca_base_var_t *var, char *src) case MCA_BASE_VAR_TYPE_VERSION_STRING: var_set_string(var, src); break; + case MCA_BASE_VAR_TYPE_SERIALIZABLE: + var->mbv_storage->serializable.deserialize(&var->mbv_storage->serializable, src); + break; case MCA_BASE_VAR_TYPE_MAX: return OPAL_ERROR; } @@ -803,11 +807,14 @@ int mca_base_var_set_value(int vari, const void *value, size_t size, mca_base_va } } - if (MCA_BASE_VAR_TYPE_STRING != var->mbv_type - && MCA_BASE_VAR_TYPE_VERSION_STRING != var->mbv_type) { - memmove(var->mbv_storage, value, ompi_var_type_sizes[var->mbv_type]); + if (MCA_BASE_VAR_TYPE_STRING == var->mbv_type || + MCA_BASE_VAR_TYPE_VERSION_STRING == var->mbv_type) { + ret = var_set_string(var, (char *) value); + } else if (MCA_BASE_VAR_TYPE_SERIALIZABLE == var->mbv_type) { + ret = var->mbv_storage->serializable.deserialize(&var->mbv_storage->serializable, + (const char *) value); } else { - var_set_string(var, (char *) value); + memmove(var->mbv_storage, value, ompi_var_type_sizes[var->mbv_type]); } var->mbv_source = source; @@ -817,7 +824,7 @@ int mca_base_var_set_value(int vari, const void *value, size_t size, mca_base_va var->mbv_source_file = append_filename_to_list(source_file); } - return OPAL_SUCCESS; + return ret; } /* @@ -847,11 +854,12 @@ int mca_base_var_deregister(int vari) } /* Release the current value if it is a string. */ - if ((MCA_BASE_VAR_TYPE_STRING == var->mbv_type - || MCA_BASE_VAR_TYPE_VERSION_STRING == var->mbv_type) - && var->mbv_storage->stringval) { + if (MCA_BASE_VAR_TYPE_STRING == var->mbv_type + || MCA_BASE_VAR_TYPE_VERSION_STRING == var->mbv_type) { free(var->mbv_storage->stringval); var->mbv_storage->stringval = NULL; + } else if (MCA_BASE_VAR_TYPE_SERIALIZABLE == var->mbv_type) { + OBJ_DESTRUCT(&var->mbv_storage->serializable); } else { OPAL_MCA_VAR_MBV_ENUMERATOR_FREE(var->mbv_enumerator); } @@ -1046,9 +1054,10 @@ int mca_base_var_build_env(char ***env, int *num_env, bool internal) continue; } - if ((MCA_BASE_VAR_TYPE_STRING == var->mbv_type + if (((MCA_BASE_VAR_TYPE_STRING == var->mbv_type || MCA_BASE_VAR_TYPE_VERSION_STRING == var->mbv_type) - && NULL == var->mbv_storage->stringval) { + && NULL == var->mbv_storage->stringval) || + !var->mbv_storage->serializable.is_set(&var->mbv_storage->serializable)) { continue; } @@ -1331,7 +1340,11 @@ static int register_variable(const char *project_name, const char *framework_nam align = OPAL_ALIGNMENT_DOUBLE; break; case MCA_BASE_VAR_TYPE_VERSION_STRING: + /* fall through */ case MCA_BASE_VAR_TYPE_STRING: + /* fall through */ + case MCA_BASE_VAR_TYPE_SERIALIZABLE: + /* fall through */ default: align = 0; break; @@ -1533,6 +1546,9 @@ int mca_base_var_register(const char *project_name, const char *framework_name, assert(NULL == enumerator || (MCA_BASE_VAR_TYPE_INT == type || MCA_BASE_VAR_TYPE_UNSIGNED_INT == type)); + + assert(MCA_BASE_VAR_TYPE_SERIALIZABLE != type || ((opal_serializable_t *) storage)->super.obj_reference_count > 0); + ret = register_variable(project_name, framework_name, component_name, variable_name, description, type, enumerator, bind, flags, info_lvl, scope, -1, storage); @@ -1855,25 +1871,26 @@ static void var_constructor(mca_base_var_t *var) */ static void var_destructor(mca_base_var_t *var) { - if ((MCA_BASE_VAR_TYPE_STRING == var->mbv_type - || MCA_BASE_VAR_TYPE_VERSION_STRING == var->mbv_type) - && NULL != var->mbv_storage && NULL != var->mbv_storage->stringval) { - free(var->mbv_storage->stringval); - var->mbv_storage->stringval = NULL; + if (NULL != var->mbv_storage) { + if (MCA_BASE_VAR_TYPE_STRING == var->mbv_type + || MCA_BASE_VAR_TYPE_VERSION_STRING == var->mbv_type) { + free(var->mbv_storage->stringval); + var->mbv_storage->stringval = NULL; + } else if (MCA_BASE_VAR_TYPE_SERIALIZABLE == var->mbv_type) { + OBJ_DESTRUCT(&var->mbv_storage->serializable); + } + + var->mbv_storage = NULL; } /* don't release the boolean enumerator */ OPAL_MCA_VAR_MBV_ENUMERATOR_FREE(var->mbv_enumerator); - if (NULL != var->mbv_long_name) { - free(var->mbv_long_name); - } + free(var->mbv_long_name); var->mbv_full_name = NULL; var->mbv_variable_name = NULL; - if (NULL != var->mbv_description) { - free(var->mbv_description); - } + free(var->mbv_description); /* Destroy the synonym array */ OBJ_DESTRUCT(&var->mbv_synonyms); @@ -1994,6 +2011,9 @@ static int var_value_string(mca_base_var_t *var, char **value_string) case MCA_BASE_VAR_TYPE_DOUBLE: ret = opal_asprintf(value_string, "%lf", value->lfval); break; + case MCA_BASE_VAR_TYPE_SERIALIZABLE: + *value_string = value->serializable.serialize(&value->serializable); + break; default: ret = -1; break; @@ -2014,6 +2034,20 @@ static int var_value_string(mca_base_var_t *var, char **value_string) return ret; } +char *mca_base_var_string_value(int vari) +{ + char *tmp = NULL; + mca_base_var_t *var; + + int ret = var_get(vari, &var, false); + if (OPAL_SUCCESS != ret) { + return NULL; + } + + (void) var_value_string(var, &tmp); + return tmp; +} + int mca_base_var_check_exclusive(const char *project, const char *type_a, const char *component_a, const char *param_a, const char *type_b, const char *component_b, const char *param_b) diff --git a/opal/mca/base/mca_base_var.h b/opal/mca/base/mca_base_var.h index 5f6e4ced3f1..0d17e93d936 100644 --- a/opal/mca/base/mca_base_var.h +++ b/opal/mca/base/mca_base_var.h @@ -19,6 +19,7 @@ * reserved. * Copyright (c) 2022 Computer Architecture and VLSI Systems (CARV) * Laboratory, ICS Forth. All rights reserved. + * Copyright (c) 2024 Google, LLC. All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow @@ -67,6 +68,7 @@ #include "opal_config.h" +#include "opal/class/opal_serializable.h" #include "opal/class/opal_list.h" #include "opal/class/opal_value_array.h" #include "opal/mca/base/mca_base_framework.h" @@ -106,6 +108,9 @@ typedef enum { MCA_BASE_VAR_TYPE_INT64_T, /** The variable is of type uint64_t */ MCA_BASE_VAR_TYPE_UINT64_T, + /** The variable is of type opal_serializable_t or + * a derivative class. */ + MCA_BASE_VAR_TYPE_SERIALIZABLE, /** Maximum variable type. */ MCA_BASE_VAR_TYPE_MAX @@ -242,6 +247,8 @@ typedef union { size_t sizetval; /** double value */ double lfval; + /** serializable object */ + opal_serializable_t serializable; } mca_base_var_storage_t; /** @@ -718,6 +725,16 @@ typedef enum { */ OPAL_DECLSPEC int mca_base_var_dump(int vari, char ***out, mca_base_var_dump_type_t output_type); +/** + * Get a string representation of a variable. + * + * @param[in] vari Variable index + * + * This function returns the string representation of the variable or NULL if an + * error occurs. It is the caller's responsibility to free the string. + */ +OPAL_DECLSPEC char *mca_base_var_string_value(int vari); + #define MCA_COMPILETIME_VER "print_compiletime_version" #define MCA_RUNTIME_VER "print_runtime_version" diff --git a/test/Makefile.am b/test/Makefile.am index b3fd5f2f32c..44bb246575c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -14,6 +14,7 @@ # Copyright (c) 2015-2016 Research Organization for Information Science # and Technology (RIST). All rights reserved. # Copyright (c) 2017 IBM Corporation. All rights reserved. +# Copyright (c) 2024 Google, LLC. All rights reserved. # $COPYRIGHT$ # # Additional copyrights may follow @@ -22,7 +23,7 @@ # # support needs to be first for dependencies -SUBDIRS = support asm class threads datatype util mpool +SUBDIRS = support asm class threads datatype util mpool mca if PROJECT_OMPI SUBDIRS += monitoring spc endif diff --git a/test/class/Makefile.am b/test/class/Makefile.am index 1e39017584b..deaeacfda2d 100644 --- a/test/class/Makefile.am +++ b/test/class/Makefile.am @@ -36,7 +36,8 @@ check_PROGRAMS = \ opal_pointer_array \ opal_lifo \ opal_fifo \ - opal_cstring + opal_cstring \ + opal_include_list TESTS = $(check_PROGRAMS) @@ -101,6 +102,12 @@ opal_cstring_LDADD = \ $(top_builddir)/test/support/libsupport.a opal_cstring_DEPENDENCIES = $(opal_cstring_LDADD) +opal_include_list_SOURCES = opal_include_list.c +opal_include_list_LDADD = \ + $(top_builddir)/opal/lib@OPAL_LIB_NAME@.la \ + $(top_builddir)/test/support/libsupport.a +opal_include_list_DEPENDENCIES = $(opal_include_list_LDADD) + clean-local: rm -f opal_bitmap_test_out.txt opal_hash_table_test_out.txt opal_proc_table_test_out.txt diff --git a/test/class/opal_include_list.c b/test/class/opal_include_list.c new file mode 100644 index 00000000000..654afc2309c --- /dev/null +++ b/test/class/opal_include_list.c @@ -0,0 +1,263 @@ +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ +/* + * Copyright (c) 2024 Google, LLC. All rights reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#include "opal_config.h" +#include + +#include "opal/class/opal_include_list.h" +#include "opal/util/argv.h" +#include "opal/constants.h" +#include "opal/runtime/opal.h" +#include "support.h" + + +#define TEST_ASSERT(cond, failure_string) \ + if (!(cond)) { \ + test_failure(failure_string); \ + return test_finalize(); \ + } + +#define TEST_EXPECT(cond, failure_string) \ + if (cond) { \ + test_pass(); \ + } else { \ + test_failure(failure_string); \ + } + +static void test_pass(void) { + fprintf (stderr, "PASS\n"); + test_success(); +} + +static void check_include_list_contents(const char *check_str, const char *value, const opal_include_list_t *include_list) +{ + bool is_exclude = false; + + if ('^' == value[0]) { + is_exclude = true; + ++value; + } + + fprintf (stderr, " Checking %s list contents: ", check_str); + char **list = opal_argv_split(value, ','); + bool contents_match = true; + for (int i = 0 ; list[i] != NULL ; ++i) { + if ((NULL == include_list->items[i] || 0 != strcasecmp(list[i], include_list->items[i])) || + (list[i+1] == NULL && include_list->items[i+1] != NULL)) { + contents_match = false; + break; + } + } + TEST_EXPECT(contents_match, "contents do not match"); + + fprintf (stderr, " Checking %s include/exclude: ", check_str); + TEST_EXPECT(is_exclude == include_list->is_exclude, "list type not correctly identified"); +} + +static int test_opal_include_list_deserialize(void) +{ + test_init("opal_include_list_deserialize"); + + fprintf (stderr, " Test opal_include_list_t construction: "); + + opal_include_list_t include_list; + OBJ_CONSTRUCT(&include_list, opal_include_list_t); + if (NULL != include_list.items || include_list.is_exclude == true) { + OBJ_DESTRUCT(&include_list); + test_failure("incorrectly initial state"); + return test_finalize(); + } else { + test_pass(); + } + + fprintf (stderr, " Test parsing an exclude list: "); + char *exclude_list_string = "^foo,baz"; + int ret = include_list.super.deserialize(&include_list.super, exclude_list_string); + TEST_EXPECT(ret == OPAL_SUCCESS, ""); + check_include_list_contents("exclude", exclude_list_string, &include_list); + + fprintf (stderr, " Test parsing an include list: "); + char *include_list_string = "foo,bar,baz"; + ret = include_list.super.deserialize(&include_list.super, include_list_string); + TEST_EXPECT(ret == OPAL_SUCCESS, ""); + check_include_list_contents("include", include_list_string, &include_list); + + fprintf (stderr, " Test opal_include_list_t destruction: "); + OBJ_DESTRUCT(&include_list); + if (NULL != include_list.items || include_list.is_exclude == true) { + test_failure("incorrect state after destruct"); + } else { + test_pass(); + } + + return test_finalize(); +} + +static void check_list_rank(opal_include_list_t *include_list, char *value, int expected_rank, + bool regex_match, bool case_sensitive) +{ + fprintf (stderr, " Test matching item=%s, expected_rank=%d, regex_match=%d, " + "case_sensitive=%d: ", value, expected_rank, regex_match, case_sensitive); + int result = opal_include_list_match(include_list, value, regex_match, case_sensitive); + if (result != expected_rank) { + char *failure_message = NULL; + asprintf(&failure_message, "actual rank=%d", result); + test_failure(failure_message); + free(failure_message); + } else { + test_pass(); + } +} + +static void test_include_list_matching(opal_include_list_t *include_list) +{ + int rank_multiplier = include_list->is_exclude ? -1 : 1; + int not_found_rank = include_list->is_exclude ? 1 : -1; + for (int i = 0 ; include_list->items[i] != NULL ; ++i) { + int rank = i + 1; + int tmp_len = strlen(include_list->items[i]) + 4; + char *tmp = alloca (tmp_len); + strncpy(tmp, include_list->items[i], tmp_len); + + check_list_rank(include_list, tmp, rank_multiplier * rank, + /*regex_match=*/false, /*case_sensitive=*/true); + + /* verify case sensitivity */ + tmp[0] = isupper(tmp[0]) ? tolower(tmp[0]) : toupper(tmp[0]); + check_list_rank(include_list, tmp, not_found_rank, + /*regex_match=*/false, /*case_sensitive=*/true); + check_list_rank(include_list, tmp, rank_multiplier * rank, + /*regex_match=*/false, /*case_sensitive=*/false); + + /* include/exclude lists look for exact matches, not partial ones. verify + * this behavior. */ + tmp[0] = isupper(tmp[0]) ? tolower(tmp[0]) : toupper(tmp[0]); + strcat(tmp, "bar"); + check_list_rank(include_list, tmp, not_found_rank, + /*regex_match=*/false, /*case_sensitive=*/true); + } +} + +static int test_opal_include_list_match_include(void) +{ + test_init("opal_include_list_match"); + + opal_include_list_t include_list; + OBJ_CONSTRUCT(&include_list, opal_include_list_t); + + char *include_list_string = "foo,baz"; + fprintf (stderr, " Test parsing an include list: "); + int ret = include_list.super.deserialize(&include_list.super, include_list_string); + TEST_EXPECT(ret == OPAL_SUCCESS, ""); + + test_include_list_matching(&include_list); + + OBJ_DESTRUCT(&include_list); + return test_finalize(); +} + +static int test_opal_include_list_match_exclude(void) +{ + test_init("opal_exclude_list_match"); + + opal_include_list_t include_list; + OBJ_CONSTRUCT(&include_list, opal_include_list_t); + + char *exclude_list_string = "^foo,baz"; + fprintf (stderr, " Test parsing an exclude list: "); + int ret = include_list.super.deserialize(&include_list.super, exclude_list_string); + TEST_EXPECT(ret == OPAL_SUCCESS, ""); + + test_include_list_matching(&include_list); + + OBJ_DESTRUCT(&include_list); + return test_finalize(); +} + +static int test_opal_include_list_match_include_regex(void) +{ + test_init("opal_include_list_match_regex"); + + opal_include_list_t include_list; + OBJ_CONSTRUCT(&include_list, opal_include_list_t); + + char *include_list_string = "foo[BX]ar,b[aE]z"; + fprintf (stderr, " Test parsing a regex include list: "); + int ret = include_list.super.deserialize(&include_list.super, include_list_string); + TEST_EXPECT(ret == OPAL_SUCCESS, ""); + + check_list_rank(&include_list, "fooBar", /*expected_rank=*/1, + /*regex_match=*/true, /*case_sensitive=*/true); + check_list_rank(&include_list, "fooXar", /*expected_rank=*/1, + /*regex_match=*/true, /*case_sensitive=*/true); + check_list_rank(&include_list, "foobar", /*expected_rank=*/-1, + /*regex_match=*/true, /*case_sensitive=*/true); + check_list_rank(&include_list, "fooxar", /*expected_rank=*/-1, + /*regex_match=*/true, /*case_sensitive=*/true); + check_list_rank(&include_list, "foobar", /*expected_rank=*/1, + /*regex_match=*/true, /*case_sensitive=*/false); + check_list_rank(&include_list, "fooxar", /*expected_rank=*/1, + /*regex_match=*/true, /*case_sensitive=*/false); + check_list_rank(&include_list, "fooxar0000", /*expected_rank=*/1, + /*regex_match=*/true, /*case_sensitive=*/false); + + OBJ_DESTRUCT(&include_list); + return test_finalize(); +} + +static int test_opal_include_list_match_exclude_regex(void) +{ + test_init("opal_exclude_list_match"); + + opal_include_list_t include_list; + OBJ_CONSTRUCT(&include_list, opal_include_list_t); + + char *exclude_list_string = "^foo[BX]ar,b[aE]z";; + fprintf (stderr, " Test parsing a regex exclude list: "); + int ret = include_list.super.deserialize(&include_list.super, exclude_list_string); + TEST_EXPECT(ret == OPAL_SUCCESS, ""); + + check_list_rank(&include_list, "fooBar", /*expected_rank=*/-1, + /*regex_match=*/true, /*case_sensitive=*/true); + check_list_rank(&include_list, "fooXar", /*expected_rank=*/-1, + /*regex_match=*/true, /*case_sensitive=*/true); + check_list_rank(&include_list, "foobar", /*expected_rank=*/1, + /*regex_match=*/true, /*case_sensitive=*/true); + check_list_rank(&include_list, "fooxar", /*expected_rank=*/1, + /*regex_match=*/true, /*case_sensitive=*/true); + check_list_rank(&include_list, "foobar", /*expected_rank=*/-1, + /*regex_match=*/true, /*case_sensitive=*/false); + check_list_rank(&include_list, "fooxar", /*expected_rank=*/-1, + /*regex_match=*/true, /*case_sensitive=*/false); + check_list_rank(&include_list, "fooxar0000", /*expected_rank=*/-1, + /*regex_match=*/true, /*case_sensitive=*/false); + + OBJ_DESTRUCT(&include_list); + return test_finalize(); +} + +int main(int argc, char *argv[]) +{ + int ret = opal_init_util(&argc, &argv); + if (OPAL_SUCCESS != ret) { + fprintf (stderr, "could not initialize opal"); + exit(1); + } + + test_opal_include_list_deserialize(); + test_opal_include_list_match_exclude(); + test_opal_include_list_match_include(); + test_opal_include_list_match_include_regex(); + test_opal_include_list_match_exclude_regex(); + + opal_finalize_util(); + + return ret; +} diff --git a/test/mca/Makefile.am b/test/mca/Makefile.am new file mode 100644 index 00000000000..f66ac617618 --- /dev/null +++ b/test/mca/Makefile.am @@ -0,0 +1,41 @@ +# -*- makefile -*- +# +# Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana +# University Research and Technology +# Corporation. All rights reserved. +# Copyright (c) 2004-2007 The University of Tennessee and The University +# of Tennessee Research Foundation. All rights +# reserved. +# Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, +# University of Stuttgart. All rights reserved. +# Copyright (c) 2004-2005 The Regents of the University of California. +# All rights reserved. +# Copyright (c) 2010-2015 Cisco Systems, Inc. All rights reserved. +# Copyright (c) 2014 Research Organization for Information Science +# and Technology (RIST). All rights reserved. +# Copyright (c) 2016 IBM Corporation. All rights reserved. +# Copyright (c) 2024 Google, LLC. All rights reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +AM_CPPFLAGS="-I$(top_srcdir)/test/support" + +check_PROGRAMS = mca_var + +TESTS = $(check_PROGRAMS) + +mca_var_SOURCES = mca_var.c +mca_var_LDADD = \ + $(top_builddir)/opal/lib@OPAL_LIB_NAME@.la \ + $(top_builddir)/test/support/libsupport.a +mca_var_DEPENDENCIES = $(mca_var_LDADD) + +clean-local: + rm -f mca_var + +distclean-local: + rm -rf *.dSYM .deps .libs *.log *.txt *.o *.trs $(check_PROGRAMS) Makefile diff --git a/test/mca/mca_var.c b/test/mca/mca_var.c new file mode 100644 index 00000000000..3471d1dc7d8 --- /dev/null +++ b/test/mca/mca_var.c @@ -0,0 +1,216 @@ +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ +/* + * Copyright (c) 2024 Google, LLC. All rights reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#include "opal_config.h" +#include + +#include "opal/class/opal_include_list.h" +#include "opal/mca/base/mca_base_var.h" +#include "opal/util/argv.h" +#include "opal/constants.h" +#include "opal/runtime/opal.h" +#include "support.h" + +/* + * Data type used for testing + */ +typedef struct test_data { + /* link list data structure */ + opal_list_item_t ll_element; + /* test data */ + size_t data; +} test_data_t; + +OBJ_CLASS_INSTANCE(test_data_t, opal_list_item_t, NULL, NULL); + +#define TEST_ASSERT(cond, failure_string) \ + if (!(cond)) { \ + test_failure(failure_string); \ + return test_finalize(); \ + } + +#define TEST_EXPECT(cond, failure_string) \ + if (cond) { \ + test_pass(); \ + } else { \ + test_failure(failure_string); \ + } + +static void test_pass(void) { + fprintf (stderr, "PASS\n"); + test_success(); +} + +static char *string_var; +static opal_include_list_t include_list_var; + +static int test_string_variable(void) +{ + test_init("mca_var string"); + + unsetenv("OMPI_MCA_test_component_test_string"); + + fprintf (stderr, "Testing string MCA variables....\n"); + + fprintf (stderr, " Testing string variable creation: "); + + const char *kProjectName = "openmpi"; + const char *kFrameworkName = "test"; + const char *kComponentName = "component"; + const char *kTestVarName = "test_string"; + + char *test_string1 = "test_string1"; + string_var = test_string1; + int ret = mca_base_var_register(kProjectName, kFrameworkName, kComponentName, kTestVarName, + "String variable for test", MCA_BASE_VAR_TYPE_STRING, + /*enumerator=*/NULL, /*bind=*/0, MCA_BASE_VAR_FLAG_SETTABLE, + OPAL_INFO_LVL_1, MCA_BASE_VAR_SCOPE_LOCAL, &string_var); + TEST_ASSERT(ret >= 0, "could not create variable"); + + int vari = mca_base_var_find(kProjectName, kFrameworkName, kComponentName, kTestVarName); + TEST_ASSERT(vari >= 0, "variable register but not found"); + TEST_ASSERT(ret == vari, "mca_base_var_register did not return the correct variable index"); + + test_pass(); + + fprintf (stderr, " Testing string variable storage: "); + TEST_ASSERT(string_var != test_string1, "set value did not re-allocate string"); + ret = test_verify_str(test_string1, string_var); + if (1 == ret) { + test_pass(); + } + + fprintf (stderr, " Testing mca_base_var_set_value: "); + + const char *kTestString2 = "test_string2"; + ret = mca_base_var_set_value(vari, kTestString2, /*size=*/0, MCA_BASE_VAR_SOURCE_SET, + /*source_file=*/NULL); + ret = test_verify_int(OPAL_SUCCESS, ret); + if (ret == 1) { + fprintf (stderr, "PASS\n"); + } + + fprintf (stderr, " Retesting string variable storage: "); + TEST_ASSERT(string_var != kTestString2, "set value did not re-allocate string"); + ret = test_verify_str(kTestString2, string_var); + if (1 == ret) { + fprintf (stderr, "PASS\n"); + } + + fprintf (stderr, " Testing de-registration: "); + ret = mca_base_var_deregister(vari); + TEST_ASSERT(OPAL_SUCCESS == ret, "deregistration failed"); + fprintf (stderr, "PASS\n"); + TEST_ASSERT(string_var == NULL, "deregistration did not nullify the storage"); + + return test_finalize(); +} + +static void check_include_list_contents(const char *check_str, const char *value, const opal_include_list_t *include_list) +{ + bool is_exclude = false; + + if ('^' == value[0]) { + is_exclude = true; + ++value; + } + + fprintf (stderr, " Checking %s list contents: ", check_str); + char **list = opal_argv_split(value, ','); + bool contents_match = true; + for (int i = 0 ; list[i] != NULL ; ++i) { + if ((NULL == include_list->items[i] || 0 != strcasecmp(list[i], include_list->items[i])) || + (list[i+1] == NULL && include_list->items[i+1] != NULL)) { + contents_match = false; + break; + } + } + TEST_EXPECT(contents_match, "contents do not match"); + + fprintf (stderr, " Checking %s include/exclude: ", check_str); + TEST_EXPECT(is_exclude == include_list->is_exclude, "list type not correctly identified"); +} + +static int test_serialized_variable(void) +{ + test_init("serialized MCA variables..."); + + unsetenv("OMPI_MCA_test_component_test_include_list"); + + fprintf (stderr, "Testing include list MCA variables....\n"); + + const char *kProjectName = "openmpi"; + const char *kFrameworkName = "test"; + const char *kComponentName = "component"; + const char *kTestVarName = "test_list"; + + OBJ_CONSTRUCT(&include_list_var, opal_include_list_t); + char *exclude_list = "^foo,baz"; + int ret = include_list_var.super.deserialize(&include_list_var.super, exclude_list); + if (OPAL_SUCCESS != ret) { + test_failure("error setting initial value"); + return test_finalize(); + } + + fprintf (stderr, " Testing include list variable creation: "); + + ret = mca_base_var_register(kProjectName, kFrameworkName, kComponentName, kTestVarName, + "String variable for test", MCA_BASE_VAR_TYPE_SERIALIZABLE, + /*enumerator=*/NULL, /*bind=*/0, MCA_BASE_VAR_FLAG_SETTABLE, + OPAL_INFO_LVL_1, MCA_BASE_VAR_SCOPE_LOCAL, &include_list_var); + TEST_ASSERT(ret >= 0, "could not create variable"); + + int vari = mca_base_var_find(kProjectName, kFrameworkName, kComponentName, kTestVarName); + TEST_ASSERT(vari >= 0, "variable register but not found"); + TEST_ASSERT(ret == vari, "mca_base_var_register did not return the correct variable index"); + + test_pass(); + + check_include_list_contents("exclude", exclude_list, &include_list_var); + + fprintf (stderr, " Testing mca_base_var_set_value: "); + + char *include_list = "foo,bar,baz"; + ret = mca_base_var_set_value(vari, include_list, /*size=*/0, MCA_BASE_VAR_SOURCE_SET, + /*source_file=*/NULL); + ret = test_verify_int(OPAL_SUCCESS, ret); + if (ret == 1) { + fprintf (stderr, "PASS\n"); + } + + check_include_list_contents("include", include_list, &include_list_var); + + fprintf (stderr, " Testing de-registration: "); + ret = mca_base_var_deregister(vari); + TEST_ASSERT(OPAL_SUCCESS == ret, "deregistration failed"); + test_pass(); + + fprintf (stderr, " Testing post-deregistration state: "); + TEST_ASSERT(include_list_var.items == NULL, "deregistration did not nullify the storage"); + test_pass(); + + return test_finalize(); +} + +int main(int argc, char *argv[]) +{ + int ret = opal_init_util(&argc, &argv); + if (OPAL_SUCCESS != ret) { + fprintf (stderr, "could not initialize opal"); + exit(1); + } + + test_string_variable(); + test_serialized_variable(); + + opal_finalize_util(); + + return ret; +}