runtime: repair slice, string, and channel printing in gdb

"Something" changed the names of types in gdb, causing the
pretty-printer matchers to fail to match.  This tracks that
change.

Updated runtime-gdb_test.go to include a slice and a channel printing test.

(The straightforward printing of a slicevar doesn't work because
of compiler DWARF problems describing the slicevar, not gdb problems).

Change-Id: I21607a955b9c894f11ecf3763aea2a6dd59a3f42
Reviewed-on: https://go-review.googlesource.com/c/go/+/235926
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/src/runtime/runtime-gdb.py b/src/runtime/runtime-gdb.py
index 6139f99..b883d87 100644
--- a/src/runtime/runtime-gdb.py
+++ b/src/runtime/runtime-gdb.py
@@ -18,6 +18,7 @@
 from __future__ import print_function
 import re
 import sys
+import gdb
 
 print("Loading Go Runtime support.", file=sys.stderr)
 #http://python3porting.com/differences.html
@@ -27,6 +28,12 @@
 goobjfile = gdb.current_objfile() or gdb.objfiles()[0]
 goobjfile.pretty_printers = []
 
+# Older gdb renders some types differently.
+def oldnew(old, new):
+  if (gdb.VERSION[0] == '7'):
+    return old
+  return new
+
 # G state (runtime2.go)
 
 def read_runtime_const(varname, default):
@@ -102,7 +109,7 @@
 class StringTypePrinter:
 	"Pretty print Go strings."
 
-	pattern = re.compile(r'^struct string( \*)?$')
+	pattern = re.compile(oldnew(r'^struct string( \*)?$',r'^string$'))
 
 	def __init__(self, val):
 		self.val = val
@@ -118,7 +125,7 @@
 class SliceTypePrinter:
 	"Pretty print slices."
 
-	pattern = re.compile(r'^struct \[\]')
+	pattern = re.compile(oldnew(r'^struct \[\]',r'^\[\]'))
 
 	def __init__(self, val):
 		self.val = val
@@ -127,7 +134,7 @@
 		return 'array'
 
 	def to_string(self):
-		return str(self.val.type)[6:]  # skip 'struct '
+		return str(self.val.type)[oldnew(6,0):]  # skip 'struct ' for old gdb
 
 	def children(self):
 		sval = SliceValue(self.val)
