1
0
mirror of https://github.com/beego/bee.git synced 2025-07-01 17:20:17 +00:00

Updated vendor

This commit is contained in:
Ruben Cid
2019-04-15 16:43:01 +02:00
parent 33dd897647
commit da91d50ab8
134 changed files with 4988 additions and 2530 deletions

View File

@ -0,0 +1,322 @@
package api
import (
"bytes"
"fmt"
"go/constant"
"go/printer"
"go/token"
"reflect"
"strconv"
"github.com/go-delve/delve/pkg/dwarf/godwarf"
"github.com/go-delve/delve/pkg/proc"
)
// ConvertBreakpoint converts from a proc.Breakpoint to
// an api.Breakpoint.
func ConvertBreakpoint(bp *proc.Breakpoint) *Breakpoint {
b := &Breakpoint{
Name: bp.Name,
ID: bp.ID,
FunctionName: bp.FunctionName,
File: bp.File,
Line: bp.Line,
Addr: bp.Addr,
Tracepoint: bp.Tracepoint,
TraceReturn: bp.TraceReturn,
Stacktrace: bp.Stacktrace,
Goroutine: bp.Goroutine,
Variables: bp.Variables,
LoadArgs: LoadConfigFromProc(bp.LoadArgs),
LoadLocals: LoadConfigFromProc(bp.LoadLocals),
TotalHitCount: bp.TotalHitCount,
}
b.HitCount = map[string]uint64{}
for idx := range bp.HitCount {
b.HitCount[strconv.Itoa(idx)] = bp.HitCount[idx]
}
var buf bytes.Buffer
printer.Fprint(&buf, token.NewFileSet(), bp.Cond)
b.Cond = buf.String()
return b
}
// ConvertThread converts a proc.Thread into an
// api thread.
func ConvertThread(th proc.Thread) *Thread {
var (
function *Function
file string
line int
pc uint64
gid int
)
loc, err := th.Location()
if err == nil {
pc = loc.PC
file = loc.File
line = loc.Line
function = ConvertFunction(loc.Fn)
}
var bp *Breakpoint
if b := th.Breakpoint(); b.Active {
bp = ConvertBreakpoint(b.Breakpoint)
}
if g, _ := proc.GetG(th); g != nil {
gid = g.ID
}
return &Thread{
ID: th.ThreadID(),
PC: pc,
File: file,
Line: line,
Function: function,
GoroutineID: gid,
Breakpoint: bp,
}
}
func prettyTypeName(typ godwarf.Type) string {
if typ == nil {
return ""
}
if typ.Common().Name != "" {
return typ.Common().Name
}
r := typ.String()
if r == "*void" {
return "unsafe.Pointer"
}
return r
}
func convertFloatValue(v *proc.Variable, sz int) string {
switch v.FloatSpecial {
case proc.FloatIsPosInf:
return "+Inf"
case proc.FloatIsNegInf:
return "-Inf"
case proc.FloatIsNaN:
return "NaN"
}
f, _ := constant.Float64Val(v.Value)
return strconv.FormatFloat(f, 'f', -1, sz)
}
// ConvertVar converts from proc.Variable to api.Variable.
func ConvertVar(v *proc.Variable) *Variable {
r := Variable{
Addr: v.Addr,
OnlyAddr: v.OnlyAddr,
Name: v.Name,
Kind: v.Kind,
Len: v.Len,
Cap: v.Cap,
Flags: VariableFlags(v.Flags),
Base: v.Base,
LocationExpr: v.LocationExpr,
DeclLine: v.DeclLine,
}
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()
}
}
}
switch v.Kind {
case reflect.Complex64:
r.Children = make([]Variable, 2)
r.Len = 2
r.Children[0].Name = "real"
r.Children[0].Kind = reflect.Float32
r.Children[1].Name = "imaginary"
r.Children[1].Kind = reflect.Float32
if v.Value != nil {
real, _ := constant.Float64Val(constant.Real(v.Value))
r.Children[0].Value = strconv.FormatFloat(real, 'f', -1, 32)
imag, _ := constant.Float64Val(constant.Imag(v.Value))
r.Children[1].Value = strconv.FormatFloat(imag, 'f', -1, 32)
} else {
r.Children[0].Value = "nil"
r.Children[1].Value = "nil"
}
case reflect.Complex128:
r.Children = make([]Variable, 2)
r.Len = 2
r.Children[0].Name = "real"
r.Children[0].Kind = reflect.Float64
r.Children[1].Name = "imaginary"
r.Children[1].Kind = reflect.Float64
if v.Value != nil {
real, _ := constant.Float64Val(constant.Real(v.Value))
r.Children[0].Value = strconv.FormatFloat(real, 'f', -1, 64)
imag, _ := constant.Float64Val(constant.Imag(v.Value))
r.Children[1].Value = strconv.FormatFloat(imag, 'f', -1, 64)
} else {
r.Children[0].Value = "nil"
r.Children[1].Value = "nil"
}
default:
r.Children = make([]Variable, len(v.Children))
for i := range v.Children {
r.Children[i] = *ConvertVar(&v.Children[i])
}
}
return &r
}
// ConvertFunction converts from gosym.Func to
// api.Function.
func ConvertFunction(fn *proc.Function) *Function {
if fn == nil {
return nil
}
// fn here used to be a *gosym.Func, the fields Type and GoType below
// corresponded to the homonymous field of gosym.Func. Since the contents of
// those fields is not documented their value was replaced with 0 when
// gosym.Func was replaced by debug_info entries.
return &Function{
Name_: fn.Name,
Type: 0,
Value: fn.Entry,
GoType: 0,
Optimized: fn.Optimized(),
}
}
// ConvertGoroutine converts from proc.G to api.Goroutine.
func ConvertGoroutine(g *proc.G) *Goroutine {
th := g.Thread
tid := 0
if th != nil {
tid = th.ThreadID()
}
r := &Goroutine{
ID: g.ID,
CurrentLoc: ConvertLocation(g.CurrentLoc),
UserCurrentLoc: ConvertLocation(g.UserCurrent()),
GoStatementLoc: ConvertLocation(g.Go()),
StartLoc: ConvertLocation(g.StartLoc()),
ThreadID: tid,
}
if g.Unreadable != nil {
r.Unreadable = g.Unreadable.Error()
}
return r
}
// ConvertLocation converts from proc.Location to api.Location.
func ConvertLocation(loc proc.Location) Location {
return Location{
PC: loc.PC,
File: loc.File,
Line: loc.Line,
Function: ConvertFunction(loc.Fn),
}
}
// ConvertAsmInstruction converts from proc.AsmInstruction to api.AsmInstruction.
func ConvertAsmInstruction(inst proc.AsmInstruction, text string) AsmInstruction {
var destloc *Location
if inst.DestLoc != nil {
r := ConvertLocation(*inst.DestLoc)
destloc = &r
}
return AsmInstruction{
Loc: ConvertLocation(inst.Loc),
DestLoc: destloc,
Text: text,
Bytes: inst.Bytes,
Breakpoint: inst.Breakpoint,
AtPC: inst.AtPC,
}
}
// LoadConfigToProc converts an api.LoadConfig to proc.LoadConfig.
func LoadConfigToProc(cfg *LoadConfig) *proc.LoadConfig {
if cfg == nil {
return nil
}
return &proc.LoadConfig{
cfg.FollowPointers,
cfg.MaxVariableRecurse,
cfg.MaxStringLen,
cfg.MaxArrayValues,
cfg.MaxStructFields,
0, // MaxMapBuckets is set internally by pkg/proc, read its documentation for an explanation.
}
}
// LoadConfigFromProc converts a proc.LoadConfig to api.LoadConfig.
func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig {
if cfg == nil {
return nil
}
return &LoadConfig{
cfg.FollowPointers,
cfg.MaxVariableRecurse,
cfg.MaxStringLen,
cfg.MaxArrayValues,
cfg.MaxStructFields,
}
}
// ConvertRegisters converts proc.Register to api.Register for a slice.
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
}
// ConvertCheckpoint converts proc.Chekcpoint to api.Checkpoint.
func ConvertCheckpoint(in proc.Checkpoint) (out Checkpoint) {
return Checkpoint(in)
}
func ConvertImage(image *proc.Image) Image {
return Image{Path: image.Path}
}

View File

@ -0,0 +1,356 @@
package api
import (
"bytes"
"fmt"
"io"
"reflect"
"strings"
)
const (
// strings longer than this will cause slices, arrays and structs to be printed on multiple lines when newlines is enabled
maxShortStringLen = 7
// string used for one indentation level (when printing on multiple lines)
indentString = "\t"
)
// SinglelineString returns a representation of v on a single line.
func (v *Variable) SinglelineString() string {
var buf bytes.Buffer
v.writeTo(&buf, true, false, true, "")
return buf.String()
}
// MultilineString returns a representation of v on multiple lines.
func (v *Variable) MultilineString(indent string) string {
var buf bytes.Buffer
v.writeTo(&buf, true, true, true, indent)
return buf.String()
}
func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, indent string) {
if v.Unreadable != "" {
fmt.Fprintf(buf, "(unreadable %s)", v.Unreadable)
return
}
if !top && v.Addr == 0 && v.Value == "" {
if includeType && v.Type != "void" {
fmt.Fprintf(buf, "%s nil", v.Type)
} else {
fmt.Fprint(buf, "nil")
}
return
}
switch v.Kind {
case reflect.Slice:
v.writeSliceTo(buf, newlines, includeType, indent)
case reflect.Array:
v.writeArrayTo(buf, newlines, includeType, indent)
case reflect.Ptr:
if v.Type == "" || len(v.Children) == 0 {
fmt.Fprint(buf, "nil")
} else if v.Children[0].OnlyAddr && v.Children[0].Addr != 0 {
if strings.Contains(v.Type, "/") {
fmt.Fprintf(buf, "(%q)(%#x)", v.Type, v.Children[0].Addr)
} else {
fmt.Fprintf(buf, "(%s)(%#x)", v.Type, v.Children[0].Addr)
}
} else {
fmt.Fprint(buf, "*")
v.Children[0].writeTo(buf, false, newlines, includeType, indent)
}
case reflect.UnsafePointer:
if len(v.Children) == 0 {
fmt.Fprintf(buf, "unsafe.Pointer(nil)")
} else {
fmt.Fprintf(buf, "unsafe.Pointer(%#x)", v.Children[0].Addr)
}
case reflect.String:
v.writeStringTo(buf)
case reflect.Chan:
if newlines {
v.writeStructTo(buf, newlines, includeType, indent)
} else {
if len(v.Children) == 0 {
fmt.Fprintf(buf, "%s nil", v.Type)
} else {
fmt.Fprintf(buf, "%s %s/%s", v.Type, v.Children[0].Value, v.Children[1].Value)
}
}
case reflect.Struct:
v.writeStructTo(buf, newlines, includeType, indent)
case reflect.Interface:
if v.Addr == 0 {
// an escaped interface variable that points to nil, this shouldn't
// happen in normal code but can happen if the variable is out of scope.
fmt.Fprintf(buf, "nil")
return
}
if includeType {
if v.Children[0].Kind == reflect.Invalid {
fmt.Fprintf(buf, "%s ", v.Type)
if v.Children[0].Addr == 0 {
fmt.Fprint(buf, "nil")
return
}
} else {
fmt.Fprintf(buf, "%s(%s) ", v.Type, v.Children[0].Type)
}
}
data := v.Children[0]
if data.Kind == reflect.Ptr {
if len(data.Children) == 0 {
fmt.Fprint(buf, "...")
} else if data.Children[0].Addr == 0 {
fmt.Fprint(buf, "nil")
} else if data.Children[0].OnlyAddr {
fmt.Fprintf(buf, "0x%x", v.Children[0].Addr)
} else {
v.Children[0].writeTo(buf, false, newlines, !includeType, indent)
}
} else if data.OnlyAddr {
if strings.Contains(v.Type, "/") {
fmt.Fprintf(buf, "*(*%q)(%#x)", v.Type, v.Addr)
} else {
fmt.Fprintf(buf, "*(*%s)(%#x)", v.Type, v.Addr)
}
} else {
v.Children[0].writeTo(buf, false, newlines, !includeType, indent)
}
case reflect.Map:
v.writeMapTo(buf, newlines, includeType, indent)
case reflect.Func:
if v.Value == "" {
fmt.Fprint(buf, "nil")
} else {
fmt.Fprintf(buf, "%s", v.Value)
}
case reflect.Complex64, reflect.Complex128:
fmt.Fprintf(buf, "(%s + %si)", v.Children[0].Value, v.Children[1].Value)
default:
if v.Value != "" {
buf.Write([]byte(v.Value))
} else {
fmt.Fprintf(buf, "(unknown %s)", v.Kind)
}
}
}
func (v *Variable) writeStringTo(buf io.Writer) {
s := v.Value
if len(s) != int(v.Len) {
s = fmt.Sprintf("%s...+%d more", s, int(v.Len)-len(s))
}
fmt.Fprintf(buf, "%q", s)
}
func (v *Variable) writeSliceTo(buf io.Writer, newlines, includeType bool, indent string) {
if includeType {
fmt.Fprintf(buf, "%s len: %d, cap: %d, ", v.Type, v.Len, v.Cap)
}
if v.Base == 0 && len(v.Children) == 0 {
fmt.Fprintf(buf, "nil")
return
}
v.writeSliceOrArrayTo(buf, newlines, indent)
}
func (v *Variable) writeArrayTo(buf io.Writer, newlines, includeType bool, indent string) {
if includeType {
fmt.Fprintf(buf, "%s ", v.Type)
}
v.writeSliceOrArrayTo(buf, newlines, indent)
}
func (v *Variable) writeStructTo(buf io.Writer, newlines, includeType bool, indent string) {
if int(v.Len) != len(v.Children) && len(v.Children) == 0 {
if strings.Contains(v.Type, "/") {
fmt.Fprintf(buf, "(*%q)(%#x)", v.Type, v.Addr)
} else {
fmt.Fprintf(buf, "(*%s)(%#x)", v.Type, v.Addr)
}
return
}
if includeType {
fmt.Fprintf(buf, "%s ", v.Type)
}
nl := v.shouldNewlineStruct(newlines)
fmt.Fprint(buf, "{")
for i := range v.Children {
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
}
fmt.Fprintf(buf, "%s: ", v.Children[i].Name)
v.Children[i].writeTo(buf, false, nl, true, indent+indentString)
if i != len(v.Children)-1 || nl {
fmt.Fprint(buf, ",")
if !nl {
fmt.Fprint(buf, " ")
}
}
}
if len(v.Children) != int(v.Len) {
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
} else {
fmt.Fprint(buf, ",")
}
fmt.Fprintf(buf, "...+%d more", int(v.Len)-len(v.Children))
}
fmt.Fprint(buf, "}")
}
func (v *Variable) writeMapTo(buf io.Writer, newlines, includeType bool, indent string) {
if includeType {
fmt.Fprintf(buf, "%s ", v.Type)
}
if v.Base == 0 && len(v.Children) == 0 {
fmt.Fprintf(buf, "nil")
return
}
nl := newlines && (len(v.Children) > 0)
fmt.Fprint(buf, "[")
for i := 0; i < len(v.Children); i += 2 {
key := &v.Children[i]
value := &v.Children[i+1]
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
}
key.writeTo(buf, false, false, false, indent+indentString)
fmt.Fprint(buf, ": ")
value.writeTo(buf, false, nl, false, indent+indentString)
if i != len(v.Children)-1 || nl {
fmt.Fprint(buf, ", ")
}
}
if len(v.Children)/2 != int(v.Len) {
if len(v.Children) != 0 {
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
} else {
fmt.Fprint(buf, ",")
}
fmt.Fprintf(buf, "...+%d more", int(v.Len)-(len(v.Children)/2))
} else {
fmt.Fprint(buf, "...")
}
}
if nl {
fmt.Fprintf(buf, "\n%s", indent)
}
fmt.Fprint(buf, "]")
}
func (v *Variable) shouldNewlineArray(newlines bool) bool {
if !newlines || len(v.Children) == 0 {
return false
}
kind, hasptr := (&v.Children[0]).recursiveKind()
switch kind {
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map, reflect.Interface:
return true
case reflect.String:
if hasptr {
return true
}
for i := range v.Children {
if len(v.Children[i].Value) > maxShortStringLen {
return true
}
}
return false
default:
return false
}
}
func (v *Variable) recursiveKind() (reflect.Kind, bool) {
hasptr := false
var kind reflect.Kind
for {
kind = v.Kind
if kind == reflect.Ptr {
hasptr = true
v = &(v.Children[0])
} else {
break
}
}
return kind, hasptr
}
func (v *Variable) shouldNewlineStruct(newlines bool) bool {
if !newlines || len(v.Children) == 0 {
return false
}
for i := range v.Children {
kind, hasptr := (&v.Children[i]).recursiveKind()
switch kind {
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map, reflect.Interface:
return true
case reflect.String:
if hasptr {
return true
}
if len(v.Children[i].Value) > maxShortStringLen {
return true
}
}
}
return false
}
func (v *Variable) writeSliceOrArrayTo(buf io.Writer, newlines bool, indent string) {
nl := v.shouldNewlineArray(newlines)
fmt.Fprint(buf, "[")
for i := range v.Children {
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
}
v.Children[i].writeTo(buf, false, nl, false, indent+indentString)
if i != len(v.Children)-1 || nl {
fmt.Fprint(buf, ",")
}
}
if len(v.Children) != int(v.Len) {
if len(v.Children) != 0 {
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
} else {
fmt.Fprint(buf, ",")
}
fmt.Fprintf(buf, "...+%d more", int(v.Len)-len(v.Children))
} else {
fmt.Fprint(buf, "...")
}
}
if nl {
fmt.Fprintf(buf, "\n%s", indent)
}
fmt.Fprint(buf, "]")
}

