Merge pull request #2780 from alexanderkyte/seq_point_optimize
[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 (!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         SGEN_ASSERT (0, cards, "we must have mod union for marked major objects");
247
248 #include "sgen-scan-object.h"
249 }
250
251 void
252 sgen_check_mod_union_consistency (void)
253 {
254         missing_remsets = FALSE;
255
256         major_collector.iterate_objects (ITERATE_OBJECTS_ALL, (IterateObjectCallbackFunc)check_mod_union_callback, (void*)FALSE);
257
258         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_mod_union_callback, (void*)TRUE);
259
260         if (!binary_protocol_is_enabled ())
261                 g_assert (!missing_remsets);
262 }
263
264 #undef HANDLE_PTR
265 #define HANDLE_PTR(ptr,obj)     do {                                    \
266                 if (*(ptr) && !LOAD_VTABLE (*(ptr)))                                            \
267                         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)); \
268         } while (0)
269
270 static void
271 check_major_refs_callback (GCObject *obj, size_t size, void *dummy)
272 {
273         char *start = (char*)obj;
274         SgenDescriptor desc = sgen_obj_get_descriptor (obj);
275
276 #include "sgen-scan-object.h"
277 }
278
279 void
280 sgen_check_major_refs (void)
281 {
282         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
283         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
284 }
285
286 /* Check that the reference is valid */
287 #undef HANDLE_PTR
288 #define HANDLE_PTR(ptr,obj)     do {    \
289                 if (*(ptr)) {   \
290                         g_assert (sgen_client_vtable_get_namespace (SGEN_LOAD_VTABLE_UNCHECKED (*(ptr))));      \
291                 }       \
292         } while (0)
293
294 /*
295  * check_object:
296  *
297  *   Perform consistency check on an object. Currently we only check that the
298  * reference fields are valid.
299  */
300 void
301 check_object (GCObject *obj)
302 {
303         char *start = (char*)obj;
304         SgenDescriptor desc;
305
306         if (!start)
307                 return;
308
309         desc = sgen_obj_get_descriptor (obj);
310
311 #include "sgen-scan-object.h"
312 }
313
314
315 static GCObject **valid_nursery_objects;
316 static int valid_nursery_object_count;
317 static gboolean broken_heap;
318
319 static void 
320 setup_mono_sgen_scan_area_with_callback (GCObject *object, size_t size, void *data)
321 {
322         valid_nursery_objects [valid_nursery_object_count++] = object;
323 }
324
325 static void
326 setup_valid_nursery_objects (void)
327 {
328         if (!valid_nursery_objects)
329                 valid_nursery_objects = (GCObject **)sgen_alloc_os_memory (DEFAULT_NURSERY_SIZE, (SgenAllocFlags)(SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE), "debugging data");
330         valid_nursery_object_count = 0;
331         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE, FALSE);
332 }
333
334 static gboolean
335 find_object_in_nursery_dump (char *object)
336 {
337         int first = 0, last = valid_nursery_object_count;
338         while (first < last) {
339                 int middle = first + ((last - first) >> 1);
340                 if (object == (char*)valid_nursery_objects [middle])
341                         return TRUE;
342
343                 if (object < (char*)valid_nursery_objects [middle])
344                         last = middle;
345                 else
346                         first = middle + 1;
347         }
348         g_assert (first == last);
349         return FALSE;
350 }
351
352 static void
353 iterate_valid_nursery_objects (IterateObjectCallbackFunc callback, void *data)
354 {
355         int i;
356         for (i = 0; i < valid_nursery_object_count; ++i) {
357                 GCObject *obj = valid_nursery_objects [i];
358                 callback (obj, safe_object_get_size (obj), data);
359         }
360 }
361
362 static char*
363 describe_nursery_ptr (char *ptr, gboolean need_setup)
364 {
365         int i;
366
367         if (need_setup)
368                 setup_valid_nursery_objects ();
369
370         for (i = 0; i < valid_nursery_object_count - 1; ++i) {
371                 if ((char*)valid_nursery_objects [i + 1] > ptr)
372                         break;
373         }
374
375         if (i >= valid_nursery_object_count || (char*)valid_nursery_objects [i] + safe_object_get_size (valid_nursery_objects [i]) < ptr) {
376                 SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)");
377                 return NULL;
378         } else {
379                 GCObject *obj = valid_nursery_objects [i];
380                 if ((char*)obj == ptr)
381                         SGEN_LOG (0, "nursery-ptr %p", obj);
382                 else
383                         SGEN_LOG (0, "nursery-ptr %p (interior-ptr offset %zd)", obj, ptr - (char*)obj);
384                 return (char*)obj;
385         }
386 }
387
388 static gboolean
389 is_valid_object_pointer (char *object)
390 {
391         if (sgen_ptr_in_nursery (object))
392                 return find_object_in_nursery_dump (object);
393         
394         if (sgen_los_is_valid_object (object))
395                 return TRUE;
396
397         if (major_collector.is_valid_object (object))
398                 return TRUE;
399         return FALSE;
400 }
401
402 static void
403 bad_pointer_spew (char *obj, char **slot)
404 {
405         char *ptr = *slot;
406         GCVTable vtable = LOAD_VTABLE ((GCObject*)obj);
407
408         SGEN_LOG (0, "Invalid object pointer %p at offset %zd in object %p (%s.%s):", ptr,
409                         (char*)slot - obj,
410                         obj, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
411         describe_pointer (ptr, FALSE);
412         broken_heap = TRUE;
413 }
414
415 static void
416 missing_remset_spew (char *obj, char **slot)
417 {
418         char *ptr = *slot;
419         GCVTable vtable = LOAD_VTABLE ((GCObject*)obj);
420
421         SGEN_LOG (0, "Oldspace->newspace reference %p at offset %zd in object %p (%s.%s) not found in remsets.",
422                         ptr, (char*)slot - obj, obj, 
423                         sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
424
425         broken_heap = TRUE;
426 }
427
428 /*
429 FIXME Flag missing remsets due to pinning as non fatal
430 */
431 #undef HANDLE_PTR
432 #define HANDLE_PTR(ptr,obj)     do {                                    \
433                 if (*(char**)ptr) {                                     \
434                         if (!is_valid_object_pointer (*(char**)ptr)) {  \
435                                 bad_pointer_spew ((char*)obj, (char**)ptr); \
436                         } else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) { \
437                                 if (!allow_missing_pinned && !SGEN_OBJECT_IS_PINNED (*(ptr)) && !sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup (*(ptr))) \
438                                         missing_remset_spew ((char*)obj, (char**)ptr); \
439                         }                                               \
440                 }                                                       \
441         } while (0)
442
443 static void
444 verify_object_pointers_callback (GCObject *obj, size_t size, void *data)
445 {
446         char *start = (char*)obj;
447         gboolean allow_missing_pinned = (gboolean) (size_t) data;
448         SgenDescriptor desc = sgen_obj_get_descriptor (obj);
449
450 #include "sgen-scan-object.h"
451 }
452
453 /*
454 FIXME:
455 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
456 depend on OP_DUMMY_USE.
457 */
458 void
459 sgen_check_whole_heap (gboolean allow_missing_pinned)
460 {
461         setup_valid_nursery_objects ();
462
463         broken_heap = FALSE;
464         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned, FALSE, TRUE);
465         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
466         sgen_los_iterate_objects (verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
467
468         g_assert (!broken_heap);
469 }
470
471 static gboolean
472 ptr_in_heap (char *object)
473 {
474         if (sgen_ptr_in_nursery (object))
475                 return TRUE;
476         
477         if (sgen_los_is_valid_object (object))
478                 return TRUE;
479
480         if (major_collector.is_valid_object (object))
481                 return TRUE;
482         return FALSE;
483 }
484
485 /*
486  * sgen_check_objref:
487  *   Do consistency checks on the object reference OBJ. Assert on failure.
488  */
489 void
490 sgen_check_objref (char *obj)
491 {
492         g_assert (ptr_in_heap (obj));
493 }
494
495 static void
496 find_pinning_ref_from_thread (char *obj, size_t size)
497 {
498 #ifndef SGEN_WITHOUT_MONO
499         char *endobj = obj + size;
500
501         FOREACH_THREAD (info) {
502                 mword *ctxstart, *ctxcurrent, *ctxend;
503                 char **start = (char**)info->client_info.stack_start;
504                 if (info->client_info.skip || info->client_info.gc_disabled)
505                         continue;
506                 while (start < (char**)info->client_info.stack_end) {
507                         if (*start >= obj && *start < endobj)
508                                 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);
509                         start++;
510                 }
511
512                 for (ctxstart = ctxcurrent = (mword*) &info->client_info.ctx, ctxend = (mword*) (&info->client_info.ctx + 1); ctxcurrent < ctxend; ctxcurrent ++) {
513                         mword w = *ctxcurrent;
514
515                         if (w >= (mword)obj && w < (mword)obj + size)
516                                 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));
517                 }
518         } FOREACH_THREAD_END
519 #endif
520 }
521
522 /*
523  * Debugging function: find in the conservative roots where @obj is being pinned.
524  */
525 static G_GNUC_UNUSED void
526 find_pinning_reference (char *obj, size_t size)
527 {
528         char **start;
529         RootRecord *root;
530         char *endobj = obj + size;
531
532         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_NORMAL], char **, start, RootRecord *, root) {
533                 /* if desc is non-null it has precise info */
534                 if (!root->root_desc) {
535                         while (start < (char**)root->end_root) {
536                                 if (*start >= obj && *start < endobj) {
537                                         SGEN_LOG (0, "Object %p referenced in pinned roots %p-%p\n", obj, start, root->end_root);
538                                 }
539                                 start++;
540                         }
541                 }
542         } SGEN_HASH_TABLE_FOREACH_END;
543
544         find_pinning_ref_from_thread (obj, size);
545 }
546
547 #undef HANDLE_PTR
548 #define HANDLE_PTR(ptr,obj)     do {                                    \
549                 char* __target = *(char**)ptr;                          \
550                 if (__target) {                                         \
551                         if (sgen_ptr_in_nursery (__target)) {           \
552                                 g_assert (!SGEN_OBJECT_IS_FORWARDED (__target)); \
553                         } else {                                        \
554                                 mword __size = sgen_safe_object_get_size ((GCObject*)__target); \
555                                 if (__size <= SGEN_MAX_SMALL_OBJ_SIZE)  \
556                                         g_assert (major_collector.is_object_live ((GCObject*)__target)); \
557                                 else                                    \
558                                         g_assert (sgen_los_object_is_pinned ((GCObject*)__target)); \
559                         }                                               \
560                 }                                                       \
561         } while (0)
562
563 static void
564 check_marked_callback (GCObject *obj, size_t size, void *dummy)
565 {
566         char *start = (char*)obj;
567         gboolean flag = (gboolean) (size_t) dummy;
568         SgenDescriptor desc;
569
570         if (sgen_ptr_in_nursery (start)) {
571                 if (flag)
572                         SGEN_ASSERT (0, SGEN_OBJECT_IS_PINNED (obj), "All objects remaining in the nursery must be pinned");
573         } else if (flag) {
574                 if (!sgen_los_object_is_pinned (obj))
575                         return;
576         } else {
577                 if (!major_collector.is_object_live (obj))
578                         return;
579         }
580
581         desc = sgen_obj_get_descriptor_safe (obj);
582
583 #include "sgen-scan-object.h"
584 }
585
586 void
587 sgen_check_heap_marked (gboolean nursery_must_be_pinned)
588 {
589         setup_valid_nursery_objects ();
590
591         iterate_valid_nursery_objects (check_marked_callback, (void*)(size_t)nursery_must_be_pinned);
592         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, check_marked_callback, (void*)FALSE);
593         sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
594 }
595
596 static void
597 check_nursery_objects_pinned_callback (char *obj, size_t size, void *data /* ScanCopyContext *ctx */)
598 {
599         gboolean pinned = (gboolean) (size_t) data;
600
601         g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
602         if (pinned)
603                 g_assert (SGEN_OBJECT_IS_PINNED (obj));
604         else
605                 g_assert (!SGEN_OBJECT_IS_PINNED (obj));
606 }
607
608 void
609 sgen_check_nursery_objects_pinned (gboolean pinned)
610 {
611         sgen_clear_nursery_fragments ();
612         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
613                         (IterateObjectCallbackFunc)check_nursery_objects_pinned_callback, (void*) (size_t) pinned /* (void*)&ctx */, FALSE, TRUE);
614 }
615
616 static void
617 verify_scan_starts (char *start, char *end)
618 {
619         size_t i;
620
621         for (i = 0; i < nursery_section->num_scan_start; ++i) {
622                 char *addr = nursery_section->scan_starts [i];
623                 if (addr > start && addr < end)
624                         SGEN_LOG (0, "NFC-BAD SCAN START [%zu] %p for obj [%p %p]", i, addr, start, end);
625         }
626 }
627
628 void
629 sgen_debug_verify_nursery (gboolean do_dump_nursery_content)
630 {
631         char *start, *end, *cur, *hole_start;
632
633         if (nursery_canaries_enabled ())
634                 SGEN_LOG (0, "Checking nursery canaries...");
635
636         /*This cleans up unused fragments */
637         sgen_nursery_allocator_prepare_for_pinning ();
638
639         hole_start = start = cur = sgen_get_nursery_start ();
640         end = sgen_get_nursery_end ();
641
642         while (cur < end) {
643                 size_t ss, size;
644                 gboolean is_array_fill;
645
646                 if (!*(void**)cur) {
647                         cur += sizeof (void*);
648                         continue;
649                 }
650
651                 if (object_is_forwarded (cur))
652                         SGEN_LOG (0, "FORWARDED OBJ %p", cur);
653                 else if (object_is_pinned (cur))
654                         SGEN_LOG (0, "PINNED OBJ %p", cur);
655
656                 ss = safe_object_get_size ((GCObject*)cur);
657                 size = SGEN_ALIGN_UP (ss);
658                 verify_scan_starts (cur, cur + size);
659                 is_array_fill = sgen_client_object_is_array_fill ((GCObject*)cur);
660                 if (do_dump_nursery_content) {
661                         GCVTable vtable = SGEN_LOAD_VTABLE ((GCObject*)cur);
662                         if (cur > hole_start)
663                                 SGEN_LOG (0, "HOLE [%p %p %d]", hole_start, cur, (int)(cur - hole_start));
664                         SGEN_LOG (0, "OBJ  [%p %p %d %d %s.%s %d]", cur, cur + size, (int)size, (int)ss,
665                                         sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable),
666                                         is_array_fill);
667                 }
668                 if (nursery_canaries_enabled () && !is_array_fill) {
669                         CHECK_CANARY_FOR_OBJECT ((GCObject*)cur, TRUE);
670                         CANARIFY_SIZE (size);
671                 }
672                 cur += size;
673                 hole_start = cur;
674         }
675 }
676
677 /*
678  * Checks that no objects in the nursery are fowarded or pinned.  This
679  * is a precondition to restarting the mutator while doing a
680  * concurrent collection.  Note that we don't clear fragments because
681  * we depend on that having happened earlier.
682  */
683 void
684 sgen_debug_check_nursery_is_clean (void)
685 {
686         char *end, *cur;
687
688         cur = sgen_get_nursery_start ();
689         end = sgen_get_nursery_end ();
690
691         while (cur < end) {
692                 size_t size;
693
694                 if (!*(void**)cur) {
695                         cur += sizeof (void*);
696                         continue;
697                 }
698
699                 g_assert (!object_is_forwarded (cur));
700                 g_assert (!object_is_pinned (cur));
701
702                 size = SGEN_ALIGN_UP (safe_object_get_size ((GCObject*)cur));
703                 verify_scan_starts (cur, cur + size);
704
705                 cur += size;
706         }
707 }
708
709 static gboolean scan_object_for_specific_ref_precise = TRUE;
710
711 #undef HANDLE_PTR
712 #define HANDLE_PTR(ptr,obj) do {                                        \
713                 if ((GCObject*)*(ptr) == key) {                         \
714                         GCVTable vtable = SGEN_LOAD_VTABLE (*(ptr));    \
715                         g_print ("found ref to %p in object %p (%s.%s) at offset %zd\n", \
716                                         key, (obj), sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), ((char*)(ptr) - (char*)(obj))); \
717                 }                                                       \
718         } while (0)
719
720 static void
721 scan_object_for_specific_ref (GCObject *obj, GCObject *key)
722 {
723         GCObject *forwarded;
724
725         if ((forwarded = SGEN_OBJECT_IS_FORWARDED (obj)))
726                 obj = forwarded;
727
728         if (scan_object_for_specific_ref_precise) {
729                 char *start = (char*)obj;
730                 SgenDescriptor desc = sgen_obj_get_descriptor_safe (obj);
731                 #include "sgen-scan-object.h"
732         } else {
733                 mword *words = (mword*)obj;
734                 size_t size = safe_object_get_size (obj);
735                 int i;
736                 for (i = 0; i < size / sizeof (mword); ++i) {
737                         if (words [i] == (mword)key) {
738                                 GCVTable vtable = SGEN_LOAD_VTABLE (obj);
739                                 g_print ("found possible ref to %p in object %p (%s.%s) at offset %zd\n",
740                                                 key, obj, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), i * sizeof (mword));
741                         }
742                 }
743         }
744 }
745
746 static void
747 scan_object_for_specific_ref_callback (GCObject *obj, size_t size, GCObject *key)
748 {
749         scan_object_for_specific_ref (obj, key);
750 }
751
752 static void
753 check_root_obj_specific_ref (RootRecord *root, GCObject *key, GCObject *obj)
754 {
755         if (key != obj)
756                 return;
757         g_print ("found ref to %p in root record %p\n", key, root);
758 }
759
760 static GCObject *check_key = NULL;
761 static RootRecord *check_root = NULL;
762
763 static void
764 check_root_obj_specific_ref_from_marker (GCObject **obj, void *gc_data)
765 {
766         check_root_obj_specific_ref (check_root, check_key, *obj);
767 }
768
769 static void
770 scan_roots_for_specific_ref (GCObject *key, int root_type)
771 {
772         void **start_root;
773         RootRecord *root;
774         check_key = key;
775
776         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], void **, start_root, RootRecord *, root) {
777                 SgenDescriptor desc = root->root_desc;
778
779                 check_root = root;
780
781                 switch (desc & ROOT_DESC_TYPE_MASK) {
782                 case ROOT_DESC_BITMAP:
783                         desc >>= ROOT_DESC_TYPE_SHIFT;
784                         while (desc) {
785                                 if (desc & 1)
786                                         check_root_obj_specific_ref (root, key, (GCObject *)*start_root);
787                                 desc >>= 1;
788                                 start_root++;
789                         }
790                         return;
791                 case ROOT_DESC_COMPLEX: {
792                         gsize *bitmap_data = (gsize *)sgen_get_complex_descriptor_bitmap (desc);
793                         int bwords = (int) ((*bitmap_data) - 1);
794                         void **start_run = start_root;
795                         bitmap_data++;
796                         while (bwords-- > 0) {
797                                 gsize bmap = *bitmap_data++;
798                                 void **objptr = start_run;
799                                 while (bmap) {
800                                         if (bmap & 1)
801                                                 check_root_obj_specific_ref (root, key, (GCObject *)*objptr);
802                                         bmap >>= 1;
803                                         ++objptr;
804                                 }
805                                 start_run += GC_BITS_PER_WORD;
806                         }
807                         break;
808                 }
809                 case ROOT_DESC_USER: {
810                         SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
811                         marker (start_root, check_root_obj_specific_ref_from_marker, NULL);
812                         break;
813                 }
814                 case ROOT_DESC_RUN_LEN:
815                         g_assert_not_reached ();
816                 default:
817                         g_assert_not_reached ();
818                 }
819         } SGEN_HASH_TABLE_FOREACH_END;
820
821         check_key = NULL;
822         check_root = NULL;
823 }
824
825 void
826 mono_gc_scan_for_specific_ref (GCObject *key, gboolean precise)
827 {
828         void **ptr;
829         RootRecord *root;
830
831         scan_object_for_specific_ref_precise = precise;
832
833         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
834                         (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE, FALSE);
835
836         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
837
838         sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
839
840         scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
841         scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
842
843         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], void **, ptr, RootRecord *, root) {
844                 while (ptr < (void**)root->end_root) {
845                         check_root_obj_specific_ref (root, (GCObject *)*ptr, key);
846                         ++ptr;
847                 }
848         } SGEN_HASH_TABLE_FOREACH_END;
849
850         if (sgen_is_world_stopped ())
851                 find_pinning_ref_from_thread ((char*)key, sizeof (GCObject));
852 }
853
854 #ifndef SGEN_WITHOUT_MONO
855
856 static MonoDomain *check_domain = NULL;
857
858 static void
859 check_obj_not_in_domain (MonoObject **o)
860 {
861         g_assert (((*o))->vtable->domain != check_domain);
862 }
863
864
865 static void
866 check_obj_not_in_domain_callback (GCObject **o, void *gc_data)
867 {
868         g_assert ((*o)->vtable->domain != check_domain);
869 }
870
871 void
872 sgen_scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
873 {
874         void **start_root;
875         RootRecord *root;
876         check_domain = domain;
877         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], void **, start_root, RootRecord *, root) {
878                 SgenDescriptor desc = root->root_desc;
879
880                 /* The MonoDomain struct is allowed to hold
881                    references to objects in its own domain. */
882                 if (start_root == (void**)domain)
883                         continue;
884
885                 switch (desc & ROOT_DESC_TYPE_MASK) {
886                 case ROOT_DESC_BITMAP:
887                         desc >>= ROOT_DESC_TYPE_SHIFT;
888                         while (desc) {
889                                 if ((desc & 1) && *start_root)
890                                         check_obj_not_in_domain ((MonoObject **)*start_root);
891                                 desc >>= 1;
892                                 start_root++;
893                         }
894                         break;
895                 case ROOT_DESC_COMPLEX: {
896                         gsize *bitmap_data = (gsize *)sgen_get_complex_descriptor_bitmap (desc);
897                         int bwords = (int)((*bitmap_data) - 1);
898                         void **start_run = start_root;
899                         bitmap_data++;
900                         while (bwords-- > 0) {
901                                 gsize bmap = *bitmap_data++;
902                                 void **objptr = start_run;
903                                 while (bmap) {
904                                         if ((bmap & 1) && *objptr)
905                                                 check_obj_not_in_domain ((MonoObject **)*objptr);
906                                         bmap >>= 1;
907                                         ++objptr;
908                                 }
909                                 start_run += GC_BITS_PER_WORD;
910                         }
911                         break;
912                 }
913                 case ROOT_DESC_USER: {
914                         SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
915                         marker (start_root, check_obj_not_in_domain_callback, NULL);
916                         break;
917                 }
918                 case ROOT_DESC_RUN_LEN:
919                         g_assert_not_reached ();
920                 default:
921                         g_assert_not_reached ();
922                 }
923         } SGEN_HASH_TABLE_FOREACH_END;
924
925         check_domain = NULL;
926 }
927
928 static gboolean
929 is_xdomain_ref_allowed (GCObject **ptr, GCObject *obj, MonoDomain *domain)
930 {
931         MonoObject *o = (MonoObject*)(obj);
932         MonoObject *ref = *ptr;
933         size_t offset = (char*)(ptr) - (char*)o;
934
935         if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
936                 return TRUE;
937         if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
938                 return TRUE;
939
940 #ifndef DISABLE_REMOTING
941         if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) &&
942                         offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
943                 return TRUE;
944 #endif
945         /* Thread.cached_culture_info */
946         if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
947                         !strcmp (ref->vtable->klass->name, "CultureInfo") &&
948                         !strcmp(o->vtable->klass->name_space, "System") &&
949                         !strcmp(o->vtable->klass->name, "Object[]"))
950                 return TRUE;
951         /*
952          *  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
953          * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
954          * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
955          * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
956          * 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
957          * 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
958          * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
959          * 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
960          * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
961          */
962         if (!strcmp (ref->vtable->klass->name_space, "System") &&
963                         !strcmp (ref->vtable->klass->name, "Byte[]") &&
964                         !strcmp (o->vtable->klass->name_space, "System.IO") &&
965                         !strcmp (o->vtable->klass->name, "MemoryStream"))
966                 return TRUE;
967         return FALSE;
968 }
969
970 static void
971 check_reference_for_xdomain (GCObject **ptr, GCObject *obj, MonoDomain *domain)
972 {
973         MonoObject *ref = *ptr;
974         size_t offset = (char*)(ptr) - (char*)obj;
975         MonoClass *klass;
976         MonoClassField *field;
977         char *str;
978
979         if (!ref || ref->vtable->domain == domain)
980                 return;
981         if (is_xdomain_ref_allowed (ptr, obj, domain))
982                 return;
983
984         field = NULL;
985         for (klass = obj->vtable->klass; klass; klass = klass->parent) {
986                 int i;
987
988                 for (i = 0; i < klass->field.count; ++i) {
989                         if (klass->fields[i].offset == offset) {
990                                 field = &klass->fields[i];
991                                 break;
992                         }
993                 }
994                 if (field)
995                         break;
996         }
997
998         if (ref->vtable->klass == mono_defaults.string_class) {
999                 MonoError error;
1000                 str = mono_string_to_utf8_checked ((MonoString*)ref, &error);
1001                 mono_error_cleanup (&error);
1002         } else
1003                 str = NULL;
1004         g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s)  -  pointed to by:\n",
1005                         obj, obj->vtable->klass->name_space, obj->vtable->klass->name,
1006                         offset, field ? field->name : "",
1007                         ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1008         mono_gc_scan_for_specific_ref (obj, TRUE);
1009         if (str)
1010                 g_free (str);
1011 }
1012
1013 #undef HANDLE_PTR
1014 #define HANDLE_PTR(ptr,obj)     check_reference_for_xdomain ((ptr), (obj), domain)
1015
1016 static void
1017 scan_object_for_xdomain_refs (GCObject *obj, mword size, void *data)
1018 {
1019         char *start = (char*)obj;
1020         MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
1021         MonoDomain *domain = vt->domain;
1022         SgenDescriptor desc = sgen_vtable_get_descriptor (vt);
1023
1024         #include "sgen-scan-object.h"
1025 }
1026
1027 void
1028 sgen_check_for_xdomain_refs (void)
1029 {
1030         LOSObject *bigobj;
1031
1032         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1033                         (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE, TRUE);
1034
1035         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1036
1037         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1038                 scan_object_for_xdomain_refs ((GCObject*)bigobj->data, sgen_los_object_size (bigobj), NULL);
1039 }
1040
1041 #endif
1042
1043 /* If not null, dump the heap after each collection into this file */
1044 static FILE *heap_dump_file = NULL;
1045
1046 void
1047 sgen_dump_occupied (char *start, char *end, char *section_start)
1048 {
1049         fprintf (heap_dump_file, "<occupied offset=\"%zd\" size=\"%zd\"/>\n", start - section_start, end - start);
1050 }
1051
1052 void
1053 sgen_dump_section (GCMemSection *section, const char *type)
1054 {
1055         char *start = section->data;
1056         char *end = section->data + section->size;
1057         char *occ_start = NULL;
1058
1059         fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
1060
1061         while (start < end) {
1062                 guint size;
1063                 //GCVTable vt;
1064                 //MonoClass *class;
1065
1066                 if (!*(void**)start) {
1067                         if (occ_start) {
1068                                 sgen_dump_occupied (occ_start, start, section->data);
1069                                 occ_start = NULL;
1070                         }
1071                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1072                         continue;
1073                 }
1074                 g_assert (start < section->next_data);
1075
1076                 if (!occ_start)
1077                         occ_start = start;
1078
1079                 //vt = SGEN_LOAD_VTABLE (start);
1080                 //class = vt->klass;
1081
1082                 size = SGEN_ALIGN_UP (safe_object_get_size ((GCObject*) start));
1083
1084                 /*
1085                 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
1086                                 start - section->data,
1087                                 vt->klass->name_space, vt->klass->name,
1088                                 size);
1089                 */
1090
1091                 start += size;
1092         }
1093         if (occ_start)
1094                 sgen_dump_occupied (occ_start, start, section->data);
1095
1096         fprintf (heap_dump_file, "</section>\n");
1097 }
1098
1099 static void
1100 dump_object (GCObject *obj, gboolean dump_location)
1101 {
1102 #ifndef SGEN_WITHOUT_MONO
1103         static char class_name [1024];
1104
1105         MonoClass *klass = mono_object_class (obj);
1106         int i, j;
1107
1108         /*
1109          * Python's XML parser is too stupid to parse angle brackets
1110          * in strings, so we just ignore them;
1111          */
1112         i = j = 0;
1113         while (klass->name [i] && j < sizeof (class_name) - 1) {
1114                 if (!strchr ("<>\"", klass->name [i]))
1115                         class_name [j++] = klass->name [i];
1116                 ++i;
1117         }
1118         g_assert (j < sizeof (class_name));
1119         class_name [j] = 0;
1120
1121         fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%zd\"",
1122                         klass->name_space, class_name,
1123                         safe_object_get_size (obj));
1124         if (dump_location) {
1125                 const char *location;
1126                 if (sgen_ptr_in_nursery (obj))
1127                         location = "nursery";
1128                 else if (safe_object_get_size (obj) <= SGEN_MAX_SMALL_OBJ_SIZE)
1129                         location = "major";
1130                 else
1131                         location = "LOS";
1132                 fprintf (heap_dump_file, " location=\"%s\"", location);
1133         }
1134         fprintf (heap_dump_file, "/>\n");
1135 #endif
1136 }
1137
1138 void
1139 sgen_debug_enable_heap_dump (const char *filename)
1140 {
1141         heap_dump_file = fopen (filename, "w");
1142         if (heap_dump_file) {
1143                 fprintf (heap_dump_file, "<sgen-dump>\n");
1144                 sgen_pin_stats_enable ();
1145         }
1146 }
1147
1148 void
1149 sgen_debug_dump_heap (const char *type, int num, const char *reason)
1150 {
1151         SgenPointerQueue *pinned_objects;
1152         LOSObject *bigobj;
1153         int i;
1154
1155         if (!heap_dump_file)
1156                 return;
1157
1158         fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
1159         if (reason)
1160                 fprintf (heap_dump_file, " reason=\"%s\"", reason);
1161         fprintf (heap_dump_file, ">\n");
1162 #ifndef SGEN_WITHOUT_MONO
1163         fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
1164 #endif
1165         sgen_dump_internal_mem_usage (heap_dump_file);
1166         fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_STACK));
1167         /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
1168         fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_OTHER));
1169
1170         fprintf (heap_dump_file, "<pinned-objects>\n");
1171         pinned_objects = sgen_pin_stats_get_object_list ();
1172         for (i = 0; i < pinned_objects->next_slot; ++i)
1173                 dump_object ((GCObject *)pinned_objects->data [i], TRUE);
1174         fprintf (heap_dump_file, "</pinned-objects>\n");
1175
1176         sgen_dump_section (nursery_section, "nursery");
1177
1178         major_collector.dump_heap (heap_dump_file);
1179
1180         fprintf (heap_dump_file, "<los>\n");
1181         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1182                 dump_object ((GCObject*)bigobj->data, FALSE);
1183         fprintf (heap_dump_file, "</los>\n");
1184
1185         fprintf (heap_dump_file, "</collection>\n");
1186 }
1187
1188 static GCObject *found_obj;
1189
1190 static void
1191 find_object_for_ptr_callback (GCObject *obj, size_t size, void *user_data)
1192 {
1193         char *ptr = (char *)user_data;
1194
1195         if (ptr >= (char*)obj && ptr < (char*)obj + size) {
1196                 g_assert (!found_obj);
1197                 found_obj = obj;
1198         }
1199 }
1200
1201 /* for use in the debugger */
1202 GCObject*
1203 sgen_find_object_for_ptr (char *ptr)
1204 {
1205         if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
1206                 found_obj = NULL;
1207                 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1208                                 find_object_for_ptr_callback, ptr, TRUE, FALSE);
1209                 if (found_obj)
1210                         return found_obj;
1211         }
1212
1213         found_obj = NULL;
1214         sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
1215         if (found_obj)
1216                 return found_obj;
1217
1218         /*
1219          * Very inefficient, but this is debugging code, supposed to
1220          * be called from gdb, so we don't care.
1221          */
1222         found_obj = NULL;
1223         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, find_object_for_ptr_callback, ptr);
1224         return found_obj;
1225 }
1226
1227 #endif /*HAVE_SGEN_GC*/