Procházet zdrojové kódy

2009-09-26 Mark Probst <[email protected]>

	* sgen-gc.c: New debug option "xdomain-checks", which scans the
	whole heap for cross-domain references before each collection.

	* sgen-scan-object.h: The scan action can now use SCAN to scan the
	object.

	* threadpool-internals.h, threadpool.c: New function
	mono_thread_pool_is_queue_array() for checking whether a given
	array is used as a (cross-domain) queue by the thread pool code.

svn path=/trunk/mono/; revision=142674
Mark Probst před 16 roky
rodič
revize
3c8bf9be67

+ 12 - 0
mono/metadata/ChangeLog

@@ -1,3 +1,15 @@
+2009-09-26  Mark Probst  <[email protected]>
+
+	* sgen-gc.c: New debug option "xdomain-checks", which scans the
+	whole heap for cross-domain references before each collection.
+
+	* sgen-scan-object.h: The scan action can now use SCAN to scan the
+	object.
+
+	* threadpool-internals.h, threadpool.c: New function
+	mono_thread_pool_is_queue_array() for checking whether a given
+	array is used as a (cross-domain) queue by the thread pool code.
+
 2009-09-26  Mark Probst  <[email protected]>
 
 	* sgen-gc.c: New function mono_gc_scan_for_specific_ref() which

+ 303 - 2
mono/metadata/sgen-gc.c

@@ -148,6 +148,7 @@
 #include "metadata/method-builder.h"
 #include "metadata/profiler-private.h"
 #include "metadata/monitor.h"
+#include "metadata/threadpool-internals.h"
 #include "utils/mono-mmap.h"
 
 #ifdef HAVE_VALGRIND_MEMCHECK_H
@@ -182,6 +183,8 @@ static FILE* gc_debug_file;
 static gboolean collect_before_allocs = FALSE;
 /* If set, do a heap consistency check before each minor collection */
 static gboolean consistency_check_at_minor_collection = FALSE;
+/* If set, check that there are no references to the domain left at domain unload */
+static gboolean xdomain_checks = FALSE;
 
 /*
 void
@@ -1319,6 +1322,120 @@ scan_area (char *start, char *end)
 		type_str, type_rlen, type_vector, type_bitmap, type_lbit, type_complex);*/
 }
 
+static gboolean
+is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
+{
+	MonoObject *o = (MonoObject*)(obj);
+	MonoObject *ref = (MonoObject*)*(ptr);
+	int offset = (char*)(ptr) - (char*)o;
+
+	if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
+		return TRUE;
+	if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
+		return TRUE;
+	if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
+			offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
+		return TRUE;
+	/* Thread.cached_culture_info */
+	if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
+			!strcmp (ref->vtable->klass->name, "CultureInfo") &&
+			!strcmp(o->vtable->klass->name_space, "System") &&
+			!strcmp(o->vtable->klass->name, "Object[]"))
+		return TRUE;
+	/*
+	 *  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
+	 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
+	 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
+	 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
+	 * 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
+	 * 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
+	 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
+	 * 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
+	 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
+	 */
+	if (!strcmp (ref->vtable->klass->name_space, "System") &&
+			!strcmp (ref->vtable->klass->name, "Byte[]") &&
+			!strcmp (o->vtable->klass->name_space, "System.IO") &&
+			!strcmp (o->vtable->klass->name, "MemoryStream"))
+		return TRUE;
+	/* append_job() in threadpool.c */
+	if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
+			!strcmp (ref->vtable->klass->name, "AsyncResult") &&
+			!strcmp (o->vtable->klass->name_space, "System") &&
+			!strcmp (o->vtable->klass->name, "Object[]") &&
+			mono_thread_pool_is_queue_array ((MonoArray*) o))
+		return TRUE;
+	return FALSE;
+}
+
+static void
+check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
+{
+	MonoObject *o = (MonoObject*)(obj);
+	MonoObject *ref = (MonoObject*)*(ptr);
+	int offset = (char*)(ptr) - (char*)o;
+	MonoClass *class;
+	MonoClassField *field;
+	char *str;
+
+	if (!ref || ref->vtable->domain == domain)
+		return;
+	if (is_xdomain_ref_allowed (ptr, obj, domain))
+		return;
+
+	field = NULL;
+	for (class = o->vtable->klass; class; class = class->parent) {
+		int i;
+
+		for (i = 0; i < class->field.count; ++i) {
+			if (class->fields[i].offset == offset) {
+				field = &class->fields[i];
+				break;
+			}
+		}
+		if (field)
+			break;
+	}
+
+	if (ref->vtable->klass == mono_defaults.string_class)
+		str = mono_string_to_utf8 (ref);
+	else
+		str = NULL;
+	g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s)  -  pointed to by:\n",
+			o, o->vtable->klass->name_space, o->vtable->klass->name,
+			offset, field ? field->name : "",
+			ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
+	mono_gc_scan_for_specific_ref (o);
+	if (str)
+		g_free (str);
+}
+
+#undef HANDLE_PTR
+#define HANDLE_PTR(ptr,obj)	check_reference_for_xdomain ((ptr), (obj), domain)
+
+static char*
+scan_object_for_xdomain_refs (char *start)
+{
+	MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
+
+	#include "sgen-scan-object.h"
+
+	return start;
+}
+
+static void
+scan_area_for_xdomain_refs (char *start, char *end)
+{
+	while (start < end) {
+		if (!*(void**)start) {
+			start += sizeof (void*); /* should be ALLOC_ALIGN, really */
+			continue;
+		}
+
+		start = scan_object_for_xdomain_refs (start);
+	}
+}
+
 #undef HANDLE_PTR
 #define HANDLE_PTR(ptr,obj) do {		\
 	if ((MonoObject*)*(ptr) == key) {	\
@@ -1513,11 +1630,81 @@ scan_area_for_domain (MonoDomain *domain, char *start, char *end)
 		}
 
 #define SCAN_OBJECT_NOSCAN
