default:
print("runtime: unexpected G.status ", hex(status), "\n")
throw("dumpgs in STW - bad status")
- case _Gdead:
+ case _Gdead, _Gdeadextra:
// ok
case _Grunnable,
_Gsyscall,
default:
print("runtime: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n")
throw("mark - bad status")
- case _Gdead:
+ case _Gdead, _Gdeadextra:
return 0
case _Grunning:
print("runtime: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n")
// were collecting the profile. But probably better to return a
// truncated profile than to crash the whole process.
//
- // For instance, needm moves a goroutine out of the _Gdead state and so
+ // For instance, needm moves a goroutine out of the _Gdeadextra state and so
// might be able to change the goroutine count without interacting with
// the scheduler. For code like that, the race windows are small and the
// combination of features is uncommon, so it's hard to be (and remain)
// in the current goroutine profile: either that it should not be profiled, or
// that a snapshot of its call stack and labels are now in the profile.
func tryRecordGoroutineProfile(gp1 *g, pcbuf []uintptr, yield func()) {
- if readgstatus(gp1) == _Gdead {
+ if status := readgstatus(gp1); status == _Gdead || status == _Gdeadextra {
// Dead goroutines should not appear in the profile. Goroutines that
// start while profile collection is active will get goroutineProfiled
// set to goroutineProfileSatisfied before transitioning out of _Gdead,
isOK := func(gp1 *g) bool {
// Checking isSystemGoroutine here makes GoroutineProfile
// consistent with both NumGoroutine and Stack.
- return gp1 != gp && readgstatus(gp1) != _Gdead && !isSystemGoroutine(gp1, false)
+ if gp1 == gp {
+ return false
+ }
+ if status := readgstatus(gp1); status == _Gdead || status == _Gdeadextra {
+ return false
+ }
+ if isSystemGoroutine(gp1, false) {
+ return false
+ }
+ return true
}
pcbuf := makeProfStack() // see saveg() for explanation
dumpgstatus(gp)
throw("invalid g status")
- case _Gdead:
+ case _Gdead, _Gdeadextra:
// Nothing to suspend.
//
// preemptStop may need to be cleared, but
_Gscanrunning,
_Gscansyscall,
_Gscanleaked,
- _Gscanpreempted:
+ _Gscanpreempted,
+ _Gscandeadextra:
if newval == oldval&^_Gscan {
success = gp.atomicstatus.CompareAndSwap(oldval, newval)
}
_Grunning,
_Gwaiting,
_Gleaked,
- _Gsyscall:
+ _Gsyscall,
+ _Gdeadextra:
if newval == oldval|_Gscan {
r := gp.atomicstatus.CompareAndSwap(oldval, newval)
if r {
}
// mp.curg is now a real goroutine.
- casgstatus(mp.curg, _Gdead, _Gsyscall)
+ casgstatus(mp.curg, _Gdeadextra, _Gsyscall)
sched.ngsys.Add(-1)
sched.nGsyscallNoP.Add(1)
gp.syscallpc = gp.sched.pc
gp.syscallsp = gp.sched.sp
gp.stktopsp = gp.sched.sp
- // malg returns status as _Gidle. Change to _Gdead before
- // adding to allg where GC can see it. We use _Gdead to hide
- // this from tracebacks and stack scans since it isn't a
- // "real" goroutine until needm grabs it.
- casgstatus(gp, _Gidle, _Gdead)
+ // malg returns status as _Gidle. Change to _Gdeadextra before
+ // adding to allg where GC can see it. _Gdeadextra hides this
+ // from traceback and stack scans.
+ casgstatus(gp, _Gidle, _Gdeadextra)
gp.m = mp
mp.curg = gp
mp.isextra = true
trace = traceAcquire()
}
- // Return mp.curg to dead state.
- casgstatus(mp.curg, _Gsyscall, _Gdead)
+ // Return mp.curg to _Gdeadextra state.
+ casgstatus(mp.curg, _Gsyscall, _Gdeadextra)
mp.curg.preemptStop = false
sched.ngsys.Add(1)
sched.nGsyscallNoP.Add(-1)
G_DEAD = read_runtime_const("'runtime._Gdead'", 6)
G_ENQUEUE_UNUSED = read_runtime_const("'runtime._Genqueue_unused'", 7)
G_COPYSTACK = read_runtime_const("'runtime._Gcopystack'", 8)
+G_DEADEXTRA = read_runtime_const("'runtime._Gdeadextra'", 10)
G_SCAN = read_runtime_const("'runtime._Gscan'", 0x1000)
G_SCANRUNNABLE = G_SCAN+G_RUNNABLE
G_SCANRUNNING = G_SCAN+G_RUNNING
G_SCANSYSCALL = G_SCAN+G_SYSCALL
G_SCANWAITING = G_SCAN+G_WAITING
+G_SCANEXTRA = G_SCAN+G_DEADEXTRA
sts = {
G_IDLE: 'idle',
G_DEAD: 'dead',
G_ENQUEUE_UNUSED: 'enqueue',
G_COPYSTACK: 'copystack',
+ G_DEADEXTRA: 'extra',
G_SCAN: 'scan',
G_SCANRUNNABLE: 'runnable+s',
G_SCANRUNNING: 'running+s',
G_SCANSYSCALL: 'syscall+s',
G_SCANWAITING: 'waiting+s',
+ G_SCANEXTRA: 'extra+s',
}
# args = gdb.string_to_argv(arg)
vp = gdb.lookup_type('void').pointer()
for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")):
- if ptr['atomicstatus']['value'] == G_DEAD:
+ if ptr['atomicstatus']['value'] in [G_DEAD, G_DEADEXTRA]:
continue
s = ' '
if ptr['m']:
"""
vp = gdb.lookup_type('void').pointer()
for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")):
- if ptr['atomicstatus']['value'] == G_DEAD:
+ if ptr['atomicstatus']['value'] in [G_DEAD, G_DEADEXTRA]:
continue
if ptr['goid'] == goid:
break
// _Gleaked represents a leaked goroutine caught by the GC.
_Gleaked // 10
+ // _Gdeadextra is a _Gdead goroutine that's attached to an extra M
+ // used for cgo callbacks.
+ _Gdeadextra // 11
+
// _Gscan combined with one of the above states other than
// _Grunning indicates that GC is scanning the stack. The
// goroutine is not executing user code and the stack is owned
_Gscanwaiting = _Gscan + _Gwaiting // 0x1004
_Gscanpreempted = _Gscan + _Gpreempted // 0x1009
_Gscanleaked = _Gscan + _Gleaked // 0x100a
+ _Gscandeadextra = _Gscan + _Gdeadextra // 0x100b
)
const (
totalDelta := 0
wasRunning := true
switch oldval {
- case _Gdead:
+ case _Gdead, _Gdeadextra:
wasRunning = false
totalDelta++
case _Gwaiting:
}
isRunning := true
switch newval {
- case _Gdead:
+ case _Gdead, _Gdeadextra:
isRunning = false
totalDelta--
if gp == bubble.main {
bubble.running++
} else {
bubble.running--
- if raceenabled && newval != _Gdead {
+ if raceenabled && newval != _Gdead && newval != _Gdeadextra {
// Record that this goroutine parking happens before
// any subsequent Wait.
racereleasemergeg(gp, bubble.raceaddr())
_Gcopystack: "copystack",
_Gleaked: "leaked",
_Gpreempted: "preempted",
+ _Gdeadextra: "waiting for cgo callback",
}
func goroutineheader(gp *g) {
// against concurrent creation of new Gs, but even with allglock we may
// miss Gs created after this loop.
forEachGRace(func(gp *g) {
- if gp == me || gp == curgp || readgstatus(gp) == _Gdead || !showf(gp) || (isSystemGoroutine(gp, false) && level < 2) {
+ if gp == me || gp == curgp {
+ return
+ }
+ if status := readgstatus(gp); status == _Gdead || status == _Gdeadextra {
+ return
+ }
+ if !showf(gp) {
+ return
+ }
+ if isSystemGoroutine(gp, false) && level < 2 {
return
}
print("\n")
if status == _Gwaiting && wr.isWaitingForSuspendG() {
tgs = tracev2.GoRunning
}
- case _Gdead:
+ case _Gdead, _Gdeadextra:
throw("tried to trace dead goroutine")
default:
throw("tried to trace goroutine with invalid or unsupported status")