1 /**
2  * \file
3  * Collector debugging
4  *
5  * Author:
6  * 	Paolo Molaro (lupus@ximian.com)
7  *  Rodrigo Kumpera (kumpera@gmail.com)
8  *
9  * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
10  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
11  * Copyright 2011 Xamarin, Inc.
12  * Copyright (C) 2012 Xamarin Inc
13  *
14  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15  */
16 
17 #include "config.h"
18 #ifdef HAVE_SGEN_GC
19 
20 #include <string.h>
21 
22 #include "mono/sgen/sgen-gc.h"
23 #include "mono/sgen/sgen-cardtable.h"
24 #include "mono/sgen/sgen-protocol.h"
25 #include "mono/sgen/sgen-memory-governor.h"
26 #include "mono/sgen/sgen-pinning.h"
27 #include "mono/sgen/sgen-client.h"
28 
29 #define LOAD_VTABLE	SGEN_LOAD_VTABLE
30 
31 #define object_is_forwarded	SGEN_OBJECT_IS_FORWARDED
32 #define object_is_pinned	SGEN_OBJECT_IS_PINNED
33 #define safe_object_get_size	sgen_safe_object_get_size
34 
35 void describe_ptr (char *ptr);
36 void check_object (GCObject *obj);
37 
38 /*
39  * ######################################################################
40  * ########  Collector debugging
41  * ######################################################################
42  */
43 
44 static const char*descriptor_types [] = {
45 	"INVALID",
46 	"run length",
47 	"bitmap",
48 	"small pointer-free",
49 	"complex",
50 	"vector",
51 	"complex arrray",
52 	"complex pointer-free"
53 };
54 
55 static char* describe_nursery_ptr (char *ptr, gboolean need_setup);
56 
57 static void
describe_pointer(char * ptr,gboolean need_setup)58 describe_pointer (char *ptr, gboolean need_setup)
59 {
60 	GCVTable vtable;
61 	SgenDescriptor desc;
62 	int type;
63 	char *start;
64 	char *forwarded;
65 	mword size;
66 
67  restart:
68 	if (sgen_ptr_in_nursery (ptr)) {
69 		start = describe_nursery_ptr (ptr, need_setup);
70 		if (!start)
71 			return;
72 		ptr = start;
73 		vtable = LOAD_VTABLE ((GCObject*)ptr);
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 			mono_sgen_los_describe_pointer (ptr);
82 			vtable = LOAD_VTABLE ((GCObject*)ptr);
83 		} else if (major_collector.ptr_is_in_non_pinned_space (ptr, &start)) {
84 			if (ptr == start)
85 				printf ("Pointer is the start of object %p in oldspace.\n", start);
86 			else if (start)
87 				printf ("Pointer is at offset 0x%x of object %p in oldspace.\n", (int)(ptr - start), start);
88 			else
89 				printf ("Pointer inside oldspace.\n");
90 			if (start)
91 				ptr = start;
92 			vtable = (GCVTable)major_collector.describe_pointer (ptr);
93 		} else if (major_collector.ptr_is_from_pinned_alloc (ptr)) {
94 			// FIXME: Handle pointers to the inside of objects
95 			printf ("Pointer is inside a pinned chunk.\n");
96 			vtable = LOAD_VTABLE ((GCObject*)ptr);
97 		} else {
98 			printf ("Pointer unknown.\n");
99 			return;
100 		}
101 	}
102 
103 	if (object_is_pinned (ptr))
104 		printf ("Object is pinned.\n");
105 
106 	if ((forwarded = (char *)object_is_forwarded (ptr))) {
107 		printf ("Object is forwarded to %p:\n", forwarded);
108 		ptr = forwarded;
109 		goto restart;
110 	}
111 
112 	printf ("VTable: %p\n", vtable);
113 	if (vtable == NULL) {
114 		printf ("VTable is invalid (empty).\n");
115 		goto invalid_vtable;
116 	}
117 	if (sgen_ptr_in_nursery (vtable)) {
118 		printf ("VTable is invalid (points inside nursery).\n");
119 		goto invalid_vtable;
120 	}
121 	printf ("Class: %s.%s\n", sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
122 
123 	desc = sgen_vtable_get_descriptor (vtable);
124 	printf ("Descriptor: %lx\n", (long)desc);
125 
126 	type = desc & DESC_TYPE_MASK;
127 	printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
128 
129 	size = sgen_safe_object_get_size ((GCObject*)ptr);
130 	printf ("Size: %d\n", (int)size);
131 
132  invalid_vtable:
133 	;
134 	sgen_client_describe_invalid_pointer ((GCObject *) ptr);
135 }
136 
137 void
describe_ptr(char * ptr)138 describe_ptr (char *ptr)
139 {
140 	describe_pointer (ptr, TRUE);
141 }
142 
143 static gboolean missing_remsets;
144 
145 /*
146  * We let a missing remset slide if the target object is pinned,
147  * because the store might have happened but the remset not yet added,
148  * but in that case the target must be pinned.  We might theoretically
149  * miss some missing remsets this way, but it's very unlikely.
150  */
151 #undef HANDLE_PTR
152 #define HANDLE_PTR(ptr,obj)	do {	\
153 		if (*(ptr) && sgen_ptr_in_nursery ((char*)*(ptr))) {	\
154 			if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup (*(ptr))) { \
155 				GCVTable __vt = SGEN_LOAD_VTABLE (obj);	\
156 				gboolean is_pinned = object_is_pinned (*(ptr));	\
157 				SGEN_LOG (0, "Oldspace->newspace reference %p at offset %zd in object %p (%s.%s) not found in remsets%s.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), sgen_client_vtable_get_namespace (__vt), sgen_client_vtable_get_name (__vt), is_pinned ? ", but object is pinned" : ""); \
158 				binary_protocol_missing_remset ((obj), __vt, (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), is_pinned); \
159 				if (!is_pinned)				\
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
check_consistency_callback(GCObject * obj,size_t size,void * dummy)170 check_consistency_callback (GCObject *obj, size_t size, void *dummy)
171 {
172 	char *start = (char*)obj;
173 	GCVTable vt = LOAD_VTABLE (obj);
174 	SgenDescriptor desc = sgen_vtable_get_descriptor (vt);
175 	SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, sgen_client_vtable_get_name (vt));
176 
177 #include "sgen-scan-object.h"
178 }
179 
180 /*
181  * Perform consistency check of the heap.
182  *
183  * Assumes the world is stopped.
184  */
185 void
sgen_check_remset_consistency(void)186 sgen_check_remset_consistency (void)
187 {
188 	// Need to add more checks
189 
190 	missing_remsets = FALSE;
191 
192 	SGEN_LOG (1, "Begin heap consistency check...");
193 
194 	// Check that oldspace->newspace pointers are registered with the collector
195 	major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
196 
197 	sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
198 
199 	SGEN_LOG (1, "Heap consistency check done.");
200 
201 	if (missing_remsets)
202 		binary_protocol_flush_buffers (TRUE);
203 	if (!binary_protocol_is_enabled ())
204 		g_assert (!missing_remsets);
205 }
206 
207 static gboolean
is_major_or_los_object_marked(GCObject * obj)208 is_major_or_los_object_marked (GCObject *obj)
209 {
210 	if (sgen_safe_object_get_size ((GCObject*)obj) > SGEN_MAX_SMALL_OBJ_SIZE) {
211 		return sgen_los_object_is_pinned (obj);
212 	} else {
213 		return sgen_get_major_collector ()->is_object_live (obj);
214 	}
215 }
216 
217 #undef HANDLE_PTR
218 #define HANDLE_PTR(ptr,obj)	do {	\
219 	if (*(ptr) && !sgen_ptr_in_nursery ((char*)*(ptr)) && !is_major_or_los_object_marked ((GCObject*)*(ptr))) { \
220 		if (!cards || !sgen_get_remset ()->find_address_with_cards (start, cards, (char*)(ptr))) { \
221 			GCVTable __vt = SGEN_LOAD_VTABLE (obj);	\
222 			SGEN_LOG (0, "major->major reference %p at offset %zd in object %p (%s.%s) not found in remsets.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), sgen_client_vtable_get_namespace (__vt), sgen_client_vtable_get_name (__vt)); \
223 			binary_protocol_missing_remset ((obj), __vt, (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
224 			missing_remsets = TRUE;				\
225 		}																\
226 	}																	\
227 	} while (0)
228 
229 static void
check_mod_union_callback(GCObject * obj,size_t size,void * dummy)230 check_mod_union_callback (GCObject *obj, size_t size, void *dummy)
231 {
232 	char *start = (char*)obj;
233 	gboolean in_los = (gboolean) (size_t) dummy;
234 	GCVTable vt = LOAD_VTABLE (obj);
235 	SgenDescriptor desc = sgen_vtable_get_descriptor (vt);
236 	guint8 *cards;
237 	SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", obj, vt, sgen_client_vtable_get_name (vt));
238 
239 	if (!is_major_or_los_object_marked (obj))
240 		return;
241 
242 	if (in_los)
243 		cards = sgen_los_header_for_object (obj)->cardtable_mod_union;
244 	else
245 		cards = sgen_get_major_collector ()->get_cardtable_mod_union_for_reference (start);
246 
247 #include "sgen-scan-object.h"
248 }
249 
250 void
sgen_check_mod_union_consistency(void)251 sgen_check_mod_union_consistency (void)
252 {
253 	missing_remsets = FALSE;
254 
255 	major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_mod_union_callback, (void*)FALSE);
256 
257 	sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_mod_union_callback, (void*)TRUE);
258 
259 	if (!binary_protocol_is_enabled ())
260 		g_assert (!missing_remsets);
261 }
262 
263 #undef HANDLE_PTR
264 #define HANDLE_PTR(ptr,obj)	do {					\
265 		if (*(ptr) && !LOAD_VTABLE (*(ptr)))						\
266 			g_error ("Could not load vtable for obj %p slot %zd (size %zd)", obj, (char*)ptr - (char*)obj, (size_t)safe_object_get_size ((GCObject*)obj)); \
267 	} while (0)
268 
269 static void
check_major_refs_callback(GCObject * obj,size_t size,void * dummy)270 check_major_refs_callback (GCObject *obj, size_t size, void *dummy)
271 {
272 	char *start = (char*)obj;
273 	SgenDescriptor desc = sgen_obj_get_descriptor (obj);
274 
275 #include "sgen-scan-object.h"
276 }
277 
278 void
sgen_check_major_refs(void)279 sgen_check_major_refs (void)
280 {
281 	major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
282 	sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
283 }
284 
285 /* Check that the reference is valid */
286 #undef HANDLE_PTR
287 #define HANDLE_PTR(ptr,obj)	do {	\
288 		if (*(ptr)) {	\
289 			g_assert (sgen_client_vtable_get_namespace (SGEN_LOAD_VTABLE_UNCHECKED (*(ptr))));	\
290 		}	\
291 	} while (0)
292 
293 /*
294  * check_object:
295  *
296  *   Perform consistency check on an object. Currently we only check that the
297  * reference fields are valid.
298  */
299 void
check_object(GCObject * obj)300 check_object (GCObject *obj)
301 {
302 	char *start = (char*)obj;
303 	SgenDescriptor desc;
304 
305 	if (!start)
306 		return;
307 
308 	desc = sgen_obj_get_descriptor (obj);
309 
310 #include "sgen-scan-object.h"
311 }
312 
313 
314 static GCObject **valid_nursery_objects;
315 static int valid_nursery_object_count;
316 static gboolean broken_heap;
317 
318 static void
setup_mono_sgen_scan_area_with_callback(GCObject * object,size_t size,void * data)319 setup_mono_sgen_scan_area_with_callback (GCObject *object, size_t size, void *data)
320 {
321 	valid_nursery_objects [valid_nursery_object_count++] = object;
322 }
323 
324 static void
setup_valid_nursery_objects(void)325 setup_valid_nursery_objects (void)
326 {
327 	if (!valid_nursery_objects)
328 		valid_nursery_objects = (GCObject **)sgen_alloc_os_memory (sgen_nursery_max_size, (SgenAllocFlags)(SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE), "debugging data", MONO_MEM_ACCOUNT_SGEN_DEBUGGING);
329 	valid_nursery_object_count = 0;
330 	sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE, FALSE);
331 }
332 
333 static gboolean
find_object_in_nursery_dump(char * object)334 find_object_in_nursery_dump (char *object)
335 {
336 	int first = 0, last = valid_nursery_object_count;
337 	while (first < last) {
338 		int middle = first + ((last - first) >> 1);
339 		if (object == (char*)valid_nursery_objects [middle])
340 			return TRUE;
341 
342 		if (object < (char*)valid_nursery_objects [middle])
343 			last = middle;
344 		else
345 			first = middle + 1;
346 	}
347 	g_assert (first == last);
348 	return FALSE;
349 }
350 
351 static void
iterate_valid_nursery_objects(IterateObjectCallbackFunc callback,void * data)352 iterate_valid_nursery_objects (IterateObjectCallbackFunc callback, void *data)
353 {
354 	int i;
355 	for (i = 0; i < valid_nursery_object_count; ++i) {
356 		GCObject *obj = valid_nursery_objects [i];
357 		callback (obj, safe_object_get_size (obj), data);
358 	}
359 }
360 
361 static char*
describe_nursery_ptr(char * ptr,gboolean need_setup)362 describe_nursery_ptr (char *ptr, gboolean need_setup)
363 {
364 	int i;
365 
366 	if (need_setup)
367 		setup_valid_nursery_objects ();
368 
369 	for (i = 0; i < valid_nursery_object_count - 1; ++i) {
370 		if ((char*)valid_nursery_objects [i + 1] > ptr)
371 			break;
372 	}
373 
374 	if (i >= valid_nursery_object_count || (char*)valid_nursery_objects [i] + safe_object_get_size (valid_nursery_objects [i]) < ptr) {
375 		SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)");
376 		return NULL;
377 	} else {
378 		GCObject *obj = valid_nursery_objects [i];
379 		if ((char*)obj == ptr)
380 			SGEN_LOG (0, "nursery-ptr %p", obj);
381 		else
382 			SGEN_LOG (0, "nursery-ptr %p (interior-ptr offset %zd)", obj, ptr - (char*)obj);
383 		return (char*)obj;
384 	}
385 }
386 
387 static gboolean
is_valid_object_pointer(char * object)388 is_valid_object_pointer (char *object)
389 {
390 	if (sgen_ptr_in_nursery (object))
391 		return find_object_in_nursery_dump (object);
392 
393 	if (sgen_los_is_valid_object (object))
394 		return TRUE;
395 
396 	if (major_collector.is_valid_object (object))
397 		return TRUE;
398 	return FALSE;
399 }
400 
401 static void
bad_pointer_spew(char * obj,char ** slot)402 bad_pointer_spew (char *obj, char **slot)
403 {
404 	char *ptr = *slot;
405 	GCVTable vtable = LOAD_VTABLE ((GCObject*)obj);
406 
407 	SGEN_LOG (0, "Invalid object pointer %p at offset %zd in object %p (%s.%s):", ptr,
408 			(char*)slot - obj,
409 			obj, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
410 	describe_pointer (ptr, FALSE);
411 	broken_heap = TRUE;
412 }
413 
414 static void
missing_remset_spew(char * obj,char ** slot)415 missing_remset_spew (char *obj, char **slot)
416 {
417 	char *ptr = *slot;
418 	GCVTable vtable = LOAD_VTABLE ((GCObject*)obj);
419 
420 	SGEN_LOG (0, "Oldspace->newspace reference %p at offset %zd in object %p (%s.%s) not found in remsets.",
421 			ptr, (char*)slot - obj, obj,
422 			sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
423 
424 	broken_heap = TRUE;
425 }
426 
427 /*
428 FIXME Flag missing remsets due to pinning as non fatal
429 */
430 #undef HANDLE_PTR
431 #define HANDLE_PTR(ptr,obj)	do {					\
432 		if (*(char**)ptr) {					\
433 			if (!is_valid_object_pointer (*(char**)ptr)) {	\
434 				bad_pointer_spew ((char*)obj, (char**)ptr); \
435 			} else if (!sgen_ptr_in_nursery (obj) && sgen_ptr_in_nursery ((char*)*ptr)) { \
436 				if (!allow_missing_pinned && !SGEN_OBJECT_IS_PINNED (*(ptr)) && !sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup (*(ptr))) \
437 					missing_remset_spew ((char*)obj, (char**)ptr); \
438 			}						\
439 		}							\
440 	} while (0)
441 
442 static void
verify_object_pointers_callback(GCObject * obj,size_t size,void * data)443 verify_object_pointers_callback (GCObject *obj, size_t size, void *data)
444 {
445 	char *start = (char*)obj;
446 	gboolean allow_missing_pinned = (gboolean) (size_t) data;
447 	SgenDescriptor desc = sgen_obj_get_descriptor_safe (obj);
448 
449 #include "sgen-scan-object.h"
450 }
451 
452 /*
453 FIXME:
454 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
455 depend on OP_DUMMY_USE.
456 */
457 void
sgen_check_whole_heap(gboolean allow_missing_pinned)458 sgen_check_whole_heap (gboolean allow_missing_pinned)
459 {
460 	setup_valid_nursery_objects ();
461 
462 	broken_heap = FALSE;
463 	sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned, FALSE, TRUE);
464 	major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
465 	sgen_los_iterate_objects (verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
466 
467 	g_assert (!broken_heap);
468 }
469 
470 static gboolean
ptr_in_heap(char * object)471 ptr_in_heap (char *object)
472 {
473 	if (sgen_ptr_in_nursery (object))
474 		return TRUE;
475 
476 	if (sgen_los_is_valid_object (object))
477 		return TRUE;
478 
479 	if (major_collector.is_valid_object (object))
480 		return TRUE;
481 	return FALSE;
482 }
483 
484 /*
485  * sgen_check_objref:
486  *   Do consistency checks on the object reference OBJ. Assert on failure.
487  */
488 void
sgen_check_objref(char * obj)489 sgen_check_objref (char *obj)
490 {
491 	g_assert (ptr_in_heap (obj));
492 }
493 
494 static void
find_pinning_ref_from_thread(char * obj,size_t size)495 find_pinning_ref_from_thread (char *obj, size_t size)
496 {
497 #ifndef SGEN_WITHOUT_MONO
498 	char *endobj = obj + size;
499 
500 	FOREACH_THREAD (info) {
501 		mword *ctxstart, *ctxcurrent, *ctxend;
502 		char **start = (char**)info->client_info.stack_start;
503 		if (info->client_info.skip || info->client_info.gc_disabled)
504 			continue;
505 		while (start < (char**)info->client_info.info.stack_end) {
506 			if (*start >= obj && *start < endobj)
507 				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->client_info.stack_start, info->client_info.info.stack_end);
508 			start++;
509 		}
510 
511 		for (ctxstart = ctxcurrent = (mword*) &info->client_info.ctx, ctxend = (mword*) (&info->client_info.ctx + 1); ctxcurrent < ctxend; ctxcurrent ++) {
512 			mword w = *ctxcurrent;
513 
514 			if (w >= (mword)obj && w < (mword)obj + size)
515 				SGEN_LOG (0, "Object %p referenced in saved reg %d of thread %p (id %p)", obj, (int) (ctxcurrent - ctxstart), info, (gpointer)mono_thread_info_get_tid (info));
516 		}
517 	} FOREACH_THREAD_END
518 #endif
519 }
520 
521 /*
522  * Debugging function: find in the conservative roots where @obj is being pinned.
523  */
524 static G_GNUC_UNUSED void
find_pinning_reference(char * obj,size_t size)525 find_pinning_reference (char *obj, size_t size)
526 {
527 	char **start;
528 	RootRecord *root;
529 	char *endobj = obj + size;
530 
531 	SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_NORMAL], char **, start, RootRecord *, root) {
532 		/* if desc is non-null it has precise info */
533 		if (!root->root_desc) {
534 			while (start < (char**)root->end_root) {
535 				if (*start >= obj && *start < endobj) {
536 					SGEN_LOG (0, "Object %p referenced in pinned roots %p-%p\n", obj, start, root->end_root);
537 				}
538 				start++;
539 			}
540 		}
541 	} SGEN_HASH_TABLE_FOREACH_END;
542 
543 	find_pinning_ref_from_thread (obj, size);
544 }
545 
546 #undef HANDLE_PTR
547 #define HANDLE_PTR(ptr,obj)	do {					\
548 		char* __target = *(char**)ptr;				\
549 		if (__target) {						\
550 			if (sgen_ptr_in_nursery (__target)) {		\
551 				g_assert (!SGEN_OBJECT_IS_FORWARDED (__target)); \
552 			} else {					\
553 				mword __size = sgen_safe_object_get_size ((GCObject*)__target); \
554 				if (__size <= SGEN_MAX_SMALL_OBJ_SIZE)	\
555 					g_assert (major_collector.is_object_live ((GCObject*)__target)); \
556 				else					\
557 					g_assert (sgen_los_object_is_pinned ((GCObject*)__target)); \
558 			}						\
559 		}							\
560 	} while (0)
561 
562 static void
check_marked_callback(GCObject * obj,size_t size,void * dummy)563 check_marked_callback (GCObject *obj, size_t size, void *dummy)
564 {
565 	char *start = (char*)obj;
566 	gboolean flag = (gboolean) (size_t) dummy;
567 	SgenDescriptor desc;
568 
569 	if (sgen_ptr_in_nursery (start)) {
570 		if (flag)
571 			SGEN_ASSERT (0, SGEN_OBJECT_IS_PINNED (obj), "All objects remaining in the nursery must be pinned");
572 	} else if (flag) {
573 		if (!sgen_los_object_is_pinned (obj))
574 			return;
575 	} else {
576 		if (!major_collector.is_object_live (obj))
577 			return;
578 	}
579 
580 	desc = sgen_obj_get_descriptor_safe (obj);
581 
582 #include "sgen-scan-object.h"
583 }
584 
585 void
sgen_check_heap_marked(gboolean nursery_must_be_pinned)586 sgen_check_heap_marked (gboolean nursery_must_be_pinned)
587 {
588 	setup_valid_nursery_objects ();
589 
590 	iterate_valid_nursery_objects (check_marked_callback, (void*)(size_t)nursery_must_be_pinned);
591 	major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, check_marked_callback, (void*)FALSE);
592 	sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
593 }
594 
595 static void
check_nursery_objects_pinned_callback(char * obj,size_t size,void * data)596 check_nursery_objects_pinned_callback (char *obj, size_t size, void *data /* ScanCopyContext *ctx */)
597 {
598 	gboolean pinned = (gboolean) (size_t) data;
599 
600 	g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
601 	if (pinned)
602 		g_assert (SGEN_OBJECT_IS_PINNED (obj));
603 	else
604 		g_assert (!SGEN_OBJECT_IS_PINNED (obj));
605 }
606 
607 void
sgen_check_nursery_objects_pinned(gboolean pinned)608 sgen_check_nursery_objects_pinned (gboolean pinned)
609 {
610 	sgen_clear_nursery_fragments ();
611 	sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
612 			(IterateObjectCallbackFunc)check_nursery_objects_pinned_callback, (void*) (size_t) pinned /* (void*)&ctx */, FALSE, TRUE);
613 }
614 
615 static void
verify_scan_starts(char * start,char * end)616 verify_scan_starts (char *start, char *end)
617 {
618 	size_t i;
619 
620 	for (i = 0; i < nursery_section->num_scan_start; ++i) {
621 		char *addr = nursery_section->scan_starts [i];
622 		if (addr > start && addr < end)
623 			SGEN_LOG (0, "NFC-BAD SCAN START [%zu] %p for obj [%p %p]", i, addr, start, end);
624 	}
625 }
626 
627 void
sgen_debug_verify_nursery(gboolean do_dump_nursery_content)628 sgen_debug_verify_nursery (gboolean do_dump_nursery_content)
629 {
630 	char *start, *end, *cur, *hole_start;
631 
632 	if (nursery_canaries_enabled ())
633 		SGEN_LOG (0, "Checking nursery canaries...");
634 
635 	/*This cleans up unused fragments */
636 	sgen_nursery_allocator_prepare_for_pinning ();
637 
638 	hole_start = start = cur = sgen_get_nursery_start ();
639 	end = sgen_get_nursery_end ();
640 
641 	while (cur < end) {
642 		size_t ss, size;
643 		gboolean is_array_fill;
644 
645 		if (!*(void**)cur) {
646 			cur += sizeof (void*);
647 			continue;
648 		}
649 
650 		if (object_is_forwarded (cur))
651 			SGEN_LOG (0, "FORWARDED OBJ %p", cur);
652 		else if (object_is_pinned (cur))
653 			SGEN_LOG (0, "PINNED OBJ %p", cur);
654 
655 		ss = safe_object_get_size ((GCObject*)cur);
656 		size = SGEN_ALIGN_UP (ss);
657 		verify_scan_starts (cur, cur + size);
658 		is_array_fill = sgen_client_object_is_array_fill ((GCObject*)cur);
659 		if (do_dump_nursery_content) {
660 			GCVTable vtable = SGEN_LOAD_VTABLE ((GCObject*)cur);
661 			if (cur > hole_start)
662 				SGEN_LOG (0, "HOLE [%p %p %d]", hole_start, cur, (int)(cur - hole_start));
663 			SGEN_LOG (0, "OBJ  [%p %p %d %d %s.%s %d]", cur, cur + size, (int)size, (int)ss,
664 					sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable),
665 					is_array_fill);
666 		}
667 		if (nursery_canaries_enabled () && !is_array_fill) {
668 			CHECK_CANARY_FOR_OBJECT ((GCObject*)cur, TRUE);
669 			CANARIFY_SIZE (size);
670 		}
671 		cur += size;
672 		hole_start = cur;
673 	}
674 }
675 
676 /*
677  * Checks that no objects in the nursery are fowarded or pinned.  This
678  * is a precondition to restarting the mutator while doing a
679  * concurrent collection.  Note that we don't clear fragments because
680  * we depend on that having happened earlier.
681  */
682 void
sgen_debug_check_nursery_is_clean(void)683 sgen_debug_check_nursery_is_clean (void)
684 {
685 	char *end, *cur;
686 
687 	cur = sgen_get_nursery_start ();
688 	end = sgen_get_nursery_end ();
689 
690 	while (cur < end) {
691 		size_t size;
692 
693 		if (!*(void**)cur) {
694 			cur += sizeof (void*);
695 			continue;
696 		}
697 
698 		g_assert (!object_is_forwarded (cur));
699 		g_assert (!object_is_pinned (cur));
700 
701 		size = SGEN_ALIGN_UP (safe_object_get_size ((GCObject*)cur));
702 		verify_scan_starts (cur, cur + size);
703 
704 		cur += size;
705 	}
706 }
707 
708 static gboolean scan_object_for_specific_ref_precise = TRUE;
709 
710 #undef HANDLE_PTR
711 #define HANDLE_PTR(ptr,obj) do {					\
712 		if ((GCObject*)*(ptr) == key) {				\
713 			GCVTable vtable = SGEN_LOAD_VTABLE (*(ptr));	\
714 			g_print ("found ref to %p in object %p (%s.%s) at offset %zd\n", \
715 					key, (obj), sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), ((char*)(ptr) - (char*)(obj))); \
716 		}							\
717 	} while (0)
718 
719 static void
scan_object_for_specific_ref(GCObject * obj,GCObject * key)720 scan_object_for_specific_ref (GCObject *obj, GCObject *key)
721 {
722 	GCObject *forwarded;
723 
724 	if ((forwarded = SGEN_OBJECT_IS_FORWARDED (obj)))
725 		obj = forwarded;
726 
727 	if (scan_object_for_specific_ref_precise) {
728 		char *start = (char*)obj;
729 		SgenDescriptor desc = sgen_obj_get_descriptor_safe (obj);
730 		#include "sgen-scan-object.h"
731 	} else {
732 		mword *words = (mword*)obj;
733 		size_t size = safe_object_get_size (obj);
734 		int i;
735 		for (i = 0; i < size / sizeof (mword); ++i) {
736 			if (words [i] == (mword)key) {
737 				GCVTable vtable = SGEN_LOAD_VTABLE (obj);
738 				g_print ("found possible ref to %p in object %p (%s.%s) at offset %zd\n",
739 						key, obj, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), i * sizeof (mword));
740 			}
741 		}
742 	}
743 }
744 
745 static void
scan_object_for_specific_ref_callback(GCObject * obj,size_t size,GCObject * key)746 scan_object_for_specific_ref_callback (GCObject *obj, size_t size, GCObject *key)
747 {
748 	scan_object_for_specific_ref (obj, key);
749 }
750 
751 static void
check_root_obj_specific_ref(RootRecord * root,GCObject * key,GCObject * obj)752 check_root_obj_specific_ref (RootRecord *root, GCObject *key, GCObject *obj)
753 {
754 	if (key != obj)
755 		return;
756 	g_print ("found ref to %p in root record %p\n", key, root);
757 }
758 
759 static GCObject *check_key = NULL;
760 static RootRecord *check_root = NULL;
761 
762 static void
check_root_obj_specific_ref_from_marker(GCObject ** obj,void * gc_data)763 check_root_obj_specific_ref_from_marker (GCObject **obj, void *gc_data)
764 {
765 	check_root_obj_specific_ref (check_root, check_key, *obj);
766 }
767 
768 static void
scan_roots_for_specific_ref(GCObject * key,int root_type)769 scan_roots_for_specific_ref (GCObject *key, int root_type)
770 {
771 	void **start_root;
772 	RootRecord *root;
773 	check_key = key;
774 
775 	SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], void **, start_root, RootRecord *, root) {
776 		SgenDescriptor desc = root->root_desc;
777 
778 		check_root = root;
779 
780 		switch (desc & ROOT_DESC_TYPE_MASK) {
781 		case ROOT_DESC_BITMAP:
782 			desc >>= ROOT_DESC_TYPE_SHIFT;
783 			while (desc) {
784 				if (desc & 1)
785 					check_root_obj_specific_ref (root, key, (GCObject *)*start_root);
786 				desc >>= 1;
787 				start_root++;
788 			}
789 			return;
790 		case ROOT_DESC_COMPLEX: {
791 			gsize *bitmap_data = (gsize *)sgen_get_complex_descriptor_bitmap (desc);
792 			int bwords = (int) ((*bitmap_data) - 1);
793 			void **start_run = start_root;
794 			bitmap_data++;
795 			while (bwords-- > 0) {
796 				gsize bmap = *bitmap_data++;
797 				void **objptr = start_run;
798 				while (bmap) {
799 					if (bmap & 1)
800 						check_root_obj_specific_ref (root, key, (GCObject *)*objptr);
801 					bmap >>= 1;
802 					++objptr;
803 				}
804 				start_run += GC_BITS_PER_WORD;
805 			}
806 			break;
807 		}
808 		case ROOT_DESC_VECTOR: {
809 			void **p;
810 
811 			for (p = start_root; p < (void**)root->end_root; p++) {
812 				if (*p)
813 					check_root_obj_specific_ref (root, key, (GCObject *)*p);
814 			}
815 			break;
816 		}
817 		case ROOT_DESC_USER: {
818 			SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
819 			marker (start_root, check_root_obj_specific_ref_from_marker, NULL);
820 			break;
821 		}
822 		case ROOT_DESC_RUN_LEN:
823 			g_assert_not_reached ();
824 		default:
825 			g_assert_not_reached ();
826 		}
827 	} SGEN_HASH_TABLE_FOREACH_END;
828 
829 	check_key = NULL;
830 	check_root = NULL;
831 }
832 
833 void
mono_gc_scan_for_specific_ref(GCObject * key,gboolean precise)834 mono_gc_scan_for_specific_ref (GCObject *key, gboolean precise)
835 {
836 	void **ptr;
837 	RootRecord *root;
838 
839 	scan_object_for_specific_ref_precise = precise;
840 
841 	sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
842 			(IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE, FALSE);
843 
844 	major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
845 
846 	sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
847 
848 	scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
849 	scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
850 
851 	SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], void **, ptr, RootRecord *, root) {
852 		while (ptr < (void**)root->end_root) {
853 			check_root_obj_specific_ref (root, (GCObject *)*ptr, key);
854 			++ptr;
855 		}
856 	} SGEN_HASH_TABLE_FOREACH_END;
857 
858 	if (sgen_is_world_stopped ())
859 		find_pinning_ref_from_thread ((char*)key, sizeof (GCObject));
860 }
861 
862 #ifndef SGEN_WITHOUT_MONO
863 
864 static MonoDomain *check_domain = NULL;
865 
866 static void
check_obj_not_in_domain(MonoObject ** o)867 check_obj_not_in_domain (MonoObject **o)
868 {
869 	g_assert (((*o))->vtable->domain != check_domain);
870 }
871 
872 
873 static void
check_obj_not_in_domain_callback(GCObject ** o,void * gc_data)874 check_obj_not_in_domain_callback (GCObject **o, void *gc_data)
875 {
876 	g_assert ((*o)->vtable->domain != check_domain);
877 }
878 
879 void
sgen_scan_for_registered_roots_in_domain(MonoDomain * domain,int root_type)880 sgen_scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
881 {
882 	void **start_root;
883 	RootRecord *root;
884 	check_domain = domain;
885 	SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], void **, start_root, RootRecord *, root) {
886 		SgenDescriptor desc = root->root_desc;
887 
888 		/* The MonoDomain struct is allowed to hold
889 		   references to objects in its own domain. */
890 		if (start_root == (void**)domain)
891 			continue;
892 
893 		switch (desc & ROOT_DESC_TYPE_MASK) {
894 		case ROOT_DESC_BITMAP:
895 			desc >>= ROOT_DESC_TYPE_SHIFT;
896 			while (desc) {
897 				if ((desc & 1) && *start_root)
898 					check_obj_not_in_domain ((MonoObject **)*start_root);
899 				desc >>= 1;
900 				start_root++;
901 			}
902 			break;
903 		case ROOT_DESC_COMPLEX: {
904 			gsize *bitmap_data = (gsize *)sgen_get_complex_descriptor_bitmap (desc);
905 			int bwords = (int)((*bitmap_data) - 1);
906 			void **start_run = start_root;
907 			bitmap_data++;
908 			while (bwords-- > 0) {
909 				gsize bmap = *bitmap_data++;
910 				void **objptr = start_run;
911 				while (bmap) {
912 					if ((bmap & 1) && *objptr)
913 						check_obj_not_in_domain ((MonoObject **)*objptr);
914 					bmap >>= 1;
915 					++objptr;
916 				}
917 				start_run += GC_BITS_PER_WORD;
918 			}
919 			break;
920 		}
921 		case ROOT_DESC_VECTOR: {
922 			void **p;
923 
924 			for (p = start_root; p < (void**)root->end_root; p++) {
925 				if (*p)
926 					check_obj_not_in_domain ((MonoObject **)*p);
927 			}
928 			break;
929 		}
930 		case ROOT_DESC_USER: {
931 			SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
932 			marker (start_root, check_obj_not_in_domain_callback, NULL);
933 			break;
934 		}
935 		case ROOT_DESC_RUN_LEN:
936 			g_assert_not_reached ();
937 		default:
938 			g_assert_not_reached ();
939 		}
940 	} SGEN_HASH_TABLE_FOREACH_END;
941 
942 	check_domain = NULL;
943 }
944 
945 static gboolean
is_xdomain_ref_allowed(GCObject ** ptr,GCObject * obj,MonoDomain * domain)946 is_xdomain_ref_allowed (GCObject **ptr, GCObject *obj, MonoDomain *domain)
947 {
948 	MonoObject *o = (MonoObject*)(obj);
949 	MonoObject *ref = *ptr;
950 	size_t offset = (char*)(ptr) - (char*)o;
951 
952 	if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
953 		return TRUE;
954 	if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
955 		return TRUE;
956 
957 #ifndef DISABLE_REMOTING
958 	if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) &&
959 			offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
960 		return TRUE;
961 #endif
962 	/*
963 	 *  at System.IO.MemoryStream.InternalConstructor (byte[],int,int,bool,bool) [0x0004d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:121
964 	 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
965 	 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
966 	 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
967 	 * at System.Runtime.Remoting.Messaging.MethodCall..ctor (System.Runtime.Remoting.Messaging.CADMethodCallMessage) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/MethodCall.cs:87
968 	 * at System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) [0x00018] in /home/schani/Work/novell/trunk/mcs/class/corlib/System/AppDomain.cs:1213
969 	 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
970 	 * at System.Runtime.Remoting.Channels.CrossAppDomainSink.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage) [0x00008] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Channels/CrossAppDomainChannel.cs:198
971 	 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
972 	 */
973 	if (!strcmp (ref->vtable->klass->name_space, "System") &&
974 			!strcmp (ref->vtable->klass->name, "Byte[]") &&
975 			!strcmp (o->vtable->klass->name_space, "System.IO") &&
976 			!strcmp (o->vtable->klass->name, "MemoryStream"))
977 		return TRUE;
978 	return FALSE;
979 }
980 
981 static void
check_reference_for_xdomain(GCObject ** ptr,GCObject * obj,MonoDomain * domain)982 check_reference_for_xdomain (GCObject **ptr, GCObject *obj, MonoDomain *domain)
983 {
984 	MonoObject *ref = *ptr;
985 	size_t offset = (char*)(ptr) - (char*)obj;
986 	MonoClass *klass;
987 	MonoClassField *field;
988 	char *str;
989 
990 	if (!ref || ref->vtable->domain == domain)
991 		return;
992 	if (is_xdomain_ref_allowed (ptr, obj, domain))
993 		return;
994 
995 	field = NULL;
996 	for (klass = obj->vtable->klass; klass; klass = klass->parent) {
997 		int i;
998 
999 		int fcount = mono_class_get_field_count (klass);
1000 		for (i = 0; i < fcount; ++i) {
1001 			if (klass->fields[i].offset == offset) {
1002 				field = &klass->fields[i];
1003 				break;
1004 			}
1005 		}
1006 		if (field)
1007 			break;
1008 	}
1009 
1010 	if (ref->vtable->klass == mono_defaults.string_class) {
1011 		MonoError error;
1012 		str = mono_string_to_utf8_checked ((MonoString*)ref, &error);
1013 		mono_error_cleanup (&error);
1014 	} else
1015 		str = NULL;
1016 	g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s)  -  pointed to by:\n",
1017 			obj, obj->vtable->klass->name_space, obj->vtable->klass->name,
1018 			offset, field ? field->name : "",
1019 			ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1020 	mono_gc_scan_for_specific_ref (obj, TRUE);
1021 	if (str)
1022 		g_free (str);
1023 }
1024 
1025 #undef HANDLE_PTR
1026 #define HANDLE_PTR(ptr,obj)	check_reference_for_xdomain ((ptr), (obj), domain)
1027 
1028 static void
scan_object_for_xdomain_refs(GCObject * obj,mword size,void * data)1029 scan_object_for_xdomain_refs (GCObject *obj, mword size, void *data)
1030 {
1031 	char *start = (char*)obj;
1032 	MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
1033 	MonoDomain *domain = vt->domain;
1034 	SgenDescriptor desc = sgen_vtable_get_descriptor (vt);
1035 
1036 	#include "sgen-scan-object.h"
1037 }
1038 
1039 void
sgen_check_for_xdomain_refs(void)1040 sgen_check_for_xdomain_refs (void)
1041 {
1042 	LOSObject *bigobj;
1043 
1044 	sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1045 			(IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE, TRUE);
1046 
1047 	major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1048 
1049 	for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1050 		scan_object_for_xdomain_refs ((GCObject*)bigobj->data, sgen_los_object_size (bigobj), NULL);
1051 }
1052 
1053 #endif
1054 
1055 /* If not null, dump the heap after each collection into this file */
1056 static FILE *heap_dump_file = NULL;
1057 
1058 void
sgen_dump_occupied(char * start,char * end,char * section_start)1059 sgen_dump_occupied (char *start, char *end, char *section_start)
1060 {
1061 	fprintf (heap_dump_file, "<occupied offset=\"%zd\" size=\"%zd\"/>\n", start - section_start, end - start);
1062 }
1063 
1064 void
sgen_dump_section(GCMemSection * section,const char * type)1065 sgen_dump_section (GCMemSection *section, const char *type)
1066 {
1067 	char *start = section->data;
1068 	char *end = section->end_data;
1069 	char *occ_start = NULL;
1070 
1071 	fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)(section->end_data - section->data));
1072 
1073 	while (start < end) {
1074 		guint size;
1075 		//GCVTable vt;
1076 		//MonoClass *class;
1077 
1078 		if (!*(void**)start) {
1079 			if (occ_start) {
1080 				sgen_dump_occupied (occ_start, start, section->data);
1081 				occ_start = NULL;
1082 			}
1083 			start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1084 			continue;
1085 		}
1086 
1087 		if (!occ_start)
1088 			occ_start = start;
1089 
1090 		//vt = SGEN_LOAD_VTABLE (start);
1091 		//class = vt->klass;
1092 
1093 		size = SGEN_ALIGN_UP (safe_object_get_size ((GCObject*) start));
1094 
1095 		/*
1096 		fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
1097 				start - section->data,
1098 				vt->klass->name_space, vt->klass->name,
1099 				size);
1100 		*/
1101 
1102 		start += size;
1103 	}
1104 	if (occ_start)
1105 		sgen_dump_occupied (occ_start, start, section->data);
1106 
1107 	fprintf (heap_dump_file, "</section>\n");
1108 }
1109 
1110 static void
dump_object(GCObject * obj,gboolean dump_location)1111 dump_object (GCObject *obj, gboolean dump_location)
1112 {
1113 #ifndef SGEN_WITHOUT_MONO
1114 	static char class_name [1024];
1115 
1116 	MonoClass *klass = mono_object_class (obj);
1117 	int i, j;
1118 
1119 	/*
1120 	 * Python's XML parser is too stupid to parse angle brackets
1121 	 * in strings, so we just ignore them;
1122 	 */
1123 	i = j = 0;
1124 	while (klass->name [i] && j < sizeof (class_name) - 1) {
1125 		if (!strchr ("<>\"", klass->name [i]))
1126 			class_name [j++] = klass->name [i];
1127 		++i;
1128 	}
1129 	g_assert (j < sizeof (class_name));
1130 	class_name [j] = 0;
1131 
1132 	fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%zd\"",
1133 			klass->name_space, class_name,
1134 			safe_object_get_size (obj));
1135 	if (dump_location) {
1136 		const char *location;
1137 		if (sgen_ptr_in_nursery (obj))
1138 			location = "nursery";
1139 		else if (safe_object_get_size (obj) <= SGEN_MAX_SMALL_OBJ_SIZE)
1140 			location = "major";
1141 		else
1142 			location = "LOS";
1143 		fprintf (heap_dump_file, " location=\"%s\"", location);
1144 	}
1145 	fprintf (heap_dump_file, "/>\n");
1146 #endif
1147 }
1148 
1149 void
sgen_debug_enable_heap_dump(const char * filename)1150 sgen_debug_enable_heap_dump (const char *filename)
1151 {
1152 	heap_dump_file = fopen (filename, "w");
1153 	if (heap_dump_file) {
1154 		fprintf (heap_dump_file, "<sgen-dump>\n");
1155 		sgen_pin_stats_enable ();
1156 	}
1157 }
1158 
1159 void
sgen_debug_dump_heap(const char * type,int num,const char * reason)1160 sgen_debug_dump_heap (const char *type, int num, const char *reason)
1161 {
1162 	SgenPointerQueue *pinned_objects;
1163 	LOSObject *bigobj;
1164 	int i;
1165 
1166 	if (!heap_dump_file)
1167 		return;
1168 
1169 	fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
1170 	if (reason)
1171 		fprintf (heap_dump_file, " reason=\"%s\"", reason);
1172 	fprintf (heap_dump_file, ">\n");
1173 #ifndef SGEN_WITHOUT_MONO
1174 	fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
1175 #endif
1176 	sgen_dump_internal_mem_usage (heap_dump_file);
1177 	fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_STACK));
1178 	/* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
1179 	fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_OTHER));
1180 
1181 	fprintf (heap_dump_file, "<pinned-objects>\n");
1182 	pinned_objects = sgen_pin_stats_get_object_list ();
1183 	for (i = 0; i < pinned_objects->next_slot; ++i)
1184 		dump_object ((GCObject *)pinned_objects->data [i], TRUE);
1185 	fprintf (heap_dump_file, "</pinned-objects>\n");
1186 
1187 	sgen_dump_section (nursery_section, "nursery");
1188 
1189 	major_collector.dump_heap (heap_dump_file);
1190 
1191 	fprintf (heap_dump_file, "<los>\n");
1192 	for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1193 		dump_object ((GCObject*)bigobj->data, FALSE);
1194 	fprintf (heap_dump_file, "</los>\n");
1195 
1196 	fprintf (heap_dump_file, "</collection>\n");
1197 }
1198 
1199 static GCObject *found_obj;
1200 
1201 static void
find_object_for_ptr_callback(GCObject * obj,size_t size,void * user_data)1202 find_object_for_ptr_callback (GCObject *obj, size_t size, void *user_data)
1203 {
1204 	char *ptr = (char *)user_data;
1205 
1206 	if (ptr >= (char*)obj && ptr < (char*)obj + size) {
1207 		g_assert (!found_obj);
1208 		found_obj = obj;
1209 	}
1210 }
1211 
1212 /* for use in the debugger */
1213 GCObject*
sgen_find_object_for_ptr(char * ptr)1214 sgen_find_object_for_ptr (char *ptr)
1215 {
1216 	if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
1217 		found_obj = NULL;
1218 		sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1219 				find_object_for_ptr_callback, ptr, TRUE, FALSE);
1220 		if (found_obj)
1221 			return found_obj;
1222 	}
1223 
1224 	found_obj = NULL;
1225 	sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
1226 	if (found_obj)
1227 		return found_obj;
1228 
1229 	/*
1230 	 * Very inefficient, but this is debugging code, supposed to
1231 	 * be called from gdb, so we don't care.
1232 	 */
1233 	found_obj = NULL;
1234 	major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, find_object_for_ptr_callback, ptr);
1235 	return found_obj;
1236 }
1237 
1238 #endif /*HAVE_SGEN_GC*/
1239