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