-#define SCAN_OBJECT_ACTION do { if (remove) memset (start, 0, skip_size); } while (0)
+#define SCAN_OBJECT_ACTION do {						\
+			if (remove) memset (start, 0, skip_size);	\
+		} while (0)
 #include "sgen-scan-object.h"
 	}
 }
 
+static MonoDomain *check_domain = NULL;
+
+static void*
+check_obj_not_in_domain (void *o)
+{
+	g_assert (((MonoObject*)o)->vtable->domain != check_domain);
+	return o;
+}
+
+static void
+scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
+{
+	int i;
+	RootRecord *root;
+	check_domain = domain;
+	for (i = 0; i < roots_hash_size [root_type]; ++i) {
+		for (root = roots_hash [root_type][i]; root; root = root->next) {
+			void **start_root = (void**)root->start_root;
+			mword desc = root->root_desc;
+
+			/* The MonoDomain struct is allowed to hold
+			   references to objects in its own domain. */
+			if (start_root == domain)
+				continue;
+
+			switch (desc & ROOT_DESC_TYPE_MASK) {
+			case ROOT_DESC_BITMAP:
+				desc >>= ROOT_DESC_TYPE_SHIFT;
+				while (desc) {
+					if ((desc & 1) && *start_root)
+						check_obj_not_in_domain (*start_root);
+					desc >>= 1;
+					start_root++;
+				}
+				break;
+			case ROOT_DESC_COMPLEX: {
+				gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
+				int bwords = (*bitmap_data) - 1;
+				void **start_run = start_root;
+				bitmap_data++;
+				while (bwords-- > 0) {
+					gsize bmap = *bitmap_data++;
+					void **objptr = start_run;
+					while (bmap) {
+						if ((bmap & 1) && *objptr)
+							check_obj_not_in_domain (*objptr);
+						bmap >>= 1;
+						++objptr;
+					}
+					start_run += GC_BITS_PER_WORD;
+				}
+				break;
+			}
+			case ROOT_DESC_USER: {
+				MonoGCMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
+				marker (start_root, check_obj_not_in_domain);
+				break;
+			}
+			case ROOT_DESC_RUN_LEN:
+				g_assert_not_reached ();
+			default:
+				g_assert_not_reached ();
+			}
+		}
+	}
+	check_domain = NULL;
+}
+
 static void
 clear_domain_process_pinned_object_callback (PinnedChunk *chunk, char *obj, size_t size, MonoDomain *domain)
 {
@@ -1531,6 +1718,27 @@ clear_domain_free_pinned_object_callback (PinnedChunk *chunk, char *obj, size_t
 		free_pinned_object (chunk, obj, size);
 }
 
+static void
+scan_pinned_object_for_xdomain_refs_callback (PinnedChunk *chunk, char *obj, size_t size, gpointer dummy)
+{
+	scan_object_for_xdomain_refs (obj);
+}
+
+static void
+check_for_xdomain_refs (void)
+{
+	GCMemSection *section;
+	LOSObject *bigobj;
+
+	for (section = section_list; section; section = section->next)
+		scan_area_for_xdomain_refs (section->data, section->end_data);
+
+	for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
+		scan_object_for_xdomain_refs (bigobj->data);
+
+	scan_pinned_objects (scan_pinned_object_for_xdomain_refs_callback, NULL);
+}
+
 /*
  * When appdomains are unloaded we can easily remove objects that have finalizers,
  * but all the others could still be present in random places on the heap.
@@ -1558,6 +1766,12 @@ mono_gc_clear_domain (MonoDomain * domain)
 		}
 	}
 
+	if (xdomain_checks && domain != mono_get_root_domain ()) {
+		scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
+		scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
+		check_for_xdomain_refs ();
+	}
+
 	for (section = section_list; section; section = section->next) {
 		scan_area_for_domain (domain, section->data, section->end_data);
 	}
@@ -2665,6 +2879,9 @@ collect_nursery (size_t requested_size)
 		}
 	}
 
+	if (xdomain_checks)
+		check_for_xdomain_refs ();
+
 	/* 
 	 * not enough room in the old generation to store all the possible data from 
 	 * the nursery in a single continuous space.
@@ -2779,6 +2996,9 @@ major_collection (void)
 		}
 	}
 
+	if (xdomain_checks)
+		check_for_xdomain_refs ();
+
 	/* 
 	 * FIXME: implement Mark/Compact
 	 * Until that is done, we can just apply mostly the same alg as for the nursery:
@@ -4542,8 +4762,16 @@ signal_desc (int signum)
 	return "unknown";
 }
 
+/*
+ * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
+ * have cross-domain checks in the write barrier.
+ */
+//#define XDOMAIN_CHECKS_IN_WBARRIER
+
 #define MANAGED_ALLOCATION
+#ifndef XDOMAIN_CHECKS_IN_WBARRIER
 #define MANAGED_WBARRIER
+#endif
 
 #ifdef MANAGED_ALLOCATION
 static gboolean
@@ -5495,11 +5723,82 @@ mono_gc_wbarrier_arrayref_copy (MonoArray *arr, gpointer slot_ptr, int count)
 	*(rs->store_next++) = count;
 }
 
