From f7cb6053d20e3adfe196352b50061de2a5616218 Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Sun, 24 Apr 2016 19:18:02 +0200 Subject: [PATCH] documentation: API documentation improvements (#507) * documentation: headless invocation should specify --api-version=2 * documentation: More API documentation * documentation: copied documentation from client.go to rpc2/server.go Fixes #164 --- Documentation/api/README.md | 2 +- Documentation/api/json-rpc/README.md | 43 +++++++++++++++++- service/rpc2/server.go | 67 ++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 2 deletions(-) diff --git a/Documentation/api/README.md b/Documentation/api/README.md index 2f39e673..916b7390 100644 --- a/Documentation/api/README.md +++ b/Documentation/api/README.md @@ -7,7 +7,7 @@ Delve exposes an API interface so that other programs, mostly IDEs and editors, In order to run Delve in "API mode", simply invoke with one of the standard commands, providing the `--headless` flag, like so: ``` -$ dlv debug --headless --log --listen=127.0.0.1:8181 +$ dlv debug --headless --api-version=2 --log --listen=127.0.0.1:8181 ``` This will start the debugger in a non-interactive mode, listening on the specified address, and will enable logging. The last two flags are optional, of course. diff --git a/Documentation/api/json-rpc/README.md b/Documentation/api/json-rpc/README.md index af6927df..860ac130 100644 --- a/Documentation/api/json-rpc/README.md +++ b/Documentation/api/json-rpc/README.md @@ -1,3 +1,44 @@ # JSON-RPC interface -Delve exposes a [JSON-RPC](http://json-rpc.org/) API interface. Here is an (incomplete) [list of language implementations](http://json-rpc.org/wiki/implementations). +Delve exposes a [JSON-RPC](http://json-rpc.org/) API interface. + +Note that this JSON-RPC interface is served over a streaming socket, *not* over HTTP. + +Here is an (incomplete) [list of language implementations](http://json-rpc.org/wiki/implementations). + +# API versions + +Delve currently supports two versions of its API. By default a headless instance of `dlv` will serve APIv1, 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. + +# API version 2 documentation + +All the methods of the type `service/rpc2.RPCServer` can be called using JSON-RPC, the documentation for these calls is [available on godoc](https://godoc.org/github.com/derekparker/delve/service/rpc2#RPCServer). + +Note that all exposed methods take one single input parameter (usually called `args`) of a struct type and also return a result of a struct type. Also note that the method name should be prefixed with `RPCServer.` in JSON-RPC. + +# Example + +Your client wants to set a breakpoint on the function `main.main`. +The first step will be calling the method `FindLocation` with `Scope = api.EvalScope{ GoroutineID: -1, Frame: 0}` and `Loc = "main.main"`. The JSON-RPC request packet should look like this: + +``` +{"method":"RPCServer.FindLocation","params":[{"Scope":{"GoroutineID":-1,"Frame":0},"Loc":"main.main"}],"id":2} +``` + +the response packet will look like this: + +``` +{"id":2,"result":{"Locations":[{"pc":4199019,"file":"/home/a/temp/callme/callme.go","line":31,"function":{"name":"main.main","value":4198992,"type":84,"goType":0}}]},"error":null} +``` + +Now your client should call the method `CreateBreakpoint` and specify `4199019` (the `pc` field in the response object) as the target address: + +``` +{"method":"RPCServer.CreateBreakpoint","params":[{"Breakpoint":{"addr":4199019}}],"id":3} +``` + +if this request is successful your client will receive the following response: + +``` +{"id":3,"result":{"Breakpoint":{"id":1,"name":"","addr":4199019,"file":"/home/a/temp/callme/callme.go","line":31,"functionName":"main.main","Cond":"","continue":false,"goroutine":false,"stacktrace":0,"LoadArgs":null,"LoadLocals":null,"hitCount":{},"totalHitCount":0}},"error":null} +``` diff --git a/service/rpc2/server.go b/service/rpc2/server.go index cb62bddd..ba957131 100644 --- a/service/rpc2/server.go +++ b/service/rpc2/server.go @@ -107,6 +107,7 @@ type ProcessPidOut struct { Pid int } +// ProcessPid returns the pid of the process we are debugging. func (s *RPCServer) ProcessPid(arg ProcessPidIn, out *ProcessPidOut) error { out.Pid = s.debugger.ProcessPid() return nil @@ -119,6 +120,7 @@ type DetachIn struct { type DetachOut struct { } +// Detach detaches the debugger, optionally killing the process. func (s *RPCServer) Detach(arg DetachIn, out *DetachOut) error { return s.debugger.Detach(arg.Kill) } @@ -129,6 +131,7 @@ type RestartIn struct { type RestartOut struct { } +// Restart restarts program. func (s *RPCServer) Restart(arg RestartIn, out *RestartOut) error { if s.config.AttachPid != 0 { return errors.New("cannot restart process Delve did not create") @@ -143,6 +146,7 @@ type StateOut struct { State *api.DebuggerState } +// State returns the current debugger state. func (s *RPCServer) State(arg StateIn, out *StateOut) error { st, err := s.debugger.State() if err != nil { @@ -156,6 +160,7 @@ type CommandOut struct { State api.DebuggerState } +// Command interrupts, continues and steps through the program. func (s *RPCServer) Command(command api.DebuggerCommand, out *CommandOut) error { st, err := s.debugger.Command(&command) if err != nil { @@ -174,6 +179,7 @@ type GetBreakpointOut struct { Breakpoint api.Breakpoint } +// GetBreakpoint gets a breakpoint by Name (if Name is not an empty string) or by ID. func (s *RPCServer) GetBreakpoint(arg GetBreakpointIn, out *GetBreakpointOut) error { var bp *api.Breakpoint if arg.Name != "" { @@ -202,6 +208,10 @@ type StacktraceOut struct { Locations []api.Stackframe } +// Stacktrace returns stacktrace of goroutine Id up to the specified Depth. +// +// If Full is set it will also the variable of all local variables +// and function arguments of all stack frames. func (s *RPCServer) Stacktrace(arg StacktraceIn, out *StacktraceOut) error { cfg := arg.Cfg if cfg == nil && arg.Full { @@ -222,6 +232,7 @@ type ListBreakpointsOut struct { Breakpoints []*api.Breakpoint } +// ListBreakpoints gets all breakpoints. func (s *RPCServer) ListBreakpoints(arg ListBreakpointsIn, out *ListBreakpointsOut) error { out.Breakpoints = s.debugger.Breakpoints() return nil @@ -235,6 +246,18 @@ type CreateBreakpointOut struct { Breakpoint api.Breakpoint } +// CreateBreakpoint creates a new breakpoint. +// +// - If arg.Breakpoint.File is not an empty string the breakpoint +// will be created on the specified file:line location +// +// - If arg.Breakpoint.FunctionName is not an empty string +// the breakpoint will be created on the specified function:line +// location. Note that setting a breakpoint on a function's entry point +// (line == 0) can have surprising consequences, it is advisable to +// use line = -1 instead which will skip the prologue. +// +// - Otherwise the value specified by arg.Breakpoint.Addr will be used. func (s *RPCServer) CreateBreakpoint(arg CreateBreakpointIn, out *CreateBreakpointOut) error { createdbp, err := s.debugger.CreateBreakpoint(&arg.Breakpoint) if err != nil { @@ -253,6 +276,8 @@ type ClearBreakpointOut struct { Breakpoint *api.Breakpoint } +// ClearBreakpoint deletes a breakpoint by Name (if Name is not an +// empty string) or by ID. func (s *RPCServer) ClearBreakpoint(arg ClearBreakpointIn, out *ClearBreakpointOut) error { var bp *api.Breakpoint if arg.Name != "" { @@ -281,6 +306,11 @@ type AmendBreakpointIn struct { type AmendBreakpointOut struct { } +// AmendBreakpoint allows user to update an existing breakpoint +// for example to change the information retrieved when the +// breakpoint is hit or to change, add or remove the break condition. +// +// arg.Breakpoint.ID must be a valid breakpoint ID func (s *RPCServer) AmendBreakpoint(arg AmendBreakpointIn, out *AmendBreakpointOut) error { return s.debugger.AmendBreakpoint(&arg.Breakpoint) } @@ -292,6 +322,7 @@ type ListThreadsOut struct { Threads []*api.Thread } +// ListThreads lists all threads. func (s *RPCServer) ListThreads(arg ListThreadsIn, out *ListThreadsOut) (err error) { out.Threads, err = s.debugger.Threads() return err @@ -305,6 +336,7 @@ type GetThreadOut struct { Thread *api.Thread } +// GetThread gets a thread by its ID. func (s *RPCServer) GetThread(arg GetThreadIn, out *GetThreadOut) error { t, err := s.debugger.FindThread(arg.Id) if err != nil { @@ -326,6 +358,7 @@ type ListPackageVarsOut struct { Variables []api.Variable } +// ListPackageVars lists all package variables in the context of the current thread. func (s *RPCServer) ListPackageVars(arg ListPackageVarsIn, out *ListPackageVarsOut) error { state, err := s.debugger.State() if err != nil { @@ -352,6 +385,7 @@ type ListRegistersOut struct { Registers string } +// ListRegisters lists registers and their values. func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) error { state, err := s.debugger.State() if err != nil { @@ -375,6 +409,7 @@ type ListLocalVarsOut struct { Variables []api.Variable } +// ListLocalVars lists all local variables in scope. func (s *RPCServer) ListLocalVars(arg ListLocalVarsIn, out *ListLocalVarsOut) error { vars, err := s.debugger.LocalVariables(arg.Scope, *api.LoadConfigToProc(&arg.Cfg)) if err != nil { @@ -393,6 +428,7 @@ type ListFunctionArgsOut struct { Args []api.Variable } +// ListFunctionArgs lists all arguments to the current function func (s *RPCServer) ListFunctionArgs(arg ListFunctionArgsIn, out *ListFunctionArgsOut) error { vars, err := s.debugger.FunctionArguments(arg.Scope, *api.LoadConfigToProc(&arg.Cfg)) if err != nil { @@ -412,6 +448,10 @@ type EvalOut struct { Variable *api.Variable } +// EvalVariable returns a variable in the specified context. +// +// See https://github.com/derekparker/delve/wiki/Expressions for +// a description of acceptable values of arg.Expr. func (s *RPCServer) Eval(arg EvalIn, out *EvalOut) error { cfg := arg.Cfg if cfg == nil { @@ -434,6 +474,8 @@ type SetIn struct { type SetOut struct { } +// Set sets the value of a variable. Only numerical types and +// pointers are currently supported. func (s *RPCServer) Set(arg SetIn, out *SetOut) error { return s.debugger.SetVariableInScope(arg.Scope, arg.Symbol, arg.Value) } @@ -446,6 +488,7 @@ type ListSourcesOut struct { Sources []string } +// ListSources lists all source files in the process matching filter. func (s *RPCServer) ListSources(arg ListSourcesIn, out *ListSourcesOut) error { ss, err := s.debugger.Sources(arg.Filter) if err != nil { @@ -463,6 +506,7 @@ type ListFunctionsOut struct { Funcs []string } +// ListFunctions lists all functions in the process matching filter. func (s *RPCServer) ListFunctions(arg ListFunctionsIn, out *ListFunctionsOut) error { fns, err := s.debugger.Functions(arg.Filter) if err != nil { @@ -480,6 +524,7 @@ type ListTypesOut struct { Types []string } +// ListTypes lists all types in the process matching filter. func (s *RPCServer) ListTypes(arg ListTypesIn, out *ListTypesOut) error { tps, err := s.debugger.Types(arg.Filter) if err != nil { @@ -496,6 +541,7 @@ type ListGoroutinesOut struct { Goroutines []*api.Goroutine } +// ListGoroutines lists all goroutines. func (s *RPCServer) ListGoroutines(arg ListGoroutinesIn, out *ListGoroutinesOut) error { gs, err := s.debugger.Goroutines() if err != nil { @@ -512,6 +558,7 @@ type AttachedToExistingProcessOut struct { Answer bool } +// AttachedToExistingProcess returns whether we attached to a running process or not func (c *RPCServer) AttachedToExistingProcess(arg AttachedToExistingProcessIn, out *AttachedToExistingProcessOut) error { if c.config.AttachPid != 0 { out.Answer = true @@ -528,6 +575,19 @@ type FindLocationOut struct { Locations []api.Location } +// FindLocation returns concrete location information described by a location expression +// +// loc ::= : | [:] | // | (+|-) | | *
+// * can be the full path of a file or just a suffix +// * ::= .. | .(*). | . | . | (*). | +// * must be unambiguous +// * // will return a location for each function matched by regex +// * + returns a location for the line that is lines after the current line +// * - returns a location for the line that is lines before the current line +// * returns a location for a line in the current file +// * *
returns the location corresponding to the specified address +// +// NOTE: this function does not actually set breakpoints. func (c *RPCServer) FindLocation(arg FindLocationIn, out *FindLocationOut) error { var err error out.Locations, err = c.debugger.FindLocation(arg.Scope, arg.Loc) @@ -544,6 +604,13 @@ type DisassembleOut struct { Disassemble api.AsmInstructions } +// Disassemble code. +// +// If both StartPC and EndPC are non-zero the specified range will be disassembled, otherwise the function containing StartPC will be disassembled. +// +// Scope is used to mark the instruction the specified gorutine is stopped at. +// +// Disassemble will also try to calculate the destination address of an absolute indirect CALL if it happens to be the instruction the selected goroutine is stopped at. 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) -- GitLab