457
vendor/github.com/go-delve/delve/service/api/types.go generated vendored Normal file
View File

@ -0,0 +1,457 @@
package api
import (
"bytes"
"errors"
"fmt"
"reflect"
"strconv"
"unicode"
"github.com/go-delve/delve/pkg/proc"
)
// ErrNotExecutable is an error returned when trying
// to debug a non-executable file.
var ErrNotExecutable = proc.ErrNotExecutable
// DebuggerState represents the current context of the debugger.
type DebuggerState struct {
// Running is true if the process is running and no other information can be collected.
Running bool
// CurrentThread is the currently selected debugger thread.
CurrentThread *Thread `json:"currentThread,omitempty"`
// SelectedGoroutine is the currently selected goroutine
SelectedGoroutine *Goroutine `json:"currentGoroutine,omitempty"`
// List of all the process threads
Threads []*Thread
// NextInProgress indicates that a next or step operation was interrupted by another breakpoint
// or a manual stop and is waiting to complete.
// While NextInProgress is set further requests for next or step may be rejected.
// Either execute continue until NextInProgress is false or call CancelNext
NextInProgress bool
// Exited indicates whether the debugged process has exited.
Exited bool `json:"exited"`
ExitStatus int `json:"exitStatus"`
// When contains a description of the current position in a recording
When string
// Filled by RPCClient.Continue, indicates an error
Err error `json:"-"`
}
// Breakpoint addresses a location at which process execution may be
// suspended.
type Breakpoint struct {
// ID is a unique identifier for the breakpoint.
ID int `json:"id"`
// User defined name of the breakpoint
Name string `json:"name"`
// Addr is the address of the breakpoint.
Addr uint64 `json:"addr"`
// File is the source file for the breakpoint.
File string `json:"file"`
// Line is a line in File for the breakpoint.
Line int `json:"line"`
// FunctionName is the name of the function at the current breakpoint, and
// may not always be available.
FunctionName string `json:"functionName,omitempty"`
// Breakpoint condition
Cond string
// Tracepoint flag, signifying this is a tracepoint.
Tracepoint bool `json:"continue"`
// TraceReturn flag signifying this is a breakpoint set at a return
// statement in a traced function.
TraceReturn bool `json:"traceReturn"`
// retrieve goroutine information
Goroutine bool `json:"goroutine"`
// number of stack frames to retrieve
Stacktrace int `json:"stacktrace"`
// expressions to evaluate
Variables []string `json:"variables,omitempty"`
// LoadArgs requests loading function arguments when the breakpoint is hit
LoadArgs *LoadConfig
// LoadLocals requests loading function locals when the breakpoint is hit
LoadLocals *LoadConfig
// number of times a breakpoint has been reached in a certain goroutine
HitCount map[string]uint64 `json:"hitCount"`
// number of times a breakpoint has been reached
TotalHitCount uint64 `json:"totalHitCount"`
}
// ValidBreakpointName returns an error if
// the name to be chosen for a breakpoint is invalid.
// The name can not be just a number, and must contain a series
// of letters or numbers.
func ValidBreakpointName(name string) error {
if _, err := strconv.Atoi(name); err == nil {
return errors.New("breakpoint name can not be a number")
}
for _, ch := range name {
if !(unicode.IsLetter(ch) || unicode.IsDigit(ch)) {
return fmt.Errorf("invalid character in breakpoint name '%c'", ch)
}
}
return nil
}
// Thread is a thread within the debugged process.
type Thread struct {
// ID is a unique identifier for the thread.
ID int `json:"id"`
// PC is the current program counter for the thread.
PC uint64 `json:"pc"`
// File is the file for the program counter.
File string `json:"file"`
// Line is the line number for the program counter.
Line int `json:"line"`
// Function is function information at the program counter. May be nil.
Function *Function `json:"function,omitempty"`
// ID of the goroutine running on this thread
GoroutineID int `json:"goroutineID"`
// Breakpoint this thread is stopped at
Breakpoint *Breakpoint `json:"breakPoint,omitempty"`
// Informations requested by the current breakpoint
BreakpointInfo *BreakpointInfo `json:"breakPointInfo,omitempty"`
// ReturnValues contains the return values of the function we just stepped out of
ReturnValues []Variable
}
// Location holds program location information.
type Location struct {
PC uint64 `json:"pc"`
File string `json:"file"`
Line int `json:"line"`
Function *Function `json:"function,omitempty"`
}
// Stackframe describes one frame in a stack trace.
type Stackframe struct {
Location
Locals []Variable
Arguments []Variable
FrameOffset int64
FramePointerOffset int64
Defers []Defer
Bottom bool `json:"Bottom,omitempty"` // Bottom is true if this is the bottom frame of the stack
Err string
}
// Defer describes a deferred function.
type Defer struct {
DeferredLoc Location // deferred function
DeferLoc Location // location of the defer statement
SP uint64 // value of SP when the function was deferred
Unreadable string
}
// Var will return the variable described by 'name' within
// this stack frame.
func (frame *Stackframe) Var(name string) *Variable {
for i := range frame.Locals {
if frame.Locals[i].Name == name {
return &frame.Locals[i]
}
}
for i := range frame.Arguments {
if frame.Arguments[i].Name == name {
return &frame.Arguments[i]
}
}
return nil
}
// Function represents thread-scoped function information.
type Function struct {
// Name is the function name.
Name_ string `json:"name"`
Value uint64 `json:"value"`
Type byte `json:"type"`
GoType uint64 `json:"goType"`
// Optimized is true if the function was optimized
Optimized bool `json:"optimized"`
}
// Name will return the function name.
func (fn *Function) Name() string {
if fn == nil {
return "???"
}
return fn.Name_
}
// VariableFlags is the type of the Flags field of Variable.
type VariableFlags uint16
const (
// VariableEscaped is set for local variables that escaped to the heap
//
// The compiler performs escape analysis on local variables, the variables
// that may outlive the stack frame are allocated on the heap instead and
// only the address is recorded on the stack. These variables will be
// marked with this flag.
VariableEscaped = (1 << iota)
// VariableShadowed is set for local variables that are shadowed by a
// variable with the same name in another scope
VariableShadowed
// VariableConstant means this variable is a constant value
VariableConstant
// VariableArgument means this variable is a function argument
VariableArgument
// VariableReturnArgument means this variable is a function return value
VariableReturnArgument
)
// Variable describes a variable.
type Variable struct {
// Name of the variable or struct member
Name string `json:"name"`
// Address of the variable or struct member
Addr uintptr `json:"addr"`
// Only the address field is filled (result of evaluating expressions like &<expr>)
OnlyAddr bool `json:"onlyAddr"`
// Go type of the variable
Type string `json:"type"`
// Type of the variable after resolving any typedefs
RealType string `json:"realType"`
Flags VariableFlags `json:"flags"`
Kind reflect.Kind `json:"kind"`
//Strings have their length capped at proc.maxArrayValues, use Len for the real length of a string
//Function variables will store the name of the function in this field
Value string `json:"value"`
// Number of elements in an array or a slice, number of keys for a map, number of struct members for a struct, length of strings
Len int64 `json:"len"`
// Cap value for slices
Cap int64 `json:"cap"`
// Array and slice elements, member fields of structs, key/value pairs of maps, value of complex numbers
// The Name field in this slice will always be the empty string except for structs (when it will be the field name) and for complex numbers (when it will be "real" and "imaginary")
// For maps each map entry will have to items in this slice, even numbered items will represent map keys and odd numbered items will represent their values
// This field's length is capped at proc.maxArrayValues for slices and arrays and 2*proc.maxArrayValues for maps, in the circumstances where the cap takes effect len(Children) != Len
// The other length cap applied to this field is related to maximum recursion depth, when the maximum recursion depth is reached this field is left empty, contrary to the previous one this cap also applies to structs (otherwise structs will always have all their member fields returned)
Children []Variable `json:"children"`
// Base address of arrays, Base address of the backing array for slices (0 for nil slices)
// Base address of the backing byte array for strings
// address of the struct backing chan and map variables
// address of the function entry point for function variables (0 for nil function pointers)
Base uintptr `json:"base"`
// Unreadable addresses will have this field set
Unreadable string `json:"unreadable"`
// LocationExpr describes the location expression of this variable's address
LocationExpr string
// DeclLine is the line number of this variable's declaration
DeclLine int64
}
// LoadConfig describes how to load values from target's memory
type LoadConfig struct {
// FollowPointers requests pointers to be automatically dereferenced.
FollowPointers bool
// MaxVariableRecurse is how far to recurse when evaluating nested types.
MaxVariableRecurse int
// MaxStringLen is the maximum number of bytes read from a string
MaxStringLen int
// MaxArrayValues is the maximum number of elements read from an array, a slice or a map.
MaxArrayValues int
// MaxStructFields is the maximum number of fields read from a struct, -1 will read all fields.
MaxStructFields int
}
// Goroutine represents the information relevant to Delve from the runtime's
// internal G structure.
type Goroutine struct {
// ID is a unique identifier for the goroutine.
ID int `json:"id"`
// Current location of the goroutine
CurrentLoc Location `json:"currentLoc"`
// Current location of the goroutine, excluding calls inside runtime
UserCurrentLoc Location `json:"userCurrentLoc"`
// Location of the go instruction that started this goroutine
GoStatementLoc Location `json:"goStatementLoc"`
// Location of the starting function
StartLoc Location `json:"startLoc"`
// ID of the associated thread for running goroutines
ThreadID int `json:"threadID"`
Unreadable string `json:"unreadable"`
}
// DebuggerCommand is a command which changes the debugger's execution state.
type DebuggerCommand struct {
// Name is the command to run.
Name string `json:"name"`
// ThreadID is used to specify which thread to use with the SwitchThread
// command.
ThreadID int `json:"threadID,omitempty"`
// GoroutineID is used to specify which thread to use with the SwitchGoroutine
// command.
GoroutineID int `json:"goroutineID,omitempty"`
// When ReturnInfoLoadConfig is not nil it will be used to load the value
// of any return variables.
ReturnInfoLoadConfig *LoadConfig
// Expr is the expression argument for a Call command
Expr string `json:"expr,omitempty"`
// UnsafeCall disabled parameter escape checking for function calls
UnsafeCall bool `json:"unsafeCall,omitempty"`
}
// BreakpointInfo contains informations about the current breakpoint
type BreakpointInfo struct {
Stacktrace []Stackframe `json:"stacktrace,omitempty"`
Goroutine *Goroutine `json:"goroutine,omitempty"`
Variables []Variable `json:"variables,omitempty"`
Arguments []Variable `json:"arguments,omitempty"`
Locals []Variable `json:"locals,omitempty"`
}
// EvalScope is the scope a command should
// be evaluated in. Describes the goroutine and frame number.
type EvalScope struct {
GoroutineID int
Frame int
DeferredCall int // when DeferredCall is n > 0 this eval scope is relative to the n-th deferred call in the current frame
}
const (
// Continue resumes process execution.
Continue = "continue"
// Rewind resumes process execution backwards (target must be a recording).
Rewind = "rewind"
// Step continues to next source line, entering function calls.
Step = "step"
// StepOut continues to the return address of the current function
StepOut = "stepOut"
// StepInstruction continues for exactly 1 cpu instruction.
StepInstruction = "stepInstruction"
// Next continues to the next source line, not entering function calls.
Next = "next"
// SwitchThread switches the debugger's current thread context.
SwitchThread = "switchThread"
// SwitchGoroutine switches the debugger's current thread context to the thread running the specified goroutine
SwitchGoroutine = "switchGoroutine"
// Halt suspends the process.
Halt = "halt"
// Call resumes process execution injecting a function call.
Call = "call"
)
// AssemblyFlavour describes the output
// of disassembled code.
type AssemblyFlavour int
const (
// GNUFlavour will disassemble using GNU assembly syntax.
GNUFlavour = AssemblyFlavour(proc.GNUFlavour)
// IntelFlavour will disassemble using Intel assembly syntax.
IntelFlavour = AssemblyFlavour(proc.IntelFlavour)
)
// AsmInstruction represents one assembly instruction at some address
type AsmInstruction struct {
// Loc is the location of this instruction
Loc Location
// Destination of CALL instructions
DestLoc *Location
// Text is the formatted representation of the instruction
Text string
// Bytes is the instruction as read from memory
Bytes []byte
// If Breakpoint is true a breakpoint is set at this instruction
Breakpoint bool
// In AtPC is true this is the instruction the current thread is stopped at
AtPC bool
}
// AsmInstructions is a slice of single instructions.
type AsmInstructions []AsmInstruction
// GetVersionIn is the argument for GetVersion.
type GetVersionIn struct {
}
// GetVersionOut is the result of GetVersion.
type GetVersionOut struct {
DelveVersion string
APIVersion int
}
// SetAPIVersionIn is the input for SetAPIVersion.
type SetAPIVersionIn struct {
APIVersion int
}
// SetAPIVersionOut is the output for SetAPIVersion.
type SetAPIVersionOut struct {
}
// Register holds information on a CPU register.
type Register struct {
Name string
Value string
}
// Registers is a list of CPU registers.
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()
}
// DiscardedBreakpoint is a breakpoint that is not
// reinstated during a restart.
type DiscardedBreakpoint struct {
Breakpoint *Breakpoint
Reason string
}
// Checkpoint is a point in the program that
// can be returned to in certain execution modes.
type Checkpoint struct {
ID int
When string
Where string
}
// Image represents a loaded shared object (go plugin or shared library)
type Image struct {
Path string
}
// Ancestor represents a goroutine ancestor
type Ancestor struct {
ID int64
Stack []Stackframe
Unreadable string
}

