proc: remove stack barrier support (#2540)
* proc: remove stack barrier support Stack barriers were removed way back in Go 1.9 so it's safe to eliminate and clean up this code now.
This commit is contained in:
parent
69485a639d
commit
d3f4a8d443
@ -2742,104 +2742,6 @@ func getg(goid int, gs []*proc.G) *proc.G {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStacktraceWithBarriers(t *testing.T) {
|
|
||||||
// Go's Garbage Collector will insert stack barriers into stacks.
|
|
||||||
// This stack barrier is inserted by overwriting the return address for the
|
|
||||||
// stack frame with the address of runtime.stackBarrier.
|
|
||||||
// The original return address is saved into the stkbar slice inside the G
|
|
||||||
// struct.
|
|
||||||
|
|
||||||
// In Go 1.9 stack barriers have been removed and this test must be disabled.
|
|
||||||
if ver, _ := goversion.Parse(runtime.Version()); ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1}) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// In Go 1.8 stack barriers are not inserted by default, this enables them.
|
|
||||||
godebugOld := os.Getenv("GODEBUG")
|
|
||||||
defer os.Setenv("GODEBUG", godebugOld)
|
|
||||||
os.Setenv("GODEBUG", "gcrescanstacks=1")
|
|
||||||
|
|
||||||
withTestProcess("binarytrees", t, func(p *proc.Target, fixture protest.Fixture) {
|
|
||||||
// We want to get a user goroutine with a stack barrier, to get that we execute the program until runtime.gcInstallStackBarrier is executed AND the goroutine it was executed onto contains a call to main.bottomUpTree
|
|
||||||
setFunctionBreakpoint(p, t, "runtime.gcInstallStackBarrier")
|
|
||||||
stackBarrierGoids := []int{}
|
|
||||||
for len(stackBarrierGoids) == 0 {
|
|
||||||
err := p.Continue()
|
|
||||||
if _, exited := err.(proc.ErrProcessExited); exited {
|
|
||||||
t.Logf("Could not run test")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
assertNoError(err, t, "Continue()")
|
|
||||||
gs, _, err := proc.GoroutinesInfo(p, 0, 0)
|
|
||||||
assertNoError(err, t, "GoroutinesInfo()")
|
|
||||||
for _, th := range p.ThreadList() {
|
|
||||||
if bp := th.Breakpoint(); bp.Breakpoint == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
goidVar := evalVariable(p, t, "gp.goid")
|
|
||||||
goid, _ := constant.Int64Val(goidVar.Value)
|
|
||||||
|
|
||||||
if g := getg(int(goid), gs); g != nil {
|
|
||||||
stack, err := g.Stacktrace(50, 0)
|
|
||||||
assertNoError(err, t, fmt.Sprintf("Stacktrace(goroutine = %d)", goid))
|
|
||||||
for _, frame := range stack {
|
|
||||||
if frame.Current.Fn != nil && frame.Current.Fn.Name == "main.bottomUpTree" {
|
|
||||||
stackBarrierGoids = append(stackBarrierGoids, int(goid))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(stackBarrierGoids) == 0 {
|
|
||||||
t.Fatalf("Could not find a goroutine with stack barriers")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("stack barrier goids: %v\n", stackBarrierGoids)
|
|
||||||
|
|
||||||
assertNoError(p.StepOut(), t, "StepOut()")
|
|
||||||
|
|
||||||
gs, _, err := proc.GoroutinesInfo(p, 0, 0)
|
|
||||||
assertNoError(err, t, "GoroutinesInfo()")
|
|
||||||
|
|
||||||
for _, goid := range stackBarrierGoids {
|
|
||||||
g := getg(goid, gs)
|
|
||||||
|
|
||||||
stack, err := g.Stacktrace(200, 0)
|
|
||||||
assertNoError(err, t, "Stacktrace()")
|
|
||||||
|
|
||||||
// Check that either main.main or main.main.func1 appear in the
|
|
||||||
// stacktrace of this goroutine, if we failed at resolving stack barriers
|
|
||||||
// correctly the stacktrace will be truncated and neither main.main or
|
|
||||||
// main.main.func1 will appear
|
|
||||||
found := false
|
|
||||||
for _, frame := range stack {
|
|
||||||
if frame.Current.Fn == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if name := frame.Current.Fn.Name; name == "main.main" || name == "main.main.func1" {
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Stacktrace for %d:\n", goid)
|
|
||||||
for _, frame := range stack {
|
|
||||||
name := "<>"
|
|
||||||
if frame.Current.Fn != nil {
|
|
||||||
name = frame.Current.Fn.Name
|
|
||||||
}
|
|
||||||
t.Logf("\t%s [CFA: %x Ret: %x] at %s:%d", name, frame.Regs.CFA, frame.Ret, frame.Current.File, frame.Current.Line)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
t.Logf("Truncated stacktrace for %d\n", goid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAttachDetach(t *testing.T) {
|
func TestAttachDetach(t *testing.T) {
|
||||||
if testBackend == "lldb" && runtime.GOOS == "linux" {
|
if testBackend == "lldb" && runtime.GOOS == "linux" {
|
||||||
bs, _ := ioutil.ReadFile("/proc/sys/kernel/yama/ptrace_scope")
|
bs, _ := ioutil.ReadFile("/proc/sys/kernel/yama/ptrace_scope")
|
||||||
|
@ -102,18 +102,13 @@ func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
|
|||||||
so := thread.BinInfo().PCToImage(regs.PC())
|
so := thread.BinInfo().PCToImage(regs.PC())
|
||||||
dwarfRegs := *(thread.BinInfo().Arch.RegistersToDwarfRegisters(so.StaticBase, regs))
|
dwarfRegs := *(thread.BinInfo().Arch.RegistersToDwarfRegisters(so.StaticBase, regs))
|
||||||
dwarfRegs.ChangeFunc = thread.SetReg
|
dwarfRegs.ChangeFunc = thread.SetReg
|
||||||
it := newStackIterator(thread.BinInfo(), thread.ProcessMemory(), dwarfRegs, 0, nil, -1, nil, 0)
|
it := newStackIterator(thread.BinInfo(), thread.ProcessMemory(), dwarfRegs, 0, nil, 0)
|
||||||
return it.stacktrace(depth)
|
return it.stacktrace(depth)
|
||||||
}
|
}
|
||||||
return g.Stacktrace(depth, 0)
|
return g.Stacktrace(depth, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
|
func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
|
||||||
stkbar, err := g.stkbar()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
bi := g.variable.bi
|
bi := g.variable.bi
|
||||||
if g.Thread != nil {
|
if g.Thread != nil {
|
||||||
regs, err := g.Thread.Registers()
|
regs, err := g.Thread.Registers()
|
||||||
@ -126,13 +121,13 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
|
|||||||
return newStackIterator(
|
return newStackIterator(
|
||||||
bi, g.variable.mem,
|
bi, g.variable.mem,
|
||||||
dwarfRegs,
|
dwarfRegs,
|
||||||
g.stack.hi, stkbar, g.stkbarPos, g, opts), nil
|
g.stack.hi, g, opts), nil
|
||||||
}
|
}
|
||||||
so := g.variable.bi.PCToImage(g.PC)
|
so := g.variable.bi.PCToImage(g.PC)
|
||||||
return newStackIterator(
|
return newStackIterator(
|
||||||
bi, g.variable.mem,
|
bi, g.variable.mem,
|
||||||
bi.Arch.addrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP, g.LR),
|
bi.Arch.addrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP, g.LR),
|
||||||
g.stack.hi, stkbar, g.stkbarPos, g, opts), nil
|
g.stack.hi, g, opts), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type StacktraceOptions uint16
|
type StacktraceOptions uint16
|
||||||
@ -187,10 +182,8 @@ type stackIterator struct {
|
|||||||
mem MemoryReadWriter
|
mem MemoryReadWriter
|
||||||
err error
|
err error
|
||||||
|
|
||||||
stackhi uint64
|
stackhi uint64
|
||||||
systemstack bool
|
systemstack bool
|
||||||
stackBarrierPC uint64
|
|
||||||
stkbar []savedLR
|
|
||||||
|
|
||||||
// regs is the register set for the current frame
|
// regs is the register set for the current frame
|
||||||
regs op.DwarfRegisters
|
regs op.DwarfRegisters
|
||||||
@ -202,36 +195,12 @@ type stackIterator struct {
|
|||||||
opts StacktraceOptions
|
opts StacktraceOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
type savedLR struct {
|
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegisters, stackhi uint64, g *G, opts StacktraceOptions) *stackIterator {
|
||||||
ptr uint64
|
|
||||||
val uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegisters, stackhi uint64, stkbar []savedLR, stkbarPos int, g *G, opts StacktraceOptions) *stackIterator {
|
|
||||||
stackBarrierFunc := bi.LookupFunc["runtime.stackBarrier"] // stack barriers were removed in Go 1.9
|
|
||||||
var stackBarrierPC uint64
|
|
||||||
if stackBarrierFunc != nil && stkbar != nil {
|
|
||||||
stackBarrierPC = stackBarrierFunc.Entry
|
|
||||||
fn := bi.PCToFunc(regs.PC())
|
|
||||||
if fn != nil && fn.Name == "runtime.stackBarrier" {
|
|
||||||
// We caught the goroutine as it's executing the stack barrier, we must
|
|
||||||
// determine whether or not g.stackPos has already been incremented or not.
|
|
||||||
if len(stkbar) > 0 && stkbar[stkbarPos].ptr < regs.SP() {
|
|
||||||
// runtime.stackBarrier has not incremented stkbarPos.
|
|
||||||
} else if stkbarPos > 0 && stkbar[stkbarPos-1].ptr < regs.SP() {
|
|
||||||
// runtime.stackBarrier has incremented stkbarPos.
|
|
||||||
stkbarPos--
|
|
||||||
} else {
|
|
||||||
return &stackIterator{err: fmt.Errorf("failed to unwind through stackBarrier at SP %x", regs.SP())}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stkbar = stkbar[stkbarPos:]
|
|
||||||
}
|
|
||||||
systemstack := true
|
systemstack := true
|
||||||
if g != nil {
|
if g != nil {
|
||||||
systemstack = g.SystemStack
|
systemstack = g.SystemStack
|
||||||
}
|
}
|
||||||
return &stackIterator{pc: regs.PC(), regs: regs, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar, systemstack: systemstack, g: g, opts: opts}
|
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}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next points the iterator to the next stack frame.
|
// Next points the iterator to the next stack frame.
|
||||||
@ -243,12 +212,6 @@ func (it *stackIterator) Next() bool {
|
|||||||
callFrameRegs, ret, retaddr := it.advanceRegs()
|
callFrameRegs, ret, retaddr := it.advanceRegs()
|
||||||
it.frame = it.newStackframe(ret, retaddr)
|
it.frame = it.newStackframe(ret, retaddr)
|
||||||
|
|
||||||
if it.stkbar != nil && it.frame.Ret == it.stackBarrierPC && it.frame.addrret == it.stkbar[0].ptr {
|
|
||||||
// Skip stack barrier frames
|
|
||||||
it.frame.Ret = it.stkbar[0].val
|
|
||||||
it.stkbar = it.stkbar[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if it.opts&StacktraceSimple == 0 {
|
if it.opts&StacktraceSimple == 0 {
|
||||||
if it.bi.Arch.switchStack(it, &callFrameRegs) {
|
if it.bi.Arch.switchStack(it, &callFrameRegs) {
|
||||||
return true
|
return true
|
||||||
|
@ -190,17 +190,15 @@ const (
|
|||||||
// G represents a runtime G (goroutine) structure (at least the
|
// G represents a runtime G (goroutine) structure (at least the
|
||||||
// fields that Delve is interested in).
|
// fields that Delve is interested in).
|
||||||
type G struct {
|
type G struct {
|
||||||
ID int // Goroutine ID
|
ID int // Goroutine ID
|
||||||
PC uint64 // PC of goroutine when it was parked.
|
PC uint64 // PC of goroutine when it was parked.
|
||||||
SP uint64 // SP of goroutine when it was parked.
|
SP uint64 // SP of goroutine when it was parked.
|
||||||
BP uint64 // BP of goroutine when it was parked (go >= 1.7).
|
BP uint64 // BP of goroutine when it was parked (go >= 1.7).
|
||||||
LR uint64 // LR of goroutine when it was parked.
|
LR uint64 // LR of goroutine when it was parked.
|
||||||
GoPC uint64 // PC of 'go' statement that created this goroutine.
|
GoPC uint64 // PC of 'go' statement that created this goroutine.
|
||||||
StartPC uint64 // PC of the first function run on this goroutine.
|
StartPC uint64 // PC of the first function run on this goroutine.
|
||||||
Status uint64
|
Status uint64
|
||||||
stkbarVar *Variable // stkbar field of g struct
|
stack stack // value of stack
|
||||||
stkbarPos int // stkbarPos field of g struct
|
|
||||||
stack stack // value of stack
|
|
||||||
|
|
||||||
WaitSince int64
|
WaitSince int64
|
||||||
WaitReason int64
|
WaitReason int64
|
||||||
@ -875,13 +873,6 @@ func (v *Variable) parseG() (*G, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stkbarVar := v.loadFieldNamed("stkbar")
|
|
||||||
stkbarVarPosFld := v.loadFieldNamed("stkbarPos")
|
|
||||||
var stkbarPos int64
|
|
||||||
if stkbarVarPosFld != nil { // stack barriers were removed in Go 1.9
|
|
||||||
stkbarPos, _ = constant.Int64Val(stkbarVarPosFld.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
status := loadInt64Maybe("atomicstatus")
|
status := loadInt64Maybe("atomicstatus")
|
||||||
|
|
||||||
if unreadable {
|
if unreadable {
|
||||||
@ -905,8 +896,6 @@ func (v *Variable) parseG() (*G, error) {
|
|||||||
WaitReason: waitReason,
|
WaitReason: waitReason,
|
||||||
CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn},
|
CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn},
|
||||||
variable: v,
|
variable: v,
|
||||||
stkbarVar: stkbarVar,
|
|
||||||
stkbarPos: int(stkbarPos),
|
|
||||||
stack: stack{hi: stackhi, lo: stacklo},
|
stack: stack{hi: stackhi, lo: stacklo},
|
||||||
}
|
}
|
||||||
return g, nil
|
return g, nil
|
||||||
@ -1026,31 +1015,6 @@ func (a *Ancestor) Stack(n int) ([]Stackframe, error) {
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the list of saved return addresses used by stack barriers
|
|
||||||
func (g *G) stkbar() ([]savedLR, error) {
|
|
||||||
if g.stkbarVar == nil { // stack barriers were removed in Go 1.9
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
g.stkbarVar.loadValue(LoadConfig{false, 1, 0, int(g.stkbarVar.Len), 3, 0})
|
|
||||||
if g.stkbarVar.Unreadable != nil {
|
|
||||||
return nil, fmt.Errorf("unreadable stkbar: %v", g.stkbarVar.Unreadable)
|
|
||||||
}
|
|
||||||
r := make([]savedLR, len(g.stkbarVar.Children))
|
|
||||||
for i, child := range g.stkbarVar.Children {
|
|
||||||
for _, field := range child.Children {
|
|
||||||
switch field.Name {
|
|
||||||
case "savedLRPtr":
|
|
||||||
ptr, _ := constant.Int64Val(field.Value)
|
|
||||||
r[i].ptr = uint64(ptr)
|
|
||||||
case "savedLRVal":
|
|
||||||
val, _ := constant.Int64Val(field.Value)
|
|
||||||
r[i].val = uint64(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Variable) structMember(memberName string) (*Variable, error) {
|
func (v *Variable) structMember(memberName string) (*Variable, error) {
|
||||||
if v.Unreadable != nil {
|
if v.Unreadable != nil {
|
||||||
return v.clone(), nil
|
return v.clone(), nil
|
||||||
|
Loading…
Reference in New Issue
Block a user