delve/service/rpc/server.go
2015-06-21 21:11:30 -05:00

290 lines
6.1 KiB
Go

package rpc
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 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
// debugger is a debugger service.
debugger *debugger.Debugger
}
// NewServer creates a new RPCServer.
func NewServer(config *service.Config, logEnabled bool) *RPCServer {
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
if !logEnabled {
log.SetOutput(ioutil.Discard)
}
return &RPCServer{
config: config,
listener: config.Listener,
}
}
// Stop detaches from the debugger and waits for it to stop.
func (s *RPCServer) Stop(kill bool) error {
return s.debugger.Detach(kill)
}
// 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 *RPCServer) Run() error {
c, err := s.listener.Accept()
if err != nil {
return err
}
// Create and start the debugger
if s.debugger, err = debugger.New(&debugger.Config{
ProcessArgs: s.config.ProcessArgs,
AttachPid: s.config.AttachPid,
}); err != nil {
return err
}
rpcs := grpc.NewServer()
rpcs.Register(s)
rpcs.ServeCodec(jsonrpc.NewServerCodec(c))
return nil
}
func (s *RPCServer) Detach(kill bool, ret *int) error {
return s.debugger.Detach(kill)
}
func (s *RPCServer) State(arg interface{}, state *api.DebuggerState) error {
st, err := s.debugger.State()
if err != nil {
return err
}
*state = *st
return nil
}
func (s *RPCServer) Command(command *api.DebuggerCommand, state *api.DebuggerState) error {
st, err := s.debugger.Command(command)
if err != nil {
return err
}
*state = *st
return nil
}
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
}
type StacktraceGoroutineArgs struct {
Id, Depth int
}
func (s *RPCServer) StacktraceGoroutine(args *StacktraceGoroutineArgs, locations *[]api.Location) error {
locs, err := s.debugger.Stacktrace(args.Id, args.Depth)
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) ListThreads(arg interface{}, threads *[]*api.Thread) error {
*threads = s.debugger.Threads()
return nil
}
func (s *RPCServer) GetThread(id int, thread *api.Thread) error {
t := s.debugger.FindThread(id)
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()
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)
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 {
if thread := s.debugger.FindThread(args.Id); thread == nil {
return fmt.Errorf("no thread with id %d", args.Id)
}
vars, err := s.debugger.PackageVariables(args.Id, args.Filter)
if err != nil {
return err
}
*variables = vars
return nil
}
func (s *RPCServer) ListRegisters(arg interface{}, registers *string) error {
state, err := s.debugger.State()
if err != nil {
return err
}
regs, err := s.debugger.Registers(state.CurrentThread.ID)
if err != nil {
return err
}
*registers = regs
return nil
}
func (s *RPCServer) ListLocalVars(arg interface{}, variables *[]api.Variable) error {
state, err := s.debugger.State()
if err != nil {
return err
}
vars, err := s.debugger.LocalVariables(state.CurrentThread.ID)
if err != nil {
return err
}
*variables = vars
return nil
}
func (s *RPCServer) ListFunctionArgs(arg interface{}, variables *[]api.Variable) error {
state, err := s.debugger.State()
if err != nil {
return err
}
vars, err := s.debugger.FunctionArguments(state.CurrentThread.ID)
if err != nil {
return err
}
*variables = vars
return nil
}
func (s *RPCServer) EvalSymbol(symbol string, variable *api.Variable) error {
state, err := s.debugger.State()
if err != nil {
return err
}
current := state.CurrentThread
if current == nil {
return errors.New("no current thread")
}
v, err := s.debugger.EvalVariableInThread(current.ID, symbol)
if err != nil {
return err
}
*variable = *v
return nil
}
type ThreadSymbolArgs struct {
Id int
Symbol string
}
func (s *RPCServer) EvalThreadSymbol(args *ThreadSymbolArgs, variable *api.Variable) error {
v, err := s.debugger.EvalVariableInThread(args.Id, args.Symbol)
if err != nil {
return err
}
*variable = *v
return nil
}
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) ListGoroutines(arg interface{}, goroutines *[]*api.Goroutine) error {
gs, err := s.debugger.Goroutines()
if err != nil {
return err
}
*goroutines = gs
return nil
}