startpanic_m()
- if dopanic_m(gp, pc, sp) {
+ if dopanic_m(gp, pc, sp, nil) {
// crash uses a decent amount of nosplit stack and we're already
// low on stack in throw, so crash on the system stack (unlike
// fatalpanic).
printpanics(msgs)
}
- docrash = dopanic_m(gp, pc, sp)
+ // If this panic is the result of a synctest bubble deadlock,
+ // print stacks for the goroutines in the bubble.
+ var sg *synctestGroup
+ if de, ok := msgs.arg.(synctestDeadlockError); ok {
+ sg = de.sg
+ }
+
+ docrash = dopanic_m(gp, pc, sp, sg)
})
if docrash {
// gp is the crashing g running on this M, but may be a user G, while getg() is
// always g0.
-func dopanic_m(gp *g, pc, sp uintptr) bool {
+// If sg is non-nil, print the stacks for goroutines in this group as well.
+func dopanic_m(gp *g, pc, sp uintptr, sg *synctestGroup) bool {
if gp.sig != 0 {
signame := signame(gp.sig)
if signame != "" {
print("\nruntime stack:\n")
traceback(pc, sp, 0, gp)
}
- if !didothers && all {
- didothers = true
- tracebackothers(gp)
+ if !didothers {
+ if all {
+ didothers = true
+ tracebackothers(gp)
+ } else if sg != nil {
+ // This panic is caused by a synctest bubble deadlock.
+ // Print stacks for goroutines in the deadlocked bubble.
+ tracebacksomeothers(gp, func(other *g) bool {
+ return sg == other.syncGroup
+ })
+ }
}
+
}
unlock(&paniclk)
raceacquireg(gp, gp.syncGroup.raceaddr())
}
if total != 1 {
- panic("deadlock: all goroutines in bubble are blocked")
+ panic(synctestDeadlockError{sg})
}
if gp.timer != nil && gp.timer.isFake {
// Verify that we haven't marked this goroutine's sleep timer as fake.
}
}
+type synctestDeadlockError struct {
+ sg *synctestGroup
+}
+
+func (synctestDeadlockError) Error() string {
+ return "deadlock: all goroutines in bubble are blocked"
+}
+
func synctestidle_c(gp *g, _ unsafe.Pointer) bool {
lock(&gp.syncGroup.mu)
canIdle := true
}
func tracebackothers(me *g) {
+ tracebacksomeothers(me, func(*g) bool { return true })
+}
+
+func tracebacksomeothers(me *g, showf func(*g) bool) {
level, _, _ := gotraceback()
// Show the current goroutine first, if we haven't already.
// 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 || isSystemGoroutine(gp, false) && level < 2 {
+ if gp == me || gp == curgp || readgstatus(gp) == _Gdead || !showf(gp) || (isSystemGoroutine(gp, false) && level < 2) {
return
}
print("\n")