GC/Profiler: added API to enumerate roots.
authorPaolo Molaro <lupus@oddwiz.org>
Tue, 16 Nov 2010 15:00:00 +0000 (16:00 +0100)
committerPaolo Molaro <lupus@oddwiz.org>
Tue, 16 Nov 2010 17:07:30 +0000 (18:07 +0100)
* profiler-private.h, profiler.c, profiler.h: added API to
enumerate the roots used during a garbage collection.
* sgen-gc.c: enumerate roots if requested by the profiler.

mono/metadata/profiler-private.h
mono/metadata/profiler.c
mono/metadata/profiler.h
mono/metadata/sgen-gc.c

index 809e483e8c94cbccad8a8bccf2be1967bf8ab581..18ec96b7aea2fae84c74fb3e77033dfca9aa3777 100644 (file)
@@ -69,6 +69,7 @@ void mono_profiler_gc_event       (MonoGCEvent e, int generation) MONO_INTERNAL;
 void mono_profiler_gc_heap_resize (gint64 new_size) MONO_INTERNAL;
 void mono_profiler_gc_moves       (void **objects, int num) MONO_INTERNAL;
 void mono_profiler_gc_handle      (int op, int type, uintptr_t handle, MonoObject *obj) MONO_INTERNAL;
+void mono_profiler_gc_roots       (int num, void **objects, int *root_types, uintptr_t *extra_info) MONO_INTERNAL;
 
 void mono_profiler_code_chunk_new (gpointer chunk, int size) MONO_INTERNAL;
 void mono_profiler_code_chunk_destroy (gpointer chunk) MONO_INTERNAL;
index 52d68739fb235a041e631f0e26451395fc82c6cd..0e4a0ce4bdcffbefb3cf434e32edeaf0f3ad141b 100644 (file)
@@ -91,6 +91,7 @@ struct _ProfilerDesc {
        MonoProfileGCResizeFunc  gc_heap_resize;
        MonoProfileGCMoveFunc    gc_moves;
        MonoProfileGCHandleFunc  gc_handle;
+       MonoProfileGCRootFunc    gc_roots;
 
        MonoProfileFunc          runtime_initialized_event;
 
@@ -770,6 +771,16 @@ mono_profiler_gc_handle (int op, int type, uintptr_t handle, MonoObject *obj)
        }
 }
 