150
vendor/github.com/go-delve/delve/service/client.go generated vendored Normal file
View File

@ -0,0 +1,150 @@
package service
import (
"time"
"github.com/go-delve/delve/service/api"
)
// Client represents a debugger service client. All client methods are
// synchronous.
type Client interface {
// Returns the pid of the process we are debugging.
ProcessPid() int
// LastModified returns the time that the process' executable was modified.
LastModified() time.Time
// Detach detaches the debugger, optionally killing the process.
Detach(killProcess bool) error
// Restarts program.
Restart() ([]api.DiscardedBreakpoint, error)
// Restarts program from the specified position.
RestartFrom(pos string, resetArgs bool, newArgs []string) ([]api.DiscardedBreakpoint, error)
// GetState returns the current debugger state.
GetState() (*api.DebuggerState, error)
// GetStateNonBlocking returns the current debugger state, returning immediately if the target is already running.
GetStateNonBlocking() (*api.DebuggerState, error)
// Continue resumes process execution.
Continue() <-chan *api.DebuggerState
// Rewind resumes process execution backwards.
Rewind() <-chan *api.DebuggerState
// Next continues to the next source line, not entering function calls.
Next() (*api.DebuggerState, error)
// Step continues to the next source line, entering function calls.
Step() (*api.DebuggerState, error)
// StepOut continues to the return address of the current function
StepOut() (*api.DebuggerState, error)
// Call resumes process execution while making a function call.
Call(expr string, unsafe bool) (*api.DebuggerState, error)
// SingleStep will step a single cpu instruction.
StepInstruction() (*api.DebuggerState, error)
// SwitchThread switches the current thread context.
SwitchThread(threadID int) (*api.DebuggerState, error)
// SwitchGoroutine switches the current goroutine (and the current thread as well)
SwitchGoroutine(goroutineID int) (*api.DebuggerState, error)
// Halt suspends the process.
Halt() (*api.DebuggerState, error)
// GetBreakpoint gets a breakpoint by ID.
GetBreakpoint(id int) (*api.Breakpoint, error)
// GetBreakpointByName gets a breakpoint by name.
GetBreakpointByName(name string) (*api.Breakpoint, error)
// CreateBreakpoint creates a new breakpoint.
CreateBreakpoint(*api.Breakpoint) (*api.Breakpoint, error)
// ListBreakpoints gets all breakpoints.
ListBreakpoints() ([]*api.Breakpoint, error)
// ClearBreakpoint deletes a breakpoint by ID.
ClearBreakpoint(id int) (*api.Breakpoint, error)
// ClearBreakpointByName deletes a breakpoint by name
ClearBreakpointByName(name string) (*api.Breakpoint, error)
// Allows user to update an existing breakpoint for example to change the information
// retrieved when the breakpoint is hit or to change, add or remove the break condition
AmendBreakpoint(*api.Breakpoint) error
// Cancels a Next or Step call that was interrupted by a manual stop or by another breakpoint
CancelNext() error
// ListThreads lists all threads.
ListThreads() ([]*api.Thread, error)
// GetThread gets a thread by its ID.
GetThread(id int) (*api.Thread, error)
// ListPackageVariables lists all package variables in the context of the current thread.
ListPackageVariables(filter string, cfg api.LoadConfig) ([]api.Variable, error)
// EvalVariable returns a variable in the context of the current thread.
EvalVariable(scope api.EvalScope, symbol string, cfg api.LoadConfig) (*api.Variable, error)
// SetVariable sets the value of a variable
SetVariable(scope api.EvalScope, symbol, value string) error
// ListSources lists all source files in the process matching filter.
ListSources(filter string) ([]string, error)
// ListFunctions lists all functions in the process matching filter.
ListFunctions(filter string) ([]string, error)
// ListTypes lists all types in the process matching filter.
ListTypes(filter string) ([]string, error)
// ListLocals lists all local variables in scope.
ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error)
// 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(threadID int, includeFp bool) (api.Registers, error)
// ListGoroutines lists all goroutines.
ListGoroutines(start, count int) ([]*api.Goroutine, int, error)
// Returns stacktrace
Stacktrace(goroutineID int, depth int, readDefers bool, cfg *api.LoadConfig) ([]api.Stackframe, error)
// Returns ancestor stacktraces
Ancestors(goroutineID int, numAncestors int, depth int) ([]api.Ancestor, error)
// Returns whether we attached to a running process or not
AttachedToExistingProcess() bool
// Returns concrete location information described by a location expression
// loc ::= <filename>:<line> | <function>[:<line>] | /<regex>/ | (+|-)<offset> | <line> | *<address>
// * <filename> can be the full path of a file or just a suffix
// * <function> ::= <package>.<receiver type>.<name> | <package>.(*<receiver type>).<name> | <receiver type>.<name> | <package>.<name> | (*<receiver type>).<name> | <name>
// * <function> must be unambiguous
// * /<regex>/ will return a location for each function matched by regex
// * +<offset> returns a location for the line that is <offset> lines after the current line
// * -<offset> returns a location for the line that is <offset> lines before the current line
// * <line> returns a location for a line in the current file
// * *<address> returns the location corresponding to the specified address
// NOTE: this function does not actually set breakpoints.
FindLocation(scope api.EvalScope, loc string) ([]api.Location, error)
// Disassemble code between startPC and endPC
DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error)
// Disassemble code of the function containing PC
DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error)
// Recorded returns true if the target is a recording.
Recorded() bool
// TraceDirectory returns the path to the trace directory for a recording.
TraceDirectory() (string, error)
// Checkpoint sets a checkpoint at the current position.
Checkpoint(where string) (checkpointID int, err error)
// ListCheckpoints gets all checkpoints.
ListCheckpoints() ([]api.Checkpoint, error)
// ClearCheckpoint removes a checkpoint
ClearCheckpoint(id int) error
// SetReturnValuesLoadConfig sets the load configuration for return values.
SetReturnValuesLoadConfig(*api.LoadConfig)
// IsMulticlien returns true if the headless instance is multiclient.
IsMulticlient() bool
// ListDynamicLibraries returns a list of loaded dynamic libraries.
ListDynamicLibraries() ([]api.Image, error)
// Disconnect closes the connection to the server without sending a Detach request first.
// If cont is true a continue command will be sent instead.
Disconnect(cont bool) error
}

44
vendor/github.com/go-delve/delve/service/config.go generated vendored Normal file
View File

