mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: introduce GOTRACEBACK=single, now the default
Abandon (but still support) the old numbering system. GOTRACEBACK=none is old 0 GOTRACEBACK=single is the new behavior GOTRACEBACK=all is old 1 GOTRACEBACK=system is old 2 GOTRACEBACK=crash is unchanged See doc comment change in runtime1.go for details. Filed #13107 to decide whether to change default back to GOTRACEBACK=all for Go 1.6 release. If you run into programs where printing only the current goroutine omits needed information, please add details in a comment on that issue. Fixes #12366. Change-Id: I82ca8b99b5d86dceb3f7102d38d2659d45dbe0db Reviewed-on: https://go-review.googlesource.com/16512 Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
845878a213
commit
bf1de1b141
11 changed files with 78 additions and 55 deletions
|
|
@ -106,15 +106,20 @@ the limit.
|
||||||
|
|
||||||
The GOTRACEBACK variable controls the amount of output generated when a Go
|
The GOTRACEBACK variable controls the amount of output generated when a Go
|
||||||
program fails due to an unrecovered panic or an unexpected runtime condition.
|
program fails due to an unrecovered panic or an unexpected runtime condition.
|
||||||
By default, a failure prints a stack trace for every extant goroutine, eliding functions
|
By default, a failure prints a stack trace for the current goroutine,
|
||||||
internal to the run-time system, and then exits with exit code 2.
|
eliding functions internal to the run-time system, and then exits with exit code 2.
|
||||||
If GOTRACEBACK=0, the per-goroutine stack traces are omitted entirely.
|
The failure prints stack traces for all goroutines if there is no current goroutine
|
||||||
If GOTRACEBACK=1, the default behavior is used.
|
or the failure is internal to the run-time.
|
||||||
If GOTRACEBACK=2, the per-goroutine stack traces include run-time functions.
|
GOTRACEBACK=none omits the goroutine stack traces entirely.
|
||||||
If GOTRACEBACK=crash, the per-goroutine stack traces include run-time functions,
|
GOTRACEBACK=single (the default) behaves as described above.
|
||||||
and if possible the program crashes in an operating-specific manner instead of
|
GOTRACEBACK=all adds stack traces for all user-created goroutines.
|
||||||
exiting. For example, on Unix systems, the program raises SIGABRT to trigger a
|
GOTRACEBACK=system is like ``all'' but adds stack frames for run-time functions
|
||||||
core dump.
|
and shows goroutines created internally by the run-time.
|
||||||
|
GOTRACEBACK=crash is like ``system'' but crashes in an operating system-specific
|
||||||
|
manner instead of exiting. For example, on Unix systems, the crash raises
|
||||||
|
SIGABRT to trigger a core dump.
|
||||||
|
For historical reasons, the GOTRACEBACK settings 0, 1, and 2 are synonyms for
|
||||||
|
none, all, and system, respectively.
|
||||||
|
|
||||||
The GOARCH, GOOS, GOPATH, and GOROOT environment variables complete
|
The GOARCH, GOOS, GOPATH, and GOROOT environment variables complete
|
||||||
the set of Go environment variables. They influence the building of Go programs
|
the set of Go environment variables. They influence the building of Go programs
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int {
|
||||||
var docrash bool
|
var docrash bool
|
||||||
var sig int
|
var sig int
|
||||||
var flags int
|
var flags int
|
||||||
|
var level int32
|
||||||
|
|
||||||
c := &sigctxt{_ureg}
|
c := &sigctxt{_ureg}
|
||||||
notestr := gostringnocopy(note)
|
notestr := gostringnocopy(note)
|
||||||
|
|
@ -97,7 +98,8 @@ Throw:
|
||||||
print(notestr, "\n")
|
print(notestr, "\n")
|
||||||
print("PC=", hex(c.pc()), "\n")
|
print("PC=", hex(c.pc()), "\n")
|
||||||
print("\n")
|
print("\n")
|
||||||
if gotraceback(&docrash) > 0 {
|
level, _, docrash = gotraceback()
|
||||||
|
if level > 0 {
|
||||||
goroutineheader(gp)
|
goroutineheader(gp)
|
||||||
tracebacktrap(c.pc(), c.sp(), 0, gp)
|
tracebacktrap(c.pc(), c.sp(), 0, gp)
|
||||||
tracebackothers(gp)
|
tracebackothers(gp)
|
||||||
|
|
|
||||||
|
|
@ -605,18 +605,21 @@ func dopanic_m(gp *g, pc, sp uintptr) {
|
||||||
print("[signal ", hex(gp.sig), " code=", hex(gp.sigcode0), " addr=", hex(gp.sigcode1), " pc=", hex(gp.sigpc), "]\n")
|
print("[signal ", hex(gp.sig), " code=", hex(gp.sigcode0), " addr=", hex(gp.sigcode1), " pc=", hex(gp.sigpc), "]\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
var docrash bool
|
level, all, docrash := gotraceback()
|
||||||
_g_ := getg()
|
_g_ := getg()
|
||||||
if t := gotraceback(&docrash); t > 0 {
|
if level > 0 {
|
||||||
|
if gp != gp.m.curg {
|
||||||
|
all = true
|
||||||
|
}
|
||||||
if gp != gp.m.g0 {
|
if gp != gp.m.g0 {
|
||||||
print("\n")
|
print("\n")
|
||||||
goroutineheader(gp)
|
goroutineheader(gp)
|
||||||
traceback(pc, sp, 0, gp)
|
traceback(pc, sp, 0, gp)
|
||||||
} else if t >= 2 || _g_.m.throwing > 0 {
|
} else if level >= 2 || _g_.m.throwing > 0 {
|
||||||
print("\nruntime stack:\n")
|
print("\nruntime stack:\n")
|
||||||
traceback(pc, sp, 0, gp)
|
traceback(pc, sp, 0, gp)
|
||||||
}
|
}
|
||||||
if !didothers {
|
if !didothers && all {
|
||||||
didothers = true
|
didothers = true
|
||||||
tracebackothers(gp)
|
tracebackothers(gp)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,30 +8,37 @@ import "unsafe"
|
||||||
|
|
||||||
// Keep a cached value to make gotraceback fast,
|
// Keep a cached value to make gotraceback fast,
|
||||||
// since we call it on every call to gentraceback.
|
// since we call it on every call to gentraceback.
|
||||||
// The cached value is a uint32 in which the low bit
|
// The cached value is a uint32 in which the low bits
|
||||||
// is the "crash" setting and the top 31 bits are the
|
// are the "crash" and "all" settings and the remaining
|
||||||
// gotraceback value.
|
// bits are the traceback value (0 off, 1 on, 2 include system).
|
||||||
var traceback_cache uint32 = 2 << 1
|
const (
|
||||||
|
tracebackCrash = 1 << iota
|
||||||
|
tracebackAll
|
||||||
|
tracebackShift = iota
|
||||||
|
)
|
||||||
|
|
||||||
// The GOTRACEBACK environment variable controls the
|
var traceback_cache uint32 = 2 << tracebackShift
|
||||||
// behavior of a Go program that is crashing and exiting.
|
|
||||||
// GOTRACEBACK=0 suppress all tracebacks
|
// gotraceback returns the current traceback settings.
|
||||||
// GOTRACEBACK=1 default behavior - show tracebacks but exclude runtime frames
|
//
|
||||||
// GOTRACEBACK=2 show tracebacks including runtime frames
|
// If level is 0, suppress all tracebacks.
|
||||||
// GOTRACEBACK=crash show tracebacks including runtime frames, then crash (core dump etc)
|
// If level is 1, show tracebacks, but exclude runtime frames.
|
||||||
|
// If level is 2, show tracebacks including runtime frames.
|
||||||
|
// If all is set, print all goroutine stacks. Otherwise, print just the current goroutine.
|
||||||
|
// If crash is set, crash (core dump, etc) after tracebacking.
|
||||||
|
//
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
func gotraceback(crash *bool) int32 {
|
func gotraceback() (level int32, all, crash bool) {
|
||||||
_g_ := getg()
|
_g_ := getg()
|
||||||
if crash != nil {
|
all = _g_.m.throwing > 0
|
||||||
*crash = false
|
|
||||||
}
|
|
||||||
if _g_.m.traceback != 0 {
|
if _g_.m.traceback != 0 {
|
||||||
return int32(_g_.m.traceback)
|
level = int32(_g_.m.traceback)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if crash != nil {
|
crash = traceback_cache&tracebackCrash != 0
|
||||||
*crash = traceback_cache&1 != 0
|
all = all || traceback_cache&tracebackAll != 0
|
||||||
}
|
level = int32(traceback_cache >> tracebackShift)
|
||||||
return int32(traceback_cache >> 1)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -365,17 +372,23 @@ func parsedebugvars() {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch p := gogetenv("GOTRACEBACK"); p {
|
switch p := gogetenv("GOTRACEBACK"); p {
|
||||||
case "":
|
case "none":
|
||||||
traceback_cache = 1 << 1
|
traceback_cache = 0
|
||||||
|
case "single", "":
|
||||||
|
traceback_cache = 1 << tracebackShift
|
||||||
|
case "all":
|
||||||
|
traceback_cache = 1<<tracebackShift | tracebackAll
|
||||||
|
case "system":
|
||||||
|
traceback_cache = 2<<tracebackShift | tracebackAll
|
||||||
case "crash":
|
case "crash":
|
||||||
traceback_cache = 2<<1 | 1
|
traceback_cache = 2<<tracebackShift | tracebackAll | tracebackCrash
|
||||||
default:
|
default:
|
||||||
traceback_cache = uint32(atoi(p)) << 1
|
traceback_cache = uint32(atoi(p))<<tracebackShift | tracebackAll
|
||||||
}
|
}
|
||||||
// when C owns the process, simply exit'ing the process on fatal errors
|
// when C owns the process, simply exit'ing the process on fatal errors
|
||||||
// and panics is surprising. Be louder and abort instead.
|
// and panics is surprising. Be louder and abort instead.
|
||||||
if islibrary || isarchive {
|
if islibrary || isarchive {
|
||||||
traceback_cache |= 1
|
traceback_cache |= tracebackCrash
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug.gcstackbarrierall > 0 {
|
if debug.gcstackbarrierall > 0 {
|
||||||
|
|
|
||||||
|
|
@ -131,8 +131,8 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
|
||||||
}
|
}
|
||||||
print("\n")
|
print("\n")
|
||||||
|
|
||||||
var docrash bool
|
level, _, docrash := gotraceback()
|
||||||
if gotraceback(&docrash) > 0 {
|
if level > 0 {
|
||||||
goroutineheader(gp)
|
goroutineheader(gp)
|
||||||
|
|
||||||
// On Linux/386, all system calls go through the vdso kernel_vsyscall routine.
|
// On Linux/386, all system calls go through the vdso kernel_vsyscall routine.
|
||||||
|
|
|
||||||
|
|
@ -165,8 +165,8 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
|
||||||
}
|
}
|
||||||
print("\n")
|
print("\n")
|
||||||
|
|
||||||
var docrash bool
|
level, _, docrash := gotraceback()
|
||||||
if gotraceback(&docrash) > 0 {
|
if level > 0 {
|
||||||
goroutineheader(gp)
|
goroutineheader(gp)
|
||||||
tracebacktrap(uintptr(c.rip()), uintptr(c.rsp()), 0, gp)
|
tracebacktrap(uintptr(c.rip()), uintptr(c.rsp()), 0, gp)
|
||||||
if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
|
if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
|
||||||
|
|
|
||||||
|
|
@ -126,8 +126,8 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
|
||||||
}
|
}
|
||||||
print("\n")
|
print("\n")
|
||||||
|
|
||||||
var docrash bool
|
level, _, docrash := gotraceback()
|
||||||
if gotraceback(&docrash) > 0 {
|
if level > 0 {
|
||||||
goroutineheader(gp)
|
goroutineheader(gp)
|
||||||
tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.lr()), gp)
|
tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.lr()), gp)
|
||||||
if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
|
if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
|
||||||
|
|
|
||||||
|
|
@ -139,8 +139,8 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
|
||||||
}
|
}
|
||||||
print("\n")
|
print("\n")
|
||||||
|
|
||||||
var docrash bool
|
level, _, docrash := gotraceback()
|
||||||
if gotraceback(&docrash) > 0 {
|
if level > 0 {
|
||||||
goroutineheader(gp)
|
goroutineheader(gp)
|
||||||
tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.lr()), gp)
|
tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.lr()), gp)
|
||||||
if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
|
if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
|
||||||
|
|
|
||||||
|
|
@ -144,8 +144,8 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
|
||||||
}
|
}
|
||||||
print("\n")
|
print("\n")
|
||||||
|
|
||||||
var docrash bool
|
level, _, docrash := gotraceback()
|
||||||
if gotraceback(&docrash) > 0 {
|
if level > 0 {
|
||||||
goroutineheader(gp)
|
goroutineheader(gp)
|
||||||
tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp)
|
tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp)
|
||||||
if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
|
if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
|
||||||
|
|
|
||||||
|
|
@ -134,8 +134,8 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
|
||||||
}
|
}
|
||||||
print("\n")
|
print("\n")
|
||||||
|
|
||||||
var docrash bool
|
level, _, docrash := gotraceback()
|
||||||
if gotraceback(&docrash) > 0 {
|
if level > 0 {
|
||||||
tracebacktrap(r.ip(), r.sp(), 0, gp)
|
tracebacktrap(r.ip(), r.sp(), 0, gp)
|
||||||
tracebackothers(gp)
|
tracebackothers(gp)
|
||||||
dumpregs(r)
|
dumpregs(r)
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
|
||||||
// instead on the g0 stack.
|
// instead on the g0 stack.
|
||||||
throw("gentraceback cannot trace user goroutine on its own stack")
|
throw("gentraceback cannot trace user goroutine on its own stack")
|
||||||
}
|
}
|
||||||
gotraceback := gotraceback(nil)
|
level, _, _ := gotraceback()
|
||||||
|
|
||||||
// Fix up returns to the stack barrier by fetching the
|
// Fix up returns to the stack barrier by fetching the
|
||||||
// original return PC from gp.stkbar.
|
// original return PC from gp.stkbar.
|
||||||
|
|
@ -367,7 +367,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
|
||||||
if frame.pc > f.entry {
|
if frame.pc > f.entry {
|
||||||
print(" +", hex(frame.pc-f.entry))
|
print(" +", hex(frame.pc-f.entry))
|
||||||
}
|
}
|
||||||
if g.m.throwing > 0 && gp == g.m.curg || gotraceback >= 2 {
|
if g.m.throwing > 0 && gp == g.m.curg || level >= 2 {
|
||||||
print(" fp=", hex(frame.fp), " sp=", hex(frame.sp))
|
print(" fp=", hex(frame.fp), " sp=", hex(frame.sp))
|
||||||
}
|
}
|
||||||
print("\n")
|
print("\n")
|
||||||
|
|
@ -582,7 +582,7 @@ func showframe(f *_func, gp *g) bool {
|
||||||
if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) {
|
if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
traceback := gotraceback(nil)
|
level, _, _ := gotraceback()
|
||||||
name := funcname(f)
|
name := funcname(f)
|
||||||
|
|
||||||
// Special case: always show runtime.panic frame, so that we can
|
// Special case: always show runtime.panic frame, so that we can
|
||||||
|
|
@ -592,7 +592,7 @@ func showframe(f *_func, gp *g) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return traceback > 1 || f != nil && contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name))
|
return level > 1 || f != nil && contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// isExportedRuntime reports whether name is an exported runtime function.
|
// isExportedRuntime reports whether name is an exported runtime function.
|
||||||
|
|
@ -658,7 +658,7 @@ func goroutineheader(gp *g) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func tracebackothers(me *g) {
|
func tracebackothers(me *g) {
|
||||||
level := gotraceback(nil)
|
level, _, _ := gotraceback()
|
||||||
|
|
||||||
// Show the current goroutine first, if we haven't already.
|
// Show the current goroutine first, if we haven't already.
|
||||||
g := getg()
|
g := getg()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue