diff --git a/Documentation/api/json-rpc/README.md b/Documentation/api/json-rpc/README.md index bfa67898..c0c7863e 100644 --- a/Documentation/api/json-rpc/README.md +++ b/Documentation/api/json-rpc/README.md @@ -6,8 +6,7 @@ Note that this JSON-RPC interface is served over a streaming socket, *not* over # API versions -Delve currently supports two versions of its API. By default a headless instance of `dlv` will serve APIv1 for backward compatibility with old clients, however new clients should use APIv2 as new features will only be made available through version 2. To select APIv2 use `--api-version=2` command line argument. -Clients can also select APIv2 by sending a [SetApiVersion](https://pkg.go.dev/github.com/go-delve/delve/service/rpccommon#RPCServer.SetApiVersion) request specifying `APIVersion = 2` after connecting to the headless instance. +Delve currently only supports v2 of its API. Support for v1 was dropped in version v1.24.0 of Delve. # API version 2 documentation diff --git a/Documentation/usage/dlv_attach.md b/Documentation/usage/dlv_attach.md index 58e4ae9d..c5de62f1 100644 --- a/Documentation/usage/dlv_attach.md +++ b/Documentation/usage/dlv_attach.md @@ -30,7 +30,7 @@ dlv attach pid [executable] [flags] ``` --accept-multiclient Allows a headless server to accept multiple client connections via JSON-RPC or DAP. --allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr - --api-version int Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1) + --api-version int Selects JSON-RPC API version when headless. The only valid value is 2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 2) --backend string Backend selection (see 'dlv help backend'). (default "default") --check-go-version Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true) --headless Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections. diff --git a/Documentation/usage/dlv_backend.md b/Documentation/usage/dlv_backend.md index 02c833f2..b2cbcbdb 100644 --- a/Documentation/usage/dlv_backend.md +++ b/Documentation/usage/dlv_backend.md @@ -30,7 +30,7 @@ Some backends can be configured using environment variables: ``` --accept-multiclient Allows a headless server to accept multiple client connections via JSON-RPC or DAP. --allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr - --api-version int Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1) + --api-version int Selects JSON-RPC API version when headless. The only valid value is 2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 2) --backend string Backend selection (see 'dlv help backend'). (default "default") --build-flags string Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v" --check-go-version Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true) diff --git a/Documentation/usage/dlv_core.md b/Documentation/usage/dlv_core.md index d582cc1f..8a62502e 100644 --- a/Documentation/usage/dlv_core.md +++ b/Documentation/usage/dlv_core.md @@ -27,7 +27,7 @@ dlv core [flags] ``` --accept-multiclient Allows a headless server to accept multiple client connections via JSON-RPC or DAP. --allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr - --api-version int Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1) + --api-version int Selects JSON-RPC API version when headless. The only valid value is 2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 2) --check-go-version Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true) --headless Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections. --init string Init file, executed by the terminal client. diff --git a/Documentation/usage/dlv_debug.md b/Documentation/usage/dlv_debug.md index 572b65f6..866b9ab2 100644 --- a/Documentation/usage/dlv_debug.md +++ b/Documentation/usage/dlv_debug.md @@ -29,7 +29,7 @@ dlv debug [package] [flags] ``` --accept-multiclient Allows a headless server to accept multiple client connections via JSON-RPC or DAP. --allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr - --api-version int Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1) + --api-version int Selects JSON-RPC API version when headless. The only valid value is 2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 2) --backend string Backend selection (see 'dlv help backend'). (default "default") --build-flags string Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v" --check-go-version Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true) diff --git a/Documentation/usage/dlv_exec.md b/Documentation/usage/dlv_exec.md index 10099651..01a8e78a 100644 --- a/Documentation/usage/dlv_exec.md +++ b/Documentation/usage/dlv_exec.md @@ -29,7 +29,7 @@ dlv exec [flags] ``` --accept-multiclient Allows a headless server to accept multiple client connections via JSON-RPC or DAP. --allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr - --api-version int Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1) + --api-version int Selects JSON-RPC API version when headless. The only valid value is 2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 2) --backend string Backend selection (see 'dlv help backend'). (default "default") --check-go-version Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true) --disable-aslr Disables address space randomization diff --git a/Documentation/usage/dlv_log.md b/Documentation/usage/dlv_log.md index b1d4eab5..f123d14c 100644 --- a/Documentation/usage/dlv_log.md +++ b/Documentation/usage/dlv_log.md @@ -41,7 +41,7 @@ and dap modes. ``` --accept-multiclient Allows a headless server to accept multiple client connections via JSON-RPC or DAP. --allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr - --api-version int Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1) + --api-version int Selects JSON-RPC API version when headless. The only valid value is 2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 2) --backend string Backend selection (see 'dlv help backend'). (default "default") --build-flags string Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v" --check-go-version Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true) diff --git a/Documentation/usage/dlv_redirect.md b/Documentation/usage/dlv_redirect.md index f4bb0689..5f7aed57 100644 --- a/Documentation/usage/dlv_redirect.md +++ b/Documentation/usage/dlv_redirect.md @@ -28,7 +28,7 @@ File redirects can also be changed using the 'restart' command. ``` --accept-multiclient Allows a headless server to accept multiple client connections via JSON-RPC or DAP. --allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr - --api-version int Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1) + --api-version int Selects JSON-RPC API version when headless. The only valid value is 2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 2) --backend string Backend selection (see 'dlv help backend'). (default "default") --build-flags string Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v" --check-go-version Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true) diff --git a/Documentation/usage/dlv_replay.md b/Documentation/usage/dlv_replay.md index a080f586..46b7ed2f 100644 --- a/Documentation/usage/dlv_replay.md +++ b/Documentation/usage/dlv_replay.md @@ -26,7 +26,7 @@ dlv replay [trace directory] [flags] ``` --accept-multiclient Allows a headless server to accept multiple client connections via JSON-RPC or DAP. --allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr - --api-version int Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1) + --api-version int Selects JSON-RPC API version when headless. The only valid value is 2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 2) --check-go-version Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true) --headless Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections. --init string Init file, executed by the terminal client. diff --git a/Documentation/usage/dlv_test.md b/Documentation/usage/dlv_test.md index d849d1da..0b57a1fe 100644 --- a/Documentation/usage/dlv_test.md +++ b/Documentation/usage/dlv_test.md @@ -31,7 +31,7 @@ dlv test [package] [flags] ``` --accept-multiclient Allows a headless server to accept multiple client connections via JSON-RPC or DAP. --allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr - --api-version int Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1) + --api-version int Selects JSON-RPC API version when headless. The only valid value is 2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 2) --backend string Backend selection (see 'dlv help backend'). (default "default") --build-flags string Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v" --check-go-version Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true) diff --git a/Documentation/usage/dlv_version.md b/Documentation/usage/dlv_version.md index 92f26b8b..5239442c 100644 --- a/Documentation/usage/dlv_version.md +++ b/Documentation/usage/dlv_version.md @@ -17,7 +17,7 @@ dlv version [flags] ``` --accept-multiclient Allows a headless server to accept multiple client connections via JSON-RPC or DAP. --allow-non-terminal-interactive Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr - --api-version int Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1) + --api-version int Selects JSON-RPC API version when headless. The only valid value is 2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 2) --backend string Backend selection (see 'dlv help backend'). (default "default") --build-flags string Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v" --check-go-version Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true) diff --git a/cmd/dlv/cmds/commands.go b/cmd/dlv/cmds/commands.go index b5b7662c..0c0fbfba 100644 --- a/cmd/dlv/cmds/commands.go +++ b/cmd/dlv/cmds/commands.go @@ -150,7 +150,7 @@ func New(docCall bool) *cobra.Command { rootCommand.PersistentFlags().BoolVarP(&headless, "headless", "", false, "Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.") rootCommand.PersistentFlags().BoolVarP(&acceptMulti, "accept-multiclient", "", false, "Allows a headless server to accept multiple client connections via JSON-RPC or DAP.") - rootCommand.PersistentFlags().IntVar(&apiVersion, "api-version", 1, "Selects JSON-RPC API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md.") + rootCommand.PersistentFlags().IntVar(&apiVersion, "api-version", 2, "Selects JSON-RPC API version when headless. The only valid value is 2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md.") must(rootCommand.RegisterFlagCompletionFunc("api-version", cobra.FixedCompletions([]string{"1", "2"}, cobra.ShellCompDirectiveNoFileComp))) rootCommand.PersistentFlags().StringVar(&initFile, "init", "", "Init file, executed by the terminal client.") must(rootCommand.MarkPersistentFlagFilename("init")) diff --git a/service/rpc1/client.go b/service/rpc1/client.go deleted file mode 100644 index 06dfec60..00000000 --- a/service/rpc1/client.go +++ /dev/null @@ -1,320 +0,0 @@ -package rpc1 - -import ( - "errors" - "fmt" - "log" - "net/rpc" - "net/rpc/jsonrpc" - - "sync" - - "github.com/go-delve/delve/service/api" -) - -// RPCClient is a RPC service.Client. -type RPCClient struct { - addr string - client *rpc.Client - haltMu sync.Mutex - haltReq bool -} - -var errAPIUnsupported = errors.New("unsupported") - -// NewClient creates a new RPCClient. -func NewClient(addr string) *RPCClient { - client, err := jsonrpc.Dial("tcp", addr) - if err != nil { - log.Fatal("dialing:", err) - } - return &RPCClient{ - addr: addr, - client: client, - } -} - -func (c *RPCClient) ProcessPid() int { - var pid int - c.call("ProcessPid", nil, &pid) - return pid -} - -func (c *RPCClient) Detach(kill bool) error { - return c.call("Detach", kill, nil) -} - -func (c *RPCClient) Restart() error { - return c.call("Restart", nil, nil) -} - -func (c *RPCClient) GetState() (*api.DebuggerState, error) { - state := new(api.DebuggerState) - err := c.call("State", nil, state) - return state, err -} - -func (c *RPCClient) Continue() <-chan *api.DebuggerState { - ch := make(chan *api.DebuggerState) - c.haltMu.Lock() - c.haltReq = false - c.haltMu.Unlock() - go func() { - for { - c.haltMu.Lock() - if c.haltReq { - c.haltMu.Unlock() - close(ch) - return - } - c.haltMu.Unlock() - state := new(api.DebuggerState) - err := c.call("Command", &api.DebuggerCommand{Name: api.Continue}, state) - if err != nil { - state.Err = err - } - if state.Exited { - // Error types apparently cannot be marshalled by Go correctly. Must reset error here. - //lint:ignore ST1005 backwards compatibility - state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus) - } - ch <- state - if err != nil || state.Exited { - close(ch) - return - } - - isbreakpoint := false - istracepoint := true - for i := range state.Threads { - if state.Threads[i].Breakpoint != nil { - isbreakpoint = true - istracepoint = istracepoint && state.Threads[i].Breakpoint.Tracepoint - } - } - - if !isbreakpoint || !istracepoint { - close(ch) - return - } - } - }() - return ch -} - -func (c *RPCClient) Next() (*api.DebuggerState, error) { - state := new(api.DebuggerState) - err := c.call("Command", &api.DebuggerCommand{Name: api.Next}, state) - return state, err -} - -func (c *RPCClient) Step() (*api.DebuggerState, error) { - state := new(api.DebuggerState) - err := c.call("Command", &api.DebuggerCommand{Name: api.Step}, state) - return state, err -} - -func (c *RPCClient) Call(expr string) (*api.DebuggerState, error) { - state := new(api.DebuggerState) - err := c.call("Command", &api.DebuggerCommand{Name: api.Call, Expr: expr}, state) - return state, err -} - -func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) { - state := new(api.DebuggerState) - err := c.call("Command", &api.DebuggerCommand{Name: api.StepInstruction}, state) - return state, err -} - -func (c *RPCClient) ReverseStepInstruction() (*api.DebuggerState, error) { - state := new(api.DebuggerState) - err := c.call("Command", &api.DebuggerCommand{Name: api.ReverseStepInstruction}, state) - return state, err -} - -func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) { - state := new(api.DebuggerState) - cmd := &api.DebuggerCommand{ - Name: api.SwitchThread, - ThreadID: threadID, - } - err := c.call("Command", cmd, state) - return state, err -} - -func (c *RPCClient) SwitchGoroutine(goroutineID int) (*api.DebuggerState, error) { - state := new(api.DebuggerState) - cmd := &api.DebuggerCommand{ - Name: api.SwitchGoroutine, - GoroutineID: int64(goroutineID), - } - err := c.call("Command", cmd, state) - return state, err -} - -func (c *RPCClient) Halt() (*api.DebuggerState, error) { - state := new(api.DebuggerState) - c.haltMu.Lock() - c.haltReq = true - c.haltMu.Unlock() - err := c.call("Command", &api.DebuggerCommand{Name: api.Halt}, state) - return state, err -} - -func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) { - breakpoint := new(api.Breakpoint) - err := c.call("GetBreakpoint", id, breakpoint) - return breakpoint, err -} - -func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) { - breakpoint := new(api.Breakpoint) - err := c.call("GetBreakpointByName", name, breakpoint) - return breakpoint, err -} - -func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) { - newBreakpoint := new(api.Breakpoint) - err := c.call("CreateBreakpoint", breakPoint, &newBreakpoint) - return newBreakpoint, err -} - -func (c *RPCClient) ListBreakpoints() ([]*api.Breakpoint, error) { - var breakpoints []*api.Breakpoint - err := c.call("ListBreakpoints", nil, &breakpoints) - return breakpoints, err -} - -func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) { - bp := new(api.Breakpoint) - err := c.call("ClearBreakpoint", id, bp) - return bp, err -} - -func (c *RPCClient) ClearBreakpointByName(name string) (*api.Breakpoint, error) { - bp := new(api.Breakpoint) - err := c.call("ClearBreakpointByName", name, bp) - return bp, err -} - -func (c *RPCClient) AmendBreakpoint(bp *api.Breakpoint) error { - err := c.call("AmendBreakpoint", bp, nil) - return err -} - -func (c *RPCClient) CancelNext() error { - return errAPIUnsupported -} - -func (c *RPCClient) ListThreads() ([]*api.Thread, error) { - var threads []*api.Thread - err := c.call("ListThreads", nil, &threads) - return threads, err -} - -func (c *RPCClient) GetThread(id int) (*api.Thread, error) { - thread := new(api.Thread) - err := c.call("GetThread", id, &thread) - return thread, err -} - -func (c *RPCClient) EvalVariable(scope api.EvalScope, symbol string) (*api.Variable, error) { - v := new(api.Variable) - err := c.call("EvalSymbol", EvalSymbolArgs{scope, symbol}, v) - return v, err -} - -func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error { - var unused int - return c.call("SetSymbol", SetSymbolArgs{scope, symbol, value}, &unused) -} - -func (c *RPCClient) ListSources(filter string) ([]string, error) { - var sources []string - err := c.call("ListSources", filter, &sources) - return sources, err -} - -func (c *RPCClient) ListFunctions(filter string) ([]string, error) { - var funcs []string - err := c.call("ListFunctions", filter, &funcs) - return funcs, err -} - -func (c *RPCClient) ListTypes(filter string) ([]string, error) { - var types []string - err := c.call("ListTypes", filter, &types) - return types, err -} - -func (c *RPCClient) ListPackageVariables(filter string) ([]api.Variable, error) { - var vars []api.Variable - err := c.call("ListPackageVars", filter, &vars) - return vars, err -} - -func (c *RPCClient) ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error) { - var vars []api.Variable - err := c.call("ListThreadPackageVars", &ThreadListArgs{Id: threadID, Filter: filter}, &vars) - return vars, err -} - -func (c *RPCClient) ListLocalVariables(scope api.EvalScope) ([]api.Variable, error) { - var vars []api.Variable - err := c.call("ListLocalVars", scope, &vars) - return vars, err -} - -func (c *RPCClient) ListRegisters() (string, error) { - var regs string - err := c.call("ListRegisters", nil, ®s) - return regs, err -} - -func (c *RPCClient) ListFunctionArgs(scope api.EvalScope) ([]api.Variable, error) { - var vars []api.Variable - err := c.call("ListFunctionArgs", scope, &vars) - return vars, err -} - -func (c *RPCClient) ListGoroutines() ([]*api.Goroutine, error) { - var goroutines []*api.Goroutine - err := c.call("ListGoroutines", nil, &goroutines) - return goroutines, err -} - -func (c *RPCClient) Stacktrace(goroutineId, depth int, full bool) ([]api.Stackframe, error) { - var locations []api.Stackframe - err := c.call("StacktraceGoroutine", &StacktraceGoroutineArgs{Id: goroutineId, Depth: depth, Full: full}, &locations) - return locations, err -} - -func (c *RPCClient) AttachedToExistingProcess() bool { - var answer bool - c.call("AttachedToExistingProcess", nil, &answer) - return answer -} - -func (c *RPCClient) FindLocation(scope api.EvalScope, loc string) ([]api.Location, error) { - var answer []api.Location - err := c.call("FindLocation", FindLocationArgs{scope, loc}, &answer) - return answer, err -} - -// DisassembleRange disassembles code between startPC and endPC -func (c *RPCClient) DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) { - var r api.AsmInstructions - err := c.call("Disassemble", DisassembleRequest{scope, startPC, endPC, flavour}, &r) - return r, err -} - -// DisassemblePC disassembles function containing pc -func (c *RPCClient) DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) { - var r api.AsmInstructions - err := c.call("Disassemble", DisassembleRequest{scope, pc, 0, flavour}, &r) - return r, err -} - -func (c *RPCClient) call(method string, args, reply interface{}) error { - return c.client.Call("RPCServer."+method, args, reply) -} diff --git a/service/rpc1/doc.go b/service/rpc1/doc.go deleted file mode 100644 index 463384bb..00000000 --- a/service/rpc1/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package rpc1 implements version 1 of Delve's API and is only -// kept for backwards compatibility. -// client.go is the old client code used by Delve's frontend (delve/cmd/dlv) and -// is preserved for backwards compatibility with integration tests. -package rpc1 diff --git a/service/rpc1/server.go b/service/rpc1/server.go deleted file mode 100644 index f05ecc74..00000000 --- a/service/rpc1/server.go +++ /dev/null @@ -1,329 +0,0 @@ -package rpc1 - -import ( - "errors" - "fmt" - - "github.com/go-delve/delve/pkg/proc" - "github.com/go-delve/delve/service" - "github.com/go-delve/delve/service/api" - "github.com/go-delve/delve/service/debugger" -) - -var defaultLoadConfig = proc.LoadConfig{ - FollowPointers: true, - MaxVariableRecurse: 1, - MaxStringLen: 64, - MaxArrayValues: 64, - MaxStructFields: -1, -} - -type RPCServer struct { - // config is all the information necessary to start the debugger and server. - config *service.Config - // debugger is a debugger service. - debugger *debugger.Debugger -} - -func NewServer(config *service.Config, debugger *debugger.Debugger) *RPCServer { - return &RPCServer{config, debugger} -} - -func (s *RPCServer) ProcessPid(arg1 interface{}, pid *int) error { - *pid = s.debugger.ProcessPid() - return nil -} - -func (s *RPCServer) Detach(kill bool, ret *int) error { - return s.debugger.Detach(kill) -} - -func (s *RPCServer) Restart(arg1 interface{}, arg2 *int) error { - if s.config.Debugger.AttachPid != 0 { - return errors.New("cannot restart process Delve did not create") - } - _, err := s.debugger.Restart(false, "", false, nil, [3]string{}, false) - return err -} - -func (s *RPCServer) State(arg interface{}, state *api.DebuggerState) error { - st, err := s.debugger.State(false) - if err != nil { - return err - } - *state = *st - return nil -} - -func (s *RPCServer) Command(command *api.DebuggerCommand, cb service.RPCCallback) { - st, err := s.debugger.Command(command, cb.SetupDoneChan(), cb.DisconnectChan()) - cb.Return(st, err) -} - -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 -} - -func (s *RPCServer) GetBreakpointByName(name string, breakpoint *api.Breakpoint) error { - bp := s.debugger.FindBreakpointByName(name) - if bp == nil { - return fmt.Errorf("no breakpoint with name %s", name) - } - *breakpoint = *bp - return nil -} - -type StacktraceGoroutineArgs struct { - Id int - Depth int - Full bool -} - -func (s *RPCServer) StacktraceGoroutine(args *StacktraceGoroutineArgs, locations *[]api.Stackframe) error { - var loadcfg *proc.LoadConfig = nil - if args.Full { - loadcfg = &defaultLoadConfig - } - locs, err := s.debugger.Stacktrace(int64(args.Id), args.Depth, 0) - if err != nil { - return err - } - *locations, err = s.debugger.ConvertStacktrace(locs, loadcfg) - return err -} - -func (s *RPCServer) ListBreakpoints(arg interface{}, breakpoints *[]*api.Breakpoint) error { - *breakpoints = s.debugger.Breakpoints(false) - return nil -} - -func (s *RPCServer) CreateBreakpoint(bp, newBreakpoint *api.Breakpoint) error { - if err := api.ValidBreakpointName(bp.Name); err != nil { - return err - } - createdbp, err := s.debugger.CreateBreakpoint(bp, "", nil, false) - 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) ClearBreakpointByName(name string, breakpoint *api.Breakpoint) error { - bp := s.debugger.FindBreakpointByName(name) - if bp == nil { - return fmt.Errorf("no breakpoint with name %s", name) - } - deleted, err := s.debugger.ClearBreakpoint(bp) - if err != nil { - return err - } - *breakpoint = *deleted - return nil -} - -func (s *RPCServer) AmendBreakpoint(amend *api.Breakpoint, unused *int) error { - *unused = 0 - if err := api.ValidBreakpointName(amend.Name); err != nil { - return err - } - return s.debugger.AmendBreakpoint(amend) -} - -func (s *RPCServer) ListThreads(arg interface{}, threads *[]*api.Thread) (err error) { - pthreads, err := s.debugger.Threads() - if err != nil { - return err - } - s.debugger.LockTarget() - defer s.debugger.UnlockTarget() - *threads = api.ConvertThreads(pthreads, s.debugger.ConvertThreadBreakpoint) - return nil -} - -func (s *RPCServer) GetThread(id int, thread *api.Thread) error { - t, err := s.debugger.FindThread(id) - if err != nil { - return err - } - if t == nil { - return fmt.Errorf("no thread with id %d", id) - } - s.debugger.LockTarget() - defer s.debugger.UnlockTarget() - *thread = *api.ConvertThread(t, s.debugger.ConvertThreadBreakpoint(t)) - return nil -} - -func (s *RPCServer) ListPackageVars(filter string, variables *[]api.Variable) error { - vars, err := s.debugger.PackageVariables(filter, defaultLoadConfig) - if err != nil { - return err - } - *variables = api.ConvertVars(vars) - return nil -} - -type ThreadListArgs struct { - Id int - Filter string -} - -func (s *RPCServer) ListThreadPackageVars(args *ThreadListArgs, variables *[]api.Variable) error { - vars, err := s.debugger.PackageVariables(args.Filter, defaultLoadConfig) - if err != nil { - return err - } - *variables = api.ConvertVars(vars) - return nil -} - -func (s *RPCServer) ListRegisters(arg interface{}, registers *string) error { - state, err := s.debugger.State(false) - if err != nil { - return err - } - - dregs, err := s.debugger.ThreadRegisters(state.CurrentThread.ID) - if err != nil { - return err - } - regs := api.Registers(api.ConvertRegisters(dregs, s.debugger.DwarfRegisterToString, false)) - *registers = regs.String() - return nil -} - -func (s *RPCServer) ListLocalVars(scope api.EvalScope, variables *[]api.Variable) error { - vars, err := s.debugger.LocalVariables(scope.GoroutineID, scope.Frame, scope.DeferredCall, defaultLoadConfig) - if err != nil { - return err - } - *variables = api.ConvertVars(vars) - return nil -} - -func (s *RPCServer) ListFunctionArgs(scope api.EvalScope, variables *[]api.Variable) error { - vars, err := s.debugger.FunctionArguments(scope.GoroutineID, scope.Frame, scope.DeferredCall, defaultLoadConfig) - if err != nil { - return err - } - *variables = api.ConvertVars(vars) - return nil -} - -type EvalSymbolArgs struct { - Scope api.EvalScope - Symbol string -} - -func (s *RPCServer) EvalSymbol(args EvalSymbolArgs, variable *api.Variable) error { - v, err := s.debugger.EvalVariableInScope(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Symbol, defaultLoadConfig) - if err != nil { - return err - } - *variable = *api.ConvertVar(v) - return nil -} - -type SetSymbolArgs struct { - Scope api.EvalScope - Symbol string - Value string -} - -func (s *RPCServer) SetSymbol(args SetSymbolArgs, unused *int) error { - *unused = 0 - return s.debugger.SetVariableInScope(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Symbol, args.Value) -} - -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, followCalls int, funcs *[]string) error { - fns, err := s.debugger.Functions(filter, followCalls) - if err != nil { - return err - } - *funcs = fns - return nil -} - -func (s *RPCServer) ListTypes(filter string, types *[]string) error { - tps, err := s.debugger.Types(filter) - if err != nil { - return err - } - *types = tps - return nil -} - -func (s *RPCServer) ListGoroutines(arg interface{}, goroutines *[]*api.Goroutine) error { - gs, _, err := s.debugger.Goroutines(0, 0) - if err != nil { - return err - } - s.debugger.LockTarget() - s.debugger.UnlockTarget() - *goroutines = api.ConvertGoroutines(s.debugger.Target(), gs) - return nil -} - -func (s *RPCServer) AttachedToExistingProcess(arg interface{}, answer *bool) error { - if s.config.Debugger.AttachPid != 0 { - *answer = true - } - return nil -} - -type FindLocationArgs struct { - Scope api.EvalScope - Loc string -} - -func (s *RPCServer) FindLocation(args FindLocationArgs, answer *[]api.Location) error { - var err error - *answer, _, err = s.debugger.FindLocation(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Loc, false, nil) - return err -} - -type DisassembleRequest struct { - Scope api.EvalScope - StartPC, EndPC uint64 - Flavour api.AssemblyFlavour -} - -func (s *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error { - insts, err := s.debugger.Disassemble(args.Scope.GoroutineID, args.StartPC, args.EndPC) - if err != nil { - return err - } - *answer = make(api.AsmInstructions, len(insts)) - for i := range insts { - (*answer)[i] = api.ConvertAsmInstruction(insts[i], s.debugger.AsmInstructionText(&insts[i], proc.AssemblyFlavour(args.Flavour))) - } - return nil -} diff --git a/service/rpccommon/server.go b/service/rpccommon/server.go index 3cde2777..3d202374 100644 --- a/service/rpccommon/server.go +++ b/service/rpccommon/server.go @@ -24,7 +24,6 @@ import ( "github.com/go-delve/delve/service/dap" "github.com/go-delve/delve/service/debugger" "github.com/go-delve/delve/service/internal/sameuser" - "github.com/go-delve/delve/service/rpc1" "github.com/go-delve/delve/service/rpc2" ) @@ -39,8 +38,6 @@ type ServerImpl struct { stopChan chan struct{} // debugger is the debugger service. debugger *debugger.Debugger - // s1 is APIv1 server. - s1 *rpc1.RPCServer // s2 is APIv2 server. s2 *rpc2.RPCServer // maps of served methods, one for each supported API. @@ -110,10 +107,11 @@ func (s *ServerImpl) Stop() error { func (s *ServerImpl) Run() error { var err error - if s.config.APIVersion < 2 { - s.config.APIVersion = 1 + if s.config.APIVersion == 0 { + s.config.APIVersion = 2 } - if s.config.APIVersion > 2 { + + if s.config.APIVersion != 2 { return errors.New("unknown API version") } @@ -123,17 +121,13 @@ func (s *ServerImpl) Run() error { return err } - s.s1 = rpc1.NewServer(s.config, s.debugger) s.s2 = rpc2.NewServer(s.config, s.debugger) rpcServer := &RPCServer{s} s.methodMaps = make([]map[string]*methodType, 2) - s.methodMaps[0] = map[string]*methodType{} s.methodMaps[1] = map[string]*methodType{} - suitableMethods(s.s1, s.methodMaps[0], s.log) - suitableMethods(rpcServer, s.methodMaps[0], s.log) suitableMethods(s.s2, s.methodMaps[1], s.log) suitableMethods(rpcServer, s.methodMaps[1], s.log) @@ -452,10 +446,7 @@ func (s *RPCServer) GetVersion(args api.GetVersionIn, out *api.GetVersionOut) er // SetApiVersion changes version of the API being served. func (s *RPCServer) SetApiVersion(args api.SetAPIVersionIn, out *api.SetAPIVersionOut) error { - if args.APIVersion < 2 { - args.APIVersion = 1 - } - if args.APIVersion > 2 { + if args.APIVersion != 2 { return errors.New("unknown API version") } s.s.config.APIVersion = args.APIVersion diff --git a/service/test/common_test.go b/service/test/common_test.go index a91b63db..9eb87615 100644 --- a/service/test/common_test.go +++ b/service/test/common_test.go @@ -8,9 +8,8 @@ import ( "strings" "testing" + "github.com/go-delve/delve/service" "github.com/go-delve/delve/service/api" - "github.com/go-delve/delve/service/rpc1" - "github.com/go-delve/delve/service/rpc2" ) func assertNoError(err error, t *testing.T, s string) { @@ -65,15 +64,8 @@ type BreakpointLister interface { ListBreakpoints() ([]*api.Breakpoint, error) } -func countBreakpoints(t *testing.T, c interface{}) int { - var bps []*api.Breakpoint - var err error - switch c := c.(type) { - case *rpc2.RPCClient: - bps, err = c.ListBreakpoints(false) - case *rpc1.RPCClient: - bps, err = c.ListBreakpoints() - } +func countBreakpoints(t *testing.T, c service.Client) int { + bps, err := c.ListBreakpoints(false) assertNoError(err, t, "ListBreakpoints()") bpcount := 0 for _, bp := range bps { diff --git a/service/test/integration1_test.go b/service/test/integration1_test.go deleted file mode 100644 index d52cf398..00000000 --- a/service/test/integration1_test.go +++ /dev/null @@ -1,1077 +0,0 @@ -package service_test - -import ( - "fmt" - "math/rand" - "net" - "path/filepath" - "runtime" - "strconv" - "strings" - "testing" - "time" - - protest "github.com/go-delve/delve/pkg/proc/test" - "github.com/go-delve/delve/service/debugger" - - "github.com/go-delve/delve/pkg/goversion" - "github.com/go-delve/delve/service" - "github.com/go-delve/delve/service/api" - "github.com/go-delve/delve/service/rpc1" - "github.com/go-delve/delve/service/rpccommon" -) - -func withTestClient1(name string, t *testing.T, fn func(c *rpc1.RPCClient)) { - withTestClient1Extended(name, t, func(c *rpc1.RPCClient, fixture protest.Fixture) { - fn(c) - }) -} - -func withTestClient1Extended(name string, t *testing.T, fn func(c *rpc1.RPCClient, fixture protest.Fixture)) { - if testBackend == "rr" { - protest.MustHaveRecordingAllowed(t) - } - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("couldn't start listener: %s\n", err) - } - defer listener.Close() - var buildFlags protest.BuildFlags - if buildMode == "pie" { - buildFlags = protest.BuildModePIE - } - fixture := protest.BuildFixture(name, buildFlags) - server := rpccommon.NewServer(&service.Config{ - Listener: listener, - ProcessArgs: []string{fixture.Path}, - Debugger: debugger.Config{ - Backend: testBackend, - }, - }) - if err := server.Run(); err != nil { - t.Fatal(err) - } - client := rpc1.NewClient(listener.Addr().String()) - defer func() { - client.Detach(true) - }() - - fn(client, fixture) -} - -func Test1RunWithInvalidPath(t *testing.T) { - if testBackend == "rr" { - // This test won't work because rr returns an error, after recording, when - // the recording failed but also when the recording succeeded but the - // inferior returned an error. Therefore we have to ignore errors from rr. - return - } - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("couldn't start listener: %s\n", err) - } - defer listener.Close() - server := rpccommon.NewServer(&service.Config{ - Listener: listener, - ProcessArgs: []string{"invalid_path"}, - Debugger: debugger.Config{ - Backend: testBackend, - }, - }) - if err := server.Run(); err == nil { - t.Fatal("Expected Run to return error for invalid program path") - } -} - -func Test1Restart_afterExit(t *testing.T) { - withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) { - origPid := c.ProcessPid() - state := <-c.Continue() - if !state.Exited { - t.Fatal("expected initial process to have exited") - } - if err := c.Restart(); err != nil { - t.Fatal(err) - } - if c.ProcessPid() == origPid { - t.Fatal("did not spawn new process, has same PID") - } - state = <-c.Continue() - if !state.Exited { - t.Fatalf("expected restarted process to have exited %v", state) - } - }) -} - -func Test1Restart_breakpointPreservation(t *testing.T) { - withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) { - _, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1, Name: "firstbreakpoint", Tracepoint: true}) - assertNoError(err, t, "CreateBreakpoint()") - stateCh := c.Continue() - - state := <-stateCh - if state.CurrentThread.Breakpoint.Name != "firstbreakpoint" || !state.CurrentThread.Breakpoint.Tracepoint { - t.Fatalf("Wrong breakpoint: %#v\n", state.CurrentThread.Breakpoint) - } - state = <-stateCh - if !state.Exited { - t.Fatal("Did not exit after first tracepoint") - } - - t.Log("Restart") - c.Restart() - stateCh = c.Continue() - state = <-stateCh - if state.CurrentThread.Breakpoint.Name != "firstbreakpoint" || !state.CurrentThread.Breakpoint.Tracepoint { - t.Fatalf("Wrong breakpoint (after restart): %#v\n", state.CurrentThread.Breakpoint) - } - state = <-stateCh - if !state.Exited { - t.Fatal("Did not exit after first tracepoint (after restart)") - } - }) -} - -func Test1Restart_duringStop(t *testing.T) { - withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) { - origPid := c.ProcessPid() - _, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1}) - if err != nil { - t.Fatal(err) - } - state := <-c.Continue() - if state.CurrentThread.Breakpoint == nil { - t.Fatal("did not hit breakpoint") - } - if err := c.Restart(); err != nil { - t.Fatal(err) - } - if c.ProcessPid() == origPid { - t.Fatal("did not spawn new process, has same PID") - } - bps, err := c.ListBreakpoints() - if err != nil { - t.Fatal(err) - } - if len(bps) == 0 { - t.Fatal("breakpoints not preserved") - } - }) -} - -func Test1ClientServer_exit(t *testing.T) { - withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) { - state, err := c.GetState() - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if e, a := false, state.Exited; e != a { - t.Fatalf("Expected exited %v, got %v", e, a) - } - state = <-c.Continue() - if state.Err == nil { - t.Fatalf("Error expected after continue") - } - if !state.Exited { - t.Fatalf("Expected exit after continue: %v", state) - } - _, err = c.GetState() - if err == nil { - t.Fatal("Expected error on querying state from exited process") - } - }) -} - -func Test1ClientServer_step(t *testing.T) { - withTestClient1("testprog", t, func(c *rpc1.RPCClient) { - _, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.helloworld", Line: -1}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - stateBefore := <-c.Continue() - if stateBefore.Err != nil { - t.Fatalf("Unexpected error: %v", stateBefore.Err) - } - - stateAfter, err := c.Step() - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - if before, after := stateBefore.CurrentThread.PC, stateAfter.CurrentThread.PC; before >= after { - t.Fatalf("Expected %#v to be greater than %#v", after, before) - } - }) -} - -func testnext(testcases []nextTest, initialLocation string, t *testing.T) { - withTestClient1("testnextprog", t, func(c *rpc1.RPCClient) { - bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: initialLocation, Line: -1}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - state := <-c.Continue() - if state.Err != nil { - t.Fatalf("Unexpected error: %v", state.Err) - } - - _, err = c.ClearBreakpoint(bp.ID) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - for _, tc := range testcases { - if state.CurrentThread.Line != tc.begin { - t.Fatalf("Program not stopped at correct spot expected %d was %d", tc.begin, state.CurrentThread.Line) - } - - t.Logf("Next for scenario %#v", tc) - state, err = c.Next() - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - if state.CurrentThread.Line != tc.end { - t.Fatalf("Program did not continue to correct next location expected %d was %d", tc.end, state.CurrentThread.Line) - } - } - }) -} - -func Test1NextGeneral(t *testing.T) { - var testcases []nextTest - - ver, _ := goversion.Parse(runtime.Version()) - - if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 7, Rev: -1}) { - testcases = []nextTest{ - {17, 19}, - {19, 20}, - {20, 23}, - {23, 24}, - {24, 26}, - {26, 31}, - {31, 23}, - {23, 24}, - {24, 26}, - {26, 31}, - {31, 23}, - {23, 24}, - {24, 26}, - {26, 27}, - {27, 28}, - {28, 34}, - } - } else { - testcases = []nextTest{ - {17, 19}, - {19, 20}, - {20, 23}, - {23, 24}, - {24, 26}, - {26, 31}, - {31, 23}, - {23, 24}, - {24, 26}, - {26, 31}, - {31, 23}, - {23, 24}, - {24, 26}, - {26, 27}, - {27, 34}, - } - } - - testnext(testcases, "main.testnext", t) -} - -func Test1NextFunctionReturn(t *testing.T) { - testcases := []nextTest{ - {13, 14}, - {14, 15}, - {15, 35}, - } - testnext(testcases, "main.helloworld", t) -} - -func Test1ClientServer_breakpointInMainThread(t *testing.T) { - withTestClient1("testprog", t, func(c *rpc1.RPCClient) { - bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.helloworld", Line: 1}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - state := <-c.Continue() - if err != nil { - t.Fatalf("Unexpected error: %v, state: %#v", err, state) - } - - pc := state.CurrentThread.PC - - if pc-1 != bp.Addr && pc != bp.Addr { - f, l := state.CurrentThread.File, state.CurrentThread.Line - t.Fatalf("Break not respected:\nPC:%#v %s:%d\nFN:%#v \n", pc, f, l, bp.Addr) - } - }) -} - -func Test1ClientServer_breakpointInSeparateGoroutine(t *testing.T) { - withTestClient1("testthreads", t, func(c *rpc1.RPCClient) { - _, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.anotherthread", Line: 1}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - state := <-c.Continue() - if state.Err != nil { - t.Fatalf("Unexpected error: %v, state: %#v", state.Err, state) - } - - f, l := state.CurrentThread.File, state.CurrentThread.Line - if f != "testthreads.go" && l != 9 { - t.Fatal("Program did not hit breakpoint") - } - }) -} - -func Test1ClientServer_breakAtNonexistentPoint(t *testing.T) { - withTestClient1("testprog", t, func(c *rpc1.RPCClient) { - _, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "nowhere", Line: 1}) - if err == nil { - t.Fatal("Should not be able to break at non existent function") - } - }) -} - -func Test1ClientServer_clearBreakpoint(t *testing.T) { - withTestClient1("testprog", t, func(c *rpc1.RPCClient) { - bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sleepytime", Line: 1}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - if e, a := 1, countBreakpoints(t, c); e != a { - t.Fatalf("Expected breakpoint count %d, got %d", e, a) - } - - deleted, err := c.ClearBreakpoint(bp.ID) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - if deleted.ID != bp.ID { - t.Fatalf("Expected deleted breakpoint ID %v, got %v", bp.ID, deleted.ID) - } - - if e, a := 0, countBreakpoints(t, c); e != a { - t.Fatalf("Expected breakpoint count %d, got %d", e, a) - } - }) -} - -func Test1ClientServer_switchThread(t *testing.T) { - withTestClient1("testnextprog", t, func(c *rpc1.RPCClient) { - // With invalid thread id - _, err := c.SwitchThread(-1) - if err == nil { - t.Fatal("Expected error for invalid thread id") - } - - _, err = c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - state := <-c.Continue() - if state.Err != nil { - t.Fatalf("Unexpected error: %v, state: %#v", state.Err, state) - } - - var nt int - ct := state.CurrentThread.ID - threads, err := c.ListThreads() - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - for _, th := range threads { - if th.ID != ct { - nt = th.ID - break - } - } - if nt == 0 { - t.Fatal("could not find thread to switch to") - } - // With valid thread id - state, err = c.SwitchThread(nt) - if err != nil { - t.Fatal(err) - } - if state.CurrentThread.ID != nt { - t.Fatal("Did not switch threads") - } - }) -} - -func Test1ClientServer_infoLocals(t *testing.T) { - withTestClient1("testnextprog", t, func(c *rpc1.RPCClient) { - fp := testProgPath(t, "testnextprog") - _, err := c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 24}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - state := <-c.Continue() - if state.Err != nil { - t.Fatalf("Unexpected error: %v, state: %#v", state.Err, state) - } - locals, err := c.ListLocalVariables(api.EvalScope{GoroutineID: -1}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if len(locals) != 3 { - t.Fatalf("Expected 3 locals, got %d %#v", len(locals), locals) - } - }) -} - -func Test1ClientServer_infoArgs(t *testing.T) { - withTestClient1("testnextprog", t, func(c *rpc1.RPCClient) { - fp := testProgPath(t, "testnextprog") - _, err := c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 47}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - state := <-c.Continue() - if state.Err != nil { - t.Fatalf("Unexpected error: %v, state: %#v", state.Err, state) - } - regs, err := c.ListRegisters() - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if regs == "" { - t.Fatal("Expected string showing registers values, got empty string") - } - locals, err := c.ListFunctionArgs(api.EvalScope{GoroutineID: -1}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if len(locals) != 2 { - t.Fatalf("Expected 2 function args, got %d %#v", len(locals), locals) - } - }) -} - -func Test1ClientServer_traceContinue(t *testing.T) { - withTestClient1("integrationprog", t, func(c *rpc1.RPCClient) { - 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 { - t.Fatalf("Unexpected error: %v\n", err) - } - count := 0 - contChan := c.Continue() - for state := range contChan { - if state.CurrentThread != nil && state.CurrentThread.Breakpoint != nil { - count++ - - t.Logf("%v", state) - - bpi := state.CurrentThread.BreakpointInfo - - if bpi.Goroutine == nil { - t.Fatalf("No goroutine information") - } - - if len(bpi.Stacktrace) == 0 { - t.Fatalf("No stacktrace\n") - } - - if len(bpi.Variables) != 1 { - t.Fatalf("Wrong number of variables returned: %d", len(bpi.Variables)) - } - - if bpi.Variables[0].Name != "i" { - t.Fatalf("Wrong variable returned %s", bpi.Variables[0].Name) - } - - t.Logf("Variable i is %v", bpi.Variables[0]) - - n, err := strconv.Atoi(bpi.Variables[0].Value) - - if err != nil || n != count-1 { - t.Fatalf("Wrong variable value %q (%v %d)", bpi.Variables[0].Value, err, count) - } - } - if state.Exited { - continue - } - t.Logf("%v", state) - if state.Err != nil { - t.Fatalf("Unexpected error during continue: %v\n", state.Err) - } - } - - if count != 3 { - t.Fatalf("Wrong number of continues hit: %d\n", count) - } - }) -} - -func Test1ClientServer_traceContinue2(t *testing.T) { - withTestClient1("integrationprog", t, func(c *rpc1.RPCClient) { - bp1, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1, Tracepoint: true}) - if err != nil { - t.Fatalf("Unexpected error: %v\n", err) - } - bp2, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: 1, Tracepoint: true}) - if err != nil { - t.Fatalf("Unexpected error: %v\n", err) - } - countMain := 0 - countSayhi := 0 - contChan := c.Continue() - for state := range contChan { - if state.CurrentThread != nil && state.CurrentThread.Breakpoint != nil { - switch state.CurrentThread.Breakpoint.ID { - case bp1.ID: - countMain++ - case bp2.ID: - countSayhi++ - } - - t.Logf("%v", state) - } - if state.Exited { - continue - } - if state.Err != nil { - t.Fatalf("Unexpected error during continue: %v\n", state.Err) - } - } - - if countMain != 1 { - t.Fatalf("Wrong number of continues (main.main) hit: %d\n", countMain) - } - - if countSayhi != 3 { - t.Fatalf("Wrong number of continues (main.sayhi) hit: %d\n", countSayhi) - } - }) -} - -func Test1ClientServer_FindLocations(t *testing.T) { - if buildMode == "pie" && runtime.GOARCH == "ppc64le" { - t.Skip("skipped on ppc64le: broken") - } - withTestClient1("locationsprog", t, func(c *rpc1.RPCClient) { - 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) - findLocationHelper(t, c, "main.anotherFunction:1", false, 1, someFunctionLine1) - findLocationHelper(t, c, "anotherFunction", false, 1, someFunctionCallAddr) - findLocationHelper(t, c, "main.anotherFunction", false, 1, someFunctionCallAddr) - findLocationHelper(t, c, fmt.Sprintf("*0x%x", someFunctionCallAddr), false, 1, someFunctionCallAddr) - findLocationHelper(t, c, "sprog.go:26", true, 0, 0) - - findLocationHelper(t, c, "String", true, 0, 0) - findLocationHelper(t, c, "main.String", true, 0, 0) - - someTypeStringFuncAddr := findLocationHelper(t, c, "locationsprog.go:14", false, 1, 0)[0] - otherTypeStringFuncAddr := findLocationHelper(t, c, "locationsprog.go:18", false, 1, 0)[0] - findLocationHelper(t, c, "SomeType.String", false, 1, someTypeStringFuncAddr) - findLocationHelper(t, c, "(*SomeType).String", false, 1, someTypeStringFuncAddr) - findLocationHelper(t, c, "main.SomeType.String", false, 1, someTypeStringFuncAddr) - findLocationHelper(t, c, "main.(*SomeType).String", false, 1, someTypeStringFuncAddr) - - // Issue #275 - readfile := findLocationHelper(t, c, "io/ioutil.ReadFile", false, 1, 0)[0] - - // Issue #296 - findLocationHelper(t, c, "/io/ioutil.ReadFile", false, 1, readfile) - findLocationHelper(t, c, "ioutil.ReadFile", false, 1, readfile) - - stringAddrs := findLocationHelper(t, c, "/^main.*Type.*String$/", false, 2, 0) - - if otherTypeStringFuncAddr != stringAddrs[0] && otherTypeStringFuncAddr != stringAddrs[1] { - t.Fatalf("Wrong locations returned for \"/.*Type.*String/\", got: %v expected: %v and %v\n", stringAddrs, someTypeStringFuncAddr, otherTypeStringFuncAddr) - } - - _, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 4, Tracepoint: false}) - if err != nil { - t.Fatalf("CreateBreakpoint(): %v\n", err) - } - - <-c.Continue() - - locationsprog35Addr := findLocationHelper(t, c, "locationsprog.go:35", false, 1, 0)[0] - findLocationHelper(t, c, fmt.Sprintf("%s:35", testProgPath(t, "locationsprog")), false, 1, locationsprog35Addr) - findLocationHelper(t, c, "+1", false, 1, locationsprog35Addr) - findLocationHelper(t, c, "35", false, 1, locationsprog35Addr) - findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:33", false, 1, 0)[0]) - }) - - withTestClient1("testnextdefer", t, func(c *rpc1.RPCClient) { - firstMainLine := findLocationHelper(t, c, "testnextdefer.go:5", false, 1, 0)[0] - findLocationHelper(t, c, "main.main", false, 1, firstMainLine) - }) - - withTestClient1("stacktraceprog", t, func(c *rpc1.RPCClient) { - stacktracemeAddr := findLocationHelper(t, c, "stacktraceprog.go:4", false, 1, 0)[0] - findLocationHelper(t, c, "main.stacktraceme", false, 1, stacktracemeAddr) - }) - - withTestClient1Extended("locationsUpperCase", t, func(c *rpc1.RPCClient, fixture protest.Fixture) { - // Upper case - findLocationHelper(t, c, "locationsUpperCase.go:6", false, 1, 0) - - // Fully qualified path - findLocationHelper(t, c, fixture.Source+":6", false, 1, 0) - bp, err := c.CreateBreakpoint(&api.Breakpoint{File: fixture.Source, Line: 6}) - if err != nil { - t.Fatalf("Could not set breakpoint in %s: %v\n", fixture.Source, err) - } - c.ClearBreakpoint(bp.ID) - - // Allow `/` or `\` on Windows - if runtime.GOOS == "windows" { - findLocationHelper(t, c, filepath.FromSlash(fixture.Source)+":6", false, 1, 0) - bp, err = c.CreateBreakpoint(&api.Breakpoint{File: filepath.FromSlash(fixture.Source), Line: 6}) - if err != nil { - t.Fatalf("Could not set breakpoint in %s: %v\n", filepath.FromSlash(fixture.Source), err) - } - c.ClearBreakpoint(bp.ID) - } - - // Case-insensitive on Windows, case-sensitive otherwise - shouldWrongCaseBeError := true - numExpectedMatches := 0 - if runtime.GOOS == "windows" { - shouldWrongCaseBeError = false - numExpectedMatches = 1 - } - findLocationHelper(t, c, strings.ToLower(fixture.Source)+":6", shouldWrongCaseBeError, numExpectedMatches, 0) - bp, err = c.CreateBreakpoint(&api.Breakpoint{File: strings.ToLower(fixture.Source), Line: 6}) - if (err == nil) == shouldWrongCaseBeError { - t.Fatalf("Could not set breakpoint in %s: %v\n", strings.ToLower(fixture.Source), err) - } - c.ClearBreakpoint(bp.ID) - }) -} - -func Test1ClientServer_FindLocationsAddr(t *testing.T) { - withTestClient1("locationsprog2", t, func(c *rpc1.RPCClient) { - <-c.Continue() - - afunction := findLocationHelper(t, c, "main.afunction", false, 1, 0)[0] - anonfunc := findLocationHelper(t, c, "main.main.func1", false, 1, 0)[0] - - findLocationHelper(t, c, "*fn1", false, 1, afunction) - findLocationHelper(t, c, "*fn3", false, 1, anonfunc) - }) -} - -func Test1ClientServer_EvalVariable(t *testing.T) { - withTestClient1("testvariables", t, func(c *rpc1.RPCClient) { - state := <-c.Continue() - - if state.Err != nil { - t.Fatalf("Continue(): %v\n", state.Err) - } - - var1, err := c.EvalVariable(api.EvalScope{GoroutineID: -1}, "a1") - assertNoError(err, t, "EvalVariable") - - t.Logf("var1: %s", var1.SinglelineString()) - - if var1.Value != "foofoofoofoofoofoo" { - t.Fatalf("Wrong variable value: %s", var1.Value) - } - }) -} - -func Test1ClientServer_SetVariable(t *testing.T) { - withTestClient1("testvariables", t, func(c *rpc1.RPCClient) { - state := <-c.Continue() - - if state.Err != nil { - t.Fatalf("Continue(): %v\n", state.Err) - } - - assertNoError(c.SetVariable(api.EvalScope{GoroutineID: -1}, "a2", "8"), t, "SetVariable()") - - a2, err := c.EvalVariable(api.EvalScope{GoroutineID: -1}, "a2") - if err != nil { - t.Fatalf("Could not evaluate variable: %v", err) - } - - t.Logf("a2: %v", a2) - - n, err := strconv.Atoi(a2.Value) - - if err != nil && n != 8 { - t.Fatalf("Wrong variable value: %v", a2) - } - }) -} - -func Test1ClientServer_FullStacktrace(t *testing.T) { - if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { - t.Skip("cgo doesn't work on darwin/arm64") - } - if runtime.GOARCH == "ppc64le" && buildMode == "pie" { - t.Skip("pie mode broken on ppc64le") - } - - lenient := false - if runtime.GOOS == "windows" { - lenient = true - } - - withTestClient1("goroutinestackprog", t, func(c *rpc1.RPCClient) { - _, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.stacktraceme", Line: -1}) - assertNoError(err, t, "CreateBreakpoint()") - state := <-c.Continue() - if state.Err != nil { - t.Fatalf("Continue(): %v\n", state.Err) - } - - gs, err := c.ListGoroutines() - assertNoError(err, t, "GoroutinesInfo()") - found := make([]bool, 10) - for _, g := range gs { - frames, err := c.Stacktrace(int(g.ID), 40, true) - assertNoError(err, t, fmt.Sprintf("Stacktrace(%d)", g.ID)) - t.Logf("goroutine %d", g.ID) - for i, frame := range frames { - t.Logf("\tframe %d off=%#x bpoff=%#x pc=%#x %s:%d %s", i, frame.FrameOffset, frame.FramePointerOffset, frame.PC, frame.File, frame.Line, frame.Function.Name()) - if frame.Function == nil { - continue - } - if frame.Function.Name() != "main.agoroutine" { - continue - } - for _, arg := range frame.Arguments { - if arg.Name != "i" { - continue - } - t.Logf("\tvariable i is %+v\n", arg) - argn, err := strconv.Atoi(arg.Value) - if err == nil { - found[argn] = true - } - } - } - } - - for i := range found { - if !found[i] { - if lenient { - lenient = false - } else { - t.Fatalf("Goroutine %d not found", i) - } - } - } - - t.Logf("continue") - - state = <-c.Continue() - if state.Err != nil { - t.Fatalf("Continue(): %v\n", state.Err) - } - - frames, err := c.Stacktrace(-1, 10, true) - assertNoError(err, t, "Stacktrace") - - cur := 3 - for i, frame := range frames { - t.Logf("\tframe %d off=%#x bpoff=%#x pc=%#x %s:%d %s", i, frame.FrameOffset, frame.FramePointerOffset, frame.PC, frame.File, frame.Line, frame.Function.Name()) - if i == 0 { - continue - } - v := frame.Var("n") - if v == nil { - t.Fatalf("Could not find value of variable n in frame %d", i) - } - vn, err := strconv.Atoi(v.Value) - if err != nil || vn != cur { - t.Fatalf("Expected value %d got %d (error: %v)", cur, vn, err) - } - cur-- - if cur < 0 { - break - } - } - }) -} - -func Test1Issue355(t *testing.T) { - // After the target process has terminated should return an error but not crash - withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) { - _, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: -1}) - assertNoError(err, t, "CreateBreakpoint()") - ch := c.Continue() - state := <-ch - tid := state.CurrentThread.ID - gid := state.SelectedGoroutine.ID - assertNoError(state.Err, t, "First Continue()") - ch = c.Continue() - state = <-ch - if !state.Exited { - t.Fatalf("Target did not terminate after second continue") - } - - ch = c.Continue() - state = <-ch - assertError(state.Err, t, "Continue()") - - s, err := c.Next() - assertErrorOrExited(s, err, t, "Next()") - s, err = c.Step() - assertErrorOrExited(s, err, t, "Step()") - s, err = c.StepInstruction() - assertErrorOrExited(s, err, t, "StepInstruction()") - s, err = c.SwitchThread(tid) - assertErrorOrExited(s, err, t, "SwitchThread()") - s, err = c.SwitchGoroutine(int(gid)) - assertErrorOrExited(s, err, t, "SwitchGoroutine()") - s, err = c.Halt() - assertErrorOrExited(s, err, t, "Halt()") - _, err = c.ListThreads() - assertError(err, t, "ListThreads()") - _, err = c.GetThread(tid) - assertError(err, t, "GetThread()") - assertError(c.SetVariable(api.EvalScope{GoroutineID: gid}, "a", "10"), t, "SetVariable()") - _, err = c.ListLocalVariables(api.EvalScope{GoroutineID: gid}) - assertError(err, t, "ListLocalVariables()") - _, err = c.ListFunctionArgs(api.EvalScope{GoroutineID: gid}) - assertError(err, t, "ListFunctionArgs()") - _, err = c.ListRegisters() - assertError(err, t, "ListRegisters()") - _, err = c.ListGoroutines() - assertError(err, t, "ListGoroutines()") - _, err = c.Stacktrace(int(gid), 10, false) - assertError(err, t, "Stacktrace()") - _, err = c.FindLocation(api.EvalScope{GoroutineID: gid}, "+1") - assertError(err, t, "FindLocation()") - _, err = c.DisassemblePC(api.EvalScope{GoroutineID: -1}, 0x40100, api.IntelFlavour) - assertError(err, t, "DisassemblePC()") - }) -} - -func Test1Disasm(t *testing.T) { - if runtime.GOARCH == "ppc64le" { - t.Skip("skipped on ppc64le: broken") - } - // Tests that disassembling by PC, range, and current PC all yield 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 - withTestClient1("locationsprog2", t, func(c *rpc1.RPCClient) { - ch := c.Continue() - state := <-ch - assertNoError(state.Err, t, "Continue()") - - locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "main.main") - assertNoError(err, t, "FindLocation()") - if len(locs) != 1 { - t.Fatalf("wrong number of locations for main.main: %d", len(locs)) - } - d1, err := c.DisassemblePC(api.EvalScope{GoroutineID: -1}, locs[0].PC, api.IntelFlavour) - assertNoError(err, t, "DisassemblePC()") - if len(d1) < 2 { - t.Fatalf("wrong size of disassembly: %d", len(d1)) - } - - pcstart := d1[0].Loc.PC - pcend := d1[len(d1)-1].Loc.PC + uint64(len(d1[len(d1)-1].Bytes)) - d2, err := c.DisassembleRange(api.EvalScope{GoroutineID: -1}, pcstart, pcend, api.IntelFlavour) - assertNoError(err, t, "DisassembleRange()") - - if len(d1) != len(d2) { - t.Logf("d1: %v", d1) - t.Logf("d2: %v", d2) - t.Fatal("mismatched length between disassemble pc and disassemble range") - } - - d3, err := c.DisassemblePC(api.EvalScope{GoroutineID: -1}, state.CurrentThread.PC, api.IntelFlavour) - assertNoError(err, t, "DisassemblePC() - second call") - - if len(d1) != len(d3) { - t.Logf("d1: %v", d1) - t.Logf("d3: %v", d3) - t.Fatal("mismatched length between the two calls of disassemble pc") - } - - // look for static call to afunction() on line 29 - found := false - for i := range d3 { - if d3[i].Loc.Line == 29 && (strings.HasPrefix(d3[i].Text, "call") || strings.HasPrefix(d3[i].Text, "CALL")) && d3[i].DestLoc != nil && d3[i].DestLoc.Function != nil && d3[i].DestLoc.Function.Name() == "main.afunction" { - found = true - break - } - } - if !found { - t.Fatal("Could not find call to main.afunction on line 29") - } - - haspc := false - for i := range d3 { - if d3[i].AtPC { - haspc = true - break - } - } - - if !haspc { - t.Logf("d3: %v", d3) - t.Fatal("PC instruction not found") - } - - if runtime.GOARCH == "386" && buildMode == "pie" { - // Skip the rest of the test because on intel 386 with PIE build mode - // the compiler will insert calls to __x86.get_pc_thunk which do not have DIEs and we can't resolve. - return - } - - startinstr := getCurinstr(d3) - count := 0 - for { - if count > 20 { - t.Fatal("too many step instructions executed without finding a call instruction") - } - state, err := c.StepInstruction() - assertNoError(err, t, fmt.Sprintf("StepInstruction() %d", count)) - - d3, err = c.DisassemblePC(api.EvalScope{GoroutineID: -1}, state.CurrentThread.PC, api.IntelFlavour) - assertNoError(err, t, fmt.Sprintf("StepInstruction() %d", count)) - - curinstr := getCurinstr(d3) - - if curinstr == nil { - t.Fatalf("Could not find current instruction %d", count) - } - - if curinstr.Loc.Line != startinstr.Loc.Line { - t.Fatal("Calling StepInstruction() repeatedly did not find the call instruction") - } - - if strings.HasPrefix(curinstr.Text, "call") || strings.HasPrefix(curinstr.Text, "CALL") { - t.Logf("call: %v", curinstr) - if curinstr.DestLoc == nil || curinstr.DestLoc.Function == nil { - t.Fatalf("Call instruction does not have destination: %v", curinstr) - } - if curinstr.DestLoc.Function.Name() != "main.afunction" { - t.Fatalf("Call instruction destination not main.afunction: %v", curinstr) - } - break - } - - count++ - } - }) -} - -func Test1NegativeStackDepthBug(t *testing.T) { - // After the target process has terminated should return an error but not crash - withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) { - _, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: -1}) - assertNoError(err, t, "CreateBreakpoint()") - ch := c.Continue() - state := <-ch - assertNoError(state.Err, t, "Continue()") - _, err = c.Stacktrace(-1, -2, true) - assertError(err, t, "Stacktrace()") - }) -} - -func Test1ClientServer_CondBreakpoint(t *testing.T) { - withTestClient1("parallel_next", t, func(c *rpc1.RPCClient) { - bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: 1}) - assertNoError(err, t, "CreateBreakpoint()") - bp.Cond = "n == 7" - assertNoError(c.AmendBreakpoint(bp), t, "AmendBreakpoint() 1") - bp, err = c.GetBreakpoint(bp.ID) - assertNoError(err, t, "GetBreakpoint() 1") - bp.Variables = append(bp.Variables, "n") - assertNoError(c.AmendBreakpoint(bp), t, "AmendBreakpoint() 2") - bp, err = c.GetBreakpoint(bp.ID) - assertNoError(err, t, "GetBreakpoint() 2") - if bp.Cond == "" { - t.Fatalf("No condition set on breakpoint %#v", bp) - } - if len(bp.Variables) != 1 { - t.Fatalf("Wrong number of expressions to evaluate on breakpoint %#v", bp) - } - state := <-c.Continue() - assertNoError(state.Err, t, "Continue()") - - nvar, err := c.EvalVariable(api.EvalScope{GoroutineID: -1}, "n") - assertNoError(err, t, "EvalVariable()") - - if nvar.SinglelineString() != "7" { - t.Fatalf("Stopped on wrong goroutine %s\n", nvar.Value) - } - }) -} - -func Test1Issue419(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 - withTestClient1("issue419", t, func(c *rpc1.RPCClient) { - go func() { - rand.Seed(time.Now().Unix()) - d := time.Duration(rand.Intn(4) + 1) - time.Sleep(d * time.Second) - _, err := c.Halt() - assertNoError(err, t, "RequestManualStop()") - }() - statech := c.Continue() - state := <-statech - assertNoError(state.Err, t, "Continue()") - }) -} - -func Test1TypesCommand(t *testing.T) { - withTestClient1("testvariables2", t, func(c *rpc1.RPCClient) { - state := <-c.Continue() - assertNoError(state.Err, t, "Continue()") - types, err := c.ListTypes("") - assertNoError(err, t, "ListTypes()") - - found := false - for i := range types { - if types[i] == "main.astruct" { - found = true - break - } - } - if !found { - t.Fatal("Type astruct not found in ListTypes output") - } - - types, err = c.ListTypes("^main.astruct$") - assertNoError(err, t, "ListTypes(\"main.astruct\")") - if len(types) != 1 { - t.Fatalf("ListTypes(\"^main.astruct$\") did not filter properly, expected 1 got %d: %v", len(types), types) - } - }) -} - -func Test1Issue406(t *testing.T) { - withTestClient1("issue406", t, func(c *rpc1.RPCClient) { - locs, err := c.FindLocation(api.EvalScope{GoroutineID: -1}, "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{GoroutineID: -1}, "cfgtree") - assertNoError(err, t, "EvalVariable()") - vs := v.MultilineString("", "") - t.Logf("cfgtree formats to: %s\n", vs) - }) -}