e713a81647959d6aac28940a7bbbdf80750777ee
[mono.git] / mono / sgen / sgen-debug.c
1 /*
2  * sgen-debug.c: Collector debugging
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *  Rodrigo Kumpera (kumpera@gmail.com)
7  *
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
12  *
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;
16  *
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.
21  *
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.
25  */
26
27 #include "config.h"
28 #ifdef HAVE_SGEN_GC
29
30 #include <string.h>
31
32 #include "mono/sgen/sgen-gc.h"
33 #include "mono/sgen/sgen-cardtable.h"
34 #include "mono/sgen/sgen-protocol.h"
35 #include "mono/sgen/sgen-memory-governor.h"
36 #include "mono/sgen/sgen-pinning.h"
37 #include "mono/sgen/sgen-client.h"
38 #ifndef SGEN_WITHOUT_MONO
39 #include "mono/metadata/sgen-bridge-internal.h"
40 #endif
41
42 #define LOAD_VTABLE     SGEN_LOAD_VTABLE
43
44 #define object_is_forwarded     SGEN_OBJECT_IS_FORWARDED
45 #define object_is_pinned        SGEN_OBJECT_IS_PINNED
46 #define safe_object_get_size    sgen_safe_object_get_size
47
48 void describe_ptr (char *ptr);
49 void check_object (char *start);
50
51 /*
52  * ######################################################################
53  * ########  Collector debugging
54  * ######################################################################
55  */
56
57 static const char*descriptor_types [] = {
58         "INVALID",
59         "run length",
60         "bitmap",
61         "small pointer-free",
62         "complex",
63         "vector",
64         "complex arrray",
65         "complex pointer-free"
66 };
67
68 static char* describe_nursery_ptr (char *ptr, gboolean need_setup);
69
70 static void
71 describe_pointer (char *ptr, gboolean need_setup)
72 {
73         GCVTable *vtable;
74         mword desc;
75         int type;
76         char *start;
77         char *forwarded;
78         mword size;
79
80  restart:
81         if (sgen_ptr_in_nursery (ptr)) {
82                 start = describe_nursery_ptr (ptr, need_setup);
83                 if (!start)
84                         return;
85                 ptr = start;
86                 vtable = (GCVTable*)LOAD_VTABLE (ptr);
87         } else {
88                 if (sgen_ptr_is_in_los (ptr, &start)) {
89                         if (ptr == start)
90                                 printf ("Pointer is the start of object %p in LOS space.\n", start);
91                         else
92                                 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
93                         ptr = start;
94                         mono_sgen_los_describe_pointer (ptr);
95                         vtable = (GCVTable*)LOAD_VTABLE (ptr);
96                 } else if (major_collector.ptr_is_in_non_pinned_space (ptr, &start)) {
97                         if (ptr == start)
98                                 printf ("Pointer is the start of object %p in oldspace.\n", start);
99                         else if (start)
100                                 printf ("Pointer is at offset 0x%x of object %p in oldspace.\n", (int)(ptr - start), start);
101                         else
102                                 printf ("Pointer inside oldspace.\n");
103                         if (start)
104                                 ptr = start;
105                         vtable = (GCVTable*)major_collector.describe_pointer (ptr);
106                 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
107                         // FIXME: Handle pointers to the inside of objects
108                         printf ("Pointer is inside a pinned chunk.\n");
109                         vtable = (GCVTable*)LOAD_VTABLE (ptr);
110                 } else {
111                         printf ("Pointer unknown.\n");
112                         return;
113                 }
114         }
115
116         if (object_is_pinned (ptr))
117                 printf ("Object is pinned.\n");
118
119         if ((forwarded = object_is_forwarded (ptr))) {
120                 printf ("Object is forwarded to %p:\n", forwarded);
121                 ptr = forwarded;
122                 goto restart;
123         }
124
125         printf ("VTable: %p\n", vtable);
126         if (vtable == NULL) {
127                 printf ("VTable is invalid (empty).\n");
128                 goto bridge;
129         }
130         if (sgen_ptr_in_nursery (vtable)) {
131                 printf ("VTable is invalid (points inside nursery).\n");
132                 goto bridge;
133         }
134         printf ("Class: %s.%s\n", sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
135
136         desc = sgen_vtable_get_descriptor ((GCVTable*)vtable);
137         printf ("Descriptor: %lx\n", (long)desc);
138
139         type = desc & DESC_TYPE_MASK;
140         printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
141
142         size = sgen_safe_object_get_size ((GCObject*)ptr);
143         printf ("Size: %d\n", (int)size);
144
145  bridge:
146         ;
147 #ifndef SGEN_WITHOUT_MONO
148         sgen_bridge_describe_pointer ((GCObject*)ptr);
149 #endif
150 }
151
152 void
153 describe_ptr (char *ptr)
154 {
155         describe_pointer (ptr, TRUE);
156 }
157
158 static gboolean missing_remsets;
159
160 /*
161  * We let a missing remset slide if the target object is pinned,
162  * because the store might have happened but the remset not yet added,
163  * but in that case the target must be pinned.  We might theoretically
164  * miss some missing remsets this way, but it's very unlikely.
165  */
166 #undef HANDLE_PTR
167 #define HANDLE_PTR(ptr,obj)     do {    \
168         if (*(ptr) && sgen_ptr_in_nursery ((char*)*(ptr))) { \
169                 if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup (*(ptr))) { \
170                         GCVTable *__vt = SGEN_LOAD_VTABLE ((obj));      \
171                         SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), sgen_client_vtable_get_namespace (__vt), sgen_client_vtable_get_name (__vt)); \
172                         binary_protocol_missing_remset ((obj), __vt, (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
173                         if (!object_is_pinned (*(ptr)))                                                         \
174                                 missing_remsets = TRUE;                                                                 \
175                 }                                                                                                                               \
176         }                                                                                                                                       \
177         } while (0)
178
179 /*
180  * Check that each object reference which points into the nursery can
181  * be found in the remembered sets.
182  */
183 static void
184 check_consistency_callback (char *start, size_t size, void *dummy)
185 {
186         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
187         mword desc = sgen_vtable_get_descriptor ((GCVTable*)vt);
188         SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, sgen_client_vtable_get_name (vt));
189
190 #include "sgen-scan-object.h"
191 }
192
193 /*
194  * Perform consistency check of the heap.
195  *
196  * Assumes the world is stopped.
197  */
198 void
199 sgen_check_consistency (void)
200 {
201         // Need to add more checks
202
203         missing_remsets = FALSE;
204
205         SGEN_LOG (1, "Begin heap consistency check...");
206
207         // Check that oldspace->newspace pointers are registered with the collector
208         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
209
210         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
211
212         SGEN_LOG (1, "Heap consistency check done.");
213
214         if (!binary_protocol_is_enabled ())
215                 g_assert (!missing_remsets);
216 }
217
218 static gboolean
219 is_major_or_los_object_marked (char *obj)
220 {
221         if (sgen_safe_object_get_size ((GCObject*)obj) > SGEN_MAX_SMALL_OBJ_SIZE) {
222                 return sgen_los_object_is_pinned (obj);
223         } else {
224                 return sgen_get_major_collector ()->is_object_live (obj);
225         }
226 }
227
228 #undef HANDLE_PTR
229 #define HANDLE_PTR(ptr,obj)     do {    \
230         if (*(ptr) && !sgen_ptr_in_nursery ((char*)*(ptr)) && !is_major_or_los_object_marked ((char*)*(ptr))) { \
231                 if (!sgen_get_remset ()->find_address_with_cards (start, cards, (char*)(ptr))) { \
232                         GCVTable *__vt = SGEN_LOAD_VTABLE ((obj));      \
233                         SGEN_LOG (0, "major->major reference %p at offset %td in object %p (%s.%s) not found in remsets.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), sgen_client_vtable_get_namespace (__vt), sgen_client_vtable_get_name (__vt)); \
234                         binary_protocol_missing_remset ((obj), __vt, (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
235                         missing_remsets = TRUE;                         \
236                 }                                                                                                                               \
237         }                                                                                                                                       \
238         } while (0)
239
240 static void
241 check_mod_union_callback (char *start, size_t size, void *dummy)
242 {
243         gboolean in_los = (gboolean) (size_t) dummy;
244         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
245         mword desc = sgen_vtable_get_descriptor ((GCVTable*)vt);
246         guint8 *cards;
247         SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, sgen_client_vtable_get_name (vt));
248
249         if (!is_major_or_los_object_marked (start))
250                 return;
251
252         if (in_los)
253                 cards = sgen_los_header_for_object (start)->cardtable_mod_union;
254         else
255                 cards = sgen_get_major_collector ()->get_cardtable_mod_union_for_object (start);
256
257         SGEN_ASSERT (0, cards, "we must have mod union for marked major objects");
258
259 #include "sgen-scan-object.h"
260 }
261
262 void
263 sgen_check_mod_union_consistency (void)
264 {
265         missing_remsets = FALSE;
266
267         major_collector.iterate_objects (ITERATE_OBJECTS_ALL, (IterateObjectCallbackFunc)check_mod_union_callback, (void*)FALSE);
268
269         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_mod_union_callback, (void*)TRUE);
270
271         if (!binary_protocol_is_enabled ())
272                 g_assert (!missing_remsets);
273 }
274
275 #undef HANDLE_PTR
276 #define HANDLE_PTR(ptr,obj)     do {                                    \
277                 if (*(ptr) && !LOAD_VTABLE (*(ptr)))                                            \
278                         g_error ("Could not load vtable for obj %p slot %zd (size %zd)", obj, (char*)ptr - (char*)obj, (size_t)safe_object_get_size ((GCObject*)obj)); \
279         } while (0)
280
281 static void
282 check_major_refs_callback (char *start, size_t size, void *dummy)
283 {
284         mword desc = sgen_obj_get_descriptor (start);
285
286 #include "sgen-scan-object.h"
287 }
288
289 void
290 sgen_check_major_refs (void)
291 {
292         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
293         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
294 }
295
296 /* Check that the reference is valid */
297 #undef HANDLE_PTR
298 #define HANDLE_PTR(ptr,obj)     do {    \
299                 if (*(ptr)) {   \
300                         g_assert (sgen_client_vtable_get_namespace (SGEN_LOAD_VTABLE_UNCHECKED (*(ptr))));      \
301                 }       \
302         } while (0)
303
304 /*
305  * check_object:
306  *
307  *   Perform consistency check on an object. Currently we only check that the
308  * reference fields are valid.
309  */
310 void
311 check_object (char *start)
312 {
313         mword desc;
314
315         if (!start)
316                 return;
317
318         desc = sgen_obj_get_descriptor (start);
319
320 #include "sgen-scan-object.h"
321 }
322
323
324 static char **valid_nursery_objects;
325 static int valid_nursery_object_count;
326 static gboolean broken_heap;
327
328 static void 
329 setup_mono_sgen_scan_area_with_callback (char *object, size_t size, void *data)
330 {
331         valid_nursery_objects [valid_nursery_object_count++] = object;
332 }
333
334 static void
335 setup_valid_nursery_objects (void)
336 {
337         if (!valid_nursery_objects)
338                 valid_nursery_objects = sgen_alloc_os_memory (DEFAULT_NURSERY_SIZE, SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging data");
339         valid_nursery_object_count = 0;
340         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE);
341 }
342
343 static gboolean
344 find_object_in_nursery_dump (char *object)
345 {
346         int first = 0, last = valid_nursery_object_count;
347         while (first < last) {
348                 int middle = first + ((last - first) >> 1);
349                 if (object == valid_nursery_objects [middle])
350                         return TRUE;
351
352                 if (object < valid_nursery_objects [middle])
353                         last = middle;
354                 else
355                         first = middle + 1;
356         }
357         g_assert (first == last);
358         return FALSE;
359 }
360
361 static void
362 iterate_valid_nursery_objects (IterateObjectCallbackFunc callback, void *data)
363 {
364         int i;
365         for (i = 0; i < valid_nursery_object_count; ++i) {
366                 char *obj = valid_nursery_objects [i];
367                 callback (obj, safe_object_get_size ((GCObject*)obj), data);
368         }
369 }
370
371 static char*
372 describe_nursery_ptr (char *ptr, gboolean need_setup)
373 {
374         int i;
375
376         if (need_setup)
377                 setup_valid_nursery_objects ();
378
379         for (i = 0; i < valid_nursery_object_count - 1; ++i) {
380                 if (valid_nursery_objects [i + 1] > ptr)
381                         break;
382         }
383
384         if (i >= valid_nursery_object_count || valid_nursery_objects [i] + safe_object_get_size ((GCObject *)valid_nursery_objects [i]) < ptr) {
385                 SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)");
386                 return NULL;
387         } else {
388                 char *obj = valid_nursery_objects [i];
389                 if (obj == ptr)
390                         SGEN_LOG (0, "nursery-ptr %p", obj);
391                 else
392                         SGEN_LOG (0, "nursery-ptr %p (interior-ptr offset %td)", obj, ptr - obj);
393                 return obj;
394         }
395 }
396
397 static gboolean
398 is_valid_object_pointer (char *object)
399 {
400         if (sgen_ptr_in_nursery (object))
401                 return find_object_in_nursery_dump (object);
402         
403         if (sgen_los_is_valid_object (object))
404                 return TRUE;
405
406         if (major_collector.is_valid_object (object))
407                 return TRUE;
408         return FALSE;
409 }
410
411 static void
412 bad_pointer_spew (char *obj, char **slot)
413 {
414         char *ptr = *slot;
415         GCVTable *vtable = (GCVTable*)LOAD_VTABLE (obj);
416
417         SGEN_LOG (0, "Invalid object pointer %p at offset %td in object %p (%s.%s):", ptr,
418                         (char*)slot - obj,
419                         obj, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
420         describe_pointer (ptr, FALSE);
421         broken_heap = TRUE;
422 }
423
424 static void
425 missing_remset_spew (char *obj, char **slot)
426 {
427         char *ptr = *slot;
428         GCVTable *vtable = (GCVTable*)LOAD_VTABLE (obj);
429
430         SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.",
431                         ptr, (char*)slot - obj, obj, 
432                         sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
433
434         broken_heap = TRUE;
435 }
436
437 /*
438 FIXME Flag missing remsets due to pinning as non fatal
439 */
440 #undef HANDLE_PTR
441 #define HANDLE_PTR(ptr,obj)     do {    \
442                 if (*(char**)ptr) {     \
443                         if (!is_valid_object_pointer (*(char**)ptr)) {  \
444                                 bad_pointer_spew ((char*)obj, (char**)ptr);     \
445                         } else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) {   \
446                                 if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup ((char*)*(ptr)) && (!allow_missing_pinned || !SGEN_OBJECT_IS_PINNED ((char*)*(ptr)))) \
447                                 missing_remset_spew ((char*)obj, (char**)ptr);  \
448                         }       \
449         } \
450         } while (0)
451
452 static void
453 verify_object_pointers_callback (char *start, size_t size, void *data)
454 {
455         gboolean allow_missing_pinned = (gboolean) (size_t) data;
456         mword desc = sgen_obj_get_descriptor (start);
457
458 #include "sgen-scan-object.h"
459 }
460
461 /*
462 FIXME:
463 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
464 depend on OP_DUMMY_USE.
465 */
466 void
467 sgen_check_whole_heap (gboolean allow_missing_pinned)
468 {
469         setup_valid_nursery_objects ();
470
471         broken_heap = FALSE;
472         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned, FALSE);
473         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
474         sgen_los_iterate_objects (verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
475
476         g_assert (!broken_heap);
477 }
478
479 static gboolean
480 ptr_in_heap (char *object)
481 {
482         if (sgen_ptr_in_nursery (object))
483                 return TRUE;
484         
485         if (sgen_los_is_valid_object (object))
486                 return TRUE;
487
488         if (major_collector.is_valid_object (object))
489                 return TRUE;
490         return FALSE;
491 }
492
493 /*
494  * sgen_check_objref:
495  *   Do consistency checks on the object reference OBJ. Assert on failure.
496  */
497 void
498 sgen_check_objref (char *obj)
499 {
500         g_assert (ptr_in_heap (obj));
501 }
502
503 static void
504 find_pinning_ref_from_thread (char *obj, size_t size)
505 {
506 #ifndef SGEN_WITHOUT_MONO
507         int j;
508         SgenThreadInfo *info;
509         char *endobj = obj + size;
510
511         FOREACH_THREAD (info) {
512                 char **start = (char**)info->client_info.stack_start;
513                 if (info->client_info.skip || info->client_info.gc_disabled)
514                         continue;
515                 while (start < (char**)info->client_info.stack_end) {
516                         if (*start >= obj && *start < endobj)
517                                 SGEN_LOG (0, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p", obj, info, (gpointer)mono_thread_info_get_tid (info), start, info->client_info.stack_start, info->client_info.stack_end);
518                         start++;
519                 }
520
521                 for (j = 0; j < ARCH_NUM_REGS; ++j) {
522 #ifdef USE_MONO_CTX
523                         mword w = ((mword*)&info->client_info.ctx) [j];
524 #else
525                         mword w = (mword)&info->client_info.regs [j];
526 #endif
527
528                         if (w >= (mword)obj && w < (mword)obj + size)
529                                 SGEN_LOG (0, "Object %p referenced in saved reg %d of thread %p (id %p)", obj, j, info, (gpointer)mono_thread_info_get_tid (info));
530                 } END_FOREACH_THREAD
531         }
532 #endif
533 }
534
535 /*
536  * Debugging function: find in the conservative roots where @obj is being pinned.
537  */
538 static G_GNUC_UNUSED void
539 find_pinning_reference (char *obj, size_t size)
540 {
541         char **start;
542         RootRecord *root;
543         char *endobj = obj + size;
544
545         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_NORMAL], start, root) {
546                 /* if desc is non-null it has precise info */
547                 if (!root->root_desc) {
548                         while (start < (char**)root->end_root) {
549                                 if (*start >= obj && *start < endobj) {
550                                         SGEN_LOG (0, "Object %p referenced in pinned roots %p-%p\n", obj, start, root->end_root);
551                                 }
552                                 start++;
553                         }
554                 }
555         } SGEN_HASH_TABLE_FOREACH_END;
556
557         find_pinning_ref_from_thread (obj, size);
558 }
559
560 #undef HANDLE_PTR
561 #define HANDLE_PTR(ptr,obj)     do {                                    \
562                 char* __target = *(char**)ptr;                          \
563                 if (__target) {                                         \
564                         if (sgen_ptr_in_nursery (__target)) {           \
565                                 g_assert (!SGEN_OBJECT_IS_FORWARDED (__target)); \
566                         } else {                                        \
567                                 mword __size = sgen_safe_object_get_size ((GCObject*)__target); \
568                                 if (__size <= SGEN_MAX_SMALL_OBJ_SIZE)  \
569                                         g_assert (major_collector.is_object_live (__target)); \
570                                 else                                    \
571                                         g_assert (sgen_los_object_is_pinned (__target)); \
572                         }                                               \
573                 }                                                       \
574         } while (0)
575
576 static void
577 check_marked_callback (char *start, size_t size, void *dummy)
578 {
579         gboolean flag = (gboolean) (size_t) dummy;
580         mword desc;
581
582         if (sgen_ptr_in_nursery (start)) {
583                 if (flag)
584                         SGEN_ASSERT (0, SGEN_OBJECT_IS_PINNED (start), "All objects remaining in the nursery must be pinned");
585         } else if (flag) {
586                 if (!sgen_los_object_is_pinned (start))
587                         return;
588         } else {
589                 if (!major_collector.is_object_live (start))
590                         return;
591         }
592
593         desc = sgen_obj_get_descriptor_safe (start);
594
595 #include "sgen-scan-object.h"
596 }
597
598 void
599 sgen_check_heap_marked (gboolean nursery_must_be_pinned)
600 {
601         setup_valid_nursery_objects ();
602
603         iterate_valid_nursery_objects (check_marked_callback, (void*)(size_t)nursery_must_be_pinned);
604         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, check_marked_callback, (void*)FALSE);
605         sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
606 }
607
608 static void
609 check_nursery_objects_pinned_callback (char *obj, size_t size, void *data /* ScanCopyContext *ctx */)
610 {
611         gboolean pinned = (gboolean) (size_t) data;
612
613         g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
614         if (pinned)
615                 g_assert (SGEN_OBJECT_IS_PINNED (obj));
616         else
617                 g_assert (!SGEN_OBJECT_IS_PINNED (obj));
618 }
619
620 void
621 sgen_check_nursery_objects_pinned (gboolean pinned)
622 {
623         sgen_clear_nursery_fragments ();
624         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
625                         (IterateObjectCallbackFunc)check_nursery_objects_pinned_callback, (void*) (size_t) pinned /* (void*)&ctx */, FALSE);
626 }
627
628 static void
629 verify_scan_starts (char *start, char *end)
630 {
631         size_t i;
632
633         for (i = 0; i < nursery_section->num_scan_start; ++i) {
634                 char *addr = nursery_section->scan_starts [i];
635                 if (addr > start && addr < end)
636                         SGEN_LOG (0, "NFC-BAD SCAN START [%zu] %p for obj [%p %p]", i, addr, start, end);
637         }
638 }
639
640 void
641 sgen_debug_verify_nursery (gboolean do_dump_nursery_content)
642 {
643         char *start, *end, *cur, *hole_start;
644
645         if (nursery_canaries_enabled ())
646                 SGEN_LOG (0, "Checking nursery canaries...");
647
648         /*This cleans up unused fragments */
649         sgen_nursery_allocator_prepare_for_pinning ();
650
651         hole_start = start = cur = sgen_get_nursery_start ();
652         end = sgen_get_nursery_end ();
653
654         while (cur < end) {
655                 size_t ss, size;
656                 gboolean is_array_fill;
657
658                 if (!*(void**)cur) {
659                         cur += sizeof (void*);
660                         continue;
661                 }
662
663                 if (object_is_forwarded (cur))
664                         SGEN_LOG (0, "FORWARDED OBJ %p", cur);
665                 else if (object_is_pinned (cur))
666                         SGEN_LOG (0, "PINNED OBJ %p", cur);
667
668                 ss = safe_object_get_size ((GCObject*)cur);
669                 size = SGEN_ALIGN_UP (ss);
670                 verify_scan_starts (cur, cur + size);
671                 is_array_fill = sgen_client_object_is_array_fill ((GCObject*)cur);
672                 if (do_dump_nursery_content) {
673                         GCVTable *vtable = SGEN_LOAD_VTABLE (cur);
674                         if (cur > hole_start)
675                                 SGEN_LOG (0, "HOLE [%p %p %d]", hole_start, cur, (int)(cur - hole_start));
676                         SGEN_LOG (0, "OBJ  [%p %p %d %d %s.%s %d]", cur, cur + size, (int)size, (int)ss,
677                                         sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable),
678                                         is_array_fill);
679                 }
680                 if (nursery_canaries_enabled () && !is_array_fill) {
681                         CHECK_CANARY_FOR_OBJECT (cur);
682                         CANARIFY_SIZE (size);
683                 }
684                 cur += size;
685                 hole_start = cur;
686         }
687 }
688
689 /*
690  * Checks that no objects in the nursery are fowarded or pinned.  This
691  * is a precondition to restarting the mutator while doing a
692  * concurrent collection.  Note that we don't clear fragments because
693  * we depend on that having happened earlier.
694  */
695 void
696 sgen_debug_check_nursery_is_clean (void)
697 {
698         char *end, *cur;
699
700         cur = sgen_get_nursery_start ();
701         end = sgen_get_nursery_end ();
702
703         while (cur < end) {
704                 size_t size;
705
706                 if (!*(void**)cur) {
707                         cur += sizeof (void*);
708                         continue;
709                 }
710
711                 g_assert (!object_is_forwarded (cur));
712                 g_assert (!object_is_pinned (cur));
713
714                 size = SGEN_ALIGN_UP (safe_object_get_size ((GCObject*)cur));
715                 verify_scan_starts (cur, cur + size);
716
717                 cur += size;
718         }
719 }
720
721 static gboolean scan_object_for_specific_ref_precise = TRUE;
722
723 #undef HANDLE_PTR
724 #define HANDLE_PTR(ptr,obj) do {                                        \
725                 if ((GCObject*)*(ptr) == key) {                         \
726                         GCVTable *vtable = SGEN_LOAD_VTABLE (*(ptr));   \
727                         g_print ("found ref to %p in object %p (%s.%s) at offset %td\n", \
728                                         key, (obj), sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), ((char*)(ptr) - (char*)(obj))); \
729                 }                                                       \
730         } while (0)
731
732 static void
733 scan_object_for_specific_ref (char *start, GCObject *key)
734 {
735         char *forwarded;
736
737         if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
738                 start = forwarded;
739
740         if (scan_object_for_specific_ref_precise) {
741                 mword desc = sgen_obj_get_descriptor_safe (start);
742                 #include "sgen-scan-object.h"
743         } else {
744                 mword *words = (mword*)start;
745                 size_t size = safe_object_get_size ((GCObject*)start);
746                 int i;
747                 for (i = 0; i < size / sizeof (mword); ++i) {
748                         if (words [i] == (mword)key) {
749                                 GCVTable *vtable = SGEN_LOAD_VTABLE (start);
750                                 g_print ("found possible ref to %p in object %p (%s.%s) at offset %td\n",
751                                                 key, start, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), i * sizeof (mword));
752                         }
753                 }
754         }
755 }
756
757 static void
758 scan_object_for_specific_ref_callback (char *obj, size_t size, GCObject *key)
759 {
760         scan_object_for_specific_ref (obj, key);
761 }
762
763 static void
764 check_root_obj_specific_ref (RootRecord *root, GCObject *key, GCObject *obj)
765 {
766         if (key != obj)
767                 return;
768         g_print ("found ref to %p in root record %p\n", key, root);
769 }
770
771 static GCObject *check_key = NULL;
772 static RootRecord *check_root = NULL;
773
774 static void
775 check_root_obj_specific_ref_from_marker (void **obj, void *gc_data)
776 {
777         check_root_obj_specific_ref (check_root, check_key, *obj);
778 }
779
780 static void
781 scan_roots_for_specific_ref (GCObject *key, int root_type)
782 {
783         void **start_root;
784         RootRecord *root;
785         check_key = key;
786
787         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
788                 mword desc = root->root_desc;
789
790                 check_root = root;
791
792                 switch (desc & ROOT_DESC_TYPE_MASK) {
793                 case ROOT_DESC_BITMAP:
794                         desc >>= ROOT_DESC_TYPE_SHIFT;
795                         while (desc) {
796                                 if (desc & 1)
797                                         check_root_obj_specific_ref (root, key, *start_root);
798                                 desc >>= 1;
799                                 start_root++;
800                         }
801                         return;
802                 case ROOT_DESC_COMPLEX: {
803                         gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
804                         int bwords = (int) ((*bitmap_data) - 1);
805                         void **start_run = start_root;
806                         bitmap_data++;
807                         while (bwords-- > 0) {
808                                 gsize bmap = *bitmap_data++;
809                                 void **objptr = start_run;
810                                 while (bmap) {
811                                         if (bmap & 1)
812                                                 check_root_obj_specific_ref (root, key, *objptr);
813                                         bmap >>= 1;
814                                         ++objptr;
815                                 }
816                                 start_run += GC_BITS_PER_WORD;
817                         }
818                         break;
819                 }
820                 case ROOT_DESC_USER: {
821                         SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
822                         marker (start_root, check_root_obj_specific_ref_from_marker, NULL);
823                         break;
824                 }
825                 case ROOT_DESC_RUN_LEN:
826                         g_assert_not_reached ();
827                 default:
828                         g_assert_not_reached ();
829                 }
830         } SGEN_HASH_TABLE_FOREACH_END;
831
832         check_key = NULL;
833         check_root = NULL;
834 }
835
836 void
837 mono_gc_scan_for_specific_ref (GCObject *key, gboolean precise)
838 {
839         void **ptr;
840         RootRecord *root;
841
842         scan_object_for_specific_ref_precise = precise;
843
844         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
845                         (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
846
847         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
848
849         sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
850
851         scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
852         scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
853
854         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], ptr, root) {
855                 while (ptr < (void**)root->end_root) {
856                         check_root_obj_specific_ref (root, *ptr, key);
857                         ++ptr;
858                 }
859         } SGEN_HASH_TABLE_FOREACH_END;
860
861         if (sgen_is_world_stopped ())
862                 find_pinning_ref_from_thread ((char*)key, sizeof (MonoObject));
863 }
864
865 #ifndef SGEN_WITHOUT_MONO
866
867 static MonoDomain *check_domain = NULL;
868
869 static void
870 check_obj_not_in_domain (MonoObject **o)
871 {
872         g_assert (((*o))->vtable->domain != check_domain);
873 }
874
875
876 static void
877 check_obj_not_in_domain_callback (void **o, void *gc_data)
878 {
879         g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
880 }
881
882 void
883 sgen_scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
884 {
885         void **start_root;
886         RootRecord *root;
887         check_domain = domain;
888         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
889                 mword desc = root->root_desc;
890
891                 /* The MonoDomain struct is allowed to hold
892                    references to objects in its own domain. */
893                 if (start_root == (void**)domain)
894                         continue;
895
896                 switch (desc & ROOT_DESC_TYPE_MASK) {
897                 case ROOT_DESC_BITMAP:
898                         desc >>= ROOT_DESC_TYPE_SHIFT;
899                         while (desc) {
900                                 if ((desc & 1) && *start_root)
901                                         check_obj_not_in_domain (*start_root);
902                                 desc >>= 1;
903                                 start_root++;
904                         }
905                         break;
906                 case ROOT_DESC_COMPLEX: {
907                         gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
908                         int bwords = (int)((*bitmap_data) - 1);
909                         void **start_run = start_root;
910                         bitmap_data++;
911                         while (bwords-- > 0) {
912                                 gsize bmap = *bitmap_data++;
913                                 void **objptr = start_run;
914                                 while (bmap) {
915                                         if ((bmap & 1) && *objptr)
916                                                 check_obj_not_in_domain (*objptr);
917                                         bmap >>= 1;
918                                         ++objptr;
919                                 }
920                                 start_run += GC_BITS_PER_WORD;
921                         }
922                         break;
923                 }
924                 case ROOT_DESC_USER: {
925                         SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
926                         marker (start_root, check_obj_not_in_domain_callback, NULL);
927                         break;
928                 }
929                 case ROOT_DESC_RUN_LEN:
930                         g_assert_not_reached ();
931                 default:
932                         g_assert_not_reached ();
933                 }
934         } SGEN_HASH_TABLE_FOREACH_END;
935
936         check_domain = NULL;
937 }
938
939 static gboolean
940 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
941 {
942         MonoObject *o = (MonoObject*)(obj);
943         MonoObject *ref = (MonoObject*)*(ptr);
944         size_t offset = (char*)(ptr) - (char*)o;
945
946         if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
947                 return TRUE;
948         if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
949                 return TRUE;
950
951 #ifndef DISABLE_REMOTING
952         if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) &&
953                         offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
954                 return TRUE;
955 #endif
956         /* Thread.cached_culture_info */
957         if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
958                         !strcmp (ref->vtable->klass->name, "CultureInfo") &&
959                         !strcmp(o->vtable->klass->name_space, "System") &&
960                         !strcmp(o->vtable->klass->name, "Object[]"))
961                 return TRUE;
962         /*
963          *  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
964          * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
965          * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
966          * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
967          * 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
968          * 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
969          * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
970          * 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
971          * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
972          */
973         if (!strcmp (ref->vtable->klass->name_space, "System") &&
974                         !strcmp (ref->vtable->klass->name, "Byte[]") &&
975                         !strcmp (o->vtable->klass->name_space, "System.IO") &&
976                         !strcmp (o->vtable->klass->name, "MemoryStream"))
977                 return TRUE;
978         return FALSE;
979 }
980
981 static void
982 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
983 {
984         MonoObject *o = (MonoObject*)(obj);
985         MonoObject *ref = (MonoObject*)*(ptr);
986         size_t offset = (char*)(ptr) - (char*)o;
987         MonoClass *class;
988         MonoClassField *field;
989         char *str;
990
991         if (!ref || ref->vtable->domain == domain)
992                 return;
993         if (is_xdomain_ref_allowed (ptr, obj, domain))
994                 return;
995
996         field = NULL;
997         for (class = o->vtable->klass; class; class = class->parent) {
998                 int i;
999
1000                 for (i = 0; i < class->field.count; ++i) {
1001                         if (class->fields[i].offset == offset) {
1002                                 field = &class->fields[i];
1003                                 break;
1004                         }
1005                 }
1006                 if (field)
1007                         break;
1008         }
1009
1010         if (ref->vtable->klass == mono_defaults.string_class)
1011                 str = mono_string_to_utf8 ((MonoString*)ref);
1012         else
1013                 str = NULL;
1014         g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s)  -  pointed to by:\n",
1015                         o, o->vtable->klass->name_space, o->vtable->klass->name,
1016                         offset, field ? field->name : "",
1017                         ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1018         mono_gc_scan_for_specific_ref (o, TRUE);
1019         if (str)
1020                 g_free (str);
1021 }
1022
1023 #undef HANDLE_PTR
1024 #define HANDLE_PTR(ptr,obj)     check_reference_for_xdomain ((ptr), (obj), domain)
1025
1026 static void
1027 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1028 {
1029         MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (start);
1030         MonoDomain *domain = vt->domain;
1031         mword desc = sgen_vtable_get_descriptor ((GCVTable*)vt);
1032
1033         #include "sgen-scan-object.h"
1034 }
1035
1036 void
1037 sgen_check_for_xdomain_refs (void)
1038 {
1039         LOSObject *bigobj;
1040
1041         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1042                         (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
1043
1044         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1045
1046         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1047                 scan_object_for_xdomain_refs (bigobj->data, sgen_los_object_size (bigobj), NULL);
1048 }
1049
1050 #endif
1051
1052 /* If not null, dump the heap after each collection into this file */
1053 static FILE *heap_dump_file = NULL;
1054
1055 void
1056 sgen_dump_occupied (char *start, char *end, char *section_start)
1057 {
1058         fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
1059 }
1060
1061 void
1062 sgen_dump_section (GCMemSection *section, const char *type)
1063 {
1064         char *start = section->data;
1065         char *end = section->data + section->size;
1066         char *occ_start = NULL;
1067
1068         fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
1069
1070         while (start < end) {
1071                 guint size;
1072                 //GCVTable *vt;
1073                 //MonoClass *class;
1074
1075                 if (!*(void**)start) {
1076                         if (occ_start) {
1077                                 sgen_dump_occupied (occ_start, start, section->data);
1078                                 occ_start = NULL;
1079                         }
1080                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1081                         continue;
1082                 }
1083                 g_assert (start < section->next_data);
1084
1085                 if (!occ_start)
1086                         occ_start = start;
1087
1088                 //vt = (GCVTable*)SGEN_LOAD_VTABLE (start);
1089                 //class = vt->klass;
1090
1091                 size = SGEN_ALIGN_UP (safe_object_get_size ((GCObject*) start));
1092
1093                 /*
1094                 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
1095                                 start - section->data,
1096                                 vt->klass->name_space, vt->klass->name,
1097                                 size);
1098                 */
1099
1100                 start += size;
1101         }
1102         if (occ_start)
1103                 sgen_dump_occupied (occ_start, start, section->data);
1104
1105         fprintf (heap_dump_file, "</section>\n");
1106 }
1107
1108 static void
1109 dump_object (GCObject *obj, gboolean dump_location)
1110 {
1111 #ifndef SGEN_WITHOUT_MONO
1112         static char class_name [1024];
1113
1114         MonoClass *class = mono_object_class (obj);
1115         int i, j;
1116
1117         /*
1118          * Python's XML parser is too stupid to parse angle brackets
1119          * in strings, so we just ignore them;
1120          */
1121         i = j = 0;
1122         while (class->name [i] && j < sizeof (class_name) - 1) {
1123                 if (!strchr ("<>\"", class->name [i]))
1124                         class_name [j++] = class->name [i];
1125                 ++i;
1126         }
1127         g_assert (j < sizeof (class_name));
1128         class_name [j] = 0;
1129
1130         fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%zd\"",
1131                         class->name_space, class_name,
1132                         safe_object_get_size (obj));
1133         if (dump_location) {
1134                 const char *location;
1135                 if (sgen_ptr_in_nursery (obj))
1136                         location = "nursery";
1137                 else if (safe_object_get_size (obj) <= SGEN_MAX_SMALL_OBJ_SIZE)
1138                         location = "major";
1139                 else
1140                         location = "LOS";
1141                 fprintf (heap_dump_file, " location=\"%s\"", location);
1142         }
1143         fprintf (heap_dump_file, "/>\n");
1144 #endif
1145 }
1146
1147 void
1148 sgen_debug_enable_heap_dump (const char *filename)
1149 {
1150         heap_dump_file = fopen (filename, "w");
1151         if (heap_dump_file) {
1152                 fprintf (heap_dump_file, "<sgen-dump>\n");
1153                 sgen_pin_stats_enable ();
1154         }
1155 }
1156
1157 void
1158 sgen_debug_dump_heap (const char *type, int num, const char *reason)
1159 {
1160         SgenPointerQueue *pinned_objects;
1161         LOSObject *bigobj;
1162         int i;
1163
1164         if (!heap_dump_file)
1165                 return;
1166
1167         fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
1168         if (reason)
1169                 fprintf (heap_dump_file, " reason=\"%s\"", reason);
1170         fprintf (heap_dump_file, ">\n");
1171 #ifndef SGEN_WITHOUT_MONO
1172         fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
1173 #endif
1174         sgen_dump_internal_mem_usage (heap_dump_file);
1175         fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_STACK));
1176         /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
1177         fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_OTHER));
1178
1179         fprintf (heap_dump_file, "<pinned-objects>\n");
1180         pinned_objects = sgen_pin_stats_get_object_list ();
1181         for (i = 0; i < pinned_objects->next_slot; ++i)
1182                 dump_object (pinned_objects->data [i], TRUE);
1183         fprintf (heap_dump_file, "</pinned-objects>\n");
1184
1185         sgen_dump_section (nursery_section, "nursery");
1186
1187         major_collector.dump_heap (heap_dump_file);
1188
1189         fprintf (heap_dump_file, "<los>\n");
1190         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1191                 dump_object ((GCObject*)bigobj->data, FALSE);
1192         fprintf (heap_dump_file, "</los>\n");
1193
1194         fprintf (heap_dump_file, "</collection>\n");
1195 }
1196
1197 static char *found_obj;
1198
1199 static void
1200 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
1201 {
1202         char *ptr = user_data;
1203
1204         if (ptr >= obj && ptr < obj + size) {
1205                 g_assert (!found_obj);
1206                 found_obj = obj;
1207         }
1208 }
1209
1210 /* for use in the debugger */
1211 char*
1212 sgen_find_object_for_ptr (char *ptr)
1213 {
1214         if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
1215                 found_obj = NULL;
1216                 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1217                                 find_object_for_ptr_callback, ptr, TRUE);
1218                 if (found_obj)
1219                         return found_obj;
1220         }
1221
1222         found_obj = NULL;
1223         sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
1224         if (found_obj)
1225                 return found_obj;
1226
1227         /*
1228          * Very inefficient, but this is debugging code, supposed to
1229          * be called from gdb, so we don't care.
1230          */
1231         found_obj = NULL;
1232         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, find_object_for_ptr_callback, ptr);
1233         return found_obj;
1234 }
1235
1236 #ifndef SGEN_WITHOUT_MONO
1237
1238 static int
1239 compare_xrefs (const void *a_ptr, const void *b_ptr)
1240 {
1241         const MonoGCBridgeXRef *a = a_ptr;
1242         const MonoGCBridgeXRef *b = b_ptr;
1243
1244         if (a->src_scc_index < b->src_scc_index)
1245                 return -1;
1246         if (a->src_scc_index > b->src_scc_index)
1247                 return 1;
1248
1249         if (a->dst_scc_index < b->dst_scc_index)
1250                 return -1;
1251         if (a->dst_scc_index > b->dst_scc_index)
1252                 return 1;
1253
1254         return 0;
1255 }
1256
1257 /*
1258 static void
1259 dump_processor_state (SgenBridgeProcessor *p)
1260 {
1261         int i;
1262
1263         printf ("------\n");
1264         printf ("SCCS %d\n", p->num_sccs);
1265         for (i = 0; i < p->num_sccs; ++i) {
1266                 int j;
1267                 MonoGCBridgeSCC *scc = p->api_sccs [i];
1268                 printf ("\tSCC %d:", i);
1269                 for (j = 0; j < scc->num_objs; ++j) {
1270                         MonoObject *obj = scc->objs [j];
1271                         printf (" %p", obj);
1272                 }
1273                 printf ("\n");
1274         }
1275
1276         printf ("XREFS %d\n", p->num_xrefs);
1277         for (i = 0; i < p->num_xrefs; ++i)
1278                 printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index);
1279
1280         printf ("-------\n");
1281 }
1282 */
1283
1284 gboolean
1285 sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcessor *b)
1286 {
1287         int i;
1288         SgenHashTable obj_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), mono_aligned_addr_hash, NULL);
1289         SgenHashTable b_scc_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), g_direct_hash, NULL);
1290         MonoGCBridgeXRef *a_xrefs, *b_xrefs;
1291         size_t xrefs_alloc_size;
1292
1293         // dump_processor_state (a);
1294         // dump_processor_state (b);
1295
1296         if (a->num_sccs != b->num_sccs)
1297                 g_error ("SCCS count expected %d but got %d", a->num_sccs, b->num_sccs);
1298         if (a->num_xrefs != b->num_xrefs)
1299                 g_error ("SCCS count expected %d but got %d", a->num_xrefs, b->num_xrefs);
1300
1301         /*
1302          * First we build a hash of each object in `a` to its respective SCC index within
1303          * `a`.  Along the way we also assert that no object is more than one SCC.
1304          */
1305         for (i = 0; i < a->num_sccs; ++i) {
1306                 int j;
1307                 MonoGCBridgeSCC *scc = a->api_sccs [i];
1308
1309                 g_assert (scc->num_objs > 0);
1310
1311                 for (j = 0; j < scc->num_objs; ++j) {
1312                         GCObject *obj = scc->objs [j];
1313                         gboolean new_entry = sgen_hash_table_replace (&obj_to_a_scc, obj, &i, NULL);
1314                         g_assert (new_entry);
1315                 }
1316         }
1317
1318         /*
1319          * Now we check whether each of the objects in `b` are in `a`, and whether the SCCs
1320          * of `b` contain the same sets of objects as those of `a`.
1321          *
1322          * While we're doing this, build a hash table to map from `b` SCC indexes to `a` SCC
1323          * indexes.
1324          */
1325         for (i = 0; i < b->num_sccs; ++i) {
1326                 MonoGCBridgeSCC *scc = b->api_sccs [i];
1327                 MonoGCBridgeSCC *a_scc;
1328                 int *a_scc_index_ptr;
1329                 int a_scc_index;
1330                 int j;
1331                 gboolean new_entry;
1332
1333                 g_assert (scc->num_objs > 0);
1334                 a_scc_index_ptr = sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [0]);
1335                 g_assert (a_scc_index_ptr);
1336                 a_scc_index = *a_scc_index_ptr;
1337
1338                 //g_print ("A SCC %d -> B SCC %d\n", a_scc_index, i);
1339
1340                 a_scc = a->api_sccs [a_scc_index];
1341                 g_assert (a_scc->num_objs == scc->num_objs);
1342
1343                 for (j = 1; j < scc->num_objs; ++j) {
1344                         a_scc_index_ptr = sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [j]);
1345                         g_assert (a_scc_index_ptr);
1346                         g_assert (*a_scc_index_ptr == a_scc_index);
1347                 }
1348
1349                 new_entry = sgen_hash_table_replace (&b_scc_to_a_scc, GINT_TO_POINTER (i), &a_scc_index, NULL);
1350                 g_assert (new_entry);
1351         }
1352
1353         /*
1354          * Finally, check that we have the same xrefs.  We do this by making copies of both
1355          * xref arrays, and replacing the SCC indexes in the copy for `b` with the
1356          * corresponding indexes in `a`.  Then we sort both arrays and assert that they're
1357          * the same.
1358          *
1359          * At the same time, check that no xref is self-referential and that there are no
1360          * duplicate ones.
1361          */
1362
1363         xrefs_alloc_size = a->num_xrefs * sizeof (MonoGCBridgeXRef);
1364         a_xrefs = sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
1365         b_xrefs = sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
1366
1367         memcpy (a_xrefs, a->api_xrefs, xrefs_alloc_size);
1368         for (i = 0; i < b->num_xrefs; ++i) {
1369                 MonoGCBridgeXRef *xref = &b->api_xrefs [i];
1370                 int *scc_index_ptr;
1371
1372                 g_assert (xref->src_scc_index != xref->dst_scc_index);
1373
1374                 scc_index_ptr = sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->src_scc_index));
1375                 g_assert (scc_index_ptr);
1376                 b_xrefs [i].src_scc_index = *scc_index_ptr;
1377
1378                 scc_index_ptr = sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->dst_scc_index));
1379                 g_assert (scc_index_ptr);
1380                 b_xrefs [i].dst_scc_index = *scc_index_ptr;
1381         }
1382
1383         qsort (a_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
1384         qsort (b_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
1385
1386         for (i = 0; i < a->num_xrefs; ++i) {
1387                 g_assert (a_xrefs [i].src_scc_index == b_xrefs [i].src_scc_index);
1388                 g_assert (a_xrefs [i].dst_scc_index == b_xrefs [i].dst_scc_index);
1389         }
1390
1391         sgen_hash_table_clean (&obj_to_a_scc);
1392         sgen_hash_table_clean (&b_scc_to_a_scc);
1393         sgen_free_internal_dynamic (a_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
1394         sgen_free_internal_dynamic (b_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
1395
1396         return TRUE;
1397 }
1398
1399 #endif
1400
1401 #endif /*HAVE_SGEN_GC*/