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