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>
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
var docrash bool
var sig int
var flags int
+ var level int32
c := &sigctxt{_ureg}
notestr := gostringnocopy(note)
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)
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)
}
// 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 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)
+// 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
+)
+
+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)
- }
- if crash != nil {
- *crash = traceback_cache&1 != 0
+ level = int32(_g_.m.traceback)
+ return
}
- return int32(traceback_cache >> 1)
+ crash = traceback_cache&tracebackCrash != 0
+ all = all || traceback_cache&tracebackAll != 0
+ level = int32(traceback_cache >> tracebackShift)
+ return
}
var (
}
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 {
}
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.
}
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 {
}
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 {
}
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 {
}
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 {
}
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)
// 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.
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")
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
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.
}
func tracebackothers(me *g) {
- level := gotraceback(nil)
+ level, _, _ := gotraceback()
// Show the current goroutine first, if we haven't already.
g := getg()