* (C) 2015 Xamarin
*/
#include <config.h>
-#ifdef CHECKED_BUILD
+
+#ifdef ENABLE_CHECKED_BUILD
#include <mono/utils/checked-build.h>
#include <mono/utils/mono-threads.h>
#include <mono/metadata/reflection-internals.h>
#include <glib.h>
+#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);
+ }
+
+ 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);
+ 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
+
#define MAX_NATIVE_BT 6
#define MAX_NATIVE_BT_PROBE (MAX_NATIVE_BT + 5)
-#define MAX_TRANSITIONS 3
-
+#define MAX_TRANSITIONS (mono_check_transition_limit ())
#ifdef HAVE_BACKTRACE_SYMBOLS
-#include <execinfo.h>
//XXX We should collect just the IPs and lazily symbolificate them.
static int
#endif
-
-typedef struct {
- GPtrArray *transitions;
- gboolean in_gc_critical_region;
-} CheckState;
-
typedef struct {
const char *name;
int from_state, next_state, suspend_count, suspend_count_delta, size;
gpointer backtrace [MAX_NATIVE_BT_PROBE];
} ThreadTransition;
-static MonoNativeTlsKey thread_status;
-
-void
-checked_build_init (void)
-{
- 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;
-}
-
static void
free_transition (ThreadTransition *t)
{
void
checked_build_thread_transition (const char *transition, void *info, int from_state, int suspend_count, int next_state, int suspend_count_delta)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_THREAD))
+ return;
+
MonoThreadInfo *cur = mono_thread_info_current_unchecked ();
CheckState *state = get_state ();
/* We currently don't record external changes as those are hard to reason about. */
g_ptr_array_add (state->transitions, t);
}
-static void
-assertion_fail (const char *msg, ...)
+void
+mono_fatal_with_history (const char *msg, ...)
{
int i;
GString* err = g_string_sized_new (100);
- CheckState *state = get_state ();
g_string_append_printf (err, "Assertion failure in thread %p due to: ", mono_native_thread_id_get ());
g_string_append_vprintf (err, msg, args);
va_end (args);
- g_string_append_printf (err, "\nLast %d state transitions: (most recent first)\n", state->transitions->len);
-
- for (i = state->transitions->len - 1; i >= 0; --i) {
- ThreadTransition *t = state->transitions->pdata [i];
- char *bt = translate_backtrace (t->backtrace, t->size);
- g_string_append_printf (err, "[%s] %s -> %s (%d) %s%d at:\n%s",
- t->name,
- mono_thread_state_name (t->from_state),
- mono_thread_state_name (t->next_state),
- t->suspend_count,
- t->suspend_count_delta > 0 ? "+" : "", //I'd like to see this sort of values: -1, 0, +1
- t->suspend_count_delta,
- bt);
- g_free (bt);
+ if (mono_check_mode_enabled (MONO_CHECK_MODE_THREAD))
+ {
+ CheckState *state = get_state ();
+
+ g_string_append_printf (err, "\nLast %d state transitions: (most recent first)\n", state->transitions->len);
+
+ for (i = state->transitions->len - 1; i >= 0; --i) {
+ ThreadTransition *t = state->transitions->pdata [i];
+ char *bt = translate_backtrace (t->backtrace, t->size);
+ g_string_append_printf (err, "[%s] %s -> %s (%d) %s%d at:\n%s",
+ t->name,
+ mono_thread_state_name (t->from_state),
+ mono_thread_state_name (t->next_state),
+ t->suspend_count,
+ t->suspend_count_delta > 0 ? "+" : "", //I'd like to see this sort of values: -1, 0, +1
+ t->suspend_count_delta,
+ bt);
+ g_free (bt);
+ }
}
g_error (err->str);
g_string_free (err, TRUE);
}
+#endif /* defined(ENABLE_CHECKED_BUILD_THREAD) */
+
+#ifdef ENABLE_CHECKED_BUILD_GC
+
void
assert_gc_safe_mode (void)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
MonoThreadInfo *cur = mono_thread_info_current ();
int state;
if (!cur)
- assertion_fail ("Expected GC Safe mode but thread is not attached");
+ mono_fatal_with_history ("Expected GC Safe mode but thread is not attached");
switch (state = mono_thread_info_current_state (cur)) {
case STATE_BLOCKING:
case STATE_BLOCKING_AND_SUSPENDED:
break;
default:
- assertion_fail ("Expected GC Safe mode but was in %s state", mono_thread_state_name (state));
+ mono_fatal_with_history ("Expected GC Safe mode but was in %s state", mono_thread_state_name (state));
}
}
void
assert_gc_unsafe_mode (void)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
MonoThreadInfo *cur = mono_thread_info_current ();
int state;
if (!cur)
- assertion_fail ("Expected GC Unsafe mode but thread is not attached");
+ mono_fatal_with_history ("Expected GC Unsafe mode but thread is not attached");
switch (state = mono_thread_info_current_state (cur)) {
case STATE_RUNNING:
case STATE_SELF_SUSPEND_REQUESTED:
break;
default:
- assertion_fail ("Expected GC Unsafe mode but was in %s state", mono_thread_state_name (state));
+ mono_fatal_with_history ("Expected GC Unsafe mode but was in %s state", mono_thread_state_name (state));
}
}
void
assert_gc_neutral_mode (void)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
MonoThreadInfo *cur = mono_thread_info_current ();
int state;
if (!cur)
- assertion_fail ("Expected GC Neutral mode but thread is not attached");
+ mono_fatal_with_history ("Expected GC Neutral mode but thread is not attached");
switch (state = mono_thread_info_current_state (cur)) {
case STATE_RUNNING:
case STATE_BLOCKING_AND_SUSPENDED:
break;
default:
- assertion_fail ("Expected GC Neutral mode but was in %s state", mono_thread_state_name (state));
+ mono_fatal_with_history ("Expected GC Neutral mode but was in %s state", mono_thread_state_name (state));
}
}
void *
critical_gc_region_begin(void)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return NULL;
+
CheckState *state = get_state ();
- state->in_gc_critical_region = TRUE;
+ state->in_gc_critical_region++;
return state;
}
void
critical_gc_region_end(void* token)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
CheckState *state = get_state();
g_assert (state == token);
- state->in_gc_critical_region = FALSE;
+ state->in_gc_critical_region--;
}
void
assert_not_in_gc_critical_region(void)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
CheckState *state = get_state();
- if (state->in_gc_critical_region) {
- MonoThreadInfo *cur = mono_thread_info_current();
- state = mono_thread_info_current_state(cur);
- assertion_fail("Expected GC Unsafe mode, but was in %s state", mono_thread_state_name(state));
+ if (state->in_gc_critical_region > 0) {
+ mono_fatal_with_history("Expected GC Unsafe mode, but was in %s state", mono_thread_state_name (mono_thread_info_current_state (mono_thread_info_current ())));
}
}
+void
+assert_in_gc_critical_region (void)
+{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ return;
+
+ CheckState *state = get_state();
+ if (state->in_gc_critical_region == 0)
+ mono_fatal_with_history("Expected GC critical region");
+}
+
+#endif /* defined(ENABLE_CHECKED_BUILD_GC) */
+
+#ifdef ENABLE_CHECKED_BUILD_METADATA
+
// check_metadata_store et al: The goal of these functions is to verify that if there is a pointer from one mempool into
// another, that the pointed-to memory is protected by the reference count mechanism for MonoImages.
//
static void
check_mempool_may_reference_mempool (void *from_ptr, void *to_ptr, gboolean require_local)
{
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_METADATA))
+ return;
+
// Null pointers are OK
if (!to_ptr)
return;
check_mempool_may_reference_mempool (from, to, TRUE);
}
-#endif /* CHECKED_BUILD */
+#endif /* defined(ENABLE_CHECKED_BUILD_METADATA) */
+
+#endif /* ENABLE_CHECKED_BUILD */