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.
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"
30 #pragma warning(disable:4312) // FIXME pointer cast to different size
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
[] = {
59 "complex pointer-free"
62 static char* describe_nursery_ptr (char *ptr
, gboolean need_setup
);
65 describe_pointer (char *ptr
, gboolean need_setup
)
75 if (sgen_ptr_in_nursery (ptr
)) {
76 start
= describe_nursery_ptr (ptr
, need_setup
);
80 vtable
= LOAD_VTABLE ((GCObject
*)ptr
);
82 if (sgen_ptr_is_in_los (ptr
, &start
)) {
84 printf ("Pointer is the start of object %p in LOS space.\n", start
);
86 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr
- start
), 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
)) {
92 printf ("Pointer is the start of object %p in oldspace.\n", start
);
94 printf ("Pointer is at offset 0x%x of object %p in oldspace.\n", (int)(ptr
- start
), start
);
96 printf ("Pointer inside oldspace.\n");
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
);
105 printf ("Pointer unknown.\n");
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
);
119 printf ("VTable: %p\n", vtable
);
120 if (vtable
== NULL
) {
121 printf ("VTable is invalid (empty).\n");
124 if (sgen_ptr_in_nursery (vtable
)) {
125 printf ("VTable is invalid (points inside nursery).\n");
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
);
141 sgen_client_describe_invalid_pointer ((GCObject
*) ptr
);
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.
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); \
167 missing_remsets = TRUE; \
173 * Check that each object reference which points into the nursery can
174 * be found in the remembered sets.
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.
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.");
209 sgen_binary_protocol_flush_buffers (TRUE
);
210 if (!sgen_binary_protocol_is_enabled ())
211 g_assert (!missing_remsets
);
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
);
220 return sgen_get_major_collector ()->is_object_live (obj
);
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; \
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
);
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
))
250 cards
= sgen_los_header_for_object (obj
)->cardtable_mod_union
;
252 cards
= sgen_get_major_collector ()->get_cardtable_mod_union_for_reference (start
);
254 #include "sgen-scan-object.h"
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
);
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)); \
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"
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 */
294 #define HANDLE_PTR(ptr,obj) do { \
296 g_assert (sgen_client_vtable_get_namespace (SGEN_LOAD_VTABLE_UNCHECKED (*(ptr)))); \
303 * Perform consistency check on an object. Currently we only check that the
304 * reference fields are valid.
307 sgen_check_object (GCObject
*obj
)
309 char *start
= (char*)obj
;
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
;
326 setup_mono_sgen_scan_area_with_callback (GCObject
*object
, size_t size
, void *data
)
328 valid_nursery_objects
[valid_nursery_object_count
++] = object
;
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
);
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
])
349 if (object
< (char*)valid_nursery_objects
[middle
])
354 g_assert (first
== last
);
359 iterate_valid_nursery_objects (IterateObjectCallbackFunc callback
, void *data
)
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
);
369 describe_nursery_ptr (char *ptr
, gboolean 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
)
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)");
385 GCObject
*obj
= valid_nursery_objects
[i
];
386 if ((char*)obj
== ptr
)
387 SGEN_LOG (0, "nursery-ptr %p", obj
);
389 SGEN_LOG (0, "nursery-ptr %p (interior-ptr offset %ld)", obj
, (long)(ptr
- (char*)obj
));
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
))
403 if (sgen_major_collector
.is_valid_object (object
))
409 bad_pointer_spew (char *obj
, char **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
);
422 missing_remset_spew (char *obj
, char **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
));
435 FIXME Flag missing remsets due to pinning as non fatal
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); \
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"
461 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
462 depend on OP_DUMMY_USE.
465 sgen_check_whole_heap (gboolean allow_missing_pinned
)
467 setup_valid_nursery_objects ();
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
);
478 ptr_in_heap (char *object
)
480 if (sgen_ptr_in_nursery (object
))
483 if (sgen_los_is_valid_object (object
))
486 if (sgen_major_collector
.is_valid_object (object
))
493 * Do consistency checks on the object reference OBJ. Assert on failure.
496 sgen_check_objref (char *obj
)
498 g_assert (ptr_in_heap (obj
));
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
)
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
);
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
));
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
)
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
);
548 } SGEN_HASH_TABLE_FOREACH_END
;
550 find_pinning_ref_from_thread (obj
, size
);
554 #define HANDLE_PTR(ptr,obj) do { \
555 char* __target = *(char**)ptr; \
557 if (sgen_ptr_in_nursery (__target)) { \
558 g_assert (!SGEN_OBJECT_IS_FORWARDED (__target)); \
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)); \
564 g_assert (sgen_los_object_is_pinned ((GCObject*)__target)); \
570 check_marked_callback (GCObject
*obj
, size_t size
, void *dummy
)
572 char *start
= (char*)obj
;
573 gboolean flag
= (gboolean
) (size_t) dummy
;
576 if (sgen_ptr_in_nursery (start
)) {
578 SGEN_ASSERT (0, SGEN_OBJECT_IS_PINNED (obj
), "All objects remaining in the nursery must be pinned");
580 if (!sgen_los_object_is_pinned (obj
))
583 if (!sgen_major_collector
.is_object_live (obj
))
587 desc
= sgen_obj_get_descriptor_safe (obj
);
589 #include "sgen-scan-object.h"
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
);
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
));
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
);
618 verify_scan_starts (char *start
, char *end
)
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
);
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 ();
645 gboolean is_array_fill
;
648 cur
+= sizeof (void*);
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
),
669 if (sgen_nursery_canaries_enabled () && !is_array_fill
) {
670 CHECK_CANARY_FOR_OBJECT ((GCObject
*)cur
, TRUE
);
671 CANARIFY_SIZE (size
);
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.
685 sgen_debug_check_nursery_is_clean (void)
689 cur
= sgen_get_nursery_start ();
690 end
= sgen_get_nursery_end ();
696 cur
+= sizeof (void*);
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
);
710 static gboolean scan_object_for_specific_ref_precise
= TRUE
;
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)))); \
722 scan_object_for_specific_ref (GCObject
*obj
, GCObject
*key
)
726 if ((forwarded
= SGEN_OBJECT_IS_FORWARDED (obj
)))
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"
734 mword
*words
= (mword
*)obj
;
735 size_t size
= safe_object_get_size (obj
);
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
)));
748 scan_object_for_specific_ref_callback (GCObject
*obj
, size_t size
, GCObject
*key
)
750 scan_object_for_specific_ref (obj
, key
);
754 check_root_obj_specific_ref (RootRecord
*root
, GCObject
*key
, GCObject
*obj
)
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
;
765 check_root_obj_specific_ref_from_marker (GCObject
**obj
, void *gc_data
)
767 check_root_obj_specific_ref (check_root
, check_key
, *obj
);
771 scan_roots_for_specific_ref (GCObject
*key
, int root_type
)
777 SGEN_HASH_TABLE_FOREACH (&sgen_roots_hash
[root_type
], void **, start_root
, RootRecord
*, root
) {
778 SgenDescriptor desc
= root
->root_desc
;
782 switch (desc
& ROOT_DESC_TYPE_MASK
) {
783 case ROOT_DESC_BITMAP
:
784 desc
>>= ROOT_DESC_TYPE_SHIFT
;
787 check_root_obj_specific_ref (root
, key
, (GCObject
*)*start_root
);
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
;
797 while (bwords
-- > 0) {
798 gsize bmap
= *bitmap_data
++;
799 void **objptr
= start_run
;
802 check_root_obj_specific_ref (root
, key
, (GCObject
*)*objptr
);
806 start_run
+= GC_BITS_PER_WORD
;
810 case ROOT_DESC_VECTOR
: {
813 for (p
= start_root
; p
< (void**)root
->end_root
; p
++) {
815 check_root_obj_specific_ref (root
, key
, (GCObject
*)*p
);
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
);
824 case ROOT_DESC_RUN_LEN
:
825 g_assert_not_reached ();
827 g_assert_not_reached ();
829 } SGEN_HASH_TABLE_FOREACH_END
;
836 mono_gc_scan_for_specific_ref (GCObject
*key
, gboolean precise
)
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
);
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
;
869 check_obj_not_in_domain (MonoObject
**o
)
871 g_assert (((*o
))->vtable
->domain
!= check_domain
);
876 check_obj_not_in_domain_callback (GCObject
**o
, void *gc_data
)
878 g_assert ((*o
)->vtable
->domain
!= check_domain
);
882 sgen_scan_for_registered_roots_in_domain (MonoDomain
*domain
, int root_type
)
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
)
895 switch (desc
& ROOT_DESC_TYPE_MASK
) {
896 case ROOT_DESC_BITMAP
:
897 desc
>>= ROOT_DESC_TYPE_SHIFT
;
899 if ((desc
& 1) && *start_root
)
900 check_obj_not_in_domain ((MonoObject
**)*start_root
);
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
;
910 while (bwords
-- > 0) {
911 gsize bmap
= *bitmap_data
++;
912 void **objptr
= start_run
;
914 if ((bmap
& 1) && *objptr
)
915 check_obj_not_in_domain ((MonoObject
**)*objptr
);
919 start_run
+= GC_BITS_PER_WORD
;
923 case ROOT_DESC_VECTOR
: {
926 for (p
= start_root
; p
< (void**)root
->end_root
; p
++) {
928 check_obj_not_in_domain ((MonoObject
**)*p
);
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
);
937 case ROOT_DESC_RUN_LEN
:
938 g_assert_not_reached ();
940 g_assert_not_reached ();
942 } SGEN_HASH_TABLE_FOREACH_END
;
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
))
955 if (o
->vtable
->klass
== mono_defaults
.internal_thread_class
&& offset
== G_STRUCT_OFFSET (MonoInternalThread
, current_appcontext
))
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
))
967 check_reference_for_xdomain (GCObject
**ptr
, GCObject
*obj
, MonoDomain
*domain
)
969 MonoObject
*ref
= *ptr
;
970 size_t offset
= (char*)(ptr
) - (char*)obj
;
972 MonoClassField
*field
;
975 if (!ref
|| ref
->vtable
->domain
== domain
)
977 if (is_xdomain_ref_allowed (ptr
, obj
, domain
))
981 for (klass
= obj
->vtable
->klass
; klass
; klass
= m_class_get_parent (klass
)) {
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
];
996 if (ref
->vtable
->klass
== mono_defaults
.string_class
) {
998 str
= mono_string_to_utf8_checked_internal ((MonoString
*)ref
, error
);
999 mono_error_cleanup (error
);
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
);
1012 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
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"
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
);
1038 /* If not null, dump the heap after each collection into this file */
1039 static FILE *heap_dump_file
= NULL
;
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
));
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
) {
1061 if (!*(void**)start
) {
1063 sgen_dump_occupied (occ_start
, start
, section
->data
);
1066 start
+= sizeof (void*); /* should be ALLOC_ALIGN, really */
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,
1088 sgen_dump_occupied (occ_start
, start
, section
->data
);
1090 fprintf (heap_dump_file
, "</section>\n");
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
);
1103 * Python's XML parser is too stupid to parse angle brackets
1104 * in strings, so we just ignore them;
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
];
1112 g_assert (j
< sizeof (class_name
));
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
)
1126 fprintf (heap_dump_file
, " location=\"%s\"", location
);
1128 fprintf (heap_dump_file
, "/>\n");
1133 dump_object_callback (GCObject
*obj
, size_t size
, gboolean dump_location
)
1135 dump_object (obj
, dump_location
);
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 ();
1149 sgen_debug_dump_heap (const char *type
, int num
, const char *reason
)
1151 SgenPointerQueue
*pinned_objects
;
1154 if (!heap_dump_file
)
1157 fprintf (heap_dump_file
, "<collection type=\"%s\" num=\"%d\"", type
, num
);
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 ());
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
;
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
);
1199 /* for use in the debugger */
1201 sgen_find_object_for_ptr (char *ptr
)
1203 if (ptr
>= sgen_nursery_section
->data
&& ptr
< sgen_nursery_section
->end_data
) {
1205 sgen_scan_area_with_callback (sgen_nursery_section
->data
, sgen_nursery_section
->end_data
,
1206 find_object_for_ptr_callback
, ptr
, TRUE
, FALSE
);
1212 sgen_los_iterate_objects (find_object_for_ptr_callback
, ptr
);
1217 * Very inefficient, but this is debugging code, supposed to
1218 * be called from gdb, so we don't care.
1221 sgen_major_collector
.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL
, find_object_for_ptr_callback
, ptr
);
1228 sgen_check_for_xdomain_refs (void)
1233 sgen_check_heap_marked (gboolean nursery_must_be_pinned
)
1238 sgen_check_major_refs (void)
1243 sgen_check_mod_union_consistency (void)
1248 sgen_check_nursery_objects_untag (void)
1253 sgen_check_remset_consistency (void)
1258 sgen_check_whole_heap (gboolean allow_missing_pinned
)
1263 sgen_debug_check_nursery_is_clean (void)
1268 sgen_debug_dump_heap (const char *type
, int num
, const char *reason
)
1273 sgen_debug_enable_heap_dump (const char *filename
)
1278 sgen_debug_verify_nursery (gboolean do_dump_nursery_content
)
1283 sgen_dump_occupied (char *start
, char *end
, char *section_start
)
1288 sgen_scan_for_registered_roots_in_domain (MonoDomain
*domain
, int root_type
)
1292 #endif /*DISABLE_SGEN_DEBUG_HELPERS */
1293 #endif /*HAVE_SGEN_GC*/