diff --git a/src/runtime/metrics.go b/src/runtime/metrics.go index 125539b682158a..ba72901ba39694 100644 --- a/src/runtime/metrics.go +++ b/src/runtime/metrics.go @@ -286,6 +286,12 @@ func initMetrics() { out.scalar = uint64(gomaxprocs) }, }, + "/sched/goroutine/running:nanoseconds": { + compute: func(_ *statAggregate, out *metricValue) { + out.kind = metricKindUint64 + out.scalar = uint64(grunningnanos()) + }, + }, "/sched/goroutines:goroutines": { compute: func(_ *statAggregate, out *metricValue) { out.kind = metricKindUint64 diff --git a/src/runtime/metrics/description.go b/src/runtime/metrics/description.go index 52351772363845..36f9e2e2e40de2 100644 --- a/src/runtime/metrics/description.go +++ b/src/runtime/metrics/description.go @@ -225,6 +225,11 @@ var allDesc = []Description{ Description: "The current runtime.GOMAXPROCS setting, or the number of operating system threads that can execute user-level Go code simultaneously.", Kind: KindUint64, }, + { + Name: "/sched/goroutine/running:nanoseconds", + Description: "Wall time spent by the current goroutine in the running state.", + Kind: KindUint64, + }, { Name: "/sched/goroutines:goroutines", Description: "Count of live goroutines.", diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go index 30fdf06a71a684..5294cb057d7901 100644 --- a/src/runtime/metrics/doc.go +++ b/src/runtime/metrics/doc.go @@ -172,6 +172,9 @@ Below is the full list of supported metrics, ordered lexicographically. operating system threads that can execute user-level Go code simultaneously. + /sched/goroutine/running:nanoseconds + Wall time spent by the current goroutine in the running state. + /sched/goroutines:goroutines Count of live goroutines. diff --git a/src/runtime/proc.go b/src/runtime/proc.go index f5e528e8e9b60d..9f938311b8f047 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -994,8 +994,18 @@ func casgstatus(gp *g, oldval, newval uint32) { } } - // Handle tracking for scheduling latencies. + // Handle tracking for scheduling and running latencies. + now := nanotime() + if newval == _Grunning { + // We're transitioning into the running state, record the timestamp for + // subsequent use. + gp.lastsched = now + } if oldval == _Grunning { + // We're transitioning out of running, record how long we were in the + // state. + gp.runningnanos += now - gp.lastsched + // Track every 8th time a goroutine transitions out of running. if gp.trackingSeq%gTrackingPeriod == 0 { gp.tracking = true @@ -1007,14 +1017,12 @@ func casgstatus(gp *g, oldval, newval uint32) { // We transitioned out of runnable, so measure how much // time we spent in this state and add it to // runnableTime. - now := nanotime() gp.runnableTime += now - gp.runnableStamp gp.runnableStamp = 0 } if newval == _Grunnable { // We just transitioned into runnable, so record what // time that happened. - now := nanotime() gp.runnableStamp = now } else if newval == _Grunning { // We're transitioning into running, so turn off @@ -3258,6 +3266,14 @@ func dropg() { setGNoWB(&_g_.m.curg, nil) } +// grunningnanos returns the wall time spent by current g in the running state. +// A goroutine may be running on an OS thread that's descheduled by the OS +// scheduler, this time still counts towards the metric. +func grunningnanos() int64 { + gp := getg() + return gp.runningnanos + nanotime() - gp.lastsched +} + // checkTimers runs any timers for the P that are ready. // If now is not 0 it is the current time. // It returns the passed time or the current time if now was passed as 0. @@ -3491,6 +3507,8 @@ func goexit0(gp *g) { gp.param = nil gp.labels = nil gp.timer = nil + gp.lastsched = 0 + gp.runningnanos = 0 if gcBlackenEnabled != 0 && gp.gcAssistBytes > 0 { // Flush assist credit to the global pool. This gives diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 1e4f872726aa89..ced56dc4f65a45 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -474,7 +474,6 @@ type g struct { traceseq uint64 // trace event sequencer tracelastp puintptr // last P emitted an event for this goroutine lockedm muintptr - sig uint32 writebuf []byte sigcode0 uintptr sigcode1 uintptr @@ -488,6 +487,9 @@ type g struct { labels unsafe.Pointer // profiler labels timer *timer // cached timer for time.Sleep selectDone uint32 // are we participating in a select and did someone win the race? + sig uint32 + lastsched int64 // timestamp when the G last started running + runningnanos int64 // wall time spent in the running state // goroutineProfiled indicates the status of this goroutine's stack for the // current in-progress goroutine profile diff --git a/src/runtime/sizeof_test.go b/src/runtime/sizeof_test.go index 9ce0a3afcdca5c..71de8052bd093c 100644 --- a/src/runtime/sizeof_test.go +++ b/src/runtime/sizeof_test.go @@ -21,7 +21,7 @@ func TestSizeof(t *testing.T) { _32bit uintptr // size on 32bit platforms _64bit uintptr // size on 64bit platforms }{ - {runtime.G{}, 240, 392}, // g, but exported for testing + {runtime.G{}, 256, 408}, // g, but exported for testing {runtime.Sudog{}, 56, 88}, // sudog, but exported for testing }