// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import (
"runtime/internal/atomic"
"runtime/internal/sys"
"unsafe"
)
// Frames may be used to get function/file/line information for a
// slice of PC values returned by Callers.
type Frames struct {
// callers is a slice of PCs that have not yet been expanded to frames.
callers []uintptr
// frames is a slice of Frames that have yet to be returned.
frames []Frame
frameStore [2]Frame
}
// Frame is the information returned by Frames for each call frame.
type Frame struct {
// PC is the program counter for the location in this frame.
// For a frame that calls another frame, this will be the
// program counter of a call instruction. Because of inlining,
// multiple frames may have the same PC value, but different
// symbolic information.
PC uintptr
// Func is the Func value of this call frame. This may be nil
// for non-Go code or fully inlined functions.
Func *Func
// Function is the package path-qualified function name of
// this call frame. If non-empty, this string uniquely
// identifies a single function in the program.
// This may be the empty string if not known.
// If Func is not nil then Function == Func.Name().
Function string
// File and Line are the file name and line number of the
// location in this frame. For non-leaf frames, this will be
// the location of a call. These may be the empty string and
// zero, respectively, if not known.
File string
Line int
// Entry point program counter for the function; may be zero
// if not known. If Func is not nil then Entry ==
// Func.Entry().
Entry uintptr
// The runtime's internal view of the function. This field
// is set (funcInfo.valid() returns true) only for Go functions,
// not for C functions.
funcInfo funcInfo
}
// 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 {
f := &Frames{callers: callers}
f.frames = f.frameStore[:0]
return f
}
// 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) {
for len(ci.frames) < 2 {
// Find the next frame.
// We need to look for 2 frames so we know what
// to return for the "more" result.
if len(ci.callers) == 0 {
break
}
pc := ci.callers[0]
ci.callers = ci.callers[1:]
funcInfo := findfunc(pc)
if !funcInfo.valid() {
if cgoSymbolizer != nil {
// Pre-expand cgo frames. We could do this
// incrementally, too, but there's no way to
// avoid allocation in this case anyway.
ci.frames = append(ci.frames, expandCgoFrames(pc)...)
}
continue
}
f := funcInfo._Func()
entry := f.Entry()
if pc > entry {
// We store the pc of the start of the instruction following
// the instruction in question (the call or the inline mark).
// This is done for historical reasons, and to make FuncForPC
// work correctly for entries in the result of runtime.Callers.
pc--
}
name := funcname(funcInfo)
if inldata := funcdata(funcInfo, _FUNCDATA_InlTree); inldata != nil {
inltree := (*[1 << 20]inlinedCall)(inldata)
// Non-strict as cgoTraceback may have added bogus PCs
// with a valid funcInfo but invalid PCDATA.
ix := pcdatavalue1(funcInfo, _PCDATA_InlTreeIndex, pc, nil, false)
if ix >= 0 {
// Note: entry is not modified. It always refers to a real frame, not an inlined one.
f = nil
name = funcnameFromNameoff(funcInfo, inltree[ix].func_)
// File/line is already correct.
// TODO: remove file/line from InlinedCall?
}
}
ci.frames = append(ci.frames, Frame{
PC: pc,
Func: f,
Function: name,
Entry: entry,
funcInfo: funcInfo,
// Note: File,Line set below
})
}
// Pop one frame from the frame list. Keep the rest.
// Avoid allocation in the common case, which is 1 or 2 frames.
switch len(ci.frames) {
case 0: // In the rare case when there are no frames at all, we return Frame{}.
return
case 1:
frame = ci.frames[0]
ci.frames = ci.frameStore[:0]
case 2:
frame = ci.frames[0]
ci.frameStore[0] = ci.frames[1]
ci.frames = ci.frameStore[:1]
default:
frame = ci.frames[0]