提交 8f0646e4 编写于 作者: A aarzilli

proc: load more registers

Adds ability to load x87, SSE and AVX registers.

Fixes #666
上级 63b8fa82
......@@ -29,6 +29,7 @@ Command | Description
[stack](#stack) | Print stack trace.
[step](#step) | Single step through program.
[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.
[threads](#threads) | Print out info for every traced thread.
[trace](#trace) | Set tracepoint.
......@@ -195,6 +196,10 @@ Aliases: p
## regs
Print contents of CPU registers.
regs [-a]
Argument -a shows more registers.
## restart
Restart process.
......@@ -242,6 +247,10 @@ Single step a single cpu instruction.
Aliases: si
## stepout
Step out of the current function.
## thread
Switch to the specified thread.
......@@ -278,3 +287,5 @@ Print package variables.
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.
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()
}
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
......@@ -34,7 +34,7 @@ func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool)
var curpc uint64
var regs Registers
if currentGoroutine {
regs, _ = thread.Registers()
regs, _ = thread.Registers(false)
if regs != nil {
curpc = regs.PC()
}
......
......@@ -88,7 +88,7 @@ func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs
if arg.Segment != 0 {
return nil
}
regs, err := thread.Registers()
regs, err := thread.Registers(false)
if err != nil {
return nil
}
......
......@@ -689,7 +689,7 @@ func (dbp *Process) Halt() (err error) {
// Registers obtains register values from the
// "current" thread of the traced process.
func (dbp *Process) Registers() (Registers, error) {
return dbp.CurrentThread.Registers()
return dbp.CurrentThread.Registers(false)
}
// PC returns the PC of the current thread.
......
......@@ -163,7 +163,7 @@ func TestHalt(t *testing.T) {
if th.running != false {
t.Fatal("expected running = false for thread", th.ID)
}
_, err := th.Registers()
_, err := th.Registers(false)
assertNoError(err, t, "Registers")
}
go func() {
......@@ -189,7 +189,7 @@ func TestHalt(t *testing.T) {
if th.running != false {
t.Fatal("expected running = false for thread", th.ID)
}
_, err := th.Registers()
_, err := th.Registers(false)
assertNoError(err, t, "Registers")
}
})
......@@ -676,6 +676,9 @@ func TestCGONext(t *testing.T) {
if runtime.GOOS == "darwin" && strings.Contains(runtime.Version(), "1.4") {
return
}
if os.Getenv("CGO_ENABLED") == "" {
return
}
withTestProcess("cgotest", t, func(p *Process, fixture protest.Fixture) {
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") {
return
}
if os.Getenv("CGO_ENABLED") == "" {
return
}
withTestProcess("cgotest", t, func(p *Process, fixture protest.Fixture) {
testGSupportFunc("cgo", t, p, fixture)
......
package proc
import (
"encoding/binary"
"syscall"
"unsafe"
......@@ -49,3 +50,47 @@ func PtracePeekUser(tid int, off uintptr) (uintptr, error) {
}
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 Developer’s 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
}
package proc
import "errors"
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"math"
"os"
"strings"
)
// Registers is an interface for a generic register type. The
// interface encapsulates the generic values / actions
......@@ -13,21 +21,210 @@ type Registers interface {
TLS() uint64
Get(int) (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")
// Registers obtains register values from the debugged process.
func (t *Thread) Registers() (Registers, error) {
return registers(t)
func (t *Thread) Registers(floatingPoint bool) (Registers, error) {
return registers(t, floatingPoint)
}
// PC returns the current PC for this thread.
func (t *Thread) PC() (uint64, error) {
regs, err := t.Registers()
regs, err := t.Registers(false)
if err != nil {
return 0, err
}
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, " "))
}
......@@ -3,9 +3,10 @@ package proc
// #include "threads_darwin.h"
import "C"
import (
"bytes"
"encoding/binary"
"fmt"
"rsc.io/x86/x86asm"
"unsafe"
)
// Regs represents CPU registers on an AMD64 processor.
......@@ -32,10 +33,10 @@ type Regs struct {
fs uint64
gs uint64
gsBase uint64
fpregs []Register
}
func (r *Regs) String() string {
var buf bytes.Buffer
func (r *Regs) Slice() []Register {
var regs = []struct {
k string
v uint64
......@@ -63,10 +64,16 @@ func (r *Regs) String() string {
{"Gs", r.gs},
{"Gs_base", r.gsBase},
}
out := make([]Register, 0, len(regs)+len(r.fpregs))
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
......@@ -259,7 +266,7 @@ func (r *Regs) Get(n int) (uint64, error) {
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 identity C.thread_identifier_info_data_t
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),
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
}
......
package proc
import "fmt"
import "bytes"
import sys "golang.org/x/sys/unix"
import "rsc.io/x86/x86asm"
import (
"fmt"
"rsc.io/x86/x86asm"
sys "golang.org/x/sys/unix"
)
// Regs is a wrapper for sys.PtraceRegs.
type Regs struct {
regs *sys.PtraceRegs
regs *sys.PtraceRegs
fpregs []Register
}
func (r *Regs) String() string {
var buf bytes.Buffer
func (r *Regs) Slice() []Register {
var regs = []struct {
k string
v uint64
......@@ -44,10 +47,16 @@ func (r *Regs) String() string {
{"Fs", r.regs.Fs},
{"Gs", r.regs.Gs},
}
out := make([]Register, 0, len(regs)+len(r.fpregs))
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.
......@@ -235,7 +244,7 @@ func (r *Regs) Get(n int) (uint64, error) {
return 0, UnknownRegisterError
}
func registers(thread *Thread) (Registers, error) {
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
var (
regs sys.PtraceRegs
err error
......@@ -244,5 +253,73 @@ func registers(thread *Thread) (Registers, error) {
if err != nil {
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
}
package proc
import (
"bytes"
"fmt"
"rsc.io/x86/x86asm"
"unsafe"
......@@ -9,32 +8,32 @@ import (
// Regs represents CPU registers on an AMD64 processor.
type Regs struct {
rax uint64
rbx uint64
rcx uint64
rdx uint64
rdi uint64
rsi uint64
rbp uint64
rsp uint64
r8 uint64
r9 uint64
r10 uint64
r11 uint64
r12 uint64
r13 uint64
r14 uint64
r15 uint64
rip uint64
eflags uint64
cs uint64
fs uint64
gs uint64
tls uint64
rax uint64
rbx uint64
rcx uint64
rdx uint64
rdi uint64
rsi uint64
rbp uint64
rsp uint64
r8 uint64
r9 uint64
r10 uint64
r11 uint64
r12 uint64
r13 uint64
r14 uint64
r15 uint64
rip uint64
eflags uint64
cs uint64
fs uint64
gs uint64
tls uint64
fltSave *_XMM_SAVE_AREA32
}
func (r *Regs) String() string {
var buf bytes.Buffer
func (r *Regs) Slice() []Register {
var regs = []struct {
k string
v uint64
......@@ -62,10 +61,38 @@ func (r *Regs) String() string {
{"Gs", r.gs},
{"TLS", r.tls},
}
outlen := len(regs)
if r.fltSave != nil {
outlen += 6 + 8 + 2 + 16
}
out := make([]Register, 0, outlen)
for _, reg := range regs {
fmt.Fprintf(&buf, "%8s = %0#16x\n", reg.k, reg.v)
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 buf.String()
return out
}
// PC returns the current program counter
......@@ -264,7 +291,7 @@ func (r *Regs) Get(n int) (uint64, error) {
return 0, UnknownRegisterError
}
func registers(thread *Thread) (Registers, error) {
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
context := newCONTEXT()
context.ContextFlags = _CONTEXT_ALL
......@@ -303,6 +330,11 @@ func registers(thread *Thread) (Registers, error) {
gs: uint64(context.SegGs),
tls: uint64(threadInfo.TebBaseAddress),
}
if floatingPoint {
regs.fltSave = &context.FltSave
}
return regs, nil
}
......
......@@ -50,7 +50,7 @@ func (t *Thread) ReturnAddress() (uint64, error) {
}
func (t *Thread) stackIterator() (*stackIterator, error) {
regs, err := t.Registers()
regs, err := t.Registers(false)
if err != nil {
return nil, err
}
......
......@@ -22,6 +22,25 @@ type _M128A struct {
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 {
P1Home uint64
P2Home uint64
......@@ -67,7 +86,7 @@ type _CONTEXT struct {
Rip uint64
FltSave [512]byte
FltSave _XMM_SAVE_AREA32
VectorRegister [26]_M128A
VectorControl uint64
......
......@@ -46,7 +46,13 @@ func BuildFixture(name string) Fixture {
// Make a (good enough) random temporary file name
r := make([]byte, 4)
rand.Read(r)
dir := fixturesDir
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)))
buildFlags := []string{"build"}
......@@ -54,10 +60,13 @@ func BuildFixture(name string) Fixture {
// Work-around for https://github.com/golang/go/issues/13154
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.Dir = fixturesDir
cmd.Dir = dir
// Build the test binary
if err := cmd.Run(); err != nil {
......
......@@ -316,7 +316,7 @@ func (dbp *Process) setInternalBreakpoints(curpc uint64, pcs []uint64, kind Brea
// SetPC sets the PC for this thread.
func (thread *Thread) SetPC(pc uint64) error {
regs, err := thread.Registers()
regs, err := thread.Registers(false)
if err != nil {
return err
}
......@@ -324,7 +324,7 @@ func (thread *Thread) SetPC(pc uint64) error {
}
func (thread *Thread) getGVariable() (*Variable, error) {
regs, err := thread.Registers()
regs, err := thread.Registers(false)
if err != nil {
return nil, err
}
......
......@@ -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);
}
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
get_identity(mach_port_name_t task, thread_identifier_info_data_t *idinfo) {
mach_msg_type_number_t idinfoCount = THREAD_IDENTIFIER_INFO_COUNT;
......
......@@ -13,6 +13,9 @@ read_memory(task_t, mach_vm_address_t, void *, mach_msg_type_number_t);
kern_return_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
set_pc(thread_act_t, uint64_t);
......
......@@ -82,7 +82,7 @@ func (t *Thread) saveRegisters() (Registers, error) {
if err != nil {
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) {
......
......@@ -9,8 +9,8 @@ import (
"reflect"
"strconv"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/proc"
"golang.org/x/debug/dwarf"
)
// ConvertBreakpoint converts from a proc.Breakpoint to
......@@ -199,7 +199,7 @@ func ConvertGoroutine(g *proc.G) *Goroutine {
CurrentLoc: ConvertLocation(g.CurrentLoc),
UserCurrentLoc: ConvertLocation(g.UserCurrent()),
GoStatementLoc: ConvertLocation(g.Go()),
ThreadID: tid,
ThreadID: tid,
}
}
......@@ -254,3 +254,11 @@ func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig {
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
}
package api
import (
"bytes"
"errors"
"fmt"
"reflect"
......@@ -290,3 +291,25 @@ type SetAPIVersionIn 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()
}
......@@ -79,7 +79,7 @@ type Client interface {
// ListFunctionArgs lists all arguments to the current function.
ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error)
// ListRegisters lists registers and their values.
ListRegisters() (string, error)
ListRegisters(threadID int, includeFp bool) (api.Registers, error)
// ListGoroutines lists all goroutines.
ListGoroutines() ([]*api.Goroutine, error)
......
......@@ -602,19 +602,19 @@ func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadCo
}
// 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()
defer d.processMutex.Unlock()
thread, found := d.process.Threads[threadID]
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 {
return "", err
return nil, err
}
return regs.String(), err
return api.ConvertRegisters(regs.Slice()), err
}
func convertVars(pv []*proc.Variable) []api.Variable {
......
......@@ -199,11 +199,11 @@ func (s *RPCServer) ListRegisters(arg interface{}, registers *string) error {
return err
}
regs, err := s.debugger.Registers(state.CurrentThread.ID)
regs, err := s.debugger.Registers(state.CurrentThread.ID, false)
if err != nil {
return err
}
*registers = regs
*registers = regs.String()
return nil
}
......
......@@ -105,7 +105,7 @@ func (c *RPCClient) Step() (*api.DebuggerState, error) {
func (c *RPCClient) StepOut() (*api.DebuggerState, error) {
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
}
......@@ -241,10 +241,10 @@ func (c *RPCClient) ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig)
return out.Variables, err
}
func (c *RPCClient) ListRegisters() (string, error) {
func (c *RPCClient) ListRegisters(threadID int, includeFp bool) (api.Registers, error) {
out := new(ListRegistersOut)
err := c.call("ListRegisters", ListRegistersIn{}, out)
return out.Registers, err
err := c.call("ListRegisters", ListRegistersIn{ThreadID: threadID, IncludeFp: includeFp}, out)
return out.Regs, err
}
func (c *RPCClient) ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {
......
......@@ -312,24 +312,32 @@ func (s *RPCServer) ListPackageVars(arg ListPackageVarsIn, out *ListPackageVarsO
}
type ListRegistersIn struct {
ThreadID int
IncludeFp bool
}
type ListRegistersOut struct {
Registers string
Regs api.Registers
}
// ListRegisters lists registers and their values.
func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) error {
state, err := s.debugger.State()
if err != nil {
return err
if arg.ThreadID == 0 {
state, err := s.debugger.State()
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 {
return err
}
out.Registers = regs
out.Regs = regs
out.Registers = out.Regs.String()
return nil
}
......
......@@ -457,11 +457,11 @@ func TestClientServer_infoArgs(t *testing.T) {
if state.Err != nil {
t.Fatalf("Unexpected error: %v, state: %#v", state.Err, state)
}
regs, err := c.ListRegisters()
regs, err := c.ListRegisters(0, false)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if regs == "" {
if len(regs) == 0 {
t.Fatal("Expected string showing registers values, got empty string")
}
locals, err := c.ListFunctionArgs(api.EvalScope{-1, 0}, normalLoadConfig)
......@@ -853,7 +853,7 @@ func TestIssue355(t *testing.T) {
assertError(err, t, "ListLocalVariables()")
_, err = c.ListFunctionArgs(api.EvalScope{gid, 0}, normalLoadConfig)
assertError(err, t, "ListFunctionArgs()")
_, err = c.ListRegisters()
_, err = c.ListRegisters(0, false)
assertError(err, t, "ListRegisters()")
_, err = c.ListGoroutines()
assertError(err, t, "ListGoroutines()")
......@@ -1149,3 +1149,47 @@ func TestClientServer_Issue528(t *testing.T) {
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)
}
}
})
}
......@@ -176,7 +176,11 @@ If regex is specified only local variables with a name matching it will be retur
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.`},
{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{"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 {
regs, err := t.client.ListRegisters()
includeFp := false
if args == "-a" {
includeFp = true
}
regs, err := t.client.ListRegisters(0, includeFp)
if err != nil {
return err
}
......
package terminal
import (
"testing"
"runtime"
"testing"
"github.com/derekparker/delve/config"
)
type tRule struct {
from string
to string
to string
}
type tCase struct {
......@@ -66,9 +66,9 @@ func platformCases() []tCase {
}
func TestSubstitutePath(t *testing.T) {
for _, c := range(platformCases()) {
for _, c := range platformCases() {
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})
}
res := New(nil, &config.Config{SubstitutePath: subRules}).substitutePath(c.path)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册