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 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
21 #include "mono/sgen/sgen-gc.h"
22 #include "mono/sgen/sgen-cardtable.h"
23 #include "mono/sgen/sgen-protocol.h"
24 #include "mono/sgen/sgen-memory-governor.h"
25 #include "mono/sgen/sgen-pinning.h"
26 #include "mono/sgen/sgen-client.h"
28 #define LOAD_VTABLE SGEN_LOAD_VTABLE
30 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
31 #define object_is_pinned SGEN_OBJECT_IS_PINNED
32 #define safe_object_get_size sgen_safe_object_get_size
34 void describe_ptr (char *ptr
);
35 void check_object (GCObject
*obj
);
38 * ######################################################################
39 * ######## Collector debugging
40 * ######################################################################
43 static const char*descriptor_types
[] = {
51 "complex pointer-free"
54 static char* describe_nursery_ptr (char *ptr
, gboolean need_setup
);
57 describe_pointer (char *ptr
, gboolean need_setup
)
67 if (sgen_ptr_in_nursery (ptr
)) {
68 start
= describe_nursery_ptr (ptr
, need_setup
);
72 vtable
= LOAD_VTABLE ((GCObject
*)ptr
);
74 if (sgen_ptr_is_in_los (ptr
, &start
)) {
76 printf ("Pointer is the start of object %p in LOS space.\n", start
);
78 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr
- start
), start
);
80 mono_sgen_los_describe_pointer (ptr
);
81 vtable
= LOAD_VTABLE ((GCObject
*)ptr
);
82 } else if (major_collector
.ptr_is_in_non_pinned_space (ptr
, &start
)) {
84 printf ("Pointer is the start of object %p in oldspace.\n", start
);
86 printf ("Pointer is at offset 0x%x of object %p in oldspace.\n", (int)(ptr
- start
), start
);
88 printf ("Pointer inside oldspace.\n");
91 vtable
= (GCVTable
)major_collector
.describe_pointer (ptr
);
92 } else if (major_collector
.ptr_is_from_pinned_alloc (ptr
)) {
93 // FIXME: Handle pointers to the inside of objects
94 printf ("Pointer is inside a pinned chunk.\n");
95 vtable
= LOAD_VTABLE ((GCObject
*)ptr
);
97 printf ("Pointer unknown.\n");
102 if (object_is_pinned (ptr
))
103 printf ("Object is pinned.\n");
105 if ((forwarded
= (char *)object_is_forwarded (ptr
))) {
106 printf ("Object is forwarded to %p:\n", forwarded
);
111 printf ("VTable: %p\n", vtable
);
112 if (vtable
== NULL
) {
113 printf ("VTable is invalid (empty).\n");
116 if (sgen_ptr_in_nursery (vtable
)) {
117 printf ("VTable is invalid (points inside nursery).\n");
120 printf ("Class: %s.%s\n", sgen_client_vtable_get_namespace (vtable
), sgen_client_vtable_get_name (vtable
));
122 desc
= sgen_vtable_get_descriptor (vtable
);
123 printf ("Descriptor: %lx\n", (long)desc
);
125 type
= desc
& DESC_TYPE_MASK
;
126 printf ("Descriptor type: %d (%s)\n", type
, descriptor_types
[type
]);
128 size
= sgen_safe_object_get_size ((GCObject
*)ptr
);
129 printf ("Size: %d\n", (int)size
);
133 sgen_client_describe_invalid_pointer ((GCObject
*) ptr
);
137 describe_ptr (char *ptr
)
139 describe_pointer (ptr
, TRUE
);
142 static gboolean missing_remsets
;
145 * We let a missing remset slide if the target object is pinned,
146 * because the store might have happened but the remset not yet added,
147 * but in that case the target must be pinned. We might theoretically
148 * miss some missing remsets this way, but it's very unlikely.
151 #define HANDLE_PTR(ptr,obj) do { \
152 if (*(ptr) && sgen_ptr_in_nursery ((char*)*(ptr))) { \
153 if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup (*(ptr))) { \
154 GCVTable __vt = SGEN_LOAD_VTABLE (obj); \
155 gboolean is_pinned = object_is_pinned (*(ptr)); \
156 SGEN_LOG (0, "Oldspace->newspace reference %p at offset %zd in object %p (%s.%s) not found in remsets%s.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), sgen_client_vtable_get_namespace (__vt), sgen_client_vtable_get_name (__vt), is_pinned ? ", but object is pinned" : ""); \
157 binary_protocol_missing_remset ((obj), __vt, (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), is_pinned); \
159 missing_remsets = TRUE; \
165 * Check that each object reference which points into the nursery can
166 * be found in the remembered sets.
169 check_consistency_callback (GCObject
*obj
, size_t size
, void *dummy
)
171 char *start
= (char*)obj
;
172 GCVTable vt
= LOAD_VTABLE (obj
);
173 SgenDescriptor desc
= sgen_vtable_get_descriptor (vt
);
174 SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start
, vt
, sgen_client_vtable_get_name (vt
));
176 #include "sgen-scan-object.h"
180 * Perform consistency check of the heap.
182 * Assumes the world is stopped.
185 sgen_check_remset_consistency (void)
187 // Need to add more checks
189 missing_remsets
= FALSE
;
191 SGEN_LOG (1, "Begin heap consistency check...");
193 // Check that oldspace->newspace pointers are registered with the collector
194 major_collector
.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL
, (IterateObjectCallbackFunc
)check_consistency_callback
, NULL
);
196 sgen_los_iterate_objects ((IterateObjectCallbackFunc
)check_consistency_callback
, NULL
);
198 SGEN_LOG (1, "Heap consistency check done.");
201 binary_protocol_flush_buffers (TRUE
);
202 if (!binary_protocol_is_enabled ())
203 g_assert (!missing_remsets
);
207 is_major_or_los_object_marked (GCObject
*obj
)
209 if (sgen_safe_object_get_size ((GCObject
*)obj
) > SGEN_MAX_SMALL_OBJ_SIZE
) {
210 return sgen_los_object_is_pinned (obj
);
212 return sgen_get_major_collector ()->is_object_live (obj
);
217 #define HANDLE_PTR(ptr,obj) do { \
218 if (*(ptr) && !sgen_ptr_in_nursery ((char*)*(ptr)) && !is_major_or_los_object_marked ((GCObject*)*(ptr))) { \
219 if (!cards || !sgen_get_remset ()->find_address_with_cards (start, cards, (char*)(ptr))) { \
220 GCVTable __vt = SGEN_LOAD_VTABLE (obj); \
221 SGEN_LOG (0, "major->major reference %p at offset %zd 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)); \
222 binary_protocol_missing_remset ((obj), __vt, (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
223 missing_remsets = TRUE; \
229 check_mod_union_callback (GCObject
*obj
, size_t size
, void *dummy
)
231 char *start
= (char*)obj
;
232 gboolean in_los
= (gboolean
) (size_t) dummy
;
233 GCVTable vt
= LOAD_VTABLE (obj
);
234 SgenDescriptor desc
= sgen_vtable_get_descriptor (vt
);
236 SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", obj
, vt
, sgen_client_vtable_get_name (vt
));
238 if (!is_major_or_los_object_marked (obj
))
242 cards
= sgen_los_header_for_object (obj
)->cardtable_mod_union
;
244 cards
= sgen_get_major_collector ()->get_cardtable_mod_union_for_reference (start
);
246 #include "sgen-scan-object.h"
250 sgen_check_mod_union_consistency (void)
252 missing_remsets
= FALSE
;
254 major_collector
.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL
, (IterateObjectCallbackFunc
)check_mod_union_callback
, (void*)FALSE
);
256 sgen_los_iterate_objects ((IterateObjectCallbackFunc
)check_mod_union_callback
, (void*)TRUE
);
258 if (!binary_protocol_is_enabled ())
259 g_assert (!missing_remsets
);
263 #define HANDLE_PTR(ptr,obj) do { \
264 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
265 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)); \
269 check_major_refs_callback (GCObject
*obj
, size_t size
, void *dummy
)
271 char *start
= (char*)obj
;
272 SgenDescriptor desc
= sgen_obj_get_descriptor (obj
);
274 #include "sgen-scan-object.h"
278 sgen_check_major_refs (void)
280 major_collector
.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL
, (IterateObjectCallbackFunc
)check_major_refs_callback
, NULL
);
281 sgen_los_iterate_objects ((IterateObjectCallbackFunc
)check_major_refs_callback
, NULL
);
284 /* Check that the reference is valid */
286 #define HANDLE_PTR(ptr,obj) do { \
288 g_assert (sgen_client_vtable_get_namespace (SGEN_LOAD_VTABLE_UNCHECKED (*(ptr)))); \
295 * Perform consistency check on an object. Currently we only check that the
296 * reference fields are valid.
299 check_object (GCObject
*obj
)
301 char *start
= (char*)obj
;
307 desc
= sgen_obj_get_descriptor (obj
);
309 #include "sgen-scan-object.h"
313 static GCObject
**valid_nursery_objects
;
314 static int valid_nursery_object_count
;
315 static gboolean broken_heap
;
318 setup_mono_sgen_scan_area_with_callback (GCObject
*object
, size_t size
, void *data
)
320 valid_nursery_objects
[valid_nursery_object_count
++] = object
;
324 setup_valid_nursery_objects (void)
326 if (!valid_nursery_objects
)
327 valid_nursery_objects
= (GCObject
**)sgen_alloc_os_memory (DEFAULT_NURSERY_SIZE
, (SgenAllocFlags
)(SGEN_ALLOC_INTERNAL
| SGEN_ALLOC_ACTIVATE
), "debugging data", MONO_MEM_ACCOUNT_SGEN_DEBUGGING
);
328 valid_nursery_object_count
= 0;
329 sgen_scan_area_with_callback (nursery_section
->data
, nursery_section
->end_data
, setup_mono_sgen_scan_area_with_callback
, NULL
, FALSE
, FALSE
);
333 find_object_in_nursery_dump (char *object
)
335 int first
= 0, last
= valid_nursery_object_count
;
336 while (first
< last
) {
337 int middle
= first
+ ((last
- first
) >> 1);
338 if (object
== (char*)valid_nursery_objects
[middle
])
341 if (object
< (char*)valid_nursery_objects
[middle
])
346 g_assert (first
== last
);
351 iterate_valid_nursery_objects (IterateObjectCallbackFunc callback
, void *data
)
354 for (i
= 0; i
< valid_nursery_object_count
; ++i
) {
355 GCObject
*obj
= valid_nursery_objects
[i
];
356 callback (obj
, safe_object_get_size (obj
), data
);
361 describe_nursery_ptr (char *ptr
, gboolean need_setup
)
366 setup_valid_nursery_objects ();
368 for (i
= 0; i
< valid_nursery_object_count
- 1; ++i
) {
369 if ((char*)valid_nursery_objects
[i
+ 1] > ptr
)
373 if (i
>= valid_nursery_object_count
|| (char*)valid_nursery_objects
[i
] + safe_object_get_size (valid_nursery_objects
[i
]) < ptr
) {
374 SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)");
377 GCObject
*obj
= valid_nursery_objects
[i
];
378 if ((char*)obj
== ptr
)
379 SGEN_LOG (0, "nursery-ptr %p", obj
);
381 SGEN_LOG (0, "nursery-ptr %p (interior-ptr offset %zd)", obj
, ptr
- (char*)obj
);
387 is_valid_object_pointer (char *object
)
389 if (sgen_ptr_in_nursery (object
))
390 return find_object_in_nursery_dump (object
);
392 if (sgen_los_is_valid_object (object
))
395 if (major_collector
.is_valid_object (object
))
401 bad_pointer_spew (char *obj
, char **slot
)
404 GCVTable vtable
= LOAD_VTABLE ((GCObject
*)obj
);
406 SGEN_LOG (0, "Invalid object pointer %p at offset %zd in object %p (%s.%s):", ptr
,
408 obj
, sgen_client_vtable_get_namespace (vtable
), sgen_client_vtable_get_name (vtable
));
409 describe_pointer (ptr
, FALSE
);
414 missing_remset_spew (char *obj
, char **slot
)
417 GCVTable vtable
= LOAD_VTABLE ((GCObject
*)obj
);
419 SGEN_LOG (0, "Oldspace->newspace reference %p at offset %zd in object %p (%s.%s) not found in remsets.",
420 ptr
, (char*)slot
- obj
, obj
,
421 sgen_client_vtable_get_namespace (vtable
), sgen_client_vtable_get_name (vtable
));
427 FIXME Flag missing remsets due to pinning as non fatal
430 #define HANDLE_PTR(ptr,obj) do { \
431 if (*(char**)ptr) { \
432 if (!is_valid_object_pointer (*(char**)ptr)) { \
433 bad_pointer_spew ((char*)obj, (char**)ptr); \
434 } else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) { \
435 if (!allow_missing_pinned && !SGEN_OBJECT_IS_PINNED (*(ptr)) && !sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup (*(ptr))) \
436 missing_remset_spew ((char*)obj, (char**)ptr); \
442 verify_object_pointers_callback (GCObject
*obj
, size_t size
, void *data
)
444 char *start
= (char*)obj
;
445 gboolean allow_missing_pinned
= (gboolean
) (size_t) data
;
446 SgenDescriptor desc
= sgen_obj_get_descriptor_safe (obj
);
448 #include "sgen-scan-object.h"
453 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
454 depend on OP_DUMMY_USE.
457 sgen_check_whole_heap (gboolean allow_missing_pinned
)
459 setup_valid_nursery_objects ();
462 sgen_scan_area_with_callback (nursery_section
->data
, nursery_section
->end_data
, verify_object_pointers_callback
, (void*) (size_t) allow_missing_pinned
, FALSE
, TRUE
);
463 major_collector
.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL
, verify_object_pointers_callback
, (void*) (size_t) allow_missing_pinned
);
464 sgen_los_iterate_objects (verify_object_pointers_callback
, (void*) (size_t) allow_missing_pinned
);
466 g_assert (!broken_heap
);
470 ptr_in_heap (char *object
)
472 if (sgen_ptr_in_nursery (object
))
475 if (sgen_los_is_valid_object (object
))
478 if (major_collector
.is_valid_object (object
))
485 * Do consistency checks on the object reference OBJ. Assert on failure.
488 sgen_check_objref (char *obj
)
490 g_assert (ptr_in_heap (obj
));
494 find_pinning_ref_from_thread (char *obj
, size_t size
)
496 #ifndef SGEN_WITHOUT_MONO
497 char *endobj
= obj
+ size
;
499 FOREACH_THREAD (info
) {
500 mword
*ctxstart
, *ctxcurrent
, *ctxend
;
501 char **start
= (char**)info
->client_info
.stack_start
;
502 if (info
->client_info
.skip
|| info
->client_info
.gc_disabled
)
504 while (start
< (char**)info
->client_info
.stack_end
) {
505 if (*start
>= obj
&& *start
< endobj
)
506 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
);
510 for (ctxstart
= ctxcurrent
= (mword
*) &info
->client_info
.ctx
, ctxend
= (mword
*) (&info
->client_info
.ctx
+ 1); ctxcurrent
< ctxend
; ctxcurrent
++) {
511 mword w
= *ctxcurrent
;
513 if (w
>= (mword
)obj
&& w
< (mword
)obj
+ size
)
514 SGEN_LOG (0, "Object %p referenced in saved reg %d of thread %p (id %p)", obj
, (int) (ctxcurrent
- ctxstart
), info
, (gpointer
)mono_thread_info_get_tid (info
));
521 * Debugging function: find in the conservative roots where @obj is being pinned.
523 static G_GNUC_UNUSED
void
524 find_pinning_reference (char *obj
, size_t size
)
528 char *endobj
= obj
+ size
;
530 SGEN_HASH_TABLE_FOREACH (&roots_hash
[ROOT_TYPE_NORMAL
], char **, start
, RootRecord
*, root
) {
531 /* if desc is non-null it has precise info */
532 if (!root
->root_desc
) {
533 while (start
< (char**)root
->end_root
) {
534 if (*start
>= obj
&& *start
< endobj
) {
535 SGEN_LOG (0, "Object %p referenced in pinned roots %p-%p\n", obj
, start
, root
->end_root
);
540 } SGEN_HASH_TABLE_FOREACH_END
;
542 find_pinning_ref_from_thread (obj
, size
);
546 #define HANDLE_PTR(ptr,obj) do { \
547 char* __target = *(char**)ptr; \
549 if (sgen_ptr_in_nursery (__target)) { \
550 g_assert (!SGEN_OBJECT_IS_FORWARDED (__target)); \
552 mword __size = sgen_safe_object_get_size ((GCObject*)__target); \
553 if (__size <= SGEN_MAX_SMALL_OBJ_SIZE) \
554 g_assert (major_collector.is_object_live ((GCObject*)__target)); \
556 g_assert (sgen_los_object_is_pinned ((GCObject*)__target)); \
562 check_marked_callback (GCObject
*obj
, size_t size
, void *dummy
)
564 char *start
= (char*)obj
;
565 gboolean flag
= (gboolean
) (size_t) dummy
;
568 if (sgen_ptr_in_nursery (start
)) {
570 SGEN_ASSERT (0, SGEN_OBJECT_IS_PINNED (obj
), "All objects remaining in the nursery must be pinned");
572 if (!sgen_los_object_is_pinned (obj
))
575 if (!major_collector
.is_object_live (obj
))
579 desc
= sgen_obj_get_descriptor_safe (obj
);
581 #include "sgen-scan-object.h"
585 sgen_check_heap_marked (gboolean nursery_must_be_pinned
)
587 setup_valid_nursery_objects ();
589 iterate_valid_nursery_objects (check_marked_callback
, (void*)(size_t)nursery_must_be_pinned
);
590 major_collector
.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL
, check_marked_callback
, (void*)FALSE
);
591 sgen_los_iterate_objects (check_marked_callback
, (void*)TRUE
);
595 check_nursery_objects_pinned_callback (char *obj
, size_t size
, void *data
/* ScanCopyContext *ctx */)
597 gboolean pinned
= (gboolean
) (size_t) data
;
599 g_assert (!SGEN_OBJECT_IS_FORWARDED (obj
));
601 g_assert (SGEN_OBJECT_IS_PINNED (obj
));
603 g_assert (!SGEN_OBJECT_IS_PINNED (obj
));
607 sgen_check_nursery_objects_pinned (gboolean pinned
)
609 sgen_clear_nursery_fragments ();
610 sgen_scan_area_with_callback (nursery_section
->data
, nursery_section
->end_data
,
611 (IterateObjectCallbackFunc
)check_nursery_objects_pinned_callback
, (void*) (size_t) pinned
/* (void*)&ctx */, FALSE
, TRUE
);
615 verify_scan_starts (char *start
, char *end
)
619 for (i
= 0; i
< nursery_section
->num_scan_start
; ++i
) {
620 char *addr
= nursery_section
->scan_starts
[i
];
621 if (addr
> start
&& addr
< end
)
622 SGEN_LOG (0, "NFC-BAD SCAN START [%zu] %p for obj [%p %p]", i
, addr
, start
, end
);
627 sgen_debug_verify_nursery (gboolean do_dump_nursery_content
)
629 char *start
, *end
, *cur
, *hole_start
;
631 if (nursery_canaries_enabled ())
632 SGEN_LOG (0, "Checking nursery canaries...");
634 /*This cleans up unused fragments */
635 sgen_nursery_allocator_prepare_for_pinning ();
637 hole_start
= start
= cur
= sgen_get_nursery_start ();
638 end
= sgen_get_nursery_end ();
642 gboolean is_array_fill
;
645 cur
+= sizeof (void*);
649 if (object_is_forwarded (cur
))
650 SGEN_LOG (0, "FORWARDED OBJ %p", cur
);
651 else if (object_is_pinned (cur
))
652 SGEN_LOG (0, "PINNED OBJ %p", cur
);
654 ss
= safe_object_get_size ((GCObject
*)cur
);
655 size
= SGEN_ALIGN_UP (ss
);
656 verify_scan_starts (cur
, cur
+ size
);
657 is_array_fill
= sgen_client_object_is_array_fill ((GCObject
*)cur
);
658 if (do_dump_nursery_content
) {
659 GCVTable vtable
= SGEN_LOAD_VTABLE ((GCObject
*)cur
);
660 if (cur
> hole_start
)
661 SGEN_LOG (0, "HOLE [%p %p %d]", hole_start
, cur
, (int)(cur
- hole_start
));
662 SGEN_LOG (0, "OBJ [%p %p %d %d %s.%s %d]", cur
, cur
+ size
, (int)size
, (int)ss
,
663 sgen_client_vtable_get_namespace (vtable
), sgen_client_vtable_get_name (vtable
),
666 if (nursery_canaries_enabled () && !is_array_fill
) {
667 CHECK_CANARY_FOR_OBJECT ((GCObject
*)cur
, TRUE
);
668 CANARIFY_SIZE (size
);
676 * Checks that no objects in the nursery are fowarded or pinned. This
677 * is a precondition to restarting the mutator while doing a
678 * concurrent collection. Note that we don't clear fragments because
679 * we depend on that having happened earlier.
682 sgen_debug_check_nursery_is_clean (void)
686 cur
= sgen_get_nursery_start ();
687 end
= sgen_get_nursery_end ();
693 cur
+= sizeof (void*);
697 g_assert (!object_is_forwarded (cur
));
698 g_assert (!object_is_pinned (cur
));
700 size
= SGEN_ALIGN_UP (safe_object_get_size ((GCObject
*)cur
));
701 verify_scan_starts (cur
, cur
+ size
);
707 static gboolean scan_object_for_specific_ref_precise
= TRUE
;
710 #define HANDLE_PTR(ptr,obj) do { \
711 if ((GCObject*)*(ptr) == key) { \
712 GCVTable vtable = SGEN_LOAD_VTABLE (*(ptr)); \
713 g_print ("found ref to %p in object %p (%s.%s) at offset %zd\n", \
714 key, (obj), sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), ((char*)(ptr) - (char*)(obj))); \
719 scan_object_for_specific_ref (GCObject
*obj
, GCObject
*key
)
723 if ((forwarded
= SGEN_OBJECT_IS_FORWARDED (obj
)))
726 if (scan_object_for_specific_ref_precise
) {
727 char *start
= (char*)obj
;
728 SgenDescriptor desc
= sgen_obj_get_descriptor_safe (obj
);
729 #include "sgen-scan-object.h"
731 mword
*words
= (mword
*)obj
;
732 size_t size
= safe_object_get_size (obj
);
734 for (i
= 0; i
< size
/ sizeof (mword
); ++i
) {
735 if (words
[i
] == (mword
)key
) {
736 GCVTable vtable
= SGEN_LOAD_VTABLE (obj
);
737 g_print ("found possible ref to %p in object %p (%s.%s) at offset %zd\n",
738 key
, obj
, sgen_client_vtable_get_namespace (vtable
), sgen_client_vtable_get_name (vtable
), i
* sizeof (mword
));
745 scan_object_for_specific_ref_callback (GCObject
*obj
, size_t size
, GCObject
*key
)
747 scan_object_for_specific_ref (obj
, key
);
751 check_root_obj_specific_ref (RootRecord
*root
, GCObject
*key
, GCObject
*obj
)
755 g_print ("found ref to %p in root record %p\n", key
, root
);
758 static GCObject
*check_key
= NULL
;
759 static RootRecord
*check_root
= NULL
;
762 check_root_obj_specific_ref_from_marker (GCObject
**obj
, void *gc_data
)
764 check_root_obj_specific_ref (check_root
, check_key
, *obj
);
768 scan_roots_for_specific_ref (GCObject
*key
, int root_type
)
774 SGEN_HASH_TABLE_FOREACH (&roots_hash
[root_type
], void **, start_root
, RootRecord
*, root
) {
775 SgenDescriptor desc
= root
->root_desc
;
779 switch (desc
& ROOT_DESC_TYPE_MASK
) {
780 case ROOT_DESC_BITMAP
:
781 desc
>>= ROOT_DESC_TYPE_SHIFT
;
784 check_root_obj_specific_ref (root
, key
, (GCObject
*)*start_root
);
789 case ROOT_DESC_COMPLEX
: {
790 gsize
*bitmap_data
= (gsize
*)sgen_get_complex_descriptor_bitmap (desc
);
791 int bwords
= (int) ((*bitmap_data
) - 1);
792 void **start_run
= start_root
;
794 while (bwords
-- > 0) {
795 gsize bmap
= *bitmap_data
++;
796 void **objptr
= start_run
;
799 check_root_obj_specific_ref (root
, key
, (GCObject
*)*objptr
);
803 start_run
+= GC_BITS_PER_WORD
;
807 case ROOT_DESC_USER
: {
808 SgenUserRootMarkFunc marker
= sgen_get_user_descriptor_func (desc
);
809 marker (start_root
, check_root_obj_specific_ref_from_marker
, NULL
);
812 case ROOT_DESC_RUN_LEN
:
813 g_assert_not_reached ();
815 g_assert_not_reached ();
817 } SGEN_HASH_TABLE_FOREACH_END
;
824 mono_gc_scan_for_specific_ref (GCObject
*key
, gboolean precise
)
829 scan_object_for_specific_ref_precise
= precise
;
831 sgen_scan_area_with_callback (nursery_section
->data
, nursery_section
->end_data
,
832 (IterateObjectCallbackFunc
)scan_object_for_specific_ref_callback
, key
, TRUE
, FALSE
);
834 major_collector
.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL
, (IterateObjectCallbackFunc
)scan_object_for_specific_ref_callback
, key
);
836 sgen_los_iterate_objects ((IterateObjectCallbackFunc
)scan_object_for_specific_ref_callback
, key
);
838 scan_roots_for_specific_ref (key
, ROOT_TYPE_NORMAL
);
839 scan_roots_for_specific_ref (key
, ROOT_TYPE_WBARRIER
);
841 SGEN_HASH_TABLE_FOREACH (&roots_hash
[ROOT_TYPE_PINNED
], void **, ptr
, RootRecord
*, root
) {
842 while (ptr
< (void**)root
->end_root
) {
843 check_root_obj_specific_ref (root
, (GCObject
*)*ptr
, key
);
846 } SGEN_HASH_TABLE_FOREACH_END
;
848 if (sgen_is_world_stopped ())
849 find_pinning_ref_from_thread ((char*)key
, sizeof (GCObject
));
852 #ifndef SGEN_WITHOUT_MONO
854 static MonoDomain
*check_domain
= NULL
;
857 check_obj_not_in_domain (MonoObject
**o
)
859 g_assert (((*o
))->vtable
->domain
!= check_domain
);
864 check_obj_not_in_domain_callback (GCObject
**o
, void *gc_data
)
866 g_assert ((*o
)->vtable
->domain
!= check_domain
);
870 sgen_scan_for_registered_roots_in_domain (MonoDomain
*domain
, int root_type
)
874 check_domain
= domain
;
875 SGEN_HASH_TABLE_FOREACH (&roots_hash
[root_type
], void **, start_root
, RootRecord
*, root
) {
876 SgenDescriptor desc
= root
->root_desc
;
878 /* The MonoDomain struct is allowed to hold
879 references to objects in its own domain. */
880 if (start_root
== (void**)domain
)
883 switch (desc
& ROOT_DESC_TYPE_MASK
) {
884 case ROOT_DESC_BITMAP
:
885 desc
>>= ROOT_DESC_TYPE_SHIFT
;
887 if ((desc
& 1) && *start_root
)
888 check_obj_not_in_domain ((MonoObject
**)*start_root
);
893 case ROOT_DESC_COMPLEX
: {
894 gsize
*bitmap_data
= (gsize
*)sgen_get_complex_descriptor_bitmap (desc
);
895 int bwords
= (int)((*bitmap_data
) - 1);
896 void **start_run
= start_root
;
898 while (bwords
-- > 0) {
899 gsize bmap
= *bitmap_data
++;
900 void **objptr
= start_run
;
902 if ((bmap
& 1) && *objptr
)
903 check_obj_not_in_domain ((MonoObject
**)*objptr
);
907 start_run
+= GC_BITS_PER_WORD
;
911 case ROOT_DESC_USER
: {
912 SgenUserRootMarkFunc marker
= sgen_get_user_descriptor_func (desc
);
913 marker (start_root
, check_obj_not_in_domain_callback
, NULL
);
916 case ROOT_DESC_RUN_LEN
:
917 g_assert_not_reached ();
919 g_assert_not_reached ();
921 } SGEN_HASH_TABLE_FOREACH_END
;
927 is_xdomain_ref_allowed (GCObject
**ptr
, GCObject
*obj
, MonoDomain
*domain
)
929 MonoObject
*o
= (MonoObject
*)(obj
);
930 MonoObject
*ref
= *ptr
;
931 size_t offset
= (char*)(ptr
) - (char*)o
;
933 if (o
->vtable
->klass
== mono_defaults
.thread_class
&& offset
== G_STRUCT_OFFSET (MonoThread
, internal_thread
))
935 if (o
->vtable
->klass
== mono_defaults
.internal_thread_class
&& offset
== G_STRUCT_OFFSET (MonoInternalThread
, current_appcontext
))
938 #ifndef DISABLE_REMOTING
939 if (mono_defaults
.real_proxy_class
->supertypes
&& mono_class_has_parent_fast (o
->vtable
->klass
, mono_defaults
.real_proxy_class
) &&
940 offset
== G_STRUCT_OFFSET (MonoRealProxy
, unwrapped_server
))
943 /* Thread.cached_culture_info */
944 if (!strcmp (ref
->vtable
->klass
->name_space
, "System.Globalization") &&
945 !strcmp (ref
->vtable
->klass
->name
, "CultureInfo") &&
946 !strcmp(o
->vtable
->klass
->name_space
, "System") &&
947 !strcmp(o
->vtable
->klass
->name
, "Object[]"))
950 * 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
951 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
952 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
953 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
954 * 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
955 * 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
956 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
957 * 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
958 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
960 if (!strcmp (ref
->vtable
->klass
->name_space
, "System") &&
961 !strcmp (ref
->vtable
->klass
->name
, "Byte[]") &&
962 !strcmp (o
->vtable
->klass
->name_space
, "System.IO") &&
963 !strcmp (o
->vtable
->klass
->name
, "MemoryStream"))
969 check_reference_for_xdomain (GCObject
**ptr
, GCObject
*obj
, MonoDomain
*domain
)
971 MonoObject
*ref
= *ptr
;
972 size_t offset
= (char*)(ptr
) - (char*)obj
;
974 MonoClassField
*field
;
977 if (!ref
|| ref
->vtable
->domain
== domain
)
979 if (is_xdomain_ref_allowed (ptr
, obj
, domain
))
983 for (klass
= obj
->vtable
->klass
; klass
; klass
= klass
->parent
) {
986 int fcount
= mono_class_get_field_count (klass
);
987 for (i
= 0; i
< fcount
; ++i
) {
988 if (klass
->fields
[i
].offset
== offset
) {
989 field
= &klass
->fields
[i
];
997 if (ref
->vtable
->klass
== mono_defaults
.string_class
) {
999 str
= mono_string_to_utf8_checked ((MonoString
*)ref
, &error
);
1000 mono_error_cleanup (&error
);
1003 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1004 obj
, obj
->vtable
->klass
->name_space
, obj
->vtable
->klass
->name
,
1005 offset
, field
? field
->name
: "",
1006 ref
, ref
->vtable
->klass
->name_space
, ref
->vtable
->klass
->name
, str
? str
: "");
1007 mono_gc_scan_for_specific_ref (obj
, TRUE
);
1013 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1016 scan_object_for_xdomain_refs (GCObject
*obj
, mword size
, void *data
)
1018 char *start
= (char*)obj
;
1019 MonoVTable
*vt
= SGEN_LOAD_VTABLE (obj
);
1020 MonoDomain
*domain
= vt
->domain
;
1021 SgenDescriptor desc
= sgen_vtable_get_descriptor (vt
);
1023 #include "sgen-scan-object.h"
1027 sgen_check_for_xdomain_refs (void)
1031 sgen_scan_area_with_callback (nursery_section
->data
, nursery_section
->end_data
,
1032 (IterateObjectCallbackFunc
)scan_object_for_xdomain_refs
, NULL
, FALSE
, TRUE
);
1034 major_collector
.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL
, (IterateObjectCallbackFunc
)scan_object_for_xdomain_refs
, NULL
);
1036 for (bigobj
= los_object_list
; bigobj
; bigobj
= bigobj
->next
)
1037 scan_object_for_xdomain_refs ((GCObject
*)bigobj
->data
, sgen_los_object_size (bigobj
), NULL
);
1042 /* If not null, dump the heap after each collection into this file */
1043 static FILE *heap_dump_file
= NULL
;
1046 sgen_dump_occupied (char *start
, char *end
, char *section_start
)
1048 fprintf (heap_dump_file
, "<occupied offset=\"%zd\" size=\"%zd\"/>\n", start
- section_start
, end
- start
);
1052 sgen_dump_section (GCMemSection
*section
, const char *type
)
1054 char *start
= section
->data
;
1055 char *end
= section
->data
+ section
->size
;
1056 char *occ_start
= NULL
;
1058 fprintf (heap_dump_file
, "<section type=\"%s\" size=\"%lu\">\n", type
, (unsigned long)section
->size
);
1060 while (start
< end
) {
1065 if (!*(void**)start
) {
1067 sgen_dump_occupied (occ_start
, start
, section
->data
);
1070 start
+= sizeof (void*); /* should be ALLOC_ALIGN, really */
1073 g_assert (start
< section
->next_data
);
1078 //vt = SGEN_LOAD_VTABLE (start);
1079 //class = vt->klass;
1081 size
= SGEN_ALIGN_UP (safe_object_get_size ((GCObject
*) start
));
1084 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
1085 start - section->data,
1086 vt->klass->name_space, vt->klass->name,
1093 sgen_dump_occupied (occ_start
, start
, section
->data
);
1095 fprintf (heap_dump_file
, "</section>\n");
1099 dump_object (GCObject
*obj
, gboolean dump_location
)
1101 #ifndef SGEN_WITHOUT_MONO
1102 static char class_name
[1024];
1104 MonoClass
*klass
= mono_object_class (obj
);
1108 * Python's XML parser is too stupid to parse angle brackets
1109 * in strings, so we just ignore them;
1112 while (klass
->name
[i
] && j
< sizeof (class_name
) - 1) {
1113 if (!strchr ("<>\"", klass
->name
[i
]))
1114 class_name
[j
++] = klass
->name
[i
];
1117 g_assert (j
< sizeof (class_name
));
1120 fprintf (heap_dump_file
, "<object class=\"%s.%s\" size=\"%zd\"",
1121 klass
->name_space
, class_name
,
1122 safe_object_get_size (obj
));
1123 if (dump_location
) {
1124 const char *location
;
1125 if (sgen_ptr_in_nursery (obj
))
1126 location
= "nursery";
1127 else if (safe_object_get_size (obj
) <= SGEN_MAX_SMALL_OBJ_SIZE
)
1131 fprintf (heap_dump_file
, " location=\"%s\"", location
);
1133 fprintf (heap_dump_file
, "/>\n");
1138 sgen_debug_enable_heap_dump (const char *filename
)
1140 heap_dump_file
= fopen (filename
, "w");
1141 if (heap_dump_file
) {
1142 fprintf (heap_dump_file
, "<sgen-dump>\n");
1143 sgen_pin_stats_enable ();
1148 sgen_debug_dump_heap (const char *type
, int num
, const char *reason
)
1150 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=\"%zu\"/>\n", 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=\"%zu\"/>\n", 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 (nursery_section
, "nursery");
1177 major_collector
.dump_heap (heap_dump_file
);
1179 fprintf (heap_dump_file
, "<los>\n");
1180 for (bigobj
= los_object_list
; bigobj
; bigobj
= bigobj
->next
)
1181 dump_object ((GCObject
*)bigobj
->data
, FALSE
);
1182 fprintf (heap_dump_file
, "</los>\n");
1184 fprintf (heap_dump_file
, "</collection>\n");
1187 static GCObject
*found_obj
;
1190 find_object_for_ptr_callback (GCObject
*obj
, size_t size
, void *user_data
)
1192 char *ptr
= (char *)user_data
;
1194 if (ptr
>= (char*)obj
&& ptr
< (char*)obj
+ size
) {
1195 g_assert (!found_obj
);
1200 /* for use in the debugger */
1202 sgen_find_object_for_ptr (char *ptr
)
1204 if (ptr
>= nursery_section
->data
&& ptr
< nursery_section
->end_data
) {
1206 sgen_scan_area_with_callback (nursery_section
->data
, nursery_section
->end_data
,
1207 find_object_for_ptr_callback
, ptr
, TRUE
, FALSE
);
1213 sgen_los_iterate_objects (find_object_for_ptr_callback
, ptr
);
1218 * Very inefficient, but this is debugging code, supposed to
1219 * be called from gdb, so we don't care.
1222 major_collector
.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL
, find_object_for_ptr_callback
, ptr
);
1226 #endif /*HAVE_SGEN_GC*/