From 545686857bc4c2e7a5306d97e5ef48f631d277bc Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 16 Feb 2015 21:56:10 -0500 Subject: [PATCH] runtime: fix GDB "info goroutines" for Go 1.5 "info goroutines" is failing because it hasn't kept up with changes in the 1.5 runtime. This fixes three issues preventing "info goroutines" from working. allg is no longer a linked list, so switch to using the allgs slice. The g struct's 'status' field is now called 'atomicstatus', so rename uses of 'status'. Finally, this was trying to parse str(pc) as an int, but str(pc) can return symbolic information after the raw hex value; fix this by stripping everything after the first space. This also adds a test for "info goroutines" to runtime-gdb_test, which was previously quite skeletal. Change-Id: I8ad83ee8640891cdd88ecd28dad31ed9b5833b7a Reviewed-on: https://go-review.googlesource.com/4935 Reviewed-by: Minux Ma --- src/runtime/runtime-gdb.py | 40 ++++++++++++++++++++++++++++----- src/runtime/runtime-gdb_test.go | 27 +++++++++++++++++++--- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/runtime/runtime-gdb.py b/src/runtime/runtime-gdb.py index cee025eb6c..33fcc76931 100644 --- a/src/runtime/runtime-gdb.py +++ b/src/runtime/runtime-gdb.py @@ -27,6 +27,31 @@ if sys.version > '3': goobjfile = gdb.current_objfile() or gdb.objfiles()[0] goobjfile.pretty_printers = [] +# +# Value wrappers +# + +class SliceValue: + "Wrapper for slice values." + + def __init__(self, val): + self.val = val + + @property + def len(self): + return int(self.val['len']) + + @property + def cap(self): + return int(self.val['cap']) + + def __getitem__(self, i): + if i < 0 or i >= self.len: + raise IndexError(i) + ptr = self.val["array"] + return (ptr + i).dereference() + + # # Pretty Printers # @@ -355,8 +380,8 @@ class GoroutinesCmd(gdb.Command): def invoke(self, _arg, _from_tty): # args = gdb.string_to_argv(arg) vp = gdb.lookup_type('void').pointer() - for ptr in linked_list(gdb.parse_and_eval("'runtime.allg'"), 'alllink'): - if ptr['status'] == 6: # 'gdead' + for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")): + if ptr['atomicstatus'] == 6: # 'gdead' continue s = ' ' if ptr['m']: @@ -370,9 +395,12 @@ class GoroutinesCmd(gdb.Command): #python3 / newer versions of gdb pc = int(pc) except gdb.error: - pc = int(str(pc), 16) + # str(pc) can return things like + # "0x429d6c ", so + # chop at first space. + pc = int(str(pc).split(None, 1)[0], 16) blk = gdb.block_for_pc(pc) - print(s, ptr['goid'], "{0:8s}".format(sts[int(ptr['status'])]), blk.function) + print(s, ptr['goid'], "{0:8s}".format(sts[int(ptr['atomicstatus'])]), blk.function) def find_goroutine(goid): @@ -386,8 +414,8 @@ def find_goroutine(goid): @return tuple (gdb.Value, gdb.Value) """ vp = gdb.lookup_type('void').pointer() - for ptr in linked_list(gdb.parse_and_eval("'runtime.allg'"), 'alllink'): - if ptr['status'] == 6: # 'gdead' + for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")): + if ptr['atomicstatus'] == 6: # 'gdead' continue if ptr['goid'] == goid: return (ptr['sched'][x].cast(vp) for x in ('pc', 'sp')) diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 4911dc000d..a946749f47 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -1,11 +1,13 @@ package runtime_test import ( + "bytes" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" + "regexp" "runtime" "testing" ) @@ -30,7 +32,7 @@ func main() { } ` -func TestGdbLoadRuntimeSupport(t *testing.T) { +func TestGdbPython(t *testing.T) { checkGdbPython(t) dir, err := ioutil.TempDir("", "go-build") @@ -54,8 +56,27 @@ func TestGdbLoadRuntimeSupport(t *testing.T) { got, _ := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", fmt.Sprintf("add-auto-load-safe-path %s/src/runtime", runtime.GOROOT()), + "-ex", "br 'main.main'", + "-ex", "run", + "-ex", "echo BEGIN info goroutines\n", + "-ex", "info goroutines", + "-ex", "echo END\n", filepath.Join(dir, "a.exe")).CombinedOutput() - if string(got) != "Loading Go Runtime support.\n" { - t.Fatalf("%s", got) + + firstLine := bytes.SplitN(got, []byte("\n"), 2)[0] + if string(firstLine) != "Loading Go Runtime support." { + t.Fatalf("failed to load Go runtime support: %s", firstLine) + } + + // Extract named BEGIN...END blocks from output + partRe := regexp.MustCompile(`(?ms)^BEGIN ([^\n]*)\n(.*?)\nEND`) + blocks := map[string]string{} + for _, subs := range partRe.FindAllSubmatch(got, -1) { + blocks[string(subs[1])] = string(subs[2]) + } + + infoGoroutinesRe := regexp.MustCompile(`\d+\s+running\s+runtime`) + if bl := blocks["info goroutines"]; !infoGoroutinesRe.MatchString(bl) { + t.Fatalf("info goroutines failed: %s", bl) } }