proc: load more registers

Adds ability to load x87, SSE and AVX registers.

Fixes #666
This commit is contained in:
aarzilli
2016-11-15 17:16:33 +01:00
parent 63b8fa8269
commit 8f0646e426
29 changed files with 701 additions and 92 deletions

View File

@ -29,6 +29,7 @@ Command | Description
[stack](#stack) | Print stack trace. [stack](#stack) | Print stack trace.
[step](#step) | Single step through program. [step](#step) | Single step through program.
[step-instruction](#step-instruction) | Single step a single cpu instruction. [step-instruction](#step-instruction) | Single step a single cpu instruction.
[stepout](#stepout) | Step out of the current function.
[thread](#thread) | Switch to the specified thread. [thread](#thread) | Switch to the specified thread.
[threads](#threads) | Print out info for every traced thread. [threads](#threads) | Print out info for every traced thread.
[trace](#trace) | Set tracepoint. [trace](#trace) | Set tracepoint.
@ -195,6 +196,10 @@ Aliases: p
## regs ## regs
Print contents of CPU registers. Print contents of CPU registers.
regs [-a]
Argument -a shows more registers.
## restart ## restart
Restart process. Restart process.
@ -242,6 +247,10 @@ Single step a single cpu instruction.
Aliases: si Aliases: si
## stepout
Step out of the current function.
## thread ## thread
Switch to the specified thread. Switch to the specified thread.
@ -278,3 +287,5 @@ Print package variables.
vars [-v] [<regex>] vars [-v] [<regex>]
If regex is specified only package variables with a name matching it will be returned. If -v is specified more information about each package variable will be shown. If regex is specified only package variables with a name matching it will be returned. If -v is specified more information about each package variable will be shown.

View File

@ -0,0 +1,19 @@
package main
import "runtime"
func fputestsetup(f64a, f64b, f64c, f64d float64, f32a, f32b, f32c, f32d float32)
func main() {
var f64a float64 = 1.1
var f64b float64 = 1.2
var f64c float64 = 1.3
var f64d float64 = 1.4
var f32a float32 = 1.5
var f32b float32 = 1.6
var f32c float32 = 1.7
var f32d float32 = 1.8
fputestsetup(f64a, f64b, f64c, f64d, f32a, f32b, f32c, f32d)
runtime.Breakpoint()
}

View File

@ -0,0 +1,56 @@
TEXT ·fputestsetup(SB),$0-48
// setup x87 stack
FMOVD f64a+0(FP), F0
FMOVD f64b+8(FP), F0
FMOVD f64c+16(FP), F0
FMOVD f64d+24(FP), F0
FMOVF f32a+32(FP), F0
FMOVF f32b+36(FP), F0
FMOVF f32c+40(FP), F0
FMOVF f32d+44(FP), F0
// setup SSE registers
// XMM0 = { f64b, f64a } = { 1.2, 1.1 }
MOVLPS f64a+0(FP), X0
MOVHPS f64b+8(FP), X0
// XMM1 = { f64d, f64c } = { 1.4, 1.3 }
MOVLPS f64c+16(FP), X1
MOVHPS f64d+24(FP), X1
// XMM2 = { f32d, f32c, f32b, f32a } = { 1.8, 1.7, 1.6, 1.5 }
MOVQ f32a+32(FP), AX
MOVQ AX, X2
MOVQ f32c+40(FP), AX
MOVQ AX, X3
PUNPCKLQDQ X3, X2
// XMM3 = { f64a, f64b } = { 1.1, 1.2 }
MOVLPS f64b+8(FP), X3
MOVHPS f64a+0(FP), X3
// XMM4 = { f64c, f64d } = { 1.3, 1.4 }
MOVLPS f64d+24(FP), X4
MOVHPS f64c+16(FP), X4
// XMM5 = { f32b, f32a, f32d, f32c } = { 1.6, 1.5, 1.8, 1.7 }
MOVQ f32c+40(FP), AX
MOVQ AX, X5
MOVQ f32a+32(FP), AX
MOVQ AX, X6
PUNPCKLQDQ X6, X5
// XMM6 = XMM0 + XMM1 = { f64b+f64d, f64a+f64c } = { 2.6, 2.4 }
MOVAPS X0,X6
ADDPD X1, X6
// XMM7 = XMM0 + XMM3 = { f64b+f64a, f64a+f64b } = { 2.3, 2.3 }
MOVAPS X0, X7
ADDPD X3, X7
// XMM8 = XMM2 + XMM5 = { f32d+f32b, f32c+f32a, f32b+f32d, f32a+f32c } = { 3.4, 3.2, 3.4, 3.2 }
MOVAPS X2, X8
ADDPS X5, X8
RET

View File

@ -34,7 +34,7 @@ func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool)
var curpc uint64 var curpc uint64
var regs Registers var regs Registers
if currentGoroutine { if currentGoroutine {
regs, _ = thread.Registers() regs, _ = thread.Registers(false)
if regs != nil { if regs != nil {
curpc = regs.PC() curpc = regs.PC()
} }

View File

@ -88,7 +88,7 @@ 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() regs, err := thread.Registers(false)
if err != nil { if err != nil {
return nil return nil
} }

View File

@ -689,7 +689,7 @@ func (dbp *Process) Halt() (err error) {
// Registers obtains register values from the // Registers obtains register values from the
// "current" thread of the traced process. // "current" thread of the traced process.
func (dbp *Process) Registers() (Registers, error) { func (dbp *Process) Registers() (Registers, error) {
return dbp.CurrentThread.Registers() return dbp.CurrentThread.Registers(false)
} }
// PC returns the PC of the current thread. // PC returns the PC of the current thread.

View File

@ -163,7 +163,7 @@ func TestHalt(t *testing.T) {
if th.running != false { if th.running != false {
t.Fatal("expected running = false for thread", th.ID) t.Fatal("expected running = false for thread", th.ID)
} }
_, err := th.Registers() _, err := th.Registers(false)
assertNoError(err, t, "Registers") assertNoError(err, t, "Registers")
} }
go func() { go func() {
@ -189,7 +189,7 @@ func TestHalt(t *testing.T) {
if th.running != false { if th.running != false {
t.Fatal("expected running = false for thread", th.ID) t.Fatal("expected running = false for thread", th.ID)
} }
_, err := th.Registers() _, err := th.Registers(false)
assertNoError(err, t, "Registers") assertNoError(err, t, "Registers")
} }
}) })
@ -676,6 +676,9 @@ func TestCGONext(t *testing.T) {
if runtime.GOOS == "darwin" && strings.Contains(runtime.Version(), "1.4") { if runtime.GOOS == "darwin" && strings.Contains(runtime.Version(), "1.4") {
return return
} }
if os.Getenv("CGO_ENABLED") == "" {
return
}
withTestProcess("cgotest", t, func(p *Process, fixture protest.Fixture) { withTestProcess("cgotest", t, func(p *Process, fixture protest.Fixture) {
pc, err := p.FindFunctionLocation("main.main", true, 0) pc, err := p.FindFunctionLocation("main.main", true, 0)
@ -895,6 +898,9 @@ func TestGetG(t *testing.T) {
if runtime.GOOS == "darwin" && strings.Contains(runtime.Version(), "1.4") { if runtime.GOOS == "darwin" && strings.Contains(runtime.Version(), "1.4") {
return return
} }
if os.Getenv("CGO_ENABLED") == "" {
return
}
withTestProcess("cgotest", t, func(p *Process, fixture protest.Fixture) { withTestProcess("cgotest", t, func(p *Process, fixture protest.Fixture) {
testGSupportFunc("cgo", t, p, fixture) testGSupportFunc("cgo", t, p, fixture)

View File

@ -1,6 +1,7 @@
package proc package proc
import ( import (
"encoding/binary"
"syscall" "syscall"
"unsafe" "unsafe"
@ -49,3 +50,47 @@ func PtracePeekUser(tid int, off uintptr) (uintptr, error) {
} }
return val, nil return val, nil
} }
// PtraceGetRegset returns floating point registers of the specified thread
// using PTRACE.
// See amd64_linux_fetch_inferior_registers in gdb/amd64-linux-nat.c.html
// and amd64_supply_xsave in gdb/amd64-tdep.c.html
// and Section 13.1 (and following) of Intel® 64 and IA-32 Architectures Software Developers Manual, Volume 1: Basic Architecture
func PtraceGetRegset(tid int) (regset PtraceXsave, err error) {
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(&regset.PtraceFpRegs)), 0, 0)
if err == syscall.Errno(0) {
err = nil
}
var xstateargs [_X86_XSTATE_MAX_SIZE]byte
iov := sys.Iovec{Base: &xstateargs[0], Len: _X86_XSTATE_MAX_SIZE}
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETREGSET, uintptr(tid), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
if err != syscall.Errno(0) {
return
} else {
err = nil
}
if _XSAVE_HEADER_START+_XSAVE_HEADER_LEN >= iov.Len {
return
}
xsaveheader := xstateargs[_XSAVE_HEADER_START : _XSAVE_HEADER_START+_XSAVE_HEADER_LEN]
xstate_bv := binary.LittleEndian.Uint64(xsaveheader[0:8])
xcomp_bv := binary.LittleEndian.Uint64(xsaveheader[8:16])
if xcomp_bv&(1<<63) != 0 {
// compact format not supported
return
}
if xstate_bv&(1<<2) == 0 {
// AVX state not present
return
}
avxstate := xstateargs[_XSAVE_EXTENDED_REGION_START:iov.Len]
regset.AvxState = true
copy(regset.YmmSpace[:], avxstate[:len(regset.YmmSpace)])
return
}

View File

@ -1,6 +1,14 @@
package proc package proc
import "errors" import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"math"
"os"
"strings"
)
// Registers is an interface for a generic register type. The // Registers is an interface for a generic register type. The
// interface encapsulates the generic values / actions // interface encapsulates the generic values / actions
@ -13,21 +21,210 @@ type Registers interface {
TLS() uint64 TLS() uint64
Get(int) (uint64, error) Get(int) (uint64, error)
SetPC(*Thread, uint64) error SetPC(*Thread, uint64) error
String() string Slice() []Register
}
type Register struct {
Name string
Value string
}
func appendWordReg(regs []Register, name string, value uint16) []Register {
return append(regs, Register{name, fmt.Sprintf("%#04x", value)})
}
func appendDwordReg(regs []Register, name string, value uint32) []Register {
return append(regs, Register{name, fmt.Sprintf("%#08x", value)})
}
func appendQwordReg(regs []Register, name string, value uint64) []Register {
return append(regs, Register{name, fmt.Sprintf("%#016x", value)})
}
func appendFlagReg(regs []Register, name string, value uint64, descr flagRegisterDescr, size int) []Register {
return append(regs, Register{name, descr.Describe(value, size)})
}
func appendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64) []Register {
var f float64
fset := false
const (
_SIGNBIT = 1 << 15
_EXP_BIAS = (1 << 14) - 1 // 2^(n-1) - 1 = 16383
_SPECIALEXP = (1 << 15) - 1 // all bits set
_HIGHBIT = 1 << 63
_QUIETBIT = 1 << 62
)
sign := 1.0
if exponent&_SIGNBIT != 0 {
sign = -1.0
}
exponent &= ^uint16(_SIGNBIT)
NaN := math.NaN()
Inf := math.Inf(+1)
switch exponent {
case 0:
switch {
case mantissa == 0:
f = sign * 0.0
fset = true
case mantissa&_HIGHBIT != 0:
f = NaN
fset = true
}
case _SPECIALEXP:
switch {
case mantissa&_HIGHBIT == 0:
f = sign * Inf
fset = true
default:
f = NaN // signaling NaN
fset = true
}
default:
if mantissa&_HIGHBIT == 0 {
f = NaN
fset = true
}
}
if !fset {
significand := float64(mantissa) / (1 << 63)
f = sign * math.Ldexp(significand, int(exponent-_EXP_BIAS))
}
return append(regs, Register{fmt.Sprintf("ST(%d)", index), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)})
}
func appendSSEReg(regs []Register, name string, xmm []byte) []Register {
buf := bytes.NewReader(xmm)
var out bytes.Buffer
var vi [16]uint8
for i := range vi {
binary.Read(buf, binary.LittleEndian, &vi[i])
}
fmt.Fprintf(&out, "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", vi[15], vi[14], vi[13], vi[12], vi[11], vi[10], vi[9], vi[8], vi[7], vi[6], vi[5], vi[4], vi[3], vi[2], vi[1], vi[0])
fmt.Fprintf(&out, "\tv2_int={ %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x }", vi[7], vi[6], vi[5], vi[4], vi[3], vi[2], vi[1], vi[0], vi[15], vi[14], vi[13], vi[12], vi[11], vi[10], vi[9], vi[8])
fmt.Fprintf(&out, "\tv4_int={ %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x }", vi[3], vi[2], vi[1], vi[0], vi[7], vi[6], vi[5], vi[4], vi[11], vi[10], vi[9], vi[8], vi[15], vi[14], vi[13], vi[12])
fmt.Fprintf(&out, "\tv8_int={ %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x }", vi[1], vi[0], vi[3], vi[2], vi[5], vi[4], vi[7], vi[6], vi[9], vi[8], vi[11], vi[10], vi[13], vi[12], vi[15], vi[14])
fmt.Fprintf(&out, "\tv16_int={ %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x }", vi[0], vi[1], vi[2], vi[3], vi[4], vi[5], vi[6], vi[7], vi[8], vi[9], vi[10], vi[11], vi[12], vi[13], vi[14], vi[15])
buf.Seek(0, os.SEEK_SET)
var v2 [2]float64
for i := range v2 {
binary.Read(buf, binary.LittleEndian, &v2[i])
}
fmt.Fprintf(&out, "\tv2_float={ %g %g }", v2[0], v2[1])
buf.Seek(0, os.SEEK_SET)
var v4 [4]float32
for i := range v4 {
binary.Read(buf, binary.LittleEndian, &v4[i])
}
fmt.Fprintf(&out, "\tv4_float={ %g %g %g %g }", v4[0], v4[1], v4[2], v4[3])
return append(regs, Register{name, out.String()})
} }
var UnknownRegisterError = errors.New("unknown register") var UnknownRegisterError = errors.New("unknown register")
// Registers obtains register values from the debugged process. // Registers obtains register values from the debugged process.
func (t *Thread) Registers() (Registers, error) { func (t *Thread) Registers(floatingPoint bool) (Registers, error) {
return registers(t) return registers(t, floatingPoint)
} }
// PC returns the current PC for this thread. // PC returns the current PC for this thread.
func (t *Thread) PC() (uint64, error) { func (t *Thread) PC() (uint64, error) {
regs, err := t.Registers() regs, err := t.Registers(false)
if err != nil { if err != nil {
return 0, err return 0, err
} }
return regs.PC(), nil return regs.PC(), nil
} }
type flagRegisterDescr []flagDescr
type flagDescr struct {
name string
mask uint64
}
var mxcsrDescription flagRegisterDescr = []flagDescr{
{"FZ", 1 << 15},
{"RZ/RN", 1<<14 | 1<<13},
{"PM", 1 << 12},
{"UM", 1 << 11},
{"OM", 1 << 10},
{"ZM", 1 << 9},
{"DM", 1 << 8},
{"IM", 1 << 7},
{"DAZ", 1 << 6},
{"PE", 1 << 5},
{"UE", 1 << 4},
{"OE", 1 << 3},
{"ZE", 1 << 2},
{"DE", 1 << 1},
{"IE", 1 << 0},
}
var eflagsDescription flagRegisterDescr = []flagDescr{
{"CF", 1 << 0},
{"", 1 << 1},
{"PF", 1 << 2},
{"AF", 1 << 4},
{"ZF", 1 << 6},
{"SF", 1 << 7},
{"TF", 1 << 8},
{"IF", 1 << 9},
{"DF", 1 << 10},
{"OF", 1 << 11},
{"IOPL", 1<<12 | 1<<13},
{"NT", 1 << 14},
{"RF", 1 << 16},
{"VM", 1 << 17},
{"AC", 1 << 18},
{"VIF", 1 << 19},
{"VIP", 1 << 20},
{"ID", 1 << 21},
}
func (descr flagRegisterDescr) Mask() uint64 {
var r uint64
for _, f := range descr {
r = r | f.mask
}
return r
}
func (descr flagRegisterDescr) Describe(reg uint64, bitsize int) string {
var r []string
for _, f := range descr {
if f.name == "" {
continue
}
// rbm is f.mask with only the right-most bit set:
// 0001 1100 -> 0000 0100
rbm := f.mask & -f.mask
if rbm == f.mask {
if reg&f.mask != 0 {
r = append(r, f.name)
}
} else {
x := (reg & f.mask) >> uint64(math.Log2(float64(rbm)))
r = append(r, fmt.Sprintf("%s=%x", f.name, x))
}
}
if reg & ^descr.Mask() != 0 {
r = append(r, fmt.Sprintf("unknown_flags=%x", reg&^descr.Mask()))
}
return fmt.Sprintf("%#0*x\t[%s]", bitsize/4, reg, strings.Join(r, " "))
}

View File

@ -3,9 +3,10 @@ package proc
// #include "threads_darwin.h" // #include "threads_darwin.h"
import "C" import "C"
import ( import (
"bytes" "encoding/binary"
"fmt" "fmt"
"rsc.io/x86/x86asm" "rsc.io/x86/x86asm"
"unsafe"
) )
// Regs represents CPU registers on an AMD64 processor. // Regs represents CPU registers on an AMD64 processor.
@ -32,10 +33,10 @@ type Regs struct {
fs uint64 fs uint64
gs uint64 gs uint64
gsBase uint64 gsBase uint64
fpregs []Register
} }
func (r *Regs) String() string { func (r *Regs) Slice() []Register {
var buf bytes.Buffer
var regs = []struct { var regs = []struct {
k string k string
v uint64 v uint64
@ -63,10 +64,16 @@ func (r *Regs) String() string {
{"Gs", r.gs}, {"Gs", r.gs},
{"Gs_base", r.gsBase}, {"Gs_base", r.gsBase},
} }
out := make([]Register, 0, len(regs)+len(r.fpregs))
for _, reg := range regs { for _, reg := range regs {
fmt.Fprintf(&buf, "%8s = %0#16x\n", reg.k, reg.v) if reg.k == "Rflags" {
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
} else {
out = appendQwordReg(out, reg.k, reg.v)
}
} }
return buf.String() out = append(out, r.fpregs...)
return out
} }
// PC returns the current program counter // PC returns the current program counter
@ -259,7 +266,7 @@ func (r *Regs) Get(n int) (uint64, error) {
return 0, UnknownRegisterError return 0, UnknownRegisterError
} }
func registers(thread *Thread) (Registers, error) { func registers(thread *Thread, floatingPoint bool) (Registers, error) {
var state C.x86_thread_state64_t var state C.x86_thread_state64_t
var identity C.thread_identifier_info_data_t var identity C.thread_identifier_info_data_t
kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &state) kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &state)
@ -306,6 +313,36 @@ func registers(thread *Thread) (Registers, error) {
gs: uint64(state.__gs), gs: uint64(state.__gs),
gsBase: uint64(identity.thread_handle), gsBase: uint64(identity.thread_handle),
} }
if floatingPoint {
// https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/thread_status.h?txt
var fpstate C.x86_float_state64_t
kret = C.get_fpu_registers(C.mach_port_name_t(thread.os.threadAct), &fpstate)
if kret != C.KERN_SUCCESS {
return nil, fmt.Errorf("could not get floating point registers")
}
regs.fpregs = appendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))
regs.fpregs = appendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))
regs.fpregs = appendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw))
regs.fpregs = appendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop))
regs.fpregs = appendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip))
regs.fpregs = appendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp))
for i, st := range []*C.char{&fpstate.__fpu_stmm0.__mmst_reg[0], &fpstate.__fpu_stmm1.__mmst_reg[0], &fpstate.__fpu_stmm2.__mmst_reg[0], &fpstate.__fpu_stmm3.__mmst_reg[0], &fpstate.__fpu_stmm4.__mmst_reg[0], &fpstate.__fpu_stmm5.__mmst_reg[0], &fpstate.__fpu_stmm6.__mmst_reg[0], &fpstate.__fpu_stmm7.__mmst_reg[0]} {
stb := C.GoBytes(unsafe.Pointer(st), 10)
mantissa := binary.LittleEndian.Uint64(stb[:8])
exponent := binary.LittleEndian.Uint16(stb[8:])
regs.fpregs = appendX87Reg(regs.fpregs, i, exponent, mantissa)
}
regs.fpregs = appendFlagReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr), mxcsrDescription, 32)
regs.fpregs = appendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask))
for i, xmm := range []*C.char{&fpstate.__fpu_xmm0.__xmm_reg[0], &fpstate.__fpu_xmm1.__xmm_reg[0], &fpstate.__fpu_xmm2.__xmm_reg[0], &fpstate.__fpu_xmm3.__xmm_reg[0], &fpstate.__fpu_xmm4.__xmm_reg[0], &fpstate.__fpu_xmm5.__xmm_reg[0], &fpstate.__fpu_xmm6.__xmm_reg[0], &fpstate.__fpu_xmm7.__xmm_reg[0], &fpstate.__fpu_xmm8.__xmm_reg[0], &fpstate.__fpu_xmm9.__xmm_reg[0], &fpstate.__fpu_xmm10.__xmm_reg[0], &fpstate.__fpu_xmm11.__xmm_reg[0], &fpstate.__fpu_xmm12.__xmm_reg[0], &fpstate.__fpu_xmm13.__xmm_reg[0], &fpstate.__fpu_xmm14.__xmm_reg[0], &fpstate.__fpu_xmm15.__xmm_reg[0]} {
regs.fpregs = appendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16))
}
}
return regs, nil return regs, nil
} }

