From fd4903870d93f44ffae042e10b5ec6975a2fe7b4 Mon Sep 17 00:00:00 2001 From: Lucas Meijer Date: Tue, 1 Jul 2014 13:07:15 +0200 Subject: [PATCH] Implement event callbacks to be used to profile the collector --- alloc.c | 40 ++++++++++++++++++++++++++++++++++++++-- darwin_stop_world.c | 6 ++++++ include/gc.h | 22 ++++++++++++++++++++++ misc.c | 21 +++++++++++++++++++++ pthread_stop_world.c | 23 +++++++++++++++++------ win32_threads.c | 10 ++++++++++ 6 files changed, 114 insertions(+), 8 deletions(-) diff --git a/alloc.c b/alloc.c index 25bb31ea..8bc68a22 100644 --- 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. */ diff --git a/darwin_stop_world.c b/darwin_stop_world.c index a6b0a5c7..35a4ba7d 100644 --- a/darwin_stop_world.c +++ b/darwin_stop_world.c @@ -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); } } diff --git a/include/gc.h b/include/gc.h index 52c82a05..a034eb0f 100644 --- a/include/gc.h +++ b/include/gc.h @@ -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 68667eef..91a422b2 100644 --- 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). */ diff --git a/pthread_stop_world.c b/pthread_stop_world.c index a68e9222..9bce8741 100644 --- a/pthread_stop_world.c +++ b/pthread_stop_world.c @@ -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? */ diff --git a/win32_threads.c b/win32_threads.c index df11a5e7..19da9cea 100644 --- a/win32_threads.c +++ b/win32_threads.c @@ -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)); } } } -- 2.40.0