2016-05-27 17:34:22 +09:00
|
|
|
// Copyright 2015 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
2015-01-14 14:32:01 -05:00
|
|
|
package runtime_test
|
|
|
|
|
|
|
|
|
|
import (
|
2015-02-16 21:56:10 -05:00
|
|
|
"bytes"
|
2022-11-03 09:08:33 -04:00
|
|
|
"flag"
|
2015-01-16 01:23:56 -05:00
|
|
|
"fmt"
|
2016-04-13 13:34:41 -04:00
|
|
|
"internal/testenv"
|
2015-01-14 14:32:01 -05:00
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
2015-02-16 21:56:10 -05:00
|
|
|
"regexp"
|
2015-01-16 01:23:56 -05:00
|
|
|
"runtime"
|
2015-07-23 22:40:30 -07:00
|
|
|
"strconv"
|
2016-11-14 18:00:17 -05:00
|
|
|
"strings"
|
2015-01-14 14:32:01 -05:00
|
|
|
"testing"
|
2022-10-26 11:44:34 -04:00
|
|
|
"time"
|
2015-01-14 14:32:01 -05:00
|
|
|
)
|
|
|
|
|
|
2020-05-29 16:01:17 -04:00
|
|
|
// NOTE: In some configurations, GDB will segfault when sent a SIGWINCH signal.
|
|
|
|
|
// Some runtime tests send SIGWINCH to the entire process group, so those tests
|
|
|
|
|
// must never run in parallel with GDB tests.
|
|
|
|
|
//
|
|
|
|
|
// See issue 39021 and https://sourceware.org/bugzilla/show_bug.cgi?id=26056.
|
|
|
|
|
|
2016-04-13 13:34:41 -04:00
|
|
|
func checkGdbEnvironment(t *testing.T) {
|
|
|
|
|
testenv.MustHaveGoBuild(t)
|
2017-11-27 16:48:48 -08:00
|
|
|
switch runtime.GOOS {
|
|
|
|
|
case "darwin":
|
2016-04-13 13:34:41 -04:00
|
|
|
t.Skip("gdb does not work on darwin")
|
2017-11-27 16:48:48 -08:00
|
|
|
case "netbsd":
|
2019-01-02 18:53:23 +00:00
|
|
|
t.Skip("gdb does not work with threads on NetBSD; see https://golang.org/issue/22893 and https://gnats.netbsd.org/52548")
|
2018-05-11 18:11:51 -04:00
|
|
|
case "windows":
|
|
|
|
|
t.Skip("gdb tests fail on Windows: https://golang.org/issue/22687")
|
2017-11-27 16:48:48 -08:00
|
|
|
case "linux":
|
|
|
|
|
if runtime.GOARCH == "ppc64" {
|
2019-01-02 18:53:23 +00:00
|
|
|
t.Skip("skipping gdb tests on linux/ppc64; see https://golang.org/issue/17366")
|
2018-06-18 17:09:37 -04:00
|
|
|
}
|
|
|
|
|
if runtime.GOARCH == "mips" {
|
|
|
|
|
t.Skip("skipping gdb tests on linux/mips; see https://golang.org/issue/25939")
|
2017-11-27 16:48:48 -08:00
|
|
|
}
|
2022-08-09 09:03:50 -04:00
|
|
|
// Disable GDB tests on alpine until issue #54352 resolved.
|
|
|
|
|
if strings.HasSuffix(testenv.Builder(), "-alpine") {
|
|
|
|
|
t.Skip("skipping gdb tests on alpine; see https://golang.org/issue/54352")
|
|
|
|
|
}
|
2019-01-02 18:53:23 +00:00
|
|
|
case "freebsd":
|
2019-01-08 00:54:58 +02:00
|
|
|
t.Skip("skipping gdb tests on FreeBSD; see https://golang.org/issue/29508")
|
2019-11-20 13:57:24 +01:00
|
|
|
case "aix":
|
|
|
|
|
if testing.Short() {
|
|
|
|
|
t.Skip("skipping gdb tests on AIX; see https://golang.org/issue/35710")
|
|
|
|
|
}
|
2020-03-31 19:44:19 +01:00
|
|
|
case "plan9":
|
|
|
|
|
t.Skip("there is no gdb on Plan 9")
|
2016-10-06 19:06:49 +00:00
|
|
|
}
|
2022-03-09 17:19:23 -05:00
|
|
|
if final := os.Getenv("GOROOT_FINAL"); final != "" && testenv.GOROOT(t) != final {
|
2016-04-13 13:34:41 -04:00
|
|
|
t.Skip("gdb test can fail with GOROOT_FINAL pending")
|
2015-01-14 14:32:01 -05:00
|
|
|
}
|
2016-04-13 13:34:41 -04:00
|
|
|
}
|
2015-07-23 22:40:30 -07:00
|
|
|
|
2020-06-02 17:53:47 -04:00
|
|
|
func checkGdbVersion(t *testing.T) {
|
2015-07-23 22:40:30 -07:00
|
|
|
// Issue 11214 reports various failures with older versions of gdb.
|
2016-04-13 13:34:41 -04:00
|
|
|
out, err := exec.Command("gdb", "--version").CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Skipf("skipping: error executing gdb: %v", err)
|
|
|
|
|
}
|
2015-07-23 22:40:30 -07:00
|
|
|
re := regexp.MustCompile(`([0-9]+)\.([0-9]+)`)
|
|
|
|
|
matches := re.FindSubmatch(out)
|
|
|
|
|
if len(matches) < 3 {
|
|
|
|
|
t.Skipf("skipping: can't determine gdb version from\n%s\n", out)
|
|
|
|
|
}
|
|
|
|
|
major, err1 := strconv.Atoi(string(matches[1]))
|
|
|
|
|
minor, err2 := strconv.Atoi(string(matches[2]))
|
|
|
|
|
if err1 != nil || err2 != nil {
|
|
|
|
|
t.Skipf("skipping: can't determine gdb version: %v, %v", err1, err2)
|
|
|
|
|
}
|
|
|
|
|
if major < 7 || (major == 7 && minor < 7) {
|
|
|
|
|
t.Skipf("skipping: gdb version %d.%d too old", major, minor)
|
|
|
|
|
}
|
|
|
|
|
t.Logf("gdb version %d.%d", major, minor)
|
2015-01-14 14:32:01 -05:00
|
|
|
}
|
|
|
|
|
|
2016-04-13 13:34:41 -04:00
|
|
|
func checkGdbPython(t *testing.T) {
|
2019-10-21 01:45:43 -07:00
|
|
|
if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
|
|
|
|
|
t.Skip("skipping gdb python tests on illumos and solaris; see golang.org/issue/20821")
|
2017-06-27 16:49:09 -07:00
|
|
|
}
|
|
|
|
|
|
2016-04-13 13:34:41 -04:00
|
|
|
cmd := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", "python import sys; print('go gdb python support')")
|
|
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Skipf("skipping due to issue running gdb: %v", err)
|
|
|
|
|
}
|
2018-03-24 18:07:58 +02:00
|
|
|
if strings.TrimSpace(string(out)) != "go gdb python support" {
|
2016-04-13 13:34:41 -04:00
|
|
|
t.Skipf("skipping due to lack of python gdb support: %s", out)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-28 12:51:30 -04:00
|
|
|
// checkCleanBacktrace checks that the given backtrace is well formed and does
|
|
|
|
|
// not contain any error messages from GDB.
|
|
|
|
|
func checkCleanBacktrace(t *testing.T, backtrace string) {
|
|
|
|
|
backtrace = strings.TrimSpace(backtrace)
|
|
|
|
|
lines := strings.Split(backtrace, "\n")
|
|
|
|
|
if len(lines) == 0 {
|
|
|
|
|
t.Fatalf("empty backtrace")
|
|
|
|
|
}
|
|
|
|
|
for i, l := range lines {
|
|
|
|
|
if !strings.HasPrefix(l, fmt.Sprintf("#%v ", i)) {
|
|
|
|
|
t.Fatalf("malformed backtrace at line %v: %v", i, l)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// TODO(mundaym): check for unknown frames (e.g. "??").
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-14 14:32:01 -05:00
|
|
|
const helloSource = `
|
|
|
|
|
import "fmt"
|
2016-06-08 22:02:08 -07:00
|
|
|
import "runtime"
|
2016-10-24 13:05:10 -04:00
|
|
|
var gslice []string
|
2015-02-21 18:18:33 +01:00
|
|
|
func main() {
|
2017-09-02 18:46:59 +02:00
|
|
|
mapvar := make(map[string]string, 13)
|
2020-06-01 14:30:24 -04:00
|
|
|
slicemap := make(map[string][]string,11)
|
|
|
|
|
chanint := make(chan int, 10)
|
|
|
|
|
chanstr := make(chan string, 10)
|
|
|
|
|
chanint <- 99
|
|
|
|
|
chanint <- 11
|
|
|
|
|
chanstr <- "spongepants"
|
|
|
|
|
chanstr <- "squarebob"
|
2015-02-21 18:18:33 +01:00
|
|
|
mapvar["abc"] = "def"
|
|
|
|
|
mapvar["ghi"] = "jkl"
|
2020-06-01 14:30:24 -04:00
|
|
|
slicemap["a"] = []string{"b","c","d"}
|
|
|
|
|
slicemap["e"] = []string{"f","g","h"}
|
2015-03-30 17:36:49 +01:00
|
|
|
strvar := "abc"
|
|
|
|
|
ptrvar := &strvar
|
2016-10-24 13:05:10 -04:00
|
|
|
slicevar := make([]string, 0, 16)
|
|
|
|
|
slicevar = append(slicevar, mapvar["abc"])
|
2018-01-16 15:31:12 -05:00
|
|
|
fmt.Println("hi")
|
2017-03-29 14:01:41 -04:00
|
|
|
runtime.KeepAlive(ptrvar)
|
2020-03-13 11:09:05 -04:00
|
|
|
_ = ptrvar // set breakpoint here
|
2016-10-24 13:05:10 -04:00
|
|
|
gslice = slicevar
|
2020-06-01 14:30:24 -04:00
|
|
|
fmt.Printf("%v, %v, %v\n", slicemap, <-chanint, <-chanstr)
|
2016-06-08 22:02:08 -07:00
|
|
|
runtime.KeepAlive(mapvar)
|
2018-01-16 15:31:12 -05:00
|
|
|
} // END_OF_PROGRAM
|
2015-01-14 14:32:01 -05:00
|
|
|
`
|
|
|
|
|
|
2018-01-16 15:31:12 -05:00
|
|
|
func lastLine(src []byte) int {
|
|
|
|
|
eop := []byte("END_OF_PROGRAM")
|
|
|
|
|
for i, l := range bytes.Split(src, []byte("\n")) {
|
|
|
|
|
if bytes.Contains(l, eop) {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-16 21:56:10 -05:00
|
|
|
func TestGdbPython(t *testing.T) {
|
2017-01-23 17:30:41 -08:00
|
|
|
testGdbPython(t, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestGdbPythonCgo(t *testing.T) {
|
2022-01-07 16:35:31 -05:00
|
|
|
if strings.HasPrefix(runtime.GOARCH, "mips") {
|
|
|
|
|
testenv.SkipFlaky(t, 37794)
|
2017-02-02 16:00:26 -05:00
|
|
|
}
|
2017-01-23 17:30:41 -08:00
|
|
|
testGdbPython(t, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testGdbPython(t *testing.T, cgo bool) {
|
2019-04-26 06:56:58 -07:00
|
|
|
if cgo {
|
|
|
|
|
testenv.MustHaveCGO(t)
|
2017-01-23 17:30:41 -08:00
|
|
|
}
|
2016-12-02 23:27:02 +00:00
|
|
|
|
2016-04-13 13:34:41 -04:00
|
|
|
checkGdbEnvironment(t)
|
2017-12-01 19:26:33 +00:00
|
|
|
t.Parallel()
|
2020-06-02 17:53:47 -04:00
|
|
|
checkGdbVersion(t)
|
2015-01-14 14:32:01 -05:00
|
|
|
checkGdbPython(t)
|
|
|
|
|
|
2021-04-07 20:16:58 +08:00
|
|
|
dir := t.TempDir()
|
2015-01-14 14:32:01 -05:00
|
|
|
|
2017-01-23 17:30:41 -08:00
|
|
|
var buf bytes.Buffer
|
|
|
|
|
buf.WriteString("package main\n")
|
|
|
|
|
if cgo {
|
|
|
|
|
buf.WriteString(`import "C"` + "\n")
|
|
|
|
|
}
|
|
|
|
|
buf.WriteString(helloSource)
|
|
|
|
|
|
2018-01-16 15:31:12 -05:00
|
|
|
src := buf.Bytes()
|
|
|
|
|
|
2020-03-13 11:09:05 -04:00
|
|
|
// Locate breakpoint line
|
|
|
|
|
var bp int
|
|
|
|
|
lines := bytes.Split(src, []byte("\n"))
|
|
|
|
|
for i, line := range lines {
|
|
|
|
|
if bytes.Contains(line, []byte("breakpoint")) {
|
|
|
|
|
bp = i
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-07 20:16:58 +08:00
|
|
|
err := os.WriteFile(filepath.Join(dir, "main.go"), src, 0644)
|
2015-01-14 14:32:01 -05:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to create file: %v", err)
|
|
|
|
|
}
|
2018-01-16 15:31:12 -05:00
|
|
|
nLines := lastLine(src)
|
2015-01-14 14:32:01 -05:00
|
|
|
|
2019-02-14 17:38:36 -05:00
|
|
|
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe", "main.go")
|
2015-01-14 14:32:01 -05:00
|
|
|
cmd.Dir = dir
|
2017-09-13 15:04:16 +02:00
|
|
|
out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
|
2015-01-14 14:32:01 -05:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("building source %v\n%s", err, out)
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-24 18:07:58 +02:00
|
|
|
args := []string{"-nx", "-q", "--batch",
|
2022-03-09 17:19:23 -05:00
|
|
|
"-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
|
2016-05-17 09:27:00 +02:00
|
|
|
"-ex", "set startup-with-shell off",
|
2019-04-19 11:53:33 -04:00
|
|
|
"-ex", "set print thread-events off",
|
2018-08-09 17:25:13 +01:00
|
|
|
}
|
|
|
|
|
if cgo {
|
|
|
|
|
// When we build the cgo version of the program, the system's
|
|
|
|
|
// linker is used. Some external linkers, like GNU gold,
|
|
|
|
|
// compress the .debug_gdb_scripts into .zdebug_gdb_scripts.
|
|
|
|
|
// Until gold and gdb can work together, temporarily load the
|
|
|
|
|
// python script directly.
|
|
|
|
|
args = append(args,
|
2022-03-09 17:19:23 -05:00
|
|
|
"-ex", "source "+filepath.Join(testenv.GOROOT(t), "src", "runtime", "runtime-gdb.py"),
|
2018-08-09 17:25:13 +01:00
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
args = append(args,
|
|
|
|
|
"-ex", "info auto-load python-scripts",
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
args = append(args,
|
2016-05-26 11:05:01 -04:00
|
|
|
"-ex", "set python print-stack full",
|
2020-03-13 11:09:05 -04:00
|
|
|
"-ex", fmt.Sprintf("br main.go:%d", bp),
|
2015-02-16 21:56:10 -05:00
|
|
|
"-ex", "run",
|
|
|
|
|
"-ex", "echo BEGIN info goroutines\n",
|
|
|
|
|
"-ex", "info goroutines",
|
|
|
|
|
"-ex", "echo END\n",
|
2015-02-21 18:18:33 +01:00
|
|
|
"-ex", "echo BEGIN print mapvar\n",
|
|
|
|
|
"-ex", "print mapvar",
|
|
|
|
|
"-ex", "echo END\n",
|
2020-06-01 14:30:24 -04:00
|
|
|
"-ex", "echo BEGIN print slicemap\n",
|
|
|
|
|
"-ex", "print slicemap",
|
|
|
|
|
"-ex", "echo END\n",
|
2015-03-30 17:36:49 +01:00
|
|
|
"-ex", "echo BEGIN print strvar\n",
|
|
|
|
|
"-ex", "print strvar",
|
2016-09-12 15:19:36 -07:00
|
|
|
"-ex", "echo END\n",
|
2020-06-01 14:30:24 -04:00
|
|
|
"-ex", "echo BEGIN print chanint\n",
|
|
|
|
|
"-ex", "print chanint",
|
|
|
|
|
"-ex", "echo END\n",
|
|
|
|
|
"-ex", "echo BEGIN print chanstr\n",
|
|
|
|
|
"-ex", "print chanstr",
|
|
|
|
|
"-ex", "echo END\n",
|
2016-10-24 13:05:10 -04:00
|
|
|
"-ex", "echo BEGIN info locals\n",
|
|
|
|
|
"-ex", "info locals",
|
|
|
|
|
"-ex", "echo END\n",
|
2017-06-07 10:30:49 -04:00
|
|
|
"-ex", "echo BEGIN goroutine 1 bt\n",
|
|
|
|
|
"-ex", "goroutine 1 bt",
|
|
|
|
|
"-ex", "echo END\n",
|
2019-04-29 02:04:26 +00:00
|
|
|
"-ex", "echo BEGIN goroutine all bt\n",
|
|
|
|
|
"-ex", "goroutine all bt",
|
|
|
|
|
"-ex", "echo END\n",
|
2018-11-04 19:23:08 -08:00
|
|
|
"-ex", "clear main.go:15", // clear the previous break point
|
2018-01-16 15:31:12 -05:00
|
|
|
"-ex", fmt.Sprintf("br main.go:%d", nLines), // new break point at the end of main
|
|
|
|
|
"-ex", "c",
|
|
|
|
|
"-ex", "echo BEGIN goroutine 1 bt at the end\n",
|
|
|
|
|
"-ex", "goroutine 1 bt",
|
|
|
|
|
"-ex", "echo END\n",
|
2016-04-24 17:36:41 -04:00
|
|
|
filepath.Join(dir, "a.exe"),
|
2018-08-09 17:25:13 +01:00
|
|
|
)
|
2020-05-26 15:55:54 -04:00
|
|
|
got, err := exec.Command("gdb", args...).CombinedOutput()
|
|
|
|
|
t.Logf("gdb output:\n%s", got)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("gdb exited with error: %v", err)
|
|
|
|
|
}
|
2015-02-16 21:56:10 -05:00
|
|
|
|
2021-09-22 10:46:32 -04:00
|
|
|
firstLine, _, _ := bytes.Cut(got, []byte("\n"))
|
2015-02-16 21:56:10 -05:00
|
|
|
if string(firstLine) != "Loading Go Runtime support." {
|
2015-07-10 05:20:20 -07:00
|
|
|
// This can happen when using all.bash with
|
|
|
|
|
// GOROOT_FINAL set, because the tests are run before
|
|
|
|
|
// the final installation of the files.
|
2016-08-30 11:08:47 -07:00
|
|
|
cmd := exec.Command(testenv.GoToolPath(t), "env", "GOROOT")
|
2015-07-10 05:20:20 -07:00
|
|
|
cmd.Env = []string{}
|
|
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
|
if err != nil && bytes.Contains(out, []byte("cannot find GOROOT")) {
|
2022-03-09 17:19:23 -05:00
|
|
|
t.Skipf("skipping because GOROOT=%s does not exist", testenv.GOROOT(t))
|
2015-07-10 05:20:20 -07:00
|
|
|
}
|
|
|
|
|
|
2015-12-29 10:16:40 -05:00
|
|
|
_, file, _, _ := runtime.Caller(1)
|
|
|
|
|
|
|
|
|
|
t.Logf("package testing source file: %s", file)
|
|
|
|
|
t.Fatalf("failed to load Go runtime support: %s\n%s", firstLine, got)
|
2015-02-16 21:56:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract named BEGIN...END blocks from output
|
|
|
|
|
partRe := regexp.MustCompile(`(?ms)^BEGIN ([^\n]*)\n(.*?)\nEND`)
|
|
|
|
|
blocks := map[string]string{}
|
|
|
|
|
for _, subs := range partRe.FindAllSubmatch(got, -1) {
|
|
|
|
|
blocks[string(subs[1])] = string(subs[2])
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 15:01:43 -05:00
|
|
|
infoGoroutinesRe := regexp.MustCompile(`\*\s+\d+\s+running\s+`)
|
2015-02-16 21:56:10 -05:00
|
|
|
if bl := blocks["info goroutines"]; !infoGoroutinesRe.MatchString(bl) {
|
|
|
|
|
t.Fatalf("info goroutines failed: %s", bl)
|
2015-01-14 14:32:01 -05:00
|
|
|
}
|
2015-02-21 18:18:33 +01:00
|
|
|
|
2018-09-13 01:46:39 +07:00
|
|
|
printMapvarRe1 := regexp.MustCompile(`^\$[0-9]+ = map\[string\]string = {\[(0x[0-9a-f]+\s+)?"abc"\] = (0x[0-9a-f]+\s+)?"def", \[(0x[0-9a-f]+\s+)?"ghi"\] = (0x[0-9a-f]+\s+)?"jkl"}$`)
|
|
|
|
|
printMapvarRe2 := regexp.MustCompile(`^\$[0-9]+ = map\[string\]string = {\[(0x[0-9a-f]+\s+)?"ghi"\] = (0x[0-9a-f]+\s+)?"jkl", \[(0x[0-9a-f]+\s+)?"abc"\] = (0x[0-9a-f]+\s+)?"def"}$`)
|
2017-09-02 18:46:59 +02:00
|
|
|
if bl := blocks["print mapvar"]; !printMapvarRe1.MatchString(bl) &&
|
|
|
|
|
!printMapvarRe2.MatchString(bl) {
|
2015-02-21 18:18:33 +01:00
|
|
|
t.Fatalf("print mapvar failed: %s", bl)
|
|
|
|
|
}
|
2015-03-30 17:36:49 +01:00
|
|
|
|
2020-06-01 14:30:24 -04:00
|
|
|
// 2 orders, and possible differences in spacing.
|
|
|
|
|
sliceMapSfx1 := `map[string][]string = {["e"] = []string = {"f", "g", "h"}, ["a"] = []string = {"b", "c", "d"}}`
|
|
|
|
|
sliceMapSfx2 := `map[string][]string = {["a"] = []string = {"b", "c", "d"}, ["e"] = []string = {"f", "g", "h"}}`
|
|
|
|
|
if bl := strings.ReplaceAll(blocks["print slicemap"], " ", " "); !strings.HasSuffix(bl, sliceMapSfx1) && !strings.HasSuffix(bl, sliceMapSfx2) {
|
|
|
|
|
t.Fatalf("print slicemap failed: %s", bl)
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-02 17:53:47 -04:00
|
|
|
chanIntSfx := `chan int = {99, 11}`
|
|
|
|
|
if bl := strings.ReplaceAll(blocks["print chanint"], " ", " "); !strings.HasSuffix(bl, chanIntSfx) {
|
|
|
|
|
t.Fatalf("print chanint failed: %s", bl)
|
|
|
|
|
}
|
2020-06-01 14:30:24 -04:00
|
|
|
|
2020-06-02 17:53:47 -04:00
|
|
|
chanStrSfx := `chan string = {"spongepants", "squarebob"}`
|
|
|
|
|
if bl := strings.ReplaceAll(blocks["print chanstr"], " ", " "); !strings.HasSuffix(bl, chanStrSfx) {
|
|
|
|
|
t.Fatalf("print chanstr failed: %s", bl)
|
2020-06-01 14:30:24 -04:00
|
|
|
}
|
|
|
|
|
|
2018-09-13 01:46:39 +07:00
|
|
|
strVarRe := regexp.MustCompile(`^\$[0-9]+ = (0x[0-9a-f]+\s+)?"abc"$`)
|
2015-03-30 17:36:49 +01:00
|
|
|
if bl := blocks["print strvar"]; !strVarRe.MatchString(bl) {
|
|
|
|
|
t.Fatalf("print strvar failed: %s", bl)
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 16:54:33 -04:00
|
|
|
// The exact format of composite values has changed over time.
|
|
|
|
|
// For issue 16338: ssa decompose phase split a slice into
|
|
|
|
|
// a collection of scalar vars holding its fields. In such cases
|
2016-10-24 13:05:10 -04:00
|
|
|
// the DWARF variable location expression should be of the
|
|
|
|
|
// form "var.field" and not just "field".
|
2018-03-14 16:54:33 -04:00
|
|
|
// However, the newer dwarf location list code reconstituted
|
|
|
|
|
// aggregates from their fields and reverted their printing
|
|
|
|
|
// back to its original form.
|
2018-11-01 11:20:11 +01:00
|
|
|
// Only test that all variables are listed in 'info locals' since
|
|
|
|
|
// different versions of gdb print variables in different
|
|
|
|
|
// order and with differing amount of information and formats.
|
2018-03-14 16:54:33 -04:00
|
|
|
|
2018-11-01 11:20:11 +01:00
|
|
|
if bl := blocks["info locals"]; !strings.Contains(bl, "slicevar") ||
|
|
|
|
|
!strings.Contains(bl, "mapvar") ||
|
|
|
|
|
!strings.Contains(bl, "strvar") {
|
2016-10-24 13:05:10 -04:00
|
|
|
t.Fatalf("info locals failed: %s", bl)
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-28 12:51:30 -04:00
|
|
|
// Check that the backtraces are well formed.
|
|
|
|
|
checkCleanBacktrace(t, blocks["goroutine 1 bt"])
|
|
|
|
|
checkCleanBacktrace(t, blocks["goroutine 1 bt at the end"])
|
|
|
|
|
|
2018-11-04 19:23:08 -08:00
|
|
|
btGoroutine1Re := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?main\.main.+at`)
|
2017-06-07 10:30:49 -04:00
|
|
|
if bl := blocks["goroutine 1 bt"]; !btGoroutine1Re.MatchString(bl) {
|
|
|
|
|
t.Fatalf("goroutine 1 bt failed: %s", bl)
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-14 07:56:17 -07:00
|
|
|
if bl := blocks["goroutine all bt"]; !btGoroutine1Re.MatchString(bl) {
|
2019-04-29 02:04:26 +00:00
|
|
|
t.Fatalf("goroutine all bt failed: %s", bl)
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-16 15:31:12 -05:00
|
|
|
btGoroutine1AtTheEndRe := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?main\.main.+at`)
|
|
|
|
|
if bl := blocks["goroutine 1 bt at the end"]; !btGoroutine1AtTheEndRe.MatchString(bl) {
|
|
|
|
|
t.Fatalf("goroutine 1 bt at the end failed: %s", bl)
|
|
|
|
|
}
|
2015-01-14 14:32:01 -05:00
|
|
|
}
|
2016-04-13 13:34:41 -04:00
|
|
|
|
|
|
|
|
const backtraceSource = `
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
//go:noinline
|
|
|
|
|
func aaa() bool { return bbb() }
|
|
|
|
|
|
|
|
|
|
//go:noinline
|
|
|
|
|
func bbb() bool { return ccc() }
|
|
|
|
|
|
|
|
|
|
//go:noinline
|
|
|
|
|
func ccc() bool { return ddd() }
|
|
|
|
|
|
|
|
|
|
//go:noinline
|
|
|
|
|
func ddd() bool { return f() }
|
|
|
|
|
|
|
|
|
|
//go:noinline
|
|
|
|
|
func eee() bool { return true }
|
|
|
|
|
|
|
|
|
|
var f = eee
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
_ = aaa()
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
// TestGdbBacktrace tests that gdb can unwind the stack correctly
|
|
|
|
|
// using only the DWARF debug info.
|
|
|
|
|
func TestGdbBacktrace(t *testing.T) {
|
2016-05-27 17:34:22 +09:00
|
|
|
if runtime.GOOS == "netbsd" {
|
|
|
|
|
testenv.SkipFlaky(t, 15603)
|
|
|
|
|
}
|
2022-11-03 09:08:33 -04:00
|
|
|
if flag.Lookup("test.parallel").Value.(flag.Getter).Get().(int) < 2 {
|
|
|
|
|
// It is possible that this test will hang for a long time due to an
|
|
|
|
|
// apparent GDB bug reported in https://go.dev/issue/37405.
|
|
|
|
|
// If test parallelism is high enough, that might be ok: the other parallel
|
|
|
|
|
// tests will finish, and then this test will finish right before it would
|
|
|
|
|
// time out. However, if test are running sequentially, a hang in this test
|
|
|
|
|
// would likely cause the remaining tests to run out of time.
|
|
|
|
|
testenv.SkipFlaky(t, 37405)
|
|
|
|
|
}
|
2016-12-02 23:27:02 +00:00
|
|
|
|
|
|
|
|
checkGdbEnvironment(t)
|
2017-12-01 19:26:33 +00:00
|
|
|
t.Parallel()
|
2020-06-02 17:53:47 -04:00
|
|
|
checkGdbVersion(t)
|
2016-05-27 17:34:22 +09:00
|
|
|
|
2021-04-07 20:16:58 +08:00
|
|
|
dir := t.TempDir()
|
2016-04-13 13:34:41 -04:00
|
|
|
|
|
|
|
|
// Build the source code.
|
|
|
|
|
src := filepath.Join(dir, "main.go")
|
2021-04-07 20:16:58 +08:00
|
|
|
err := os.WriteFile(src, []byte(backtraceSource), 0644)
|
2016-04-13 13:34:41 -04:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to create file: %v", err)
|
|
|
|
|
}
|
2019-02-14 17:38:36 -05:00
|
|
|
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe", "main.go")
|
2016-04-13 13:34:41 -04:00
|
|
|
cmd.Dir = dir
|
2017-09-13 15:04:16 +02:00
|
|
|
out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
|
2016-04-13 13:34:41 -04:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("building source %v\n%s", err, out)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Execute gdb commands.
|
2022-11-03 09:08:33 -04:00
|
|
|
start := time.Now()
|
2016-04-13 13:34:41 -04:00
|
|
|
args := []string{"-nx", "-batch",
|
2022-03-09 17:19:23 -05:00
|
|
|
"-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
|
2016-05-17 09:27:00 +02:00
|
|
|
"-ex", "set startup-with-shell off",
|
2016-04-13 13:34:41 -04:00
|
|
|
"-ex", "break main.eee",
|
|
|
|
|
"-ex", "run",
|
|
|
|
|
"-ex", "backtrace",
|
|
|
|
|
"-ex", "continue",
|
|
|
|
|
filepath.Join(dir, "a.exe"),
|
|
|
|
|
}
|
2022-11-03 09:08:33 -04:00
|
|
|
cmd = testenv.Command(t, "gdb", args...)
|
|
|
|
|
|
|
|
|
|
// Work around the GDB hang reported in https://go.dev/issue/37405.
|
|
|
|
|
// Sometimes (rarely), the GDB process hangs completely when the Go program
|
|
|
|
|
// exits, and we suspect that the bug is on the GDB side.
|
|
|
|
|
//
|
|
|
|
|
// The default Cancel function added by testenv.Command will mark the test as
|
|
|
|
|
// failed if it is in danger of timing out, but we want to instead mark it as
|
|
|
|
|
// skipped. Change the Cancel function to kill the process and merely log
|
|
|
|
|
// instead of failing the test.
|
|
|
|
|
//
|
|
|
|
|
// (This approach does not scale: if the test parallelism is less than or
|
|
|
|
|
// equal to the number of tests that run right up to the deadline, then the
|
|
|
|
|
// remaining parallel tests are likely to time out. But as long as it's just
|
|
|
|
|
// this one flaky test, it's probably fine..?)
|
|
|
|
|
//
|
|
|
|
|
// If there is no deadline set on the test at all, relying on the timeout set
|
|
|
|
|
// by testenv.Command will cause the test to hang indefinitely, but that's
|
|
|
|
|
// what “no deadline” means, after all — and it's probably the right behavior
|
|
|
|
|
// anyway if someone is trying to investigate and fix the GDB bug.
|
|
|
|
|
cmd.Cancel = func() error {
|
|
|
|
|
t.Logf("GDB command timed out after %v: %v", time.Since(start), cmd)
|
|
|
|
|
return cmd.Process.Kill()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
got, err := cmd.CombinedOutput()
|
2020-05-26 15:55:54 -04:00
|
|
|
t.Logf("gdb output:\n%s", got)
|
|
|
|
|
if err != nil {
|
2022-02-08 15:07:21 -05:00
|
|
|
if bytes.Contains(got, []byte("internal-error: wait returned unexpected status 0x0")) {
|
2022-02-11 11:48:47 -05:00
|
|
|
// GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28551
|
2022-02-08 15:07:21 -05:00
|
|
|
testenv.SkipFlaky(t, 43068)
|
|
|
|
|
}
|
2022-02-11 11:48:47 -05:00
|
|
|
if bytes.Contains(got, []byte("Couldn't get registers: No such process.")) {
|
|
|
|
|
// GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=9086
|
|
|
|
|
testenv.SkipFlaky(t, 50838)
|
|
|
|
|
}
|
2022-06-08 12:24:55 -04:00
|
|
|
if bytes.Contains(got, []byte(" exited normally]\n")) {
|
|
|
|
|
// GDB bug: Sometimes the inferior exits fine,
|
|
|
|
|
// but then GDB hangs.
|
|
|
|
|
testenv.SkipFlaky(t, 37405)
|
|
|
|
|
}
|
2020-05-26 15:55:54 -04:00
|
|
|
t.Fatalf("gdb exited with error: %v", err)
|
|
|
|
|
}
|
2016-04-13 13:34:41 -04:00
|
|
|
|
|
|
|
|
// Check that the backtrace matches the source code.
|
|
|
|
|
bt := []string{
|
|
|
|
|
"eee",
|
|
|
|
|
"ddd",
|
|
|
|
|
"ccc",
|
|
|
|
|
"bbb",
|
|
|
|
|
"aaa",
|
|
|
|
|
"main",
|
|
|
|
|
}
|
|
|
|
|
for i, name := range bt {
|
|
|
|
|
s := fmt.Sprintf("#%v.*main\\.%v", i, name)
|
|
|
|
|
re := regexp.MustCompile(s)
|
|
|
|
|
if found := re.Find(got) != nil; !found {
|
2020-05-26 15:55:54 -04:00
|
|
|
t.Fatalf("could not find '%v' in backtrace", s)
|
2016-04-13 13:34:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-14 18:00:17 -05:00
|
|
|
|
|
|
|
|
const autotmpTypeSource = `
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
type astruct struct {
|
|
|
|
|
a, b int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
var iface interface{} = map[string]astruct{}
|
|
|
|
|
var iface2 interface{} = []astruct{}
|
|
|
|
|
println(iface, iface2)
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
// TestGdbAutotmpTypes ensures that types of autotmp variables appear in .debug_info
|
|
|
|
|
// See bug #17830.
|
|
|
|
|
func TestGdbAutotmpTypes(t *testing.T) {
|
|
|
|
|
checkGdbEnvironment(t)
|
2017-12-01 19:26:33 +00:00
|
|
|
t.Parallel()
|
2020-06-02 17:53:47 -04:00
|
|
|
checkGdbVersion(t)
|
2016-11-14 18:00:17 -05:00
|
|
|
|
2019-02-28 09:13:56 +01:00
|
|
|
if runtime.GOOS == "aix" && testing.Short() {
|
|
|
|
|
t.Skip("TestGdbAutotmpTypes is too slow on aix/ppc64")
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-07 20:16:58 +08:00
|
|
|
dir := t.TempDir()
|
2016-11-14 18:00:17 -05:00
|
|
|
|
|
|
|
|
// Build the source code.
|
|
|
|
|
src := filepath.Join(dir, "main.go")
|
2021-04-07 20:16:58 +08:00
|
|
|
err := os.WriteFile(src, []byte(autotmpTypeSource), 0644)
|
2016-11-14 18:00:17 -05:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to create file: %v", err)
|
|
|
|
|
}
|
2019-02-14 17:38:36 -05:00
|
|
|
cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-o", "a.exe", "main.go")
|
2016-11-14 18:00:17 -05:00
|
|
|
cmd.Dir = dir
|
2017-09-13 15:04:16 +02:00
|
|
|
out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
|
2016-11-14 18:00:17 -05:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("building source %v\n%s", err, out)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Execute gdb commands.
|
|
|
|
|
args := []string{"-nx", "-batch",
|
2022-03-09 17:19:23 -05:00
|
|
|
"-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
|
2016-11-14 18:00:17 -05:00
|
|
|
"-ex", "set startup-with-shell off",
|
2021-12-09 16:09:24 -06:00
|
|
|
// Some gdb may set scheduling-locking as "step" by default. This prevents background tasks
|
|
|
|
|
// (e.g GC) from completing which may result in a hang when executing the step command.
|
|
|
|
|
// See #49852.
|
|
|
|
|
"-ex", "set scheduler-locking off",
|
2016-11-14 18:00:17 -05:00
|
|
|
"-ex", "break main.main",
|
|
|
|
|
"-ex", "run",
|
|
|
|
|
"-ex", "step",
|
|
|
|
|
"-ex", "info types astruct",
|
|
|
|
|
filepath.Join(dir, "a.exe"),
|
|
|
|
|
}
|
2020-05-26 15:55:54 -04:00
|
|
|
got, err := exec.Command("gdb", args...).CombinedOutput()
|
|
|
|
|
t.Logf("gdb output:\n%s", got)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("gdb exited with error: %v", err)
|
|
|
|
|
}
|
2016-11-14 18:00:17 -05:00
|
|
|
|
|
|
|
|
sgot := string(got)
|
|
|
|
|
|
|
|
|
|
// Check that the backtrace matches the source code.
|
|
|
|
|
types := []string{
|
2018-09-13 01:46:39 +07:00
|
|
|
"[]main.astruct;",
|
|
|
|
|
"bucket<string,main.astruct>;",
|
|
|
|
|
"hash<string,main.astruct>;",
|
|
|
|
|
"main.astruct;",
|
|
|
|
|
"hash<string,main.astruct> * map[string]main.astruct;",
|
2016-11-14 18:00:17 -05:00
|
|
|
}
|
|
|
|
|
for _, name := range types {
|
|
|
|
|
if !strings.Contains(sgot, name) {
|
2020-05-26 15:55:54 -04:00
|
|
|
t.Fatalf("could not find %s in 'info typrs astruct' output", name)
|
2016-11-14 18:00:17 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-03 11:59:18 +02:00
|
|
|
|
|
|
|
|
const constsSource = `
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
const aConstant int = 42
|
|
|
|
|
const largeConstant uint64 = ^uint64(0)
|
|
|
|
|
const minusOne int64 = -1
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
println("hello world")
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
func TestGdbConst(t *testing.T) {
|
|
|
|
|
checkGdbEnvironment(t)
|
2017-12-01 19:26:33 +00:00
|
|
|
t.Parallel()
|
2020-06-02 17:53:47 -04:00
|
|
|
checkGdbVersion(t)
|
2017-09-03 11:59:18 +02:00
|
|
|
|
2021-04-07 20:16:58 +08:00
|
|
|
dir := t.TempDir()
|
2017-09-03 11:59:18 +02:00
|
|
|
|
|
|
|
|
// Build the source code.
|
|
|
|
|
src := filepath.Join(dir, "main.go")
|
2021-04-07 20:16:58 +08:00
|
|
|
err := os.WriteFile(src, []byte(constsSource), 0644)
|
2017-09-03 11:59:18 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to create file: %v", err)
|
|
|
|
|
}
|
2019-02-14 17:38:36 -05:00
|
|
|
cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-o", "a.exe", "main.go")
|
2017-09-03 11:59:18 +02:00
|
|
|
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",
|
2022-03-09 17:19:23 -05:00
|
|
|
"-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
|
2017-09-03 11:59:18 +02:00
|
|
|
"-ex", "set startup-with-shell off",
|
|
|
|
|
"-ex", "break main.main",
|
|
|
|
|
"-ex", "run",
|
|
|
|
|
"-ex", "print main.aConstant",
|
|
|
|
|
"-ex", "print main.largeConstant",
|
|
|
|
|
"-ex", "print main.minusOne",
|
2018-09-26 16:39:02 -04:00
|
|
|
"-ex", "print 'runtime.mSpanInUse'",
|
2017-10-09 10:08:54 -07:00
|
|
|
"-ex", "print 'runtime._PageSize'",
|
2017-09-03 11:59:18 +02:00
|
|
|
filepath.Join(dir, "a.exe"),
|
|
|
|
|
}
|
2020-05-26 15:55:54 -04:00
|
|
|
got, err := exec.Command("gdb", args...).CombinedOutput()
|
|
|
|
|
t.Logf("gdb output:\n%s", got)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("gdb exited with error: %v", err)
|
|
|
|
|
}
|
2017-09-03 11:59:18 +02:00
|
|
|
|
2018-09-26 21:10:21 +00:00
|
|
|
sgot := strings.ReplaceAll(string(got), "\r\n", "\n")
|
2017-09-03 11:59:18 +02:00
|
|
|
|
2017-10-09 10:08:54 -07:00
|
|
|
if !strings.Contains(sgot, "\n$1 = 42\n$2 = 18446744073709551615\n$3 = -1\n$4 = 1 '\\001'\n$5 = 8192") {
|
2017-09-03 11:59:18 +02:00
|
|
|
t.Fatalf("output mismatch")
|
|
|
|
|
}
|
|
|
|
|
}
|
runtime: perform crashes outside systemstack
CL 93658 moved stack trace printing inside a systemstack call to
sidestep complexity in case the runtime is in a inconsistent state.
Unfortunately, debuggers generating backtraces for a Go panic
will be confused and come up with a technical correct but useless
stack. This CL moves just the crash performing - typically a SIGABRT
signal - outside the systemstack call to improve backtraces.
Unfortunately, the crash function now needs to be marked nosplit and
that triggers the no split stackoverflow check. To work around that,
split fatalpanic in two: fatalthrow for runtime.throw and fatalpanic for
runtime.gopanic. Only Go panics really needs crashes on the right stack
and there is enough stack for gopanic.
Example program:
package main
import "runtime/debug"
func main() {
debug.SetTraceback("crash")
crash()
}
func crash() {
panic("panic!")
}
Before:
(lldb) bt
* thread #1, name = 'simple', stop reason = signal SIGABRT
* frame #0: 0x000000000044ffe4 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438cfb simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #4: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #5: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #6: 0x000000000042a980 simple at proc.go:1094
frame #7: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #8: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #9: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #10: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #11: 0x000000000042a980 simple at proc.go:1094
frame #12: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #13: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #14: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #15: 0x000000000042a980 simple at proc.go:1094
frame #16: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #17: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
After:
(lldb) bt
* thread #7, stop reason = signal SIGABRT
* frame #0: 0x0000000000450024 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438d1b simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ee9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004264e3 simple`runtime.fatalpanic(msgs=<unavailable>) at panic.go:664
frame #4: 0x0000000000425f1b simple`runtime.gopanic(e=<unavailable>) at panic.go:537
frame #5: 0x0000000000470c62 simple`main.crash at simple.go:11
frame #6: 0x0000000000470c00 simple`main.main at simple.go:6
frame #7: 0x0000000000427be7 simple`runtime.main at proc.go:198
frame #8: 0x000000000044ef91 simple`runtime.goexit at <autogenerated>:1
Updates #22716
Change-Id: Ib5fa35c13662c1dac2f1eac8b59c4a5824b98d92
Reviewed-on: https://go-review.googlesource.com/110065
Run-TryBot: Elias Naur <elias.naur@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2018-04-29 16:29:43 +02:00
|
|
|
|
|
|
|
|
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()
|
2020-06-02 17:53:47 -04:00
|
|
|
checkGdbVersion(t)
|
runtime: perform crashes outside systemstack
CL 93658 moved stack trace printing inside a systemstack call to
sidestep complexity in case the runtime is in a inconsistent state.
Unfortunately, debuggers generating backtraces for a Go panic
will be confused and come up with a technical correct but useless
stack. This CL moves just the crash performing - typically a SIGABRT
signal - outside the systemstack call to improve backtraces.
Unfortunately, the crash function now needs to be marked nosplit and
that triggers the no split stackoverflow check. To work around that,
split fatalpanic in two: fatalthrow for runtime.throw and fatalpanic for
runtime.gopanic. Only Go panics really needs crashes on the right stack
and there is enough stack for gopanic.
Example program:
package main
import "runtime/debug"
func main() {
debug.SetTraceback("crash")
crash()
}
func crash() {
panic("panic!")
}
Before:
(lldb) bt
* thread #1, name = 'simple', stop reason = signal SIGABRT
* frame #0: 0x000000000044ffe4 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438cfb simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #4: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #5: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #6: 0x000000000042a980 simple at proc.go:1094
frame #7: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #8: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #9: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #10: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #11: 0x000000000042a980 simple at proc.go:1094
frame #12: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #13: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #14: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #15: 0x000000000042a980 simple at proc.go:1094
frame #16: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #17: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
After:
(lldb) bt
* thread #7, stop reason = signal SIGABRT
* frame #0: 0x0000000000450024 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438d1b simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ee9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004264e3 simple`runtime.fatalpanic(msgs=<unavailable>) at panic.go:664
frame #4: 0x0000000000425f1b simple`runtime.gopanic(e=<unavailable>) at panic.go:537
frame #5: 0x0000000000470c62 simple`main.crash at simple.go:11
frame #6: 0x0000000000470c00 simple`main.main at simple.go:6
frame #7: 0x0000000000427be7 simple`runtime.main at proc.go:198
frame #8: 0x000000000044ef91 simple`runtime.goexit at <autogenerated>:1
Updates #22716
Change-Id: Ib5fa35c13662c1dac2f1eac8b59c4a5824b98d92
Reviewed-on: https://go-review.googlesource.com/110065
Run-TryBot: Elias Naur <elias.naur@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2018-04-29 16:29:43 +02:00
|
|
|
|
2021-04-07 20:16:58 +08:00
|
|
|
dir := t.TempDir()
|
runtime: perform crashes outside systemstack
CL 93658 moved stack trace printing inside a systemstack call to
sidestep complexity in case the runtime is in a inconsistent state.
Unfortunately, debuggers generating backtraces for a Go panic
will be confused and come up with a technical correct but useless
stack. This CL moves just the crash performing - typically a SIGABRT
signal - outside the systemstack call to improve backtraces.
Unfortunately, the crash function now needs to be marked nosplit and
that triggers the no split stackoverflow check. To work around that,
split fatalpanic in two: fatalthrow for runtime.throw and fatalpanic for
runtime.gopanic. Only Go panics really needs crashes on the right stack
and there is enough stack for gopanic.
Example program:
package main
import "runtime/debug"
func main() {
debug.SetTraceback("crash")
crash()
}
func crash() {
panic("panic!")
}
Before:
(lldb) bt
* thread #1, name = 'simple', stop reason = signal SIGABRT
* frame #0: 0x000000000044ffe4 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438cfb simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #4: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #5: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #6: 0x000000000042a980 simple at proc.go:1094
frame #7: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #8: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #9: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #10: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #11: 0x000000000042a980 simple at proc.go:1094
frame #12: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #13: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #14: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #15: 0x000000000042a980 simple at proc.go:1094
frame #16: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #17: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
After:
(lldb) bt
* thread #7, stop reason = signal SIGABRT
* frame #0: 0x0000000000450024 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438d1b simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ee9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004264e3 simple`runtime.fatalpanic(msgs=<unavailable>) at panic.go:664
frame #4: 0x0000000000425f1b simple`runtime.gopanic(e=<unavailable>) at panic.go:537
frame #5: 0x0000000000470c62 simple`main.crash at simple.go:11
frame #6: 0x0000000000470c00 simple`main.main at simple.go:6
frame #7: 0x0000000000427be7 simple`runtime.main at proc.go:198
frame #8: 0x000000000044ef91 simple`runtime.goexit at <autogenerated>:1
Updates #22716
Change-Id: Ib5fa35c13662c1dac2f1eac8b59c4a5824b98d92
Reviewed-on: https://go-review.googlesource.com/110065
Run-TryBot: Elias Naur <elias.naur@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2018-04-29 16:29:43 +02:00
|
|
|
|
|
|
|
|
// Build the source code.
|
|
|
|
|
src := filepath.Join(dir, "main.go")
|
2021-04-07 20:16:58 +08:00
|
|
|
err := os.WriteFile(src, []byte(panicSource), 0644)
|
runtime: perform crashes outside systemstack
CL 93658 moved stack trace printing inside a systemstack call to
sidestep complexity in case the runtime is in a inconsistent state.
Unfortunately, debuggers generating backtraces for a Go panic
will be confused and come up with a technical correct but useless
stack. This CL moves just the crash performing - typically a SIGABRT
signal - outside the systemstack call to improve backtraces.
Unfortunately, the crash function now needs to be marked nosplit and
that triggers the no split stackoverflow check. To work around that,
split fatalpanic in two: fatalthrow for runtime.throw and fatalpanic for
runtime.gopanic. Only Go panics really needs crashes on the right stack
and there is enough stack for gopanic.
Example program:
package main
import "runtime/debug"
func main() {
debug.SetTraceback("crash")
crash()
}
func crash() {
panic("panic!")
}
Before:
(lldb) bt
* thread #1, name = 'simple', stop reason = signal SIGABRT
* frame #0: 0x000000000044ffe4 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438cfb simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #4: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #5: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #6: 0x000000000042a980 simple at proc.go:1094
frame #7: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #8: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #9: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #10: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #11: 0x000000000042a980 simple at proc.go:1094
frame #12: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #13: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #14: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #15: 0x000000000042a980 simple at proc.go:1094
frame #16: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #17: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
After:
(lldb) bt
* thread #7, stop reason = signal SIGABRT
* frame #0: 0x0000000000450024 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438d1b simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ee9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004264e3 simple`runtime.fatalpanic(msgs=<unavailable>) at panic.go:664
frame #4: 0x0000000000425f1b simple`runtime.gopanic(e=<unavailable>) at panic.go:537
frame #5: 0x0000000000470c62 simple`main.crash at simple.go:11
frame #6: 0x0000000000470c00 simple`main.main at simple.go:6
frame #7: 0x0000000000427be7 simple`runtime.main at proc.go:198
frame #8: 0x000000000044ef91 simple`runtime.goexit at <autogenerated>:1
Updates #22716
Change-Id: Ib5fa35c13662c1dac2f1eac8b59c4a5824b98d92
Reviewed-on: https://go-review.googlesource.com/110065
Run-TryBot: Elias Naur <elias.naur@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2018-04-29 16:29:43 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to create file: %v", err)
|
|
|
|
|
}
|
2019-02-14 17:38:36 -05:00
|
|
|
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe", "main.go")
|
runtime: perform crashes outside systemstack
CL 93658 moved stack trace printing inside a systemstack call to
sidestep complexity in case the runtime is in a inconsistent state.
Unfortunately, debuggers generating backtraces for a Go panic
will be confused and come up with a technical correct but useless
stack. This CL moves just the crash performing - typically a SIGABRT
signal - outside the systemstack call to improve backtraces.
Unfortunately, the crash function now needs to be marked nosplit and
that triggers the no split stackoverflow check. To work around that,
split fatalpanic in two: fatalthrow for runtime.throw and fatalpanic for
runtime.gopanic. Only Go panics really needs crashes on the right stack
and there is enough stack for gopanic.
Example program:
package main
import "runtime/debug"
func main() {
debug.SetTraceback("crash")
crash()
}
func crash() {
panic("panic!")
}
Before:
(lldb) bt
* thread #1, name = 'simple', stop reason = signal SIGABRT
* frame #0: 0x000000000044ffe4 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438cfb simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #4: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #5: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #6: 0x000000000042a980 simple at proc.go:1094
frame #7: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #8: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #9: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #10: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #11: 0x000000000042a980 simple at proc.go:1094
frame #12: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #13: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #14: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #15: 0x000000000042a980 simple at proc.go:1094
frame #16: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #17: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
After:
(lldb) bt
* thread #7, stop reason = signal SIGABRT
* frame #0: 0x0000000000450024 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438d1b simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ee9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004264e3 simple`runtime.fatalpanic(msgs=<unavailable>) at panic.go:664
frame #4: 0x0000000000425f1b simple`runtime.gopanic(e=<unavailable>) at panic.go:537
frame #5: 0x0000000000470c62 simple`main.crash at simple.go:11
frame #6: 0x0000000000470c00 simple`main.main at simple.go:6
frame #7: 0x0000000000427be7 simple`runtime.main at proc.go:198
frame #8: 0x000000000044ef91 simple`runtime.goexit at <autogenerated>:1
Updates #22716
Change-Id: Ib5fa35c13662c1dac2f1eac8b59c4a5824b98d92
Reviewed-on: https://go-review.googlesource.com/110065
Run-TryBot: Elias Naur <elias.naur@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2018-04-29 16:29:43 +02:00
|
|
|
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",
|
2022-03-09 17:19:23 -05:00
|
|
|
"-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
|
runtime: perform crashes outside systemstack
CL 93658 moved stack trace printing inside a systemstack call to
sidestep complexity in case the runtime is in a inconsistent state.
Unfortunately, debuggers generating backtraces for a Go panic
will be confused and come up with a technical correct but useless
stack. This CL moves just the crash performing - typically a SIGABRT
signal - outside the systemstack call to improve backtraces.
Unfortunately, the crash function now needs to be marked nosplit and
that triggers the no split stackoverflow check. To work around that,
split fatalpanic in two: fatalthrow for runtime.throw and fatalpanic for
runtime.gopanic. Only Go panics really needs crashes on the right stack
and there is enough stack for gopanic.
Example program:
package main
import "runtime/debug"
func main() {
debug.SetTraceback("crash")
crash()
}
func crash() {
panic("panic!")
}
Before:
(lldb) bt
* thread #1, name = 'simple', stop reason = signal SIGABRT
* frame #0: 0x000000000044ffe4 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438cfb simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #4: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #5: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #6: 0x000000000042a980 simple at proc.go:1094
frame #7: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #8: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #9: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #10: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #11: 0x000000000042a980 simple at proc.go:1094
frame #12: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #13: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #14: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #15: 0x000000000042a980 simple at proc.go:1094
frame #16: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #17: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
After:
(lldb) bt
* thread #7, stop reason = signal SIGABRT
* frame #0: 0x0000000000450024 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438d1b simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ee9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004264e3 simple`runtime.fatalpanic(msgs=<unavailable>) at panic.go:664
frame #4: 0x0000000000425f1b simple`runtime.gopanic(e=<unavailable>) at panic.go:537
frame #5: 0x0000000000470c62 simple`main.crash at simple.go:11
frame #6: 0x0000000000470c00 simple`main.main at simple.go:6
frame #7: 0x0000000000427be7 simple`runtime.main at proc.go:198
frame #8: 0x000000000044ef91 simple`runtime.goexit at <autogenerated>:1
Updates #22716
Change-Id: Ib5fa35c13662c1dac2f1eac8b59c4a5824b98d92
Reviewed-on: https://go-review.googlesource.com/110065
Run-TryBot: Elias Naur <elias.naur@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2018-04-29 16:29:43 +02:00
|
|
|
"-ex", "set startup-with-shell off",
|
|
|
|
|
"-ex", "run",
|
|
|
|
|
"-ex", "backtrace",
|
|
|
|
|
filepath.Join(dir, "a.exe"),
|
|
|
|
|
}
|
2020-05-26 15:55:54 -04:00
|
|
|
got, err := exec.Command("gdb", args...).CombinedOutput()
|
|
|
|
|
t.Logf("gdb output:\n%s", got)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("gdb exited with error: %v", err)
|
|
|
|
|
}
|
runtime: perform crashes outside systemstack
CL 93658 moved stack trace printing inside a systemstack call to
sidestep complexity in case the runtime is in a inconsistent state.
Unfortunately, debuggers generating backtraces for a Go panic
will be confused and come up with a technical correct but useless
stack. This CL moves just the crash performing - typically a SIGABRT
signal - outside the systemstack call to improve backtraces.
Unfortunately, the crash function now needs to be marked nosplit and
that triggers the no split stackoverflow check. To work around that,
split fatalpanic in two: fatalthrow for runtime.throw and fatalpanic for
runtime.gopanic. Only Go panics really needs crashes on the right stack
and there is enough stack for gopanic.
Example program:
package main
import "runtime/debug"
func main() {
debug.SetTraceback("crash")
crash()
}
func crash() {
panic("panic!")
}
Before:
(lldb) bt
* thread #1, name = 'simple', stop reason = signal SIGABRT
* frame #0: 0x000000000044ffe4 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438cfb simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #4: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #5: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #6: 0x000000000042a980 simple at proc.go:1094
frame #7: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #8: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #9: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #10: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #11: 0x000000000042a980 simple at proc.go:1094
frame #12: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #13: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #14: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #15: 0x000000000042a980 simple at proc.go:1094
frame #16: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #17: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
After:
(lldb) bt
* thread #7, stop reason = signal SIGABRT
* frame #0: 0x0000000000450024 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438d1b simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ee9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004264e3 simple`runtime.fatalpanic(msgs=<unavailable>) at panic.go:664
frame #4: 0x0000000000425f1b simple`runtime.gopanic(e=<unavailable>) at panic.go:537
frame #5: 0x0000000000470c62 simple`main.crash at simple.go:11
frame #6: 0x0000000000470c00 simple`main.main at simple.go:6
frame #7: 0x0000000000427be7 simple`runtime.main at proc.go:198
frame #8: 0x000000000044ef91 simple`runtime.goexit at <autogenerated>:1
Updates #22716
Change-Id: Ib5fa35c13662c1dac2f1eac8b59c4a5824b98d92
Reviewed-on: https://go-review.googlesource.com/110065
Run-TryBot: Elias Naur <elias.naur@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2018-04-29 16:29:43 +02:00
|
|
|
|
|
|
|
|
// Check that the backtrace matches the source code.
|
|
|
|
|
bt := []string{
|
|
|
|
|
`crash`,
|
|
|
|
|
`main`,
|
|
|
|
|
}
|
|
|
|
|
for _, name := range bt {
|
2018-06-06 12:38:35 -04:00
|
|
|
s := fmt.Sprintf("(#.* .* in )?main\\.%v", name)
|
runtime: perform crashes outside systemstack
CL 93658 moved stack trace printing inside a systemstack call to
sidestep complexity in case the runtime is in a inconsistent state.
Unfortunately, debuggers generating backtraces for a Go panic
will be confused and come up with a technical correct but useless
stack. This CL moves just the crash performing - typically a SIGABRT
signal - outside the systemstack call to improve backtraces.
Unfortunately, the crash function now needs to be marked nosplit and
that triggers the no split stackoverflow check. To work around that,
split fatalpanic in two: fatalthrow for runtime.throw and fatalpanic for
runtime.gopanic. Only Go panics really needs crashes on the right stack
and there is enough stack for gopanic.
Example program:
package main
import "runtime/debug"
func main() {
debug.SetTraceback("crash")
crash()
}
func crash() {
panic("panic!")
}
Before:
(lldb) bt
* thread #1, name = 'simple', stop reason = signal SIGABRT
* frame #0: 0x000000000044ffe4 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438cfb simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #4: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #5: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #6: 0x000000000042a980 simple at proc.go:1094
frame #7: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #8: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #9: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #10: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #11: 0x000000000042a980 simple at proc.go:1094
frame #12: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #13: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #14: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #15: 0x000000000042a980 simple at proc.go:1094
frame #16: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #17: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
After:
(lldb) bt
* thread #7, stop reason = signal SIGABRT
* frame #0: 0x0000000000450024 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438d1b simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ee9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004264e3 simple`runtime.fatalpanic(msgs=<unavailable>) at panic.go:664
frame #4: 0x0000000000425f1b simple`runtime.gopanic(e=<unavailable>) at panic.go:537
frame #5: 0x0000000000470c62 simple`main.crash at simple.go:11
frame #6: 0x0000000000470c00 simple`main.main at simple.go:6
frame #7: 0x0000000000427be7 simple`runtime.main at proc.go:198
frame #8: 0x000000000044ef91 simple`runtime.goexit at <autogenerated>:1
Updates #22716
Change-Id: Ib5fa35c13662c1dac2f1eac8b59c4a5824b98d92
Reviewed-on: https://go-review.googlesource.com/110065
Run-TryBot: Elias Naur <elias.naur@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2018-04-29 16:29:43 +02:00
|
|
|
re := regexp.MustCompile(s)
|
|
|
|
|
if found := re.Find(got) != nil; !found {
|
2020-05-26 15:55:54 -04:00
|
|
|
t.Fatalf("could not find '%v' in backtrace", s)
|
runtime: perform crashes outside systemstack
CL 93658 moved stack trace printing inside a systemstack call to
sidestep complexity in case the runtime is in a inconsistent state.
Unfortunately, debuggers generating backtraces for a Go panic
will be confused and come up with a technical correct but useless
stack. This CL moves just the crash performing - typically a SIGABRT
signal - outside the systemstack call to improve backtraces.
Unfortunately, the crash function now needs to be marked nosplit and
that triggers the no split stackoverflow check. To work around that,
split fatalpanic in two: fatalthrow for runtime.throw and fatalpanic for
runtime.gopanic. Only Go panics really needs crashes on the right stack
and there is enough stack for gopanic.
Example program:
package main
import "runtime/debug"
func main() {
debug.SetTraceback("crash")
crash()
}
func crash() {
panic("panic!")
}
Before:
(lldb) bt
* thread #1, name = 'simple', stop reason = signal SIGABRT
* frame #0: 0x000000000044ffe4 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438cfb simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #4: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #5: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #6: 0x000000000042a980 simple at proc.go:1094
frame #7: 0x0000000000438ec9 simple`runtime.crash at signal_unix.go:525
frame #8: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #9: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #10: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #11: 0x000000000042a980 simple at proc.go:1094
frame #12: 0x00000000004268f5 simple`runtime.dopanic_m(gp=<unavailable>, pc=<unavailable>, sp=<unavailable>) at panic.go:758
frame #13: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #14: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
frame #15: 0x000000000042a980 simple at proc.go:1094
frame #16: 0x000000000044bead simple`runtime.fatalpanic.func1 at panic.go:657
frame #17: 0x000000000044d066 simple`runtime.systemstack at <autogenerated>:1
After:
(lldb) bt
* thread #7, stop reason = signal SIGABRT
* frame #0: 0x0000000000450024 simple`runtime.raise at <autogenerated>:1
frame #1: 0x0000000000438d1b simple`runtime.dieFromSignal(sig=<unavailable>) at signal_unix.go:424
frame #2: 0x0000000000438ee9 simple`runtime.crash at signal_unix.go:525
frame #3: 0x00000000004264e3 simple`runtime.fatalpanic(msgs=<unavailable>) at panic.go:664
frame #4: 0x0000000000425f1b simple`runtime.gopanic(e=<unavailable>) at panic.go:537
frame #5: 0x0000000000470c62 simple`main.crash at simple.go:11
frame #6: 0x0000000000470c00 simple`main.main at simple.go:6
frame #7: 0x0000000000427be7 simple`runtime.main at proc.go:198
frame #8: 0x000000000044ef91 simple`runtime.goexit at <autogenerated>:1
Updates #22716
Change-Id: Ib5fa35c13662c1dac2f1eac8b59c4a5824b98d92
Reviewed-on: https://go-review.googlesource.com/110065
Run-TryBot: Elias Naur <elias.naur@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2018-04-29 16:29:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-27 11:04:21 +00:00
|
|
|
|
|
|
|
|
const InfCallstackSource = `
|
|
|
|
|
package main
|
|
|
|
|
import "C"
|
|
|
|
|
import "time"
|
|
|
|
|
|
|
|
|
|
func loop() {
|
|
|
|
|
for i := 0; i < 1000; i++ {
|
|
|
|
|
time.Sleep(time.Millisecond*5)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
go loop()
|
|
|
|
|
time.Sleep(time.Second * 1)
|
|
|
|
|
}
|
|
|
|
|
`
|
2020-04-20 20:33:19 +00:00
|
|
|
|
2020-03-27 11:04:21 +00:00
|
|
|
// TestGdbInfCallstack tests that gdb can unwind the callstack of cgo programs
|
|
|
|
|
// on arm64 platforms without endless frames of function 'crossfunc1'.
|
|
|
|
|
// https://golang.org/issue/37238
|
|
|
|
|
func TestGdbInfCallstack(t *testing.T) {
|
|
|
|
|
checkGdbEnvironment(t)
|
|
|
|
|
|
|
|
|
|
testenv.MustHaveCGO(t)
|
|
|
|
|
if runtime.GOARCH != "arm64" {
|
|
|
|
|
t.Skip("skipping infinite callstack test on non-arm64 arches")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t.Parallel()
|
2020-06-02 17:53:47 -04:00
|
|
|
checkGdbVersion(t)
|
2020-03-27 11:04:21 +00:00
|
|
|
|
2021-04-07 20:16:58 +08:00
|
|
|
dir := t.TempDir()
|
2020-03-27 11:04:21 +00:00
|
|
|
|
|
|
|
|
// Build the source code.
|
|
|
|
|
src := filepath.Join(dir, "main.go")
|
2021-04-07 20:16:58 +08:00
|
|
|
err := os.WriteFile(src, []byte(InfCallstackSource), 0644)
|
2020-03-27 11:04:21 +00:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to create file: %v", err)
|
|
|
|
|
}
|
|
|
|
|
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe", "main.go")
|
|
|
|
|
cmd.Dir = dir
|
|
|
|
|
out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("building source %v\n%s", err, out)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Execute gdb commands.
|
|
|
|
|
// 'setg_gcc' is the first point where we can reproduce the issue with just one 'run' command.
|
|
|
|
|
args := []string{"-nx", "-batch",
|
2022-03-09 17:19:23 -05:00
|
|
|
"-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
|
2020-03-27 11:04:21 +00:00
|
|
|
"-ex", "set startup-with-shell off",
|
|
|
|
|
"-ex", "break setg_gcc",
|
|
|
|
|
"-ex", "run",
|
|
|
|
|
"-ex", "backtrace 3",
|
|
|
|
|
"-ex", "disable 1",
|
|
|
|
|
"-ex", "continue",
|
|
|
|
|
filepath.Join(dir, "a.exe"),
|
|
|
|
|
}
|
2020-05-26 15:55:54 -04:00
|
|
|
got, err := exec.Command("gdb", args...).CombinedOutput()
|
|
|
|
|
t.Logf("gdb output:\n%s", got)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("gdb exited with error: %v", err)
|
|
|
|
|
}
|
2020-03-27 11:04:21 +00:00
|
|
|
|
|
|
|
|
// Check that the backtrace matches
|
|
|
|
|
// We check the 3 inner most frames only as they are present certainly, according to gcc_<OS>_arm64.c
|
|
|
|
|
bt := []string{
|
|
|
|
|
`setg_gcc`,
|
|
|
|
|
`crosscall1`,
|
|
|
|
|
`threadentry`,
|
|
|
|
|
}
|
|
|
|
|
for i, name := range bt {
|
|
|
|
|
s := fmt.Sprintf("#%v.*%v", i, name)
|
|
|
|
|
re := regexp.MustCompile(s)
|
|
|
|
|
if found := re.Find(got) != nil; !found {
|
2020-05-26 15:55:54 -04:00
|
|
|
t.Fatalf("could not find '%v' in backtrace", s)
|
2020-03-27 11:04:21 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|