[go] runtime: record parent goroutine ID, and print it in stack traces

104 views
Skip to first unread message

Gopher Robot (Gerrit)

unread,
Feb 21, 2023, 12:35:26 PM2/21/23
to Nick Ripley, goph...@pubsubhelper.golang.org, golang-...@googlegroups.com, Michael Pratt, Cherry Mui, Ethan Reesor, Austin Clements, Michael Knyszek, golang-co...@googlegroups.com

Gopher Robot submitted this change.

View Change

Approvals: Nick Ripley: Run TryBots Ethan Reesor: Looks good to me, but someone else must approve Michael Pratt: Looks good to me, approved; Automatically submit change Gopher Robot: TryBots succeeded Cherry Mui: Looks good to me, approved
runtime: record parent goroutine ID, and print it in stack traces

Fixes #38651

Change-Id: Id46d684ee80e208c018791a06c26f304670ed159
Reviewed-on: https://go-review.googlesource.com/c/go/+/435337
Run-TryBot: Nick Ripley <nick....@datadoghq.com>
Reviewed-by: Ethan Reesor <ethan....@gmail.com>
Auto-Submit: Michael Pratt <mpr...@google.com>
Reviewed-by: Michael Pratt <mpr...@google.com>
Reviewed-by: Cherry Mui <cher...@google.com>
TryBot-Result: Gopher Robot <go...@golang.org>
---
M src/runtime/export_test.go
M src/runtime/proc.go
M src/runtime/proc_test.go
M src/runtime/runtime2.go
M src/runtime/sizeof_test.go
M src/runtime/traceback.go
M src/runtime/traceback_test.go
7 files changed, 62 insertions(+), 6 deletions(-)

diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index e7476e6..2575897 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -540,6 +540,10 @@
return getg()
}

+func Goid() uint64 {
+ return getg().goid
+}
+
func GIsWaitingOnMutex(gp *G) bool {
return readgstatus(gp) == _Gwaiting && gp.waitreason.isMutexWait()
}
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index d57a31c..aba2e2b 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -4283,6 +4283,7 @@
newg.sched.pc = abi.FuncPCABI0(goexit) + sys.PCQuantum // +PCQuantum so that previous instruction is in same function
newg.sched.g = guintptr(unsafe.Pointer(newg))
gostartcallfn(&newg.sched, fn)
+ newg.parentGoid = callergp.goid
newg.gopc = callerpc
newg.ancestors = saveAncestors(callergp)
newg.startpc = fn.fn
diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go
index f354fac..d240dc4 100644
--- a/src/runtime/proc_test.go
+++ b/src/runtime/proc_test.go
@@ -415,7 +415,10 @@
n := runtime.NumGoroutine()
buf = buf[:runtime.Stack(buf, true)]

- nstk := strings.Count(string(buf), "goroutine ")
+ // To avoid double-counting "goroutine" in "goroutine $m [running]:"
+ // and "created by $func in goroutine $n", remove the latter
+ output := strings.ReplaceAll(string(buf), "in goroutine", "")
+ nstk := strings.Count(output, "goroutine ")
if n == nstk {
break
}
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index 9381d1e..044a9a7 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -479,6 +479,7 @@
sigcode0 uintptr
sigcode1 uintptr
sigpc uintptr
+ parentGoid uint64 // goid of goroutine that created this goroutine
gopc uintptr // pc of go statement that created this goroutine
ancestors *[]ancestorInfo // ancestor information goroutine(s) that created this goroutine (only used if debug.tracebackancestors)
startpc uintptr // pc of goroutine function
diff --git a/src/runtime/sizeof_test.go b/src/runtime/sizeof_test.go
index 9ce0a3a..bfb5d6e 100644
--- a/src/runtime/sizeof_test.go
+++ b/src/runtime/sizeof_test.go
@@ -21,7 +21,7 @@
_32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms
}{
- {runtime.G{}, 240, 392}, // g, but exported for testing
+ {runtime.G{}, 248, 400}, // g, but exported for testing
{runtime.Sudog{}, 56, 88}, // sudog, but exported for testing
}

diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 37f35d5..6773509 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -701,12 +701,16 @@
pc := gp.gopc
f := findfunc(pc)
if f.valid() && showframe(f, gp, false, funcID_normal, funcID_normal) && gp.goid != 1 {
- printcreatedby1(f, pc)
+ printcreatedby1(f, pc, gp.parentGoid)
}
}

-func printcreatedby1(f funcInfo, pc uintptr) {
- print("created by ", funcname(f), "\n")
+func printcreatedby1(f funcInfo, pc uintptr, goid uint64) {
+ print("created by ", funcname(f))
+ if goid != 0 {
+ print(" in goroutine ", goid)
+ }
+ print("\n")
tracepc := pc // back up to CALL instruction for funcline.
if pc > f.entry() {
tracepc -= sys.PCQuantum
@@ -806,7 +810,9 @@
// Show what created goroutine, except main goroutine (goid 1).
f := findfunc(ancestor.gopc)
if f.valid() && showfuncinfo(f, false, funcID_normal, funcID_normal) && ancestor.goid != 1 {
- printcreatedby1(f, ancestor.gopc)
+ // In ancestor mode, we'll already print the goroutine ancestor.
+ // Pass 0 for the goid parameter so we don't print it again.
+ printcreatedby1(f, ancestor.gopc, 0)
}
}

diff --git a/src/runtime/traceback_test.go b/src/runtime/traceback_test.go
index 97eb921..8b19087 100644
--- a/src/runtime/traceback_test.go
+++ b/src/runtime/traceback_test.go
@@ -6,9 +6,12 @@

import (
"bytes"
+ "fmt"
"internal/abi"
"internal/testenv"
"runtime"
+ "strings"
+ "sync"
"testing"
)

@@ -420,3 +423,23 @@
func poisonStack() [20]int {
return [20]int{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
}
+
+func TestTracebackParentChildGoroutines(t *testing.T) {
+ parent := fmt.Sprintf("goroutine %d", runtime.Goid())
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ buf := make([]byte, 1<<10)
+ // We collect the stack only for this goroutine (by passing
+ // false to runtime.Stack). We expect to see the current
+ // goroutine ID, and the parent goroutine ID in a message like
+ // "created by ... in goroutine N".
+ stack := string(buf[:runtime.Stack(buf, false)])
+ child := fmt.Sprintf("goroutine %d", runtime.Goid())
+ if !strings.Contains(stack, parent) || !strings.Contains(stack, child) {
+ t.Errorf("did not see parent (%s) and child (%s) IDs in stack, got %s", parent, child, stack)
+ }
+ }()
+ wg.Wait()
+}

To view, visit change 435337. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: Id46d684ee80e208c018791a06c26f304670ed159
Gerrit-Change-Number: 435337
Gerrit-PatchSet: 8
Gerrit-Owner: Nick Ripley <nick....@datadoghq.com>
Gerrit-Reviewer: Austin Clements <aus...@google.com>
Gerrit-Reviewer: Cherry Mui <cher...@google.com>
Gerrit-Reviewer: Ethan Reesor <ethan....@gmail.com>
Gerrit-Reviewer: Gopher Robot <go...@golang.org>
Gerrit-Reviewer: Michael Knyszek <mkny...@google.com>
Gerrit-Reviewer: Michael Pratt <mpr...@google.com>
Gerrit-Reviewer: Nick Ripley <nick....@datadoghq.com>
Gerrit-MessageType: merged
Reply all
Reply to author
Forward
0 new messages