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