@ -0,0 +1,44 @@
package service
import "net"
// Config provides the configuration to start a Debugger and expose it with a
// service.
//
// Only one of ProcessArgs or AttachPid should be specified. If ProcessArgs is
// provided, a new process will be launched. Otherwise, the debugger will try
// to attach to an existing process with AttachPid.
type Config struct {
// Listener is used to serve requests.
Listener net.Listener
// ProcessArgs are the arguments to launch a new process.
ProcessArgs []string
// WorkingDir is working directory of the new process. This field is used
// only when launching a new process.
WorkingDir string
// AttachPid is the PID of an existing process to which the debugger should
// attach.
AttachPid int
// AcceptMulti configures the server to accept multiple connection.
// Note that the server API is not reentrant and clients will have to coordinate.
AcceptMulti bool
// APIVersion selects which version of the API to serve (default: 1).
APIVersion int
// CoreFile specifies the path to the core dump to open.
CoreFile string
// DebugInfoDirectories is the list of directories to look for
// when resolving external debug info files.
DebugInfoDirectories []string
// Selects server backend.
Backend string
// Foreground lets target process access stdin.
Foreground bool
// DisconnectChan will be closed by the server when the client disconnects
DisconnectChan chan<- struct{}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
package debugger
import (
"fmt"
sys "golang.org/x/sys/unix"
)
func attachErrorMessage(pid int, err error) error {
//TODO: mention certificates?
return fmt.Errorf("could not attach to pid %d: %s", pid, err)
}
func stopProcess(pid int) error {
return sys.Kill(pid, sys.SIGSTOP)
}

View File

@ -0,0 +1,36 @@
package debugger
import (
"fmt"
"io/ioutil"
"os"
"syscall"
sys "golang.org/x/sys/unix"
)
func attachErrorMessage(pid int, err error) error {
fallbackerr := fmt.Errorf("could not attach to pid %d: %s", pid, err)
if serr, ok := err.(syscall.Errno); ok {
switch serr {
case syscall.EPERM:
bs, err := ioutil.ReadFile("/proc/sys/kernel/yama/ptrace_scope")
if err == nil && len(bs) >= 1 && bs[0] != '0' {
// Yama documentation: https://www.kernel.org/doc/Documentation/security/Yama.txt
return fmt.Errorf("Could not attach to pid %d: this could be caused by a kernel security setting, try writing \"0\" to /proc/sys/kernel/yama/ptrace_scope", pid)
}
fi, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
if err != nil {
return fallbackerr
}
if fi.Sys().(*syscall.Stat_t).Uid != uint32(os.Getuid()) {
return fmt.Errorf("Could not attach to pid %d: current user does not own the process", pid)
}
}
}
return fallbackerr
}
func stopProcess(pid int) error {
return sys.Kill(pid, sys.SIGSTOP)
}

View File

@ -0,0 +1,16 @@
package debugger
import (
"fmt"
)
func attachErrorMessage(pid int, err error) error {
return fmt.Errorf("could not attach to pid %d: %s", pid, err)
}
func stopProcess(pid int) error {
// We cannot gracefully stop a process on Windows,
// so just ignore this request and let `Detach` kill
// the process.
return nil
}

View File

@ -0,0 +1,426 @@
package debugger
import (
"fmt"
"go/constant"
"path"
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/service/api"
)
const maxFindLocationCandidates = 5
type LocationSpec interface {
Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error)
}
type NormalLocationSpec struct {
Base string
FuncBase *FuncLocationSpec
LineOffset int
}
type RegexLocationSpec struct {
FuncRegex string
}
type AddrLocationSpec struct {
AddrExpr string
}
type OffsetLocationSpec struct {
Offset int
}
type LineLocationSpec struct {
Line int
}
type FuncLocationSpec struct {
PackageName string
AbsolutePackage bool
ReceiverName string
PackageOrReceiverName string
BaseName string
}
func parseLocationSpec(locStr string) (LocationSpec, error) {
rest := locStr
malformed := func(reason string) error {
return fmt.Errorf("Malformed breakpoint location \"%s\" at %d: %s", locStr, len(locStr)-len(rest), reason)
}
if len(rest) <= 0 {
return nil, malformed("empty string")
}
switch rest[0] {
case '+', '-':
offset, err := strconv.Atoi(rest)
if err != nil {
return nil, malformed(err.Error())
}
return &OffsetLocationSpec{offset}, nil
case '/':
if rest[len(rest)-1] == '/' {
rx, rest := readRegex(rest[1:])
if len(rest) < 0 {
return nil, malformed("non-terminated regular expression")
}
if len(rest) > 1 {
return nil, malformed("no line offset can be specified for regular expression locations")
}
return &RegexLocationSpec{rx}, nil
} else {
return parseLocationSpecDefault(locStr, rest)
}
case '*':
return &AddrLocationSpec{rest[1:]}, nil
default:
return parseLocationSpecDefault(locStr, rest)
}
}
func parseLocationSpecDefault(locStr, rest string) (LocationSpec, error) {
malformed := func(reason string) error {
return fmt.Errorf("Malformed breakpoint location \"%s\" at %d: %s", locStr, len(locStr)-len(rest), reason)
}
v := strings.Split(rest, ":")
if len(v) > 2 {
// On Windows, path may contain ":", so split only on last ":"
v = []string{strings.Join(v[0:len(v)-1], ":"), v[len(v)-1]}
}
if len(v) == 1 {
n, err := strconv.ParseInt(v[0], 0, 64)
if err == nil {
return &LineLocationSpec{int(n)}, nil
}
}
spec := &NormalLocationSpec{}
spec.Base = v[0]
spec.FuncBase = parseFuncLocationSpec(spec.Base)
if len(v) < 2 {
spec.LineOffset = -1
return spec, nil
}
rest = v[1]
var err error
spec.LineOffset, err = strconv.Atoi(rest)
if err != nil || spec.LineOffset < 0 {
return nil, malformed("line offset negative or not a number")
}
return spec, nil
}
func readRegex(in string) (rx string, rest string) {
out := make([]rune, 0, len(in))
escaped := false
for i, ch := range in {
if escaped {
if ch == '/' {
out = append(out, '/')
} else {
out = append(out, '\\')
out = append(out, ch)
}
escaped = false
} else {
switch ch {
case '\\':
escaped = true
case '/':
return string(out), in[i:]
default:
out = append(out, ch)
}
}
}
return string(out), ""
}
func parseFuncLocationSpec(in string) *FuncLocationSpec {
var v []string
pathend := strings.LastIndex(in, "/")
if pathend < 0 {
v = strings.Split(in, ".")
} else {
v = strings.Split(in[pathend:], ".")
if len(v) > 0 {
v[0] = in[:pathend] + v[0]
}
}
var spec FuncLocationSpec
switch len(v) {
case 1:
spec.BaseName = v[0]
case 2:
spec.BaseName = v[1]
r := stripReceiverDecoration(v[0])
if r != v[0] {
spec.ReceiverName = r
} else if strings.Contains(r, "/") {
spec.PackageName = r
} else {
spec.PackageOrReceiverName = r
}
case 3:
spec.BaseName = v[2]
spec.ReceiverName = stripReceiverDecoration(v[1])
spec.PackageName = v[0]
default:
return nil
}
if strings.HasPrefix(spec.PackageName, "/") {
spec.PackageName = spec.PackageName[1:]
spec.AbsolutePackage = true
}
if strings.Contains(spec.BaseName, "/") || strings.Contains(spec.ReceiverName, "/") {
return nil
}
return &spec
}
func stripReceiverDecoration(in string) string {
if len(in) < 3 {
return in
}
if (in[0] != '(') || (in[1] != '*') || (in[len(in)-1] != ')') {
return in
}
return in[2 : len(in)-1]
}
func (spec *FuncLocationSpec) Match(sym proc.Function) bool {
if spec.BaseName != sym.BaseName() {
return false
}
recv := stripReceiverDecoration(sym.ReceiverName())
if spec.ReceiverName != "" && spec.ReceiverName != recv {
return false
}
if spec.PackageName != "" {
if spec.AbsolutePackage {
if spec.PackageName != sym.PackageName() {
return false
}
} else {
if !partialPathMatch(spec.PackageName, sym.PackageName()) {
return false
}
}
}
if spec.PackageOrReceiverName != "" && !partialPathMatch(spec.PackageOrReceiverName, sym.PackageName()) && spec.PackageOrReceiverName != recv {
return false
}
return true
}
func (loc *RegexLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
funcs := d.target.BinInfo().Functions
matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
if err != nil {
return nil, err
}
r := make([]api.Location, 0, len(matches))
for i := range matches {
addr, err := proc.FindFunctionLocation(d.target, matches[i], true, 0)
if err == nil {
r = append(r, api.Location{PC: addr})
}
}
return r, nil
}
func (loc *AddrLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
if scope == nil {
addr, err := strconv.ParseInt(loc.AddrExpr, 0, 64)
if err != nil {
return nil, fmt.Errorf("could not determine current location (scope is nil)")
}
return []api.Location{{PC: uint64(addr)}}, nil
} else {
v, err := scope.EvalExpression(loc.AddrExpr, proc.LoadConfig{true, 0, 0, 0, 0, 0})
if err != nil {
return nil, err
}
if v.Unreadable != nil {
return nil, v.Unreadable
}
switch v.Kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
addr, _ := constant.Uint64Val(v.Value)
return []api.Location{{PC: addr}}, nil
case reflect.Func:
_, _, fn := d.target.BinInfo().PCToLine(uint64(v.Base))
pc, err := proc.FirstPCAfterPrologue(d.target, fn, false)
if err != nil {
return nil, err
}
return []api.Location{{PC: uint64(pc)}}, nil
default:
return nil, fmt.Errorf("wrong expression kind: %v", v.Kind)
}
}
}
func (loc *NormalLocationSpec) FileMatch(path string) bool {
return partialPathMatch(loc.Base, path)
}
func tryMatchRelativePathByProc(expr, debugname, file string) bool {
return len(expr) > 0 && expr[0] == '.' && file == path.Join(path.Dir(debugname), expr)
}
func partialPathMatch(expr, path string) bool {
if runtime.GOOS == "windows" {
// Accept `expr` which is case-insensitive and slash-insensitive match to `path`
expr = strings.ToLower(filepath.ToSlash(expr))
path = strings.ToLower(filepath.ToSlash(path))
}
if len(expr) < len(path)-1 {
return strings.HasSuffix(path, expr) && (path[len(path)-len(expr)-1] == '/')
} else {
return expr == path
}
}
type AmbiguousLocationError struct {
Location string
CandidatesString []string
CandidatesLocation []api.Location
}
func (ale AmbiguousLocationError) Error() string {
var candidates []string
if ale.CandidatesLocation != nil {
for i := range ale.CandidatesLocation {
candidates = append(candidates, ale.CandidatesLocation[i].Function.Name())
}
} else {
candidates = ale.CandidatesString
}
return fmt.Sprintf("Location \"%s\" ambiguous: %s…", ale.Location, strings.Join(candidates, ", "))
}
func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
limit := maxFindLocationCandidates
var candidateFiles []string
for _, file := range d.target.BinInfo().Sources {
if loc.FileMatch(file) || (len(d.processArgs) >= 1 && tryMatchRelativePathByProc(loc.Base, d.processArgs[0], file)) {
candidateFiles = append(candidateFiles, file)
if len(candidateFiles) >= limit {
break
}
}
}
limit -= len(candidateFiles)
var candidateFuncs []string
if loc.FuncBase != nil {
for _, f := range d.target.BinInfo().Functions {
if !loc.FuncBase.Match(f) {
continue
}
if loc.Base == f.Name {
// if an exact match for the function name is found use it
candidateFuncs = []string{f.Name}
break
}
candidateFuncs = append(candidateFuncs, f.Name)
if len(candidateFuncs) >= limit {
break
}
}
}
if matching := len(candidateFiles) + len(candidateFuncs); matching == 0 {
// if no result was found treat this locations string could be an
// expression that the user forgot to prefix with '*', try treating it as
// such.
addrSpec := &AddrLocationSpec{locStr}
locs, err := addrSpec.Find(d, scope, locStr)
if err != nil {
return nil, fmt.Errorf("Location \"%s\" not found", locStr)
}
return locs, nil
} else if matching > 1 {
return nil, AmbiguousLocationError{Location: locStr, CandidatesString: append(candidateFiles, candidateFuncs...)}
}
// len(candidateFiles) + len(candidateFuncs) == 1
var addr uint64
var err error
if len(candidateFiles) == 1 {
if loc.LineOffset < 0 {
return nil, fmt.Errorf("Malformed breakpoint location, no line offset specified")
}
addr, err = proc.FindFileLocation(d.target, candidateFiles[0], loc.LineOffset)
} else { // len(candidateFUncs) == 1
if loc.LineOffset < 0 {
addr, err = proc.FindFunctionLocation(d.target, candidateFuncs[0], true, 0)
} else {
addr, err = proc.FindFunctionLocation(d.target, candidateFuncs[0], false, loc.LineOffset)
}
}
if err != nil {
return nil, err
}
return []api.Location{{PC: addr}}, nil
}
func (loc *OffsetLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
if scope == nil {
return nil, fmt.Errorf("could not determine current location (scope is nil)")
}
if loc.Offset == 0 {
return []api.Location{{PC: scope.PC}}, nil
}
file, line, fn := d.target.BinInfo().PCToLine(scope.PC)
if fn == nil {
return nil, fmt.Errorf("could not determine current location")
}
addr, err := proc.FindFileLocation(d.target, file, line+loc.Offset)
return []api.Location{{PC: addr}}, err
}
func (loc *LineLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
if scope == nil {
return nil, fmt.Errorf("could not determine current location (scope is nil)")
}
file, _, fn := d.target.BinInfo().PCToLine(scope.PC)
if fn == nil {
return nil, fmt.Errorf("could not determine current location")
}
addr, err := proc.FindFileLocation(d.target, file, loc.Line)
return []api.Location{{PC: addr}}, err
}

View File

@ -0,0 +1,60 @@
package service
import (
"errors"
"net"
"sync"
)
// ListenerPipe returns a full-duplex in-memory connection, like net.Pipe.
// Unlike net.Pipe one end of the connection is returned as an object
// satisfying the net.Listener interface.
// The first call to the Accept method of this object will return a net.Conn
// connected to the other net.Conn returned by ListenerPipe.
// Any subsequent calls to Accept will block until the listener is closed.
func ListenerPipe() (net.Listener, net.Conn) {
conn0, conn1 := net.Pipe()
return &preconnectedListener{conn: conn0, closech: make(chan struct{})}, conn1
}
// preconnectedListener satisfies the net.Listener interface by accepting a
// single pre-established connection.
// The first call to Accept will return the conn field, any subsequent call
// will block until the listener is closed.
type preconnectedListener struct {
accepted bool
conn net.Conn
closech chan struct{}
closeMu sync.Mutex
acceptMu sync.Mutex
}
// Accept returns the pre-established connection the first time it's called,
// it blocks until the listener is closed on every subsequent call.
func (l *preconnectedListener) Accept() (net.Conn, error) {
l.acceptMu.Lock()
defer l.acceptMu.Unlock()
if !l.accepted {
l.accepted = true
return l.conn, nil
}
<-l.closech
return nil, errors.New("accept failed: listener closed")
}
// Close closes the listener.
func (l *preconnectedListener) Close() error {
l.closeMu.Lock()
defer l.closeMu.Unlock()
if l.closech == nil {
return nil
}
close(l.closech)
l.closech = nil
return nil
}
// Addr returns the listener's network address.
func (l *preconnectedListener) Addr() net.Addr {
return l.conn.LocalAddr()
}

313
vendor/github.com/go-delve/delve/service/rpc1/client.go generated vendored Normal file
View File