+static char*
+find_object_for_ptr_in_area (char *ptr, char *start, char *end)
+{
+	while (start < end) {
+		char *old_start;
+
+		if (!*(void**)start) {
+			start += sizeof (void*); /* should be ALLOC_ALIGN, really */
+			continue;
+		}
+
+		old_start = start;
+
+		#define SCAN_OBJECT_NOSCAN
+		#include "sgen-scan-object.h"
+
+		if (ptr >= old_start && ptr < start)
+			return old_start;
+	}
+
+	return NULL;
+}
+
+static char *found_obj;
+
+static void
+find_object_for_ptr_in_pinned_chunk_callback (PinnedChunk *chunk, char *obj, size_t size, char *ptr)
+{
+	if (ptr >= obj && ptr < obj + size) {
+		g_assert (!found_obj);
+		found_obj = obj;
+	}
+}
+
+static char*
+find_object_for_ptr (char *ptr)
+{
+	GCMemSection *section;
+	LOSObject *bigobj;
+
+	for (section = section_list; section; section = section->next) {
+		if (ptr >= section->data && ptr < section->end_data)
+			return find_object_for_ptr_in_area (ptr, section->data, section->end_data);
+	}
+
+	for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
+		if (ptr >= bigobj->data && ptr < bigobj->data + bigobj->size)
+			return bigobj->data;
+	}
+
+	found_obj = NULL;
+	scan_pinned_objects (find_object_for_ptr_in_pinned_chunk_callback, ptr);
+	return found_obj;
+}
+
 void
 mono_gc_wbarrier_generic_nostore (gpointer ptr)
 {
 	RememberedSet *rs;
 	TLAB_ACCESS_INIT;
+
+#ifdef XDOMAIN_CHECKS_IN_WBARRIER
+	if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
+		char *start = find_object_for_ptr (ptr);
+		MonoObject *value = *(MonoObject**)ptr;
+		LOCK_GC;
+		g_assert (start);
+		if (start) {
+			MonoObject *obj = (MonoObject*)start;
+			if (obj->vtable->domain != value->vtable->domain)
+				g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
+		}
+		UNLOCK_GC;
+	}
+#endif
+
 	if (ptr_in_nursery (ptr) || !ptr_in_heap (ptr)) {
 		DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
 		return;
@@ -6084,12 +6383,14 @@ mono_gc_base_init (void)
 				collect_before_allocs = TRUE;
 			} else if (!strcmp (opt, "check-at-minor-collections")) {
 				consistency_check_at_minor_collection = TRUE;
+			} else if (!strcmp (opt, "xdomain-checks")) {
+				xdomain_checks = TRUE;
 			} else if (!strcmp (opt, "clear-at-gc")) {
 				nursery_clear_policy = CLEAR_AT_GC;
 			} else {
 				fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
 				fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
-				fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, clear-at-gc.\n");
+				fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
 				exit (1);
 			}
 		}

+ 22 - 7
mono/metadata/sgen-scan-object.h

@@ -11,7 +11,8 @@
  * SCAN_OBJECT_ACTION - is invoked after an object has been scanned.
  * The object's start is "start", its length in bytes (including
  * padding at the end) is "skip_size".  "desc" is the object's GC
