]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: make NumGoroutine and Stack agree not to include system goroutines
authorRuss Cox <rsc@golang.org>
Thu, 7 Jan 2016 02:16:01 +0000 (21:16 -0500)
committerRuss Cox <rsc@golang.org>
Fri, 8 Jan 2016 15:25:00 +0000 (15:25 +0000)
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/18343
Reviewed-by: Austin Clements <austin@google.com>
src/runtime/mprof.go
src/runtime/proc.go
src/runtime/proc_test.go
src/runtime/runtime2.go
src/runtime/testdata/testprog/misc.go [new file with mode: 0644]

index 684ab0b0551eef8ff7f92b599e971deb3622c6b0..eb7231aec22142d05abf28c80ce6367010b836ab 100644 (file)
@@ -576,12 +576,17 @@ func Stack(buf []byte, all bool) int {
                pc := getcallerpc(unsafe.Pointer(&buf))
                systemstack(func() {
                        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)]
                        goroutineheader(gp)
                        traceback(pc, sp, 0, gp)
                        if all {
                                tracebackothers(gp)
                        }
+                       g0.m.traceback = 0
                        n = len(g0.writebuf)
                        g0.writebuf = nil
                })
index 23429fd774adf040ed1a05c1a1914aabbd5bff04..d80b33e9c4104ff5d73c9dd6f8dbdba4b5f57811 100644 (file)
@@ -2162,6 +2162,9 @@ func goexit0(gp *g) {
        _g_ := getg()
 
        casgstatus(gp, _Grunning, _Gdead)
+       if isSystemGoroutine(gp) {
+               atomic.Xadd(&sched.ngsys, -1)
+       }
        gp.m = nil
        gp.lockedm = nil
        _g_.m.lockedg = nil
@@ -2693,6 +2696,9 @@ func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr
        gostartcallfn(&newg.sched, fn)
        newg.gopc = callerpc
        newg.startpc = fn.fn
+       if isSystemGoroutine(newg) {
+               atomic.Xadd(&sched.ngsys, +1)
+       }
        casgstatus(newg, _Gdead, _Grunnable)
 
        if _p_.goidcache == _p_.goidcacheend {
@@ -2885,7 +2891,7 @@ func badunlockosthread() {
 }
 
 func gcount() int32 {
-       n := int32(allglen) - sched.ngfree
+       n := int32(allglen) - sched.ngfree - int32(atomic.Load(&sched.ngsys))
        for i := 0; ; i++ {
                _p_ := allp[i]
                if _p_ == nil {
index 30798f723dc6ad5d7fa7fb494573f00efc6fd2c7..f3e90bcbd76b4ea7dc33dc38cbd466e92581cbb2 100644 (file)
@@ -9,6 +9,7 @@ import (
        "net"
        "runtime"
        "runtime/debug"
+       "strings"
        "sync"
        "sync/atomic"
        "syscall"
@@ -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) {
        if testing.Short() {
                t.Skip("skipping in -short mode")
index d9a449b68b45d72d38956406a737170bf482fa3e..a4ad749d2589b8c5a4ca797acb5dfc1e64ca280c 100644 (file)
@@ -418,6 +418,8 @@ type schedt struct {
        mcount       int32    // number of m's that have been created
        maxmcount    int32    // maximum number of m's allowed (or die)
 
+       ngsys uint32 // number of system goroutines; updated atomically
+
        pidle      puintptr // idle p's
        npidle     uint32
        nmspinning uint32 // See "Worker thread parking/unparking" comment in proc.go.
diff --git a/src/runtime/testdata/testprog/misc.go b/src/runtime/testdata/testprog/misc.go
new file mode 100644 (file)
index 0000000..237680f
--- /dev/null
@@ -0,0 +1,15 @@
+// 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())
+}