@ -0,0 +1,313 @@
package rpc1
import (
"errors"
"fmt"
"log"
"net/rpc"
"net/rpc/jsonrpc"
"sync"
"github.com/go-delve/delve/service/api"
)
// Client is a RPC service.Client.
type RPCClient struct {
addr string
client *rpc.Client
haltMu sync.Mutex
haltReq bool
}
var unsupportedApiError = errors.New("unsupported")
// NewClient creates a new RPCClient.
func NewClient(addr string) *RPCClient {
client, err := jsonrpc.Dial("tcp", addr)
if err != nil {
log.Fatal("dialing:", err)
}
return &RPCClient{
addr: addr,
client: client,
}
}
func (c *RPCClient) ProcessPid() int {
var pid int
c.call("ProcessPid", nil, &pid)
return pid
}
func (c *RPCClient) Detach(kill bool) error {
return c.call("Detach", kill, nil)
}
func (c *RPCClient) Restart() error {
return c.call("Restart", nil, nil)
}
func (c *RPCClient) GetState() (*api.DebuggerState, error) {
state := new(api.DebuggerState)
err := c.call("State", nil, state)
return state, err
}
func (c *RPCClient) Continue() <-chan *api.DebuggerState {
ch := make(chan *api.DebuggerState)
c.haltMu.Lock()
c.haltReq = false
c.haltMu.Unlock()
go func() {
for {
c.haltMu.Lock()
if c.haltReq {
c.haltMu.Unlock()
close(ch)
return
}
c.haltMu.Unlock()
state := new(api.DebuggerState)
err := c.call("Command", &api.DebuggerCommand{Name: api.Continue}, state)
if err != nil {
state.Err = err
}
if state.Exited {
// Error types apparently cannot be marshalled by Go correctly. Must reset error here.
state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus)
}
ch <- state
if err != nil || state.Exited {
close(ch)
return
}
isbreakpoint := false
istracepoint := true
for i := range state.Threads {
if state.Threads[i].Breakpoint != nil {
isbreakpoint = true
istracepoint = istracepoint && state.Threads[i].Breakpoint.Tracepoint
}
}
if !isbreakpoint || !istracepoint {
close(ch)
return
}
}
}()
return ch
}
func (c *RPCClient) Next() (*api.DebuggerState, error) {
state := new(api.DebuggerState)
err := c.call("Command", &api.DebuggerCommand{Name: api.Next}, state)
return state, err
}
func (c *RPCClient) Step() (*api.DebuggerState, error) {
state := new(api.DebuggerState)
err := c.call("Command", &api.DebuggerCommand{Name: api.Step}, state)
return state, err
}
func (c *RPCClient) Call(expr string) (*api.DebuggerState, error) {
state := new(api.DebuggerState)
err := c.call("Command", &api.DebuggerCommand{Name: api.Call, Expr: expr}, state)
return state, err
}
func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) {
state := new(api.DebuggerState)
err := c.call("Command", &api.DebuggerCommand{Name: api.StepInstruction}, state)
return state, err
}
func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) {
state := new(api.DebuggerState)
cmd := &api.DebuggerCommand{
Name: api.SwitchThread,
ThreadID: threadID,
}
err := c.call("Command", cmd, state)
return state, err
}
func (c *RPCClient) SwitchGoroutine(goroutineID int) (*api.DebuggerState, error) {
state := new(api.DebuggerState)
cmd := &api.DebuggerCommand{
Name: api.SwitchGoroutine,
GoroutineID: goroutineID,
}
err := c.call("Command", cmd, state)
return state, err
}
func (c *RPCClient) Halt() (*api.DebuggerState, error) {
state := new(api.DebuggerState)
c.haltMu.Lock()
c.haltReq = true
c.haltMu.Unlock()
err := c.call("Command", &api.DebuggerCommand{Name: api.Halt}, state)
return state, err
}
func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) {
breakpoint := new(api.Breakpoint)
err := c.call("GetBreakpoint", id, breakpoint)
return breakpoint, err
}
func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) {
breakpoint := new(api.Breakpoint)
err := c.call("GetBreakpointByName", name, breakpoint)
return breakpoint, err
}
func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) {
newBreakpoint := new(api.Breakpoint)
err := c.call("CreateBreakpoint", breakPoint, &newBreakpoint)
return newBreakpoint, err
}
func (c *RPCClient) ListBreakpoints() ([]*api.Breakpoint, error) {
var breakpoints []*api.Breakpoint
err := c.call("ListBreakpoints", nil, &breakpoints)
return breakpoints, err
}
func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) {
bp := new(api.Breakpoint)
err := c.call("ClearBreakpoint", id, bp)
return bp, err
}
func (c *RPCClient) ClearBreakpointByName(name string) (*api.Breakpoint, error) {
bp := new(api.Breakpoint)
err := c.call("ClearBreakpointByName", name, bp)
return bp, err
}
func (c *RPCClient) AmendBreakpoint(bp *api.Breakpoint) error {
err := c.call("AmendBreakpoint", bp, nil)
return err
}
func (c *RPCClient) CancelNext() error {
return unsupportedApiError
}
func (c *RPCClient) ListThreads() ([]*api.Thread, error) {
var threads []*api.Thread
err := c.call("ListThreads", nil, &threads)
return threads, err
}
func (c *RPCClient) GetThread(id int) (*api.Thread, error) {
thread := new(api.Thread)
err := c.call("GetThread", id, &thread)
return thread, err
}
func (c *RPCClient) EvalVariable(scope api.EvalScope, symbol string) (*api.Variable, error) {
v := new(api.Variable)
err := c.call("EvalSymbol", EvalSymbolArgs{scope, symbol}, v)
return v, err
}
func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error {
var unused int
return c.call("SetSymbol", SetSymbolArgs{scope, symbol, value}, &unused)
}
func (c *RPCClient) ListSources(filter string) ([]string, error) {
var sources []string
err := c.call("ListSources", filter, &sources)
return sources, err
}
func (c *RPCClient) ListFunctions(filter string) ([]string, error) {
var funcs []string
err := c.call("ListFunctions", filter, &funcs)
return funcs, err
}
func (c *RPCClient) ListTypes(filter string) ([]string, error) {
var types []string
err := c.call("ListTypes", filter, &types)
return types, err
}
func (c *RPCClient) ListPackageVariables(filter string) ([]api.Variable, error) {
var vars []api.Variable
err := c.call("ListPackageVars", filter, &vars)
return vars, err
}
func (c *RPCClient) ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error) {
var vars []api.Variable
err := c.call("ListThreadPackageVars", &ThreadListArgs{Id: threadID, Filter: filter}, &vars)
return vars, err
}
func (c *RPCClient) ListLocalVariables(scope api.EvalScope) ([]api.Variable, error) {
var vars []api.Variable
err := c.call("ListLocalVars", scope, &vars)
return vars, err
}
func (c *RPCClient) ListRegisters() (string, error) {
var regs string
err := c.call("ListRegisters", nil, &regs)
return regs, err
}
func (c *RPCClient) ListFunctionArgs(scope api.EvalScope) ([]api.Variable, error) {
var vars []api.Variable
err := c.call("ListFunctionArgs", scope, &vars)
return vars, err
}
func (c *RPCClient) ListGoroutines() ([]*api.Goroutine, error) {
var goroutines []*api.Goroutine
err := c.call("ListGoroutines", nil, &goroutines)
return goroutines, err
}
func (c *RPCClient) Stacktrace(goroutineId, depth int, full bool) ([]api.Stackframe, error) {
var locations []api.Stackframe
err := c.call("StacktraceGoroutine", &StacktraceGoroutineArgs{Id: goroutineId, Depth: depth, Full: full}, &locations)
return locations, err
}
func (c *RPCClient) AttachedToExistingProcess() bool {
var answer bool
c.call("AttachedToExistingProcess", nil, &answer)
return answer
}
func (c *RPCClient) FindLocation(scope api.EvalScope, loc string) ([]api.Location, error) {
var answer []api.Location
err := c.call("FindLocation", FindLocationArgs{scope, loc}, &answer)
return answer, err
}
// Disassemble code between startPC and endPC
func (c *RPCClient) DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
var r api.AsmInstructions
err := c.call("Disassemble", DisassembleRequest{scope, startPC, endPC, flavour}, &r)
return r, err
}
// Disassemble function containing pc
func (c *RPCClient) DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
var r api.AsmInstructions
err := c.call("Disassemble", DisassembleRequest{scope, pc, 0, flavour}, &r)
return r, err
}
func (c *RPCClient) call(method string, args, reply interface{}) error {
return c.client.Call("RPCServer."+method, args, reply)
}

View File

@ -0,0 +1,5 @@
This package implements version 1 of Delve's API and is only
kept here for backwards compatibility. Client.go is the old
client code used by Delve's frontend (delve/cmd/dlv), it is
only preserved here for the backwards compatibility tests in
service/test/integration1_test.go.

322
vendor/github.com/go-delve/delve/service/rpc1/server.go generated vendored Normal file
View File

