diff options
author | Simon Marlow <marlowsd@gmail.com> | 2015-02-11 14:19:21 +0000 |
---|---|---|
committer | Simon Marlow <marlowsd@gmail.com> | 2017-11-16 13:49:05 +0000 |
commit | 2f4638735ad1526d6502a4706bffafffb93e24da (patch) | |
tree | 8711e34b498ea52d0a8092fb763892c07c602d92 /rts | |
parent | 07ac921f48baea84b40835b0b7c476806f7f63f6 (diff) | |
download | haskell-2f4638735ad1526d6502a4706bffafffb93e24da.tar.gz |
Detect overly long GC sync
Summary:
GC sync is the time between a GC being intiated and all the mutator
threads finally stopping so that the GC can start. Problems that cause
the GC sync to be delayed are hard to find and can cause dramatic
slowdowns for heavily parallel programs.
The new flag --long-gc-sync=<time> helps by emitting a warning and
calling a user-overridable hook when the GC sync time exceeds the
specified threshold. A debugger can be used to set a breakpoint when
this happens and inspect the stacks of threads to find the culprit.
Test Plan:
```
$ ./inplace/bin/ghc-stage2 +RTS --long-gc-sync=0.0000001 -S
Alloc Copied Live GC GC TOT TOT Page Flts
bytes bytes bytes user elap user elap
1135856 51144 153736 0.000 0.000 0.002 0.002 0 0 (Gen: 0)
1034760 94704 188752 0.000 0.000 0.002 0.002 0 0 (Gen: 0)
1038888 134832 228888 0.009 0.009 0.011 0.011 0 0 (Gen: 1)
1025288 90128 235184 0.000 0.000 0.012 0.012 0 0 (Gen: 0)
1049088 130080 333984 0.000 0.000 0.013 0.013 0 0 (Gen: 0)
Warning: waited 0us for GC sync
1034424 73360 331976 0.000 0.000 0.013 0.013 0 0 (Gen: 0)
```
Also tested on a real production problem.
Reviewers: niteria, bgamari, erikd
Subscribers: rwbarton, thomie
Differential Revision: https://phabricator.haskell.org/D4193
Diffstat (limited to 'rts')
-rw-r--r-- | rts/RtsFlags.c | 15 | ||||
-rw-r--r-- | rts/hooks/Hooks.h | 2 | ||||
-rw-r--r-- | rts/hooks/LongGCSync.c | 41 | ||||
-rw-r--r-- | rts/sm/GC.c | 22 | ||||
-rw-r--r-- | rts/sm/GCThread.h | 6 |
5 files changed, 80 insertions, 6 deletions
diff --git a/rts/RtsFlags.c b/rts/RtsFlags.c index c0571368cd..ecb9228234 100644 --- a/rts/RtsFlags.c +++ b/rts/RtsFlags.c @@ -69,7 +69,9 @@ const RtsConfig defaultRtsConfig = { .stackOverflowHook = StackOverflowHook, .outOfHeapHook = OutOfHeapHook, .mallocFailHook = MallocFailHook, - .gcDoneHook = NULL + .gcDoneHook = NULL, + .longGCSync = LongGCSync, + .longGCSyncEnd = LongGCSyncEnd }; /* @@ -165,6 +167,7 @@ void initRtsFlagsDefaults(void) RtsFlags.GcFlags.numa = false; RtsFlags.GcFlags.numaMask = 1; RtsFlags.GcFlags.ringBell = false; + RtsFlags.GcFlags.longGCSync = 0; /* detection turned off */ RtsFlags.DebugFlags.scheduler = false; RtsFlags.DebugFlags.interpreter = false; @@ -937,6 +940,16 @@ error = true; } } #endif + else if (!strncmp("long-gc-sync=", &rts_argv[arg][2], 13)) { + OPTION_SAFE; + if (rts_argv[arg][2] == '\0') { + /* use default */ + } else { + RtsFlags.GcFlags.longGCSync = + fsecondsToTime(atof(rts_argv[arg]+16)); + } + break; + } else { OPTION_SAFE; errorBelch("unknown RTS option: %s",rts_argv[arg]); diff --git a/rts/hooks/Hooks.h b/rts/hooks/Hooks.h index 760e1daefc..24500f19e7 100644 --- a/rts/hooks/Hooks.h +++ b/rts/hooks/Hooks.h @@ -22,5 +22,7 @@ extern void StackOverflowHook (W_ stack_size); extern void OutOfHeapHook (W_ request_size, W_ heap_size); extern void MallocFailHook (W_ request_size /* in bytes */, const char *msg); extern void FlagDefaultsHook (void); +extern void LongGCSync (uint32_t capno, Time t); +extern void LongGCSyncEnd (Time t); #include "EndPrivate.h" diff --git a/rts/hooks/LongGCSync.c b/rts/hooks/LongGCSync.c new file mode 100644 index 0000000000..351406df98 --- /dev/null +++ b/rts/hooks/LongGCSync.c @@ -0,0 +1,41 @@ +/* ----------------------------------------------------------------------------- + * + * User-overridable RTS hooks. + * + * ---------------------------------------------------------------------------*/ + +#include "PosixSource.h" +#include "Rts.h" +#include "sm/GC.h" +#include "sm/GCThread.h" +#include "Hooks.h" + +/* + * Called when --long-gc-sync=<time> has expired during a GC sync. The idea is + * that you can set a breakpoint on this function in gdb and try to determine + * which thread was holding up the GC sync. + */ +void LongGCSync (uint32_t me USED_IF_THREADS, Time t STG_UNUSED) +{ +#if defined(THREADED_RTS) + { + uint32_t i; + for (i=0; i < n_capabilities; i++) { + if (i != me && gc_threads[i]->wakeup != GC_THREAD_STANDING_BY) { + debugBelch("Warning: slow GC sync: still waiting for cap %d\n", + i); + } + } + } +#endif +} + +/* + * Called at the end of a GC sync which was longer than --long-gc-sync=<time>. + * The idea is that you can use this function to log stats about the length of + * GC syncs. + */ +void LongGCSyncEnd (Time t) +{ + debugBelch("Warning: waited %" FMT_Word64 "us for GC sync\n", TimeToUS(t)); +} diff --git a/rts/sm/GC.c b/rts/sm/GC.c index aa804a8b76..4dbc5e0aa6 100644 --- a/rts/sm/GC.c +++ b/rts/sm/GC.c @@ -48,6 +48,7 @@ #include "Stable.h" #include "CheckUnload.h" #include "CNF.h" +#include "RtsFlags.h" #include <string.h> // for memset() #include <unistd.h> @@ -825,11 +826,6 @@ static void heapOverflow(void) Initialise the gc_thread structures. -------------------------------------------------------------------------- */ -#define GC_THREAD_INACTIVE 0 -#define GC_THREAD_STANDING_BY 1 -#define GC_THREAD_RUNNING 2 -#define GC_THREAD_WAITING_TO_CONTINUE 3 - static void new_gc_thread (uint32_t n, gc_thread *t) { @@ -1132,6 +1128,9 @@ waitForGcThreads (Capability *cap USED_IF_THREADS, bool idle_cap[]) const uint32_t me = cap->no; uint32_t i, j; bool retry = true; + Time t0, t1, t2; + + t0 = t1 = t2 = getProcessElapsedTime(); while(retry) { for (i=0; i < n_threads; i++) { @@ -1153,6 +1152,19 @@ waitForGcThreads (Capability *cap USED_IF_THREADS, bool idle_cap[]) if (!retry) break; yieldThread(); } + + t2 = getProcessElapsedTime(); + if (RtsFlags.GcFlags.longGCSync != 0 && + t2 - t1 > RtsFlags.GcFlags.longGCSync) { + /* call this every longGCSync of delay */ + rtsConfig.longGCSync(cap->no, t2 - t0); + t1 = t2; + } + } + + if (RtsFlags.GcFlags.longGCSync != 0 && + t2 - t0 > RtsFlags.GcFlags.longGCSync) { + rtsConfig.longGCSyncEnd(t2 - t0); } } diff --git a/rts/sm/GCThread.h b/rts/sm/GCThread.h index bb206db64c..e865dabe5d 100644 --- a/rts/sm/GCThread.h +++ b/rts/sm/GCThread.h @@ -116,6 +116,12 @@ typedef struct gen_workspace_ { of the GC threads ------------------------------------------------------------------------- */ +/* values for the wakeup field */ +#define GC_THREAD_INACTIVE 0 +#define GC_THREAD_STANDING_BY 1 +#define GC_THREAD_RUNNING 2 +#define GC_THREAD_WAITING_TO_CONTINUE 3 + typedef struct gc_thread_ { Capability *cap; |