Scott Baker | eee8dd8 | 2019-09-24 12:52:34 -0700 | [diff] [blame] | 1 | package metrics |
| 2 | |
| 3 | import ( |
| 4 | "runtime" |
| 5 | "runtime/pprof" |
Scott Baker | 611f6bd | 2019-10-18 13:45:19 -0700 | [diff] [blame] | 6 | "sync" |
Scott Baker | eee8dd8 | 2019-09-24 12:52:34 -0700 | [diff] [blame] | 7 | "time" |
| 8 | ) |
| 9 | |
| 10 | var ( |
| 11 | memStats runtime.MemStats |
| 12 | runtimeMetrics struct { |
| 13 | MemStats struct { |
| 14 | Alloc Gauge |
| 15 | BuckHashSys Gauge |
| 16 | DebugGC Gauge |
| 17 | EnableGC Gauge |
| 18 | Frees Gauge |
| 19 | HeapAlloc Gauge |
| 20 | HeapIdle Gauge |
| 21 | HeapInuse Gauge |
| 22 | HeapObjects Gauge |
| 23 | HeapReleased Gauge |
| 24 | HeapSys Gauge |
| 25 | LastGC Gauge |
| 26 | Lookups Gauge |
| 27 | Mallocs Gauge |
| 28 | MCacheInuse Gauge |
| 29 | MCacheSys Gauge |
| 30 | MSpanInuse Gauge |
| 31 | MSpanSys Gauge |
| 32 | NextGC Gauge |
| 33 | NumGC Gauge |
| 34 | GCCPUFraction GaugeFloat64 |
| 35 | PauseNs Histogram |
| 36 | PauseTotalNs Gauge |
| 37 | StackInuse Gauge |
| 38 | StackSys Gauge |
| 39 | Sys Gauge |
| 40 | TotalAlloc Gauge |
| 41 | } |
| 42 | NumCgoCall Gauge |
| 43 | NumGoroutine Gauge |
| 44 | NumThread Gauge |
| 45 | ReadMemStats Timer |
| 46 | } |
| 47 | frees uint64 |
| 48 | lookups uint64 |
| 49 | mallocs uint64 |
| 50 | numGC uint32 |
| 51 | numCgoCalls int64 |
| 52 | |
Scott Baker | 611f6bd | 2019-10-18 13:45:19 -0700 | [diff] [blame] | 53 | threadCreateProfile = pprof.Lookup("threadcreate") |
| 54 | registerRuntimeMetricsOnce = sync.Once{} |
Scott Baker | eee8dd8 | 2019-09-24 12:52:34 -0700 | [diff] [blame] | 55 | ) |
| 56 | |
| 57 | // Capture new values for the Go runtime statistics exported in |
| 58 | // runtime.MemStats. This is designed to be called as a goroutine. |
| 59 | func CaptureRuntimeMemStats(r Registry, d time.Duration) { |
| 60 | for _ = range time.Tick(d) { |
| 61 | CaptureRuntimeMemStatsOnce(r) |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | // Capture new values for the Go runtime statistics exported in |
| 66 | // runtime.MemStats. This is designed to be called in a background |
| 67 | // goroutine. Giving a registry which has not been given to |
| 68 | // RegisterRuntimeMemStats will panic. |
| 69 | // |
| 70 | // Be very careful with this because runtime.ReadMemStats calls the C |
| 71 | // functions runtime·semacquire(&runtime·worldsema) and runtime·stoptheworld() |
| 72 | // and that last one does what it says on the tin. |
| 73 | func CaptureRuntimeMemStatsOnce(r Registry) { |
| 74 | t := time.Now() |
| 75 | runtime.ReadMemStats(&memStats) // This takes 50-200us. |
| 76 | runtimeMetrics.ReadMemStats.UpdateSince(t) |
| 77 | |
| 78 | runtimeMetrics.MemStats.Alloc.Update(int64(memStats.Alloc)) |
| 79 | runtimeMetrics.MemStats.BuckHashSys.Update(int64(memStats.BuckHashSys)) |
| 80 | if memStats.DebugGC { |
| 81 | runtimeMetrics.MemStats.DebugGC.Update(1) |
| 82 | } else { |
| 83 | runtimeMetrics.MemStats.DebugGC.Update(0) |
| 84 | } |
| 85 | if memStats.EnableGC { |
| 86 | runtimeMetrics.MemStats.EnableGC.Update(1) |
| 87 | } else { |
| 88 | runtimeMetrics.MemStats.EnableGC.Update(0) |
| 89 | } |
| 90 | |
| 91 | runtimeMetrics.MemStats.Frees.Update(int64(memStats.Frees - frees)) |
| 92 | runtimeMetrics.MemStats.HeapAlloc.Update(int64(memStats.HeapAlloc)) |
| 93 | runtimeMetrics.MemStats.HeapIdle.Update(int64(memStats.HeapIdle)) |
| 94 | runtimeMetrics.MemStats.HeapInuse.Update(int64(memStats.HeapInuse)) |
| 95 | runtimeMetrics.MemStats.HeapObjects.Update(int64(memStats.HeapObjects)) |
| 96 | runtimeMetrics.MemStats.HeapReleased.Update(int64(memStats.HeapReleased)) |
| 97 | runtimeMetrics.MemStats.HeapSys.Update(int64(memStats.HeapSys)) |
| 98 | runtimeMetrics.MemStats.LastGC.Update(int64(memStats.LastGC)) |
| 99 | runtimeMetrics.MemStats.Lookups.Update(int64(memStats.Lookups - lookups)) |
| 100 | runtimeMetrics.MemStats.Mallocs.Update(int64(memStats.Mallocs - mallocs)) |
| 101 | runtimeMetrics.MemStats.MCacheInuse.Update(int64(memStats.MCacheInuse)) |
| 102 | runtimeMetrics.MemStats.MCacheSys.Update(int64(memStats.MCacheSys)) |
| 103 | runtimeMetrics.MemStats.MSpanInuse.Update(int64(memStats.MSpanInuse)) |
| 104 | runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys)) |
| 105 | runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC)) |
| 106 | runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC)) |
| 107 | runtimeMetrics.MemStats.GCCPUFraction.Update(gcCPUFraction(&memStats)) |
| 108 | |
| 109 | // <https://code.google.com/p/go/source/browse/src/pkg/runtime/mgc0.c> |
| 110 | i := numGC % uint32(len(memStats.PauseNs)) |
| 111 | ii := memStats.NumGC % uint32(len(memStats.PauseNs)) |
| 112 | if memStats.NumGC-numGC >= uint32(len(memStats.PauseNs)) { |
| 113 | for i = 0; i < uint32(len(memStats.PauseNs)); i++ { |
| 114 | runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) |
| 115 | } |
| 116 | } else { |
| 117 | if i > ii { |
| 118 | for ; i < uint32(len(memStats.PauseNs)); i++ { |
| 119 | runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) |
| 120 | } |
| 121 | i = 0 |
| 122 | } |
| 123 | for ; i < ii; i++ { |
| 124 | runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) |
| 125 | } |
| 126 | } |
| 127 | frees = memStats.Frees |
| 128 | lookups = memStats.Lookups |
| 129 | mallocs = memStats.Mallocs |
| 130 | numGC = memStats.NumGC |
| 131 | |
| 132 | runtimeMetrics.MemStats.PauseTotalNs.Update(int64(memStats.PauseTotalNs)) |
| 133 | runtimeMetrics.MemStats.StackInuse.Update(int64(memStats.StackInuse)) |
| 134 | runtimeMetrics.MemStats.StackSys.Update(int64(memStats.StackSys)) |
| 135 | runtimeMetrics.MemStats.Sys.Update(int64(memStats.Sys)) |
| 136 | runtimeMetrics.MemStats.TotalAlloc.Update(int64(memStats.TotalAlloc)) |
| 137 | |
| 138 | currentNumCgoCalls := numCgoCall() |
| 139 | runtimeMetrics.NumCgoCall.Update(currentNumCgoCalls - numCgoCalls) |
| 140 | numCgoCalls = currentNumCgoCalls |
| 141 | |
| 142 | runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine())) |
| 143 | |
| 144 | runtimeMetrics.NumThread.Update(int64(threadCreateProfile.Count())) |
| 145 | } |
| 146 | |
| 147 | // Register runtimeMetrics for the Go runtime statistics exported in runtime and |
| 148 | // specifically runtime.MemStats. The runtimeMetrics are named by their |
| 149 | // fully-qualified Go symbols, i.e. runtime.MemStats.Alloc. |
| 150 | func RegisterRuntimeMemStats(r Registry) { |
Scott Baker | 611f6bd | 2019-10-18 13:45:19 -0700 | [diff] [blame] | 151 | registerRuntimeMetricsOnce.Do(func() { |
| 152 | runtimeMetrics.MemStats.Alloc = NewGauge() |
| 153 | runtimeMetrics.MemStats.BuckHashSys = NewGauge() |
| 154 | runtimeMetrics.MemStats.DebugGC = NewGauge() |
| 155 | runtimeMetrics.MemStats.EnableGC = NewGauge() |
| 156 | runtimeMetrics.MemStats.Frees = NewGauge() |
| 157 | runtimeMetrics.MemStats.HeapAlloc = NewGauge() |
| 158 | runtimeMetrics.MemStats.HeapIdle = NewGauge() |
| 159 | runtimeMetrics.MemStats.HeapInuse = NewGauge() |
| 160 | runtimeMetrics.MemStats.HeapObjects = NewGauge() |
| 161 | runtimeMetrics.MemStats.HeapReleased = NewGauge() |
| 162 | runtimeMetrics.MemStats.HeapSys = NewGauge() |
| 163 | runtimeMetrics.MemStats.LastGC = NewGauge() |
| 164 | runtimeMetrics.MemStats.Lookups = NewGauge() |
| 165 | runtimeMetrics.MemStats.Mallocs = NewGauge() |
| 166 | runtimeMetrics.MemStats.MCacheInuse = NewGauge() |
| 167 | runtimeMetrics.MemStats.MCacheSys = NewGauge() |
| 168 | runtimeMetrics.MemStats.MSpanInuse = NewGauge() |
| 169 | runtimeMetrics.MemStats.MSpanSys = NewGauge() |
| 170 | runtimeMetrics.MemStats.NextGC = NewGauge() |
| 171 | runtimeMetrics.MemStats.NumGC = NewGauge() |
| 172 | runtimeMetrics.MemStats.GCCPUFraction = NewGaugeFloat64() |
| 173 | runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015)) |
| 174 | runtimeMetrics.MemStats.PauseTotalNs = NewGauge() |
| 175 | runtimeMetrics.MemStats.StackInuse = NewGauge() |
| 176 | runtimeMetrics.MemStats.StackSys = NewGauge() |
| 177 | runtimeMetrics.MemStats.Sys = NewGauge() |
| 178 | runtimeMetrics.MemStats.TotalAlloc = NewGauge() |
| 179 | runtimeMetrics.NumCgoCall = NewGauge() |
| 180 | runtimeMetrics.NumGoroutine = NewGauge() |
| 181 | runtimeMetrics.NumThread = NewGauge() |
| 182 | runtimeMetrics.ReadMemStats = NewTimer() |
Scott Baker | eee8dd8 | 2019-09-24 12:52:34 -0700 | [diff] [blame] | 183 | |
Scott Baker | 611f6bd | 2019-10-18 13:45:19 -0700 | [diff] [blame] | 184 | r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc) |
| 185 | r.Register("runtime.MemStats.BuckHashSys", runtimeMetrics.MemStats.BuckHashSys) |
| 186 | r.Register("runtime.MemStats.DebugGC", runtimeMetrics.MemStats.DebugGC) |
| 187 | r.Register("runtime.MemStats.EnableGC", runtimeMetrics.MemStats.EnableGC) |
| 188 | r.Register("runtime.MemStats.Frees", runtimeMetrics.MemStats.Frees) |
| 189 | r.Register("runtime.MemStats.HeapAlloc", runtimeMetrics.MemStats.HeapAlloc) |
| 190 | r.Register("runtime.MemStats.HeapIdle", runtimeMetrics.MemStats.HeapIdle) |
| 191 | r.Register("runtime.MemStats.HeapInuse", runtimeMetrics.MemStats.HeapInuse) |
| 192 | r.Register("runtime.MemStats.HeapObjects", runtimeMetrics.MemStats.HeapObjects) |
| 193 | r.Register("runtime.MemStats.HeapReleased", runtimeMetrics.MemStats.HeapReleased) |
| 194 | r.Register("runtime.MemStats.HeapSys", runtimeMetrics.MemStats.HeapSys) |
| 195 | r.Register("runtime.MemStats.LastGC", runtimeMetrics.MemStats.LastGC) |
| 196 | r.Register("runtime.MemStats.Lookups", runtimeMetrics.MemStats.Lookups) |
| 197 | r.Register("runtime.MemStats.Mallocs", runtimeMetrics.MemStats.Mallocs) |
| 198 | r.Register("runtime.MemStats.MCacheInuse", runtimeMetrics.MemStats.MCacheInuse) |
| 199 | r.Register("runtime.MemStats.MCacheSys", runtimeMetrics.MemStats.MCacheSys) |
| 200 | r.Register("runtime.MemStats.MSpanInuse", runtimeMetrics.MemStats.MSpanInuse) |
| 201 | r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys) |
| 202 | r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC) |
| 203 | r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC) |
| 204 | r.Register("runtime.MemStats.GCCPUFraction", runtimeMetrics.MemStats.GCCPUFraction) |
| 205 | r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs) |
| 206 | r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs) |
| 207 | r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse) |
| 208 | r.Register("runtime.MemStats.StackSys", runtimeMetrics.MemStats.StackSys) |
| 209 | r.Register("runtime.MemStats.Sys", runtimeMetrics.MemStats.Sys) |
| 210 | r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc) |
| 211 | r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall) |
| 212 | r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine) |
| 213 | r.Register("runtime.NumThread", runtimeMetrics.NumThread) |
| 214 | r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats) |
| 215 | }) |
Scott Baker | eee8dd8 | 2019-09-24 12:52:34 -0700 | [diff] [blame] | 216 | } |