@ -0,0 +1,322 @@
package rpc1
import (
"errors"
"fmt"
"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"
)
var defaultLoadConfig = proc.LoadConfig{true, 1, 64, 64, -1, 0}
type RPCServer struct {
// config is all the information necessary to start the debugger and server.
config *service.Config
// debugger is a debugger service.
debugger *debugger.Debugger
}
func NewServer(config *service.Config, debugger *debugger.Debugger) *RPCServer {
return &RPCServer{config, debugger}
}
func (s *RPCServer) ProcessPid(arg1 interface{}, pid *int) error {
*pid = s.debugger.ProcessPid()
return nil
}
func (s *RPCServer) Detach(kill bool, ret *int) error {
err := s.debugger.Detach(kill)
if s.config.DisconnectChan != nil {
close(s.config.DisconnectChan)
}
return err
}
func (s *RPCServer) Restart(arg1 interface{}, arg2 *int) error {
if s.config.AttachPid != 0 {
return errors.New("cannot restart process Delve did not create")
}
_, err := s.debugger.Restart("", false, nil)
return err
}
func (s *RPCServer) State(arg interface{}, state *api.DebuggerState) error {
st, err := s.debugger.State(false)
if err != nil {
return err
}
*state = *st
return nil
}
func (s *RPCServer) Command(command *api.DebuggerCommand, cb service.RPCCallback) {
st, err := s.debugger.Command(command)
cb.Return(st, err)
}
func (s *RPCServer) GetBreakpoint(id int, breakpoint *api.Breakpoint) error {
bp := s.debugger.FindBreakpoint(id)
if bp == nil {
return fmt.Errorf("no breakpoint with id %d", id)
}
*breakpoint = *bp
return nil
}
func (s *RPCServer) GetBreakpointByName(name string, breakpoint *api.Breakpoint) error {
bp := s.debugger.FindBreakpointByName(name)
if bp == nil {
return fmt.Errorf("no breakpoint with name %s", name)
}
*breakpoint = *bp
return nil
}
type StacktraceGoroutineArgs struct {
Id int
Depth int
Full bool
}
func (s *RPCServer) StacktraceGoroutine(args *StacktraceGoroutineArgs, locations *[]api.Stackframe) error {
var loadcfg *proc.LoadConfig = nil
if args.Full {
loadcfg = &defaultLoadConfig
}
locs, err := s.debugger.Stacktrace(args.Id, args.Depth, false, loadcfg)
if err != nil {
return err
}
*locations = locs
return nil
}
func (s *RPCServer) ListBreakpoints(arg interface{}, breakpoints *[]*api.Breakpoint) error {
*breakpoints = s.debugger.Breakpoints()
return nil
}
func (s *RPCServer) CreateBreakpoint(bp, newBreakpoint *api.Breakpoint) error {
createdbp, err := s.debugger.CreateBreakpoint(bp)
if err != nil {
return err
}
*newBreakpoint = *createdbp
return nil
}
func (s *RPCServer) ClearBreakpoint(id int, breakpoint *api.Breakpoint) error {
bp := s.debugger.FindBreakpoint(id)
if bp == nil {
return fmt.Errorf("no breakpoint with id %d", id)
}
deleted, err := s.debugger.ClearBreakpoint(bp)
if err != nil {
return err
}
*breakpoint = *deleted
return nil
}
func (s *RPCServer) ClearBreakpointByName(name string, breakpoint *api.Breakpoint) error {
bp := s.debugger.FindBreakpointByName(name)
if bp == nil {
return fmt.Errorf("no breakpoint with name %s", name)
}
deleted, err := s.debugger.ClearBreakpoint(bp)
if err != nil {
return err
}
*breakpoint = *deleted
return nil
}
func (s *RPCServer) AmendBreakpoint(amend *api.Breakpoint, unused *int) error {
*unused = 0
return s.debugger.AmendBreakpoint(amend)
}
func (s *RPCServer) ListThreads(arg interface{}, threads *[]*api.Thread) (err error) {
*threads, err = s.debugger.Threads()
return err
}
func (s *RPCServer) GetThread(id int, thread *api.Thread) error {
t, err := s.debugger.FindThread(id)
if err != nil {
return err
}
if t == nil {
return fmt.Errorf("no thread with id %d", id)
}
*thread = *t
return nil
}
func (s *RPCServer) ListPackageVars(filter string, variables *[]api.Variable) error {
state, err := s.debugger.State(false)
if err != nil {
return err
}
current := state.CurrentThread
if current == nil {
return fmt.Errorf("no current thread")
}
vars, err := s.debugger.PackageVariables(current.ID, filter, defaultLoadConfig)
if err != nil {
return err
}
*variables = vars
return nil
}
type ThreadListArgs struct {
Id int
Filter string
}
func (s *RPCServer) ListThreadPackageVars(args *ThreadListArgs, variables *[]api.Variable) error {
thread, err := s.debugger.FindThread(args.Id)
if err != nil {
return err
}
if thread == nil {
return fmt.Errorf("no thread with id %d", args.Id)
}
vars, err := s.debugger.PackageVariables(args.Id, args.Filter, defaultLoadConfig)
if err != nil {
return err
}
*variables = vars
return nil
}
func (s *RPCServer) ListRegisters(arg interface{}, registers *string) error {
state, err := s.debugger.State(false)
if err != nil {
return err
}
regs, err := s.debugger.Registers(state.CurrentThread.ID, false)
if err != nil {
return err
}
*registers = regs.String()
return nil
}
func (s *RPCServer) ListLocalVars(scope api.EvalScope, variables *[]api.Variable) error {
vars, err := s.debugger.LocalVariables(scope, defaultLoadConfig)
if err != nil {
return err
}
*variables = vars
return nil
}
func (s *RPCServer) ListFunctionArgs(scope api.EvalScope, variables *[]api.Variable) error {
vars, err := s.debugger.FunctionArguments(scope, defaultLoadConfig)
if err != nil {
return err
}
*variables = vars
return nil
}
type EvalSymbolArgs struct {
Scope api.EvalScope
Symbol string
}
func (s *RPCServer) EvalSymbol(args EvalSymbolArgs, variable *api.Variable) error {
v, err := s.debugger.EvalVariableInScope(args.Scope, args.Symbol, defaultLoadConfig)
if err != nil {
return err
}
*variable = *v
return nil
}
type SetSymbolArgs struct {
Scope api.EvalScope
Symbol string
Value string
}
func (s *RPCServer) SetSymbol(args SetSymbolArgs, unused *int) error {
*unused = 0
return s.debugger.SetVariableInScope(args.Scope, args.Symbol, args.Value)
}
func (s *RPCServer) ListSources(filter string, sources *[]string) error {
ss, err := s.debugger.Sources(filter)
if err != nil {
return err
}
*sources = ss
return nil
}
func (s *RPCServer) ListFunctions(filter string, funcs *[]string) error {
fns, err := s.debugger.Functions(filter)
if err != nil {
return err
}
*funcs = fns
return nil
}
func (s *RPCServer) ListTypes(filter string, types *[]string) error {
tps, err := s.debugger.Types(filter)
if err != nil {
return err
}
*types = tps
return nil
}
func (s *RPCServer) ListGoroutines(arg interface{}, goroutines *[]*api.Goroutine) error {
gs, _, err := s.debugger.Goroutines(0, 0)
if err != nil {
return err
}
*goroutines = gs
return nil
}
func (c *RPCServer) AttachedToExistingProcess(arg interface{}, answer *bool) error {
if c.config.AttachPid != 0 {
*answer = true
}
return nil
}
type FindLocationArgs struct {
Scope api.EvalScope
Loc string
}
func (c *RPCServer) FindLocation(args FindLocationArgs, answer *[]api.Location) error {
var err error
*answer, err = c.debugger.FindLocation(args.Scope, args.Loc)
return err
}
type DisassembleRequest struct {
Scope api.EvalScope
StartPC, EndPC uint64
Flavour api.AssemblyFlavour
}
func (c *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error {
var err error
*answer, err = c.debugger.Disassemble(args.Scope, args.StartPC, args.EndPC, args.Flavour)
return err
}

412
vendor/github.com/go-delve/delve/service/rpc2/client.go generated vendored Normal file
View File

@ -0,0 +1,412 @@
package rpc2
import (
"fmt"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"time"
"github.com/go-delve/delve/service"
"github.com/go-delve/delve/service/api"
)
// Client is a RPC service.Client.
type RPCClient struct {
client *rpc.Client
retValLoadCfg *api.LoadConfig
}
// Ensure the implementation satisfies the interface.
var _ service.Client = &RPCClient{}
// NewClient creates a new RPCClient.
func NewClient(addr string) *RPCClient {
client, err := jsonrpc.Dial("tcp", addr)
if err != nil {
log.Fatal("dialing:", err)
}
return newFromRPCClient(client)
}
func newFromRPCClient(client *rpc.Client) *RPCClient {
c := &RPCClient{client: client}
c.call("SetApiVersion", api.SetAPIVersionIn{2}, &api.SetAPIVersionOut{})
return c
}
// NewClientFromConn creates a new RPCClient from the given connection.
func NewClientFromConn(conn net.Conn) *RPCClient {
return newFromRPCClient(jsonrpc.NewClient(conn))
}
func (c *RPCClient) ProcessPid() int {
out := new(ProcessPidOut)
c.call("ProcessPid", ProcessPidIn{}, out)
return out.Pid
}
func (c *RPCClient) LastModified() time.Time {
out := new(LastModifiedOut)
c.call("LastModified", LastModifiedIn{}, out)
return out.Time
}
func (c *RPCClient) Detach(kill bool) error {
defer c.client.Close()
out := new(DetachOut)
return c.call("Detach", DetachIn{kill}, out)
}
func (c *RPCClient) Restart() ([]api.DiscardedBreakpoint, error) {
out := new(RestartOut)
err := c.call("Restart", RestartIn{"", false, nil}, out)
return out.DiscardedBreakpoints, err
}
func (c *RPCClient) RestartFrom(pos string, resetArgs bool, newArgs []string) ([]api.DiscardedBreakpoint, error) {
out := new(RestartOut)
err := c.call("Restart", RestartIn{pos, resetArgs, newArgs}, out)
return out.DiscardedBreakpoints, err
}
func (c *RPCClient) GetState() (*api.DebuggerState, error) {
var out StateOut
err := c.call("State", StateIn{NonBlocking: false}, &out)
return out.State, err
}
func (c *RPCClient) GetStateNonBlocking() (*api.DebuggerState, error) {
var out StateOut
err := c.call("State", StateIn{NonBlocking: true}, &out)
return out.State, err
}
func (c *RPCClient) Continue() <-chan *api.DebuggerState {
return c.continueDir(api.Continue)
}
func (c *RPCClient) Rewind() <-chan *api.DebuggerState {
return c.continueDir(api.Rewind)
}
func (c *RPCClient) continueDir(cmd string) <-chan *api.DebuggerState {
ch := make(chan *api.DebuggerState)
go func() {
for {
out := new(CommandOut)
err := c.call("Command", &api.DebuggerCommand{Name: cmd, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
state := out.State
if err != nil {
state.Err = err
}
if state.Exited {
// Error types apparently cannot be marshalled by Go correctly. Must reset error here.
state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus)
}
ch <- &state
if err != nil || state.Exited {
close(ch)
return
}
isbreakpoint := false
istracepoint := true
for i := range state.Threads {
if state.Threads[i].Breakpoint != nil {
isbreakpoint = true
istracepoint = istracepoint && (state.Threads[i].Breakpoint.Tracepoint || state.Threads[i].Breakpoint.TraceReturn)
}
}
if !isbreakpoint || !istracepoint {
close(ch)
return
}
}
}()
return ch
}
func (c *RPCClient) Next() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.Next, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
return &out.State, err
}
func (c *RPCClient) Step() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.Step, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
return &out.State, err
}
func (c *RPCClient) StepOut() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.StepOut, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
return &out.State, err
}
func (c *RPCClient) Call(expr string, unsafe bool) (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.Call, ReturnInfoLoadConfig: c.retValLoadCfg, Expr: expr, UnsafeCall: unsafe}, &out)
return &out.State, err
}
func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.StepInstruction}, &out)
return &out.State, err
}
func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) {
var out CommandOut
cmd := api.DebuggerCommand{
Name: api.SwitchThread,
ThreadID: threadID,
}
err := c.call("Command", cmd, &out)
return &out.State, err
}
func (c *RPCClient) SwitchGoroutine(goroutineID int) (*api.DebuggerState, error) {
var out CommandOut
cmd := api.DebuggerCommand{
Name: api.SwitchGoroutine,
GoroutineID: goroutineID,
}
err := c.call("Command", cmd, &out)
return &out.State, err
}
func (c *RPCClient) Halt() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.Halt}, &out)
return &out.State, err
}
func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) {
var out GetBreakpointOut
err := c.call("GetBreakpoint", GetBreakpointIn{id, ""}, &out)
return &out.Breakpoint, err
}
func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) {
var out GetBreakpointOut
err := c.call("GetBreakpoint", GetBreakpointIn{0, name}, &out)
return &out.Breakpoint, err
}
func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) {
var out CreateBreakpointOut
err := c.call("CreateBreakpoint", CreateBreakpointIn{*breakPoint}, &out)
return &out.Breakpoint, err
}
func (c *RPCClient) ListBreakpoints() ([]*api.Breakpoint, error) {
var out ListBreakpointsOut
err := c.call("ListBreakpoints", ListBreakpointsIn{}, &out)
return out.Breakpoints, err
}
func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) {
var out ClearBreakpointOut
err := c.call("ClearBreakpoint", ClearBreakpointIn{id, ""}, &out)
return out.Breakpoint, err
}
func (c *RPCClient) ClearBreakpointByName(name string) (*api.Breakpoint, error) {
var out ClearBreakpointOut
err := c.call("ClearBreakpoint", ClearBreakpointIn{0, name}, &out)
return out.Breakpoint, err
}
func (c *RPCClient) AmendBreakpoint(bp *api.Breakpoint) error {
out := new(AmendBreakpointOut)
err := c.call("AmendBreakpoint", AmendBreakpointIn{*bp}, out)
return err
}
func (c *RPCClient) CancelNext() error {
var out CancelNextOut
return c.call("CancelNext", CancelNextIn{}, &out)
}
func (c *RPCClient) ListThreads() ([]*api.Thread, error) {
var out ListThreadsOut
err := c.call("ListThreads", ListThreadsIn{}, &out)
return out.Threads, err
}
func (c *RPCClient) GetThread(id int) (*api.Thread, error) {
var out GetThreadOut
err := c.call("GetThread", GetThreadIn{id}, &out)
return out.Thread, err
}
func (c *RPCClient) EvalVariable(scope api.EvalScope, expr string, cfg api.LoadConfig) (*api.Variable, error) {
var out EvalOut
err := c.call("Eval", EvalIn{scope, expr, &cfg}, &out)
return out.Variable, err
}
func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error {
out := new(SetOut)
return c.call("Set", SetIn{scope, symbol, value}, out)
}
func (c *RPCClient) ListSources(filter string) ([]string, error) {
sources := new(ListSourcesOut)
err := c.call("ListSources", ListSourcesIn{filter}, sources)
return sources.Sources, err
}
func (c *RPCClient) ListFunctions(filter string) ([]string, error) {
funcs := new(ListFunctionsOut)
err := c.call("ListFunctions", ListFunctionsIn{filter}, funcs)
return funcs.Funcs, err
}
func (c *RPCClient) ListTypes(filter string) ([]string, error) {
types := new(ListTypesOut)
err := c.call("ListTypes", ListTypesIn{filter}, types)
return types.Types, err
}
func (c *RPCClient) ListPackageVariables(filter string, cfg api.LoadConfig) ([]api.Variable, error) {
var out ListPackageVarsOut
err := c.call("ListPackageVars", ListPackageVarsIn{filter, cfg}, &out)
return out.Variables, err
}
func (c *RPCClient) ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {
var out ListLocalVarsOut
err := c.call("ListLocalVars", ListLocalVarsIn{scope, cfg}, &out)
return out.Variables, err
}
func (c *RPCClient) ListRegisters(threadID int, includeFp bool) (api.Registers, error) {
out := new(ListRegistersOut)
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) {
var out ListFunctionArgsOut
err := c.call("ListFunctionArgs", ListFunctionArgsIn{scope, cfg}, &out)
return out.Args, err
}
func (c *RPCClient) ListGoroutines(start, count int) ([]*api.Goroutine, int, error) {
var out ListGoroutinesOut
err := c.call("ListGoroutines", ListGoroutinesIn{start, count}, &out)
return out.Goroutines, out.Nextg, err
}
func (c *RPCClient) Stacktrace(goroutineId, depth int, readDefers bool, cfg *api.LoadConfig) ([]api.Stackframe, error) {
var out StacktraceOut
err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, false, readDefers, cfg}, &out)
return out.Locations, err
}
func (c *RPCClient) Ancestors(goroutineID int, numAncestors int, depth int) ([]api.Ancestor, error) {
var out AncestorsOut
err := c.call("Ancestors", AncestorsIn{goroutineID, numAncestors, depth}, &out)
return out.Ancestors, err
}
func (c *RPCClient) AttachedToExistingProcess() bool {
out := new(AttachedToExistingProcessOut)
c.call("AttachedToExistingProcess", AttachedToExistingProcessIn{}, out)
return out.Answer
}
func (c *RPCClient) FindLocation(scope api.EvalScope, loc string) ([]api.Location, error) {
var out FindLocationOut
err := c.call("FindLocation", FindLocationIn{scope, loc}, &out)
return out.Locations, err
}
// Disassemble code between startPC and endPC
func (c *RPCClient) DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
var out DisassembleOut
err := c.call("Disassemble", DisassembleIn{scope, startPC, endPC, flavour}, &out)
return out.Disassemble, err
}
// Disassemble function containing pc
func (c *RPCClient) DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
var out DisassembleOut
err := c.call("Disassemble", DisassembleIn{scope, pc, 0, flavour}, &out)
return out.Disassemble, err
}
// Recorded returns true if the debugger target is a recording.
func (c *RPCClient) Recorded() bool {
out := new(RecordedOut)
c.call("Recorded", RecordedIn{}, out)
return out.Recorded
}
// TraceDirectory returns the path to the trace directory for a recording.
func (c *RPCClient) TraceDirectory() (string, error) {
var out RecordedOut
err := c.call("Recorded", RecordedIn{}, &out)
return out.TraceDirectory, err
}
// Checkpoint sets a checkpoint at the current position.
func (c *RPCClient) Checkpoint(where string) (checkpointID int, err error) {
var out CheckpointOut
err = c.call("Checkpoint", CheckpointIn{where}, &out)
return out.ID, err
}
// ListCheckpoints gets all checkpoints.
func (c *RPCClient) ListCheckpoints() ([]api.Checkpoint, error) {
var out ListCheckpointsOut
err := c.call("ListCheckpoints", ListCheckpointsIn{}, &out)
return out.Checkpoints, err
}
// ClearCheckpoint removes a checkpoint
func (c *RPCClient) ClearCheckpoint(id int) error {
var out ClearCheckpointOut
err := c.call("ClearCheckpoint", ClearCheckpointIn{id}, &out)
return err
}
func (c *RPCClient) SetReturnValuesLoadConfig(cfg *api.LoadConfig) {
c.retValLoadCfg = cfg
}
func (c *RPCClient) FunctionReturnLocations(fnName string) ([]uint64, error) {
var out FunctionReturnLocationsOut
err := c.call("FunctionReturnLocations", FunctionReturnLocationsIn{fnName}, &out)
return out.Addrs, err
}
func (c *RPCClient) IsMulticlient() bool {
var out IsMulticlientOut
c.call("IsMulticlient", IsMulticlientIn{}, &out)
return out.IsMulticlient
}
func (c *RPCClient) Disconnect(cont bool) error {
if cont {
out := new(CommandOut)
c.client.Go("RPCServer.Command", &api.DebuggerCommand{Name: api.Continue, ReturnInfoLoadConfig: c.retValLoadCfg}, &out, nil)
}
return c.client.Close()
}
func (c *RPCClient) ListDynamicLibraries() ([]api.Image, error) {
var out ListDynamicLibrariesOut
c.call("ListDynamicLibraries", ListDynamicLibrariesIn{}, &out)
return out.List, nil
}
func (c *RPCClient) call(method string, args, reply interface{}) error {
return c.client.Call("RPCServer."+method, args, reply)
}

724
vendor/github.com/go-delve/delve/service/rpc2/server.go generated vendored Normal file
View File

