mirror of
https://github.com/golang/go
synced 2024-11-17 10:04:43 -07:00
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 <mpratt@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
29bbca5c2c
commit
4289bd365c
@ -93,8 +93,12 @@ messages are prefixed with "runtime:".
|
||||
For unrecoverable errors where user code is expected to be at fault for the
|
||||
failure (such as racing map writes), use `fatal`.
|
||||
|
||||
For runtime error debugging, it's useful to run with
|
||||
`GOTRACEBACK=system` or `GOTRACEBACK=crash`.
|
||||
For runtime error debugging, it may be useful to run with `GOTRACEBACK=system`
|
||||
or `GOTRACEBACK=crash`. The output of `panic` and `fatal` is as described by
|
||||
`GOTRACEBACK`. The output of `throw` always includes runtime frames, metadata
|
||||
and all goroutines regardless of `GOTRACEBACK` (i.e., equivalent to
|
||||
`GOTRACEBACK=system). Whether `throw` crashes or not is still controlled by
|
||||
`GOTRACEBACK`.
|
||||
|
||||
Synchronization
|
||||
===============
|
||||
|
@ -120,7 +120,7 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int {
|
||||
return _NCONT
|
||||
}
|
||||
Throw:
|
||||
_g_.m.throwing = 1
|
||||
_g_.m.throwing = throwTypeRuntime
|
||||
_g_.m.caughtsig.set(gp)
|
||||
startpanic_m()
|
||||
print(notestr, "\n")
|
||||
|
@ -11,6 +11,28 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// throwType indicates the current type of ongoing throw, which affects the
|
||||
// amount of detail printed to stderr. Higher values include more detail.
|
||||
type throwType uint32
|
||||
|
||||
const (
|
||||
// throwTypeNone means that we are not throwing.
|
||||
throwTypeNone throwType = iota
|
||||
|
||||
// throwTypeUser is a throw due to a problem with the application.
|
||||
//
|
||||
// These throws do not include runtime frames, system goroutines, or
|
||||
// frame metadata.
|
||||
throwTypeUser
|
||||
|
||||
// throwTypeRuntime is a throw due to a problem with Go itself.
|
||||
//
|
||||
// These throws include as much information as possible to aid in
|
||||
// debugging the runtime, including runtime frames, system goroutines,
|
||||
// and frame metadata.
|
||||
throwTypeRuntime
|
||||
)
|
||||
|
||||
// We have two different ways of doing defers. The older way involves creating a
|
||||
// defer record at the time that a defer statement is executing and adding it to a
|
||||
// defer chain. This chain is inspected by the deferreturn call at all function
|
||||
@ -1003,13 +1025,16 @@ func throw(s string) {
|
||||
print("fatal error: ", s, "\n")
|
||||
})
|
||||
|
||||
fatalthrow()
|
||||
fatalthrow(throwTypeRuntime)
|
||||
}
|
||||
|
||||
// fatal triggers a fatal error that dumps a stack trace and exits.
|
||||
//
|
||||
// fatal is equivalent to throw, but is used when user code is expected to be
|
||||
// at fault for the failure, such as racing map writes.
|
||||
//
|
||||
// fatal does not include runtime frames, system goroutines, or frame metadata
|
||||
// (fp, sp, pc) in the stack trace unless GOTRACEBACK=system or higher.
|
||||
//go:nosplit
|
||||
func fatal(s string) {
|
||||
// Everything fatal does should be recursively nosplit so it
|
||||
@ -1018,7 +1043,7 @@ func fatal(s string) {
|
||||
print("fatal error: ", s, "\n")
|
||||
})
|
||||
|
||||
fatalthrow()
|
||||
fatalthrow(throwTypeUser)
|
||||
}
|
||||
|
||||
// runningPanicDefers is non-zero while running deferred functions for panic.
|
||||
@ -1063,13 +1088,13 @@ func recovery(gp *g) {
|
||||
// process.
|
||||
//
|
||||
//go:nosplit
|
||||
func fatalthrow() {
|
||||
func fatalthrow(t throwType) {
|
||||
pc := getcallerpc()
|
||||
sp := getcallersp()
|
||||
gp := getg()
|
||||
|
||||
if gp.m.throwing == 0 {
|
||||
gp.m.throwing = 1
|
||||
if gp.m.throwing == throwTypeNone {
|
||||
gp.m.throwing = t
|
||||
}
|
||||
|
||||
// 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 {
|
||||
print("\n")
|
||||
goroutineheader(gp)
|
||||
traceback(pc, sp, 0, gp)
|
||||
} else if level >= 2 || _g_.m.throwing > 0 {
|
||||
} else if level >= 2 || _g_.m.throwing >= throwTypeRuntime {
|
||||
print("\nruntime stack:\n")
|
||||
traceback(pc, sp, 0, gp)
|
||||
}
|
||||
@ -1258,7 +1283,7 @@ func canpanic(gp *g) bool {
|
||||
if gp == nil || gp != mp.curg {
|
||||
return false
|
||||
}
|
||||
if mp.locks != 0 || mp.mallocing != 0 || mp.throwing != 0 || mp.preemptoff != "" || mp.dying != 0 {
|
||||
if mp.locks != 0 || mp.mallocing != 0 || mp.throwing != throwTypeNone || mp.preemptoff != "" || mp.dying != 0 {
|
||||
return false
|
||||
}
|
||||
status := readgstatus(gp)
|
||||
|
@ -4088,7 +4088,6 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr) *g {
|
||||
_g_ := getg()
|
||||
|
||||
if fn == nil {
|
||||
_g_.m.throwing = -1 // do not dump full stacks
|
||||
fatal("go of nil func value")
|
||||
}
|
||||
acquirem() // disable preemption because it can be holding p in a local var
|
||||
@ -5045,7 +5044,6 @@ func checkdead() {
|
||||
}
|
||||
}
|
||||
|
||||
getg().m.throwing = -1 // do not dump full stacks
|
||||
unlock(&sched.lock) // unlock so that GODEBUG=scheddetail=1 doesn't hang
|
||||
fatal("all goroutines are asleep - deadlock!")
|
||||
}
|
||||
|
@ -38,9 +38,13 @@ func gotraceback() (level int32, all, crash bool) {
|
||||
_g_ := getg()
|
||||
t := atomic.Load(&traceback_cache)
|
||||
crash = t&tracebackCrash != 0
|
||||
all = _g_.m.throwing > 0 || t&tracebackAll != 0
|
||||
all = _g_.m.throwing >= throwTypeUser || t&tracebackAll != 0
|
||||
if _g_.m.traceback != 0 {
|
||||
level = int32(_g_.m.traceback)
|
||||
} else if _g_.m.throwing >= throwTypeRuntime {
|
||||
// Always include runtime frames in runtime throws unless
|
||||
// otherwise overridden by m.traceback.
|
||||
level = 2
|
||||
} else {
|
||||
level = int32(t >> tracebackShift)
|
||||
}
|
||||
|
@ -532,7 +532,7 @@ type m struct {
|
||||
oldp puintptr // the p that was attached before executing a syscall
|
||||
id int64
|
||||
mallocing int32
|
||||
throwing int32
|
||||
throwing throwType
|
||||
preemptoff string // if != "", keep curg running on this m
|
||||
locks int32
|
||||
dying int32
|
||||
|
@ -698,7 +698,7 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
|
||||
return
|
||||
}
|
||||
|
||||
_g_.m.throwing = 1
|
||||
_g_.m.throwing = throwTypeRuntime
|
||||
_g_.m.caughtsig.set(gp)
|
||||
|
||||
if crashing == 0 {
|
||||
|
@ -226,7 +226,7 @@ func winthrow(info *exceptionrecord, r *context, gp *g) {
|
||||
}
|
||||
print("\n")
|
||||
|
||||
_g_.m.throwing = 1
|
||||
_g_.m.throwing = throwTypeRuntime
|
||||
_g_.m.caughtsig.set(gp)
|
||||
|
||||
level, _, docrash := gotraceback()
|
||||
|
@ -447,7 +447,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
|
||||
if frame.pc > f.entry() {
|
||||
print(" +", hex(frame.pc-f.entry()))
|
||||
}
|
||||
if gp.m != nil && gp.m.throwing > 0 && gp == gp.m.curg || level >= 2 {
|
||||
if gp.m != nil && gp.m.throwing >= throwTypeRuntime && gp == gp.m.curg || level >= 2 {
|
||||
print(" fp=", hex(frame.fp), " sp=", hex(frame.sp), " pc=", hex(frame.pc))
|
||||
}
|
||||
print("\n")
|
||||
@ -913,7 +913,7 @@ func gcallers(gp *g, skip int, pcbuf []uintptr) int {
|
||||
// be printed during a traceback.
|
||||
func showframe(f funcInfo, gp *g, firstFrame bool, funcID, childID funcID) bool {
|
||||
g := getg()
|
||||
if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) {
|
||||
if g.m.throwing >= throwTypeRuntime && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) {
|
||||
return true
|
||||
}
|
||||
return showfuncinfo(f, firstFrame, funcID, childID)
|
||||
|
Loading…
Reference in New Issue
Block a user