View File

@ -1,17 +1,20 @@
package proc package proc
import "fmt" import (
import "bytes" "fmt"
import sys "golang.org/x/sys/unix"
import "rsc.io/x86/x86asm" "rsc.io/x86/x86asm"
sys "golang.org/x/sys/unix"
)
// Regs is a wrapper for sys.PtraceRegs. // Regs is a wrapper for sys.PtraceRegs.
type Regs struct { type Regs struct {
regs *sys.PtraceRegs regs *sys.PtraceRegs
fpregs []Register
} }
func (r *Regs) String() string { func (r *Regs) Slice() []Register {
var buf bytes.Buffer
var regs = []struct { var regs = []struct {
k string k string
v uint64 v uint64
@ -44,10 +47,16 @@ func (r *Regs) String() string {
{"Fs", r.regs.Fs}, {"Fs", r.regs.Fs},
{"Gs", r.regs.Gs}, {"Gs", r.regs.Gs},
} }
out := make([]Register, 0, len(regs)+len(r.fpregs))
for _, reg := range regs { for _, reg := range regs {
fmt.Fprintf(&buf, "%8s = %0#16x\n", reg.k, reg.v) if reg.k == "Eflags" {
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
} else {
out = appendQwordReg(out, reg.k, reg.v)
}
} }
return buf.String() out = append(out, r.fpregs...)
return out
} }
// PC returns the value of RIP register. // PC returns the value of RIP register.
@ -235,7 +244,7 @@ func (r *Regs) Get(n int) (uint64, error) {
return 0, UnknownRegisterError return 0, UnknownRegisterError
} }
func registers(thread *Thread) (Registers, error) { func registers(thread *Thread, floatingPoint bool) (Registers, error) {
var ( var (
regs sys.PtraceRegs regs sys.PtraceRegs
err error err error
@ -244,5 +253,73 @@ func registers(thread *Thread) (Registers, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Regs{&regs}, nil r := &Regs{&regs, nil}
if floatingPoint {
r.fpregs, err = thread.fpRegisters()
if err != nil {
return nil, err
}
}
return r, nil
}
// tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
type PtraceFpRegs struct {
Cwd uint16
Swd uint16
Ftw uint16
Fop uint16
Rip uint64
Rdp uint64
Mxcsr uint32
MxcrMask uint32
StSpace [32]uint32
XmmSpace [256]byte
padding [24]uint32
}
type PtraceXsave struct {
PtraceFpRegs
AvxState bool // contains AVX state
YmmSpace [256]byte
}
const (
_X86_XSTATE_MAX_SIZE = 2688
_NT_X86_XSTATE = 0x202
_XSAVE_HEADER_START = 512
_XSAVE_HEADER_LEN = 64
_XSAVE_EXTENDED_REGION_START = 576
_XSAVE_SSE_REGION_LEN = 416
)
func (thread *Thread) fpRegisters() (regs []Register, err error) {
var fpregs PtraceXsave
thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
// x87 registers
regs = appendWordReg(regs, "CW", fpregs.Cwd)
regs = appendWordReg(regs, "SW", fpregs.Swd)
regs = appendWordReg(regs, "TW", fpregs.Ftw)
regs = appendWordReg(regs, "FOP", fpregs.Fop)
regs = appendQwordReg(regs, "FIP", fpregs.Rip)
regs = appendQwordReg(regs, "FDP", fpregs.Rdp)
for i := 0; i < len(fpregs.StSpace); i += 4 {
regs = appendX87Reg(regs, i/4, uint16(fpregs.StSpace[i+2]), uint64(fpregs.StSpace[i+1])<<32|uint64(fpregs.StSpace[i]))
}
// SSE registers
regs = appendFlagReg(regs, "MXCSR", uint64(fpregs.Mxcsr), mxcsrDescription, 32)
regs = appendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask)
for i := 0; i < len(fpregs.XmmSpace); i += 16 {
regs = appendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), fpregs.XmmSpace[i:i+16])
if fpregs.AvxState {
regs = appendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), fpregs.YmmSpace[i:i+16])
}
}
return
} }