@ -0,0 +1,724 @@
package rpc2
import (
"errors"
"fmt"
"time"
"github.com/go-delve/delve/service"
"github.com/go-delve/delve/service/api"
"github.com/go-delve/delve/service/debugger"
)
type RPCServer struct {
// config is all the information necessary to start the debugger and server.
config *service.Config
// debugger is a debugger service.
debugger *debugger.Debugger
}
func NewServer(config *service.Config, debugger *debugger.Debugger) *RPCServer {
return &RPCServer{config, debugger}
}
type ProcessPidIn struct {
}
type ProcessPidOut struct {
Pid int
}
// ProcessPid returns the pid of the process we are debugging.
func (s *RPCServer) ProcessPid(arg ProcessPidIn, out *ProcessPidOut) error {
out.Pid = s.debugger.ProcessPid()
return nil
}
type LastModifiedIn struct {
}
type LastModifiedOut struct {
Time time.Time
}
func (s *RPCServer) LastModified(arg LastModifiedIn, out *LastModifiedOut) error {
out.Time = s.debugger.LastModified()
return nil
}
type DetachIn struct {
Kill bool
}
type DetachOut struct {
}
// Detach detaches the debugger, optionally killing the process.
func (s *RPCServer) Detach(arg DetachIn, out *DetachOut) error {
err := s.debugger.Detach(arg.Kill)
if s.config.DisconnectChan != nil {
close(s.config.DisconnectChan)
s.config.DisconnectChan = nil
}
return err
}
type RestartIn struct {
// Position to restart from, if it starts with 'c' it's a checkpoint ID,
// otherwise it's an event number. Only valid for recorded targets.
Position string
// ResetArgs tell whether NewArgs should take effect.
ResetArgs bool
// NewArgs are arguments to launch a new process. They replace only the
// argv[1] and later. Argv[0] cannot be changed.
NewArgs []string
}
type RestartOut struct {
DiscardedBreakpoints []api.DiscardedBreakpoint
}
// Restart restarts program.
func (s *RPCServer) Restart(arg RestartIn, out *RestartOut) error {
if s.config.AttachPid != 0 {
return errors.New("cannot restart process Delve did not create")
}
var err error
out.DiscardedBreakpoints, err = s.debugger.Restart(arg.Position, arg.ResetArgs, arg.NewArgs)
return err
}
type StateIn struct {
// If NonBlocking is true State will return immediately even if the target process is running.
NonBlocking bool
}
type StateOut struct {
State *api.DebuggerState
}
// State returns the current debugger state.
func (s *RPCServer) State(arg StateIn, out *StateOut) error {
st, err := s.debugger.State(arg.NonBlocking)
if err != nil {
return err
}
out.State = st
return nil
}
type CommandOut struct {
State api.DebuggerState
}
// Command interrupts, continues and steps through the program.
func (s *RPCServer) Command(command api.DebuggerCommand, cb service.RPCCallback) {
st, err := s.debugger.Command(&command)
if err != nil {
cb.Return(nil, err)
return
}
var out CommandOut
out.State = *st
cb.Return(out, nil)
}
type GetBreakpointIn struct {
Id int
Name string
}
type GetBreakpointOut struct {
Breakpoint api.Breakpoint
}
// GetBreakpoint gets a breakpoint by Name (if Name is not an empty string) or by ID.
func (s *RPCServer) GetBreakpoint(arg GetBreakpointIn, out *GetBreakpointOut) error {
var bp *api.Breakpoint
if arg.Name != "" {
bp = s.debugger.FindBreakpointByName(arg.Name)
if bp == nil {
return fmt.Errorf("no breakpoint with name %s", arg.Name)
}
} else {
bp = s.debugger.FindBreakpoint(arg.Id)
if bp == nil {
return fmt.Errorf("no breakpoint with id %d", arg.Id)
}
}
out.Breakpoint = *bp
return nil
}
type StacktraceIn struct {
Id int
Depth int
Full bool
Defers bool // read deferred functions
Cfg *api.LoadConfig
}
type StacktraceOut struct {
Locations []api.Stackframe
}
// Stacktrace returns stacktrace of goroutine Id up to the specified Depth.
//
// If Full is set it will also the variable of all local variables
// and function arguments of all stack frames.
func (s *RPCServer) Stacktrace(arg StacktraceIn, out *StacktraceOut) error {
cfg := arg.Cfg
if cfg == nil && arg.Full {
cfg = &api.LoadConfig{true, 1, 64, 64, -1}
}
var err error
out.Locations, err = s.debugger.Stacktrace(arg.Id, arg.Depth, arg.Defers, api.LoadConfigToProc(cfg))
return err
}
type AncestorsIn struct {
GoroutineID int
NumAncestors int
Depth int
}
type AncestorsOut struct {
Ancestors []api.Ancestor
}
// Ancestors returns the stacktraces for the ancestors of a goroutine.
func (s *RPCServer) Ancestors(arg AncestorsIn, out *AncestorsOut) error {
var err error
out.Ancestors, err = s.debugger.Ancestors(arg.GoroutineID, arg.NumAncestors, arg.Depth)
return err
}
type ListBreakpointsIn struct {
}
type ListBreakpointsOut struct {
Breakpoints []*api.Breakpoint
}
// ListBreakpoints gets all breakpoints.
func (s *RPCServer) ListBreakpoints(arg ListBreakpointsIn, out *ListBreakpointsOut) error {
out.Breakpoints = s.debugger.Breakpoints()
return nil
}
type CreateBreakpointIn struct {
Breakpoint api.Breakpoint
}
type CreateBreakpointOut struct {
Breakpoint api.Breakpoint
}
// CreateBreakpoint creates a new breakpoint.
//
// - If arg.Breakpoint.File is not an empty string the breakpoint
// will be created on the specified file:line location
//
// - If arg.Breakpoint.FunctionName is not an empty string
// the breakpoint will be created on the specified function:line
// location. Note that setting a breakpoint on a function's entry point
// (line == 0) can have surprising consequences, it is advisable to
// use line = -1 instead which will skip the prologue.
//
// - Otherwise the value specified by arg.Breakpoint.Addr will be used.
func (s *RPCServer) CreateBreakpoint(arg CreateBreakpointIn, out *CreateBreakpointOut) error {
createdbp, err := s.debugger.CreateBreakpoint(&arg.Breakpoint)
if err != nil {
return err
}
out.Breakpoint = *createdbp
return nil
}
type ClearBreakpointIn struct {
Id int
Name string
}
type ClearBreakpointOut struct {
Breakpoint *api.Breakpoint
}
// ClearBreakpoint deletes a breakpoint by Name (if Name is not an
// empty string) or by ID.
func (s *RPCServer) ClearBreakpoint(arg ClearBreakpointIn, out *ClearBreakpointOut) error {
var bp *api.Breakpoint
if arg.Name != "" {
bp = s.debugger.FindBreakpointByName(arg.Name)
if bp == nil {
return fmt.Errorf("no breakpoint with name %s", arg.Name)
}
} else {
bp = s.debugger.FindBreakpoint(arg.Id)
if bp == nil {
return fmt.Errorf("no breakpoint with id %d", arg.Id)
}
}
deleted, err := s.debugger.ClearBreakpoint(bp)
if err != nil {
return err
}
out.Breakpoint = deleted
return nil
}
type AmendBreakpointIn struct {
Breakpoint api.Breakpoint
}
type AmendBreakpointOut struct {
}
// AmendBreakpoint allows user to update an existing breakpoint
// for example to change the information retrieved when the
// breakpoint is hit or to change, add or remove the break condition.
//
// arg.Breakpoint.ID must be a valid breakpoint ID
func (s *RPCServer) AmendBreakpoint(arg AmendBreakpointIn, out *AmendBreakpointOut) error {
return s.debugger.AmendBreakpoint(&arg.Breakpoint)
}
type CancelNextIn struct {
}
type CancelNextOut struct {
}
func (s *RPCServer) CancelNext(arg CancelNextIn, out *CancelNextOut) error {
return s.debugger.CancelNext()
}
type ListThreadsIn struct {
}
type ListThreadsOut struct {
Threads []*api.Thread
}
// ListThreads lists all threads.
func (s *RPCServer) ListThreads(arg ListThreadsIn, out *ListThreadsOut) (err error) {
out.Threads, err = s.debugger.Threads()
return err
}
type GetThreadIn struct {
Id int
}
type GetThreadOut struct {
Thread *api.Thread
}
// GetThread gets a thread by its ID.
func (s *RPCServer) GetThread(arg GetThreadIn, out *GetThreadOut) error {
t, err := s.debugger.FindThread(arg.Id)
if err != nil {
return err
}
if t == nil {
return fmt.Errorf("no thread with id %d", arg.Id)
}
out.Thread = t
return nil
}
type ListPackageVarsIn struct {
Filter string
Cfg api.LoadConfig
}
type ListPackageVarsOut struct {
Variables []api.Variable
}
// ListPackageVars lists all package variables in the context of the current thread.
func (s *RPCServer) ListPackageVars(arg ListPackageVarsIn, out *ListPackageVarsOut) error {
state, err := s.debugger.State(false)
if err != nil {
return err
}
current := state.CurrentThread
if current == nil {
return fmt.Errorf("no current thread")
}
vars, err := s.debugger.PackageVariables(current.ID, arg.Filter, *api.LoadConfigToProc(&arg.Cfg))
if err != nil {
return err
}
out.Variables = vars
return nil
}
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 {
if arg.ThreadID == 0 {
state, err := s.debugger.State(false)
if err != nil {
return err
}
arg.ThreadID = state.CurrentThread.ID
}
regs, err := s.debugger.Registers(arg.ThreadID, arg.IncludeFp)
if err != nil {
return err
}
out.Regs = regs
out.Registers = out.Regs.String()
return nil
}
type ListLocalVarsIn struct {
Scope api.EvalScope
Cfg api.LoadConfig
}
type ListLocalVarsOut struct {
Variables []api.Variable
}
// 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))
if err != nil {
return err
}
out.Variables = vars
return nil
}
type ListFunctionArgsIn struct {
Scope api.EvalScope
Cfg api.LoadConfig
}
type ListFunctionArgsOut struct {
Args []api.Variable
}
// 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))
if err != nil {
return err
}
out.Args = vars
return nil
}
type EvalIn struct {
Scope api.EvalScope
Expr string
Cfg *api.LoadConfig
}
type EvalOut struct {
Variable *api.Variable
}
// EvalVariable returns a variable in the specified context.
//
// See https://github.com/go-delve/delve/wiki/Expressions for
// a description of acceptable values of arg.Expr.
func (s *RPCServer) Eval(arg EvalIn, out *EvalOut) error {
cfg := arg.Cfg
if cfg == nil {
cfg = &api.LoadConfig{true, 1, 64, 64, -1}
}
v, err := s.debugger.EvalVariableInScope(arg.Scope, arg.Expr, *api.LoadConfigToProc(cfg))
if err != nil {
return err
}
out.Variable = v
return nil
}
type SetIn struct {
Scope api.EvalScope
Symbol string
Value string
}
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)
}
type ListSourcesIn struct {
Filter string
}
type ListSourcesOut struct {
Sources []string
}
// ListSources lists all source files in the process matching filter.
func (s *RPCServer) ListSources(arg ListSourcesIn, out *ListSourcesOut) error {
ss, err := s.debugger.Sources(arg.Filter)
if err != nil {
return err
}
out.Sources = ss
return nil
}
type ListFunctionsIn struct {
Filter string
}
type ListFunctionsOut struct {
Funcs []string
}
// ListFunctions lists all functions in the process matching filter.
func (s *RPCServer) ListFunctions(arg ListFunctionsIn, out *ListFunctionsOut) error {
fns, err := s.debugger.Functions(arg.Filter)
if err != nil {
return err
}
out.Funcs = fns
return nil
}
type ListTypesIn struct {
Filter string
}
type ListTypesOut struct {
Types []string
}
// ListTypes lists all types in the process matching filter.
func (s *RPCServer) ListTypes(arg ListTypesIn, out *ListTypesOut) error {
tps, err := s.debugger.Types(arg.Filter)
if err != nil {
return err
}
out.Types = tps
return nil
}
type ListGoroutinesIn struct {
Start int
Count int
}
type ListGoroutinesOut struct {
Goroutines []*api.Goroutine
Nextg int
}
// ListGoroutines lists all goroutines.
// If Count is specified ListGoroutines will return at the first Count
// goroutines and an index in Nextg, that can be passed as the Start
// parameter, to get more goroutines from ListGoroutines.
// Passing a value of Start that wasn't returned by ListGoroutines will skip
// an undefined number of goroutines.
func (s *RPCServer) ListGoroutines(arg ListGoroutinesIn, out *ListGoroutinesOut) error {
gs, nextg, err := s.debugger.Goroutines(arg.Start, arg.Count)
if err != nil {
return err
}
out.Goroutines = gs
out.Nextg = nextg
return nil
}
type AttachedToExistingProcessIn struct {
}
type AttachedToExistingProcessOut struct {
Answer bool
}
// AttachedToExistingProcess returns whether we attached to a running process or not
func (c *RPCServer) AttachedToExistingProcess(arg AttachedToExistingProcessIn, out *AttachedToExistingProcessOut) error {
if c.config.AttachPid != 0 {
out.Answer = true
}
return nil
}
type FindLocationIn struct {
Scope api.EvalScope
Loc string
}
type FindLocationOut struct {
Locations []api.Location
}
// FindLocation returns concrete location information described by a location expression
//
// loc ::= <filename>:<line> | <function>[:<line>] | /<regex>/ | (+|-)<offset> | <line> | *<address>
// * <filename> can be the full path of a file or just a suffix
// * <function> ::= <package>.<receiver type>.<name> | <package>.(*<receiver type>).<name> | <receiver type>.<name> | <package>.<name> | (*<receiver type>).<name> | <name>
// * <function> must be unambiguous
// * /<regex>/ will return a location for each function matched by regex
// * +<offset> returns a location for the line that is <offset> lines after the current line
// * -<offset> returns a location for the line that is <offset> lines before the current line
// * <line> returns a location for a line in the current file
// * *<address> returns the location corresponding to the specified address
//
// 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)
return err
}
type DisassembleIn struct {
Scope api.EvalScope
StartPC, EndPC uint64
Flavour api.AssemblyFlavour
}
type DisassembleOut struct {
Disassemble api.AsmInstructions
}
// Disassemble code.
//
// If both StartPC and EndPC are non-zero the specified range will be disassembled, otherwise the function containing StartPC will be disassembled.
//
// Scope is used to mark the instruction the specified goroutine is stopped at.
//
// 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, arg.StartPC, arg.EndPC, arg.Flavour)
return err
}
type RecordedIn struct {
}
type RecordedOut struct {
Recorded bool
TraceDirectory string
}
func (s *RPCServer) Recorded(arg RecordedIn, out *RecordedOut) error {
out.Recorded, out.TraceDirectory = s.debugger.Recorded()
return nil
}
type CheckpointIn struct {
Where string
}
type CheckpointOut struct {
ID int
}
func (s *RPCServer) Checkpoint(arg CheckpointIn, out *CheckpointOut) error {
var err error
out.ID, err = s.debugger.Checkpoint(arg.Where)
return err
}
type ListCheckpointsIn struct {
}
type ListCheckpointsOut struct {
Checkpoints []api.Checkpoint
}
func (s *RPCServer) ListCheckpoints(arg ListCheckpointsIn, out *ListCheckpointsOut) error {
var err error
out.Checkpoints, err = s.debugger.Checkpoints()
return err
}
type ClearCheckpointIn struct {
ID int
}
type ClearCheckpointOut struct {
}
func (s *RPCServer) ClearCheckpoint(arg ClearCheckpointIn, out *ClearCheckpointOut) error {
return s.debugger.ClearCheckpoint(arg.ID)
}
type IsMulticlientIn struct {
}
type IsMulticlientOut struct {
// IsMulticlient returns true if the headless instance was started with --accept-multiclient
IsMulticlient bool
}
func (s *RPCServer) IsMulticlient(arg IsMulticlientIn, out *IsMulticlientOut) error {
*out = IsMulticlientOut{
IsMulticlient: s.config.AcceptMulti,
}
return nil
}
// FunctionReturnLocationsIn holds arguments for the
// FunctionReturnLocationsRPC call. It holds the name of
// the function for which all return locations should be
// given.
type FunctionReturnLocationsIn struct {
// FnName is the name of the function for which all
// return locations should be given.
FnName string
}
// FunctionReturnLocationsOut holds the result of the FunctionReturnLocations
// RPC call. It provides the list of addresses that the given function returns,
// for example with a `RET` instruction or `CALL runtime.deferreturn`.
type FunctionReturnLocationsOut struct {
// Addrs is the list of all locations where the given function returns.
Addrs []uint64
}
// FunctionReturnLocations is the implements the client call of the same name. Look at client documentation for more information.
func (s *RPCServer) FunctionReturnLocations(in FunctionReturnLocationsIn, out *FunctionReturnLocationsOut) error {
addrs, err := s.debugger.FunctionReturnLocations(in.FnName)
if err != nil {
return err
}
*out = FunctionReturnLocationsOut{
Addrs: addrs,
}
return nil
}
// ListDynamicLibrariesIn holds the arguments of ListDynamicLibraries
type ListDynamicLibrariesIn struct {
}
// ListDynamicLibrariesOut holds the return values of ListDynamicLibraries
type ListDynamicLibrariesOut struct {
List []api.Image
}
func (s *RPCServer) ListDynamicLibraries(in ListDynamicLibrariesIn, out *ListDynamicLibrariesOut) error {
out.List = s.debugger.ListDynamicLibraries()
return nil
}

