github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/Documentation/api/ClientHowto.md (about) 1 # How to write a Delve client, an informal guide 2 3 ## Spawning the backend 4 5 The `dlv` binary built by our `Makefile` contains both the backend and a 6 simple command line client. If you are writing your own client you will 7 probably want to run only the backend, you can do this by specifying the 8 `--headless` option, for example: 9 10 ``` 11 $ dlv --headless debug 12 ``` 13 14 The rest of the command line remains unchanged. You can use `debug`, `exec`, 15 `test`, etc... along with `--headless` and they will work. If this project 16 is part of a larger IDE integration then you probably have your own build 17 system and do not wish to offload this task to Delve, in that case it's 18 perfectly fine to always use the `dlv exec` command but do remember that: 19 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`. 20 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. 21 22 It would also be nice for your users if you provided a way to attach to a running process, like `dlv attach` does. 23 24 Command line arguments that should be handed to the inferior process should be specified on dlv's command line after a "--" argument: 25 26 ``` 27 dlv exec --headless ./somebinary -- these arguments are for the inferior process 28 ``` 29 30 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`. 31 32 This will, however, cause problems if you actually spawn multiple instances of the debugger. 33 34 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. 35 36 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. 37 38 ## Controlling the backend 39 40 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). 41 42 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 [godoc](https://godoc.org/github.com/go-delve/Delve/service/rpc2#RPCServer). 43 44 ### Example 45 46 Let's say you are trying to create a breakpoint. By looking at [godoc](https://godoc.org/github.com/go-delve/Delve/service/rpc2#RPCServer) you'll find that there is a `CreateBreakpoint` method in `RPCServer`. 47 48 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. 49 50 The call that you could want to make, in pseudo-code, would be: 51 52 ``` 53 RPCServer.CreateBreakpoint(CreateBreakpointIn{ File: "/User/you/some/file.go", Line: 16 }) 54 ``` 55 56 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: 57 58 ``` 59 {"method":"RPCServer.CreateBreakpoint","params":[{"Breakpoint":{"file":"/User/you/some/file.go","line":16}}],"id":27} 60 ``` 61 62 Delve will respond by sending a response packet that will look like this: 63 64 ``` 65 {"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} 66 ``` 67 68 ## Selecting the API version 69 70 Delve currently supports two version of its API, APIv1 and APIv2. By default 71 a headless instance of `dlv` will serve APIv1 for backward-compatibility 72 with older clients, however new clients should use APIv2 as new features 73 will only be made available through version 2. The preferred method of 74 switching to APIv2 is to send the `RPCServer.SetApiVersion` command right 75 after connecting to the backend. 76 Alternatively the `--api-version=2` command line option can be used when 77 spawning the backend. 78 79 ## Diagnostics 80 81 Just like any other program, both Delve and your client have bugs. To help 82 with determining where the problem is you should log the exchange of 83 messages between Delve and your client somehow. 84 85 If you don't want to do this yourself you can also pass the options `--log 86 --log-output=rpc` to Delve. In fact the `--log-output` has many useful 87 values and you should expose it to users, if possible, so that we can 88 diagnose problems that are hard to reproduce. 89 90 ## Using RPCServer.Command 91 92 `Command` is probably the most important API entry point. It lets your 93 client stop (`Name == "halt"`) and resume (`Name == "continue"`) execution 94 of the inferior process. 95 96 The return value of `Command` is a `DebuggerState` object. If you lose the 97 DebuggerState object returned by your last call to `Command` you can ask for 98 a new copy with `RPCServer.State`. 99 100 ### Dealing with simultaneous breakpoints 101 102 Since Go is a programming language with a big emphasis on concurrency and 103 parallelism it's possible that multiple goroutines will stop at a breakpoint 104 simultaneously. This may at first seem incredibly unlikely but you must 105 understand that between the time a breakpoint is triggered and the point 106 where the debugger finishes stopping all threads of the inferior process 107 thousands of CPU instructions have to be executed, which make simultaneous 108 breakpoint triggering not that unlikely. 109 110 You should signal to your user *all* the breakpoints that occur after 111 executing a command, not just the first one. To do this iterate through the 112 `Threads` array in `DebuggerState` and note all the threads that have a non 113 nil `Breakpoint` member. 114 115 ### Special continue commands 116 117 In addition to "halt" and vanilla "continue" `Command` offers a few extra 118 flavours of continue that automatically set interesting temporary 119 breakpoints: "next" will continue until the next line of the program, 120 "stepout" will continue until the function returns, "step" is just like 121 "next" but it will step into function calls (but skip all calls to 122 unexported runtime functions). 123 124 All of "next", "step" and "stepout" operate on the selected goroutine. The 125 selected goroutine is described by the `SelectedGoroutine` field of 126 `DebuggerState`. Every time `Command` returns the selected goroutine will be 127 reset to the goroutine that triggered the breakpoint. 128 129 If multiple breakpoints are triggered simultaneously the selected goroutine 130 will be chosen randomly between the goroutines that are stopped at a 131 breakpoint. If a breakpoint is hit by a thread that is executing on the 132 system stack *there will be no selected goroutine*. If the "halt" command is 133 called *there may not be a selected goroutine*. 134 135 The selected goroutine can be changed using the "switchGoroutine" command. 136 If "switchGoroutine" is used to switch to a goroutine that's currently 137 parked SelectedGoroutine and CurrentThread will be mismatched. Always prefer 138 SelectedGoroutine over CurrentThread, you should ignore CurrentThread 139 entirely unless SelectedGoroutine is nil. 140 141 ### Special continue commands and asynchronous breakpoints 142 143 Because of the way go internals work it is not possible for a debugger to 144 resume a single goroutine. Therefore it's possible that after executing a 145 next/step/stepout a goroutine other than the goroutine the next/step/stepout 146 was executed on will hit a breakpoint. 147 148 If this happens Delve will return a DebuggerState with NextInProgress set to 149 true. When this happens your client has two options: 150 151 * 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"` 152 * You can abort the next/step/stepout operation using `RPCServer.CancelNext`. 153 154 It is important to note that while NextInProgress is true it is not possible 155 to call next/step/stepout again without using CancelNext first. There can 156 not be multiple next/step/stepout operations in progress at any time. 157 158 ### RPCServer.Command and stale executable files 159 160 It's possible (albeit unfortunate) that your user will decide to change the 161 source of the program being executed in the debugger, while the debugger is 162 running. Because of this it would be advisable that your client check that 163 the executable is not stale every time `Command` returns and notify the user 164 that the executable being run is stale and line numbers may nor align 165 properly anymore. 166 167 You can do this bookkeeping yourself, but Delve can also help you with the 168 `LastModified` call that returns the LastModified time of the executable 169 file when Delve started it. 170 171 ## Using RPCServer.CreateBreakpoint 172 173 The only two fields you probably want to fill of the Breakpoint argument of 174 CreateBreakpoint are File and Line. The file name should be the absolute 175 path to the file as the compiler saw it. 176 177 For example if the compiler saw this path: 178 179 ``` 180 /Users/you/go/src/something/something.go 181 ``` 182 183 But `/Users/you/go/src/something` is a symbolic link to 184 `/Users/you/projects/golang/something` the path *must* be specified as 185 `/Users/you/go/src/something/something.go` and 186 `/Users/you/projects/golang/something/something.go` will not be recognized 187 as valid. 188 189 If you want to let your users specify a breakpoint on a function selected 190 from a list of all functions you should specify the name of the function in 191 the FunctionName field of Breakpoint. 192 193 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) 194 you should call RPCServer.FindLocation and 195 then use the returned slice of Location objects to create Breakpoints to 196 pass to CreateBreakpoint: just fill each Breakpoint.Addr with the 197 contents of the corresponding Location.PC. 198 199 ## Looking into variables 200 201 There are several API entry points to evaluate variables in Delve: 202 203 * RPCServer.ListPackageVars returns all global variables in all packages 204 * PRCServer.ListLocalVars returns all local variables of a stack frame 205 * RPCServer.ListFunctionArgs returns all function arguments of a stack frame 206 * RPCServer.Eval evaluates an expression on a given stack frame 207 208 All those API calls take a LoadConfig argument. The LoadConfig specifies how 209 much of the variable's value should actually be loaded. Because of 210 LoadConfig a variable could be loaded incompletely, you should always notify 211 the user of this: 212 213 * 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. 214 * For maps the load is incomplete if: `Variable.Len > len(Variable.Children) / 2` 215 * For interfaces the load is incomplete if the only children has the onlyAddr attribute set to true. 216 217 ### Loading more of a Variable 218 219 You can also give the user an option to continue loading an incompletely 220 loaded variable. To load a struct that wasn't loaded automatically evaluate 221 the expression returned by: 222 223 ``` 224 fmt.Sprintf("*(*%q)(%#x)", v.Type, v.Addr) 225 ``` 226 227 where v is the variable that was truncated. 228 229 To load more elements from an array, slice or string: 230 231 ``` 232 fmt.Sprintf("(*(*%q)(%#x))[%d:]", v.Type, v.Addr, len(v.Children)) 233 ``` 234 235 To load more elements from a map: 236 237 ``` 238 fmt.Sprintf("(*(*%q)(%#x))[%d:]", v.Type, v.Addr, len(v.Children)/2) 239 ``` 240 241 All the evaluation API calls except ListPackageVars also take a EvalScope 242 argument, this specifies which stack frame you are interested in. If you 243 are interested in the topmost stack frame of the current goroutine (or 244 thread) use: `EvalScope{ GoroutineID: -1, Frame: 0 }`. 245 246 More information on the expression language interpreted by RPCServer.Eval 247 can be found [here](//github.com/go-delve/Delve/tree/master/Documentation/cli/expr.md). 248 249 ### Variable shadowing 250 251 Let's assume you are debugging a piece of code that looks like this: 252 253 ``` 254 for i := 0; i < N; i++ { 255 for i := 0; i < M; i++ { 256 f(i) // <-- debugger is stopped here 257 } 258 } 259 ``` 260 261 The response to a ListLocalVars request will list two variables named `i`, 262 because at that point in the code two variables named `i` exist and are in 263 scope. Only one (the innermost one), however, is visible to the user. The 264 other one is *shadowed*. 265 266 Delve will tell you which variable is shadowed through the `Flags` field of 267 the `Variable` object. If `Flags` has the `VariableShadowed` bit set then 268 the variable in question is shadowed. 269 270 Users of your client should be able to distinguish between shadowed and 271 non-shadowed variables. 272 273 ## Gracefully ending the debug session 274 275 To ensure that Delve cleans up after itself by deleting the `debug` or `debug.test` binary it creates 276 and killing any processes spawned by the program being debugged, the `Detach` command needs to be called. 277 In case you are disconnecting a running program, ensure to halt the program before trying to detach. 278 279 ## Testing the Client 280 281 A set of [example programs is 282 available](https://github.com/aarzilli/delve_client_testing) to test corner 283 cases in handling breakpoints and displaying data structures. Follow the 284 instructions in the README.txt file.