github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/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) | 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  ```