proc refactoring: make stack, disassemble and eval independent of proc.Process (#786)

* proc: Refactor stackIterator to use memoryReadWriter and BinaryInfo

* proc: refactor EvalScope to use memoryReadWriter and BinaryInfo

* proc: refactor Disassemble to use memoryReadWriter and BinaryInfo
This commit is contained in:
Alessandro Arzilli 2017-04-14 01:19:57 +02:00 committed by Derek Parker
parent f605716160
commit b5a06f7aa8
14 changed files with 230 additions and 170 deletions

@ -130,7 +130,7 @@ func (bp *Breakpoint) checkCondition(thread *Thread) (bool, error) {
}
}
}
scope, err := thread.Scope()
scope, err := thread.GoroutineScope()
if err != nil {
return true, err
}

@ -16,14 +16,27 @@ const (
IntelFlavour
)
func (dbp *Process) Disassemble(g *G, startPC, endPC uint64) ([]AsmInstruction, error) {
if g == nil {
regs, _ := dbp.currentThread.Registers(false)
return Disassemble(dbp.currentThread, regs, dbp.breakpoints, &dbp.bi, startPC, endPC)
}
var regs Registers
thread := dbp.currentThread
if g.thread != nil {
thread = g.thread
regs, _ = g.thread.Registers(false)
}
return Disassemble(thread, regs, dbp.breakpoints, &dbp.bi, startPC, endPC)
}
// Disassemble disassembles target memory between startPC and endPC
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool) ([]AsmInstruction, error) {
if thread.dbp.exited {
return nil, &ProcessExitedError{}
}
mem, err := thread.readMemory(uintptr(startPC), int(endPC-startPC))
func Disassemble(memrw memoryReadWriter, regs Registers, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, startPC, endPC uint64) ([]AsmInstruction, error) {
mem, err := memrw.readMemory(uintptr(startPC), int(endPC-startPC))
if err != nil {
return nil, err
}
@ -32,27 +45,23 @@ func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool)
pc := startPC
var curpc uint64
var regs Registers
if currentGoroutine {
regs, _ = thread.Registers(false)
if regs != nil {
curpc = regs.PC()
}
if regs != nil {
curpc = regs.PC()
}
for len(mem) > 0 {
bp, atbp := thread.dbp.breakpoints[pc]
bp, atbp := breakpoints[pc]
if atbp {
for i := range bp.OriginalData {
mem[i] = bp.OriginalData[i]
}
}
file, line, fn := thread.dbp.bi.PCToLine(pc)
file, line, fn := bi.PCToLine(pc)
loc := Location{PC: pc, File: file, Line: line, Fn: fn}
inst, err := asmDecode(mem, pc)
if err == nil {
atpc := currentGoroutine && (curpc == pc)
destloc := thread.resolveCallArg(inst, atpc, regs)
atpc := (regs != nil) && (curpc == pc)
destloc := resolveCallArg(inst, atpc, regs, memrw, bi)
r = append(r, AsmInstruction{Loc: loc, DestLoc: destloc, Bytes: mem[:inst.Len], Breakpoint: atbp, AtPC: atpc, Inst: inst})
pc += uint64(inst.Size())

@ -63,7 +63,7 @@ func (inst *AsmInstruction) IsCall() bool {
return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
}
func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers) *Location {
func resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers, mem memoryReadWriter, bininfo *BinaryInfo) *Location {
if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
return nil
}
@ -89,10 +89,6 @@ func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs
if arg.Segment != 0 {
return nil
}
regs, err := thread.Registers(false)
if err != nil {
return nil
}
base, err1 := regs.Get(int(arg.Base))
index, err2 := regs.Get(int(arg.Index))
if err1 != nil || err2 != nil {
@ -100,7 +96,7 @@ func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs
}
addr := uintptr(int64(base) + int64(index*uint64(arg.Scale)) + arg.Disp)
//TODO: should this always be 64 bits instead of inst.MemBytes?
pcbytes, err := thread.readMemory(addr, inst.MemBytes)
pcbytes, err := mem.readMemory(addr, inst.MemBytes)
if err != nil {
return nil
}
@ -109,7 +105,7 @@ func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs
return nil
}
file, line, fn := thread.dbp.bi.PCToLine(pc)
file, line, fn := bininfo.PCToLine(pc)
if fn == nil {
return nil
}
@ -143,10 +139,14 @@ func init() {
}
}
func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
return FirstPCAfterPrologue(dbp.currentThread, dbp.breakpoints, &dbp.bi, fn, sameline)
}
// FirstPCAfterPrologue returns the address of the first instruction after the prologue for function fn
// If sameline is set FirstPCAfterPrologue will always return an address associated with the same line as fn.Entry
func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
text, err := dbp.CurrentThread().Disassemble(fn.Entry, fn.End, false)
func FirstPCAfterPrologue(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fn *gosym.Func, sameline bool) (uint64, error) {
text, err := Disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End)
if err != nil {
return fn.Entry, err
}

