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) } }