[io-layer] add URLs for some ximian bug numbers in sockets.cs
[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         } 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 #include "sgen-scan-object.h"
176 }
177
178 /*
179  * Perform consistency check of the heap.
180  *
181  * Assumes the world is stopped.
182  */
183 void
184 sgen_check_consistency (void)
185 {
186         // Need to add more checks
187
188         missing_remsets = FALSE;
189
190         SGEN_LOG (1, "Begin heap consistency check...");
191
192         // Check that oldspace->newspace pointers are registered with the collector
193         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
194
195         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
196
197         SGEN_LOG (1, "Heap consistency check done.");
198
199         if (!binary_protocol_is_enabled ())
200                 g_assert (!missing_remsets);
201 }
202
203
204 #undef HANDLE_PTR
205 #define HANDLE_PTR(ptr,obj)     do {                                    \
206                 if (*(ptr) && !LOAD_VTABLE (*(ptr)))                                            \
207                         g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj));          \
208         } while (0)
209
210 static void
211 check_major_refs_callback (char *start, size_t size, void *dummy)
212 {
213 #include "sgen-scan-object.h"
214 }
215
216 void
217 sgen_check_major_refs (void)
218 {
219         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
220         sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
221 }
222
223 /* Check that the reference is valid */
224 #undef HANDLE_PTR
225 #define HANDLE_PTR(ptr,obj)     do {    \
226                 if (*(ptr)) {   \
227                         g_assert (sgen_safe_name (*(ptr)) != NULL);     \
228                 }       \
229         } while (0)
230
231 /*
232  * check_object:
233  *
234  *   Perform consistency check on an object. Currently we only check that the
235  * reference fields are valid.
236  */
237 void
238 check_object (char *start)
239 {
240         if (!start)
241                 return;
242
243 #include "sgen-scan-object.h"
244 }
245
246
247 static char **valid_nursery_objects;
248 static int valid_nursery_object_count;
249 static gboolean broken_heap;
250
251 static void 
252 setup_mono_sgen_scan_area_with_callback (char *object, size_t size, void *data)
253 {
254         valid_nursery_objects [valid_nursery_object_count++] = object;
255 }
256
257 static void
258 setup_valid_nursery_objects (void)
259 {
260         if (!valid_nursery_objects)
261                 valid_nursery_objects = sgen_alloc_os_memory (DEFAULT_NURSERY_SIZE, SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "debugging data");
262         valid_nursery_object_count = 0;
263         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE);
264 }
265
266 static gboolean
267 find_object_in_nursery_dump (char *object)
268 {
269         int first = 0, last = valid_nursery_object_count;
270         while (first < last) {
271                 int middle = first + ((last - first) >> 1);
272                 if (object == valid_nursery_objects [middle])
273                         return TRUE;
274
275                 if (object < valid_nursery_objects [middle])
276                         last = middle;
277                 else
278                         first = middle + 1;
279         }
280         g_assert (first == last);
281         return FALSE;
282 }
283
284 static char*
285 describe_nursery_ptr (char *ptr, gboolean need_setup)
286 {
287         int i;
288
289         if (need_setup)
290                 setup_valid_nursery_objects ();
291
292         for (i = 0; i < valid_nursery_object_count; ++i) {
293                 if (valid_nursery_objects [i] >= ptr)
294                         break;
295         }
296
297         if (i >= valid_nursery_object_count || valid_nursery_objects [i] + safe_object_get_size ((MonoObject *)valid_nursery_objects [i]) < ptr) {
298                 SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)\n");
299                 return NULL;
300         } else {
301                 char *obj = valid_nursery_objects [i];
302                 if (obj == ptr)
303                         SGEN_LOG (0, "nursery-ptr\n");
304                 else
305                         SGEN_LOG (0, "nursery-ptr (interior-ptr offset %td)\n", ptr - obj);
306                 return obj;
307         }
308 }
309
310 static gboolean
311 is_valid_object_pointer (char *object)
312 {
313         if (sgen_ptr_in_nursery (object))
314                 return find_object_in_nursery_dump (object);
315         
316         if (sgen_los_is_valid_object (object))
317                 return TRUE;
318
319         if (major_collector.is_valid_object (object))
320                 return TRUE;
321         return FALSE;
322 }
323
324 static void
325 bad_pointer_spew (char *obj, char **slot)
326 {
327         char *ptr = *slot;
328         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
329
330         SGEN_LOG (0, "Invalid object pointer %p at offset %td in object %p (%s.%s):", ptr,
331                 (char*)slot - obj,
332                 obj, vtable->klass->name_space, vtable->klass->name);
333         describe_pointer (ptr, FALSE);
334         broken_heap = TRUE;
335 }
336
337 static void
338 missing_remset_spew (char *obj, char **slot)
339 {
340         char *ptr = *slot;
341         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
342
343         SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.",
344                 ptr, (char*)slot - obj, obj, 
345                 vtable->klass->name_space, vtable->klass->name);
346
347         broken_heap = TRUE;
348 }
349
350 /*
351 FIXME Flag missing remsets due to pinning as non fatal
352 */
353 #undef HANDLE_PTR
354 #define HANDLE_PTR(ptr,obj)     do {    \
355                 if (*(char**)ptr) {     \
356                         if (!is_valid_object_pointer (*(char**)ptr)) {  \
357                                 bad_pointer_spew ((char*)obj, (char**)ptr);     \
358                         } else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) {   \
359                                 if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup ((char*)*(ptr)) && (!allow_missing_pinned || !SGEN_OBJECT_IS_PINNED ((char*)*(ptr)))) \
360                                 missing_remset_spew ((char*)obj, (char**)ptr);  \
361                         }       \
362         } \
363         } while (0)
364
365 static void
366 verify_object_pointers_callback (char *start, size_t size, void *data)
367 {
368         gboolean allow_missing_pinned = (gboolean)data;
369
370 #include "sgen-scan-object.h"
371 }
372
373 /*
374 FIXME:
375 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
376 depend on OP_DUMMY_USE.
377 */
378 void
379 sgen_check_whole_heap (gboolean allow_missing_pinned)
380 {
381         setup_valid_nursery_objects ();
382
383         broken_heap = FALSE;
384         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, (void*)allow_missing_pinned, FALSE);
385         major_collector.iterate_objects (TRUE, TRUE, verify_object_pointers_callback, (void*)allow_missing_pinned);
386         sgen_los_iterate_objects (verify_object_pointers_callback, (void*)allow_missing_pinned);
387
388         g_assert (!broken_heap);
389 }
390
391 static gboolean
392 ptr_in_heap (char *object)
393 {
394         if (sgen_ptr_in_nursery (object))
395                 return TRUE;
396         
397         if (sgen_los_is_valid_object (object))
398                 return TRUE;
399
400         if (major_collector.is_valid_object (object))
401                 return TRUE;
402         return FALSE;
403 }
404
405 /*
406  * sgen_check_objref:
407  *   Do consistency checks on the object reference OBJ. Assert on failure.
408  */
409 void
410 sgen_check_objref (char *obj)
411 {
412         g_assert (ptr_in_heap (obj));
413 }
414
415 static void
416 find_pinning_ref_from_thread (char *obj, size_t size)
417 {
418         int j;
419         SgenThreadInfo *info;
420         char *endobj = obj + size;
421
422         FOREACH_THREAD (info) {
423                 char **start = (char**)info->stack_start;
424                 if (info->skip)
425                         continue;
426                 while (start < (char**)info->stack_end) {
427                         if (*start >= obj && *start < endobj) {
428                                 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);
429                         }
430                         start++;
431                 }
432
433                 for (j = 0; j < ARCH_NUM_REGS; ++j) {
434 #ifdef USE_MONO_CTX
435                         mword w = ((mword*)&info->ctx) [j];
436 #else
437                         mword w = (mword)&info->regs [j];
438 #endif
439
440                         if (w >= (mword)obj && w < (mword)obj + size)
441                                 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));
442                 } END_FOREACH_THREAD
443         }
444 }
445
446 /*
447  * Debugging function: find in the conservative roots where @obj is being pinned.
448  */
449 static G_GNUC_UNUSED void
450 find_pinning_reference (char *obj, size_t size)
451 {
452         char **start;
453         RootRecord *root;
454         char *endobj = obj + size;
455
456         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_NORMAL], start, root) {
457                 /* if desc is non-null it has precise info */
458                 if (!root->root_desc) {
459                         while (start < (char**)root->end_root) {
460                                 if (*start >= obj && *start < endobj) {
461                                         SGEN_LOG (0, "Object %p referenced in pinned roots %p-%p\n", obj, start, root->end_root);
462                                 }
463                                 start++;
464                         }
465                 }
466         } SGEN_HASH_TABLE_FOREACH_END;
467
468         find_pinning_ref_from_thread (obj, size);
469 }
470
471 #undef HANDLE_PTR
472 #define HANDLE_PTR(ptr,obj)     do {                                    \
473                 char* __target = *(char**)ptr;                          \
474                 if (__target) {                                         \
475                         g_assert (is_valid_object_pointer (__target));  \
476                         if (sgen_ptr_in_nursery (__target)) {           \
477                                 g_assert (SGEN_OBJECT_IS_PINNED (__target)); \
478                         } else if (sgen_los_is_valid_object (__target)) { \
479                                 g_assert (sgen_los_object_is_pinned (__target)); \
480                         } else if (major_collector.is_valid_object (__target)) { \
481                                 g_assert (major_collector.is_object_live (__target)); \
482                         } else {                                        \
483                                 g_assert_not_reached ();                \
484                         }                                               \
485                 }                                                       \
486         } while (0)
487
488 static void
489 check_marked_callback (char *start, size_t size, void *dummy)
490 {
491         gboolean is_los = (gboolean)dummy;
492
493         if (is_los) {
494                 if (!sgen_los_object_is_pinned (start))
495                         return;
496         } else {
497                 if (!major_collector.is_object_live (start))
498                         return;
499         }
500
501 #include "sgen-scan-object.h"
502 }
503
504 void
505 sgen_check_major_heap_marked (void)
506 {
507         setup_valid_nursery_objects ();
508
509         major_collector.iterate_objects (TRUE, TRUE, check_marked_callback, (void*)FALSE);
510         sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
511 }
512
513 static void
514 check_nursery_objects_pinned_callback (char *obj, size_t size, void *data /* ScanCopyContext *ctx */)
515 {
516         gboolean pinned = (gboolean)data;
517
518         g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
519         if (pinned)
520                 g_assert (SGEN_OBJECT_IS_PINNED (obj));
521         else
522                 g_assert (!SGEN_OBJECT_IS_PINNED (obj));
523 }
524
525 void
526 sgen_check_nursery_objects_pinned (gboolean pinned)
527 {
528         sgen_clear_nursery_fragments ();
529         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
530                         (IterateObjectCallbackFunc)check_nursery_objects_pinned_callback, (void*)pinned /* (void*)&ctx */, FALSE);
531 }
532
533 #endif /*HAVE_SGEN_GC*/