Introduce JSON-RPC service
This commit is contained in:
parent
5642e0a106
commit
687dc4172d
4
Makefile
4
Makefile
@ -31,7 +31,7 @@ ifeq "$(CERT)" ""
|
||||
endif
|
||||
go test $(PREFIX)/terminal $(PREFIX)/dwarf/frame $(PREFIX)/dwarf/op $(PREFIX)/dwarf/util $(PREFIX)/source $(PREFIX)/dwarf/line
|
||||
go test -c $(PREFIX)/proc && codesign -s $(CERT) ./proc.test && ./proc.test $(TESTFLAGS) && rm ./proc.test
|
||||
go test -c $(PREFIX)/service/rest && codesign -s $(CERT) ./rest.test && ./rest.test $(TESTFLAGS) && rm ./rest.test
|
||||
go test -c $(PREFIX)/service/test && codesign -s $(CERT) ./test.test && ./test.test $(TESTFLAGS) && rm ./test.test
|
||||
else
|
||||
go test -v ./...
|
||||
endif
|
||||
@ -51,7 +51,7 @@ ifeq "$(UNAME)" "Darwin"
|
||||
ifeq "$(CERT)" ""
|
||||
$(error You must provide a CERT env var)
|
||||
endif
|
||||
go test -c $(PREFIX)/service/rest && codesign -s $(CERT) ./rest.test && ./rest.test -test.run $(RUN) && rm ./rest.test
|
||||
go test -c $(PREFIX)/service/test && codesign -s $(CERT) ./test.test && ./test.test -test.run $(RUN) && rm ./test.test
|
||||
else
|
||||
go test $(PREFIX)/service/rest -run $(RUN)
|
||||
endif
|
||||
|
@ -13,7 +13,7 @@ func anotherthread(wg *sync.WaitGroup) {
|
||||
|
||||
func main() {
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 100000; i++ {
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go anotherthread(&wg)
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ import (
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/rest"
|
||||
"github.com/derekparker/delve/service/rpc"
|
||||
"github.com/derekparker/delve/terminal"
|
||||
)
|
||||
|
||||
@ -38,11 +40,13 @@ func main() {
|
||||
var addr string
|
||||
var logEnabled bool
|
||||
var headless bool
|
||||
var http bool
|
||||
|
||||
flag.BoolVar(&printv, "version", false, "Print version number and exit.")
|
||||
flag.StringVar(&addr, "addr", "localhost:0", "Debugging server listen address.")
|
||||
flag.BoolVar(&logEnabled, "log", false, "Enable debugging server logging.")
|
||||
flag.BoolVar(&headless, "headless", false, "Run in headless mode.")
|
||||
flag.BoolVar(&http, "http", false, "Start HTTP server instead of RPC.")
|
||||
flag.Parse()
|
||||
|
||||
if flag.NFlag() == 0 && len(flag.Args()) == 0 {
|
||||
@ -60,12 +64,12 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
status := run(addr, logEnabled, headless)
|
||||
status := run(addr, logEnabled, headless, http)
|
||||
fmt.Println("[Hope I was of service hunting your bug!]")
|
||||
os.Exit(status)
|
||||
}
|
||||
|
||||
func run(addr string, logEnabled, headless bool) int {
|
||||
func run(addr string, logEnabled, headless, http bool) int {
|
||||
// Collect launch arguments
|
||||
var processArgs []string
|
||||
var attachPid int
|
||||
@ -121,18 +125,32 @@ func run(addr string, logEnabled, headless bool) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Create and start a REST debugger server
|
||||
server := rest.NewServer(&rest.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: processArgs,
|
||||
AttachPid: attachPid,
|
||||
}, logEnabled)
|
||||
// Create and start a debugger server
|
||||
var server service.Server
|
||||
if http {
|
||||
server = rest.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: processArgs,
|
||||
AttachPid: attachPid,
|
||||
}, logEnabled)
|
||||
} else {
|
||||
server = rpc.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: processArgs,
|
||||
AttachPid: attachPid,
|
||||
}, logEnabled)
|
||||
}
|
||||
go server.Run()
|
||||
|
||||
var status int
|
||||
if !headless {
|
||||
// Create and start a terminal
|
||||
client := rest.NewClient(listener.Addr().String())
|
||||
var client service.Client
|
||||
if http {
|
||||
client = rest.NewClient(listener.Addr().String())
|
||||
} else {
|
||||
client = rpc.NewClient(listener.Addr().String())
|
||||
}
|
||||
term := terminal.New(client)
|
||||
err, status = term.Run()
|
||||
} else {
|
||||
|
@ -101,7 +101,10 @@ func (dbp *Process) Detach(kill bool) (err error) {
|
||||
// Clean up any breakpoints we've set.
|
||||
for _, bp := range dbp.Breakpoints {
|
||||
if bp != nil {
|
||||
dbp.ClearBreakpoint(bp.Addr)
|
||||
_, err := dbp.ClearBreakpoint(bp.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
dbp.execPtraceFunc(func() {
|
||||
|
@ -3,6 +3,7 @@ package proc
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
@ -15,7 +16,7 @@ func init() {
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
protest.RunTestsWithFixtures(m)
|
||||
os.Exit(protest.RunTestsWithFixtures(m))
|
||||
}
|
||||
|
||||
func withTestProcess(name string, t *testing.T, fn func(p *Process, fixture protest.Fixture)) {
|
||||
|
@ -55,13 +55,12 @@ func BuildFixture(name string) Fixture {
|
||||
|
||||
// RunTestsWithFixtures will pre-compile test fixtures before running test
|
||||
// methods. Test binaries are deleted before exiting.
|
||||
func RunTestsWithFixtures(m *testing.M) {
|
||||
func RunTestsWithFixtures(m *testing.M) int {
|
||||
status := m.Run()
|
||||
|
||||
// Remove the fixtures.
|
||||
for _, f := range Fixtures {
|
||||
os.Remove(f.Path)
|
||||
}
|
||||
|
||||
os.Exit(status)
|
||||
return status
|
||||
}
|
||||
|
19
service/config.go
Normal file
19
service/config.go
Normal file
@ -0,0 +1,19 @@
|
||||
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
|
||||
// AttachPid is the PID of an existing process to which the debugger should
|
||||
// attach.
|
||||
AttachPid int
|
||||
}
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
restful "github.com/emicklei/go-restful"
|
||||
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
"github.com/derekparker/delve/service/debugger"
|
||||
)
|
||||
@ -16,31 +17,15 @@ import (
|
||||
// RESTServer exposes a Debugger via a HTTP REST API.
|
||||
type RESTServer struct {
|
||||
// config is all the information necessary to start the debugger and server.
|
||||
config *Config
|
||||
config *service.Config
|
||||
// listener is used to serve HTTP.
|
||||
listener net.Listener
|
||||
// debugger is a debugger service.
|
||||
debugger *debugger.Debugger
|
||||
}
|
||||
|
||||
// Config provides the configuration to start a Debugger and expose it with a
|
||||
// RESTServer.
|
||||
//
|
||||
// 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 HTTP.
|
||||
Listener net.Listener
|
||||
// ProcessArgs are the arguments to launch a new process.
|
||||
ProcessArgs []string
|
||||
// AttachPid is the PID of an existing process to which the debugger should
|
||||
// attach.
|
||||
AttachPid int
|
||||
}
|
||||
|
||||
// NewServer creates a new RESTServer.
|
||||
func NewServer(config *Config, logEnabled bool) *RESTServer {
|
||||
func NewServer(config *service.Config, logEnabled bool) *RESTServer {
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||
if !logEnabled {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
|
186
service/rpc/client.go
Normal file
186
service/rpc/client.go
Normal file
@ -0,0 +1,186 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
// Client is a RPC service.Client.
|
||||
type RPCClient struct {
|
||||
addr string
|
||||
client *rpc.Client
|
||||
}
|
||||
|
||||
// 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 &RPCClient{
|
||||
addr: addr,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RPCClient) Detach(kill bool) error {
|
||||
return c.call("Detach", kill, 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() (*api.DebuggerState, error) {
|
||||
state := new(api.DebuggerState)
|
||||
err := c.call("Command", &api.DebuggerCommand{Name: api.Continue}, state)
|
||||
return state, err
|
||||
}
|
||||
|
||||
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) 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) Halt() (*api.DebuggerState, error) {
|
||||
state := new(api.DebuggerState)
|
||||
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) 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) 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(symbol string) (*api.Variable, error) {
|
||||
v := new(api.Variable)
|
||||
err := c.call("EvalSymbol", symbol, v)
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) EvalVariableFor(threadID int, symbol string) (*api.Variable, error) {
|
||||
v := new(api.Variable)
|
||||
err := c.call("EvalThreadSymbol", threadID, v)
|
||||
return v, err
|
||||
}
|
||||
|
||||
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) 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() ([]api.Variable, error) {
|
||||
var vars []api.Variable
|
||||
err := c.call("ListLocalVars", nil, &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() ([]api.Variable, error) {
|
||||
var vars []api.Variable
|
||||
err := c.call("ListFunctionArgs", nil, &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) ([]*api.Location, error) {
|
||||
var locations []*api.Location
|
||||
err := c.call("StacktraceGoroutine", &StacktraceGoroutineArgs{Id: 1, Depth: depth}, &locations)
|
||||
return locations, 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)
|
||||
}
|
289
service/rpc/server.go
Normal file
289
service/rpc/server.go
Normal file
@ -0,0 +1,289 @@
|
||||
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
|
||||
}
|
6
service/server.go
Normal file
6
service/server.go
Normal file
@ -0,0 +1,6 @@
|
||||
package service
|
||||
|
||||
type Server interface {
|
||||
Run() error
|
||||
Stop(bool) error
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package rest
|
||||
package servicetest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -8,8 +9,11 @@ import (
|
||||
"testing"
|
||||
|
||||
protest "github.com/derekparker/delve/proc/test"
|
||||
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
"github.com/derekparker/delve/service/rest"
|
||||
"github.com/derekparker/delve/service/rpc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -17,7 +21,7 @@ func init() {
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
protest.RunTestsWithFixtures(m)
|
||||
os.Exit(protest.RunTestsWithFixtures(m))
|
||||
}
|
||||
|
||||
func withTestClient(name string, t *testing.T, fn func(c service.Client)) {
|
||||
@ -25,14 +29,33 @@ func withTestClient(name string, t *testing.T, fn func(c service.Client)) {
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't start listener: %s\n", err)
|
||||
}
|
||||
server := NewServer(&Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: []string{protest.BuildFixture(name).Path},
|
||||
}, false)
|
||||
go server.Run()
|
||||
client := NewClient(listener.Addr().String())
|
||||
defer client.Detach(true)
|
||||
fn(client)
|
||||
defer listener.Close()
|
||||
// Test REST service
|
||||
restService := func() {
|
||||
fmt.Println("---- RUNNING TEST WITH REST CLIENT ----")
|
||||
server := rest.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: []string{protest.BuildFixture(name).Path},
|
||||
}, false)
|
||||
go server.Run()
|
||||
client := rest.NewClient(listener.Addr().String())
|
||||
defer client.Detach(true)
|
||||
fn(client)
|
||||
}
|
||||
// Test RPC service
|
||||
rpcService := func() {
|
||||
fmt.Println("---- RUNNING TEST WITH RPC CLIENT ----")
|
||||
server := rpc.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: []string{protest.BuildFixture(name).Path},
|
||||
}, false)
|
||||
go server.Run()
|
||||
client := rpc.NewClient(listener.Addr().String())
|
||||
defer client.Detach(true)
|
||||
fn(client)
|
||||
}
|
||||
rpcService()
|
||||
restService()
|
||||
}
|
||||
|
||||
func TestClientServer_exit(t *testing.T) {
|
Loading…
Reference in New Issue
Block a user