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