[sgen] DTrace probe for when a GC is requested.
[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-ssb.h"
33 #include "metadata/sgen-protocol.h"
34 #include "metadata/sgen-memory-governor.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         "run_length",
53         "small_bitmap",
54         "string",
55         "complex",
56         "vector",
57         "array",
58         "large_bitmap",
59         "complex_arr"
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         } else {
81                 if (sgen_ptr_is_in_los (ptr, &start)) {
82                         if (ptr == start)
83                                 printf ("Pointer is the start of object %p in LOS space.\n", start);
84                         else
85                                 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
86                         ptr = start;
87                         mono_sgen_los_describe_pointer (ptr);
88                 } else if (major_collector.ptr_is_in_non_pinned_space (ptr, &start)) {
89                         if (ptr == start)
90                                 printf ("Pointer is the start of object %p in oldspace.\n", start);
91                         else if (start)
92                                 printf ("Pointer is at offset 0x%x of object %p in oldspace.\n", (int)(ptr - start), start);
93                         else
94                                 printf ("Pointer inside oldspace.\n");
95                         if (start)
96                                 ptr = start;
97                         major_collector.describe_pointer (ptr);
98                 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
99                         printf ("Pointer is inside a pinned chunk.\n");
100                 } else {
101                         printf ("Pointer unknown.\n");
102                         return;
103                 }
104         }
105
106         if (object_is_pinned (ptr))
107                 printf ("Object is pinned.\n");
108
109         if ((forwarded = object_is_forwarded (ptr))) {
110                 printf ("Object is forwarded to %p:\n", forwarded);
111                 ptr = forwarded;
112                 goto restart;
113         }
114
115         // FIXME: Handle pointers to the inside of objects
116         vtable = (MonoVTable*)LOAD_VTABLE (ptr);
117
118         printf ("VTable: %p\n", vtable);
119         if (vtable == NULL) {
120                 printf ("VTable is invalid (empty).\n");
121                 return;
122         }
123         if (sgen_ptr_in_nursery (vtable)) {
124                 printf ("VTable is invalid (points inside nursery).\n");
125                 return;
126         }
127         printf ("Class: %s\n", vtable->klass->name);
128
129         desc = ((GCVTable*)vtable)->desc;
130         printf ("Descriptor: %lx\n", (long)desc);
131
132         type = desc & 0x7;
133         printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
134
135         size = sgen_safe_object_get_size ((MonoObject*)ptr);
136         printf ("Size: %td\n", size);
137 }
138
139 void
140 describe_ptr (char *ptr)
141 {
142         describe_pointer (ptr, TRUE);
143 }
144
145 static gboolean missing_remsets;
146
147 /*
148  * We let a missing remset slide if the target object is pinned,
149  * because the store might have happened but the remset not yet added,
150  * but in that case the target must be pinned.  We might theoretically
151  * miss some missing remsets this way, but it's very unlikely.
152  */
153 #undef HANDLE_PTR
154 #define HANDLE_PTR(ptr,obj)     do {    \
155         if (*(ptr) && sgen_ptr_in_nursery ((char*)*(ptr))) { \
156                 if (!sgen_get_remset ()->find_address ((char*)(ptr))) { \
157                         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); \
158                         binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
159                         if (!object_is_pinned (*(ptr)))                                                         \
160                                 missing_remsets = TRUE;                                                                 \
161                 }                                                                                                                               \
162         }                                                                                                                                       \
163         } while (0)
164
165 /*
166  * Check that each object reference which points into the nursery can
167  * be found in the remembered sets.
168  */
169 static void
170 check_consistency_callback (char *start, size_t size, void *dummy)
171 {
172         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
173         SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, vt->klass->name);
174
175 #define SCAN_OBJECT_ACTION
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_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 (TRUE, TRUE, (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 (!binary_protocol_is_enabled ())
201                 g_assert (!missing_remsets);
202 }
203
204
205 #undef HANDLE_PTR
206 #define HANDLE_PTR(ptr,obj)     do {                                    \
207                 if (*(ptr) && !LOAD_VTABLE (*(ptr)))                                            \
208                         g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj));          \
209         } while (0)
210
211 static void
212 check_major_refs_callback (char *start, size_t size, void *dummy)
213 {
214 #define SCAN_OBJECT_ACTION
215 #include "sgen-scan-object.h"
216 }
217
218 void
219 sgen_check_major_refs (void)
220 {
221         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
222         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
223 }
224
225 /* Check that the reference is valid */
226 #undef HANDLE_PTR
227 #define HANDLE_PTR(ptr,obj)     do {    \
228                 if (*(ptr)) {   \
229                         g_assert (sgen_safe_name (*(ptr)) != NULL);     \
230                 }       \
231         } while (0)
232
233 /*
234  * check_object:
235  *
236  *   Perform consistency check on an object. Currently we only check that the
237  * reference fields are valid.
238  */
239 void
240 check_object (char *start)
241 {
242         if (!start)
243                 return;
244
245 #include "sgen-scan-object.h"
246 }
247
248
249 static char **valid_nursery_objects;
250 static int valid_nursery_object_count;
251 static gboolean broken_heap;
252
253 static void 
254 setup_mono_sgen_scan_area_with_callback (char *object, size_t size, void *data)
255 {
256         valid_nursery_objects [valid_nursery_object_count++] = object;
257 }
258
259 static void
260 setup_valid_nursery_objects (void)
261 {
262         if (!valid_nursery_objects)
263                 valid_nursery_objects = sgen_alloc_os_memory (DEFAULT_NURSERY_SIZE, SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging data");
264         valid_nursery_object_count = 0;
265         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE);
266 }
267
268 static gboolean
269 find_object_in_nursery_dump (char *object)
270 {
271         int first = 0, last = valid_nursery_object_count;
272         while (first < last) {
273                 int middle = first + ((last - first) >> 1);
274                 if (object == valid_nursery_objects [middle])
275                         return TRUE;
276
277                 if (object < valid_nursery_objects [middle])
278                         last = middle;
279                 else
280                         first = middle + 1;
281         }
282         g_assert (first == last);
283         return FALSE;
284 }
285
286 static char*
287 describe_nursery_ptr (char *ptr, gboolean need_setup)
288 {
289         int i;
290
291         if (need_setup)
292                 setup_valid_nursery_objects ();
293
294         for (i = 0; i < valid_nursery_object_count; ++i) {
295                 if (valid_nursery_objects [i] >= ptr)
296                         break;
297         }
298
299         if (i >= valid_nursery_object_count || valid_nursery_objects [i] + safe_object_get_size ((MonoObject *)valid_nursery_objects [i]) < ptr) {
300                 SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)\n");
301                 return NULL;
302         } else {
303                 char *obj = valid_nursery_objects [i];
304                 if (obj == ptr)
305                         SGEN_LOG (0, "nursery-ptr\n");
306                 else
307                         SGEN_LOG (0, "nursery-ptr (interior-ptr offset %td)\n", ptr - obj);
308                 return obj;
309         }
310 }
311
312 static gboolean
313 is_valid_object_pointer (char *object)
314 {
315         if (sgen_ptr_in_nursery (object))
316                 return find_object_in_nursery_dump (object);
317         
318         if (sgen_los_is_valid_object (object))
319                 return TRUE;
320
321         if (major_collector.is_valid_object (object))
322                 return TRUE;
323         return FALSE;
324 }
325
326 static void
327 bad_pointer_spew (char *obj, char **slot)
328 {
329         char *ptr = *slot;
330         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
331
332         SGEN_LOG (0, "Invalid object pointer %p at offset %td in object %p (%s.%s):", ptr,
333                 (char*)slot - obj,
334                 obj, vtable->klass->name_space, vtable->klass->name);
335         describe_pointer (ptr, FALSE);
336         broken_heap = TRUE;
337 }
338
339 static void
340 missing_remset_spew (char *obj, char **slot)
341 {
342         char *ptr = *slot;
343         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
344
345         SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.",
346                 ptr, (char*)slot - obj, obj, 
347                 vtable->klass->name_space, vtable->klass->name);
348
349         broken_heap = TRUE;
350 }
351
352 /*
353 FIXME Flag missing remsets due to pinning as non fatal
354 */
355 #undef HANDLE_PTR
356 #define HANDLE_PTR(ptr,obj)     do {    \
357                 if (*(char**)ptr) {     \
358                         if (!is_valid_object_pointer (*(char**)ptr)) {  \
359                                 bad_pointer_spew ((char*)obj, (char**)ptr);     \
360                         } else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) {   \
361                                 if (!sgen_get_remset ()->find_address ((char*)(ptr))) \
362                                 missing_remset_spew ((char*)obj, (char**)ptr);  \
363                         }       \
364         } \
365         } while (0)
366
367 static void
368 verify_object_pointers_callback (char *start, size_t size, void *dummy)
369 {
370 #define SCAN_OBJECT_ACTION
371 #include "sgen-scan-object.h"
372 }
373
374 /*
375 FIXME:
376 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
377 depend on OP_DUMMY_USE.
378 */
379 void
380 sgen_check_whole_heap (void)
381 {
382         setup_valid_nursery_objects ();
383
384         broken_heap = FALSE;
385         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, NULL, FALSE);
386         major_collector.iterate_objects (TRUE, TRUE, verify_object_pointers_callback, NULL);
387         sgen_los_iterate_objects (verify_object_pointers_callback, NULL);       
388
389         g_assert (!broken_heap);
390 }
391
392 static gboolean
393 ptr_in_heap (char *object)
394 {
395         if (sgen_ptr_in_nursery (object))
396                 return TRUE;
397         
398         if (sgen_los_is_valid_object (object))
399                 return TRUE;
400
401         if (major_collector.is_valid_object (object))
402                 return TRUE;
403         return FALSE;
404 }
405
406 /*
407  * sgen_check_objref:
408  *   Do consistency checks on the object reference OBJ. Assert on failure.
409  */
410 void
411 sgen_check_objref (char *obj)
412 {
413         g_assert (ptr_in_heap (obj));
414 }
415
416 static void
417 find_pinning_ref_from_thread (char *obj, size_t size)
418 {
419         int j;
420         SgenThreadInfo *info;
421         char *endobj = obj + size;
422
423         FOREACH_THREAD (info) {
424                 char **start = (char**)info->stack_start;
425                 if (info->skip)
426                         continue;
427                 while (start < (char**)info->stack_end) {
428                         if (*start >= obj && *start < endobj) {
429                                 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);
430                         }
431                         start++;
432                 }
433
434                 for (j = 0; j < ARCH_NUM_REGS; ++j) {
435 #ifdef USE_MONO_CTX
436                         mword w = ((mword*)&info->ctx) [j];
437 #else
438                         mword w = (mword)&info->regs [j];
439 #endif
440
441                         if (w >= (mword)obj && w < (mword)obj + size)
442                                 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));
443                 } END_FOREACH_THREAD
444         }
445 }
446
447 /*
448  * Debugging function: find in the conservative roots where @obj is being pinned.
449  */
450 static G_GNUC_UNUSED void
451 find_pinning_reference (char *obj, size_t size)
452 {
453         char **start;
454         RootRecord *root;
455         char *endobj = obj + size;
456
457         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_NORMAL], start, root) {
458                 /* if desc is non-null it has precise info */
459                 if (!root->root_desc) {
460                         while (start < (char**)root->end_root) {
461                                 if (*start >= obj && *start < endobj) {
462                                         SGEN_LOG (0, "Object %p referenced in pinned roots %p-%p\n", obj, start, root->end_root);
463                                 }
464                                 start++;
465                         }
466                 }
467         } SGEN_HASH_TABLE_FOREACH_END;
468
469         find_pinning_ref_from_thread (obj, size);
470 }
471
472 #undef HANDLE_PTR
473 #define HANDLE_PTR(ptr,obj)     do {                                    \
474                 char* __target = *(char**)ptr;                          \
475                 if (__target) {                                         \
476                         g_assert (is_valid_object_pointer (__target));  \
477                         if (sgen_ptr_in_nursery (__target)) {           \
478                                 g_assert (SGEN_OBJECT_IS_PINNED (__target)); \
479                         } else if (sgen_los_is_valid_object (__target)) { \
480                                 g_assert (sgen_los_object_is_pinned (__target)); \
481                         } else if (major_collector.is_valid_object (__target)) { \
482                                 g_assert (major_collector.is_object_live (__target)); \
483                         } else {                                        \
484                                 g_assert_not_reached ();                \
485                         }                                               \
486                 }                                                       \
487         } while (0)
488
489 static void
490 check_marked_callback (char *start, size_t size, void *dummy)
491 {
492         gboolean is_los = (gboolean)dummy;
493
494         if (is_los) {
495                 if (!sgen_los_object_is_pinned (start))
496                         return;
497         } else {
498                 if (!major_collector.is_object_live (start))
499                         return;
500         }
501
502 #define SCAN_OBJECT_ACTION
503 #include "sgen-scan-object.h"
504 }
505
506 void
507 sgen_check_major_heap_marked (void)
508 {
509         setup_valid_nursery_objects ();
510
511         major_collector.iterate_objects (TRUE, TRUE, check_marked_callback, (void*)FALSE);
512         sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
513 }
514
515 #endif /*HAVE_SGEN_GC*/