package runtime
import (
"internal/godebugs"
"unsafe"
)
var (
metricsSema uint32 = 1
metricsInit bool
metrics map [string ]metricData
sizeClassBuckets []float64
timeHistBuckets []float64
)
type metricData struct {
deps statDepSet
compute func (in *statAggregate , out *metricValue )
}
func metricsLock () {
semacquire1 (&metricsSema , true , 0 , 0 , waitReasonSemacquire )
if raceenabled {
raceacquire (unsafe .Pointer (&metricsSema ))
}
}
func metricsUnlock () {
if raceenabled {
racerelease (unsafe .Pointer (&metricsSema ))
}
semrelease (&metricsSema )
}
func initMetrics () {
if metricsInit {
return
}
sizeClassBuckets = make ([]float64 , _NumSizeClasses , _NumSizeClasses +1 )
sizeClassBuckets [0 ] = 1
for i := 1 ; i < _NumSizeClasses ; i ++ {
sizeClassBuckets [i ] = float64 (class_to_size [i ] + 1 )
}
sizeClassBuckets = append (sizeClassBuckets , float64Inf ())
timeHistBuckets = timeHistogramMetricsBuckets ()
metrics = map [string ]metricData {
"/cgo/go-to-c-calls:calls" : {
compute : func (_ *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (NumCgoCall ())
},
},
"/cpu/classes/gc/mark/assist:cpu-seconds" : {
deps : makeStatDepSet (cpuStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (in .cpuStats .gcAssistTime ))
},
},
"/cpu/classes/gc/mark/dedicated:cpu-seconds" : {
deps : makeStatDepSet (cpuStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (in .cpuStats .gcDedicatedTime ))
},
},
"/cpu/classes/gc/mark/idle:cpu-seconds" : {
deps : makeStatDepSet (cpuStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (in .cpuStats .gcIdleTime ))
},
},
"/cpu/classes/gc/pause:cpu-seconds" : {
deps : makeStatDepSet (cpuStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (in .cpuStats .gcPauseTime ))
},
},
"/cpu/classes/gc/total:cpu-seconds" : {
deps : makeStatDepSet (cpuStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (in .cpuStats .gcTotalTime ))
},
},
"/cpu/classes/idle:cpu-seconds" : {
deps : makeStatDepSet (cpuStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (in .cpuStats .idleTime ))
},
},
"/cpu/classes/scavenge/assist:cpu-seconds" : {
deps : makeStatDepSet (cpuStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (in .cpuStats .scavengeAssistTime ))
},
},
"/cpu/classes/scavenge/background:cpu-seconds" : {
deps : makeStatDepSet (cpuStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (in .cpuStats .scavengeBgTime ))
},
},
"/cpu/classes/scavenge/total:cpu-seconds" : {
deps : makeStatDepSet (cpuStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (in .cpuStats .scavengeTotalTime ))
},
},
"/cpu/classes/total:cpu-seconds" : {
deps : makeStatDepSet (cpuStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (in .cpuStats .totalTime ))
},
},
"/cpu/classes/user:cpu-seconds" : {
deps : makeStatDepSet (cpuStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (in .cpuStats .userTime ))
},
},
"/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/scan/globals:bytes" : {
deps : makeStatDepSet (gcStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .gcStats .globalsScan
},
},
"/gc/scan/heap:bytes" : {
deps : makeStatDepSet (gcStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .gcStats .heapScan
},
},
"/gc/scan/stack:bytes" : {
deps : makeStatDepSet (gcStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .gcStats .stackScan
},
},
"/gc/scan/total:bytes" : {
deps : makeStatDepSet (gcStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .gcStats .totalScan
},
},
"/gc/heap/allocs-by-size:bytes" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
hist := out .float64HistOrInit (sizeClassBuckets )
hist .counts [len (hist .counts )-1 ] = uint64 (in .heapStats .largeAllocCount )
for i , count := range in .heapStats .smallAllocCount [1 :] {
hist .counts [i ] = uint64 (count )
}
},
},
"/gc/heap/allocs:bytes" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .heapStats .totalAllocated
},
},
"/gc/heap/allocs:objects" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .heapStats .totalAllocs
},
},
"/gc/heap/frees-by-size:bytes" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
hist := out .float64HistOrInit (sizeClassBuckets )
hist .counts [len (hist .counts )-1 ] = uint64 (in .heapStats .largeFreeCount )
for i , count := range in .heapStats .smallFreeCount [1 :] {
hist .counts [i ] = uint64 (count )
}
},
},
"/gc/heap/frees:bytes" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .heapStats .totalFreed
},
},
"/gc/heap/frees:objects" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .heapStats .totalFrees
},
},
"/gc/heap/goal:bytes" : {
deps : makeStatDepSet (sysStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .sysStats .heapGoal
},
},
"/gc/gomemlimit:bytes" : {
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (gcController .memoryLimit .Load ())
},
},
"/gc/gogc:percent" : {
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (gcController .gcPercent .Load ())
},
},
"/gc/heap/live:bytes" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = gcController .heapMarked
},
},
"/gc/heap/objects:objects" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .heapStats .numObjects
},
},
"/gc/heap/tiny/allocs:objects" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (in .heapStats .tinyAllocCount )
},
},
"/gc/limiter/last-enabled:gc-cycle" : {
compute : func (_ *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (gcCPULimiter .lastEnabledCycle .Load ())
},
},
"/gc/pauses:seconds" : {
compute : func (_ *statAggregate , out *metricValue ) {
hist := out .float64HistOrInit (timeHistBuckets )
hist .counts [0 ] = memstats .gcPauseDist .underflow .Load ()
for i := range memstats .gcPauseDist .counts {
hist .counts [i +1 ] = memstats .gcPauseDist .counts [i ].Load ()
}
hist .counts [len (hist .counts )-1 ] = memstats .gcPauseDist .overflow .Load ()
},
},
"/gc/stack/starting-size:bytes" : {
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (startingStackSize )
},
},
"/memory/classes/heap/free:bytes" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (in .heapStats .committed - in .heapStats .inHeap -
in .heapStats .inStacks - in .heapStats .inWorkBufs -
in .heapStats .inPtrScalarBits )
},
},
"/memory/classes/heap/objects:bytes" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .heapStats .inObjects
},
},
"/memory/classes/heap/released:bytes" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (in .heapStats .released )
},
},
"/memory/classes/heap/stacks:bytes" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (in .heapStats .inStacks )
},
},
"/memory/classes/heap/unused:bytes" : {
deps : makeStatDepSet (heapStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (in .heapStats .inHeap ) - in .heapStats .inObjects
},
},
"/memory/classes/metadata/mcache/free:bytes" : {
deps : makeStatDepSet (sysStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .sysStats .mCacheSys - in .sysStats .mCacheInUse
},
},
"/memory/classes/metadata/mcache/inuse:bytes" : {
deps : makeStatDepSet (sysStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .sysStats .mCacheInUse
},
},
"/memory/classes/metadata/mspan/free:bytes" : {
deps : makeStatDepSet (sysStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .sysStats .mSpanSys - in .sysStats .mSpanInUse
},
},
"/memory/classes/metadata/mspan/inuse:bytes" : {
deps : makeStatDepSet (sysStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .sysStats .mSpanInUse
},
},
"/memory/classes/metadata/other:bytes" : {
deps : makeStatDepSet (heapStatsDep , sysStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (in .heapStats .inWorkBufs +in .heapStats .inPtrScalarBits ) + in .sysStats .gcMiscSys
},
},
"/memory/classes/os-stacks:bytes" : {
deps : makeStatDepSet (sysStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .sysStats .stacksSys
},
},
"/memory/classes/other:bytes" : {
deps : makeStatDepSet (sysStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .sysStats .otherSys
},
},
"/memory/classes/profiling/buckets:bytes" : {
deps : makeStatDepSet (sysStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = in .sysStats .buckHashSys
},
},
"/memory/classes/total:bytes" : {
deps : makeStatDepSet (heapStatsDep , sysStatsDep ),
compute : func (in *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (in .heapStats .committed +in .heapStats .released ) +
in .sysStats .stacksSys + in .sysStats .mSpanSys +
in .sysStats .mCacheSys + in .sysStats .buckHashSys +
in .sysStats .gcMiscSys + in .sysStats .otherSys
},
},
"/sched/gomaxprocs:threads" : {
compute : func (_ *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (gomaxprocs )
},
},
"/sched/goroutines:goroutines" : {
compute : func (_ *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = uint64 (gcount ())
},
},
"/sched/latencies:seconds" : {
compute : func (_ *statAggregate , out *metricValue ) {
hist := out .float64HistOrInit (timeHistBuckets )
hist .counts [0 ] = sched .timeToRun .underflow .Load ()
for i := range sched .timeToRun .counts {
hist .counts [i +1 ] = sched .timeToRun .counts [i ].Load ()
}
hist .counts [len (hist .counts )-1 ] = sched .timeToRun .overflow .Load ()
},
},
"/sync/mutex/wait/total:seconds" : {
compute : func (_ *statAggregate , out *metricValue ) {
out .kind = metricKindFloat64
out .scalar = float64bits (nsToSec (sched .totalMutexWaitTime .Load ()))
},
},
}
for _ , info := range godebugs .All {
if !info .Opaque {
metrics ["/godebug/non-default-behavior/" +info .Name +":events" ] = metricData {compute : compute0 }
}
}
metricsInit = true
}
func compute0 (_ *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = 0
}
type metricReader func () uint64
func (f metricReader ) compute (_ *statAggregate , out *metricValue ) {
out .kind = metricKindUint64
out .scalar = f ()
}
func godebug_registerMetric (name string , read func () uint64 ) {
metricsLock ()
initMetrics ()
d , ok := metrics [name ]
if !ok {
throw ("runtime: unexpected metric registration for " + name )
}
d .compute = metricReader (read ).compute
metrics [name ] = d
metricsUnlock ()
}
type statDep uint
const (
heapStatsDep statDep = iota
sysStatsDep
cpuStatsDep
gcStatsDep
numStatsDeps
)
type statDepSet [1 ]uint64
func makeStatDepSet (deps ...statDep ) statDepSet {
var s statDepSet
for _ , d := range deps {
s [d /64 ] |= 1 << (d % 64 )
}
return s
}
func (s statDepSet ) difference (b statDepSet ) statDepSet {
var c statDepSet
for i := range s {
c [i ] = s [i ] &^ b [i ]
}
return c
}
func (s statDepSet ) union (b statDepSet ) statDepSet {
var c statDepSet
for i := range s {
c [i ] = s [i ] | b [i ]
}
return c
}
func (s *statDepSet ) empty () bool {
for _ , c := range s {
if c != 0 {
return false
}
}
return true
}
func (s *statDepSet ) has (d statDep ) bool {
return s [d /64 ]&(1 <<(d %64 )) != 0
}
type heapStatsAggregate struct {
heapStatsDelta
inObjects uint64
numObjects uint64
totalAllocated uint64
totalFreed uint64
totalAllocs uint64
totalFrees uint64
}
func (a *heapStatsAggregate ) compute () {
memstats .heapStats .read (&a .heapStatsDelta )
a .totalAllocs = a .largeAllocCount
a .totalFrees = a .largeFreeCount
a .totalAllocated = a .largeAlloc
a .totalFreed = a .largeFree
for i := range a .smallAllocCount {
na := a .smallAllocCount [i ]
nf := a .smallFreeCount [i ]
a .totalAllocs += na
a .totalFrees += nf
a .totalAllocated += na * uint64 (class_to_size [i ])
a .totalFreed += nf * uint64 (class_to_size [i ])
}
a .inObjects = a .totalAllocated - a .totalFreed
a .numObjects = a .totalAllocs - a .totalFrees
}
type sysStatsAggregate struct {
stacksSys uint64
mSpanSys uint64
mSpanInUse uint64
mCacheSys uint64
mCacheInUse uint64
buckHashSys uint64
gcMiscSys uint64
otherSys uint64
heapGoal uint64
gcCyclesDone uint64
gcCyclesForced uint64
}
func (a *sysStatsAggregate ) compute () {
a .stacksSys = memstats .stacks_sys .load ()
a .buckHashSys = memstats .buckhash_sys .load ()
a .gcMiscSys = memstats .gcMiscSys .load ()
a .otherSys = memstats .other_sys .load ()
a .heapGoal = gcController .heapGoal ()
a .gcCyclesDone = uint64 (memstats .numgc )
a .gcCyclesForced = uint64 (memstats .numforcedgc )
systemstack (func () {
lock (&mheap_ .lock )
a .mSpanSys = memstats .mspan_sys .load ()
a .mSpanInUse = uint64 (mheap_ .spanalloc .inuse )
a .mCacheSys = memstats .mcache_sys .load ()
a .mCacheInUse = uint64 (mheap_ .cachealloc .inuse )
unlock (&mheap_ .lock )
})
}
type cpuStatsAggregate struct {
cpuStats
}
func (a *cpuStatsAggregate ) compute () {
a .cpuStats = work .cpuStats
}
type gcStatsAggregate struct {
heapScan uint64
stackScan uint64
globalsScan uint64
totalScan uint64
}
func (a *gcStatsAggregate ) compute () {
a .heapScan = gcController .heapScan .Load ()
a .stackScan = uint64 (gcController .lastStackScan .Load ())
a .globalsScan = gcController .globalsScan .Load ()
a .totalScan = a .heapScan + a .stackScan + a .globalsScan
}
func nsToSec (ns int64 ) float64 {
return float64 (ns ) / 1e9
}
type statAggregate struct {
ensured statDepSet
heapStats heapStatsAggregate
sysStats sysStatsAggregate
cpuStats cpuStatsAggregate
gcStats gcStatsAggregate
}
func (a *statAggregate ) ensure (deps *statDepSet ) {
missing := deps .difference (a .ensured )
if missing .empty () {
return
}
for i := statDep (0 ); i < numStatsDeps ; i ++ {
if !missing .has (i ) {
continue
}
switch i {
case heapStatsDep :
a .heapStats .compute ()
case sysStatsDep :
a .sysStats .compute ()
case cpuStatsDep :
a .cpuStats .compute ()
case gcStatsDep :
a .gcStats .compute ()
}
}
a .ensured = a .ensured .union (missing )
}
type metricKind int
const (
metricKindBad metricKind = iota
metricKindUint64
metricKindFloat64
metricKindFloat64Histogram
)
type metricSample struct {
name string
value metricValue
}
type metricValue struct {
kind metricKind
scalar uint64
pointer unsafe .Pointer
}
func (v *metricValue ) float64HistOrInit (buckets []float64 ) *metricFloat64Histogram {
var hist *metricFloat64Histogram
if v .kind == metricKindFloat64Histogram && v .pointer != nil {
hist = (*metricFloat64Histogram )(v .pointer )
} else {
v .kind = metricKindFloat64Histogram
hist = new (metricFloat64Histogram )
v .pointer = unsafe .Pointer (hist )
}
hist .buckets = buckets
if len (hist .counts ) != len (hist .buckets )-1 {
hist .counts = make ([]uint64 , len (buckets )-1 )
}
return hist
}
type metricFloat64Histogram struct {
counts []uint64
buckets []float64
}
var agg statAggregate
type metricName struct {
name string
kind metricKind
}
func readMetricNames () []string {
metricsLock ()
initMetrics ()
n := len (metrics )
metricsUnlock ()
list := make ([]string , 0 , n )
metricsLock ()
for name := range metrics {
list = append (list , name )
}
metricsUnlock ()
return list
}
func readMetrics (samplesp unsafe .Pointer , len int , cap int ) {
sl := slice {samplesp , len , cap }
samples := *(*[]metricSample )(unsafe .Pointer (&sl ))
metricsLock ()
initMetrics ()
agg = statAggregate {}
for i := range samples {
sample := &samples [i ]
data , ok := metrics [sample .name ]
if !ok {
sample .value .kind = metricKindBad
continue
}
agg .ensure (&data .deps )
data .compute (&agg , &sample .value )
}
metricsUnlock ()
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .