return sigtable[sig].name
}
+//go:nosplit
func crash() {
*(*int32)(nil) = 0
}
notify(unsafe.Pointer(funcPC(sigtramp)))
}
+//go:nosplit
func crash() {
notify(nil)
*(*int)(nil) = 0
if gp.m.throwing == 0 {
gp.m.throwing = 1
}
- fatalpanic(nil)
+ fatalthrow()
*(*int)(nil) = 0 // not reached
}
gogo(&gp.sched)
}
-// fatalpanic implements an unrecoverable panic. It freezes the
-// system, prints panic messages if msgs != nil, prints stack traces
-// starting from its caller, and terminates the process.
+// fatalthrow implements an unrecoverable runtime throw. It freezes the
+// system, prints stack traces starting from its caller, and terminates the
+// process.
//
-// If msgs != nil, it also decrements runningPanicDefers once main is
-// blocked from exiting.
+//go:nosplit
+func fatalthrow() {
+ pc := getcallerpc()
+ sp := getcallersp()
+ gp := getg()
+ // Switch to the system stack to avoid any stack growth, which
+ // may make things worse if the runtime is in a bad state.
+ systemstack(func() {
+ startpanic_m()
+
+ if dopanic_m(gp, pc, sp) {
+ // crash uses a decent amount of nosplit stack and we're already
+ // low on stack in throw, so crash on the system stack (unlike
+ // fatalpanic).
+ crash()
+ }
+
+ exit(2)
+ })
+
+ *(*int)(nil) = 0 // not reached
+}
+
+// fatalpanic implements an unrecoverable panic. It is like fatalthrow, except
+// that if msgs != nil, fatalpanic also prints panic messages and decrements
+// runningPanicDefers once main is blocked from exiting.
//
//go:nosplit
func fatalpanic(msgs *_panic) {
pc := getcallerpc()
sp := getcallersp()
gp := getg()
+ var docrash bool
// Switch to the system stack to avoid any stack growth, which
// may make things worse if the runtime is in a bad state.
systemstack(func() {
printpanics(msgs)
}
- dopanic_m(gp, pc, sp) // should never return
+ docrash = dopanic_m(gp, pc, sp)
})
+
+ if docrash {
+ // By crashing outside the above systemstack call, debuggers
+ // will not be confused when generating a backtrace.
+ // Function crash is marked nosplit to avoid stack growth.
+ crash()
+ }
+
+ systemstack(func() {
+ exit(2)
+ })
+
*(*int)(nil) = 0 // not reached
}
var didothers bool
var deadlock mutex
-func dopanic_m(gp *g, pc, sp uintptr) {
+func dopanic_m(gp *g, pc, sp uintptr) bool {
if gp.sig != 0 {
signame := signame(gp.sig)
if signame != "" {
lock(&deadlock)
}
- if docrash {
- crash()
- }
-
- exit(2)
+ return docrash
}
// canpanic returns false if a signal should throw instead of
t.Fatalf("output mismatch")
}
}
+
+const panicSource = `
+package main
+
+import "runtime/debug"
+
+func main() {
+ debug.SetTraceback("crash")
+ crash()
+}
+
+func crash() {
+ panic("panic!")
+}
+`
+
+// TestGdbPanic tests that gdb can unwind the stack correctly
+// from SIGABRTs from Go panics.
+func TestGdbPanic(t *testing.T) {
+ checkGdbEnvironment(t)
+ t.Parallel()
+ checkGdbVersion(t)
+
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatalf("failed to create temp directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ // Build the source code.
+ src := filepath.Join(dir, "main.go")
+ err = ioutil.WriteFile(src, []byte(panicSource), 0644)
+ if err != nil {
+ t.Fatalf("failed to create file: %v", err)
+ }
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe")
+ cmd.Dir = dir
+ out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
+ if err != nil {
+ t.Fatalf("building source %v\n%s", err, out)
+ }
+
+ // Execute gdb commands.
+ args := []string{"-nx", "-batch",
+ "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"),
+ "-ex", "set startup-with-shell off",
+ "-ex", "run",
+ "-ex", "backtrace",
+ filepath.Join(dir, "a.exe"),
+ }
+ got, _ := exec.Command("gdb", args...).CombinedOutput()
+
+ // Check that the backtrace matches the source code.
+ bt := []string{
+ `crash`,
+ `main`,
+ }
+ for _, name := range bt {
+ s := fmt.Sprintf("#.* .* in main\\.%v", name)
+ re := regexp.MustCompile(s)
+ if found := re.Find(got) != nil; !found {
+ t.Errorf("could not find '%v' in backtrace", s)
+ t.Fatalf("gdb output:\n%v", string(got))
+ }
+ }
+}
setsig(sig, funcPC(sighandler))
}
+//go:nosplit
func crash() {
if GOOS == "darwin" {
// OS X core dumps are linear dumps of the mapped memory,
return ""
}
+//go:nosplit
func crash() {
// TODO: This routine should do whatever is needed
// to make the Windows program abort/crash as it