[2019-12] [threads] Add back mono_threads_attach_tools_thread as a public API (#18074)
[mono-project.git] / mono / utils / checked-build.c
blob864c8f888f4d4d459ebac6e7a5362e8c691ffbd3
1 /**
2 * \file
3 * Expensive asserts used when mono is built with --with-checked-build=yes
5 * Author:
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * (C) 2015 Xamarin
9 */
10 #include <config.h>
11 #include <mono/utils/mono-compiler.h>
13 #ifdef ENABLE_CHECKED_BUILD
15 #include <mono/utils/checked-build.h>
16 #include <mono/utils/mono-threads.h>
17 #include <mono/utils/mono-threads-coop.h>
18 #include <mono/utils/mono-tls.h>
19 #include <mono/metadata/mempool.h>
20 #include <mono/metadata/metadata-internals.h>
21 #include <mono/metadata/image-internals.h>
22 #include <mono/metadata/loaded-images-internals.h>
23 #include <mono/metadata/class-internals.h>
24 #include <mono/metadata/reflection-internals.h>
25 #include <glib.h>
27 #ifdef HAVE_BACKTRACE_SYMBOLS
28 #include <execinfo.h>
29 #endif
31 // Selective-enable support
33 // Returns true for check modes which are allowed by both the current DISABLE_ macros and the MONO_CHECK_MODE env var.
34 // Argument may be a bitmask; if so, result is true if at least one specified mode is enabled.
35 mono_bool
36 mono_check_mode_enabled (MonoCheckMode query)
38 static MonoCheckMode check_mode = MONO_CHECK_MODE_UNKNOWN;
39 if (G_UNLIKELY (check_mode == MONO_CHECK_MODE_UNKNOWN))
41 MonoCheckMode env_check_mode = MONO_CHECK_MODE_NONE;
42 gchar *env_string = g_getenv ("MONO_CHECK_MODE");
44 if (env_string)
46 gchar **env_split = g_strsplit (env_string, ",", 0);
47 for (gchar **env_component = env_split; *env_component; env_component++)
49 mono_bool check_all = g_str_equal (*env_component, "all");
50 #ifdef ENABLE_CHECKED_BUILD_GC
51 if (check_all || g_str_equal (*env_component, "gc"))
52 env_check_mode |= MONO_CHECK_MODE_GC;
53 #endif
54 #ifdef ENABLE_CHECKED_BUILD_METADATA
55 if (check_all || g_str_equal (*env_component, "metadata"))
56 env_check_mode |= MONO_CHECK_MODE_METADATA;
57 #endif
58 #ifdef ENABLE_CHECKED_BUILD_THREAD
59 if (check_all || g_str_equal (*env_component, "thread"))
60 env_check_mode |= MONO_CHECK_MODE_THREAD;
61 #endif
63 g_strfreev (env_split);
64 g_free (env_string);
67 check_mode = env_check_mode;
69 return check_mode & query;
72 static int
73 mono_check_transition_limit (void)
75 static int transition_limit = -1;
76 if (transition_limit < 0) {
77 gchar *env_string = g_getenv ("MONO_CHECK_THREAD_TRANSITION_HISTORY");
78 if (env_string) {
79 transition_limit = atoi (env_string);
80 g_free (env_string);
81 } else {
82 transition_limit = 3;
85 return transition_limit;
88 #define MAX_NATIVE_BT 6
89 #define MAX_NATIVE_BT_PROBE (MAX_NATIVE_BT + 5)
90 #define MAX_TRANSITIONS (mono_check_transition_limit ())
92 typedef struct {
93 const char *name;
94 int from_state, next_state, suspend_count, suspend_count_delta, size;
95 gpointer backtrace [MAX_NATIVE_BT_PROBE];
96 } ThreadTransition;
98 typedef struct {
99 guint32 in_gc_critical_region;
100 // ring buffer of transitions, indexed by two guint16 indices:
101 // push at buf_end, iterate from buf_start. valid range is
102 // buf_start ... buf_end - 1 mod MAX_TRANSITIONS
103 gint32 ringbuf;
104 ThreadTransition transitions [MONO_ZERO_LEN_ARRAY];
105 } CheckState;
107 static MonoNativeTlsKey thread_status;
109 static mono_mutex_t backtrace_mutex;
112 void
113 checked_build_init (void)
115 // Init state for get_state, which can be called either by gc or thread mode
116 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC | MONO_CHECK_MODE_THREAD))
117 mono_native_tls_alloc (&thread_status, NULL);
118 #if HAVE_BACKTRACE_SYMBOLS
119 mono_os_mutex_init (&backtrace_mutex);
120 #endif
123 static gboolean
124 backtrace_mutex_trylock (void)
126 return mono_os_mutex_trylock (&backtrace_mutex) == 0;
129 static void
130 backtrace_mutex_unlock (void)
132 return mono_os_mutex_unlock (&backtrace_mutex);
135 static CheckState*
136 get_state (void)
138 CheckState *state = (CheckState*)mono_native_tls_get_value (thread_status);
139 if (!state) {
140 state = (CheckState*) g_malloc0 (sizeof (CheckState) + sizeof(ThreadTransition) * MAX_TRANSITIONS);
141 mono_native_tls_set_value (thread_status, state);
144 return state;
147 static void
148 ringbuf_unpack (gint32 ringbuf, guint16 *buf_start, guint16 *buf_end)
150 *buf_start = (guint16) (ringbuf >> 16);
151 *buf_end = (guint16) (ringbuf & 0x00FF);
154 static gint32
155 ringbuf_pack (guint16 buf_start, guint16 buf_end)
157 return ((((gint32)buf_start) << 16) | ((gint32)buf_end));
160 static int
161 ringbuf_size (guint32 ringbuf, int n)
163 guint16 buf_start, buf_end;
164 ringbuf_unpack (ringbuf, &buf_start, &buf_end);
165 if (buf_end > buf_start)
166 return buf_end - buf_start;
167 else
168 return n - (buf_start - buf_end);
171 static guint16
172 ringbuf_push (gint32 *ringbuf, int n)
174 gint32 ringbuf_old, ringbuf_new;
175 guint16 buf_start, buf_end;
176 guint16 cur;
177 retry:
178 ringbuf_old = *ringbuf;
179 ringbuf_unpack (ringbuf_old, &buf_start, &buf_end);
180 cur = buf_end++;
181 if (buf_end == n)
182 buf_end = 0;
183 if (buf_end == buf_start) {
184 if (++buf_start == n)
185 buf_start = 0;
187 ringbuf_new = ringbuf_pack (buf_start, buf_end);
188 if (mono_atomic_cas_i32 (ringbuf, ringbuf_new, ringbuf_old) != ringbuf_old)
189 goto retry;
190 return cur;
193 #ifdef ENABLE_CHECKED_BUILD_THREAD
195 #ifdef HAVE_BACKTRACE_SYMBOLS
197 //XXX We should collect just the IPs and lazily symbolificate them.
198 static int
199 collect_backtrace (gpointer out_data[])
201 #if defined (__GNUC__) && !defined (__clang__)
202 /* GNU libc backtrace calls _Unwind_Backtrace in libgcc, which internally may take a lock. */
203 /* Suppose we're using hybrid suspend and T1 is in GC Unsafe and T2 is
204 * GC Safe. T1 will be coop suspended, and T2 will be async suspended.
205 * Suppose T1 is in RUNNING, and T2 just changed from RUNNING to
206 * BLOCKING and it is in trace_state_change to record this fact.
208 * suspend initiator: switches T1 to ASYNC_SUSPEND_REQUESTED
209 * suspend initiator: switches T2 to BLOCKING_SUSPEND_REQUESTED and sends a suspend signal
210 * T1: calls mono_threads_transition_state_poll (),
211 * T1: switches to SELF_SUSPENDED and starts trace_state_change ()
212 * T2: is still in checked_build_thread_transition for the RUNNING->BLOCKING transition and calls backtrace ()
213 * T2: suspend signal lands while T2 is in backtrace() holding a lock; T2 switches to BLOCKING_ASYNC_SUSPENDED () and waits for resume
214 * T1: calls backtrace (), waits for the lock ()
215 * suspend initiator: waiting for T1 to suspend.
217 * At this point we're deadlocked.
219 * So what we'll do is try to take a lock before calling backtrace and
220 * only collect a backtrace if there is no contention.
222 int i;
223 for (i = 0; i < 2; i++ ) {
224 if (backtrace_mutex_trylock ()) {
225 int sz = backtrace (out_data, MAX_NATIVE_BT_PROBE);
226 backtrace_mutex_unlock ();
227 return sz;
228 } else {
229 mono_thread_info_yield ();
232 /* didn't get a backtrace, oh well. */
233 return 0;
234 #else
235 return backtrace (out_data, MAX_NATIVE_BT_PROBE);
236 #endif
239 static char*
240 translate_backtrace (gpointer native_trace[], int size)
242 if (size == 0)
243 return g_strdup ("");
244 char **names = backtrace_symbols (native_trace, size);
245 GString* bt = g_string_sized_new (100);
247 int i, j = -1;
249 //Figure out the cut point of useless backtraces
250 //We'll skip up to the caller of checked_build_thread_transition
251 for (i = 0; i < size; ++i) {
252 if (strstr (names [i], "checked_build_thread_transition")) {
253 j = i + 1;
254 break;
258 if (j == -1)
259 j = 0;
260 for (i = j; i < size; ++i) {
261 if (i - j <= MAX_NATIVE_BT)
262 g_string_append_printf (bt, "\tat %s\n", names [i]);
265 g_free (names);
266 return g_string_free (bt, FALSE);
269 #else
271 static int
272 collect_backtrace (gpointer out_data[])
274 return 0;
277 static char*
278 translate_backtrace (gpointer native_trace[], int size)
280 return g_strdup ("\tno backtrace available\n");
283 #endif
285 void
286 checked_build_thread_transition (const char *transition, void *info, int from_state, int suspend_count, int next_state, int suspend_count_delta, gboolean capture_backtrace)
288 if (!mono_check_mode_enabled (MONO_CHECK_MODE_THREAD))
289 return;
291 /* We currently don't record external changes as those are hard to reason about. */
292 if (!mono_thread_info_is_current ((THREAD_INFO_TYPE*)info))
293 return;
295 CheckState *state = get_state ();
297 guint16 cur = ringbuf_push (&state->ringbuf, MAX_TRANSITIONS);
299 ThreadTransition *t = &state->transitions[cur];
300 t->name = transition;
301 t->from_state = from_state;
302 t->next_state = next_state;
303 t->suspend_count = suspend_count;
304 t->suspend_count_delta = suspend_count_delta;
305 if (capture_backtrace)
306 t->size = collect_backtrace (t->backtrace);
307 else
308 t->size = 0;
311 void
312 mono_fatal_with_history (const char * volatile msg, ...)
314 GString* err = g_string_sized_new (100);
316 g_string_append_printf (err, "Assertion failure in thread %p due to: ", mono_native_thread_id_get ());
318 va_list args;
319 va_start (args, msg);
320 g_string_append_vprintf (err, msg, args);
321 va_end (args);
323 if (mono_check_mode_enabled (MONO_CHECK_MODE_THREAD))
325 CheckState *state = get_state ();
326 guint16 cur, end;
327 int len = ringbuf_size (state->ringbuf, MAX_TRANSITIONS);
329 g_string_append_printf (err, "\nLast %d state transitions: (most recent first)\n", len);
331 ringbuf_unpack (state->ringbuf, &cur, &end);
332 while (cur != end) {
333 ThreadTransition *t = &state->transitions[cur];
334 char *bt = translate_backtrace (t->backtrace, t->size);
335 g_string_append_printf (err, "[%s] %s -> %s (%d) %s%d at:\n%s",
336 t->name,
337 mono_thread_state_name (t->from_state),
338 mono_thread_state_name (t->next_state),
339 t->suspend_count,
340 t->suspend_count_delta > 0 ? "+" : "", //I'd like to see this sort of values: -1, 0, +1
341 t->suspend_count_delta,
342 bt);
343 g_free (bt);
344 if (++cur == MAX_TRANSITIONS)
345 cur = 0;
349 g_error (err->str);
350 g_string_free (err, TRUE);
353 #endif /* defined(ENABLE_CHECKED_BUILD_THREAD) */
355 #ifdef ENABLE_CHECKED_BUILD_GC
357 void
358 assert_gc_safe_mode (const char *file, int lineno)
360 if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
361 return;
363 MonoThreadInfo *cur = mono_thread_info_current ();
365 if (!cur)
366 mono_fatal_with_history ("%s:%d: Expected GC Safe mode but thread is not attached", file, lineno);
368 int state = mono_thread_info_current_state (cur);
369 switch (state) {
370 case STATE_BLOCKING:
371 case STATE_BLOCKING_SELF_SUSPENDED:
372 case STATE_BLOCKING_SUSPEND_REQUESTED:
373 break;
374 default:
375 mono_fatal_with_history ("%s:%d: Expected GC Safe mode but was in %s state", file, lineno, mono_thread_state_name (state));
379 void
380 assert_gc_unsafe_mode (const char *file, int lineno)
382 if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
383 return;
385 MonoThreadInfo *cur = mono_thread_info_current ();
387 if (!cur)
388 mono_fatal_with_history ("%s:%d: Expected GC Unsafe mode but thread is not attached", file, lineno);
390 int state = mono_thread_info_current_state (cur);
391 switch (state) {
392 case STATE_RUNNING:
393 case STATE_ASYNC_SUSPEND_REQUESTED:
394 break;
395 default:
396 mono_fatal_with_history ("%s:%d: Expected GC Unsafe mode but was in %s state", file, lineno, mono_thread_state_name (state));
400 void
401 assert_gc_neutral_mode (const char *file, int lineno)
403 if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
404 return;
406 MonoThreadInfo *cur = mono_thread_info_current ();
408 if (!cur)
409 mono_fatal_with_history ("%s:%d: Expected GC Neutral mode but thread is not attached", file, lineno);
411 int state = mono_thread_info_current_state (cur);
412 switch (state) {
413 case STATE_RUNNING:
414 case STATE_ASYNC_SUSPEND_REQUESTED:
415 case STATE_BLOCKING:
416 case STATE_BLOCKING_SELF_SUSPENDED:
417 case STATE_BLOCKING_SUSPEND_REQUESTED:
418 break;
419 default:
420 mono_fatal_with_history ("%s:%d: Expected GC Neutral mode but was in %s state", file, lineno, mono_thread_state_name (state));
424 void *
425 critical_gc_region_begin(void)
427 if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
428 return NULL;
430 CheckState *state = get_state ();
431 state->in_gc_critical_region++;
432 return state;
436 void
437 critical_gc_region_end(void* token)
439 if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
440 return;
442 CheckState *state = get_state();
443 g_assert (state == token);
444 state->in_gc_critical_region--;
447 void
448 assert_not_in_gc_critical_region(void)
450 if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
451 return;
453 CheckState *state = get_state();
454 if (state->in_gc_critical_region > 0) {
455 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 ())));
459 void
460 assert_in_gc_critical_region (void)
462 if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
463 return;
465 CheckState *state = get_state();
466 if (state->in_gc_critical_region == 0)
467 mono_fatal_with_history("Expected GC critical region");
470 #endif /* defined(ENABLE_CHECKED_BUILD_GC) */
472 #ifdef ENABLE_CHECKED_BUILD_METADATA
474 // check_metadata_store et al: The goal of these functions is to verify that if there is a pointer from one mempool into
475 // another, that the pointed-to memory is protected by the reference count mechanism for MonoImages.
477 // Note: The code below catches only some kinds of failures. Failures outside its scope notably incode:
478 // * Code below absolutely assumes that no mempool is ever held as "mempool" member by more than one Image or ImageSet at once
479 // * Code below assumes reference counts never underflow (ie: if we have a pointer to something, it won't be deallocated while we're looking at it)
480 // Locking strategy is a little slapdash overall.
482 // Reference audit support
483 #define check_mempool_assert_message(...) \
484 g_assertion_message("Mempool reference violation: " __VA_ARGS__)
486 typedef struct
488 MonoImage *image;
489 MonoImageSet *image_set;
490 } MonoMemPoolOwner;
492 static MonoMemPoolOwner mono_mempool_no_owner = {NULL,NULL};
494 static gboolean
495 check_mempool_owner_eq (MonoMemPoolOwner a, MonoMemPoolOwner b)
497 return a.image == b.image && a.image_set == b.image_set;
500 // Say image X "references" image Y if X either contains Y in its modules field, or X’s "references" field contains an
501 // assembly whose image is Y.
502 // Say image X transitively references image Y if there is any chain of images-referencing-images which leads from X to Y.
503 // Once the mempools for two pointers have been looked up, there are four possibilities:
505 // Case 1. Image FROM points to Image TO: Legal if FROM transitively references TO
507 // We'll do a simple BFS graph search on images. For each image we visit:
508 static void
509 check_image_search (GHashTable *visited, GPtrArray *next, MonoImage *candidate, MonoImage *goal, gboolean *success)
511 // Image hasn't even been loaded-- ignore it
512 if (!candidate)
513 return;
515 // Image has already been visited-- ignore it
516 if (g_hash_table_lookup_extended (visited, candidate, NULL, NULL))
517 return;
519 // Image is the target-- mark success
520 if (candidate == goal)
522 *success = TRUE;
523 return;
526 // Unvisited image, queue it to have its children visited
527 g_hash_table_insert (visited, candidate, NULL);
528 g_ptr_array_add (next, candidate);
529 return;
532 static gboolean
533 check_image_may_reference_image(MonoImage *from, MonoImage *to)
535 if (to == from) // Shortcut
536 return TRUE;
538 // Corlib is never unloaded, and all images implicitly reference it.
539 // Some images avoid explicitly referencing it as an optimization, so special-case it here.
540 if (to == mono_defaults.corlib)
541 return TRUE;
543 // Non-dynamic images may NEVER reference dynamic images
544 if (to->dynamic && !from->dynamic)
545 return FALSE;
547 // FIXME: We currently give a dynamic images a pass on the reference rules.
548 // Dynamic images may ALWAYS reference non-dynamic images.
549 // We allow this because the dynamic image code is known "messy", and in theory it is already
550 // protected because dynamic images can only reference classes their assembly has retained.
551 // However, long term, we should make this rigorous.
552 if (from->dynamic && !to->dynamic)
553 return TRUE;
555 gboolean success = FALSE;
557 // Images to inspect on this pass, images to inspect on the next pass
558 GPtrArray *current = g_ptr_array_sized_new (1), *next = g_ptr_array_new ();
560 // Because in practice the image graph contains cycles, we must track which images we've visited
561 GHashTable *visited = g_hash_table_new (NULL, NULL);
563 #define CHECK_IMAGE_VISIT(i) check_image_search (visited, next, (i), to, &success)
565 CHECK_IMAGE_VISIT (from); // Initially "next" contains only from node
567 // For each pass exhaust the "to check" queue while filling up the "check next" queue
568 while (!success && next->len > 0) // Halt on success or when out of nodes to process
570 // Swap "current" and "next" and clear next
571 GPtrArray *temp = current;
572 current = next;
573 next = temp;
574 g_ptr_array_set_size (next, 0);
576 int current_idx;
577 for(current_idx = 0; current_idx < current->len; current_idx++)
579 MonoImage *checking = (MonoImage*)g_ptr_array_index (current, current_idx);
581 mono_image_lock (checking);
583 // For each queued image visit all directly referenced images
584 int inner_idx;
586 // 'files' and 'modules' semantically contain the same items but because of lazy loading we must check both
587 for (inner_idx = 0; !success && inner_idx < checking->file_count; inner_idx++)
589 CHECK_IMAGE_VISIT (checking->files[inner_idx]);
592 for (inner_idx = 0; !success && inner_idx < checking->module_count; inner_idx++)
594 CHECK_IMAGE_VISIT (checking->modules[inner_idx]);
597 for (inner_idx = 0; !success && inner_idx < checking->nreferences; inner_idx++)
599 // Assembly references are lazy-loaded and thus allowed to be NULL.
600 // If they are NULL, we don't care about them for this search, because their images haven't impacted ref_count yet.
601 if (checking->references[inner_idx])
603 CHECK_IMAGE_VISIT (checking->references[inner_idx]->image);
607 mono_image_unlock (checking);
611 g_ptr_array_free (current, TRUE); g_ptr_array_free (next, TRUE); g_hash_table_destroy (visited);
613 return success;
616 // Case 2. ImageSet FROM points to Image TO: One of FROM's "images" either is, or transitively references, TO.
617 static gboolean
618 check_image_set_may_reference_image (MonoImageSet *from, MonoImage *to)
620 // See above-- All images implicitly reference corlib
621 if (to == mono_defaults.corlib)
622 return TRUE;
624 int idx;
625 gboolean success = FALSE;
626 mono_image_set_lock (from);
627 for (idx = 0; !success && idx < from->nimages; idx++)
629 if (check_image_may_reference_image (from->images[idx], to))
630 success = TRUE;
632 mono_image_set_unlock (from);
634 return success; // No satisfying image found in from->images
637 // Case 3. ImageSet FROM points to ImageSet TO: The images in TO are a strict subset of FROM (no transitive relationship is important here)
638 static gboolean
639 check_image_set_may_reference_image_set (MonoImageSet *from, MonoImageSet *to)
641 if (to == from)
642 return TRUE;
644 gboolean valid = TRUE; // Until proven otherwise
646 mono_image_set_lock (from); mono_image_set_lock (to);
648 int to_idx, from_idx;
649 for (to_idx = 0; valid && to_idx < to->nimages; to_idx++)
651 gboolean seen = FALSE;
653 // If TO set includes corlib, the FROM set may
654 // implicitly reference corlib, even if it's not
655 // present in the set explicitly.
656 if (to->images[to_idx] == mono_defaults.corlib)
657 seen = TRUE;
659 // For each item in to->images, scan over from->images seeking a path to it.
660 for (from_idx = 0; !seen && from_idx < from->nimages; from_idx++)
662 if (check_image_may_reference_image (from->images[from_idx], to->images[to_idx]))
663 seen = TRUE;
666 // If the to->images item is not found in from->images, the subset check has failed
667 if (!seen)
668 valid = FALSE;
671 mono_image_set_unlock (from); mono_image_set_unlock (to);
673 return valid; // All items in "to" were found in "from"
676 // Case 4. Image FROM points to ImageSet TO: FROM transitively references *ALL* of the “images” listed in TO
677 static gboolean
678 check_image_may_reference_image_set (MonoImage *from, MonoImageSet *to)
680 if (to->nimages == 0) // Malformed image_set
681 return FALSE;
683 gboolean valid = TRUE;
685 mono_image_set_lock (to);
686 int idx;
687 for (idx = 0; valid && idx < to->nimages; idx++)
689 if (!check_image_may_reference_image (from, to->images[idx]))
690 valid = FALSE;
692 mono_image_set_unlock (to);
694 return valid; // All images in to->images checked out
697 // Small helper-- get a descriptive string for a MonoMemPoolOwner
698 // Callers are obligated to free buffer with g_free after use
699 static const char *
700 check_mempool_owner_name (MonoMemPoolOwner owner)
702 GString *result = g_string_new (NULL);
703 if (owner.image)
705 if (owner.image->dynamic)
706 g_string_append (result, "(Dynamic)");
707 g_string_append (result, owner.image->name);
709 else if (owner.image_set)
711 char *temp = mono_image_set_description (owner.image_set);
712 g_string_append (result, "(Image set)");
713 g_string_append (result, temp);
714 g_free (temp);
716 else
718 g_string_append (result, "(Non-image memory)");
720 return g_string_free (result, FALSE);
723 // Helper -- surf various image-locating functions looking for the owner of this pointer
724 static MonoMemPoolOwner
725 mono_find_mempool_owner (void *ptr)
727 MonoMemPoolOwner owner = mono_mempool_no_owner;
729 owner.image = mono_find_image_owner (ptr);
730 if (!check_mempool_owner_eq (owner, mono_mempool_no_owner))
731 return owner;
733 owner.image_set = mono_find_image_set_owner (ptr);
734 if (!check_mempool_owner_eq (owner, mono_mempool_no_owner))
735 return owner;
737 owner.image = mono_find_dynamic_image_owner (ptr);
739 return owner;
742 // Actually perform reference audit
743 static void
744 check_mempool_may_reference_mempool (void *from_ptr, void *to_ptr, gboolean require_local)
746 if (!mono_check_mode_enabled (MONO_CHECK_MODE_METADATA))
747 return;
749 // Null pointers are OK
750 if (!to_ptr)
751 return;
753 MonoMemPoolOwner from = mono_find_mempool_owner (from_ptr), to = mono_find_mempool_owner (to_ptr);
755 if (require_local)
757 if (!check_mempool_owner_eq (from,to))
758 check_mempool_assert_message ("Pointer in image %s should have been internal, but instead pointed to image %s", check_mempool_owner_name (from), check_mempool_owner_name (to));
761 // Writing into unknown mempool
762 else if (check_mempool_owner_eq (from, mono_mempool_no_owner))
764 check_mempool_assert_message ("Non-image memory attempting to write pointer to image %s", check_mempool_owner_name (to));
767 // Reading from unknown mempool
768 else if (check_mempool_owner_eq (to, mono_mempool_no_owner))
770 check_mempool_assert_message ("Attempting to write pointer from image %s to non-image memory", check_mempool_owner_name (from));
773 // Split out the four cases described above:
774 else if (from.image && to.image)
776 if (!check_image_may_reference_image (from.image, to.image))
777 check_mempool_assert_message ("Image %s tried to point to image %s, but does not retain a reference", check_mempool_owner_name (from), check_mempool_owner_name (to));
780 else if (from.image && to.image_set)
782 if (!check_image_may_reference_image_set (from.image, to.image_set))
783 check_mempool_assert_message ("Image %s tried to point to image set %s, but does not retain a reference", check_mempool_owner_name (from), check_mempool_owner_name (to));
786 else if (from.image_set && to.image_set)
788 if (!check_image_set_may_reference_image_set (from.image_set, to.image_set))
789 check_mempool_assert_message ("Image set %s tried to point to image set %s, but does not retain a reference", check_mempool_owner_name (from), check_mempool_owner_name (to));
792 else if (from.image_set && to.image)
794 if (!check_image_set_may_reference_image (from.image_set, to.image))
795 check_mempool_assert_message ("Image set %s tried to point to image %s, but does not retain a reference", check_mempool_owner_name (from), check_mempool_owner_name (to));
798 else
800 check_mempool_assert_message ("Internal logic error: Unreachable code");
804 void
805 check_metadata_store (void *from, void *to)
807 check_mempool_may_reference_mempool (from, to, FALSE);
810 void
811 check_metadata_store_local (void *from, void *to)
813 check_mempool_may_reference_mempool (from, to, TRUE);
816 #endif /* defined(ENABLE_CHECKED_BUILD_METADATA) */
817 #else /* ENABLE_CHECKED_BUILD */
819 MONO_EMPTY_SOURCE_FILE (checked_build);
820 #endif /* ENABLE_CHECKED_BUILD */