* [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 <vargaz@users.noreply.github.com>
Co-authored-by: Zoltan Varga <vargaz@gmail.com>
#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"
" --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
#ifdef HOST_WIN32
int mixed_mode = FALSE;
#endif
+ ERROR_DECL (error);
#ifdef MOONLIGHT
#ifndef HOST_WIN32
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;
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)
w32api.h \
w32subset.h \
unlocked.h \
- ward.h
+ ward.h \
+ options.h \
+ options-def.h \
+ options.c
arch_sources =
--- /dev/null
+/**
+ * \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_<flag name> 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
--- /dev/null
+/**
+ * \file Runtime options
+ *
+ * Copyright 2020 Microsoft
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+#include <stdio.h>
+
+#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;
+}
--- /dev/null
+/**
+ * \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 <config.h>
+#include <glib.h>
+
+#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
<ClInclude Include="$(MonoSourceLocation)\mono\utils\unlocked.h" />
<ClCompile Include="$(MonoSourceLocation)\mono\utils\mono-state.c" />
<ClInclude Include="$(MonoSourceLocation)\mono\utils\mono-state.h" />
+ <ClInclude Include="$(MonoSourceLocation)\mono\utils\options.h" />
+ <ClCompile Include="$(MonoSourceLocation)\mono\utils\options.c" />
+ <ClInclude Include="$(MonoSourceLocation)\mono\utils\options-def.h" />
</ItemGroup>
<ItemGroup Label="libmonoutilsinclude_headers">
<ClInclude Include="$(MonoSourceLocation)\mono\utils\mono-logger.h" />
<ClCompile Include="$(MonoSourceLocation)\mono\utils\checked-build.c">
<Filter>Source Files$(MonoUtilsFilterSubFolder)\common</Filter>
</ClCompile>
+ <ClCompile Include="$(MonoSourceLocation)\mono\utils\options.c">
+ <Filter>Header Files$(MonoUtilsFilterSubFolder)\common</Filter>
+ </ClCompile>
<ClInclude Include="$(MonoSourceLocation)\mono\utils\checked-build.h">
<Filter>Header Files$(MonoUtilsFilterSubFolder)\common</Filter>
</ClInclude>
<ClInclude Include="$(MonoSourceLocation)\mono\utils\mono-state.h">
<Filter>Header Files$(MonoUtilsFilterSubFolder)\common</Filter>
</ClInclude>
+ <ClInclude Include="$(MonoSourceLocation)\mono\utils\options.h">
+ <Filter>Header Files$(MonoUtilsFilterSubFolder)\common</Filter>
+ </ClInclude>
+ <ClInclude Include="$(MonoSourceLocation)\mono\utils\options-def.h">
+ <Filter>Header Files$(MonoUtilsFilterSubFolder)\common</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup Label="libmonoutilsinclude_headers">
<ClInclude Include="$(MonoSourceLocation)\mono\utils\mono-logger.h">