@ -67,7 +67,10 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
// try to interpret the selector as a package variable
if maybePkg, ok := node.X.(*ast.Ident); ok {
if maybePkg.Name == "runtime" && node.Sel.Name == "curg" {
return scope.Thread.getGVariable()
if scope.gvar == nil {
return nilVariable, nil
}
return scope.gvar.clone(), nil
} else if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil {
return v, nil
}
@ -105,7 +108,7 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
return scope.evalBinary(node)
case *ast.BasicLit:
return newConstant(constant.MakeFromLiteral(node.Value, node.Kind, 0), scope.Thread), nil
return newConstant(constant.MakeFromLiteral(node.Value, node.Kind, 0), scope.Mem), nil
default:
return nil, fmt.Errorf("expression %T not implemented", t)
@ -141,7 +144,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
fnnode = p.X
}
styp, err := scope.Thread.dbp.bi.findTypeExpr(fnnode)
styp, err := scope.bi.findTypeExpr(fnnode)
if err != nil {
return nil, err
}
@ -149,7 +152,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
converr := fmt.Errorf("can not convert %q to %s", exprToString(node.Args[0]), typ.String())
v := newVariable("", 0, styp, scope.Thread.dbp, scope.Thread)
v := newVariable("", 0, styp, scope.bi, scope.Mem)
v.loaded = true
switch ttyp := typ.(type) {
@ -435,7 +438,7 @@ func realBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
switch node.Name {
case "true", "false":
return newConstant(constant.MakeBool(node.Name == "true"), scope.Thread), nil
return newConstant(constant.MakeBool(node.Name == "true"), scope.Mem), nil
case "nil":
return nilVariable, nil
}
@ -454,7 +457,7 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
return v, nil
}
// if it's not a local variable then it could be a package variable w/o explicit package name
_, _, fn := scope.Thread.dbp.bi.PCToLine(scope.PC)
_, _, fn := scope.bi.PCToLine(scope.PC)
if fn != nil {
if v, err = scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil {
v.Name = node.Name
@ -492,7 +495,7 @@ func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, err
if xv.Children[0].Addr == 0 {
return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type))
}
typ, err := scope.Thread.dbp.bi.findTypeExpr(node.Type)
typ, err := scope.bi.findTypeExpr(node.Type)
if err != nil {
return nil, err
}
@ -637,7 +640,7 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
xev.OnlyAddr = true
typename := "*" + xev.DwarfType.Common().Name
rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.Thread.dbp.bi.arch.PtrSize()), Name: typename}, Type: xev.DwarfType})
rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.bi.arch.PtrSize()), Name: typename}, Type: xev.DwarfType})
rv.Children = []Variable{*xev}
rv.loaded = true

