proc: replace SavedRegisters interface with a Copy method
Fncall.go was written with the assumption that the object returned by proc.Thread.Registers does not change after we call proc.Thread.SetPC/etc. This is true for the native backend but not for gdbserial. I had anticipated this problem and introduced the Save/SavedRegisters mechanism during the first implementation of fncall.go but that's insufficient. Instead: 1. clarify that the object returned by proc.Thread.Registers could change when the CPU registers are modified. 2. add a Copy method to Registers that returns a copy of the registers that are guaranteed not to change when the CPU registers change. 3. remove the Save/SavedRegisters mechanism. This solution leaves us the option, in the future, to cache the output of proc.(Thread).Registers, avoiding a system call every time it's called.
This commit is contained in:
parent
f1e66f075f
commit
438e51f330
@ -239,7 +239,7 @@ func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) RestoreRegisters(proc.SavedRegisters) error {
|
func (t *Thread) RestoreRegisters(proc.Registers) error {
|
||||||
return errors.New("not supported")
|
return errors.New("not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,6 +426,6 @@ func (r *Registers) Slice() []proc.Register {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registers) Save() proc.SavedRegisters {
|
func (r *Registers) Copy() proc.Registers {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ type functionCallState struct {
|
|||||||
// finished is true if the function call terminated
|
// finished is true if the function call terminated
|
||||||
finished bool
|
finished bool
|
||||||
// savedRegs contains the saved registers
|
// savedRegs contains the saved registers
|
||||||
savedRegs SavedRegisters
|
savedRegs Registers
|
||||||
// expr contains an expression describing the current function call
|
// expr contains an expression describing the current function call
|
||||||
expr string
|
expr string
|
||||||
// err contains a saved error
|
// err contains a saved error
|
||||||
@ -112,6 +112,7 @@ func CallFunction(p Process, expr string, retLoadCfg *LoadConfig) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
regs = regs.Copy()
|
||||||
if regs.SP()-256 <= g.stacklo {
|
if regs.SP()-256 <= g.stacklo {
|
||||||
return ErrNotEnoughStack
|
return ErrNotEnoughStack
|
||||||
}
|
}
|
||||||
@ -139,7 +140,7 @@ func CallFunction(p Process, expr string, retLoadCfg *LoadConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fncall.inProgress = true
|
fncall.inProgress = true
|
||||||
fncall.savedRegs = regs.Save()
|
fncall.savedRegs = regs
|
||||||
fncall.expr = expr
|
fncall.expr = expr
|
||||||
fncall.fn = fn
|
fncall.fn = fn
|
||||||
fncall.closureAddr = closureAddr
|
fncall.closureAddr = closureAddr
|
||||||
@ -155,7 +156,7 @@ func fncallLog(fmtstr string, args ...interface{}) {
|
|||||||
if !logflags.FnCall() {
|
if !logflags.FnCall() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logrus.WithFields(logrus.Fields{"layer": "proc", "kind": "fncall"}).Debugf(fmtstr, args...)
|
logrus.WithFields(logrus.Fields{"layer": "proc", "kind": "fncall"}).Infof(fmtstr, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writePointer writes val as an architecture pointer at addr in mem.
|
// writePointer writes val as an architecture pointer at addr in mem.
|
||||||
@ -401,6 +402,7 @@ func (fncall *functionCallState) step(p Process) {
|
|||||||
fncall.inProgress = false
|
fncall.inProgress = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
regs = regs.Copy()
|
||||||
|
|
||||||
rax, _ := regs.Get(int(x86asm.RAX))
|
rax, _ := regs.Get(int(x86asm.RAX))
|
||||||
|
|
||||||
|
@ -187,6 +187,7 @@ func New(process *os.Process) *Process {
|
|||||||
gcmdok: true,
|
gcmdok: true,
|
||||||
threadStopInfo: true,
|
threadStopInfo: true,
|
||||||
process: process,
|
process: process,
|
||||||
|
common: proc.NewCommonProcess(true),
|
||||||
}
|
}
|
||||||
|
|
||||||
if process != nil {
|
if process != nil {
|
||||||
@ -1188,9 +1189,8 @@ func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
|
|||||||
return &t.regs, nil
|
return &t.regs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) RestoreRegisters(regs proc.SavedRegisters) error {
|
func (t *Thread) RestoreRegisters(savedRegs proc.Registers) error {
|
||||||
gdbregs := regs.(*gdbRegisters)
|
copy(t.regs.buf, savedRegs.(*gdbRegisters).buf)
|
||||||
t.regs = *gdbregs
|
|
||||||
return t.writeRegisters()
|
return t.writeRegisters()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1804,7 +1804,7 @@ func (regs *gdbRegisters) Slice() []proc.Register {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (regs *gdbRegisters) Save() proc.SavedRegisters {
|
func (regs *gdbRegisters) Copy() proc.Registers {
|
||||||
savedRegs := &gdbRegisters{}
|
savedRegs := &gdbRegisters{}
|
||||||
savedRegs.init(regs.regsInfo)
|
savedRegs.init(regs.regsInfo)
|
||||||
copy(savedRegs.buf, regs.buf)
|
copy(savedRegs.buf, regs.buf)
|
||||||
|
@ -866,6 +866,10 @@ func writeAsciiBytes(w io.Writer, data []byte) {
|
|||||||
|
|
||||||
// executes 'M' (write memory) command
|
// executes 'M' (write memory) command
|
||||||
func (conn *gdbConn) writeMemory(addr uintptr, data []byte) (written int, err error) {
|
func (conn *gdbConn) writeMemory(addr uintptr, data []byte) (written int, err error) {
|
||||||
|
if len(data) == 0 {
|
||||||
|
// LLDB can't parse requests for 0-length writes and hangs if we emit them
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
conn.outbuf.Reset()
|
conn.outbuf.Reset()
|
||||||
//TODO(aarzilli): do not send packets larger than conn.PacketSize
|
//TODO(aarzilli): do not send packets larger than conn.PacketSize
|
||||||
fmt.Fprintf(&conn.outbuf, "$M%x,%x:", addr, len(data))
|
fmt.Fprintf(&conn.outbuf, "$M%x,%x:", addr, len(data))
|
||||||
|
@ -367,10 +367,7 @@ func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
|||||||
return regs, nil
|
return regs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type savedRegisters struct {
|
func (r *Regs) Copy() proc.Registers {
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Regs) Save() proc.SavedRegisters {
|
|
||||||
//TODO(aarzilli): implement this to support function calls
|
//TODO(aarzilli): implement this to support function calls
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -324,14 +324,6 @@ func (thread *Thread) fpRegisters() (regs []proc.Register, fpregs proc.LinuxX86X
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type savedRegisters struct {
|
func (r *Regs) Copy() proc.Registers {
|
||||||
regs sys.PtraceRegs
|
return r
|
||||||
fpregs proc.LinuxX86Xstate
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Regs) Save() proc.SavedRegisters {
|
|
||||||
savedRegs := &savedRegisters{}
|
|
||||||
savedRegs.regs = *r.regs
|
|
||||||
savedRegs.fpregs = *r.fpregset
|
|
||||||
return savedRegs
|
|
||||||
}
|
}
|
||||||
|
@ -378,10 +378,7 @@ func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
|||||||
return regs, nil
|
return regs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type savedRegisters struct {
|
func (r *Regs) Copy() proc.Registers {
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Regs) Save() proc.SavedRegisters {
|
|
||||||
//TODO(aarzilli): implement this to support function calls
|
//TODO(aarzilli): implement this to support function calls
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package native
|
package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/proc"
|
"github.com/derekparker/delve/pkg/proc"
|
||||||
@ -151,12 +150,8 @@ func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
|
|||||||
return registers(t, floatingPoint)
|
return registers(t, floatingPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) RestoreRegisters(savedRegs proc.SavedRegisters) error {
|
func (t *Thread) RestoreRegisters(savedRegs proc.Registers) error {
|
||||||
sr, ok := savedRegs.(*savedRegisters)
|
return t.restoreRegisters(savedRegs)
|
||||||
if !ok {
|
|
||||||
return errors.New("unknown saved register set")
|
|
||||||
}
|
|
||||||
return t.restoreRegisters(sr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) PC() (uint64, error) {
|
func (t *Thread) PC() (uint64, error) {
|
||||||
|
@ -146,6 +146,6 @@ func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
|
|||||||
return len(buf), nil
|
return len(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) restoreRegisters(sr *savedRegisters) error {
|
func (t *Thread) restoreRegisters(sr proc.Registers) error {
|
||||||
return errors.New("not implemented")
|
return errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
@ -82,20 +82,22 @@ func (t *Thread) Blocked() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) restoreRegisters(sr *savedRegisters) error {
|
func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
|
||||||
|
sr := savedRegs.(*Regs)
|
||||||
|
|
||||||
var restoreRegistersErr error
|
var restoreRegistersErr error
|
||||||
t.dbp.execPtraceFunc(func() {
|
t.dbp.execPtraceFunc(func() {
|
||||||
restoreRegistersErr = sys.PtraceSetRegs(t.ID, &sr.regs)
|
restoreRegistersErr = sys.PtraceSetRegs(t.ID, sr.regs)
|
||||||
if restoreRegistersErr != nil {
|
if restoreRegistersErr != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if sr.fpregs.Xsave != nil {
|
if sr.fpregset.Xsave != nil {
|
||||||
iov := sys.Iovec{Base: &sr.fpregs.Xsave[0], Len: uint64(len(sr.fpregs.Xsave))}
|
iov := sys.Iovec{Base: &sr.fpregset.Xsave[0], Len: uint64(len(sr.fpregset.Xsave))}
|
||||||
_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(t.ID), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
|
_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(t.ID), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETFPREGS, uintptr(t.ID), uintptr(0), uintptr(unsafe.Pointer(&sr.fpregs.PtraceFpRegs)), 0, 0)
|
_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETFPREGS, uintptr(t.ID), uintptr(0), uintptr(unsafe.Pointer(&sr.fpregset.PtraceFpRegs)), 0, 0)
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
if restoreRegistersErr == syscall.Errno(0) {
|
if restoreRegistersErr == syscall.Errno(0) {
|
||||||
|
@ -150,6 +150,6 @@ func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
|
|||||||
return int(count), err
|
return int(count), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) restoreRegisters(sr *savedRegisters) error {
|
func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
|
||||||
return errors.New("not implemented")
|
return errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,9 @@ type Registers interface {
|
|||||||
GAddr() (uint64, bool)
|
GAddr() (uint64, bool)
|
||||||
Get(int) (uint64, error)
|
Get(int) (uint64, error)
|
||||||
Slice() []Register
|
Slice() []Register
|
||||||
// Save saves a copy of this object that will survive restarts
|
// Copy returns a copy of this registers that is guaranteed not to change
|
||||||
Save() SavedRegisters
|
// when the registers of the associated thread change.
|
||||||
|
Copy() Registers
|
||||||
}
|
}
|
||||||
|
|
||||||
type Register struct {
|
type Register struct {
|
||||||
@ -34,9 +35,6 @@ type Register struct {
|
|||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SavedRegisters interface {
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendWordReg appends a word (16 bit) register to regs.
|
// AppendWordReg appends a word (16 bit) register to regs.
|
||||||
func AppendWordReg(regs []Register, name string, value uint16) []Register {
|
func AppendWordReg(regs []Register, name string, value uint16) []Register {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
@ -250,7 +250,19 @@ func MustSupportFunctionCalls(t *testing.T, testBackend string) {
|
|||||||
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) {
|
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) {
|
||||||
t.Skip("this version of Go does not support function calls")
|
t.Skip("this version of Go does not support function calls")
|
||||||
}
|
}
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
t.Skip("this backend does not support function calls")
|
const unsupported = "this backend does not support function calls"
|
||||||
|
|
||||||
|
if testBackend == "rr" {
|
||||||
|
t.Skip(unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
if testBackend == "native" {
|
||||||
|
t.Skip(unsupported)
|
||||||
|
}
|
||||||
|
case "windows":
|
||||||
|
t.Skip(unsupported)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,17 @@ type Thread interface {
|
|||||||
// nil if the thread is not stopped at any breakpoint.
|
// nil if the thread is not stopped at any breakpoint.
|
||||||
Breakpoint() BreakpointState
|
Breakpoint() BreakpointState
|
||||||
ThreadID() int
|
ThreadID() int
|
||||||
|
|
||||||
|
// Registers returns the CPU registers of this thread. The contents of the
|
||||||
|
// variable returned may or may not change to reflect the new CPU status
|
||||||
|
// when the thread is resumed or the registers are changed by calling
|
||||||
|
// SetPC/SetSP/etc.
|
||||||
|
// To insure that the the returned variable won't change call the Copy
|
||||||
|
// method of Registers.
|
||||||
Registers(floatingPoint bool) (Registers, error)
|
Registers(floatingPoint bool) (Registers, error)
|
||||||
|
|
||||||
// RestoreRegisters restores saved registers
|
// RestoreRegisters restores saved registers
|
||||||
RestoreRegisters(SavedRegisters) error
|
RestoreRegisters(Registers) error
|
||||||
Arch() Arch
|
Arch() Arch
|
||||||
BinInfo() *BinaryInfo
|
BinInfo() *BinaryInfo
|
||||||
StepInstruction() error
|
StepInstruction() error
|
||||||
|
Loading…
Reference in New Issue
Block a user