1
0
mirror of https://github.com/golang/go synced 2024-11-23 09:40:04 -07: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:
Russ Cox 2015-10-30 11:03:02 -04:00
parent 845878a213
commit bf1de1b141
11 changed files with 78 additions and 55 deletions

View File

@ -106,15 +106,20 @@ the limit.
The GOTRACEBACK variable controls the amount of output generated when a Go
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
internal to the run-time system, and then exits with exit code 2.
If GOTRACEBACK=0, the per-goroutine stack traces are omitted entirely.
If GOTRACEBACK=1, the default behavior is used.
If GOTRACEBACK=2, the per-goroutine stack traces include run-time functions.
If GOTRACEBACK=crash, the per-goroutine stack traces include run-time functions,
and if possible the program crashes in an operating-specific manner instead of
exiting. For example, on Unix systems, the program raises SIGABRT to trigger a
core dump.
By default, a failure prints a stack trace for the current goroutine,
eliding functions internal to the run-time system, and then exits with exit code 2.
The failure prints stack traces for all goroutines if there is no current goroutine
or the failure is internal to the run-time.
GOTRACEBACK=none omits the goroutine stack traces entirely.
GOTRACEBACK=single (the default) behaves as described above.
GOTRACEBACK=all adds stack traces for all user-created goroutines.
GOTRACEBACK=system is like ``all'' but adds stack frames for run-time functions
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 set of Go environment variables. They influence the building of Go programs

View File

@ -14,6 +14,7 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int {
var docrash bool
var sig int
var flags int
var level int32
c := &sigctxt{_ureg}
notestr := gostringnocopy(note)
@ -97,7 +98,8 @@ Throw:
print(notestr, "\n")
print("PC=", hex(c.pc()), "\n")
print("\n")
if gotraceback(&docrash) > 0 {
level, _, docrash = gotraceback()
if level > 0 {
goroutineheader(gp)
tracebacktrap(c.pc(), c.sp(), 0, gp)
tracebackothers(gp)

View File

@ -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")
}
var docrash bool
level, all, docrash := gotraceback()
_g_ := getg()
if t := gotraceback(&docrash); t > 0 {
if level > 0 {
if gp != gp.m.curg {
all = true
}
if gp != gp.m.g0 {
print("\n")
goroutineheader(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")
traceback(pc, sp, 0, gp)
}
if !didothers {
if !didothers && all {
didothers = true
tracebackothers(gp)
}

View File

@ -8,30 +8,37 @@ import "unsafe"
// Keep a cached value to make gotraceback fast,
// since we call it on every call to gentraceback.
// The cached value is a uint32 in which the low bit
// is the "crash" setting and the top 31 bits are the
// gotraceback value.
var traceback_cache uint32 = 2 << 1
// The cached value is a uint32 in which the low bits
// are the "crash" and "all" settings and the remaining
// bits are the traceback value (0 off, 1 on, 2 include system).
const (
tracebackCrash = 1 << iota
tracebackAll
tracebackShift = iota
)
// The GOTRACEBACK environment variable controls the
// behavior of a Go program that is crashing and exiting.
// GOTRACEBACK=0 suppress all tracebacks
// GOTRACEBACK=1 default behavior - show tracebacks but exclude runtime frames
// GOTRACEBACK=2 show tracebacks including runtime frames
// GOTRACEBACK=crash show tracebacks including runtime frames, then crash (core dump etc)
var traceback_cache uint32 = 2 << tracebackShift
// gotraceback returns the current traceback settings.
//
// If level is 0, suppress all tracebacks.
// 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
func gotraceback(crash *bool) int32 {
func gotraceback() (level int32, all, crash bool) {
_g_ := getg()
if crash != nil {
*crash = false
}
all = _g_.m.throwing > 0
if _g_.m.traceback != 0 {
return int32(_g_.m.traceback)
level = int32(_g_.m.traceback)
return
}
if crash != nil {
*crash = traceback_cache&1 != 0
}
return int32(traceback_cache >> 1)
crash = traceback_cache&tracebackCrash != 0
all = all || traceback_cache&tracebackAll != 0
level = int32(traceback_cache >> tracebackShift)
return
}
var (
@ -365,17 +372,23 @@ func parsedebugvars() {
}
switch p := gogetenv("GOTRACEBACK"); p {
case "":
traceback_cache = 1 << 1
case "none":
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":
traceback_cache = 2<<1 | 1
traceback_cache = 2<<tracebackShift | tracebackAll | tracebackCrash
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
// and panics is surprising. Be louder and abort instead.
if islibrary || isarchive {
traceback_cache |= 1
traceback_cache |= tracebackCrash
}
if debug.gcstackbarrierall > 0 {

View File

@ -131,8 +131,8 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
}
print("\n")
var docrash bool
if gotraceback(&docrash) > 0 {
level, _, docrash := gotraceback()
if level > 0 {
goroutineheader(gp)
// On Linux/386, all system calls go through the vdso kernel_vsyscall routine.

View File

@ -165,8 +165,8 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
}
print("\n")
var docrash bool
if gotraceback(&docrash) > 0 {
level, _, docrash := gotraceback()
if level > 0 {
goroutineheader(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 {

View File

@ -126,8 +126,8 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
}
print("\n")
var docrash bool
if gotraceback(&docrash) > 0 {
level, _, docrash := gotraceback()
if level > 0 {
goroutineheader(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 {

View File

@ -139,8 +139,8 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
}
print("\n")
var docrash bool
if gotraceback(&docrash) > 0 {
level, _, docrash := gotraceback()
if level > 0 {
goroutineheader(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 {

View File

@ -144,8 +144,8 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
}
print("\n")
var docrash bool
if gotraceback(&docrash) > 0 {
level, _, docrash := gotraceback()
if level > 0 {
goroutineheader(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 {

View File

@ -134,8 +134,8 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
}
print("\n")
var docrash bool
if gotraceback(&docrash) > 0 {
level, _, docrash := gotraceback()
if level > 0 {
tracebacktrap(r.ip(), r.sp(), 0, gp)
tracebackothers(gp)
dumpregs(r)

View File

@ -139,7 +139,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// instead on the g0 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
// 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 {
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("\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()) {
return true
}
traceback := gotraceback(nil)
level, _, _ := gotraceback()
name := funcname(f)
// 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 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.
@ -658,7 +658,7 @@ func goroutineheader(gp *g) {
}
func tracebackothers(me *g) {
level := gotraceback(nil)
level, _, _ := gotraceback()
// Show the current goroutine first, if we haven't already.
g := getg()