Fix roslyn install with AOT disabled.
[mono-project.git] / mono / sgen / sgen-debug.c
blob195894f8cbf1eecfa6997df325edc00fd9caf643
1 /*
2 * sgen-debug.c: Collector debugging
4 * Author:
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.
16 #include "config.h"
17 #ifdef HAVE_SGEN_GC
19 #include <string.h>
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 [] = {
44 "INVALID",
45 "run length",
46 "bitmap",
47 "small pointer-free",
48 "complex",
49 "vector",
50 "complex arrray",
51 "complex pointer-free"
54 static char* describe_nursery_ptr (char *ptr, gboolean need_setup);
56 static void
57 describe_pointer (char *ptr, gboolean need_setup)
59 GCVTable vtable;
60 SgenDescriptor desc;
61 int type;
62 char *start;
63 char *forwarded;
64 mword size;
66 restart:
67 if (sgen_ptr_in_nursery (ptr)) {
68 start = describe_nursery_ptr (ptr, need_setup);
69 if (!start)
70 return;
71 ptr = start;
72 vtable = LOAD_VTABLE ((GCObject*)ptr);
73 } else {
74 if (sgen_ptr_is_in_los (ptr, &start)) {
75 if (ptr == start)
76 printf ("Pointer is the start of object %p in LOS space.\n", start);
77 else
78 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
79 ptr = 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)) {
83 if (ptr == start)
84 printf ("Pointer is the start of object %p in oldspace.\n", start);
85 else if (start)
86 printf ("Pointer is at offset 0x%x of object %p in oldspace.\n", (int)(ptr - start), start);
87 else
88 printf ("Pointer inside oldspace.\n");
89 if (start)
90 ptr = start;
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);
96 } else {
97 printf ("Pointer unknown.\n");
98 return;
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);
107 ptr = forwarded;
108 goto restart;
111 printf ("VTable: %p\n", vtable);
112 if (vtable == NULL) {
113 printf ("VTable is invalid (empty).\n");
114 goto invalid_vtable;
116 if (sgen_ptr_in_nursery (vtable)) {
117 printf ("VTable is invalid (points inside nursery).\n");
118 goto invalid_vtable;
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);
131 invalid_vtable:
133 sgen_client_describe_invalid_pointer ((GCObject *) ptr);
136 void
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.
150 #undef HANDLE_PTR
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); \
158 if (!is_pinned) \
159 missing_remsets = TRUE; \
162 } while (0)
165 * Check that each object reference which points into the nursery can
166 * be found in the remembered sets.
168 static void
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.
184 void
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.");
200 if (missing_remsets)
201 binary_protocol_flush_buffers (TRUE);
202 if (!binary_protocol_is_enabled ())
203 g_assert (!missing_remsets);
206 static gboolean
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);
211 } else {
212 return sgen_get_major_collector ()->is_object_live (obj);
216 #undef HANDLE_PTR
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; \
226 } while (0)
228 static void
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);
235 guint8 *cards;
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))
239 return;
241 if (in_los)
242 cards = sgen_los_header_for_object (obj)->cardtable_mod_union;
243 else
244 cards = sgen_get_major_collector ()->get_cardtable_mod_union_for_reference (start);
246 #include "sgen-scan-object.h"
249 void
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);
262 #undef HANDLE_PTR
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)); \
266 } while (0)
268 static void
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"
277 void
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 */
285 #undef HANDLE_PTR
286 #define HANDLE_PTR(ptr,obj) do { \
287 if (*(ptr)) { \
288 g_assert (sgen_client_vtable_get_namespace (SGEN_LOAD_VTABLE_UNCHECKED (*(ptr)))); \
290 } while (0)
293 * check_object:
295 * Perform consistency check on an object. Currently we only check that the
296 * reference fields are valid.
298 void
299 check_object (GCObject *obj)
301 char *start = (char*)obj;
302 SgenDescriptor desc;
304 if (!start)
305 return;
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;
317 static void
318 setup_mono_sgen_scan_area_with_callback (GCObject *object, size_t size, void *data)
320 valid_nursery_objects [valid_nursery_object_count++] = object;
323 static void
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);
332 static gboolean
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])
339 return TRUE;
341 if (object < (char*)valid_nursery_objects [middle])
342 last = middle;
343 else
344 first = middle + 1;
346 g_assert (first == last);
347 return FALSE;
350 static void
351 iterate_valid_nursery_objects (IterateObjectCallbackFunc callback, void *data)
353 int i;
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);
360 static char*
361 describe_nursery_ptr (char *ptr, gboolean need_setup)
363 int i;
365 if (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)
370 break;
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)");
375 return NULL;
376 } else {
377 GCObject *obj = valid_nursery_objects [i];
378 if ((char*)obj == ptr)
379 SGEN_LOG (0, "nursery-ptr %p", obj);
380 else
381 SGEN_LOG (0, "nursery-ptr %p (interior-ptr offset %zd)", obj, ptr - (char*)obj);
382 return (char*)obj;
386 static gboolean
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))
393 return TRUE;
395 if (major_collector.is_valid_object (object))
396 return TRUE;
397 return FALSE;
400 static void
401 bad_pointer_spew (char *obj, char **slot)
403 char *ptr = *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,
407 (char*)slot - obj,
408 obj, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
409 describe_pointer (ptr, FALSE);
410 broken_heap = TRUE;
413 static void
414 missing_remset_spew (char *obj, char **slot)
416 char *ptr = *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));
423 broken_heap = TRUE;
427 FIXME Flag missing remsets due to pinning as non fatal
429 #undef HANDLE_PTR
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); \
439 } while (0)
441 static void
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"
452 FIXME:
453 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
454 depend on OP_DUMMY_USE.
456 void
457 sgen_check_whole_heap (gboolean allow_missing_pinned)
459 setup_valid_nursery_objects ();
461 broken_heap = FALSE;
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);
469 static gboolean
470 ptr_in_heap (char *object)
472 if (sgen_ptr_in_nursery (object))
473 return TRUE;
475 if (sgen_los_is_valid_object (object))
476 return TRUE;
478 if (major_collector.is_valid_object (object))
479 return TRUE;
480 return FALSE;
484 * sgen_check_objref:
485 * Do consistency checks on the object reference OBJ. Assert on failure.
487 void
488 sgen_check_objref (char *obj)
490 g_assert (ptr_in_heap (obj));
493 static void
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)
503 continue;
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);
507 start++;
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));
516 } FOREACH_THREAD_END
517 #endif
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)
526 char **start;
527 RootRecord *root;
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);
537 start++;
540 } SGEN_HASH_TABLE_FOREACH_END;
542 find_pinning_ref_from_thread (obj, size);
545 #undef HANDLE_PTR
546 #define HANDLE_PTR(ptr,obj) do { \
547 char* __target = *(char**)ptr; \
548 if (__target) { \
549 if (sgen_ptr_in_nursery (__target)) { \
550 g_assert (!SGEN_OBJECT_IS_FORWARDED (__target)); \
551 } else { \
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)); \
555 else \
556 g_assert (sgen_los_object_is_pinned ((GCObject*)__target)); \
559 } while (0)
561 static void
562 check_marked_callback (GCObject *obj, size_t size, void *dummy)
564 char *start = (char*)obj;
565 gboolean flag = (gboolean) (size_t) dummy;
566 SgenDescriptor desc;
568 if (sgen_ptr_in_nursery (start)) {
569 if (flag)
570 SGEN_ASSERT (0, SGEN_OBJECT_IS_PINNED (obj), "All objects remaining in the nursery must be pinned");
571 } else if (flag) {
572 if (!sgen_los_object_is_pinned (obj))
573 return;
574 } else {
575 if (!major_collector.is_object_live (obj))
576 return;
579 desc = sgen_obj_get_descriptor_safe (obj);
581 #include "sgen-scan-object.h"
584 void
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);
594 static void
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));
600 if (pinned)
601 g_assert (SGEN_OBJECT_IS_PINNED (obj));
602 else
603 g_assert (!SGEN_OBJECT_IS_PINNED (obj));
606 void
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);
614 static void
615 verify_scan_starts (char *start, char *end)
617 size_t i;
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);
626 void
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 ();
640 while (cur < end) {
641 size_t ss, size;
642 gboolean is_array_fill;
644 if (!*(void**)cur) {
645 cur += sizeof (void*);
646 continue;
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),
664 is_array_fill);
666 if (nursery_canaries_enabled () && !is_array_fill) {
667 CHECK_CANARY_FOR_OBJECT ((GCObject*)cur, TRUE);
668 CANARIFY_SIZE (size);
670 cur += size;
671 hole_start = cur;
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.
681 void
682 sgen_debug_check_nursery_is_clean (void)
684 char *end, *cur;
686 cur = sgen_get_nursery_start ();
687 end = sgen_get_nursery_end ();
689 while (cur < end) {
690 size_t size;
692 if (!*(void**)cur) {
693 cur += sizeof (void*);
694 continue;
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);
703 cur += size;
707 static gboolean scan_object_for_specific_ref_precise = TRUE;
709 #undef HANDLE_PTR
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))); \
716 } while (0)
718 static void
719 scan_object_for_specific_ref (GCObject *obj, GCObject *key)
721 GCObject *forwarded;
723 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (obj)))
724 obj = forwarded;
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"
730 } else {
731 mword *words = (mword*)obj;
732 size_t size = safe_object_get_size (obj);
733 int i;
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));
744 static void
745 scan_object_for_specific_ref_callback (GCObject *obj, size_t size, GCObject *key)
747 scan_object_for_specific_ref (obj, key);
750 static void
751 check_root_obj_specific_ref (RootRecord *root, GCObject *key, GCObject *obj)
753 if (key != obj)
754 return;
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;
761 static void
762 check_root_obj_specific_ref_from_marker (GCObject **obj, void *gc_data)
764 check_root_obj_specific_ref (check_root, check_key, *obj);
767 static void
768 scan_roots_for_specific_ref (GCObject *key, int root_type)
770 void **start_root;
771 RootRecord *root;
772 check_key = key;
774 SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], void **, start_root, RootRecord *, root) {
775 SgenDescriptor desc = root->root_desc;
777 check_root = root;
779 switch (desc & ROOT_DESC_TYPE_MASK) {
780 case ROOT_DESC_BITMAP:
781 desc >>= ROOT_DESC_TYPE_SHIFT;
782 while (desc) {
783 if (desc & 1)
784 check_root_obj_specific_ref (root, key, (GCObject *)*start_root);
785 desc >>= 1;
786 start_root++;
788 return;
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;
793 bitmap_data++;
794 while (bwords-- > 0) {
795 gsize bmap = *bitmap_data++;
796 void **objptr = start_run;
797 while (bmap) {
798 if (bmap & 1)
799 check_root_obj_specific_ref (root, key, (GCObject *)*objptr);
800 bmap >>= 1;
801 ++objptr;
803 start_run += GC_BITS_PER_WORD;
805 break;
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);
810 break;
812 case ROOT_DESC_RUN_LEN:
813 g_assert_not_reached ();
814 default:
815 g_assert_not_reached ();
817 } SGEN_HASH_TABLE_FOREACH_END;
819 check_key = NULL;
820 check_root = NULL;
823 void
824 mono_gc_scan_for_specific_ref (GCObject *key, gboolean precise)
826 void **ptr;
827 RootRecord *root;
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);
844 ++ptr;
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;
856 static void
857 check_obj_not_in_domain (MonoObject **o)
859 g_assert (((*o))->vtable->domain != check_domain);
863 static void
864 check_obj_not_in_domain_callback (GCObject **o, void *gc_data)
866 g_assert ((*o)->vtable->domain != check_domain);
869 void
870 sgen_scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
872 void **start_root;
873 RootRecord *root;
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)
881 continue;
883 switch (desc & ROOT_DESC_TYPE_MASK) {
884 case ROOT_DESC_BITMAP:
885 desc >>= ROOT_DESC_TYPE_SHIFT;
886 while (desc) {
887 if ((desc & 1) && *start_root)
888 check_obj_not_in_domain ((MonoObject **)*start_root);
889 desc >>= 1;
890 start_root++;
892 break;
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;
897 bitmap_data++;
898 while (bwords-- > 0) {
899 gsize bmap = *bitmap_data++;
900 void **objptr = start_run;
901 while (bmap) {
902 if ((bmap & 1) && *objptr)
903 check_obj_not_in_domain ((MonoObject **)*objptr);
904 bmap >>= 1;
905 ++objptr;
907 start_run += GC_BITS_PER_WORD;
909 break;
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);
914 break;
916 case ROOT_DESC_RUN_LEN:
917 g_assert_not_reached ();
918 default:
919 g_assert_not_reached ();
921 } SGEN_HASH_TABLE_FOREACH_END;
923 check_domain = NULL;
926 static gboolean
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))
934 return TRUE;
935 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
936 return TRUE;
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))
941 return TRUE;
942 #endif
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[]"))
948 return TRUE;
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"))
964 return TRUE;
965 return FALSE;
968 static void
969 check_reference_for_xdomain (GCObject **ptr, GCObject *obj, MonoDomain *domain)
971 MonoObject *ref = *ptr;
972 size_t offset = (char*)(ptr) - (char*)obj;
973 MonoClass *klass;
974 MonoClassField *field;
975 char *str;
977 if (!ref || ref->vtable->domain == domain)
978 return;
979 if (is_xdomain_ref_allowed (ptr, obj, domain))
980 return;
982 field = NULL;
983 for (klass = obj->vtable->klass; klass; klass = klass->parent) {
984 int i;
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];
990 break;
993 if (field)
994 break;
997 if (ref->vtable->klass == mono_defaults.string_class) {
998 MonoError error;
999 str = mono_string_to_utf8_checked ((MonoString*)ref, &error);
1000 mono_error_cleanup (&error);
1001 } else
1002 str = NULL;
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);
1008 if (str)
1009 g_free (str);
1012 #undef HANDLE_PTR
1013 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1015 static void
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"
1026 void
1027 sgen_check_for_xdomain_refs (void)
1029 LOSObject *bigobj;
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);
1040 #endif
1042 /* If not null, dump the heap after each collection into this file */
1043 static FILE *heap_dump_file = NULL;
1045 void
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);
1051 void
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) {
1061 guint size;
1062 //GCVTable vt;
1063 //MonoClass *class;
1065 if (!*(void**)start) {
1066 if (occ_start) {
1067 sgen_dump_occupied (occ_start, start, section->data);
1068 occ_start = NULL;
1070 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1071 continue;
1073 g_assert (start < section->next_data);
1075 if (!occ_start)
1076 occ_start = start;
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,
1087 size);
1090 start += size;
1092 if (occ_start)
1093 sgen_dump_occupied (occ_start, start, section->data);
1095 fprintf (heap_dump_file, "</section>\n");
1098 static void
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);
1105 int i, j;
1108 * Python's XML parser is too stupid to parse angle brackets
1109 * in strings, so we just ignore them;
1111 i = j = 0;
1112 while (klass->name [i] && j < sizeof (class_name) - 1) {
1113 if (!strchr ("<>\"", klass->name [i]))
1114 class_name [j++] = klass->name [i];
1115 ++i;
1117 g_assert (j < sizeof (class_name));
1118 class_name [j] = 0;
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)
1128 location = "major";
1129 else
1130 location = "LOS";
1131 fprintf (heap_dump_file, " location=\"%s\"", location);
1133 fprintf (heap_dump_file, "/>\n");
1134 #endif
1137 void
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 ();
1147 void
1148 sgen_debug_dump_heap (const char *type, int num, const char *reason)
1150 SgenPointerQueue *pinned_objects;
1151 LOSObject *bigobj;
1152 int i;
1154 if (!heap_dump_file)
1155 return;
1157 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
1158 if (reason)
1159 fprintf (heap_dump_file, " reason=\"%s\"", reason);
1160 fprintf (heap_dump_file, ">\n");
1161 #ifndef SGEN_WITHOUT_MONO
1162 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
1163 #endif
1164 sgen_dump_internal_mem_usage (heap_dump_file);
1165 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%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;
1189 static void
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);
1196 found_obj = obj;
1200 /* for use in the debugger */
1201 GCObject*
1202 sgen_find_object_for_ptr (char *ptr)
1204 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
1205 found_obj = NULL;
1206 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1207 find_object_for_ptr_callback, ptr, TRUE, FALSE);
1208 if (found_obj)
1209 return found_obj;
1212 found_obj = NULL;
1213 sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
1214 if (found_obj)
1215 return found_obj;
1218 * Very inefficient, but this is debugging code, supposed to
1219 * be called from gdb, so we don't care.
1221 found_obj = NULL;
1222 major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, find_object_for_ptr_callback, ptr);
1223 return found_obj;
1226 #endif /*HAVE_SGEN_GC*/