View File

@ -1,7 +1,6 @@
package proc package proc
import ( import (
"bytes"
"fmt" "fmt"
"rsc.io/x86/x86asm" "rsc.io/x86/x86asm"
"unsafe" "unsafe"
@ -9,32 +8,32 @@ import (
// Regs represents CPU registers on an AMD64 processor. // Regs represents CPU registers on an AMD64 processor.
type Regs struct { type Regs struct {
rax uint64 rax uint64
rbx uint64 rbx uint64
rcx uint64 rcx uint64
rdx uint64 rdx uint64
rdi uint64 rdi uint64
rsi uint64 rsi uint64
rbp uint64 rbp uint64
rsp uint64 rsp uint64
r8 uint64 r8 uint64
r9 uint64 r9 uint64
r10 uint64 r10 uint64
r11 uint64 r11 uint64
r12 uint64 r12 uint64
r13 uint64 r13 uint64
r14 uint64 r14 uint64
r15 uint64 r15 uint64
rip uint64 rip uint64
eflags uint64 eflags uint64
cs uint64 cs uint64
fs uint64 fs uint64
gs uint64 gs uint64
tls uint64 tls uint64
fltSave *_XMM_SAVE_AREA32
} }
func (r *Regs) String() string { func (r *Regs) Slice() []Register {
var buf bytes.Buffer
var regs = []struct { var regs = []struct {
k string k string
v uint64 v uint64
@ -62,10 +61,38 @@ func (r *Regs) String() string {
{"Gs", r.gs}, {"Gs", r.gs},
{"TLS", r.tls}, {"TLS", r.tls},
} }
for _, reg := range regs { outlen := len(regs)
fmt.Fprintf(&buf, "%8s = %0#16x\n", reg.k, reg.v) if r.fltSave != nil {
outlen += 6 + 8 + 2 + 16
} }
return buf.String() out := make([]Register, 0, outlen)
for _, reg := range regs {
if reg.k == "Eflags" {
out = append(out, Register{reg.k, eflagsDescription.Describe(reg.v, 64)})
} else {
out = appendQwordReg(out, reg.k, reg.v)
}
}
if r.fltSave != nil {
out = appendWordReg(out, "CW", r.fltSave.ControlWord)
out = appendWordReg(out, "SW", r.fltSave.StatusWord)
out = appendWordReg(out, "TW", uint16(r.fltSave.TagWord))
out = appendWordReg(out, "FOP", r.fltSave.ErrorOpcode)
out = appendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset))
out = appendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset))
for i := range r.fltSave.FloatRegisters {
out = appendX87Reg(out, i, uint16(r.fltSave.FloatRegisters[i].High), r.fltSave.FloatRegisters[i].Low)
}
out = appendFlagReg(out, "MXCSR", uint64(r.fltSave.MxCsr), mxcsrDescription, 32)
out = appendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask)
for i := 0; i < len(r.fltSave.XmmRegisters); i += 16 {
out = appendSSEReg(out, fmt.Sprintf("XMM%d", i/16), r.fltSave.XmmRegisters[i:i+16])
}
}
return out
} }
// PC returns the current program counter // PC returns the current program counter
@ -264,7 +291,7 @@ func (r *Regs) Get(n int) (uint64, error) {
return 0, UnknownRegisterError return 0, UnknownRegisterError
} }
func registers(thread *Thread) (Registers, error) { func registers(thread *Thread, floatingPoint bool) (Registers, error) {
context := newCONTEXT() context := newCONTEXT()
context.ContextFlags = _CONTEXT_ALL context.ContextFlags = _CONTEXT_ALL
@ -303,6 +330,11 @@ func registers(thread *Thread) (Registers, error) {
gs: uint64(context.SegGs), gs: uint64(context.SegGs),
tls: uint64(threadInfo.TebBaseAddress), tls: uint64(threadInfo.TebBaseAddress),
} }
if floatingPoint {
regs.fltSave = &context.FltSave
}
return regs, nil return regs, nil
} }

