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  }