aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/traceback.go
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2023-02-14 12:25:11 -0500
committerAustin Clements <austin@google.com>2023-03-10 17:59:37 +0000
commit3e360b035f4c3014e9564f4994c68ccc296ef629 (patch)
tree1eddb9edefe065d015026fe628b8b62e7f7e3320 /src/runtime/traceback.go
parent9872428a715a907a122ad705b5fa42f11a385664 (diff)
downloadgo-3e360b035f4c3014e9564f4994c68ccc296ef629.tar.xz
runtime: new API for filling PC traceback buffers
Currently, filling PC traceback buffers is one of the jobs of gentraceback. This moves it into a new function, tracebackPCs, with a simple API built around unwinder, and changes all callers to use this new API. Updates #54466. Change-Id: Id2038bded81bf533a5a4e71178a7c014904d938c Reviewed-on: https://go-review.googlesource.com/c/go/+/468300 Reviewed-by: Michael Pratt <mpratt@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Austin Clements <austin@google.com>
Diffstat (limited to 'src/runtime/traceback.go')
-rw-r--r--src/runtime/traceback.go92
1 files changed, 53 insertions, 39 deletions
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 968823316e..b100a3c3b2 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -591,27 +591,64 @@ func (u *unwinder) cgoCallers(pcBuf []uintptr) int {
return len(pcBuf)
}
-// Generic traceback. Handles runtime stack prints (pcbuf == nil),
-// and the runtime.Callers function (pcbuf != nil).
-// A little clunky to merge these, but avoids
-// duplicating the code and all its subtlety.
+// tracebackPCs populates pcBuf with the return addresses for each frame from u
+// and returns the number of PCs written to pcBuf. The returned PCs correspond
+// to "logical frames" rather than "physical frames"; that is if A is inlined
+// into B, this will still return a PCs for both A and B. This also includes PCs
+// generated by the cgo unwinder, if one is registered.
+//
+// If skip != 0, this skips this many logical frames.
+//
+// Callers should set the unwindSilentErrors flag on u.
+func tracebackPCs(u *unwinder, skip int, pcBuf []uintptr) int {
+ var cgoBuf [32]uintptr
+ n := 0
+ for ; n < len(pcBuf) && u.valid(); u.next() {
+ f := u.frame.fn
+ cgoN := u.cgoCallers(cgoBuf[:])
+
+ // TODO: Why does &u.cache cause u to escape?
+ for iu, uf := newInlineUnwinder(f, u.symPC(), noEscapePtr(&u.cache)); n < len(pcBuf) && uf.valid(); uf = iu.next(uf) {
+ sf := iu.srcFunc(uf)
+ if sf.funcID == funcID_wrapper && elideWrapperCalling(u.calleeFuncID) {
+ // ignore wrappers
+ } else if skip > 0 {
+ skip--
+ } else {
+ // Callers expect the pc buffer to contain return addresses
+ // and do the -1 themselves, so we add 1 to the call PC to
+ // create a return PC.
+ pcBuf[n] = uf.pc + 1
+ n++
+ }
+ u.calleeFuncID = sf.funcID
+ }
+ // Add cgo frames (if we're done skipping over the requested number of
+ // Go frames).
+ if skip == 0 {
+ n += copy(pcBuf[n:], cgoBuf[:cgoN])
+ }
+ }
+ return n
+}
+
+// Generic traceback. Handles runtime stack prints (pcbuf == nil).
//
// The skip argument is only valid with pcbuf != nil and counts the number
// of logical frames to skip rather than physical frames (with inlining, a
// PC in pcbuf can represent multiple calls).
func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max int, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer, flags uint) int {
+ if pcbuf != nil {
+ throw("pcbuf argument no longer supported")
+ }
if callback != nil {
throw("callback argument no longer supported")
}
// Translate flags
var uflags unwindFlags
- printing := pcbuf == nil && callback == nil
- if printing {
- uflags |= unwindPrintErrors
- } else {
- uflags |= unwindSilentErrors
- }
+ printing := true
+ uflags |= unwindPrintErrors
if flags&_TraceTrap != 0 {
uflags |= unwindTrap
}
@@ -634,33 +671,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
cgoN := u.cgoCallers(cgoBuf[:])
- if pcbuf != nil {
- // TODO: Why does cache escape? (Same below)
- for iu, uf := newInlineUnwinder(f, u.symPC(), noEscapePtr(&u.cache)); uf.valid(); uf = iu.next(uf) {
- sf := iu.srcFunc(uf)
- if sf.funcID == funcID_wrapper && elideWrapperCalling(u.calleeFuncID) {
- // ignore wrappers
- } else if skip > 0 {
- skip--
- } else if n < max {
- // Callers expect the pc buffer to contain return addresses
- // and do the -1 themselves, so we add 1 to the call PC to
- // create a return PC.
- (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = uf.pc + 1
- n++
- }
- u.calleeFuncID = sf.funcID
- }
- // Add cgo frames
- if skip == 0 { // skip only applies to Go frames
- for i := 0; i < cgoN && n < max; i++ {
- (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = cgoBuf[i]
- n++
- }
- }
- n-- // offset n++ below
- }
-
if printing {
// assume skip=0 for printing.
//
@@ -981,13 +991,17 @@ func callers(skip int, pcbuf []uintptr) int {
gp := getg()
var n int
systemstack(func() {
- n = gentraceback(pc, sp, 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
+ var u unwinder
+ u.initAt(pc, sp, 0, gp, unwindSilentErrors)
+ n = tracebackPCs(&u, skip, pcbuf)
})
return n
}
func gcallers(gp *g, skip int, pcbuf []uintptr) int {
- return gentraceback(^uintptr(0), ^uintptr(0), 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
+ var u unwinder
+ u.init(gp, unwindSilentErrors)
+ return tracebackPCs(&u, skip, pcbuf)
}
// showframe reports whether the frame with the given characteristics should