1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:14:46 -07:00

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 <minux@golang.org>
This commit is contained in:
Austin Clements 2015-02-16 21:56:10 -05:00
parent 277eddb8f2
commit 545686857b
2 changed files with 58 additions and 9 deletions

View File

@ -27,6 +27,31 @@ if sys.version > '3':
goobjfile = gdb.current_objfile() or gdb.objfiles()[0] goobjfile = gdb.current_objfile() or gdb.objfiles()[0]
goobjfile.pretty_printers = [] 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 # Pretty Printers
# #
@ -355,8 +380,8 @@ class GoroutinesCmd(gdb.Command):
def invoke(self, _arg, _from_tty): def invoke(self, _arg, _from_tty):
# args = gdb.string_to_argv(arg) # args = gdb.string_to_argv(arg)
vp = gdb.lookup_type('void').pointer() vp = gdb.lookup_type('void').pointer()
for ptr in linked_list(gdb.parse_and_eval("'runtime.allg'"), 'alllink'): for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")):
if ptr['status'] == 6: # 'gdead' if ptr['atomicstatus'] == 6: # 'gdead'
continue continue
s = ' ' s = ' '
if ptr['m']: if ptr['m']:
@ -370,9 +395,12 @@ class GoroutinesCmd(gdb.Command):
#python3 / newer versions of gdb #python3 / newer versions of gdb
pc = int(pc) pc = int(pc)
except gdb.error: except gdb.error:
pc = int(str(pc), 16) # str(pc) can return things like
# "0x429d6c <runtime.gopark+284>", so
# chop at first space.
pc = int(str(pc).split(None, 1)[0], 16)
blk = gdb.block_for_pc(pc) 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): def find_goroutine(goid):
@ -386,8 +414,8 @@ def find_goroutine(goid):
@return tuple (gdb.Value, gdb.Value) @return tuple (gdb.Value, gdb.Value)
""" """
vp = gdb.lookup_type('void').pointer() vp = gdb.lookup_type('void').pointer()
for ptr in linked_list(gdb.parse_and_eval("'runtime.allg'"), 'alllink'): for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")):
if ptr['status'] == 6: # 'gdead' if ptr['atomicstatus'] == 6: # 'gdead'
continue continue
if ptr['goid'] == goid: if ptr['goid'] == goid:
return (ptr['sched'][x].cast(vp) for x in ('pc', 'sp')) return (ptr['sched'][x].cast(vp) for x in ('pc', 'sp'))

View File

@ -1,11 +1,13 @@
package runtime_test package runtime_test
import ( import (
"bytes"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp"
"runtime" "runtime"
"testing" "testing"
) )
@ -30,7 +32,7 @@ func main() {
} }
` `
func TestGdbLoadRuntimeSupport(t *testing.T) { func TestGdbPython(t *testing.T) {
checkGdbPython(t) checkGdbPython(t)
dir, err := ioutil.TempDir("", "go-build") dir, err := ioutil.TempDir("", "go-build")
@ -54,8 +56,27 @@ func TestGdbLoadRuntimeSupport(t *testing.T) {
got, _ := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", got, _ := exec.Command("gdb", "-nx", "-q", "--batch", "-iex",
fmt.Sprintf("add-auto-load-safe-path %s/src/runtime", runtime.GOROOT()), 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() 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)
} }
} }