mirror of
https://github.com/beego/bee.git
synced 2024-11-01 05:00:55 +00:00
313 lines
8.2 KiB
Go
313 lines
8.2 KiB
Go
package rpc1
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net/rpc"
|
|
"net/rpc/jsonrpc"
|
|
|
|
"sync"
|
|
|
|
"github.com/derekparker/delve/service/api"
|
|
)
|
|
|
|
// Client is a RPC service.Client.
|
|
type RPCClient struct {
|
|
addr string
|
|
processPid int
|
|
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) 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, ®s)
|
|
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) url(path string) string {
|
|
return fmt.Sprintf("http://%s%s", c.addr, path)
|
|
}
|
|
|
|
func (c *RPCClient) call(method string, args, reply interface{}) error {
|
|
return c.client.Call("RPCServer."+method, args, reply)
|
|
}
|