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