delve/service/rpc2/server.go

552 lines
10 KiB
Go
Raw Normal View History

package rpc2
import (
"errors"
"fmt"
"io/ioutil"
"log"
"net"
grpc "net/rpc"
"net/rpc/jsonrpc"
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api"
"github.com/derekparker/delve/service/debugger"
)
type ServerImpl struct {
s *RPCServer
}
type RPCServer 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 a debugger service.
debugger *debugger.Debugger
}
// NewServer creates a new RPCServer.
func NewServer(config *service.Config, logEnabled bool) *ServerImpl {
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
if !logEnabled {
log.SetOutput(ioutil.Discard)
}
return &ServerImpl{
&RPCServer{
config: config,
listener: config.Listener,
stopChan: make(chan struct{}),
},
}
}
// Stop detaches from the debugger and waits for it to stop.
func (s *ServerImpl) Stop(kill bool) error {
if s.s.config.AcceptMulti {
close(s.s.stopChan)
s.s.listener.Close()
}
err := s.s.debugger.Detach(kill)
if err != nil {
return err
}
return 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
// Create and start the debugger
if s.s.debugger, err = debugger.New(&debugger.Config{
ProcessArgs: s.s.config.ProcessArgs,
AttachPid: s.s.config.AttachPid,
}); err != nil {
return err
}
rpcs := grpc.NewServer()
rpcs.Register(s.s)
go func() {
defer s.s.listener.Close()
for {
c, err := s.s.listener.Accept()
if err != nil {
select {
case <-s.s.stopChan:
// We were supposed to exit, do nothing and return
return
default:
panic(err)
}
}
go rpcs.ServeCodec(jsonrpc.NewServerCodec(c))
if !s.s.config.AcceptMulti {
break
}
}
}()
return nil
}
func (s *ServerImpl) Restart() error {
return s.s.Restart(RestartIn{}, nil)
}
type ProcessPidIn struct {
}
type ProcessPidOut struct {
Pid int
}
func (s *RPCServer) ProcessPid(arg ProcessPidIn, out *ProcessPidOut) error {
out.Pid = s.debugger.ProcessPid()
return nil
}
type DetachIn struct {
Kill bool
}
type DetachOut struct {
}
func (s *RPCServer) Detach(arg DetachIn, out *DetachOut) error {
return s.debugger.Detach(arg.Kill)
}
type RestartIn struct {
}
type RestartOut struct {
}
func (s *RPCServer) Restart(arg RestartIn, out *RestartOut) error {
if s.config.AttachPid != 0 {
return errors.New("cannot restart process Delve did not create")
}
return s.debugger.Restart()
}
type StateIn struct {
}
type StateOut struct {
State *api.DebuggerState
}
func (s *RPCServer) State(arg StateIn, out *StateOut) error {
st, err := s.debugger.State()
if err != nil {
return err
}
out.State = st
return nil
}
type CommandOut struct {
State api.DebuggerState
}
func (s *RPCServer) Command(command api.DebuggerCommand, out *CommandOut) error {
st, err := s.debugger.Command(&command)
if err != nil {
return err
}
out.State = *st
return nil
}
type GetBreakpointIn struct {
Id int
Name string
}
type GetBreakpointOut struct {
Breakpoint api.Breakpoint
}
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
Cfg *api.LoadConfig
}
type StacktraceOut struct {
Locations []api.Stackframe
}
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 }
}
locs, err := s.debugger.Stacktrace(arg.Id, arg.Depth, api.LoadConfigToProc(cfg))
if err != nil {
return err
}
out.Locations = locs
return nil
}
type ListBreakpointsIn struct {
}
type ListBreakpointsOut struct {
Breakpoints []*api.Breakpoint
}
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
}
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
}
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 {
}
func (s *RPCServer) AmendBreakpoint(arg AmendBreakpointIn, out *AmendBreakpointOut) error {
return s.debugger.AmendBreakpoint(&arg.Breakpoint)
}
type ListThreadsIn struct {
}
type ListThreadsOut struct {
Threads []*api.Thread
}
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
}
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
}
func (s *RPCServer) ListPackageVars(arg ListPackageVarsIn, out *ListPackageVarsOut) error {
state, err := s.debugger.State()
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 {
}
type ListRegistersOut struct {
Registers string
}
func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) error {
state, err := s.debugger.State()
if err != nil {
return err
}
regs, err := s.debugger.Registers(state.CurrentThread.ID)
if err != nil {
return err
}
out.Registers = regs
return nil
}
type ListLocalVarsIn struct {
Scope api.EvalScope
Cfg api.LoadConfig
}
type ListLocalVarsOut struct {
Variables []api.Variable
}
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
}
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
}
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 {
}
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
}
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
}
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
}
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 {
}
type ListGoroutinesOut struct {
Goroutines []*api.Goroutine
}
func (s *RPCServer) ListGoroutines(arg ListGoroutinesIn, out *ListGoroutinesOut) error {
gs, err := s.debugger.Goroutines()
if err != nil {
return err
}
out.Goroutines = gs
return nil
}
type AttachedToExistingProcessIn struct {
}
type AttachedToExistingProcessOut struct {
Answer bool
}
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
}
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
}
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
}