+#ifdef HAVE_BACKTRACE_SYMBOLS
+#include <execinfo.h>
+#endif
+
+// Selective-enable support
+
+// Returns true for check modes which are allowed by both the current DISABLE_ macros and the MONO_CHECK_MODE env var.
+// Argument may be a bitmask; if so, result is true if at least one specified mode is enabled.
+mono_bool
+mono_check_mode_enabled (MonoCheckMode query)
+{
+ static MonoCheckMode check_mode = MONO_CHECK_MODE_UNKNOWN;
+ if (G_UNLIKELY (check_mode == MONO_CHECK_MODE_UNKNOWN))
+ {
+ MonoCheckMode env_check_mode = MONO_CHECK_MODE_NONE;
+ const gchar *env_string = g_getenv ("MONO_CHECK_MODE");
+
+ if (env_string)
+ {
+ gchar **env_split = g_strsplit (env_string, ",", 0);
+ for (gchar **env_component = env_split; *env_component; env_component++)
+ {
+ mono_bool check_all = g_str_equal (*env_component, "all");
+#ifdef ENABLE_CHECKED_BUILD_GC
+ if (check_all || g_str_equal (*env_component, "gc"))
+ env_check_mode |= MONO_CHECK_MODE_GC;
+#endif
+#ifdef ENABLE_CHECKED_BUILD_METADATA
+ if (check_all || g_str_equal (*env_component, "metadata"))
+ env_check_mode |= MONO_CHECK_MODE_METADATA;
+#endif
+#ifdef ENABLE_CHECKED_BUILD_THREAD
+ if (check_all || g_str_equal (*env_component, "thread"))
+ env_check_mode |= MONO_CHECK_MODE_THREAD;
+#endif
+ }
+ g_strfreev (env_split);
+ g_free (env_string);
+ }
+
+ check_mode = env_check_mode;
+ }
+ return check_mode & query;
+}
+
+static int
+mono_check_transition_limit (void)
+{
+ static int transition_limit = -1;
+ if (transition_limit < 0) {
+ const gchar *env_string = g_getenv ("MONO_CHECK_THREAD_TRANSITION_HISTORY");
+ if (env_string) {
+ transition_limit = atoi (env_string);
+ g_free (env_string);
+ } else {
+ transition_limit = 3;
+ }
+ }
+ return transition_limit;
+}
+
+typedef struct {
+ GPtrArray *transitions;
+ guint32 in_gc_critical_region;
+} CheckState;
+
+static MonoNativeTlsKey thread_status;
+
+void
+checked_build_init (void)
+{
+ // Init state for get_state, which can be called either by gc or thread mode
+ if (mono_check_mode_enabled (MONO_CHECK_MODE_GC | MONO_CHECK_MODE_THREAD))
+ mono_native_tls_alloc (&thread_status, NULL);
+}
+
+static CheckState*
+get_state (void)
+{
+ CheckState *state = mono_native_tls_get_value (thread_status);
+ if (!state) {
+ state = g_new0 (CheckState, 1);
+ state->transitions = g_ptr_array_new ();
+ mono_native_tls_set_value (thread_status, state);
+ }
+
+ return state;
+}
+
+#ifdef ENABLE_CHECKED_BUILD_THREAD
+