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