Added new API mono_runtime_set_main_args() and fixed CommandLine for embedders.
[mono.git] / mono / metadata / sgen-debug.c
1 /*
2  * sgen-debug.c: Collector debugging
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *  Rodrigo Kumpera (kumpera@gmail.com)
7  *
8  * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
9  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10  * Copyright 2011 Xamarin, Inc.
11  * Copyright (C) 2012 Xamarin Inc
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Library General Public
15  * License 2.0 as published by the Free Software Foundation;
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License 2.0 along with this library; if not, write to the Free
24  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  */
26
27 #include "config.h"
28 #ifdef HAVE_SGEN_GC
29
30 #include "metadata/sgen-gc.h"
31 #include "metadata/sgen-cardtable.h"
32 #include "metadata/sgen-protocol.h"
33 #include "metadata/sgen-memory-governor.h"
34 #include "metadata/sgen-pinning.h"
35
36 #define LOAD_VTABLE     SGEN_LOAD_VTABLE
37
38 #define object_is_forwarded     SGEN_OBJECT_IS_FORWARDED
39 #define object_is_pinned        SGEN_OBJECT_IS_PINNED
40 #define safe_object_get_size    sgen_safe_object_get_size
41
42 void describe_ptr (char *ptr);
43 void check_object (char *start);
44
45 /*
46  * ######################################################################
47  * ########  Collector debugging
48  * ######################################################################
49  */
50
51 const char*descriptor_types [] = {
52         "INVALID",
53         "run_length",
54         "small_bitmap",
55         "complex",
56         "vector",
57         "large_bitmap",
58         "complex_arr",
59         "complex_ptrfree"
60 };
61
62 static char* describe_nursery_ptr (char *ptr, gboolean need_setup);
63
64 static void
65 describe_pointer (char *ptr, gboolean need_setup)
66 {
67         MonoVTable *vtable;
68         mword desc;
69         int type;
70         char *start;
71         char *forwarded;
72         mword size;
73
74  restart:
75         if (sgen_ptr_in_nursery (ptr)) {
76                 start = describe_nursery_ptr (ptr, need_setup);
77                 if (!start)
78                         return;
79                 ptr = start;
80                 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
81         } else {
82                 if (sgen_ptr_is_in_los (ptr, &start)) {
83                         if (ptr == start)
84                                 printf ("Pointer is the start of object %p in LOS space.\n", start);
85                         else
86                                 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
87                         ptr = start;
88                         mono_sgen_los_describe_pointer (ptr);
89                         vtable = (MonoVTable*)LOAD_VTABLE (ptr);
90                 } else if (major_collector.ptr_is_in_non_pinned_space (ptr, &start)) {
91                         if (ptr == start)
92                                 printf ("Pointer is the start of object %p in oldspace.\n", start);
93                         else if (start)
94                                 printf ("Pointer is at offset 0x%x of object %p in oldspace.\n", (int)(ptr - start), start);
95                         else
96                                 printf ("Pointer inside oldspace.\n");
97                         if (start)
98                                 ptr = start;
99                         vtable = major_collector.describe_pointer (ptr);
100                 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
101                         // FIXME: Handle pointers to the inside of objects
102                         printf ("Pointer is inside a pinned chunk.\n");
103                         vtable = (MonoVTable*)LOAD_VTABLE (ptr);
104                 } else {
105                         printf ("Pointer unknown.\n");
106                         return;
107                 }
108         }
109
110         if (object_is_pinned (ptr))
111                 printf ("Object is pinned.\n");
112
113         if ((forwarded = object_is_forwarded (ptr))) {
114                 printf ("Object is forwarded to %p:\n", forwarded);
115                 ptr = forwarded;
116                 goto restart;
117         }
118
119         printf ("VTable: %p\n", vtable);
120         if (vtable == NULL) {
121                 printf ("VTable is invalid (empty).\n");
122                 goto bridge;
123         }
124         if (sgen_ptr_in_nursery (vtable)) {
125                 printf ("VTable is invalid (points inside nursery).\n");
126                 goto bridge;
127         }
128         printf ("Class: %s\n", vtable->klass->name);
129
130         desc = ((GCVTable*)vtable)->desc;
131         printf ("Descriptor: %lx\n", (long)desc);
132
133         type = desc & 0x7;
134         printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
135
136         size = sgen_safe_object_get_size ((MonoObject*)ptr);
137         printf ("Size: %d\n", (int)size);
138
139  bridge:
140         sgen_bridge_describe_pointer ((MonoObject*)ptr);
141 }
142
143 void
144 describe_ptr (char *ptr)
145 {
146         describe_pointer (ptr, TRUE);
147 }
148
149 static gboolean missing_remsets;
150
151 /*
152  * We let a missing remset slide if the target object is pinned,
153  * because the store might have happened but the remset not yet added,
154  * but in that case the target must be pinned.  We might theoretically
155  * miss some missing remsets this way, but it's very unlikely.
156  */
157 #undef HANDLE_PTR
158 #define HANDLE_PTR(ptr,obj)     do {    \
159         if (*(ptr) && sgen_ptr_in_nursery ((char*)*(ptr))) { \
160                 if (!sgen_get_remset ()->find_address ((char*)(ptr))) { \
161                         SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
162                         binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
163                         if (!object_is_pinned (*(ptr)))                                                         \
164                                 missing_remsets = TRUE;                                                                 \
165                 }                                                                                                                               \
166         }                                                                                                                                       \
167         } while (0)
168
169 /*
170  * Check that each object reference which points into the nursery can
171  * be found in the remembered sets.
172  */
173 static void
174 check_consistency_callback (char *start, size_t size, void *dummy)
175 {
176         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
177         SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, vt->klass->name);
178
179 #include "sgen-scan-object.h"
180 }
181
182 /*
183  * Perform consistency check of the heap.
184  *
185  * Assumes the world is stopped.
186  */
187 void
188 sgen_check_consistency (void)
189 {
190         // Need to add more checks
191
192         missing_remsets = FALSE;
193
194         SGEN_LOG (1, "Begin heap consistency check...");
195
196         // Check that oldspace->newspace pointers are registered with the collector
197         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
198
199         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
200
201         SGEN_LOG (1, "Heap consistency check done.");
202
203         if (!binary_protocol_is_enabled ())
204                 g_assert (!missing_remsets);
205 }
206
207 static gboolean
208 is_major_or_los_object_marked (char *obj)
209 {
210         if (sgen_safe_object_get_size ((MonoObject*)obj) > SGEN_MAX_SMALL_OBJ_SIZE) {
211                 return sgen_los_object_is_pinned (obj);
212         } else {
213                 return sgen_get_major_collector ()->is_object_live (obj);
214         }
215 }
216
217 #undef HANDLE_PTR
218 #define HANDLE_PTR(ptr,obj)     do {    \
219         if (*(ptr) && !sgen_ptr_in_nursery ((char*)*(ptr)) && !is_major_or_los_object_marked ((char*)*(ptr))) { \
220                 if (!sgen_get_remset ()->find_address_with_cards (start, cards, (char*)(ptr))) { \
221                         SGEN_LOG (0, "major->major reference %p at offset %td in object %p (%s.%s) not found in remsets.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
222                         binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
223                 }                                                                                                                               \
224         }                                                                                                                                       \
225         } while (0)
226
227 static void
228 check_mod_union_callback (char *start, size_t size, void *dummy)
229 {
230         gboolean in_los = (gboolean) (size_t) dummy;
231         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
232         guint8 *cards;
233         SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, vt->klass->name);
234
235         if (!is_major_or_los_object_marked (start))
236                 return;
237
238         if (in_los)
239                 cards = sgen_los_header_for_object (start)->cardtable_mod_union;
240         else
241                 cards = sgen_get_major_collector ()->get_cardtable_mod_union_for_object (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 (TRUE, TRUE, (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 %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj));          \
265         } while (0)
266
267 static void
268 check_major_refs_callback (char *start, size_t size, void *dummy)
269 {
270 #include "sgen-scan-object.h"
271 }
272
273 void
274 sgen_check_major_refs (void)
275 {
276         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
277         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
278 }
279
280 /* Check that the reference is valid */
281 #undef HANDLE_PTR
282 #define HANDLE_PTR(ptr,obj)     do {    \
283                 if (*(ptr)) {   \
284                         g_assert (sgen_safe_name (*(ptr)) != NULL);     \
285                 }       \
286         } while (0)
287
288 /*
289  * check_object:
290  *
291  *   Perform consistency check on an object. Currently we only check that the
292  * reference fields are valid.
293  */
294 void
295 check_object (char *start)
296 {
297         if (!start)
298                 return;
299
300 #include "sgen-scan-object.h"
301 }
302
303
304 static char **valid_nursery_objects;
305 static int valid_nursery_object_count;
306 static gboolean broken_heap;
307
308 static void 
309 setup_mono_sgen_scan_area_with_callback (char *object, size_t size, void *data)
310 {
311         valid_nursery_objects [valid_nursery_object_count++] = object;
312 }
313
314 static void
315 setup_valid_nursery_objects (void)
316 {
317         if (!valid_nursery_objects)
318                 valid_nursery_objects = sgen_alloc_os_memory (DEFAULT_NURSERY_SIZE, SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging data");
319         valid_nursery_object_count = 0;
320         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE);
321 }
322
323 static gboolean
324 find_object_in_nursery_dump (char *object)
325 {
326         int first = 0, last = valid_nursery_object_count;
327         while (first < last) {
328                 int middle = first + ((last - first) >> 1);
329                 if (object == valid_nursery_objects [middle])
330                         return TRUE;
331
332                 if (object < valid_nursery_objects [middle])
333                         last = middle;
334                 else
335                         first = middle + 1;
336         }
337         g_assert (first == last);
338         return FALSE;
339 }
340
341 static char*
342 describe_nursery_ptr (char *ptr, gboolean need_setup)
343 {
344         int i;
345
346         if (need_setup)
347                 setup_valid_nursery_objects ();
348
349         for (i = 0; i < valid_nursery_object_count; ++i) {
350                 if (valid_nursery_objects [i] >= ptr)
351                         break;
352         }
353
354         if (i >= valid_nursery_object_count || valid_nursery_objects [i] + safe_object_get_size ((MonoObject *)valid_nursery_objects [i]) < ptr) {
355                 SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)\n");
356                 return NULL;
357         } else {
358                 char *obj = valid_nursery_objects [i];
359                 if (obj == ptr)
360                         SGEN_LOG (0, "nursery-ptr\n");
361                 else
362                         SGEN_LOG (0, "nursery-ptr (interior-ptr offset %td)\n", ptr - obj);
363                 return obj;
364         }
365 }
366
367 static gboolean
368 is_valid_object_pointer (char *object)
369 {
370         if (sgen_ptr_in_nursery (object))
371                 return find_object_in_nursery_dump (object);
372         
373         if (sgen_los_is_valid_object (object))
374                 return TRUE;
375
376         if (major_collector.is_valid_object (object))
377                 return TRUE;
378         return FALSE;
379 }
380
381 static void
382 bad_pointer_spew (char *obj, char **slot)
383 {
384         char *ptr = *slot;
385         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
386
387         SGEN_LOG (0, "Invalid object pointer %p at offset %td in object %p (%s.%s):", ptr,
388                 (char*)slot - obj,
389                 obj, vtable->klass->name_space, vtable->klass->name);
390         describe_pointer (ptr, FALSE);
391         broken_heap = TRUE;
392 }
393
394 static void
395 missing_remset_spew (char *obj, char **slot)
396 {
397         char *ptr = *slot;
398         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
399
400         SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.",
401                 ptr, (char*)slot - obj, obj, 
402                 vtable->klass->name_space, vtable->klass->name);
403
404         broken_heap = TRUE;
405 }
406
407 /*
408 FIXME Flag missing remsets due to pinning as non fatal
409 */
410 #undef HANDLE_PTR
411 #define HANDLE_PTR(ptr,obj)     do {    \
412                 if (*(char**)ptr) {     \
413                         if (!is_valid_object_pointer (*(char**)ptr)) {  \
414                                 bad_pointer_spew ((char*)obj, (char**)ptr);     \
415                         } else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) {   \
416                                 if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup ((char*)*(ptr)) && (!allow_missing_pinned || !SGEN_OBJECT_IS_PINNED ((char*)*(ptr)))) \
417                                 missing_remset_spew ((char*)obj, (char**)ptr);  \
418                         }       \
419         } \
420         } while (0)
421
422 static void
423 verify_object_pointers_callback (char *start, size_t size, void *data)
424 {
425         gboolean allow_missing_pinned = (gboolean) (size_t) data;
426
427 #include "sgen-scan-object.h"
428 }
429
430 /*
431 FIXME:
432 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
433 depend on OP_DUMMY_USE.
434 */
435 void
436 sgen_check_whole_heap (gboolean allow_missing_pinned)
437 {
438         setup_valid_nursery_objects ();
439
440         broken_heap = FALSE;
441         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned, FALSE);
442         major_collector.iterate_objects (TRUE, TRUE, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
443         sgen_los_iterate_objects (verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
444
445         g_assert (!broken_heap);
446 }
447
448 static gboolean
449 ptr_in_heap (char *object)
450 {
451         if (sgen_ptr_in_nursery (object))
452                 return TRUE;
453         
454         if (sgen_los_is_valid_object (object))
455                 return TRUE;
456
457         if (major_collector.is_valid_object (object))
458                 return TRUE;
459         return FALSE;
460 }
461
462 /*
463  * sgen_check_objref:
464  *   Do consistency checks on the object reference OBJ. Assert on failure.
465  */
466 void
467 sgen_check_objref (char *obj)
468 {
469         g_assert (ptr_in_heap (obj));
470 }
471
472 static void
473 find_pinning_ref_from_thread (char *obj, size_t size)
474 {
475         int j;
476         SgenThreadInfo *info;
477         char *endobj = obj + size;
478
479         FOREACH_THREAD (info) {
480                 char **start = (char**)info->stack_start;
481                 if (info->skip)
482                         continue;
483                 while (start < (char**)info->stack_end) {
484                         if (*start >= obj && *start < endobj) {
485                                 SGEN_LOG (0, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p", obj, info, (gpointer)mono_thread_info_get_tid (info), start, info->stack_start, info->stack_end);
486                         }
487                         start++;
488                 }
489
490                 for (j = 0; j < ARCH_NUM_REGS; ++j) {
491 #ifdef USE_MONO_CTX
492                         mword w = ((mword*)&info->ctx) [j];
493 #else
494                         mword w = (mword)&info->regs [j];
495 #endif
496
497                         if (w >= (mword)obj && w < (mword)obj + size)
498                                 SGEN_LOG (0, "Object %p referenced in saved reg %d of thread %p (id %p)", obj, j, info, (gpointer)mono_thread_info_get_tid (info));
499                 } END_FOREACH_THREAD
500         }
501 }
502
503 /*
504  * Debugging function: find in the conservative roots where @obj is being pinned.
505  */
506 static G_GNUC_UNUSED void
507 find_pinning_reference (char *obj, size_t size)
508 {
509         char **start;
510         RootRecord *root;
511         char *endobj = obj + size;
512
513         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_NORMAL], start, root) {
514                 /* if desc is non-null it has precise info */
515                 if (!root->root_desc) {
516                         while (start < (char**)root->end_root) {
517                                 if (*start >= obj && *start < endobj) {
518                                         SGEN_LOG (0, "Object %p referenced in pinned roots %p-%p\n", obj, start, root->end_root);
519                                 }
520                                 start++;
521                         }
522                 }
523         } SGEN_HASH_TABLE_FOREACH_END;
524
525         find_pinning_ref_from_thread (obj, size);
526 }
527
528 #undef HANDLE_PTR
529 #define HANDLE_PTR(ptr,obj)     do {                                    \
530                 char* __target = *(char**)ptr;                          \
531                 if (__target) {                                         \
532                         g_assert (is_valid_object_pointer (__target));  \
533                         if (sgen_ptr_in_nursery (__target)) {           \
534                                 g_assert (SGEN_OBJECT_IS_PINNED (__target)); \
535                         } else if (sgen_los_is_valid_object (__target)) { \
536                                 g_assert (sgen_los_object_is_pinned (__target)); \
537                         } else if (major_collector.is_valid_object (__target)) { \
538                                 g_assert (major_collector.is_object_live (__target)); \
539                         } else {                                        \
540                                 g_assert_not_reached ();                \
541                         }                                               \
542                 }                                                       \
543         } while (0)
544
545 static void
546 check_marked_callback (char *start, size_t size, void *dummy)
547 {
548         gboolean is_los = (gboolean) (size_t) dummy;
549
550         if (is_los) {
551                 if (!sgen_los_object_is_pinned (start))
552                         return;
553         } else {
554                 if (!major_collector.is_object_live (start))
555                         return;
556         }
557
558 #include "sgen-scan-object.h"
559 }
560
561 void
562 sgen_check_major_heap_marked (void)
563 {
564         setup_valid_nursery_objects ();
565
566         major_collector.iterate_objects (TRUE, TRUE, check_marked_callback, (void*)FALSE);
567         sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
568 }
569
570 static void
571 check_nursery_objects_pinned_callback (char *obj, size_t size, void *data /* ScanCopyContext *ctx */)
572 {
573         gboolean pinned = (gboolean) (size_t) data;
574
575         g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
576         if (pinned)
577                 g_assert (SGEN_OBJECT_IS_PINNED (obj));
578         else
579                 g_assert (!SGEN_OBJECT_IS_PINNED (obj));
580 }
581
582 void
583 sgen_check_nursery_objects_pinned (gboolean pinned)
584 {
585         sgen_clear_nursery_fragments ();
586         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
587                         (IterateObjectCallbackFunc)check_nursery_objects_pinned_callback, (void*) (size_t) pinned /* (void*)&ctx */, FALSE);
588 }
589
590 #endif /*HAVE_SGEN_GC*/