Skip to content

Commit ab8121a

Browse files
mknyszekgopherbot
authored andcommitted
runtime/metrics: add metric for total goroutines created
For golang#15490. Change-Id: Ic587dda1f42d613ea131a6b53ce6ba6e6cadf4c7 Reviewed-on: https://go-review.googlesource.com/c/go/+/690398 Reviewed-by: Michael Pratt <[email protected]> Auto-Submit: Michael Knyszek <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 13df972 commit ab8121a

File tree

6 files changed

+49
-5
lines changed

6 files changed

+49
-5
lines changed

src/runtime/metrics.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,13 @@ func initMetrics() {
500500
out.scalar = uint64(in.schedStats.gWaiting)
501501
},
502502
},
503+
"/sched/goroutines-created:goroutines": {
504+
deps: makeStatDepSet(schedStatsDep),
505+
compute: func(in *statAggregate, out *metricValue) {
506+
out.kind = metricKindUint64
507+
out.scalar = uint64(in.schedStats.gCreated)
508+
},
509+
},
503510
"/sched/latencies:seconds": {
504511
compute: func(_ *statAggregate, out *metricValue) {
505512
sched.timeToRun.write(out)
@@ -779,6 +786,7 @@ type schedStatsAggregate struct {
779786
gRunnable uint64
780787
gNonGo uint64
781788
gWaiting uint64
789+
gCreated uint64
782790
}
783791

784792
// compute populates the schedStatsAggregate with values from the runtime.
@@ -790,10 +798,12 @@ func (a *schedStatsAggregate) compute() {
790798
lock(&sched.lock)
791799

792800
// Collect running/runnable from per-P run queues.
801+
a.gCreated += sched.goroutinesCreated.Load()
793802
for _, p := range allp {
794803
if p == nil || p.status == _Pdead {
795804
break
796805
}
806+
a.gCreated += p.goroutinesCreated
797807
switch p.status {
798808
case _Prunning:
799809
a.gRunning++

src/runtime/metrics/description.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,12 @@ var allDesc = []Description{
437437
Description: "The current runtime.GOMAXPROCS setting, or the number of operating system threads that can execute user-level Go code simultaneously.",
438438
Kind: KindUint64,
439439
},
440+
{
441+
Name: "/sched/goroutines-created:goroutines",
442+
Description: "Count of goroutines created since program start.",
443+
Cumulative: true,
444+
Kind: KindUint64,
445+
},
440446
{
441447
Name: "/sched/goroutines/not-in-go:goroutines",
442448
Description: "Approximate count of goroutines running or blocked in a system call or cgo call. Not guaranteed to add up to /sched/goroutines:goroutines with other goroutine metrics.",

src/runtime/metrics/doc.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,9 @@ Below is the full list of supported metrics, ordered lexicographically.
509509
operating system threads that can execute user-level Go code
510510
simultaneously.
511511
512+
/sched/goroutines-created:goroutines
513+
Count of goroutines created since program start.
514+
512515
/sched/goroutines/not-in-go:goroutines
513516
Approximate count of goroutines running or blocked in
514517
a system call or cgo call. Not guaranteed to add up to

src/runtime/metrics_test.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,12 +1583,14 @@ func TestReadMetricsSched(t *testing.T) {
15831583
runnable
15841584
running
15851585
waiting
1586+
created
15861587
)
1587-
var s [4]metrics.Sample
1588-
s[0].Name = "/sched/goroutines/not-in-go:goroutines"
1589-
s[1].Name = "/sched/goroutines/runnable:goroutines"
1590-
s[2].Name = "/sched/goroutines/running:goroutines"
1591-
s[3].Name = "/sched/goroutines/waiting:goroutines"
1588+
var s [5]metrics.Sample
1589+
s[notInGo].Name = "/sched/goroutines/not-in-go:goroutines"
1590+
s[runnable].Name = "/sched/goroutines/runnable:goroutines"
1591+
s[running].Name = "/sched/goroutines/running:goroutines"
1592+
s[waiting].Name = "/sched/goroutines/waiting:goroutines"
1593+
s[created].Name = "/sched/goroutines-created:goroutines"
15921594

15931595
logMetrics := func(t *testing.T, s []metrics.Sample) {
15941596
for i := range s {
@@ -1645,6 +1647,9 @@ func TestReadMetricsSched(t *testing.T) {
16451647
check(t, &s[waiting], 0, waitingSlack)
16461648
})
16471649

1650+
metrics.Read(s[:])
1651+
createdAfterBase := s[created].Value.Uint64()
1652+
16481653
// Force Running count to be high. We'll use these goroutines
16491654
// for Runnable, too.
16501655
const count = 10
@@ -1673,6 +1678,11 @@ func TestReadMetricsSched(t *testing.T) {
16731678
// of runnable goroutines all spinning. We cannot write anything
16741679
// out.
16751680
if testenv.HasParallelism() {
1681+
t.Run("created", func(t *testing.T) {
1682+
metrics.Read(s[:])
1683+
logMetrics(t, s[:])
1684+
checkEq(t, &s[created], createdAfterBase+count)
1685+
})
16761686
t.Run("running", func(t *testing.T) {
16771687
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(count + 4))
16781688
// It can take a little bit for the scheduler to
@@ -1706,6 +1716,11 @@ func TestReadMetricsSched(t *testing.T) {
17061716
exit.Store(1)
17071717

17081718
// Now we can check our invariants.
1719+
t.Run("created", func(t *testing.T) {
1720+
// Look for count-1 goroutines because we read metrics
1721+
// *before* t.Run goroutine was created for this sub-test.
1722+
checkEq(t, &s[created], createdAfterBase+count-1)
1723+
})
17091724
t.Run("running", func(t *testing.T) {
17101725
logMetrics(t, s[:])
17111726
checkEq(t, &s[running], 1)

src/runtime/proc.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5262,6 +5262,7 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr, parked bool, waitreaso
52625262
racereleasemergeg(newg, unsafe.Pointer(&labelSync))
52635263
}
52645264
}
5265+
pp.goroutinesCreated++
52655266
releasem(mp)
52665267

52675268
return newg
@@ -5841,6 +5842,8 @@ func (pp *p) destroy() {
58415842
pp.gcAssistTime = 0
58425843
gcCleanups.queued += pp.cleanupsQueued
58435844
pp.cleanupsQueued = 0
5845+
sched.goroutinesCreated.Add(int64(pp.goroutinesCreated))
5846+
pp.goroutinesCreated = 0
58445847
pp.xRegs.free()
58455848
pp.status = _Pdead
58465849
}

src/runtime/runtime2.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,9 @@ type p struct {
764764
// gcStopTime is the nanotime timestamp that this P last entered _Pgcstop.
765765
gcStopTime int64
766766

767+
// goroutinesCreated is the total count of goroutines created by this P.
768+
goroutinesCreated uint64
769+
767770
// xRegs is the per-P extended register state used by asynchronous
768771
// preemption. This is an empty struct on platforms that don't use extended
769772
// register state.
@@ -892,6 +895,10 @@ type schedt struct {
892895
// M, but waiting for locks within the runtime. This field stores the value
893896
// for Ms that have exited.
894897
totalRuntimeLockWaitTime atomic.Int64
898+
899+
// goroutinesCreated (plus the value of goroutinesCreated on each P in allp)
900+
// is the sum of all goroutines created by the program.
901+
goroutinesCreated atomic.Uint64
895902
}
896903

897904
// Values for the flags field of a sigTabT.

0 commit comments

Comments
 (0)