Home | History | Annotate | Download | only in runtime
      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 
     64 	checkGdbPython(t)
     65 
     66 	dir, err := ioutil.TempDir("", "go-build")
     67 	if err != nil {
     68 		t.Fatalf("failed to create temp directory: %v", err)
     69 	}
     70 	defer os.RemoveAll(dir)
     71 
     72 	src := filepath.Join(dir, "main.go")
     73 	err = ioutil.WriteFile(src, []byte(helloSource), 0644)
     74 	if err != nil {
     75 		t.Fatalf("failed to create file: %v", err)
     76 	}
     77 
     78 	cmd := exec.Command("go", "build", "-o", "a.exe")
     79 	cmd.Dir = dir
     80 	out, err := testEnv(cmd).CombinedOutput()
     81 	if err != nil {
     82 		t.Fatalf("building source %v\n%s", err, out)
     83 	}
     84 
     85 	args := []string{"-nx", "-q", "--batch", "-iex",
     86 		fmt.Sprintf("add-auto-load-safe-path %s/src/runtime", runtime.GOROOT()),
     87 		"-ex", "br main.go:10",
     88 		"-ex", "run",
     89 		"-ex", "echo BEGIN info goroutines\n",
     90 		"-ex", "info goroutines",
     91 		"-ex", "echo END\n",
     92 		"-ex", "echo BEGIN print mapvar\n",
     93 		"-ex", "print mapvar",
     94 		"-ex", "echo END\n",
     95 		"-ex", "echo BEGIN print strvar\n",
     96 		"-ex", "print strvar",
     97 		"-ex", "echo END\n",
     98 		"-ex", "echo BEGIN print ptrvar\n",
     99 		"-ex", "print ptrvar",
    100 		"-ex", "echo END\n"}
    101 
    102 	// without framepointer, gdb cannot backtrace our non-standard
    103 	// stack frames on RISC architectures.
    104 	canBackTrace := false
    105 	switch runtime.GOARCH {
    106 	case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64":
    107 		canBackTrace = true
    108 		args = append(args,
    109 			"-ex", "echo BEGIN goroutine 2 bt\n",
    110 			"-ex", "goroutine 2 bt",
    111 			"-ex", "echo END\n")
    112 	}
    113 
    114 	args = append(args, filepath.Join(dir, "a.exe"))
    115 	got, _ := exec.Command("gdb", args...).CombinedOutput()
    116 
    117 	firstLine := bytes.SplitN(got, []byte("\n"), 2)[0]
    118 	if string(firstLine) != "Loading Go Runtime support." {
    119 		// This can happen when using all.bash with
    120 		// GOROOT_FINAL set, because the tests are run before
    121 		// the final installation of the files.
    122 		cmd := exec.Command("go", "env", "GOROOT")
    123 		cmd.Env = []string{}
    124 		out, err := cmd.CombinedOutput()
    125 		if err != nil && bytes.Contains(out, []byte("cannot find GOROOT")) {
    126 			t.Skipf("skipping because GOROOT=%s does not exist", runtime.GOROOT())
    127 		}
    128 
    129 		t.Fatalf("failed to load Go runtime support: %s", firstLine)
    130 	}
    131 
    132 	// Extract named BEGIN...END blocks from output
    133 	partRe := regexp.MustCompile(`(?ms)^BEGIN ([^\n]*)\n(.*?)\nEND`)
    134 	blocks := map[string]string{}
    135 	for _, subs := range partRe.FindAllSubmatch(got, -1) {
    136 		blocks[string(subs[1])] = string(subs[2])
    137 	}
    138 
    139 	infoGoroutinesRe := regexp.MustCompile(`\*\s+\d+\s+running\s+`)
    140 	if bl := blocks["info goroutines"]; !infoGoroutinesRe.MatchString(bl) {
    141 		t.Fatalf("info goroutines failed: %s", bl)
    142 	}
    143 
    144 	printMapvarRe := regexp.MustCompile(`\Q = map[string]string = {["abc"] = "def", ["ghi"] = "jkl"}\E$`)
    145 	if bl := blocks["print mapvar"]; !printMapvarRe.MatchString(bl) {
    146 		t.Fatalf("print mapvar failed: %s", bl)
    147 	}
    148 
    149 	strVarRe := regexp.MustCompile(`\Q = "abc"\E$`)
    150 	if bl := blocks["print strvar"]; !strVarRe.MatchString(bl) {
    151 		t.Fatalf("print strvar failed: %s", bl)
    152 	}
    153 
    154 	if bl := blocks["print ptrvar"]; !strVarRe.MatchString(bl) {
    155 		t.Fatalf("print ptrvar failed: %s", bl)
    156 	}
    157 
    158 	btGoroutineRe := regexp.MustCompile(`^#0\s+runtime.+at`)
    159 	if bl := blocks["goroutine 2 bt"]; canBackTrace && !btGoroutineRe.MatchString(bl) {
    160 		t.Fatalf("goroutine 2 bt failed: %s", bl)
    161 	} else if !canBackTrace {
    162 		t.Logf("gdb cannot backtrace for GOARCH=%s, skipped goroutine backtrace test", runtime.GOARCH)
    163 	}
    164 }
    165