Skip to content

Commit c5bafc8

Browse files
committed
runtime: make NumGoroutine and Stack agree not to include system goroutines
Before, NumGoroutine counted system goroutines and Stack (usually) didn't show them, which was inconsistent and confusing. To resolve which way they should be consistent, it seems like package main import "runtime" func main() { println(runtime.NumGoroutine()) } should print 1 regardless of internal runtime details. Make it so. Fixes golang#11706. Change-Id: I6bfe26a901de517728192cfb26a5568c4ef4fe47 Reviewed-on: https://go-review.googlesource.com/18343 Reviewed-by: Austin Clements <[email protected]>
1 parent 20d745c commit c5bafc8

5 files changed

Lines changed: 47 additions & 1 deletion

File tree

src/runtime/mprof.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,12 +576,17 @@ func Stack(buf []byte, all bool) int {
576576
pc := getcallerpc(unsafe.Pointer(&buf))
577577
systemstack(func() {
578578
g0 := getg()
579+
// Force traceback=1 to override GOTRACEBACK setting,
580+
// so that Stack's results are consistent.
581+
// GOTRACEBACK is only about crash dumps.
582+
g0.m.traceback = 1
579583
g0.writebuf = buf[0:0:len(buf)]
580584
goroutineheader(gp)
581585
traceback(pc, sp, 0, gp)
582586
if all {
583587
tracebackothers(gp)
584588
}
589+
g0.m.traceback = 0
585590
n = len(g0.writebuf)
586591
g0.writebuf = nil
587592
})

src/runtime/proc.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2162,6 +2162,9 @@ func goexit0(gp *g) {
21622162
_g_ := getg()
21632163

21642164
casgstatus(gp, _Grunning, _Gdead)
2165+
if isSystemGoroutine(gp) {
2166+
atomic.Xadd(&sched.ngsys, -1)
2167+
}
21652168
gp.m = nil
21662169
gp.lockedm = nil
21672170
_g_.m.lockedg = nil
@@ -2693,6 +2696,9 @@ func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr
26932696
gostartcallfn(&newg.sched, fn)
26942697
newg.gopc = callerpc
26952698
newg.startpc = fn.fn
2699+
if isSystemGoroutine(newg) {
2700+
atomic.Xadd(&sched.ngsys, +1)
2701+
}
26962702
casgstatus(newg, _Gdead, _Grunnable)
26972703

26982704
if _p_.goidcache == _p_.goidcacheend {
@@ -2885,7 +2891,7 @@ func badunlockosthread() {
28852891
}
28862892

28872893
func gcount() int32 {
2888-
n := int32(allglen) - sched.ngfree
2894+
n := int32(allglen) - sched.ngfree - int32(atomic.Load(&sched.ngsys))
28892895
for i := 0; ; i++ {
28902896
_p_ := allp[i]
28912897
if _p_ == nil {

src/runtime/proc_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net"
1010
"runtime"
1111
"runtime/debug"
12+
"strings"
1213
"sync"
1314
"sync/atomic"
1415
"syscall"
@@ -336,6 +337,23 @@ func TestGCFairness(t *testing.T) {
336337
}
337338
}
338339

340+
func TestNumGoroutine(t *testing.T) {
341+
output := runTestProg(t, "testprog", "NumGoroutine")
342+
want := "1\n"
343+
if output != want {
344+
t.Fatalf("want %q, got %q", want, output)
345+
}
346+
347+
buf := make([]byte, 1<<20)
348+
buf = buf[:runtime.Stack(buf, true)]
349+
350+
n := runtime.NumGoroutine()
351+
352+
if nstk := strings.Count(string(buf), "goroutine "); n != nstk {
353+
t.Fatalf("NumGoroutine=%d, but found %d goroutines in stack dump", n, nstk)
354+
}
355+
}
356+
339357
func TestPingPongHog(t *testing.T) {
340358
if testing.Short() {
341359
t.Skip("skipping in -short mode")

src/runtime/runtime2.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,8 @@ type schedt struct {
418418
mcount int32 // number of m's that have been created
419419
maxmcount int32 // maximum number of m's allowed (or die)
420420

421+
ngsys uint32 // number of system goroutines; updated atomically
422+
421423
pidle puintptr // idle p's
422424
npidle uint32
423425
nmspinning uint32 // See "Worker thread parking/unparking" comment in proc.go.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2016 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import "runtime"
8+
9+
func init() {
10+
register("NumGoroutine", NumGoroutine)
11+
}
12+
13+
func NumGoroutine() {
14+
println(runtime.NumGoroutine())
15+
}

0 commit comments

Comments
 (0)