mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 10:47:27 +08:00 
			
		
		
		
	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
					Alessandro Arzilli
				
			
				
					committed by
					
						 Derek Parker
						Derek Parker
					
				
			
			
				
	
			
			
			 Derek Parker
						Derek Parker
					
				
			
						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 { | 	if err != nil { | ||||||
| 		return true, err | 		return true, err | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -16,14 +16,27 @@ const ( | |||||||
| 	IntelFlavour | 	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 | // 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 | // 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 | // 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) { | func Disassemble(memrw memoryReadWriter, regs Registers, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, startPC, endPC uint64) ([]AsmInstruction, error) { | ||||||
| 	if thread.dbp.exited { | 	mem, err := memrw.readMemory(uintptr(startPC), int(endPC-startPC)) | ||||||
| 		return nil, &ProcessExitedError{} |  | ||||||
| 	} |  | ||||||
| 	mem, err := thread.readMemory(uintptr(startPC), int(endPC-startPC)) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -32,27 +45,23 @@ func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool) | |||||||
| 	pc := startPC | 	pc := startPC | ||||||
|  |  | ||||||
| 	var curpc uint64 | 	var curpc uint64 | ||||||
| 	var regs Registers |  | ||||||
| 	if currentGoroutine { |  | ||||||
| 		regs, _ = thread.Registers(false) |  | ||||||
| 	if regs != nil { | 	if regs != nil { | ||||||
| 		curpc = regs.PC() | 		curpc = regs.PC() | ||||||
| 	} | 	} | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for len(mem) > 0 { | 	for len(mem) > 0 { | ||||||
| 		bp, atbp := thread.dbp.breakpoints[pc] | 		bp, atbp := breakpoints[pc] | ||||||
| 		if atbp { | 		if atbp { | ||||||
| 			for i := range bp.OriginalData { | 			for i := range bp.OriginalData { | ||||||
| 				mem[i] = bp.OriginalData[i] | 				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} | 		loc := Location{PC: pc, File: file, Line: line, Fn: fn} | ||||||
| 		inst, err := asmDecode(mem, pc) | 		inst, err := asmDecode(mem, pc) | ||||||
| 		if err == nil { | 		if err == nil { | ||||||
| 			atpc := currentGoroutine && (curpc == pc) | 			atpc := (regs != nil) && (curpc == pc) | ||||||
| 			destloc := thread.resolveCallArg(inst, atpc, regs) | 			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}) | 			r = append(r, AsmInstruction{Loc: loc, DestLoc: destloc, Bytes: mem[:inst.Len], Breakpoint: atbp, AtPC: atpc, Inst: inst}) | ||||||
|  |  | ||||||
| 			pc += uint64(inst.Size()) | 			pc += uint64(inst.Size()) | ||||||
|  | |||||||
| @ -63,7 +63,7 @@ func (inst *AsmInstruction) IsCall() bool { | |||||||
| 	return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL | 	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 { | 	if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| @ -89,10 +89,6 @@ func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs | |||||||
| 		if arg.Segment != 0 { | 		if arg.Segment != 0 { | ||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
| 		regs, err := thread.Registers(false) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 		base, err1 := regs.Get(int(arg.Base)) | 		base, err1 := regs.Get(int(arg.Base)) | ||||||
| 		index, err2 := regs.Get(int(arg.Index)) | 		index, err2 := regs.Get(int(arg.Index)) | ||||||
| 		if err1 != nil || err2 != nil { | 		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) | 		addr := uintptr(int64(base) + int64(index*uint64(arg.Scale)) + arg.Disp) | ||||||
| 		//TODO: should this always be 64 bits instead of inst.MemBytes? | 		//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 { | 		if err != nil { | ||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
| @ -109,7 +105,7 @@ func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	file, line, fn := thread.dbp.bi.PCToLine(pc) | 	file, line, fn := bininfo.PCToLine(pc) | ||||||
| 	if fn == nil { | 	if fn == nil { | ||||||
| 		return 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 | // 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 | // 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) { | func FirstPCAfterPrologue(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fn *gosym.Func, sameline bool) (uint64, error) { | ||||||
| 	text, err := dbp.CurrentThread().Disassemble(fn.Entry, fn.End, false) | 	text, err := Disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fn.Entry, err | 		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 | 		// try to interpret the selector as a package variable | ||||||
| 		if maybePkg, ok := node.X.(*ast.Ident); ok { | 		if maybePkg, ok := node.X.(*ast.Ident); ok { | ||||||
| 			if maybePkg.Name == "runtime" && node.Sel.Name == "curg" { | 			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 { | 			} else if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil { | ||||||
| 				return v, nil | 				return v, nil | ||||||
| 			} | 			} | ||||||
| @ -105,7 +108,7 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) { | |||||||
| 		return scope.evalBinary(node) | 		return scope.evalBinary(node) | ||||||
|  |  | ||||||
| 	case *ast.BasicLit: | 	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: | 	default: | ||||||
| 		return nil, fmt.Errorf("expression %T not implemented", t) | 		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 | 		fnnode = p.X | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	styp, err := scope.Thread.dbp.bi.findTypeExpr(fnnode) | 	styp, err := scope.bi.findTypeExpr(fnnode) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		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()) | 	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 | 	v.loaded = true | ||||||
|  |  | ||||||
| 	switch ttyp := typ.(type) { | 	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) { | func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) { | ||||||
| 	switch node.Name { | 	switch node.Name { | ||||||
| 	case "true", "false": | 	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": | 	case "nil": | ||||||
| 		return nilVariable, nil | 		return nilVariable, nil | ||||||
| 	} | 	} | ||||||
| @ -454,7 +457,7 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) { | |||||||
| 		return v, nil | 		return v, nil | ||||||
| 	} | 	} | ||||||
| 	// if it's not a local variable then it could be a package variable w/o explicit package name | 	// 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 fn != nil { | ||||||
| 		if v, err = scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil { | 		if v, err = scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil { | ||||||
| 			v.Name = node.Name | 			v.Name = node.Name | ||||||
| @ -492,7 +495,7 @@ func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, err | |||||||
| 	if xv.Children[0].Addr == 0 { | 	if xv.Children[0].Addr == 0 { | ||||||
| 		return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type)) | 		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 { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -637,7 +640,7 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) { | |||||||
| 	xev.OnlyAddr = true | 	xev.OnlyAddr = true | ||||||
|  |  | ||||||
| 	typename := "*" + xev.DwarfType.Common().Name | 	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.Children = []Variable{*xev} | ||||||
| 	rv.loaded = true | 	rv.loaded = true | ||||||
|  |  | ||||||
|  | |||||||
| @ -11,9 +11,9 @@ type moduleData struct { | |||||||
| 	typemapVar    *Variable | 	typemapVar    *Variable | ||||||
| } | } | ||||||
|  |  | ||||||
| func (bi *BinaryInfo) loadModuleData(thread *Thread) (err error) { | func loadModuleData(bi *BinaryInfo, mem memoryReadWriter) (err error) { | ||||||
| 	bi.loadModuleDataOnce.Do(func() { | 	bi.loadModuleDataOnce.Do(func() { | ||||||
| 		scope, _ := thread.Scope() | 		scope := &EvalScope{0, 0, mem, nil, bi} | ||||||
| 		var md *Variable | 		var md *Variable | ||||||
| 		md, err = scope.packageVarAddr("runtime.firstmoduledata") | 		md, err = scope.packageVarAddr("runtime.firstmoduledata") | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @ -56,10 +56,9 @@ func (bi *BinaryInfo) loadModuleData(thread *Thread) (err error) { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| func (bi *BinaryInfo) resolveTypeOff(typeAddr uintptr, off uintptr, thread *Thread) (*Variable, error) { | func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryReadWriter) (*Variable, error) { | ||||||
| 	var mem memoryReadWriter = thread |  | ||||||
| 	// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go | 	// 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 | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -76,7 +75,7 @@ func (bi *BinaryInfo) resolveTypeOff(typeAddr uintptr, off uintptr, thread *Thre | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if md == nil { | 	if md == nil { | ||||||
| 		v, err := bi.reflectOffsMapAccess(off, thread) | 		v, err := reflectOffsMapAccess(bi, off, mem) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| @ -91,23 +90,22 @@ func (bi *BinaryInfo) resolveTypeOff(typeAddr uintptr, off uintptr, thread *Thre | |||||||
|  |  | ||||||
| 	res := md.types + uintptr(off) | 	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) { | func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryReadWriter) (name, tag string, pkgpathoff int32, err error) { | ||||||
| 	var mem memoryReadWriter = thread |  | ||||||
| 	// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go | 	// 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 | 		return "", "", 0, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, md := range bi.moduleData { | 	for _, md := range bi.moduleData { | ||||||
| 		if typeAddr >= md.types && typeAddr < md.etypes { | 		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 { | 	if err != nil { | ||||||
| 		return "", "", 0, err | 		return "", "", 0, err | ||||||
| 	} | 	} | ||||||
| @ -117,11 +115,11 @@ func (bi *BinaryInfo) resolveNameOff(typeAddr uintptr, off uintptr, thread *Thre | |||||||
| 		return "", "", 0, resv.Unreadable | 		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) { | func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem memoryReadWriter) (*Variable, error) { | ||||||
| 	scope, _ := thread.Scope() | 	scope := &EvalScope{0, 0, mem, nil, bi} | ||||||
| 	reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs") | 	reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -132,7 +130,7 @@ func (bi *BinaryInfo) reflectOffsMapAccess(off uintptr, thread *Thread) (*Variab | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), thread)) | 	return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem)) | ||||||
| } | } | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @ -142,7 +140,7 @@ const ( | |||||||
| 	nameflagHasPkg   = 1 << 2 | 	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 | 	off := addr | ||||||
| 	namedata, err := mem.readMemory(off, 3) | 	namedata, err := mem.readMemory(off, 3) | ||||||
| 	off += 3 | 	off += 3 | ||||||
|  | |||||||
| @ -179,36 +179,44 @@ func (dbp *Process) LoadInformation(path string) error { | |||||||
| 	return nil | 	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. | // FindFileLocation returns the PC for a given file:line. | ||||||
| // Assumes that `file` is normailzed to lower case and '/' on Windows. | // Assumes that `file` is normailzed to lower case and '/' on Windows. | ||||||
| func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) { | func FindFileLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fileName string, lineno int) (uint64, error) { | ||||||
| 	pc, fn, err := dbp.bi.goSymTable.LineToPC(fileName, lineno) | 	pc, fn, err := bi.goSymTable.LineToPC(fileName, lineno) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, err | 		return 0, err | ||||||
| 	} | 	} | ||||||
| 	if fn.Entry == pc { | 	if fn.Entry == pc { | ||||||
| 		pc, _ = dbp.FirstPCAfterPrologue(fn, true) | 		pc, _ = FirstPCAfterPrologue(mem, breakpoints, bi, fn, true) | ||||||
| 	} | 	} | ||||||
| 	return pc, nil | 	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 | // 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 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 | // 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 | // 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: | // Note that setting breakpoints at that address will cause surprising behavior: | ||||||
| // https://github.com/derekparker/delve/issues/170 | // https://github.com/derekparker/delve/issues/170 | ||||||
| func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) { | func FindFunctionLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, funcName string, firstLine bool, lineOffset int) (uint64, error) { | ||||||
| 	origfn := dbp.bi.goSymTable.LookupFunc(funcName) | 	origfn := bi.goSymTable.LookupFunc(funcName) | ||||||
| 	if origfn == nil { | 	if origfn == nil { | ||||||
| 		return 0, fmt.Errorf("Could not find function %s\n", funcName) | 		return 0, fmt.Errorf("Could not find function %s\n", funcName) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if firstLine { | 	if firstLine { | ||||||
| 		return dbp.FirstPCAfterPrologue(origfn, false) | 		return FirstPCAfterPrologue(mem, breakpoints, bi, origfn, false) | ||||||
| 	} else if lineOffset > 0 { | 	} else if lineOffset > 0 { | ||||||
| 		filename, lineno, _ := dbp.bi.goSymTable.PCToLine(origfn.Entry) | 		filename, lineno, _ := bi.goSymTable.PCToLine(origfn.Entry) | ||||||
| 		breakAddr, _, err := dbp.bi.goSymTable.LineToPC(filename, lineno+lineOffset) | 		breakAddr, _, err := bi.goSymTable.LineToPC(filename, lineno+lineOffset) | ||||||
| 		return breakAddr, err | 		return breakAddr, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -384,7 +392,11 @@ func (dbp *Process) Continue() error { | |||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					return err | 					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 { | 				if err != nil { | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| @ -772,7 +784,8 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e | |||||||
| 		return nil, err | 		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 { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -831,12 +844,12 @@ func (dbp *Process) execPtraceFunc(fn func()) { | |||||||
| 	<-dbp.ptraceDoneChan | 	<-dbp.ptraceDoneChan | ||||||
| } | } | ||||||
|  |  | ||||||
| func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error) { | func (scope *EvalScope) getGoInformation() (ver GoVersion, isextld bool, err error) { | ||||||
| 	vv, err := dbp.EvalPackageVariable("runtime.buildVersion", LoadConfig{true, 0, 64, 0, 0}) | 	vv, err := scope.packageVarAddr("runtime.buildVersion") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		err = fmt.Errorf("Could not determine version number: %v\n", err) | 		return ver, false, fmt.Errorf("Could not determine version number: %v", err) | ||||||
| 		return |  | ||||||
| 	} | 	} | ||||||
|  | 	vv.loadValue(LoadConfig{true, 0, 64, 0, 0}) | ||||||
| 	if vv.Unreadable != nil { | 	if vv.Unreadable != nil { | ||||||
| 		err = fmt.Errorf("Unreadable version number: %v\n", vv.Unreadable) | 		err = fmt.Errorf("Unreadable version number: %v\n", vv.Unreadable) | ||||||
| 		return | 		return | ||||||
| @ -848,7 +861,7 @@ func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error) | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	rdr := dbp.bi.DwarfReader() | 	rdr := scope.bi.DwarfReader() | ||||||
| 	rdr.Seek(0) | 	rdr.Seek(0) | ||||||
| 	for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() { | 	for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() { | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @ -892,15 +905,14 @@ func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) { | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	if g == nil { | 	if g == nil { | ||||||
| 		return dbp.currentThread.Scope() | 		return dbp.currentThread.ThreadScope() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var out EvalScope | 	var thread *Thread | ||||||
|  |  | ||||||
| 	if g.thread == nil { | 	if g.thread == nil { | ||||||
| 		out.Thread = dbp.currentThread | 		thread = dbp.currentThread | ||||||
| 	} else { | 	} else { | ||||||
| 		out.Thread = g.thread | 		thread = g.thread | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	locs, err := g.Stacktrace(frame) | 	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) | 		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() { | func (dbp *Process) postExit() { | ||||||
|  | |||||||
| @ -1011,7 +1011,7 @@ func TestIssue239(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func evalVariable(p *Process, symbol string) (*Variable, error) { | func evalVariable(p *Process, symbol string) (*Variable, error) { | ||||||
| 	scope, err := p.currentThread.Scope() | 	scope, err := p.currentThread.GoroutineScope() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -1019,7 +1019,7 @@ func evalVariable(p *Process, symbol string) (*Variable, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func setVariable(p *Process, symbol, value string) error { | func setVariable(p *Process, symbol, value string) error { | ||||||
| 	scope, err := p.currentThread.Scope() | 	scope, err := p.currentThread.GoroutineScope() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @ -1194,7 +1194,7 @@ func TestPointerSetting(t *testing.T) { | |||||||
| 		pval(1) | 		pval(1) | ||||||
|  |  | ||||||
| 		// change p1 to point to i2 | 		// change p1 to point to i2 | ||||||
| 		scope, err := p.currentThread.Scope() | 		scope, err := p.currentThread.GoroutineScope() | ||||||
| 		assertNoError(err, t, "Scope()") | 		assertNoError(err, t, "Scope()") | ||||||
| 		i2addr, err := scope.EvalExpression("i2", normalLoadConfig) | 		i2addr, err := scope.EvalExpression("i2", normalLoadConfig) | ||||||
| 		assertNoError(err, t, "EvalExpression()") | 		assertNoError(err, t, "EvalExpression()") | ||||||
| @ -1333,7 +1333,7 @@ func TestBreakpointCountsWithDetection(t *testing.T) { | |||||||
| 				if th.CurrentBreakpoint == nil { | 				if th.CurrentBreakpoint == nil { | ||||||
| 					continue | 					continue | ||||||
| 				} | 				} | ||||||
| 				scope, err := th.Scope() | 				scope, err := th.GoroutineScope() | ||||||
| 				assertNoError(err, t, "Scope()") | 				assertNoError(err, t, "Scope()") | ||||||
| 				v, err := scope.EvalVariable("i", normalLoadConfig) | 				v, err := scope.EvalVariable("i", normalLoadConfig) | ||||||
| 				assertNoError(err, t, "evalVariable") | 				assertNoError(err, t, "evalVariable") | ||||||
| @ -1466,7 +1466,7 @@ func TestPointerLoops(t *testing.T) { | |||||||
| func BenchmarkLocalVariables(b *testing.B) { | func BenchmarkLocalVariables(b *testing.B) { | ||||||
| 	withTestProcess("testvariables", b, func(p *Process, fixture protest.Fixture) { | 	withTestProcess("testvariables", b, func(p *Process, fixture protest.Fixture) { | ||||||
| 		assertNoError(p.Continue(), b, "Continue() returned an error") | 		assertNoError(p.Continue(), b, "Continue() returned an error") | ||||||
| 		scope, err := p.currentThread.Scope() | 		scope, err := p.currentThread.GoroutineScope() | ||||||
| 		assertNoError(err, b, "Scope()") | 		assertNoError(err, b, "Scope()") | ||||||
| 		for i := 0; i < b.N; i++ { | 		for i := 0; i < b.N; i++ { | ||||||
| 			_, err := scope.LocalVariables(normalLoadConfig) | 			_, err := scope.LocalVariables(normalLoadConfig) | ||||||
| @ -1692,7 +1692,7 @@ func TestPackageVariables(t *testing.T) { | |||||||
| 	withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { | 	withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { | ||||||
| 		err := p.Continue() | 		err := p.Continue() | ||||||
| 		assertNoError(err, t, "Continue()") | 		assertNoError(err, t, "Continue()") | ||||||
| 		scope, err := p.currentThread.Scope() | 		scope, err := p.currentThread.GoroutineScope() | ||||||
| 		assertNoError(err, t, "Scope()") | 		assertNoError(err, t, "Scope()") | ||||||
| 		vars, err := scope.PackageVariables(normalLoadConfig) | 		vars, err := scope.PackageVariables(normalLoadConfig) | ||||||
| 		assertNoError(err, t, "PackageVariables()") | 		assertNoError(err, t, "PackageVariables()") | ||||||
| @ -2314,7 +2314,9 @@ func TestStepOnCallPtrInstr(t *testing.T) { | |||||||
| 			} | 			} | ||||||
| 			pc, err := p.currentThread.PC() | 			pc, err := p.currentThread.PC() | ||||||
| 			assertNoError(err, t, "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()") | 			assertNoError(err, t, "Disassemble()") | ||||||
| 			if text[0].IsCall() { | 			if text[0].IsCall() { | ||||||
| 				found = true | 				found = true | ||||||
| @ -2450,7 +2452,7 @@ func BenchmarkTrace(b *testing.B) { | |||||||
| 		b.ResetTimer() | 		b.ResetTimer() | ||||||
| 		for i := 0; i < b.N; i++ { | 		for i := 0; i < b.N; i++ { | ||||||
| 			assertNoError(p.Continue(), b, "Continue()") | 			assertNoError(p.Continue(), b, "Continue()") | ||||||
| 			s, err := p.currentThread.Scope() | 			s, err := p.currentThread.GoroutineScope() | ||||||
| 			assertNoError(err, b, "Scope()") | 			assertNoError(err, b, "Scope()") | ||||||
| 			_, err = s.FunctionArguments(LoadConfig{false, 0, 64, 0, 3}) | 			_, err = s.FunctionArguments(LoadConfig{false, 0, 64, 0, 3}) | ||||||
| 			assertNoError(err, b, "FunctionArguments()") | 			assertNoError(err, b, "FunctionArguments()") | ||||||
|  | |||||||
| @ -38,9 +38,9 @@ type Stackframe struct { | |||||||
| 	addrret uint64 | 	addrret uint64 | ||||||
| } | } | ||||||
|  |  | ||||||
| // Scope returns a new EvalScope using this frame. | // FrameToScope returns a new EvalScope for this frame | ||||||
| func (frame *Stackframe) Scope(thread *Thread) *EvalScope { | func (p *Process) FrameToScope(frame Stackframe) *EvalScope { | ||||||
| 	return &EvalScope{Thread: thread, PC: frame.Current.PC, CFA: frame.CFA} | 	return &EvalScope{frame.Current.PC, frame.CFA, p.currentThread, nil, p.BinInfo()} | ||||||
| } | } | ||||||
|  |  | ||||||
| // ReturnAddress returns the return address of the function | // ReturnAddress returns the return address of the function | ||||||
| @ -61,7 +61,7 @@ func (t *Thread) stackIterator(stkbar []savedLR, stkbarPos int) (*stackIterator, | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		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. | // Stacktrace returns the stack trace for thread. | ||||||
| @ -82,7 +82,7 @@ func (g *G) stackIterator() (*stackIterator, error) { | |||||||
| 	if g.thread != nil { | 	if g.thread != nil { | ||||||
| 		return g.thread.stackIterator(stkbar, g.stkbarPos) | 		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. | // Stacktrace returns the stack trace for a goroutine. | ||||||
| @ -117,7 +117,8 @@ type stackIterator struct { | |||||||
| 	top        bool | 	top        bool | ||||||
| 	atend      bool | 	atend      bool | ||||||
| 	frame      Stackframe | 	frame      Stackframe | ||||||
| 	dbp        *Process | 	bi         *BinaryInfo | ||||||
|  | 	mem        memoryReadWriter | ||||||
| 	err        error | 	err        error | ||||||
|  |  | ||||||
| 	stackBarrierPC uint64 | 	stackBarrierPC uint64 | ||||||
| @ -129,12 +130,12 @@ type savedLR struct { | |||||||
| 	val uint64 | 	val uint64 | ||||||
| } | } | ||||||
|  |  | ||||||
| func newStackIterator(dbp *Process, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator { | func newStackIterator(bi *BinaryInfo, mem memoryReadWriter, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator { | ||||||
| 	stackBarrierFunc := dbp.bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9 | 	stackBarrierFunc := bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9 | ||||||
| 	var stackBarrierPC uint64 | 	var stackBarrierPC uint64 | ||||||
| 	if stackBarrierFunc != nil && stkbar != nil { | 	if stackBarrierFunc != nil && stkbar != nil { | ||||||
| 		stackBarrierPC = stackBarrierFunc.Entry | 		stackBarrierPC = stackBarrierFunc.Entry | ||||||
| 		fn := dbp.bi.goSymTable.PCToFunc(pc) | 		fn := bi.goSymTable.PCToFunc(pc) | ||||||
| 		if fn != nil && fn.Name == runtimeStackBarrier { | 		if fn != nil && fn.Name == runtimeStackBarrier { | ||||||
| 			// We caught the goroutine as it's executing the stack barrier, we must | 			// 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. | 			// 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:] | 		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. | // Next points the iterator to the next stack frame. | ||||||
| @ -157,7 +158,7 @@ func (it *stackIterator) Next() bool { | |||||||
| 	if it.err != nil || it.atend { | 	if it.err != nil || it.atend { | ||||||
| 		return false | 		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 it.err != nil { | ||||||
| 		if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top { | 		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} | 			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.top = false | ||||||
| 	it.pc = it.frame.Ret | 	it.pc = it.frame.Ret | ||||||
| 	it.sp = uint64(it.frame.CFA) | 	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 | 	return true | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -205,37 +206,37 @@ func (it *stackIterator) Err() error { | |||||||
| 	return it.err | 	return it.err | ||||||
| } | } | ||||||
|  |  | ||||||
| func (dbp *Process) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) { | func (it *stackIterator) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) { | ||||||
| 	fde, err := dbp.bi.frameEntries.FDEForPC(pc) | 	fde, err := it.bi.frameEntries.FDEForPC(pc) | ||||||
| 	if _, nofde := err.(*frame.NoFDEForPCError); nofde { | 	if _, nofde := err.(*frame.NoFDEForPCError); nofde { | ||||||
| 		if bp == 0 { | 		if bp == 0 { | ||||||
| 			return Stackframe{}, err | 			return Stackframe{}, err | ||||||
| 		} | 		} | ||||||
| 		// When no FDE is available attempt to use BP instead | 		// When no FDE is available attempt to use BP instead | ||||||
| 		retaddr := uintptr(int(bp) + dbp.bi.arch.PtrSize()) | 		retaddr := uintptr(int(bp) + it.bi.arch.PtrSize()) | ||||||
| 		cfa := int64(retaddr) + int64(dbp.bi.arch.PtrSize()) | 		cfa := int64(retaddr) + int64(it.bi.arch.PtrSize()) | ||||||
| 		return dbp.newStackframe(pc, cfa, retaddr, nil, top) | 		return it.newStackframe(pc, cfa, retaddr, nil, top) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	spoffset, retoffset := fde.ReturnAddressOffset(pc) | 	spoffset, retoffset := fde.ReturnAddressOffset(pc) | ||||||
| 	cfa := int64(sp) + spoffset | 	cfa := int64(sp) + spoffset | ||||||
|  |  | ||||||
| 	retaddr := uintptr(cfa + retoffset) | 	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 { | 	if retaddr == 0 { | ||||||
| 		return Stackframe{}, NullAddrError{} | 		return Stackframe{}, NullAddrError{} | ||||||
| 	} | 	} | ||||||
| 	f, l, fn := dbp.bi.PCToLine(pc) | 	f, l, fn := it.bi.PCToLine(pc) | ||||||
| 	ret, err := readUintRaw(dbp.currentThread, retaddr, int64(dbp.bi.arch.PtrSize())) | 	ret, err := readUintRaw(it.mem, retaddr, int64(it.bi.arch.PtrSize())) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return Stackframe{}, err | 		return Stackframe{}, err | ||||||
| 	} | 	} | ||||||
| 	r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr)} | 	r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr)} | ||||||
| 	if !top { | 	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 | 		r.Call.PC = r.Current.PC | ||||||
| 	} else { | 	} else { | ||||||
| 		r.Call = r.Current | 		r.Call = r.Current | ||||||
|  | |||||||
| @ -168,13 +168,16 @@ func (dbp *Process) next(stepInto bool) error { | |||||||
|  |  | ||||||
| 	csource := filepath.Ext(topframe.Current.File) != ".go" | 	csource := filepath.Ext(topframe.Current.File) != ".go" | ||||||
| 	thread := dbp.currentThread | 	thread := dbp.currentThread | ||||||
| 	currentGoroutine := false | 	var regs Registers | ||||||
| 	if dbp.selectedGoroutine != nil && dbp.selectedGoroutine.thread != nil { | 	if dbp.selectedGoroutine != nil && dbp.selectedGoroutine.thread != nil { | ||||||
| 		thread = dbp.selectedGoroutine.thread | 		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 { | 	if err != nil && stepInto { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @ -429,8 +432,8 @@ func (thread *Thread) Halt() (err error) { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| // Scope returns the current EvalScope for this thread. | // ThreadScope returns an EvalScope for this thread. | ||||||
| func (thread *Thread) Scope() (*EvalScope, error) { | func (thread *Thread) ThreadScope() (*EvalScope, error) { | ||||||
| 	locations, err := thread.Stacktrace(0) | 	locations, err := thread.Stacktrace(0) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -438,7 +441,23 @@ func (thread *Thread) Scope() (*EvalScope, error) { | |||||||
| 	if len(locations) < 1 { | 	if len(locations) < 1 { | ||||||
| 		return nil, errors.New("could not decode first frame") | 		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 | // 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. | // _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 | // The returned string is in the format that's used in DWARF data | ||||||
| func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error) { | 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 | 		return e.typename, e.kind, nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -244,7 +244,7 @@ func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error) | |||||||
| 		return typename, kind, err | 		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 | 	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 | 	// For a description of how memory is organized for type names read | ||||||
| 	// the comment to 'type name struct' in $GOROOT/src/reflect/type.go | 	// 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 { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| @ -303,7 +303,7 @@ func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string | |||||||
| 	if ut := uncommon(_type, tflag); ut != nil { | 	if ut := uncommon(_type, tflag); ut != nil { | ||||||
| 		if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil { | 		if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil { | ||||||
| 			pkgPathOff, _ := constant.Int64Val(pkgPathField.Value) | 			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 { | 			if err != nil { | ||||||
| 				return "", err | 				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, | // (optional) and then by an array of pointers to runtime._type, | ||||||
| // one for each input and output argument. | // one for each input and output argument. | ||||||
| func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string, error) { | 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 { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 	prtyp := pointerTo(rtyp, _type.dbp.bi.arch) | 	prtyp := pointerTo(rtyp, _type.bi.arch) | ||||||
|  |  | ||||||
| 	uadd := _type.RealType.Common().ByteSize | 	uadd := _type.RealType.Common().ByteSize | ||||||
| 	if ut := uncommon(_type, tflag); ut != nil { | 	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++ { | 	for i := int64(0); i < inCount; i++ { | ||||||
| 		argtype := cursortyp.maybeDereference() | 		argtype := cursortyp.maybeDereference() | ||||||
| 		cursortyp.Addr += uintptr(_type.dbp.bi.arch.PtrSize()) | 		cursortyp.Addr += uintptr(_type.bi.arch.PtrSize()) | ||||||
| 		argtypename, _, err := nameOfRuntimeType(argtype) | 		argtypename, _, err := nameOfRuntimeType(argtype) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return "", err | 			return "", err | ||||||
| @ -434,7 +434,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string | |||||||
| 		buf.WriteString(" (") | 		buf.WriteString(" (") | ||||||
| 		for i := int64(0); i < outCount; i++ { | 		for i := int64(0); i < outCount; i++ { | ||||||
| 			argtype := cursortyp.maybeDereference() | 			argtype := cursortyp.maybeDereference() | ||||||
| 			cursortyp.Addr += uintptr(_type.dbp.bi.arch.PtrSize()) | 			cursortyp.Addr += uintptr(_type.bi.arch.PtrSize()) | ||||||
| 			argtypename, _, err := nameOfRuntimeType(argtype) | 			argtypename, _, err := nameOfRuntimeType(argtype) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return "", err | 				return "", err | ||||||
| @ -473,14 +473,14 @@ func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, err | |||||||
| 			case "name": | 			case "name": | ||||||
| 				nameoff, _ := constant.Int64Val(im.Children[i].Value) | 				nameoff, _ := constant.Int64Val(im.Children[i].Value) | ||||||
| 				var err error | 				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 { | 				if err != nil { | ||||||
| 					return "", err | 					return "", err | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 			case "typ": | 			case "typ": | ||||||
| 				typeoff, _ := constant.Int64Val(im.Children[i].Value) | 				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 { | 				if err != nil { | ||||||
| 					return "", err | 					return "", err | ||||||
| 				} | 				} | ||||||
| @ -536,7 +536,7 @@ func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error) | |||||||
| 			case "name": | 			case "name": | ||||||
| 				nameoff, _ := constant.Int64Val(field.Children[i].Value) | 				nameoff, _ := constant.Int64Val(field.Children[i].Value) | ||||||
| 				var err error | 				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 { | 				if err != nil { | ||||||
| 					return "", err | 					return "", err | ||||||
| 				} | 				} | ||||||
| @ -578,13 +578,13 @@ func fieldToType(_type *Variable, fieldName string) (string, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func specificRuntimeType(_type *Variable, kind int64) (*Variable, 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 { | 	if err != nil { | ||||||
| 		return nil, err | 		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 { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -602,7 +602,7 @@ func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) { | |||||||
|  |  | ||||||
| 	newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType { | 	newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType { | ||||||
| 		r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size())) | 		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, "len", uintptrtyp, uintptr(uintptrtyp.Size())) | ||||||
| 		appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size())) | 		appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size())) | ||||||
| 		return &dwarf.SliceType{StructType: *r, ElemType: elemtype} | 		return &dwarf.SliceType{StructType: *r, ElemType: elemtype} | ||||||
| @ -746,7 +746,7 @@ func uncommon(_type *Variable, tflag int64) *Variable { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	typ, err := _type.dbp.bi.findType("runtime.uncommontype") | 	typ, err := _type.bi.findType("runtime.uncommontype") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -51,7 +51,7 @@ type Variable struct { | |||||||
| 	RealType  dwarf.Type | 	RealType  dwarf.Type | ||||||
| 	Kind      reflect.Kind | 	Kind      reflect.Kind | ||||||
| 	mem       memoryReadWriter | 	mem       memoryReadWriter | ||||||
| 	dbp       *Process | 	bi        *BinaryInfo | ||||||
|  |  | ||||||
| 	Value        constant.Value | 	Value        constant.Value | ||||||
| 	FloatSpecial FloatSpecial | 	FloatSpecial FloatSpecial | ||||||
| @ -132,15 +132,16 @@ type G struct { | |||||||
| 	thread *Thread | 	thread *Thread | ||||||
|  |  | ||||||
| 	variable *Variable | 	variable *Variable | ||||||
| 	dbp      *Process |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // EvalScope is the scope for variable evaluation. Contains the thread, | // EvalScope is the scope for variable evaluation. Contains the thread, | ||||||
| // current location (PC), and canonical frame address. | // current location (PC), and canonical frame address. | ||||||
| type EvalScope struct { | type EvalScope struct { | ||||||
| 	Thread *Thread | 	PC   uint64           // Current instruction of the evaluation frame | ||||||
| 	PC     uint64 | 	CFA  int64            // Stack address of the evaluation frame | ||||||
| 	CFA    int64 | 	Mem  memoryReadWriter // Target's memory | ||||||
|  | 	gvar *Variable | ||||||
|  | 	bi   *BinaryInfo | ||||||
| } | } | ||||||
|  |  | ||||||
| // IsNilErr is returned when a variable is nil. | // 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 { | 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 { | 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 { | 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{ | 	v := &Variable{ | ||||||
| 		Name:      name, | 		Name:      name, | ||||||
| 		Addr:      addr, | 		Addr:      addr, | ||||||
| 		DwarfType: dwarfType, | 		DwarfType: dwarfType, | ||||||
| 		mem:       mem, | 		mem:       mem, | ||||||
| 		dbp:       dbp, | 		bi:        bi, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	v.RealType = resolveTypedef(v.DwarfType) | 	v.RealType = resolveTypedef(v.DwarfType) | ||||||
| @ -190,7 +191,7 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, dbp *Process, | |||||||
| 		v.stride = 1 | 		v.stride = 1 | ||||||
| 		v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}} | 		v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}} | ||||||
| 		if v.Addr != 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: | 	case *dwarf.SliceType: | ||||||
| 		v.Kind = reflect.Slice | 		v.Kind = reflect.Slice | ||||||
| @ -321,17 +322,17 @@ func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) { | |||||||
| // DwarfReader returns the DwarfReader containing the | // DwarfReader returns the DwarfReader containing the | ||||||
| // Dwarf information for the target process. | // Dwarf information for the target process. | ||||||
| func (scope *EvalScope) DwarfReader() *reader.Reader { | func (scope *EvalScope) DwarfReader() *reader.Reader { | ||||||
| 	return scope.Thread.dbp.bi.DwarfReader() | 	return scope.bi.DwarfReader() | ||||||
| } | } | ||||||
|  |  | ||||||
| // Type returns the Dwarf type entry at `offset`. | // Type returns the Dwarf type entry at `offset`. | ||||||
| func (scope *EvalScope) Type(offset dwarf.Offset) (dwarf.Type, error) { | 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. | // PtrSize returns the size of a pointer. | ||||||
| func (scope *EvalScope) PtrSize() int { | 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 | // ChanRecvBlocked returns whether the goroutine is blocked on | ||||||
| @ -362,12 +363,11 @@ func (ng NoGError) Error() string { | |||||||
|  |  | ||||||
| func (gvar *Variable) parseG() (*G, error) { | func (gvar *Variable) parseG() (*G, error) { | ||||||
| 	mem := gvar.mem | 	mem := gvar.mem | ||||||
| 	dbp := gvar.dbp |  | ||||||
| 	gaddr := uint64(gvar.Addr) | 	gaddr := uint64(gvar.Addr) | ||||||
| 	_, deref := gvar.RealType.(*dwarf.PtrType) | 	_, deref := gvar.RealType.(*dwarf.PtrType) | ||||||
|  |  | ||||||
| 	if deref { | 	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 { | 		if err != nil { | ||||||
| 			return nil, fmt.Errorf("error derefing *G %s", err) | 			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) | 	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{ | 	g := &G{ | ||||||
| 		ID:         int(id), | 		ID:         int(id), | ||||||
| 		GoPC:       uint64(gopc), | 		GoPC:       uint64(gopc), | ||||||
| @ -417,7 +417,6 @@ func (gvar *Variable) parseG() (*G, error) { | |||||||
| 		variable:   gvar, | 		variable:   gvar, | ||||||
| 		stkbarVar:  stkbarVar, | 		stkbarVar:  stkbarVar, | ||||||
| 		stkbarPos:  int(stkbarPos), | 		stkbarPos:  int(stkbarPos), | ||||||
| 		dbp:        gvar.dbp, |  | ||||||
| 	} | 	} | ||||||
| 	return g, nil | 	return g, nil | ||||||
| } | } | ||||||
| @ -498,7 +497,7 @@ func (g *G) UserCurrent() Location { | |||||||
| // Go returns the location of the 'go' statement | // Go returns the location of the 'go' statement | ||||||
| // that spawned this goroutine. | // that spawned this goroutine. | ||||||
| func (g *G) Go() Location { | 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} | 	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) { | func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) { | ||||||
| 	reader := scope.DwarfReader() | 	reader := scope.DwarfReader() | ||||||
| 	off, err := scope.Thread.dbp.bi.findFunctionDebugInfo(scope.PC) | 	off, err := scope.bi.findFunctionDebugInfo(scope.PC) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -658,7 +657,7 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) { | |||||||
| // EvalPackageVariable will evaluate the package level variable | // EvalPackageVariable will evaluate the package level variable | ||||||
| // specified by 'name'. | // specified by 'name'. | ||||||
| func (dbp *Process) EvalPackageVariable(name string, cfg LoadConfig) (*Variable, error) { | 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) | 	v, err := scope.packageVarAddr(name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -1207,7 +1206,7 @@ func (v *Variable) writeBool(value bool) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (v *Variable) readFunctionPtr() { | 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 { | 	if err != nil { | ||||||
| 		v.Unreadable = err | 		v.Unreadable = err | ||||||
| 		return | 		return | ||||||
| @ -1221,14 +1220,14 @@ func (v *Variable) readFunctionPtr() { | |||||||
| 		return | 		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 { | 	if err != nil { | ||||||
| 		v.Unreadable = err | 		v.Unreadable = err | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	v.Base = uintptr(binary.LittleEndian.Uint64(val)) | 	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 { | 	if fn == nil { | ||||||
| 		v.Unreadable = fmt.Errorf("could not find function for %#v", v.Base) | 		v.Unreadable = fmt.Errorf("could not find function for %#v", v.Base) | ||||||
| 		return | 		return | ||||||
| @ -1603,7 +1602,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		typ, err = v.dbp.bi.findType(typename) | 		typ, err = v.bi.findType(typename) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err) | 			v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err) | ||||||
| 			return | 			return | ||||||
| @ -1627,7 +1626,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		typ, err = v.dbp.bi.findTypeExpr(t) | 		typ, err = v.bi.findTypeExpr(t) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err) | 			v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err) | ||||||
| 			return | 			return | ||||||
| @ -1637,7 +1636,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig | |||||||
| 	if kind&kindDirectIface == 0 { | 	if kind&kindDirectIface == 0 { | ||||||
| 		realtyp := resolveTypedef(typ) | 		realtyp := resolveTypedef(typ) | ||||||
| 		if _, isptr := realtyp.(*dwarf.PtrType); !isptr { | 		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 | // Fetches all variables of a specific type in the current function scope | ||||||
| func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) { | func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) { | ||||||
| 	reader := scope.DwarfReader() | 	reader := scope.DwarfReader() | ||||||
| 	off, err := scope.Thread.dbp.bi.findFunctionDebugInfo(scope.PC) | 	off, err := scope.bi.findFunctionDebugInfo(scope.PC) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -26,8 +26,32 @@ type Info interface { | |||||||
| 	ThreadInfo | 	ThreadInfo | ||||||
| 	GoroutineInfo | 	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) | 	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) | 	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) | 	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. | // VariableEval is an interface for dealing with eval scopes. | ||||||
| type VariableEval interface { | type VariableEval interface { | ||||||
|  | 	FrameToScope(proc.Stackframe) *proc.EvalScope | ||||||
| 	ConvertEvalScope(gid, frame int) (*proc.EvalScope, error) | 	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 { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @ -602,7 +602,7 @@ func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadCo | |||||||
| 	if !found { | 	if !found { | ||||||
| 		return nil, fmt.Errorf("couldn't find thread %d", threadID) | 		return nil, fmt.Errorf("couldn't find thread %d", threadID) | ||||||
| 	} | 	} | ||||||
| 	scope, err := thread.Scope() | 	scope, err := thread.ThreadScope() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		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)} | 		frame := api.Stackframe{Location: api.ConvertLocation(rawlocs[i].Call)} | ||||||
| 		if cfg != nil && rawlocs[i].Current.Fn != nil { | 		if cfg != nil && rawlocs[i].Current.Fn != nil { | ||||||
| 			var err error | 			var err error | ||||||
| 			scope := rawlocs[i].Scope(d.target.CurrentThread()) | 			scope := d.target.FrameToScope(rawlocs[i]) | ||||||
| 			locals, err := scope.LocalVariables(*cfg) | 			locals, err := scope.LocalVariables(*cfg) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, err | 				return nil, err | ||||||
| @ -808,20 +808,12 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo | |||||||
| 		endPC = fn.End | 		endPC = fn.End | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	currentGoroutine := true | 	g, err := d.target.FindGoroutine(scope.GoroutineID) | ||||||
| 	thread := d.target.CurrentThread() | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
| 	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 |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	insts, err := thread.Disassemble(startPC, endPC, currentGoroutine) | 	insts, err := d.target.Disassemble(g, startPC, endPC) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		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) { | 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 { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -68,7 +68,7 @@ func (tc *varTest) alternateVarTest() varTest { | |||||||
| } | } | ||||||
|  |  | ||||||
| func setVariable(p *proc.Process, symbol, value string) error { | func setVariable(p *proc.Process, symbol, value string) error { | ||||||
| 	scope, err := p.CurrentThread().Scope() | 	scope, err := p.CurrentThread().GoroutineScope() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @ -348,7 +348,7 @@ func TestLocalVariables(t *testing.T) { | |||||||
| 		assertNoError(err, t, "Continue() returned an error") | 		assertNoError(err, t, "Continue() returned an error") | ||||||
|  |  | ||||||
| 		for _, tc := range testcases { | 		for _, tc := range testcases { | ||||||
| 			scope, err := p.CurrentThread().Scope() | 			scope, err := p.CurrentThread().GoroutineScope() | ||||||
| 			assertNoError(err, t, "AsScope()") | 			assertNoError(err, t, "AsScope()") | ||||||
| 			vars, err := tc.fn(scope, pnormalLoadConfig) | 			vars, err := tc.fn(scope, pnormalLoadConfig) | ||||||
| 			assertNoError(err, t, "LocalVariables() returned an error") | 			assertNoError(err, t, "LocalVariables() returned an error") | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user