whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
# How to write a Delve client, an informal guide
|
||||
|
||||
## Spawning the backend
|
||||
|
||||
The `dlv` binary built by our `Makefile` contains both the backend and a
|
||||
simple command line client. If you are writing your own client you will
|
||||
probably want to run only the backend, you can do this by specifying the
|
||||
`--headless` option, for example:
|
||||
|
||||
```
|
||||
$ dlv --headless debug
|
||||
```
|
||||
|
||||
The rest of the command line remains unchanged. You can use `debug`, `exec`,
|
||||
`test`, etc... along with `--headless` and they will work. If this project
|
||||
is part of a larger IDE integration then you probably have your own build
|
||||
system and do not wish to offload this task to Delve, in that case it's
|
||||
perfectly fine to always use the `dlv exec` command but do remember that:
|
||||
1. Delve may not have all the information necessary to properly debug optimized binaries, so it is recommended to disable them via: `-gcflags='all=-N -l`.
|
||||
2. your users *do want* to debug their tests so you should also provide some way to build the test executable (equivalent to `go test -c --gcflags='all=-N -l'`) and pass it to Delve.
|
||||
|
||||
It would also be nice for your users if you provided a way to attach to a running process, like `dlv attach` does.
|
||||
|
||||
Command line arguments that should be handed to the inferior process should be specified on dlv's command line after a "--" argument:
|
||||
|
||||
```
|
||||
dlv exec --headless ./somebinary -- these arguments are for the inferior process
|
||||
```
|
||||
|
||||
Specifying a static port number, like in the [README](//github.com/go-delve/delve/tree/master/Documentation/README.md) example, can be done using `--listen=127.0.0.1:portnumber`.
|
||||
|
||||
This will, however, cause problems if you actually spawn multiple instances of the debugger.
|
||||
|
||||
It's probably better to let Delve pick a random unused port number on its own. To do this do not specify any `--listen` option and read one line of output from dlv's stdout. If the first line emitted by dlv starts with "API server listening at: " then dlv started correctly and the rest of the line specifies the address that Delve is listening at.
|
||||
|
||||
The `--log-dest` option can be used to redirect the "API server listening at:" message to a file or to a file descriptor. If the flag is not specified, the message will be output to stdout while other log messages are output to stderr.
|
||||
|
||||
## Controlling the backend
|
||||
|
||||
Once you have a running headless instance you can connect to it and start sending commands. Delve's protocol is built on top of the [JSON-RPC 1.0 specification](https://www.jsonrpc.org/specification_v1).
|
||||
|
||||
The methods of a `service/rpc2.RPCServer` are exposed through this connection, to find out which requests you can send see the documentation of RPCServer on [Go Reference](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer).
|
||||
|
||||
### Example
|
||||
|
||||
Let's say you are trying to create a breakpoint. By looking at [Go Reference](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer) you'll find that there is a `CreateBreakpoint` method in `RPCServer`.
|
||||
|
||||
This method, like all other methods of RPCServer that you can call through the API, has two arguments: `args` and `out`: `args` contains all the input arguments of `CreateBreakpoint`, while `out` is what `CreateBreakpoint` will return to you.
|
||||
|
||||
The call that you could want to make, in pseudo-code, would be:
|
||||
|
||||
```
|
||||
RPCServer.CreateBreakpoint(CreateBreakpointIn{ File: "/User/you/some/file.go", Line: 16 })
|
||||
```
|
||||
|
||||
To actually send this request on the JSON-RPC connection you just have to convert the CreateBreakpointIn object to json and then wrap everything into a JSON-RPC envelope:
|
||||
|
||||
```
|
||||
{"method":"RPCServer.CreateBreakpoint","params":[{"Breakpoint":{"file":"/User/you/some/file.go","line":16}}],"id":27}
|
||||
```
|
||||
|
||||
Delve will respond by sending a response packet that will look like this:
|
||||
|
||||
```
|
||||
{"id":27, "result": {"Breakpoint": {"id":3, "name":"", "addr":4538829, "file":"/User/you/some/file.go", "line":16, "functionName":"main.main", "Cond":"", "continue":false, "goroutine":false, "stacktrace":0, "LoadArgs":null, "LoadLocals":null, "hitCount":{}, "totalHitCount":0}}, "error":null}
|
||||
```
|
||||
|
||||
## Selecting the API version
|
||||
|
||||
Delve currently supports two version of its API, APIv1 and APIv2. By default
|
||||
a headless instance of `dlv` will serve APIv1 for backward-compatibility
|
||||
with older clients, however new clients should use APIv2 as new features
|
||||
will only be made available through version 2. The preferred method of
|
||||
switching to APIv2 is to send the `RPCServer.SetApiVersion` command right
|
||||
after connecting to the backend.
|
||||
Alternatively the `--api-version=2` command line option can be used when
|
||||
spawning the backend.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
Just like any other program, both Delve and your client have bugs. To help
|
||||
with determining where the problem is you should log the exchange of
|
||||
messages between Delve and your client somehow.
|
||||
|
||||
If you don't want to do this yourself you can also pass the options `--log
|
||||
--log-output=rpc` to Delve. In fact the `--log-output` has many useful
|
||||
values and you should expose it to users, if possible, so that we can
|
||||
diagnose problems that are hard to reproduce.
|
||||
|
||||
## Using RPCServer.Command
|
||||
|
||||
`Command` is probably the most important API entry point. It lets your
|
||||
client stop (`Name == "halt"`) and resume (`Name == "continue"`) execution
|
||||
of the inferior process.
|
||||
|
||||
The return value of `Command` is a `DebuggerState` object. If you lose the
|
||||
DebuggerState object returned by your last call to `Command` you can ask for
|
||||
a new copy with `RPCServer.State`.
|
||||
|
||||
### Dealing with simultaneous breakpoints
|
||||
|
||||
Since Go is a programming language with a big emphasis on concurrency and
|
||||
parallelism it's possible that multiple goroutines will stop at a breakpoint
|
||||
simultaneously. This may at first seem incredibly unlikely but you must
|
||||
understand that between the time a breakpoint is triggered and the point
|
||||
where the debugger finishes stopping all threads of the inferior process
|
||||
thousands of CPU instructions have to be executed, which make simultaneous
|
||||
breakpoint triggering not that unlikely.
|
||||
|
||||
You should signal to your user *all* the breakpoints that occur after
|
||||
executing a command, not just the first one. To do this iterate through the
|
||||
`Threads` array in `DebuggerState` and note all the threads that have a non
|
||||
nil `Breakpoint` member.
|
||||
|
||||
### Special continue commands
|
||||
|
||||
In addition to "halt" and vanilla "continue" `Command` offers a few extra
|
||||
flavours of continue that automatically set interesting temporary
|
||||
breakpoints: "next" will continue until the next line of the program,
|
||||
"stepout" will continue until the function returns, "step" is just like
|
||||
"next" but it will step into function calls (but skip all calls to
|
||||
unexported runtime functions).
|
||||
|
||||
All of "next", "step" and "stepout" operate on the selected goroutine. The
|
||||
selected goroutine is described by the `SelectedGoroutine` field of
|
||||
`DebuggerState`. Every time `Command` returns the selected goroutine will be
|
||||
reset to the goroutine that triggered the breakpoint.
|
||||
|
||||
If multiple breakpoints are triggered simultaneously the selected goroutine
|
||||
will be chosen randomly between the goroutines that are stopped at a
|
||||
breakpoint. If a breakpoint is hit by a thread that is executing on the
|
||||
system stack *there will be no selected goroutine*. If the "halt" command is
|
||||
called *there may not be a selected goroutine*.
|
||||
|
||||
The selected goroutine can be changed using the "switchGoroutine" command.
|
||||
If "switchGoroutine" is used to switch to a goroutine that's currently
|
||||
parked SelectedGoroutine and CurrentThread will be mismatched. Always prefer
|
||||
SelectedGoroutine over CurrentThread, you should ignore CurrentThread
|
||||
entirely unless SelectedGoroutine is nil.
|
||||
|
||||
### Special continue commands and asynchronous breakpoints
|
||||
|
||||
Because of the way go internals work it is not possible for a debugger to
|
||||
resume a single goroutine. Therefore it's possible that after executing a
|
||||
next/step/stepout a goroutine other than the goroutine the next/step/stepout
|
||||
was executed on will hit a breakpoint.
|
||||
|
||||
If this happens Delve will return a DebuggerState with NextInProgress set to
|
||||
true. When this happens your client has two options:
|
||||
|
||||
* You can signal that a different breakpoint was hit and then automatically attempt to complete the next/step/stepout by calling `RPCServer.Command` with `Name == "continue"`
|
||||
* You can abort the next/step/stepout operation using `RPCServer.CancelNext`.
|
||||
|
||||
It is important to note that while NextInProgress is true it is not possible
|
||||
to call next/step/stepout again without using CancelNext first. There can
|
||||
not be multiple next/step/stepout operations in progress at any time.
|
||||
|
||||
### RPCServer.Command and stale executable files
|
||||
|
||||
It's possible (albeit unfortunate) that your user will decide to change the
|
||||
source of the program being executed in the debugger, while the debugger is
|
||||
running. Because of this it would be advisable that your client check that
|
||||
the executable is not stale every time `Command` returns and notify the user
|
||||
that the executable being run is stale and line numbers may nor align
|
||||
properly anymore.
|
||||
|
||||
You can do this bookkeeping yourself, but Delve can also help you with the
|
||||
`LastModified` call that returns the LastModified time of the executable
|
||||
file when Delve started it.
|
||||
|
||||
## Using RPCServer.CreateBreakpoint
|
||||
|
||||
The only two fields you probably want to fill of the Breakpoint argument of
|
||||
CreateBreakpoint are File and Line. The file name should be the absolute
|
||||
path to the file as the compiler saw it.
|
||||
|
||||
For example if the compiler saw this path:
|
||||
|
||||
```
|
||||
/Users/you/go/src/something/something.go
|
||||
```
|
||||
|
||||
But `/Users/you/go/src/something` is a symbolic link to
|
||||
`/Users/you/projects/golang/something` the path *must* be specified as
|
||||
`/Users/you/go/src/something/something.go` and
|
||||
`/Users/you/projects/golang/something/something.go` will not be recognized
|
||||
as valid.
|
||||
|
||||
If you want to let your users specify a breakpoint on a function selected
|
||||
from a list of all functions you should specify the name of the function in
|
||||
the FunctionName field of Breakpoint.
|
||||
|
||||
If you want to support the [same language as dlv's break and trace commands](//github.com/go-delve/delve/tree/master/Documentation/cli/locspec.md)
|
||||
you should call RPCServer.FindLocation and
|
||||
then use the returned slice of Location objects to create Breakpoints to
|
||||
pass to CreateBreakpoint: just fill each Breakpoint.Addr with the
|
||||
contents of the corresponding Location.PC.
|
||||
|
||||
## Looking into variables
|
||||
|
||||
There are several API entry points to evaluate variables in Delve:
|
||||
|
||||
* RPCServer.ListPackageVars returns all global variables in all packages
|
||||
* PRCServer.ListLocalVars returns all local variables of a stack frame
|
||||
* RPCServer.ListFunctionArgs returns all function arguments of a stack frame
|
||||
* RPCServer.Eval evaluates an expression on a given stack frame
|
||||
|
||||
All those API calls take a LoadConfig argument. The LoadConfig specifies how
|
||||
much of the variable's value should actually be loaded. Because of
|
||||
LoadConfig a variable could be loaded incompletely, you should always notify
|
||||
the user of this:
|
||||
|
||||
* For strings, arrays, slices *and structs* the load is incomplete if: `Variable.Len > len(Variable.Children)`. This can happen to structs even if LoadConfig.MaxStructFields is -1 when MaxVariableRecurse is reached.
|
||||
* For maps the load is incomplete if: `Variable.Len > len(Variable.Children) / 2`
|
||||
* For interfaces the load is incomplete if the only children has the onlyAddr attribute set to true.
|
||||
|
||||
### Loading more of a Variable
|
||||
|
||||
You can also give the user an option to continue loading an incompletely
|
||||
loaded variable. To load a struct that wasn't loaded automatically evaluate
|
||||
the expression returned by:
|
||||
|
||||
```
|
||||
fmt.Sprintf("*(*%q)(%#x)", v.Type, v.Addr)
|
||||
```
|
||||
|
||||
where v is the variable that was truncated.
|
||||
|
||||
To load more elements from an array, slice or string:
|
||||
|
||||
```
|
||||
fmt.Sprintf("(*(*%q)(%#x))[%d:]", v.Type, v.Addr, len(v.Children))
|
||||
```
|
||||
|
||||
To load more elements from a map:
|
||||
|
||||
```
|
||||
fmt.Sprintf("(*(*%q)(%#x))[%d:]", v.Type, v.Addr, len(v.Children)/2)
|
||||
```
|
||||
|
||||
All the evaluation API calls except ListPackageVars also take a EvalScope
|
||||
argument, this specifies which stack frame you are interested in. If you
|
||||
are interested in the topmost stack frame of the current goroutine (or
|
||||
thread) use: `EvalScope{ GoroutineID: -1, Frame: 0 }`.
|
||||
|
||||
More information on the expression language interpreted by RPCServer.Eval
|
||||
can be found [here](//github.com/go-delve/delve/tree/master/Documentation/cli/expr.md).
|
||||
|
||||
### Variable shadowing
|
||||
|
||||
Let's assume you are debugging a piece of code that looks like this:
|
||||
|
||||
```
|
||||
for i := 0; i < N; i++ {
|
||||
for i := 0; i < M; i++ {
|
||||
f(i) // <-- debugger is stopped here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The response to a ListLocalVars request will list two variables named `i`,
|
||||
because at that point in the code two variables named `i` exist and are in
|
||||
scope. Only one (the innermost one), however, is visible to the user. The
|
||||
other one is *shadowed*.
|
||||
|
||||
Delve will tell you which variable is shadowed through the `Flags` field of
|
||||
the `Variable` object. If `Flags` has the `VariableShadowed` bit set then
|
||||
the variable in question is shadowed.
|
||||
|
||||
Users of your client should be able to distinguish between shadowed and
|
||||
non-shadowed variables.
|
||||
|
||||
## Gracefully ending the debug session
|
||||
|
||||
To ensure that Delve cleans up after itself by deleting the `debug` or `debug.test` binary it creates
|
||||
and killing any processes spawned by the program being debugged, the `Detach` command needs to be called.
|
||||
In case you are disconnecting a running program, ensure to halt the program before trying to detach.
|
||||
|
||||
## Testing the Client
|
||||
|
||||
A set of [example programs is
|
||||
available](https://github.com/aarzilli/delve_client_testing) to test corner
|
||||
cases in handling breakpoints and displaying data structures. Follow the
|
||||
instructions in the README.txt file.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Server/Client API Documentation
|
||||
|
||||
Delve exposes two API interfaces, JSON-RPC and DAP, so that frontends other than the built-in [terminal client](../cli/README.md), such as [IDEs and editors](../EditorIntegration.md), can interact with Delve programmatically. The [JSON-RPC API](json-rpc/README.md) is used by the [terminal client](../cli/README.md), and will always stay up to date in lockstep regardless of new features. The [DAP API](dap/README.md) is a popular generic API already in use by many [tools](https://microsoft.github.io/debug-adapter-protocol/implementors/tools/).
|
||||
|
||||
## Usage
|
||||
|
||||
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 --api-version=2 --log --log-output=debugger,dap,rpc --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 logging flags as well as the server address are optional, of course.
|
||||
|
||||
Optionally, you may also specify the `--accept-multiclient` flag if you would like to connect multiple JSON-RPC or DAP clients to the API.
|
||||
|
||||
You can connect to the headless debugger from Delve itself using the `connect` subcommand:
|
||||
|
||||
```
|
||||
$ dlv connect 127.0.0.1:8181
|
||||
```
|
||||
|
||||
This can be useful for remote debugging.
|
||||
|
||||
## API Interfaces
|
||||
|
||||
Delve has been architected in such a way as to allow multiple client/server implementations. All of the "business logic" as it were is abstracted away from the actual client/server implementations, allowing for easy implementation of new API interfaces.
|
||||
|
||||
### Current API Interfaces
|
||||
|
||||
- [JSON-RPC](json-rpc/README.md)
|
||||
- [DAP](dap/README.md)
|
||||
@@ -0,0 +1,147 @@
|
||||
# DAP Interface
|
||||
|
||||
Delve exposes a [DAP](https://microsoft.github.io/debug-adapter-protocol/overview) API interface.
|
||||
|
||||
This interface is served over a streaming TCP socket using `dlv` server in one of the two headless modes:
|
||||
1. [`dlv dap`](../../usage/dlv_dap.md) - starts a single-use DAP-only server that waits for a client to specify launch/attach configuration for starting the debug session.
|
||||
2. `dlv --headless <command> <debuggee>` - starts a general server, enters a debug session for the specified debuggee and waits for a [JSON-RPC](../json-rpc/README.md) or a [DAP](https://microsoft.github.io/debug-adapter-protocol/overview) remote-attach client to begin interactive debugging. Can be used in multi-client mode with the following options:
|
||||
* `--accept-multiclient` - use to support connections from multiple clients
|
||||
* `--continue` - use to resume debuggee execution as soon as server session starts
|
||||
|
||||
See [Launch and Attach Configurations](#launch-and-attach-configurations) for more usage details of these two options.
|
||||
|
||||
The primary user of this mode is [VS Code Go](https://github.com/golang/vscode-go). Please see its
|
||||
detailed [debugging documentation](https://github.com/golang/vscode-go/blob/master/docs/debugging.md) for additional information.
|
||||
|
||||
## Debug Adapter Protocol
|
||||
|
||||
[DAP](https://microsoft.github.io/debug-adapter-protocol/specification) is a general debugging protocol supported by many [tools](https://microsoft.github.io/debug-adapter-protocol/implementors/tools/) and [programming languages](https://microsoft.github.io/debug-adapter-protocol/implementors/adapters/). We tailored it to Go specifics, such as mapping [threads request](https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Threads) to communicate goroutines and [exceptionInfo request](https://microsoft.github.io/debug-adapter-protocol/specification#Requests_ExceptionInfo) to support panics and fatal errors.
|
||||
|
||||
See [dap.Server.handleRequest](https://github.com/go-delve/delve/search?q=handleRequest) and capabilities set in [dap.Server.onInitializeRequest](https://github.com/go-delve/delve/search?q=onInitializeRequest) for an up-to-date list of supported requests and options.
|
||||
|
||||
## Launch and Attach Configurations
|
||||
|
||||
In addition to the general [DAP spec](https://microsoft.github.io/debug-adapter-protocol/specification), the server supports the following implementation-specific configuration options for starting the debug session:
|
||||
|
||||
<table border=1>
|
||||
<tr><th>request<th>mode<th>required<th colspan=9>optional<th></tr>
|
||||
<tr><td rowspan=5>launch<br><a href="https://pkg.go.dev/github.com/go-delve/delve/service/dap#LaunchConfig">godoc</a>
|
||||
<td>debug<td>program <td>dlvCwd<td>env<td>backend<td>args<td>cwd<td>buildFlags<td>output<td>noDebug
|
||||
<td rowspan=7>
|
||||
substitutePath<br>
|
||||
stopOnEntry<br>
|
||||
stackTraceDepth<br>
|
||||
showGlobalVariables<br>
|
||||
showRegisters<br>
|
||||
showPprofLabels<br>
|
||||
hideSystemGoroutines<br>
|
||||
goroutineFilters
|
||||
</tr>
|
||||
<tr>
|
||||
<td>test<td>program <td>dlvCwd<td>env<td>backend<td>args<td>cwd<td>buildFlags<td>output<td>noDebug</tr>
|
||||
<tr>
|
||||
<td>exec<td>program <td>dlvCwd<td>env<td>backend<td>args<td>cwd<td> <td> <td>noDebug</tr>
|
||||
<tr>
|
||||
<td>core<td>program<br>corefilePath<td>dlvCwd<td>env<td> <td> <td> <td> <td> <td> </tr>
|
||||
<tr>
|
||||
<td>replay<td>traceDirPath <td>dlvCwd<td>env<td> <td> <td> <td> <td> <td> </tr>
|
||||
<tr><td rowspan=2>attach<br><a href="https://pkg.go.dev/github.com/go-delve/delve/service/dap#AttachConfig">godoc</a>
|
||||
<td>local<td>processId <td> <td> <td>backend<td> <td> <td> <td> <td> </tr>
|
||||
<tr>
|
||||
<td>remote<td> <td> <td> <td> <td> <td> <td> <td> <td> </tr>
|
||||
</table>
|
||||
|
||||
|
||||
Not all of the configurations are supported by each of the two available DAP servers:
|
||||
|
||||
<table border=1>
|
||||
<tr>
|
||||
<th>request<th>"mode":<th>`dlv dap`<th>`dlv --headless` <th> Description <th> Typical Client Usage
|
||||
</tr>
|
||||
<tr>
|
||||
<td>launch<td>"debug"<br>"test"<br>"exec"<br>"replay"<br>"core"<td>supported<td>NOT supported <td> Tells the `dlv dap` server to launch the specified target and start debugging it.
|
||||
<td rowspan=2>The client would launch the `dlv dap` server for the user or allow them to specify `host:port` of an external (a.k.a. remote) server.
|
||||
<tr>
|
||||
<td>attach<td>"local"<td>supported<td>NOT supported<td>Tells the `dlv dap` server to attach to an existing process local to the server.
|
||||
</tr>
|
||||
<tr>
|
||||
<td>attach<td>"remote"<td>NOT supported<td>supported<td>Tells the `dlv --headless` server that it is expected to already be debugging a target specified as part of its command-line invocation.
|
||||
<td>The client would expect `host:port` specification of an external (a.k.a. remote) server that the user already started with target <a href="https://github.com/go-delve/delve/blob/master/Documentation/usage/README.md">command and args</a>.
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Disconnect and Shutdown
|
||||
|
||||
### Single-Client Mode
|
||||
|
||||
When used with `dlv dap` or `dlv --headless --accept-multiclient=false` (default), the DAP server will shut itself down at the end of the debug session, when the client sends a [disconnect request](https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Disconnect). If the debuggee was launched, it will be taken down as well. If the debuggee was attached to, `terminateDebuggee` option will be respected.
|
||||
|
||||
When the program terminates, we send a [terminated event](https://microsoft.github.io/debug-adapter-protocol/specification#Events_Terminated), which is expected to trigger a [disconnect request](https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Disconnect) from the client for a session and a server shutdown. The [restart request](https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Restart) is not yet supported.
|
||||
|
||||
The server also shuts down in case of a client connection error or SIGTERM signal, taking down a launched process, but letting an attached process continue.
|
||||
|
||||
Pressing Ctrl-C on the terminal where a headless server is running sends SIGINT to the debuggee, foregrounded in headless mode to support debugging interactive programs.
|
||||
|
||||
### Multi-Client Mode
|
||||
|
||||
When used with `dlv --headless --accept-multiclient=true`, the DAP server will honor the multi-client mode when a client [disconnects](https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Disconnect) or client connection fails. The server will remain running and ready for a new client connection, and the debuggee will remain in whatever state it was at the time of disconnect - running or halted. Once [`suspendDebuggee`](https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Disconnect) option is supported by frontends like VS Code ([vscode/issues/134412](https://github.com/microsoft/vscode/issues/134412)), we will update the server to offer this as a way to specify debuggee state on disconnect.
|
||||
|
||||
The client may request full shutdown of the server and the debuggee with [`terminateDebuggee`](https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Disconnect) option.
|
||||
|
||||
The server shuts down in response to a SIGTERM signal, taking down a launched process, but letting an attached process continue.
|
||||
|
||||
Pressing Ctrl-C on the terminal where a headless server is running sends SIGINT to the debuggee, foregrounded in headless mode to support debugging interactive programs.
|
||||
|
||||
## Debugger Output
|
||||
|
||||
The debugger always logs one of the following on start-up to stdout:
|
||||
* `dlv dap`:
|
||||
* `DAP server listening at: <host>:<port>`
|
||||
* `dlv --headless`:
|
||||
* `API server listening at: <host>:<port>`
|
||||
|
||||
This can be used to confirm that server start-up succeeded.
|
||||
|
||||
The server uses [output events](https://microsoft.github.io/debug-adapter-protocol/specification#Events_Output) to communicate errors and select status messages to the client. For example:
|
||||
|
||||
```
|
||||
Step interrupted by a breakpoint. Use 'Continue' to resume the original step command.
|
||||
invalid command: Unable to step while the previous step is interrupted by a breakpoint.
|
||||
Use 'Continue' to resume the original step command.
|
||||
Detaching and terminating target process
|
||||
```
|
||||
|
||||
More detailed logging can be enabled with `--log --log-output=dap` as part of the [`dlv` command](../../usage/dlv.md).
|
||||
It will record the server-side DAP message traffic. For example,
|
||||
```
|
||||
2022-01-04T00:27:57-08:00 debug layer=dap [<- from client]{"seq":1,"type":"request","command":"initialize","arguments":{"clientID":"vscode","clientName":"Visual Studio Code","adapterID":"go","locale":"en-us","linesStartAt1":true,"columnsStartAt1":true,"pathFormat":"path","supportsVariableType":true,"supportsVariablePaging":true,"supportsRunInTerminalRequest":true,"supportsMemoryReferences":true,"supportsProgressReporting":true,"supportsInvalidatedEvent":true}}
|
||||
2022-01-04T00:27:57-08:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":1,"success":true,"command":"initialize","body":{"supportsConfigurationDoneRequest":true,"supportsFunctionBreakpoints":true,"supportsConditionalBreakpoints":true,"supportsEvaluateForHovers":true,"supportsSetVariable":true,"supportsExceptionInfoRequest":true,"supportTerminateDebuggee":true,"supportsDelayedStackTraceLoading":true,"supportsLogPoints":true,"supportsDisassembleRequest":true,"supportsClipboardContext":true,"supportsSteppingGranularity":true,"supportsInstructionBreakpoints":true}}
|
||||
2022-01-04T00:27:57-08:00 debug layer=dap [<- from client]{"seq":2,"type":"request","command":"launch","arguments":{"name":"Launch file","type":"go","request":"launch","mode":"debug","program":"./temp.go","hideSystemGoroutines":true,"__buildDir":"/Users/polina/go/src","__sessionId":"2ad0f0c1-a1fd-4fff-9fff-b8bc9a933fe5"}}
|
||||
2022-01-04T00:27:57-08:00 debug layer=dap parsed launch config: {
|
||||
"mode": "debug",
|
||||
"program": "./temp.go",
|
||||
"backend": "default",
|
||||
"stackTraceDepth": 50,
|
||||
"hideSystemGoroutines": true
|
||||
}
|
||||
...
|
||||
```
|
||||
This logging is written to stderr and is not forwarded via
|
||||
[output events](https://microsoft.github.io/debug-adapter-protocol/specification#Events_Output).
|
||||
|
||||
## Debuggee Output
|
||||
|
||||
Debuggee's stdout and stderr are written to stdout and stderr respectfully and are not forwarded via
|
||||
[output events](https://microsoft.github.io/debug-adapter-protocol/specification#Events_Output).
|
||||
|
||||
## Versions
|
||||
|
||||
The initial DAP support was released in [v1.6.1](https://github.com/go-delve/delve/releases/tag/v1.6.1) with many additional improvements in subsequent versions. The [remote attach](https://github.com/go-delve/delve/issues/2328) support was added in [v1.7.3](https://github.com/go-delve/delve/releases/tag/v1.7.3).
|
||||
|
||||
The DAP API changes are backward-compatible as all new features are opt-in only. To update to a new [DAP version](https://microsoft.github.io/debug-adapter-protocol/changelog) and import a new DAP feature into delve,
|
||||
one must first update the [go-dap](https://github.com/google/go-dap) dependency.
|
||||
|
||||
<!--- TODO:
|
||||
- most requests are handled synchronously and block
|
||||
- hence many commands not supported when running, but setting breakpoints is
|
||||
--->
|
||||
@@ -0,0 +1,43 @@
|
||||
# JSON-RPC interface
|
||||
|
||||
Delve exposes a [JSON-RPC](https://www.jsonrpc.org/specification_v1) API interface.
|
||||
|
||||
Note that this JSON-RPC interface is served over a streaming socket, *not* over HTTP.
|
||||
|
||||
# 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.
|
||||
|
||||
# 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://pkg.go.dev/github.com/go-delve/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}
|
||||
```
|
||||
Reference in New Issue
Block a user