Implement event callbacks to be used to profile the collector
authorLucas Meijer <lucas@unity3d.com>
Tue, 1 Jul 2014 11:07:15 +0000 (13:07 +0200)
committerLucas Meijer <lucas@unity3d.com>
Tue, 1 Jul 2014 11:09:50 +0000 (13:09 +0200)
alloc.c
darwin_stop_world.c
include/gc.h
misc.c
pthread_stop_world.c
win32_threads.c

diff --git a/alloc.c b/alloc.c
index 25bb31e..8bc68a2 100644 (file)
--- a/alloc.c
+++ b/alloc.c
@@ -88,6 +88,8 @@ STATIC GC_bool GC_need_full_gc = FALSE;
 
 STATIC word GC_used_heap_size_after_full = 0;
 
+extern GC_on_collection_event_proc GC_on_collection_event;
+
 /* GC_copyright symbol is externally visible. */
 char * const GC_copyright[] =
 {"Copyright 1988,1989 Hans-J. Boehm and Alan J. Demers ",
@@ -429,6 +431,9 @@ GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func)
         }
     }
     GC_notify_full_gc();
+    if (GC_on_collection_event)
+      GC_on_collection_event(GC_EVENT_COLLECTION_BEGIN, NULL);
+
 #   ifndef SMALL_CONFIG
       if (GC_print_stats) {
         GET_TIME(start_time);
@@ -581,6 +586,17 @@ GC_API int GC_CALL GC_collect_a_little(void)
 # define COMMA_IF_USE_MUNMAP(x) /* empty */
 #endif
 
+static void start_world()
+{
+  if (GC_on_collection_event)
+    GC_on_collection_event(GC_EVENT_STARTWORLD_BEGIN, NULL);
+
+  START_WORLD();
+
+  if (GC_on_collection_event)
+    GC_on_collection_event(GC_EVENT_STARTWORLD_END, NULL);
+}
+
 /*
  * Assumes lock is held.  We stop the world and mark from all roots.
  * If stop_func() ever returns TRUE, we may fail and return FALSE.
@@ -606,7 +622,14 @@ STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func)
         GET_TIME(start_time);
 #   endif
 
+    if (GC_on_collection_event)
+      GC_on_collection_event(GC_EVENT_STOPWORLD_BEGIN, NULL);
+
     STOP_WORLD();
+
+    if (GC_on_collection_event)
+      GC_on_collection_event(GC_EVENT_STOPWORLD_END, NULL);
+
 #   ifdef THREAD_LOCAL_ALLOC
       GC_world_stopped = TRUE;
 #   endif
@@ -625,6 +648,9 @@ STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func)
             GC_clear_a_few_frames();
             GC_noop6(0,0,0,0,0,0);
 
+        if (GC_on_collection_event)
+          GC_on_collection_event(GC_EVENT_MARK_BEGIN, NULL);
+
         GC_initiate_gc();
         for (i = 0;;i++) {
           if ((*stop_func)()) {
@@ -634,12 +660,19 @@ STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func)
 #           ifdef THREAD_LOCAL_ALLOC
               GC_world_stopped = FALSE;
 #           endif
-            START_WORLD();
+
+            if (GC_on_collection_event)
+              GC_on_collection_event(GC_EVENT_MARK_END, NULL);
+
+            start_world();
             return(FALSE);
           }
           if (GC_mark_some(GC_approx_sp())) break;
         }
 
+    if (GC_on_collection_event)
+      GC_on_collection_event(GC_EVENT_MARK_END, NULL);
+
     GC_gc_no++;
     GC_DBGLOG_PRINTF("GC #%lu freed %ld bytes, heap %lu KiB"
                      IF_USE_MUNMAP(" (+ %lu KiB unmapped)") "\n",
@@ -655,7 +688,7 @@ STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func)
 #   ifdef THREAD_LOCAL_ALLOC
       GC_world_stopped = FALSE;
 #   endif
-    START_WORLD();
+    start_world();
 #   ifndef SMALL_CONFIG
       if (GC_PRINT_STATS_FLAG) {
         unsigned long time_diff;
@@ -961,6 +994,9 @@ STATIC void GC_finish_collection(void)
                       MS_TIME_DIFF(done_time,finalize_time));
       }
 #   endif
+
+    if (GC_on_collection_event)
+      GC_on_collection_event(GC_EVENT_COLLECTION_END, NULL);
 }
 
 /* If stop_func == 0 then GC_default_stop_func is used instead.         */
index a6b0a5c..35a4ba7 100644 (file)
@@ -458,6 +458,8 @@ STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count,
 
 #endif /* !GC_NO_THREADS_DISCOVERY */
 
+extern GC_on_collection_event_proc GC_on_collection_event;
+
 /* Caller holds allocation lock.        */
 GC_INNER void GC_stop_world(void)
 {
@@ -540,6 +542,8 @@ GC_INNER void GC_stop_world(void)
           kern_result = thread_suspend(p->stop_info.mach_thread);
           if (kern_result != KERN_SUCCESS)
             ABORT("thread_suspend failed");
+          if (GC_on_collection_event)
+              GC_on_collection_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)p->stop_info.mach_thread);
         }
       }
     }
