1
0
mirror of https://github.com/golang/go synced 2024-11-18 18:04:46 -07:00

runtime: never show system goroutines in traceback

Fixes #9791

g.issystem flag setup races with other code wherever we set it.
Even if we set both in parent goroutine and in the system goroutine,
it is still possible that some other goroutine crashes
before the flag is set. We could pass issystem flag to newproc1,
but we start all goroutines with go nowadays.

Instead look at g.startpc to distinguish system goroutines (similar to topofstack).

Change-Id: Ia3467968dee27fa07d9fecedd4c2b00928f26645
Reviewed-on: https://go-review.googlesource.com/4113
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Dmitry Vyukov 2015-02-07 15:31:18 +03:00
parent 84e2567537
commit 59495e8dfd
9 changed files with 61 additions and 14 deletions

View File

@ -10,6 +10,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp"
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
@ -17,17 +18,20 @@ import (
"text/template" "text/template"
) )
// testEnv excludes GODEBUG from the environment
// to prevent its output from breaking tests that
// are trying to parse other command output.
func testEnv(cmd *exec.Cmd) *exec.Cmd { func testEnv(cmd *exec.Cmd) *exec.Cmd {
if cmd.Env != nil { if cmd.Env != nil {
panic("environment already set") panic("environment already set")
} }
for _, env := range os.Environ() { for _, env := range os.Environ() {
// Exclude GODEBUG from the environment to prevent its output
// from breaking tests that are trying to parse other command output.
if strings.HasPrefix(env, "GODEBUG=") { if strings.HasPrefix(env, "GODEBUG=") {
continue continue
} }
// Exclude GOTRACEBACK for the same reason.
if strings.HasPrefix(env, "GOTRACEBACK=") {
continue
}
cmd.Env = append(cmd.Env, env) cmd.Env = append(cmd.Env, env)
} }
return cmd return cmd
@ -217,6 +221,14 @@ func TestMainGoroutineId(t *testing.T) {
} }
} }
func TestNoHelperGoroutines(t *testing.T) {
output := executeTest(t, noHelperGoroutinesSource, nil)
matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
t.Fatalf("want to see only goroutine 1, see:\n%s", output)
}
}
func TestBreakpoint(t *testing.T) { func TestBreakpoint(t *testing.T) {
output := executeTest(t, breakpointSource, nil) output := executeTest(t, breakpointSource, nil)
want := "runtime.Breakpoint()" want := "runtime.Breakpoint()"
@ -431,6 +443,22 @@ func main() {
} }
` `
const noHelperGoroutinesSource = `
package main
import (
"runtime"
"time"
)
func init() {
i := 0
runtime.SetFinalizer(&i, func(p *int) {})
time.AfterFunc(time.Hour, func() {})
panic("oops")
}
func main() {
}
`
const breakpointSource = ` const breakpointSource = `
package main package main
import "runtime" import "runtime"

View File

@ -344,7 +344,7 @@ func dumpgoroutine(gp *g) {
dumpint(uint64(gp.goid)) dumpint(uint64(gp.goid))
dumpint(uint64(gp.gopc)) dumpint(uint64(gp.gopc))
dumpint(uint64(readgstatus(gp))) dumpint(uint64(readgstatus(gp)))
dumpbool(gp.issystem) dumpbool(isSystemGoroutine(gp))
dumpbool(false) // isbackground dumpbool(false) // isbackground
dumpint(uint64(gp.waitsince)) dumpint(uint64(gp.waitsince))
dumpstr(gp.waitreason) dumpstr(gp.waitreason)

View File

@ -102,7 +102,10 @@ func wakefing() *g {
return res return res
} }
var fingCreate uint32 var (
fingCreate uint32
fingRunning bool
)
func createfing() { func createfing() {
// start the finalizer goroutine exactly once // start the finalizer goroutine exactly once
@ -126,9 +129,7 @@ func runfinq() {
gp := getg() gp := getg()
fing = gp fing = gp
fingwait = true fingwait = true
gp.issystem = true
goparkunlock(&finlock, "finalizer wait", traceEvGoBlock) goparkunlock(&finlock, "finalizer wait", traceEvGoBlock)
gp.issystem = false
continue continue
} }
unlock(&finlock) unlock(&finlock)
@ -169,7 +170,9 @@ func runfinq() {
default: default:
throw("bad kind in runfinq") throw("bad kind in runfinq")
} }
fingRunning = true
reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz)) reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz))
fingRunning = false
// drop finalizer queue references to finalized object // drop finalizer queue references to finalized object
f.fn = nil f.fn = nil

View File

