Source file
src/runtime/metrics.go
1
2
3
4
5 package runtime
6
7
8
9 import (
10 "runtime/internal/atomic"
11 "unsafe"
12 )
13
14 var (
15
16
17
18 metricsSema uint32 = 1
19 metricsInit bool
20 metrics map[string]metricData
21
22 sizeClassBuckets []float64
23 timeHistBuckets []float64
24 )
25
26 type metricData struct {
27
28
29
30 deps statDepSet
31
32
33
34 compute func(in *statAggregate, out *metricValue)
35 }
36
37
38
39
40 func initMetrics() {
41 if metricsInit {
42 return
43 }
44
45 sizeClassBuckets = make([]float64, _NumSizeClasses, _NumSizeClasses+1)
46
47
48
49 sizeClassBuckets[0] = 1
50 for i := 1; i < _NumSizeClasses; i++ {
51
52
53
54
55
56
57
58
59
60
61
62 sizeClassBuckets[i] = float64(class_to_size[i] + 1)
63 }
64 sizeClassBuckets = append(sizeClassBuckets, float64Inf())
65
66 timeHistBuckets = timeHistogramMetricsBuckets()
67 metrics = map[string]metricData{
68 "/gc/cycles/automatic:gc-cycles": {
69 deps: makeStatDepSet(sysStatsDep),
70 compute: func(in *statAggregate, out *metricValue) {
71 out.kind = metricKindUint64
72 out.scalar = in.sysStats.gcCyclesDone - in.sysStats.gcCyclesForced
73 },
74 },
75 "/gc/cycles/forced:gc-cycles": {
76 deps: makeStatDepSet(sysStatsDep),
77 compute: func(in *statAggregate, out *metricValue) {
78 out.kind = metricKindUint64
79 out.scalar = in.sysStats.gcCyclesForced
80 },
81 },
82 "/gc/cycles/total:gc-cycles": {
83 deps: makeStatDepSet(sysStatsDep),
84 compute: func(in *statAggregate, out *metricValue) {
85 out.kind = metricKindUint64
86 out.scalar = in.sysStats.gcCyclesDone
87 },
88 },
89 "/gc/heap/allocs-by-size:bytes": {
90 deps: makeStatDepSet(heapStatsDep),
91 compute: func(in *statAggregate, out *metricValue) {
92 hist := out.float64HistOrInit(sizeClassBuckets)
93 hist.counts[len(hist.counts)-1] = uint64(in.heapStats.largeAllocCount)
94
95
96 for i, count := range in.heapStats.smallAllocCount[1:] {
97 hist.counts[i] = uint64(count)
98 }
99 },
100 },
101 "/gc/heap/allocs:bytes": {
102 deps: makeStatDepSet(heapStatsDep),
103 compute: func(in *statAggregate, out *metricValue) {
104 out.kind = metricKindUint64
105 out.scalar = in.heapStats.totalAllocated
106 },
107 },
108 "/gc/heap/allocs:objects": {
109 deps: makeStatDepSet(heapStatsDep),
110 compute: func(in *statAggregate, out *metricValue) {
111 out.kind = metricKindUint64
112 out.scalar = in.heapStats.totalAllocs
113 },
114 },
115 "/gc/heap/frees-by-size:bytes": {
116 deps: makeStatDepSet(heapStatsDep),
117 compute: func(in *statAggregate, out *metricValue) {
118 hist := out.float64HistOrInit(sizeClassBuckets)
119 hist.counts[len(hist.counts)-1] = uint64(in.heapStats.largeFreeCount)
120
121
122 for i, count := range in.heapStats.smallFreeCount[1:] {
123 hist.counts[i] = uint64(count)
124 }
125 },
126 },
127 "/gc/heap/frees:bytes": {
128 deps: makeStatDepSet(heapStatsDep),
129 compute: func(in *statAggregate, out *metricValue) {
130 out.kind = metricKindUint64
131 out.scalar = in.heapStats.totalFreed
132 },
133 },
134 "/gc/heap/frees:objects": {
135 deps: makeStatDepSet(heapStatsDep),
136 compute: func(in *statAggregate, out *metricValue) {
137 out.kind = metricKindUint64
138 out.scalar = in.heapStats.totalFrees
139 },
140 },
141 "/gc/heap/goal:bytes": {
142 deps: makeStatDepSet(sysStatsDep),
143 compute: func(in *statAggregate, out *metricValue) {
144 out.kind = metricKindUint64
145 out.scalar = in.sysStats.heapGoal
146 },
147 },
148 "/gc/heap/objects:objects": {
149 deps: makeStatDepSet(heapStatsDep),
150 compute: func(in *statAggregate, out *metricValue) {
151 out.kind = metricKindUint64
152 out.scalar = in.heapStats.numObjects
153 },
154 },
155 "/gc/heap/tiny/allocs:objects": {
156 deps: makeStatDepSet(heapStatsDep),
157 compute: func(in *statAggregate, out *metricValue) {
158 out.kind = metricKindUint64
159 out.scalar = uint64(in.heapStats.tinyAllocCount)
160 },
161 },
162 "/gc/pauses:seconds": {
163 compute: func(_ *statAggregate, out *metricValue) {
164 hist := out.float64HistOrInit(timeHistBuckets)
165
166
167
168 hist.counts[0] = atomic.Load64(&memstats.gcPauseDist.underflow)
169 for i := range memstats.gcPauseDist.counts {
170 hist.counts[i+1] = atomic.Load64(&memstats.gcPauseDist.counts[i])
171 }
172 },
173 },
174 "/memory/classes/heap/free:bytes": {
175 deps: makeStatDepSet(heapStatsDep),
176 compute: func(in *statAggregate, out *metricValue) {
177 out.kind = metricKindUint64
178 out.scalar = uint64(in.heapStats.committed - in.heapStats.inHeap -
179 in.heapStats.inStacks - in.heapStats.inWorkBufs -
180 in.heapStats.inPtrScalarBits)
181 },
182 },
183 "/memory/classes/heap/objects:bytes": {
184 deps: makeStatDepSet(heapStatsDep),
185 compute: func(in *statAggregate, out *metricValue) {
186 out.kind = metricKindUint64
187 out.scalar = in.heapStats.inObjects
188 },
189 },
190 "/memory/classes/heap/released:bytes": {
191 deps: makeStatDepSet(heapStatsDep),
192 compute: func(in *statAggregate, out *metricValue) {
193 out.kind = metricKindUint64
194 out.scalar = uint64(in.heapStats.released)
195 },
196 },
197 "/memory/classes/heap/stacks:bytes": {
198 deps: makeStatDepSet(heapStatsDep),
199 compute: func(in *statAggregate, out *metricValue) {
200 out.kind = metricKindUint64
201 out.scalar = uint64(in.heapStats.inStacks)
202 },
203 },
204 "/memory/classes/heap/unused:bytes": {
205 deps: makeStatDepSet(heapStatsDep),
206 compute: func(in *statAggregate, out *metricValue) {
207 out.kind = metricKindUint64
208 out.scalar = uint64(in.heapStats.inHeap) - in.heapStats.inObjects
209 },
210 },
211 "/memory/classes/metadata/mcache/free:bytes": {
212 deps: makeStatDepSet(sysStatsDep),
213 compute: func(in *statAggregate, out *metricValue) {
214 out.kind = metricKindUint64
215 out.scalar = in.sysStats.mCacheSys - in.sysStats.mCacheInUse
216 },
217 },
218 "/memory/classes/metadata/mcache/inuse:bytes": {
219 deps: makeStatDepSet(sysStatsDep),
220 compute: func(in *statAggregate, out *metricValue) {
221 out.kind = metricKindUint64
222 out.scalar = in.sysStats.mCacheInUse
223 },
224 },
225 "/memory/classes/metadata/mspan/free:bytes": {
226 deps: makeStatDepSet(sysStatsDep),
227 compute: func(in *statAggregate, out *metricValue) {
228 out.kind = metricKindUint64
229 out.scalar = in.sysStats.mSpanSys - in.sysStats.mSpanInUse
230 },
231 },
232 "/memory/classes/metadata/mspan/inuse:bytes": {
233 deps: makeStatDepSet(sysStatsDep),
234 compute: func(in *statAggregate, out *metricValue) {
235 out.kind = metricKindUint64
236 out.scalar = in.sysStats.mSpanInUse
237 },
238 },
239 "/memory/classes/metadata/other:bytes": {
240 deps: makeStatDepSet(heapStatsDep, sysStatsDep),
241 compute: func(in *statAggregate, out *metricValue) {
242 out.kind = metricKindUint64
243 out.scalar = uint64(in.heapStats.inWorkBufs+in.heapStats.inPtrScalarBits) + in.sysStats.gcMiscSys
244 },
245 },
246 "/memory/classes/os-stacks:bytes": {
247 deps: makeStatDepSet(sysStatsDep),
248 compute: func(in *statAggregate, out *metricValue) {
249 out.kind = metricKindUint64
250 out.scalar = in.sysStats.stacksSys
251 },
252 },
253 "/memory/classes/other:bytes": {
254 deps: makeStatDepSet(sysStatsDep),
255 compute: func(in *statAggregate, out *metricValue) {
256 out.kind = metricKindUint64
257 out.scalar = in.sysStats.otherSys
258 },
259 },
260 "/memory/classes/profiling/buckets:bytes": {
261 deps: makeStatDepSet(sysStatsDep),
262 compute: func(in *statAggregate, out *metricValue) {
263 out.kind = metricKindUint64
264 out.scalar = in.sysStats.buckHashSys
265 },
266 },
267 "/memory/classes/total:bytes": {
268 deps: makeStatDepSet(heapStatsDep, sysStatsDep),
269 compute: func(in *statAggregate, out *metricValue) {
270 out.kind = metricKindUint64
271 out.scalar = uint64(in.heapStats.committed+in.heapStats.released) +
272 in.sysStats.stacksSys + in.sysStats.mSpanSys +
273 in.sysStats.mCacheSys + in.sysStats.buckHashSys +
274 in.sysStats.gcMiscSys + in.sysStats.otherSys
275 },
276 },
277 "/sched/goroutines:goroutines": {
278 compute: func(_ *statAggregate, out *metricValue) {
279 out.kind = metricKindUint64
280 out.scalar = uint64(gcount())
281 },
282 },
283 "/sched/latencies:seconds": {
284 compute: func(_ *statAggregate, out *metricValue) {
285 hist := out.float64HistOrInit(timeHistBuckets)
286 hist.counts[0] = atomic.Load64(&sched.timeToRun.underflow)
287 for i := range sched.timeToRun.counts {
288 hist.counts[i+1] = atomic.Load64(&sched.timeToRun.counts[i])
289 }
290 },
291 },
292 }
293 metricsInit = true
294 }
295
296
297
298 type statDep uint
299
300 const (
301 heapStatsDep statDep = iota
302 sysStatsDep
303 numStatsDeps
304 )
305
306
307
308
309 type statDepSet [1]uint64
310
311
312 func makeStatDepSet(deps ...statDep) statDepSet {
313 var s statDepSet
314 for _, d := range deps {
315 s[d/64] |= 1 << (d % 64)
316 }
317 return s
318 }
319
320
321 func (s statDepSet) difference(b statDepSet) statDepSet {
322 var c statDepSet
323 for i := range s {
324 c[i] = s[i] &^ b[i]
325 }
326 return c
327 }
328
329
330 func (s statDepSet) union(b statDepSet) statDepSet {
331 var c statDepSet
332 for i := range s {
333 c[i] = s[i] | b[i]
334 }
335 return c
336 }
337
338
339 func (s *statDepSet) empty() bool {
340 for _, c := range s {
341 if c != 0 {
342 return false
343 }
344 }
345 return true
346 }
347
348
349 func (s *statDepSet) has(d statDep) bool {
350 return s[d/64]&(1<<(d%64)) != 0
351 }
352
353
354
355
356
357
358 type heapStatsAggregate struct {
359 heapStatsDelta
360
361
362
363
364 inObjects uint64
365
366
367 numObjects uint64
368
369
370
371 totalAllocated uint64
372
373
374
375 totalFreed uint64
376
377
378
379 totalAllocs uint64
380
381
382
383 totalFrees uint64
384 }
385
386
387 func (a *heapStatsAggregate) compute() {
388 memstats.heapStats.read(&a.heapStatsDelta)
389
390
391 a.totalAllocs = uint64(a.largeAllocCount)
392 a.totalFrees = uint64(a.largeFreeCount)
393 a.totalAllocated = uint64(a.largeAlloc)
394 a.totalFreed = uint64(a.largeFree)
395 for i := range a.smallAllocCount {
396 na := uint64(a.smallAllocCount[i])
397 nf := uint64(a.smallFreeCount[i])
398 a.totalAllocs += na
399 a.totalFrees += nf
400 a.totalAllocated += na * uint64(class_to_size[i])
401 a.totalFreed += nf * uint64(class_to_size[i])
402 }
403 a.inObjects = a.totalAllocated - a.totalFreed
404 a.numObjects = a.totalAllocs - a.totalFrees
405 }
406
407
408
409
410
411
412
413
414 type sysStatsAggregate struct {
415 stacksSys uint64
416 mSpanSys uint64
417 mSpanInUse uint64
418 mCacheSys uint64
419 mCacheInUse uint64
420 buckHashSys uint64
421 gcMiscSys uint64
422 otherSys uint64
423 heapGoal uint64
424 gcCyclesDone uint64
425 gcCyclesForced uint64
426 }
427
428
429 func (a *sysStatsAggregate) compute() {
430 a.stacksSys = memstats.stacks_sys.load()
431 a.buckHashSys = memstats.buckhash_sys.load()
432 a.gcMiscSys = memstats.gcMiscSys.load()
433 a.otherSys = memstats.other_sys.load()
434 a.heapGoal = atomic.Load64(&gcController.heapGoal)
435 a.gcCyclesDone = uint64(memstats.numgc)
436 a.gcCyclesForced = uint64(memstats.numforcedgc)
437
438 systemstack(func() {
439 lock(&mheap_.lock)
440 a.mSpanSys = memstats.mspan_sys.load()
441 a.mSpanInUse = uint64(mheap_.spanalloc.inuse)
442 a.mCacheSys = memstats.mcache_sys.load()
443 a.mCacheInUse = uint64(mheap_.cachealloc.inuse)
444 unlock(&mheap_.lock)
445 })
446 }
447
448
449
450
451
452
453 type statAggregate struct {
454 ensured statDepSet
455 heapStats heapStatsAggregate
456 sysStats sysStatsAggregate
457 }
458
459
460
461 func (a *statAggregate) ensure(deps *statDepSet) {
462 missing := deps.difference(a.ensured)
463 if missing.empty() {
464 return
465 }
466 for i := statDep(0); i < numStatsDeps; i++ {
467 if !missing.has(i) {
468 continue
469 }
470 switch i {
471 case heapStatsDep:
472 a.heapStats.compute()
473 case sysStatsDep:
474 a.sysStats.compute()
475 }
476 }
477 a.ensured = a.ensured.union(missing)
478 }
479
480
481
482 type metricKind int
483
484 const (
485
486
487 metricKindBad metricKind = iota
488 metricKindUint64
489 metricKindFloat64
490 metricKindFloat64Histogram
491 )
492
493
494
495 type metricSample struct {
496 name string
497 value metricValue
498 }
499
500
501
502 type metricValue struct {
503 kind metricKind
504 scalar uint64
505 pointer unsafe.Pointer
506 }
507
508
509
510
511 func (v *metricValue) float64HistOrInit(buckets []float64) *metricFloat64Histogram {
512 var hist *metricFloat64Histogram
513 if v.kind == metricKindFloat64Histogram && v.pointer != nil {
514 hist = (*metricFloat64Histogram)(v.pointer)
515 } else {
516 v.kind = metricKindFloat64Histogram
517 hist = new(metricFloat64Histogram)
518 v.pointer = unsafe.Pointer(hist)
519 }
520 hist.buckets = buckets
521 if len(hist.counts) != len(hist.buckets)-1 {
522 hist.counts = make([]uint64, len(buckets)-1)
523 }
524 return hist
525 }
526
527
528
529 type metricFloat64Histogram struct {
530 counts []uint64
531 buckets []float64
532 }
533
534
535
536
537
538
539 var agg statAggregate
540
541
542
543
544 func readMetrics(samplesp unsafe.Pointer, len int, cap int) {
545
546 sl := slice{samplesp, len, cap}
547 samples := *(*[]metricSample)(unsafe.Pointer(&sl))
548
549
550
551
552 semacquire1(&metricsSema, true, 0, 0)
553
554
555 initMetrics()
556
557
558 agg = statAggregate{}
559
560
561 for i := range samples {
562 sample := &samples[i]
563 data, ok := metrics[sample.name]
564 if !ok {
565 sample.value.kind = metricKindBad
566 continue
567 }
568
569
570 agg.ensure(&data.deps)
571
572
573 data.compute(&agg, &sample.value)
574 }
575
576 semrelease(&metricsSema)
577 }
578
View as plain text