Merge pull request #347 from JamesB7/master
[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  *
12  * Permission is hereby granted, free of charge, to any person obtaining
13  * a copy of this software and associated documentation files (the
14  * "Software"), to deal in the Software without restriction, including
15  * without limitation the rights to use, copy, modify, merge, publish,
16  * distribute, sublicense, and/or sell copies of the Software, and to
17  * permit persons to whom the Software is furnished to do so, subject to
18  * the following conditions:
19  * 
20  * The above copyright notice and this permission notice shall be
21  * included in all copies or substantial portions of the Software.
22  * 
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30  */
31
32 #include "config.h"
33 #ifdef HAVE_SGEN_GC
34
35 #include "metadata/sgen-gc.h"
36 #include "metadata/sgen-cardtable.h"
37 #include "metadata/sgen-ssb.h"
38 #include "metadata/sgen-protocol.h"
39
40 #define LOAD_VTABLE     SGEN_LOAD_VTABLE
41
42 #define object_is_forwarded     SGEN_OBJECT_IS_FORWARDED
43 #define object_is_pinned        SGEN_OBJECT_IS_PINNED
44 #define safe_object_get_size    sgen_safe_object_get_size
45
46 void describe_ptr (char *ptr);
47 void check_object (char *start);
48
49 /*
50  * ######################################################################
51  * ########  Collector debugging
52  * ######################################################################
53  */
54
55 const char*descriptor_types [] = {
56         "run_length",
57         "small_bitmap",
58         "string",
59         "complex",
60         "vector",
61         "array",
62         "large_bitmap",
63         "complex_arr"
64 };
65
66 void
67 describe_ptr (char *ptr)
68 {
69         MonoVTable *vtable;
70         mword desc;
71         int type;
72         char *start;
73
74         if (sgen_ptr_in_nursery (ptr)) {
75                 printf ("Pointer inside nursery.\n");
76         } else {
77                 if (sgen_ptr_is_in_los (ptr, &start)) {
78                         if (ptr == start)
79                                 printf ("Pointer is the start of object %p in LOS space.\n", start);
80                         else
81                                 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
82                         ptr = start;
83                 } else if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
84                         printf ("Pointer inside oldspace.\n");
85                 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
86                         printf ("Pointer is inside a pinned chunk.\n");
87                 } else {
88                         printf ("Pointer unknown.\n");
89                         return;
90                 }
91         }
92
93         if (object_is_pinned (ptr))
94                 printf ("Object is pinned.\n");
95
96         if (object_is_forwarded (ptr))
97                 printf ("Object is forwared.\n");
98
99         // FIXME: Handle pointers to the inside of objects
100         vtable = (MonoVTable*)LOAD_VTABLE (ptr);
101
102         printf ("VTable: %p\n", vtable);
103         if (vtable == NULL) {
104                 printf ("VTable is invalid (empty).\n");
105                 return;
106         }
107         if (sgen_ptr_in_nursery (vtable)) {
108                 printf ("VTable is invalid (points inside nursery).\n");
109                 return;
110         }
111         printf ("Class: %s\n", vtable->klass->name);
112
113         desc = ((GCVTable*)vtable)->desc;
114         printf ("Descriptor: %lx\n", (long)desc);
115
116         type = desc & 0x7;
117         printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
118 }
119
120 static gboolean missing_remsets;
121
122 /*
123  * We let a missing remset slide if the target object is pinned,
124  * because the store might have happened but the remset not yet added,
125  * but in that case the target must be pinned.  We might theoretically
126  * miss some missing remsets this way, but it's very unlikely.
127  */
128 #undef HANDLE_PTR
129 #define HANDLE_PTR(ptr,obj)     do {    \
130         if (*(ptr) && sgen_ptr_in_nursery ((char*)*(ptr))) { \
131                 if (!sgen_get_remset ()->find_address ((char*)(ptr))) { \
132                         fprintf (gc_debug_file, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.\n", *(ptr), (char*)(ptr) - (char*)(obj), (obj), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
133                         binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
134                         if (!object_is_pinned (*(ptr)))                                                         \
135                                 missing_remsets = TRUE;                                                                 \
136                 }                                                                                                                               \
137         }                                                                                                                                       \
138         } while (0)
139
140 /*
141  * Check that each object reference which points into the nursery can
142  * be found in the remembered sets.
143  */
144 static void
145 check_consistency_callback (char *start, size_t size, void *dummy)
146 {
147         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
148         DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
149
150 #define SCAN_OBJECT_ACTION
151 #include "sgen-scan-object.h"
152 }
153
154 /*
155  * Perform consistency check of the heap.
156  *
157  * Assumes the world is stopped.
158  */
159 void
160 sgen_check_consistency (void)
161 {
162         // Need to add more checks
163
164         missing_remsets = FALSE;
165
166         DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
167
168         // Check that oldspace->newspace pointers are registered with the collector
169         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
170
171         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
172
173         DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
174
175         if (!binary_protocol_is_enabled ())
176                 g_assert (!missing_remsets);
177 }
178
179
180 #undef HANDLE_PTR
181 #define HANDLE_PTR(ptr,obj)     do {                                    \
182                 if (*(ptr) && !LOAD_VTABLE (*(ptr)))                                            \
183                         g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj));          \
184         } while (0)
185
186 static void
187 check_major_refs_callback (char *start, size_t size, void *dummy)
188 {
189 #define SCAN_OBJECT_ACTION
190 #include "sgen-scan-object.h"
191 }
192
193 void
194 sgen_check_major_refs (void)
195 {
196         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
197         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
198 }
199
200 /* Check that the reference is valid */
201 #undef HANDLE_PTR
202 #define HANDLE_PTR(ptr,obj)     do {    \
203                 if (*(ptr)) {   \
204                         g_assert (sgen_safe_name (*(ptr)) != NULL);     \
205                 }       \
206         } while (0)
207
208 /*
209  * check_object:
210  *
211  *   Perform consistency check on an object. Currently we only check that the
212  * reference fields are valid.
213  */
214 void
215 check_object (char *start)
216 {
217         if (!start)
218                 return;
219
220 #include "sgen-scan-object.h"
221 }
222
223
224 static char **valid_nursery_objects;
225 static int valid_nursery_object_count;
226 static gboolean broken_heap;
227
228 static void 
229 setup_mono_sgen_scan_area_with_callback (char *object, size_t size, void *data)
230 {
231         valid_nursery_objects [valid_nursery_object_count++] = object;
232 }
233
234 static gboolean
235 find_object_in_nursery_dump (char *object)
236 {
237         int first = 0, last = valid_nursery_object_count;
238         while (first < last) {
239                 int middle = first + ((last - first) >> 1);
240                 if (object == valid_nursery_objects [middle])
241                         return TRUE;
242
243                 if (object < valid_nursery_objects [middle])
244                         last = middle;
245                 else
246                         first = middle + 1;
247         }
248         g_assert (first == last);
249         return FALSE;
250 }
251
252 static void
253 describe_nursery_ptr (char *ptr)
254 {
255         int i;
256
257         fprintf (gc_debug_file, "nursery-ptr ");
258         for (i = 0; i < valid_nursery_object_count; ++i) {
259                 if (valid_nursery_objects [i] >= ptr)
260                         break;
261         }
262
263         if (i >= valid_nursery_object_count || valid_nursery_objects [i] + safe_object_get_size ((MonoObject *)valid_nursery_objects [i]) < ptr) {
264                 fprintf (gc_debug_file, "(unalloc'd-memory)");
265         } else {
266                 char *obj = valid_nursery_objects [i];
267                 MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
268                 int size = safe_object_get_size ((MonoObject *)obj);
269
270                 if (obj == ptr)
271                         fprintf (gc_debug_file, "(object %s.%s size %d)", 
272                                 vtable->klass->name_space, vtable->klass->name, size);
273                 else
274                         fprintf (gc_debug_file, "(interior-ptr offset %td of %p (%s.%s) size %d)",
275                                 ptr - obj, obj,
276                                 vtable->klass->name_space, vtable->klass->name, size);
277         }
278 }
279
280 static gboolean
281 is_valid_object_pointer (char *object)
282 {
283         if (sgen_ptr_in_nursery (object))
284                 return find_object_in_nursery_dump (object);
285         
286         if (sgen_los_is_valid_object (object))
287                 return TRUE;
288
289         if (major_collector.is_valid_object (object))
290                 return TRUE;
291         return FALSE;
292 }
293
294
295 static void
296 describe_pointer (char *ptr)
297 {
298         if (sgen_ptr_in_nursery (ptr)) {
299                 describe_nursery_ptr (ptr);
300         } else if (major_collector.describe_pointer (ptr)) {
301                 //Nothing really
302         } else if (!mono_sgen_los_describe_pointer (ptr)) {
303                 fprintf (gc_debug_file, "non-heap-ptr");
304         }
305 }
306
307 static void
308 bad_pointer_spew (char *obj, char **slot)
309 {
310         char *ptr = *slot;
311         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
312
313         fprintf (gc_debug_file, "Invalid object pointer %p [", ptr);
314         describe_pointer (ptr);
315         fprintf (gc_debug_file, "] at offset %td in object %p (%s.%s).\n",
316                 (char*)slot - obj,
317                 obj, vtable->klass->name_space, vtable->klass->name);
318         broken_heap = TRUE;
319 }
320
321 static void
322 missing_remset_spew (char *obj, char **slot)
323 {
324         char *ptr = *slot;
325         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
326
327     fprintf (gc_debug_file,  "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.\n",
328                 ptr, (char*)slot - obj, obj, 
329                 vtable->klass->name_space, vtable->klass->name);
330
331         broken_heap = TRUE;
332 }
333
334 /*
335 FIXME Flag missing remsets due to pinning as non fatal
336 */
337 #undef HANDLE_PTR
338 #define HANDLE_PTR(ptr,obj)     do {    \
339                 if (*(char**)ptr) {     \
340                         if (!is_valid_object_pointer (*(char**)ptr)) {  \
341                                 bad_pointer_spew ((char*)obj, (char**)ptr);     \
342                         } else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) {   \
343                                 if (!sgen_get_remset ()->find_address ((char*)(ptr))) \
344                                 missing_remset_spew ((char*)obj, (char**)ptr);  \
345                         }       \
346         } \
347         } while (0)
348
349 static void
350 verify_object_pointers_callback (char *start, size_t size, void *dummy)
351 {
352 #define SCAN_OBJECT_ACTION
353 #include "sgen-scan-object.h"
354 }
355
356 /*
357 FIXME:
358 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
359 depend on OP_DUMMY_USE.
360 */
361 void
362 sgen_check_whole_heap (void)
363 {
364         /*setup valid_nursery_objects*/
365         if (!valid_nursery_objects)
366                 valid_nursery_objects = sgen_alloc_os_memory (DEFAULT_NURSERY_SIZE, TRUE);
367         valid_nursery_object_count = 0;
368         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE);
369
370         broken_heap = FALSE;
371         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, NULL, FALSE);
372         major_collector.iterate_objects (TRUE, TRUE, verify_object_pointers_callback, NULL);
373         sgen_los_iterate_objects (verify_object_pointers_callback, NULL);       
374
375         g_assert (!broken_heap);
376 }
377 #endif /*HAVE_SGEN_GC*/