View File

@ -0,0 +1,6 @@
package service
// RPCCallback is used by RPC methods to return their result asynchronously.
type RPCCallback interface {
Return(out interface{}, err error)
}

View File

@ -0,0 +1,439 @@
package rpccommon
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"reflect"
"runtime"
"sync"
"unicode"
"unicode/utf8"
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/version"
"github.com/go-delve/delve/service"
"github.com/go-delve/delve/service/api"
"github.com/go-delve/delve/service/debugger"
"github.com/go-delve/delve/service/rpc1"
"github.com/go-delve/delve/service/rpc2"
"github.com/sirupsen/logrus"
)
// ServerImpl implements a JSON-RPC server that can switch between two
// versions of the API.
type ServerImpl struct {
// config is all the information necessary to start the debugger and server.
config *service.Config
// listener is used to serve HTTP.
listener net.Listener
// stopChan is used to stop the listener goroutine.
stopChan chan struct{}
// debugger is the debugger service.
debugger *debugger.Debugger
// s1 is APIv1 server.
s1 *rpc1.RPCServer
// s2 is APIv2 server.
s2 *rpc2.RPCServer
// maps of served methods, one for each supported API.
methodMaps []map[string]*methodType
log *logrus.Entry
}
type RPCCallback struct {
s *ServerImpl
sending *sync.Mutex
codec rpc.ServerCodec
req rpc.Request
}
// RPCServer implements the RPC method calls common to all versions of the API.
type RPCServer struct {
s *ServerImpl
}
type methodType struct {
method reflect.Method
Rcvr reflect.Value
ArgType reflect.Type
ReplyType reflect.Type
Synchronous bool
}
// NewServer creates a new RPCServer.
func NewServer(config *service.Config) *ServerImpl {
logger := logflags.RPCLogger()
if config.APIVersion < 2 {
logger.Info("Using API v1")
}
if config.Foreground {
// Print listener address
logflags.WriteAPIListeningMessage(config.Listener.Addr().String())
}
return &ServerImpl{
config: config,
listener: config.Listener,
stopChan: make(chan struct{}),
log: logger,
}
}
// Stop stops the JSON-RPC server.
func (s *ServerImpl) Stop() error {
if s.config.AcceptMulti {
close(s.stopChan)
s.listener.Close()
}
kill := s.config.AttachPid == 0
return s.debugger.Detach(kill)
}
// Restart restarts the debugger.
func (s *ServerImpl) Restart() error {
if s.config.AttachPid != 0 {
return errors.New("cannot restart process Delve did not create")
}
return s.s2.Restart(rpc2.RestartIn{}, nil)
}
// Run starts a debugger and exposes it with an HTTP server. The debugger
// itself can be stopped with the `detach` API. Run blocks until the HTTP
// server stops.
func (s *ServerImpl) Run() error {
var err error
if s.config.APIVersion < 2 {
s.config.APIVersion = 1
}
if s.config.APIVersion > 2 {
return fmt.Errorf("unknown API version")
}
// Create and start the debugger
if s.debugger, err = debugger.New(&debugger.Config{
AttachPid: s.config.AttachPid,
WorkingDir: s.config.WorkingDir,
CoreFile: s.config.CoreFile,
Backend: s.config.Backend,
Foreground: s.config.Foreground,
DebugInfoDirectories: s.config.DebugInfoDirectories,
},
s.config.ProcessArgs); err != nil {
return err
}
s.s1 = rpc1.NewServer(s.config, s.debugger)
s.s2 = rpc2.NewServer(s.config, s.debugger)
rpcServer := &RPCServer{s}
s.methodMaps = make([]map[string]*methodType, 2)
s.methodMaps[0] = map[string]*methodType{}
s.methodMaps[1] = map[string]*methodType{}
suitableMethods(s.s1, s.methodMaps[0], s.log)
suitableMethods(rpcServer, s.methodMaps[0], s.log)
suitableMethods(s.s2, s.methodMaps[1], s.log)
suitableMethods(rpcServer, s.methodMaps[1], s.log)
go func() {
defer s.listener.Close()
for {
c, err := s.listener.Accept()
if err != nil {
select {
case <-s.stopChan:
// We were supposed to exit, do nothing and return
return
default:
panic(err)
}
}
go s.serveJSONCodec(c)
if !s.config.AcceptMulti {
break
}
}
}()
return nil
}
// Precompute the reflect type for error. Can't use error directly
// because Typeof takes an empty interface value. This is annoying.
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
// Is this an exported - upper case - name?
func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(rune)
}
// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
// PkgPath will be non-empty even for an exported type,
// so we need to check the type name as well.
return isExported(t.Name()) || t.PkgPath() == ""
}
// Fills methods map with the methods of receiver that should be made
// available through the RPC interface.
// These are all the public methods of rcvr that have one of those
// two signatures:
// func (rcvr ReceiverType) Method(in InputType, out *ReplyType) error
// func (rcvr ReceiverType) Method(in InputType, cb service.RPCCallback)
func suitableMethods(rcvr interface{}, methods map[string]*methodType, log *logrus.Entry) {
typ := reflect.TypeOf(rcvr)
rcvrv := reflect.ValueOf(rcvr)
sname := reflect.Indirect(rcvrv).Type().Name()
if sname == "" {
log.Debugf("rpc.Register: no service name for type %s", typ)
return
}
for m := 0; m < typ.NumMethod(); m++ {
method := typ.Method(m)
mname := method.Name
mtype := method.Type
// method must be exported
if method.PkgPath != "" {
continue
}
// Method needs three ins: (receive, *args, *reply) or (receiver, *args, *RPCCallback)
if mtype.NumIn() != 3 {
log.Warn("method", mname, "has wrong number of ins:", mtype.NumIn())
continue
}
// First arg need not be a pointer.
argType := mtype.In(1)
if !isExportedOrBuiltinType(argType) {
log.Warn(mname, "argument type not exported:", argType)
continue
}
replyType := mtype.In(2)
synchronous := replyType.String() != "service.RPCCallback"
if synchronous {
// Second arg must be a pointer.
if replyType.Kind() != reflect.Ptr {
log.Warn("method", mname, "reply type not a pointer:", replyType)
continue
}
// Reply type must be exported.
if !isExportedOrBuiltinType(replyType) {
log.Warn("method", mname, "reply type not exported:", replyType)
continue
}
// Method needs one out.
if mtype.NumOut() != 1 {
log.Warn("method", mname, "has wrong number of outs:", mtype.NumOut())
continue
}
// The return type of the method must be error.
if returnType := mtype.Out(0); returnType != typeOfError {
log.Warn("method", mname, "returns", returnType.String(), "not error")
continue
}
} else {
// Method needs zero outs.
if mtype.NumOut() != 0 {
log.Warn("method", mname, "has wrong number of outs:", mtype.NumOut())
continue
}
}
methods[sname+"."+mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType, Synchronous: synchronous, Rcvr: rcvrv}
}
}
func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) {
defer func() {
if !s.config.AcceptMulti && s.config.DisconnectChan != nil {
close(s.config.DisconnectChan)
}
}()
sending := new(sync.Mutex)
codec := jsonrpc.NewServerCodec(conn)
var req rpc.Request
var resp rpc.Response
for {
req = rpc.Request{}
err := codec.ReadRequestHeader(&req)
if err != nil {
if err != io.EOF {
s.log.Error("rpc:", err)
}
break
}
mtype, ok := s.methodMaps[s.config.APIVersion-1][req.ServiceMethod]
if !ok {
s.log.Errorf("rpc: can't find method %s", req.ServiceMethod)
continue
}
var argv, replyv reflect.Value
// Decode the argument value.
argIsValue := false // if true, need to indirect before calling.
if mtype.ArgType.Kind() == reflect.Ptr {
argv = reflect.New(mtype.ArgType.Elem())
} else {
argv = reflect.New(mtype.ArgType)
argIsValue = true
}
// argv guaranteed to be a pointer now.
if err = codec.ReadRequestBody(argv.Interface()); err != nil {
return
}
if argIsValue {
argv = argv.Elem()
}
if mtype.Synchronous {
if logflags.RPC() {
argvbytes, _ := json.Marshal(argv.Interface())
s.log.Debugf("<- %s(%T%s)", req.ServiceMethod, argv.Interface(), argvbytes)
}
replyv = reflect.New(mtype.ReplyType.Elem())
function := mtype.method.Func
var returnValues []reflect.Value
var errInter interface{}
func() {
defer func() {
if ierr := recover(); ierr != nil {
errInter = newInternalError(ierr, 2)
}
}()
returnValues = function.Call([]reflect.Value{mtype.Rcvr, argv, replyv})
errInter = returnValues[0].Interface()
}()
errmsg := ""
if errInter != nil {
errmsg = errInter.(error).Error()
}
resp = rpc.Response{}
if logflags.RPC() {
replyvbytes, _ := json.Marshal(replyv.Interface())
s.log.Debugf("-> %T%s error: %q", replyv.Interface(), replyvbytes, errmsg)
}
s.sendResponse(sending, &req, &resp, replyv.Interface(), codec, errmsg)
} else {
if logflags.RPC() {
argvbytes, _ := json.Marshal(argv.Interface())
s.log.Debugf("(async %d) <- %s(%T%s)", req.Seq, req.ServiceMethod, argv.Interface(), argvbytes)
}
function := mtype.method.Func
ctl := &RPCCallback{s, sending, codec, req}
go func() {
defer func() {
if ierr := recover(); ierr != nil {
ctl.Return(nil, newInternalError(ierr, 2))
}
}()
function.Call([]reflect.Value{mtype.Rcvr, argv, reflect.ValueOf(ctl)})
}()
}
}
codec.Close()
}
// A value sent as a placeholder for the server's response value when the server
// receives an invalid request. It is never decoded by the client since the Response
// contains an error when it is used.
var invalidRequest = struct{}{}
func (s *ServerImpl) sendResponse(sending *sync.Mutex, req *rpc.Request, resp *rpc.Response, reply interface{}, codec rpc.ServerCodec, errmsg string) {
resp.ServiceMethod = req.ServiceMethod
if errmsg != "" {
resp.Error = errmsg
reply = invalidRequest
}
resp.Seq = req.Seq
sending.Lock()
defer sending.Unlock()
err := codec.WriteResponse(resp, reply)
if err != nil {
s.log.Error("writing response:", err)
}
}
func (cb *RPCCallback) Return(out interface{}, err error) {
errmsg := ""
if err != nil {
errmsg = err.Error()
}
var resp rpc.Response
if logflags.RPC() {
outbytes, _ := json.Marshal(out)
cb.s.log.Debugf("(async %d) -> %T%s error: %q", cb.req.Seq, out, outbytes, errmsg)
}
cb.s.sendResponse(cb.sending, &cb.req, &resp, out, cb.codec, errmsg)
}
// GetVersion returns the version of delve as well as the API version
// currently served.
func (s *RPCServer) GetVersion(args api.GetVersionIn, out *api.GetVersionOut) error {
out.DelveVersion = version.DelveVersion.String()
out.APIVersion = s.s.config.APIVersion
return nil
}
// Changes version of the API being served.
func (s *RPCServer) SetApiVersion(args api.SetAPIVersionIn, out *api.SetAPIVersionOut) error {
if args.APIVersion < 2 {
args.APIVersion = 1
}
if args.APIVersion > 2 {
return fmt.Errorf("unknown API version")
}
s.s.config.APIVersion = args.APIVersion
return nil
}
type internalError struct {
Err interface{}
Stack []internalErrorFrame
}
type internalErrorFrame struct {
Pc uintptr
Func string
File string
Line int
}
func newInternalError(ierr interface{}, skip int) *internalError {
r := &internalError{ierr, nil}
for i := skip; ; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
fname := "<unknown>"
fn := runtime.FuncForPC(pc)
if fn != nil {
fname = fn.Name()
}
r.Stack = append(r.Stack, internalErrorFrame{pc, fname, file, line})
}
return r
}
func (err *internalError) Error() string {
var out bytes.Buffer
fmt.Fprintf(&out, "Internal debugger error: %v\n", err.Err)
for _, frame := range err.Stack {
fmt.Fprintf(&out, "%s (%#x)\n\t%s:%d\n", frame.Func, frame.Pc, frame.File, frame.Line)
}
return out.String()
}

8
vendor/github.com/go-delve/delve/service/server.go generated vendored Normal file
View File

@ -0,0 +1,8 @@
package service
// Server represents a server for a remote client
// to connect to.
type Server interface {
Run() error
Stop() error
}