@ -62,7 +62,6 @@ func clearpools() {
// bggc holds the state of the backgroundgc. // bggc holds the state of the backgroundgc.
func backgroundgc() { func backgroundgc() {
bggc.g = getg() bggc.g = getg()
bggc.g.issystem = true
for { for {
gcwork(0) gcwork(0)
lock(&bggc.lock) lock(&bggc.lock)
@ -73,7 +72,6 @@ func backgroundgc() {
func bgsweep() { func bgsweep() {
sweep.g = getg() sweep.g = getg()
getg().issystem = true
for { for {
for gosweepone() != ^uintptr(0) { for gosweepone() != ^uintptr(0) {
sweep.nbgsweep++ sweep.nbgsweep++

View File

@ -110,7 +110,6 @@ func init() {
func forcegchelper() { func forcegchelper() {
forcegc.g = getg() forcegc.g = getg()
forcegc.g.issystem = true
for { for {
lock(&forcegc.lock) lock(&forcegc.lock)
if forcegc.idle != 0 { if forcegc.idle != 0 {

View File

@ -2636,7 +2636,7 @@ func checkdead() {
lock(&allglock) lock(&allglock)
for i := 0; i < len(allgs); i++ { for i := 0; i < len(allgs); i++ {
gp := allgs[i] gp := allgs[i]
if gp.issystem { if isSystemGoroutine(gp) {
continue continue
} }
s := readgstatus(gp) s := readgstatus(gp)

View File

@ -199,7 +199,6 @@ type g struct {
waitsince int64 // approx time when the g become blocked waitsince int64 // approx time when the g become blocked
waitreason string // if status==gwaiting waitreason string // if status==gwaiting
schedlink *g schedlink *g
issystem bool // do not output in stack dump, ignore in deadlock detector
preempt bool // preemption signal, duplicates stackguard0 = stackpreempt preempt bool // preemption signal, duplicates stackguard0 = stackpreempt
paniconfault bool // panic (instead of crash) on unexpected fault address paniconfault bool // panic (instead of crash) on unexpected fault address
preemptscan bool // preempted g does scan for gc preemptscan bool // preempted g does scan for gc

View File

@ -153,7 +153,6 @@ func deltimer(t *timer) bool {
// If addtimer inserts a new earlier event, addtimer1 wakes timerproc early. // If addtimer inserts a new earlier event, addtimer1 wakes timerproc early.
func timerproc() { func timerproc() {
timers.gp = getg() timers.gp = getg()
timers.gp.issystem = true
for { for {
lock(&timers.lock) lock(&timers.lock)
timers.sleeping = false timers.sleeping = false

View File

@ -39,6 +39,11 @@ var (
mstartPC uintptr mstartPC uintptr
rt0_goPC uintptr rt0_goPC uintptr
sigpanicPC uintptr sigpanicPC uintptr
runfinqPC uintptr
backgroundgcPC uintptr
bgsweepPC uintptr
forcegchelperPC uintptr
timerprocPC uintptr
systemstack_switchPC uintptr systemstack_switchPC uintptr
externalthreadhandlerp uintptr // initialized elsewhere externalthreadhandlerp uintptr // initialized elsewhere
@ -56,6 +61,11 @@ func tracebackinit() {
mstartPC = funcPC(mstart) mstartPC = funcPC(mstart)
rt0_goPC = funcPC(rt0_go) rt0_goPC = funcPC(rt0_go)
sigpanicPC = funcPC(sigpanic) sigpanicPC = funcPC(sigpanic)
runfinqPC = funcPC(runfinq)
backgroundgcPC = funcPC(backgroundgc)
bgsweepPC = funcPC(bgsweep)
forcegchelperPC = funcPC(forcegchelper)
timerprocPC = funcPC(timerproc)
systemstack_switchPC = funcPC(systemstack_switch) systemstack_switchPC = funcPC(systemstack_switch)
} }
@ -606,7 +616,7 @@ func tracebackothers(me *g) {
lock(&allglock) lock(&allglock)
for _, gp := range allgs { for _, gp := range allgs {
if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || gp.issystem && level < 2 { if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || isSystemGoroutine(gp) && level < 2 {
continue continue
} }
print("\n") print("\n")
@ -631,3 +641,14 @@ func topofstack(f *_func) bool {
pc == rt0_goPC || pc == rt0_goPC ||
externalthreadhandlerp != 0 && pc == externalthreadhandlerp externalthreadhandlerp != 0 && pc == externalthreadhandlerp
} }
// isSystemGoroutine returns true if the goroutine g must be omitted in
// stack dumps and deadlock detector.
func isSystemGoroutine(gp *g) bool {
pc := gp.startpc
return pc == runfinqPC && !fingRunning ||
pc == backgroundgcPC ||
pc == bgsweepPC ||
pc == forcegchelperPC ||
pc == timerprocPC
}