Skip to content

runtime,runtime/metrics: track on-cpu time per goroutine #51347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/runtime/metrics.go
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions src/runtime/metrics/description.go
Original file line number Diff line number Diff line change
@@ -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.",
3 changes: 3 additions & 0 deletions src/runtime/metrics/doc.go
Original file line number Diff line number Diff line change
@@ -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.

24 changes: 21 additions & 3 deletions src/runtime/proc.go
Original file line number Diff line number Diff line change
@@ -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
4 changes: 3 additions & 1 deletion src/runtime/runtime2.go
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion src/runtime/sizeof_test.go
Original file line number Diff line number Diff line change
@@ -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
}