[sgen] Move `sgen_safe_name()` to client code.
[mono.git] / mono / metadata / sgen-debug.c
index 1387676c75858cb57b6c6a403fb0f00d78705c1e..5c604b2a7d338d7b6f509f33131216d365755b21 100644 (file)
@@ -32,6 +32,7 @@
 #include "metadata/sgen-protocol.h"
 #include "metadata/sgen-memory-governor.h"
 #include "metadata/sgen-pinning.h"
+#include "metadata/sgen-client.h"
 #include "metadata/threadpool-internals.h"
 
 #define LOAD_VTABLE    SGEN_LOAD_VTABLE
@@ -51,13 +52,13 @@ void check_object (char *start);
 
 const char*descriptor_types [] = {
        "INVALID",
-       "run_length",
-       "small_bitmap",
+       "run length",
+       "bitmap",
+       "small pointer-free",
        "complex",
        "vector",
-       "large_bitmap",
-       "complex_arr",
-       "complex_ptrfree"
+       "complex arrray",
+       "complex pointer-free"
 };
 
 static char* describe_nursery_ptr (char *ptr, gboolean need_setup);
@@ -126,12 +127,12 @@ describe_pointer (char *ptr, gboolean need_setup)
                printf ("VTable is invalid (points inside nursery).\n");
                goto bridge;
        }
-       printf ("Class: %s\n", vtable->klass->name);
+       printf ("Class: %s.%s\n", vtable->klass->name_space, vtable->klass->name);
 
        desc = ((GCVTable*)vtable)->desc;
        printf ("Descriptor: %lx\n", (long)desc);
 
-       type = desc & 0x7;
+       type = desc & DESC_TYPE_MASK;
        printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
 
        size = sgen_safe_object_get_size ((MonoObject*)ptr);
@@ -174,7 +175,8 @@ static gboolean missing_remsets;
 static void
 check_consistency_callback (char *start, size_t size, void *dummy)
 {
-       GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
+       MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (start);
+       mword desc = sgen_vtable_get_descriptor (vt);
        SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, vt->klass->name);
 
 #include "sgen-scan-object.h"
@@ -230,7 +232,8 @@ static void
 check_mod_union_callback (char *start, size_t size, void *dummy)
 {
        gboolean in_los = (gboolean) (size_t) dummy;
-       GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
+       MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (start);
+       mword desc = sgen_vtable_get_descriptor (vt);
        guint8 *cards;
        SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, vt->klass->name);
 
@@ -269,6 +272,8 @@ sgen_check_mod_union_consistency (void)
 static void
 check_major_refs_callback (char *start, size_t size, void *dummy)
 {
+       mword desc = sgen_obj_get_descriptor (start);
+
 #include "sgen-scan-object.h"
 }
 
@@ -283,7 +288,7 @@ sgen_check_major_refs (void)
 #undef HANDLE_PTR
 #define HANDLE_PTR(ptr,obj)    do {    \
                if (*(ptr)) {   \
-                       g_assert (sgen_safe_name (*(ptr)) != NULL);     \
+                       g_assert (sgen_client_object_safe_name (*(ptr)) != NULL);       \
                }       \
        } while (0)
 
@@ -296,9 +301,13 @@ sgen_check_major_refs (void)
 void
 check_object (char *start)
 {
+       mword desc;
+
        if (!start)
                return;
 
+       desc = sgen_obj_get_descriptor (start);
+
 #include "sgen-scan-object.h"
 }
 
@@ -340,6 +349,16 @@ find_object_in_nursery_dump (char *object)
        return FALSE;
 }
 
+static void
+iterate_valid_nursery_objects (IterateObjectCallbackFunc callback, void *data)
+{
+       int i;
+       for (i = 0; i < valid_nursery_object_count; ++i) {
+               char *obj = valid_nursery_objects [i];
+               callback (obj, safe_object_get_size ((MonoObject*)obj), data);
+       }
+}
+
 static char*
 describe_nursery_ptr (char *ptr, gboolean need_setup)
 {
@@ -348,20 +367,20 @@ describe_nursery_ptr (char *ptr, gboolean need_setup)
        if (need_setup)
                setup_valid_nursery_objects ();
 
-       for (i = 0; i < valid_nursery_object_count; ++i) {
-               if (valid_nursery_objects [i] >= ptr)
+       for (i = 0; i < valid_nursery_object_count - 1; ++i) {
+               if (valid_nursery_objects [i + 1] > ptr)
                        break;
        }
 
        if (i >= valid_nursery_object_count || valid_nursery_objects [i] + safe_object_get_size ((MonoObject *)valid_nursery_objects [i]) < ptr) {
-               SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)\n");
+               SGEN_LOG (0, "nursery-ptr (unalloc'd-memory)");
                return NULL;
        } else {
                char *obj = valid_nursery_objects [i];
                if (obj == ptr)
-                       SGEN_LOG (0, "nursery-ptr\n");
+                       SGEN_LOG (0, "nursery-ptr %p", obj);
                else
-                       SGEN_LOG (0, "nursery-ptr (interior-ptr offset %td)\n", ptr - obj);
+                       SGEN_LOG (0, "nursery-ptr %p (interior-ptr offset %td)", obj, ptr - obj);
                return obj;
        }
 }
