service: move some type conversions from service/debugger to rpc pkgs
Move the conversion of some 'proc' types from service/debugger into service/rpc1 and service/rpc2. The methods of service/debugger.(*Debugger) are also used by service/dap which requires these types to be converted differently and converting them twice is inefficent and doesn't make much sense. Updates #2161
This commit is contained in:
parent
e8dbbef374
commit
6ef7aa8743
@ -7,7 +7,9 @@ import (
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-delve/delve/pkg/dwarf/godwarf"
|
||||
"github.com/go-delve/delve/pkg/dwarf/op"
|
||||
@ -109,7 +111,16 @@ func ConvertThread(th proc.Thread) *Thread {
|
||||
}
|
||||
}
|
||||
|
||||
func prettyTypeName(typ godwarf.Type) string {
|
||||
// ConvertThreads converts a slice of proc.Thread into a slice of api.Thread.
|
||||
func ConvertThreads(threads []proc.Thread) []*Thread {
|
||||
r := make([]*Thread, len(threads))
|
||||
for i := range threads {
|
||||
r[i] = ConvertThread(threads[i])
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func PrettyTypeName(typ godwarf.Type) string {
|
||||
if typ == nil {
|
||||
return ""
|
||||
}
|
||||
@ -152,29 +163,14 @@ func ConvertVar(v *proc.Variable) *Variable {
|
||||
DeclLine: v.DeclLine,
|
||||
}
|
||||
|
||||
r.Type = prettyTypeName(v.DwarfType)
|
||||
r.RealType = prettyTypeName(v.RealType)
|
||||
r.Type = PrettyTypeName(v.DwarfType)
|
||||
r.RealType = PrettyTypeName(v.RealType)
|
||||
|
||||
if v.Unreadable != nil {
|
||||
r.Unreadable = v.Unreadable.Error()
|
||||
}
|
||||
|
||||
if v.Value != nil {
|
||||
switch v.Kind {
|
||||
case reflect.Float32:
|
||||
r.Value = convertFloatValue(v, 32)
|
||||
case reflect.Float64:
|
||||
r.Value = convertFloatValue(v, 64)
|
||||
case reflect.String, reflect.Func:
|
||||
r.Value = constant.StringVal(v.Value)
|
||||
default:
|
||||
if cd := v.ConstDescr(); cd != "" {
|
||||
r.Value = fmt.Sprintf("%s (%s)", cd, v.Value.String())
|
||||
} else {
|
||||
r.Value = v.Value.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
r.Value = VariableValueAsString(v)
|
||||
|
||||
switch v.Kind {
|
||||
case reflect.Complex64:
|
||||
@ -230,6 +226,38 @@ func ConvertVar(v *proc.Variable) *Variable {
|
||||
return &r
|
||||
}
|
||||
|
||||
func VariableValueAsString(v *proc.Variable) string {
|
||||
if v.Value == nil {
|
||||
return ""
|
||||
}
|
||||
switch v.Kind {
|
||||
case reflect.Float32:
|
||||
return convertFloatValue(v, 32)
|
||||
case reflect.Float64:
|
||||
return convertFloatValue(v, 64)
|
||||
case reflect.String, reflect.Func:
|
||||
return constant.StringVal(v.Value)
|
||||
default:
|
||||
if cd := v.ConstDescr(); cd != "" {
|
||||
return fmt.Sprintf("%s (%s)", cd, v.Value.String())
|
||||
} else {
|
||||
return v.Value.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertVars converts from []*proc.Variable to []api.Variable.
|
||||
func ConvertVars(pv []*proc.Variable) []Variable {
|
||||
if pv == nil {
|
||||
return nil
|
||||
}
|
||||
vars := make([]Variable, 0, len(pv))
|
||||
for _, v := range pv {
|
||||
vars = append(vars, *ConvertVar(v))
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
// ConvertFunction converts from gosym.Func to
|
||||
// api.Function.
|
||||
func ConvertFunction(fn *proc.Function) *Function {
|
||||
@ -271,6 +299,15 @@ func ConvertGoroutine(g *proc.G) *Goroutine {
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertGoroutines converts from []*proc.G to []*api.Goroutine.
|
||||
func ConvertGoroutines(gs []*proc.G) []*Goroutine {
|
||||
goroutines := make([]*Goroutine, len(gs))
|
||||
for i := range gs {
|
||||
goroutines[i] = ConvertGoroutine(gs[i])
|
||||
}
|
||||
return goroutines
|
||||
}
|
||||
|
||||
// ConvertLocation converts from proc.Location to api.Location.
|
||||
func ConvertLocation(loc proc.Location) Location {
|
||||
return Location{
|
||||
@ -327,29 +364,55 @@ func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig {
|
||||
}
|
||||
}
|
||||
|
||||
var canonicalRegisterOrder = map[string]int{
|
||||
// amd64
|
||||
"rip": 0,
|
||||
"rsp": 1,
|
||||
"rax": 2,
|
||||
"rbx": 3,
|
||||
"rcx": 4,
|
||||
"rdx": 5,
|
||||
|
||||
// arm64
|
||||
"pc": 0,
|
||||
"sp": 1,
|
||||
}
|
||||
|
||||
// ConvertRegisters converts proc.Register to api.Register for a slice.
|
||||
func ConvertRegisters(in op.DwarfRegisters, arch *proc.Arch, floatingPoint bool) (out []Register) {
|
||||
if floatingPoint {
|
||||
in.Reg(^uint64(0)) // force loading all registers
|
||||
}
|
||||
func ConvertRegisters(in *op.DwarfRegisters, dwarfRegisterToString func(int, *op.DwarfRegister) (string, bool, string), floatingPoint bool) (out []Register) {
|
||||
out = make([]Register, 0, in.CurrentSize())
|
||||
for i := 0; i < in.CurrentSize(); i++ {
|
||||
reg := in.Reg(uint64(i))
|
||||
if reg == nil {
|
||||
continue
|
||||
}
|
||||
name, fp, repr := arch.DwarfRegisterToString(i, reg)
|
||||
name, fp, repr := dwarfRegisterToString(i, reg)
|
||||
if !floatingPoint && fp {
|
||||
continue
|
||||
}
|
||||
out = append(out, Register{name, repr, i})
|
||||
}
|
||||
return
|
||||
}
|
||||
// Sort the registers in a canonical order we prefer, this is mostly
|
||||
// because the DWARF register numbering for AMD64 is weird.
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
a, b := out[i], out[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
|
||||
|
||||
// ConvertCheckpoint converts proc.Chekcpoint to api.Checkpoint.
|
||||
func ConvertCheckpoint(in proc.Checkpoint) (out Checkpoint) {
|
||||
return Checkpoint(in)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func ConvertImage(image *proc.Image) Image {
|
||||
|
@ -1,5 +1,7 @@
|
||||
package dap
|
||||
|
||||
import "github.com/go-delve/delve/pkg/proc"
|
||||
|
||||
const startHandle = 1000
|
||||
|
||||
// handlesMap maps arbitrary values to unique sequential ids.
|
||||
@ -32,3 +34,27 @@ func (hs *handlesMap) get(handle int) (interface{}, bool) {
|
||||
v, ok := hs.handleToVal[handle]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
type variablesHandlesMap struct {
|
||||
m *handlesMap
|
||||
}
|
||||
|
||||
func newVariablesHandlesMap() *variablesHandlesMap {
|
||||
return &variablesHandlesMap{newHandlesMap()}
|
||||
}
|
||||
|
||||
func (hs *variablesHandlesMap) create(value *proc.Variable) int {
|
||||
return hs.m.create(value)
|
||||
}
|
||||
|
||||
func (hs *variablesHandlesMap) get(handle int) (*proc.Variable, bool) {
|
||||
v, ok := hs.m.get(handle)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return v.(*proc.Variable), true
|
||||
}
|
||||
|
||||
func (hs *variablesHandlesMap) reset() {
|
||||
hs.m.reset()
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
@ -59,7 +60,7 @@ type Server struct {
|
||||
stackFrameHandles *handlesMap
|
||||
// variableHandles maps compound variables to unique references within their stack frame.
|
||||
// See also comment for convertVariable.
|
||||
variableHandles *handlesMap
|
||||
variableHandles *variablesHandlesMap
|
||||
// args tracks special settings for handling debug session requests.
|
||||
args launchAttachArgs
|
||||
}
|
||||
@ -93,7 +94,7 @@ func NewServer(config *service.Config) *Server {
|
||||
stopChan: make(chan struct{}),
|
||||
log: logger,
|
||||
stackFrameHandles: newHandlesMap(),
|
||||
variableHandles: newHandlesMap(),
|
||||
variableHandles: newVariablesHandlesMap(),
|
||||
args: defaultArgs,
|
||||
}
|
||||
}
|
||||
@ -593,6 +594,9 @@ func (s *Server) onThreadsRequest(request *dap.ThreadsRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
s.debugger.LockTarget()
|
||||
defer s.debugger.UnlockTarget()
|
||||
|
||||
threads := make([]dap.Thread, len(gs))
|
||||
if len(threads) == 0 {
|
||||
// Depending on the debug session stage, goroutines information
|
||||
@ -604,8 +608,9 @@ func (s *Server) onThreadsRequest(request *dap.ThreadsRequest) {
|
||||
} else {
|
||||
for i, g := range gs {
|
||||
threads[i].Id = g.ID
|
||||
if loc := g.UserCurrentLoc; loc.Function != nil {
|
||||
threads[i].Name = loc.Function.Name()
|
||||
loc := g.UserCurrent()
|
||||
if loc.Fn != nil {
|
||||
threads[i].Name = loc.Fn.Name
|
||||
} else {
|
||||
threads[i].Name = fmt.Sprintf("%s@%d", loc.File, loc.Line)
|
||||
}
|
||||
@ -668,17 +673,22 @@ type stackFrame struct {
|
||||
// This is a mandatory request to support.
|
||||
func (s *Server) onStackTraceRequest(request *dap.StackTraceRequest) {
|
||||
goroutineID := request.Arguments.ThreadId
|
||||
locs, err := s.debugger.Stacktrace(goroutineID, s.args.stackTraceDepth, 0, nil /*skip locals & args*/)
|
||||
frames, err := s.debugger.Stacktrace(goroutineID, s.args.stackTraceDepth, 0)
|
||||
if err != nil {
|
||||
s.sendErrorResponse(request.Request, UnableToProduceStackTrace, "Unable to produce stack trace", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
stackFrames := make([]dap.StackFrame, len(locs))
|
||||
for i, loc := range locs {
|
||||
stackFrames := make([]dap.StackFrame, len(frames))
|
||||
for i, frame := range frames {
|
||||
loc := &frame.Call
|
||||
uniqueStackFrameID := s.stackFrameHandles.create(stackFrame{goroutineID, i})
|
||||
stackFrames[i] = dap.StackFrame{Id: uniqueStackFrameID, Line: loc.Line}
|
||||
stackFrames[i].Name = loc.Function.Name()
|
||||
if loc.Fn == nil {
|
||||
stackFrames[i].Name = "???"
|
||||
} else {
|
||||
stackFrames[i].Name = loc.Fn.Name
|
||||
}
|
||||
if loc.File != "<autogenerated>" {
|
||||
stackFrames[i].Source = dap.Source{Name: filepath.Base(loc.File), Path: loc.File}
|
||||
}
|
||||
@ -692,7 +702,7 @@ func (s *Server) onStackTraceRequest(request *dap.StackTraceRequest) {
|
||||
}
|
||||
response := &dap.StackTraceResponse{
|
||||
Response: *newResponse(request.Request),
|
||||
Body: dap.StackTraceResponseBody{StackFrames: stackFrames, TotalFrames: len(locs)},
|
||||
Body: dap.StackTraceResponseBody{StackFrames: stackFrames, TotalFrames: len(frames)},
|
||||
}
|
||||
s.send(response)
|
||||
}
|
||||
@ -706,25 +716,26 @@ func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
scope := api.EvalScope{GoroutineID: sf.(stackFrame).goroutineID, Frame: sf.(stackFrame).frameIndex}
|
||||
goid := sf.(stackFrame).goroutineID
|
||||
frame := sf.(stackFrame).frameIndex
|
||||
// TODO(polina): Support setting config via launch/attach args
|
||||
cfg := proc.LoadConfig{FollowPointers: true, MaxVariableRecurse: 1, MaxStringLen: 64, MaxArrayValues: 64, MaxStructFields: -1}
|
||||
|
||||
// Retrieve arguments
|
||||
args, err := s.debugger.FunctionArguments(scope, cfg)
|
||||
args, err := s.debugger.FunctionArguments(goid, frame, 0, cfg)
|
||||
if err != nil {
|
||||
s.sendErrorResponse(request.Request, UnableToListArgs, "Unable to list args", err.Error())
|
||||
return
|
||||
}
|
||||
argScope := api.Variable{Name: "Arguments", Children: args}
|
||||
argScope := &proc.Variable{Name: "Arguments", Children: slicePtrVarToSliceVar(args)}
|
||||
|
||||
// Retrieve local variables
|
||||
locals, err := s.debugger.LocalVariables(scope, cfg)
|
||||
locals, err := s.debugger.LocalVariables(goid, frame, 0, cfg)
|
||||
if err != nil {
|
||||
s.sendErrorResponse(request.Request, UnableToListLocals, "Unable to list local vars", err.Error())
|
||||
return
|
||||
}
|
||||
locScope := api.Variable{Name: "Locals", Children: locals}
|
||||
locScope := &proc.Variable{Name: "Locals", Children: slicePtrVarToSliceVar(locals)}
|
||||
|
||||
// TODO(polina): Annotate shadowed variables
|
||||
// TODO(polina): Retrieve global variables
|
||||
@ -740,15 +751,22 @@ func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
|
||||
s.send(response)
|
||||
}
|
||||
|
||||
func slicePtrVarToSliceVar(vars []*proc.Variable) []proc.Variable {
|
||||
r := make([]proc.Variable, len(vars))
|
||||
for i := range vars {
|
||||
r[i] = *vars[i]
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// onVariablesRequest handles 'variables' requests.
|
||||
// This is a mandatory request to support.
|
||||
func (s *Server) onVariablesRequest(request *dap.VariablesRequest) {
|
||||
variable, ok := s.variableHandles.get(request.Arguments.VariablesReference)
|
||||
v, ok := s.variableHandles.get(request.Arguments.VariablesReference)
|
||||
if !ok {
|
||||
s.sendErrorResponse(request.Request, UnableToLookupVariable, "Unable to lookup variable", fmt.Sprintf("unknown reference %d", request.Arguments.VariablesReference))
|
||||
return
|
||||
}
|
||||
v := variable.(api.Variable)
|
||||
children := make([]dap.Variable, 0)
|
||||
// TODO(polina): check and handle if variable loaded incompletely
|
||||
// https://github.com/go-delve/delve/blob/master/Documentation/api/ClientHowto.md#looking-into-variables
|
||||
@ -759,8 +777,8 @@ func (s *Server) onVariablesRequest(request *dap.VariablesRequest) {
|
||||
// A map will have twice as many children as there are key-value elements.
|
||||
kvIndex := i / 2
|
||||
// Process children in pairs: even indices are map keys, odd indices are values.
|
||||
key, keyref := s.convertVariable(v.Children[i])
|
||||
val, valref := s.convertVariable(v.Children[i+1])
|
||||
key, keyref := s.convertVariable(&v.Children[i])
|
||||
val, valref := s.convertVariable(&v.Children[i+1])
|
||||
// If key or value or both are scalars, we can use
|
||||
// a single variable to represet key:value format.
|
||||
// Otherwise, we must return separate variables for both.
|
||||
@ -792,7 +810,8 @@ func (s *Server) onVariablesRequest(request *dap.VariablesRequest) {
|
||||
}
|
||||
case reflect.Slice, reflect.Array:
|
||||
children = make([]dap.Variable, len(v.Children))
|
||||
for i, c := range v.Children {
|
||||
for i := range v.Children {
|
||||
c := &v.Children[i]
|
||||
value, varref := s.convertVariable(c)
|
||||
children[i] = dap.Variable{
|
||||
Name: fmt.Sprintf("[%d]", i),
|
||||
@ -802,7 +821,8 @@ func (s *Server) onVariablesRequest(request *dap.VariablesRequest) {
|
||||
}
|
||||
default:
|
||||
children = make([]dap.Variable, len(v.Children))
|
||||
for i, c := range v.Children {
|
||||
for i := range v.Children {
|
||||
c := &v.Children[i]
|
||||
value, variablesReference := s.convertVariable(c)
|
||||
children[i] = dap.Variable{
|
||||
Name: c.Name,
|
||||
@ -827,11 +847,12 @@ func (s *Server) onVariablesRequest(request *dap.VariablesRequest) {
|
||||
// can be issued to get the elements of the compound variable. As a custom, a zero
|
||||
// reference, reminiscent of a zero pointer, is used to indicate that a scalar
|
||||
// variable cannot be "dereferenced" to get its elements (as there are none).
|
||||
func (s *Server) convertVariable(v api.Variable) (value string, variablesReference int) {
|
||||
if v.Unreadable != "" {
|
||||
value = fmt.Sprintf("unreadable <%s>", v.Unreadable)
|
||||
func (s *Server) convertVariable(v *proc.Variable) (value string, variablesReference int) {
|
||||
if v.Unreadable != nil {
|
||||
value = fmt.Sprintf("unreadable <%v>", v.Unreadable)
|
||||
return
|
||||
}
|
||||
typeName := api.PrettyTypeName(v.DwarfType)
|
||||
switch v.Kind {
|
||||
case reflect.UnsafePointer:
|
||||
if len(v.Children) == 0 {
|
||||
@ -840,51 +861,51 @@ func (s *Server) convertVariable(v api.Variable) (value string, variablesReferen
|
||||
value = fmt.Sprintf("unsafe.Pointer(%#x)", v.Children[0].Addr)
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if v.Type == "" || len(v.Children) == 0 {
|
||||
if v.DwarfType == nil || len(v.Children) == 0 {
|
||||
value = "nil"
|
||||
} else if v.Children[0].Addr == 0 {
|
||||
value = "nil <" + v.Type + ">"
|
||||
} else if v.Children[0].Type == "void" {
|
||||
value = "nil <" + typeName + ">"
|
||||
} else if v.Children[0].Kind == reflect.Invalid {
|
||||
value = "void"
|
||||
} else {
|
||||
value = fmt.Sprintf("<%s>(%#x)", v.Type, v.Children[0].Addr)
|
||||
value = fmt.Sprintf("<%s>(%#x)", typeName, v.Children[0].Addr)
|
||||
variablesReference = s.variableHandles.create(v)
|
||||
}
|
||||
case reflect.Array:
|
||||
value = "<" + v.Type + ">"
|
||||
value = "<" + typeName + ">"
|
||||
if len(v.Children) > 0 {
|
||||
variablesReference = s.variableHandles.create(v)
|
||||
}
|
||||
case reflect.Slice:
|
||||
if v.Base == 0 {
|
||||
value = "nil <" + v.Type + ">"
|
||||
value = "nil <" + typeName + ">"
|
||||
} else {
|
||||
value = fmt.Sprintf("<%s> (length: %d, cap: %d)", v.Type, v.Len, v.Cap)
|
||||
value = fmt.Sprintf("<%s> (length: %d, cap: %d)", typeName, v.Len, v.Cap)
|
||||
if len(v.Children) > 0 {
|
||||
variablesReference = s.variableHandles.create(v)
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
if v.Base == 0 {
|
||||
value = "nil <" + v.Type + ">"
|
||||
value = "nil <" + typeName + ">"
|
||||
} else {
|
||||
value = fmt.Sprintf("<%s> (length: %d)", v.Type, v.Len)
|
||||
value = fmt.Sprintf("<%s> (length: %d)", typeName, v.Len)
|
||||
if len(v.Children) > 0 {
|
||||
variablesReference = s.variableHandles.create(v)
|
||||
}
|
||||
}
|
||||
case reflect.String:
|
||||
lenNotLoaded := v.Len - int64(len(v.Value))
|
||||
vvalue := v.Value
|
||||
vvalue := constant.StringVal(v.Value)
|
||||
lenNotLoaded := v.Len - int64(len(vvalue))
|
||||
if lenNotLoaded > 0 {
|
||||
vvalue += fmt.Sprintf("...+%d more", lenNotLoaded)
|
||||
}
|
||||
value = fmt.Sprintf("%q", vvalue)
|
||||
case reflect.Chan:
|
||||
if len(v.Children) == 0 {
|
||||
value = "nil <" + v.Type + ">"
|
||||
value = "nil <" + typeName + ">"
|
||||
} else {
|
||||
value = "<" + v.Type + ">"
|
||||
value = "<" + typeName + ">"
|
||||
variablesReference = s.variableHandles.create(v)
|
||||
}
|
||||
case reflect.Interface:
|
||||
@ -896,16 +917,31 @@ func (s *Server) convertVariable(v api.Variable) (value string, variablesReferen
|
||||
// happens to contain 0.
|
||||
value = "nil"
|
||||
} else if len(v.Children) == 0 || v.Children[0].Kind == reflect.Invalid && v.Children[0].Addr == 0 {
|
||||
value = "nil <" + v.Type + ">"
|
||||
value = "nil <" + typeName + ">"
|
||||
} else {
|
||||
value = "<" + v.Type + ">"
|
||||
value = "<" + typeName + ">"
|
||||
variablesReference = s.variableHandles.create(v)
|
||||
}
|
||||
default: // Struct, complex, scalar
|
||||
if v.Value != "" {
|
||||
value = v.Value
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
v.Children = make([]proc.Variable, 2)
|
||||
v.Children[0].Name = "real"
|
||||
v.Children[0].Value = constant.Real(v.Value)
|
||||
v.Children[1].Name = "imaginary"
|
||||
v.Children[1].Value = constant.Imag(v.Value)
|
||||
if v.Kind == reflect.Complex64 {
|
||||
v.Children[0].Kind = reflect.Float32
|
||||
v.Children[1].Kind = reflect.Float32
|
||||
} else {
|
||||
value = "<" + v.Type + ">"
|
||||
v.Children[0].Kind = reflect.Float64
|
||||
v.Children[1].Kind = reflect.Float64
|
||||
}
|
||||
fallthrough
|
||||
default: // Struct, complex, scalar
|
||||
vvalue := api.VariableValueAsString(v)
|
||||
if vvalue != "" {
|
||||
value = vvalue
|
||||
} else {
|
||||
value = "<" + typeName + ">"
|
||||
}
|
||||
if len(v.Children) > 0 {
|
||||
variablesReference = s.variableHandles.create(v)
|
||||
|
@ -552,7 +552,7 @@ func (d *Debugger) state(retLoadCfg *proc.LoadConfig) (*api.DebuggerState, error
|
||||
th := api.ConvertThread(thread)
|
||||
|
||||
if retLoadCfg != nil {
|
||||
th.ReturnValues = convertVars(thread.Common().ReturnValues(*retLoadCfg))
|
||||
th.ReturnValues = api.ConvertVars(thread.Common().ReturnValues(*retLoadCfg))
|
||||
}
|
||||
|
||||
state.Threads = append(state.Threads, th)
|
||||
@ -825,7 +825,7 @@ func (d *Debugger) findBreakpointByName(name string) *api.Breakpoint {
|
||||
}
|
||||
|
||||
// Threads returns the threads of the target process.
|
||||
func (d *Debugger) Threads() ([]*api.Thread, error) {
|
||||
func (d *Debugger) Threads() ([]proc.Thread, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
@ -833,15 +833,11 @@ func (d *Debugger) Threads() ([]*api.Thread, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
threads := []*api.Thread{}
|
||||
for _, th := range d.target.ThreadList() {
|
||||
threads = append(threads, api.ConvertThread(th))
|
||||
}
|
||||
return threads, nil
|
||||
return d.target.ThreadList(), nil
|
||||
}
|
||||
|
||||
// FindThread returns the thread for the given 'id'.
|
||||
func (d *Debugger) FindThread(id int) (*api.Thread, error) {
|
||||
func (d *Debugger) FindThread(id int) (proc.Thread, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
@ -851,7 +847,7 @@ func (d *Debugger) FindThread(id int) (*api.Thread, error) {
|
||||
|
||||
for _, th := range d.target.ThreadList() {
|
||||
if th.ThreadID() == id {
|
||||
return api.ConvertThread(th), nil
|
||||
return th, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
@ -1079,12 +1075,12 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
||||
}
|
||||
if bp.LoadArgs != nil {
|
||||
if vars, err := s.FunctionArguments(*api.LoadConfigToProc(bp.LoadArgs)); err == nil {
|
||||
bpi.Arguments = convertVars(vars)
|
||||
bpi.Arguments = api.ConvertVars(vars)
|
||||
}
|
||||
}
|
||||
if bp.LoadLocals != nil {
|
||||
if locals, err := s.LocalVariables(*api.LoadConfigToProc(bp.LoadLocals)); err == nil {
|
||||
bpi.Locals = convertVars(locals)
|
||||
bpi.Locals = api.ConvertVars(locals)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1157,7 +1153,7 @@ func (d *Debugger) Types(filter string) ([]string, error) {
|
||||
|
||||
// PackageVariables returns a list of package variables for the thread,
|
||||
// optionally regexp filtered using regexp described in 'filter'.
|
||||
func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadConfig) ([]*proc.Variable, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
@ -1166,7 +1162,6 @@ func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadCo
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
vars := []api.Variable{}
|
||||
thread, found := d.target.FindThread(threadID)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||
@ -1179,146 +1174,93 @@ func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadCo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range pv {
|
||||
if regex.Match([]byte(v.Name)) {
|
||||
vars = append(vars, *api.ConvertVar(v))
|
||||
pvr := pv[:0]
|
||||
for i := range pv {
|
||||
if regex.Match([]byte(pv[i].Name)) {
|
||||
pvr = append(pvr, pv[i])
|
||||
}
|
||||
}
|
||||
return vars, err
|
||||
return pvr, nil
|
||||
}
|
||||
|
||||
// Registers returns string representation of the CPU registers.
|
||||
func (d *Debugger) Registers(threadID int, scope *api.EvalScope, floatingPoint bool) (api.Registers, error) {
|
||||
// ThreadRegisters returns registers of the specified thread.
|
||||
func (d *Debugger) ThreadRegisters(threadID int, floatingPoint bool) (*op.DwarfRegisters, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
var dregs op.DwarfRegisters
|
||||
|
||||
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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dregs = d.target.BinInfo().Arch.RegistersToDwarfRegisters(0, regs)
|
||||
thread, found := d.target.FindThread(threadID)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||
}
|
||||
r := api.ConvertRegisters(dregs, d.target.BinInfo().Arch, floatingPoint)
|
||||
if floatingPoint && dregs.FloatLoadError != nil {
|
||||
return nil, dregs.FloatLoadError
|
||||
regs, err := thread.Registers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Sort the registers in a canonical order we prefer, this is mostly
|
||||
// because the DWARF register numbering for AMD64 is weird.
|
||||
sort.Slice(r, func(i, j int) bool {
|
||||
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
|
||||
dregs := d.target.BinInfo().Arch.RegistersToDwarfRegisters(0, regs)
|
||||
return &dregs, nil
|
||||
}
|
||||
|
||||
var canonicalRegisterOrder = map[string]int{
|
||||
// amd64
|
||||
"rip": 0,
|
||||
"rsp": 1,
|
||||
"rax": 2,
|
||||
"rbx": 3,
|
||||
"rcx": 4,
|
||||
"rdx": 5,
|
||||
// ScopeRegisters returns registers for the specified scope.
|
||||
func (d *Debugger) ScopeRegisters(goid, frame, deferredCall int, floatingPoint bool) (*op.DwarfRegisters, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
// arm64
|
||||
"pc": 0,
|
||||
"sp": 1,
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s.Regs, nil
|
||||
}
|
||||
|
||||
func convertVars(pv []*proc.Variable) []api.Variable {
|
||||
if pv == nil {
|
||||
return nil
|
||||
}
|
||||
vars := make([]api.Variable, 0, len(pv))
|
||||
for _, v := range pv {
|
||||
vars = append(vars, *api.ConvertVar(v))
|
||||
}
|
||||
return vars
|
||||
// DwarfRegisterToString returns the name and value representation of the given register.
|
||||
func (d *Debugger) DwarfRegisterToString(i int, reg *op.DwarfRegister) (string, bool, string) {
|
||||
return d.target.BinInfo().Arch.DwarfRegisterToString(i, reg)
|
||||
}
|
||||
|
||||
// LocalVariables returns a list of the local variables.
|
||||
func (d *Debugger) LocalVariables(scope api.EvalScope, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
func (d *Debugger) LocalVariables(goid, frame, deferredCall int, cfg proc.LoadConfig) ([]*proc.Variable, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
s, err := proc.ConvertEvalScope(d.target, scope.GoroutineID, scope.Frame, scope.DeferredCall)
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv, err := s.LocalVariables(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertVars(pv), err
|
||||
return s.LocalVariables(cfg)
|
||||
}
|
||||
|
||||
// FunctionArguments returns the arguments to the current function.
|
||||
func (d *Debugger) FunctionArguments(scope api.EvalScope, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
func (d *Debugger) FunctionArguments(goid, frame, deferredCall int, cfg proc.LoadConfig) ([]*proc.Variable, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
s, err := proc.ConvertEvalScope(d.target, scope.GoroutineID, scope.Frame, scope.DeferredCall)
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv, err := s.FunctionArguments(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertVars(pv), nil
|
||||
return s.FunctionArguments(cfg)
|
||||
}
|
||||
|
||||
// EvalVariableInScope will attempt to evaluate the variable represented by 'symbol'
|
||||
// in the scope provided.
|
||||
func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string, cfg proc.LoadConfig) (*api.Variable, error) {
|
||||
func (d *Debugger) EvalVariableInScope(goid, frame, deferredCall int, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
s, err := proc.ConvertEvalScope(d.target, scope.GoroutineID, scope.Frame, scope.DeferredCall)
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := s.EvalVariable(symbol, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return api.ConvertVar(v), err
|
||||
return s.EvalVariable(symbol, cfg)
|
||||
}
|
||||
|
||||
// SetVariableInScope will set the value of the variable represented by
|
||||
// 'symbol' to the value given, in the given scope.
|
||||
func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol, value string) error {
|
||||
func (d *Debugger) SetVariableInScope(goid, frame, deferredCall int, symbol, value string) error {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
s, err := proc.ConvertEvalScope(d.target, scope.GoroutineID, scope.Frame, scope.DeferredCall)
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1326,25 +1268,16 @@ func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol, value string)
|
||||
}
|
||||
|
||||
// Goroutines will return a list of goroutines in the target process.
|
||||
func (d *Debugger) Goroutines(start, count int) ([]*api.Goroutine, int, error) {
|
||||
func (d *Debugger) Goroutines(start, count int) ([]*proc.G, int, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
goroutines := []*api.Goroutine{}
|
||||
gs, nextg, err := proc.GoroutinesInfo(d.target, start, count)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
for _, g := range gs {
|
||||
goroutines = append(goroutines, api.ConvertGoroutine(g))
|
||||
}
|
||||
return goroutines, nextg, err
|
||||
return proc.GoroutinesInfo(d.target, start, count)
|
||||
}
|
||||
|
||||
// Stacktrace returns a list of Stackframes for the given goroutine. The
|
||||
// length of the returned list will be min(stack_len, depth).
|
||||
// If 'full' is true, then local vars, function args, etc will be returned as well.
|
||||
func (d *Debugger) Stacktrace(goroutineID, depth int, opts api.StacktraceOptions, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
|
||||
func (d *Debugger) Stacktrace(goroutineID, depth int, opts api.StacktraceOptions) ([]proc.Stackframe, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
@ -1352,23 +1285,16 @@ func (d *Debugger) Stacktrace(goroutineID, depth int, opts api.StacktraceOptions
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rawlocs []proc.Stackframe
|
||||
|
||||
g, err := proc.FindGoroutine(d.target, goroutineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g == nil {
|
||||
rawlocs, err = proc.ThreadStacktrace(d.target.CurrentThread(), depth)
|
||||
return proc.ThreadStacktrace(d.target.CurrentThread(), depth)
|
||||
} else {
|
||||
rawlocs, err = g.Stacktrace(depth, proc.StacktraceOptions(opts))
|
||||
return g.Stacktrace(depth, proc.StacktraceOptions(opts))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d.convertStacktrace(rawlocs, cfg)
|
||||
}
|
||||
|
||||
// Ancestors returns the stacktraces for the ancestors of a goroutine.
|
||||
@ -1413,6 +1339,15 @@ func (d *Debugger) Ancestors(goroutineID, numAncestors, depth int) ([]api.Ancest
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// ConvertStacktrace converts a slice of proc.Stackframe into a slice of
|
||||
// api.Stackframe, loading local variables and arguments of each frame if
|
||||
// cfg is not nil.
|
||||
func (d *Debugger) ConvertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
return d.convertStacktrace(rawlocs, cfg)
|
||||
}
|
||||
|
||||
func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
|
||||
locations := make([]api.Stackframe, 0, len(rawlocs))
|
||||
for i := range rawlocs {
|
||||
@ -1441,8 +1376,8 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo
|
||||
return nil, err
|
||||
}
|
||||
|
||||
frame.Locals = convertVars(locals)
|
||||
frame.Arguments = convertVars(arguments)
|
||||
frame.Locals = api.ConvertVars(locals)
|
||||
frame.Arguments = api.ConvertVars(arguments)
|
||||
}
|
||||
locations = append(locations, frame)
|
||||
}
|
||||
@ -1481,7 +1416,7 @@ func (d *Debugger) convertDefers(defers []*proc.Defer) []api.Defer {
|
||||
}
|
||||
|
||||
// FindLocation will find the location specified by 'locStr'.
|
||||
func (d *Debugger) FindLocation(scope api.EvalScope, locStr string, includeNonExecutableLines bool) ([]api.Location, error) {
|
||||
func (d *Debugger) FindLocation(goid, frame, deferredCall int, locStr string, includeNonExecutableLines bool) ([]api.Location, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
@ -1494,7 +1429,7 @@ func (d *Debugger) FindLocation(scope api.EvalScope, locStr string, includeNonEx
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s, _ := proc.ConvertEvalScope(d.target, scope.GoroutineID, scope.Frame, scope.DeferredCall)
|
||||
s, _ := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
|
||||
locs, err := loc.Find(d.target, d.processArgs, s, locStr, includeNonExecutableLines)
|
||||
for i := range locs {
|
||||
@ -1511,7 +1446,7 @@ func (d *Debugger) FindLocation(scope api.EvalScope, locStr string, includeNonEx
|
||||
|
||||
// Disassemble code between startPC and endPC.
|
||||
// if endPC == 0 it will find the function containing startPC and disassemble the whole function.
|
||||
func (d *Debugger) Disassemble(goroutineID int, addr1, addr2 uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
|
||||
func (d *Debugger) Disassemble(goroutineID int, addr1, addr2 uint64) ([]proc.AsmInstruction, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
@ -1539,17 +1474,13 @@ func (d *Debugger) Disassemble(goroutineID int, addr1, addr2 uint64, flavour api
|
||||
}
|
||||
regs, _ := curthread.Registers()
|
||||
|
||||
insts, err := proc.Disassemble(curthread, regs, d.target.Breakpoints(), d.target.BinInfo(), addr1, addr2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disass := make(api.AsmInstructions, len(insts))
|
||||
return proc.Disassemble(curthread, regs, d.target.Breakpoints(), d.target.BinInfo(), addr1, addr2)
|
||||
}
|
||||
|
||||
for i := range insts {
|
||||
disass[i] = api.ConvertAsmInstruction(insts[i], insts[i].Text(proc.AssemblyFlavour(flavour), d.target.BinInfo()))
|
||||
}
|
||||
|
||||
return disass, nil
|
||||
func (d *Debugger) AsmInstructionText(inst *proc.AsmInstruction, flavour proc.AssemblyFlavour) string {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
return inst.Text(flavour, d.target.BinInfo())
|
||||
}
|
||||
|
||||
// Recorded returns true if the target is a recording.
|
||||
@ -1567,18 +1498,10 @@ func (d *Debugger) Checkpoint(where string) (int, error) {
|
||||
}
|
||||
|
||||
// Checkpoints will return a list of checkpoints.
|
||||
func (d *Debugger) Checkpoints() ([]api.Checkpoint, error) {
|
||||
func (d *Debugger) Checkpoints() ([]proc.Checkpoint, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
cps, err := d.target.Checkpoints()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := make([]api.Checkpoint, len(cps))
|
||||
for i := range cps {
|
||||
r[i] = api.ConvertCheckpoint(cps[i])
|
||||
}
|
||||
return r, nil
|
||||
return d.target.Checkpoints()
|
||||
}
|
||||
|
||||
// ClearCheckpoint will clear the checkpoint of the given ID.
|
||||
@ -1589,16 +1512,11 @@ func (d *Debugger) ClearCheckpoint(id int) error {
|
||||
}
|
||||
|
||||
// ListDynamicLibraries returns a list of loaded dynamic libraries.
|
||||
func (d *Debugger) ListDynamicLibraries() []api.Image {
|
||||
func (d *Debugger) ListDynamicLibraries() []*proc.Image {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
bi := d.target.BinInfo()
|
||||
r := make([]api.Image, 0, len(bi.Images)-1)
|
||||
// skips the first image because it's the executable file
|
||||
for i := range bi.Images[1:] {
|
||||
r = append(r, api.ConvertImage(bi.Images[i+1]))
|
||||
}
|
||||
return r
|
||||
return d.target.BinInfo().Images[1:] // skips the first image because it's the executable file
|
||||
|
||||
}
|
||||
|
||||
// ExamineMemory returns the raw memory stored at the given address.
|
||||
@ -1652,30 +1570,10 @@ func (d *Debugger) GetVersion(out *api.GetVersionOut) error {
|
||||
// ListPackagesBuildInfo returns the list of packages used by the program along with
|
||||
// the directory where each package was compiled and optionally the list of
|
||||
// files constituting the package.
|
||||
func (d *Debugger) ListPackagesBuildInfo(includeFiles bool) []api.PackageBuildInfo {
|
||||
func (d *Debugger) ListPackagesBuildInfo(includeFiles bool) []*proc.PackageBuildInfo {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
pkgs := d.target.BinInfo().ListPackagesBuildInfo(includeFiles)
|
||||
r := make([]api.PackageBuildInfo, 0, len(pkgs))
|
||||
for _, pkg := range pkgs {
|
||||
var files []string
|
||||
|
||||
if len(pkg.Files) > 0 {
|
||||
files = make([]string, 0, len(pkg.Files))
|
||||
for file := range pkg.Files {
|
||||
files = append(files, file)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(files)
|
||||
|
||||
r = append(r, api.PackageBuildInfo{
|
||||
ImportPath: pkg.ImportPath,
|
||||
DirectoryPath: pkg.DirectoryPath,
|
||||
Files: files,
|
||||
})
|
||||
}
|
||||
return r
|
||||
return d.target.BinInfo().ListPackagesBuildInfo(includeFiles)
|
||||
}
|
||||
|
||||
// StopRecording stops a recording (if one is in progress)
|
||||
@ -1688,6 +1586,16 @@ func (d *Debugger) StopRecording() error {
|
||||
return d.stopRecording()
|
||||
}
|
||||
|
||||
// LockTarget acquires the target mutex.
|
||||
func (d *Debugger) LockTarget() {
|
||||
d.targetMutex.Lock()
|
||||
}
|
||||
|
||||
// UnlockTarget releases the target mutex.
|
||||
func (d *Debugger) UnlockTarget() {
|
||||
d.targetMutex.Unlock()
|
||||
}
|
||||
|
||||
func go11DecodeErrorCheck(err error) error {
|
||||
if _, isdecodeerr := err.(dwarf.DecodeError); !isdecodeerr {
|
||||
return err
|
||||
|
@ -93,12 +93,12 @@ func (s *RPCServer) StacktraceGoroutine(args *StacktraceGoroutineArgs, locations
|
||||
if args.Full {
|
||||
loadcfg = &defaultLoadConfig
|
||||
}
|
||||
locs, err := s.debugger.Stacktrace(args.Id, args.Depth, 0, loadcfg)
|
||||
locs, err := s.debugger.Stacktrace(args.Id, args.Depth, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*locations = locs
|
||||
return nil
|
||||
*locations, err = s.debugger.ConvertStacktrace(locs, loadcfg)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListBreakpoints(arg interface{}, breakpoints *[]*api.Breakpoint) error {
|
||||
@ -147,8 +147,14 @@ func (s *RPCServer) AmendBreakpoint(amend *api.Breakpoint, unused *int) error {
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListThreads(arg interface{}, threads *[]*api.Thread) (err error) {
|
||||
*threads, err = s.debugger.Threads()
|
||||
return err
|
||||
pthreads, err := s.debugger.Threads()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.debugger.LockTarget()
|
||||
defer s.debugger.UnlockTarget()
|
||||
*threads = api.ConvertThreads(pthreads)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) GetThread(id int, thread *api.Thread) error {
|
||||
@ -159,7 +165,9 @@ func (s *RPCServer) GetThread(id int, thread *api.Thread) error {
|
||||
if t == nil {
|
||||
return fmt.Errorf("no thread with id %d", id)
|
||||
}
|
||||
*thread = *t
|
||||
s.debugger.LockTarget()
|
||||
defer s.debugger.UnlockTarget()
|
||||
*thread = *api.ConvertThread(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -178,7 +186,7 @@ func (s *RPCServer) ListPackageVars(filter string, variables *[]api.Variable) er
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*variables = vars
|
||||
*variables = api.ConvertVars(vars)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -200,7 +208,7 @@ func (s *RPCServer) ListThreadPackageVars(args *ThreadListArgs, variables *[]api
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*variables = vars
|
||||
*variables = api.ConvertVars(vars)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -210,29 +218,30 @@ func (s *RPCServer) ListRegisters(arg interface{}, registers *string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
regs, err := s.debugger.Registers(state.CurrentThread.ID, nil, false)
|
||||
dregs, err := s.debugger.ThreadRegisters(state.CurrentThread.ID, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
regs := api.Registers(api.ConvertRegisters(dregs, s.debugger.DwarfRegisterToString, false))
|
||||
*registers = regs.String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListLocalVars(scope api.EvalScope, variables *[]api.Variable) error {
|
||||
vars, err := s.debugger.LocalVariables(scope, defaultLoadConfig)
|
||||
vars, err := s.debugger.LocalVariables(scope.GoroutineID, scope.Frame, scope.DeferredCall, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*variables = vars
|
||||
*variables = api.ConvertVars(vars)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListFunctionArgs(scope api.EvalScope, variables *[]api.Variable) error {
|
||||
vars, err := s.debugger.FunctionArguments(scope, defaultLoadConfig)
|
||||
vars, err := s.debugger.FunctionArguments(scope.GoroutineID, scope.Frame, scope.DeferredCall, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*variables = vars
|
||||
*variables = api.ConvertVars(vars)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -242,11 +251,11 @@ type EvalSymbolArgs struct {
|
||||
}
|
||||
|
||||
func (s *RPCServer) EvalSymbol(args EvalSymbolArgs, variable *api.Variable) error {
|
||||
v, err := s.debugger.EvalVariableInScope(args.Scope, args.Symbol, defaultLoadConfig)
|
||||
v, err := s.debugger.EvalVariableInScope(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Symbol, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*variable = *v
|
||||
*variable = *api.ConvertVar(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -258,7 +267,7 @@ type SetSymbolArgs struct {
|
||||
|
||||
func (s *RPCServer) SetSymbol(args SetSymbolArgs, unused *int) error {
|
||||
*unused = 0
|
||||
return s.debugger.SetVariableInScope(args.Scope, args.Symbol, args.Value)
|
||||
return s.debugger.SetVariableInScope(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Symbol, args.Value)
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListSources(filter string, sources *[]string) error {
|
||||
@ -293,7 +302,9 @@ func (s *RPCServer) ListGoroutines(arg interface{}, goroutines *[]*api.Goroutine
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*goroutines = gs
|
||||
s.debugger.LockTarget()
|
||||
s.debugger.UnlockTarget()
|
||||
*goroutines = api.ConvertGoroutines(gs)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -311,7 +322,7 @@ type FindLocationArgs struct {
|
||||
|
||||
func (c *RPCServer) FindLocation(args FindLocationArgs, answer *[]api.Location) error {
|
||||
var err error
|
||||
*answer, err = c.debugger.FindLocation(args.Scope, args.Loc, false)
|
||||
*answer, err = c.debugger.FindLocation(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Loc, false)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -323,6 +334,13 @@ type DisassembleRequest struct {
|
||||
|
||||
func (c *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error {
|
||||
var err error
|
||||
*answer, err = c.debugger.Disassemble(args.Scope.GoroutineID, args.StartPC, args.EndPC, args.Flavour)
|
||||
return err
|
||||
insts, err := c.debugger.Disassemble(args.Scope.GoroutineID, args.StartPC, args.EndPC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*answer = make(api.AsmInstructions, len(insts))
|
||||
for i := range insts {
|
||||
(*answer)[i] = api.ConvertAsmInstruction(insts[i], c.debugger.AsmInstructionText(&insts[i], proc.AssemblyFlavour(args.Flavour)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -3,8 +3,11 @@ package rpc2
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/go-delve/delve/pkg/dwarf/op"
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
"github.com/go-delve/delve/service"
|
||||
"github.com/go-delve/delve/service/api"
|
||||
"github.com/go-delve/delve/service/debugger"
|
||||
@ -189,7 +192,11 @@ func (s *RPCServer) Stacktrace(arg StacktraceIn, out *StacktraceOut) error {
|
||||
arg.Opts |= api.StacktraceReadDefers
|
||||
}
|
||||
var err error
|
||||
out.Locations, err = s.debugger.Stacktrace(arg.Id, arg.Depth, arg.Opts, api.LoadConfigToProc(cfg))
|
||||
rawlocs, err := s.debugger.Stacktrace(arg.Id, arg.Depth, arg.Opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Locations, err = s.debugger.ConvertStacktrace(rawlocs, api.LoadConfigToProc(cfg))
|
||||
return err
|
||||
}
|
||||
|
||||
@ -320,8 +327,14 @@ type ListThreadsOut struct {
|
||||
|
||||
// ListThreads lists all threads.
|
||||
func (s *RPCServer) ListThreads(arg ListThreadsIn, out *ListThreadsOut) (err error) {
|
||||
out.Threads, err = s.debugger.Threads()
|
||||
return err
|
||||
threads, err := s.debugger.Threads()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.debugger.LockTarget()
|
||||
defer s.debugger.UnlockTarget()
|
||||
out.Threads = api.ConvertThreads(threads)
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetThreadIn struct {
|
||||
@ -341,7 +354,9 @@ func (s *RPCServer) GetThread(arg GetThreadIn, out *GetThreadOut) error {
|
||||
if t == nil {
|
||||
return fmt.Errorf("no thread with id %d", arg.Id)
|
||||
}
|
||||
out.Thread = t
|
||||
s.debugger.LockTarget()
|
||||
defer s.debugger.UnlockTarget()
|
||||
out.Thread = api.ConvertThread(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -370,7 +385,7 @@ func (s *RPCServer) ListPackageVars(arg ListPackageVarsIn, out *ListPackageVarsO
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Variables = vars
|
||||
out.Variables = api.ConvertVars(vars)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -397,11 +412,18 @@ func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) er
|
||||
arg.ThreadID = state.CurrentThread.ID
|
||||
}
|
||||
|
||||
regs, err := s.debugger.Registers(arg.ThreadID, arg.Scope, arg.IncludeFp)
|
||||
var regs *op.DwarfRegisters
|
||||
var err error
|
||||
|
||||
if arg.Scope != nil {
|
||||
regs, err = s.debugger.ScopeRegisters(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.IncludeFp)
|
||||
} else {
|
||||
regs, err = s.debugger.ThreadRegisters(arg.ThreadID, arg.IncludeFp)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Regs = regs
|
||||
out.Regs = api.ConvertRegisters(regs, s.debugger.DwarfRegisterToString, arg.IncludeFp)
|
||||
out.Registers = out.Regs.String()
|
||||
|
||||
return nil
|
||||
@ -418,11 +440,11 @@ type ListLocalVarsOut struct {
|
||||
|
||||
// ListLocalVars lists all local variables in scope.
|
||||
func (s *RPCServer) ListLocalVars(arg ListLocalVarsIn, out *ListLocalVarsOut) error {
|
||||
vars, err := s.debugger.LocalVariables(arg.Scope, *api.LoadConfigToProc(&arg.Cfg))
|
||||
vars, err := s.debugger.LocalVariables(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, *api.LoadConfigToProc(&arg.Cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Variables = vars
|
||||
out.Variables = api.ConvertVars(vars)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -437,11 +459,11 @@ type ListFunctionArgsOut struct {
|
||||
|
||||
// ListFunctionArgs lists all arguments to the current function
|
||||
func (s *RPCServer) ListFunctionArgs(arg ListFunctionArgsIn, out *ListFunctionArgsOut) error {
|
||||
vars, err := s.debugger.FunctionArguments(arg.Scope, *api.LoadConfigToProc(&arg.Cfg))
|
||||
vars, err := s.debugger.FunctionArguments(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, *api.LoadConfigToProc(&arg.Cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Args = vars
|
||||
out.Args = api.ConvertVars(vars)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -464,11 +486,11 @@ func (s *RPCServer) Eval(arg EvalIn, out *EvalOut) error {
|
||||
if cfg == nil {
|
||||
cfg = &api.LoadConfig{FollowPointers: true, MaxVariableRecurse: 1, MaxStringLen: 64, MaxArrayValues: 64, MaxStructFields: -1}
|
||||
}
|
||||
v, err := s.debugger.EvalVariableInScope(arg.Scope, arg.Expr, *api.LoadConfigToProc(cfg))
|
||||
v, err := s.debugger.EvalVariableInScope(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Expr, *api.LoadConfigToProc(cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Variable = v
|
||||
out.Variable = api.ConvertVar(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -484,7 +506,7 @@ type SetOut struct {
|
||||
// Set sets the value of a variable. Only numerical types and
|
||||
// pointers are currently supported.
|
||||
func (s *RPCServer) Set(arg SetIn, out *SetOut) error {
|
||||
return s.debugger.SetVariableInScope(arg.Scope, arg.Symbol, arg.Value)
|
||||
return s.debugger.SetVariableInScope(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Symbol, arg.Value)
|
||||
}
|
||||
|
||||
type ListSourcesIn struct {
|
||||
@ -562,7 +584,9 @@ func (s *RPCServer) ListGoroutines(arg ListGoroutinesIn, out *ListGoroutinesOut)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Goroutines = gs
|
||||
s.debugger.LockTarget()
|
||||
defer s.debugger.UnlockTarget()
|
||||
out.Goroutines = api.ConvertGoroutines(gs)
|
||||
out.Nextg = nextg
|
||||
return nil
|
||||
}
|
||||
@ -607,7 +631,7 @@ type FindLocationOut struct {
|
||||
// NOTE: this function does not actually set breakpoints.
|
||||
func (c *RPCServer) FindLocation(arg FindLocationIn, out *FindLocationOut) error {
|
||||
var err error
|
||||
out.Locations, err = c.debugger.FindLocation(arg.Scope, arg.Loc, arg.IncludeNonExecutableLines)
|
||||
out.Locations, err = c.debugger.FindLocation(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Loc, arg.IncludeNonExecutableLines)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -630,8 +654,15 @@ type DisassembleOut struct {
|
||||
// Disassemble will also try to calculate the destination address of an absolute indirect CALL if it happens to be the instruction the selected goroutine is stopped at.
|
||||
func (c *RPCServer) Disassemble(arg DisassembleIn, out *DisassembleOut) error {
|
||||
var err error
|
||||
out.Disassemble, err = c.debugger.Disassemble(arg.Scope.GoroutineID, arg.StartPC, arg.EndPC, arg.Flavour)
|
||||
return err
|
||||
insts, err := c.debugger.Disassemble(arg.Scope.GoroutineID, arg.StartPC, arg.EndPC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Disassemble = make(api.AsmInstructions, len(insts))
|
||||
for i := range insts {
|
||||
out.Disassemble[i] = api.ConvertAsmInstruction(insts[i], c.debugger.AsmInstructionText(&insts[i], proc.AssemblyFlavour(arg.Flavour)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RecordedIn struct {
|
||||
@ -670,8 +701,15 @@ type ListCheckpointsOut struct {
|
||||
|
||||
func (s *RPCServer) ListCheckpoints(arg ListCheckpointsIn, out *ListCheckpointsOut) error {
|
||||
var err error
|
||||
out.Checkpoints, err = s.debugger.Checkpoints()
|
||||
return err
|
||||
cps, err := s.debugger.Checkpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Checkpoints = make([]api.Checkpoint, len(cps))
|
||||
for i := range cps {
|
||||
out.Checkpoints[i] = api.Checkpoint(cps[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ClearCheckpointIn struct {
|
||||
@ -740,7 +778,11 @@ type ListDynamicLibrariesOut struct {
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListDynamicLibraries(in ListDynamicLibrariesIn, out *ListDynamicLibrariesOut) error {
|
||||
out.List = s.debugger.ListDynamicLibraries()
|
||||
imgs := s.debugger.ListDynamicLibraries()
|
||||
out.List = make([]api.Image, 0, len(imgs))
|
||||
for i := range imgs {
|
||||
out.List[i] = api.ConvertImage(imgs[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -760,7 +802,26 @@ type ListPackagesBuildInfoOut struct {
|
||||
// Note that the directory path is a best guess and may be wrong is a tool
|
||||
// other than cmd/go is used to perform the build.
|
||||
func (s *RPCServer) ListPackagesBuildInfo(in ListPackagesBuildInfoIn, out *ListPackagesBuildInfoOut) error {
|
||||
out.List = s.debugger.ListPackagesBuildInfo(in.IncludeFiles)
|
||||
pkgs := s.debugger.ListPackagesBuildInfo(in.IncludeFiles)
|
||||
out.List = make([]api.PackageBuildInfo, 0, len(pkgs))
|
||||
for _, pkg := range pkgs {
|
||||
var files []string
|
||||
|
||||
if len(pkg.Files) > 0 {
|
||||
files = make([]string, 0, len(pkg.Files))
|
||||
for file := range pkg.Files {
|
||||
files = append(files, file)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(files)
|
||||
|
||||
out.List = append(out.List, api.PackageBuildInfo{
|
||||
ImportPath: pkg.ImportPath,
|
||||
DirectoryPath: pkg.DirectoryPath,
|
||||
Files: files,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user