blob: aabe52da3c42312c96fb0382a64867ef7eb23987 [file] [log] [blame]
Mikio Harac340f482016-05-27 17:34:22 +09001// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Paul Nasrata25af2e2015-01-14 14:32:01 -05005package runtime_test
6
7import (
Austin Clements54568682015-02-16 21:56:10 -05008 "bytes"
Shenghou Ma810a4992015-01-16 01:23:56 -05009 "fmt"
Michael Munday55154cf2016-04-13 13:34:41 -040010 "internal/testenv"
Paul Nasrata25af2e2015-01-14 14:32:01 -050011 "io/ioutil"
12 "os"
13 "os/exec"
14 "path/filepath"
Austin Clements54568682015-02-16 21:56:10 -050015 "regexp"
Shenghou Ma810a4992015-01-16 01:23:56 -050016 "runtime"
Ian Lance Tayloreb248c42015-07-23 22:40:30 -070017 "strconv"
Paul Nasrata25af2e2015-01-14 14:32:01 -050018 "testing"
19)
20
Michael Munday55154cf2016-04-13 13:34:41 -040021func checkGdbEnvironment(t *testing.T) {
22 testenv.MustHaveGoBuild(t)
23 if runtime.GOOS == "darwin" {
24 t.Skip("gdb does not work on darwin")
Paul Nasrata25af2e2015-01-14 14:32:01 -050025 }
Michael Munday55154cf2016-04-13 13:34:41 -040026 if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
27 t.Skip("gdb test can fail with GOROOT_FINAL pending")
Paul Nasrata25af2e2015-01-14 14:32:01 -050028 }
Michael Munday55154cf2016-04-13 13:34:41 -040029}
Ian Lance Tayloreb248c42015-07-23 22:40:30 -070030
Michael Munday55154cf2016-04-13 13:34:41 -040031func checkGdbVersion(t *testing.T) {
Ian Lance Tayloreb248c42015-07-23 22:40:30 -070032 // Issue 11214 reports various failures with older versions of gdb.
Michael Munday55154cf2016-04-13 13:34:41 -040033 out, err := exec.Command("gdb", "--version").CombinedOutput()
34 if err != nil {
35 t.Skipf("skipping: error executing gdb: %v", err)
36 }
Ian Lance Tayloreb248c42015-07-23 22:40:30 -070037 re := regexp.MustCompile(`([0-9]+)\.([0-9]+)`)
38 matches := re.FindSubmatch(out)
39 if len(matches) < 3 {
40 t.Skipf("skipping: can't determine gdb version from\n%s\n", out)
41 }
42 major, err1 := strconv.Atoi(string(matches[1]))
43 minor, err2 := strconv.Atoi(string(matches[2]))
44 if err1 != nil || err2 != nil {
45 t.Skipf("skipping: can't determine gdb version: %v, %v", err1, err2)
46 }
47 if major < 7 || (major == 7 && minor < 7) {
48 t.Skipf("skipping: gdb version %d.%d too old", major, minor)
49 }
50 t.Logf("gdb version %d.%d", major, minor)
Paul Nasrata25af2e2015-01-14 14:32:01 -050051}
52
Michael Munday55154cf2016-04-13 13:34:41 -040053func checkGdbPython(t *testing.T) {
54 cmd := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", "python import sys; print('go gdb python support')")
55 out, err := cmd.CombinedOutput()
56
57 if err != nil {
58 t.Skipf("skipping due to issue running gdb: %v", err)
59 }
60 if string(out) != "go gdb python support\n" {
61 t.Skipf("skipping due to lack of python gdb support: %s", out)
62 }
63}
64
Paul Nasrata25af2e2015-01-14 14:32:01 -050065const helloSource = `
66package main
67import "fmt"
Jan Kratochvil1c82e232015-02-21 18:18:33 +010068func main() {
69 mapvar := make(map[string]string,5)
70 mapvar["abc"] = "def"
71 mapvar["ghi"] = "jkl"
Lee Packhamc45751e2015-03-30 17:36:49 +010072 strvar := "abc"
73 ptrvar := &strvar
74 fmt.Println("hi") // line 10
75 _ = ptrvar
Jan Kratochvil1c82e232015-02-21 18:18:33 +010076}
Paul Nasrata25af2e2015-01-14 14:32:01 -050077`
78
Austin Clements54568682015-02-16 21:56:10 -050079func TestGdbPython(t *testing.T) {
Michael Munday55154cf2016-04-13 13:34:41 -040080 checkGdbEnvironment(t)
81 checkGdbVersion(t)
Paul Nasrata25af2e2015-01-14 14:32:01 -050082 checkGdbPython(t)
83
84 dir, err := ioutil.TempDir("", "go-build")
85 if err != nil {
86 t.Fatalf("failed to create temp directory: %v", err)
87 }
88 defer os.RemoveAll(dir)
89
90 src := filepath.Join(dir, "main.go")
91 err = ioutil.WriteFile(src, []byte(helloSource), 0644)
92 if err != nil {
93 t.Fatalf("failed to create file: %v", err)
94 }
95
96 cmd := exec.Command("go", "build", "-o", "a.exe")
97 cmd.Dir = dir
Austin Clements4a1957d2015-05-26 15:21:18 -040098 out, err := testEnv(cmd).CombinedOutput()
Paul Nasrata25af2e2015-01-14 14:32:01 -050099 if err != nil {
100 t.Fatalf("building source %v\n%s", err, out)
101 }
102
Shenghou Ma7e49c812015-04-28 22:44:40 -0400103 args := []string{"-nx", "-q", "--batch", "-iex",
Shenghou Ma810a4992015-01-16 01:23:56 -0500104 fmt.Sprintf("add-auto-load-safe-path %s/src/runtime", runtime.GOROOT()),
Cuihtlauac ALVARADO2380a032016-05-17 09:27:00 +0200105 "-ex", "set startup-with-shell off",
Russ Cox83746fd2015-12-29 10:16:40 -0500106 "-ex", "info auto-load python-scripts",
Austin Clements966baed2016-05-26 11:05:01 -0400107 "-ex", "set python print-stack full",
Lee Packhamc45751e2015-03-30 17:36:49 +0100108 "-ex", "br main.go:10",
Austin Clements54568682015-02-16 21:56:10 -0500109 "-ex", "run",
110 "-ex", "echo BEGIN info goroutines\n",
111 "-ex", "info goroutines",
112 "-ex", "echo END\n",
Jan Kratochvil1c82e232015-02-21 18:18:33 +0100113 "-ex", "echo BEGIN print mapvar\n",
114 "-ex", "print mapvar",
115 "-ex", "echo END\n",
Lee Packhamc45751e2015-03-30 17:36:49 +0100116 "-ex", "echo BEGIN print strvar\n",
117 "-ex", "print strvar",
Shenghou Ma7e49c812015-04-28 22:44:40 -0400118 "-ex", "echo END\n"}
119
120 // without framepointer, gdb cannot backtrace our non-standard
121 // stack frames on RISC architectures.
122 canBackTrace := false
123 switch runtime.GOARCH {
Michael Munday0f08dd22016-03-18 19:02:52 -0400124 case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64", "mips64", "mips64le", "s390x":
Shenghou Ma7e49c812015-04-28 22:44:40 -0400125 canBackTrace = true
126 args = append(args,
127 "-ex", "echo BEGIN goroutine 2 bt\n",
128 "-ex", "goroutine 2 bt",
129 "-ex", "echo END\n")
130 }
131
132 args = append(args, filepath.Join(dir, "a.exe"))
133 got, _ := exec.Command("gdb", args...).CombinedOutput()
Austin Clements54568682015-02-16 21:56:10 -0500134
135 firstLine := bytes.SplitN(got, []byte("\n"), 2)[0]
136 if string(firstLine) != "Loading Go Runtime support." {
Ian Lance Taylor6a90b1d2015-07-10 05:20:20 -0700137 // This can happen when using all.bash with
138 // GOROOT_FINAL set, because the tests are run before
139 // the final installation of the files.
140 cmd := exec.Command("go", "env", "GOROOT")
141 cmd.Env = []string{}
142 out, err := cmd.CombinedOutput()
143 if err != nil && bytes.Contains(out, []byte("cannot find GOROOT")) {
144 t.Skipf("skipping because GOROOT=%s does not exist", runtime.GOROOT())
145 }
146
Russ Cox83746fd2015-12-29 10:16:40 -0500147 _, file, _, _ := runtime.Caller(1)
148
149 t.Logf("package testing source file: %s", file)
150 t.Fatalf("failed to load Go runtime support: %s\n%s", firstLine, got)
Austin Clements54568682015-02-16 21:56:10 -0500151 }
152
153 // Extract named BEGIN...END blocks from output
154 partRe := regexp.MustCompile(`(?ms)^BEGIN ([^\n]*)\n(.*?)\nEND`)
155 blocks := map[string]string{}
156 for _, subs := range partRe.FindAllSubmatch(got, -1) {
157 blocks[string(subs[1])] = string(subs[2])
158 }
159
Austin Clements1ab55a32015-02-17 15:01:43 -0500160 infoGoroutinesRe := regexp.MustCompile(`\*\s+\d+\s+running\s+`)
Austin Clements54568682015-02-16 21:56:10 -0500161 if bl := blocks["info goroutines"]; !infoGoroutinesRe.MatchString(bl) {
162 t.Fatalf("info goroutines failed: %s", bl)
Paul Nasrata25af2e2015-01-14 14:32:01 -0500163 }
Jan Kratochvil1c82e232015-02-21 18:18:33 +0100164
165 printMapvarRe := regexp.MustCompile(`\Q = map[string]string = {["abc"] = "def", ["ghi"] = "jkl"}\E$`)
166 if bl := blocks["print mapvar"]; !printMapvarRe.MatchString(bl) {
167 t.Fatalf("print mapvar failed: %s", bl)
168 }
Lee Packhamc45751e2015-03-30 17:36:49 +0100169
170 strVarRe := regexp.MustCompile(`\Q = "abc"\E$`)
171 if bl := blocks["print strvar"]; !strVarRe.MatchString(bl) {
172 t.Fatalf("print strvar failed: %s", bl)
173 }
174
Derek Buitenhuis53840ad2015-04-10 15:13:04 -0400175 btGoroutineRe := regexp.MustCompile(`^#0\s+runtime.+at`)
Shenghou Ma7e49c812015-04-28 22:44:40 -0400176 if bl := blocks["goroutine 2 bt"]; canBackTrace && !btGoroutineRe.MatchString(bl) {
Derek Buitenhuis53840ad2015-04-10 15:13:04 -0400177 t.Fatalf("goroutine 2 bt failed: %s", bl)
Shenghou Ma7e49c812015-04-28 22:44:40 -0400178 } else if !canBackTrace {
179 t.Logf("gdb cannot backtrace for GOARCH=%s, skipped goroutine backtrace test", runtime.GOARCH)
Derek Buitenhuis53840ad2015-04-10 15:13:04 -0400180 }
Paul Nasrata25af2e2015-01-14 14:32:01 -0500181}
Michael Munday55154cf2016-04-13 13:34:41 -0400182
183const backtraceSource = `
184package main
185
186//go:noinline
187func aaa() bool { return bbb() }
188
189//go:noinline
190func bbb() bool { return ccc() }
191
192//go:noinline
193func ccc() bool { return ddd() }
194
195//go:noinline
196func ddd() bool { return f() }
197
198//go:noinline
199func eee() bool { return true }
200
201var f = eee
202
203func main() {
204 _ = aaa()
205}
206`
207
208// TestGdbBacktrace tests that gdb can unwind the stack correctly
209// using only the DWARF debug info.
210func TestGdbBacktrace(t *testing.T) {
211 checkGdbEnvironment(t)
212 checkGdbVersion(t)
213
Mikio Harac340f482016-05-27 17:34:22 +0900214 if runtime.GOOS == "netbsd" {
215 testenv.SkipFlaky(t, 15603)
216 }
217
Michael Munday55154cf2016-04-13 13:34:41 -0400218 dir, err := ioutil.TempDir("", "go-build")
219 if err != nil {
220 t.Fatalf("failed to create temp directory: %v", err)
221 }
222 defer os.RemoveAll(dir)
223
224 // Build the source code.
225 src := filepath.Join(dir, "main.go")
226 err = ioutil.WriteFile(src, []byte(backtraceSource), 0644)
227 if err != nil {
228 t.Fatalf("failed to create file: %v", err)
229 }
230 cmd := exec.Command("go", "build", "-o", "a.exe")
231 cmd.Dir = dir
232 out, err := testEnv(cmd).CombinedOutput()
233 if err != nil {
234 t.Fatalf("building source %v\n%s", err, out)
235 }
236
237 // Execute gdb commands.
238 args := []string{"-nx", "-batch",
Cuihtlauac ALVARADO2380a032016-05-17 09:27:00 +0200239 "-ex", "set startup-with-shell off",
Michael Munday55154cf2016-04-13 13:34:41 -0400240 "-ex", "break main.eee",
241 "-ex", "run",
242 "-ex", "backtrace",
243 "-ex", "continue",
244 filepath.Join(dir, "a.exe"),
245 }
246 got, _ := exec.Command("gdb", args...).CombinedOutput()
247
248 // Check that the backtrace matches the source code.
249 bt := []string{
250 "eee",
251 "ddd",
252 "ccc",
253 "bbb",
254 "aaa",
255 "main",
256 }
257 for i, name := range bt {
258 s := fmt.Sprintf("#%v.*main\\.%v", i, name)
259 re := regexp.MustCompile(s)
260 if found := re.Find(got) != nil; !found {
261 t.Errorf("could not find '%v' in backtrace", s)
262 t.Fatalf("gdb output:\n%v", string(got))
263 }
264 }
265}