]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: fix GDB goroutine N command when N is running
authorAustin Clements <austin@google.com>
Wed, 7 Jun 2017 14:30:49 +0000 (10:30 -0400)
committerAustin Clements <austin@google.com>
Thu, 8 Jun 2017 13:18:26 +0000 (13:18 +0000)
The current implementation of "goroutine N cmd" assumes it can get
goroutine N's state from the goroutine's sched buffer. But this only
works if the goroutine is blocked. Extend find_goroutine so that, if
there is no saved scheduler state for a goorutine, it tries to find
the thread the goroutine is running on and use the thread's current
register state. We also extend find_goroutine to understand saved
syscall register state.

Fixes #13887.

Change-Id: I739008a8987471deaa4a9da918655e4042cf969b
Reviewed-on: https://go-review.googlesource.com/45031
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/runtime/runtime-gdb.py
src/runtime/runtime-gdb_test.go

index 5c9b2a08e8f1a365a1fd5ae5276c258c622f60f9..26f36b51199e279de324601a77a870661c8a9b74 100644 (file)
@@ -416,8 +416,37 @@ def find_goroutine(goid):
                if ptr['atomicstatus'] == 6:  # 'gdead'
                        continue
                if ptr['goid'] == goid:
-                       return (ptr['sched'][x].cast(vp) for x in ('pc', 'sp'))
-       return None, None
+                       break
+       else:
+               return None, None
+       # Get the goroutine's saved state.
+       pc, sp = ptr['sched']['pc'], ptr['sched']['sp']
+       # If the goroutine is stopped, sched.sp will be non-0.
+       if sp != 0:
+               return pc.cast(vp), sp.cast(vp)
+       # If the goroutine is in a syscall, use syscallpc/sp.
+       pc, sp = ptr['syscallpc'], ptr['syscallsp']
+       if sp != 0:
+               return pc.cast(vp), sp.cast(vp)
+       # Otherwise, the goroutine is running, so it doesn't have
+       # saved scheduler state. Find G's OS thread.
+       m = ptr['m']
+       if m == 0:
+               return None, None
+       for thr in gdb.selected_inferior().threads():
+               if thr.ptid[1] == m['procid']:
+                       break
+        else:
+               return None, None
+       # Get scheduler state from the G's OS thread state.
+       curthr = gdb.selected_thread()
+       try:
+               thr.switch()
+               pc = gdb.parse_and_eval('$pc')
+               sp = gdb.parse_and_eval('$sp')
+       finally:
+               curthr.switch()
+       return pc.cast(vp), sp.cast(vp)
 
 
 class GoroutineCmd(gdb.Command):
index 3cc41e0b7f2b39b9febd2f1fe575a9a6767a75b2..b025e189f0426560b78cc1627aa679680e9b8e87 100644 (file)
@@ -157,6 +157,9 @@ func testGdbPython(t *testing.T, cgo bool) {
                "-ex", "info locals",
                "-ex", "echo END\n",
                "-ex", "down", // back to fmt.Println (goroutine 2 below only works at bottom of stack.  TODO: fix that)
+               "-ex", "echo BEGIN goroutine 1 bt\n",
+               "-ex", "goroutine 1 bt",
+               "-ex", "echo END\n",
                "-ex", "echo BEGIN goroutine 2 bt\n",
                "-ex", "goroutine 2 bt",
                "-ex", "echo END\n",
@@ -213,8 +216,13 @@ func testGdbPython(t *testing.T, cgo bool) {
                t.Fatalf("info locals failed: %s", bl)
        }
 
-       btGoroutineRe := regexp.MustCompile(`^#0\s+(0x[0-9a-f]+\s+in\s+)?runtime.+at`)
-       if bl := blocks["goroutine 2 bt"]; !btGoroutineRe.MatchString(bl) {
+       btGoroutine1Re := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?fmt\.Println.+at`)
+       if bl := blocks["goroutine 1 bt"]; !btGoroutine1Re.MatchString(bl) {
+               t.Fatalf("goroutine 1 bt failed: %s", bl)
+       }
+
+       btGoroutine2Re := regexp.MustCompile(`(?m)^#0\s+(0x[0-9a-f]+\s+in\s+)?runtime.+at`)
+       if bl := blocks["goroutine 2 bt"]; !btGoroutine2Re.MatchString(bl) {
                t.Fatalf("goroutine 2 bt failed: %s", bl)
        }
 }