proc,service: allow printing registers for arbitrary frames (#1875)
Adds an optional scope prefix to the `regs` command which allows printing registers for any stack frame (as long as they were somehow saved). Issue #1838 is not yet to be closed since we are still not recovering the registers of a segfaulting frame. Updates #1838
This commit is contained in:
parent
186786235f
commit
d925f6b719
@ -45,7 +45,7 @@ goroutines(Start, Count) | Equivalent to API call [ListGoroutines](https://godoc
|
|||||||
local_vars(Scope, Cfg) | Equivalent to API call [ListLocalVars](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListLocalVars)
|
local_vars(Scope, Cfg) | Equivalent to API call [ListLocalVars](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListLocalVars)
|
||||||
package_vars(Filter, Cfg) | Equivalent to API call [ListPackageVars](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackageVars)
|
package_vars(Filter, Cfg) | Equivalent to API call [ListPackageVars](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackageVars)
|
||||||
packages_build_info(IncludeFiles) | Equivalent to API call [ListPackagesBuildInfo](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackagesBuildInfo)
|
packages_build_info(IncludeFiles) | Equivalent to API call [ListPackagesBuildInfo](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackagesBuildInfo)
|
||||||
registers(ThreadID, IncludeFp) | Equivalent to API call [ListRegisters](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListRegisters)
|
registers(ThreadID, IncludeFp, Scope) | Equivalent to API call [ListRegisters](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListRegisters)
|
||||||
sources(Filter) | Equivalent to API call [ListSources](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListSources)
|
sources(Filter) | Equivalent to API call [ListSources](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListSources)
|
||||||
threads() | Equivalent to API call [ListThreads](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListThreads)
|
threads() | Equivalent to API call [ListThreads](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListThreads)
|
||||||
types(Filter) | Equivalent to API call [ListTypes](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListTypes)
|
types(Filter) | Equivalent to API call [ListTypes](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListTypes)
|
||||||
|
@ -426,30 +426,34 @@ func (a *AMD64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AMD64) DwarfRegisterToString(name string, reg *op.DwarfRegister) string {
|
func (a *AMD64) DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) {
|
||||||
name = strings.ToLower(name)
|
name, ok := amd64DwarfToName[i]
|
||||||
switch name {
|
if !ok {
|
||||||
|
name = fmt.Sprintf("unknown%d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch n := strings.ToLower(name); n {
|
||||||
case "rflags":
|
case "rflags":
|
||||||
return eflagsDescription.Describe(reg.Uint64Val, 64)
|
return name, false, eflagsDescription.Describe(reg.Uint64Val, 64)
|
||||||
|
|
||||||
case "cw", "sw", "tw", "fop":
|
case "cw", "sw", "tw", "fop":
|
||||||
return fmt.Sprintf("%#04x", reg.Uint64Val)
|
return name, true, fmt.Sprintf("%#04x", reg.Uint64Val)
|
||||||
|
|
||||||
case "mxcsr_mask":
|
case "mxcsr_mask":
|
||||||
return fmt.Sprintf("%#08x", reg.Uint64Val)
|
return name, true, fmt.Sprintf("%#08x", reg.Uint64Val)
|
||||||
|
|
||||||
case "mxcsr":
|
case "mxcsr":
|
||||||
return mxcsrDescription.Describe(reg.Uint64Val, 32)
|
return name, true, mxcsrDescription.Describe(reg.Uint64Val, 32)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if reg.Bytes != nil && strings.HasPrefix(name, "xmm") {
|
if reg.Bytes != nil && strings.HasPrefix(n, "xmm") {
|
||||||
return formatSSEReg(reg.Bytes)
|
return name, true, formatSSEReg(reg.Bytes)
|
||||||
} else if reg.Bytes != nil && strings.HasPrefix(name, "st(") {
|
} else if reg.Bytes != nil && strings.HasPrefix(n, "st(") {
|
||||||
return formatX87Reg(reg.Bytes)
|
return name, true, formatX87Reg(reg.Bytes)
|
||||||
} else if reg.Bytes == nil || (reg.Bytes != nil && len(reg.Bytes) <= 8) {
|
} else if reg.Bytes == nil || (reg.Bytes != nil && len(reg.Bytes) <= 8) {
|
||||||
return fmt.Sprintf("%#016x", reg.Uint64Val)
|
return name, false, fmt.Sprintf("%#016x", reg.Uint64Val)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("%#x", reg.Bytes)
|
return name, false, fmt.Sprintf("%#x", reg.Bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ type Arch interface {
|
|||||||
RegSize(uint64) int
|
RegSize(uint64) int
|
||||||
RegistersToDwarfRegisters(uint64, Registers) op.DwarfRegisters
|
RegistersToDwarfRegisters(uint64, Registers) op.DwarfRegisters
|
||||||
AddrAndStackRegsToDwarfRegisters(uint64, uint64, uint64, uint64, uint64) op.DwarfRegisters
|
AddrAndStackRegsToDwarfRegisters(uint64, uint64, uint64, uint64, uint64) op.DwarfRegisters
|
||||||
DwarfRegisterToString(string, *op.DwarfRegister) string
|
DwarfRegisterToString(int, *op.DwarfRegister) (string, bool, string)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -411,8 +411,22 @@ func (a *ARM64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ARM64) DwarfRegisterToString(name string, reg *op.DwarfRegister) string {
|
func (a *ARM64) DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) {
|
||||||
if reg.Bytes != nil && (name[0] == 'v' || name[0] == 'V') {
|
// see arm64DwarfToHardware table for explanation
|
||||||
|
switch {
|
||||||
|
case i <= 30:
|
||||||
|
name = fmt.Sprintf("X%d", i)
|
||||||
|
case i == 31:
|
||||||
|
name = "SP"
|
||||||
|
case i == 32:
|
||||||
|
name = "PC"
|
||||||
|
case i >= 64 && i <= 95:
|
||||||
|
name = fmt.Sprintf("V%d", i-64)
|
||||||
|
default:
|
||||||
|
name = fmt.Sprintf("unknown%d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if reg.Bytes != nil && name[0] == 'V' {
|
||||||
buf := bytes.NewReader(reg.Bytes)
|
buf := bytes.NewReader(reg.Bytes)
|
||||||
|
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
@ -445,9 +459,9 @@ func (a *ARM64) DwarfRegisterToString(name string, reg *op.DwarfRegister) string
|
|||||||
}
|
}
|
||||||
fmt.Fprintf(&out, "\tv4_float={ %g %g %g %g }", v4[0], v4[1], v4[2], v4[3])
|
fmt.Fprintf(&out, "\tv4_float={ %g %g %g %g }", v4[0], v4[1], v4[2], v4[3])
|
||||||
|
|
||||||
return out.String()
|
return name, true, out.String()
|
||||||
} else if reg.Bytes == nil || (reg.Bytes != nil && len(reg.Bytes) < 16) {
|
} else if reg.Bytes == nil || (reg.Bytes != nil && len(reg.Bytes) < 16) {
|
||||||
return fmt.Sprintf("%#016x", reg.Uint64Val)
|
return name, false, fmt.Sprintf("%#016x", reg.Uint64Val)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%#x", reg.Bytes)
|
return name, false, fmt.Sprintf("%#x", reg.Bytes)
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,17 @@ func withCoreFile(t *testing.T, name, args string) *proc.Target {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func logRegisters(t *testing.T, regs proc.Registers, arch proc.Arch) {
|
||||||
|
dregs := arch.RegistersToDwarfRegisters(0, regs)
|
||||||
|
for i, reg := range dregs.Regs {
|
||||||
|
if reg == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, _, value := arch.DwarfRegisterToString(i, reg)
|
||||||
|
t.Logf("%s = %s", name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCore(t *testing.T) {
|
func TestCore(t *testing.T) {
|
||||||
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
|
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
|
||||||
return
|
return
|
||||||
@ -243,11 +254,7 @@ func TestCore(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Couldn't get current thread registers: %v", err)
|
t.Fatalf("Couldn't get current thread registers: %v", err)
|
||||||
}
|
}
|
||||||
regslice := regs.Slice(true)
|
logRegisters(t, regs, p.BinInfo().Arch)
|
||||||
arch := p.BinInfo().Arch
|
|
||||||
for _, reg := range regslice {
|
|
||||||
t.Logf("%s = %s", reg.Name, arch.DwarfRegisterToString(reg.Name, reg.Reg))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCoreFpRegisters(t *testing.T) {
|
func TestCoreFpRegisters(t *testing.T) {
|
||||||
@ -314,18 +321,17 @@ func TestCoreFpRegisters(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
arch := p.BinInfo().Arch
|
arch := p.BinInfo().Arch
|
||||||
for _, reg := range regs.Slice(true) {
|
logRegisters(t, regs, arch)
|
||||||
t.Logf("%s = %s", reg.Name, arch.DwarfRegisterToString(reg.Name, reg.Reg))
|
dregs := arch.RegistersToDwarfRegisters(0, regs)
|
||||||
}
|
|
||||||
|
|
||||||
for _, regtest := range regtests {
|
for _, regtest := range regtests {
|
||||||
found := false
|
found := false
|
||||||
for _, reg := range regs.Slice(true) {
|
for i, reg := range dregs.Regs {
|
||||||
if reg.Name == regtest.name {
|
regname, _, regval := arch.DwarfRegisterToString(i, reg)
|
||||||
|
if reg != nil && regname == regtest.name {
|
||||||
found = true
|
found = true
|
||||||
regval := arch.DwarfRegisterToString(reg.Name, reg.Reg)
|
|
||||||
if !strings.HasPrefix(regval, regtest.value) {
|
if !strings.HasPrefix(regval, regtest.value) {
|
||||||
t.Fatalf("register %s expected %q got %q", reg.Name, regtest.value, regval)
|
t.Fatalf("register %s expected %q got %q", regname, regtest.value, regval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1638,7 +1638,13 @@ func regs(t *Term, ctx callContext, args string) error {
|
|||||||
if args == "-a" {
|
if args == "-a" {
|
||||||
includeFp = true
|
includeFp = true
|
||||||
}
|
}
|
||||||
regs, err := t.client.ListRegisters(0, includeFp)
|
var regs api.Registers
|
||||||
|
var err error
|
||||||
|
if ctx.Scope.GoroutineID < 0 && ctx.Scope.Frame == 0 {
|
||||||
|
regs, err = t.client.ListThreadRegisters(0, includeFp)
|
||||||
|
} else {
|
||||||
|
regs, err = t.client.ListScopeRegisters(ctx.Scope, includeFp)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -967,7 +967,7 @@ func TestIssue1493(t *testing.T) {
|
|||||||
ra := term.MustExec("regs -a")
|
ra := term.MustExec("regs -a")
|
||||||
nra := len(strings.Split(ra, "\n"))
|
nra := len(strings.Split(ra, "\n"))
|
||||||
t.Logf("regs -a: %s", ra)
|
t.Logf("regs -a: %s", ra)
|
||||||
if nr > nra/2 {
|
if nr > nra/2+1 {
|
||||||
t.Fatalf("'regs' returned too many registers (%d) compared to 'regs -a' (%d)", nr, nra)
|
t.Fatalf("'regs' returned too many registers (%d) compared to 'regs -a' (%d)", nr, nra)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -934,6 +934,15 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
|
|||||||
return starlark.None, decorateError(thread, err)
|
return starlark.None, decorateError(thread, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(args) > 2 && args[2] != starlark.None {
|
||||||
|
err := unmarshalStarlarkValue(args[2], &rpcArgs.Scope, "Scope")
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scope := env.ctx.Scope()
|
||||||
|
rpcArgs.Scope = &scope
|
||||||
|
}
|
||||||
for _, kv := range kwargs {
|
for _, kv := range kwargs {
|
||||||
var err error
|
var err error
|
||||||
switch kv[0].(starlark.String) {
|
switch kv[0].(starlark.String) {
|
||||||
@ -941,6 +950,8 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
|
|||||||
err = unmarshalStarlarkValue(kv[1], &rpcArgs.ThreadID, "ThreadID")
|
err = unmarshalStarlarkValue(kv[1], &rpcArgs.ThreadID, "ThreadID")
|
||||||
case "IncludeFp":
|
case "IncludeFp":
|
||||||
err = unmarshalStarlarkValue(kv[1], &rpcArgs.IncludeFp, "IncludeFp")
|
err = unmarshalStarlarkValue(kv[1], &rpcArgs.IncludeFp, "IncludeFp")
|
||||||
|
case "Scope":
|
||||||
|
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Scope, "Scope")
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unknown argument %q", kv[0])
|
err = fmt.Errorf("unknown argument %q", kv[0])
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/go-delve/delve/pkg/dwarf/godwarf"
|
"github.com/go-delve/delve/pkg/dwarf/godwarf"
|
||||||
|
"github.com/go-delve/delve/pkg/dwarf/op"
|
||||||
"github.com/go-delve/delve/pkg/proc"
|
"github.com/go-delve/delve/pkg/proc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -328,10 +329,18 @@ func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ConvertRegisters converts proc.Register to api.Register for a slice.
|
// ConvertRegisters converts proc.Register to api.Register for a slice.
|
||||||
func ConvertRegisters(in []proc.Register, arch proc.Arch) (out []Register) {
|
func ConvertRegisters(in op.DwarfRegisters, arch proc.Arch, floatingPoint bool) (out []Register) {
|
||||||
out = make([]Register, len(in))
|
out = make([]Register, 0, len(in.Regs))
|
||||||
for i := range in {
|
for i := range in.Regs {
|
||||||
out[i] = Register{in[i].Name, arch.DwarfRegisterToString(in[i].Name, in[i].Reg)}
|
reg := in.Reg(uint64(i))
|
||||||
|
if reg == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, fp, repr := arch.DwarfRegisterToString(i, reg)
|
||||||
|
if !floatingPoint && fp {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, Register{name, repr, i})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -442,8 +442,9 @@ type SetAPIVersionOut struct {
|
|||||||
|
|
||||||
// Register holds information on a CPU register.
|
// Register holds information on a CPU register.
|
||||||
type Register struct {
|
type Register struct {
|
||||||
Name string
|
Name string
|
||||||
Value string
|
Value string
|
||||||
|
DwarfNumber int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registers is a list of CPU registers.
|
// Registers is a list of CPU registers.
|
||||||
|
@ -93,8 +93,10 @@ type Client interface {
|
|||||||
ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error)
|
ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error)
|
||||||
// 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.
|
// ListThreadRegisters lists registers and their values, for the given thread.
|
||||||
ListRegisters(threadID int, includeFp bool) (api.Registers, error)
|
ListThreadRegisters(threadID int, includeFp bool) (api.Registers, error)
|
||||||
|
// ListScopeRegisters lists registers and their values, for the given scope.
|
||||||
|
ListScopeRegisters(scope api.EvalScope, includeFp bool) (api.Registers, error)
|
||||||
|
|
||||||
// ListGoroutines lists all goroutines.
|
// ListGoroutines lists all goroutines.
|
||||||
ListGoroutines(start, count int) ([]*api.Goroutine, int, error)
|
ListGoroutines(start, count int) ([]*api.Goroutine, int, error)
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-delve/delve/pkg/dwarf/op"
|
||||||
"github.com/go-delve/delve/pkg/goversion"
|
"github.com/go-delve/delve/pkg/goversion"
|
||||||
"github.com/go-delve/delve/pkg/logflags"
|
"github.com/go-delve/delve/pkg/logflags"
|
||||||
"github.com/go-delve/delve/pkg/proc"
|
"github.com/go-delve/delve/pkg/proc"
|
||||||
@ -949,19 +950,65 @@ 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, floatingPoint bool) (api.Registers, error) {
|
func (d *Debugger) Registers(threadID int, scope *api.EvalScope, floatingPoint bool) (api.Registers, error) {
|
||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
thread, found := d.target.FindThread(threadID)
|
var dregs op.DwarfRegisters
|
||||||
if !found {
|
|
||||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
if scope != nil {
|
||||||
|
s, err := proc.ConvertEvalScope(d.target, scope.GoroutineID, scope.Frame, scope.DeferredCall)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dregs = s.Regs
|
||||||
|
} else {
|
||||||
|
thread, found := d.target.FindThread(threadID)
|
||||||
|
if !found {
|
||||||
|
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||||
|
}
|
||||||
|
regs, err := thread.Registers(floatingPoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dregs = d.target.BinInfo().Arch.RegistersToDwarfRegisters(0, regs)
|
||||||
}
|
}
|
||||||
regs, err := thread.Registers(floatingPoint)
|
r := api.ConvertRegisters(dregs, d.target.BinInfo().Arch, floatingPoint)
|
||||||
if err != nil {
|
// Sort the registers in a canonical order we prefer, this is mostly
|
||||||
return nil, err
|
// because the DWARF register numbering for AMD64 is weird.
|
||||||
}
|
sort.Slice(r, func(i, j int) bool {
|
||||||
return api.ConvertRegisters(regs.Slice(floatingPoint), d.target.BinInfo().Arch), err
|
a, b := r[i], r[j]
|
||||||
|
an, aok := canonicalRegisterOrder[strings.ToLower(a.Name)]
|
||||||
|
bn, bok := canonicalRegisterOrder[strings.ToLower(b.Name)]
|
||||||
|
// Registers that don't appear in canonicalRegisterOrder sort after registers that do.
|
||||||
|
if !aok {
|
||||||
|
an = 1000
|
||||||
|
}
|
||||||
|
if !bok {
|
||||||
|
bn = 1000
|
||||||
|
}
|
||||||
|
if an == bn {
|
||||||
|
// keep registers that don't appear in canonicalRegisterOrder in DWARF order
|
||||||
|
return a.DwarfNumber < b.DwarfNumber
|
||||||
|
}
|
||||||
|
return an < bn
|
||||||
|
|
||||||
|
})
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var canonicalRegisterOrder = map[string]int{
|
||||||
|
// amd64
|
||||||
|
"rip": 0,
|
||||||
|
"rsp": 1,
|
||||||
|
"rax": 2,
|
||||||
|
"rbx": 3,
|
||||||
|
"rcx": 4,
|
||||||
|
"rdx": 5,
|
||||||
|
|
||||||
|
// arm64
|
||||||
|
"pc": 0,
|
||||||
|
"sp": 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertVars(pv []*proc.Variable) []api.Variable {
|
func convertVars(pv []*proc.Variable) []api.Variable {
|
||||||
|
@ -204,7 +204,7 @@ func (s *RPCServer) ListRegisters(arg interface{}, registers *string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
regs, err := s.debugger.Registers(state.CurrentThread.ID, false)
|
regs, err := s.debugger.Registers(state.CurrentThread.ID, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -292,9 +292,15 @@ func (c *RPCClient) ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig)
|
|||||||
return out.Variables, err
|
return out.Variables, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RPCClient) ListRegisters(threadID int, includeFp bool) (api.Registers, error) {
|
func (c *RPCClient) ListThreadRegisters(threadID int, includeFp bool) (api.Registers, error) {
|
||||||
out := new(ListRegistersOut)
|
out := new(ListRegistersOut)
|
||||||
err := c.call("ListRegisters", ListRegistersIn{ThreadID: threadID, IncludeFp: includeFp}, out)
|
err := c.call("ListRegisters", ListRegistersIn{ThreadID: threadID, IncludeFp: includeFp, Scope: nil}, out)
|
||||||
|
return out.Regs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RPCClient) ListScopeRegisters(scope api.EvalScope, includeFp bool) (api.Registers, error) {
|
||||||
|
out := new(ListRegistersOut)
|
||||||
|
err := c.call("ListRegisters", ListRegistersIn{ThreadID: 0, IncludeFp: includeFp, Scope: &scope}, out)
|
||||||
return out.Regs, err
|
return out.Regs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,6 +368,7 @@ func (s *RPCServer) ListPackageVars(arg ListPackageVarsIn, out *ListPackageVarsO
|
|||||||
type ListRegistersIn struct {
|
type ListRegistersIn struct {
|
||||||
ThreadID int
|
ThreadID int
|
||||||
IncludeFp bool
|
IncludeFp bool
|
||||||
|
Scope *api.EvalScope
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListRegistersOut struct {
|
type ListRegistersOut struct {
|
||||||
@ -376,8 +377,10 @@ type ListRegistersOut struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListRegisters lists registers and their values.
|
// ListRegisters lists registers and their values.
|
||||||
|
// If ListRegistersIn.Scope is not nil the registers of that eval scope will
|
||||||
|
// be returned, otherwise ListRegistersIn.ThreadID will be used.
|
||||||
func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) error {
|
func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) error {
|
||||||
if arg.ThreadID == 0 {
|
if arg.ThreadID == 0 && arg.Scope == nil {
|
||||||
state, err := s.debugger.State(false)
|
state, err := s.debugger.State(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -385,7 +388,7 @@ func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) er
|
|||||||
arg.ThreadID = state.CurrentThread.ID
|
arg.ThreadID = state.CurrentThread.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
regs, err := s.debugger.Registers(arg.ThreadID, arg.IncludeFp)
|
regs, err := s.debugger.Registers(arg.ThreadID, arg.Scope, arg.IncludeFp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -510,13 +510,28 @@ 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(0, false)
|
regs, err := c.ListThreadRegisters(0, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if len(regs) == 0 {
|
if len(regs) == 0 {
|
||||||
t.Fatal("Expected string showing registers values, got empty string")
|
t.Fatal("Expected string showing registers values, got empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regs, err = c.ListScopeRegisters(api.EvalScope{GoroutineID: -1, Frame: 0}, false)
|
||||||
|
assertNoError(err, t, "ListScopeRegisters(-1, 0)")
|
||||||
|
if len(regs) == 0 {
|
||||||
|
t.Fatal("Expected string showing registers values, got empty string")
|
||||||
|
}
|
||||||
|
t.Logf("GoroutineID: -1, Frame: 0\n%s", regs.String())
|
||||||
|
|
||||||
|
regs, err = c.ListScopeRegisters(api.EvalScope{GoroutineID: -1, Frame: 1}, false)
|
||||||
|
assertNoError(err, t, "ListScopeRegisters(-1, 1)")
|
||||||
|
if len(regs) == 0 {
|
||||||
|
t.Fatal("Expected string showing registers values, got empty string")
|
||||||
|
}
|
||||||
|
t.Logf("GoroutineID: -1, Frame: 1\n%s", regs.String())
|
||||||
|
|
||||||
locals, err := c.ListFunctionArgs(api.EvalScope{-1, 0, 0}, normalLoadConfig)
|
locals, err := c.ListFunctionArgs(api.EvalScope{-1, 0, 0}, normalLoadConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
@ -925,8 +940,10 @@ func TestIssue355(t *testing.T) {
|
|||||||
assertError(err, t, "ListLocalVariables()")
|
assertError(err, t, "ListLocalVariables()")
|
||||||
_, err = c.ListFunctionArgs(api.EvalScope{gid, 0, 0}, normalLoadConfig)
|
_, err = c.ListFunctionArgs(api.EvalScope{gid, 0, 0}, normalLoadConfig)
|
||||||
assertError(err, t, "ListFunctionArgs()")
|
assertError(err, t, "ListFunctionArgs()")
|
||||||
_, err = c.ListRegisters(0, false)
|
_, err = c.ListThreadRegisters(0, false)
|
||||||
assertError(err, t, "ListRegisters()")
|
assertError(err, t, "ListThreadRegisters()")
|
||||||
|
_, err = c.ListScopeRegisters(api.EvalScope{gid, 0, 0}, false)
|
||||||
|
assertError(err, t, "ListScopeRegisters()")
|
||||||
_, _, err = c.ListGoroutines(0, 0)
|
_, _, err = c.ListGoroutines(0, 0)
|
||||||
assertError(err, t, "ListGoroutines()")
|
assertError(err, t, "ListGoroutines()")
|
||||||
_, err = c.Stacktrace(gid, 10, 0, &normalLoadConfig)
|
_, err = c.Stacktrace(gid, 10, 0, &normalLoadConfig)
|
||||||
@ -1279,8 +1296,8 @@ func TestClientServer_FpRegisters(t *testing.T) {
|
|||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestClient2("fputest/", t, func(c service.Client) {
|
withTestClient2("fputest/", t, func(c service.Client) {
|
||||||
<-c.Continue()
|
<-c.Continue()
|
||||||
regs, err := c.ListRegisters(0, true)
|
regs, err := c.ListThreadRegisters(0, true)
|
||||||
assertNoError(err, t, "ListRegisters()")
|
assertNoError(err, t, "ListThreadRegisters()")
|
||||||
|
|
||||||
t.Logf("%s", regs.String())
|
t.Logf("%s", regs.String())
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user