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:
parent
f605716160
commit
b5a06f7aa8
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user