- * descriptor.
+ * descriptor.  The action can use the macro
+ * "SCAN" to scan the object.
  */
 
 #ifndef SCAN_OBJECT_ACTION
@@ -31,16 +32,20 @@
 	switch (desc & 0x7) {
 	case DESC_TYPE_STRING:
 		STRING_SIZE (skip_size, start);
+#define SCAN
 		SCAN_OBJECT_ACTION;
+#undef SCAN
 		start += skip_size;
 		break;
 	case DESC_TYPE_RUN_LENGTH:
 		OBJ_RUN_LEN_SIZE (skip_size, desc, start);
 		g_assert (skip_size);
+#define SCAN OBJ_RUN_LEN_FOREACH_PTR (desc, start)
 #ifndef SCAN_OBJECT_NOSCAN
-		OBJ_RUN_LEN_FOREACH_PTR (desc, start);
+		SCAN;
 #endif
 		SCAN_OBJECT_ACTION;
+#undef SCAN
 		start += skip_size;
 		break;
 	case DESC_TYPE_ARRAY:
@@ -48,29 +53,35 @@
 		skip_size = safe_object_get_size ((MonoObject*)start);
 		skip_size += (ALLOC_ALIGN - 1);
 		skip_size &= ~(ALLOC_ALIGN - 1);
+#define SCAN OBJ_VECTOR_FOREACH_PTR (vt, start)
 #ifndef SCAN_OBJECT_NOSCAN
-		OBJ_VECTOR_FOREACH_PTR (vt, start);
+		SCAN;
 #endif
 		SCAN_OBJECT_ACTION;
+#undef SCAN
 		start += skip_size;
 		break;
 	case DESC_TYPE_SMALL_BITMAP:
 		OBJ_BITMAP_SIZE (skip_size, desc, start);
 		g_assert (skip_size);
+#define SCAN OBJ_BITMAP_FOREACH_PTR (desc, start)
 #ifndef SCAN_OBJECT_NOSCAN
-		OBJ_BITMAP_FOREACH_PTR (desc, start);
+		SCAN;
 #endif
 		SCAN_OBJECT_ACTION;
+#undef SCAN
 		start += skip_size;
 		break;
 	case DESC_TYPE_LARGE_BITMAP:
 		skip_size = safe_object_get_size ((MonoObject*)start);
 		skip_size += (ALLOC_ALIGN - 1);
 		skip_size &= ~(ALLOC_ALIGN - 1);
+#define SCAN OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start)
 #ifndef SCAN_OBJECT_NOSCAN
-		OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start);
+		SCAN;
 #endif
 		SCAN_OBJECT_ACTION;
+#undef SCAN
 		start += skip_size;
 		break;
 	case DESC_TYPE_COMPLEX:
@@ -78,10 +89,12 @@
 		skip_size = safe_object_get_size ((MonoObject*)start);
 		skip_size += (ALLOC_ALIGN - 1);
 		skip_size &= ~(ALLOC_ALIGN - 1);
+#define SCAN OBJ_COMPLEX_FOREACH_PTR (vt, start)
 #ifndef SCAN_OBJECT_NOSCAN
-		OBJ_COMPLEX_FOREACH_PTR (vt, start);
+		SCAN;
 #endif
 		SCAN_OBJECT_ACTION;
+#undef SCAN
 		start += skip_size;
 		break;
 	case DESC_TYPE_COMPLEX_ARR:
@@ -89,10 +102,12 @@
 		skip_size = safe_object_get_size ((MonoObject*)start);
 		skip_size += (ALLOC_ALIGN - 1);
 		skip_size &= ~(ALLOC_ALIGN - 1);
+#define SCAN OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start)
 #ifndef SCAN_OBJECT_NOSCAN
-		OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start);
+		SCAN;
 #endif
 		SCAN_OBJECT_ACTION;
+#undef SCAN
 		start += skip_size;
 		break;
 	default:

+ 2 - 0
mono/metadata/threadpool-internals.h

@@ -2,4 +2,6 @@
 #define _MONO_THREADPOOL_INTERNALS_H_
 
 void mono_thread_pool_remove_socket (int sock) MONO_INTERNAL;
+gboolean mono_thread_pool_is_queue_array (MonoArray *o) MONO_INTERNAL;
+
 #endif

+ 6 - 0
mono/metadata/threadpool.c

@@ -1457,6 +1457,12 @@ threadpool_free_queue (ThreadPool *tp)
 	tp->first_elem = tp->next_elem = 0;
 }
 
+gboolean
+mono_thread_pool_is_queue_array (MonoArray *o)
+{
+	return o == async_tp.array || o == async_io_tp.array;
+}
+
 static void
 async_invoke_thread (gpointer data)
 {