View File

@ -50,7 +50,7 @@ func (t *Thread) ReturnAddress() (uint64, error) {
} }
func (t *Thread) stackIterator() (*stackIterator, error) { func (t *Thread) stackIterator() (*stackIterator, error) {
regs, err := t.Registers() regs, err := t.Registers(false)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -22,6 +22,25 @@ type _M128A struct {
High int64 High int64
} }
type _XMM_SAVE_AREA32 struct {
ControlWord uint16
StatusWord uint16
TagWord byte
Reserved1 byte
ErrorOpcode uint16
ErrorOffset uint32
ErrorSelector uint16
Reserved2 uint16
DataOffset uint32
DataSelector uint16
Reserved3 uint16
MxCsr uint32
MxCsr_Mask uint32
FloatRegisters [8]_M128A
XmmRegisters [256]byte
Reserved4 [96]byte
}
type _CONTEXT struct { type _CONTEXT struct {
P1Home uint64 P1Home uint64
P2Home uint64 P2Home uint64
@ -67,7 +86,7 @@ type _CONTEXT struct {
Rip uint64 Rip uint64
FltSave [512]byte FltSave _XMM_SAVE_AREA32
VectorRegister [26]_M128A VectorRegister [26]_M128A
VectorControl uint64 VectorControl uint64

View File

@ -46,7 +46,13 @@ func BuildFixture(name string) Fixture {
// Make a (good enough) random temporary file name // Make a (good enough) random temporary file name
r := make([]byte, 4) r := make([]byte, 4)
rand.Read(r) rand.Read(r)
dir := fixturesDir
path := filepath.Join(fixturesDir, name+".go") path := filepath.Join(fixturesDir, name+".go")
if name[len(name)-1] == '/' {
dir = filepath.Join(dir, name)
path = ""
name = name[:len(name)-1]
}
tmpfile := filepath.Join(os.TempDir(), fmt.Sprintf("%s.%s", name, hex.EncodeToString(r))) tmpfile := filepath.Join(os.TempDir(), fmt.Sprintf("%s.%s", name, hex.EncodeToString(r)))
buildFlags := []string{"build"} buildFlags := []string{"build"}
@ -54,10 +60,13 @@ func BuildFixture(name string) Fixture {
// Work-around for https://github.com/golang/go/issues/13154 // Work-around for https://github.com/golang/go/issues/13154
buildFlags = append(buildFlags, "-ldflags=-linkmode internal") buildFlags = append(buildFlags, "-ldflags=-linkmode internal")
} }
buildFlags = append(buildFlags, "-gcflags=-N -l", "-o", tmpfile, name+".go") buildFlags = append(buildFlags, "-gcflags=-N -l", "-o", tmpfile)
if path != "" {
buildFlags = append(buildFlags, name+".go")
}
cmd := exec.Command("go", buildFlags...) cmd := exec.Command("go", buildFlags...)
cmd.Dir = fixturesDir cmd.Dir = dir
// Build the test binary // Build the test binary
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {

View File

@ -316,7 +316,7 @@ func (dbp *Process) setInternalBreakpoints(curpc uint64, pcs []uint64, kind Brea
// SetPC sets the PC for this thread. // SetPC sets the PC for this thread.
func (thread *Thread) SetPC(pc uint64) error { func (thread *Thread) SetPC(pc uint64) error {
regs, err := thread.Registers() regs, err := thread.Registers(false)
if err != nil { if err != nil {
return err return err
} }
@ -324,7 +324,7 @@ func (thread *Thread) SetPC(pc uint64) error {
} }
func (thread *Thread) getGVariable() (*Variable, error) { func (thread *Thread) getGVariable() (*Variable, error) {
regs, err := thread.Registers() regs, err := thread.Registers(false)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -50,6 +50,13 @@ get_registers(mach_port_name_t task, x86_thread_state64_t *state) {
return thread_get_state(task, x86_THREAD_STATE64, (thread_state_t)state, &stateCount); return thread_get_state(task, x86_THREAD_STATE64, (thread_state_t)state, &stateCount);
} }
kern_return_t
get_fpu_registers(mach_port_name_t task, x86_float_state64_t *state) {
kern_return_t kret;
mach_msg_type_number_t stateCount = x86_FLOAT_STATE64_COUNT;
return thread_get_state(task, x86_FLOAT_STATE64, (thread_state_t)state, &stateCount);
}
kern_return_t kern_return_t
get_identity(mach_port_name_t task, thread_identifier_info_data_t *idinfo) { get_identity(mach_port_name_t task, thread_identifier_info_data_t *idinfo) {
mach_msg_type_number_t idinfoCount = THREAD_IDENTIFIER_INFO_COUNT; mach_msg_type_number_t idinfoCount = THREAD_IDENTIFIER_INFO_COUNT;

View File

@ -13,6 +13,9 @@ read_memory(task_t, mach_vm_address_t, void *, mach_msg_type_number_t);
kern_return_t kern_return_t
get_registers(mach_port_name_t, x86_thread_state64_t*); get_registers(mach_port_name_t, x86_thread_state64_t*);
kern_return_t
get_fpu_registers(mach_port_name_t, x86_float_state64_t *);
kern_return_t kern_return_t
set_pc(thread_act_t, uint64_t); set_pc(thread_act_t, uint64_t);

View File

@ -82,7 +82,7 @@ func (t *Thread) saveRegisters() (Registers, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("could not save register contents") return nil, fmt.Errorf("could not save register contents")
} }
return &Regs{&t.os.registers}, nil return &Regs{&t.os.registers, nil}, nil
} }
func (t *Thread) restoreRegisters() (err error) { func (t *Thread) restoreRegisters() (err error) {

View File

@ -9,8 +9,8 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/proc" "github.com/derekparker/delve/proc"
"golang.org/x/debug/dwarf"
) )
// ConvertBreakpoint converts from a proc.Breakpoint to // ConvertBreakpoint converts from a proc.Breakpoint to
@ -199,7 +199,7 @@ func ConvertGoroutine(g *proc.G) *Goroutine {
CurrentLoc: ConvertLocation(g.CurrentLoc), CurrentLoc: ConvertLocation(g.CurrentLoc),
UserCurrentLoc: ConvertLocation(g.UserCurrent()), UserCurrentLoc: ConvertLocation(g.UserCurrent()),
GoStatementLoc: ConvertLocation(g.Go()), GoStatementLoc: ConvertLocation(g.Go()),
ThreadID: tid, ThreadID: tid,
} }
} }
@ -254,3 +254,11 @@ func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig {
cfg.MaxStructFields, cfg.MaxStructFields,
} }
} }
func ConvertRegisters(in []proc.Register) (out []Register) {
out = make([]Register, len(in))
for i := range in {
out[i] = Register{in[i].Name, in[i].Value}
}
return
}

View File

@ -1,6 +1,7 @@
package api package api
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
@ -290,3 +291,25 @@ type SetAPIVersionIn struct {
type SetAPIVersionOut struct { type SetAPIVersionOut struct {
} }
type Register struct {
Name string
Value string
}
type Registers []Register
func (regs Registers) String() string {
maxlen := 0
for _, reg := range regs {
if n := len(reg.Name); n > maxlen {
maxlen = n
}
}
var buf bytes.Buffer
for _, reg := range regs {
fmt.Fprintf(&buf, "%*s = %s\n", maxlen, reg.Name, reg.Value)
}
return buf.String()
}

View File

@ -79,7 +79,7 @@ type Client interface {
// ListFunctionArgs lists all arguments to the current function. // ListFunctionArgs lists all arguments to the current function.
ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error)
// ListRegisters lists registers and their values. // ListRegisters lists registers and their values.
ListRegisters() (string, error) ListRegisters(threadID int, includeFp bool) (api.Registers, error)
// ListGoroutines lists all goroutines. // ListGoroutines lists all goroutines.
ListGoroutines() ([]*api.Goroutine, error) ListGoroutines() ([]*api.Goroutine, error)

View File

@ -602,19 +602,19 @@ func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadCo
} }
// Registers returns string representation of the CPU registers. // Registers returns string representation of the CPU registers.
func (d *Debugger) Registers(threadID int) (string, error) { func (d *Debugger) Registers(threadID int, floatingPoint bool) (api.Registers, error) {
d.processMutex.Lock() d.processMutex.Lock()
defer d.processMutex.Unlock() defer d.processMutex.Unlock()
thread, found := d.process.Threads[threadID] thread, found := d.process.Threads[threadID]
if !found { if !found {
return "", fmt.Errorf("couldn't find thread %d", threadID) return nil, fmt.Errorf("couldn't find thread %d", threadID)
} }
regs, err := thread.Registers() regs, err := thread.Registers(floatingPoint)
if err != nil { if err != nil {
return "", err return nil, err
} }
return regs.String(), err return api.ConvertRegisters(regs.Slice()), err
} }
func convertVars(pv []*proc.Variable) []api.Variable { func convertVars(pv []*proc.Variable) []api.Variable {

View File

@ -199,11 +199,11 @@ func (s *RPCServer) ListRegisters(arg interface{}, registers *string) error {
return err return err
} }
regs, err := s.debugger.Registers(state.CurrentThread.ID) regs, err := s.debugger.Registers(state.CurrentThread.ID, false)
if err != nil { if err != nil {
return err return err
} }
*registers = regs *registers = regs.String()
return nil return nil
} }