@@ -654,6 +658,8 @@ GC_INNER void GC_start_world(void)
         if ((p->flags & FINISHED) == 0 && !p->thread_blocked &&
              p->stop_info.mach_thread != my_thread)
           GC_thread_resume(p->stop_info.mach_thread);
+          if (GC_on_collection_event)
+            GC_on_collection_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)p->stop_info.mach_thread);
       }
     }
 
index 52c82a0..a034eb0 100644 (file)
@@ -129,6 +129,28 @@ GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void);
                         /* Both the supplied setter and the getter      */
                         /* acquire the GC lock (to avoid data races).   */
 
+typedef enum
+{
+    GC_EVENT_COLLECTION_BEGIN,
+    GC_EVENT_STOPWORLD_BEGIN,
+    GC_EVENT_STOPWORLD_END,
+    GC_EVENT_MARK_BEGIN,
+    GC_EVENT_MARK_END,
+    GC_EVENT_STARTWORLD_BEGIN,
+    GC_EVENT_STARTWORLD_END,
+    GC_EVENT_COLLECTION_END,
+    GC_EVENT_THREAD_SUSPENDED,
+    GC_EVENT_THREAD_UNSUSPENDED
+} GCEventKind;
+
+typedef void (GC_CALLBACK * GC_on_collection_event_proc)(GCEventKind, void*);
+                        /* Invoked to indicate progress through the     */
+                        /* collection process.                          */
+                        /* Called with the world stopped (and the       */
+                        /* allocation lock held).  May be 0.            */
+GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc);
+GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void);
 GC_API GC_ATTR_DEPRECATED int GC_find_leak;
                         /* Do not actually garbage collect, but simply  */
                         /* report inaccessible memory that was not      */
diff --git a/misc.c b/misc.c
index 68667ee..91a422b 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -2034,6 +2034,27 @@ GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void)
     return fn;
 }
 
