aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/symtab.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2024-03-14 15:32:00 -0400
committerRuss Cox <rsc@golang.org>2024-03-15 17:11:58 +0000
commit22f5e33031042ca2ac8521e4e7dc0783e8c0cdca (patch)
treec88ed60a4f54caddfe89542121a8ac9db4b2338b /src/runtime/symtab.go
parent88480fadcc5874f880623ca50713d0c62cc5259b (diff)
downloadgo-22f5e33031042ca2ac8521e4e7dc0783e8c0cdca.tar.xz
runtime: allow omitting virtual PCs from runtime.CallersFrames input
This makes CL 561635's test pass without any changes to the traceback textual format. The test in this CL is copied identically from CL 561635. Change-Id: I5130abdfefd9940f98f20c283cca6cd159e37617 Reviewed-on: https://go-review.googlesource.com/c/go/+/571798 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
Diffstat (limited to 'src/runtime/symtab.go')
-rw-r--r--src/runtime/symtab.go34
1 files changed, 32 insertions, 2 deletions
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 96a2d29079..8b9977f428 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -18,6 +18,9 @@ type Frames struct {
// callers is a slice of PCs that have not yet been expanded to frames.
callers []uintptr
+ // nextPC is a next PC to expand ahead of processing callers.
+ nextPC uintptr
+
// frames is a slice of Frames that have yet to be returned.
frames []Frame
frameStore [2]Frame
@@ -96,8 +99,12 @@ func (ci *Frames) Next() (frame Frame, more bool) {
if len(ci.callers) == 0 {
break
}
- pc := ci.callers[0]
- ci.callers = ci.callers[1:]
+ var pc uintptr
+ if ci.nextPC != 0 {
+ pc, ci.nextPC = ci.nextPC, 0
+ } else {
+ pc, ci.callers = ci.callers[0], ci.callers[1:]
+ }
funcInfo := findfunc(pc)
if !funcInfo.valid() {
if cgoSymbolizer != nil {
@@ -125,6 +132,29 @@ func (ci *Frames) Next() (frame Frame, more bool) {
// Note: entry is not modified. It always refers to a real frame, not an inlined one.
// File/line from funcline1 below are already correct.
f = nil
+
+ // When CallersFrame is invoked using the PC list returned by Callers,
+ // the PC list includes virtual PCs corresponding to each outer frame
+ // around an innermost real inlined PC.
+ // We also want to support code passing in a PC list extracted from a
+ // stack trace, and there only the real PCs are printed, not the virtual ones.
+ // So check to see if the implied virtual PC for this PC (obtained from the
+ // unwinder itself) is the next PC in ci.callers. If not, insert it.
+ // The +1 here correspond to the pc-- above: the output of Callers
+ // and therefore the input to CallersFrames is return PCs from the stack;
+ // The pc-- backs up into the CALL instruction (not the first byte of the CALL
+ // instruction, but good enough to find it nonetheless).
+ // There are no cycles in implied virtual PCs (some number of frames were
+ // inlined, but that number is finite), so this unpacking cannot cause an infinite loop.
+ for unext := u.next(uf); unext.valid() && len(ci.callers) > 0 && ci.callers[0] != unext.pc+1; unext = u.next(unext) {
+ snext := u.srcFunc(unext)
+ if snext.funcID == abi.FuncIDWrapper && elideWrapperCalling(sf.funcID) {
+ // Skip, because tracebackPCs (inside runtime.Callers) would too.
+ continue
+ }
+ ci.nextPC = unext.pc + 1
+ break
+ }
}
ci.frames = append(ci.frames, Frame{
PC: pc,