Commit 69c2c564 authored by Keith Randall's avatar Keith Randall Committed by Keith Randall

cmd/compile,runtime: redo mid-stack inlining tracebacks

Work involved in getting a stack trace is divided between
runtime.Callers and runtime.CallersFrames.

Before this CL, runtime.Callers returns a pc per runtime frame.
runtime.CallersFrames is responsible for expanding a runtime frame
into potentially multiple user frames.

After this CL, runtime.Callers returns a pc per user frame.
runtime.CallersFrames just maps those to user frame info.

Entries in the result of runtime.Callers are now pcs
of the calls (or of the inline marks), not of the instruction
just after the call.

Fixes #29007
Fixes #28640
Update #26320

Change-Id: I1c9567596ff73dc73271311005097a9188c3406f
Reviewed-on: https://go-review.googlesource.com/c/152537
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarDavid Chase <drchase@google.com>
parent c043fc4f
...@@ -179,7 +179,6 @@ func testCallbackCallers(t *testing.T) { ...@@ -179,7 +179,6 @@ func testCallbackCallers(t *testing.T) {
pc := make([]uintptr, 100) pc := make([]uintptr, 100)
n := 0 n := 0
name := []string{ name := []string{
"runtime.call16",
"runtime.cgocallbackg1", "runtime.cgocallbackg1",
"runtime.cgocallbackg", "runtime.cgocallbackg",
"runtime.cgocallback_gofunc", "runtime.cgocallback_gofunc",
...@@ -193,9 +192,6 @@ func testCallbackCallers(t *testing.T) { ...@@ -193,9 +192,6 @@ func testCallbackCallers(t *testing.T) {
"testing.tRunner", "testing.tRunner",
"runtime.goexit", "runtime.goexit",
} }
if unsafe.Sizeof((*byte)(nil)) == 8 {
name[0] = "runtime.call32"
}
nestedCall(func() { nestedCall(func() {
n = runtime.Callers(4, pc) n = runtime.Callers(4, pc)
}) })
......
...@@ -141,7 +141,7 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) { ...@@ -141,7 +141,7 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) {
} }
} }
func ginsnop(pp *gc.Progs) { func ginsnop(pp *gc.Progs) *obj.Prog {
// This is actually not the x86 NOP anymore, // This is actually not the x86 NOP anymore,
// but at the point where it gets used, AX is dead // but at the point where it gets used, AX is dead
// so it's okay if we lose the high bits. // so it's okay if we lose the high bits.
...@@ -150,4 +150,5 @@ func ginsnop(pp *gc.Progs) { ...@@ -150,4 +150,5 @@ func ginsnop(pp *gc.Progs) {
p.From.Reg = x86.REG_AX p.From.Reg = x86.REG_AX
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = x86.REG_AX p.To.Reg = x86.REG_AX
return p
} }
...@@ -68,11 +68,12 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) { ...@@ -68,11 +68,12 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) {
} }
} }
func ginsnop(pp *gc.Progs) { func ginsnop(pp *gc.Progs) *obj.Prog {
p := pp.Prog(arm.AAND) p := pp.Prog(arm.AAND)
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = arm.REG_R0 p.From.Reg = arm.REG_R0
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = arm.REG_R0 p.To.Reg = arm.REG_R0
p.Scond = arm.C_SCOND_EQ p.Scond = arm.C_SCOND_EQ
return p
} }
...@@ -79,7 +79,8 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) { ...@@ -79,7 +79,8 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) {
} }
} }
func ginsnop(pp *gc.Progs) { func ginsnop(pp *gc.Progs) *obj.Prog {
p := pp.Prog(arm64.AHINT) p := pp.Prog(arm64.AHINT)
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
return p
} }
...@@ -174,6 +174,7 @@ var goopnames = []string{ ...@@ -174,6 +174,7 @@ var goopnames = []string{
OGT: ">", OGT: ">",
OIF: "if", OIF: "if",
OIMAG: "imag", OIMAG: "imag",
OINLMARK: "inlmark",
ODEREF: "*", ODEREF: "*",
OLEN: "len", OLEN: "len",
OLE: "<=", OLE: "<=",
...@@ -942,6 +943,9 @@ func (n *Node) stmtfmt(s fmt.State, mode fmtMode) { ...@@ -942,6 +943,9 @@ func (n *Node) stmtfmt(s fmt.State, mode fmtMode) {
case ORETJMP: case ORETJMP:
mode.Fprintf(s, "retjmp %v", n.Sym) mode.Fprintf(s, "retjmp %v", n.Sym)
case OINLMARK:
mode.Fprintf(s, "inlmark %d", n.Xoffset)
case OGO: case OGO:
mode.Fprintf(s, "go %v", n.Left) mode.Fprintf(s, "go %v", n.Left)
......
...@@ -257,7 +257,7 @@ type Arch struct { ...@@ -257,7 +257,7 @@ type Arch struct {
PadFrame func(int64) int64 PadFrame func(int64) int64
ZeroRange func(*Progs, *obj.Prog, int64, int64, *uint32) *obj.Prog ZeroRange func(*Progs, *obj.Prog, int64, int64, *uint32) *obj.Prog
Ginsnop func(*Progs) Ginsnop func(*Progs) *obj.Prog
// SSAMarkMoves marks any MOVXconst ops that need to avoid clobbering flags. // SSAMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
SSAMarkMoves func(*SSAGenState, *ssa.Block) SSAMarkMoves func(*SSAGenState, *ssa.Block)
......
...@@ -1063,6 +1063,15 @@ func mkinlcall(n, fn *Node, maxCost int32) *Node { ...@@ -1063,6 +1063,15 @@ func mkinlcall(n, fn *Node, maxCost int32) *Node {
} }
newIndex := Ctxt.InlTree.Add(parent, n.Pos, fn.Sym.Linksym()) newIndex := Ctxt.InlTree.Add(parent, n.Pos, fn.Sym.Linksym())
// Add a inline mark just before the inlined body.
// This mark is inline in the code so that it's a reasonable spot
// to put a breakpoint. Not sure if that's really necessary or not
// (in which case it could go at the end of the function instead).
inlMark := nod(OINLMARK, nil, nil)
inlMark.Pos = n.Pos
inlMark.Xoffset = int64(newIndex)
ninit.Append(inlMark)
if genDwarfInline > 0 { if genDwarfInline > 0 {
if !fn.Sym.Linksym().WasInlined() { if !fn.Sym.Linksym().WasInlined() {
Ctxt.DwFixups.SetPrecursorFunc(fn.Sym.Linksym(), fn) Ctxt.DwFixups.SetPrecursorFunc(fn.Sym.Linksym(), fn)
......
...@@ -553,7 +553,7 @@ func (o *Order) stmt(n *Node) { ...@@ -553,7 +553,7 @@ func (o *Order) stmt(n *Node) {
default: default:
Fatalf("orderstmt %v", n.Op) Fatalf("orderstmt %v", n.Op)
case OVARKILL, OVARLIVE: case OVARKILL, OVARLIVE, OINLMARK:
o.out = append(o.out, n) o.out = append(o.out, n)
case OAS: case OAS:
......
...@@ -1204,6 +1204,9 @@ func (s *state) stmt(n *Node) { ...@@ -1204,6 +1204,9 @@ func (s *state) stmt(n *Node) {
p := s.expr(n.Left) p := s.expr(n.Left)
s.nilCheck(p) s.nilCheck(p)
case OINLMARK:
s.newValue1I(ssa.OpInlMark, types.TypeVoid, n.Xoffset, s.mem())
default: default:
s.Fatalf("unhandled stmt %v", n.Op) s.Fatalf("unhandled stmt %v", n.Op)
} }
...@@ -5163,6 +5166,14 @@ func genssa(f *ssa.Func, pp *Progs) { ...@@ -5163,6 +5166,14 @@ func genssa(f *ssa.Func, pp *Progs) {
if v.Args[0].Reg() != v.Reg() { if v.Args[0].Reg() != v.Reg() {
v.Fatalf("OpConvert should be a no-op: %s; %s", v.Args[0].LongString(), v.LongString()) v.Fatalf("OpConvert should be a no-op: %s; %s", v.Args[0].LongString(), v.LongString())
} }
case ssa.OpInlMark:
p := thearch.Ginsnop(s.pp)
if pp.curfn.Func.lsym != nil {
// lsym is nil if the function name is "_".
pp.curfn.Func.lsym.Func.AddInlMark(p, v.AuxInt32())
}
// TODO: if matching line number, merge somehow with previous instruction?
default: default:
// let the backend handle it // let the backend handle it
// Special case for first line in function; move it to the start. // Special case for first line in function; move it to the start.
...@@ -5543,6 +5554,7 @@ func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog { ...@@ -5543,6 +5554,7 @@ func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog {
s.PrepareCall(v) s.PrepareCall(v)
p := s.Prog(obj.ACALL) p := s.Prog(obj.ACALL)
p.Pos = v.Pos
if sym, ok := v.Aux.(*obj.LSym); ok { if sym, ok := v.Aux.(*obj.LSym); ok {
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN p.To.Name = obj.NAME_EXTERN
......
...@@ -46,6 +46,7 @@ type Node struct { ...@@ -46,6 +46,7 @@ type Node struct {
// - ODOT, ODOTPTR, and OINDREGSP use it to indicate offset relative to their base address. // - ODOT, ODOTPTR, and OINDREGSP use it to indicate offset relative to their base address.
// - OSTRUCTKEY uses it to store the named field's offset. // - OSTRUCTKEY uses it to store the named field's offset.
// - Named OLITERALs use it to store their ambient iota value. // - Named OLITERALs use it to store their ambient iota value.
// - OINLMARK stores an index into the inlTree data structure.
// Possibly still more uses. If you find any, document them. // Possibly still more uses. If you find any, document them.
Xoffset int64 Xoffset int64
...@@ -750,6 +751,7 @@ const ( ...@@ -750,6 +751,7 @@ const (
OVARKILL // variable is dead OVARKILL // variable is dead
OVARLIVE // variable is alive OVARLIVE // variable is alive
OINDREGSP // offset plus indirect of REGSP, such as 8(SP). OINDREGSP // offset plus indirect of REGSP, such as 8(SP).
OINLMARK // start of an inlined body, with file/line of caller. Xoffset is an index into the inline tree.
// arch-specific opcodes // arch-specific opcodes
ORETJMP // return to other function ORETJMP // return to other function
......
...@@ -322,6 +322,9 @@ func walkstmt(n *Node) *Node { ...@@ -322,6 +322,9 @@ func walkstmt(n *Node) *Node {
case ORETJMP: case ORETJMP:
break break
case OINLMARK:
break
case OSELECT: case OSELECT:
walkselect(n) walkselect(n)
......
...@@ -59,10 +59,11 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) { ...@@ -59,10 +59,11 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) {
} }
} }
func ginsnop(pp *gc.Progs) { func ginsnop(pp *gc.Progs) *obj.Prog {
p := pp.Prog(mips.ANOR) p := pp.Prog(mips.ANOR)
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = mips.REG_R0 p.From.Reg = mips.REG_R0
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R0 p.To.Reg = mips.REG_R0
return p
} }
...@@ -63,10 +63,11 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) { ...@@ -63,10 +63,11 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) {
} }
} }
func ginsnop(pp *gc.Progs) { func ginsnop(pp *gc.Progs) *obj.Prog {
p := pp.Prog(mips.ANOR) p := pp.Prog(mips.ANOR)
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = mips.REG_R0 p.From.Reg = mips.REG_R0
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R0 p.To.Reg = mips.REG_R0
return p
} }
...@@ -58,15 +58,16 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) { ...@@ -58,15 +58,16 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) {
} }
} }
func ginsnop(pp *gc.Progs) { func ginsnop(pp *gc.Progs) *obj.Prog {
p := pp.Prog(ppc64.AOR) p := pp.Prog(ppc64.AOR)
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = ppc64.REG_R0 p.From.Reg = ppc64.REG_R0
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = ppc64.REG_R0 p.To.Reg = ppc64.REG_R0
return p
} }
func ginsnop2(pp *gc.Progs) { func ginsnop2(pp *gc.Progs) *obj.Prog {
// PPC64 is unusual because TWO nops are required // PPC64 is unusual because TWO nops are required
// (see gc/cgen.go, gc/plive.go -- copy of comment below) // (see gc/cgen.go, gc/plive.go -- copy of comment below)
// //
...@@ -87,7 +88,7 @@ func ginsnop2(pp *gc.Progs) { ...@@ -87,7 +88,7 @@ func ginsnop2(pp *gc.Progs) {
p.From.Reg = ppc64.REGSP p.From.Reg = ppc64.REGSP
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = ppc64.REG_R2 p.To.Reg = ppc64.REG_R2
} else { return p
ginsnop(pp)
} }
return ginsnop(pp)
} }
...@@ -104,10 +104,11 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) { ...@@ -104,10 +104,11 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) {
} }
} }
func ginsnop(pp *gc.Progs) { func ginsnop(pp *gc.Progs) *obj.Prog {
p := pp.Prog(s390x.AOR) p := pp.Prog(s390x.AOR)
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = int16(s390x.REG_R0) p.From.Reg = int16(s390x.REG_R0)
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = int16(s390x.REG_R0) p.To.Reg = int16(s390x.REG_R0)
return p
} }
...@@ -85,7 +85,7 @@ func liveValues(f *Func, reachable []bool) (live []bool, liveOrderStmts []*Value ...@@ -85,7 +85,7 @@ func liveValues(f *Func, reachable []bool) (live []bool, liveOrderStmts []*Value
} }
} }
if v.Type.IsVoid() && !live[v.ID] { if v.Type.IsVoid() && !live[v.ID] {
// The only Void ops are nil checks. We must keep these. // The only Void ops are nil checks and inline marks. We must keep these.
live[v.ID] = true live[v.ID] = true
q = append(q, v) q = append(q, v)
if v.Pos.IsStmt() != src.PosNotStmt { if v.Pos.IsStmt() != src.PosNotStmt {
......
...@@ -480,6 +480,10 @@ var genericOps = []opData{ ...@@ -480,6 +480,10 @@ var genericOps = []opData{
{name: "VarLive", argLength: 1, aux: "Sym", symEffect: "Read", zeroWidth: true}, // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem {name: "VarLive", argLength: 1, aux: "Sym", symEffect: "Read", zeroWidth: true}, // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem
{name: "KeepAlive", argLength: 2, typ: "Mem", zeroWidth: true}, // arg[0] is a value that must be kept alive until this mark. arg[1]=mem, returns mem {name: "KeepAlive", argLength: 2, typ: "Mem", zeroWidth: true}, // arg[0] is a value that must be kept alive until this mark. arg[1]=mem, returns mem
// InlMark marks the start of an inlined function body. Its AuxInt field
// distinguishes which entry in the local inline tree it is marking.
{name: "InlMark", argLength: 1, aux: "Int32", typ: "Void"}, // arg[0]=mem, returns void.
// Ops for breaking 64-bit operations on 32-bit architectures // Ops for breaking 64-bit operations on 32-bit architectures
{name: "Int64Make", argLength: 2, typ: "UInt64"}, // arg0=hi, arg1=lo {name: "Int64Make", argLength: 2, typ: "UInt64"}, // arg0=hi, arg1=lo
{name: "Int64Hi", argLength: 1, typ: "UInt32"}, // high 32-bit of arg0 {name: "Int64Hi", argLength: 1, typ: "UInt32"}, // high 32-bit of arg0
......
...@@ -21,7 +21,7 @@ func checkLower(f *Func) { ...@@ -21,7 +21,7 @@ func checkLower(f *Func) {
continue // lowered continue // lowered
} }
switch v.Op { switch v.Op {
case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive, OpKeepAlive, OpSelect0, OpSelect1, OpConvert: case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive, OpKeepAlive, OpSelect0, OpSelect1, OpConvert, OpInlMark:
continue // ok not to lower continue // ok not to lower
case OpGetG: case OpGetG:
if f.Config.hasGReg { if f.Config.hasGReg {
......
...@@ -2398,6 +2398,7 @@ const ( ...@@ -2398,6 +2398,7 @@ const (
OpVarKill OpVarKill
OpVarLive OpVarLive
OpKeepAlive OpKeepAlive
OpInlMark
OpInt64Make OpInt64Make
OpInt64Hi OpInt64Hi
OpInt64Lo OpInt64Lo
...@@ -29796,6 +29797,12 @@ var opcodeTable = [...]opInfo{ ...@@ -29796,6 +29797,12 @@ var opcodeTable = [...]opInfo{
zeroWidth: true, zeroWidth: true,
generic: true, generic: true,
}, },
{
name: "InlMark",
auxType: auxInt32,
argLen: 1,
generic: true,
},
{ {
name: "Int64Make", name: "Int64Make",
argLen: 2, argLen: 2,
......
...@@ -58,8 +58,8 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) { ...@@ -58,8 +58,8 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) {
} }
} }
func ginsnop(pp *gc.Progs) { func ginsnop(pp *gc.Progs) *obj.Prog {
pp.Prog(wasm.ANop) return pp.Prog(wasm.ANop)
} }
func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) { func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
...@@ -134,10 +134,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { ...@@ -134,10 +134,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
if sym, ok := v.Aux.(*obj.LSym); ok { if sym, ok := v.Aux.(*obj.LSym); ok {
p := s.Prog(obj.ACALL) p := s.Prog(obj.ACALL)
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym} p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym}
p.Pos = v.Pos
} else { } else {
getValue64(s, v.Args[0]) getValue64(s, v.Args[0])
p := s.Prog(obj.ACALL) p := s.Prog(obj.ACALL)
p.To = obj.Addr{Type: obj.TYPE_NONE} p.To = obj.Addr{Type: obj.TYPE_NONE}
p.Pos = v.Pos
} }
case ssa.OpWasmLoweredMove: case ssa.OpWasmLoweredMove:
......
...@@ -53,10 +53,11 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) { ...@@ -53,10 +53,11 @@ func zeroAuto(pp *gc.Progs, n *gc.Node) {
} }
} }
func ginsnop(pp *gc.Progs) { func ginsnop(pp *gc.Progs) *obj.Prog {
p := pp.Prog(x86.AXCHGL) p := pp.Prog(x86.AXCHGL)
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = x86.REG_AX p.From.Reg = x86.REG_AX
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = x86.REG_AX p.To.Reg = x86.REG_AX
return p
} }
...@@ -119,10 +119,11 @@ type FuncData struct { ...@@ -119,10 +119,11 @@ type FuncData struct {
// An InlinedCall is a node in an InlTree. // An InlinedCall is a node in an InlTree.
// See cmd/internal/obj.InlTree for details. // See cmd/internal/obj.InlTree for details.
type InlinedCall struct { type InlinedCall struct {
Parent int64 Parent int64
File string File string
Line int64 Line int64
Func SymID Func SymID
ParentPC int64
} }
// A Package is a parsed Go object file or archive defining a Go package. // A Package is a parsed Go object file or archive defining a Go package.
...@@ -610,6 +611,7 @@ func (r *objReader) parseObject(prefix []byte) error { ...@@ -610,6 +611,7 @@ func (r *objReader) parseObject(prefix []byte) error {
f.InlTree[i].File = r.readSymID().Name f.InlTree[i].File = r.readSymID().Name
f.InlTree[i].Line = r.readInt() f.InlTree[i].Line = r.readInt()
f.InlTree[i].Func = r.readSymID() f.InlTree[i].Func = r.readSymID()
f.InlTree[i].ParentPC = r.readInt()
} }
} }
} }
......
...@@ -47,9 +47,10 @@ type InlTree struct { ...@@ -47,9 +47,10 @@ type InlTree struct {
// InlinedCall is a node in an InlTree. // InlinedCall is a node in an InlTree.
type InlinedCall struct { type InlinedCall struct {
Parent int // index of the parent in the InlTree or < 0 if outermost call Parent int // index of the parent in the InlTree or < 0 if outermost call
Pos src.XPos // position of the inlined call Pos src.XPos // position of the inlined call
Func *LSym // function that was inlined Func *LSym // function that was inlined
ParentPC int32 // PC of instruction just before inlined body. Only valid in local trees.
} }
// Add adds a new call to the tree, returning its index. // Add adds a new call to the tree, returning its index.
...@@ -76,6 +77,10 @@ func (tree *InlTree) CallPos(inlIndex int) src.XPos { ...@@ -76,6 +77,10 @@ func (tree *InlTree) CallPos(inlIndex int) src.XPos {
return tree.nodes[inlIndex].Pos return tree.nodes[inlIndex].Pos
} }
func (tree *InlTree) setParentPC(inlIndex int, pc int32) {
tree.nodes[inlIndex].ParentPC = pc
}
// OutermostPos returns the outermost position corresponding to xpos, // OutermostPos returns the outermost position corresponding to xpos,
// which is where xpos was ultimately inlined to. In the example for // which is where xpos was ultimately inlined to. In the example for
// InlTree, main() contains inlined AST nodes from h(), but the // InlTree, main() contains inlined AST nodes from h(), but the
...@@ -106,6 +111,6 @@ func (ctxt *Link) InnermostPos(xpos src.XPos) src.Pos { ...@@ -106,6 +111,6 @@ func (ctxt *Link) InnermostPos(xpos src.XPos) src.Pos {
func dumpInlTree(ctxt *Link, tree InlTree) { func dumpInlTree(ctxt *Link, tree InlTree) {
for i, call := range tree.nodes { for i, call := range tree.nodes {
pos := ctxt.PosTable.Pos(call.Pos) pos := ctxt.PosTable.Pos(call.Pos)
ctxt.Logf("%0d | %0d | %s (%s)\n", i, call.Parent, call.Func, pos) ctxt.Logf("%0d | %0d | %s (%s) pc=%d\n", i, call.Parent, call.Func, pos, call.ParentPC)
} }
} }
...@@ -393,11 +393,12 @@ type LSym struct { ...@@ -393,11 +393,12 @@ type LSym struct {
// A FuncInfo contains extra fields for STEXT symbols. // A FuncInfo contains extra fields for STEXT symbols.
type FuncInfo struct { type FuncInfo struct {
Args int32 Args int32
Locals int32 Locals int32
Text *Prog Text *Prog
Autom []*Auto Autom []*Auto
Pcln Pcln Pcln Pcln
InlMarks []InlMark
dwarfInfoSym *LSym dwarfInfoSym *LSym
dwarfLocSym *LSym dwarfLocSym *LSym
...@@ -411,6 +412,23 @@ type FuncInfo struct { ...@@ -411,6 +412,23 @@ type FuncInfo struct {
StackObjects *LSym StackObjects *LSym
} }
type InlMark struct {
// When unwinding from an instruction in an inlined body, mark
// where we should unwind to.
// id records the global inlining id of the inlined body.
// p records the location of an instruction in the parent (inliner) frame.
p *Prog
id int32
}
// Mark p as the instruction to set as the pc when
// "unwinding" the inlining global frame id. Usually it should be
// instruction with a file:line at the callsite, and occur
// just before the body of the inlined function.
func (fi *FuncInfo) AddInlMark(p *Prog, id int32) {
fi.InlMarks = append(fi.InlMarks, InlMark{p: p, id: id})
}
//go:generate stringer -type ABI //go:generate stringer -type ABI
// ABI is the calling convention of a text symbol. // ABI is the calling convention of a text symbol.
......
...@@ -388,6 +388,7 @@ func (w *objWriter) writeSym(s *LSym) { ...@@ -388,6 +388,7 @@ func (w *objWriter) writeSym(s *LSym) {
w.writeRefIndex(fsym) w.writeRefIndex(fsym)
w.writeInt(int64(l)) w.writeInt(int64(l))
w.writeRefIndex(call.Func) w.writeRefIndex(call.Func)
w.writeInt(int64(call.ParentPC))
} }
} }
......
...@@ -193,6 +193,19 @@ func (s *pcinlineState) addBranch(ctxt *Link, globalIndex int) int { ...@@ -193,6 +193,19 @@ func (s *pcinlineState) addBranch(ctxt *Link, globalIndex int) int {
return localIndex return localIndex
} }
func (s *pcinlineState) setParentPC(ctxt *Link, globalIndex int, pc int32) {
localIndex, ok := s.globalToLocal[globalIndex]
if !ok {
// We know where to unwind to when we need to unwind a body identified
// by globalIndex. But there may be no instructions generated by that
// body (it's empty, or its instructions were CSEd with other things, etc.).
// In that case, we don't need an unwind entry.
// TODO: is this really right? Seems to happen a whole lot...
return
}
s.localTree.setParentPC(localIndex, pc)
}
// pctoinline computes the index into the local inlining tree to use at p. // pctoinline computes the index into the local inlining tree to use at p.
// If p is not the result of inlining, pctoinline returns -1. Because p.Pos // If p is not the result of inlining, pctoinline returns -1. Because p.Pos
// applies to p, phase == 0 (before p) takes care of the update. // applies to p, phase == 0 (before p) takes care of the update.
...@@ -323,6 +336,9 @@ func linkpcln(ctxt *Link, cursym *LSym) { ...@@ -323,6 +336,9 @@ func linkpcln(ctxt *Link, cursym *LSym) {
pcinlineState := new(pcinlineState) pcinlineState := new(pcinlineState)
funcpctab(ctxt, &pcln.Pcinline, cursym, "pctoinline", pcinlineState.pctoinline, nil) funcpctab(ctxt, &pcln.Pcinline, cursym, "pctoinline", pcinlineState.pctoinline, nil)
for _, inlMark := range cursym.Func.InlMarks {
pcinlineState.setParentPC(ctxt, int(inlMark.id), int32(inlMark.p.Pc))
}
pcln.InlTree = pcinlineState.localTree pcln.InlTree = pcinlineState.localTree
if ctxt.Debugpcln == "pctoinline" && len(pcln.InlTree.nodes) > 0 { if ctxt.Debugpcln == "pctoinline" && len(pcln.InlTree.nodes) > 0 {
ctxt.Logf("-- inlining tree for %s:\n", cursym) ctxt.Logf("-- inlining tree for %s:\n", cursym)
......
...@@ -243,7 +243,6 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { ...@@ -243,7 +243,6 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
for p := s.Func.Text; p != nil; p = p.Link { for p := s.Func.Text; p != nil; p = p.Link {
prevBase := base prevBase := base
base = ctxt.PosTable.Pos(p.Pos).Base() base = ctxt.PosTable.Pos(p.Pos).Base()
switch p.As { switch p.As {
case ABlock, ALoop, AIf: case ABlock, ALoop, AIf:
explicitBlockDepth++ explicitBlockDepth++
...@@ -279,7 +278,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { ...@@ -279,7 +278,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
// more often to avoid bloat of the BrTable instruction. // more often to avoid bloat of the BrTable instruction.
// The "base != prevBase" condition detects inlined instructions. They are an // The "base != prevBase" condition detects inlined instructions. They are an
// implicit call, so entering and leaving this section affects the stack trace. // implicit call, so entering and leaving this section affects the stack trace.
if p.As == ACALLNORESUME || p.As == obj.ANOP || p.Spadj != 0 || base != prevBase { if p.As == ACALLNORESUME || p.As == obj.ANOP || p.As == ANop || p.Spadj != 0 || base != prevBase {
pc++ pc++
} }
} }
......
...@@ -4,6 +4,11 @@ ...@@ -4,6 +4,11 @@
package objabi package objabi
import (
"strconv"
"strings"
)
// A FuncID identifies particular functions that need to be treated // A FuncID identifies particular functions that need to be treated
// specially by the runtime. // specially by the runtime.
// Note that in some situations involving plugins, there may be multiple // Note that in some situations involving plugins, there may be multiple
...@@ -30,4 +35,62 @@ const ( ...@@ -30,4 +35,62 @@ const (
FuncID_gogo FuncID_gogo
FuncID_externalthreadhandler FuncID_externalthreadhandler
FuncID_debugCallV1 FuncID_debugCallV1
FuncID_gopanic
FuncID_panicwrap
FuncID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
) )
// Get the function ID for the named function in the named file.
// The function should be package-qualified.
func GetFuncID(name, file string) FuncID {
switch name {
case "runtime.main":
return FuncID_runtime_main
case "runtime.goexit":
return FuncID_goexit
case "runtime.jmpdefer":
return FuncID_jmpdefer
case "runtime.mcall":
return FuncID_mcall
case "runtime.morestack":
return FuncID_morestack
case "runtime.mstart":
return FuncID_mstart
case "runtime.rt0_go":
return FuncID_rt0_go
case "runtime.asmcgocall":
return FuncID_asmcgocall
case "runtime.sigpanic":
return FuncID_sigpanic
case "runtime.runfinq":
return FuncID_runfinq
case "runtime.gcBgMarkWorker":
return FuncID_gcBgMarkWorker
case "runtime.systemstack_switch":
return FuncID_systemstack_switch
case "runtime.systemstack":
return FuncID_systemstack
case "runtime.cgocallback_gofunc":
return FuncID_cgocallback_gofunc
case "runtime.gogo":
return FuncID_gogo
case "runtime.externalthreadhandler":
return FuncID_externalthreadhandler
case "runtime.debugCallV1":
return FuncID_debugCallV1
case "runtime.gopanic":
return FuncID_gopanic
case "runtime.panicwrap":
return FuncID_panicwrap
}
if file == "<autogenerated>" && !strings.HasSuffix(name, ".init") {
return FuncID_wrapper
}
if strings.HasPrefix(name, "runtime.call") {
if _, err := strconv.Atoi(name[12:]); err == nil {
// runtime.callXX reflect call wrappers.
return FuncID_wrapper
}
}
return FuncID_normal
}
...@@ -368,10 +368,13 @@ func (ctxt *Link) pclntab() { ...@@ -368,10 +368,13 @@ func (ctxt *Link) pclntab() {
numberfile(ctxt, call.File) numberfile(ctxt, call.File)
nameoff := nameToOffset(call.Func.Name) nameoff := nameToOffset(call.Func.Name)
inlTreeSym.SetUint32(ctxt.Arch, int64(i*16+0), uint32(call.Parent)) inlTreeSym.SetUint16(ctxt.Arch, int64(i*20+0), uint16(call.Parent))
inlTreeSym.SetUint32(ctxt.Arch, int64(i*16+4), uint32(call.File.Value)) inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func.Name, call.Func.File)))
inlTreeSym.SetUint32(ctxt.Arch, int64(i*16+8), uint32(call.Line)) // byte 3 is unused
inlTreeSym.SetUint32(ctxt.Arch, int64(i*16+12), uint32(nameoff)) inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+4), uint32(call.File.Value))
inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+8), uint32(call.Line))
inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+12), uint32(nameoff))
inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+16), uint32(call.ParentPC))
} }
pcln.Funcdata[objabi.FUNCDATA_InlTree] = inlTreeSym pcln.Funcdata[objabi.FUNCDATA_InlTree] = inlTreeSym
...@@ -386,43 +389,12 @@ func (ctxt *Link) pclntab() { ...@@ -386,43 +389,12 @@ func (ctxt *Link) pclntab() {
off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Pcdata)))) off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Pcdata))))
// funcID uint8 // funcID uint8
funcID := objabi.FuncID_normal var file string
switch s.Name { if s.FuncInfo != nil && len(s.FuncInfo.File) > 0 {
case "runtime.main": file = s.FuncInfo.File[0].Name
funcID = objabi.FuncID_runtime_main
case "runtime.goexit":
funcID = objabi.FuncID_goexit
case "runtime.jmpdefer":
funcID = objabi.FuncID_jmpdefer
case "runtime.mcall":
funcID = objabi.FuncID_mcall
case "runtime.morestack":
funcID = objabi.FuncID_morestack
case "runtime.mstart":
funcID = objabi.FuncID_mstart
case "runtime.rt0_go":
funcID = objabi.FuncID_rt0_go
case "runtime.asmcgocall":
funcID = objabi.FuncID_asmcgocall
case "runtime.sigpanic":
funcID = objabi.FuncID_sigpanic
case "runtime.runfinq":
funcID = objabi.FuncID_runfinq
case "runtime.gcBgMarkWorker":
funcID = objabi.FuncID_gcBgMarkWorker
case "runtime.systemstack_switch":
funcID = objabi.FuncID_systemstack_switch
case "runtime.systemstack":
funcID = objabi.FuncID_systemstack
case "runtime.cgocallback_gofunc":
funcID = objabi.FuncID_cgocallback_gofunc
case "runtime.gogo":
funcID = objabi.FuncID_gogo
case "runtime.externalthreadhandler":
funcID = objabi.FuncID_externalthreadhandler
case "runtime.debugCallV1":
funcID = objabi.FuncID_debugCallV1
} }
funcID := objabi.GetFuncID(s.Name, file)
off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID))) off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
// unused // unused
......
...@@ -318,6 +318,7 @@ overwrite: ...@@ -318,6 +318,7 @@ overwrite:
pc.InlTree[i].File = r.readSymIndex() pc.InlTree[i].File = r.readSymIndex()
pc.InlTree[i].Line = r.readInt32() pc.InlTree[i].Line = r.readInt32()
pc.InlTree[i].Func = r.readSymIndex() pc.InlTree[i].Func = r.readSymIndex()
pc.InlTree[i].ParentPC = r.readInt32()
} }
if !dupok { if !dupok {
......
...@@ -28,7 +28,7 @@ type Symbol struct { ...@@ -28,7 +28,7 @@ type Symbol struct {
Sub *Symbol Sub *Symbol
Outer *Symbol Outer *Symbol
Gotype *Symbol Gotype *Symbol
File string File string // actually package!
auxinfo *AuxSymbol auxinfo *AuxSymbol
Sect *Section Sect *Section
FuncInfo *FuncInfo FuncInfo *FuncInfo
...@@ -150,6 +150,10 @@ func (s *Symbol) SetUint8(arch *sys.Arch, r int64, v uint8) int64 { ...@@ -150,6 +150,10 @@ func (s *Symbol) SetUint8(arch *sys.Arch, r int64, v uint8) int64 {
return s.setUintXX(arch, r, uint64(v), 1) return s.setUintXX(arch, r, uint64(v), 1)
} }
func (s *Symbol) SetUint16(arch *sys.Arch, r int64, v uint16) int64 {
return s.setUintXX(arch, r, uint64(v), 2)
}
func (s *Symbol) SetUint32(arch *sys.Arch, r int64, v uint32) int64 { func (s *Symbol) SetUint32(arch *sys.Arch, r int64, v uint32) int64 {
return s.setUintXX(arch, r, uint64(v), 4) return s.setUintXX(arch, r, uint64(v), 4)
} }
...@@ -507,10 +511,11 @@ type FuncInfo struct { ...@@ -507,10 +511,11 @@ type FuncInfo struct {
// InlinedCall is a node in a local inlining tree (FuncInfo.InlTree). // InlinedCall is a node in a local inlining tree (FuncInfo.InlTree).
type InlinedCall struct { type InlinedCall struct {
Parent int32 // index of parent in InlTree Parent int32 // index of parent in InlTree
File *Symbol // file of the inlined call File *Symbol // file of the inlined call
Line int32 // line number of the inlined call Line int32 // line number of the inlined call
Func *Symbol // function that was inlined Func *Symbol // function that was inlined
ParentPC int32 // PC of the instruction just before the inlined body (offset from function start)
} }
type Pcdata struct { type Pcdata struct {
......
...@@ -166,27 +166,13 @@ import "runtime/internal/sys" ...@@ -166,27 +166,13 @@ import "runtime/internal/sys"
// program counter, file name, and line number within the file of the corresponding // program counter, file name, and line number within the file of the corresponding
// call. The boolean ok is false if it was not possible to recover the information. // call. The boolean ok is false if it was not possible to recover the information.
func Caller(skip int) (pc uintptr, file string, line int, ok bool) { func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
// Make room for three PCs: the one we were asked for, rpc := make([]uintptr, 1)
// what it called, so that CallersFrames can see if it "called" n := callers(skip+1, rpc[:])
// sigpanic, and possibly a PC for skipPleaseUseCallersFrames. if n < 1 {
var rpc [3]uintptr
if callers(skip, rpc[:]) < 2 {
return return
} }
var stackExpander stackExpander frame, _ := CallersFrames(rpc).Next()
callers := stackExpander.init(rpc[:]) return frame.PC, frame.File, frame.Line, frame.PC != 0
// We asked for one extra, so skip that one. If this is sigpanic,
// stepping over this frame will set up state in Frames so the
// next frame is correct.
callers, _, ok = stackExpander.next(callers, true)
if !ok {
return
}
_, frame, _ := stackExpander.next(callers, true)
pc = frame.PC
file = frame.File
line = frame.Line
return
} }
// Callers fills the slice pc with the return program counters of function invocations // Callers fills the slice pc with the return program counters of function invocations
......
...@@ -208,7 +208,7 @@ func (b *profileBuilder) pbMapping(tag int, id, base, limit, offset uint64, file ...@@ -208,7 +208,7 @@ func (b *profileBuilder) pbMapping(tag int, id, base, limit, offset uint64, file
} }
// locForPC returns the location ID for addr. // locForPC returns the location ID for addr.
// addr must be a return PC. This returns the location of the call. // addr must a PC which is part of a call or the PC of an inline marker. This returns the location of the call.
// It may emit to b.pb, so there must be no message encoding in progress. // It may emit to b.pb, so there must be no message encoding in progress.
func (b *profileBuilder) locForPC(addr uintptr) uint64 { func (b *profileBuilder) locForPC(addr uintptr) uint64 {
id := uint64(b.locs[addr]) id := uint64(b.locs[addr])
...@@ -236,7 +236,7 @@ func (b *profileBuilder) locForPC(addr uintptr) uint64 { ...@@ -236,7 +236,7 @@ func (b *profileBuilder) locForPC(addr uintptr) uint64 {
if frame.PC == 0 { if frame.PC == 0 {
// If we failed to resolve the frame, at least make up // If we failed to resolve the frame, at least make up
// a reasonable call PC. This mostly happens in tests. // a reasonable call PC. This mostly happens in tests.
frame.PC = addr - 1 frame.PC = addr
} }
// We can't write out functions while in the middle of the // We can't write out functions while in the middle of the
...@@ -403,16 +403,7 @@ func (b *profileBuilder) build() { ...@@ -403,16 +403,7 @@ func (b *profileBuilder) build() {
} }
locs = locs[:0] locs = locs[:0]
for i, addr := range e.stk { for _, addr := range e.stk {
// Addresses from stack traces point to the
// next instruction after each call, except
// for the leaf, which points to where the
// signal occurred. locForPC expects return
// PCs, so increment the leaf address to look
// like a return PC.
if i == 0 {
addr++
}
l := b.locForPC(addr) l := b.locForPC(addr)
if l == 0 { // runtime.goexit if l == 0 { // runtime.goexit
continue continue
......
...@@ -133,11 +133,11 @@ func TestConvertCPUProfile(t *testing.T) { ...@@ -133,11 +133,11 @@ func TestConvertCPUProfile(t *testing.T) {
samples := []*profile.Sample{ samples := []*profile.Sample{
{Value: []int64{20, 20 * 2000 * 1000}, Location: []*profile.Location{ {Value: []int64{20, 20 * 2000 * 1000}, Location: []*profile.Location{
{ID: 1, Mapping: map1, Address: addr1}, {ID: 1, Mapping: map1, Address: addr1},
{ID: 2, Mapping: map1, Address: addr1 + 1}, {ID: 2, Mapping: map1, Address: addr1 + 2},
}}, }},
{Value: []int64{40, 40 * 2000 * 1000}, Location: []*profile.Location{ {Value: []int64{40, 40 * 2000 * 1000}, Location: []*profile.Location{
{ID: 3, Mapping: map2, Address: addr2}, {ID: 3, Mapping: map2, Address: addr2},
{ID: 4, Mapping: map2, Address: addr2 + 1}, {ID: 4, Mapping: map2, Address: addr2 + 2},
}}, }},
} }
checkProfile(t, p, period, periodType, sampleType, samples, "") checkProfile(t, p, period, periodType, sampleType, samples, "")
......
...@@ -14,11 +14,7 @@ import ( ...@@ -14,11 +14,7 @@ import (
func TestConvertMemProfile(t *testing.T) { func TestConvertMemProfile(t *testing.T) {
addr1, addr2, map1, map2 := testPCs(t) addr1, addr2, map1, map2 := testPCs(t)
// MemProfileRecord stacks are return PCs, so add one to the a1, a2 := uintptr(addr1), uintptr(addr2)
// addresses recorded in the "profile". The proto profile
// locations are call PCs, so conversion will subtract one
// from these and get back to addr1 and addr2.
a1, a2 := uintptr(addr1)+1, uintptr(addr2)+1
rate := int64(512 * 1024) rate := int64(512 * 1024)
rec := []runtime.MemProfileRecord{ rec := []runtime.MemProfileRecord{
{AllocBytes: 4096, FreeBytes: 1024, AllocObjects: 4, FreeObjects: 1, Stack0: [32]uintptr{a1, a2}}, {AllocBytes: 4096, FreeBytes: 1024, AllocObjects: 4, FreeObjects: 1, Stack0: [32]uintptr{a1, a2}},
......
This diff is collapsed.
...@@ -179,6 +179,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in ...@@ -179,6 +179,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
var cache pcvalueCache var cache pcvalueCache
lastFuncID := funcID_normal
n := 0 n := 0
for n < max { for n < max {
// Typically: // Typically:
...@@ -344,48 +345,44 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in ...@@ -344,48 +345,44 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
} }
if pcbuf != nil { if pcbuf != nil {
if skip == 0 { // backup to CALL instruction to read inlining info (same logic as below)
(*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = frame.pc tracepc := frame.pc
} else { if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic {
// backup to CALL instruction to read inlining info (same logic as below) tracepc--
tracepc := frame.pc }
if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic {
tracepc--
}
inldata := funcdata(f, _FUNCDATA_InlTree)
// no inlining info, skip the physical frame
if inldata == nil {
skip--
goto skipped
}
ix := pcdatavalue(f, _PCDATA_InlTreeIndex, tracepc, &cache) // If there is inlining info, record the inner frames.
if inldata := funcdata(f, _FUNCDATA_InlTree); inldata != nil {
inltree := (*[1 << 20]inlinedCall)(inldata) inltree := (*[1 << 20]inlinedCall)(inldata)
// skip the logical (inlined) frames for {
logicalSkipped := 0 ix := pcdatavalue(f, _PCDATA_InlTreeIndex, tracepc, &cache)
for ix >= 0 && skip > 0 { if ix < 0 {
skip-- break
logicalSkipped++ }
ix = inltree[ix].parent if inltree[ix].funcID == funcID_wrapper && elideWrapperCalling(lastFuncID) {
} // ignore wrappers
} else if skip > 0 {
// skip the physical frame if there's more to skip skip--
if skip > 0 { } else if n < max {
skip-- (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = tracepc
goto skipped n++
} }
lastFuncID = inltree[ix].funcID
// now we have a partially skipped frame // Back up to an instruction in the "caller".
(*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = frame.pc tracepc = frame.fn.entry + uintptr(inltree[ix].parentPc)
// if there's room, pcbuf[1] is a skip PC that encodes the number of skipped frames in pcbuf[0]
if n+1 < max {
n++
pc := skipPC + uintptr(logicalSkipped)
(*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = pc
} }
} }
// Record the main frame.
if f.funcID == funcID_wrapper && elideWrapperCalling(lastFuncID) {
// Ignore wrapper functions (except when they trigger panics).
} else if skip > 0 {
skip--
} else if n < max {
(*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = tracepc
n++
}
lastFuncID = f.funcID
n-- // offset n++ below
} }
if printing { if printing {
...@@ -396,7 +393,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in ...@@ -396,7 +393,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// called panic rather than the wrapped // called panic rather than the wrapped
// function. Otherwise, leave them out. // function. Otherwise, leave them out.
name := funcname(f) name := funcname(f)
nextElideWrapper := elideWrapperCalling(name) nextElideWrapper := elideWrapperCalling(f.funcID)
if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0, elideWrapper && nprint != 0) { if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0, elideWrapper && nprint != 0) {
// Print during crash. // Print during crash.
// main(0x1, 0x2, 0x3) // main(0x1, 0x2, 0x3)
...@@ -418,7 +415,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in ...@@ -418,7 +415,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
file = funcfile(f, inltree[ix].file) file = funcfile(f, inltree[ix].file)
line = inltree[ix].line line = inltree[ix].line
ix = inltree[ix].parent ix = int32(inltree[ix].parent)
} }
} }
if name == "runtime.gopanic" { if name == "runtime.gopanic" {
...@@ -451,7 +448,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in ...@@ -451,7 +448,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
} }
n++ n++
skipped:
if f.funcID == funcID_cgocallback_gofunc && len(cgoCtxt) > 0 { if f.funcID == funcID_cgocallback_gofunc && len(cgoCtxt) > 0 {
ctxt := cgoCtxt[len(cgoCtxt)-1] ctxt := cgoCtxt[len(cgoCtxt)-1]
cgoCtxt = cgoCtxt[:len(cgoCtxt)-1] cgoCtxt = cgoCtxt[:len(cgoCtxt)-1]
...@@ -798,7 +794,7 @@ func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) bool { ...@@ -798,7 +794,7 @@ func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) bool {
file = funcfile(f, inltree[ix].file) file = funcfile(f, inltree[ix].file)
line = inltree[ix].line line = inltree[ix].line
ix = inltree[ix].parent ix = int32(inltree[ix].parent)
} }
} }
name := funcname(f) name := funcname(f)
...@@ -811,7 +807,7 @@ func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) bool { ...@@ -811,7 +807,7 @@ func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) bool {
print(" +", hex(pc-f.entry)) print(" +", hex(pc-f.entry))
} }
print("\n") print("\n")
return elideWrapperCalling(name) return elideWrapperCalling(f.funcID)
} }
func callers(skip int, pcbuf []uintptr) int { func callers(skip int, pcbuf []uintptr) int {
...@@ -877,11 +873,11 @@ func isExportedRuntime(name string) bool { ...@@ -877,11 +873,11 @@ func isExportedRuntime(name string) bool {
} }
// elideWrapperCalling reports whether a wrapper function that called // elideWrapperCalling reports whether a wrapper function that called
// function "name" should be elided from stack traces. // function id should be elided from stack traces.
func elideWrapperCalling(name string) bool { func elideWrapperCalling(id funcID) bool {
// If the wrapper called a panic function instead of the // If the wrapper called a panic function instead of the
// wrapped function, we want to include it in stacks. // wrapped function, we want to include it in stacks.
return !(name == "runtime.gopanic" || name == "runtime.sigpanic" || name == "runtime.panicwrap") return !(id == funcID_gopanic || id == funcID_sigpanic || id == funcID_panicwrap)
} }
var gStatusStrings = [...]string{ var gStatusStrings = [...]string{
......
...@@ -29,7 +29,7 @@ func f() { ...@@ -29,7 +29,7 @@ func f() {
} }
func g() { func g() {
_, file, line, _ := runtime.Caller(3) _, file, line, _ := runtime.Caller(2)
if !strings.HasSuffix(file, "issue5856.go") || line != 28 { if !strings.HasSuffix(file, "issue5856.go") || line != 28 {
fmt.Printf("BUG: defer called from %s:%d, want issue5856.go:28\n", file, line) fmt.Printf("BUG: defer called from %s:%d, want issue5856.go:28\n", file, line)
os.Exit(1) os.Exit(1)
......
...@@ -56,11 +56,11 @@ func testCallersFrames(skp int) (frames []string) { ...@@ -56,11 +56,11 @@ func testCallersFrames(skp int) (frames []string) {
} }
var expectedFrames [][]string = [][]string{ var expectedFrames [][]string = [][]string{
0: {"runtime.Callers", "main.testCallers", "main.main"}, 0: {"runtime.Callers", "main.testCallers", "main.testCallers", "main.testCallers", "main.testCallers", "main.main"},
1: {"main.testCallers", "main.main"}, 1: {"main.testCallers", "main.testCallers", "main.testCallers", "main.testCallers", "main.main"},
2: {"main.testCallers", "runtime.skipPleaseUseCallersFrames", "main.main"}, 2: {"main.testCallers", "main.testCallers", "main.testCallers", "main.main"},
3: {"main.testCallers", "runtime.skipPleaseUseCallersFrames", "main.main"}, 3: {"main.testCallers", "main.testCallers", "main.main"},
4: {"main.testCallers", "runtime.skipPleaseUseCallersFrames", "main.main"}, 4: {"main.testCallers", "main.main"},
5: {"main.main"}, 5: {"main.main"},
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment