delve/pkg/proc/linutil/regs_arm64_arch.go
Alessandro Arzilli 200994bc8f
proc/*: only load floating point registers when needed (#1981)
Changes implementations of proc.Registers interface and the
op.DwarfRegisters struct so that floating point registers can be loaded
only when they are needed.
Removes the floatingPoint parameter from proc.Thread.Registers.
This accomplishes three things:

1. it simplifies the proc.Thread.Registers interface
2. it makes it impossible to accidentally create a broken set of saved
   registers or of op.DwarfRegisters by accidentally calling
   Registers(false)
3. it improves general performance of Delve by avoiding to load
   floating point registers as much as possible

Floating point registers are loaded under two circumstances:

1. When the Slice method is called with floatingPoint == true
2. When the Copy method is called

Benchmark before:

BenchmarkConditionalBreakpoints-4   	       1	4327350142 ns/op

Benchmark after:

BenchmarkConditionalBreakpoints-4   	       1	3852642917 ns/op

Updates #1549
2020-05-13 11:56:50 -07:00

167 lines
4.1 KiB
Go

package linutil
import (
"fmt"
"golang.org/x/arch/arm64/arm64asm"
"github.com/go-delve/delve/pkg/proc"
)
// Regs is a wrapper for sys.PtraceRegs.
type ARM64Registers struct {
Regs *ARM64PtraceRegs //general-purpose registers
Fpregs []proc.Register //Formatted floating point registers
Fpregset []byte //holding all floating point register values
loadFpRegs func(*ARM64Registers) error
}
func NewARM64Registers(regs *ARM64PtraceRegs, loadFpRegs func(*ARM64Registers) error) *ARM64Registers {
return &ARM64Registers{Regs: regs, loadFpRegs: loadFpRegs}
}
// ARM64PtraceRegs is the struct used by the linux kernel to return the
// general purpose registers for ARM64 CPUs.
// copy from sys/unix/ztypes_linux_arm64.go:735
type ARM64PtraceRegs struct {
Regs [31]uint64
Sp uint64
Pc uint64
Pstate uint64
}
// Slice returns the registers as a list of (name, value) pairs.
func (r *ARM64Registers) Slice(floatingPoint bool) ([]proc.Register, error) {
var regs64 = []struct {
k string
v uint64
}{
{"X0", r.Regs.Regs[0]},
{"X1", r.Regs.Regs[1]},
{"X2", r.Regs.Regs[2]},
{"X3", r.Regs.Regs[3]},
{"X4", r.Regs.Regs[4]},
{"X5", r.Regs.Regs[5]},
{"X6", r.Regs.Regs[6]},
{"X7", r.Regs.Regs[7]},
{"X8", r.Regs.Regs[8]},
{"X9", r.Regs.Regs[9]},
{"X10", r.Regs.Regs[10]},
{"X11", r.Regs.Regs[11]},
{"X12", r.Regs.Regs[12]},
{"X13", r.Regs.Regs[13]},
{"X14", r.Regs.Regs[14]},
{"X15", r.Regs.Regs[15]},
{"X16", r.Regs.Regs[16]},
{"X17", r.Regs.Regs[17]},
{"X18", r.Regs.Regs[18]},
{"X19", r.Regs.Regs[19]},
{"X20", r.Regs.Regs[20]},
{"X21", r.Regs.Regs[21]},
{"X22", r.Regs.Regs[22]},
{"X23", r.Regs.Regs[23]},
{"X24", r.Regs.Regs[24]},
{"X25", r.Regs.Regs[25]},
{"X26", r.Regs.Regs[26]},
{"X27", r.Regs.Regs[27]},
{"X28", r.Regs.Regs[28]},
{"X29", r.Regs.Regs[29]},
{"X30", r.Regs.Regs[30]},
{"SP", r.Regs.Sp},
{"PC", r.Regs.Pc},
{"PSTATE", r.Regs.Pstate},
}
out := make([]proc.Register, 0, len(regs64)+len(r.Fpregs))
for _, reg := range regs64 {
out = proc.AppendUint64Register(out, reg.k, reg.v)
}
var floatLoadError error
if floatingPoint {
if r.loadFpRegs != nil {
floatLoadError = r.loadFpRegs(r)
r.loadFpRegs = nil
}
out = append(out, r.Fpregs...)
}
return out, floatLoadError
}
// PC returns the value of RIP register.
func (r *ARM64Registers) PC() uint64 {
return r.Regs.Pc
}
// SP returns the value of RSP register.
func (r *ARM64Registers) SP() uint64 {
return r.Regs.Sp
}
func (r *ARM64Registers) BP() uint64 {
return r.Regs.Regs[29]
}
// TLS returns the address of the thread local storage memory segment.
func (r *ARM64Registers) TLS() uint64 {
return 0
}
// GAddr returns the address of the G variable if it is known, 0 and false
// otherwise.
func (r *ARM64Registers) GAddr() (uint64, bool) {
return r.Regs.Regs[28], true
}
// Get returns the value of the n-th register (in arm64asm order).
func (r *ARM64Registers) Get(n int) (uint64, error) {
reg := arm64asm.Reg(n)
if reg >= arm64asm.X0 && reg <= arm64asm.X30 {
return r.Regs.Regs[reg-arm64asm.X0], nil
}
return 0, proc.ErrUnknownRegister
}
// Copy returns a copy of these registers that is guarenteed not to change.
func (r *ARM64Registers) Copy() (proc.Registers, error) {
if r.loadFpRegs != nil {
err := r.loadFpRegs(r)
r.loadFpRegs = nil
if err != nil {
return nil, err
}
}
var rr ARM64Registers
rr.Regs = &ARM64PtraceRegs{}
*(rr.Regs) = *(r.Regs)
if r.Fpregs != nil {
rr.Fpregs = make([]proc.Register, len(r.Fpregs))
copy(rr.Fpregs, r.Fpregs)
}
if r.Fpregset != nil {
rr.Fpregset = make([]byte, len(r.Fpregset))
copy(rr.Fpregset, r.Fpregset)
}
return &rr, nil
}
type ARM64PtraceFpRegs struct {
Vregs []byte
Fpsr uint32
Fpcr uint32
}
const _ARM_FP_REGS_LENGTH = 512
func (fpregs *ARM64PtraceFpRegs) Decode() (regs []proc.Register) {
for i := 0; i < len(fpregs.Vregs); i += 16 {
regs = proc.AppendBytesRegister(regs, fmt.Sprintf("V%d", i/16), fpregs.Vregs[i:i+16])
}
return
}
func (fpregs *ARM64PtraceFpRegs) Byte() []byte {
fpregs.Vregs = make([]byte, _ARM_FP_REGS_LENGTH)
return fpregs.Vregs[:]
}