@ -11,9 +11,9 @@ type moduleData struct {
typemapVar *Variable
}
func (bi *BinaryInfo) loadModuleData(thread *Thread) (err error) {
func loadModuleData(bi *BinaryInfo, mem memoryReadWriter) (err error) {
bi.loadModuleDataOnce.Do(func() {
scope, _ := thread.Scope()
scope := &EvalScope{0, 0, mem, nil, bi}
var md *Variable
md, err = scope.packageVarAddr("runtime.firstmoduledata")
if err != nil {
@ -56,10 +56,9 @@ func (bi *BinaryInfo) loadModuleData(thread *Thread) (err error) {
return
}
func (bi *BinaryInfo) resolveTypeOff(typeAddr uintptr, off uintptr, thread *Thread) (*Variable, error) {
var mem memoryReadWriter = thread
func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryReadWriter) (*Variable, error) {
// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
if err := bi.loadModuleData(thread); err != nil {
if err := loadModuleData(bi, mem); err != nil {
return nil, err
}
@ -76,7 +75,7 @@ func (bi *BinaryInfo) resolveTypeOff(typeAddr uintptr, off uintptr, thread *Thre
}
if md == nil {
v, err := bi.reflectOffsMapAccess(off, thread)
v, err := reflectOffsMapAccess(bi, off, mem)
if err != nil {
return nil, err
}
@ -91,23 +90,22 @@ func (bi *BinaryInfo) resolveTypeOff(typeAddr uintptr, off uintptr, thread *Thre
res := md.types + uintptr(off)
return newVariable("", res, rtyp, thread.dbp, thread), nil
return newVariable("", res, rtyp, bi, mem), nil
}
func (bi *BinaryInfo) resolveNameOff(typeAddr uintptr, off uintptr, thread *Thread) (name, tag string, pkgpathoff int32, err error) {
var mem memoryReadWriter = thread
func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
if err = bi.loadModuleData(thread); err != nil {
if err = loadModuleData(bi, mem); err != nil {
return "", "", 0, err
}
for _, md := range bi.moduleData {
if typeAddr >= md.types && typeAddr < md.etypes {
return bi.loadName(md.types+off, mem)
return loadName(bi, md.types+off, mem)
}
}
v, err := bi.reflectOffsMapAccess(off, thread)
v, err := reflectOffsMapAccess(bi, off, mem)
if err != nil {
return "", "", 0, err
}
@ -117,11 +115,11 @@ func (bi *BinaryInfo) resolveNameOff(typeAddr uintptr, off uintptr, thread *Thre
return "", "", 0, resv.Unreadable
}
return bi.loadName(resv.Addr, mem)
return loadName(bi, resv.Addr, mem)
}
func (bi *BinaryInfo) reflectOffsMapAccess(off uintptr, thread *Thread) (*Variable, error) {
scope, _ := thread.Scope()
func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem memoryReadWriter) (*Variable, error) {
scope := &EvalScope{0, 0, mem, nil, bi}
reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
if err != nil {
return nil, err
@ -132,7 +130,7 @@ func (bi *BinaryInfo) reflectOffsMapAccess(off uintptr, thread *Thread) (*Variab
return nil, err
}
return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), thread))
return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem))
}
const (
@ -142,7 +140,7 @@ const (
nameflagHasPkg = 1 << 2
)
func (bi *BinaryInfo) loadName(addr uintptr, mem memoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
func loadName(bi *BinaryInfo, addr uintptr, mem memoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
off := addr
namedata, err := mem.readMemory(off, 3)
off += 3

@ -179,36 +179,44 @@ func (dbp *Process) LoadInformation(path string) error {
return nil
}
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
return FindFileLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, fileName, lineno)
}
// FindFileLocation returns the PC for a given file:line.
// Assumes that `file` is normailzed to lower case and '/' on Windows.
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
pc, fn, err := dbp.bi.goSymTable.LineToPC(fileName, lineno)
func FindFileLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fileName string, lineno int) (uint64, error) {
pc, fn, err := bi.goSymTable.LineToPC(fileName, lineno)
if err != nil {
return 0, err
}
if fn.Entry == pc {
pc, _ = dbp.FirstPCAfterPrologue(fn, true)
pc, _ = FirstPCAfterPrologue(mem, breakpoints, bi, fn, true)
}
return pc, nil
}
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
return FindFunctionLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, funcName, firstLine, lineOffset)
}
// FindFunctionLocation finds address of a function's line
// If firstLine == true is passed FindFunctionLocation will attempt to find the first line of the function
// If lineOffset is passed FindFunctionLocation will return the address of that line
// Pass lineOffset == 0 and firstLine == false if you want the address for the function's entry point
// Note that setting breakpoints at that address will cause surprising behavior:
// https://github.com/derekparker/delve/issues/170
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
origfn := dbp.bi.goSymTable.LookupFunc(funcName)
func FindFunctionLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, funcName string, firstLine bool, lineOffset int) (uint64, error) {
origfn := bi.goSymTable.LookupFunc(funcName)
if origfn == nil {
return 0, fmt.Errorf("Could not find function %s\n", funcName)
}
if firstLine {
return dbp.FirstPCAfterPrologue(origfn, false)
return FirstPCAfterPrologue(mem, breakpoints, bi, origfn, false)
} else if lineOffset > 0 {
filename, lineno, _ := dbp.bi.goSymTable.PCToLine(origfn.Entry)
breakAddr, _, err := dbp.bi.goSymTable.LineToPC(filename, lineno+lineOffset)
filename, lineno, _ := bi.goSymTable.PCToLine(origfn.Entry)
breakAddr, _, err := bi.goSymTable.LineToPC(filename, lineno+lineOffset)
return breakAddr, err
}
@ -384,7 +392,11 @@ func (dbp *Process) Continue() error {
if err != nil {
return err
}
text, err := dbp.currentThread.Disassemble(pc, pc+maxInstructionLength, true)
regs, err := dbp.currentThread.Registers(false)
if err != nil {
return err
}
text, err := Disassemble(dbp.currentThread, regs, dbp.breakpoints, &dbp.bi, pc, pc+maxInstructionLength)
if err != nil {
return err
}
@ -772,7 +784,8 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
return nil, err
}
ver, isextld, err := dbp.getGoInformation()
scope := &EvalScope{0, 0, dbp.currentThread, nil, dbp.BinInfo()}
ver, isextld, err := scope.getGoInformation()
if err != nil {
return nil, err
}
@ -831,12 +844,12 @@ func (dbp *Process) execPtraceFunc(fn func()) {
<-dbp.ptraceDoneChan
}
func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error) {
vv, err := dbp.EvalPackageVariable("runtime.buildVersion", LoadConfig{true, 0, 64, 0, 0})
func (scope *EvalScope) getGoInformation() (ver GoVersion, isextld bool, err error) {
vv, err := scope.packageVarAddr("runtime.buildVersion")
if err != nil {
err = fmt.Errorf("Could not determine version number: %v\n", err)
return
return ver, false, fmt.Errorf("Could not determine version number: %v", err)
}
vv.loadValue(LoadConfig{true, 0, 64, 0, 0})
if vv.Unreadable != nil {
err = fmt.Errorf("Unreadable version number: %v\n", vv.Unreadable)
return
@ -848,7 +861,7 @@ func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error)
return
}
rdr := dbp.bi.DwarfReader()
rdr := scope.bi.DwarfReader()
rdr.Seek(0)
for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
if err != nil {
@ -892,15 +905,14 @@ func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) {
return nil, err
}
if g == nil {
return dbp.currentThread.Scope()
return dbp.currentThread.ThreadScope()
}
var out EvalScope
var thread *Thread
if g.thread == nil {
out.Thread = dbp.currentThread
thread = dbp.currentThread
} else {
out.Thread = g.thread
thread = g.thread
}
locs, err := g.Stacktrace(frame)
@ -912,9 +924,9 @@ func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) {
return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
}
out.PC, out.CFA = locs[frame].Current.PC, locs[frame].CFA
PC, CFA := locs[frame].Current.PC, locs[frame].CFA
return &out, nil
return &EvalScope{PC, CFA, thread, g.variable, dbp.BinInfo()}, nil
}
func (dbp *Process) postExit() {

@ -1011,7 +1011,7 @@ func TestIssue239(t *testing.T) {
}
func evalVariable(p *Process, symbol string) (*Variable, error) {
scope, err := p.currentThread.Scope()
scope, err := p.currentThread.GoroutineScope()
if err != nil {
return nil, err
}
@ -1019,7 +1019,7 @@ func evalVariable(p *Process, symbol string) (*Variable, error) {
}
func setVariable(p *Process, symbol, value string) error {
scope, err := p.currentThread.Scope()
scope, err := p.currentThread.GoroutineScope()
if err != nil {
return err
}
@ -1194,7 +1194,7 @@ func TestPointerSetting(t *testing.T) {
pval(1)
// change p1 to point to i2
scope, err := p.currentThread.Scope()
scope, err := p.currentThread.GoroutineScope()
assertNoError(err, t, "Scope()")
i2addr, err := scope.EvalExpression("i2", normalLoadConfig)
assertNoError(err, t, "EvalExpression()")
@ -1333,7 +1333,7 @@ func TestBreakpointCountsWithDetection(t *testing.T) {
if th.CurrentBreakpoint == nil {
continue
}
scope, err := th.Scope()
scope, err := th.GoroutineScope()
assertNoError(err, t, "Scope()")
v, err := scope.EvalVariable("i", normalLoadConfig)
assertNoError(err, t, "evalVariable")
@ -1466,7 +1466,7 @@ func TestPointerLoops(t *testing.T) {
func BenchmarkLocalVariables(b *testing.B) {
withTestProcess("testvariables", b, func(p *Process, fixture protest.Fixture) {
assertNoError(p.Continue(), b, "Continue() returned an error")
scope, err := p.currentThread.Scope()
scope, err := p.currentThread.GoroutineScope()
assertNoError(err, b, "Scope()")
for i := 0; i < b.N; i++ {
_, err := scope.LocalVariables(normalLoadConfig)
@ -1692,7 +1692,7 @@ func TestPackageVariables(t *testing.T) {
withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) {
err := p.Continue()
assertNoError(err, t, "Continue()")
scope, err := p.currentThread.Scope()
scope, err := p.currentThread.GoroutineScope()
assertNoError(err, t, "Scope()")
vars, err := scope.PackageVariables(normalLoadConfig)
assertNoError(err, t, "PackageVariables()")
@ -2314,7 +2314,9 @@ func TestStepOnCallPtrInstr(t *testing.T) {
}
pc, err := p.currentThread.PC()
assertNoError(err, t, "PC()")
text, err := p.currentThread.Disassemble(pc, pc+maxInstructionLength, true)
regs, err := p.currentThread.Registers(false)
assertNoError(err, t, "Registers()")
text, err := Disassemble(p.currentThread, regs, p.breakpoints, p.BinInfo(), pc, pc+maxInstructionLength)
assertNoError(err, t, "Disassemble()")
if text[0].IsCall() {
found = true
@ -2450,7 +2452,7 @@ func BenchmarkTrace(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
assertNoError(p.Continue(), b, "Continue()")
s, err := p.currentThread.Scope()
s, err := p.currentThread.GoroutineScope()
assertNoError(err, b, "Scope()")
_, err = s.FunctionArguments(LoadConfig{false, 0, 64, 0, 3})
assertNoError(err, b, "FunctionArguments()")

@ -38,9 +38,9 @@ type Stackframe struct {
addrret uint64
}
// Scope returns a new EvalScope using this frame.
func (frame *Stackframe) Scope(thread *Thread) *EvalScope {
return &EvalScope{Thread: thread, PC: frame.Current.PC, CFA: frame.CFA}
// FrameToScope returns a new EvalScope for this frame
func (p *Process) FrameToScope(frame Stackframe) *EvalScope {
return &EvalScope{frame.Current.PC, frame.CFA, p.currentThread, nil, p.BinInfo()}
}
// ReturnAddress returns the return address of the function
@ -61,7 +61,7 @@ func (t *Thread) stackIterator(stkbar []savedLR, stkbarPos int) (*stackIterator,
if err != nil {
return nil, err
}
return newStackIterator(t.dbp, regs.PC(), regs.SP(), regs.BP(), stkbar, stkbarPos), nil
return newStackIterator(&t.dbp.bi, t, regs.PC(), regs.SP(), regs.BP(), stkbar, stkbarPos), nil
}
// Stacktrace returns the stack trace for thread.
@ -82,7 +82,7 @@ func (g *G) stackIterator() (*stackIterator, error) {
if g.thread != nil {
return g.thread.stackIterator(stkbar, g.stkbarPos)
}
return newStackIterator(g.dbp, g.PC, g.SP, 0, stkbar, g.stkbarPos), nil
return newStackIterator(g.variable.bi, g.variable.mem, g.PC, g.SP, 0, stkbar, g.stkbarPos), nil
}
// Stacktrace returns the stack trace for a goroutine.
@ -117,7 +117,8 @@ type stackIterator struct {
top bool
atend bool
frame Stackframe
dbp *Process
bi *BinaryInfo
mem memoryReadWriter
err error
stackBarrierPC uint64
@ -129,12 +130,12 @@ type savedLR struct {
val uint64
}
func newStackIterator(dbp *Process, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
stackBarrierFunc := dbp.bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9
func newStackIterator(bi *BinaryInfo, mem memoryReadWriter, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
stackBarrierFunc := bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9
var stackBarrierPC uint64
if stackBarrierFunc != nil && stkbar != nil {
stackBarrierPC = stackBarrierFunc.Entry
fn := dbp.bi.goSymTable.PCToFunc(pc)
fn := bi.goSymTable.PCToFunc(pc)
if fn != nil && fn.Name == runtimeStackBarrier {
// 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.
@ -149,7 +150,7 @@ func newStackIterator(dbp *Process, pc, sp, bp uint64, stkbar []savedLR, stkbarP
}
stkbar = stkbar[stkbarPos:]
}
return &stackIterator{pc: pc, sp: sp, bp: bp, top: true, dbp: dbp, err: nil, atend: false, stackBarrierPC: stackBarrierPC, stkbar: stkbar}
return &stackIterator{pc: pc, sp: sp, bp: bp, top: true, bi: bi, mem: mem, err: nil, atend: false, stackBarrierPC: stackBarrierPC, stkbar: stkbar}
}
// Next points the iterator to the next stack frame.
@ -157,7 +158,7 @@ func (it *stackIterator) Next() bool {
if it.err != nil || it.atend {
return false
}
it.frame, it.err = it.dbp.frameInfo(it.pc, it.sp, it.bp, it.top)
it.frame, it.err = it.frameInfo(it.pc, it.sp, it.bp, it.top)
if it.err != nil {
if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top {
it.frame = Stackframe{Current: Location{PC: it.pc, File: "?", Line: -1}, Call: Location{PC: it.pc, File: "?", Line: -1}, CFA: 0, Ret: 0}
@ -188,7 +189,7 @@ func (it *stackIterator) Next() bool {
it.top = false
it.pc = it.frame.Ret
it.sp = uint64(it.frame.CFA)
it.bp, _ = readUintRaw(it.dbp.currentThread, uintptr(it.bp), int64(it.dbp.bi.arch.PtrSize()))
it.bp, _ = readUintRaw(it.mem, uintptr(it.bp), int64(it.bi.arch.PtrSize()))
return true
}
@ -205,37 +206,37 @@ func (it *stackIterator) Err() error {
return it.err
}
func (dbp *Process) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) {
fde, err := dbp.bi.frameEntries.FDEForPC(pc)
func (it *stackIterator) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) {
fde, err := it.bi.frameEntries.FDEForPC(pc)
if _, nofde := err.(*frame.NoFDEForPCError); nofde {
if bp == 0 {
return Stackframe{}, err
}
// When no FDE is available attempt to use BP instead
retaddr := uintptr(int(bp) + dbp.bi.arch.PtrSize())
cfa := int64(retaddr) + int64(dbp.bi.arch.PtrSize())
return dbp.newStackframe(pc, cfa, retaddr, nil, top)
retaddr := uintptr(int(bp) + it.bi.arch.PtrSize())
cfa := int64(retaddr) + int64(it.bi.arch.PtrSize())
return it.newStackframe(pc, cfa, retaddr, nil, top)
}
spoffset, retoffset := fde.ReturnAddressOffset(pc)
cfa := int64(sp) + spoffset
retaddr := uintptr(cfa + retoffset)
return dbp.newStackframe(pc, cfa, retaddr, fde, top)
return it.newStackframe(pc, cfa, retaddr, fde, top)
}
func (dbp *Process) newStackframe(pc uint64, cfa int64, retaddr uintptr, fde *frame.FrameDescriptionEntry, top bool) (Stackframe, error) {
func (it *stackIterator) newStackframe(pc uint64, cfa int64, retaddr uintptr, fde *frame.FrameDescriptionEntry, top bool) (Stackframe, error) {
if retaddr == 0 {
return Stackframe{}, NullAddrError{}
}
f, l, fn := dbp.bi.PCToLine(pc)
ret, err := readUintRaw(dbp.currentThread, retaddr, int64(dbp.bi.arch.PtrSize()))
f, l, fn := it.bi.PCToLine(pc)
ret, err := readUintRaw(it.mem, retaddr, int64(it.bi.arch.PtrSize()))
if err != nil {
return Stackframe{}, err
}
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr)}
if !top {
r.Call.File, r.Call.Line, r.Call.Fn = dbp.bi.PCToLine(pc - 1)
r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(pc - 1)
r.Call.PC = r.Current.PC
} else {
r.Call = r.Current

@ -168,13 +168,16 @@ func (dbp *Process) next(stepInto bool) error {
csource := filepath.Ext(topframe.Current.File) != ".go"
thread := dbp.currentThread
currentGoroutine := false
var regs Registers
if dbp.selectedGoroutine != nil && dbp.selectedGoroutine.thread != nil {
thread = dbp.selectedGoroutine.thread
currentGoroutine = true
regs, err = thread.Registers(false)
if err != nil {
return err
}
}
text, err := thread.Disassemble(topframe.FDE.Begin(), topframe.FDE.End(), currentGoroutine)
text, err := Disassemble(thread, regs, dbp.breakpoints, &dbp.bi, topframe.FDE.Begin(), topframe.FDE.End())
if err != nil && stepInto {
return err
}
@ -429,8 +432,8 @@ func (thread *Thread) Halt() (err error) {
return
}
// Scope returns the current EvalScope for this thread.
func (thread *Thread) Scope() (*EvalScope, error) {
// ThreadScope returns an EvalScope for this thread.
func (thread *Thread) ThreadScope() (*EvalScope, error) {
locations, err := thread.Stacktrace(0)
if err != nil {
return nil, err
@ -438,7 +441,23 @@ func (thread *Thread) Scope() (*EvalScope, error) {
if len(locations) < 1 {
return nil, errors.New("could not decode first frame")
}
return locations[0].Scope(thread), nil
return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, nil, thread.dbp.BinInfo()}, nil
}
// GoroutineScope returns an EvalScope for the goroutine running on this thread.
func (thread *Thread) GoroutineScope() (*EvalScope, error) {
locations, err := thread.Stacktrace(0)
if err != nil {
return nil, err
}
if len(locations) < 1 {
return nil, errors.New("could not decode first frame")
}
gvar, err := thread.getGVariable()
if err != nil {
return nil, err
}
return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, gvar, thread.dbp.BinInfo()}, nil
}
// SetCurrentBreakpoint sets the current breakpoint that this

@ -221,7 +221,7 @@ type nameOfRuntimeTypeEntry struct {
// _type is a non-loaded Variable pointing to runtime._type struct in the target.
// The returned string is in the format that's used in DWARF data
func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error) {
if e, ok := _type.dbp.bi.nameOfRuntimeType[_type.Addr]; ok {
if e, ok := _type.bi.nameOfRuntimeType[_type.Addr]; ok {
return e.typename, e.kind, nil
}
@ -244,7 +244,7 @@ func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error)
return typename, kind, err
}
_type.dbp.bi.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename, kind}
_type.bi.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename, kind}
return typename, kind, nil
}
@ -277,7 +277,7 @@ func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string
// For a description of how memory is organized for type names read
// the comment to 'type name struct' in $GOROOT/src/reflect/type.go
typename, _, _, err = _type.dbp.bi.resolveNameOff(_type.Addr, uintptr(strOff), _type.dbp.currentThread)
typename, _, _, err = resolveNameOff(_type.bi, _type.Addr, uintptr(strOff), _type.mem)
if err != nil {
return "", err
}
@ -303,7 +303,7 @@ func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string
if ut := uncommon(_type, tflag); ut != nil {
if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
pkgPath, _, _, err := _type.dbp.bi.resolveNameOff(_type.Addr, uintptr(pkgPathOff), _type.dbp.currentThread)
pkgPath, _, _, err := resolveNameOff(_type.bi, _type.Addr, uintptr(pkgPathOff), _type.mem)
if err != nil {
return "", err
}
@ -376,11 +376,11 @@ func nameOfUnnamedRuntimeType(_type *Variable, kind, tflag int64) (string, error
// (optional) and then by an array of pointers to runtime._type,
// one for each input and output argument.
func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string, error) {
rtyp, err := _type.dbp.bi.findType("runtime._type")
rtyp, err := _type.bi.findType("runtime._type")
if err != nil {
return "", err
}
prtyp := pointerTo(rtyp, _type.dbp.bi.arch)
prtyp := pointerTo(rtyp, _type.bi.arch)
uadd := _type.RealType.Common().ByteSize
if ut := uncommon(_type, tflag); ut != nil {
@ -407,7 +407,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
for i := int64(0); i < inCount; i++ {
argtype := cursortyp.maybeDereference()
cursortyp.Addr += uintptr(_type.dbp.bi.arch.PtrSize())
cursortyp.Addr += uintptr(_type.bi.arch.PtrSize())
argtypename, _, err := nameOfRuntimeType(argtype)
if err != nil {
return "", err
@ -434,7 +434,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
buf.WriteString(" (")
for i := int64(0); i < outCount; i++ {
argtype := cursortyp.maybeDereference()
cursortyp.Addr += uintptr(_type.dbp.bi.arch.PtrSize())
cursortyp.Addr += uintptr(_type.bi.arch.PtrSize())
argtypename, _, err := nameOfRuntimeType(argtype)
if err != nil {
return "", err
@ -473,14 +473,14 @@ func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, err
case "name":
nameoff, _ := constant.Int64Val(im.Children[i].Value)
var err error
methodname, _, _, err = _type.dbp.bi.resolveNameOff(_type.Addr, uintptr(nameoff), _type.dbp.currentThread)
methodname, _, _, err = resolveNameOff(_type.bi, _type.Addr, uintptr(nameoff), _type.mem)
if err != nil {
return "", err
}
case "typ":
typeoff, _ := constant.Int64Val(im.Children[i].Value)
typ, err := _type.dbp.bi.resolveTypeOff(_type.Addr, uintptr(typeoff), _type.dbp.currentThread)
typ, err := resolveTypeOff(_type.bi, _type.Addr, uintptr(typeoff), _type.mem)
if err != nil {
return "", err
}
@ -536,7 +536,7 @@ func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error)
case "name":
nameoff, _ := constant.Int64Val(field.Children[i].Value)
var err error
fieldname, _, _, err = _type.dbp.bi.loadName(uintptr(nameoff), _type.mem)
fieldname, _, _, err = loadName(_type.bi, uintptr(nameoff), _type.mem)
if err != nil {
return "", err
}
@ -578,13 +578,13 @@ func fieldToType(_type *Variable, fieldName string) (string, error) {
}
func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
rtyp, err := _type.dbp.bi.findType("runtime._type")
rtyp, err := _type.bi.findType("runtime._type")
if err != nil {
return nil, err
}
prtyp := pointerTo(rtyp, _type.dbp.bi.arch)
prtyp := pointerTo(rtyp, _type.bi.arch)
uintptrtyp, err := _type.dbp.bi.findType("uintptr")
uintptrtyp, err := _type.bi.findType("uintptr")
if err != nil {
return nil, err
}
@ -602,7 +602,7 @@ func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType {
r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size()))
appendField(r, "array", pointerTo(elemtype, _type.dbp.bi.arch), 0)
appendField(r, "array", pointerTo(elemtype, _type.bi.arch), 0)
appendField(r, "len", uintptrtyp, uintptr(uintptrtyp.Size()))
appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size()))
return &dwarf.SliceType{StructType: *r, ElemType: elemtype}
@ -746,7 +746,7 @@ func uncommon(_type *Variable, tflag int64) *Variable {
return nil
}
typ, err := _type.dbp.bi.findType("runtime.uncommontype")
typ, err := _type.bi.findType("runtime.uncommontype")
if err != nil {
return nil
}

@ -51,7 +51,7 @@ type Variable struct {
RealType dwarf.Type
Kind reflect.Kind
mem memoryReadWriter
dbp *Process
bi *BinaryInfo
Value constant.Value
FloatSpecial FloatSpecial
@ -132,15 +132,16 @@ type G struct {
thread *Thread
variable *Variable
dbp *Process
}
// EvalScope is the scope for variable evaluation. Contains the thread,
// current location (PC), and canonical frame address.
type EvalScope struct {
Thread *Thread
PC uint64
CFA int64
PC uint64 // Current instruction of the evaluation frame
CFA int64 // Stack address of the evaluation frame
Mem memoryReadWriter // Target's memory
gvar *Variable
bi *BinaryInfo
}
// IsNilErr is returned when a variable is nil.
@ -153,24 +154,24 @@ func (err *IsNilErr) Error() string {
}
func (scope *EvalScope) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable {
return newVariable(name, addr, dwarfType, scope.Thread.dbp, scope.Thread)
return newVariable(name, addr, dwarfType, scope.bi, scope.Mem)
}
func (t *Thread) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable {
return newVariable(name, addr, dwarfType, t.dbp, t)
return newVariable(name, addr, dwarfType, t.dbp.BinInfo(), t)
}
func (v *Variable) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable {
return newVariable(name, addr, dwarfType, v.dbp, v.mem)
return newVariable(name, addr, dwarfType, v.bi, v.mem)
}
func newVariable(name string, addr uintptr, dwarfType dwarf.Type, dbp *Process, mem memoryReadWriter) *Variable {
func newVariable(name string, addr uintptr, dwarfType dwarf.Type, bi *BinaryInfo, mem memoryReadWriter) *Variable {
v := &Variable{
Name: name,
Addr: addr,
DwarfType: dwarfType,
mem: mem,
dbp: dbp,
bi: bi,
}
v.RealType = resolveTypedef(v.DwarfType)
@ -190,7 +191,7 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, dbp *Process,
v.stride = 1
v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}}
if v.Addr != 0 {
v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.dbp.bi.arch, v.Addr)
v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.bi.arch, v.Addr)
}
case *dwarf.SliceType:
v.Kind = reflect.Slice
@ -321,17 +322,17 @@ func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) {
// DwarfReader returns the DwarfReader containing the
// Dwarf information for the target process.
func (scope *EvalScope) DwarfReader() *reader.Reader {
return scope.Thread.dbp.bi.DwarfReader()
return scope.bi.DwarfReader()
}
// Type returns the Dwarf type entry at `offset`.
func (scope *EvalScope) Type(offset dwarf.Offset) (dwarf.Type, error) {
return scope.Thread.dbp.bi.dwarf.Type(offset)
return scope.bi.dwarf.Type(offset)
}
// PtrSize returns the size of a pointer.
func (scope *EvalScope) PtrSize() int {
return scope.Thread.dbp.bi.arch.PtrSize()
return scope.bi.arch.PtrSize()
}
// ChanRecvBlocked returns whether the goroutine is blocked on
@ -362,12 +363,11 @@ func (ng NoGError) Error() string {
func (gvar *Variable) parseG() (*G, error) {
mem := gvar.mem
dbp := gvar.dbp
gaddr := uint64(gvar.Addr)
_, deref := gvar.RealType.(*dwarf.PtrType)
if deref {
gaddrbytes, err := mem.readMemory(uintptr(gaddr), dbp.bi.arch.PtrSize())
gaddrbytes, err := mem.readMemory(uintptr(gaddr), gvar.bi.arch.PtrSize())
if err != nil {
return nil, fmt.Errorf("error derefing *G %s", err)
}
@ -405,7 +405,7 @@ func (gvar *Variable) parseG() (*G, error) {
}
status, _ := constant.Int64Val(gvar.fieldVariable("atomicstatus").Value)
f, l, fn := gvar.dbp.bi.goSymTable.PCToLine(uint64(pc))
f, l, fn := gvar.bi.PCToLine(uint64(pc))
g := &G{
ID: int(id),
GoPC: uint64(gopc),
@ -417,7 +417,6 @@ func (gvar *Variable) parseG() (*G, error) {
variable: gvar,
stkbarVar: stkbarVar,
stkbarPos: int(stkbarPos),
dbp: gvar.dbp,
}
return g, nil
}
@ -498,7 +497,7 @@ func (g *G) UserCurrent() Location {
// Go returns the location of the 'go' statement
// that spawned this goroutine.
func (g *G) Go() Location {
f, l, fn := g.dbp.bi.goSymTable.PCToLine(g.GoPC)
f, l, fn := g.variable.bi.goSymTable.PCToLine(g.GoPC)
return Location{PC: g.GoPC, File: f, Line: l, Fn: fn}
}
@ -586,7 +585,7 @@ func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry, cfg LoadCon
func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) {
reader := scope.DwarfReader()
off, err := scope.Thread.dbp.bi.findFunctionDebugInfo(scope.PC)
off, err := scope.bi.findFunctionDebugInfo(scope.PC)
if err != nil {
return nil, err
}
@ -658,7 +657,7 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
// EvalPackageVariable will evaluate the package level variable
// specified by 'name'.
func (dbp *Process) EvalPackageVariable(name string, cfg LoadConfig) (*Variable, error) {
scope := &EvalScope{Thread: dbp.currentThread, PC: 0, CFA: 0}
scope := &EvalScope{0, 0, dbp.currentThread, nil, dbp.BinInfo()}
v, err := scope.packageVarAddr(name)
if err != nil {
@ -1207,7 +1206,7 @@ func (v *Variable) writeBool(value bool) error {
}
func (v *Variable) readFunctionPtr() {
val, err := v.mem.readMemory(v.Addr, v.dbp.bi.arch.PtrSize())
val, err := v.mem.readMemory(v.Addr, v.bi.arch.PtrSize())
if err != nil {
v.Unreadable = err
return
@ -1221,14 +1220,14 @@ func (v *Variable) readFunctionPtr() {
return
}
val, err = v.mem.readMemory(fnaddr, v.dbp.bi.arch.PtrSize())
val, err = v.mem.readMemory(fnaddr, v.bi.arch.PtrSize())
if err != nil {
v.Unreadable = err
return
}
v.Base = uintptr(binary.LittleEndian.Uint64(val))
fn := v.dbp.bi.goSymTable.PCToFunc(uint64(v.Base))
fn := v.bi.goSymTable.PCToFunc(uint64(v.Base))
if fn == nil {
v.Unreadable = fmt.Errorf("could not find function for %#v", v.Base)
return
@ -1603,7 +1602,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
return
}
typ, err = v.dbp.bi.findType(typename)
typ, err = v.bi.findType(typename)
if err != nil {
v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err)
return
@ -1627,7 +1626,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
return
}
typ, err = v.dbp.bi.findTypeExpr(t)
typ, err = v.bi.findTypeExpr(t)
if err != nil {
v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err)
return
@ -1637,7 +1636,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
if kind&kindDirectIface == 0 {
realtyp := resolveTypedef(typ)
if _, isptr := realtyp.(*dwarf.PtrType); !isptr {
typ = pointerTo(typ, v.dbp.bi.arch)
typ = pointerTo(typ, v.bi.arch)
}
}
@ -1655,7 +1654,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
// Fetches all variables of a specific type in the current function scope
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) {
reader := scope.DwarfReader()
off, err := scope.Thread.dbp.bi.findFunctionDebugInfo(scope.PC)
off, err := scope.bi.findFunctionDebugInfo(scope.PC)
if err != nil {
return nil, err
}

@ -26,8 +26,32 @@ type Info interface {
ThreadInfo
GoroutineInfo
// Disassemble disassembles target memory between startPC and endPC, marking
// the current instruction being executed in goroutine g.
Disassemble(g *proc.G, startPC, endPC uint64) ([]proc.AsmInstruction, error)
// FindFileLocation returns the address of the first instruction belonging
// to line lineNumber in file fileName.
FindFileLocation(fileName string, lineNumber int) (uint64, error)
// FirstPCAfterPrologue returns the first instruction address after fn's
// prologue.
// If sameline is true and the first instruction after the prologue belongs
// to a different source line the entry point will be returned instead.
FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error)
// FindFunctionLocation finds address of a function's line
//
// If firstLine == true is passed FindFunctionLocation will attempt to find
// the first line of the function.
//
// If lineOffset is passed FindFunctionLocation will return the address of
// that line.
//
// Pass lineOffset == 0 and firstLine == false if you want the address for
// the function's entry point. Note that setting breakpoints at that
// address will cause surprising behavior:
// https://github.com/derekparker/delve/issues/170
FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error)
}
@ -70,6 +94,7 @@ type BreakpointManipulation interface {
// VariableEval is an interface for dealing with eval scopes.
type VariableEval interface {
FrameToScope(proc.Stackframe) *proc.EvalScope
ConvertEvalScope(gid, frame int) (*proc.EvalScope, error)
}

@ -490,7 +490,7 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
}
}
s, err := d.target.Threads()[state.Threads[i].ID].Scope()
s, err := d.target.Threads()[state.Threads[i].ID].GoroutineScope()
if err != nil {
return err
}
@ -602,7 +602,7 @@ func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadCo
if !found {
return nil, fmt.Errorf("couldn't find thread %d", threadID)
}
scope, err := thread.Scope()
scope, err := thread.ThreadScope()
if err != nil {
return nil, err
}
@ -752,7 +752,7 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo
frame := api.Stackframe{Location: api.ConvertLocation(rawlocs[i].Call)}
if cfg != nil && rawlocs[i].Current.Fn != nil {
var err error
scope := rawlocs[i].Scope(d.target.CurrentThread())
scope := d.target.FrameToScope(rawlocs[i])
locals, err := scope.LocalVariables(*cfg)
if err != nil {
return nil, err
@ -808,20 +808,12 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo
endPC = fn.End
}
currentGoroutine := true
thread := d.target.CurrentThread()
if s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame); err == nil {
thread = s.Thread
if scope.GoroutineID != -1 {
g, _ := s.Thread.GetG()
if g == nil || g.ID != scope.GoroutineID {
currentGoroutine = false
}
}
g, err := d.target.FindGoroutine(scope.GoroutineID)
if err != nil {
return nil, err
}
insts, err := thread.Disassemble(startPC, endPC, currentGoroutine)
insts, err := d.target.Disassemble(g, startPC, endPC)
if err != nil {
return nil, err
}

@ -54,7 +54,7 @@ func assertVariable(t *testing.T, variable *proc.Variable, expected varTest) {
}
func evalVariable(p *proc.Process, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
scope, err := p.CurrentThread().Scope()
scope, err := p.CurrentThread().GoroutineScope()
if err != nil {
return nil, err
}
@ -68,7 +68,7 @@ func (tc *varTest) alternateVarTest() varTest {
}
func setVariable(p *proc.Process, symbol, value string) error {
scope, err := p.CurrentThread().Scope()
scope, err := p.CurrentThread().GoroutineScope()
if err != nil {
return err
}
@ -348,7 +348,7 @@ func TestLocalVariables(t *testing.T) {
assertNoError(err, t, "Continue() returned an error")
for _, tc := range testcases {
scope, err := p.CurrentThread().Scope()
scope, err := p.CurrentThread().GoroutineScope()
assertNoError(err, t, "AsScope()")
vars, err := tc.fn(scope, pnormalLoadConfig)
assertNoError(err, t, "LocalVariables() returned an error")