1package metrics
2
3import (
4	"runtime/debug"
5	"sync"
6	"time"
7)
8
9var (
10	debugMetrics struct {
11		GCStats struct {
12			LastGC Gauge
13			NumGC  Gauge
14			Pause  Histogram
15			//PauseQuantiles Histogram
16			PauseTotal Gauge
17		}
18		ReadGCStats Timer
19	}
20	gcStats                  debug.GCStats
21	registerDebugMetricsOnce = sync.Once{}
22)
23
24// Capture new values for the Go garbage collector statistics exported in
25// debug.GCStats.  This is designed to be called as a goroutine.
26func CaptureDebugGCStats(r Registry, d time.Duration) {
27	for _ = range time.Tick(d) {
28		CaptureDebugGCStatsOnce(r)
29	}
30}
31
32// Capture new values for the Go garbage collector statistics exported in
33// debug.GCStats.  This is designed to be called in a background goroutine.
34// Giving a registry which has not been given to RegisterDebugGCStats will
35// panic.
36//
37// Be careful (but much less so) with this because debug.ReadGCStats calls
38// the C function runtime·lock(runtime·mheap) which, while not a stop-the-world
39// operation, isn't something you want to be doing all the time.
40func CaptureDebugGCStatsOnce(r Registry) {
41	lastGC := gcStats.LastGC
42	t := time.Now()
43	debug.ReadGCStats(&gcStats)
44	debugMetrics.ReadGCStats.UpdateSince(t)
45
46	debugMetrics.GCStats.LastGC.Update(int64(gcStats.LastGC.UnixNano()))
47	debugMetrics.GCStats.NumGC.Update(int64(gcStats.NumGC))
48	if lastGC != gcStats.LastGC && 0 < len(gcStats.Pause) {
49		debugMetrics.GCStats.Pause.Update(int64(gcStats.Pause[0]))
50	}
51	//debugMetrics.GCStats.PauseQuantiles.Update(gcStats.PauseQuantiles)
52	debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal))
53}
54
55// Register metrics for the Go garbage collector statistics exported in
56// debug.GCStats.  The metrics are named by their fully-qualified Go symbols,
57// i.e. debug.GCStats.PauseTotal.
58func RegisterDebugGCStats(r Registry) {
59	registerDebugMetricsOnce.Do(func() {
60		debugMetrics.GCStats.LastGC = NewGauge()
61		debugMetrics.GCStats.NumGC = NewGauge()
62		debugMetrics.GCStats.Pause = NewHistogram(NewExpDecaySample(1028, 0.015))
63		//debugMetrics.GCStats.PauseQuantiles = NewHistogram(NewExpDecaySample(1028, 0.015))
64		debugMetrics.GCStats.PauseTotal = NewGauge()
65		debugMetrics.ReadGCStats = NewTimer()
66
67		r.Register("debug.GCStats.LastGC", debugMetrics.GCStats.LastGC)
68		r.Register("debug.GCStats.NumGC", debugMetrics.GCStats.NumGC)
69		r.Register("debug.GCStats.Pause", debugMetrics.GCStats.Pause)
70		//r.Register("debug.GCStats.PauseQuantiles", debugMetrics.GCStats.PauseQuantiles)
71		r.Register("debug.GCStats.PauseTotal", debugMetrics.GCStats.PauseTotal)
72		r.Register("debug.ReadGCStats", debugMetrics.ReadGCStats)
73	})
74}
75
76// Allocate an initial slice for gcStats.Pause to avoid allocations during
77// normal operation.
78func init() {
79	gcStats.Pause = make([]time.Duration, 11)
80}
81