Skip to content

Commit 4289bd3

Browse files
committed
runtime: simply user throws, expand runtime throws
This gives explicit names to the possible states of throwing (-1, 0, 1). m.throwing is now one of: throwTypeOff: not throwing, previously == 0 throwTypeUser: user throw, previously == -1 throwTypeRuntime: runtime throw, previously == 1 For runtime throws, we now always include frame metadata and system goroutines regardless of GOTRACEBACK to aid in debugging the runtime. For user throws, we no longer include frame metadata or runtime frames, unless GOTRACEBACK=system or higher. For #51485. Change-Id: If252e2377a0b6385ce7756b937929be4273a56c0 Reviewed-on: https://go-review.googlesource.com/c/go/+/390421 Run-TryBot: Michael Pratt <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> Reviewed-by: Austin Clements <[email protected]>
1 parent 29bbca5 commit 4289bd3

File tree

9 files changed

+49
-18
lines changed

9 files changed

+49
-18
lines changed

src/runtime/HACKING.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,12 @@ messages are prefixed with "runtime:".
9393
For unrecoverable errors where user code is expected to be at fault for the
9494
failure (such as racing map writes), use `fatal`.
9595

96-
For runtime error debugging, it's useful to run with
97-
`GOTRACEBACK=system` or `GOTRACEBACK=crash`.
96+
For runtime error debugging, it may be useful to run with `GOTRACEBACK=system`
97+
or `GOTRACEBACK=crash`. The output of `panic` and `fatal` is as described by
98+
`GOTRACEBACK`. The output of `throw` always includes runtime frames, metadata
99+
and all goroutines regardless of `GOTRACEBACK` (i.e., equivalent to
100+
`GOTRACEBACK=system). Whether `throw` crashes or not is still controlled by
101+
`GOTRACEBACK`.
98102

99103
Synchronization
100104
===============

src/runtime/os3_plan9.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int {
120120
return _NCONT
121121
}
122122
Throw:
123-
_g_.m.throwing = 1
123+
_g_.m.throwing = throwTypeRuntime
124124
_g_.m.caughtsig.set(gp)
125125
startpanic_m()
126126
print(notestr, "\n")

src/runtime/panic.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,28 @@ import (
1111
"unsafe"
1212
)
1313

14+
// throwType indicates the current type of ongoing throw, which affects the
15+
// amount of detail printed to stderr. Higher values include more detail.
16+
type throwType uint32
17+
18+
const (
19+
// throwTypeNone means that we are not throwing.
20+
throwTypeNone throwType = iota
21+
22+
// throwTypeUser is a throw due to a problem with the application.
23+
//
24+
// These throws do not include runtime frames, system goroutines, or
25+
// frame metadata.
26+
throwTypeUser
27+
28+
// throwTypeRuntime is a throw due to a problem with Go itself.
29+
//
30+
// These throws include as much information as possible to aid in
31+
// debugging the runtime, including runtime frames, system goroutines,
32+
// and frame metadata.
33+
throwTypeRuntime
34+
)
35+
1436
// We have two different ways of doing defers. The older way involves creating a
1537
// defer record at the time that a defer statement is executing and adding it to a
1638
// defer chain. This chain is inspected by the deferreturn call at all function
@@ -1003,13 +1025,16 @@ func throw(s string) {
10031025
print("fatal error: ", s, "\n")
10041026
})
10051027

1006-
fatalthrow()
1028+
fatalthrow(throwTypeRuntime)
10071029
}
10081030

10091031
// fatal triggers a fatal error that dumps a stack trace and exits.
10101032
//
10111033
// fatal is equivalent to throw, but is used when user code is expected to be
10121034
// at fault for the failure, such as racing map writes.
1035+
//
1036+
// fatal does not include runtime frames, system goroutines, or frame metadata
1037+
// (fp, sp, pc) in the stack trace unless GOTRACEBACK=system or higher.
10131038
//go:nosplit
10141039
func fatal(s string) {
10151040
// Everything fatal does should be recursively nosplit so it
@@ -1018,7 +1043,7 @@ func fatal(s string) {
10181043
print("fatal error: ", s, "\n")
10191044
})
10201045

1021-
fatalthrow()
1046+
fatalthrow(throwTypeUser)
10221047
}
10231048

10241049
// runningPanicDefers is non-zero while running deferred functions for panic.
@@ -1063,13 +1088,13 @@ func recovery(gp *g) {
10631088
// process.
10641089
//
10651090
//go:nosplit
1066-
func fatalthrow() {
1091+
func fatalthrow(t throwType) {
10671092
pc := getcallerpc()
10681093
sp := getcallersp()
10691094
gp := getg()
10701095

1071-
if gp.m.throwing == 0 {
1072-
gp.m.throwing = 1
1096+
if gp.m.throwing == throwTypeNone {
1097+
gp.m.throwing = t
10731098
}
10741099

10751100
// Switch to the system stack to avoid any stack growth, which may make
@@ -1216,7 +1241,7 @@ func dopanic_m(gp *g, pc, sp uintptr) bool {
12161241
print("\n")
12171242
goroutineheader(gp)
12181243
traceback(pc, sp, 0, gp)
1219-
} else if level >= 2 || _g_.m.throwing > 0 {
1244+
} else if level >= 2 || _g_.m.throwing >= throwTypeRuntime {
12201245
print("\nruntime stack:\n")
12211246
traceback(pc, sp, 0, gp)
12221247
}
@@ -1258,7 +1283,7 @@ func canpanic(gp *g) bool {
12581283
if gp == nil || gp != mp.curg {
12591284
return false
12601285
}
1261-
if mp.locks != 0 || mp.mallocing != 0 || mp.throwing != 0 || mp.preemptoff != "" || mp.dying != 0 {
1286+
if mp.locks != 0 || mp.mallocing != 0 || mp.throwing != throwTypeNone || mp.preemptoff != "" || mp.dying != 0 {
12621287
return false
12631288
}
12641289
status := readgstatus(gp)

src/runtime/proc.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4088,7 +4088,6 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr) *g {
40884088
_g_ := getg()
40894089

40904090
if fn == nil {
4091-
_g_.m.throwing = -1 // do not dump full stacks
40924091
fatal("go of nil func value")
40934092
}
40944093
acquirem() // disable preemption because it can be holding p in a local var
@@ -5045,7 +5044,6 @@ func checkdead() {
50455044
}
50465045
}
50475046

5048-
getg().m.throwing = -1 // do not dump full stacks
50495047
unlock(&sched.lock) // unlock so that GODEBUG=scheddetail=1 doesn't hang
50505048
fatal("all goroutines are asleep - deadlock!")
50515049
}

src/runtime/runtime1.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,13 @@ func gotraceback() (level int32, all, crash bool) {
3838
_g_ := getg()
3939
t := atomic.Load(&traceback_cache)
4040
crash = t&tracebackCrash != 0
41-
all = _g_.m.throwing > 0 || t&tracebackAll != 0
41+
all = _g_.m.throwing >= throwTypeUser || t&tracebackAll != 0
4242
if _g_.m.traceback != 0 {
4343
level = int32(_g_.m.traceback)
44+
} else if _g_.m.throwing >= throwTypeRuntime {
45+
// Always include runtime frames in runtime throws unless
46+
// otherwise overridden by m.traceback.
47+
level = 2
4448
} else {
4549
level = int32(t >> tracebackShift)
4650
}

src/runtime/runtime2.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ type m struct {
532532
oldp puintptr // the p that was attached before executing a syscall
533533
id int64
534534
mallocing int32
535-
throwing int32
535+
throwing throwType
536536
preemptoff string // if != "", keep curg running on this m
537537
locks int32
538538
dying int32

src/runtime/signal_unix.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,7 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
698698
return
699699
}
700700

701-
_g_.m.throwing = 1
701+
_g_.m.throwing = throwTypeRuntime
702702
_g_.m.caughtsig.set(gp)
703703

704704
if crashing == 0 {

src/runtime/signal_windows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ func winthrow(info *exceptionrecord, r *context, gp *g) {
226226
}
227227
print("\n")
228228

229-
_g_.m.throwing = 1
229+
_g_.m.throwing = throwTypeRuntime
230230
_g_.m.caughtsig.set(gp)
231231

232232
level, _, docrash := gotraceback()

src/runtime/traceback.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
447447
if frame.pc > f.entry() {
448448
print(" +", hex(frame.pc-f.entry()))
449449
}
450-
if gp.m != nil && gp.m.throwing > 0 && gp == gp.m.curg || level >= 2 {
450+
if gp.m != nil && gp.m.throwing >= throwTypeRuntime && gp == gp.m.curg || level >= 2 {
451451
print(" fp=", hex(frame.fp), " sp=", hex(frame.sp), " pc=", hex(frame.pc))
452452
}
453453
print("\n")
@@ -913,7 +913,7 @@ func gcallers(gp *g, skip int, pcbuf []uintptr) int {
913913
// be printed during a traceback.
914914
func showframe(f funcInfo, gp *g, firstFrame bool, funcID, childID funcID) bool {
915915
g := getg()
916-
if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) {
916+
if g.m.throwing >= throwTypeRuntime && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) {
917917
return true
918918
}
919919
return showfuncinfo(f, firstFrame, funcID, childID)

0 commit comments

Comments
 (0)