mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 01:27:16 +08:00
proc: read context from sigtrampgo, fixes TestCgoStacktrace2 on 1.21 (#3401)
* logflags,proc: flag to log stacktrace execution Add a log flag to write logs about what the stacktracer does. * proc: read context from sigtrampgo, fixes TestCgoStacktrace2 on 1.21 Changes stacktrace code to read the signal context from the arguments of sigtrampgo. Also changes the automatic fatalthrow breakpoint for go 1.21. In combination these two changes fix TestCgoStacktrace2 on Go 1.21 on various platforms.
This commit is contained in:
committed by
GitHub
parent
1647fa6b5e
commit
d963eb1057
@ -6,10 +6,12 @@ import (
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-delve/delve/pkg/dwarf/frame"
|
||||
"github.com/go-delve/delve/pkg/dwarf/op"
|
||||
"github.com/go-delve/delve/pkg/dwarf/reader"
|
||||
"github.com/go-delve/delve/pkg/logflags"
|
||||
)
|
||||
|
||||
// This code is partly adapted from runtime.gentraceback in
|
||||
@ -91,7 +93,7 @@ func (frame *Stackframe) FramePointerOffset() int64 {
|
||||
|
||||
// ThreadStacktrace returns the stack trace for thread.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
|
||||
func ThreadStacktrace(tgt *Target, thread Thread, depth int) ([]Stackframe, error) {
|
||||
g, _ := GetG(thread)
|
||||
if g == nil {
|
||||
regs, err := thread.Registers()
|
||||
@ -101,13 +103,13 @@ func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
|
||||
so := thread.BinInfo().PCToImage(regs.PC())
|
||||
dwarfRegs := *(thread.BinInfo().Arch.RegistersToDwarfRegisters(so.StaticBase, regs))
|
||||
dwarfRegs.ChangeFunc = thread.SetReg
|
||||
it := newStackIterator(thread.BinInfo(), thread.ProcessMemory(), dwarfRegs, 0, nil, 0)
|
||||
it := newStackIterator(tgt, thread.BinInfo(), thread.ProcessMemory(), dwarfRegs, 0, nil, 0)
|
||||
return it.stacktrace(depth)
|
||||
}
|
||||
return g.Stacktrace(depth, 0)
|
||||
return GoroutineStacktrace(tgt, g, depth, 0)
|
||||
}
|
||||
|
||||
func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
|
||||
func goroutineStackIterator(tgt *Target, g *G, opts StacktraceOptions) (*stackIterator, error) {
|
||||
bi := g.variable.bi
|
||||
if g.Thread != nil {
|
||||
regs, err := g.Thread.Registers()
|
||||
@ -118,13 +120,13 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
|
||||
dwarfRegs := *(bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs))
|
||||
dwarfRegs.ChangeFunc = g.Thread.SetReg
|
||||
return newStackIterator(
|
||||
bi, g.variable.mem,
|
||||
tgt, bi, g.variable.mem,
|
||||
dwarfRegs,
|
||||
g.stack.hi, g, opts), nil
|
||||
}
|
||||
so := g.variable.bi.PCToImage(g.PC)
|
||||
return newStackIterator(
|
||||
bi, g.variable.mem,
|
||||
tgt, bi, g.variable.mem,
|
||||
bi.Arch.addrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP, g.LR),
|
||||
g.stack.hi, g, opts), nil
|
||||
}
|
||||
@ -145,10 +147,10 @@ const (
|
||||
StacktraceG
|
||||
)
|
||||
|
||||
// Stacktrace returns the stack trace for a goroutine.
|
||||
// GoroutineStacktrace returns the stack trace for a goroutine.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func (g *G) Stacktrace(depth int, opts StacktraceOptions) ([]Stackframe, error) {
|
||||
it, err := g.stackIterator(opts)
|
||||
func GoroutineStacktrace(tgt *Target, g *G, depth int, opts StacktraceOptions) ([]Stackframe, error) {
|
||||
it, err := goroutineStackIterator(tgt, g, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -173,13 +175,14 @@ func (n NullAddrError) Error() string {
|
||||
// required to iterate and walk the program
|
||||
// stack.
|
||||
type stackIterator struct {
|
||||
pc uint64
|
||||
top bool
|
||||
atend bool
|
||||
frame Stackframe
|
||||
bi *BinaryInfo
|
||||
mem MemoryReadWriter
|
||||
err error
|
||||
pc uint64
|
||||
top bool
|
||||
atend bool
|
||||
frame Stackframe
|
||||
target *Target
|
||||
bi *BinaryInfo
|
||||
mem MemoryReadWriter
|
||||
err error
|
||||
|
||||
stackhi uint64
|
||||
systemstack bool
|
||||
@ -194,12 +197,12 @@ type stackIterator struct {
|
||||
opts StacktraceOptions
|
||||
}
|
||||
|
||||
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegisters, stackhi uint64, g *G, opts StacktraceOptions) *stackIterator {
|
||||
func newStackIterator(tgt *Target, bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegisters, stackhi uint64, g *G, opts StacktraceOptions) *stackIterator {
|
||||
systemstack := true
|
||||
if g != nil {
|
||||
systemstack = g.SystemStack
|
||||
}
|
||||
return &stackIterator{pc: regs.PC(), regs: regs, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, systemstack: systemstack, g: g, opts: opts}
|
||||
return &stackIterator{pc: regs.PC(), regs: regs, top: true, target: tgt, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, systemstack: systemstack, g: g, opts: opts}
|
||||
}
|
||||
|
||||
// Next points the iterator to the next stack frame.
|
||||
@ -208,11 +211,54 @@ func (it *stackIterator) Next() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if logflags.Stack() {
|
||||
logger := logflags.StackLogger()
|
||||
w := &strings.Builder{}
|
||||
fmt.Fprintf(w, "current pc = %#x CFA = %#x FrameBase = %#x ", it.pc, it.regs.CFA, it.regs.FrameBase)
|
||||
for i := 0; i < it.regs.CurrentSize(); i++ {
|
||||
reg := it.regs.Reg(uint64(i))
|
||||
if reg == nil {
|
||||
continue
|
||||
}
|
||||
name, _, _ := it.bi.Arch.DwarfRegisterToString(i, reg)
|
||||
fmt.Fprintf(w, " %s = %#x", name, reg.Uint64Val)
|
||||
}
|
||||
logger.Debugf("%s", w.String())
|
||||
}
|
||||
|
||||
callFrameRegs, ret, retaddr := it.advanceRegs()
|
||||
it.frame = it.newStackframe(ret, retaddr)
|
||||
|
||||
if logflags.Stack() {
|
||||
logger := logflags.StackLogger()
|
||||
fnname := "?"
|
||||
if it.frame.Call.Fn != nil {
|
||||
fnname = it.frame.Call.Fn.Name
|
||||
}
|
||||
logger.Debugf("new frame %#x %s:%d at %s", it.frame.Call.PC, it.frame.Call.File, it.frame.Call.Line, fnname)
|
||||
}
|
||||
|
||||
if it.frame.Current.Fn != nil && it.frame.Current.Fn.Name == "runtime.sigtrampgo" && it.target != nil {
|
||||
regs, err := it.readSigtrampgoContext()
|
||||
if err != nil {
|
||||
logflags.DebuggerLogger().Errorf("could not read runtime.sigtrampgo context: %v", err)
|
||||
} else {
|
||||
so := it.bi.PCToImage(regs.PC())
|
||||
regs.StaticBase = so.StaticBase
|
||||
it.pc = regs.PC()
|
||||
it.regs = *regs
|
||||
it.top = false
|
||||
if it.g != nil && it.g.ID != 0 {
|
||||
it.systemstack = !(uint64(it.regs.SP()) >= it.g.stack.lo && uint64(it.regs.SP()) < it.g.stack.hi)
|
||||
}
|
||||
logflags.StackLogger().Debugf("sigtramp context read")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if it.opts&StacktraceSimple == 0 {
|
||||
if it.bi.Arch.switchStack(it, &callFrameRegs) {
|
||||
logflags.StackLogger().Debugf("stack switched")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -398,11 +444,18 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin
|
||||
framectx = it.bi.Arch.fixFrameUnwindContext(fde.EstablishFrame(it.pc), it.pc, it.bi)
|
||||
}
|
||||
|
||||
logger := logflags.StackLogger()
|
||||
|
||||
logger.Debugf("advanceRegs at %#x", it.pc)
|
||||
|
||||
cfareg, err := it.executeFrameRegRule(0, framectx.CFA, 0)
|
||||
if cfareg == nil {
|
||||
it.err = fmt.Errorf("CFA becomes undefined at PC %#x: %v", it.pc, err)
|
||||
return op.DwarfRegisters{}, 0, 0
|
||||
}
|
||||
if logflags.Stack() {
|
||||
logger.Debugf("\tCFA rule %s -> %#x", ruleString(&framectx.CFA, it.bi.Arch.RegnumToString), cfareg.Uint64Val)
|
||||
}
|
||||
it.regs.CFA = int64(cfareg.Uint64Val)
|
||||
|
||||
callimage := it.bi.PCToImage(it.pc)
|
||||
@ -426,7 +479,16 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin
|
||||
callFrameRegs.AddReg(callFrameRegs.SPRegNum, cfareg)
|
||||
|
||||
for i, regRule := range framectx.Regs {
|
||||
if logflags.Stack() {
|
||||
logger.Debugf("\t%s rule %s ", it.bi.Arch.RegnumToString(i), ruleString(®Rule, it.bi.Arch.RegnumToString))
|
||||
|
||||
}
|
||||
reg, err := it.executeFrameRegRule(i, regRule, it.regs.CFA)
|
||||
if reg != nil {
|
||||
logger.Debugf("\t\t-> %#x", reg.Uint64Val)
|
||||
} else {
|
||||
logger.Debugf("\t\t-> nothing (%v)", err)
|
||||
}
|
||||
callFrameRegs.AddReg(i, reg)
|
||||
if i == framectx.RetAddrReg {
|
||||
if reg == nil {
|
||||
@ -701,3 +763,34 @@ func (d *Defer) DeferredFunc(p *Target) (file string, line int, fn *Function) {
|
||||
file, line = bi.EntryLineForFunc(fn)
|
||||
return file, line, fn
|
||||
}
|
||||
|
||||
func ruleString(rule *frame.DWRule, regnumToString func(uint64) string) string {
|
||||
switch rule.Rule {
|
||||
case frame.RuleUndefined:
|
||||
return "undefined"
|
||||
case frame.RuleSameVal:
|
||||
return "sameval"
|
||||
case frame.RuleOffset:
|
||||
return fmt.Sprintf("[cfa+%d]", rule.Offset)
|
||||
case frame.RuleValOffset:
|
||||
return fmt.Sprintf("cfa+%d", rule.Offset)
|
||||
case frame.RuleRegister:
|
||||
return fmt.Sprintf("R(%d)", rule.Reg)
|
||||
case frame.RuleExpression:
|
||||
w := &strings.Builder{}
|
||||
op.PrettyPrint(w, rule.Expression, regnumToString)
|
||||
return fmt.Sprintf("[expr(%s)]", w.String())
|
||||
case frame.RuleValExpression:
|
||||
w := &strings.Builder{}
|
||||
op.PrettyPrint(w, rule.Expression, regnumToString)
|
||||
return fmt.Sprintf("expr(%s)", w.String())
|
||||
case frame.RuleArchitectural:
|
||||
return "architectural"
|
||||
case frame.RuleCFA:
|
||||
return fmt.Sprintf("R(%d)+%d", rule.Reg, rule.Offset)
|
||||
case frame.RuleFramePointer:
|
||||
return fmt.Sprintf("[R(%d)] framepointer", rule.Reg)
|
||||
default:
|
||||
return fmt.Sprintf("unknown_rule(%d)", rule.Rule)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user