View File

@ -105,7 +105,7 @@ func (c *RPCClient) Step() (*api.DebuggerState, error) {
func (c *RPCClient) StepOut() (*api.DebuggerState, error) { func (c *RPCClient) StepOut() (*api.DebuggerState, error) {
var out CommandOut var out CommandOut
err := c.call("Command", &api.DebuggerCommand{ Name: api.StepOut}, &out) err := c.call("Command", &api.DebuggerCommand{Name: api.StepOut}, &out)
return &out.State, err return &out.State, err
} }
@ -241,10 +241,10 @@ func (c *RPCClient) ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig)
return out.Variables, err return out.Variables, err
} }
func (c *RPCClient) ListRegisters() (string, error) { func (c *RPCClient) ListRegisters(threadID int, includeFp bool) (api.Registers, error) {
out := new(ListRegistersOut) out := new(ListRegistersOut)
err := c.call("ListRegisters", ListRegistersIn{}, out) err := c.call("ListRegisters", ListRegistersIn{ThreadID: threadID, IncludeFp: includeFp}, out)
return out.Registers, err return out.Regs, err
} }
func (c *RPCClient) ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) { func (c *RPCClient) ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {

View File

@ -312,24 +312,32 @@ func (s *RPCServer) ListPackageVars(arg ListPackageVarsIn, out *ListPackageVarsO
} }
type ListRegistersIn struct { type ListRegistersIn struct {
ThreadID int
IncludeFp bool
} }
type ListRegistersOut struct { type ListRegistersOut struct {
Registers string Registers string
Regs api.Registers
} }
// ListRegisters lists registers and their values. // ListRegisters lists registers and their values.
func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) error { func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) error {
state, err := s.debugger.State() if arg.ThreadID == 0 {
if err != nil { state, err := s.debugger.State()
return err if err != nil {
return err
}
arg.ThreadID = state.CurrentThread.ID
} }
regs, err := s.debugger.Registers(state.CurrentThread.ID) regs, err := s.debugger.Registers(arg.ThreadID, arg.IncludeFp)
if err != nil { if err != nil {
return err return err
} }
out.Registers = regs out.Regs = regs
out.Registers = out.Regs.String()
return nil return nil
} }

