Skip to content

Commit 86101b0

Browse files
neildgopherbot
authored andcommitted
runtime: print stack traces for bubbled goroutines on synctest deadlock
When synctest.Run panics due to every goroutine in the bubble being blocked, print a stack trace for every goroutine in the bubble. For #67434 Change-Id: Ie751c2ee6fa136930b18f4bee0277ff30da46905 Reviewed-on: https://go-review.googlesource.com/c/go/+/645719 Auto-Submit: Damien Neil <[email protected]> Reviewed-by: Michael Pratt <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent ab2a92d commit 86101b0

File tree

3 files changed

+37
-8
lines changed

3 files changed

+37
-8
lines changed

src/runtime/panic.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,7 @@ func fatalthrow(t throwType) {
12721272

12731273
startpanic_m()
12741274

1275-
if dopanic_m(gp, pc, sp) {
1275+
if dopanic_m(gp, pc, sp, nil) {
12761276
// crash uses a decent amount of nosplit stack and we're already
12771277
// low on stack in throw, so crash on the system stack (unlike
12781278
// fatalpanic).
@@ -1310,7 +1310,14 @@ func fatalpanic(msgs *_panic) {
13101310
printpanics(msgs)
13111311
}
13121312

1313-
docrash = dopanic_m(gp, pc, sp)
1313+
// If this panic is the result of a synctest bubble deadlock,
1314+
// print stacks for the goroutines in the bubble.
1315+
var sg *synctestGroup
1316+
if de, ok := msgs.arg.(synctestDeadlockError); ok {
1317+
sg = de.sg
1318+
}
1319+
1320+
docrash = dopanic_m(gp, pc, sp, sg)
13141321
})
13151322

13161323
if docrash {
@@ -1392,7 +1399,8 @@ var deadlock mutex
13921399

13931400
// gp is the crashing g running on this M, but may be a user G, while getg() is
13941401
// always g0.
1395-
func dopanic_m(gp *g, pc, sp uintptr) bool {
1402+
// If sg is non-nil, print the stacks for goroutines in this group as well.
1403+
func dopanic_m(gp *g, pc, sp uintptr, sg *synctestGroup) bool {
13961404
if gp.sig != 0 {
13971405
signame := signame(gp.sig)
13981406
if signame != "" {
@@ -1416,10 +1424,19 @@ func dopanic_m(gp *g, pc, sp uintptr) bool {
14161424
print("\nruntime stack:\n")
14171425
traceback(pc, sp, 0, gp)
14181426
}
1419-
if !didothers && all {
1420-
didothers = true
1421-
tracebackothers(gp)
1427+
if !didothers {
1428+
if all {
1429+
didothers = true
1430+
tracebackothers(gp)
1431+
} else if sg != nil {
1432+
// This panic is caused by a synctest bubble deadlock.
1433+
// Print stacks for goroutines in the deadlocked bubble.
1434+
tracebacksomeothers(gp, func(other *g) bool {
1435+
return sg == other.syncGroup
1436+
})
1437+
}
14221438
}
1439+
14231440
}
14241441
unlock(&paniclk)
14251442

src/runtime/synctest.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ func synctestRun(f func()) {
220220
raceacquireg(gp, gp.syncGroup.raceaddr())
221221
}
222222
if total != 1 {
223-
panic("deadlock: all goroutines in bubble are blocked")
223+
panic(synctestDeadlockError{sg})
224224
}
225225
if gp.timer != nil && gp.timer.isFake {
226226
// Verify that we haven't marked this goroutine's sleep timer as fake.
@@ -229,6 +229,14 @@ func synctestRun(f func()) {
229229
}
230230
}
231231

232+
type synctestDeadlockError struct {
233+
sg *synctestGroup
234+
}
235+
236+
func (synctestDeadlockError) Error() string {
237+
return "deadlock: all goroutines in bubble are blocked"
238+
}
239+
232240
func synctestidle_c(gp *g, _ unsafe.Pointer) bool {
233241
lock(&gp.syncGroup.mu)
234242
canIdle := true

src/runtime/traceback.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,10 @@ func goroutineheader(gp *g) {
12611261
}
12621262

12631263
func tracebackothers(me *g) {
1264+
tracebacksomeothers(me, func(*g) bool { return true })
1265+
}
1266+
1267+
func tracebacksomeothers(me *g, showf func(*g) bool) {
12641268
level, _, _ := gotraceback()
12651269

12661270
// Show the current goroutine first, if we haven't already.
@@ -1279,7 +1283,7 @@ func tracebackothers(me *g) {
12791283
// against concurrent creation of new Gs, but even with allglock we may
12801284
// miss Gs created after this loop.
12811285
forEachGRace(func(gp *g) {
1282-
if gp == me || gp == curgp || readgstatus(gp) == _Gdead || isSystemGoroutine(gp, false) && level < 2 {
1286+
if gp == me || gp == curgp || readgstatus(gp) == _Gdead || !showf(gp) || (isSystemGoroutine(gp, false) && level < 2) {
12831287
return
12841288
}
12851289
print("\n")

0 commit comments

Comments
 (0)