mirror of
https://github.com/go-delve/delve.git
synced 2025-10-30 02:07:58 +08:00
service,terminal: APIv2 plus method to select API version (#460)
New API version with better backwards compatibility plus mechanism to select the API version that a headless instance should use. Adds service/test/cmd/typecheckrpc.go to type check the RPC interface.
This commit is contained in:
committed by
Derek Parker
parent
f37a26d525
commit
af4798e2a9
@ -17,6 +17,7 @@ The goal of this tool is to provide a simple yet powerful interface for debuggin
|
||||
|
||||
```
|
||||
--accept-multiclient[=false]: Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate
|
||||
--api-version=1: Selects API version when headless
|
||||
--build-flags="": Build flags, to be passed to the compiler.
|
||||
--headless[=false]: Run debug server only, in headless mode.
|
||||
--init="": Init file, executed by the terminal client.
|
||||
@ -34,4 +35,4 @@ The goal of this tool is to provide a simple yet powerful interface for debuggin
|
||||
* [dlv trace](dlv_trace.md) - Compile and begin tracing program.
|
||||
* [dlv version](dlv_version.md) - Prints version.
|
||||
|
||||
###### Auto generated by spf13/cobra on 19-Feb-2016
|
||||
###### Auto generated by spf13/cobra on 11-Apr-2016
|
||||
|
||||
@ -15,6 +15,7 @@ dlv attach pid
|
||||
|
||||
```
|
||||
--accept-multiclient[=false]: Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate
|
||||
--api-version=1: Selects API version when headless
|
||||
--build-flags="": Build flags, to be passed to the compiler.
|
||||
--headless[=false]: Run debug server only, in headless mode.
|
||||
--init="": Init file, executed by the terminal client.
|
||||
@ -25,4 +26,4 @@ dlv attach pid
|
||||
### SEE ALSO
|
||||
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
|
||||
|
||||
###### Auto generated by spf13/cobra on 19-Feb-2016
|
||||
###### Auto generated by spf13/cobra on 11-Apr-2016
|
||||
|
||||
@ -15,6 +15,7 @@ dlv connect addr
|
||||
|
||||
```
|
||||
--accept-multiclient[=false]: Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate
|
||||
--api-version=1: Selects API version when headless
|
||||
--build-flags="": Build flags, to be passed to the compiler.
|
||||
--headless[=false]: Run debug server only, in headless mode.
|
||||
--init="": Init file, executed by the terminal client.
|
||||
@ -25,4 +26,4 @@ dlv connect addr
|
||||
### SEE ALSO
|
||||
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
|
||||
|
||||
###### Auto generated by spf13/cobra on 19-Feb-2016
|
||||
###### Auto generated by spf13/cobra on 11-Apr-2016
|
||||
|
||||
@ -16,6 +16,7 @@ dlv debug [package]
|
||||
|
||||
```
|
||||
--accept-multiclient[=false]: Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate
|
||||
--api-version=1: Selects API version when headless
|
||||
--build-flags="": Build flags, to be passed to the compiler.
|
||||
--headless[=false]: Run debug server only, in headless mode.
|
||||
--init="": Init file, executed by the terminal client.
|
||||
@ -26,4 +27,4 @@ dlv debug [package]
|
||||
### SEE ALSO
|
||||
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
|
||||
|
||||
###### Auto generated by spf13/cobra on 19-Feb-2016
|
||||
###### Auto generated by spf13/cobra on 11-Apr-2016
|
||||
|
||||
@ -15,6 +15,7 @@ dlv exec [./path/to/binary]
|
||||
|
||||
```
|
||||
--accept-multiclient[=false]: Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate
|
||||
--api-version=1: Selects API version when headless
|
||||
--build-flags="": Build flags, to be passed to the compiler.
|
||||
--headless[=false]: Run debug server only, in headless mode.
|
||||
--init="": Init file, executed by the terminal client.
|
||||
@ -25,4 +26,4 @@ dlv exec [./path/to/binary]
|
||||
### SEE ALSO
|
||||
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
|
||||
|
||||
###### Auto generated by spf13/cobra on 19-Feb-2016
|
||||
###### Auto generated by spf13/cobra on 11-Apr-2016
|
||||
|
||||
@ -15,6 +15,7 @@ dlv run
|
||||
|
||||
```
|
||||
--accept-multiclient[=false]: Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate
|
||||
--api-version=1: Selects API version when headless
|
||||
--build-flags="": Build flags, to be passed to the compiler.
|
||||
--headless[=false]: Run debug server only, in headless mode.
|
||||
--init="": Init file, executed by the terminal client.
|
||||
@ -25,4 +26,4 @@ dlv run
|
||||
### SEE ALSO
|
||||
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
|
||||
|
||||
###### Auto generated by spf13/cobra on 19-Feb-2016
|
||||
###### Auto generated by spf13/cobra on 11-Apr-2016
|
||||
|
||||
@ -15,6 +15,7 @@ dlv test [package]
|
||||
|
||||
```
|
||||
--accept-multiclient[=false]: Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate
|
||||
--api-version=1: Selects API version when headless
|
||||
--build-flags="": Build flags, to be passed to the compiler.
|
||||
--headless[=false]: Run debug server only, in headless mode.
|
||||
--init="": Init file, executed by the terminal client.
|
||||
@ -25,4 +26,4 @@ dlv test [package]
|
||||
### SEE ALSO
|
||||
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
|
||||
|
||||
###### Auto generated by spf13/cobra on 19-Feb-2016
|
||||
###### Auto generated by spf13/cobra on 11-Apr-2016
|
||||
|
||||
@ -22,6 +22,7 @@ dlv trace [package] regexp
|
||||
|
||||
```
|
||||
--accept-multiclient[=false]: Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate
|
||||
--api-version=1: Selects API version when headless
|
||||
--build-flags="": Build flags, to be passed to the compiler.
|
||||
--headless[=false]: Run debug server only, in headless mode.
|
||||
--init="": Init file, executed by the terminal client.
|
||||
@ -32,4 +33,4 @@ dlv trace [package] regexp
|
||||
### SEE ALSO
|
||||
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
|
||||
|
||||
###### Auto generated by spf13/cobra on 19-Feb-2016
|
||||
###### Auto generated by spf13/cobra on 11-Apr-2016
|
||||
|
||||
@ -15,6 +15,7 @@ dlv version
|
||||
|
||||
```
|
||||
--accept-multiclient[=false]: Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate
|
||||
--api-version=1: Selects API version when headless
|
||||
--build-flags="": Build flags, to be passed to the compiler.
|
||||
--headless[=false]: Run debug server only, in headless mode.
|
||||
--init="": Init file, executed by the terminal client.
|
||||
@ -25,4 +26,4 @@ dlv version
|
||||
### SEE ALSO
|
||||
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
|
||||
|
||||
###### Auto generated by spf13/cobra on 19-Feb-2016
|
||||
###### Auto generated by spf13/cobra on 11-Apr-2016
|
||||
|
||||
@ -15,7 +15,8 @@ import (
|
||||
"github.com/derekparker/delve/config"
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
"github.com/derekparker/delve/service/rpc"
|
||||
"github.com/derekparker/delve/service/rpc1"
|
||||
"github.com/derekparker/delve/service/rpc2"
|
||||
"github.com/derekparker/delve/terminal"
|
||||
"github.com/derekparker/delve/version"
|
||||
"github.com/spf13/cobra"
|
||||
@ -26,6 +27,8 @@ var (
|
||||
Log bool
|
||||
// Headless is whether to run without terminal.
|
||||
Headless bool
|
||||
// ApiVersion is the requested API version while running headless
|
||||
ApiVersion int
|
||||
// AcceptMulti allows multiple clients to connect to the same server
|
||||
AcceptMulti bool
|
||||
// Addr is the debugging server listen address.
|
||||
@ -78,6 +81,7 @@ func New() *cobra.Command {
|
||||
RootCommand.PersistentFlags().BoolVarP(&Log, "log", "", false, "Enable debugging server logging.")
|
||||
RootCommand.PersistentFlags().BoolVarP(&Headless, "headless", "", false, "Run debug server only, in headless mode.")
|
||||
RootCommand.PersistentFlags().BoolVarP(&AcceptMulti, "accept-multiclient", "", false, "Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate")
|
||||
RootCommand.PersistentFlags().IntVar(&ApiVersion, "api-version", 1, "Selects API version when headless")
|
||||
RootCommand.PersistentFlags().StringVar(&InitFile, "init", "", "Init file, executed by the terminal client.")
|
||||
RootCommand.PersistentFlags().StringVar(&BuildFlags, "build-flags", buildFlagsDefault, "Build flags, to be passed to the compiler.")
|
||||
|
||||
@ -239,7 +243,7 @@ func traceCmd(cmd *cobra.Command, args []string) {
|
||||
defer listener.Close()
|
||||
|
||||
// Create and start a debug server
|
||||
server := rpc.NewServer(&service.Config{
|
||||
server := rpc2.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: processArgs,
|
||||
AttachPid: traceAttachPid,
|
||||
@ -248,7 +252,7 @@ func traceCmd(cmd *cobra.Command, args []string) {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
}
|
||||
client := rpc.NewClient(listener.Addr().String())
|
||||
client := rpc2.NewClient(listener.Addr().String())
|
||||
funcs, err := client.ListFunctions(regexp)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
@ -322,7 +326,7 @@ func splitArgs(cmd *cobra.Command, args []string) ([]string, []string) {
|
||||
func connect(addr string, conf *config.Config) int {
|
||||
// Create and start a terminal - attach to running instance
|
||||
var client service.Client
|
||||
client = rpc.NewClient(addr)
|
||||
client = rpc2.NewClient(addr)
|
||||
term := terminal.New(client, conf)
|
||||
status, err := term.Run()
|
||||
if err != nil {
|
||||
@ -344,13 +348,37 @@ func execute(attachPid int, processArgs []string, conf *config.Config) int {
|
||||
fmt.Fprintf(os.Stderr, "Warning: init file ignored\n")
|
||||
}
|
||||
|
||||
var server interface {
|
||||
Run() error
|
||||
Stop(bool) error
|
||||
}
|
||||
|
||||
|
||||
if !Headless {
|
||||
ApiVersion = 2
|
||||
}
|
||||
|
||||
// Create and start a debugger server
|
||||
server := rpc.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: processArgs,
|
||||
AttachPid: attachPid,
|
||||
AcceptMulti: AcceptMulti,
|
||||
}, Log)
|
||||
switch ApiVersion {
|
||||
case 1:
|
||||
server = rpc1.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: processArgs,
|
||||
AttachPid: attachPid,
|
||||
AcceptMulti: AcceptMulti,
|
||||
}, Log)
|
||||
case 2:
|
||||
server = rpc2.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: processArgs,
|
||||
AttachPid: attachPid,
|
||||
AcceptMulti: AcceptMulti,
|
||||
}, Log)
|
||||
default:
|
||||
fmt.Println("Unknown API version %d", ApiVersion)
|
||||
return 1
|
||||
}
|
||||
|
||||
if err := server.Run(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
@ -365,7 +393,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config) int {
|
||||
} else {
|
||||
// Create and start a terminal
|
||||
var client service.Client
|
||||
client = rpc.NewClient(listener.Addr().String())
|
||||
client = rpc2.NewClient(listener.Addr().String())
|
||||
term := terminal.New(client, conf)
|
||||
term.InitFile = InitFile
|
||||
status, err = term.Run()
|
||||
|
||||
@ -59,8 +59,6 @@ type Client interface {
|
||||
ListPackageVariables(filter string) ([]api.Variable, error)
|
||||
// EvalVariable returns a variable in the context of the current thread.
|
||||
EvalVariable(scope api.EvalScope, symbol string) (*api.Variable, error)
|
||||
// ListPackageVariablesFor lists all package variables in the context of a thread.
|
||||
ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error)
|
||||
|
||||
// SetVariable sets the value of a variable
|
||||
SetVariable(scope api.EvalScope, symbol, value string) error
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package rpc
|
||||
package rpc1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
5
service/rpc1/readme.txtr
Normal file
5
service/rpc1/readme.txtr
Normal 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.
|
||||
@ -1,4 +1,4 @@
|
||||
package rpc
|
||||
package rpc1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -35,6 +35,7 @@ func NewServer(config *service.Config, logEnabled bool) *ServerImpl {
|
||||
if !logEnabled {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
}
|
||||
log.Printf("Using API v1")
|
||||
|
||||
return &ServerImpl{
|
||||
&RPCServer{
|
||||
290
service/rpc2/client.go
Normal file
290
service/rpc2/client.go
Normal file
@ -0,0 +1,290 @@
|
||||
package rpc2
|
||||
|
||||
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
|
||||
processPid int
|
||||
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) ProcessPid() int {
|
||||
out := new(ProcessPidOut)
|
||||
c.call("ProcessPid", ProcessPidIn{}, out)
|
||||
return out.Pid
|
||||
}
|
||||
|
||||
func (c *RPCClient) Detach(kill bool) error {
|
||||
out := new(DetachOut)
|
||||
return c.call("Detach", DetachIn{kill}, out)
|
||||
}
|
||||
|
||||
func (c *RPCClient) Restart() error {
|
||||
out := new(RestartOut)
|
||||
return c.call("Restart", RestartIn{}, out)
|
||||
}
|
||||
|
||||
func (c *RPCClient) GetState() (*api.DebuggerState, error) {
|
||||
var out StateOut
|
||||
err := c.call("State", StateIn{}, &out)
|
||||
return out.State, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Continue() <-chan *api.DebuggerState {
|
||||
ch := make(chan *api.DebuggerState)
|
||||
go func() {
|
||||
for {
|
||||
out := new(CommandOut)
|
||||
err := c.call("Command", &api.DebuggerCommand{Name: api.Continue}, &out)
|
||||
state := out.State
|
||||
if err != nil {
|
||||
state.Err = err
|
||||
}
|
||||
if state.Exited {
|
||||
// Error types apparantly 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) {
|
||||
var out CommandOut
|
||||
err := c.call("Command", api.DebuggerCommand{Name: api.Next}, &out)
|
||||
return &out.State, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Step() (*api.DebuggerState, error) {
|
||||
var out CommandOut
|
||||
err := c.call("Command", api.DebuggerCommand{Name: api.Step}, &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) 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) (*api.Variable, error) {
|
||||
var out EvalOut
|
||||
err := c.call("Eval", EvalIn{scope, expr}, &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) ([]api.Variable, error) {
|
||||
var out ListPackageVarsOut
|
||||
err := c.call("ListPackageVars", ListPackageVarsIn{filter}, &out)
|
||||
return out.Variables, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListLocalVariables(scope api.EvalScope) ([]api.Variable, error) {
|
||||
var out ListLocalVarsOut
|
||||
err := c.call("ListLocalVars", ListLocalVarsIn{scope}, &out)
|
||||
return out.Variables, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListRegisters() (string, error) {
|
||||
out := new(ListRegistersOut)
|
||||
err := c.call("ListRegisters", ListRegistersIn{}, out)
|
||||
return out.Registers, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListFunctionArgs(scope api.EvalScope) ([]api.Variable, error) {
|
||||
var out ListFunctionArgsOut
|
||||
err := c.call("ListFunctionArgs", ListFunctionArgsIn{scope}, &out)
|
||||
return out.Args, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListGoroutines() ([]*api.Goroutine, error) {
|
||||
var out ListGoroutinesOut
|
||||
err := c.call("ListGoroutines", ListGoroutinesIn{}, &out)
|
||||
return out.Goroutines, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Stacktrace(goroutineId, depth int, full bool) ([]api.Stackframe, error) {
|
||||
var out StacktraceOut
|
||||
err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, full}, &out)
|
||||
return out.Locations, 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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
538
service/rpc2/server.go
Normal file
538
service/rpc2/server.go
Normal file
@ -0,0 +1,538 @@
|
||||
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
|
||||
}
|
||||
|
||||
type StacktraceOut struct {
|
||||
Locations []api.Stackframe
|
||||
}
|
||||
|
||||
func (s *RPCServer) Stacktrace(arg StacktraceIn, out *StacktraceOut) error {
|
||||
locs, err := s.debugger.Stacktrace(arg.Id, arg.Depth, arg.Full)
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
type ListLocalVarsOut struct {
|
||||
Variables []api.Variable
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListLocalVars(arg ListLocalVarsIn, out *ListLocalVarsOut) error {
|
||||
vars, err := s.debugger.LocalVariables(arg.Scope)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Variables = vars
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListFunctionArgsIn struct {
|
||||
Scope api.EvalScope
|
||||
}
|
||||
|
||||
type ListFunctionArgsOut struct {
|
||||
Args []api.Variable
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListFunctionArgs(arg ListFunctionArgsIn, out *ListFunctionArgsOut) error {
|
||||
vars, err := s.debugger.FunctionArguments(arg.Scope)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Args = vars
|
||||
return nil
|
||||
}
|
||||
|
||||
type EvalIn struct {
|
||||
Scope api.EvalScope
|
||||
Expr string
|
||||
}
|
||||
|
||||
type EvalOut struct {
|
||||
Variable *api.Variable
|
||||
}
|
||||
|
||||
func (s *RPCServer) Eval(arg EvalIn, out *EvalOut) error {
|
||||
v, err := s.debugger.EvalVariableInScope(arg.Scope, arg.Expr)
|
||||
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
|
||||
}
|
||||
204
service/test/cmd/typecheckrpc.go
Normal file
204
service/test/cmd/typecheckrpc.go
Normal file
@ -0,0 +1,204 @@
|
||||
package main
|
||||
// This program checks the types of the arguments of calls to
|
||||
// the API in service/rpc2/client.go (done using rpc2.(*Client).call)
|
||||
// against the declared types of API methods in srvice/rpc2/server.go
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/importer"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func findRPCDir() string {
|
||||
const parent = ".."
|
||||
RPCDir := "service/rpc2"
|
||||
for depth := 0; depth < 10; depth++ {
|
||||
if _, err := os.Stat(RPCDir); err == nil {
|
||||
break
|
||||
}
|
||||
RPCDir = filepath.Join(parent, RPCDir)
|
||||
}
|
||||
return RPCDir
|
||||
}
|
||||
|
||||
func parseFiles(path string) (*token.FileSet, *types.Package, types.Info, *ast.File) {
|
||||
fset := token.NewFileSet()
|
||||
files := []*ast.File{}
|
||||
|
||||
for _, name := range []string{"server.go", "client.go"} {
|
||||
f, err := parser.ParseFile(fset, filepath.Join(path, name), nil, 0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
files = append(files, f)
|
||||
}
|
||||
|
||||
info := types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
Defs: make(map[*ast.Ident]types.Object),
|
||||
Uses: make(map[*ast.Ident]types.Object),
|
||||
}
|
||||
|
||||
var conf types.Config
|
||||
conf.Importer = importer.Default()
|
||||
pkg, err := conf.Check(path, fset, files, &info)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return fset, pkg, info, files[1]
|
||||
}
|
||||
|
||||
func getMethods(pkg *types.Package, typename string) map[string]*types.Func {
|
||||
r := make(map[string]*types.Func)
|
||||
mset := types.NewMethodSet(types.NewPointer(pkg.Scope().Lookup(typename).Type()))
|
||||
for i := 0; i < mset.Len(); i++ {
|
||||
fn := mset.At(i).Obj().(*types.Func)
|
||||
r[fn.Name()] = fn
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func publicMethodOf(decl ast.Decl, receiver string) *ast.FuncDecl {
|
||||
fndecl, isfunc := decl.(*ast.FuncDecl)
|
||||
if !isfunc {
|
||||
return nil
|
||||
}
|
||||
if fndecl.Name.Name[0] >= 'a' && fndecl.Name.Name[0] <= 'z' {
|
||||
return nil
|
||||
}
|
||||
if fndecl.Recv == nil || len(fndecl.Recv.List) != 1 {
|
||||
return nil
|
||||
}
|
||||
starexpr, isstar := fndecl.Recv.List[0].Type.(*ast.StarExpr)
|
||||
if !isstar {
|
||||
return nil
|
||||
}
|
||||
identexpr, isident := starexpr.X.(*ast.Ident)
|
||||
if !isident || identexpr.Name != receiver {
|
||||
return nil
|
||||
}
|
||||
if fndecl.Body == nil {
|
||||
return nil
|
||||
}
|
||||
return fndecl
|
||||
}
|
||||
|
||||
func findCallCall(fndecl *ast.FuncDecl) *ast.CallExpr {
|
||||
for _, stmt := range fndecl.Body.List {
|
||||
var x ast.Expr = nil
|
||||
|
||||
switch s := stmt.(type) {
|
||||
case *ast.AssignStmt:
|
||||
if len(s.Rhs) == 1 {
|
||||
x = s.Rhs[0]
|
||||
}
|
||||
case *ast.ReturnStmt:
|
||||
if len(s.Results) == 1 {
|
||||
x = s.Results[0]
|
||||
}
|
||||
case *ast.ExprStmt:
|
||||
x = s.X
|
||||
}
|
||||
|
||||
callx, iscall := x.(*ast.CallExpr)
|
||||
if !iscall {
|
||||
continue
|
||||
}
|
||||
fun, issel := callx.Fun.(*ast.SelectorExpr)
|
||||
if !issel || fun.Sel.Name != "call" {
|
||||
continue
|
||||
}
|
||||
return callx
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func qf(*types.Package) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func main() {
|
||||
RPCDir := findRPCDir()
|
||||
|
||||
fset, pkg, info, clientAst := parseFiles(RPCDir)
|
||||
|
||||
serverMethods := getMethods(pkg, "RPCServer")
|
||||
_ = serverMethods
|
||||
errcount := 0
|
||||
|
||||
for _, decl := range clientAst.Decls {
|
||||
fndecl := publicMethodOf(decl, "RPCClient")
|
||||
if fndecl == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if fndecl.Name.Name == "Continue" {
|
||||
// complex function, skip check
|
||||
continue
|
||||
}
|
||||
|
||||
callx := findCallCall(fndecl)
|
||||
|
||||
if callx == nil {
|
||||
log.Printf("%s: could not find RPC call", fset.Position(fndecl.Pos()))
|
||||
errcount++
|
||||
continue
|
||||
}
|
||||
|
||||
if len(callx.Args) != 3 {
|
||||
log.Printf("%s: wrong number of arguments for RPC call", fset.Position(callx.Pos()))
|
||||
errcount++
|
||||
continue
|
||||
}
|
||||
|
||||
arg0, arg0islit := callx.Args[0].(*ast.BasicLit)
|
||||
arg1 := callx.Args[1]
|
||||
arg2 := callx.Args[2]
|
||||
if !arg0islit || arg0.Kind != token.STRING {
|
||||
continue
|
||||
}
|
||||
name, _ := strconv.Unquote(arg0.Value)
|
||||
serverMethod := serverMethods[name]
|
||||
if serverMethod == nil {
|
||||
log.Printf("%s: could not find RPC method %q", fset.Position(callx.Pos()), name)
|
||||
errcount++
|
||||
continue
|
||||
}
|
||||
|
||||
params := serverMethod.Type().(*types.Signature).Params()
|
||||
|
||||
if a, e := info.TypeOf(arg1), params.At(0).Type(); !types.AssignableTo(a, e) {
|
||||
log.Printf("%s: wrong type of first argument %s, expected %s", fset.Position(callx.Pos()), types.TypeString(a, qf), types.TypeString(e, qf))
|
||||
errcount++
|
||||
continue
|
||||
}
|
||||
|
||||
if a, e := info.TypeOf(arg2), params.At(1).Type(); !types.AssignableTo(a, e) {
|
||||
log.Printf("%s: wrong type of second argument %s, expected %s", fset.Position(callx.Pos()), types.TypeString(a, qf), types.TypeString(e, qf))
|
||||
errcount++
|
||||
continue
|
||||
}
|
||||
|
||||
if clit, ok := arg1.(*ast.CompositeLit); ok {
|
||||
typ := params.At(0).Type()
|
||||
st := typ.Underlying().(*types.Struct)
|
||||
if len(clit.Elts) != st.NumFields() && types.TypeString(typ, qf) != "DebuggerCommand" {
|
||||
log.Printf("%s: wrong number of fields in first argument's literal %d, expected %d", fset.Position(callx.Pos()), len(clit.Elts), st.NumFields())
|
||||
errcount++
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errcount > 0 {
|
||||
log.Printf("%d errors", errcount)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
100
service/test/common_test.go
Normal file
100
service/test/common_test.go
Normal file
@ -0,0 +1,100 @@
|
||||
package servicetest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
func assertNoError(err error, t *testing.T, s string) {
|
||||
if err != nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fname := filepath.Base(file)
|
||||
t.Fatalf("failed assertion at %s:%d: %s - %s\n", fname, line, s, err)
|
||||
}
|
||||
}
|
||||
|
||||
func assertError(err error, t *testing.T, s string) {
|
||||
if err == nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fname := filepath.Base(file)
|
||||
t.Fatalf("failed assertion at %s:%d: %s (no error)\n", fname, line, s)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(2)
|
||||
}
|
||||
|
||||
type nextTest struct {
|
||||
begin, end int
|
||||
}
|
||||
|
||||
func countBreakpoints(t *testing.T, c service.Client) int {
|
||||
bps, err := c.ListBreakpoints()
|
||||
assertNoError(err, t, "ListBreakpoints()")
|
||||
bpcount := 0
|
||||
for _, bp := range bps {
|
||||
if bp.ID >= 0 {
|
||||
bpcount++
|
||||
}
|
||||
}
|
||||
return bpcount
|
||||
}
|
||||
|
||||
func testProgPath(t *testing.T, name string) string {
|
||||
fp, err := filepath.Abs(fmt.Sprintf("_fixtures/%s.go", name))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := os.Stat(fp); err != nil {
|
||||
fp, err = filepath.Abs(fmt.Sprintf("../../_fixtures/%s.go", name))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return fp
|
||||
}
|
||||
|
||||
func findLocationHelper(t *testing.T, c service.Client, loc string, shouldErr bool, count int, checkAddr uint64) []uint64 {
|
||||
locs, err := c.FindLocation(api.EvalScope{-1, 0}, loc)
|
||||
t.Logf("FindLocation(\"%s\") → %v\n", loc, locs)
|
||||
|
||||
if shouldErr {
|
||||
if err == nil {
|
||||
t.Fatalf("Resolving location <%s> didn't return an error: %v", loc, locs)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving location <%s>: %v", loc, err)
|
||||
}
|
||||
}
|
||||
|
||||
if (count >= 0) && (len(locs) != count) {
|
||||
t.Fatalf("Wrong number of breakpoints returned for location <%s> (got %d, expected %d)", loc, len(locs), count)
|
||||
}
|
||||
|
||||
if checkAddr != 0 && checkAddr != locs[0].PC {
|
||||
t.Fatalf("Wrong address returned for location <%s> (got %v, epected %v)", loc, locs[0].PC, checkAddr)
|
||||
}
|
||||
|
||||
addrs := make([]uint64, len(locs))
|
||||
for i := range locs {
|
||||
addrs[i] = locs[i].PC
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
func getCurinstr(d3 api.AsmInstructions) *api.AsmInstruction {
|
||||
for i := range d3 {
|
||||
if d3[i].AtPC {
|
||||
return &d3[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
1077
service/test/integration1_test.go
Normal file
1077
service/test/integration1_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@ -17,47 +16,23 @@ import (
|
||||
"github.com/derekparker/delve/proc"
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
"github.com/derekparker/delve/service/rpc"
|
||||
"github.com/derekparker/delve/service/rpc2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(2)
|
||||
}
|
||||
|
||||
func assertNoError(err error, t *testing.T, s string) {
|
||||
if err != nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fname := filepath.Base(file)
|
||||
t.Fatalf("failed assertion at %s:%d: %s - %s\n", fname, line, s, err)
|
||||
}
|
||||
}
|
||||
|
||||
func assertError(err error, t *testing.T, s string) {
|
||||
if err == nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fname := filepath.Base(file)
|
||||
t.Fatalf("failed assertion at %s:%d: %s (no error)\n", fname, line, s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(protest.RunTestsWithFixtures(m))
|
||||
}
|
||||
|
||||
func withTestClient(name string, t *testing.T, fn func(c service.Client)) {
|
||||
func withTestClient2(name string, t *testing.T, fn func(c service.Client)) {
|
||||
listener, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't start listener: %s\n", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
server := rpc.NewServer(&service.Config{
|
||||
server := rpc2.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: []string{protest.BuildFixture(name).Path},
|
||||
}, false)
|
||||
if err := server.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client := rpc.NewClient(listener.Addr().String())
|
||||
client := rpc2.NewClient(listener.Addr().String())
|
||||
defer func() {
|
||||
client.Detach(true)
|
||||
}()
|
||||
@ -71,7 +46,7 @@ func TestRunWithInvalidPath(t *testing.T) {
|
||||
t.Fatalf("couldn't start listener: %s\n", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
server := rpc.NewServer(&service.Config{
|
||||
server := rpc2.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: []string{"invalid_path"},
|
||||
}, false)
|
||||
@ -81,7 +56,7 @@ func TestRunWithInvalidPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRestart_afterExit(t *testing.T) {
|
||||
withTestClient("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient2("continuetestprog", t, func(c service.Client) {
|
||||
origPid := c.ProcessPid()
|
||||
state := <-c.Continue()
|
||||
if !state.Exited {
|
||||
@ -101,7 +76,7 @@ func TestRestart_afterExit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRestart_breakpointPreservation(t *testing.T) {
|
||||
withTestClient("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient2("continuetestprog", t, func(c service.Client) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1, Name: "firstbreakpoint", Tracepoint: true})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
stateCh := c.Continue()
|
||||
@ -130,7 +105,7 @@ func TestRestart_breakpointPreservation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRestart_duringStop(t *testing.T) {
|
||||
withTestClient("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient2("continuetestprog", t, func(c service.Client) {
|
||||
origPid := c.ProcessPid()
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1})
|
||||
if err != nil {
|
||||
@ -159,7 +134,7 @@ func TestRestart_duringStop(t *testing.T) {
|
||||
func TestRestart_attachPid(t *testing.T) {
|
||||
// Assert it does not work and returns error.
|
||||
// We cannot restart a process we did not spawn.
|
||||
server := rpc.NewServer(&service.Config{
|
||||
server := rpc2.NewServer(&service.Config{
|
||||
Listener: nil,
|
||||
AttachPid: 999,
|
||||
}, false)
|
||||
@ -169,7 +144,7 @@ func TestRestart_attachPid(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_exit(t *testing.T) {
|
||||
withTestClient("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient2("continuetestprog", t, func(c service.Client) {
|
||||
state, err := c.GetState()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -192,7 +167,7 @@ func TestClientServer_exit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_step(t *testing.T) {
|
||||
withTestClient("testprog", t, func(c service.Client) {
|
||||
withTestClient2("testprog", t, func(c service.Client) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.helloworld", Line: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -214,12 +189,8 @@ func TestClientServer_step(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
type nextTest struct {
|
||||
begin, end int
|
||||
}
|
||||
|
||||
func testnext(testcases []nextTest, initialLocation string, t *testing.T) {
|
||||
withTestClient("testnextprog", t, func(c service.Client) {
|
||||
func testnext2(testcases []nextTest, initialLocation string, t *testing.T) {
|
||||
withTestClient2("testnextprog", t, func(c service.Client) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: initialLocation, Line: -1})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -310,7 +281,7 @@ func TestNextFunctionReturn(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_breakpointInMainThread(t *testing.T) {
|
||||
withTestClient("testprog", t, func(c service.Client) {
|
||||
withTestClient2("testprog", t, func(c service.Client) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.helloworld", Line: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -331,7 +302,7 @@ func TestClientServer_breakpointInMainThread(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_breakpointInSeparateGoroutine(t *testing.T) {
|
||||
withTestClient("testthreads", t, func(c service.Client) {
|
||||
withTestClient2("testthreads", t, func(c service.Client) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.anotherthread", Line: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -350,7 +321,7 @@ func TestClientServer_breakpointInSeparateGoroutine(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_breakAtNonexistentPoint(t *testing.T) {
|
||||
withTestClient("testprog", t, func(c service.Client) {
|
||||
withTestClient2("testprog", t, func(c service.Client) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "nowhere", Line: 1})
|
||||
if err == nil {
|
||||
t.Fatal("Should not be able to break at non existent function")
|
||||
@ -358,20 +329,8 @@ func TestClientServer_breakAtNonexistentPoint(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func countBreakpoints(t *testing.T, c service.Client) int {
|
||||
bps, err := c.ListBreakpoints()
|
||||
assertNoError(err, t, "ListBreakpoints()")
|
||||
bpcount := 0
|
||||
for _, bp := range bps {
|
||||
if bp.ID >= 0 {
|
||||
bpcount++
|
||||
}
|
||||
}
|
||||
return bpcount
|
||||
}
|
||||
|
||||
func TestClientServer_clearBreakpoint(t *testing.T) {
|
||||
withTestClient("testprog", t, func(c service.Client) {
|
||||
withTestClient2("testprog", t, func(c service.Client) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sleepytime", Line: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -397,7 +356,7 @@ func TestClientServer_clearBreakpoint(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_switchThread(t *testing.T) {
|
||||
withTestClient("testnextprog", t, func(c service.Client) {
|
||||
withTestClient2("testnextprog", t, func(c service.Client) {
|
||||
// With invalid thread id
|
||||
_, err := c.SwitchThread(-1)
|
||||
if err == nil {
|
||||
@ -439,22 +398,8 @@ func TestClientServer_switchThread(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func testProgPath(t *testing.T, name string) string {
|
||||
fp, err := filepath.Abs(fmt.Sprintf("_fixtures/%s.go", name))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := os.Stat(fp); err != nil {
|
||||
fp, err = filepath.Abs(fmt.Sprintf("../../_fixtures/%s.go", name))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return fp
|
||||
}
|
||||
|
||||
func TestClientServer_infoLocals(t *testing.T) {
|
||||
withTestClient("testnextprog", t, func(c service.Client) {
|
||||
withTestClient2("testnextprog", t, func(c service.Client) {
|
||||
fp := testProgPath(t, "testnextprog")
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 23})
|
||||
if err != nil {
|
||||
@ -475,7 +420,7 @@ func TestClientServer_infoLocals(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_infoArgs(t *testing.T) {
|
||||
withTestClient("testnextprog", t, func(c service.Client) {
|
||||
withTestClient2("testnextprog", t, func(c service.Client) {
|
||||
fp := testProgPath(t, "testnextprog")
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 47})
|
||||
if err != nil {
|
||||
@ -503,7 +448,7 @@ func TestClientServer_infoArgs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_traceContinue(t *testing.T) {
|
||||
withTestClient("integrationprog", t, func(c service.Client) {
|
||||
withTestClient2("integrationprog", t, func(c service.Client) {
|
||||
fp := testProgPath(t, "integrationprog")
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 15, Tracepoint: true, Goroutine: true, Stacktrace: 5, Variables: []string{"i"}})
|
||||
if err != nil {
|
||||
@ -560,7 +505,7 @@ func TestClientServer_traceContinue(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_traceContinue2(t *testing.T) {
|
||||
withTestClient("integrationprog", t, func(c service.Client) {
|
||||
withTestClient2("integrationprog", t, func(c service.Client) {
|
||||
bp1, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1, Tracepoint: true})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v\n", err)
|
||||
@ -602,37 +547,8 @@ func TestClientServer_traceContinue2(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func findLocationHelper(t *testing.T, c service.Client, loc string, shouldErr bool, count int, checkAddr uint64) []uint64 {
|
||||
locs, err := c.FindLocation(api.EvalScope{-1, 0}, loc)
|
||||
t.Logf("FindLocation(\"%s\") → %v\n", loc, locs)
|
||||
|
||||
if shouldErr {
|
||||
if err == nil {
|
||||
t.Fatalf("Resolving location <%s> didn't return an error: %v", loc, locs)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving location <%s>: %v", loc, err)
|
||||
}
|
||||
}
|
||||
|
||||
if (count >= 0) && (len(locs) != count) {
|
||||
t.Fatalf("Wrong number of breakpoints returned for location <%s> (got %d, expected %d)", loc, len(locs), count)
|
||||
}
|
||||
|
||||
if checkAddr != 0 && checkAddr != locs[0].PC {
|
||||
t.Fatalf("Wrong address returned for location <%s> (got %v, epected %v)", loc, locs[0].PC, checkAddr)
|
||||
}
|
||||
|
||||
addrs := make([]uint64, len(locs))
|
||||
for i := range locs {
|
||||
addrs[i] = locs[i].PC
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
func TestClientServer_FindLocations(t *testing.T) {
|
||||
withTestClient("locationsprog", t, func(c service.Client) {
|
||||
withTestClient2("locationsprog", t, func(c service.Client) {
|
||||
someFunctionCallAddr := findLocationHelper(t, c, "locationsprog.go:26", false, 1, 0)[0]
|
||||
someFunctionLine1 := findLocationHelper(t, c, "locationsprog.go:27", false, 1, 0)[0]
|
||||
findLocationHelper(t, c, "anotherFunction:1", false, 1, someFunctionLine1)
|
||||
@ -679,17 +595,17 @@ func TestClientServer_FindLocations(t *testing.T) {
|
||||
findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:32", false, 1, 0)[0])
|
||||
})
|
||||
|
||||
withTestClient("testnextdefer", t, func(c service.Client) {
|
||||
withTestClient2("testnextdefer", t, func(c service.Client) {
|
||||
firstMainLine := findLocationHelper(t, c, "testnextdefer.go:5", false, 1, 0)[0]
|
||||
findLocationHelper(t, c, "main.main", false, 1, firstMainLine)
|
||||
})
|
||||
|
||||
withTestClient("stacktraceprog", t, func(c service.Client) {
|
||||
withTestClient2("stacktraceprog", t, func(c service.Client) {
|
||||
stacktracemeAddr := findLocationHelper(t, c, "stacktraceprog.go:4", false, 1, 0)[0]
|
||||
findLocationHelper(t, c, "main.stacktraceme", false, 1, stacktracemeAddr)
|
||||
})
|
||||
|
||||
withTestClient("locationsUpperCase", t, func(c service.Client) {
|
||||
withTestClient2("locationsUpperCase", t, func(c service.Client) {
|
||||
// Upper case
|
||||
findLocationHelper(t, c, "locationsUpperCase.go:6", false, 1, 0)
|
||||
|
||||
@ -729,7 +645,7 @@ func TestClientServer_FindLocations(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_FindLocationsAddr(t *testing.T) {
|
||||
withTestClient("locationsprog2", t, func(c service.Client) {
|
||||
withTestClient2("locationsprog2", t, func(c service.Client) {
|
||||
<-c.Continue()
|
||||
|
||||
afunction := findLocationHelper(t, c, "main.afunction", false, 1, 0)[0]
|
||||
@ -741,7 +657,7 @@ func TestClientServer_FindLocationsAddr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_EvalVariable(t *testing.T) {
|
||||
withTestClient("testvariables", t, func(c service.Client) {
|
||||
withTestClient2("testvariables", t, func(c service.Client) {
|
||||
state := <-c.Continue()
|
||||
|
||||
if state.Err != nil {
|
||||
@ -760,7 +676,7 @@ func TestClientServer_EvalVariable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_SetVariable(t *testing.T) {
|
||||
withTestClient("testvariables", t, func(c service.Client) {
|
||||
withTestClient2("testvariables", t, func(c service.Client) {
|
||||
state := <-c.Continue()
|
||||
|
||||
if state.Err != nil {
|
||||
@ -782,7 +698,7 @@ func TestClientServer_SetVariable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_FullStacktrace(t *testing.T) {
|
||||
withTestClient("goroutinestackprog", t, func(c service.Client) {
|
||||
withTestClient2("goroutinestackprog", t, func(c service.Client) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.stacktraceme", Line: -1})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
state := <-c.Continue()
|
||||
@ -855,7 +771,7 @@ func TestClientServer_FullStacktrace(t *testing.T) {
|
||||
|
||||
func TestIssue355(t *testing.T) {
|
||||
// After the target process has terminated should return an error but not crash
|
||||
withTestClient("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient2("continuetestprog", t, func(c service.Client) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: -1})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
ch := c.Continue()
|
||||
@ -911,21 +827,12 @@ func TestIssue355(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func getCurinstr(d3 api.AsmInstructions) *api.AsmInstruction {
|
||||
for i := range d3 {
|
||||
if d3[i].AtPC {
|
||||
return &d3[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestDisasm(t *testing.T) {
|
||||
// Tests that disassembling by PC, range, and current PC all yeld similar results
|
||||
// Tests that disassembly by current PC will return a disassembly containing the instruction at PC
|
||||
// Tests that stepping on a calculated CALL instruction will yield a disassembly that contains the
|
||||
// effective destination of the CALL instruction
|
||||
withTestClient("locationsprog2", t, func(c service.Client) {
|
||||
withTestClient2("locationsprog2", t, func(c service.Client) {
|
||||
ch := c.Continue()
|
||||
state := <-ch
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
@ -1027,7 +934,7 @@ func TestDisasm(t *testing.T) {
|
||||
|
||||
func TestNegativeStackDepthBug(t *testing.T) {
|
||||
// After the target process has terminated should return an error but not crash
|
||||
withTestClient("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient2("continuetestprog", t, func(c service.Client) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: -1})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
ch := c.Continue()
|
||||
@ -1039,7 +946,7 @@ func TestNegativeStackDepthBug(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_CondBreakpoint(t *testing.T) {
|
||||
withTestClient("parallel_next", t, func(c service.Client) {
|
||||
withTestClient2("parallel_next", t, func(c service.Client) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: 1})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
bp.Cond = "n == 7"
|
||||
@ -1069,7 +976,7 @@ func TestClientServer_CondBreakpoint(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSkipPrologue(t *testing.T) {
|
||||
withTestClient("locationsprog2", t, func(c service.Client) {
|
||||
withTestClient2("locationsprog2", t, func(c service.Client) {
|
||||
<-c.Continue()
|
||||
|
||||
afunction := findLocationHelper(t, c, "main.afunction", false, 1, 0)[0]
|
||||
@ -1085,7 +992,7 @@ func TestSkipPrologue(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSkipPrologue2(t *testing.T) {
|
||||
withTestClient("callme", t, func(c service.Client) {
|
||||
withTestClient2("callme", t, func(c service.Client) {
|
||||
callme := findLocationHelper(t, c, "main.callme", false, 1, 0)[0]
|
||||
callmeZ := findLocationHelper(t, c, "main.callme:0", false, 1, 0)[0]
|
||||
findLocationHelper(t, c, "callme.go:5", false, 1, callme)
|
||||
@ -1113,7 +1020,7 @@ func TestSkipPrologue2(t *testing.T) {
|
||||
func TestIssue419(t *testing.T) {
|
||||
// Calling service/rpc.(*Client).Halt could cause a crash because both Halt and Continue simultaneously
|
||||
// try to read 'runtime.g' and debug/dwarf.Data.Type is not thread safe
|
||||
withTestClient("issue419", t, func(c service.Client) {
|
||||
withTestClient2("issue419", t, func(c service.Client) {
|
||||
go func() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
d := time.Duration(rand.Intn(4) + 1)
|
||||
@ -1128,7 +1035,7 @@ func TestIssue419(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTypesCommand(t *testing.T) {
|
||||
withTestClient("testvariables2", t, func(c service.Client) {
|
||||
withTestClient2("testvariables2", t, func(c service.Client) {
|
||||
state := <-c.Continue()
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
types, err := c.ListTypes("")
|
||||
@ -1152,3 +1059,19 @@ func TestTypesCommand(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestIssue406(t *testing.T) {
|
||||
withTestClient2("issue406", t, func(c service.Client) {
|
||||
locs, err := c.FindLocation(api.EvalScope{-1, 0}, "issue406.go:146")
|
||||
assertNoError(err, t, "FindLocation()")
|
||||
_, err = c.CreateBreakpoint(&api.Breakpoint{Addr: locs[0].PC})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
ch := c.Continue()
|
||||
state := <-ch
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
v, err := c.EvalVariable(api.EvalScope{-1, 0}, "cfgtree")
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
vs := v.MultilineString("")
|
||||
t.Logf("cfgtree formats to: %s\n", vs)
|
||||
})
|
||||
}
|
||||
@ -7,7 +7,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/derekparker/delve/proc"
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
|
||||
protest "github.com/derekparker/delve/proc/test"
|
||||
@ -646,19 +645,3 @@ func TestUnsafePointer(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestIssue406(t *testing.T) {
|
||||
withTestClient("issue406", t, func(c service.Client) {
|
||||
locs, err := c.FindLocation(api.EvalScope{-1, 0}, "issue406.go:146")
|
||||
assertNoError(err, t, "FindLocation()")
|
||||
_, err = c.CreateBreakpoint(&api.Breakpoint{Addr: locs[0].PC})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
ch := c.Continue()
|
||||
state := <-ch
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
v, err := c.EvalVariable(api.EvalScope{-1, 0}, "cfgtree")
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
vs := v.MultilineString("")
|
||||
t.Logf("cfgtree formats to: %s\n", vs)
|
||||
})
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"github.com/derekparker/delve/proc/test"
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
"github.com/derekparker/delve/service/rpc"
|
||||
"github.com/derekparker/delve/service/rpc2"
|
||||
)
|
||||
|
||||
type FakeTerminal struct {
|
||||
@ -76,14 +76,14 @@ func withTestTerminal(name string, t testing.TB, fn func(*FakeTerminal)) {
|
||||
t.Fatalf("couldn't start listener: %s\n", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
server := rpc.NewServer(&service.Config{
|
||||
server := rpc2.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: []string{test.BuildFixture(name).Path},
|
||||
}, false)
|
||||
if err := server.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client := rpc.NewClient(listener.Addr().String())
|
||||
client := rpc2.NewClient(listener.Addr().String())
|
||||
defer func() {
|
||||
client.Detach(true)
|
||||
}()
|
||||
|
||||
Reference in New Issue
Block a user