From 583d335ffeca7c93c627ae8fb7509e1f549e6496 Mon Sep 17 00:00:00 2001 From: Derek Parker Date: Wed, 7 Aug 2019 20:44:37 -0700 Subject: [PATCH] pkg/proc: Untangle Arch from G struct More untangling. Arch doesn't actually need to know anything about a Goroutine. --- pkg/proc/arch.go | 25 ++++++---- pkg/proc/stack.go | 11 ++++- pkg/proc/variables.go | 108 +++++++++++++++++++++--------------------- 3 files changed, 78 insertions(+), 66 deletions(-) diff --git a/pkg/proc/arch.go b/pkg/proc/arch.go index d8265f55..a31f9494 100644 --- a/pkg/proc/arch.go +++ b/pkg/proc/arch.go @@ -18,7 +18,7 @@ type Arch interface { FixFrameUnwindContext(*frame.FrameContext, uint64, *BinaryInfo) *frame.FrameContext RegSize(uint64) int RegistersToDwarfRegisters(uint64, Registers) op.DwarfRegisters - GoroutineToDwarfRegisters(*G) op.DwarfRegisters + AddrAndStackRegsToDwarfRegisters(uint64, uint64, uint64, uint64) op.DwarfRegisters } // AMD64 represents the AMD64 CPU architecture. @@ -293,15 +293,20 @@ func (a *AMD64) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op. } } -// GoroutineToDwarfRegisters extract the saved DWARF registers from a parked -// goroutine in the format used by the DWARF expression interpreter. -func (a *AMD64) GoroutineToDwarfRegisters(g *G) op.DwarfRegisters { +// AddrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in +// PC, SP, and BP registers in the format used by the DWARF expression interpreter. +func (a *AMD64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp uint64) op.DwarfRegisters { dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1) - dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(g.PC) - dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(g.SP) - dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(g.BP) + dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc) + dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) + dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp) - so := g.variable.bi.PCToImage(g.PC) - - return op.DwarfRegisters{StaticBase: so.StaticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum} + return op.DwarfRegisters{ + StaticBase: staticBase, + Regs: dregs, + ByteOrder: binary.LittleEndian, + PCRegNum: amd64DwarfIPRegNum, + SPRegNum: amd64DwarfSPRegNum, + BPRegNum: amd64DwarfBPRegNum, + } } diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index 4e6da257..de7de20c 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -119,9 +119,16 @@ func (g *G) stackIterator() (*stackIterator, error) { return nil, err } so := g.variable.bi.PCToImage(regs.PC()) - return newStackIterator(g.variable.bi, g.Thread, g.variable.bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs), g.stackhi, stkbar, g.stkbarPos, g), nil + return newStackIterator( + g.variable.bi, g.Thread, + g.variable.bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs), + g.stackhi, stkbar, g.stkbarPos, g), nil } - return newStackIterator(g.variable.bi, g.variable.mem, g.variable.bi.Arch.GoroutineToDwarfRegisters(g), g.stackhi, stkbar, g.stkbarPos, g), nil + so := g.variable.bi.PCToImage(g.PC) + return newStackIterator( + g.variable.bi, g.variable.mem, + g.variable.bi.Arch.AddrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP), + g.stackhi, stkbar, g.stkbarPos, g), nil } // Stacktrace returns the stack trace for a goroutine. diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index 9c01038a..57e47873 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -213,6 +213,60 @@ type G struct { Unreadable error // could not read the G struct } +// Defer returns the top-most defer of the goroutine. +func (g *G) Defer() *Defer { + if g.variable.Unreadable != nil { + return nil + } + dvar := g.variable.fieldVariable("_defer").maybeDereference() + if dvar.Addr == 0 { + return nil + } + d := &Defer{variable: dvar} + d.load() + return d +} + +// UserCurrent returns the location the users code is at, +// or was at before entering a runtime function. +func (g *G) UserCurrent() Location { + it, err := g.stackIterator() + if err != nil { + return g.CurrentLoc + } + for it.Next() { + frame := it.Frame() + if frame.Call.Fn != nil { + name := frame.Call.Fn.Name + if strings.Contains(name, ".") && (!strings.HasPrefix(name, "runtime.") || isExportedRuntime(name)) { + return frame.Call + } + } + } + return g.CurrentLoc +} + +// Go returns the location of the 'go' statement +// that spawned this goroutine. +func (g *G) Go() Location { + pc := g.GoPC + if fn := g.variable.bi.PCToFunc(pc); fn != nil { + // Backup to CALL instruction. + // Mimics runtime/traceback.go:677. + if g.GoPC > fn.Entry { + pc-- + } + } + f, l, fn := g.variable.bi.PCToLine(pc) + return Location{PC: g.GoPC, File: f, Line: l, Fn: fn} +} + +// StartLoc returns the starting location of the goroutine. +func (g *G) StartLoc() Location { + f, l, fn := g.variable.bi.PCToLine(g.StartPC) + return Location{PC: g.StartPC, File: f, Line: l, Fn: fn} +} + type Ancestor struct { ID int64 // Goroutine ID Unreadable error @@ -601,20 +655,6 @@ func (v *Variable) fieldVariable(name string) *Variable { return nil } -// Defer returns the top-most defer of the goroutine. -func (g *G) Defer() *Defer { - if g.variable.Unreadable != nil { - return nil - } - dvar := g.variable.fieldVariable("_defer").maybeDereference() - if dvar.Addr == 0 { - return nil - } - d := &Defer{variable: dvar} - d.load() - return d -} - // From $GOROOT/src/runtime/traceback.go:597 // isExportedRuntime reports whether name is an exported runtime function. // It is only for runtime functions, so ASCII A-Z is fine. @@ -623,46 +663,6 @@ func isExportedRuntime(name string) bool { return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z' } -// UserCurrent returns the location the users code is at, -// or was at before entering a runtime function. -func (g *G) UserCurrent() Location { - it, err := g.stackIterator() - if err != nil { - return g.CurrentLoc - } - for it.Next() { - frame := it.Frame() - if frame.Call.Fn != nil { - name := frame.Call.Fn.Name - if strings.Contains(name, ".") && (!strings.HasPrefix(name, "runtime.") || isExportedRuntime(name)) { - return frame.Call - } - } - } - return g.CurrentLoc -} - -// Go returns the location of the 'go' statement -// that spawned this goroutine. -func (g *G) Go() Location { - pc := g.GoPC - if fn := g.variable.bi.PCToFunc(pc); fn != nil { - // Backup to CALL instruction. - // Mimics runtime/traceback.go:677. - if g.GoPC > fn.Entry { - pc-- - } - } - f, l, fn := g.variable.bi.PCToLine(pc) - return Location{PC: g.GoPC, File: f, Line: l, Fn: fn} -} - -// StartLoc returns the starting location of the goroutine. -func (g *G) StartLoc() Location { - f, l, fn := g.variable.bi.PCToLine(g.StartPC) - return Location{PC: g.StartPC, File: f, Line: l, Fn: fn} -} - var errTracebackAncestorsDisabled = errors.New("tracebackancestors is disabled") // Ancestors returns the list of ancestors for g.