mirror of
https://github.com/golang/go
synced 2024-11-19 05:54:44 -07:00
runtime: make internal CallersFrames-equivalent that doesn't escape PC slice
The Frames API forces the PC slice to escape to the heap because it stores it in the Frames object. However, we'd like to use this API for call stack expansion internally in the runtime in places where it would be very good to avoid heap allocation. This commit makes this possible by pulling the bulk of the Frames implementation into an internal frameExpander API. The key difference between these APIs is that the frameExpander does not hold the PC slice; instead, the caller is responsible for threading the PC slice through the frameExpander API calls. This makes it possible to keep the PC slice on the stack. The Frames API then becomes a thin shim around the frameExpander that keeps the PC slice in the Frames object. Change-Id: If6b2d0b9132a2a905a0cf5deced9feddce76fc0e Reviewed-on: https://go-review.googlesource.com/40610 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: David Lazar <lazard@golang.org>
This commit is contained in:
parent
668cca6cb4
commit
38521004ed
@ -16,17 +16,9 @@ type Frames struct {
|
||||
// callers is a slice of PCs that have not yet been expanded.
|
||||
callers []uintptr
|
||||
|
||||
// If previous caller in iteration was a panic, then
|
||||
// ci.callers[0] is the address of the faulting instruction
|
||||
// instead of the return address of the call.
|
||||
wasPanic bool
|
||||
|
||||
// expander expands the current PC into a sequence of Frames.
|
||||
expander pcExpander
|
||||
|
||||
// skip > 0 indicates that skip frames in the first expansion
|
||||
// should be skipped over and callers[1] should also be skipped.
|
||||
skip int
|
||||
// stackExpander expands callers into a sequence of Frames,
|
||||
// tracking the necessary state across PCs.
|
||||
stackExpander stackExpander
|
||||
}
|
||||
|
||||
// Frame is the information returned by Frames for each call frame.
|
||||
@ -51,23 +43,48 @@ type Frame struct {
|
||||
Entry uintptr
|
||||
}
|
||||
|
||||
// stackExpander expands a call stack of PCs into a sequence of
|
||||
// Frames. It tracks state across PCs necessary to perform this
|
||||
// expansion.
|
||||
//
|
||||
// This is the core of the Frames implementation, but is a separate
|
||||
// internal API to make it possible to use within the runtime without
|
||||
// heap-allocating the PC slice. The only difference with the public
|
||||
// Frames API is that the caller is responsible for threading the PC
|
||||
// slice between expansion steps in this API. If escape analysis were
|
||||
// smarter, we may not need this (though it may have to be a lot
|
||||
// smarter).
|
||||
type stackExpander struct {
|
||||
// pcExpander expands the current PC into a sequence of Frames.
|
||||
pcExpander pcExpander
|
||||
|
||||
// If previous caller in iteration was a panic, then the next
|
||||
// PC in the call stack is the address of the faulting
|
||||
// instruction instead of the return address of the call.
|
||||
wasPanic bool
|
||||
|
||||
// skip > 0 indicates that skip frames in the expansion of the
|
||||
// first PC should be skipped over and callers[1] should also
|
||||
// be skipped.
|
||||
skip int
|
||||
}
|
||||
|
||||
// CallersFrames takes a slice of PC values returned by Callers and
|
||||
// prepares to return function/file/line information.
|
||||
// Do not change the slice until you are done with the Frames.
|
||||
func CallersFrames(callers []uintptr) *Frames {
|
||||
ci := &Frames{}
|
||||
ci.init(callers)
|
||||
ci.callers = ci.stackExpander.init(callers)
|
||||
return ci
|
||||
}
|
||||
|
||||
func (ci *Frames) init(callers []uintptr) {
|
||||
func (se *stackExpander) init(callers []uintptr) []uintptr {
|
||||
if len(callers) >= 1 {
|
||||
pc := callers[0]
|
||||
s := pc - skipPC
|
||||
if s >= 0 && s < sizeofSkipFunction {
|
||||
// Ignore skip frame callers[0] since this means the caller trimmed the PC slice.
|
||||
ci.callers = callers[1:]
|
||||
return
|
||||
return callers[1:]
|
||||
}
|
||||
}
|
||||
if len(callers) >= 2 {
|
||||
@ -75,42 +92,48 @@ func (ci *Frames) init(callers []uintptr) {
|
||||
s := pc - skipPC
|
||||
if s > 0 && s < sizeofSkipFunction {
|
||||
// Skip the first s inlined frames when we expand the first PC.
|
||||
ci.skip = int(s)
|
||||
se.skip = int(s)
|
||||
}
|
||||
}
|
||||
ci.callers = callers
|
||||
return callers
|
||||
}
|
||||
|
||||
// Next returns frame information for the next caller.
|
||||
// If more is false, there are no more callers (the Frame value is valid).
|
||||
func (ci *Frames) Next() (frame Frame, more bool) {
|
||||
if !ci.expander.more {
|
||||
ci.callers, frame, more = ci.stackExpander.next(ci.callers)
|
||||
return
|
||||
}
|
||||
|
||||
func (se *stackExpander) next(callers []uintptr) (ncallers []uintptr, frame Frame, more bool) {
|
||||
ncallers = callers
|
||||
if !se.pcExpander.more {
|
||||
// Expand the next PC.
|
||||
if len(ci.callers) == 0 {
|
||||
ci.wasPanic = false
|
||||
return Frame{}, false
|
||||
if len(ncallers) == 0 {
|
||||
se.wasPanic = false
|
||||
return ncallers, Frame{}, false
|
||||
}
|
||||
ci.expander.init(ci.callers[0], ci.wasPanic)
|
||||
ci.callers = ci.callers[1:]
|
||||
ci.wasPanic = ci.expander.funcInfo.valid() && ci.expander.funcInfo.entry == sigpanicPC
|
||||
if ci.skip > 0 {
|
||||
for ; ci.skip > 0; ci.skip-- {
|
||||
ci.expander.next()
|
||||
se.pcExpander.init(ncallers[0], se.wasPanic)
|
||||
ncallers = ncallers[1:]
|
||||
se.wasPanic = se.pcExpander.funcInfo.valid() && se.pcExpander.funcInfo.entry == sigpanicPC
|
||||
if se.skip > 0 {
|
||||
for ; se.skip > 0; se.skip-- {
|
||||
se.pcExpander.next()
|
||||
}
|
||||
ci.skip = 0
|
||||
se.skip = 0
|
||||
// Drop skipPleaseUseCallersFrames.
|
||||
ci.callers = ci.callers[1:]
|
||||
ncallers = ncallers[1:]
|
||||
}
|
||||
if !ci.expander.more {
|
||||
if !se.pcExpander.more {
|
||||
// No symbolic information for this PC.
|
||||
// However, we return at least one frame for
|
||||
// every PC, so return an invalid frame.
|
||||
return Frame{}, len(ci.callers) > 0
|
||||
return ncallers, Frame{}, len(ncallers) > 0
|
||||
}
|
||||
}
|
||||
|
||||
frame = ci.expander.next()
|
||||
return frame, ci.expander.more || len(ci.callers) > 0
|
||||
frame = se.pcExpander.next()
|
||||
return ncallers, frame, se.pcExpander.more || len(ncallers) > 0
|
||||
}
|
||||
|
||||
// A pcExpander expands a single PC into a sequence of Frames.
|
||||
|
Loading…
Reference in New Issue
Block a user