github.com/aloncn/graphics-go@v0.0.1/src/runtime/runtime-gdb_test.go (about) 1 package runtime_test 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "regexp" 11 "runtime" 12 "strconv" 13 "testing" 14 ) 15 16 func checkGdbPython(t *testing.T) { 17 cmd := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", "python import sys; print('go gdb python support')") 18 out, err := cmd.CombinedOutput() 19 20 if err != nil { 21 t.Skipf("skipping due to issue running gdb: %v", err) 22 } 23 if string(out) != "go gdb python support\n" { 24 t.Skipf("skipping due to lack of python gdb support: %s", out) 25 } 26 27 // Issue 11214 reports various failures with older versions of gdb. 28 out, err = exec.Command("gdb", "--version").CombinedOutput() 29 re := regexp.MustCompile(`([0-9]+)\.([0-9]+)`) 30 matches := re.FindSubmatch(out) 31 if len(matches) < 3 { 32 t.Skipf("skipping: can't determine gdb version from\n%s\n", out) 33 } 34 major, err1 := strconv.Atoi(string(matches[1])) 35 minor, err2 := strconv.Atoi(string(matches[2])) 36 if err1 != nil || err2 != nil { 37 t.Skipf("skipping: can't determine gdb version: %v, %v", err1, err2) 38 } 39 if major < 7 || (major == 7 && minor < 7) { 40 t.Skipf("skipping: gdb version %d.%d too old", major, minor) 41 } 42 t.Logf("gdb version %d.%d", major, minor) 43 } 44 45 const helloSource = ` 46 package main 47 import "fmt" 48 func main() { 49 mapvar := make(map[string]string,5) 50 mapvar["abc"] = "def" 51 mapvar["ghi"] = "jkl" 52 strvar := "abc" 53 ptrvar := &strvar 54 fmt.Println("hi") // line 10 55 _ = ptrvar 56 } 57 ` 58 59 func TestGdbPython(t *testing.T) { 60 if runtime.GOOS == "darwin" { 61 t.Skip("gdb does not work on darwin") 62 } 63 if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final { 64 t.Skip("gdb test can fail with GOROOT_FINAL pending") 65 } 66 67 checkGdbPython(t) 68 69 dir, err := ioutil.TempDir("", "go-build") 70 if err != nil { 71 t.Fatalf("failed to create temp directory: %v", err) 72 } 73 defer os.RemoveAll(dir) 74 75 src := filepath.Join(dir, "main.go") 76 err = ioutil.WriteFile(src, []byte(helloSource), 0644) 77 if err != nil { 78 t.Fatalf("failed to create file: %v", err) 79 } 80 81 cmd := exec.Command("go", "build", "-o", "a.exe") 82 cmd.Dir = dir 83 out, err := testEnv(cmd).CombinedOutput() 84 if err != nil { 85 t.Fatalf("building source %v\n%s", err, out) 86 } 87 88 args := []string{"-nx", "-q", "--batch", "-iex", 89 fmt.Sprintf("add-auto-load-safe-path %s/src/runtime", runtime.GOROOT()), 90 "-ex", "info auto-load python-scripts", 91 "-ex", "br main.go:10", 92 "-ex", "run", 93 "-ex", "echo BEGIN info goroutines\n", 94 "-ex", "info goroutines", 95 "-ex", "echo END\n", 96 "-ex", "echo BEGIN print mapvar\n", 97 "-ex", "print mapvar", 98 "-ex", "echo END\n", 99 "-ex", "echo BEGIN print strvar\n", 100 "-ex", "print strvar", 101 "-ex", "echo END\n", 102 "-ex", "echo BEGIN print ptrvar\n", 103 "-ex", "print ptrvar", 104 "-ex", "echo END\n"} 105 106 // without framepointer, gdb cannot backtrace our non-standard 107 // stack frames on RISC architectures. 108 canBackTrace := false 109 switch runtime.GOARCH { 110 case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64", "mips64", "mips64le": 111 canBackTrace = true 112 args = append(args, 113 "-ex", "echo BEGIN goroutine 2 bt\n", 114 "-ex", "goroutine 2 bt", 115 "-ex", "echo END\n") 116 } 117 118 args = append(args, filepath.Join(dir, "a.exe")) 119 got, _ := exec.Command("gdb", args...).CombinedOutput() 120 121 firstLine := bytes.SplitN(got, []byte("\n"), 2)[0] 122 if string(firstLine) != "Loading Go Runtime support." { 123 // This can happen when using all.bash with 124 // GOROOT_FINAL set, because the tests are run before 125 // the final installation of the files. 126 cmd := exec.Command("go", "env", "GOROOT") 127 cmd.Env = []string{} 128 out, err := cmd.CombinedOutput() 129 if err != nil && bytes.Contains(out, []byte("cannot find GOROOT")) { 130 t.Skipf("skipping because GOROOT=%s does not exist", runtime.GOROOT()) 131 } 132 133 _, file, _, _ := runtime.Caller(1) 134 135 t.Logf("package testing source file: %s", file) 136 t.Fatalf("failed to load Go runtime support: %s\n%s", firstLine, got) 137 } 138 139 // Extract named BEGIN...END blocks from output 140 partRe := regexp.MustCompile(`(?ms)^BEGIN ([^\n]*)\n(.*?)\nEND`) 141 blocks := map[string]string{} 142 for _, subs := range partRe.FindAllSubmatch(got, -1) { 143 blocks[string(subs[1])] = string(subs[2]) 144 } 145 146 infoGoroutinesRe := regexp.MustCompile(`\*\s+\d+\s+running\s+`) 147 if bl := blocks["info goroutines"]; !infoGoroutinesRe.MatchString(bl) { 148 t.Fatalf("info goroutines failed: %s", bl) 149 } 150 151 printMapvarRe := regexp.MustCompile(`\Q = map[string]string = {["abc"] = "def", ["ghi"] = "jkl"}\E$`) 152 if bl := blocks["print mapvar"]; !printMapvarRe.MatchString(bl) { 153 t.Fatalf("print mapvar failed: %s", bl) 154 } 155 156 strVarRe := regexp.MustCompile(`\Q = "abc"\E$`) 157 if bl := blocks["print strvar"]; !strVarRe.MatchString(bl) { 158 t.Fatalf("print strvar failed: %s", bl) 159 } 160 161 if bl := blocks["print ptrvar"]; !strVarRe.MatchString(bl) { 162 t.Fatalf("print ptrvar failed: %s", bl) 163 } 164 165 btGoroutineRe := regexp.MustCompile(`^#0\s+runtime.+at`) 166 if bl := blocks["goroutine 2 bt"]; canBackTrace && !btGoroutineRe.MatchString(bl) { 167 t.Fatalf("goroutine 2 bt failed: %s", bl) 168 } else if !canBackTrace { 169 t.Logf("gdb cannot backtrace for GOARCH=%s, skipped goroutine backtrace test", runtime.GOARCH) 170 } 171 }