+/*
+ * ######################################################################
+ * ######## Collector debugging
+ * ######################################################################
+ */
+
+const char*descriptor_types [] = {
+ "run_length",
+ "small_bitmap",
+ "string",
+ "complex",
+ "vector",
+ "array",
+ "large_bitmap",
+ "complex_arr"
+};
+
+void
+describe_ptr (char *ptr)
+{
+ GCMemSection *section;
+ MonoVTable *vtable;
+ mword desc;
+ int type;
+
+ if (ptr_in_nursery (ptr)) {
+ printf ("Pointer inside nursery.\n");
+ } else {
+ for (section = section_list; section;) {
+ if (ptr >= section->data && ptr < section->data + section->size)
+ break;
+ section = section->next;
+ }
+
+ if (section) {
+ printf ("Pointer inside oldspace.\n");
+ } else if (obj_is_from_pinned_alloc (ptr)) {
+ printf ("Pointer is inside a pinned chunk.\n");
+ } else {
+ printf ("Pointer unknown.\n");
+ return;
+ }
+ }
+
+ if (object_is_pinned (ptr))
+ printf ("Object is pinned.\n");
+
+ if (object_is_forwarded (ptr))
+ printf ("Object is forwared.\n");
+
+ // FIXME: Handle pointers to the inside of objects
+ vtable = (MonoVTable*)LOAD_VTABLE (ptr);
+
+ printf ("VTable: %p\n", vtable);
+ if (vtable == NULL) {
+ printf ("VTable is invalid (empty).\n");
+ return;
+ }
+ if (ptr_in_nursery (vtable)) {
+ printf ("VTable is invalid (points inside nursery).\n");
+ return;
+ }
+ printf ("Class: %s\n", vtable->klass->name);
+
+ desc = ((GCVTable*)vtable)->desc;
+ printf ("Descriptor: %lx\n", desc);
+
+ type = desc & 0x7;
+ printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
+}
+
+static mword*
+find_in_remset_loc (mword *p, char *addr, gboolean *found)
+{
+ void **ptr;
+ mword count, desc;
+ size_t skip_size;
+
+ switch ((*p) & REMSET_TYPE_MASK) {
+ case REMSET_LOCATION:
+ if (*p == (mword)addr)
+ *found = TRUE;
+ return p + 1;
+ case REMSET_RANGE:
+ ptr = (void**)(*p & ~REMSET_TYPE_MASK);
+ count = p [1];
+ if ((void**)addr >= ptr && (void**)addr < ptr + count)
+ *found = TRUE;
+ return p + 2;
+ case REMSET_OBJECT:
+ ptr = (void**)(*p & ~REMSET_TYPE_MASK);
+ count = safe_object_get_size ((MonoObject*)ptr);
+ count += (ALLOC_ALIGN - 1);
+ count &= (ALLOC_ALIGN - 1);
+ count /= sizeof (mword);
+ if ((void**)addr >= ptr && (void**)addr < ptr + count)
+ *found = TRUE;
+ return p + 1;
+ case REMSET_OTHER: {
+ switch (p [1]) {
+ case REMSET_VTYPE:
+ ptr = (void**)(*p & ~REMSET_TYPE_MASK);
+ desc = p [2];
+
+ switch (desc & 0x7) {
+ case DESC_TYPE_RUN_LENGTH:
+ OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
+ /* The descriptor includes the size of MonoObject */
+ skip_size -= sizeof (MonoObject);
+ if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
+ *found = TRUE;
+ break;
+ default:
+ // FIXME:
+ g_assert_not_reached ();
+ }
+
+ return p + 3;
+ case REMSET_ROOT_LOCATION:
+ return p + 2;
+ default:
+ g_assert_not_reached ();
+ }
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ }
+ return NULL;
+}
+
+/*
+ * Return whenever ADDR occurs in the remembered sets
+ */
+static gboolean
+find_in_remsets (char *addr)
+{
+ int i;
+ SgenThreadInfo *info;
+ RememberedSet *remset;
+ mword *p;
+ gboolean found = FALSE;
+
+ /* the global one */
+ for (remset = global_remset; remset; remset = remset->next) {
+ DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %zd\n", remset->data, remset->store_next, remset->store_next - remset->data));
+ for (p = remset->data; p < remset->store_next;) {
+ p = find_in_remset_loc (p, addr, &found);
+ if (found)
+ return TRUE;
+ }
+ }
+ /* the per-thread ones */
+ for (i = 0; i < THREAD_HASH_SIZE; ++i) {
+ for (info = thread_table [i]; info; info = info->next) {
+ for (remset = info->remset; remset; remset = remset->next) {
+ DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %zd\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
+ for (p = remset->data; p < remset->store_next;) {
+ p = find_in_remset_loc (p, addr, &found);
+ if (found)
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+#undef HANDLE_PTR
+#define HANDLE_PTR(ptr,obj) do { \
+ if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
+ if (!find_in_remsets ((char*)(ptr))) { \
+ fprintf (gc_debug_file, "Oldspace->newspace reference %p at offset %zd 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); \
+ g_assert_not_reached (); \
+ } \
+ } \
+ } while (0)
+
+/*
+ * Check that each object reference inside the area which points into the nursery
+ * can be found in the remembered sets.
+ */
+static void __attribute__((noinline))
+check_remsets_for_area (char *start, char *end)
+{
+ GCVTable *vt;
+ size_t skip_size;
+ int type;
+ int type_str = 0, type_rlen = 0, type_bitmap = 0, type_vector = 0, type_lbit = 0, type_complex = 0;
+ mword desc;
+ new_obj_references = 0;
+ obj_references_checked = 0;
+ while (start < end) {
+ if (!*(void**)start) {
+ start += sizeof (void*); /* should be ALLOC_ALIGN, really */
+ continue;
+ }
+ vt = (GCVTable*)LOAD_VTABLE (start);
+ DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
+ if (0) {
+ MonoObject *obj = (MonoObject*)start;
+ g_print ("found at %p (0x%lx): %s.%s\n", start, (long)vt->desc, obj->vtable->klass->name_space, obj->vtable->klass->name);
+ }
+ desc = vt->desc;
+ type = desc & 0x7;
+ if (type == DESC_TYPE_STRING) {
+ STRING_SIZE (skip_size, start);
+ start += skip_size;
+ type_str++;
+ continue;
+ } else if (type == DESC_TYPE_RUN_LENGTH) {
+ OBJ_RUN_LEN_SIZE (skip_size, desc, start);
+ g_assert (skip_size);
+ OBJ_RUN_LEN_FOREACH_PTR (desc,start);
+ start += skip_size;
+ type_rlen++;
+ continue;
+ } else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
+ skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
+ skip_size *= mono_array_length ((MonoArray*)start);
+ skip_size += sizeof (MonoArray);
+ skip_size += (ALLOC_ALIGN - 1);
+ skip_size &= ~(ALLOC_ALIGN - 1);
+ OBJ_VECTOR_FOREACH_PTR (vt, start);
+ if (((MonoArray*)start)->bounds) {
+ /* account for the bounds */
+ skip_size += sizeof (MonoArrayBounds) * vt->klass->rank;
+ }
+ start += skip_size;
+ type_vector++;
+ continue;
+ } else if (type == DESC_TYPE_SMALL_BITMAP) {
+ OBJ_BITMAP_SIZE (skip_size, desc, start);
+ g_assert (skip_size);
+ OBJ_BITMAP_FOREACH_PTR (desc,start);
+ start += skip_size;
+ type_bitmap++;
+ continue;
+ } else if (type == DESC_TYPE_LARGE_BITMAP) {
+ skip_size = safe_object_get_size ((MonoObject*)start);
+ skip_size += (ALLOC_ALIGN - 1);
+ skip_size &= ~(ALLOC_ALIGN - 1);
+ OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start);
+ start += skip_size;
+ type_lbit++;
+ continue;
+ } else if (type == DESC_TYPE_COMPLEX) {
+ /* this is a complex object */
+ skip_size = safe_object_get_size ((MonoObject*)start);
+ skip_size += (ALLOC_ALIGN - 1);
+ skip_size &= ~(ALLOC_ALIGN - 1);
+ OBJ_COMPLEX_FOREACH_PTR (vt, start);
+ start += skip_size;
+ type_complex++;
+ continue;
+ } else if (type == DESC_TYPE_COMPLEX_ARR) {
+ /* this is an array of complex structs */
+ skip_size = mono_array_element_size (((MonoVTable*)vt)->klass);
+ skip_size *= mono_array_length ((MonoArray*)start);
+ skip_size += sizeof (MonoArray);
+ skip_size += (ALLOC_ALIGN - 1);
+ skip_size &= ~(ALLOC_ALIGN - 1);
+ OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start);
+ if (((MonoArray*)start)->bounds) {
+ /* account for the bounds */
+ skip_size += sizeof (MonoArrayBounds) * vt->klass->rank;
+ }
+ start += skip_size;
+ type_complex++;
+ continue;
+ } else {
+ g_assert (0);
+ }
+ }
+}
+
+/*
+ * Perform consistency check of the heap.
+ *
+ * Assumes the world is stopped.
+ */
+void
+check_consistency (void)
+{
+ GCMemSection *section;
+
+ // Need to add more checks
+ // FIXME: Create a general heap enumeration function and use that
+
+ DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
+
+ // Check that oldspace->newspace pointers are registered with the collector
+ for (section = section_list; section; section = section->next) {
+ if (section->role == MEMORY_ROLE_GEN0)
+ continue;
+ DEBUG (2, fprintf (gc_debug_file, "Scan of old section: %p-%p, size: %d\n", section->data, section->next_data, (int)(section->next_data - section->data)));
+ check_remsets_for_area (section->data, section->next_data);
+ }
+
+ DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
+}
+
+/* Check that the reference is valid */
+#undef HANDLE_PTR
+#define HANDLE_PTR(ptr,obj) do { \
+ if (*(ptr)) { \
+ g_assert (safe_name (*(ptr)) != NULL); \
+ } \
+ } while (0)
+
+/*
+ * check_object:
+ *
+ * Perform consistency check on an object. Currently we only check that the
+ * reference fields are valid.
+ */
+char*
+check_object (char *start)
+{
+ GCVTable *vt;
+ size_t skip_size;
+ mword desc;
+
+ if (!start)
+ return NULL;
+
+ vt = (GCVTable*)LOAD_VTABLE (start);
+ //type = vt->desc & 0x7;
+
+ desc = vt->desc;
+ switch (desc & 0x7) {
+ case DESC_TYPE_STRING:
+ STRING_SIZE (skip_size, start);
+ return start + skip_size;
+ case DESC_TYPE_RUN_LENGTH:
+ OBJ_RUN_LEN_FOREACH_PTR (desc,start);
+ OBJ_RUN_LEN_SIZE (skip_size, desc, start);
+ g_assert (skip_size);
+ return start + skip_size;
+ case DESC_TYPE_ARRAY:
+ case DESC_TYPE_VECTOR:
+ OBJ_VECTOR_FOREACH_PTR (vt, start);
+ skip_size = safe_object_get_size ((MonoObject*)start);
+ skip_size += (ALLOC_ALIGN - 1);
+ skip_size &= ~(ALLOC_ALIGN - 1);
+ return start + skip_size;
+ case DESC_TYPE_SMALL_BITMAP:
+ OBJ_BITMAP_FOREACH_PTR (desc,start);
+ OBJ_BITMAP_SIZE (skip_size, desc, start);
+ return start + skip_size;
+ case DESC_TYPE_LARGE_BITMAP:
+ OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start);
+ skip_size = safe_object_get_size ((MonoObject*)start);
+ skip_size += (ALLOC_ALIGN - 1);
+ skip_size &= ~(ALLOC_ALIGN - 1);
+ return start + skip_size;
+ case DESC_TYPE_COMPLEX:
+ OBJ_COMPLEX_FOREACH_PTR (vt, start);
+ /* this is a complex object */
+ skip_size = safe_object_get_size ((MonoObject*)start);
+ skip_size += (ALLOC_ALIGN - 1);
+ skip_size &= ~(ALLOC_ALIGN - 1);
+ return start + skip_size;
+ case DESC_TYPE_COMPLEX_ARR:
+ OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start);
+ /* this is an array of complex structs */
+ skip_size = safe_object_get_size ((MonoObject*)start);
+ skip_size += (ALLOC_ALIGN - 1);
+ skip_size &= ~(ALLOC_ALIGN - 1);
+ return start + skip_size;
+ }
+ g_assert_not_reached ();
+ return NULL;
+}
+