Source file
src/runtime/runtime-gdb_test.go
Documentation: runtime
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 runtime.GOARCH == "mips" || runtime.GOARCH == "mipsle" || runtime.GOARCH == "mips64" {
157 testenv.SkipFlaky(t, 18784)
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.SplitN(got, []byte("\n"), 2)[0]
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 := exec.Command("gdb", args...).CombinedOutput()
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 testenv.SkipFlaky(t, 43068)
432 }
433 t.Fatalf("gdb exited with error: %v", err)
434 }
435
436
437 bt := []string{
438 "eee",
439 "ddd",
440 "ccc",
441 "bbb",
442 "aaa",
443 "main",
444 }
445 for i, name := range bt {
446 s := fmt.Sprintf("#%v.*main\\.%v", i, name)
447 re := regexp.MustCompile(s)
448 if found := re.Find(got) != nil; !found {
449 t.Fatalf("could not find '%v' in backtrace", s)
450 }
451 }
452 }
453
454 const autotmpTypeSource = `
455 package main
456
457 type astruct struct {
458 a, b int
459 }
460
461 func main() {
462 var iface interface{} = map[string]astruct{}
463 var iface2 interface{} = []astruct{}
464 println(iface, iface2)
465 }
466 `
467
468
469
470 func TestGdbAutotmpTypes(t *testing.T) {
471 checkGdbEnvironment(t)
472 t.Parallel()
473 checkGdbVersion(t)
474
475 if runtime.GOOS == "aix" && testing.Short() {
476 t.Skip("TestGdbAutotmpTypes is too slow on aix/ppc64")
477 }
478
479 dir := t.TempDir()
480
481
482 src := filepath.Join(dir, "main.go")
483 err := os.WriteFile(src, []byte(autotmpTypeSource), 0644)
484 if err != nil {
485 t.Fatalf("failed to create file: %v", err)
486 }
487 cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-o", "a.exe", "main.go")
488 cmd.Dir = dir
489 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
490 if err != nil {
491 t.Fatalf("building source %v\n%s", err, out)
492 }
493
494
495 args := []string{"-nx", "-batch",
496 "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"),
497 "-ex", "set startup-with-shell off",
498 "-ex", "break main.main",
499 "-ex", "run",
500 "-ex", "step",
501 "-ex", "info types astruct",
502 filepath.Join(dir, "a.exe"),
503 }
504 got, err := exec.Command("gdb", args...).CombinedOutput()
505 t.Logf("gdb output:\n%s", got)
506 if err != nil {
507 t.Fatalf("gdb exited with error: %v", err)
508 }
509
510 sgot := string(got)
511
512
513 types := []string{
514 "[]main.astruct;",
515 "bucket<string,main.astruct>;",
516 "hash<string,main.astruct>;",
517 "main.astruct;",
518 "hash<string,main.astruct> * map[string]main.astruct;",
519 }
520 for _, name := range types {
521 if !strings.Contains(sgot, name) {
522 t.Fatalf("could not find %s in 'info typrs astruct' output", name)
523 }
524 }
525 }
526
527 const constsSource = `
528 package main
529
530 const aConstant int = 42
531 const largeConstant uint64 = ^uint64(0)
532 const minusOne int64 = -1
533
534 func main() {
535 println("hello world")
536 }
537 `
538
539 func TestGdbConst(t *testing.T) {
540 checkGdbEnvironment(t)
541 t.Parallel()
542 checkGdbVersion(t)
543
544 dir := t.TempDir()
545
546
547 src := filepath.Join(dir, "main.go")
548 err := os.WriteFile(src, []byte(constsSource), 0644)
549 if err != nil {
550 t.Fatalf("failed to create file: %v", err)
551 }
552 cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=all=-N -l", "-o", "a.exe", "main.go")
553 cmd.Dir = dir
554 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
555 if err != nil {
556 t.Fatalf("building source %v\n%s", err, out)
557 }
558
559
560 args := []string{"-nx", "-batch",
561 "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"),
562 "-ex", "set startup-with-shell off",
563 "-ex", "break main.main",
564 "-ex", "run",
565 "-ex", "print main.aConstant",
566 "-ex", "print main.largeConstant",
567 "-ex", "print main.minusOne",
568 "-ex", "print 'runtime.mSpanInUse'",
569 "-ex", "print 'runtime._PageSize'",
570 filepath.Join(dir, "a.exe"),
571 }
572 got, err := exec.Command("gdb", args...).CombinedOutput()
573 t.Logf("gdb output:\n%s", got)
574 if err != nil {
575 t.Fatalf("gdb exited with error: %v", err)
576 }
577
578 sgot := strings.ReplaceAll(string(got), "\r\n", "\n")
579
580 if !strings.Contains(sgot, "\n$1 = 42\n$2 = 18446744073709551615\n$3 = -1\n$4 = 1 '\\001'\n$5 = 8192") {
581 t.Fatalf("output mismatch")
582 }
583 }
584
585 const panicSource = `
586 package main
587
588 import "runtime/debug"
589
590 func main() {
591 debug.SetTraceback("crash")
592 crash()
593 }
594
595 func crash() {
596 panic("panic!")
597 }
598 `
599
600
601
602 func TestGdbPanic(t *testing.T) {
603 checkGdbEnvironment(t)
604 t.Parallel()
605 checkGdbVersion(t)
606
607 dir := t.TempDir()
608
609
610 src := filepath.Join(dir, "main.go")
611 err := os.WriteFile(src, []byte(panicSource), 0644)
612 if err != nil {
613 t.Fatalf("failed to create file: %v", err)
614 }
615 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe", "main.go")
616 cmd.Dir = dir
617 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
618 if err != nil {
619 t.Fatalf("building source %v\n%s", err, out)
620 }
621
622
623 args := []string{"-nx", "-batch",
624 "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"),
625 "-ex", "set startup-with-shell off",
626 "-ex", "run",
627 "-ex", "backtrace",
628 filepath.Join(dir, "a.exe"),
629 }
630 got, err := exec.Command("gdb", args...).CombinedOutput()
631 t.Logf("gdb output:\n%s", got)
632 if err != nil {
633 t.Fatalf("gdb exited with error: %v", err)
634 }
635
636
637 bt := []string{
638 `crash`,
639 `main`,
640 }
641 for _, name := range bt {
642 s := fmt.Sprintf("(#.* .* in )?main\\.%v", name)
643 re := regexp.MustCompile(s)
644 if found := re.Find(got) != nil; !found {
645 t.Fatalf("could not find '%v' in backtrace", s)
646 }
647 }
648 }
649
650 const InfCallstackSource = `
651 package main
652 import "C"
653 import "time"
654
655 func loop() {
656 for i := 0; i < 1000; i++ {
657 time.Sleep(time.Millisecond*5)
658 }
659 }
660
661 func main() {
662 go loop()
663 time.Sleep(time.Second * 1)
664 }
665 `
666
667
668
669
670 func TestGdbInfCallstack(t *testing.T) {
671 checkGdbEnvironment(t)
672
673 testenv.MustHaveCGO(t)
674 if runtime.GOARCH != "arm64" {
675 t.Skip("skipping infinite callstack test on non-arm64 arches")
676 }
677
678 t.Parallel()
679 checkGdbVersion(t)
680
681 dir := t.TempDir()
682
683
684 src := filepath.Join(dir, "main.go")
685 err := os.WriteFile(src, []byte(InfCallstackSource), 0644)
686 if err != nil {
687 t.Fatalf("failed to create file: %v", err)
688 }
689 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe", "main.go")
690 cmd.Dir = dir
691 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
692 if err != nil {
693 t.Fatalf("building source %v\n%s", err, out)
694 }
695
696
697
698 args := []string{"-nx", "-batch",
699 "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"),
700 "-ex", "set startup-with-shell off",
701 "-ex", "break setg_gcc",
702 "-ex", "run",
703 "-ex", "backtrace 3",
704 "-ex", "disable 1",
705 "-ex", "continue",
706 filepath.Join(dir, "a.exe"),
707 }
708 got, err := exec.Command("gdb", args...).CombinedOutput()
709 t.Logf("gdb output:\n%s", got)
710 if err != nil {
711 t.Fatalf("gdb exited with error: %v", err)
712 }
713
714
715
716 bt := []string{
717 `setg_gcc`,
718 `crosscall1`,
719 `threadentry`,
720 }
721 for i, name := range bt {
722 s := fmt.Sprintf("#%v.*%v", i, name)
723 re := regexp.MustCompile(s)
724 if found := re.Find(got) != nil; !found {
725 t.Fatalf("could not find '%v' in backtrace", s)
726 }
727 }
728 }
729
View as plain text