Commit 3619ab8b by Ian Lance Taylor

runtime: fix isSystemGoroutine for gccgo

    
    The gc toolchain decides whether a goroutine is a system goroutine by
    comparing startpc to a list of saved special PCs.  In gccgo that
    approach does not work as startpc is often a thunk that invokes the
    real function with arguments, so the thunk address never matches the
    saved special PCs.
    
    This patch fixes gccgo's understanding of system goroutines.  Since
    there are only a limited number of them, we simply change each one to
    mark itself as special.
    
    This fixes stack dumps and functions like runtime.NumGoroutine to
    behave more like gc.  It also fixes the goprint test in the gc
    testsuite.
    
    Reviewed-on: https://go-review.googlesource.com/43156

From-SVN: r247931
parent 628c06d6
822ab419bf7d1c705cdce1c12133e7a11f56be2e 619848ccd463ac385e9912df008e7e8e6301a284
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -100,8 +100,7 @@ func wakefing() *g { ...@@ -100,8 +100,7 @@ func wakefing() *g {
} }
var ( var (
fingCreate uint32 fingCreate uint32
fingRunning bool
) )
func createfing() { func createfing() {
...@@ -113,17 +112,19 @@ func createfing() { ...@@ -113,17 +112,19 @@ func createfing() {
// This is the goroutine that runs all of the finalizers // This is the goroutine that runs all of the finalizers
func runfinq() { func runfinq() {
setSystemGoroutine()
var ( var (
ef eface ef eface
ifac iface ifac iface
) )
gp := getg()
for { for {
lock(&finlock) lock(&finlock)
fb := finq fb := finq
finq = nil finq = nil
if fb == nil { if fb == nil {
gp := getg()
fing = gp fing = gp
fingwait = true fingwait = true
goparkunlock(&finlock, "finalizer wait", traceEvGoBlock, 1) goparkunlock(&finlock, "finalizer wait", traceEvGoBlock, 1)
...@@ -160,9 +161,17 @@ func runfinq() { ...@@ -160,9 +161,17 @@ func runfinq() {
default: default:
throw("bad kind in runfinq") throw("bad kind in runfinq")
} }
fingRunning = true // This is not a system goroutine while
// running the actual finalizer.
// This matters because we want this
// goroutine to appear in a stack dump
// if the finalizer crashes.
// The gc toolchain handles this using
// a global variable fingRunning,
// but we don't need that.
gp.isSystemGoroutine = false
reflectcall(f.ft, f.fn, false, false, &param, nil) reflectcall(f.ft, f.fn, false, false, &param, nil)
fingRunning = false gp.isSystemGoroutine = true
// Drop finalizer queue heap references // Drop finalizer queue heap references
// before hiding them from markroot. // before hiding them from markroot.
......
...@@ -1423,6 +1423,8 @@ func gcBgMarkPrepare() { ...@@ -1423,6 +1423,8 @@ func gcBgMarkPrepare() {
} }
func gcBgMarkWorker(_p_ *p) { func gcBgMarkWorker(_p_ *p) {
setSystemGoroutine()
gp := getg() gp := getg()
type parkInfo struct { type parkInfo struct {
......
...@@ -48,6 +48,8 @@ func finishsweep_m() { ...@@ -48,6 +48,8 @@ func finishsweep_m() {
} }
func bgsweep(c chan int) { func bgsweep(c chan int) {
setSystemGoroutine()
sweep.g = getg() sweep.g = getg()
lock(&sweep.lock) lock(&sweep.lock)
......
...@@ -237,6 +237,8 @@ func init() { ...@@ -237,6 +237,8 @@ func init() {
} }
func forcegchelper() { func forcegchelper() {
setSystemGoroutine()
forcegc.g = getg() forcegc.g = getg()
for { for {
lock(&forcegc.lock) lock(&forcegc.lock)
...@@ -450,7 +452,6 @@ func schedinit() { ...@@ -450,7 +452,6 @@ func schedinit() {
sched.maxmcount = 10000 sched.maxmcount = 10000
tracebackinit()
mallocinit() mallocinit()
mcommoninit(_g_.m) mcommoninit(_g_.m)
alginit() // maps must not be used before this call alginit() // maps must not be used before this call
...@@ -2688,9 +2689,6 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g { ...@@ -2688,9 +2689,6 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g {
newg.param = arg newg.param = arg
newg.gopc = getcallerpc(unsafe.Pointer(&fn)) newg.gopc = getcallerpc(unsafe.Pointer(&fn))
newg.startpc = fn newg.startpc = fn
if isSystemGoroutine(newg) {
atomic.Xadd(&sched.ngsys, +1)
}
// The stack is dirty from the argument frame, so queue it for // The stack is dirty from the argument frame, so queue it for
// scanning. Do this before setting it to runnable so we still // scanning. Do this before setting it to runnable so we still
// own the G. If we're recycling a G, it may already be on the // own the G. If we're recycling a G, it may already be on the
...@@ -2729,6 +2727,18 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g { ...@@ -2729,6 +2727,18 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g {
return newg return newg
} }
// setSystemGoroutine marks this goroutine as a "system goroutine".
// In the gc toolchain this is done by comparing startpc to a list of
// saved special PCs. In gccgo that approach does not work as startpc
// is often a thunk that invokes the real function with arguments,
// so the thunk address never matches the saved special PCs. Instead,
// since there are only a limited number of "system goroutines",
// we force each one to mark itself as special.
func setSystemGoroutine() {
getg().isSystemGoroutine = true
atomic.Xadd(&sched.ngsys, +1)
}
// Put on gfree list. // Put on gfree list.
// If local list is too long, transfer a batch to the global list. // If local list is too long, transfer a batch to the global list.
func gfput(_p_ *p, gp *g) { func gfput(_p_ *p, gp *g) {
......
...@@ -415,6 +415,8 @@ type g struct { ...@@ -415,6 +415,8 @@ type g struct {
scanningself bool // whether goroutine is scanning its own stack scanningself bool // whether goroutine is scanning its own stack
isSystemGoroutine bool // whether goroutine is a "system" goroutine
traceback *tracebackg // stack traceback buffer traceback *tracebackg // stack traceback buffer
context g_ucontext_t // saved context for setcontext context g_ucontext_t // saved context for setcontext
......
...@@ -152,6 +152,8 @@ func deltimer(t *timer) bool { ...@@ -152,6 +152,8 @@ func deltimer(t *timer) bool {
// It sleeps until the next event in the timers heap. // It sleeps until the next event in the timers heap.
// If addtimer inserts a new earlier event, it wakes timerproc early. // If addtimer inserts a new earlier event, it wakes timerproc early.
func timerproc() { func timerproc() {
setSystemGoroutine()
timers.gp = getg() timers.gp = getg()
for { for {
lock(&timers.lock) lock(&timers.lock)
......
...@@ -9,7 +9,7 @@ package runtime ...@@ -9,7 +9,7 @@ package runtime
import ( import (
"runtime/internal/sys" "runtime/internal/sys"
"unsafe" _ "unsafe" // for go:linkname
) )
// For gccgo, use go:linkname to rename compiler-called functions to // For gccgo, use go:linkname to rename compiler-called functions to
...@@ -20,34 +20,6 @@ import ( ...@@ -20,34 +20,6 @@ import (
//go:linkname goroutineheader runtime.goroutineheader //go:linkname goroutineheader runtime.goroutineheader
//go:linkname printcreatedby runtime.printcreatedby //go:linkname printcreatedby runtime.printcreatedby
var (
// initialized in tracebackinit
runfinqPC uintptr
bgsweepPC uintptr
forcegchelperPC uintptr
timerprocPC uintptr
gcBgMarkWorkerPC uintptr
)
func tracebackinit() {
// Go variable initialization happens late during runtime startup.
// Instead of initializing the variables above in the declarations,
// schedinit calls this function so that the variables are
// initialized and available earlier in the startup sequence.
// This doesn't use funcPC to avoid memory allocation.
// FIXME: We should be able to use funcPC when escape analysis is on.
f1 := runfinq
runfinqPC = **(**uintptr)(unsafe.Pointer(&f1))
f2 := bgsweep
bgsweepPC = **(**uintptr)(unsafe.Pointer(&f2))
f3 := forcegchelper
forcegchelperPC = **(**uintptr)(unsafe.Pointer(&f3))
f4 := timerproc
timerprocPC = **(**uintptr)(unsafe.Pointer(&f4))
f5 := gcBgMarkWorker
gcBgMarkWorkerPC = **(**uintptr)(unsafe.Pointer(&f5))
}
func printcreatedby(gp *g) { func printcreatedby(gp *g) {
// Show what created goroutine, except main goroutine (goid 1). // Show what created goroutine, except main goroutine (goid 1).
pc := gp.gopc pc := gp.gopc
...@@ -196,15 +168,7 @@ func goroutineheader(gp *g) { ...@@ -196,15 +168,7 @@ func goroutineheader(gp *g) {
// isSystemGoroutine reports whether the goroutine g must be omitted in // isSystemGoroutine reports whether the goroutine g must be omitted in
// stack dumps and deadlock detector. // stack dumps and deadlock detector.
func isSystemGoroutine(gp *g) bool { func isSystemGoroutine(gp *g) bool {
// FIXME: This doesn't work reliably for gccgo because in many return gp.isSystemGoroutine
// cases the startpc field will be set to a thunk rather than
// to one of these addresses.
pc := gp.startpc
return pc == runfinqPC && !fingRunning ||
pc == bgsweepPC ||
pc == forcegchelperPC ||
pc == timerprocPC ||
pc == gcBgMarkWorkerPC
} }
func tracebackothers(me *g) { func tracebackothers(me *g) {
......
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