+GC_on_collection_event_proc GC_on_collection_event = NULL;
+
+GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc fn)
+{
+    /* fn may be 0 (means no event notifier). */
+    DCL_LOCK_STATE;
+    LOCK();
+    GC_on_collection_event = fn;
+    UNLOCK();
+}
+
+GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void)
+{
+    GC_on_collection_event_proc fn;
+    DCL_LOCK_STATE;
+    LOCK();
+    fn = GC_on_collection_event;
+    UNLOCK();
+    return fn;
+}
+
 GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc fn)
 {
     /* fn may be 0 (means no finalizer notifier). */
index a68e922..9bce874 100644 (file)
@@ -466,7 +466,7 @@ STATIC int GC_suspend_all(void)
 {
   int n_live_threads = 0;
   int i;
-
+  int thread_id;
 # ifndef NACL
     GC_thread p;
 #   ifndef GC_OPENBSD_UTHREADS
@@ -499,13 +499,20 @@ STATIC int GC_suspend_all(void)
                 if (pthread_stackseg_np(p->id, &stack))
                   ABORT("pthread_stackseg_np failed");
                 p -> stop_info.stack_ptr = (ptr_t)stack.ss_sp - stack.ss_size;
+
+                if (GC_on_collection_event)
+                  GC_on_collection_event(GC_EVENT_THREAD_SUSPENDED, (void *)p->id);
               }
 #           else
 #             ifndef PLATFORM_ANDROID
-                result = pthread_kill(p -> id, GC_sig_suspend);
+                thread_id = p -> id;
+                result = pthread_kill(thread_id, GC_sig_suspend);
 #             else
-                result = android_thread_kill(p -> kernel_id, GC_sig_suspend);
+                thread_id = p -> kernel_id;
+                result = android_thread_kill(thread_id, GC_sig_suspend);
 #             endif
+              if (GC_on_collection_event)
+                GC_on_collection_event(GC_EVENT_THREAD_SUSPENDED, (void *)threadid);
               switch(result) {
                 case ESRCH:
                     /* Not really there anymore.  Possible? */
@@ -825,11 +832,15 @@ GC_INNER void GC_start_world(void)
               ABORT("pthread_resume_np failed");
 #         else
 #           ifndef PLATFORM_ANDROID
-              result = pthread_kill(p -> id, GC_sig_thr_restart);
+              thread_id = p -> id;
+              result = pthread_kill(thread_id, GC_sig_thr_restart);
 #           else
-              result = android_thread_kill(p -> kernel_id,
-                                           GC_sig_thr_restart);
+              thread_id = p -> kernel_id;
+              result = android_thread_kill(thread_id, GC_sig_thr_restart);
 #           endif
+            if (GC_on_collection_event)
+              GC_on_collection_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)thread_id);
+
             switch(result) {
                 case ESRCH:
                     /* Not really there anymore.  Possible? */
index df11a5e..19da9ce 100644 (file)
@@ -1182,6 +1182,8 @@ STATIC void GC_suspend(GC_thread t)
                 /* TRUE only if GC_stop_world() acquired GC_write_cs.   */
 #endif
 
+extern GC_on_collection_event_proc GC_on_collection_event;
+
 GC_INNER void GC_stop_world(void)
 {
   DWORD thread_id = GetCurrentThreadId();
@@ -1227,6 +1229,8 @@ GC_INNER void GC_stop_world(void)
         if (t -> stack_base != 0 && t -> thread_blocked_sp == NULL
             && t -> id != thread_id) {
           GC_suspend((GC_thread)t);
+          if (GC_on_collection_event)
+            GC_on_collection_event(GC_EVENT_THREAD_SUSPENDED, (void *)THREAD_HANDLE(t));
         }
       }
     } else
@@ -1240,6 +1244,8 @@ GC_INNER void GC_stop_world(void)
         if (t -> stack_base != 0 && t -> thread_blocked_sp == NULL
             && !KNOWN_FINISHED(t) && t -> id != thread_id) {
           GC_suspend(t);
+          if (GC_on_collection_event)
+            GC_on_collection_event(GC_EVENT_THREAD_SUSPENDED, (void *)THREAD_HANDLE(t));
         }
       }
     }
@@ -1273,6 +1279,8 @@ GC_INNER void GC_start_world(void)
         if (ResumeThread(THREAD_HANDLE(t)) == (DWORD)-1)
           ABORT("ResumeThread failed");
         t -> suspended = FALSE;
+        if (GC_on_collection_event)
+          GC_on_collection_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)THREAD_HANDLE(t));
       }
     }
   } else {
@@ -1287,6 +1295,8 @@ GC_INNER void GC_start_world(void)
             ABORT("ResumeThread failed");
           UNPROTECT_THREAD(t);
           t -> suspended = FALSE;
+          if (GC_on_collection_event)
+            GC_on_collection_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)THREAD_HANDLE(t));
         }
       }
     }