@@ -425,6 +444,7 @@ static void
 verify_object_pointers_callback (char *start, size_t size, void *data)
 {
        gboolean allow_missing_pinned = (gboolean) (size_t) data;
+       mword desc = sgen_obj_get_descriptor (start);
 
 #include "sgen-scan-object.h"
 }
@@ -531,15 +551,14 @@ find_pinning_reference (char *obj, size_t size)
 #define HANDLE_PTR(ptr,obj)    do {                                    \
                char* __target = *(char**)ptr;                          \
                if (__target) {                                         \
-                       g_assert (is_valid_object_pointer (__target));  \
                        if (sgen_ptr_in_nursery (__target)) {           \
-                               g_assert (SGEN_OBJECT_IS_PINNED (__target)); \
-                       } else if (sgen_los_is_valid_object (__target)) { \
-                               g_assert (sgen_los_object_is_pinned (__target)); \
-                       } else if (major_collector.is_valid_object (__target)) { \
-                               g_assert (major_collector.is_object_live (__target)); \
+                               g_assert (!SGEN_OBJECT_IS_FORWARDED (__target)); \
                        } else {                                        \
-                               g_assert_not_reached ();                \
+                               mword __size = sgen_safe_object_get_size ((MonoObject*)__target); \
+                               if (__size <= SGEN_MAX_SMALL_OBJ_SIZE)  \
+                                       g_assert (major_collector.is_object_live (__target)); \
+                               else                                    \
+                                       g_assert (sgen_los_object_is_pinned (__target)); \
                        }                                               \
                }                                                       \
        } while (0)
@@ -547,9 +566,13 @@ find_pinning_reference (char *obj, size_t size)
 static void
 check_marked_callback (char *start, size_t size, void *dummy)
 {
-       gboolean is_los = (gboolean) (size_t) dummy;
+       gboolean flag = (gboolean) (size_t) dummy;
+       mword desc;
 
-       if (is_los) {
+       if (sgen_ptr_in_nursery (start)) {
+               if (flag)
+                       SGEN_ASSERT (0, SGEN_OBJECT_IS_PINNED (start), "All objects remaining in the nursery must be pinned");
+       } else if (flag) {
                if (!sgen_los_object_is_pinned (start))
                        return;
        } else {
@@ -557,14 +580,17 @@ check_marked_callback (char *start, size_t size, void *dummy)
                        return;
        }
 
+       desc = sgen_obj_get_descriptor_safe (start);
+
 #include "sgen-scan-object.h"
 }
 
 void
-sgen_check_major_heap_marked (void)
+sgen_check_heap_marked (gboolean nursery_must_be_pinned)
 {
        setup_valid_nursery_objects ();
 
+       iterate_valid_nursery_objects (check_marked_callback, (void*)(size_t)nursery_must_be_pinned);
        major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, check_marked_callback, (void*)FALSE);
        sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
 }
@@ -595,7 +621,7 @@ static gboolean scan_object_for_specific_ref_precise = TRUE;
 #define HANDLE_PTR(ptr,obj) do {               \
        if ((MonoObject*)*(ptr) == key) {       \
        g_print ("found ref to %p in object %p (%s) at offset %td\n",   \
-                       key, (obj), sgen_safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
+                       key, (obj), sgen_client_object_safe_name ((MonoObject*)(obj)), ((char*)(ptr) - (char*)(obj))); \
        }                                                               \
        } while (0)
 
@@ -608,6 +634,7 @@ scan_object_for_specific_ref (char *start, MonoObject *key)
                start = forwarded;
 
        if (scan_object_for_specific_ref_precise) {
+               mword desc = sgen_obj_get_descriptor_safe (start);
                #include "sgen-scan-object.h"
        } else {
                mword *words = (mword*)start;
@@ -616,7 +643,7 @@ scan_object_for_specific_ref (char *start, MonoObject *key)
                for (i = 0; i < size / sizeof (mword); ++i) {
                        if (words [i] == (mword)key) {
                                g_print ("found possible ref to %p in object %p (%s) at offset %td\n",
-                                               key, start, sgen_safe_name (start), i * sizeof (mword));
+                                               key, start, sgen_client_object_safe_name ((MonoObject*)start), i * sizeof (mword));
                        }
                }
        }
@@ -896,7 +923,9 @@ check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
 static void
 scan_object_for_xdomain_refs (char *start, mword size, void *data)
 {
-       MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
+       MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (start);
+       MonoDomain *domain = vt->domain;
+       mword desc = sgen_vtable_get_descriptor (vt);
 
        #include "sgen-scan-object.h"
 }
@@ -915,6 +944,147 @@ sgen_check_for_xdomain_refs (void)
                scan_object_for_xdomain_refs (bigobj->data, sgen_los_object_size (bigobj), NULL);
 }
 
