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