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