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.
30 #include "metadata/sgen-gc.h"
31 #include "metadata/sgen-cardtable.h"
32 #include "metadata/sgen-protocol.h"
33 #include "metadata/sgen-memory-governor.h"
34 #include "metadata/sgen-pinning.h"
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 describe_ptr (char *ptr);
43 void check_object (char *start);
46 * ######################################################################
47 * ######## Collector debugging
48 * ######################################################################
51 const char*descriptor_types [] = {
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 = (MonoVTable*)LOAD_VTABLE (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 = (MonoVTable*)LOAD_VTABLE (ptr);
90 } else if (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 = major_collector.describe_pointer (ptr);
100 } else if (major_collector.obj_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 = (MonoVTable*)LOAD_VTABLE (ptr);
105 printf ("Pointer unknown.\n");
110 if (object_is_pinned (ptr))
111 printf ("Object is pinned.\n");
113 if ((forwarded = 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\n", vtable->klass->name);
130 desc = ((GCVTable*)vtable)->desc;
131 printf ("Descriptor: %lx\n", (long)desc);
134 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
136 size = sgen_safe_object_get_size ((MonoObject*)ptr);
137 printf ("Size: %d\n", (int)size);
140 sgen_bridge_describe_pointer ((MonoObject*)ptr);
144 describe_ptr (char *ptr)
146 describe_pointer (ptr, TRUE);
149 static gboolean missing_remsets;
152 * We let a missing remset slide if the target object is pinned,
153 * because the store might have happened but the remset not yet added,
154 * but in that case the target must be pinned. We might theoretically
155 * miss some missing remsets this way, but it's very unlikely.
158 #define HANDLE_PTR(ptr,obj) do { \
159 if (*(ptr) && sgen_ptr_in_nursery ((char*)*(ptr))) { \
160 if (!sgen_get_remset ()->find_address ((char*)(ptr))) { \
161 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), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
162 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
163 if (!object_is_pinned (*(ptr))) \
164 missing_remsets = TRUE; \
170 * Check that each object reference which points into the nursery can
171 * be found in the remembered sets.
174 check_consistency_callback (char *start, size_t size, void *dummy)
176 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
177 SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, vt->klass->name);
179 #include "sgen-scan-object.h"
183 * Perform consistency check of the heap.
185 * Assumes the world is stopped.
188 sgen_check_consistency (void)
190 // Need to add more checks
192 missing_remsets = FALSE;
194 SGEN_LOG (1, "Begin heap consistency check...");
196 // Check that oldspace->newspace pointers are registered with the collector
197 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
199 sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
201 SGEN_LOG (1, "Heap consistency check done.");
203 if (!binary_protocol_is_enabled ())
204 g_assert (!missing_remsets);
208 is_major_or_los_object_marked (char *obj)
210 if (sgen_safe_object_get_size ((MonoObject*)obj) > SGEN_MAX_SMALL_OBJ_SIZE) {
211 return sgen_los_object_is_pinned (obj);
213 return sgen_get_major_collector ()->is_object_live (obj);
218 #define HANDLE_PTR(ptr,obj) do { \
219 if (*(ptr) && !sgen_ptr_in_nursery ((char*)*(ptr)) && !is_major_or_los_object_marked ((char*)*(ptr))) { \
220 if (!sgen_get_remset ()->find_address_with_cards (start, cards, (char*)(ptr))) { \
221 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), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
222 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
228 check_mod_union_callback (char *start, size_t size, void *dummy)
230 gboolean in_los = (gboolean) (size_t) dummy;
231 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
233 SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, vt->klass->name);
235 if (!is_major_or_los_object_marked (start))
239 cards = sgen_los_header_for_object (start)->cardtable_mod_union;
241 cards = sgen_get_major_collector ()->get_cardtable_mod_union_for_object (start);
243 SGEN_ASSERT (0, cards, "we must have mod union for marked major objects");
245 #include "sgen-scan-object.h"
249 sgen_check_mod_union_consistency (void)
251 missing_remsets = FALSE;
253 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_mod_union_callback, (void*)FALSE);
255 sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_mod_union_callback, (void*)TRUE);
257 if (!binary_protocol_is_enabled ())
258 g_assert (!missing_remsets);
262 #define HANDLE_PTR(ptr,obj) do { \
263 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
264 g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj)); \
268 check_major_refs_callback (char *start, size_t size, void *dummy)
270 #include "sgen-scan-object.h"
274 sgen_check_major_refs (void)
276 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
277 sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
280 /* Check that the reference is valid */
282 #define HANDLE_PTR(ptr,obj) do { \
284 g_assert (sgen_safe_name (*(ptr)) != NULL); \
291 * Perform consistency check on an object. Currently we only check that the
292 * reference fields are valid.
295 check_object (char *start)
300 #include "sgen-scan-object.h"
304 static char **valid_nursery_objects;
305 static int valid_nursery_object_count;
306 static gboolean broken_heap;
309 setup_mono_sgen_scan_area_with_callback (char *object, size_t size, void *data)
311 valid_nursery_objects [valid_nursery_object_count++] = object;
315 setup_valid_nursery_objects (void)
317 if (!valid_nursery_objects)
318 valid_nursery_objects = sgen_alloc_os_memory (DEFAULT_NURSERY_SIZE, SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging data");
319 valid_nursery_object_count = 0;
320 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE);
324 find_object_in_nursery_dump (char *object)
326 int first = 0, last = valid_nursery_object_count;
327 while (first < last) {
328 int middle = first + ((last - first) >> 1);
329 if (object == valid_nursery_objects [middle])
332 if (object < valid_nursery_objects [middle])
337 g_assert (first == last);
342 describe_nursery_ptr (char *ptr, gboolean need_setup)
347 setup_valid_nursery_objects ();
349 for (i = 0; i < valid_nursery_object_count; ++i) {
350 if (valid_nursery_objects [i] >= ptr)
354 if (i >= valid_nursery_object_count || valid_nursery_objects [i] + safe_object_get_size ((MonoObject *)valid_nursery_objects [i]) < ptr) {
355 SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)\n");
358 char *obj = valid_nursery_objects [i];
360 SGEN_LOG (0, "nursery-ptr\n");
362 SGEN_LOG (0, "nursery-ptr (interior-ptr offset %td)\n", ptr - obj);
368 is_valid_object_pointer (char *object)
370 if (sgen_ptr_in_nursery (object))
371 return find_object_in_nursery_dump (object);
373 if (sgen_los_is_valid_object (object))
376 if (major_collector.is_valid_object (object))
382 bad_pointer_spew (char *obj, char **slot)
385 MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
387 SGEN_LOG (0, "Invalid object pointer %p at offset %td in object %p (%s.%s):", ptr,
389 obj, vtable->klass->name_space, vtable->klass->name);
390 describe_pointer (ptr, FALSE);
395 missing_remset_spew (char *obj, char **slot)
398 MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
400 SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.",
401 ptr, (char*)slot - obj, obj,
402 vtable->klass->name_space, vtable->klass->name);
408 FIXME Flag missing remsets due to pinning as non fatal
411 #define HANDLE_PTR(ptr,obj) do { \
412 if (*(char**)ptr) { \
413 if (!is_valid_object_pointer (*(char**)ptr)) { \
414 bad_pointer_spew ((char*)obj, (char**)ptr); \
415 } else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) { \
416 if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup ((char*)*(ptr)) && (!allow_missing_pinned || !SGEN_OBJECT_IS_PINNED ((char*)*(ptr)))) \
417 missing_remset_spew ((char*)obj, (char**)ptr); \
423 verify_object_pointers_callback (char *start, size_t size, void *data)
425 gboolean allow_missing_pinned = (gboolean) (size_t) data;
427 #include "sgen-scan-object.h"
432 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
433 depend on OP_DUMMY_USE.
436 sgen_check_whole_heap (gboolean allow_missing_pinned)
438 setup_valid_nursery_objects ();
441 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned, FALSE);
442 major_collector.iterate_objects (TRUE, TRUE, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
443 sgen_los_iterate_objects (verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
445 g_assert (!broken_heap);
449 ptr_in_heap (char *object)
451 if (sgen_ptr_in_nursery (object))
454 if (sgen_los_is_valid_object (object))
457 if (major_collector.is_valid_object (object))
464 * Do consistency checks on the object reference OBJ. Assert on failure.
467 sgen_check_objref (char *obj)
469 g_assert (ptr_in_heap (obj));
473 find_pinning_ref_from_thread (char *obj, size_t size)
476 SgenThreadInfo *info;
477 char *endobj = obj + size;
479 FOREACH_THREAD (info) {
480 char **start = (char**)info->stack_start;
483 while (start < (char**)info->stack_end) {
484 if (*start >= obj && *start < endobj) {
485 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->stack_start, info->stack_end);
490 for (j = 0; j < ARCH_NUM_REGS; ++j) {
492 mword w = ((mword*)&info->ctx) [j];
494 mword w = (mword)&info->regs [j];
497 if (w >= (mword)obj && w < (mword)obj + size)
498 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));
504 * Debugging function: find in the conservative roots where @obj is being pinned.
506 static G_GNUC_UNUSED void
507 find_pinning_reference (char *obj, size_t size)
511 char *endobj = obj + size;
513 SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_NORMAL], start, root) {
514 /* if desc is non-null it has precise info */
515 if (!root->root_desc) {
516 while (start < (char**)root->end_root) {
517 if (*start >= obj && *start < endobj) {
518 SGEN_LOG (0, "Object %p referenced in pinned roots %p-%p\n", obj, start, root->end_root);
523 } SGEN_HASH_TABLE_FOREACH_END;
525 find_pinning_ref_from_thread (obj, size);
529 #define HANDLE_PTR(ptr,obj) do { \
530 char* __target = *(char**)ptr; \
532 g_assert (is_valid_object_pointer (__target)); \
533 if (sgen_ptr_in_nursery (__target)) { \
534 g_assert (SGEN_OBJECT_IS_PINNED (__target)); \
535 } else if (sgen_los_is_valid_object (__target)) { \
536 g_assert (sgen_los_object_is_pinned (__target)); \
537 } else if (major_collector.is_valid_object (__target)) { \
538 g_assert (major_collector.is_object_live (__target)); \
540 g_assert_not_reached (); \
546 check_marked_callback (char *start, size_t size, void *dummy)
548 gboolean is_los = (gboolean) (size_t) dummy;
551 if (!sgen_los_object_is_pinned (start))
554 if (!major_collector.is_object_live (start))
558 #include "sgen-scan-object.h"
562 sgen_check_major_heap_marked (void)
564 setup_valid_nursery_objects ();
566 major_collector.iterate_objects (TRUE, TRUE, check_marked_callback, (void*)FALSE);
567 sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
571 check_nursery_objects_pinned_callback (char *obj, size_t size, void *data /* ScanCopyContext *ctx */)
573 gboolean pinned = (gboolean) (size_t) data;
575 g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
577 g_assert (SGEN_OBJECT_IS_PINNED (obj));
579 g_assert (!SGEN_OBJECT_IS_PINNED (obj));
583 sgen_check_nursery_objects_pinned (gboolean pinned)
585 sgen_clear_nursery_fragments ();
586 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
587 (IterateObjectCallbackFunc)check_nursery_objects_pinned_callback, (void*) (size_t) pinned /* (void*)&ctx */, FALSE);
590 #endif /*HAVE_SGEN_GC*/