Merge pull request #1066 from esdrubal/bug19313
[mono.git] / mono / metadata / 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 "metadata/sgen-gc.h"
31 #include "metadata/sgen-cardtable.h"
32 #include "metadata/sgen-protocol.h"
33 #include "metadata/sgen-memory-governor.h"
34 #include "metadata/sgen-pinning.h"
35 #include "metadata/threadpool-internals.h"
36
37 #define LOAD_VTABLE     SGEN_LOAD_VTABLE
38
39 #define object_is_forwarded     SGEN_OBJECT_IS_FORWARDED
40 #define object_is_pinned        SGEN_OBJECT_IS_PINNED
41 #define safe_object_get_size    sgen_safe_object_get_size
42
43 void describe_ptr (char *ptr);
44 void check_object (char *start);
45
46 /*
47  * ######################################################################
48  * ########  Collector debugging
49  * ######################################################################
50  */
51
52 const char*descriptor_types [] = {
53         "INVALID",
54         "run_length",
55         "small_bitmap",
56         "complex",
57         "vector",
58         "large_bitmap",
59         "complex_arr",
60         "complex_ptrfree"
61 };
62
63 static char* describe_nursery_ptr (char *ptr, gboolean need_setup);
64
65 static void
66 describe_pointer (char *ptr, gboolean need_setup)
67 {
68         MonoVTable *vtable;
69         mword desc;
70         int type;
71         char *start;
72         char *forwarded;
73         mword size;
74
75  restart:
76         if (sgen_ptr_in_nursery (ptr)) {
77                 start = describe_nursery_ptr (ptr, need_setup);
78                 if (!start)
79                         return;
80                 ptr = start;
81                 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
82         } else {
83                 if (sgen_ptr_is_in_los (ptr, &start)) {
84                         if (ptr == start)
85                                 printf ("Pointer is the start of object %p in LOS space.\n", start);
86                         else
87                                 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
88                         ptr = start;
89                         mono_sgen_los_describe_pointer (ptr);
90                         vtable = (MonoVTable*)LOAD_VTABLE (ptr);
91                 } else if (major_collector.ptr_is_in_non_pinned_space (ptr, &start)) {
92                         if (ptr == start)
93                                 printf ("Pointer is the start of object %p in oldspace.\n", start);
94                         else if (start)
95                                 printf ("Pointer is at offset 0x%x of object %p in oldspace.\n", (int)(ptr - start), start);
96                         else
97                                 printf ("Pointer inside oldspace.\n");
98                         if (start)
99                                 ptr = start;
100                         vtable = major_collector.describe_pointer (ptr);
101                 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
102                         // FIXME: Handle pointers to the inside of objects
103                         printf ("Pointer is inside a pinned chunk.\n");
104                         vtable = (MonoVTable*)LOAD_VTABLE (ptr);
105                 } else {
106                         printf ("Pointer unknown.\n");
107                         return;
108                 }
109         }
110
111         if (object_is_pinned (ptr))
112                 printf ("Object is pinned.\n");
113
114         if ((forwarded = object_is_forwarded (ptr))) {
115                 printf ("Object is forwarded to %p:\n", forwarded);
116                 ptr = forwarded;
117                 goto restart;
118         }
119
120         printf ("VTable: %p\n", vtable);
121         if (vtable == NULL) {
122                 printf ("VTable is invalid (empty).\n");
123                 goto bridge;
124         }
125         if (sgen_ptr_in_nursery (vtable)) {
126                 printf ("VTable is invalid (points inside nursery).\n");
127                 goto bridge;
128         }
129         printf ("Class: %s\n", vtable->klass->name);
130
131         desc = ((GCVTable*)vtable)->desc;
132         printf ("Descriptor: %lx\n", (long)desc);
133
134         type = desc & 0x7;
135         printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
136
137         size = sgen_safe_object_get_size ((MonoObject*)ptr);
138         printf ("Size: %d\n", (int)size);
139
140  bridge:
141         sgen_bridge_describe_pointer ((MonoObject*)ptr);
142 }
143
144 void
145 describe_ptr (char *ptr)
146 {
147         describe_pointer (ptr, TRUE);
148 }
149
150 static gboolean missing_remsets;
151
152 /*
153  * We let a missing remset slide if the target object is pinned,
154  * because the store might have happened but the remset not yet added,
155  * but in that case the target must be pinned.  We might theoretically
156  * miss some missing remsets this way, but it's very unlikely.
157  */
158 #undef HANDLE_PTR
159 #define HANDLE_PTR(ptr,obj)     do {    \
160         if (*(ptr) && sgen_ptr_in_nursery ((char*)*(ptr))) { \
161                 if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup (*(ptr))) { \
162                         SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
163                         binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
164                         if (!object_is_pinned (*(ptr)))                                                         \
165                                 missing_remsets = TRUE;                                                                 \
166                 }                                                                                                                               \
167         }                                                                                                                                       \
168         } while (0)
169
170 /*
171  * Check that each object reference which points into the nursery can
172  * be found in the remembered sets.
173  */
174 static void
175 check_consistency_callback (char *start, size_t size, void *dummy)
176 {
177         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
178         SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, vt->klass->name);
179
180 #include "sgen-scan-object.h"
181 }
182
183 /*
184  * Perform consistency check of the heap.
185  *
186  * Assumes the world is stopped.
187  */
188 void
189 sgen_check_consistency (void)
190 {
191         // Need to add more checks
192
193         missing_remsets = FALSE;
194
195         SGEN_LOG (1, "Begin heap consistency check...");
196
197         // Check that oldspace->newspace pointers are registered with the collector
198         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
199
200         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
201
202         SGEN_LOG (1, "Heap consistency check done.");
203
204         if (!binary_protocol_is_enabled ())
205                 g_assert (!missing_remsets);
206 }
207
208 static gboolean
209 is_major_or_los_object_marked (char *obj)
210 {
211         if (sgen_safe_object_get_size ((MonoObject*)obj) > SGEN_MAX_SMALL_OBJ_SIZE) {
212                 return sgen_los_object_is_pinned (obj);
213         } else {
214                 return sgen_get_major_collector ()->is_object_live (obj);
215         }
216 }
217
218 #undef HANDLE_PTR
219 #define HANDLE_PTR(ptr,obj)     do {    \
220         if (*(ptr) && !sgen_ptr_in_nursery ((char*)*(ptr)) && !is_major_or_los_object_marked ((char*)*(ptr))) { \
221                 if (!sgen_get_remset ()->find_address_with_cards (start, cards, (char*)(ptr))) { \
222                         SGEN_LOG (0, "major->major reference %p at offset %td in object %p (%s.%s) not found in remsets.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
223                         binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
224                         missing_remsets = TRUE;                         \
225                 }                                                                                                                               \
226         }                                                                                                                                       \
227         } while (0)
228
229 static void
230 check_mod_union_callback (char *start, size_t size, void *dummy)
231 {
232         gboolean in_los = (gboolean) (size_t) dummy;
233         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
234         guint8 *cards;
235         SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, vt->klass->name);
236
237         if (!is_major_or_los_object_marked (start))
238                 return;
239
240         if (in_los)
241                 cards = sgen_los_header_for_object (start)->cardtable_mod_union;
242         else
243                 cards = sgen_get_major_collector ()->get_cardtable_mod_union_for_object (start);
244
245         SGEN_ASSERT (0, cards, "we must have mod union for marked major objects");
246
247 #include "sgen-scan-object.h"
248 }
249
250 void
251 sgen_check_mod_union_consistency (void)
252 {
253         missing_remsets = FALSE;
254
255         major_collector.iterate_objects (ITERATE_OBJECTS_ALL, (IterateObjectCallbackFunc)check_mod_union_callback, (void*)FALSE);
256
257         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_mod_union_callback, (void*)TRUE);
258
259         if (!binary_protocol_is_enabled ())
260                 g_assert (!missing_remsets);
261 }
262
263 #undef HANDLE_PTR
264 #define HANDLE_PTR(ptr,obj)     do {                                    \
265                 if (*(ptr) && !LOAD_VTABLE (*(ptr)))                                            \
266                         g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj));          \
267         } while (0)
268
269 static void
270 check_major_refs_callback (char *start, size_t size, void *dummy)
271 {
272 #include "sgen-scan-object.h"
273 }
274
275 void
276 sgen_check_major_refs (void)
277 {
278         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
279         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
280 }
281
282 /* Check that the reference is valid */
283 #undef HANDLE_PTR
284 #define HANDLE_PTR(ptr,obj)     do {    \
285                 if (*(ptr)) {   \
286                         g_assert (sgen_safe_name (*(ptr)) != NULL);     \
287                 }       \
288         } while (0)
289
290 /*
291  * check_object:
292  *
293  *   Perform consistency check on an object. Currently we only check that the
294  * reference fields are valid.
295  */
296 void
297 check_object (char *start)
298 {
299         if (!start)
300                 return;
301
302 #include "sgen-scan-object.h"
303 }
304
305
306 static char **valid_nursery_objects;
307 static int valid_nursery_object_count;
308 static gboolean broken_heap;
309
310 static void 
311 setup_mono_sgen_scan_area_with_callback (char *object, size_t size, void *data)
312 {
313         valid_nursery_objects [valid_nursery_object_count++] = object;
314 }
315
316 static void
317 setup_valid_nursery_objects (void)
318 {
319         if (!valid_nursery_objects)
320                 valid_nursery_objects = sgen_alloc_os_memory (DEFAULT_NURSERY_SIZE, SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging data");
321         valid_nursery_object_count = 0;
322         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE);
323 }
324
325 static gboolean
326 find_object_in_nursery_dump (char *object)
327 {
328         int first = 0, last = valid_nursery_object_count;
329         while (first < last) {
330                 int middle = first + ((last - first) >> 1);
331                 if (object == valid_nursery_objects [middle])
332                         return TRUE;
333
334                 if (object < valid_nursery_objects [middle])
335                         last = middle;
336                 else
337                         first = middle + 1;
338         }
339         g_assert (first == last);
340         return FALSE;
341 }
342
343 static char*
344 describe_nursery_ptr (char *ptr, gboolean need_setup)
345 {
346         int i;
347
348         if (need_setup)
349                 setup_valid_nursery_objects ();
350
351         for (i = 0; i < valid_nursery_object_count; ++i) {
352                 if (valid_nursery_objects [i] >= ptr)
353                         break;
354         }
355
356         if (i >= valid_nursery_object_count || valid_nursery_objects [i] + safe_object_get_size ((MonoObject *)valid_nursery_objects [i]) < ptr) {
357                 SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)\n");
358                 return NULL;
359         } else {
360                 char *obj = valid_nursery_objects [i];
361                 if (obj == ptr)
362                         SGEN_LOG (0, "nursery-ptr\n");
363                 else
364                         SGEN_LOG (0, "nursery-ptr (interior-ptr offset %td)\n", ptr - obj);
365                 return obj;
366         }
367 }
368
369 static gboolean
370 is_valid_object_pointer (char *object)
371 {
372         if (sgen_ptr_in_nursery (object))
373                 return find_object_in_nursery_dump (object);
374         
375         if (sgen_los_is_valid_object (object))
376                 return TRUE;
377
378         if (major_collector.is_valid_object (object))
379                 return TRUE;
380         return FALSE;
381 }
382
383 static void
384 bad_pointer_spew (char *obj, char **slot)
385 {
386         char *ptr = *slot;
387         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
388
389         SGEN_LOG (0, "Invalid object pointer %p at offset %td in object %p (%s.%s):", ptr,
390                 (char*)slot - obj,
391                 obj, vtable->klass->name_space, vtable->klass->name);
392         describe_pointer (ptr, FALSE);
393         broken_heap = TRUE;
394 }
395
396 static void
397 missing_remset_spew (char *obj, char **slot)
398 {
399         char *ptr = *slot;
400         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
401
402         SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.",
403                 ptr, (char*)slot - obj, obj, 
404                 vtable->klass->name_space, vtable->klass->name);
405
406         broken_heap = TRUE;
407 }
408
409 /*
410 FIXME Flag missing remsets due to pinning as non fatal
411 */
412 #undef HANDLE_PTR
413 #define HANDLE_PTR(ptr,obj)     do {    \
414                 if (*(char**)ptr) {     \
415                         if (!is_valid_object_pointer (*(char**)ptr)) {  \
416                                 bad_pointer_spew ((char*)obj, (char**)ptr);     \
417                         } else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) {   \
418                                 if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup ((char*)*(ptr)) && (!allow_missing_pinned || !SGEN_OBJECT_IS_PINNED ((char*)*(ptr)))) \
419                                 missing_remset_spew ((char*)obj, (char**)ptr);  \
420                         }       \
421         } \
422         } while (0)
423
424 static void
425 verify_object_pointers_callback (char *start, size_t size, void *data)
426 {
427         gboolean allow_missing_pinned = (gboolean) (size_t) data;
428
429 #include "sgen-scan-object.h"
430 }
431
432 /*
433 FIXME:
434 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
435 depend on OP_DUMMY_USE.
436 */
437 void
438 sgen_check_whole_heap (gboolean allow_missing_pinned)
439 {
440         setup_valid_nursery_objects ();
441
442         broken_heap = FALSE;
443         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned, FALSE);
444         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
445         sgen_los_iterate_objects (verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
446
447         g_assert (!broken_heap);
448 }
449
450 static gboolean
451 ptr_in_heap (char *object)
452 {
453         if (sgen_ptr_in_nursery (object))
454                 return TRUE;
455         
456         if (sgen_los_is_valid_object (object))
457                 return TRUE;
458
459         if (major_collector.is_valid_object (object))
460                 return TRUE;
461         return FALSE;
462 }
463
464 /*
465  * sgen_check_objref:
466  *   Do consistency checks on the object reference OBJ. Assert on failure.
467  */
468 void
469 sgen_check_objref (char *obj)
470 {
471         g_assert (ptr_in_heap (obj));
472 }
473
474 static void
475 find_pinning_ref_from_thread (char *obj, size_t size)
476 {
477         int j;
478         SgenThreadInfo *info;
479         char *endobj = obj + size;
480
481         FOREACH_THREAD (info) {
482                 char **start = (char**)info->stack_start;
483                 if (info->skip)
484                         continue;
485                 while (start < (char**)info->stack_end) {
486                         if (*start >= obj && *start < endobj) {
487                                 SGEN_LOG (0, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p", obj, info, (gpointer)mono_thread_info_get_tid (info), start, info->stack_start, info->stack_end);
488                         }
489                         start++;
490                 }
491
492                 for (j = 0; j < ARCH_NUM_REGS; ++j) {
493 #ifdef USE_MONO_CTX
494                         mword w = ((mword*)&info->ctx) [j];
495 #else
496                         mword w = (mword)&info->regs [j];
497 #endif
498
499                         if (w >= (mword)obj && w < (mword)obj + size)
500                                 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));
501                 } END_FOREACH_THREAD
502         }
503 }
504
505 /*
506  * Debugging function: find in the conservative roots where @obj is being pinned.
507  */
508 static G_GNUC_UNUSED void
509 find_pinning_reference (char *obj, size_t size)
510 {
511         char **start;
512         RootRecord *root;
513         char *endobj = obj + size;
514
515         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_NORMAL], start, root) {
516                 /* if desc is non-null it has precise info */
517                 if (!root->root_desc) {
518                         while (start < (char**)root->end_root) {
519                                 if (*start >= obj && *start < endobj) {
520                                         SGEN_LOG (0, "Object %p referenced in pinned roots %p-%p\n", obj, start, root->end_root);
521                                 }
522                                 start++;
523                         }
524                 }
525         } SGEN_HASH_TABLE_FOREACH_END;
526
527         find_pinning_ref_from_thread (obj, size);
528 }
529
530 #undef HANDLE_PTR
531 #define HANDLE_PTR(ptr,obj)     do {                                    \
532                 char* __target = *(char**)ptr;                          \
533                 if (__target) {                                         \
534                         g_assert (is_valid_object_pointer (__target));  \
535                         if (sgen_ptr_in_nursery (__target)) {           \
536                                 g_assert (SGEN_OBJECT_IS_PINNED (__target)); \
537                         } else if (sgen_los_is_valid_object (__target)) { \
538                                 g_assert (sgen_los_object_is_pinned (__target)); \
539                         } else if (major_collector.is_valid_object (__target)) { \
540                                 g_assert (major_collector.is_object_live (__target)); \
541                         } else {                                        \
542                                 g_assert_not_reached ();                \
543                         }                                               \
544                 }                                                       \
545         } while (0)
546
547 static void
548 check_marked_callback (char *start, size_t size, void *dummy)
549 {
550         gboolean is_los = (gboolean) (size_t) dummy;
551
552         if (is_los) {
553                 if (!sgen_los_object_is_pinned (start))
554                         return;
555         } else {
556                 if (!major_collector.is_object_live (start))
557                         return;
558         }
559
560 #include "sgen-scan-object.h"
561 }
562
563 void
564 sgen_check_major_heap_marked (void)
565 {
566         setup_valid_nursery_objects ();
567
568         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, check_marked_callback, (void*)FALSE);
569         sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
570 }
571
572 static void
573 check_nursery_objects_pinned_callback (char *obj, size_t size, void *data /* ScanCopyContext *ctx */)
574 {
575         gboolean pinned = (gboolean) (size_t) data;
576
577         g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
578         if (pinned)
579                 g_assert (SGEN_OBJECT_IS_PINNED (obj));
580         else
581                 g_assert (!SGEN_OBJECT_IS_PINNED (obj));
582 }
583
584 void
585 sgen_check_nursery_objects_pinned (gboolean pinned)
586 {
587         sgen_clear_nursery_fragments ();
588         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
589                         (IterateObjectCallbackFunc)check_nursery_objects_pinned_callback, (void*) (size_t) pinned /* (void*)&ctx */, FALSE);
590 }
591
592 static gboolean scan_object_for_specific_ref_precise = TRUE;
593
594 #undef HANDLE_PTR
595 #define HANDLE_PTR(ptr,obj) do {                \
596         if ((MonoObject*)*(ptr) == key) {       \
597         g_print ("found ref to %p in object %p (%s) at offset %td\n",   \
598                         key, (obj), sgen_safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
599         }                                                               \
600         } while (0)
601
602 static void
603 scan_object_for_specific_ref (char *start, MonoObject *key)
604 {
605         char *forwarded;
606
607         if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
608                 start = forwarded;
609
610         if (scan_object_for_specific_ref_precise) {
611                 #include "sgen-scan-object.h"
612         } else {
613                 mword *words = (mword*)start;
614                 size_t size = safe_object_get_size ((MonoObject*)start);
615                 int i;
616                 for (i = 0; i < size / sizeof (mword); ++i) {
617                         if (words [i] == (mword)key) {
618                                 g_print ("found possible ref to %p in object %p (%s) at offset %td\n",
619                                                 key, start, sgen_safe_name (start), i * sizeof (mword));
620                         }
621                 }
622         }
623 }
624
625 static void
626 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
627 {
628         scan_object_for_specific_ref (obj, key);
629 }
630
631 static void
632 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
633 {
634         if (key != obj)
635                 return;
636         g_print ("found ref to %p in root record %p\n", key, root);
637 }
638
639 static MonoObject *check_key = NULL;
640 static RootRecord *check_root = NULL;
641
642 static void
643 check_root_obj_specific_ref_from_marker (void **obj, void *gc_data)
644 {
645         check_root_obj_specific_ref (check_root, check_key, *obj);
646 }
647
648 static void
649 scan_roots_for_specific_ref (MonoObject *key, int root_type)
650 {
651         void **start_root;
652         RootRecord *root;
653         check_key = key;
654
655         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
656                 mword desc = root->root_desc;
657
658                 check_root = root;
659
660                 switch (desc & ROOT_DESC_TYPE_MASK) {
661                 case ROOT_DESC_BITMAP:
662                         desc >>= ROOT_DESC_TYPE_SHIFT;
663                         while (desc) {
664                                 if (desc & 1)
665                                         check_root_obj_specific_ref (root, key, *start_root);
666                                 desc >>= 1;
667                                 start_root++;
668                         }
669                         return;
670                 case ROOT_DESC_COMPLEX: {
671                         gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
672                         int bwords = (int) ((*bitmap_data) - 1);
673                         void **start_run = start_root;
674                         bitmap_data++;
675                         while (bwords-- > 0) {
676                                 gsize bmap = *bitmap_data++;
677                                 void **objptr = start_run;
678                                 while (bmap) {
679                                         if (bmap & 1)
680                                                 check_root_obj_specific_ref (root, key, *objptr);
681                                         bmap >>= 1;
682                                         ++objptr;
683                                 }
684                                 start_run += GC_BITS_PER_WORD;
685                         }
686                         break;
687                 }
688                 case ROOT_DESC_USER: {
689                         MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
690                         marker (start_root, check_root_obj_specific_ref_from_marker, NULL);
691                         break;
692                 }
693                 case ROOT_DESC_RUN_LEN:
694                         g_assert_not_reached ();
695                 default:
696                         g_assert_not_reached ();
697                 }
698         } SGEN_HASH_TABLE_FOREACH_END;
699
700         check_key = NULL;
701         check_root = NULL;
702 }
703
704 void
705 mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise)
706 {
707         void **ptr;
708         RootRecord *root;
709
710         scan_object_for_specific_ref_precise = precise;
711
712         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
713                         (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
714
715         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
716
717         sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
718
719         scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
720         scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
721
722         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], ptr, root) {
723                 while (ptr < (void**)root->end_root) {
724                         check_root_obj_specific_ref (root, *ptr, key);
725                         ++ptr;
726                 }
727         } SGEN_HASH_TABLE_FOREACH_END;
728 }
729
730 static MonoDomain *check_domain = NULL;
731
732 static void
733 check_obj_not_in_domain (void **o)
734 {
735         g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
736 }
737
738
739 static void
740 check_obj_not_in_domain_callback (void **o, void *gc_data)
741 {
742         g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
743 }
744
745 void
746 sgen_scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
747 {
748         void **start_root;
749         RootRecord *root;
750         check_domain = domain;
751         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
752                 mword desc = root->root_desc;
753
754                 /* The MonoDomain struct is allowed to hold
755                    references to objects in its own domain. */
756                 if (start_root == (void**)domain)
757                         continue;
758
759                 switch (desc & ROOT_DESC_TYPE_MASK) {
760                 case ROOT_DESC_BITMAP:
761                         desc >>= ROOT_DESC_TYPE_SHIFT;
762                         while (desc) {
763                                 if ((desc & 1) && *start_root)
764                                         check_obj_not_in_domain (*start_root);
765                                 desc >>= 1;
766                                 start_root++;
767                         }
768                         break;
769                 case ROOT_DESC_COMPLEX: {
770                         gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
771                         int bwords = (int)((*bitmap_data) - 1);
772                         void **start_run = start_root;
773                         bitmap_data++;
774                         while (bwords-- > 0) {
775                                 gsize bmap = *bitmap_data++;
776                                 void **objptr = start_run;
777                                 while (bmap) {
778                                         if ((bmap & 1) && *objptr)
779                                                 check_obj_not_in_domain (*objptr);
780                                         bmap >>= 1;
781                                         ++objptr;
782                                 }
783                                 start_run += GC_BITS_PER_WORD;
784                         }
785                         break;
786                 }
787                 case ROOT_DESC_USER: {
788                         MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
789                         marker (start_root, check_obj_not_in_domain_callback, NULL);
790                         break;
791                 }
792                 case ROOT_DESC_RUN_LEN:
793                         g_assert_not_reached ();
794                 default:
795                         g_assert_not_reached ();
796                 }
797         } SGEN_HASH_TABLE_FOREACH_END;
798
799         check_domain = NULL;
800 }
801
802 static gboolean
803 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
804 {
805         MonoObject *o = (MonoObject*)(obj);
806         MonoObject *ref = (MonoObject*)*(ptr);
807         size_t offset = (char*)(ptr) - (char*)o;
808
809         if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
810                 return TRUE;
811         if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
812                 return TRUE;
813
814 #ifndef DISABLE_REMOTING
815         if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) &&
816                         offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
817                 return TRUE;
818 #endif
819         /* Thread.cached_culture_info */
820         if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
821                         !strcmp (ref->vtable->klass->name, "CultureInfo") &&
822                         !strcmp(o->vtable->klass->name_space, "System") &&
823                         !strcmp(o->vtable->klass->name, "Object[]"))
824                 return TRUE;
825         /*
826          *  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
827          * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
828          * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
829          * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
830          * 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
831          * 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
832          * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
833          * 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
834          * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
835          */
836         if (!strcmp (ref->vtable->klass->name_space, "System") &&
837                         !strcmp (ref->vtable->klass->name, "Byte[]") &&
838                         !strcmp (o->vtable->klass->name_space, "System.IO") &&
839                         !strcmp (o->vtable->klass->name, "MemoryStream"))
840                 return TRUE;
841         /* append_job() in threadpool.c */
842         if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
843                         !strcmp (ref->vtable->klass->name, "AsyncResult") &&
844                         !strcmp (o->vtable->klass->name_space, "System") &&
845                         !strcmp (o->vtable->klass->name, "Object[]") &&
846                         mono_thread_pool_is_queue_array ((MonoArray*) o))
847                 return TRUE;
848         return FALSE;
849 }
850
851 static void
852 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
853 {
854         MonoObject *o = (MonoObject*)(obj);
855         MonoObject *ref = (MonoObject*)*(ptr);
856         size_t offset = (char*)(ptr) - (char*)o;
857         MonoClass *class;
858         MonoClassField *field;
859         char *str;
860
861         if (!ref || ref->vtable->domain == domain)
862                 return;
863         if (is_xdomain_ref_allowed (ptr, obj, domain))
864                 return;
865
866         field = NULL;
867         for (class = o->vtable->klass; class; class = class->parent) {
868                 int i;
869
870                 for (i = 0; i < class->field.count; ++i) {
871                         if (class->fields[i].offset == offset) {
872                                 field = &class->fields[i];
873                                 break;
874                         }
875                 }
876                 if (field)
877                         break;
878         }
879
880         if (ref->vtable->klass == mono_defaults.string_class)
881                 str = mono_string_to_utf8 ((MonoString*)ref);
882         else
883                 str = NULL;
884         g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s)  -  pointed to by:\n",
885                         o, o->vtable->klass->name_space, o->vtable->klass->name,
886                         offset, field ? field->name : "",
887                         ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
888         mono_gc_scan_for_specific_ref (o, TRUE);
889         if (str)
890                 g_free (str);
891 }
892
893 #undef HANDLE_PTR
894 #define HANDLE_PTR(ptr,obj)     check_reference_for_xdomain ((ptr), (obj), domain)
895
896 static void
897 scan_object_for_xdomain_refs (char *start, mword size, void *data)
898 {
899         MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
900
901         #include "sgen-scan-object.h"
902 }
903
904 void
905 sgen_check_for_xdomain_refs (void)
906 {
907         LOSObject *bigobj;
908
909         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
910                         (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
911
912         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
913
914         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
915                 scan_object_for_xdomain_refs (bigobj->data, sgen_los_object_size (bigobj), NULL);
916 }
917
918 static int
919 compare_xrefs (const void *a_ptr, const void *b_ptr)
920 {
921         const MonoGCBridgeXRef *a = a_ptr;
922         const MonoGCBridgeXRef *b = b_ptr;
923
924         if (a->src_scc_index < b->src_scc_index)
925                 return -1;
926         if (a->src_scc_index > b->src_scc_index)
927                 return 1;
928
929         if (a->dst_scc_index < b->dst_scc_index)
930                 return -1;
931         if (a->dst_scc_index > b->dst_scc_index)
932                 return 1;
933
934         return 0;
935 }
936
937 /*
938 static void
939 dump_processor_state (SgenBridgeProcessor *p)
940 {
941         int i;
942
943         printf ("------\n");
944         printf ("SCCS %d\n", p->num_sccs);
945         for (i = 0; i < p->num_sccs; ++i) {
946                 int j;
947                 MonoGCBridgeSCC *scc = p->api_sccs [i];
948                 printf ("\tSCC %d:", i);
949                 for (j = 0; j < scc->num_objs; ++j) {
950                         MonoObject *obj = scc->objs [j];
951                         printf (" %p", obj);
952                 }
953                 printf ("\n");
954         }
955
956         printf ("XREFS %d\n", p->num_xrefs);
957         for (i = 0; i < p->num_xrefs; ++i)
958                 printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index);
959
960         printf ("-------\n");
961 }
962 */
963
964 gboolean
965 sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcessor *b)
966 {
967         int i;
968         SgenHashTable obj_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), mono_aligned_addr_hash, NULL);
969         SgenHashTable b_scc_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), g_direct_hash, NULL);
970         MonoGCBridgeXRef *a_xrefs, *b_xrefs;
971         size_t xrefs_alloc_size;
972
973         // dump_processor_state (a);
974         // dump_processor_state (b);
975
976         if (a->num_sccs != b->num_sccs)
977                 g_error ("SCCS count expected %d but got %d", a->num_sccs, b->num_sccs);
978         if (a->num_xrefs != b->num_xrefs)
979                 g_error ("SCCS count expected %d but got %d", a->num_xrefs, b->num_xrefs);
980
981         /*
982          * First we build a hash of each object in `a` to its respective SCC index within
983          * `a`.  Along the way we also assert that no object is more than one SCC.
984          */
985         for (i = 0; i < a->num_sccs; ++i) {
986                 int j;
987                 MonoGCBridgeSCC *scc = a->api_sccs [i];
988
989                 g_assert (scc->num_objs > 0);
990
991                 for (j = 0; j < scc->num_objs; ++j) {
992                         MonoObject *obj = scc->objs [j];
993                         gboolean new_entry = sgen_hash_table_replace (&obj_to_a_scc, obj, &i, NULL);
994                         g_assert (new_entry);
995                 }
996         }
997
998         /*
999          * Now we check whether each of the objects in `b` are in `a`, and whether the SCCs
1000          * of `b` contain the same sets of objects as those of `a`.
1001          *
1002          * While we're doing this, build a hash table to map from `b` SCC indexes to `a` SCC
1003          * indexes.
1004          */
1005         for (i = 0; i < b->num_sccs; ++i) {
1006                 MonoGCBridgeSCC *scc = b->api_sccs [i];
1007                 MonoGCBridgeSCC *a_scc;
1008                 int *a_scc_index_ptr;
1009                 int a_scc_index;
1010                 int j;
1011                 gboolean new_entry;
1012
1013                 g_assert (scc->num_objs > 0);
1014                 a_scc_index_ptr = sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [0]);
1015                 g_assert (a_scc_index_ptr);
1016                 a_scc_index = *a_scc_index_ptr;
1017
1018                 //g_print ("A SCC %d -> B SCC %d\n", a_scc_index, i);
1019
1020                 a_scc = a->api_sccs [a_scc_index];
1021                 g_assert (a_scc->num_objs == scc->num_objs);
1022
1023                 for (j = 1; j < scc->num_objs; ++j) {
1024                         a_scc_index_ptr = sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [j]);
1025                         g_assert (a_scc_index_ptr);
1026                         g_assert (*a_scc_index_ptr == a_scc_index);
1027                 }
1028
1029                 new_entry = sgen_hash_table_replace (&b_scc_to_a_scc, GINT_TO_POINTER (i), &a_scc_index, NULL);
1030                 g_assert (new_entry);
1031         }
1032
1033         /*
1034          * Finally, check that we have the same xrefs.  We do this by making copies of both
1035          * xref arrays, and replacing the SCC indexes in the copy for `b` with the
1036          * corresponding indexes in `a`.  Then we sort both arrays and assert that they're
1037          * the same.
1038          *
1039          * At the same time, check that no xref is self-referential and that there are no
1040          * duplicate ones.
1041          */
1042
1043         xrefs_alloc_size = a->num_xrefs * sizeof (MonoGCBridgeXRef);
1044         a_xrefs = sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
1045         b_xrefs = sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
1046
1047         memcpy (a_xrefs, a->api_xrefs, xrefs_alloc_size);
1048         for (i = 0; i < b->num_xrefs; ++i) {
1049                 MonoGCBridgeXRef *xref = &b->api_xrefs [i];
1050                 int *scc_index_ptr;
1051
1052                 g_assert (xref->src_scc_index != xref->dst_scc_index);
1053
1054                 scc_index_ptr = sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->src_scc_index));
1055                 g_assert (scc_index_ptr);
1056                 b_xrefs [i].src_scc_index = *scc_index_ptr;
1057
1058                 scc_index_ptr = sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->dst_scc_index));
1059                 g_assert (scc_index_ptr);
1060                 b_xrefs [i].dst_scc_index = *scc_index_ptr;
1061         }
1062
1063         qsort (a_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
1064         qsort (b_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
1065
1066         for (i = 0; i < a->num_xrefs; ++i) {
1067                 g_assert (a_xrefs [i].src_scc_index == b_xrefs [i].src_scc_index);
1068                 g_assert (a_xrefs [i].dst_scc_index == b_xrefs [i].dst_scc_index);
1069         }
1070
1071         sgen_hash_table_clean (&obj_to_a_scc);
1072         sgen_hash_table_clean (&b_scc_to_a_scc);
1073         sgen_free_internal_dynamic (a_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
1074         sgen_free_internal_dynamic (b_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
1075
1076         return TRUE;
1077 }
1078
1079 #endif /*HAVE_SGEN_GC*/