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:
parent
84e2567537
commit
59495e8dfd
@ -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"
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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++
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user