View File

@ -457,11 +457,11 @@ func TestClientServer_infoArgs(t *testing.T) {
if state.Err != nil { if state.Err != nil {
t.Fatalf("Unexpected error: %v, state: %#v", state.Err, state) t.Fatalf("Unexpected error: %v, state: %#v", state.Err, state)
} }
regs, err := c.ListRegisters() regs, err := c.ListRegisters(0, false)
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
if regs == "" { if len(regs) == 0 {
t.Fatal("Expected string showing registers values, got empty string") t.Fatal("Expected string showing registers values, got empty string")
} }
locals, err := c.ListFunctionArgs(api.EvalScope{-1, 0}, normalLoadConfig) locals, err := c.ListFunctionArgs(api.EvalScope{-1, 0}, normalLoadConfig)
@ -853,7 +853,7 @@ func TestIssue355(t *testing.T) {
assertError(err, t, "ListLocalVariables()") assertError(err, t, "ListLocalVariables()")
_, err = c.ListFunctionArgs(api.EvalScope{gid, 0}, normalLoadConfig) _, err = c.ListFunctionArgs(api.EvalScope{gid, 0}, normalLoadConfig)
assertError(err, t, "ListFunctionArgs()") assertError(err, t, "ListFunctionArgs()")
_, err = c.ListRegisters() _, err = c.ListRegisters(0, false)
assertError(err, t, "ListRegisters()") assertError(err, t, "ListRegisters()")
_, err = c.ListGoroutines() _, err = c.ListGoroutines()
assertError(err, t, "ListGoroutines()") assertError(err, t, "ListGoroutines()")
@ -1149,3 +1149,47 @@ func TestClientServer_Issue528(t *testing.T) {
findLocationHelper(t, c, "State.Close", false, 1, 0) findLocationHelper(t, c, "State.Close", false, 1, 0)
}) })
} }
func TestClientServer_FpRegisters(t *testing.T) {
regtests := []struct{ name, value string }{
{"ST(0)", "0x3fffe666660000000000"},
{"ST(1)", "0x3fffd9999a0000000000"},
{"ST(2)", "0x3fffcccccd0000000000"},
{"ST(3)", "0x3fffc000000000000000"},
{"ST(4)", "0x3fffb333333333333000"},
{"ST(5)", "0x3fffa666666666666800"},
{"ST(6)", "0x3fff9999999999999800"},
{"ST(7)", "0x3fff8cccccccccccd000"},
{"XMM0", "0x3ff33333333333333ff199999999999a v2_int={ 3ff199999999999a 3ff3333333333333 } v4_int={ 9999999a 3ff19999 33333333 3ff33333 } v8_int={ 999a 9999 9999 3ff1 3333 3333 3333 3ff3 } v16_int={ 9a 99 99 99 99 99 f1 3f 33 33 33 33 33 33 f3 3f }"},
{"XMM1", "0x3ff66666666666663ff4cccccccccccd"},
{"XMM2", "0x3fe666663fd9999a3fcccccd3fc00000"},
{"XMM3", "0x3ff199999999999a3ff3333333333333"},
{"XMM4", "0x3ff4cccccccccccd3ff6666666666666"},
{"XMM5", "0x3fcccccd3fc000003fe666663fd9999a"},
{"XMM6", "0x4004cccccccccccc4003333333333334"},
{"XMM7", "0x40026666666666664002666666666666"},
{"XMM8", "0x4059999a404ccccd4059999a404ccccd"},
}
withTestClient2("fputest/", t, func(c service.Client) {
<-c.Continue()
regs, err := c.ListRegisters(0, true)
assertNoError(err, t, "ListRegisters()")
t.Logf("%s", regs.String())
for _, regtest := range regtests {
found := false
for _, reg := range regs {
if reg.Name == regtest.name {
found = true
if !strings.HasPrefix(reg.Value, regtest.value) {
t.Fatalf("register %s expected %q got %q", reg.Name, regtest.value, reg.Value)
}
}
}
if !found {
t.Fatalf("register %s not found: %v", regtest.name, regs)
}
}
})
}

