diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index d2cafaf1..194f552b 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -2742,104 +2742,6 @@ func getg(goid int, gs []*proc.G) *proc.G { 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) { if testBackend == "lldb" && runtime.GOOS == "linux" { bs, _ := ioutil.ReadFile("/proc/sys/kernel/yama/ptrace_scope") diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index 1befb68f..68a7338a 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -102,18 +102,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, -1, nil, 0) + it := newStackIterator(thread.BinInfo(), thread.ProcessMemory(), dwarfRegs, 0, nil, 0) return it.stacktrace(depth) } return g.Stacktrace(depth, 0) } func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) { - stkbar, err := g.stkbar() - if err != nil { - return nil, err - } - bi := g.variable.bi if g.Thread != nil { regs, err := g.Thread.Registers() @@ -126,13 +121,13 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) { return newStackIterator( bi, g.variable.mem, dwarfRegs, - g.stack.hi, stkbar, g.stkbarPos, g, opts), nil + g.stack.hi, g, opts), nil } so := g.variable.bi.PCToImage(g.PC) return newStackIterator( bi, g.variable.mem, 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 @@ -187,10 +182,8 @@ type stackIterator struct { mem MemoryReadWriter err error - stackhi uint64 - systemstack bool - stackBarrierPC uint64 - stkbar []savedLR + stackhi uint64 + systemstack bool // regs is the register set for the current frame regs op.DwarfRegisters @@ -202,36 +195,12 @@ type stackIterator struct { opts StacktraceOptions } -type savedLR struct { - 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:] - } +func newStackIterator(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, 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. @@ -243,12 +212,6 @@ func (it *stackIterator) Next() bool { callFrameRegs, ret, retaddr := it.advanceRegs() 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.bi.Arch.switchStack(it, &callFrameRegs) { return true diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index 76bd4471..2190f7e2 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -190,17 +190,15 @@ const ( // G represents a runtime G (goroutine) structure (at least the // fields that Delve is interested in). type G struct { - ID int // Goroutine ID - PC uint64 // PC 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). - LR uint64 // LR of goroutine when it was parked. - GoPC uint64 // PC of 'go' statement that created this goroutine. - StartPC uint64 // PC of the first function run on this goroutine. - Status uint64 - stkbarVar *Variable // stkbar field of g struct - stkbarPos int // stkbarPos field of g struct - stack stack // value of stack + ID int // Goroutine ID + PC uint64 // PC 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). + LR uint64 // LR of goroutine when it was parked. + GoPC uint64 // PC of 'go' statement that created this goroutine. + StartPC uint64 // PC of the first function run on this goroutine. + Status uint64 + stack stack // value of stack WaitSince 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") if unreadable { @@ -905,8 +896,6 @@ func (v *Variable) parseG() (*G, error) { WaitReason: waitReason, CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn}, variable: v, - stkbarVar: stkbarVar, - stkbarPos: int(stkbarPos), stack: stack{hi: stackhi, lo: stacklo}, } return g, nil @@ -1026,31 +1015,6 @@ func (a *Ancestor) Stack(n int) ([]Stackframe, error) { 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) { if v.Unreadable != nil { return v.clone(), nil