+/* If not null, dump the heap after each collection into this file */
+static FILE *heap_dump_file = NULL;
+
+void
+sgen_dump_occupied (char *start, char *end, char *section_start)
+{
+       fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
+}
+
+void
+sgen_dump_section (GCMemSection *section, const char *type)
+{
+       char *start = section->data;
+       char *end = section->data + section->size;
+       char *occ_start = NULL;
+       GCVTable *vt;
+       char *old_start G_GNUC_UNUSED = NULL; /* just for debugging */
+
+       fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
+
+       while (start < end) {
+               guint size;
+               MonoClass *class G_GNUC_UNUSED;
+
+               if (!*(void**)start) {
+                       if (occ_start) {
+                               sgen_dump_occupied (occ_start, start, section->data);
+                               occ_start = NULL;
+                       }
+                       start += sizeof (void*); /* should be ALLOC_ALIGN, really */
+                       continue;
+               }
+               g_assert (start < section->next_data);
+
+               if (!occ_start)
+                       occ_start = start;
+
+               vt = (GCVTable*)LOAD_VTABLE (start);
+               class = vt->klass;
+
+               size = SGEN_ALIGN_UP (safe_object_get_size ((MonoObject*) start));
+
+               /*
+               fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
+                               start - section->data,
+                               vt->klass->name_space, vt->klass->name,
+                               size);
+               */
+
+               old_start = start;
+               start += size;
+       }
+       if (occ_start)
+               sgen_dump_occupied (occ_start, start, section->data);
+
+       fprintf (heap_dump_file, "</section>\n");
+}
+
+static void
+dump_object (MonoObject *obj, gboolean dump_location)
+{
+       static char class_name [1024];
+
+       MonoClass *class = mono_object_class (obj);
+       int i, j;
+
+       /*
+        * Python's XML parser is too stupid to parse angle brackets
+        * in strings, so we just ignore them;
+        */
+       i = j = 0;
+       while (class->name [i] && j < sizeof (class_name) - 1) {
+               if (!strchr ("<>\"", class->name [i]))
+                       class_name [j++] = class->name [i];
+               ++i;
+       }
+       g_assert (j < sizeof (class_name));
+       class_name [j] = 0;
+
+       fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%zd\"",
+                       class->name_space, class_name,
+                       safe_object_get_size (obj));
+       if (dump_location) {
+               const char *location;
+               if (sgen_ptr_in_nursery (obj))
+                       location = "nursery";
+               else if (safe_object_get_size (obj) <= SGEN_MAX_SMALL_OBJ_SIZE)
+                       location = "major";
+               else
+                       location = "LOS";
+               fprintf (heap_dump_file, " location=\"%s\"", location);
+       }
+       fprintf (heap_dump_file, "/>\n");
+}
+
+void
+sgen_debug_enable_heap_dump (const char *filename)
+{
+       heap_dump_file = fopen (filename, "w");
+       if (heap_dump_file) {
+               fprintf (heap_dump_file, "<sgen-dump>\n");
+               sgen_pin_stats_enable ();
+       }
+}
+
+void
+sgen_debug_dump_heap (const char *type, int num, const char *reason)
+{
+       ObjectList *list;
+       LOSObject *bigobj;
+
+       if (!heap_dump_file)
+               return;
+
+       fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
+       if (reason)
+               fprintf (heap_dump_file, " reason=\"%s\"", reason);
+       fprintf (heap_dump_file, ">\n");
+       fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
+       sgen_dump_internal_mem_usage (heap_dump_file);
+       fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_STACK));
+       /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
+       fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_OTHER));
+
+       fprintf (heap_dump_file, "<pinned-objects>\n");
+       for (list = sgen_pin_stats_get_object_list (); list; list = list->next)
+               dump_object (list->obj, TRUE);
+       fprintf (heap_dump_file, "</pinned-objects>\n");
+
+       sgen_dump_section (nursery_section, "nursery");
+
+       major_collector.dump_heap (heap_dump_file);
+
+       fprintf (heap_dump_file, "<los>\n");
+       for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
+               dump_object ((MonoObject*)bigobj->data, FALSE);
+       fprintf (heap_dump_file, "</los>\n");
+
+       fprintf (heap_dump_file, "</collection>\n");
+}
+
 static int
 compare_xrefs (const void *a_ptr, const void *b_ptr)
 {
@@ -1076,4 +1246,43 @@ sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcess
        return TRUE;
 }
 
+static char *found_obj;
+
+static void
+find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
+{
+       char *ptr = user_data;
+
+       if (ptr >= obj && ptr < obj + size) {
+               g_assert (!found_obj);
+               found_obj = obj;
+       }
+}
+
+/* for use in the debugger */
+char*
+sgen_find_object_for_ptr (char *ptr)
+{
+       if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
+               found_obj = NULL;
+               sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
+                               find_object_for_ptr_callback, ptr, TRUE);
+               if (found_obj)
+                       return found_obj;
+       }
+
+       found_obj = NULL;
+       sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
+       if (found_obj)
+               return found_obj;
+
+       /*
+        * Very inefficient, but this is debugging code, supposed to
+        * be called from gdb, so we don't care.
+        */
+       found_obj = NULL;
+       major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, find_object_for_ptr_callback, ptr);
+       return found_obj;
+}
+
 #endif /*HAVE_SGEN_GC*/