mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime/gdb: use goroutine atomicstatus to determine the state
Previously find_goroutine determined whether a goroutine is stopped by checking the sched.sp field. This heuristic doesn't always hold but causes find_goroutine to return bogus pc/sp info for running goroutines. This change uses the atomicstatus bit to determine the state which is more accurate. R=go1.11 Change-Id: I537d432d9e0363257120a196ce2ba52da2970f59 Reviewed-on: https://go-review.googlesource.com/49691 Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
ef175731ff
commit
dc3bef3635
2 changed files with 32 additions and 6 deletions
|
|
@ -462,9 +462,11 @@ def find_goroutine(goid):
|
||||||
return None, None
|
return None, None
|
||||||
# Get the goroutine's saved state.
|
# Get the goroutine's saved state.
|
||||||
pc, sp = ptr['sched']['pc'], ptr['sched']['sp']
|
pc, sp = ptr['sched']['pc'], ptr['sched']['sp']
|
||||||
# If the goroutine is stopped, sched.sp will be non-0.
|
status = ptr['atomicstatus']&~G_SCAN
|
||||||
if sp != 0:
|
# Goroutine is not running nor in syscall, so use the info in goroutine
|
||||||
|
if status != G_RUNNING and status != G_SYSCALL:
|
||||||
return pc.cast(vp), sp.cast(vp)
|
return pc.cast(vp), sp.cast(vp)
|
||||||
|
|
||||||
# If the goroutine is in a syscall, use syscallpc/sp.
|
# If the goroutine is in a syscall, use syscallpc/sp.
|
||||||
pc, sp = ptr['syscallpc'], ptr['syscallsp']
|
pc, sp = ptr['syscallpc'], ptr['syscallsp']
|
||||||
if sp != 0:
|
if sp != 0:
|
||||||
|
|
|
||||||
|
|
@ -87,13 +87,24 @@ func main() {
|
||||||
ptrvar := &strvar
|
ptrvar := &strvar
|
||||||
slicevar := make([]string, 0, 16)
|
slicevar := make([]string, 0, 16)
|
||||||
slicevar = append(slicevar, mapvar["abc"])
|
slicevar = append(slicevar, mapvar["abc"])
|
||||||
fmt.Println("hi") // line 13
|
fmt.Println("hi")
|
||||||
runtime.KeepAlive(ptrvar)
|
runtime.KeepAlive(ptrvar)
|
||||||
|
_ = ptrvar
|
||||||
gslice = slicevar
|
gslice = slicevar
|
||||||
runtime.KeepAlive(mapvar)
|
runtime.KeepAlive(mapvar)
|
||||||
}
|
} // END_OF_PROGRAM
|
||||||
`
|
`
|
||||||
|
|
||||||
|
func lastLine(src []byte) int {
|
||||||
|
eop := []byte("END_OF_PROGRAM")
|
||||||
|
for i, l := range bytes.Split(src, []byte("\n")) {
|
||||||
|
if bytes.Contains(l, eop) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func TestGdbPython(t *testing.T) {
|
func TestGdbPython(t *testing.T) {
|
||||||
testGdbPython(t, false)
|
testGdbPython(t, false)
|
||||||
}
|
}
|
||||||
|
|
@ -128,11 +139,13 @@ func testGdbPython(t *testing.T, cgo bool) {
|
||||||
}
|
}
|
||||||
buf.WriteString(helloSource)
|
buf.WriteString(helloSource)
|
||||||
|
|
||||||
src := filepath.Join(dir, "main.go")
|
src := buf.Bytes()
|
||||||
err = ioutil.WriteFile(src, buf.Bytes(), 0644)
|
|
||||||
|
err = ioutil.WriteFile(filepath.Join(dir, "main.go"), src, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create file: %v", err)
|
t.Fatalf("failed to create file: %v", err)
|
||||||
}
|
}
|
||||||
|
nLines := lastLine(src)
|
||||||
|
|
||||||
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe")
|
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe")
|
||||||
cmd.Dir = dir
|
cmd.Dir = dir
|
||||||
|
|
@ -168,9 +181,16 @@ func testGdbPython(t *testing.T, cgo bool) {
|
||||||
"-ex", "echo BEGIN goroutine 2 bt\n",
|
"-ex", "echo BEGIN goroutine 2 bt\n",
|
||||||
"-ex", "goroutine 2 bt",
|
"-ex", "goroutine 2 bt",
|
||||||
"-ex", "echo END\n",
|
"-ex", "echo END\n",
|
||||||
|
"-ex", "clear fmt.Println", // clear the previous break point
|
||||||
|
"-ex", fmt.Sprintf("br main.go:%d", nLines), // new break point at the end of main
|
||||||
|
"-ex", "c",
|
||||||
|
"-ex", "echo BEGIN goroutine 1 bt at the end\n",
|
||||||
|
"-ex", "goroutine 1 bt",
|
||||||
|
"-ex", "echo END\n",
|
||||||
filepath.Join(dir, "a.exe"),
|
filepath.Join(dir, "a.exe"),
|
||||||
}
|
}
|
||||||
got, _ := exec.Command("gdb", args...).CombinedOutput()
|
got, _ := exec.Command("gdb", args...).CombinedOutput()
|
||||||
|
t.Logf("gdb output: %s\n", got)
|
||||||
|
|
||||||
firstLine := bytes.SplitN(got, []byte("\n"), 2)[0]
|
firstLine := bytes.SplitN(got, []byte("\n"), 2)[0]
|
||||||
if string(firstLine) != "Loading Go Runtime support." {
|
if string(firstLine) != "Loading Go Runtime support." {
|
||||||
|
|
@ -232,6 +252,10 @@ func testGdbPython(t *testing.T, cgo bool) {
|
||||||
if bl := blocks["goroutine 2 bt"]; !btGoroutine2Re.MatchString(bl) {
|
if bl := blocks["goroutine 2 bt"]; !btGoroutine2Re.MatchString(bl) {
|
||||||
t.Fatalf("goroutine 2 bt failed: %s", bl)
|
t.Fatalf("goroutine 2 bt failed: %s", bl)
|
||||||
}
|
}
|
||||||
|
btGoroutine1AtTheEndRe := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?main\.main.+at`)
|
||||||
|
if bl := blocks["goroutine 1 bt at the end"]; !btGoroutine1AtTheEndRe.MatchString(bl) {
|
||||||
|
t.Fatalf("goroutine 1 bt at the end failed: %s", bl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const backtraceSource = `
|
const backtraceSource = `
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue