Source file
src/runtime/runtime-gdb_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "bytes"
9 "fmt"
10 "internal/testenv"
11 "os"
12 "os/exec"
13 "path/filepath"
14 "regexp"
15 "runtime"
16 "strconv"
17 "strings"
18 "testing"
19 )
20
21
22
23
24
25
26
27 func checkGdbEnvironment(t *testing.T) {
28 testenv.MustHaveGoBuild(t)
29 switch runtime.GOOS {
30 case "darwin":
31 t.Skip("gdb does not work on darwin")
32 case "netbsd":
33 t.Skip("gdb does not work with threads on NetBSD; see https://golang.org/issue/22893 and https://gnats.netbsd.org/52548")
34 case "windows":
35 t.Skip("gdb tests fail on Windows: https://golang.org/issue/22687")
36 case "linux":
37 if runtime.GOARCH == "ppc64" {
38 t.Skip("skipping gdb tests on linux/ppc64; see https://golang.org/issue/17366")
39 }
40 if runtime.GOARCH == "mips" {
41 t.Skip("skipping gdb tests on linux/mips; see https://golang.org/issue/25939")
42 }
43 case "freebsd":
44 t.Skip("skipping gdb tests on FreeBSD; see https://golang.org/issue/29508")
45 case "aix":
46 if testing.Short() {
47 t.Skip("skipping gdb tests on AIX; see https://golang.org/issue/35710")
48 }
49 case "plan9":
50 t.Skip("there is no gdb on Plan 9")
51 }
52 if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
53 t.Skip("gdb test can fail with GOROOT_FINAL pending")
54 }
55 }
56
57 func checkGdbVersion(t *testing.T) {
58
59 out, err := exec.Command("gdb", "--version").CombinedOutput()
60 if err != nil {
61 t.Skipf("skipping: error executing gdb: %v", err)
62 }
63 re := regexp.MustCompile(`([0-9]+)\.([0-9]+)`)
64 matches := re.FindSubmatch(out)
65 if len(matches) < 3 {
66 t.Skipf("skipping: can't determine gdb version from\n%s\n", out)
67 }
68 major, err1 := strconv.Atoi(string(matches[1]))
69 minor, err2 := strconv.Atoi(string(matches[2]))
70 if err1 != nil || err2 != nil {
71 t.Skipf("skipping: can't determine gdb version: %v, %v", err1, err2)
72 }
73 if major < 7 || (major == 7 && minor < 7) {
74 t.Skipf("skipping: gdb version %d.%d too old", major, minor)
75 }
76 t.Logf("gdb version %d.%d", major, minor)
77 }
78
79 func checkGdbPython(t *testing.T) {
80 if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
81 t.Skip("skipping gdb python tests on illumos and solaris; see golang.org/issue/20821")
82 }
83
84 cmd := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", "python import sys; print('go gdb python support')")
85 out, err := cmd.CombinedOutput()
86
87 if err != nil {
88 t.Skipf("skipping due to issue running gdb: %v", err)
89 }
90 if strings.TrimSpace(string(out)) != "go gdb python support" {
91 t.Skipf("skipping due to lack of python gdb support: %s", out)
92 }
93 }
94
95
96
97 func checkCleanBacktrace(t *testing.T, backtrace string) {
98 backtrace = strings.TrimSpace(backtrace)
99 lines := strings.Split(backtrace, "\n")
100 if len(lines) == 0 {
101 t.Fatalf("empty backtrace")
102 }
103 for i, l := range lines {
104 if !strings.HasPrefix(l, fmt.Sprintf("#%v ", i)) {
105 t.Fatalf("malformed backtrace at line %v: %v", i, l)
106 }
107 }
108
109 }
110
111 const helloSource = `
112 import "fmt"
113 import "runtime"
114 var gslice []string
115 func main() {
116 mapvar := make(map[string]string, 13)
117 slicemap := make(map[string][]string,11)
118 chanint := make(chan int, 10)
119 chanstr := make(chan string, 10)
120 chanint <- 99
121 chanint <- 11
122 chanstr <- "spongepants"
123 chanstr <- "squarebob"
124 mapvar["abc"] = "def"
125 mapvar["ghi"] = "jkl"
126 slicemap["a"] = []string{"b","c","d"}
127 slicemap["e"] = []string{"f","g","h"}
128 strvar := "abc"
129 ptrvar := &strvar
130 slicevar := make([]string, 0, 16)
131 slicevar = append(slicevar, mapvar["abc"])
132 fmt.Println("hi")
133 runtime.KeepAlive(ptrvar)
134 _ = ptrvar // set breakpoint here
135 gslice = slicevar
136 fmt.Printf("%v, %v, %v\n", slicemap, <-chanint, <-chanstr)
137 runtime.KeepAlive(mapvar)
138 } // END_OF_PROGRAM
139 `
140
141 func lastLine(src []byte) int {
142 eop := []byte("END_OF_PROGRAM")
143 for i, l := range bytes.Split(src, []byte("\n")) {
144 if bytes.Contains(l, eop) {
145 return i
146 }
147 }
148 return 0
149 }
150
151 func TestGdbPython(t *testing.T) {
152 testGdbPython(t, false)
153 }
154
155 func TestGdbPythonCgo(t *testing.T) {
156 if strings.HasPrefix(runtime.GOARCH, "mips") {
157 testenv.SkipFlaky(t, 37794)
158 }
159 testGdbPython(t, true)
160 }
161
162 func testGdbPython(t *testing.T, cgo bool) {
163 if cgo {
164 testenv.MustHaveCGO(t)
165 }
166
167 checkGdbEnvironment(t)
168 t.Parallel()
169 checkGdbVersion(t)
170 checkGdbPython(t)
171
172 dir := t.TempDir()
173
174 var buf bytes.Buffer
175 buf.WriteString("package main\n")
176 if cgo {
177 buf.WriteString(`import "C"` + "\n")
178 }
179 buf.WriteString(helloSource)
180
181 src := buf.Bytes()
182
183
184 var bp int
185 lines := bytes.Split(src, []byte("\n"))
186 for i, line := range lines {
187 if bytes.Contains(line, []byte("breakpoint")) {
188 bp = i
189 break
190 }
191 }
192
193 err := os.WriteFile(filepath.Join(dir, "main.go"), src, 0644)
194 if err != nil {
195 t.Fatalf("failed to create file: %v", err)
196 }
197 nLines := lastLine(src)
198
199 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe", "main.go")
200 cmd.Dir = dir
201 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
202 if err != nil {
203 t.Fatalf("building source %v\n%s", err, out)
204 }
205
206 args := []string{"-nx", "-q", "--batch",
207 "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"),
208 "-ex", "set startup-with-shell off",
209 "-ex", "set print thread-events off",
210 }
211 if cgo {
212
213
214
215
216
217 args = append(args,
218 "-ex", "source "+filepath.Join(runtime.GOROOT(), "src", "runtime", "runtime-gdb.py"),
219 )
220 } else {
221 args = append(args,
222 "-ex", "info auto-load python-scripts",
223 )
224 }
225 args = append(args,
226 "-ex", "set python print-stack full",
227 "-ex", fmt.Sprintf("br main.go:%d", bp),
228 "-ex", "run",
229 "-ex", "echo BEGIN info goroutines\n",
230 "-ex", "info goroutines",
231 "-ex", "echo END\n",
232 "-ex", "echo BEGIN print mapvar\n",
233 "-ex", "print mapvar",
234 "-ex", "echo END\n",
235 "-ex", "echo BEGIN print slicemap\n",
236 "-ex", "print slicemap",
237 "-ex", "echo END\n",
238 "-ex", "echo BEGIN print strvar\n",
239 "-ex", "print strvar",
240 "-ex", "echo END\n",
241 "-ex", "echo BEGIN print chanint\n",
242 "-ex", "print chanint",
243 "-ex", "echo END\n",
244 "-ex", "echo BEGIN print chanstr\n",
245 "-ex", "print chanstr",
246 "-ex", "echo END\n",
247 "-ex", "echo BEGIN info locals\n",
248 "-ex", "info locals",
249 "-ex", "echo END\n",
250 "-ex", "echo BEGIN goroutine 1 bt\n",
251 "-ex", "goroutine 1 bt",
252 "-ex", "echo END\n",
253 "-ex", "echo BEGIN goroutine all bt\n",
254 "-ex", "goroutine all bt",
255 "-ex", "echo END\n",
256 "-ex", "clear main.go:15",
257 "-ex", fmt.Sprintf("br main.go:%d", nLines),
258 "-ex", "c",
259 "-ex", "echo BEGIN goroutine 1 bt at the end\n",
260 "-ex", "goroutine 1 bt",
261 "-ex", "echo END\n",
262 filepath.Join(dir, "a.exe"),
263 )
264 got, err := exec.Command("gdb", args...).CombinedOutput()
265 t.Logf("gdb output:\n%s", got)
266 if err != nil {
267 t.Fatalf("gdb exited with error: %v", err)
268 }
269
270 firstLine, _, _ := bytes.Cut(got, []byte("\n"))
271 if string(firstLine) != "Loading Go Runtime support." {
272
273
274
275 cmd := exec.Command(testenv.GoToolPath(t), "env", "GOROOT")
276 cmd.Env = []string{}
277 out, err := cmd.CombinedOutput()
278 if err != nil && bytes.Contains(out, []byte("cannot find GOROOT")) {
279 t.Skipf("skipping because GOROOT=%s does not exist", runtime.GOROOT())
280 }
281
282 _, file, _, _ := runtime.Caller(1)
283
284 t.Logf("package testing source file: %s", file)
285 t.Fatalf("failed to load Go runtime support: %s\n%s", firstLine, got)
286 }
287
288
289 partRe := regexp.MustCompile(`(?ms)^BEGIN ([^\n]*)\n(.*?)\nEND`)
290 blocks := map[string]string{}
291 for _, subs := range partRe.FindAllSubmatch(got, -1) {
292 blocks[string(subs[1])] = string(subs[2])
293 }
294
295 infoGoroutinesRe := regexp.MustCompile(`\*\s+\d+\s+running\s+`)
296 if bl := blocks["info goroutines"]; !infoGoroutinesRe.MatchString(bl) {
297 t.Fatalf("info goroutines failed: %s", bl)
298 }
299
300 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"}$`)
301 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"}$`)
302 if bl := blocks["print mapvar"]; !printMapvarRe1.MatchString(bl) &&
303 !printMapvarRe2.MatchString(bl) {
304 t.Fatalf("print mapvar failed: %s", bl)
305 }
306
307
308 sliceMapSfx1 := `map[string][]string = {["e"] = []string = {"f", "g", "h"}, ["a"] = []string = {"b", "c", "d"}}`
309 sliceMapSfx2 := `map[string][]string = {["a"] = []string = {"b", "c", "d"}, ["e"] = []string = {"f", "g", "h"}}`
310 if bl := strings.ReplaceAll(blocks["print slicemap"], " ", " "); !strings.HasSuffix(bl, sliceMapSfx1) && !strings.HasSuffix(bl, sliceMapSfx2) {
311 t.Fatalf("print slicemap failed: %s", bl)
312 }
313
314 chanIntSfx := `chan int = {99, 11}`
315 if bl := strings.ReplaceAll(blocks["print chanint"], " ", " "); !strings.HasSuffix(bl, chanIntSfx) {
316 t.Fatalf("print chanint failed: %s", bl)
317 }
318
319 chanStrSfx := `chan string = {"spongepants", "squarebob"}`
320 if bl := strings.ReplaceAll(blocks["print chanstr"], " ", " "); !strings.HasSuffix(bl, chanStrSfx) {
321 t.Fatalf("print chanstr failed: %s", bl)
322 }
323
324 strVarRe := regexp.MustCompile(`^\$[0-9]+ = (0x[0-9a-f]+\s+)?"abc"$`)
325 if bl := blocks["print strvar"]; !strVarRe.MatchString(bl) {
326 t.Fatalf("print strvar failed: %s", bl)
327 }
328
329
330
331
332
333
334
335
336
337
338
339
340
341 if bl := blocks["info locals"]; !strings.Contains(bl, "slicevar") ||
342 !strings.Contains(bl, "mapvar") ||
343 !strings.Contains(bl, "strvar") {
344 t.Fatalf("info locals failed: %s", bl)
345 }
346
347
348 checkCleanBacktrace(t, blocks["goroutine 1 bt"])
349 checkCleanBacktrace(t, blocks["goroutine 1 bt at the end"])
350
351 btGoroutine1Re := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?main\.main.+at`)
352 if bl := blocks["goroutine 1 bt"]; !btGoroutine1Re.MatchString(bl) {
353 t.Fatalf("goroutine 1 bt failed: %s", bl)
354 }
355
356 if bl := blocks["goroutine all bt"]; !btGoroutine1Re.MatchString(bl) {
357 t.Fatalf("goroutine all bt failed: %s", bl)
358 }
359
360 btGoroutine1AtTheEndRe := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?main\.main.+at`)
361 if bl := blocks["goroutine 1 bt at the end"]; !btGoroutine1AtTheEndRe.MatchString(bl) {
362 t.Fatalf("goroutine 1 bt at the end failed: %s", bl)
363 }
364 }
365
366 const backtraceSource = `
367 package main
368
369 //go:noinline
370 func aaa() bool { return bbb() }
371
372 //go:noinline
373 func bbb() bool { return ccc() }
374
375 //go:noinline
376 func ccc() bool { return ddd() }
377
378 //go:noinline
379 func ddd() bool { return f() }
380
381 //go:noinline
382 func eee() bool { return true }
383
384 var f = eee
385
386 func main() {
387 _ = aaa()
388 }
389 `
390
391
392
393 func TestGdbBacktrace(t *testing.T) {
394 if runtime.GOOS == "netbsd" {
395 testenv.SkipFlaky(t, 15603)
396 }
397
398 checkGdbEnvironment(t)
399 t.Parallel()
400 checkGdbVersion(t)
401
402 dir := t.TempDir()
403
404
405 src := filepath.Join(dir, "main.go")
406 err := os.WriteFile(src, []byte(backtraceSource), 0644)
407 if err != nil {
408 t.Fatalf("failed to create file: %v", err)
409 }
410 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe", "main.go")
411 cmd.Dir = dir
412 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
413 if err != nil {
414 t.Fatalf("building source %v\n%s", err, out)
415 }
416
417
418 args := []string{"-nx", "-batch",
419 "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"),
420 "-ex", "set startup-with-shell off",
421 "-ex", "break main.eee",
422 "-ex", "run",
423 "-ex", "backtrace",
424 "-ex", "continue",
425 filepath.Join(dir, "a.exe"),
426 }
427 got, err := testenv.RunWithTimeout(t, exec.Command("gdb", args...))
428 t.Logf("gdb output:\n%s", got)
429 if err != nil {
430 if bytes.Contains(got, []byte("internal-error: wait returned unexpected status 0x0")) {
431
432 testenv.SkipFlaky(t, 43068)
433 }
434 if bytes.Contains(got, []byte("Couldn't get registers: No such process.")) {
435
436 testenv.SkipFlaky(t, 50838)
437 }
438 t.Fatalf("gdb exited with error: %v", err)
439 }
440
441
442 bt := []string{
443 "eee",
444 "ddd",
445 "ccc",
446 "bbb",
447 "aaa",
448 "main",
449 }
450 for i, name := range bt {
451 s := fmt.Sprintf("#%v.*main\\.%v", i, name)
452 re := regexp.MustCompile(s)
453 if found := re.Find(got) != nil; !found {
454 t.Fatalf("could not find '%v' in backtrace", s)
455 }
456 }
457 }
458
459 const autotmpTypeSource = `
460 package main
461
462 type astruct struct {
463 a, b int
464 }
465
466 func main() {
467 var iface interface{} = map[string]astruct{}
468 var iface2 interface{} = []astruct{}
469 println(iface, iface2)
470 }
471 `
472
473
474
475 func TestGdbAutotmpTypes(t *testing.T) {
476 checkGdbEnvironment(t)
477 t.Parallel()
478 checkGdbVersion(t)
479
480 if runtime.GOOS == "aix" && testing.Short() {
481 t.Skip("TestGdbAutotmpTypes is too slow on aix/ppc64")
482 }
483
484 dir := t.TempDir()
485
486
487 src := filepath.Join(dir, "main.go")
488 err := os.WriteFile(src, []byte(autotmpTypeSource), 0644)
489 if err != nil {
490 t.Fatalf("failed to create file: %v", err)
491 }
492 cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-o", "a.exe", "main.go")
493 cmd.Dir = dir
494 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
495 if err != nil {
496 t.Fatalf("building source %v\n%s", err, out)
497 }
498
499
500 args := []string{"-nx", "-batch",
501 "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"),
502 "-ex", "set startup-with-shell off",
503 "-ex", "break main.main",
504 "-ex", "run",
505 "-ex", "step",
506 "-ex", "info types astruct",
507 filepath.Join(dir, "a.exe"),
508 }
509 got, err := exec.Command("gdb", args...).CombinedOutput()
510 t.Logf("gdb output:\n%s", got)
511 if err != nil {
512 t.Fatalf("gdb exited with error: %v", err)
513 }
514
515 sgot := string(got)
516
517
518 types := []string{
519 "[]main.astruct;",
520 "bucket<string,main.astruct>;",
521 "hash<string,main.astruct>;",
522 "main.astruct;",
523 "hash<string,main.astruct> * map[string]main.astruct;",
524 }
525 for _, name := range types {
526 if !strings.Contains(sgot, name) {
527 t.Fatalf("could not find %s in 'info typrs astruct' output", name)
528 }
529 }
530 }
531
532 const constsSource = `
533 package main
534
535 const aConstant int = 42
536 const largeConstant uint64 = ^uint64(0)
537 const minusOne int64 = -1
538
539 func main() {
540 println("hello world")
541 }
542 `
543
544 func TestGdbConst(t *testing.T) {
545 checkGdbEnvironment(t)
546 t.Parallel()
547 checkGdbVersion(t)
548
549 dir := t.TempDir()
550
551
552 src := filepath.Join(dir, "main.go")
553 err := os.WriteFile(src, []byte(constsSource), 0644)
554 if err != nil {
555 t.Fatalf("failed to create file: %v", err)
556 }
557 cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-o", "a.exe", "main.go")
558 cmd.Dir = dir
559 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
560 if err != nil {
561 t.Fatalf("building source %v\n%s", err, out)
562 }
563
564
565 args := []string{"-nx", "-batch",
566 "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"),
567 "-ex", "set startup-with-shell off",
568 "-ex", "break main.main",
569 "-ex", "run",
570 "-ex", "print main.aConstant",
571 "-ex", "print main.largeConstant",
572 "-ex", "print main.minusOne",
573 "-ex", "print 'runtime.mSpanInUse'",
574 "-ex", "print 'runtime._PageSize'",
575 filepath.Join(dir, "a.exe"),
576 }
577 got, err := exec.Command("gdb", args...).CombinedOutput()
578 t.Logf("gdb output:\n%s", got)
579 if err != nil {
580 t.Fatalf("gdb exited with error: %v", err)
581 }
582
583 sgot := strings.ReplaceAll(string(got), "\r\n", "\n")
584
585 if !strings.Contains(sgot, "\n$1 = 42\n$2 = 18446744073709551615\n$3 = -1\n$4 = 1 '\\001'\n$5 = 8192") {
586 t.Fatalf("output mismatch")
587 }
588 }
589
590 const panicSource = `
591 package main
592
593 import "runtime/debug"
594
595 func main() {
596 debug.SetTraceback("crash")
597 crash()
598 }
599
600 func crash() {
601 panic("panic!")
602 }
603 `
604
605
606
607 func TestGdbPanic(t *testing.T) {
608 checkGdbEnvironment(t)
609 t.Parallel()
610 checkGdbVersion(t)
611
612 dir := t.TempDir()
613
614
615 src := filepath.Join(dir, "main.go")
616 err := os.WriteFile(src, []byte(panicSource), 0644)
617 if err != nil {
618 t.Fatalf("failed to create file: %v", err)
619 }
620 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe", "main.go")
621 cmd.Dir = dir
622 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
623 if err != nil {
624 t.Fatalf("building source %v\n%s", err, out)
625 }
626
627
628 args := []string{"-nx", "-batch",
629 "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"),
630 "-ex", "set startup-with-shell off",
631 "-ex", "run",
632 "-ex", "backtrace",
633 filepath.Join(dir, "a.exe"),
634 }
635 got, err := exec.Command("gdb", args...).CombinedOutput()
636 t.Logf("gdb output:\n%s", got)
637 if err != nil {
638 t.Fatalf("gdb exited with error: %v", err)
639 }
640
641
642 bt := []string{
643 `crash`,
644 `main`,
645 }
646 for _, name := range bt {
647 s := fmt.Sprintf("(#.* .* in )?main\\.%v", name)
648 re := regexp.MustCompile(s)
649 if found := re.Find(got) != nil; !found {
650 t.Fatalf("could not find '%v' in backtrace", s)
651 }
652 }
653 }
654
655 const InfCallstackSource = `
656 package main
657 import "C"
658 import "time"
659
660 func loop() {
661 for i := 0; i < 1000; i++ {
662 time.Sleep(time.Millisecond*5)
663 }
664 }
665
666 func main() {
667 go loop()
668 time.Sleep(time.Second * 1)
669 }
670 `
671
672
673
674
675 func TestGdbInfCallstack(t *testing.T) {
676 checkGdbEnvironment(t)
677
678 testenv.MustHaveCGO(t)
679 if runtime.GOARCH != "arm64" {
680 t.Skip("skipping infinite callstack test on non-arm64 arches")
681 }
682
683 t.Parallel()
684 checkGdbVersion(t)
685
686 dir := t.TempDir()
687
688
689 src := filepath.Join(dir, "main.go")
690 err := os.WriteFile(src, []byte(InfCallstackSource), 0644)
691 if err != nil {
692 t.Fatalf("failed to create file: %v", err)
693 }
694 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe", "main.go")
695 cmd.Dir = dir
696 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
697 if err != nil {
698 t.Fatalf("building source %v\n%s", err, out)
699 }
700
701
702
703 args := []string{"-nx", "-batch",
704 "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"),
705 "-ex", "set startup-with-shell off",
706 "-ex", "break setg_gcc",
707 "-ex", "run",
708 "-ex", "backtrace 3",
709 "-ex", "disable 1",
710 "-ex", "continue",
711 filepath.Join(dir, "a.exe"),
712 }
713 got, err := exec.Command("gdb", args...).CombinedOutput()
714 t.Logf("gdb output:\n%s", got)
715 if err != nil {
716 t.Fatalf("gdb exited with error: %v", err)
717 }
718
719
720
721 bt := []string{
722 `setg_gcc`,
723 `crosscall1`,
724 `threadentry`,
725 }
726 for i, name := range bt {
727 s := fmt.Sprintf("#%v.*%v", i, name)
728 re := regexp.MustCompile(s)
729 if found := re.Find(got) != nil; !found {
730 t.Fatalf("could not find '%v' in backtrace", s)
731 }
732 }
733 }
734
View as plain text