runtime,runtime/metrics: add heap goal and GC cycle metrics

This change adds three new metrics: the heap goal, GC cycle count, and
forced GC count. These metrics are identical to their MemStats
counterparts.

For #37112.

Change-Id: I5a5e8dd550c0d646e5dcdbdf38274895e27cdd88
Reviewed-on: https://go-review.googlesource.com/c/go/+/247044
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
Michael Anthony Knyszek 2020-08-06 16:47:58 +00:00 committed by Michael Knyszek
parent 07c3f65d53
commit a8b28ebc87
4 changed files with 86 additions and 8 deletions

View file

@ -7,6 +7,7 @@ package runtime
// Metrics implementation exported to runtime/metrics. // Metrics implementation exported to runtime/metrics.
import ( import (
"runtime/internal/atomic"
"unsafe" "unsafe"
) )
@ -38,6 +39,34 @@ func initMetrics() {
return return
} }
metrics = map[string]metricData{ metrics = map[string]metricData{
"/gc/cycles/automatic:gc-cycles": {
deps: makeStatDepSet(sysStatsDep),
compute: func(in *statAggregate, out *metricValue) {
out.kind = metricKindUint64
out.scalar = in.sysStats.gcCyclesDone - in.sysStats.gcCyclesForced
},
},
"/gc/cycles/forced:gc-cycles": {
deps: makeStatDepSet(sysStatsDep),
compute: func(in *statAggregate, out *metricValue) {
out.kind = metricKindUint64
out.scalar = in.sysStats.gcCyclesForced
},
},
"/gc/cycles/total:gc-cycles": {
deps: makeStatDepSet(sysStatsDep),
compute: func(in *statAggregate, out *metricValue) {
out.kind = metricKindUint64
out.scalar = in.sysStats.gcCyclesDone
},
},
"/gc/heap/goal:bytes": {
deps: makeStatDepSet(sysStatsDep),
compute: func(in *statAggregate, out *metricValue) {
out.kind = metricKindUint64
out.scalar = in.sysStats.heapGoal
},
},
"/gc/heap/objects:objects": { "/gc/heap/objects:objects": {
deps: makeStatDepSet(heapStatsDep), deps: makeStatDepSet(heapStatsDep),
compute: func(in *statAggregate, out *metricValue) { compute: func(in *statAggregate, out *metricValue) {
@ -248,14 +277,17 @@ func (a *heapStatsAggregate) compute() {
// heapStatsAggregate, means there could be some skew, but because of // heapStatsAggregate, means there could be some skew, but because of
// these stats are independent, there's no real consistency issue here. // these stats are independent, there's no real consistency issue here.
type sysStatsAggregate struct { type sysStatsAggregate struct {
stacksSys uint64 stacksSys uint64
mSpanSys uint64 mSpanSys uint64
mSpanInUse uint64 mSpanInUse uint64
mCacheSys uint64 mCacheSys uint64
mCacheInUse uint64 mCacheInUse uint64
buckHashSys uint64 buckHashSys uint64
gcMiscSys uint64 gcMiscSys uint64
otherSys uint64 otherSys uint64
heapGoal uint64
gcCyclesDone uint64
gcCyclesForced uint64
} }
// compute populates the sysStatsAggregate with values from the runtime. // compute populates the sysStatsAggregate with values from the runtime.
@ -264,6 +296,9 @@ func (a *sysStatsAggregate) compute() {
a.buckHashSys = memstats.buckhash_sys.load() a.buckHashSys = memstats.buckhash_sys.load()
a.gcMiscSys = memstats.gcMiscSys.load() a.gcMiscSys = memstats.gcMiscSys.load()
a.otherSys = memstats.other_sys.load() a.otherSys = memstats.other_sys.load()
a.heapGoal = atomic.Load64(&memstats.next_gc)
a.gcCyclesDone = uint64(memstats.numgc)
a.gcCyclesForced = uint64(memstats.numforcedgc)
systemstack(func() { systemstack(func() {
lock(&mheap_.lock) lock(&mheap_.lock)

View file

@ -50,6 +50,29 @@ type Description struct {
// The English language descriptions below must be kept in sync with the // The English language descriptions below must be kept in sync with the
// descriptions of each metric in doc.go. // descriptions of each metric in doc.go.
var allDesc = []Description{ var allDesc = []Description{
{
Name: "/gc/cycles/automatic:gc-cycles",
Description: "Count of completed GC cycles generated by the Go runtime.",
Kind: KindUint64,
Cumulative: true,
},
{
Name: "/gc/cycles/forced:gc-cycles",
Description: "Count of completed forced GC cycles.",
Kind: KindUint64,
Cumulative: true,
},
{
Name: "/gc/cycles/total:gc-cycles",
Description: "Count of all completed GC cycles.",
Kind: KindUint64,
Cumulative: true,
},
{
Name: "/gc/heap/goal:bytes",
Description: "Heap size target for the end of the GC cycle.",
Kind: KindUint64,
},
{ {
Name: "/gc/heap/objects:objects", Name: "/gc/heap/objects:objects",
Description: "Number of objects, live or unswept, occupying heap memory.", Description: "Number of objects, live or unswept, occupying heap memory.",

View file

@ -44,6 +44,18 @@ the documentation of the Name field of the Description struct.
Supported metrics Supported metrics
/gc/cycles/automatic:gc-cycles
Count of completed GC cycles generated by the Go runtime.
/gc/cycles/forced:gc-cycles
Count of completed forced GC cycles.
/gc/cycles/total:gc-cycles
Count of all completed GC cycles.
/gc/heap/goal:bytes
Heap size target for the end of the GC cycle.
/gc/heap/objects:objects /gc/heap/objects:objects
Number of objects, live or unswept, occupying heap memory. Number of objects, live or unswept, occupying heap memory.

View file

@ -72,6 +72,14 @@ func TestReadMetrics(t *testing.T) {
checkUint64(t, name, samples[i].Value.Uint64(), mstats.Sys) checkUint64(t, name, samples[i].Value.Uint64(), mstats.Sys)
case "/gc/heap/objects:objects": case "/gc/heap/objects:objects":
checkUint64(t, name, samples[i].Value.Uint64(), mstats.HeapObjects) checkUint64(t, name, samples[i].Value.Uint64(), mstats.HeapObjects)
case "/gc/heap/goal:bytes":
checkUint64(t, name, samples[i].Value.Uint64(), mstats.NextGC)
case "/gc/cycles/automatic:gc-cycles":
checkUint64(t, name, samples[i].Value.Uint64(), uint64(mstats.NumGC-mstats.NumForcedGC))
case "/gc/cycles/forced:gc-cycles":
checkUint64(t, name, samples[i].Value.Uint64(), uint64(mstats.NumForcedGC))
case "/gc/cycles/total:gc-cycles":
checkUint64(t, name, samples[i].Value.Uint64(), uint64(mstats.NumGC))
} }
} }
} }