[LoongArch64] Part-5:add loongarch support in some files for LoongArch64. (#21769)
[mono-project.git] / mono / sgen / sgen-debug.c
blobe2546e49a1109a39f26f49289cdbf86778437e31
1 /**
2 * \file
3 * Collector debugging
5 * Author:
6 * Paolo Molaro (lupus@ximian.com)
7 * Rodrigo Kumpera (kumpera@gmail.com)
9 * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
10 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
11 * Copyright 2011 Xamarin, Inc.
12 * Copyright (C) 2012 Xamarin Inc
14 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
17 #include "config.h"
18 #ifdef HAVE_SGEN_GC
20 #include <string.h>
22 #include "mono/sgen/sgen-gc.h"
23 #include "mono/sgen/sgen-cardtable.h"
24 #include "mono/sgen/sgen-protocol.h"
25 #include "mono/sgen/sgen-memory-governor.h"
26 #include "mono/sgen/sgen-pinning.h"
27 #include "mono/sgen/sgen-client.h"
29 #if _MSC_VER
30 #pragma warning(disable:4312) // FIXME pointer cast to different size
31 #endif
33 #ifndef DISABLE_SGEN_DEBUG_HELPERS
36 #define LOAD_VTABLE SGEN_LOAD_VTABLE
38 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
39 #define object_is_pinned SGEN_OBJECT_IS_PINNED
40 #define safe_object_get_size sgen_safe_object_get_size
42 void sgen_describe_ptr (char *ptr);
43 void sgen_check_object (GCObject *obj);
46 * ######################################################################
47 * ######## Collector debugging
48 * ######################################################################
51 static const char*descriptor_types [] = {
52 "INVALID",
53 "run length",
54 "bitmap",
55 "small pointer-free",
56 "complex",
57 "vector",
58 "complex arrray",
59 "complex pointer-free"
62 static char* describe_nursery_ptr (char *ptr, gboolean need_setup);
64 static void
65 describe_pointer (char *ptr, gboolean need_setup)
67 GCVTable vtable;
68 SgenDescriptor desc;
69 int type;
70 char *start;
71 char *forwarded;
72 mword size;
74 restart:
75 if (sgen_ptr_in_nursery (ptr)) {
76 start = describe_nursery_ptr (ptr, need_setup);
77 if (!start)
78 return;
79 ptr = start;
80 vtable = LOAD_VTABLE ((GCObject*)ptr);
81 } else {
82 if (sgen_ptr_is_in_los (ptr, &start)) {
83 if (ptr == start)
84 printf ("Pointer is the start of object %p in LOS space.\n", start);
85 else
86 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
87 ptr = start;
88 mono_sgen_los_describe_pointer (ptr);
89 vtable = LOAD_VTABLE ((GCObject*)ptr);
90 } else if (sgen_major_collector.ptr_is_in_non_pinned_space (ptr, &start)) {
91 if (ptr == start)
92 printf ("Pointer is the start of object %p in oldspace.\n", start);
93 else if (start)
94 printf ("Pointer is at offset 0x%x of object %p in oldspace.\n", (int)(ptr - start), start);
95 else
96 printf ("Pointer inside oldspace.\n");
97 if (start)
98 ptr = start;
99 vtable = (GCVTable)sgen_major_collector.describe_pointer (ptr);
100 } else if (sgen_major_collector.ptr_is_from_pinned_alloc (ptr)) {
101 // FIXME: Handle pointers to the inside of objects
102 printf ("Pointer is inside a pinned chunk.\n");
103 vtable = LOAD_VTABLE ((GCObject*)ptr);
104 } else {
105 printf ("Pointer unknown.\n");
106 return;
110 if (object_is_pinned (ptr))
111 printf ("Object is pinned.\n");
113 if ((forwarded = (char *)object_is_forwarded (ptr))) {
114 printf ("Object is forwarded to %p:\n", forwarded);
115 ptr = forwarded;
116 goto restart;
119 printf ("VTable: %p\n", vtable);
120 if (vtable == NULL) {
121 printf ("VTable is invalid (empty).\n");
122 goto invalid_vtable;
124 if (sgen_ptr_in_nursery (vtable)) {
125 printf ("VTable is invalid (points inside nursery).\n");
126 goto invalid_vtable;
128 printf ("Class: %s.%s\n", sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
130 desc = sgen_vtable_get_descriptor (vtable);
131 printf ("Descriptor: %lx\n", (long)desc);
133 type = desc & DESC_TYPE_MASK;
134 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
136 size = sgen_safe_object_get_size ((GCObject*)ptr);
137 printf ("Size: %d\n", (int)size);
139 invalid_vtable:
141 sgen_client_describe_invalid_pointer ((GCObject *) ptr);
144 void
145 sgen_describe_ptr (char *ptr)
147 describe_pointer (ptr, TRUE);
150 static gboolean missing_remsets;
153 * We let a missing remset slide if the target object is pinned,
154 * because the store might have happened but the remset not yet added,
155 * but in that case the target must be pinned. We might theoretically
156 * miss some missing remsets this way, but it's very unlikely.
158 #undef HANDLE_PTR
159 #define HANDLE_PTR(ptr,obj) do { \
160 if (*(ptr) && sgen_ptr_in_nursery ((char*)*(ptr))) { \
161 if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup (*(ptr))) { \
162 GCVTable __vt = SGEN_LOAD_VTABLE (obj); \
163 gboolean is_pinned = object_is_pinned (*(ptr)); \
164 SGEN_LOG (0, "Oldspace->newspace reference %p at offset %ld in object %p (%s.%s) not found in remsets%s.", *(ptr), (long)((char*)(ptr) - (char*)(obj)), (obj), sgen_client_vtable_get_namespace (__vt), sgen_client_vtable_get_name (__vt), is_pinned ? ", but object is pinned" : ""); \
165 sgen_binary_protocol_missing_remset ((obj), __vt, (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), is_pinned); \
166 if (!is_pinned) \
167 missing_remsets = TRUE; \
170 } while (0)
173 * Check that each object reference which points into the nursery can
174 * be found in the remembered sets.
176 static void
177 check_consistency_callback (GCObject *obj, size_t size, void *dummy)
179 char *start = (char*)obj;
180 GCVTable vt = LOAD_VTABLE (obj);
181 SgenDescriptor desc = sgen_vtable_get_descriptor (vt);
182 SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, sgen_client_vtable_get_name (vt));
184 #include "sgen-scan-object.h"
188 * Perform consistency check of the heap.
190 * Assumes the world is stopped.
192 void
193 sgen_check_remset_consistency (void)
195 // Need to add more checks
197 missing_remsets = FALSE;
199 SGEN_LOG (1, "Begin heap consistency check...");
201 // Check that oldspace->newspace pointers are registered with the collector
202 sgen_major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
204 sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
206 SGEN_LOG (1, "Heap consistency check done.");
208 if (missing_remsets)
209 sgen_binary_protocol_flush_buffers (TRUE);
210 if (!sgen_binary_protocol_is_enabled ())
211 g_assert (!missing_remsets);
214 static gboolean
215 is_major_or_los_object_marked (GCObject *obj)
217 if (sgen_safe_object_get_size ((GCObject*)obj) > SGEN_MAX_SMALL_OBJ_SIZE) {
218 return sgen_los_object_is_pinned (obj);
219 } else {
220 return sgen_get_major_collector ()->is_object_live (obj);
224 #undef HANDLE_PTR
225 #define HANDLE_PTR(ptr,obj) do { \
226 if (*(ptr) && !sgen_ptr_in_nursery ((char*)*(ptr)) && !is_major_or_los_object_marked ((GCObject*)*(ptr))) { \
227 if (!cards || !sgen_get_remset ()->find_address_with_cards (start, cards, (char*)(ptr))) { \
228 GCVTable __vt = SGEN_LOAD_VTABLE (obj); \
229 SGEN_LOG (0, "major->major reference %p at offset %ld in object %p (%s.%s) not found in remsets.", *(ptr), (long)((char*)(ptr) - (char*)(obj)), (obj), sgen_client_vtable_get_namespace (__vt), sgen_client_vtable_get_name (__vt)); \
230 sgen_binary_protocol_missing_remset ((obj), __vt, (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
231 missing_remsets = TRUE; \
234 } while (0)
236 static void
237 check_mod_union_callback (GCObject *obj, size_t size, void *dummy)
239 char *start = (char*)obj;
240 gboolean in_los = (gboolean) (size_t) dummy;
241 GCVTable vt = LOAD_VTABLE (obj);
242 SgenDescriptor desc = sgen_vtable_get_descriptor (vt);
243 guint8 *cards;
244 SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", obj, vt, sgen_client_vtable_get_name (vt));
246 if (!is_major_or_los_object_marked (obj))
247 return;
249 if (in_los)
250 cards = sgen_los_header_for_object (obj)->cardtable_mod_union;
251 else
252 cards = sgen_get_major_collector ()->get_cardtable_mod_union_for_reference (start);
254 #include "sgen-scan-object.h"
257 void
258 sgen_check_mod_union_consistency (void)
260 missing_remsets = FALSE;
262 sgen_major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_mod_union_callback, (void*)FALSE);
264 sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_mod_union_callback, (void*)TRUE);
266 if (!sgen_binary_protocol_is_enabled ())
267 g_assert (!missing_remsets);
270 #undef HANDLE_PTR
271 #define HANDLE_PTR(ptr,obj) do { \
272 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
273 g_error ("Could not load vtable for obj %p slot %ld (size %ld)", obj, (long)((char*)ptr - (char*)obj), (long)safe_object_get_size ((GCObject*)obj)); \
274 } while (0)
276 static void
277 check_major_refs_callback (GCObject *obj, size_t size, void *dummy)
279 char *start = (char*)obj;
280 SgenDescriptor desc = sgen_obj_get_descriptor (obj);
282 #include "sgen-scan-object.h"
285 void
286 sgen_check_major_refs (void)
288 sgen_major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
289 sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
292 /* Check that the reference is valid */
293 #undef HANDLE_PTR
294 #define HANDLE_PTR(ptr,obj) do { \
295 if (*(ptr)) { \
296 g_assert (sgen_client_vtable_get_namespace (SGEN_LOAD_VTABLE_UNCHECKED (*(ptr)))); \
298 } while (0)
301 * sgen_check_object:
303 * Perform consistency check on an object. Currently we only check that the
304 * reference fields are valid.
306 void
307 sgen_check_object (GCObject *obj)
309 char *start = (char*)obj;
310 SgenDescriptor desc;
312 if (!start)
313 return;
315 desc = sgen_obj_get_descriptor (obj);
317 #include "sgen-scan-object.h"
321 static GCObject **valid_nursery_objects;
322 static int valid_nursery_object_count;
323 static gboolean broken_heap;
325 static void
326 setup_mono_sgen_scan_area_with_callback (GCObject *object, size_t size, void *data)
328 valid_nursery_objects [valid_nursery_object_count++] = object;
331 static void
332 setup_valid_nursery_objects (void)
334 if (!valid_nursery_objects)
335 valid_nursery_objects = (GCObject **)sgen_alloc_os_memory (sgen_nursery_max_size, (SgenAllocFlags)(SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE), "debugging data", MONO_MEM_ACCOUNT_SGEN_DEBUGGING);
336 valid_nursery_object_count = 0;
337 sgen_scan_area_with_callback (sgen_nursery_section->data, sgen_nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE, FALSE);
340 static gboolean
341 find_object_in_nursery_dump (char *object)
343 int first = 0, last = valid_nursery_object_count;
344 while (first < last) {
345 int middle = first + ((last - first) >> 1);
346 if (object == (char*)valid_nursery_objects [middle])
347 return TRUE;
349 if (object < (char*)valid_nursery_objects [middle])
350 last = middle;
351 else
352 first = middle + 1;
354 g_assert (first == last);
355 return FALSE;
358 static void
359 iterate_valid_nursery_objects (IterateObjectCallbackFunc callback, void *data)
361 int i;
362 for (i = 0; i < valid_nursery_object_count; ++i) {
363 GCObject *obj = valid_nursery_objects [i];
364 callback (obj, safe_object_get_size (obj), data);
368 static char*
369 describe_nursery_ptr (char *ptr, gboolean need_setup)
371 int i;
373 if (need_setup)
374 setup_valid_nursery_objects ();
376 for (i = 0; i < valid_nursery_object_count - 1; ++i) {
377 if ((char*)valid_nursery_objects [i + 1] > ptr)
378 break;
381 if (i >= valid_nursery_object_count || (char*)valid_nursery_objects [i] + safe_object_get_size (valid_nursery_objects [i]) < ptr) {
382 SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)");
383 return NULL;
384 } else {
385 GCObject *obj = valid_nursery_objects [i];
386 if ((char*)obj == ptr)
387 SGEN_LOG (0, "nursery-ptr %p", obj);
388 else
389 SGEN_LOG (0, "nursery-ptr %p (interior-ptr offset %ld)", obj, (long)(ptr - (char*)obj));
390 return (char*)obj;
394 static gboolean
395 is_valid_object_pointer (char *object)
397 if (sgen_ptr_in_nursery (object))
398 return find_object_in_nursery_dump (object);
400 if (sgen_los_is_valid_object (object))
401 return TRUE;
403 if (sgen_major_collector.is_valid_object (object))
404 return TRUE;
405 return FALSE;
408 static void
409 bad_pointer_spew (char *obj, char **slot)
411 char *ptr = *slot;
412 GCVTable vtable = LOAD_VTABLE ((GCObject*)obj);
414 SGEN_LOG (0, "Invalid object pointer %p at offset %ld in object %p (%s.%s):", ptr,
415 (long)((char*)slot - obj),
416 obj, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
417 describe_pointer (ptr, FALSE);
418 broken_heap = TRUE;
421 static void
422 missing_remset_spew (char *obj, char **slot)
424 char *ptr = *slot;
425 GCVTable vtable = LOAD_VTABLE ((GCObject*)obj);
427 SGEN_LOG (0, "Oldspace->newspace reference %p at offset %ld in object %p (%s.%s) not found in remsets.",
428 ptr, (long)((char*)slot - obj), obj,
429 sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
431 broken_heap = TRUE;
435 FIXME Flag missing remsets due to pinning as non fatal
437 #undef HANDLE_PTR
438 #define HANDLE_PTR(ptr,obj) do { \
439 if (*(char**)ptr) { \
440 if (!is_valid_object_pointer (*(char**)ptr)) { \
441 bad_pointer_spew ((char*)obj, (char**)ptr); \
442 } else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) { \
443 if (!allow_missing_pinned && !SGEN_OBJECT_IS_PINNED (*(ptr)) && !sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup (*(ptr))) \
444 missing_remset_spew ((char*)obj, (char**)ptr); \
447 } while (0)
449 static void
450 verify_object_pointers_callback (GCObject *obj, size_t size, void *data)
452 char *start = (char*)obj;
453 gboolean allow_missing_pinned = (gboolean) (size_t) data;
454 SgenDescriptor desc = sgen_obj_get_descriptor_safe (obj);
456 #include "sgen-scan-object.h"
460 FIXME:
461 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
462 depend on OP_DUMMY_USE.
464 void
465 sgen_check_whole_heap (gboolean allow_missing_pinned)
467 setup_valid_nursery_objects ();
469 broken_heap = FALSE;
470 sgen_scan_area_with_callback (sgen_nursery_section->data, sgen_nursery_section->end_data, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned, FALSE, TRUE);
471 sgen_major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
472 sgen_los_iterate_objects (verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
474 g_assert (!broken_heap);
477 static gboolean
478 ptr_in_heap (char *object)
480 if (sgen_ptr_in_nursery (object))
481 return TRUE;
483 if (sgen_los_is_valid_object (object))
484 return TRUE;
486 if (sgen_major_collector.is_valid_object (object))
487 return TRUE;
488 return FALSE;
492 * sgen_check_objref:
493 * Do consistency checks on the object reference OBJ. Assert on failure.
495 void
496 sgen_check_objref (char *obj)
498 g_assert (ptr_in_heap (obj));
501 static void
502 find_pinning_ref_from_thread (char *obj, size_t size)
504 #ifndef SGEN_WITHOUT_MONO
505 char *endobj = obj + size;
507 FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) {
508 mword *ctxstart, *ctxcurrent, *ctxend;
509 char **start = (char**)info->client_info.stack_start;
510 if (info->client_info.skip)
511 continue;
512 while (start < (char**)info->client_info.info.stack_end) {
513 if (*start >= obj && *start < endobj)
514 SGEN_LOG (0, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p", obj, info, (gpointer)mono_thread_info_get_tid (info), start, info->client_info.stack_start, info->client_info.info.stack_end);
515 start++;
518 for (ctxstart = ctxcurrent = (mword*) &info->client_info.ctx, ctxend = (mword*) (&info->client_info.ctx + 1); ctxcurrent < ctxend; ctxcurrent ++) {
519 mword w = *ctxcurrent;
521 if (w >= (mword)obj && w < (mword)obj + size)
522 SGEN_LOG (0, "Object %p referenced in saved reg %d of thread %p (id %p)", obj, (int) (ctxcurrent - ctxstart), info, (gpointer)(gsize)mono_thread_info_get_tid (info));
524 } FOREACH_THREAD_END
525 #endif
529 * Debugging function: find in the conservative roots where @obj is being pinned.
531 static G_GNUC_UNUSED void
532 find_pinning_reference (char *obj, size_t size)
534 char **start;
535 RootRecord *root;
536 char *endobj = obj + size;
538 SGEN_HASH_TABLE_FOREACH (&sgen_roots_hash [ROOT_TYPE_NORMAL], char **, start, RootRecord *, root) {
539 /* if desc is non-null it has precise info */
540 if (!root->root_desc) {
541 while (start < (char**)root->end_root) {
542 if (*start >= obj && *start < endobj) {
543 SGEN_LOG (0, "Object %p referenced in pinned roots %p-%p\n", obj, start, root->end_root);
545 start++;
548 } SGEN_HASH_TABLE_FOREACH_END;
550 find_pinning_ref_from_thread (obj, size);
553 #undef HANDLE_PTR
554 #define HANDLE_PTR(ptr,obj) do { \
555 char* __target = *(char**)ptr; \
556 if (__target) { \
557 if (sgen_ptr_in_nursery (__target)) { \
558 g_assert (!SGEN_OBJECT_IS_FORWARDED (__target)); \
559 } else { \
560 mword __size = sgen_safe_object_get_size ((GCObject*)__target); \
561 if (__size <= SGEN_MAX_SMALL_OBJ_SIZE) \
562 g_assert (sgen_major_collector.is_object_live ((GCObject*)__target)); \
563 else \
564 g_assert (sgen_los_object_is_pinned ((GCObject*)__target)); \
567 } while (0)
569 static void
570 check_marked_callback (GCObject *obj, size_t size, void *dummy)
572 char *start = (char*)obj;
573 gboolean flag = (gboolean) (size_t) dummy;
574 SgenDescriptor desc;
576 if (sgen_ptr_in_nursery (start)) {
577 if (flag)
578 SGEN_ASSERT (0, SGEN_OBJECT_IS_PINNED (obj), "All objects remaining in the nursery must be pinned");
579 } else if (flag) {
580 if (!sgen_los_object_is_pinned (obj))
581 return;
582 } else {
583 if (!sgen_major_collector.is_object_live (obj))
584 return;
587 desc = sgen_obj_get_descriptor_safe (obj);
589 #include "sgen-scan-object.h"
592 void
593 sgen_check_heap_marked (gboolean nursery_must_be_pinned)
595 setup_valid_nursery_objects ();
597 iterate_valid_nursery_objects (check_marked_callback, (void*)(size_t)nursery_must_be_pinned);
598 sgen_major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, check_marked_callback, (void*)FALSE);
599 sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
602 static void
603 check_nursery_objects_untag_callback (char *obj, size_t size, void *data)
605 g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
606 g_assert (!SGEN_OBJECT_IS_PINNED (obj));
609 void
610 sgen_check_nursery_objects_untag (void)
612 sgen_clear_nursery_fragments ();
613 sgen_scan_area_with_callback (sgen_nursery_section->data, sgen_nursery_section->end_data,
614 (IterateObjectCallbackFunc)check_nursery_objects_untag_callback, NULL, FALSE, TRUE);
617 static void
618 verify_scan_starts (char *start, char *end)
620 size_t i;
622 for (i = 0; i < sgen_nursery_section->num_scan_start; ++i) {
623 char *addr = sgen_nursery_section->scan_starts [i];
624 if (addr > start && addr < end)
625 SGEN_LOG (0, "NFC-BAD SCAN START [%lu] %p for obj [%p %p]", (unsigned long)i, addr, start, end);
629 void
630 sgen_debug_verify_nursery (gboolean do_dump_nursery_content)
632 char *start, *end, *cur, *hole_start;
634 if (sgen_nursery_canaries_enabled ())
635 SGEN_LOG (0, "Checking nursery canaries...");
637 /*This cleans up unused fragments */
638 sgen_nursery_allocator_prepare_for_pinning ();
640 hole_start = start = cur = sgen_get_nursery_start ();
641 end = sgen_get_nursery_end ();
643 while (cur < end) {
644 size_t ss, size;
645 gboolean is_array_fill;
647 if (!*(void**)cur) {
648 cur += sizeof (void*);
649 continue;
652 if (object_is_forwarded (cur))
653 SGEN_LOG (0, "FORWARDED OBJ %p", cur);
654 else if (object_is_pinned (cur))
655 SGEN_LOG (0, "PINNED OBJ %p", cur);
657 ss = safe_object_get_size ((GCObject*)cur);
658 size = SGEN_ALIGN_UP (ss);
659 verify_scan_starts (cur, cur + size);
660 is_array_fill = sgen_client_object_is_array_fill ((GCObject*)cur);
661 if (do_dump_nursery_content) {
662 GCVTable vtable = SGEN_LOAD_VTABLE ((GCObject*)cur);
663 if (cur > hole_start)
664 SGEN_LOG (0, "HOLE [%p %p %d]", hole_start, cur, (int)(cur - hole_start));
665 SGEN_LOG (0, "OBJ [%p %p %d %d %s.%s %d]", cur, cur + size, (int)size, (int)ss,
666 sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable),
667 is_array_fill);
669 if (sgen_nursery_canaries_enabled () && !is_array_fill) {
670 CHECK_CANARY_FOR_OBJECT ((GCObject*)cur, TRUE);
671 CANARIFY_SIZE (size);
673 cur += size;
674 hole_start = cur;
679 * Checks that no objects in the nursery are fowarded or pinned. This
680 * is a precondition to restarting the mutator while doing a
681 * concurrent collection. Note that we don't clear fragments because
682 * we depend on that having happened earlier.
684 void
685 sgen_debug_check_nursery_is_clean (void)
687 char *end, *cur;
689 cur = sgen_get_nursery_start ();
690 end = sgen_get_nursery_end ();
692 while (cur < end) {
693 size_t size;
695 if (!*(void**)cur) {
696 cur += sizeof (void*);
697 continue;
700 g_assert (!object_is_forwarded (cur));
701 g_assert (!object_is_pinned (cur));
703 size = SGEN_ALIGN_UP (safe_object_get_size ((GCObject*)cur));
704 verify_scan_starts (cur, cur + size);
706 cur += size;
710 static gboolean scan_object_for_specific_ref_precise = TRUE;
712 #undef HANDLE_PTR
713 #define HANDLE_PTR(ptr,obj) do { \
714 if ((GCObject*)*(ptr) == key) { \
715 GCVTable vtable = SGEN_LOAD_VTABLE (*(ptr)); \
716 g_print ("found ref to %p in object %p (%s.%s) at offset %ld\n", \
717 key, (obj), sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), (long)(((char*)(ptr) - (char*)(obj)))); \
719 } while (0)
721 static void
722 scan_object_for_specific_ref (GCObject *obj, GCObject *key)
724 GCObject *forwarded;
726 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (obj)))
727 obj = forwarded;
729 if (scan_object_for_specific_ref_precise) {
730 char *start = (char*)obj;
731 SgenDescriptor desc = sgen_obj_get_descriptor_safe (obj);
732 #include "sgen-scan-object.h"
733 } else {
734 mword *words = (mword*)obj;
735 size_t size = safe_object_get_size (obj);
736 int i;
737 for (i = 0; i < size / sizeof (mword); ++i) {
738 if (words [i] == (mword)key) {
739 GCVTable vtable = SGEN_LOAD_VTABLE (obj);
740 g_print ("found possible ref to %p in object %p (%s.%s) at offset %ld\n",
741 key, obj, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), (long)(i * sizeof (mword)));
747 static void
748 scan_object_for_specific_ref_callback (GCObject *obj, size_t size, GCObject *key)
750 scan_object_for_specific_ref (obj, key);
753 static void
754 check_root_obj_specific_ref (RootRecord *root, GCObject *key, GCObject *obj)
756 if (key != obj)
757 return;
758 g_print ("found ref to %p in root record %p\n", key, root);
761 static GCObject *check_key = NULL;
762 static RootRecord *check_root = NULL;
764 static void
765 check_root_obj_specific_ref_from_marker (GCObject **obj, void *gc_data)
767 check_root_obj_specific_ref (check_root, check_key, *obj);
770 static void
771 scan_roots_for_specific_ref (GCObject *key, int root_type)
773 void **start_root;
774 RootRecord *root;
775 check_key = key;
777 SGEN_HASH_TABLE_FOREACH (&sgen_roots_hash [root_type], void **, start_root, RootRecord *, root) {
778 SgenDescriptor desc = root->root_desc;
780 check_root = root;
782 switch (desc & ROOT_DESC_TYPE_MASK) {
783 case ROOT_DESC_BITMAP:
784 desc >>= ROOT_DESC_TYPE_SHIFT;
785 while (desc) {
786 if (desc & 1)
787 check_root_obj_specific_ref (root, key, (GCObject *)*start_root);
788 desc >>= 1;
789 start_root++;
791 return;
792 case ROOT_DESC_COMPLEX: {
793 gsize *bitmap_data = (gsize *)sgen_get_complex_descriptor_bitmap (desc);
794 int bwords = (int) ((*bitmap_data) - 1);
795 void **start_run = start_root;
796 bitmap_data++;
797 while (bwords-- > 0) {
798 gsize bmap = *bitmap_data++;
799 void **objptr = start_run;
800 while (bmap) {
801 if (bmap & 1)
802 check_root_obj_specific_ref (root, key, (GCObject *)*objptr);
803 bmap >>= 1;
804 ++objptr;
806 start_run += GC_BITS_PER_WORD;
808 break;
810 case ROOT_DESC_VECTOR: {
811 void **p;
813 for (p = start_root; p < (void**)root->end_root; p++) {
814 if (*p)
815 check_root_obj_specific_ref (root, key, (GCObject *)*p);
817 break;
819 case ROOT_DESC_USER: {
820 SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
821 marker (start_root, check_root_obj_specific_ref_from_marker, NULL);
822 break;
824 case ROOT_DESC_RUN_LEN:
825 g_assert_not_reached ();
826 default:
827 g_assert_not_reached ();
829 } SGEN_HASH_TABLE_FOREACH_END;
831 check_key = NULL;
832 check_root = NULL;
835 void
836 mono_gc_scan_for_specific_ref (GCObject *key, gboolean precise)
838 void **ptr;
839 RootRecord *root;
841 scan_object_for_specific_ref_precise = precise;
843 sgen_scan_area_with_callback (sgen_nursery_section->data, sgen_nursery_section->end_data,
844 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE, FALSE);
846 sgen_major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
848 sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
850 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
851 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
853 SGEN_HASH_TABLE_FOREACH (&sgen_roots_hash [ROOT_TYPE_PINNED], void **, ptr, RootRecord *, root) {
854 while (ptr < (void**)root->end_root) {
855 check_root_obj_specific_ref (root, (GCObject *)*ptr, key);
856 ++ptr;
858 } SGEN_HASH_TABLE_FOREACH_END;
860 if (sgen_is_world_stopped ())
861 find_pinning_ref_from_thread ((char*)key, sizeof (GCObject));
864 #ifndef SGEN_WITHOUT_MONO
866 static MonoDomain *check_domain = NULL;
868 static void
869 check_obj_not_in_domain (MonoObject **o)
871 g_assert (((*o))->vtable->domain != check_domain);
875 static void
876 check_obj_not_in_domain_callback (GCObject **o, void *gc_data)
878 g_assert ((*o)->vtable->domain != check_domain);
881 void
882 sgen_scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
884 void **start_root;
885 RootRecord *root;
886 check_domain = domain;
887 SGEN_HASH_TABLE_FOREACH (&sgen_roots_hash [root_type], void **, start_root, RootRecord *, root) {
888 SgenDescriptor desc = root->root_desc;
890 /* The MonoDomain struct is allowed to hold
891 references to objects in its own domain. */
892 if (start_root == (void**)domain)
893 continue;
895 switch (desc & ROOT_DESC_TYPE_MASK) {
896 case ROOT_DESC_BITMAP:
897 desc >>= ROOT_DESC_TYPE_SHIFT;
898 while (desc) {
899 if ((desc & 1) && *start_root)
900 check_obj_not_in_domain ((MonoObject **)*start_root);
901 desc >>= 1;
902 start_root++;
904 break;
905 case ROOT_DESC_COMPLEX: {
906 gsize *bitmap_data = (gsize *)sgen_get_complex_descriptor_bitmap (desc);
907 int bwords = (int)((*bitmap_data) - 1);
908 void **start_run = start_root;
909 bitmap_data++;
910 while (bwords-- > 0) {
911 gsize bmap = *bitmap_data++;
912 void **objptr = start_run;
913 while (bmap) {
914 if ((bmap & 1) && *objptr)
915 check_obj_not_in_domain ((MonoObject **)*objptr);
916 bmap >>= 1;
917 ++objptr;
919 start_run += GC_BITS_PER_WORD;
921 break;
923 case ROOT_DESC_VECTOR: {
924 void **p;
926 for (p = start_root; p < (void**)root->end_root; p++) {
927 if (*p)
928 check_obj_not_in_domain ((MonoObject **)*p);
930 break;
932 case ROOT_DESC_USER: {
933 SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
934 marker (start_root, check_obj_not_in_domain_callback, NULL);
935 break;
937 case ROOT_DESC_RUN_LEN:
938 g_assert_not_reached ();
939 default:
940 g_assert_not_reached ();
942 } SGEN_HASH_TABLE_FOREACH_END;
944 check_domain = NULL;
947 static gboolean
948 is_xdomain_ref_allowed (GCObject **ptr, GCObject *obj, MonoDomain *domain)
950 MonoObject *o = (MonoObject*)(obj);
951 size_t offset = (char*)(ptr) - (char*)o;
953 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
954 return TRUE;
955 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
956 return TRUE;
958 #ifndef DISABLE_REMOTING
959 if (m_class_get_supertypes (mono_defaults.real_proxy_class) && mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) &&
960 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
961 return TRUE;
962 #endif
963 return FALSE;
966 static void
967 check_reference_for_xdomain (GCObject **ptr, GCObject *obj, MonoDomain *domain)
969 MonoObject *ref = *ptr;
970 size_t offset = (char*)(ptr) - (char*)obj;
971 MonoClass *klass;
972 MonoClassField *field;
973 char *str;
975 if (!ref || ref->vtable->domain == domain)
976 return;
977 if (is_xdomain_ref_allowed (ptr, obj, domain))
978 return;
980 field = NULL;
981 for (klass = obj->vtable->klass; klass; klass = m_class_get_parent (klass)) {
982 int i;
984 int fcount = mono_class_get_field_count (klass);
985 MonoClassField *klass_fields = m_class_get_fields (klass);
986 for (i = 0; i < fcount; ++i) {
987 if (klass_fields[i].offset == offset) {
988 field = &klass_fields[i];
989 break;
992 if (field)
993 break;
996 if (ref->vtable->klass == mono_defaults.string_class) {
997 ERROR_DECL (error);
998 str = mono_string_to_utf8_checked_internal ((MonoString*)ref, error);
999 mono_error_cleanup (error);
1000 } else
1001 str = NULL;
1002 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1003 obj, m_class_get_name_space (obj->vtable->klass), m_class_get_name (obj->vtable->klass),
1004 offset, field ? field->name : "",
1005 ref, m_class_get_name_space (ref->vtable->klass), m_class_get_name (ref->vtable->klass), str ? str : "");
1006 mono_gc_scan_for_specific_ref (obj, TRUE);
1007 if (str)
1008 g_free (str);
1011 #undef HANDLE_PTR
1012 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1014 static void
1015 scan_object_for_xdomain_refs (GCObject *obj, mword size, void *data)
1017 char *start = (char*)obj;
1018 MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
1019 MonoDomain *domain = vt->domain;
1020 SgenDescriptor desc = sgen_vtable_get_descriptor (vt);
1022 #include "sgen-scan-object.h"
1025 void
1026 sgen_check_for_xdomain_refs (void)
1028 sgen_scan_area_with_callback (sgen_nursery_section->data, sgen_nursery_section->end_data,
1029 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE, TRUE);
1031 sgen_major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1033 sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1036 #endif
1038 /* If not null, dump the heap after each collection into this file */
1039 static FILE *heap_dump_file = NULL;
1041 void
1042 sgen_dump_occupied (char *start, char *end, char *section_start)
1044 fprintf (heap_dump_file, "<occupied offset=\"%ld\" size=\"%ld\"/>\n", (long)(start - section_start), (long)(end - start));
1047 void
1048 sgen_dump_section (GCMemSection *section, const char *type)
1050 char *start = section->data;
1051 char *end = section->end_data;
1052 char *occ_start = NULL;
1054 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)(section->end_data - section->data));
1056 while (start < end) {
1057 guint size;
1058 //GCVTable vt;
1059 //MonoClass *class;
1061 if (!*(void**)start) {
1062 if (occ_start) {
1063 sgen_dump_occupied (occ_start, start, section->data);
1064 occ_start = NULL;
1066 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1067 continue;
1070 if (!occ_start)
1071 occ_start = start;
1073 //vt = SGEN_LOAD_VTABLE (start);
1074 //class = vt->klass;
1076 size = SGEN_ALIGN_UP (safe_object_get_size ((GCObject*) start));
1079 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
1080 start - section->data,
1081 vt->klass->name_space, vt->klass->name,
1082 size);
1085 start += size;
1087 if (occ_start)
1088 sgen_dump_occupied (occ_start, start, section->data);
1090 fprintf (heap_dump_file, "</section>\n");
1093 static void
1094 dump_object (GCObject *obj, gboolean dump_location)
1096 #ifndef SGEN_WITHOUT_MONO
1097 static char class_name [1024];
1099 MonoClass *klass = mono_object_class (obj);
1100 int i, j;
1103 * Python's XML parser is too stupid to parse angle brackets
1104 * in strings, so we just ignore them;
1106 i = j = 0;
1107 while (m_class_get_name (klass) [i] && j < sizeof (class_name) - 1) {
1108 if (!strchr ("<>\"", m_class_get_name (klass) [i]))
1109 class_name [j++] = m_class_get_name (klass) [i];
1110 ++i;
1112 g_assert (j < sizeof (class_name));
1113 class_name [j] = 0;
1115 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%ld\"",
1116 m_class_get_name_space (klass), class_name,
1117 (long)safe_object_get_size (obj));
1118 if (dump_location) {
1119 const char *location;
1120 if (sgen_ptr_in_nursery (obj))
1121 location = "nursery";
1122 else if (safe_object_get_size (obj) <= SGEN_MAX_SMALL_OBJ_SIZE)
1123 location = "major";
1124 else
1125 location = "LOS";
1126 fprintf (heap_dump_file, " location=\"%s\"", location);
1128 fprintf (heap_dump_file, "/>\n");
1129 #endif
1132 static void
1133 dump_object_callback (GCObject *obj, size_t size, gboolean dump_location)
1135 dump_object (obj, dump_location);
1138 void
1139 sgen_debug_enable_heap_dump (const char *filename)
1141 heap_dump_file = fopen (filename, "w");
1142 if (heap_dump_file) {
1143 fprintf (heap_dump_file, "<sgen-dump>\n");
1144 sgen_pin_stats_enable ();
1148 void
1149 sgen_debug_dump_heap (const char *type, int num, const char *reason)
1151 SgenPointerQueue *pinned_objects;
1152 int i;
1154 if (!heap_dump_file)
1155 return;
1157 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
1158 if (reason)
1159 fprintf (heap_dump_file, " reason=\"%s\"", reason);
1160 fprintf (heap_dump_file, ">\n");
1161 #ifndef SGEN_WITHOUT_MONO
1162 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
1163 #endif
1164 sgen_dump_internal_mem_usage (heap_dump_file);
1165 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%lu\"/>\n", (unsigned long)sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_STACK));
1166 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
1167 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%lu\"/>\n", (unsigned long)sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_OTHER));
1169 fprintf (heap_dump_file, "<pinned-objects>\n");
1170 pinned_objects = sgen_pin_stats_get_object_list ();
1171 for (i = 0; i < pinned_objects->next_slot; ++i)
1172 dump_object ((GCObject *)pinned_objects->data [i], TRUE);
1173 fprintf (heap_dump_file, "</pinned-objects>\n");
1175 sgen_dump_section (sgen_nursery_section, "nursery");
1177 sgen_major_collector.dump_heap (heap_dump_file);
1179 fprintf (heap_dump_file, "<los>\n");
1180 sgen_los_iterate_objects ((IterateObjectCallbackFunc)dump_object_callback, (void*)FALSE);
1181 fprintf (heap_dump_file, "</los>\n");
1183 fprintf (heap_dump_file, "</collection>\n");
1186 static GCObject *found_obj;
1188 static void
1189 find_object_for_ptr_callback (GCObject *obj, size_t size, void *user_data)
1191 char *ptr = (char *)user_data;
1193 if (ptr >= (char*)obj && ptr < (char*)obj + size) {
1194 g_assert (!found_obj);
1195 found_obj = obj;
1199 /* for use in the debugger */
1200 GCObject*
1201 sgen_find_object_for_ptr (char *ptr)
1203 if (ptr >= sgen_nursery_section->data && ptr < sgen_nursery_section->end_data) {
1204 found_obj = NULL;
1205 sgen_scan_area_with_callback (sgen_nursery_section->data, sgen_nursery_section->end_data,
1206 find_object_for_ptr_callback, ptr, TRUE, FALSE);
1207 if (found_obj)
1208 return found_obj;
1211 found_obj = NULL;
1212 sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
1213 if (found_obj)
1214 return found_obj;
1217 * Very inefficient, but this is debugging code, supposed to
1218 * be called from gdb, so we don't care.
1220 found_obj = NULL;
1221 sgen_major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, find_object_for_ptr_callback, ptr);
1222 return found_obj;
1225 #else
1227 void
1228 sgen_check_for_xdomain_refs (void)
1232 void
1233 sgen_check_heap_marked (gboolean nursery_must_be_pinned)
1237 void
1238 sgen_check_major_refs (void)
1242 void
1243 sgen_check_mod_union_consistency (void)
1247 void
1248 sgen_check_nursery_objects_untag (void)
1252 void
1253 sgen_check_remset_consistency (void)
1257 void
1258 sgen_check_whole_heap (gboolean allow_missing_pinned)
1262 void
1263 sgen_debug_check_nursery_is_clean (void)
1267 void
1268 sgen_debug_dump_heap (const char *type, int num, const char *reason)
1272 void
1273 sgen_debug_enable_heap_dump (const char *filename)
1277 void
1278 sgen_debug_verify_nursery (gboolean do_dump_nursery_content)
1282 void
1283 sgen_dump_occupied (char *start, char *end, char *section_start)
1287 void
1288 sgen_scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1292 #endif /*DISABLE_SGEN_DEBUG_HELPERS */
1293 #endif /*HAVE_SGEN_GC*/