1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
|
# Introduction
Passing a file with the .star extension to the `source` command will cause delve to interpret it as a starlark script.
Starlark is a dialect of python, a [specification of its syntax can be found here](https://github.com/google/starlark-go/blob/master/doc/spec.md).
In addition to the normal starlark built-ins delve defines [a number of global functions](#Starlark-built-ins) that can be used to interact with the debugger.
After the file has been evaluated delve will bind any function starting with `command_` to a command-line command: for example `command_goroutines_wait_reason` will be bound to `goroutines_wait_reason`.
Then if a function named `main` exists it will be executed.
Global functions with a name that begins with a capital letter will be available to other scripts.
# Starlark built-ins
<!-- BEGIN MAPPING TABLE -->
Function | API Call
---------|---------
amend_breakpoint(Breakpoint) | Equivalent to API call [AmendBreakpoint](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.AmendBreakpoint)
ancestors(GoroutineID, NumAncestors, Depth) | Equivalent to API call [Ancestors](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.Ancestors)
attached_to_existing_process() | Equivalent to API call [AttachedToExistingProcess](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.AttachedToExistingProcess)
build_id() | Equivalent to API call [BuildID](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.BuildID)
cancel_next() | Equivalent to API call [CancelNext](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.CancelNext)
checkpoint(Where) | Equivalent to API call [Checkpoint](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.Checkpoint)
clear_breakpoint(Id, Name) | Equivalent to API call [ClearBreakpoint](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ClearBreakpoint)
clear_checkpoint(ID) | Equivalent to API call [ClearCheckpoint](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ClearCheckpoint)
raw_command(Name, ThreadID, GoroutineID, ReturnInfoLoadConfig, Expr, WithEvents, UnsafeCall) | Equivalent to API call [Command](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.Command)
create_breakpoint(Breakpoint, LocExpr, SubstitutePathRules, Suspended) | Equivalent to API call [CreateBreakpoint](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.CreateBreakpoint)
create_ebpf_tracepoint(FunctionName) | Equivalent to API call [CreateEBPFTracepoint](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.CreateEBPFTracepoint)
create_watchpoint(Scope, Expr, Type) | Equivalent to API call [CreateWatchpoint](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.CreateWatchpoint)
debug_info_directories(Set, List) | Equivalent to API call [DebugInfoDirectories](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.DebugInfoDirectories)
detach(Kill) | Equivalent to API call [Detach](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.Detach)
disassemble(Scope, StartPC, EndPC, Flavour) | Equivalent to API call [Disassemble](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.Disassemble)
dump_cancel() | Equivalent to API call [DumpCancel](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.DumpCancel)
dump_start(Destination) | Equivalent to API call [DumpStart](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.DumpStart)
dump_wait(Wait) | Equivalent to API call [DumpWait](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.DumpWait)
eval(Scope, Expr, Cfg) | Equivalent to API call [Eval](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.Eval)
examine_memory(Address, Length) | Equivalent to API call [ExamineMemory](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ExamineMemory)
find_location(Scope, Loc, IncludeNonExecutableLines, SubstitutePathRules) | Equivalent to API call [FindLocation](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.FindLocation)
follow_exec(Enable, Regex) | Equivalent to API call [FollowExec](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.FollowExec)
follow_exec_enabled() | Equivalent to API call [FollowExecEnabled](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.FollowExecEnabled)
function_return_locations(FnName) | Equivalent to API call [FunctionReturnLocations](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.FunctionReturnLocations)
get_breakpoint(Id, Name) | Equivalent to API call [GetBreakpoint](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.GetBreakpoint)
get_buffered_tracepoints() | Equivalent to API call [GetBufferedTracepoints](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.GetBufferedTracepoints)
get_thread(Id) | Equivalent to API call [GetThread](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.GetThread)
guess_substitute_path(Args) | Equivalent to API call [GuessSubstitutePath](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.GuessSubstitutePath)
is_multiclient() | Equivalent to API call [IsMulticlient](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.IsMulticlient)
last_modified() | Equivalent to API call [LastModified](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.LastModified)
breakpoints(All) | Equivalent to API call [ListBreakpoints](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListBreakpoints)
checkpoints() | Equivalent to API call [ListCheckpoints](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListCheckpoints)
dynamic_libraries() | Equivalent to API call [ListDynamicLibraries](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListDynamicLibraries)
function_args(Scope, Cfg) | Equivalent to API call [ListFunctionArgs](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListFunctionArgs)
functions(Filter, FollowCalls) | Equivalent to API call [ListFunctions](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListFunctions)
goroutines(Start, Count, Filters, GoroutineGroupingOptions, EvalScope) | Equivalent to API call [ListGoroutines](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListGoroutines)
local_vars(Scope, Cfg) | Equivalent to API call [ListLocalVars](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListLocalVars)
package_vars(Filter, Cfg) | Equivalent to API call [ListPackageVars](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackageVars)
packages_build_info(IncludeFiles, Filter) | Equivalent to API call [ListPackagesBuildInfo](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackagesBuildInfo)
registers(ThreadID, IncludeFp, Scope) | Equivalent to API call [ListRegisters](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListRegisters)
sources(Filter) | Equivalent to API call [ListSources](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListSources)
targets() | Equivalent to API call [ListTargets](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListTargets)
threads() | Equivalent to API call [ListThreads](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListThreads)
types(Filter) | Equivalent to API call [ListTypes](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ListTypes)
process_pid() | Equivalent to API call [ProcessPid](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ProcessPid)
recorded() | Equivalent to API call [Recorded](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.Recorded)
restart(Position, ResetArgs, NewArgs, Rerecord, Rebuild, NewRedirects) | Equivalent to API call [Restart](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.Restart)
set_expr(Scope, Symbol, Value) | Equivalent to API call [Set](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.Set)
stacktrace(Id, Depth, Full, Defers, Opts, Cfg) | Equivalent to API call [Stacktrace](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.Stacktrace)
state(NonBlocking) | Equivalent to API call [State](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.State)
toggle_breakpoint(Id, Name) | Equivalent to API call [ToggleBreakpoint](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.ToggleBreakpoint)
dlv_command(command) | Executes the specified command as if typed at the dlv_prompt
read_file(path) | Reads the file as a string
write_file(path, contents) | Writes string to a file
cur_scope() | Returns the current evaluation scope
default_load_config() | Returns the current default load configuration
<!-- END MAPPING TABLE -->
In addition to these built-ins, the [time](https://pkg.go.dev/go.starlark.net/lib/time#pkg-variables) library from the starlark-go project is also available to scripts.
## Should I use raw_command or dlv_command?
There are two ways to resume the execution of the target program:
raw_command("continue")
dlv_command("continue")
The first one maps to the API call [Command](https://pkg.go.dev/github.com/go-delve/delve/service/rpc2#RPCServer.Command). As such all the caveats explained in the [Client HowTo](../api/ClientHowto.md).
The latter is equivalent to typing `continue` to the `(dlv)` command line and should do what you expect.
In general `dlv_command("continue")` should be preferred, unless the behavior you wish to produces diverges significantly from that of the command line's `continue`.
# Creating new commands
Any global function with a name starting with `command_` will be made available as a command line command. If the function has a single argument named `args` all arguments passed on the command line will be passed to the function as a single string.
Otherwise arguments passed on the command line are interpreted as starlark expressions. See the [expression arguments](#expression-arguments) example.
If the command function has a doc string it will be used as a help message.
## Using custom commands with the `on` prefix
All custom starlark commands can be used with the `on` command (see `help on`) to execute when a breakpoint is hit:
```python
def command_my_trace(args):
"""Custom trace command that executes when a breakpoint is hit."""
print("Tracing:", args)
```
You can use this command like this:
```
(dlv) break main.go:10
(dlv) on 1 my_trace
```
# Working with variables
Variables of the target program can be accessed using `local_vars`, `function_args` or the `eval` functions. Each variable will be returned as a [Variable](https://pkg.go.dev/github.com/go-delve/delve/service/api#Variable) struct, with one special field: `Value`.
As a convenience a special global object exists, called `tgt`: evaluating `tgt.varname` is equivalent to evaluating `eval(None, "varname").Variable.Value`.
## Variable.Value
The `Value` field will return the value of the target variable converted to a starlark value:
* integers, floating point numbers and strings are represented by equivalent starlark values
* structs are represented as starlark dictionaries
* slices and arrays are represented by starlark lists
* maps are represented by starlark dicts
* pointers and interfaces are represented by a one-element starlark list containing the value they point to
For example, given this variable in the target program:
```go
type astruct struct {
A int
B int
}
s2 := []astruct{{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}}
```
The following is possible:
```
>>> s2 = eval(None, "s2").Variable
>>> s2.Value[0] # access of a slice item by index
main.astruct {A: 1, B: 2}
>>> a = s2.Value[1]
>>> a.Value.A # access to a struct field
3
>>> a.Value.A + 10 # arithmetic on the value of s2[1].X
13
>>> a.Value["B"] # access to a struct field, using dictionary syntax
4
```
For more examples see the [linked list example](#Print-all-elements-of-a-linked-list) below.
# Examples
## Listing goroutines and making custom commands
Create a `goroutine_start_line` command that prints the starting line of each goroutine, sets `gsl` as an alias:
```python
def command_goroutine_start_line(args):
gs = goroutines().Goroutines
for g in gs:
line = read_file(g.StartLoc.File).splitlines()[g.StartLoc.Line-1].strip()
print(g.ID, "\t", g.StartLoc.File + ":" + str(g.StartLoc.Line), "\t", line)
def main():
dlv_command("config alias goroutine_start_line gsl")
```
Use it like this:
```
(dlv) source goroutine_start_line.star
(dlv) goroutine_start_line
1 /usr/local/go/src/runtime/proc.go:110 func main() {
2 /usr/local/go/src/runtime/proc.go:242 func forcegchelper() {
17 /usr/local/go/src/runtime/mgcsweep.go:64 func bgsweep(c chan int) {
18 /usr/local/go/src/runtime/mfinal.go:161 func runfinq() {
(dlv) gsl
1 /usr/local/go/src/runtime/proc.go:110 func main() {
2 /usr/local/go/src/runtime/proc.go:242 func forcegchelper() {
17 /usr/local/go/src/runtime/mgcsweep.go:64 func bgsweep(c chan int) {
18 /usr/local/go/src/runtime/mfinal.go:161 func runfinq() {
```
## Expression arguments
After evaluating this script:
```python
def command_echo(args):
print(args)
def command_echo_expr(a, b, c):
print("a", a, "b", b, "c", c)
```
The first command, `echo`, takes its arguments as a single string, while for `echo_expr` it will be possible to pass starlark expression as arguments:
```
(dlv) echo 2+2, 2-1, 2*3
"2+2, 2-1, 2*3"
(dlv) echo_expr 2+2, 2-1, 2*3
a 4 b 1 c 6
```
## Creating breakpoints
Set a breakpoint on all private methods of package `main`:
```python
def main():
for f in functions().Funcs:
v = f.split('.')
if len(v) != 2:
continue
if v[0] != "main":
continue
if v[1][0] >= 'a' and v[1][0] <= 'z':
create_breakpoint({ "FunctionName": f, "Line": -1 }) # see documentation of RPCServer.CreateBreakpoint
```
## Switching goroutines
Create a command, `switch_to_main_goroutine`, that searches for a goroutine running a function in the main package and switches to it:
```python
def command_switch_to_main_goroutine(args):
for g in goroutines().Goroutines:
if g.currentLoc.function != None and g.currentLoc.function.name.startswith("main."):
print("switching to:", g.id)
raw_command("switchGoroutine", GoroutineID=g.id)
break
```
## Listing goroutines
Create a command, "goexcl", that lists all goroutines excluding the ones stopped on a specified function.
```python
def command_goexcl(args):
"""Prints all goroutines not stopped in the function passed as argument."""
excluded = 0
start = 0
while start >= 0:
gr = goroutines(start, 10)
start = gr.Nextg
for g in gr.Goroutines:
fn = g.UserCurrentLoc.Function
if fn == None:
print("Goroutine", g.ID, "User:", g.UserCurrentLoc.File, g.UserCurrentLoc.Line)
elif fn.Name_ != args:
print("Goroutine", g.ID, "User:", g.UserCurrentLoc.File, g.UserCurrentLoc.Line, fn.Name_)
else:
excluded = excluded + 1
print("Excluded", excluded, "goroutines")
```
Usage:
```
(dlv) goexcl main.somefunc
```
prints all goroutines that are not stopped inside `main.somefunc`.
## Repeatedly executing the target until a breakpoint is hit.
Repeatedly call continue and restart until the target hits a breakpoint.
```python
def command_flaky(args):
"Repeatedly runs program until a breakpoint is hit"
while True:
if dlv_command("continue") == None:
break
dlv_command("restart")
```
## Print all elements of a linked list
```python
def command_linked_list(args):
"""Prints the contents of a linked list.
linked_list <var_name> <next_field_name> <max_depth>
Prints up to max_depth elements of the linked list variable 'var_name' using 'next_field_name' as the name of the link field.
"""
var_name, next_field_name, max_depth = args.split(" ")
max_depth = int(max_depth)
next_name = var_name
v = eval(None, var_name).Variable.Value
for i in range(0, max_depth):
print(str(i)+":",v)
if v[0] == None:
break
v = v[next_field_name]
```
## Find an array element matching a predicate
```python
def command_find_array(arr, pred):
"""Calls pred for each element of the array or slice 'arr' returns the index of the first element for which pred returns true.
find_arr <arr> <pred>
Example use (find the first element of slice 's2' with field A equal to 5):
find_arr "s2", lambda x: x.A == 5
"""
arrv = eval(None, arr).Variable
for i in range(0, arrv.Len):
v = arrv.Value[i]
if pred(v):
print("found", i)
return
print("not found")
```
## Rerunning a program until it fails or hits a breakpoint
```python
def command_flaky(args):
"Continues and restarts the target program repeatedly (re-recording it on the rr backend), until a breakpoint is hit"
count = 1
while True:
if dlv_command("continue") == None:
break
print("restarting", count, "...")
count = count+1
restart(Rerecord=True)
```
## Passing a struct as an argument
Struct literals can be passed to built-ins as Starlark dictionaries. For example, the following snippet passes
in an [api.EvalScope](https://pkg.go.dev/github.com/go-delve/delve/service/api#EvalScope)
and [api.LoadConfig](https://pkg.go.dev/github.com/go-delve/delve/service/api#LoadConfig)
to the `eval` built-in. `None` can be passed for optional arguments, and
trailing optional arguments can be elided completely.
```python
var = eval(
{"GoroutineID": 42, "Frame": 5},
"myVar",
{"FollowPointers":True, "MaxVariableRecurse":2, "MaxStringLen":100, "MaxArrayValues":10, "MaxStructFields":100}
)
```
## Chain breakpoints
Chain a number of breakpoints such that breakpoint n+1 is only hit after breakpoint n is hit:
```python
def command_breakchain(*args):
v = args.split(" ")
bp = get_breakpoint(int(v[0]), "").Breakpoint
bp.HitCond = "== 1"
amend_breakpoint(bp)
for i in range(1, len(v)):
bp = get_breakpoint(int(v[i]), "").Breakpoint
if i != len(v)-1:
bp.HitCond = "== 1"
bp.Cond = "delve.bphitcount[" + v[i-1] + "] > 0"
amend_breakpoint(bp)
```
To be used as `chain 1 2 3` where `1`, `2`, and `3` are IDs of breakpoints to chain together.
|