mirror of
https://github.com/go-delve/delve.git
synced 2025-10-27 20:23:41 +08:00
proc: load more registers
Adds ability to load x87, SSE and AVX registers. Fixes #666
This commit is contained in:
@ -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.
|
||||
|
||||
|
||||
|
||||
19
_fixtures/fputest/fputest.go
Normal file
19
_fixtures/fputest/fputest.go
Normal 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()
|
||||
}
|
||||
56
_fixtures/fputest/fputest_amd64.s
Normal file
56
_fixtures/fputest/fputest_amd64.s
Normal 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
|
||||
@ -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)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
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(®set.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
|
||||
}
|
||||
|
||||
@ -1,6 +1,14 @@
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
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{®s}, nil
|
||||
r := &Regs{®s, 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
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
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},
|
||||
}
|
||||
for _, reg := range regs {
|
||||
fmt.Fprintf(&buf, "%8s = %0#16x\n", reg.k, reg.v)
|
||||
outlen := len(regs)
|
||||
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
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
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
|
||||
}
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user