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