2 * sgen-debug.c: Collector debugging
5 * Paolo Molaro (lupus@ximian.com)
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10 * Copyright 2011 Xamarin, Inc.
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:
20 * The above copyright notice and this permission notice shall be
21 * included in all copies or substantial portions of the Software.
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.
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"
41 #define LOAD_VTABLE SGEN_LOAD_VTABLE
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
47 void describe_ptr (char *ptr);
48 void check_object (char *start);
51 * ######################################################################
52 * ######## Collector debugging
53 * ######################################################################
56 const char*descriptor_types [] = {
68 describe_ptr (char *ptr)
75 if (sgen_ptr_in_nursery (ptr)) {
76 printf ("Pointer inside nursery.\n");
78 if (sgen_ptr_is_in_los (ptr, &start)) {
80 printf ("Pointer is the start of object %p in LOS space.\n", start);
82 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), 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");
89 printf ("Pointer unknown.\n");
94 if (object_is_pinned (ptr))
95 printf ("Object is pinned.\n");
97 if (object_is_forwarded (ptr))
98 printf ("Object is forwared.\n");
100 // FIXME: Handle pointers to the inside of objects
101 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
103 printf ("VTable: %p\n", vtable);
104 if (vtable == NULL) {
105 printf ("VTable is invalid (empty).\n");
108 if (sgen_ptr_in_nursery (vtable)) {
109 printf ("VTable is invalid (points inside nursery).\n");
112 printf ("Class: %s\n", vtable->klass->name);
114 desc = ((GCVTable*)vtable)->desc;
115 printf ("Descriptor: %lx\n", (long)desc);
118 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
121 static gboolean missing_remsets;
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.
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; \
142 * Check that each object reference which points into the nursery can
143 * be found in the remembered sets.
146 check_consistency_callback (char *start, size_t size, void *dummy)
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));
151 #define SCAN_OBJECT_ACTION
152 #include "sgen-scan-object.h"
156 * Perform consistency check of the heap.
158 * Assumes the world is stopped.
161 sgen_check_consistency (void)
163 // Need to add more checks
165 missing_remsets = FALSE;
167 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
169 // Check that oldspace->newspace pointers are registered with the collector
170 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
172 sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
174 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
176 if (!binary_protocol_is_enabled ())
177 g_assert (!missing_remsets);
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)); \
188 check_major_refs_callback (char *start, size_t size, void *dummy)
190 #define SCAN_OBJECT_ACTION
191 #include "sgen-scan-object.h"
195 sgen_check_major_refs (void)
197 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
198 sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
201 /* Check that the reference is valid */
203 #define HANDLE_PTR(ptr,obj) do { \
205 g_assert (sgen_safe_name (*(ptr)) != NULL); \
212 * Perform consistency check on an object. Currently we only check that the
213 * reference fields are valid.
216 check_object (char *start)
221 #include "sgen-scan-object.h"
225 static char **valid_nursery_objects;
226 static int valid_nursery_object_count;
227 static gboolean broken_heap;
230 setup_mono_sgen_scan_area_with_callback (char *object, size_t size, void *data)
232 valid_nursery_objects [valid_nursery_object_count++] = object;
236 find_object_in_nursery_dump (char *object)
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])
244 if (object < valid_nursery_objects [middle])
249 g_assert (first == last);
254 describe_nursery_ptr (char *ptr)
258 fprintf (gc_debug_file, "nursery-ptr ");
259 for (i = 0; i < valid_nursery_object_count; ++i) {
260 if (valid_nursery_objects [i] >= ptr)
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)");
267 char *obj = valid_nursery_objects [i];
268 MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
269 int size = safe_object_get_size ((MonoObject *)obj);
272 fprintf (gc_debug_file, "(object %s.%s size %d)",
273 vtable->klass->name_space, vtable->klass->name, size);
275 fprintf (gc_debug_file, "(interior-ptr offset %td of %p (%s.%s) size %d)",
277 vtable->klass->name_space, vtable->klass->name, size);
282 is_valid_object_pointer (char *object)
284 if (sgen_ptr_in_nursery (object))
285 return find_object_in_nursery_dump (object);
287 if (sgen_los_is_valid_object (object))
290 if (major_collector.is_valid_object (object))
297 describe_pointer (char *ptr)
299 if (sgen_ptr_in_nursery (ptr)) {
300 describe_nursery_ptr (ptr);
301 } else if (major_collector.describe_pointer (ptr)) {
303 } else if (!mono_sgen_los_describe_pointer (ptr)) {
304 fprintf (gc_debug_file, "non-heap-ptr");
309 bad_pointer_spew (char *obj, char **slot)
312 MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
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",
318 obj, vtable->klass->name_space, vtable->klass->name);
323 missing_remset_spew (char *obj, char **slot)
326 MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
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);
336 FIXME Flag missing remsets due to pinning as non fatal
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); \
351 verify_object_pointers_callback (char *start, size_t size, void *dummy)
353 #define SCAN_OBJECT_ACTION
354 #include "sgen-scan-object.h"
359 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
360 depend on OP_DUMMY_USE.
363 sgen_check_whole_heap (void)
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);
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);
376 g_assert (!broken_heap);
380 ptr_in_heap (char *object)
382 if (sgen_ptr_in_nursery (object))
385 if (sgen_los_is_valid_object (object))
388 if (major_collector.is_valid_object (object))
395 * Do consistency checks on the object reference OBJ. Assert on failure.
398 sgen_check_objref (char *obj)
400 g_assert (ptr_in_heap (obj));
404 find_pinning_ref_from_thread (char *obj, size_t size)
407 SgenThreadInfo *info;
408 char *endobj = obj + size;
410 FOREACH_THREAD (info) {
411 char **start = (char**)info->stack_start;
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));
421 for (j = 0; j < ARCH_NUM_REGS; ++j) {
423 mword w = ((mword*)&info->ctx) [j];
425 mword w = (mword)&info->regs [j];
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)));
435 * Debugging function: find in the conservative roots where @obj is being pinned.
437 static G_GNUC_UNUSED void
438 find_pinning_reference (char *obj, size_t size)
442 char *endobj = obj + size;
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));
454 } SGEN_HASH_TABLE_FOREACH_END;
456 find_pinning_ref_from_thread (obj, size);
459 #endif /*HAVE_SGEN_GC*/