From: monojenkins Date: Thu, 15 Oct 2020 15:25:38 +0000 (-0400) Subject: [runtime] Add an options API. (#32595) X-Git-Tag: submit/tizen/20210909.063632~5061 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=eb1756e97d23df13bc6fe798e3566935c5caa7d0;p=platform%2Fupstream%2Fdotnet%2Fruntime.git [runtime] Add an options API. (#32595) * [runtime] Add an options API. Add a general options API to the runtime, based on the flags API in Google V8: ```https://chromium.googlesource.com/v8/v8.git/+/refs/heads/master/src/flags/``` Supported features: * Definition of runtime options in a declarative way. * Options are mapped to C globals. * BOOL/INT/STRING data types. * Generic option parsing code. * Generic usage code. * Read-only flags for build-time optimization. This is designed to replace the many option parsing functions in the runtime, MONO_DEBUG, the many mono_set_... functions etc. * Fix the build. Co-authored-by: vargaz Co-authored-by: Zoltan Varga --- diff --git a/src/mono/mono/mini/driver.c b/src/mono/mono/mini/driver.c index e7e0ed5..5115400 100644 --- a/src/mono/mono/mini/driver.c +++ b/src/mono/mono/mini/driver.c @@ -52,6 +52,7 @@ #include "mono/utils/mono-counters.h" #include "mono/utils/mono-hwcap.h" #include "mono/utils/mono-logger-internals.h" +#include "mono/utils/options.h" #include "mono/metadata/w32handle.h" #include "mono/metadata/callspec.h" #include "mono/metadata/custom-attrs-internals.h" @@ -1655,6 +1656,9 @@ mini_usage (void) " --handlers Install custom handlers, use --help-handlers for details.\n" " --aot-path=PATH List of additional directories to search for AOT images.\n" ); + + g_print ("\nOptions:\n"); + mono_options_print_usage (); } static void @@ -2115,6 +2119,7 @@ mono_main (int argc, char* argv[]) #ifdef HOST_WIN32 int mixed_mode = FALSE; #endif + ERROR_DECL (error); #ifdef MOONLIGHT #ifndef HOST_WIN32 @@ -2157,6 +2162,14 @@ mono_main (int argc, char* argv[]) enable_debugging = TRUE; #endif + mono_options_parse_options ((const char**)argv + 1, argc - 1, &argc, error); + argc ++; + if (!is_ok (error)) { + g_printerr ("%s", mono_error_get_message (error)); + mono_error_cleanup (error); + return 1; + } + for (i = 1; i < argc; ++i) { if (argv [i] [0] != '-') break; diff --git a/src/mono/mono/utils/CMakeLists.txt b/src/mono/mono/utils/CMakeLists.txt index 8b57108..76b5ad4 100644 --- a/src/mono/mono/utils/CMakeLists.txt +++ b/src/mono/mono/utils/CMakeLists.txt @@ -182,7 +182,10 @@ set(utils_common_sources refcount.h w32api.h unlocked.h - ward.h) + ward.h + options.h + options-def.h + options.c) if(MONO_CROSS_COMPILE) set(utils_arch_sources mach-support-unknown.c) diff --git a/src/mono/mono/utils/Makefile.am b/src/mono/mono/utils/Makefile.am index bf0b6be..8d9bd11 100644 --- a/src/mono/mono/utils/Makefile.am +++ b/src/mono/mono/utils/Makefile.am @@ -233,7 +233,10 @@ monoutils_sources = \ w32api.h \ w32subset.h \ unlocked.h \ - ward.h + ward.h \ + options.h \ + options-def.h \ + options.c arch_sources = diff --git a/src/mono/mono/utils/options-def.h b/src/mono/mono/utils/options-def.h new file mode 100644 index 0000000..00a13ec --- /dev/null +++ b/src/mono/mono/utils/options-def.h @@ -0,0 +1,65 @@ +/** + * \file Runtime options + * + * Copyright 2020 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +/* + * This file defines all the flags/options which can be set at runtime. + * + * Options defined here generate a C variable named mono_ initialized to its default value. + * The variables are exported using MONO_API. + * The _READONLY variants generate C const variables so the compiler can optimize away their usage. + * Option types: + * BOOL - gboolean + * INT - int + * STRING - (malloc-ed) char* + * + * Option can be set on the command line using: + * --[no-]-option (bool) + * --option=value (int/string) + * --option value (int/string) + */ + +/* + * This is a template header, the file including this needs to define this macro: + * DEFINE_OPTION_FULL(flag_type, ctype, c_name, cmd_name, def_value, comment) + * Optionally, define + * DEFINE_OPTION_READONLY as well. + */ +#ifndef DEFINE_OPTION_FULL +#error "" +#endif +#ifndef DEFINE_OPTION_READONLY +#define DEFINE_OPTION_READONLY(flag_type, ctype, c_name, cmd_name, def_value, comment) DEFINE_OPTION_FULL(flag_type, ctype, c_name, cmd_name, def_value, comment) +#endif + +/* Types of flags */ +#define DEFINE_BOOL(name, cmd_name, def_value, comment) DEFINE_OPTION_FULL(MONO_OPTION_BOOL, gboolean, name, cmd_name, def_value, comment) +#define DEFINE_BOOL_READONLY(name, cmd_name, def_value, comment) DEFINE_OPTION_READONLY(MONO_OPTION_BOOL_READONLY, gboolean, name, cmd_name, def_value, comment) +#define DEFINE_INT(name, cmd_name, def_value, comment) DEFINE_OPTION_FULL(MONO_OPTION_INT, int, name, cmd_name, def_value, comment) +#define DEFINE_STRING(name, cmd_name, def_value, comment) DEFINE_OPTION_FULL(MONO_OPTION_STRING, char*, name, cmd_name, def_value, comment) + +/* + * List of runtime flags + */ + +// FIXME: To avoid empty arrays, remove later +DEFINE_BOOL(bool_flag, "bool-flag", FALSE, "Example") + +/* +DEFINE_BOOL(bool_flag, "bool-flag", FALSE, "Example") +DEFINE_INT(int_flag, "int-flag", 0, "Example") +DEFINE_STRING(string_flag, "string-flag", NULL, "Example") + +#ifdef ENABLE_EXAMPLE +DEFINE_BOOL(readonly_flag, "readonly-flag", FALSE, "Example") +#else +DEFINE_BOOL_READONLY(readonly_flag, "readonly-flag", FALSE, "Example") +#endif +*/ + +/* Cleanup */ +#undef DEFINE_OPTION_FULL +#undef DEFINE_OPTION_READONLY diff --git a/src/mono/mono/utils/options.c b/src/mono/mono/utils/options.c new file mode 100644 index 0000000..f25b0fd --- /dev/null +++ b/src/mono/mono/utils/options.c @@ -0,0 +1,224 @@ +/** + * \file Runtime options + * + * Copyright 2020 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include + +#include "options.h" +#include "mono/utils/mono-error-internals.h" + +typedef enum { + MONO_OPTION_BOOL, + MONO_OPTION_BOOL_READONLY, + MONO_OPTION_INT, + MONO_OPTION_STRING +} MonoOptionType; + +/* Define flags */ +#define DEFINE_OPTION_FULL(option_type, ctype, c_name, cmd_name, def_value, comment) \ + ctype mono_opt_##c_name = def_value; +#define DEFINE_OPTION_READONLY(option_type, ctype, c_name, cmd_name, def_value, comment) +#include "options-def.h" + +/* Flag metadata */ +typedef struct { + MonoOptionType option_type; + gpointer addr; + const char *cmd_name; + int cmd_name_len; +} OptionData; + +static OptionData option_meta[] = { +#define DEFINE_OPTION_FULL(option_type, ctype, c_name, cmd_name, def_value, comment) \ + { option_type, &mono_opt_##c_name, cmd_name, sizeof (cmd_name) - 1 }, +#define DEFINE_OPTION_READONLY(option_type, ctype, c_name, cmd_name, def_value, comment) \ + { option_type, NULL, cmd_name, sizeof (cmd_name) - 1 }, +#include "options-def.h" +}; + +static const char* +option_type_to_str (MonoOptionType type) +{ + switch (type) { + case MONO_OPTION_BOOL: + return "bool"; + case MONO_OPTION_BOOL_READONLY: + return "bool (read-only)"; + case MONO_OPTION_INT: + return "int"; + case MONO_OPTION_STRING: + return "string"; + default: + g_assert_not_reached (); + return NULL; + } +} + +static char * +option_value_to_str (MonoOptionType type, gconstpointer addr) +{ + switch (type) { + case MONO_OPTION_BOOL: + case MONO_OPTION_BOOL_READONLY: + return *(gboolean*)addr ? g_strdup ("true") : g_strdup ("false"); + case MONO_OPTION_INT: + return g_strdup_printf ("%d", *(int*)addr); + case MONO_OPTION_STRING: + return *(char**)addr ? g_strdup_printf ("%s", *(char**)addr) : g_strdup ("\"\""); + default: + g_assert_not_reached (); + return NULL; + } +} + +void +mono_options_print_usage (void) +{ +#define DEFINE_OPTION_FULL(option_type, ctype, c_name, cmd_name, def_value, comment) do { \ + char *val = option_value_to_str (option_type, &mono_opt_##c_name); \ + g_printf (" --%s (%s)\n\ttype: %s default: %s\n", cmd_name, comment, option_type_to_str (option_type), val); \ + g_free (val); \ + } while (0); +#include "options-def.h" +} + +/* + * mono_optiond_parse_options: + * + * Set options based on the command line arguments in ARGV/ARGC. + * Remove processed arguments from ARGV and set *OUT_ARGC to the + * number of remaining arguments. + * + * NOTE: This only sets the variables, the caller might need to do + * additional processing based on the new values of the variables. + */ +void +mono_options_parse_options (const char **argv, int argc, int *out_argc, MonoError *error) +{ + int aindex = 0; + int i; + GHashTable *option_hash = NULL; + + while (aindex < argc) { + const char *arg = argv [aindex]; + + if (!(arg [0] == '-' && arg [1] == '-')) { + aindex ++; + continue; + } + arg = arg + 2; + + if (option_hash == NULL) { + /* Compute a hash to avoid n^2 behavior */ + option_hash = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; i < G_N_ELEMENTS (option_meta); ++i) { + g_hash_table_insert (option_hash, (gpointer)option_meta [i].cmd_name, &option_meta [i]); + } + } + + /* Compute flag name */ + char *arg_copy = g_strdup (arg); + char *optname = arg_copy; + int len = strlen (arg); + int equals_sign_index = -1; + /* Handle no- prefix */ + if (optname [0] == 'n' && optname [1] == 'o' && optname [2] == '-') { + optname += 3; + } else { + /* Handle option=value */ + for (int i = 0; i < len; ++i) { + if (optname [i] == '=') { + equals_sign_index = i; + optname [i] = '\0'; + break; + } + } + } + + OptionData *option = (OptionData*)g_hash_table_lookup (option_hash, optname); + g_free (arg_copy); + + if (!option) { + aindex ++; + continue; + } + + switch (option->option_type) { + case MONO_OPTION_BOOL: + case MONO_OPTION_BOOL_READONLY: { + gboolean negate = FALSE; + if (len == option->cmd_name_len) { + } else if (arg [0] == 'n' && arg [1] == 'o' && arg [2] == '-' && len == option->cmd_name_len + 3) { + negate = TRUE; + } else { + break; + } + if (option->option_type == MONO_OPTION_BOOL_READONLY) { + mono_error_set_error (error, 1, "Unable to set option '%s' as it's read-only.\n", arg); + break; + } + *(gboolean*)option->addr = negate ? FALSE : TRUE; + argv [aindex] = NULL; + break; + } + case MONO_OPTION_INT: + case MONO_OPTION_STRING: { + const char *value = NULL; + + if (len == option->cmd_name_len) { + // --option value + if (aindex + 1 == argc) { + mono_error_set_error (error, 1, "Missing value for option '%s'.\n", option->cmd_name); + break; + } + value = argv [aindex + 1]; + argv [aindex] = NULL; + argv [aindex + 1] = NULL; + aindex ++; + } else if (equals_sign_index != -1) { + // option=value + value = arg + equals_sign_index + 1; + argv [aindex] = NULL; + } else { + g_assert_not_reached (); + } + + if (option->option_type == MONO_OPTION_STRING) { + *(char**)option->addr = g_strdup (value); + } else { + char *endp; + long v = strtol (value, &endp, 10); + if (!value [0] || *endp) { + mono_error_set_error (error, 1, "Invalid value for option '%s': '%s'.\n", option->cmd_name, value); + break; + } + *(int*)option->addr = (int)v; + } + break; + } + default: + g_assert_not_reached (); + break; + } + + if (!is_ok (error)) + break; + aindex ++; + } + + if (option_hash) + g_hash_table_destroy (option_hash); + if (!is_ok (error)) + return; + + /* Remove processed arguments */ + aindex = 0; + for (i = 0; i < argc; ++i) { + if (argv [i]) + argv [aindex ++] = argv [i]; + } + *out_argc = aindex; +} diff --git a/src/mono/mono/utils/options.h b/src/mono/mono/utils/options.h new file mode 100644 index 0000000..bb3d24b --- /dev/null +++ b/src/mono/mono/utils/options.h @@ -0,0 +1,29 @@ +/** + * \file Runtime options + * + * Copyright 2020 Microsoft + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_UTILS_FLAGS_H__ +#define __MONO_UTILS_FLAGS_H__ + +#include +#include + +#include "mono/utils/mono-error.h" + +/* Declare list of options */ +/* Each option will declare an exported C variable named mono_opt_... */ +MONO_BEGIN_DECLS +#define DEFINE_OPTION_FULL(flag_type, ctype, c_name, cmd_name, def_value, comment) \ + MONO_API_DATA ctype mono_opt_##c_name; +#define DEFINE_OPTION_READONLY(flag_type, ctype, c_name, cmd_name, def_value, comment) \ + static const ctype mono_opt_##c_name = def_value; +#include "options-def.h" +MONO_END_DECLS + +void mono_options_print_usage (void); + +void mono_options_parse_options (const char **args, int argc, int *out_argc, MonoError *error); + +#endif diff --git a/src/mono/msvc/libmonoutils-common.targets b/src/mono/msvc/libmonoutils-common.targets index 28e3d1c..d53b74b 100644 --- a/src/mono/msvc/libmonoutils-common.targets +++ b/src/mono/msvc/libmonoutils-common.targets @@ -201,6 +201,9 @@ + + + diff --git a/src/mono/msvc/libmonoutils-common.targets.filters b/src/mono/msvc/libmonoutils-common.targets.filters index c60ab514..5692e16 100644 --- a/src/mono/msvc/libmonoutils-common.targets.filters +++ b/src/mono/msvc/libmonoutils-common.targets.filters @@ -463,6 +463,9 @@ Source Files$(MonoUtilsFilterSubFolder)\common + + Header Files$(MonoUtilsFilterSubFolder)\common + Header Files$(MonoUtilsFilterSubFolder)\common @@ -484,6 +487,12 @@ Header Files$(MonoUtilsFilterSubFolder)\common + + Header Files$(MonoUtilsFilterSubFolder)\common + + + Header Files$(MonoUtilsFilterSubFolder)\common +