@@ -195,7 +202,7 @@
 	to inspect their contents with this pretty printer.
 	"""
 
-	pattern = re.compile(r'^struct hchan<.*>$')
+	pattern = re.compile(oldnew(r'^struct hchan<.*>$',r'^chan '))
 
 	def __init__(self, val):
 		self.val = val
@@ -209,7 +216,7 @@
 	def children(self):
 		# see chan.c chanbuf(). et is the type stolen from hchan<T>::recvq->first->elem
 		et = [x.type for x in self.val['recvq']['first'].type.target().fields() if x.name == 'elem'][0]
-		ptr = (self.val.address + 1).cast(et.pointer())
+		ptr = (self.val.address["buf"]).cast(et)
 		for i in range(self.val["qcount"]):
 			j = (self.val["recvx"] + i) % self.val["dataqsiz"]
 			yield ('[{0}]'.format(i), (ptr + j).dereference())
@@ -229,8 +236,6 @@
 	return matcher
 
 goobjfile.pretty_printers.extend([makematcher(var) for var in vars().values() if hasattr(var, 'pattern')])
-
-
 #
 #  Utilities
 #
diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go
index 7cfd5b9..f1ff51d 100644
--- a/src/runtime/runtime-gdb_test.go
+++ b/src/runtime/runtime-gdb_test.go
@@ -55,7 +55,9 @@
 	}
 }
 
-func checkGdbVersion(t *testing.T) {
+// checkGdbVersion ensures gdb version is supported, and returns major version
+// to allow testing conditional on gdb version.
+func checkGdbVersion(t *testing.T) int {
 	// Issue 11214 reports various failures with older versions of gdb.
 	out, err := exec.Command("gdb", "--version").CombinedOutput()
 	if err != nil {
@@ -75,6 +77,7 @@
 		t.Skipf("skipping: gdb version %d.%d too old", major, minor)
 	}
 	t.Logf("gdb version %d.%d", major, minor)
+	return major
 }
 
 func checkGdbPython(t *testing.T) {
@@ -115,8 +118,17 @@
 var gslice []string
 func main() {
 	mapvar := make(map[string]string, 13)
+	slicemap := make(map[string][]string,11)
+    chanint := make(chan int, 10)
+    chanstr := make(chan string, 10)
+    chanint <- 99
+	chanint <- 11
+    chanstr <- "spongepants"
+    chanstr <- "squarebob"
 	mapvar["abc"] = "def"
 	mapvar["ghi"] = "jkl"
+	slicemap["a"] = []string{"b","c","d"}
+    slicemap["e"] = []string{"f","g","h"}
 	strvar := "abc"
 	ptrvar := &strvar
 	slicevar := make([]string, 0, 16)
@@ -125,6 +137,7 @@
 	runtime.KeepAlive(ptrvar)
 	_ = ptrvar // set breakpoint here
 	gslice = slicevar
+	fmt.Printf("%v, %v, %v\n", slicemap, <-chanint, <-chanstr)
 	runtime.KeepAlive(mapvar)
 }  // END_OF_PROGRAM
 `
@@ -157,7 +170,7 @@
 
 	checkGdbEnvironment(t)
 	t.Parallel()
-	checkGdbVersion(t)
+	gdbMajor := checkGdbVersion(t)
 	checkGdbPython(t)
 
 	dir, err := ioutil.TempDir("", "go-build")
@@ -227,9 +240,18 @@
 		"-ex", "echo BEGIN print mapvar\n",
 		"-ex", "print mapvar",
 		"-ex", "echo END\n",
+		"-ex", "echo BEGIN print slicemap\n",
+		"-ex", "print slicemap",
+		"-ex", "echo END\n",
 		"-ex", "echo BEGIN print strvar\n",
 		"-ex", "print strvar",
 		"-ex", "echo END\n",
+		"-ex", "echo BEGIN print chanint\n",
+		"-ex", "print chanint",
+		"-ex", "echo END\n",
+		"-ex", "echo BEGIN print chanstr\n",
+		"-ex", "print chanstr",
+		"-ex", "echo END\n",
 		"-ex", "echo BEGIN info locals\n",
 		"-ex", "info locals",
 		"-ex", "echo END\n",
@@ -290,6 +312,25 @@
 		t.Fatalf("print mapvar failed: %s", bl)
 	}
 
+	// 2 orders, and possible differences in spacing.
+	sliceMapSfx1 := `map[string][]string = {["e"] = []string = {"f", "g", "h"}, ["a"] = []string = {"b", "c", "d"}}`
+	sliceMapSfx2 := `map[string][]string = {["a"] = []string = {"b", "c", "d"}, ["e"] = []string = {"f", "g", "h"}}`
+	if bl := strings.ReplaceAll(blocks["print slicemap"], "  ", " "); !strings.HasSuffix(bl, sliceMapSfx1) && !strings.HasSuffix(bl, sliceMapSfx2) {
+		t.Fatalf("print slicemap failed: %s", bl)
+	}
+
+	if gdbMajor > 7 {
+		chanIntSfx := `chan int = {99, 11}`
+		if bl := strings.ReplaceAll(blocks["print chanint"], "  ", " "); !strings.HasSuffix(bl, chanIntSfx) {
+			t.Fatalf("print chanint failed: %s", bl)
+		}
+
+		chanStrSfx := `chan string = {"spongepants", "squarebob"}`
+		if bl := strings.ReplaceAll(blocks["print chanstr"], "  ", " "); !strings.HasSuffix(bl, chanStrSfx) {
+			t.Fatalf("print chanstr failed: %s", bl)
+		}
+	}
+
 	strVarRe := regexp.MustCompile(`^\$[0-9]+ = (0x[0-9a-f]+\s+)?"abc"$`)
 	if bl := blocks["print strvar"]; !strVarRe.MatchString(bl) {
 		t.Fatalf("print strvar failed: %s", bl)
@@ -366,7 +407,7 @@
 
 	checkGdbEnvironment(t)
 	t.Parallel()
-	checkGdbVersion(t)
+	_ = checkGdbVersion(t)
 
 	dir, err := ioutil.TempDir("", "go-build")
 	if err != nil {
@@ -440,7 +481,7 @@
 func TestGdbAutotmpTypes(t *testing.T) {
 	checkGdbEnvironment(t)
 	t.Parallel()
-	checkGdbVersion(t)
+	_ = checkGdbVersion(t)
 
 	if runtime.GOOS == "aix" && testing.Short() {
 		t.Skip("TestGdbAutotmpTypes is too slow on aix/ppc64")
@@ -513,7 +554,7 @@
 func TestGdbConst(t *testing.T) {
 	checkGdbEnvironment(t)
 	t.Parallel()
-	checkGdbVersion(t)
+	_ = checkGdbVersion(t)
 
 	dir, err := ioutil.TempDir("", "go-build")
 	if err != nil {
@@ -580,7 +621,7 @@
 func TestGdbPanic(t *testing.T) {
 	checkGdbEnvironment(t)
 	t.Parallel()
-	checkGdbVersion(t)
+	_ = checkGdbVersion(t)
 
 	dir, err := ioutil.TempDir("", "go-build")
 	if err != nil {
@@ -658,7 +699,7 @@
 	}
 
 	t.Parallel()
-	checkGdbVersion(t)
+	_ = checkGdbVersion(t)
 
 	dir, err := ioutil.TempDir("", "go-build")
 	if err != nil {