mirror of
https://github.com/golang/go
synced 2024-09-29 01:24:34 -06:00
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.ripley@datadoghq.com> Reviewed-by: Ethan Reesor <ethan.reesor@gmail.com> Auto-Submit: Michael Pratt <mpratt@google.com> Reviewed-by: Michael Pratt <mpratt@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
81eda3a339
commit
51225f6fc6
@ -540,6 +540,10 @@ func Getg() *G {
|
|||||||
return getg()
|
return getg()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Goid() uint64 {
|
||||||
|
return getg().goid
|
||||||
|
}
|
||||||
|
|
||||||
func GIsWaitingOnMutex(gp *G) bool {
|
func GIsWaitingOnMutex(gp *G) bool {
|
||||||
return readgstatus(gp) == _Gwaiting && gp.waitreason.isMutexWait()
|
return readgstatus(gp) == _Gwaiting && gp.waitreason.isMutexWait()
|
||||||
}
|
}
|
||||||
|
@ -4283,6 +4283,7 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr) *g {
|
|||||||
newg.sched.pc = abi.FuncPCABI0(goexit) + sys.PCQuantum // +PCQuantum so that previous instruction is in same function
|
newg.sched.pc = abi.FuncPCABI0(goexit) + sys.PCQuantum // +PCQuantum so that previous instruction is in same function
|
||||||
newg.sched.g = guintptr(unsafe.Pointer(newg))
|
newg.sched.g = guintptr(unsafe.Pointer(newg))
|
||||||
gostartcallfn(&newg.sched, fn)
|
gostartcallfn(&newg.sched, fn)
|
||||||
|
newg.parentGoid = callergp.goid
|
||||||
newg.gopc = callerpc
|
newg.gopc = callerpc
|
||||||
newg.ancestors = saveAncestors(callergp)
|
newg.ancestors = saveAncestors(callergp)
|
||||||
newg.startpc = fn.fn
|
newg.startpc = fn.fn
|
||||||
|
@ -415,7 +415,10 @@ func TestNumGoroutine(t *testing.T) {
|
|||||||
n := runtime.NumGoroutine()
|
n := runtime.NumGoroutine()
|
||||||
buf = buf[:runtime.Stack(buf, true)]
|
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 {
|
if n == nstk {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -479,6 +479,7 @@ type g struct {
|
|||||||
sigcode0 uintptr
|
sigcode0 uintptr
|
||||||
sigcode1 uintptr
|
sigcode1 uintptr
|
||||||
sigpc uintptr
|
sigpc uintptr
|
||||||
|
parentGoid uint64 // goid of goroutine that created this goroutine
|
||||||
gopc uintptr // pc of go statement 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)
|
ancestors *[]ancestorInfo // ancestor information goroutine(s) that created this goroutine (only used if debug.tracebackancestors)
|
||||||
startpc uintptr // pc of goroutine function
|
startpc uintptr // pc of goroutine function
|
||||||
|
@ -21,7 +21,7 @@ func TestSizeof(t *testing.T) {
|
|||||||
_32bit uintptr // size on 32bit platforms
|
_32bit uintptr // size on 32bit platforms
|
||||||
_64bit uintptr // size on 64bit 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
|
{runtime.Sudog{}, 56, 88}, // sudog, but exported for testing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,12 +701,16 @@ func printcreatedby(gp *g) {
|
|||||||
pc := gp.gopc
|
pc := gp.gopc
|
||||||
f := findfunc(pc)
|
f := findfunc(pc)
|
||||||
if f.valid() && showframe(f, gp, false, funcID_normal, funcID_normal) && gp.goid != 1 {
|
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) {
|
func printcreatedby1(f funcInfo, pc uintptr, goid uint64) {
|
||||||
print("created by ", funcname(f), "\n")
|
print("created by ", funcname(f))
|
||||||
|
if goid != 0 {
|
||||||
|
print(" in goroutine ", goid)
|
||||||
|
}
|
||||||
|
print("\n")
|
||||||
tracepc := pc // back up to CALL instruction for funcline.
|
tracepc := pc // back up to CALL instruction for funcline.
|
||||||
if pc > f.entry() {
|
if pc > f.entry() {
|
||||||
tracepc -= sys.PCQuantum
|
tracepc -= sys.PCQuantum
|
||||||
@ -806,7 +810,9 @@ func printAncestorTraceback(ancestor ancestorInfo) {
|
|||||||
// Show what created goroutine, except main goroutine (goid 1).
|
// Show what created goroutine, except main goroutine (goid 1).
|
||||||
f := findfunc(ancestor.gopc)
|
f := findfunc(ancestor.gopc)
|
||||||
if f.valid() && showfuncinfo(f, false, funcID_normal, funcID_normal) && ancestor.goid != 1 {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,9 +6,12 @@ package runtime_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"internal/abi"
|
"internal/abi"
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -420,3 +423,23 @@ func testTracebackArgs11b(a, b, c, d int32) int {
|
|||||||
func poisonStack() [20]int {
|
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}
|
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()
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user