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