View File

@ -176,7 +176,11 @@ If regex is specified only local variables with a name matching it will be retur
vars [-v] [<regex>] vars [-v] [<regex>]
If regex is specified only package variables with a name matching it will be returned. If -v is specified more information about each package variable will be shown.`}, If regex is specified only package variables with a name matching it will be returned. If -v is specified more information about each package variable will be shown.`},
{aliases: []string{"regs"}, cmdFn: regs, helpMsg: "Print contents of CPU registers."}, {aliases: []string{"regs"}, cmdFn: regs, helpMsg: `Print contents of CPU registers.
regs [-a]
Argument -a shows more registers.`},
{aliases: []string{"exit", "quit", "q"}, cmdFn: exitCommand, helpMsg: "Exit the debugger."}, {aliases: []string{"exit", "quit", "q"}, cmdFn: exitCommand, helpMsg: "Exit the debugger."},
{aliases: []string{"list", "ls"}, allowedPrefixes: scopePrefix, cmdFn: listCommand, helpMsg: `Show source code. {aliases: []string{"list", "ls"}, allowedPrefixes: scopePrefix, cmdFn: listCommand, helpMsg: `Show source code.
@ -963,7 +967,11 @@ func vars(t *Term, ctx callContext, args string) error {
} }
func regs(t *Term, ctx callContext, args string) error { func regs(t *Term, ctx callContext, args string) error {
regs, err := t.client.ListRegisters() includeFp := false
if args == "-a" {
includeFp = true
}
regs, err := t.client.ListRegisters(0, includeFp)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,15 +1,15 @@
package terminal package terminal
import ( import (
"testing"
"runtime" "runtime"
"testing"
"github.com/derekparker/delve/config" "github.com/derekparker/delve/config"
) )
type tRule struct { type tRule struct {
from string from string
to string to string
} }
type tCase struct { type tCase struct {
@ -66,9 +66,9 @@ func platformCases() []tCase {
} }
func TestSubstitutePath(t *testing.T) { func TestSubstitutePath(t *testing.T) {
for _, c := range(platformCases()) { for _, c := range platformCases() {
var subRules config.SubstitutePathRules var subRules config.SubstitutePathRules
for _, r := range(c.rules) { for _, r := range c.rules {
subRules = append(subRules, config.SubstitutePathRule{From: r.from, To: r.to}) subRules = append(subRules, config.SubstitutePathRule{From: r.from, To: r.to})
} }
res := New(nil, &config.Config{SubstitutePath: subRules}).substitutePath(c.path) res := New(nil, &config.Config{SubstitutePath: subRules}).substitutePath(c.path)