Commit c5bafc82 authored by Russ Cox's avatar Russ Cox

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 #11706.

Change-Id: I6bfe26a901de517728192cfb26a5568c4ef4fe47
Reviewed-on: https://go-review.googlesource.com/18343Reviewed-by: default avatarAustin Clements <austin@google.com>
parent 20d745c5
...@@ -576,12 +576,17 @@ func Stack(buf []byte, all bool) int { ...@@ -576,12 +576,17 @@ func Stack(buf []byte, all bool) int {
pc := getcallerpc(unsafe.Pointer(&buf)) pc := getcallerpc(unsafe.Pointer(&buf))
systemstack(func() { systemstack(func() {
g0 := getg() g0 := getg()
// Force traceback=1 to override GOTRACEBACK setting,
// so that Stack's results are consistent.
// GOTRACEBACK is only about crash dumps.
g0.m.traceback = 1
g0.writebuf = buf[0:0:len(buf)] g0.writebuf = buf[0:0:len(buf)]
goroutineheader(gp) goroutineheader(gp)
traceback(pc, sp, 0, gp) traceback(pc, sp, 0, gp)
if all { if all {
tracebackothers(gp) tracebackothers(gp)
} }
g0.m.traceback = 0
n = len(g0.writebuf) n = len(g0.writebuf)
g0.writebuf = nil g0.writebuf = nil
}) })
......
...@@ -2162,6 +2162,9 @@ func goexit0(gp *g) { ...@@ -2162,6 +2162,9 @@ func goexit0(gp *g) {
_g_ := getg() _g_ := getg()
casgstatus(gp, _Grunning, _Gdead) casgstatus(gp, _Grunning, _Gdead)
if isSystemGoroutine(gp) {
atomic.Xadd(&sched.ngsys, -1)
}
gp.m = nil gp.m = nil
gp.lockedm = nil gp.lockedm = nil
_g_.m.lockedg = nil _g_.m.lockedg = nil
...@@ -2693,6 +2696,9 @@ func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr ...@@ -2693,6 +2696,9 @@ func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr
gostartcallfn(&newg.sched, fn) gostartcallfn(&newg.sched, fn)
newg.gopc = callerpc newg.gopc = callerpc
newg.startpc = fn.fn newg.startpc = fn.fn
if isSystemGoroutine(newg) {
atomic.Xadd(&sched.ngsys, +1)
}
casgstatus(newg, _Gdead, _Grunnable) casgstatus(newg, _Gdead, _Grunnable)
if _p_.goidcache == _p_.goidcacheend { if _p_.goidcache == _p_.goidcacheend {
...@@ -2885,7 +2891,7 @@ func badunlockosthread() { ...@@ -2885,7 +2891,7 @@ func badunlockosthread() {
} }
func gcount() int32 { func gcount() int32 {
n := int32(allglen) - sched.ngfree n := int32(allglen) - sched.ngfree - int32(atomic.Load(&sched.ngsys))
for i := 0; ; i++ { for i := 0; ; i++ {
_p_ := allp[i] _p_ := allp[i]
if _p_ == nil { if _p_ == nil {
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"net" "net"
"runtime" "runtime"
"runtime/debug" "runtime/debug"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
...@@ -336,6 +337,23 @@ func TestGCFairness(t *testing.T) { ...@@ -336,6 +337,23 @@ func TestGCFairness(t *testing.T) {
} }
} }
func TestNumGoroutine(t *testing.T) {
output := runTestProg(t, "testprog", "NumGoroutine")
want := "1\n"
if output != want {
t.Fatalf("want %q, got %q", want, output)
}
buf := make([]byte, 1<<20)
buf = buf[:runtime.Stack(buf, true)]
n := runtime.NumGoroutine()
if nstk := strings.Count(string(buf), "goroutine "); n != nstk {
t.Fatalf("NumGoroutine=%d, but found %d goroutines in stack dump", n, nstk)
}
}
func TestPingPongHog(t *testing.T) { func TestPingPongHog(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("skipping in -short mode") t.Skip("skipping in -short mode")
......
...@@ -418,6 +418,8 @@ type schedt struct { ...@@ -418,6 +418,8 @@ type schedt struct {
mcount int32 // number of m's that have been created mcount int32 // number of m's that have been created
maxmcount int32 // maximum number of m's allowed (or die) maxmcount int32 // maximum number of m's allowed (or die)
ngsys uint32 // number of system goroutines; updated atomically
pidle puintptr // idle p's pidle puintptr // idle p's
npidle uint32 npidle uint32
nmspinning uint32 // See "Worker thread parking/unparking" comment in proc.go. nmspinning uint32 // See "Worker thread parking/unparking" comment in proc.go.
......
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import "runtime"
func init() {
register("NumGoroutine", NumGoroutine)
}
func NumGoroutine() {
println(runtime.NumGoroutine())
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment