2 * sgen-debug.c: Collector debugging
5 * Paolo Molaro (lupus@ximian.com)
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10 * Copyright 2011 Xamarin, Inc.
11 * Copyright (C) 2012 Xamarin Inc
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License 2.0 as published by the Free Software Foundation;
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License 2.0 along with this library; if not, write to the Free
24 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32 #include "mono/metadata/sgen-gc.h"
33 #include "mono/metadata/sgen-cardtable.h"
34 #include "mono/metadata/sgen-protocol.h"
35 #include "mono/metadata/sgen-memory-governor.h"
36 #include "mono/metadata/sgen-pinning.h"
37 #include "mono/metadata/sgen-client.h"
38 #ifndef SGEN_WITHOUT_MONO
39 #include "mono/metadata/sgen-bridge-internal.h"
42 #define LOAD_VTABLE SGEN_LOAD_VTABLE
44 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
45 #define object_is_pinned SGEN_OBJECT_IS_PINNED
46 #define safe_object_get_size sgen_safe_object_get_size
48 void describe_ptr (char *ptr);
49 void check_object (char *start);
52 * ######################################################################
53 * ######## Collector debugging
54 * ######################################################################
57 static const char*descriptor_types [] = {
65 "complex pointer-free"
68 static char* describe_nursery_ptr (char *ptr, gboolean need_setup);
71 describe_pointer (char *ptr, gboolean need_setup)
81 if (sgen_ptr_in_nursery (ptr)) {
82 start = describe_nursery_ptr (ptr, need_setup);
86 vtable = (GCVTable*)LOAD_VTABLE (ptr);
88 if (sgen_ptr_is_in_los (ptr, &start)) {
90 printf ("Pointer is the start of object %p in LOS space.\n", start);
92 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
94 mono_sgen_los_describe_pointer (ptr);
95 vtable = (GCVTable*)LOAD_VTABLE (ptr);
96 } else if (major_collector.ptr_is_in_non_pinned_space (ptr, &start)) {
98 printf ("Pointer is the start of object %p in oldspace.\n", start);
100 printf ("Pointer is at offset 0x%x of object %p in oldspace.\n", (int)(ptr - start), start);
102 printf ("Pointer inside oldspace.\n");
105 vtable = (GCVTable*)major_collector.describe_pointer (ptr);
106 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
107 // FIXME: Handle pointers to the inside of objects
108 printf ("Pointer is inside a pinned chunk.\n");
109 vtable = (GCVTable*)LOAD_VTABLE (ptr);
111 printf ("Pointer unknown.\n");
116 if (object_is_pinned (ptr))
117 printf ("Object is pinned.\n");
119 if ((forwarded = object_is_forwarded (ptr))) {
120 printf ("Object is forwarded to %p:\n", forwarded);
125 printf ("VTable: %p\n", vtable);
126 if (vtable == NULL) {
127 printf ("VTable is invalid (empty).\n");
130 if (sgen_ptr_in_nursery (vtable)) {
131 printf ("VTable is invalid (points inside nursery).\n");
134 printf ("Class: %s.%s\n", sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
136 desc = sgen_vtable_get_descriptor ((GCVTable*)vtable);
137 printf ("Descriptor: %lx\n", (long)desc);
139 type = desc & DESC_TYPE_MASK;
140 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
142 size = sgen_safe_object_get_size ((GCObject*)ptr);
143 printf ("Size: %d\n", (int)size);
147 #ifndef SGEN_WITHOUT_MONO
148 sgen_bridge_describe_pointer ((GCObject*)ptr);
153 describe_ptr (char *ptr)
155 describe_pointer (ptr, TRUE);
158 static gboolean missing_remsets;
161 * We let a missing remset slide if the target object is pinned,
162 * because the store might have happened but the remset not yet added,
163 * but in that case the target must be pinned. We might theoretically
164 * miss some missing remsets this way, but it's very unlikely.
167 #define HANDLE_PTR(ptr,obj) do { \
168 if (*(ptr) && sgen_ptr_in_nursery ((char*)*(ptr))) { \
169 if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup (*(ptr))) { \
170 GCVTable *__vt = SGEN_LOAD_VTABLE ((obj)); \
171 SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), sgen_client_vtable_get_namespace (__vt), sgen_client_vtable_get_name (__vt)); \
172 binary_protocol_missing_remset ((obj), __vt, (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
173 if (!object_is_pinned (*(ptr))) \
174 missing_remsets = TRUE; \
180 * Check that each object reference which points into the nursery can
181 * be found in the remembered sets.
184 check_consistency_callback (char *start, size_t size, void *dummy)
186 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
187 mword desc = sgen_vtable_get_descriptor ((GCVTable*)vt);
188 SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, sgen_client_vtable_get_name (vt));
190 #include "sgen-scan-object.h"
194 * Perform consistency check of the heap.
196 * Assumes the world is stopped.
199 sgen_check_consistency (void)
201 // Need to add more checks
203 missing_remsets = FALSE;
205 SGEN_LOG (1, "Begin heap consistency check...");
207 // Check that oldspace->newspace pointers are registered with the collector
208 major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
210 sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
212 SGEN_LOG (1, "Heap consistency check done.");
214 if (!binary_protocol_is_enabled ())
215 g_assert (!missing_remsets);
219 is_major_or_los_object_marked (char *obj)
221 if (sgen_safe_object_get_size ((GCObject*)obj) > SGEN_MAX_SMALL_OBJ_SIZE) {
222 return sgen_los_object_is_pinned (obj);
224 return sgen_get_major_collector ()->is_object_live (obj);
229 #define HANDLE_PTR(ptr,obj) do { \
230 if (*(ptr) && !sgen_ptr_in_nursery ((char*)*(ptr)) && !is_major_or_los_object_marked ((char*)*(ptr))) { \
231 if (!sgen_get_remset ()->find_address_with_cards (start, cards, (char*)(ptr))) { \
232 GCVTable *__vt = SGEN_LOAD_VTABLE ((obj)); \
233 SGEN_LOG (0, "major->major reference %p at offset %td in object %p (%s.%s) not found in remsets.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), sgen_client_vtable_get_namespace (__vt), sgen_client_vtable_get_name (__vt)); \
234 binary_protocol_missing_remset ((obj), __vt, (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
235 missing_remsets = TRUE; \
241 check_mod_union_callback (char *start, size_t size, void *dummy)
243 gboolean in_los = (gboolean) (size_t) dummy;
244 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
245 mword desc = sgen_vtable_get_descriptor ((GCVTable*)vt);
247 SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, sgen_client_vtable_get_name (vt));
249 if (!is_major_or_los_object_marked (start))
253 cards = sgen_los_header_for_object (start)->cardtable_mod_union;
255 cards = sgen_get_major_collector ()->get_cardtable_mod_union_for_object (start);
257 SGEN_ASSERT (0, cards, "we must have mod union for marked major objects");
259 #include "sgen-scan-object.h"
263 sgen_check_mod_union_consistency (void)
265 missing_remsets = FALSE;
267 major_collector.iterate_objects (ITERATE_OBJECTS_ALL, (IterateObjectCallbackFunc)check_mod_union_callback, (void*)FALSE);
269 sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_mod_union_callback, (void*)TRUE);
271 if (!binary_protocol_is_enabled ())
272 g_assert (!missing_remsets);
276 #define HANDLE_PTR(ptr,obj) do { \
277 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
278 g_error ("Could not load vtable for obj %p slot %zd (size %zd)", obj, (char*)ptr - (char*)obj, (size_t)safe_object_get_size ((GCObject*)obj)); \
282 check_major_refs_callback (char *start, size_t size, void *dummy)
284 mword desc = sgen_obj_get_descriptor (start);
286 #include "sgen-scan-object.h"
290 sgen_check_major_refs (void)
292 major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
293 sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
296 /* Check that the reference is valid */
298 #define HANDLE_PTR(ptr,obj) do { \
300 g_assert (sgen_client_vtable_get_namespace (SGEN_LOAD_VTABLE_UNCHECKED (*(ptr)))); \
307 * Perform consistency check on an object. Currently we only check that the
308 * reference fields are valid.
311 check_object (char *start)
318 desc = sgen_obj_get_descriptor (start);
320 #include "sgen-scan-object.h"
324 static char **valid_nursery_objects;
325 static int valid_nursery_object_count;
326 static gboolean broken_heap;
329 setup_mono_sgen_scan_area_with_callback (char *object, size_t size, void *data)
331 valid_nursery_objects [valid_nursery_object_count++] = object;
335 setup_valid_nursery_objects (void)
337 if (!valid_nursery_objects)
338 valid_nursery_objects = sgen_alloc_os_memory (DEFAULT_NURSERY_SIZE, SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging data");
339 valid_nursery_object_count = 0;
340 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE);
344 find_object_in_nursery_dump (char *object)
346 int first = 0, last = valid_nursery_object_count;
347 while (first < last) {
348 int middle = first + ((last - first) >> 1);
349 if (object == valid_nursery_objects [middle])
352 if (object < valid_nursery_objects [middle])
357 g_assert (first == last);
362 iterate_valid_nursery_objects (IterateObjectCallbackFunc callback, void *data)
365 for (i = 0; i < valid_nursery_object_count; ++i) {
366 char *obj = valid_nursery_objects [i];
367 callback (obj, safe_object_get_size ((GCObject*)obj), data);
372 describe_nursery_ptr (char *ptr, gboolean need_setup)
377 setup_valid_nursery_objects ();
379 for (i = 0; i < valid_nursery_object_count - 1; ++i) {
380 if (valid_nursery_objects [i + 1] > ptr)
384 if (i >= valid_nursery_object_count || valid_nursery_objects [i] + safe_object_get_size ((GCObject *)valid_nursery_objects [i]) < ptr) {
385 SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)");
388 char *obj = valid_nursery_objects [i];
390 SGEN_LOG (0, "nursery-ptr %p", obj);
392 SGEN_LOG (0, "nursery-ptr %p (interior-ptr offset %td)", obj, ptr - obj);
398 is_valid_object_pointer (char *object)
400 if (sgen_ptr_in_nursery (object))
401 return find_object_in_nursery_dump (object);
403 if (sgen_los_is_valid_object (object))
406 if (major_collector.is_valid_object (object))
412 bad_pointer_spew (char *obj, char **slot)
415 GCVTable *vtable = (GCVTable*)LOAD_VTABLE (obj);
417 SGEN_LOG (0, "Invalid object pointer %p at offset %td in object %p (%s.%s):", ptr,
419 obj, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
420 describe_pointer (ptr, FALSE);
425 missing_remset_spew (char *obj, char **slot)
428 GCVTable *vtable = (GCVTable*)LOAD_VTABLE (obj);
430 SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.",
431 ptr, (char*)slot - obj, obj,
432 sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
438 FIXME Flag missing remsets due to pinning as non fatal
441 #define HANDLE_PTR(ptr,obj) do { \
442 if (*(char**)ptr) { \
443 if (!is_valid_object_pointer (*(char**)ptr)) { \
444 bad_pointer_spew ((char*)obj, (char**)ptr); \
445 } else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) { \
446 if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup ((char*)*(ptr)) && (!allow_missing_pinned || !SGEN_OBJECT_IS_PINNED ((char*)*(ptr)))) \
447 missing_remset_spew ((char*)obj, (char**)ptr); \
453 verify_object_pointers_callback (char *start, size_t size, void *data)
455 gboolean allow_missing_pinned = (gboolean) (size_t) data;
456 mword desc = sgen_obj_get_descriptor (start);
458 #include "sgen-scan-object.h"
463 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
464 depend on OP_DUMMY_USE.
467 sgen_check_whole_heap (gboolean allow_missing_pinned)
469 setup_valid_nursery_objects ();
472 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned, FALSE);
473 major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
474 sgen_los_iterate_objects (verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
476 g_assert (!broken_heap);
480 ptr_in_heap (char *object)
482 if (sgen_ptr_in_nursery (object))
485 if (sgen_los_is_valid_object (object))
488 if (major_collector.is_valid_object (object))
495 * Do consistency checks on the object reference OBJ. Assert on failure.
498 sgen_check_objref (char *obj)
500 g_assert (ptr_in_heap (obj));
504 find_pinning_ref_from_thread (char *obj, size_t size)
506 #ifndef SGEN_WITHOUT_MONO
508 SgenThreadInfo *info;
509 char *endobj = obj + size;
511 FOREACH_THREAD (info) {
512 char **start = (char**)info->client_info.stack_start;
513 if (info->client_info.skip)
515 while (start < (char**)info->client_info.stack_end) {
516 if (*start >= obj && *start < endobj)
517 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.stack_end);
521 for (j = 0; j < ARCH_NUM_REGS; ++j) {
523 mword w = ((mword*)&info->client_info.ctx) [j];
525 mword w = (mword)&info->client_info.regs [j];
528 if (w >= (mword)obj && w < (mword)obj + size)
529 SGEN_LOG (0, "Object %p referenced in saved reg %d of thread %p (id %p)", obj, j, info, (gpointer)mono_thread_info_get_tid (info));
536 * Debugging function: find in the conservative roots where @obj is being pinned.
538 static G_GNUC_UNUSED void
539 find_pinning_reference (char *obj, size_t size)
543 char *endobj = obj + size;
545 SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_NORMAL], start, root) {
546 /* if desc is non-null it has precise info */
547 if (!root->root_desc) {
548 while (start < (char**)root->end_root) {
549 if (*start >= obj && *start < endobj) {
550 SGEN_LOG (0, "Object %p referenced in pinned roots %p-%p\n", obj, start, root->end_root);
555 } SGEN_HASH_TABLE_FOREACH_END;
557 find_pinning_ref_from_thread (obj, size);
561 #define HANDLE_PTR(ptr,obj) do { \
562 char* __target = *(char**)ptr; \
564 if (sgen_ptr_in_nursery (__target)) { \
565 g_assert (!SGEN_OBJECT_IS_FORWARDED (__target)); \
567 mword __size = sgen_safe_object_get_size ((GCObject*)__target); \
568 if (__size <= SGEN_MAX_SMALL_OBJ_SIZE) \
569 g_assert (major_collector.is_object_live (__target)); \
571 g_assert (sgen_los_object_is_pinned (__target)); \
577 check_marked_callback (char *start, size_t size, void *dummy)
579 gboolean flag = (gboolean) (size_t) dummy;
582 if (sgen_ptr_in_nursery (start)) {
584 SGEN_ASSERT (0, SGEN_OBJECT_IS_PINNED (start), "All objects remaining in the nursery must be pinned");
586 if (!sgen_los_object_is_pinned (start))
589 if (!major_collector.is_object_live (start))
593 desc = sgen_obj_get_descriptor_safe (start);
595 #include "sgen-scan-object.h"
599 sgen_check_heap_marked (gboolean nursery_must_be_pinned)
601 setup_valid_nursery_objects ();
603 iterate_valid_nursery_objects (check_marked_callback, (void*)(size_t)nursery_must_be_pinned);
604 major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, check_marked_callback, (void*)FALSE);
605 sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
609 check_nursery_objects_pinned_callback (char *obj, size_t size, void *data /* ScanCopyContext *ctx */)
611 gboolean pinned = (gboolean) (size_t) data;
613 g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
615 g_assert (SGEN_OBJECT_IS_PINNED (obj));
617 g_assert (!SGEN_OBJECT_IS_PINNED (obj));
621 sgen_check_nursery_objects_pinned (gboolean pinned)
623 sgen_clear_nursery_fragments ();
624 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
625 (IterateObjectCallbackFunc)check_nursery_objects_pinned_callback, (void*) (size_t) pinned /* (void*)&ctx */, FALSE);
629 verify_scan_starts (char *start, char *end)
633 for (i = 0; i < nursery_section->num_scan_start; ++i) {
634 char *addr = nursery_section->scan_starts [i];
635 if (addr > start && addr < end)
636 SGEN_LOG (0, "NFC-BAD SCAN START [%zu] %p for obj [%p %p]", i, addr, start, end);
641 sgen_debug_verify_nursery (gboolean do_dump_nursery_content)
643 char *start, *end, *cur, *hole_start;
645 if (nursery_canaries_enabled ())
646 SGEN_LOG (0, "Checking nursery canaries...");
648 /*This cleans up unused fragments */
649 sgen_nursery_allocator_prepare_for_pinning ();
651 hole_start = start = cur = sgen_get_nursery_start ();
652 end = sgen_get_nursery_end ();
656 gboolean is_array_fill;
659 cur += sizeof (void*);
663 if (object_is_forwarded (cur))
664 SGEN_LOG (0, "FORWARDED OBJ %p", cur);
665 else if (object_is_pinned (cur))
666 SGEN_LOG (0, "PINNED OBJ %p", cur);
668 ss = safe_object_get_size ((GCObject*)cur);
669 size = SGEN_ALIGN_UP (ss);
670 verify_scan_starts (cur, cur + size);
671 is_array_fill = sgen_client_object_is_array_fill ((GCObject*)cur);
672 if (do_dump_nursery_content) {
673 GCVTable *vtable = SGEN_LOAD_VTABLE (cur);
674 if (cur > hole_start)
675 SGEN_LOG (0, "HOLE [%p %p %d]", hole_start, cur, (int)(cur - hole_start));
676 SGEN_LOG (0, "OBJ [%p %p %d %d %s.%s %d]", cur, cur + size, (int)size, (int)ss,
677 sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable),
680 if (nursery_canaries_enabled () && !is_array_fill) {
681 CHECK_CANARY_FOR_OBJECT (cur);
682 CANARIFY_SIZE (size);
690 * Checks that no objects in the nursery are fowarded or pinned. This
691 * is a precondition to restarting the mutator while doing a
692 * concurrent collection. Note that we don't clear fragments because
693 * we depend on that having happened earlier.
696 sgen_debug_check_nursery_is_clean (void)
700 cur = sgen_get_nursery_start ();
701 end = sgen_get_nursery_end ();
707 cur += sizeof (void*);
711 g_assert (!object_is_forwarded (cur));
712 g_assert (!object_is_pinned (cur));
714 size = SGEN_ALIGN_UP (safe_object_get_size ((GCObject*)cur));
715 verify_scan_starts (cur, cur + size);
721 static gboolean scan_object_for_specific_ref_precise = TRUE;
724 #define HANDLE_PTR(ptr,obj) do { \
725 if ((GCObject*)*(ptr) == key) { \
726 GCVTable *vtable = SGEN_LOAD_VTABLE (*(ptr)); \
727 g_print ("found ref to %p in object %p (%s.%s) at offset %td\n", \
728 key, (obj), sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), ((char*)(ptr) - (char*)(obj))); \
733 scan_object_for_specific_ref (char *start, GCObject *key)
737 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
740 if (scan_object_for_specific_ref_precise) {
741 mword desc = sgen_obj_get_descriptor_safe (start);
742 #include "sgen-scan-object.h"
744 mword *words = (mword*)start;
745 size_t size = safe_object_get_size ((GCObject*)start);
747 for (i = 0; i < size / sizeof (mword); ++i) {
748 if (words [i] == (mword)key) {
749 GCVTable *vtable = SGEN_LOAD_VTABLE (start);
750 g_print ("found possible ref to %p in object %p (%s.%s) at offset %td\n",
751 key, start, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), i * sizeof (mword));
758 scan_object_for_specific_ref_callback (char *obj, size_t size, GCObject *key)
760 scan_object_for_specific_ref (obj, key);
764 check_root_obj_specific_ref (RootRecord *root, GCObject *key, GCObject *obj)
768 g_print ("found ref to %p in root record %p\n", key, root);
771 static GCObject *check_key = NULL;
772 static RootRecord *check_root = NULL;
775 check_root_obj_specific_ref_from_marker (void **obj, void *gc_data)
777 check_root_obj_specific_ref (check_root, check_key, *obj);
781 scan_roots_for_specific_ref (GCObject *key, int root_type)
787 SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
788 mword desc = root->root_desc;
792 switch (desc & ROOT_DESC_TYPE_MASK) {
793 case ROOT_DESC_BITMAP:
794 desc >>= ROOT_DESC_TYPE_SHIFT;
797 check_root_obj_specific_ref (root, key, *start_root);
802 case ROOT_DESC_COMPLEX: {
803 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
804 int bwords = (int) ((*bitmap_data) - 1);
805 void **start_run = start_root;
807 while (bwords-- > 0) {
808 gsize bmap = *bitmap_data++;
809 void **objptr = start_run;
812 check_root_obj_specific_ref (root, key, *objptr);
816 start_run += GC_BITS_PER_WORD;
820 case ROOT_DESC_USER: {
821 SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
822 marker (start_root, check_root_obj_specific_ref_from_marker, NULL);
825 case ROOT_DESC_RUN_LEN:
826 g_assert_not_reached ();
828 g_assert_not_reached ();
830 } SGEN_HASH_TABLE_FOREACH_END;
837 mono_gc_scan_for_specific_ref (GCObject *key, gboolean precise)
842 scan_object_for_specific_ref_precise = precise;
844 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
845 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
847 major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
849 sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
851 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
852 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
854 SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], ptr, root) {
855 while (ptr < (void**)root->end_root) {
856 check_root_obj_specific_ref (root, *ptr, key);
859 } SGEN_HASH_TABLE_FOREACH_END;
862 #ifndef SGEN_WITHOUT_MONO
864 static MonoDomain *check_domain = NULL;
867 check_obj_not_in_domain (MonoObject **o)
869 g_assert (((*o))->vtable->domain != check_domain);
874 check_obj_not_in_domain_callback (void **o, void *gc_data)
876 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
880 sgen_scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
884 check_domain = domain;
885 SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
886 mword desc = root->root_desc;
888 /* The MonoDomain struct is allowed to hold
889 references to objects in its own domain. */
890 if (start_root == (void**)domain)
893 switch (desc & ROOT_DESC_TYPE_MASK) {
894 case ROOT_DESC_BITMAP:
895 desc >>= ROOT_DESC_TYPE_SHIFT;
897 if ((desc & 1) && *start_root)
898 check_obj_not_in_domain (*start_root);
903 case ROOT_DESC_COMPLEX: {
904 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
905 int bwords = (int)((*bitmap_data) - 1);
906 void **start_run = start_root;
908 while (bwords-- > 0) {
909 gsize bmap = *bitmap_data++;
910 void **objptr = start_run;
912 if ((bmap & 1) && *objptr)
913 check_obj_not_in_domain (*objptr);
917 start_run += GC_BITS_PER_WORD;
921 case ROOT_DESC_USER: {
922 SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
923 marker (start_root, check_obj_not_in_domain_callback, NULL);
926 case ROOT_DESC_RUN_LEN:
927 g_assert_not_reached ();
929 g_assert_not_reached ();
931 } SGEN_HASH_TABLE_FOREACH_END;
937 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
939 MonoObject *o = (MonoObject*)(obj);
940 MonoObject *ref = (MonoObject*)*(ptr);
941 size_t offset = (char*)(ptr) - (char*)o;
943 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
945 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
948 #ifndef DISABLE_REMOTING
949 if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) &&
950 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
953 /* Thread.cached_culture_info */
954 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
955 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
956 !strcmp(o->vtable->klass->name_space, "System") &&
957 !strcmp(o->vtable->klass->name, "Object[]"))
960 * at System.IO.MemoryStream.InternalConstructor (byte[],int,int,bool,bool) [0x0004d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:121
961 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
962 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
963 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
964 * at System.Runtime.Remoting.Messaging.MethodCall..ctor (System.Runtime.Remoting.Messaging.CADMethodCallMessage) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/MethodCall.cs:87
965 * at System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) [0x00018] in /home/schani/Work/novell/trunk/mcs/class/corlib/System/AppDomain.cs:1213
966 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
967 * at System.Runtime.Remoting.Channels.CrossAppDomainSink.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage) [0x00008] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Channels/CrossAppDomainChannel.cs:198
968 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
970 if (!strcmp (ref->vtable->klass->name_space, "System") &&
971 !strcmp (ref->vtable->klass->name, "Byte[]") &&
972 !strcmp (o->vtable->klass->name_space, "System.IO") &&
973 !strcmp (o->vtable->klass->name, "MemoryStream"))
979 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
981 MonoObject *o = (MonoObject*)(obj);
982 MonoObject *ref = (MonoObject*)*(ptr);
983 size_t offset = (char*)(ptr) - (char*)o;
985 MonoClassField *field;
988 if (!ref || ref->vtable->domain == domain)
990 if (is_xdomain_ref_allowed (ptr, obj, domain))
994 for (class = o->vtable->klass; class; class = class->parent) {
997 for (i = 0; i < class->field.count; ++i) {
998 if (class->fields[i].offset == offset) {
999 field = &class->fields[i];
1007 if (ref->vtable->klass == mono_defaults.string_class)
1008 str = mono_string_to_utf8 ((MonoString*)ref);
1011 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1012 o, o->vtable->klass->name_space, o->vtable->klass->name,
1013 offset, field ? field->name : "",
1014 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1015 mono_gc_scan_for_specific_ref (o, TRUE);
1021 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1024 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1026 MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (start);
1027 MonoDomain *domain = vt->domain;
1028 mword desc = sgen_vtable_get_descriptor ((GCVTable*)vt);
1030 #include "sgen-scan-object.h"
1034 sgen_check_for_xdomain_refs (void)
1038 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1039 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
1041 major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1043 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1044 scan_object_for_xdomain_refs (bigobj->data, sgen_los_object_size (bigobj), NULL);
1049 /* If not null, dump the heap after each collection into this file */
1050 static FILE *heap_dump_file = NULL;
1053 sgen_dump_occupied (char *start, char *end, char *section_start)
1055 fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
1059 sgen_dump_section (GCMemSection *section, const char *type)
1061 char *start = section->data;
1062 char *end = section->data + section->size;
1063 char *occ_start = NULL;
1065 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
1067 while (start < end) {
1072 if (!*(void**)start) {
1074 sgen_dump_occupied (occ_start, start, section->data);
1077 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1080 g_assert (start < section->next_data);
1085 //vt = (GCVTable*)SGEN_LOAD_VTABLE (start);
1086 //class = vt->klass;
1088 size = SGEN_ALIGN_UP (safe_object_get_size ((GCObject*) start));
1091 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
1092 start - section->data,
1093 vt->klass->name_space, vt->klass->name,
1100 sgen_dump_occupied (occ_start, start, section->data);
1102 fprintf (heap_dump_file, "</section>\n");
1106 dump_object (GCObject *obj, gboolean dump_location)
1108 #ifndef SGEN_WITHOUT_MONO
1109 static char class_name [1024];
1111 MonoClass *class = mono_object_class (obj);
1115 * Python's XML parser is too stupid to parse angle brackets
1116 * in strings, so we just ignore them;
1119 while (class->name [i] && j < sizeof (class_name) - 1) {
1120 if (!strchr ("<>\"", class->name [i]))
1121 class_name [j++] = class->name [i];
1124 g_assert (j < sizeof (class_name));
1127 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%zd\"",
1128 class->name_space, class_name,
1129 safe_object_get_size (obj));
1130 if (dump_location) {
1131 const char *location;
1132 if (sgen_ptr_in_nursery (obj))
1133 location = "nursery";
1134 else if (safe_object_get_size (obj) <= SGEN_MAX_SMALL_OBJ_SIZE)
1138 fprintf (heap_dump_file, " location=\"%s\"", location);
1140 fprintf (heap_dump_file, "/>\n");
1145 sgen_debug_enable_heap_dump (const char *filename)
1147 heap_dump_file = fopen (filename, "w");
1148 if (heap_dump_file) {
1149 fprintf (heap_dump_file, "<sgen-dump>\n");
1150 sgen_pin_stats_enable ();
1155 sgen_debug_dump_heap (const char *type, int num, const char *reason)
1157 SgenPointerQueue *pinned_objects;
1161 if (!heap_dump_file)
1164 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
1166 fprintf (heap_dump_file, " reason=\"%s\"", reason);
1167 fprintf (heap_dump_file, ">\n");
1168 #ifndef SGEN_WITHOUT_MONO
1169 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
1171 sgen_dump_internal_mem_usage (heap_dump_file);
1172 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_STACK));
1173 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
1174 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_OTHER));
1176 fprintf (heap_dump_file, "<pinned-objects>\n");
1177 pinned_objects = sgen_pin_stats_get_object_list ();
1178 for (i = 0; i < pinned_objects->next_slot; ++i)
1179 dump_object (pinned_objects->data [i], TRUE);
1180 fprintf (heap_dump_file, "</pinned-objects>\n");
1182 sgen_dump_section (nursery_section, "nursery");
1184 major_collector.dump_heap (heap_dump_file);
1186 fprintf (heap_dump_file, "<los>\n");
1187 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1188 dump_object ((GCObject*)bigobj->data, FALSE);
1189 fprintf (heap_dump_file, "</los>\n");
1191 fprintf (heap_dump_file, "</collection>\n");
1194 static char *found_obj;
1197 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
1199 char *ptr = user_data;
1201 if (ptr >= obj && ptr < obj + size) {
1202 g_assert (!found_obj);
1207 /* for use in the debugger */
1209 sgen_find_object_for_ptr (char *ptr)
1211 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
1213 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1214 find_object_for_ptr_callback, ptr, TRUE);
1220 sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
1225 * Very inefficient, but this is debugging code, supposed to
1226 * be called from gdb, so we don't care.
1229 major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, find_object_for_ptr_callback, ptr);
1233 #ifndef SGEN_WITHOUT_MONO
1236 compare_xrefs (const void *a_ptr, const void *b_ptr)
1238 const MonoGCBridgeXRef *a = a_ptr;
1239 const MonoGCBridgeXRef *b = b_ptr;
1241 if (a->src_scc_index < b->src_scc_index)
1243 if (a->src_scc_index > b->src_scc_index)
1246 if (a->dst_scc_index < b->dst_scc_index)
1248 if (a->dst_scc_index > b->dst_scc_index)
1256 dump_processor_state (SgenBridgeProcessor *p)
1260 printf ("------\n");
1261 printf ("SCCS %d\n", p->num_sccs);
1262 for (i = 0; i < p->num_sccs; ++i) {
1264 MonoGCBridgeSCC *scc = p->api_sccs [i];
1265 printf ("\tSCC %d:", i);
1266 for (j = 0; j < scc->num_objs; ++j) {
1267 MonoObject *obj = scc->objs [j];
1268 printf (" %p", obj);
1273 printf ("XREFS %d\n", p->num_xrefs);
1274 for (i = 0; i < p->num_xrefs; ++i)
1275 printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index);
1277 printf ("-------\n");
1282 sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcessor *b)
1285 SgenHashTable obj_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), mono_aligned_addr_hash, NULL);
1286 SgenHashTable b_scc_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), g_direct_hash, NULL);
1287 MonoGCBridgeXRef *a_xrefs, *b_xrefs;
1288 size_t xrefs_alloc_size;
1290 // dump_processor_state (a);
1291 // dump_processor_state (b);
1293 if (a->num_sccs != b->num_sccs)
1294 g_error ("SCCS count expected %d but got %d", a->num_sccs, b->num_sccs);
1295 if (a->num_xrefs != b->num_xrefs)
1296 g_error ("SCCS count expected %d but got %d", a->num_xrefs, b->num_xrefs);
1299 * First we build a hash of each object in `a` to its respective SCC index within
1300 * `a`. Along the way we also assert that no object is more than one SCC.
1302 for (i = 0; i < a->num_sccs; ++i) {
1304 MonoGCBridgeSCC *scc = a->api_sccs [i];
1306 g_assert (scc->num_objs > 0);
1308 for (j = 0; j < scc->num_objs; ++j) {
1309 GCObject *obj = scc->objs [j];
1310 gboolean new_entry = sgen_hash_table_replace (&obj_to_a_scc, obj, &i, NULL);
1311 g_assert (new_entry);
1316 * Now we check whether each of the objects in `b` are in `a`, and whether the SCCs
1317 * of `b` contain the same sets of objects as those of `a`.
1319 * While we're doing this, build a hash table to map from `b` SCC indexes to `a` SCC
1322 for (i = 0; i < b->num_sccs; ++i) {
1323 MonoGCBridgeSCC *scc = b->api_sccs [i];
1324 MonoGCBridgeSCC *a_scc;
1325 int *a_scc_index_ptr;
1330 g_assert (scc->num_objs > 0);
1331 a_scc_index_ptr = sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [0]);
1332 g_assert (a_scc_index_ptr);
1333 a_scc_index = *a_scc_index_ptr;
1335 //g_print ("A SCC %d -> B SCC %d\n", a_scc_index, i);
1337 a_scc = a->api_sccs [a_scc_index];
1338 g_assert (a_scc->num_objs == scc->num_objs);
1340 for (j = 1; j < scc->num_objs; ++j) {
1341 a_scc_index_ptr = sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [j]);
1342 g_assert (a_scc_index_ptr);
1343 g_assert (*a_scc_index_ptr == a_scc_index);
1346 new_entry = sgen_hash_table_replace (&b_scc_to_a_scc, GINT_TO_POINTER (i), &a_scc_index, NULL);
1347 g_assert (new_entry);
1351 * Finally, check that we have the same xrefs. We do this by making copies of both
1352 * xref arrays, and replacing the SCC indexes in the copy for `b` with the
1353 * corresponding indexes in `a`. Then we sort both arrays and assert that they're
1356 * At the same time, check that no xref is self-referential and that there are no
1360 xrefs_alloc_size = a->num_xrefs * sizeof (MonoGCBridgeXRef);
1361 a_xrefs = sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
1362 b_xrefs = sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
1364 memcpy (a_xrefs, a->api_xrefs, xrefs_alloc_size);
1365 for (i = 0; i < b->num_xrefs; ++i) {
1366 MonoGCBridgeXRef *xref = &b->api_xrefs [i];
1369 g_assert (xref->src_scc_index != xref->dst_scc_index);
1371 scc_index_ptr = sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->src_scc_index));
1372 g_assert (scc_index_ptr);
1373 b_xrefs [i].src_scc_index = *scc_index_ptr;
1375 scc_index_ptr = sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->dst_scc_index));
1376 g_assert (scc_index_ptr);
1377 b_xrefs [i].dst_scc_index = *scc_index_ptr;
1380 qsort (a_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
1381 qsort (b_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
1383 for (i = 0; i < a->num_xrefs; ++i) {
1384 g_assert (a_xrefs [i].src_scc_index == b_xrefs [i].src_scc_index);
1385 g_assert (a_xrefs [i].dst_scc_index == b_xrefs [i].dst_scc_index);
1388 sgen_hash_table_clean (&obj_to_a_scc);
1389 sgen_hash_table_clean (&b_scc_to_a_scc);
1390 sgen_free_internal_dynamic (a_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
1391 sgen_free_internal_dynamic (b_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
1398 #endif /*HAVE_SGEN_GC*/