+void
+mono_profiler_gc_roots (int num, void **objects, int *root_types, uintptr_t *extra_info)
+{
+       ProfilerDesc *prof;
+       for (prof = prof_list; prof; prof = prof->next) {
+               if ((prof->events & MONO_PROFILE_GC_ROOTS) && prof->gc_roots)
+                       prof->gc_roots (prof->profiler, num, objects, root_types, extra_info);
+       }
+}
+
 void
 mono_profiler_install_gc (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback)
 {
@@ -805,19 +816,26 @@ mono_profiler_install_gc_moves (MonoProfileGCMoveFunc callback)
 /**
  * mono_profiler_install_gc_roots:
  * @handle_callback: callback function
+ * @roots_callback: callback function
  *
  * Install the @handle_callback function that the GC will call when GC
  * handles are created or destroyed.
  * The callback receives an operation, which is either #MONO_PROFILER_GC_HANDLE_CREATED
  * or #MONO_PROFILER_GC_HANDLE_DESTROYED, the handle type, the handle value and the
  * object pointer, if present.
+ * Install the @roots_callback function that the GC will call when tracing
+ * the roots for a collection.
+ * The callback receives the number of elements and three arrays: an array
+ * of objects, an array of root types and flags and an array of extra info.
+ * The size of each array is given by the first argument.
  */
 void
-mono_profiler_install_gc_roots (MonoProfileGCHandleFunc handle_callback)
+mono_profiler_install_gc_roots (MonoProfileGCHandleFunc handle_callback, MonoProfileGCRootFunc roots_callback)
 {
        if (!prof_list)
                return;
        prof_list->gc_handle = handle_callback;
+       prof_list->gc_roots = roots_callback;
 }
 
 void
index 79f5f6f01af2242deb48e553d2811cccf21d4916..09299a7cfd7449ff68560bf6e781667375282ef0 100644 (file)
@@ -89,6 +89,19 @@ typedef enum {
        MONO_PROFILER_GC_HANDLE_DESTROYED
 } MonoProfileGCHandleEvent;
 
+typedef enum {
+       MONO_PROFILE_GC_ROOT_PINNING  = 1 << 8,
+       MONO_PROFILE_GC_ROOT_WEAKREF  = 2 << 8,
+       MONO_PROFILE_GC_ROOT_INTERIOR = 4 << 8,
+       /* the above are flags, the type is in the low 2 bytes */
+       MONO_PROFILE_GC_ROOT_STACK = 0,
+       MONO_PROFILE_GC_ROOT_FINALIZER = 1,
+       MONO_PROFILE_GC_ROOT_HANDLE = 2,
+       MONO_PROFILE_GC_ROOT_OTHER = 3,
+       MONO_PROFILE_GC_ROOT_MISC = 4, /* could be stack, handle, etc. */
+       MONO_PROFILE_GC_ROOT_TYPEMASK = 0xff
+} MonoProfileGCRootType;
+
 /*
  * Functions that the runtime will call on the profiler.
  */
@@ -123,6 +136,7 @@ typedef void (*MonoProfileGCFunc)         (MonoProfiler *prof, MonoGCEvent event
 typedef void (*MonoProfileGCMoveFunc)     (MonoProfiler *prof, void **objects, int num);
 typedef void (*MonoProfileGCResizeFunc)   (MonoProfiler *prof, int64_t new_size);
 typedef void (*MonoProfileGCHandleFunc)   (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj);
+typedef void (*MonoProfileGCRootFunc)     (MonoProfiler *prof, int num_roots, void **objects, int *root_types, uintptr_t *extra_info);
 
 typedef void (*MonoProfileIomapFunc) (MonoProfiler *prof, const char *report, const char *pathname, const char *new_pathname);
 
@@ -168,7 +182,7 @@ void mono_profiler_install_coverage_filter (MonoProfileCoverageFilterFunc callba
 void mono_profiler_coverage_get  (MonoProfiler *prof, MonoMethod *method, MonoProfileCoverageFunc func);
 void mono_profiler_install_gc    (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback);
 void mono_profiler_install_gc_moves    (MonoProfileGCMoveFunc callback);
-void mono_profiler_install_gc_roots    (MonoProfileGCHandleFunc handle_callback);
+void mono_profiler_install_gc_roots    (MonoProfileGCHandleFunc handle_callback, MonoProfileGCRootFunc roots_callback);
 void mono_profiler_install_runtime_initialized (MonoProfileFunc runtime_initialized_callback);
 
 void mono_profiler_install_code_chunk_new (MonoProfilerCodeChunkNew callback);
index 1c7ec482eea8f12b5c653ef92afa1fce7773efde..47542393e5120632364b4102a6ce4f1f57aeb365 100644 (file)
@@ -607,6 +607,33 @@ static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
 static mword roots_size = 0; /* amount of memory in the root set */
 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
 
+#define GC_ROOT_NUM 32
+typedef struct {
+       int count;
+       void *objects [GC_ROOT_NUM];
+       int root_types [GC_ROOT_NUM];
+       uintptr_t extra_info [GC_ROOT_NUM];
+} GCRootReport;
+
+static void
+notify_gc_roots (GCRootReport *report)
+{
+       if (!report->count)
+               return;
+       mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
+       report->count = 0;
+}
+
+static void
+add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
+{
+       if (report->count == GC_ROOT_NUM)
+               notify_gc_roots (report);
+       report->objects [report->count] = object;
+       report->root_types [report->count] = rtype;
+       report->extra_info [report->count++] = ((MonoVTable*)LOAD_VTABLE (object))->klass;
+}
+
 /* 
  * The current allocation cursors
  * We allocate objects in the nursery.
@@ -785,6 +812,8 @@ static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean p
 static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
+static void report_finalizer_roots (void);
+static void report_registered_roots (void);
 static void find_pinning_ref_from_thread (char *obj, size_t size);
 static void update_current_thread_stack (void *start);
 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
@@ -1793,6 +1822,13 @@ pin_objects_from_addresses (GCMemSection *section, void **start, void **end, voi
                start++;
        }
        //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
+       if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
+               GCRootReport report;
+               report.count = 0;
+               for (idx = 0; idx < count; ++idx)
+                       add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING, 0);
+               notify_gc_roots (&report);
+       }
        return count;
 }
 
@@ -2207,6 +2243,106 @@ mono_gc_get_nursery (int *shift_bits, size_t *size)
        return nursery_start;
 }
 
+static void
+report_finalizer_roots_list (FinalizeEntry *list)
+{
+       GCRootReport report;
+       FinalizeEntry *fin;
+
+       report.count = 0;
+       for (fin = list; fin; fin = fin->next) {
+               if (!fin->object)
+                       continue;
+               add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
+       }
+       notify_gc_roots (&report);
+}
+
+static void
+report_finalizer_roots (void)
+{
+       report_finalizer_roots_list (fin_ready_list);
+       report_finalizer_roots_list (critical_fin_list);
+}
+
+static GCRootReport *root_report;
+
+static void
+single_arg_report_root (void **obj)
+{
+       if (*obj)
+               add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
+}
+
+static void
+precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
+{
+       switch (desc & ROOT_DESC_TYPE_MASK) {
+       case ROOT_DESC_BITMAP:
+               desc >>= ROOT_DESC_TYPE_SHIFT;
+               while (desc) {
+                       if ((desc & 1) && *start_root) {
+                               add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
+                       }
+                       desc >>= 1;
+                       start_root++;
+               }
+               return;
+       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) {
+                                       add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
+                               }
+                               bmap >>= 1;
+                               ++objptr;
+                       }
+                       start_run += GC_BITS_PER_WORD;
+               }
+               break;
+       }
+       case ROOT_DESC_USER: {
+               MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
+               root_report = report;
+               marker (start_root, single_arg_report_root);
+               break;
+       }
+       case ROOT_DESC_RUN_LEN:
+               g_assert_not_reached ();
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+static void
+report_registered_roots_by_type (int root_type)
+{
+       GCRootReport report;
+       int i;
+       RootRecord *root;
+       report.count = 0;
+       for (i = 0; i < roots_hash_size [root_type]; ++i) {
+               for (root = roots_hash [root_type][i]; root; root = root->next) {
+                       DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
+                       precisely_report_roots_from (&report, (void**)root->start_root, (void**)root->end_root, root->root_desc);
+               }
+       }
+       notify_gc_roots (&report);
+}
+
+static void
+report_registered_roots (void)
+{
+       report_registered_roots_by_type (ROOT_TYPE_NORMAL);
+       report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
+}
+
 static void
 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue)
 {
@@ -2774,6 +2910,10 @@ collect_nursery (size_t requested_size)
 
        drain_gray_stack (&gray_queue);
 
+       if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
+               report_registered_roots ();
+       if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
+               report_finalizer_roots ();
        TV_GETTIME (atv);
        time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
        /* registered roots, this includes static fields */
@@ -2965,6 +3105,8 @@ major_do_collection (const char *reason)
 
        workers_start_all_workers (1);
 
+       if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
+               report_registered_roots ();
        TV_GETTIME (atv);
        time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
 
@@ -2984,6 +3126,8 @@ major_do_collection (const char *reason)
        TV_GETTIME (btv);
        time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
 
+       if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
+               report_finalizer_roots ();
        /* scan the list of objects ready for finalization */
        scan_finalizer_entries (major_collector.copy_or_mark_object, fin_ready_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
        scan_finalizer_entries (major_collector.copy_or_mark_object, critical_fin_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);