gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/terminal/command.go (about)

     1  // Package terminal implements functions for responding to user
     2  // input and dispatching to appropriate backend commands.
     3  package terminal
     4  
     5  //lint:file-ignore ST1005 errors here can be capitalized
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"errors"
    11  	"fmt"
    12  	"go/parser"
    13  	"go/scanner"
    14  	"io"
    15  	"math"
    16  	"os"
    17  	"os/exec"
    18  	"path/filepath"
    19  	"reflect"
    20  	"regexp"
    21  	"runtime"
    22  	"sort"
    23  	"strconv"
    24  	"strings"
    25  	"text/tabwriter"
    26  	"time"
    27  
    28  	"github.com/cosiner/argv"
    29  	"gitlab.com/Raven-IO/raven-delve/pkg/config"
    30  	"gitlab.com/Raven-IO/raven-delve/pkg/locspec"
    31  	"gitlab.com/Raven-IO/raven-delve/pkg/proc/debuginfod"
    32  	"gitlab.com/Raven-IO/raven-delve/service"
    33  	"gitlab.com/Raven-IO/raven-delve/service/api"
    34  	"gitlab.com/Raven-IO/raven-delve/service/rpc2"
    35  )
    36  
    37  const optimizedFunctionWarning = "Warning: debugging optimized function"
    38  
    39  type cmdPrefix int
    40  
    41  const (
    42  	noPrefix = cmdPrefix(0)
    43  	onPrefix = cmdPrefix(1 << iota)
    44  	deferredPrefix
    45  	revPrefix
    46  )
    47  
    48  type callContext struct {
    49  	Prefix     cmdPrefix
    50  	Scope      api.EvalScope
    51  	Breakpoint *api.Breakpoint
    52  }
    53  
    54  func (ctx *callContext) scoped() bool {
    55  	return ctx.Scope.GoroutineID >= 0 || ctx.Scope.Frame > 0
    56  }
    57  
    58  type frameDirection int
    59  
    60  const (
    61  	frameSet frameDirection = iota
    62  	frameUp
    63  	frameDown
    64  )
    65  
    66  type cmdfunc func(t *Term, ctx callContext, args string) error
    67  
    68  type command struct {
    69  	aliases         []string
    70  	builtinAliases  []string
    71  	group           commandGroup
    72  	allowedPrefixes cmdPrefix
    73  	helpMsg         string
    74  	cmdFn           cmdfunc
    75  }
    76  
    77  // Returns true if the command string matches one of the aliases for this command
    78  func (c command) match(cmdstr string) bool {
    79  	for _, v := range c.aliases {
    80  		if v == cmdstr {
    81  			return true
    82  		}
    83  	}
    84  	return false
    85  }
    86  
    87  // Commands represents the commands for Delve terminal process.
    88  type Commands struct {
    89  	cmds   []command
    90  	client service.Client
    91  	frame  int // Current frame as set by frame/up/down commands.
    92  }
    93  
    94  var (
    95  	// longLoadConfig loads more information:
    96  	// * Follows pointers
    97  	// * Loads more array values
    98  	// * Does not limit struct fields
    99  	longLoadConfig = api.LoadConfig{FollowPointers: true, MaxVariableRecurse: 1, MaxStringLen: 64, MaxArrayValues: 64, MaxStructFields: -1}
   100  	// ShortLoadConfig loads less information, not following pointers
   101  	// and limiting struct fields loaded to 3.
   102  	ShortLoadConfig = api.LoadConfig{MaxStringLen: 64, MaxStructFields: 3}
   103  )
   104  
   105  // byFirstAlias will sort by the first
   106  // alias of a command.
   107  type byFirstAlias []command
   108  
   109  func (a byFirstAlias) Len() int           { return len(a) }
   110  func (a byFirstAlias) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   111  func (a byFirstAlias) Less(i, j int) bool { return a[i].aliases[0] < a[j].aliases[0] }
   112  
   113  // DebugCommands returns a Commands struct with default commands defined.
   114  func DebugCommands(client service.Client) *Commands {
   115  	c := &Commands{client: client}
   116  
   117  	c.cmds = []command{
   118  		{aliases: []string{"help", "h"}, cmdFn: c.help, helpMsg: `Prints the help message.
   119  
   120  	help [command]
   121  
   122  Type "help" followed by the name of a command for more information about it.`},
   123  		{aliases: []string{"break", "b"}, group: breakCmds, cmdFn: breakpoint, helpMsg: `Sets a breakpoint.
   124  
   125  	break [name] [locspec] [if <condition>]
   126  
   127  Locspec is a location specifier in the form of:
   128  
   129    * *<address> Specifies the location of memory address address. address can be specified as a decimal, hexadecimal or octal number
   130    * <filename>:<line> Specifies the line in filename. filename can be the partial path to a file or even just the base name as long as the expression remains unambiguous.
   131    * <line> Specifies the line in the current file
   132    * +<offset> Specifies the line offset lines after the current one
   133    * -<offset> Specifies the line offset lines before the current one
   134    * <function>[:<line>] Specifies the line inside function.
   135        The full syntax for function is <package>.(*<receiver type>).<function name> however the only required element is the function name,
   136        everything else can be omitted as long as the expression remains unambiguous. For setting a breakpoint on an init function (ex: main.init),
   137        the <filename>:<line> syntax should be used to break in the correct init function at the correct location.
   138    * /<regex>/ Specifies the location of all the functions matching regex
   139  
   140  If locspec is omitted a breakpoint will be set on the current line.
   141  
   142  If you would like to assign a name to the breakpoint you can do so with the form:
   143  
   144    break mybpname main.go:4
   145  
   146  Finally, you can assign a condition to the newly created breakpoint by using the 'if' postfix form, like so:
   147  
   148    break main.go:55 if i == 5
   149  
   150  Alternatively you can set a condition on a breakpoint after created by using the 'on' command.
   151  
   152  See also: "help on", "help cond" and "help clear"`},
   153  		{aliases: []string{"trace", "t"}, group: breakCmds, cmdFn: tracepoint, allowedPrefixes: onPrefix, helpMsg: `Set tracepoint.
   154  
   155  	trace [name] [locspec]
   156  
   157  A tracepoint is a breakpoint that does not stop the execution of the program, instead when the tracepoint is hit a notification is displayed. See Documentation/cli/locspec.md for the syntax of locspec. If locspec is omitted a tracepoint will be set on the current line.
   158  
   159  See also: "help on", "help cond" and "help clear"`},
   160  		{aliases: []string{"watch"}, group: breakCmds, cmdFn: watchpoint, helpMsg: `Set watchpoint.
   161  	
   162  	watch [-r|-w|-rw] <expr>
   163  	
   164  	-r	stops when the memory location is read
   165  	-w	stops when the memory location is written
   166  	-rw	stops when the memory location is read or written
   167  
   168  The memory location is specified with the same expression language used by 'print', for example:
   169  
   170  	watch v
   171  	watch -w *(*int)(0x1400007c018)
   172  
   173  will watch the address of variable 'v' and writes to an int at addr '0x1400007c018'.
   174  
   175  Note that writes that do not change the value of the watched memory address might not be reported.
   176  
   177  See also: "help print".`},
   178  		{aliases: []string{"restart", "r"}, group: runCmds, cmdFn: restart, helpMsg: `Restart process.
   179  
   180  For recorded targets the command takes the following forms:
   181  
   182  	restart					resets to the start of the recording
   183  	restart [checkpoint]			resets the recording to the given checkpoint
   184  	restart -r [newargv...]	[redirects...]	re-records the target process
   185  	
   186  For live targets the command takes the following forms:
   187  
   188  	restart [newargv...] [redirects...]	restarts the process
   189  
   190  If newargv is omitted the process is restarted (or re-recorded) with the same argument vector.
   191  If -noargs is specified instead, the argument vector is cleared.
   192  
   193  A list of file redirections can be specified after the new argument list to override the redirections defined using the '--redirect' command line option. A syntax similar to Unix shells is used:
   194  
   195  	<input.txt	redirects the standard input of the target process from input.txt
   196  	>output.txt	redirects the standard output of the target process to output.txt
   197  	2>error.txt	redirects the standard error of the target process to error.txt
   198  `},
   199  		{aliases: []string{"rebuild"}, group: runCmds, cmdFn: c.rebuild, allowedPrefixes: revPrefix, helpMsg: "Rebuild the target executable and restarts it. It does not work if the executable was not built by delve."},
   200  		{aliases: []string{"continue", "c"}, group: runCmds, cmdFn: c.cont, allowedPrefixes: revPrefix, helpMsg: `Run until breakpoint or program termination.
   201  
   202  	continue [<locspec>]
   203  
   204  Optional locspec argument allows you to continue until a specific location is reached. The program will halt if a breakpoint is hit before reaching the specified location.
   205  
   206  For example:
   207  
   208  	continue main.main
   209  	continue encoding/json.Marshal
   210  `},
   211  		{aliases: []string{"step", "s"}, group: runCmds, cmdFn: c.step, allowedPrefixes: revPrefix, helpMsg: "Single step through program."},
   212  		{aliases: []string{"step-instruction", "si", "stepi"}, group: runCmds, allowedPrefixes: revPrefix, cmdFn: c.stepInstruction, helpMsg: "Single step a single cpu instruction."},
   213  		{aliases: []string{"next-instruction", "ni", "nexti"}, group: runCmds, allowedPrefixes: revPrefix, cmdFn: c.nextInstruction, helpMsg: "Single step a single cpu instruction, skipping function calls."},
   214  		{aliases: []string{"next", "n"}, group: runCmds, cmdFn: c.next, allowedPrefixes: revPrefix, helpMsg: `Step over to next source line.
   215  
   216  	next [count]
   217  
   218  Optional [count] argument allows you to skip multiple lines.
   219  `},
   220  		{aliases: []string{"stepout", "so"}, group: runCmds, allowedPrefixes: revPrefix, cmdFn: c.stepout, helpMsg: "Step out of the current function."},
   221  		{aliases: []string{"call"}, group: runCmds, cmdFn: c.call, helpMsg: `Resumes process, injecting a function call (EXPERIMENTAL!!!)
   222  	
   223  	call [-unsafe] <function call expression>
   224  	
   225  Current limitations:
   226  - only pointers to stack-allocated objects can be passed as argument.
   227  - only some automatic type conversions are supported.
   228  - functions can only be called on running goroutines that are not
   229    executing the runtime.
   230  - the current goroutine needs to have at least 256 bytes of free space on
   231    the stack.
   232  - functions can only be called when the goroutine is stopped at a safe
   233    point.
   234  - calling a function will resume execution of all goroutines.
   235  - only supported on linux's native backend.
   236  `},
   237  		{aliases: []string{"threads"}, group: goroutineCmds, cmdFn: threads, helpMsg: "Print out info for every traced thread."},
   238  		{aliases: []string{"thread", "tr"}, group: goroutineCmds, cmdFn: thread, helpMsg: `Switch to the specified thread.
   239  
   240  	thread <id>`},
   241  		{aliases: []string{"clear"}, group: breakCmds, cmdFn: clear, helpMsg: `Deletes breakpoint.
   242  
   243  	clear <breakpoint name or id>`},
   244  		{aliases: []string{"clearall"}, group: breakCmds, cmdFn: clearAll, helpMsg: `Deletes multiple breakpoints.
   245  
   246  	clearall [<locspec>]
   247  
   248  If called with the locspec argument it will delete all the breakpoints matching the locspec. If locspec is omitted all breakpoints are deleted.`},
   249  		{aliases: []string{"toggle"}, group: breakCmds, cmdFn: toggle, helpMsg: `Toggles on or off a breakpoint.
   250  
   251  toggle <breakpoint name or id>`},
   252  		{aliases: []string{"goroutines", "grs"}, group: goroutineCmds, cmdFn: c.goroutines, helpMsg: `List program goroutines.
   253  
   254  	goroutines [-u|-r|-g|-s] [-t [depth]] [-l] [-with loc expr] [-without loc expr] [-group argument] [-chan expr] [-exec command]
   255  
   256  Print out info for every goroutine. The flag controls what information is shown along with each goroutine:
   257  
   258  	-u	displays location of topmost stackframe in user code (default)
   259  	-r	displays location of topmost stackframe (including frames inside private runtime functions)
   260  	-g	displays location of go instruction that created the goroutine
   261  	-s	displays location of the start function
   262  	-t	displays goroutine's stacktrace (an optional depth value can be specified, default: 10)
   263  	-l	displays goroutine's labels
   264  
   265  If no flag is specified the default is -u, i.e. the first frame within the first 30 frames that is not executing a runtime private function.
   266  
   267  FILTERING
   268  
   269  If -with or -without are specified only goroutines that match the given condition are returned.
   270  
   271  To only display goroutines where the specified location contains (or does not contain, for -without and -wo) expr as a substring, use:
   272  
   273  	goroutines -with (userloc|curloc|goloc|startloc) expr
   274  	goroutines -w (userloc|curloc|goloc|startloc) expr
   275  	goroutines -without (userloc|curloc|goloc|startloc) expr
   276  	goroutines -wo (userloc|curloc|goloc|startloc) expr
   277  
   278  	Where:
   279  	userloc: filter by the location of the topmost stackframe in user code
   280  	curloc: filter by the location of the topmost stackframe (including frames inside private runtime functions)
   281  	goloc: filter by the location of the go instruction that created the goroutine
   282  	startloc: filter by the location of the start function
   283  	
   284  To only display goroutines that have (or do not have) the specified label key and value, use:
   285  
   286  	goroutines -with label key=value
   287  	goroutines -without label key=value
   288  	
   289  To only display goroutines that have (or do not have) the specified label key, use:
   290  
   291  	goroutines -with label key
   292  	goroutines -without label key
   293  	
   294  To only display goroutines that are running (or are not running) on a OS thread, use:
   295  
   296  
   297  	goroutines -with running
   298  	goroutines -without running
   299  	
   300  To only display user (or runtime) goroutines, use:
   301  
   302  	goroutines -with user
   303  	goroutines -without user
   304  
   305  CHANNELS
   306  	
   307  To only show goroutines waiting to send to or receive from a specific channel use:
   308  
   309  	goroutines -chan expr
   310  	
   311  Note that 'expr' must not contain spaces.
   312  
   313  GROUPING
   314  
   315  	goroutines -group (userloc|curloc|goloc|startloc|running|user)
   316  
   317  	Where:
   318  	userloc: groups goroutines by the location of the topmost stackframe in user code
   319  	curloc: groups goroutines by the location of the topmost stackframe
   320  	goloc: groups goroutines by the location of the go instruction that created the goroutine
   321  	startloc: groups goroutines by the location of the start function
   322  	running: groups goroutines by whether they are running or not
   323  	user: groups goroutines by weather they are user or runtime goroutines
   324  
   325  
   326  Groups goroutines by the given location, running status or user classification, up to 5 goroutines per group will be displayed as well as the total number of goroutines in the group.
   327  
   328  	goroutines -group label key
   329  
   330  Groups goroutines by the value of the label with the specified key.
   331  
   332  EXEC
   333  
   334  	goroutines -exec <command>
   335  
   336  Runs the command on every goroutine.
   337  `},
   338  		{aliases: []string{"goroutine", "gr"}, group: goroutineCmds, allowedPrefixes: onPrefix, cmdFn: c.goroutine, helpMsg: `Shows or changes current goroutine
   339  
   340  	goroutine
   341  	goroutine <id>
   342  	goroutine <id> <command>
   343  
   344  Called without arguments it will show information about the current goroutine.
   345  Called with a single argument it will switch to the specified goroutine.
   346  Called with more arguments it will execute a command on the specified goroutine.`},
   347  		{aliases: []string{"breakpoints", "bp"}, group: breakCmds, cmdFn: breakpoints, helpMsg: `Print out info for active breakpoints.
   348  	
   349  	breakpoints [-a]
   350  
   351  Specifying -a prints all physical breakpoint, including internal breakpoints.`},
   352  		{aliases: []string{"print", "p"}, group: dataCmds, allowedPrefixes: onPrefix | deferredPrefix, cmdFn: c.printVar, helpMsg: `Evaluate an expression.
   353  
   354  	[goroutine <n>] [frame <m>] print [%format] <expression>
   355  
   356  See Documentation/cli/expr.md for a description of supported expressions.
   357  
   358  The optional format argument is a format specifier, like the ones used by the fmt package. For example "print %x v" will print v as an hexadecimal number.`},
   359  		{aliases: []string{"whatis"}, group: dataCmds, cmdFn: whatisCommand, helpMsg: `Prints type of an expression.
   360  
   361  	whatis <expression>`},
   362  		{aliases: []string{"set"}, group: dataCmds, cmdFn: setVar, helpMsg: `Changes the value of a variable.
   363  
   364  	[goroutine <n>] [frame <m>] set <variable> = <value>
   365  
   366  See Documentation/cli/expr.md for a description of supported expressions. Only numerical variables and pointers can be changed.`},
   367  		{aliases: []string{"sources"}, cmdFn: sources, helpMsg: `Print list of source files.
   368  
   369  	sources [<regex>]
   370  
   371  If regex is specified only the source files matching it will be returned.`},
   372  		{aliases: []string{"funcs"}, cmdFn: funcs, helpMsg: `Print list of functions.
   373  
   374  	funcs [<regex>]
   375  
   376  If regex is specified only the functions matching it will be returned.`},
   377  		{aliases: []string{"types"}, cmdFn: types, helpMsg: `Print list of types
   378  
   379  	types [<regex>]
   380  
   381  If regex is specified only the types matching it will be returned.`},
   382  		{aliases: []string{"packages"}, cmdFn: packages, helpMsg: `Print list of packages.
   383  
   384  	packages [<regex>]
   385  
   386  If regex is specified only the packages matching it will be returned.`},
   387  		{aliases: []string{"args"}, allowedPrefixes: onPrefix | deferredPrefix, group: dataCmds, cmdFn: args, helpMsg: `Print function arguments.
   388  
   389  	[goroutine <n>] [frame <m>] args [-v] [<regex>]
   390  
   391  If regex is specified only function arguments with a name matching it will be returned. If -v is specified more information about each function argument will be shown.`},
   392  		{aliases: []string{"locals"}, allowedPrefixes: onPrefix | deferredPrefix, group: dataCmds, cmdFn: locals, helpMsg: `Print local variables.
   393  
   394  	[goroutine <n>] [frame <m>] locals [-v] [<regex>]
   395  
   396  The name of variables that are shadowed in the current scope will be shown in parenthesis.
   397  
   398  If regex is specified only local variables with a name matching it will be returned. If -v is specified more information about each local variable will be shown.`},
   399  		{aliases: []string{"vars"}, cmdFn: vars, group: dataCmds, helpMsg: `Print package variables.
   400  
   401  	vars [-v] [<regex>]
   402  
   403  If regex is specified only package variables with a name matching it will be returned. If -v is specified more information about each package variable will be shown.`},
   404  		{aliases: []string{"regs"}, cmdFn: regs, group: dataCmds, helpMsg: `Print contents of CPU registers.
   405  
   406  	regs [-a]
   407  
   408  Argument -a shows more registers. Individual registers can also be displayed by 'print' and 'display'. See Documentation/cli/expr.md.`},
   409  		{aliases: []string{"exit", "quit", "q"}, cmdFn: exitCommand, helpMsg: `Exit the debugger.
   410  		
   411  	exit [-c]
   412  	
   413  When connected to a headless instance started with the --accept-multiclient, pass -c to resume the execution of the target process before disconnecting.`},
   414  		{aliases: []string{"list", "ls", "l"}, cmdFn: listCommand, helpMsg: `Show source code.
   415  
   416  	[goroutine <n>] [frame <m>] list [<locspec>]
   417  
   418  Show source around current point or provided locspec.
   419  
   420  For example:
   421  
   422  	frame 1 list 69
   423  	list testvariables.go:10000
   424  	list main.main:30
   425  	list 40`},
   426  		{aliases: []string{"stack", "bt"}, allowedPrefixes: onPrefix, group: stackCmds, cmdFn: stackCommand, helpMsg: `Print stack trace.
   427  
   428  	[goroutine <n>] [frame <m>] stack [<depth>] [-full] [-offsets] [-defer] [-a <n>] [-adepth <depth>] [-mode <mode>]
   429  
   430  	-full		every stackframe is decorated with the value of its local variables and arguments.
   431  	-offsets	prints frame offset of each frame.
   432  	-defer		prints deferred function call stack for each frame.
   433  	-a <n>		prints stacktrace of n ancestors of the selected goroutine (target process must have tracebackancestors enabled)
   434  	-adepth <depth>	configures depth of ancestor stacktrace
   435  	-mode <mode>	specifies the stacktrace mode, possible values are:
   436  			normal	- attempts to automatically switch between cgo frames and go frames
   437  			simple	- disables automatic switch between cgo and go
   438  			fromg	- starts from the registers stored in the runtime.g struct
   439  `},
   440  		{aliases: []string{"frame"},
   441  			group: stackCmds,
   442  			cmdFn: func(t *Term, ctx callContext, arg string) error {
   443  				return c.frameCommand(t, ctx, arg, frameSet)
   444  			},
   445  			helpMsg: `Set the current frame, or execute command on a different frame.
   446  
   447  	frame <m>
   448  	frame <m> <command>
   449  
   450  The first form sets frame used by subsequent commands such as "print" or "set".
   451  The second form runs the command on the given frame.`},
   452  		{aliases: []string{"up"},
   453  			group: stackCmds,
   454  			cmdFn: func(t *Term, ctx callContext, arg string) error {
   455  				return c.frameCommand(t, ctx, arg, frameUp)
   456  			},
   457  			helpMsg: `Move the current frame up.
   458  
   459  	up [<m>]
   460  	up [<m>] <command>
   461  
   462  Move the current frame up by <m>. The second form runs the command on the given frame.`},
   463  		{aliases: []string{"down"},
   464  			group: stackCmds,
   465  			cmdFn: func(t *Term, ctx callContext, arg string) error {
   466  				return c.frameCommand(t, ctx, arg, frameDown)
   467  			},
   468  			helpMsg: `Move the current frame down.
   469  
   470  	down [<m>]
   471  	down [<m>] <command>
   472  
   473  Move the current frame down by <m>. The second form runs the command on the given frame.`},
   474  		{aliases: []string{"deferred"}, group: stackCmds, cmdFn: c.deferredCommand, helpMsg: `Executes command in the context of a deferred call.
   475  
   476  	deferred <n> <command>
   477  
   478  Executes the specified command (print, args, locals) in the context of the n-th deferred call in the current frame.`},
   479  		{aliases: []string{"source"}, cmdFn: c.sourceCommand, helpMsg: `Executes a file containing a list of delve commands
   480  
   481  	source <path>
   482  	
   483  If path ends with the .star extension it will be interpreted as a starlark script. See Documentation/cli/starlark.md for the syntax.
   484  
   485  If path is a single '-' character an interactive starlark interpreter will start instead. Type 'exit' to exit.`},
   486  		{aliases: []string{"disassemble", "disass"}, cmdFn: disassCommand, helpMsg: `Disassembler.
   487  
   488  	[goroutine <n>] [frame <m>] disassemble [-a <start> <end>] [-l <locspec>]
   489  
   490  If no argument is specified the function being executed in the selected stack frame will be executed.
   491  
   492  	-a <start> <end>	disassembles the specified address range
   493  	-l <locspec>		disassembles the specified function`},
   494  		{aliases: []string{"on"}, group: breakCmds, cmdFn: c.onCmd, helpMsg: `Executes a command when a breakpoint is hit.
   495  
   496  	on <breakpoint name or id> <command>
   497  	on <breakpoint name or id> -edit
   498  	
   499  
   500  Supported commands: print, stack, goroutine, trace and cond. 
   501  To convert a breakpoint into a tracepoint use:
   502  	
   503  	on <breakpoint name or id> trace
   504  
   505  The command 'on <bp> cond <cond-arguments>' is equivalent to 'cond <bp> <cond-arguments>'.
   506  
   507  The command 'on x -edit' can be used to edit the list of commands executed when the breakpoint is hit.`},
   508  		{aliases: []string{"condition", "cond"}, group: breakCmds, cmdFn: conditionCmd, allowedPrefixes: onPrefix, helpMsg: `Set breakpoint condition.
   509  
   510  	condition <breakpoint name or id> <boolean expression>.
   511  	condition -hitcount <breakpoint name or id> <operator> <argument>.
   512  	condition -per-g-hitcount <breakpoint name or id> <operator> <argument>.
   513  	condition -clear <breakpoint name or id>.
   514  
   515  Specifies that the breakpoint, tracepoint or watchpoint should break only if the boolean expression is true.
   516  
   517  See Documentation/cli/expr.md for a description of supported expressions.
   518  
   519  With the -hitcount option a condition on the breakpoint hit count can be set, the following operators are supported
   520  
   521  	condition -hitcount bp > n
   522  	condition -hitcount bp >= n
   523  	condition -hitcount bp < n
   524  	condition -hitcount bp <= n
   525  	condition -hitcount bp == n
   526  	condition -hitcount bp != n
   527  	condition -hitcount bp % n
   528  
   529  The -per-g-hitcount option works like -hitcount, but use per goroutine hitcount to compare with n.
   530  
   531  With the -clear option a condition on the breakpoint can removed.
   532  	
   533  The '% n' form means we should stop at the breakpoint when the hitcount is a multiple of n.
   534  
   535  Examples:
   536  
   537  	cond 2 i == 10				breakpoint 2 will stop when variable i equals 10
   538  	cond name runtime.curg.goid == 5	breakpoint 'name' will stop only on goroutine 5
   539  	cond -clear 2				the condition on breakpoint 2 will be removed
   540  `},
   541  		{aliases: []string{"config"}, cmdFn: configureCmd, helpMsg: `Changes configuration parameters.
   542  
   543  	config -list
   544  
   545  Show all configuration parameters.
   546  
   547  	config -save
   548  
   549  Saves the configuration file to disk, overwriting the current configuration file.
   550  
   551  	config <parameter> <value>
   552  
   553  Changes the value of a configuration parameter.
   554  
   555  	config substitute-path <from> <to>
   556  	config substitute-path <from>
   557  	config substitute-path -clear
   558  
   559  Adds or removes a path substitution rule, if -clear is used all
   560  substitute-path rules are removed. Without arguments shows the current list
   561  of substitute-path rules.
   562  See also Documentation/cli/substitutepath.md for how the rules are applied.
   563  
   564  	config alias <command> <alias>
   565  	config alias <alias>
   566  
   567  Defines <alias> as an alias to <command> or removes an alias.
   568  
   569  	config debug-info-directories -add <path>
   570  	config debug-info-directories -rm <path>
   571  	config debug-info-directories -clear
   572  
   573  Adds, removes or clears debug-info-directories.`},
   574  
   575  		{aliases: []string{"edit", "ed"}, cmdFn: edit, helpMsg: `Open where you are in $DELVE_EDITOR or $EDITOR
   576  
   577  	edit [locspec]
   578  	
   579  If locspec is omitted edit will open the current source file in the editor, otherwise it will open the specified location.`},
   580  		{aliases: []string{"libraries"}, cmdFn: libraries, helpMsg: `List loaded dynamic libraries`},
   581  
   582  		{aliases: []string{"examinemem", "x"}, group: dataCmds, cmdFn: examineMemoryCmd, helpMsg: `Examine raw memory at the given address.
   583  
   584  Examine memory:
   585  
   586  	examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] <address>
   587  	examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] -x <expression>
   588  
   589  Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal).
   590  Length is the number of bytes (default 1) and must be less than or equal to 1000.
   591  Address is the memory location of the target to examine. Please note '-len' is deprecated by '-count and -size'.
   592  Expression can be an integer expression or pointer value of the memory location to examine.
   593  
   594  For example:
   595  
   596      x -fmt hex -count 20 -size 1 0xc00008af38
   597      x -fmt hex -count 20 -size 1 -x 0xc00008af38 + 8
   598      x -fmt hex -count 20 -size 1 -x &myVar
   599      x -fmt hex -count 20 -size 1 -x myPtrVar`},
   600  
   601  		{aliases: []string{"display"}, group: dataCmds, cmdFn: display, helpMsg: `Print value of an expression every time the program stops.
   602  
   603  	display -a [%format] <expression>
   604  	display -d <number>
   605  
   606  The '-a' option adds an expression to the list of expression printed every time the program stops. The '-d' option removes the specified expression from the list.
   607  
   608  If display is called without arguments it will print the value of all expression in the list.`},
   609  
   610  		{aliases: []string{"dump"}, cmdFn: dump, helpMsg: `Creates a core dump from the current process state
   611  
   612  	dump <output file>
   613  
   614  The core dump is always written in ELF, even on systems (windows, macOS) where this is not customary. For environments other than linux/amd64 threads and registers are dumped in a format that only Delve can read back.`},
   615  
   616  		{aliases: []string{"transcript"}, cmdFn: transcript, helpMsg: `Appends command output to a file.
   617  
   618  	transcript [-t] [-x] <output file>
   619  	transcript -off
   620  
   621  Output of Delve's command is appended to the specified output file. If '-t' is specified and the output file exists it is truncated. If '-x' is specified output to stdout is suppressed instead.
   622  
   623  Using the -off option disables the transcript.`},
   624  
   625  		{aliases: []string{"target"}, cmdFn: target, helpMsg: `Manages child process debugging.
   626  
   627  	target follow-exec [-on [regex]] [-off]
   628  
   629  Enables or disables follow exec mode. When follow exec mode Delve will automatically attach to new child processes executed by the target process. An optional regular expression can be passed to 'target follow-exec', only child processes with a command line matching the regular expression will be followed.
   630  
   631  	target list
   632  
   633  List currently attached processes.
   634  
   635  	target switch [pid]
   636  
   637  Switches to the specified process.`},
   638  	}
   639  
   640  	addrecorded := client == nil
   641  	if !addrecorded {
   642  		if state, err := client.GetStateNonBlocking(); err == nil {
   643  			addrecorded = state.Recording
   644  			if !addrecorded {
   645  				addrecorded = client.Recorded()
   646  			}
   647  		}
   648  	}
   649  
   650  	if addrecorded {
   651  		c.cmds = append(c.cmds,
   652  			command{
   653  				aliases: []string{"rewind", "rw"},
   654  				group:   runCmds,
   655  				cmdFn:   c.rewind,
   656  				helpMsg: "Run backwards until breakpoint or start of recorded history.",
   657  			},
   658  			command{
   659  				aliases: []string{"check", "checkpoint"},
   660  				cmdFn:   checkpoint,
   661  				helpMsg: `Creates a checkpoint at the current position.
   662  
   663  	checkpoint [note]
   664  
   665  The "note" is arbitrary text that can be used to identify the checkpoint, if it is not specified it defaults to the current filename:line position.`,
   666  			},
   667  			command{
   668  				aliases: []string{"checkpoints"},
   669  				cmdFn:   checkpoints,
   670  				helpMsg: "Print out info for existing checkpoints.",
   671  			},
   672  			command{
   673  				aliases: []string{"clear-checkpoint", "clearcheck"},
   674  				cmdFn:   clearCheckpoint,
   675  				helpMsg: `Deletes checkpoint.
   676  
   677  	clear-checkpoint <id>`,
   678  			},
   679  			command{
   680  				aliases: []string{"rev"},
   681  				group:   runCmds,
   682  				cmdFn:   c.revCmd,
   683  				helpMsg: `Reverses the execution of the target program for the command specified.
   684  Currently, rev next, step, step-instruction and stepout commands are supported.`,
   685  			})
   686  	}
   687  
   688  	sort.Sort(byFirstAlias(c.cmds))
   689  	return c
   690  }
   691  
   692  // Register custom commands. Expects cf to be a func of type cmdfunc,
   693  // returning only an error.
   694  func (c *Commands) Register(cmdstr string, cf cmdfunc, helpMsg string) {
   695  	for _, v := range c.cmds {
   696  		if v.match(cmdstr) {
   697  			v.cmdFn = cf
   698  			return
   699  		}
   700  	}
   701  
   702  	c.cmds = append(c.cmds, command{aliases: []string{cmdstr}, cmdFn: cf, helpMsg: helpMsg})
   703  }
   704  
   705  // Find will look up the command function for the given command input.
   706  // If it cannot find the command it will default to noCmdAvailable().
   707  // If the command is an empty string it will replay the last command.
   708  func (c *Commands) Find(cmdstr string, prefix cmdPrefix) command {
   709  	// If <enter> use last command, if there was one.
   710  	if cmdstr == "" {
   711  		return command{aliases: []string{"nullcmd"}, cmdFn: nullCommand}
   712  	}
   713  
   714  	for _, v := range c.cmds {
   715  		if v.match(cmdstr) {
   716  			if prefix != noPrefix && v.allowedPrefixes&prefix == 0 {
   717  				continue
   718  			}
   719  			return v
   720  		}
   721  	}
   722  
   723  	return command{aliases: []string{"nocmd"}, cmdFn: noCmdAvailable}
   724  }
   725  
   726  // CallWithContext takes a command and a context that command should be executed in.
   727  func (c *Commands) CallWithContext(cmdstr string, t *Term, ctx callContext) error {
   728  	vals := strings.SplitN(strings.TrimSpace(cmdstr), " ", 2)
   729  	cmdname := vals[0]
   730  	var args string
   731  	if len(vals) > 1 {
   732  		args = strings.TrimSpace(vals[1])
   733  	}
   734  	return c.Find(cmdname, ctx.Prefix).cmdFn(t, ctx, args)
   735  }
   736  
   737  // Call takes a command to execute.
   738  func (c *Commands) Call(cmdstr string, t *Term) error {
   739  	ctx := callContext{Prefix: noPrefix, Scope: api.EvalScope{GoroutineID: -1, Frame: c.frame, DeferredCall: 0}}
   740  	return c.CallWithContext(cmdstr, t, ctx)
   741  }
   742  
   743  // Merge takes aliases defined in the config struct and merges them with the default aliases.
   744  func (c *Commands) Merge(allAliases map[string][]string) {
   745  	for i := range c.cmds {
   746  		if c.cmds[i].builtinAliases != nil {
   747  			c.cmds[i].aliases = append(c.cmds[i].aliases[:0], c.cmds[i].builtinAliases...)
   748  		}
   749  	}
   750  	for i := range c.cmds {
   751  		if aliases, ok := allAliases[c.cmds[i].aliases[0]]; ok {
   752  			if c.cmds[i].builtinAliases == nil {
   753  				c.cmds[i].builtinAliases = make([]string, len(c.cmds[i].aliases))
   754  				copy(c.cmds[i].builtinAliases, c.cmds[i].aliases)
   755  			}
   756  			c.cmds[i].aliases = append(c.cmds[i].aliases, aliases...)
   757  		}
   758  	}
   759  }
   760  
   761  var errNoCmd = errors.New("command not available")
   762  
   763  func noCmdAvailable(t *Term, ctx callContext, args string) error {
   764  	return errNoCmd
   765  }
   766  
   767  func nullCommand(t *Term, ctx callContext, args string) error {
   768  	return nil
   769  }
   770  
   771  func (c *Commands) help(t *Term, ctx callContext, args string) error {
   772  	if args != "" {
   773  		for _, cmd := range c.cmds {
   774  			for _, alias := range cmd.aliases {
   775  				if alias == args {
   776  					fmt.Fprintln(t.stdout, cmd.helpMsg)
   777  					return nil
   778  				}
   779  			}
   780  		}
   781  		return errNoCmd
   782  	}
   783  
   784  	fmt.Fprintln(t.stdout, "The following commands are available:")
   785  
   786  	for _, cgd := range commandGroupDescriptions {
   787  		fmt.Fprintf(t.stdout, "\n%s:\n", cgd.description)
   788  		w := new(tabwriter.Writer)
   789  		w.Init(t.stdout, 0, 8, 0, '-', 0)
   790  		for _, cmd := range c.cmds {
   791  			if cmd.group != cgd.group {
   792  				continue
   793  			}
   794  			h := cmd.helpMsg
   795  			if idx := strings.Index(h, "\n"); idx >= 0 {
   796  				h = h[:idx]
   797  			}
   798  			if len(cmd.aliases) > 1 {
   799  				fmt.Fprintf(w, "    %s (alias: %s) \t %s\n", cmd.aliases[0], strings.Join(cmd.aliases[1:], " | "), h)
   800  			} else {
   801  				fmt.Fprintf(w, "    %s \t %s\n", cmd.aliases[0], h)
   802  			}
   803  		}
   804  		if err := w.Flush(); err != nil {
   805  			return err
   806  		}
   807  	}
   808  
   809  	fmt.Fprintln(t.stdout)
   810  	fmt.Fprintln(t.stdout, "Type help followed by a command for full documentation.")
   811  	return nil
   812  }
   813  
   814  type byThreadID []*api.Thread
   815  
   816  func (a byThreadID) Len() int           { return len(a) }
   817  func (a byThreadID) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   818  func (a byThreadID) Less(i, j int) bool { return a[i].ID < a[j].ID }
   819  
   820  func threads(t *Term, ctx callContext, args string) error {
   821  	threads, err := t.client.ListThreads()
   822  	if err != nil {
   823  		return err
   824  	}
   825  	state, err := t.client.GetState()
   826  	if err != nil {
   827  		return err
   828  	}
   829  	sort.Sort(byThreadID(threads))
   830  	done := false
   831  	t.stdout.pw.PageMaybe(func() { done = false })
   832  	for _, th := range threads {
   833  		if done {
   834  			break
   835  		}
   836  		prefix := "  "
   837  		if state.CurrentThread != nil && state.CurrentThread.ID == th.ID {
   838  			prefix = "* "
   839  		}
   840  		if th.Function != nil {
   841  			fmt.Fprintf(t.stdout, "%sThread %d at %#v %s:%d %s\n",
   842  				prefix, th.ID, th.PC, t.formatPath(th.File),
   843  				th.Line, th.Function.Name())
   844  		} else {
   845  			fmt.Fprintf(t.stdout, "%sThread %s\n", prefix, t.formatThread(th))
   846  		}
   847  	}
   848  	return nil
   849  }
   850  
   851  func thread(t *Term, ctx callContext, args string) error {
   852  	if len(args) == 0 {
   853  		return fmt.Errorf("you must specify a thread")
   854  	}
   855  	tid, err := strconv.Atoi(args)
   856  	if err != nil {
   857  		return err
   858  	}
   859  	oldState, err := t.client.GetState()
   860  	if err != nil {
   861  		return err
   862  	}
   863  	newState, err := t.client.SwitchThread(tid)
   864  	if err != nil {
   865  		return err
   866  	}
   867  
   868  	oldThread := "<none>"
   869  	newThread := "<none>"
   870  	if oldState.CurrentThread != nil {
   871  		oldThread = strconv.Itoa(oldState.CurrentThread.ID)
   872  	}
   873  	if newState.CurrentThread != nil {
   874  		newThread = strconv.Itoa(newState.CurrentThread.ID)
   875  	}
   876  	fmt.Fprintf(t.stdout, "Switched from %s to %s\n", oldThread, newThread)
   877  	return nil
   878  }
   879  
   880  type byGoroutineID []*api.Goroutine
   881  
   882  func (a byGoroutineID) Len() int           { return len(a) }
   883  func (a byGoroutineID) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   884  func (a byGoroutineID) Less(i, j int) bool { return a[i].ID < a[j].ID }
   885  
   886  func (c *Commands) printGoroutines(t *Term, ctx callContext, indent string, gs []*api.Goroutine, fgl api.FormatGoroutineLoc, flags api.PrintGoroutinesFlags, depth int, cmd string, pdone *bool, state *api.DebuggerState) error {
   887  	for _, g := range gs {
   888  		if t.longCommandCanceled() || (pdone != nil && *pdone) {
   889  			break
   890  		}
   891  		prefix := indent + "  "
   892  		if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID {
   893  			prefix = indent + "* "
   894  		}
   895  		fmt.Fprintf(t.stdout, "%sGoroutine %s\n", prefix, t.formatGoroutine(g, fgl))
   896  		if flags&api.PrintGoroutinesLabels != 0 {
   897  			writeGoroutineLabels(t.stdout, g, indent+"\t")
   898  		}
   899  		if flags&api.PrintGoroutinesStack != 0 {
   900  			stack, err := t.client.Stacktrace(g.ID, depth, 0, nil)
   901  			if err != nil {
   902  				return err
   903  			}
   904  			printStack(t, t.stdout, stack, indent+"\t", false)
   905  		}
   906  		if cmd != "" {
   907  			ctx.Scope.GoroutineID = g.ID
   908  			if err := c.CallWithContext(cmd, t, ctx); err != nil {
   909  				return err
   910  			}
   911  		}
   912  	}
   913  	return nil
   914  }
   915  
   916  func (c *Commands) goroutines(t *Term, ctx callContext, argstr string) error {
   917  	filters, group, fgl, flags, depth, batchSize, cmd, err := api.ParseGoroutineArgs(argstr)
   918  	if err != nil {
   919  		return err
   920  	}
   921  
   922  	state, err := t.client.GetState()
   923  	if err != nil {
   924  		return err
   925  	}
   926  	var (
   927  		start         = 0
   928  		gslen         = 0
   929  		gs            []*api.Goroutine
   930  		groups        []api.GoroutineGroup
   931  		tooManyGroups bool
   932  	)
   933  	done := false
   934  	t.stdout.pw.PageMaybe(func() { done = true })
   935  	t.longCommandStart()
   936  	for start >= 0 {
   937  		if t.longCommandCanceled() || done {
   938  			fmt.Fprintf(t.stdout, "interrupted\n")
   939  			return nil
   940  		}
   941  		gs, groups, start, tooManyGroups, err = t.client.ListGoroutinesWithFilter(start, batchSize, filters, &group, &api.EvalScope{GoroutineID: -1, Frame: c.frame})
   942  		if err != nil {
   943  			return err
   944  		}
   945  		if len(groups) > 0 {
   946  			for i := range groups {
   947  				fmt.Fprintf(t.stdout, "%s\n", groups[i].Name)
   948  				err = c.printGoroutines(t, ctx, "\t", gs[groups[i].Offset:][:groups[i].Count], fgl, flags, depth, cmd, &done, state)
   949  				if err != nil {
   950  					return err
   951  				}
   952  				fmt.Fprintf(t.stdout, "\tTotal: %d\n", groups[i].Total)
   953  				if i != len(groups)-1 {
   954  					fmt.Fprintf(t.stdout, "\n")
   955  				}
   956  			}
   957  			if tooManyGroups {
   958  				fmt.Fprintf(t.stdout, "Too many groups\n")
   959  			}
   960  		} else {
   961  			sort.Sort(byGoroutineID(gs))
   962  			err = c.printGoroutines(t, ctx, "", gs, fgl, flags, depth, cmd, &done, state)
   963  			if err != nil {
   964  				return err
   965  			}
   966  			gslen += len(gs)
   967  		}
   968  	}
   969  	if gslen > 0 {
   970  		fmt.Fprintf(t.stdout, "[%d goroutines]\n", gslen)
   971  	}
   972  	return nil
   973  }
   974  
   975  func selectedGID(state *api.DebuggerState) int64 {
   976  	if state.SelectedGoroutine == nil {
   977  		return 0
   978  	}
   979  	return state.SelectedGoroutine.ID
   980  }
   981  
   982  func (c *Commands) goroutine(t *Term, ctx callContext, argstr string) error {
   983  	args := config.Split2PartsBySpace(argstr)
   984  
   985  	if ctx.Prefix == onPrefix {
   986  		if len(args) != 1 || args[0] != "" {
   987  			return errors.New("too many arguments to goroutine")
   988  		}
   989  		ctx.Breakpoint.Goroutine = true
   990  		return nil
   991  	}
   992  
   993  	if len(args) == 1 {
   994  		if args[0] == "" {
   995  			return printscope(t)
   996  		}
   997  		gid, err := strconv.ParseInt(argstr, 10, 64)
   998  		if err != nil {
   999  			return err
  1000  		}
  1001  
  1002  		oldState, err := t.client.GetState()
  1003  		if err != nil {
  1004  			return err
  1005  		}
  1006  		newState, err := t.client.SwitchGoroutine(gid)
  1007  		if err != nil {
  1008  			return err
  1009  		}
  1010  		c.frame = 0
  1011  		fmt.Fprintf(t.stdout, "Switched from %d to %d (thread %d)\n", selectedGID(oldState), gid, newState.CurrentThread.ID)
  1012  		return nil
  1013  	}
  1014  
  1015  	var err error
  1016  	ctx.Scope.GoroutineID, err = strconv.ParseInt(args[0], 10, 64)
  1017  	if err != nil {
  1018  		return err
  1019  	}
  1020  	return c.CallWithContext(args[1], t, ctx)
  1021  }
  1022  
  1023  // Handle "frame", "up", "down" commands.
  1024  func (c *Commands) frameCommand(t *Term, ctx callContext, argstr string, direction frameDirection) error {
  1025  	frame := 1
  1026  	arg := ""
  1027  	if len(argstr) == 0 {
  1028  		if direction == frameSet {
  1029  			return errors.New("not enough arguments")
  1030  		}
  1031  	} else {
  1032  		args := config.Split2PartsBySpace(argstr)
  1033  		var err error
  1034  		if frame, err = strconv.Atoi(args[0]); err != nil {
  1035  			return err
  1036  		}
  1037  		if len(args) > 1 {
  1038  			arg = args[1]
  1039  		}
  1040  	}
  1041  	switch direction {
  1042  	case frameUp:
  1043  		frame = c.frame + frame
  1044  	case frameDown:
  1045  		frame = c.frame - frame
  1046  	}
  1047  	if len(arg) > 0 {
  1048  		ctx.Scope.Frame = frame
  1049  		return c.CallWithContext(arg, t, ctx)
  1050  	}
  1051  	if frame < 0 {
  1052  		return fmt.Errorf("Invalid frame %d", frame)
  1053  	}
  1054  	stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, frame, 0, nil)
  1055  	if err != nil {
  1056  		return err
  1057  	}
  1058  	if frame >= len(stack) {
  1059  		return fmt.Errorf("Invalid frame %d", frame)
  1060  	}
  1061  	c.frame = frame
  1062  	state, err := t.client.GetState()
  1063  	if err != nil {
  1064  		return err
  1065  	}
  1066  	printcontext(t, state)
  1067  	th := stack[frame]
  1068  	fmt.Fprintf(t.stdout, "Frame %d: %s:%d (PC: %x)\n", frame, t.formatPath(th.File), th.Line, th.PC)
  1069  	printfile(t, th.File, th.Line, true)
  1070  	return nil
  1071  }
  1072  
  1073  func (c *Commands) deferredCommand(t *Term, ctx callContext, argstr string) error {
  1074  	ctx.Prefix = deferredPrefix
  1075  
  1076  	space := strings.IndexRune(argstr, ' ')
  1077  	if space < 0 {
  1078  		return errors.New("not enough arguments")
  1079  	}
  1080  
  1081  	var err error
  1082  	ctx.Scope.DeferredCall, err = strconv.Atoi(argstr[:space])
  1083  	if err != nil {
  1084  		return err
  1085  	}
  1086  	if ctx.Scope.DeferredCall <= 0 {
  1087  		return errors.New("argument of deferred must be a number greater than 0 (use 'stack -defer' to see the list of deferred calls)")
  1088  	}
  1089  	return c.CallWithContext(argstr[space:], t, ctx)
  1090  }
  1091  
  1092  func printscope(t *Term) error {
  1093  	state, err := t.client.GetState()
  1094  	if err != nil {
  1095  		return err
  1096  	}
  1097  
  1098  	fmt.Fprintf(t.stdout, "Thread %s\n", t.formatThread(state.CurrentThread))
  1099  	if state.SelectedGoroutine != nil {
  1100  		writeGoroutineLong(t, t.stdout, state.SelectedGoroutine, "")
  1101  	}
  1102  	return nil
  1103  }
  1104  
  1105  func (t *Term) formatThread(th *api.Thread) string {
  1106  	if th == nil {
  1107  		return "<nil>"
  1108  	}
  1109  	return fmt.Sprintf("%d at %s:%d", th.ID, t.formatPath(th.File), th.Line)
  1110  }
  1111  
  1112  func (t *Term) formatLocation(loc api.Location) string {
  1113  	return fmt.Sprintf("%s:%d %s (%#v)", t.formatPath(loc.File), loc.Line, loc.Function.Name(), loc.PC)
  1114  }
  1115  
  1116  func (t *Term) formatGoroutine(g *api.Goroutine, fgl api.FormatGoroutineLoc) string {
  1117  	if g == nil {
  1118  		return "<nil>"
  1119  	}
  1120  	if g.Unreadable != "" {
  1121  		return fmt.Sprintf("(unreadable %s)", g.Unreadable)
  1122  	}
  1123  	var locname string
  1124  	var loc api.Location
  1125  	switch fgl {
  1126  	case api.FglRuntimeCurrent:
  1127  		locname = "Runtime"
  1128  		loc = g.CurrentLoc
  1129  	case api.FglUserCurrent:
  1130  		locname = "User"
  1131  		loc = g.UserCurrentLoc
  1132  	case api.FglGo:
  1133  		locname = "Go"
  1134  		loc = g.GoStatementLoc
  1135  	case api.FglStart:
  1136  		locname = "Start"
  1137  		loc = g.StartLoc
  1138  	}
  1139  
  1140  	buf := new(strings.Builder)
  1141  	fmt.Fprintf(buf, "%d - %s: %s", g.ID, locname, t.formatLocation(loc))
  1142  	if g.ThreadID != 0 {
  1143  		fmt.Fprintf(buf, " (thread %d)", g.ThreadID)
  1144  	}
  1145  
  1146  	if (g.Status == api.GoroutineWaiting || g.Status == api.GoroutineSyscall) && g.WaitReason != 0 {
  1147  		var wr string
  1148  		if g.WaitReason > 0 && g.WaitReason < int64(len(waitReasonStrings)) {
  1149  			wr = waitReasonStrings[g.WaitReason]
  1150  		} else {
  1151  			wr = fmt.Sprintf("unknown wait reason %d", g.WaitReason)
  1152  		}
  1153  		fmt.Fprintf(buf, " [%s", wr)
  1154  		if g.WaitSince > 0 {
  1155  			fmt.Fprintf(buf, " %d", g.WaitSince)
  1156  		}
  1157  		fmt.Fprintf(buf, "]")
  1158  	}
  1159  
  1160  	return buf.String()
  1161  }
  1162  
  1163  var waitReasonStrings = [...]string{
  1164  	"",
  1165  	"GC assist marking",
  1166  	"IO wait",
  1167  	"chan receive (nil chan)",
  1168  	"chan send (nil chan)",
  1169  	"dumping heap",
  1170  	"garbage collection",
  1171  	"garbage collection scan",
  1172  	"panicwait",
  1173  	"select",
  1174  	"select (no cases)",
  1175  	"GC assist wait",
  1176  	"GC sweep wait",
  1177  	"GC scavenge wait",
  1178  	"chan receive",
  1179  	"chan send",
  1180  	"finalizer wait",
  1181  	"force gc (idle)",
  1182  	"semacquire",
  1183  	"sleep",
  1184  	"sync.Cond.Wait",
  1185  	"timer goroutine (idle)",
  1186  	"trace reader (blocked)",
  1187  	"wait for GC cycle",
  1188  	"GC worker (idle)",
  1189  	"preempted",
  1190  	"debug call",
  1191  	"GC mark termination",
  1192  	"stopping the world",
  1193  	"flushing proc caches",
  1194  	"trace goroutine status",
  1195  	"trace proc status",
  1196  	"page trace flush",
  1197  	"coroutine",
  1198  }
  1199  
  1200  func writeGoroutineLong(t *Term, w io.Writer, g *api.Goroutine, prefix string) {
  1201  	fmt.Fprintf(w, "%sGoroutine %d:\n%s\tRuntime: %s\n%s\tUser: %s\n%s\tGo: %s\n%s\tStart: %s\n",
  1202  		prefix, g.ID,
  1203  		prefix, t.formatLocation(g.CurrentLoc),
  1204  		prefix, t.formatLocation(g.UserCurrentLoc),
  1205  		prefix, t.formatLocation(g.GoStatementLoc),
  1206  		prefix, t.formatLocation(g.StartLoc))
  1207  	writeGoroutineLabels(w, g, prefix+"\t")
  1208  }
  1209  
  1210  func writeGoroutineLabels(w io.Writer, g *api.Goroutine, prefix string) {
  1211  	const maxNumberOfGoroutineLabels = 5
  1212  
  1213  	if len(g.Labels) <= 0 {
  1214  		return
  1215  	}
  1216  
  1217  	keys := make([]string, 0, len(g.Labels))
  1218  	for k := range g.Labels {
  1219  		keys = append(keys, k)
  1220  	}
  1221  	sort.Strings(keys)
  1222  	more := false
  1223  	if len(keys) > maxNumberOfGoroutineLabels {
  1224  		more = true
  1225  		keys = keys[:maxNumberOfGoroutineLabels]
  1226  	}
  1227  	fmt.Fprintf(w, "%sLabels: ", prefix)
  1228  	for i, k := range keys {
  1229  		fmt.Fprintf(w, "%q:%q", k, g.Labels[k])
  1230  		if i != len(keys)-1 {
  1231  			fmt.Fprintf(w, ", ")
  1232  		} else if more {
  1233  			fmt.Fprintf(w, "... (%d more)", len(g.Labels)-maxNumberOfGoroutineLabels)
  1234  		}
  1235  	}
  1236  	fmt.Fprintf(w, "\n")
  1237  }
  1238  
  1239  func restart(t *Term, ctx callContext, args string) error {
  1240  	if t.client.Recorded() {
  1241  		return restartRecorded(t, ctx, args)
  1242  	}
  1243  
  1244  	return restartLive(t, ctx, args)
  1245  }
  1246  
  1247  func restartRecorded(t *Term, ctx callContext, args string) error {
  1248  	v := config.Split2PartsBySpace(args)
  1249  
  1250  	rerecord := false
  1251  	resetArgs := false
  1252  	newArgv := []string{}
  1253  	newRedirects := [3]string{}
  1254  	restartPos := ""
  1255  
  1256  	if len(v) > 0 {
  1257  		if v[0] == "-r" {
  1258  			rerecord = true
  1259  			if len(v) == 2 {
  1260  				var err error
  1261  				resetArgs, newArgv, newRedirects, err = parseNewArgv(v[1])
  1262  				if err != nil {
  1263  					return err
  1264  				}
  1265  			}
  1266  		} else {
  1267  			if len(v) > 1 {
  1268  				return fmt.Errorf("too many arguments to restart")
  1269  			}
  1270  			restartPos = v[0]
  1271  		}
  1272  	}
  1273  
  1274  	if err := restartIntl(t, rerecord, restartPos, resetArgs, newArgv, newRedirects); err != nil {
  1275  		return err
  1276  	}
  1277  
  1278  	state, err := t.client.GetState()
  1279  	if err != nil {
  1280  		return err
  1281  	}
  1282  	printcontext(t, state)
  1283  	printPos(t, state.CurrentThread, printPosShowArrow)
  1284  	t.onStop()
  1285  	return nil
  1286  }
  1287  
  1288  // parseOptionalCount parses an optional count argument.
  1289  // If there are not arguments, a value of 1 is returned as the default.
  1290  func parseOptionalCount(arg string) (int64, error) {
  1291  	if len(arg) == 0 {
  1292  		return 1, nil
  1293  	}
  1294  	return strconv.ParseInt(arg, 0, 64)
  1295  }
  1296  
  1297  func restartLive(t *Term, ctx callContext, args string) error {
  1298  	t.oldPid = 0
  1299  	resetArgs, newArgv, newRedirects, err := parseNewArgv(args)
  1300  	if err != nil {
  1301  		return err
  1302  	}
  1303  
  1304  	if err := restartIntl(t, false, "", resetArgs, newArgv, newRedirects); err != nil {
  1305  		return err
  1306  	}
  1307  
  1308  	fmt.Fprintln(t.stdout, "Process restarted with PID", t.client.ProcessPid())
  1309  	return nil
  1310  }
  1311  
  1312  func restartIntl(t *Term, rerecord bool, restartPos string, resetArgs bool, newArgv []string, newRedirects [3]string) error {
  1313  	discarded, err := t.client.RestartFrom(rerecord, restartPos, resetArgs, newArgv, newRedirects, false)
  1314  	if err != nil {
  1315  		return err
  1316  	}
  1317  	for i := range discarded {
  1318  		fmt.Fprintf(t.stdout, "Discarded %s at %s: %v\n", formatBreakpointName(discarded[i].Breakpoint, false), t.formatBreakpointLocation(discarded[i].Breakpoint), discarded[i].Reason)
  1319  	}
  1320  	return nil
  1321  }
  1322  
  1323  func parseNewArgv(args string) (resetArgs bool, newArgv []string, newRedirects [3]string, err error) {
  1324  	if args == "" {
  1325  		return false, nil, [3]string{}, nil
  1326  	}
  1327  	v, err := argv.Argv(args,
  1328  		func(s string) (string, error) {
  1329  			return "", fmt.Errorf("Backtick not supported in '%s'", s)
  1330  		},
  1331  		nil)
  1332  	if err != nil {
  1333  		return false, nil, [3]string{}, err
  1334  	}
  1335  	if len(v) != 1 {
  1336  		return false, nil, [3]string{}, fmt.Errorf("illegal commandline '%s'", args)
  1337  	}
  1338  	w := v[0]
  1339  	if len(w) == 0 {
  1340  		return false, nil, [3]string{}, nil
  1341  	}
  1342  	if w[0] == "-noargs" {
  1343  		if len(w) > 1 {
  1344  			return false, nil, [3]string{}, fmt.Errorf("too many arguments to restart")
  1345  		}
  1346  		return true, nil, [3]string{}, nil
  1347  	}
  1348  	redirs := [3]string{}
  1349  	for len(w) > 0 {
  1350  		var found bool
  1351  		var err error
  1352  		w, found, err = parseOneRedirect(w, &redirs)
  1353  		if err != nil {
  1354  			return false, nil, [3]string{}, err
  1355  		}
  1356  		if !found {
  1357  			break
  1358  		}
  1359  	}
  1360  	return true, w, redirs, nil
  1361  }
  1362  
  1363  func parseOneRedirect(w []string, redirs *[3]string) ([]string, bool, error) {
  1364  	prefixes := []string{"<", ">", "2>"}
  1365  	names := []string{"stdin", "stdout", "stderr"}
  1366  	if len(w) >= 2 {
  1367  		for _, prefix := range prefixes {
  1368  			if w[len(w)-2] == prefix {
  1369  				w[len(w)-2] += w[len(w)-1]
  1370  				w = w[:len(w)-1]
  1371  				break
  1372  			}
  1373  		}
  1374  	}
  1375  	for i, prefix := range prefixes {
  1376  		if strings.HasPrefix(w[len(w)-1], prefix) {
  1377  			if redirs[i] != "" {
  1378  				return nil, false, fmt.Errorf("redirect error: %s redirected twice", names[i])
  1379  			}
  1380  			redirs[i] = w[len(w)-1][len(prefix):]
  1381  			return w[:len(w)-1], true, nil
  1382  		}
  1383  	}
  1384  	return w, false, nil
  1385  }
  1386  
  1387  func printcontextNoState(t *Term) {
  1388  	state, _ := t.client.GetState()
  1389  	if state == nil || state.CurrentThread == nil {
  1390  		return
  1391  	}
  1392  	printcontext(t, state)
  1393  }
  1394  
  1395  func (c *Commands) rebuild(t *Term, ctx callContext, args string) error {
  1396  	if ctx.Prefix == revPrefix {
  1397  		return c.rewind(t, ctx, args)
  1398  	}
  1399  	defer t.onStop()
  1400  	discarded, err := t.client.Restart(true)
  1401  	if len(discarded) > 0 {
  1402  		fmt.Fprintf(t.stdout, "not all breakpoints could be restored.")
  1403  	}
  1404  	return err
  1405  }
  1406  
  1407  func (c *Commands) cont(t *Term, ctx callContext, args string) error {
  1408  	if args != "" {
  1409  		tmp, err := setBreakpoint(t, ctx, false, args)
  1410  		if err != nil {
  1411  			if !strings.Contains(err.Error(), "Breakpoint exists") {
  1412  				return err
  1413  			}
  1414  		}
  1415  		defer func() {
  1416  			for _, bp := range tmp {
  1417  				if _, err := t.client.ClearBreakpoint(bp.ID); err != nil {
  1418  					fmt.Fprintf(t.stdout, "failed to clear temporary breakpoint: %d", bp.ID)
  1419  				}
  1420  			}
  1421  		}()
  1422  	}
  1423  	if ctx.Prefix == revPrefix {
  1424  		return c.rewind(t, ctx, args)
  1425  	}
  1426  	defer t.onStop()
  1427  	c.frame = 0
  1428  	stateChan := t.client.Continue()
  1429  	var state *api.DebuggerState
  1430  	for state = range stateChan {
  1431  		if state.Err != nil {
  1432  			printcontextNoState(t)
  1433  			return state.Err
  1434  		}
  1435  		printcontext(t, state)
  1436  	}
  1437  	printPos(t, state.CurrentThread, printPosShowArrow)
  1438  	return nil
  1439  }
  1440  
  1441  func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, shouldPrintFile bool) error {
  1442  	defer t.onStop()
  1443  	if !state.NextInProgress {
  1444  		if shouldPrintFile {
  1445  			printPos(t, state.CurrentThread, printPosShowArrow)
  1446  		}
  1447  		return nil
  1448  	}
  1449  	skipBreakpoints := false
  1450  	for {
  1451  		fmt.Fprintf(t.stdout, "\tbreakpoint hit during %s", op)
  1452  		if !skipBreakpoints {
  1453  			fmt.Fprintf(t.stdout, "\n")
  1454  			answer, err := promptAutoContinue(t, op)
  1455  			switch answer {
  1456  			case "f": // finish next
  1457  				skipBreakpoints = true
  1458  				fallthrough
  1459  			case "c": // continue once
  1460  				fmt.Fprintf(t.stdout, "continuing...\n")
  1461  			case "s": // stop and cancel
  1462  				fallthrough
  1463  			default:
  1464  				t.client.CancelNext()
  1465  				printPos(t, state.CurrentThread, printPosShowArrow)
  1466  				return err
  1467  			}
  1468  		} else {
  1469  			fmt.Fprintf(t.stdout, ", continuing...\n")
  1470  		}
  1471  		stateChan := t.client.DirectionCongruentContinue()
  1472  		var state *api.DebuggerState
  1473  		for state = range stateChan {
  1474  			if state.Err != nil {
  1475  				printcontextNoState(t)
  1476  				return state.Err
  1477  			}
  1478  			printcontext(t, state)
  1479  		}
  1480  		if !state.NextInProgress {
  1481  			printPos(t, state.CurrentThread, printPosShowArrow)
  1482  			return nil
  1483  		}
  1484  	}
  1485  }
  1486  
  1487  func promptAutoContinue(t *Term, op string) (string, error) {
  1488  	for {
  1489  		answer, err := t.line.Prompt(fmt.Sprintf("[c] continue [s] stop here and cancel %s, [f] finish %s skipping all breakpoints? ", op, op))
  1490  		if err != nil {
  1491  			return "", err
  1492  		}
  1493  		answer = strings.ToLower(strings.TrimSpace(answer))
  1494  		switch answer {
  1495  		case "f", "c", "s":
  1496  			return answer, nil
  1497  		}
  1498  	}
  1499  }
  1500  
  1501  func scopePrefixSwitch(t *Term, ctx callContext) error {
  1502  	if ctx.Scope.GoroutineID > 0 {
  1503  		_, err := t.client.SwitchGoroutine(ctx.Scope.GoroutineID)
  1504  		if err != nil {
  1505  			return err
  1506  		}
  1507  	}
  1508  	return nil
  1509  }
  1510  
  1511  func exitedToError(state *api.DebuggerState, err error) (*api.DebuggerState, error) {
  1512  	if err == nil && state.Exited {
  1513  		return nil, fmt.Errorf("Process %d has exited with status %d", state.Pid, state.ExitStatus)
  1514  	}
  1515  	return state, err
  1516  }
  1517  
  1518  func (c *Commands) step(t *Term, ctx callContext, args string) error {
  1519  	if err := scopePrefixSwitch(t, ctx); err != nil {
  1520  		return err
  1521  	}
  1522  	c.frame = 0
  1523  	stepfn := t.client.Step
  1524  	if ctx.Prefix == revPrefix {
  1525  		stepfn = t.client.ReverseStep
  1526  	}
  1527  	state, err := exitedToError(stepfn())
  1528  	if err != nil {
  1529  		printcontextNoState(t)
  1530  		return err
  1531  	}
  1532  	printcontext(t, state)
  1533  	return continueUntilCompleteNext(t, state, "step", true)
  1534  }
  1535  
  1536  var errNotOnFrameZero = errors.New("not on topmost frame")
  1537  
  1538  // stepInstruction implements the step-instruction (stepi) command.
  1539  func (c *Commands) stepInstruction(t *Term, ctx callContext, args string) error {
  1540  	return stepInstruction(t, ctx, c.frame, false)
  1541  }
  1542  
  1543  // nextInstruction implements the next-instruction (nexti) command.
  1544  func (c *Commands) nextInstruction(t *Term, ctx callContext, args string) error {
  1545  	return stepInstruction(t, ctx, c.frame, true)
  1546  }
  1547  
  1548  func stepInstruction(t *Term, ctx callContext, frame int, skipCalls bool) error {
  1549  	if err := scopePrefixSwitch(t, ctx); err != nil {
  1550  		return err
  1551  	}
  1552  	if frame != 0 {
  1553  		return errNotOnFrameZero
  1554  	}
  1555  
  1556  	defer t.onStop()
  1557  
  1558  	var fn func(bool) (*api.DebuggerState, error)
  1559  	if ctx.Prefix == revPrefix {
  1560  		fn = t.client.ReverseStepInstruction
  1561  	} else {
  1562  		fn = t.client.StepInstruction
  1563  	}
  1564  
  1565  	state, err := exitedToError(fn(skipCalls))
  1566  	if err != nil {
  1567  		printcontextNoState(t)
  1568  		return err
  1569  	}
  1570  	printcontext(t, state)
  1571  	printPos(t, state.CurrentThread, printPosShowArrow|printPosStepInstruction)
  1572  	return nil
  1573  }
  1574  
  1575  func (c *Commands) revCmd(t *Term, ctx callContext, args string) error {
  1576  	if len(args) == 0 {
  1577  		return errors.New("not enough arguments")
  1578  	}
  1579  
  1580  	ctx.Prefix = revPrefix
  1581  	return c.CallWithContext(args, t, ctx)
  1582  }
  1583  
  1584  func (c *Commands) next(t *Term, ctx callContext, args string) error {
  1585  	if err := scopePrefixSwitch(t, ctx); err != nil {
  1586  		return err
  1587  	}
  1588  	if c.frame != 0 {
  1589  		return errNotOnFrameZero
  1590  	}
  1591  
  1592  	nextfn := t.client.Next
  1593  	if ctx.Prefix == revPrefix {
  1594  		nextfn = t.client.ReverseNext
  1595  	}
  1596  
  1597  	var count int64
  1598  	var err error
  1599  	if count, err = parseOptionalCount(args); err != nil {
  1600  		return err
  1601  	} else if count <= 0 {
  1602  		return errors.New("Invalid next count")
  1603  	}
  1604  	for ; count > 0; count-- {
  1605  		state, err := exitedToError(nextfn())
  1606  		if err != nil {
  1607  			printcontextNoState(t)
  1608  			return err
  1609  		}
  1610  		// If we're about the exit the loop, print the context.
  1611  		finishedNext := count == 1
  1612  		if finishedNext {
  1613  			printcontext(t, state)
  1614  		}
  1615  		if err := continueUntilCompleteNext(t, state, "next", finishedNext); err != nil {
  1616  			return err
  1617  		}
  1618  	}
  1619  	return nil
  1620  }
  1621  
  1622  func (c *Commands) stepout(t *Term, ctx callContext, args string) error {
  1623  	if err := scopePrefixSwitch(t, ctx); err != nil {
  1624  		return err
  1625  	}
  1626  	if c.frame != 0 {
  1627  		return errNotOnFrameZero
  1628  	}
  1629  
  1630  	stepoutfn := t.client.StepOut
  1631  	if ctx.Prefix == revPrefix {
  1632  		stepoutfn = t.client.ReverseStepOut
  1633  	}
  1634  
  1635  	state, err := exitedToError(stepoutfn())
  1636  	if err != nil {
  1637  		printcontextNoState(t)
  1638  		return err
  1639  	}
  1640  	printcontext(t, state)
  1641  	return continueUntilCompleteNext(t, state, "stepout", true)
  1642  }
  1643  
  1644  func (c *Commands) call(t *Term, ctx callContext, args string) error {
  1645  	if err := scopePrefixSwitch(t, ctx); err != nil {
  1646  		return err
  1647  	}
  1648  	const unsafePrefix = "-unsafe "
  1649  	unsafe := false
  1650  	if strings.HasPrefix(args, unsafePrefix) {
  1651  		unsafe = true
  1652  		args = args[len(unsafePrefix):]
  1653  	}
  1654  	state, err := exitedToError(t.client.Call(ctx.Scope.GoroutineID, args, unsafe))
  1655  	c.frame = 0
  1656  	if err != nil {
  1657  		printcontextNoState(t)
  1658  		return err
  1659  	}
  1660  	printcontext(t, state)
  1661  	return continueUntilCompleteNext(t, state, "call", true)
  1662  }
  1663  
  1664  func clear(t *Term, ctx callContext, args string) error {
  1665  	if len(args) == 0 {
  1666  		return fmt.Errorf("not enough arguments")
  1667  	}
  1668  	id, err := strconv.Atoi(args)
  1669  	var bp *api.Breakpoint
  1670  	if err == nil {
  1671  		bp, err = t.client.ClearBreakpoint(id)
  1672  	} else {
  1673  		bp, err = t.client.ClearBreakpointByName(args)
  1674  	}
  1675  	if err != nil {
  1676  		return err
  1677  	}
  1678  	fmt.Fprintf(t.stdout, "%s cleared at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
  1679  	return nil
  1680  }
  1681  
  1682  func clearAll(t *Term, ctx callContext, args string) error {
  1683  	breakPoints, err := t.client.ListBreakpoints(false)
  1684  	if err != nil {
  1685  		return err
  1686  	}
  1687  
  1688  	var locPCs map[uint64]struct{}
  1689  	if args != "" {
  1690  		locs, _, err := t.client.FindLocation(api.EvalScope{GoroutineID: -1, Frame: 0}, args, true, t.substitutePathRules())
  1691  		if err != nil {
  1692  			return err
  1693  		}
  1694  		locPCs = make(map[uint64]struct{})
  1695  		for _, loc := range locs {
  1696  			for _, pc := range loc.PCs {
  1697  				locPCs[pc] = struct{}{}
  1698  			}
  1699  			locPCs[loc.PC] = struct{}{}
  1700  		}
  1701  	}
  1702  
  1703  	for _, bp := range breakPoints {
  1704  		if locPCs != nil {
  1705  			if _, ok := locPCs[bp.Addr]; !ok {
  1706  				continue
  1707  			}
  1708  		}
  1709  
  1710  		if bp.ID < 0 {
  1711  			continue
  1712  		}
  1713  
  1714  		_, err := t.client.ClearBreakpoint(bp.ID)
  1715  		if err != nil {
  1716  			fmt.Fprintf(t.stdout, "Couldn't delete %s at %s: %s\n", formatBreakpointName(bp, false), t.formatBreakpointLocation(bp), err)
  1717  		}
  1718  		fmt.Fprintf(t.stdout, "%s cleared at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
  1719  	}
  1720  	return nil
  1721  }
  1722  
  1723  func toggle(t *Term, ctx callContext, args string) error {
  1724  	if args == "" {
  1725  		return fmt.Errorf("not enough arguments")
  1726  	}
  1727  	id, err := strconv.Atoi(args)
  1728  	var bp *api.Breakpoint
  1729  	if err == nil {
  1730  		bp, err = t.client.ToggleBreakpoint(id)
  1731  	} else {
  1732  		bp, err = t.client.ToggleBreakpointByName(args)
  1733  	}
  1734  	if err != nil {
  1735  		return err
  1736  	}
  1737  	fmt.Fprintf(t.stdout, "%s toggled at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
  1738  	return nil
  1739  }
  1740  
  1741  // byID sorts breakpoints by ID.
  1742  type byID []*api.Breakpoint
  1743  
  1744  func (a byID) Len() int           { return len(a) }
  1745  func (a byID) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
  1746  func (a byID) Less(i, j int) bool { return a[i].ID < a[j].ID }
  1747  
  1748  func breakpoints(t *Term, ctx callContext, args string) error {
  1749  	breakPoints, err := t.client.ListBreakpoints(args == "-a")
  1750  	if err != nil {
  1751  		return err
  1752  	}
  1753  	sort.Sort(byID(breakPoints))
  1754  	for _, bp := range breakPoints {
  1755  		enabled := "(enabled)"
  1756  		if bp.Disabled {
  1757  			enabled = "(disabled)"
  1758  		} else if bp.ExprString != "" {
  1759  			enabled = "(suspended)"
  1760  		}
  1761  		fmt.Fprintf(t.stdout, "%s %s", formatBreakpointName(bp, true), enabled)
  1762  		if bp.ExprString != "" {
  1763  			fmt.Fprintf(t.stdout, " at %s\n", bp.ExprString)
  1764  		} else {
  1765  			fmt.Fprintf(t.stdout, " at %v (%d)\n", t.formatBreakpointLocation(bp), bp.TotalHitCount)
  1766  		}
  1767  
  1768  		attrs := formatBreakpointAttrs("\t", bp, false)
  1769  
  1770  		if len(attrs) > 0 {
  1771  			fmt.Fprintf(t.stdout, "%s\n", strings.Join(attrs, "\n"))
  1772  		}
  1773  	}
  1774  	return nil
  1775  }
  1776  
  1777  func formatBreakpointAttrs(prefix string, bp *api.Breakpoint, includeTrace bool) []string {
  1778  	var attrs []string
  1779  	if bp.Cond != "" {
  1780  		attrs = append(attrs, fmt.Sprintf("%scond %s", prefix, bp.Cond))
  1781  	}
  1782  	if bp.HitCond != "" {
  1783  		if bp.HitCondPerG {
  1784  			attrs = append(attrs, fmt.Sprintf("%scond -per-g-hitcount %s", prefix, bp.HitCond))
  1785  		} else {
  1786  			attrs = append(attrs, fmt.Sprintf("%scond -hitcount %s", prefix, bp.HitCond))
  1787  		}
  1788  	}
  1789  	if bp.Stacktrace > 0 {
  1790  		attrs = append(attrs, fmt.Sprintf("%sstack %d", prefix, bp.Stacktrace))
  1791  	}
  1792  	if bp.Goroutine {
  1793  		attrs = append(attrs, fmt.Sprintf("%sgoroutine", prefix))
  1794  	}
  1795  	if bp.LoadArgs != nil {
  1796  		if *(bp.LoadArgs) == longLoadConfig {
  1797  			attrs = append(attrs, fmt.Sprintf("%sargs -v", prefix))
  1798  		} else {
  1799  			attrs = append(attrs, fmt.Sprintf("%sargs", prefix))
  1800  		}
  1801  	}
  1802  	if bp.LoadLocals != nil {
  1803  		if *(bp.LoadLocals) == longLoadConfig {
  1804  			attrs = append(attrs, fmt.Sprintf("%slocals -v", prefix))
  1805  		} else {
  1806  			attrs = append(attrs, fmt.Sprintf("%slocals", prefix))
  1807  		}
  1808  	}
  1809  	for i := range bp.Variables {
  1810  		attrs = append(attrs, fmt.Sprintf("%sprint %s", prefix, bp.Variables[i]))
  1811  	}
  1812  	if includeTrace && bp.Tracepoint {
  1813  		attrs = append(attrs, fmt.Sprintf("%strace", prefix))
  1814  	}
  1815  	for i := range bp.VerboseDescr {
  1816  		attrs = append(attrs, fmt.Sprintf("%s%s", prefix, bp.VerboseDescr[i]))
  1817  	}
  1818  	return attrs
  1819  }
  1820  
  1821  func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) ([]*api.Breakpoint, error) {
  1822  	var (
  1823  		cond string
  1824  		spec string
  1825  
  1826  		requestedBp = &api.Breakpoint{}
  1827  	)
  1828  
  1829  	parseSpec := func(args []string) error {
  1830  		switch len(args) {
  1831  		case 1:
  1832  			if len(args[0]) != 0 {
  1833  				spec = argstr
  1834  			} else {
  1835  				// no arg specified
  1836  				spec = "+0"
  1837  			}
  1838  		case 2:
  1839  			if api.ValidBreakpointName(args[0]) == nil {
  1840  				requestedBp.Name = args[0]
  1841  				spec = args[1]
  1842  			} else {
  1843  				spec = argstr
  1844  			}
  1845  		default:
  1846  			return fmt.Errorf("address required")
  1847  		}
  1848  		return nil
  1849  	}
  1850  
  1851  	args := config.Split2PartsBySpace(argstr)
  1852  	if err := parseSpec(args); err != nil {
  1853  		return nil, err
  1854  	}
  1855  
  1856  	requestedBp.Tracepoint = tracepoint
  1857  	locs, substSpec, findLocErr := t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
  1858  	if findLocErr != nil {
  1859  		r := regexp.MustCompile(`^if | if `)
  1860  		if match := r.FindStringIndex(argstr); match != nil {
  1861  			requestedBp.Name = ""
  1862  			cond = argstr[match[1]:]
  1863  			argstr = argstr[:match[0]]
  1864  			args = config.Split2PartsBySpace(argstr)
  1865  			if err := parseSpec(args); err != nil {
  1866  				return nil, err
  1867  			}
  1868  			locs, substSpec, findLocErr = t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
  1869  		}
  1870  	}
  1871  	if findLocErr != nil && requestedBp.Name != "" {
  1872  		requestedBp.Name = ""
  1873  		spec = argstr
  1874  		var err2 error
  1875  		var substSpec2 string
  1876  		locs, substSpec2, err2 = t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
  1877  		if err2 == nil {
  1878  			findLocErr = nil
  1879  			substSpec = substSpec2
  1880  		}
  1881  	}
  1882  	if findLocErr != nil && shouldAskToSuspendBreakpoint(t) {
  1883  		fmt.Fprintf(os.Stderr, "Command failed: %s\n", findLocErr.Error())
  1884  		question := "Set a suspended breakpoint (Delve will try to set this breakpoint when a plugin is loaded) [Y/n]?"
  1885  		if isErrProcessExited(findLocErr) {
  1886  			question = "Set a suspended breakpoint (Delve will try to set this breakpoint when the process is restarted) [Y/n]?"
  1887  		}
  1888  		answer, err := yesno(t.line, question, "yes")
  1889  		if err != nil {
  1890  			return nil, err
  1891  		}
  1892  		if !answer {
  1893  			return nil, nil
  1894  		}
  1895  		findLocErr = nil
  1896  		bp, err := t.client.CreateBreakpointWithExpr(requestedBp, spec, t.substitutePathRules(), true)
  1897  		if err != nil {
  1898  			return nil, err
  1899  		}
  1900  		fmt.Fprintf(t.stdout, "%s set at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
  1901  		return nil, nil
  1902  	}
  1903  
  1904  	if findLocErr != nil {
  1905  		return nil, findLocErr
  1906  	}
  1907  	if substSpec != "" {
  1908  		spec = substSpec
  1909  	}
  1910  
  1911  	created := []*api.Breakpoint{}
  1912  	for _, loc := range locs {
  1913  		requestedBp.Addr = loc.PC
  1914  		requestedBp.Addrs = loc.PCs
  1915  		requestedBp.AddrPid = loc.PCPids
  1916  		if tracepoint {
  1917  			requestedBp.LoadArgs = &ShortLoadConfig
  1918  		}
  1919  
  1920  		requestedBp.Cond = cond
  1921  		bp, err := t.client.CreateBreakpointWithExpr(requestedBp, spec, t.substitutePathRules(), false)
  1922  		if err != nil {
  1923  			return nil, err
  1924  		}
  1925  		created = append(created, bp)
  1926  
  1927  		fmt.Fprintf(t.stdout, "%s set at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
  1928  	}
  1929  
  1930  	var shouldSetReturnBreakpoints bool
  1931  	loc, err := locspec.Parse(spec)
  1932  	if err != nil {
  1933  		return nil, err
  1934  	}
  1935  	switch t := loc.(type) {
  1936  	case *locspec.NormalLocationSpec:
  1937  		shouldSetReturnBreakpoints = t.LineOffset == -1 && t.FuncBase != nil
  1938  	case *locspec.RegexLocationSpec:
  1939  		shouldSetReturnBreakpoints = true
  1940  	}
  1941  	if tracepoint && shouldSetReturnBreakpoints && locs[0].Function != nil {
  1942  		for i := range locs {
  1943  			if locs[i].Function == nil {
  1944  				continue
  1945  			}
  1946  			addrs, err := t.client.(*rpc2.RPCClient).FunctionReturnLocations(locs[0].Function.Name())
  1947  			if err != nil {
  1948  				return nil, err
  1949  			}
  1950  			for j := range addrs {
  1951  				_, err = t.client.CreateBreakpoint(&api.Breakpoint{
  1952  					Addr:        addrs[j],
  1953  					TraceReturn: true,
  1954  					Line:        -1,
  1955  					LoadArgs:    &ShortLoadConfig,
  1956  				})
  1957  				if err != nil {
  1958  					return nil, err
  1959  				}
  1960  			}
  1961  		}
  1962  	}
  1963  	return created, nil
  1964  }
  1965  
  1966  func breakpoint(t *Term, ctx callContext, args string) error {
  1967  	_, err := setBreakpoint(t, ctx, false, args)
  1968  	return err
  1969  }
  1970  
  1971  func tracepoint(t *Term, ctx callContext, args string) error {
  1972  	if ctx.Prefix == onPrefix {
  1973  		if args != "" {
  1974  			return errors.New("too many arguments to trace")
  1975  		}
  1976  		ctx.Breakpoint.Tracepoint = true
  1977  		return nil
  1978  	}
  1979  	_, err := setBreakpoint(t, ctx, true, args)
  1980  	return err
  1981  }
  1982  
  1983  func getEditorName() (string, error) {
  1984  	var editor string
  1985  	if editor = os.Getenv("DELVE_EDITOR"); editor == "" {
  1986  		if editor = os.Getenv("EDITOR"); editor == "" {
  1987  			return "", fmt.Errorf("Neither DELVE_EDITOR or EDITOR is set")
  1988  		}
  1989  	}
  1990  	return editor, nil
  1991  }
  1992  
  1993  func runEditor(args ...string) error {
  1994  	editor, err := getEditorName()
  1995  	if err != nil {
  1996  		return err
  1997  	}
  1998  	cmd := exec.Command(editor, args...)
  1999  	cmd.Stdin = os.Stdin
  2000  	cmd.Stdout = os.Stdout
  2001  	cmd.Stderr = os.Stderr
  2002  	return cmd.Run()
  2003  }
  2004  
  2005  func edit(t *Term, ctx callContext, args string) error {
  2006  	file, lineno, _, err := getLocation(t, ctx, args, false)
  2007  	if err != nil {
  2008  		return err
  2009  	}
  2010  	editor, err := getEditorName()
  2011  	if err != nil {
  2012  		return err
  2013  	}
  2014  	switch editor {
  2015  	case "code":
  2016  		return runEditor("--goto", fmt.Sprintf("%s:%d", file, lineno))
  2017  	case "hx":
  2018  		return runEditor(fmt.Sprintf("%s:%d", file, lineno))
  2019  	case "vi", "vim", "nvim":
  2020  		return runEditor(fmt.Sprintf("+%d", lineno), file)
  2021  	default:
  2022  		return runEditor(fmt.Sprintf("+%d", lineno), file)
  2023  	}
  2024  }
  2025  
  2026  func watchpoint(t *Term, ctx callContext, args string) error {
  2027  	v := strings.SplitN(args, " ", 2)
  2028  	if len(v) != 2 {
  2029  		return errors.New("wrong number of arguments: watch [-r|-w|-rw] <expr>")
  2030  	}
  2031  	var wtype api.WatchType
  2032  	switch v[0] {
  2033  	case "-r":
  2034  		wtype = api.WatchRead
  2035  	case "-w":
  2036  		wtype = api.WatchWrite
  2037  	case "-rw":
  2038  		wtype = api.WatchRead | api.WatchWrite
  2039  	default:
  2040  		return fmt.Errorf("wrong argument %q to watch", v[0])
  2041  	}
  2042  	bp, err := t.client.CreateWatchpoint(ctx.Scope, v[1], wtype)
  2043  	if err != nil {
  2044  		return err
  2045  	}
  2046  	fmt.Fprintf(t.stdout, "%s set at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
  2047  	return nil
  2048  }
  2049  
  2050  func examineMemoryCmd(t *Term, ctx callContext, argstr string) error {
  2051  	var (
  2052  		address uint64
  2053  		err     error
  2054  		ok      bool
  2055  		args    = strings.Split(argstr, " ")
  2056  	)
  2057  
  2058  	// Default value
  2059  	priFmt := byte('x')
  2060  	count := 1
  2061  	size := 1
  2062  	isExpr := false
  2063  
  2064  	// nextArg returns the next argument that is not an empty string, if any, and
  2065  	// advances the args slice to the position after that.
  2066  	nextArg := func() string {
  2067  		for len(args) > 0 {
  2068  			arg := args[0]
  2069  			args = args[1:]
  2070  			if arg != "" {
  2071  				return arg
  2072  			}
  2073  		}
  2074  		return ""
  2075  	}
  2076  
  2077  loop:
  2078  	for {
  2079  		switch cmd := nextArg(); cmd {
  2080  		case "":
  2081  			// no more arguments
  2082  			break loop
  2083  		case "-fmt":
  2084  			arg := nextArg()
  2085  			if arg == "" {
  2086  				return fmt.Errorf("expected argument after -fmt")
  2087  			}
  2088  			fmtMapToPriFmt := map[string]byte{
  2089  				"oct":         'o',
  2090  				"octal":       'o',
  2091  				"hex":         'x',
  2092  				"hexadecimal": 'x',
  2093  				"dec":         'd',
  2094  				"decimal":     'd',
  2095  				"bin":         'b',
  2096  				"binary":      'b',
  2097  			}
  2098  			priFmt, ok = fmtMapToPriFmt[arg]
  2099  			if !ok {
  2100  				return fmt.Errorf("%q is not a valid format", arg)
  2101  			}
  2102  		case "-count", "-len":
  2103  			arg := nextArg()
  2104  			if arg == "" {
  2105  				return fmt.Errorf("expected argument after -count/-len")
  2106  			}
  2107  			var err error
  2108  			count, err = strconv.Atoi(arg)
  2109  			if err != nil || count <= 0 {
  2110  				return fmt.Errorf("count/len must be a positive integer")
  2111  			}
  2112  		case "-size":
  2113  			arg := nextArg()
  2114  			if arg == "" {
  2115  				return fmt.Errorf("expected argument after -size")
  2116  			}
  2117  			var err error
  2118  			size, err = strconv.Atoi(arg)
  2119  			if err != nil || size <= 0 || size > 8 {
  2120  				return fmt.Errorf("size must be a positive integer (<=8)")
  2121  			}
  2122  		case "-x":
  2123  			isExpr = true
  2124  			break loop // remaining args are going to be interpreted as expression
  2125  		default:
  2126  			if len(args) > 0 {
  2127  				return fmt.Errorf("unknown option %q", args[0])
  2128  			}
  2129  			args = []string{cmd}
  2130  			break loop // only one arg left to be evaluated as a uint
  2131  		}
  2132  	}
  2133  
  2134  	// TODO, maybe configured by user.
  2135  	if count*size > 1000 {
  2136  		return fmt.Errorf("read memory range (count*size) must be less than or equal to 1000 bytes")
  2137  	}
  2138  
  2139  	if len(args) == 0 {
  2140  		return fmt.Errorf("no address specified")
  2141  	}
  2142  
  2143  	if isExpr {
  2144  		expr := strings.Join(args, " ")
  2145  		val, err := t.client.EvalVariable(ctx.Scope, expr, t.loadConfig())
  2146  		if err != nil {
  2147  			return err
  2148  		}
  2149  
  2150  		// "-x &myVar" or "-x myPtrVar"
  2151  		if val.Kind == reflect.Ptr {
  2152  			if len(val.Children) < 1 {
  2153  				return fmt.Errorf("bug? invalid pointer: %#v", val)
  2154  			}
  2155  			address = val.Children[0].Addr
  2156  			// "-x 0xc000079f20 + 8" or -x 824634220320 + 8
  2157  		} else if val.Kind == reflect.Int && val.Value != "" {
  2158  			address, err = strconv.ParseUint(val.Value, 0, 64)
  2159  			if err != nil {
  2160  				return fmt.Errorf("bad expression result: %q: %s", val.Value, err)
  2161  			}
  2162  		} else {
  2163  			return fmt.Errorf("unsupported expression type: %s", val.Kind)
  2164  		}
  2165  	} else {
  2166  		address, err = strconv.ParseUint(args[0], 0, 64)
  2167  		if err != nil {
  2168  			return fmt.Errorf("convert address into uintptr type failed, %s", err)
  2169  		}
  2170  	}
  2171  
  2172  	memArea, isLittleEndian, err := t.client.ExamineMemory(address, count*size)
  2173  	if err != nil {
  2174  		return err
  2175  	}
  2176  	t.stdout.pw.PageMaybe(nil)
  2177  	fmt.Fprint(t.stdout, api.PrettyExamineMemory(uintptr(address), memArea, isLittleEndian, priFmt, size))
  2178  	return nil
  2179  }
  2180  
  2181  func parseFormatArg(args string) (fmtstr, argsOut string) {
  2182  	if len(args) < 1 || args[0] != '%' {
  2183  		return "", args
  2184  	}
  2185  	v := strings.SplitN(args, " ", 2)
  2186  	if len(v) == 1 {
  2187  		return v[0], ""
  2188  	}
  2189  	return v[0], v[1]
  2190  }
  2191  
  2192  const maxPrintVarChanGoroutines = 100
  2193  
  2194  func (c *Commands) printVar(t *Term, ctx callContext, args string) error {
  2195  	if len(args) == 0 {
  2196  		return fmt.Errorf("not enough arguments")
  2197  	}
  2198  	if ctx.Prefix == onPrefix {
  2199  		ctx.Breakpoint.Variables = append(ctx.Breakpoint.Variables, args)
  2200  		return nil
  2201  	}
  2202  	fmtstr, args := parseFormatArg(args)
  2203  	val, err := t.client.EvalVariable(ctx.Scope, args, t.loadConfig())
  2204  	if err != nil {
  2205  		return err
  2206  	}
  2207  
  2208  	t.stdout.pw.PageMaybe(nil)
  2209  
  2210  	fmt.Fprintln(t.stdout, val.MultilineString("", fmtstr))
  2211  
  2212  	if val.Kind == reflect.Chan {
  2213  		fmt.Fprintln(t.stdout)
  2214  		gs, _, _, _, err := t.client.ListGoroutinesWithFilter(0, maxPrintVarChanGoroutines, []api.ListGoroutinesFilter{{Kind: api.GoroutineWaitingOnChannel, Arg: fmt.Sprintf("*(*%q)(%#x)", val.Type, val.Addr)}}, nil, &ctx.Scope)
  2215  		if err != nil {
  2216  			fmt.Fprintf(t.stdout, "Error reading channel wait queue: %v", err)
  2217  		} else {
  2218  			fmt.Fprintln(t.stdout, "Goroutines waiting on this channel:")
  2219  			state, err := t.client.GetState()
  2220  			if err != nil {
  2221  				fmt.Fprintf(t.stdout, "Error printing channel wait queue: %v", err)
  2222  			}
  2223  			var done bool
  2224  			c.printGoroutines(t, ctx, "", gs, api.FglUserCurrent, 0, 0, "", &done, state)
  2225  		}
  2226  	}
  2227  	return nil
  2228  }
  2229  
  2230  func whatisCommand(t *Term, ctx callContext, args string) error {
  2231  	if len(args) == 0 {
  2232  		return fmt.Errorf("not enough arguments")
  2233  	}
  2234  	val, err := t.client.EvalVariable(ctx.Scope, args, ShortLoadConfig)
  2235  	if err != nil {
  2236  		return err
  2237  	}
  2238  	if val.Flags&api.VariableCPURegister != 0 {
  2239  		fmt.Fprintln(t.stdout, "CPU Register")
  2240  		return nil
  2241  	}
  2242  	if val.Type != "" {
  2243  		fmt.Fprintln(t.stdout, val.Type)
  2244  	}
  2245  	if val.RealType != val.Type {
  2246  		fmt.Fprintf(t.stdout, "Real type: %s\n", val.RealType)
  2247  	}
  2248  	if val.Kind == reflect.Interface && len(val.Children) > 0 {
  2249  		fmt.Fprintf(t.stdout, "Concrete type: %s\n", val.Children[0].Type)
  2250  	}
  2251  	if t.conf.ShowLocationExpr && val.LocationExpr != "" {
  2252  		fmt.Fprintf(t.stdout, "location: %s\n", val.LocationExpr)
  2253  	}
  2254  	return nil
  2255  }
  2256  
  2257  func setVar(t *Term, ctx callContext, args string) error {
  2258  	// HACK: in go '=' is not an operator, we detect the error and try to recover from it by splitting the input string
  2259  	_, err := parser.ParseExpr(args)
  2260  	if err == nil {
  2261  		return fmt.Errorf("syntax error '=' not found")
  2262  	}
  2263  
  2264  	el, ok := err.(scanner.ErrorList)
  2265  	if !ok || el[0].Msg != "expected '==', found '='" {
  2266  		return err
  2267  	}
  2268  
  2269  	lexpr := args[:el[0].Pos.Offset]
  2270  	rexpr := args[el[0].Pos.Offset+1:]
  2271  	return t.client.SetVariable(ctx.Scope, lexpr, rexpr)
  2272  }
  2273  
  2274  func (t *Term) printFilteredVariables(varType string, vars []api.Variable, filter string, cfg api.LoadConfig) error {
  2275  	reg, err := regexp.Compile(filter)
  2276  	if err != nil {
  2277  		return err
  2278  	}
  2279  	match := false
  2280  	t.stdout.pw.PageMaybe(nil)
  2281  	for _, v := range vars {
  2282  		if reg == nil || reg.Match([]byte(v.Name)) {
  2283  			match = true
  2284  			name := v.Name
  2285  			if v.Flags&api.VariableShadowed != 0 {
  2286  				name = "(" + name + ")"
  2287  			}
  2288  			if cfg == ShortLoadConfig {
  2289  				fmt.Fprintf(t.stdout, "%s = %s\n", name, v.SinglelineString())
  2290  			} else {
  2291  				fmt.Fprintf(t.stdout, "%s = %s\n", name, v.MultilineString("", ""))
  2292  			}
  2293  		}
  2294  	}
  2295  	if !match {
  2296  		fmt.Fprintf(t.stdout, "(no %s)\n", varType)
  2297  	}
  2298  	return nil
  2299  }
  2300  
  2301  func (t *Term) printSortedStrings(v []string, err error) error {
  2302  	if err != nil {
  2303  		return err
  2304  	}
  2305  	sort.Strings(v)
  2306  	done := false
  2307  	t.stdout.pw.PageMaybe(func() { done = false })
  2308  	for _, d := range v {
  2309  		if done {
  2310  			break
  2311  		}
  2312  		fmt.Fprintln(t.stdout, d)
  2313  	}
  2314  	return nil
  2315  }
  2316  
  2317  func sources(t *Term, ctx callContext, args string) error {
  2318  	return t.printSortedStrings(t.client.ListSources(args))
  2319  }
  2320  
  2321  func packages(t *Term, ctx callContext, args string) error {
  2322  	info, err := t.client.ListPackagesBuildInfo(args, false)
  2323  	if err != nil {
  2324  		return err
  2325  	}
  2326  	pkgs := make([]string, 0, len(info))
  2327  	for _, i := range info {
  2328  		pkgs = append(pkgs, i.ImportPath)
  2329  	}
  2330  	return t.printSortedStrings(pkgs, nil)
  2331  }
  2332  
  2333  func funcs(t *Term, ctx callContext, args string) error {
  2334  	return t.printSortedStrings(t.client.ListFunctions(args))
  2335  }
  2336  
  2337  func types(t *Term, ctx callContext, args string) error {
  2338  	return t.printSortedStrings(t.client.ListTypes(args))
  2339  }
  2340  
  2341  func parseVarArguments(args string, t *Term) (filter string, cfg api.LoadConfig) {
  2342  	if v := config.Split2PartsBySpace(args); len(v) >= 1 && v[0] == "-v" {
  2343  		if len(v) == 2 {
  2344  			return v[1], t.loadConfig()
  2345  		} else {
  2346  			return "", t.loadConfig()
  2347  		}
  2348  	}
  2349  	return args, ShortLoadConfig
  2350  }
  2351  
  2352  func args(t *Term, ctx callContext, args string) error {
  2353  	filter, cfg := parseVarArguments(args, t)
  2354  	if ctx.Prefix == onPrefix {
  2355  		if filter != "" {
  2356  			return fmt.Errorf("filter not supported on breakpoint")
  2357  		}
  2358  		ctx.Breakpoint.LoadArgs = &cfg
  2359  		return nil
  2360  	}
  2361  	vars, err := t.client.ListFunctionArgs(ctx.Scope, cfg)
  2362  	if err != nil {
  2363  		return err
  2364  	}
  2365  	return t.printFilteredVariables("args", vars, filter, cfg)
  2366  }
  2367  
  2368  func locals(t *Term, ctx callContext, args string) error {
  2369  	filter, cfg := parseVarArguments(args, t)
  2370  	if ctx.Prefix == onPrefix {
  2371  		if filter != "" {
  2372  			return fmt.Errorf("filter not supported on breakpoint")
  2373  		}
  2374  		ctx.Breakpoint.LoadLocals = &cfg
  2375  		return nil
  2376  	}
  2377  	locals, err := t.client.ListLocalVariables(ctx.Scope, cfg)
  2378  	if err != nil {
  2379  		return err
  2380  	}
  2381  	return t.printFilteredVariables("locals", locals, filter, cfg)
  2382  }
  2383  
  2384  func vars(t *Term, ctx callContext, args string) error {
  2385  	filter, cfg := parseVarArguments(args, t)
  2386  	vars, err := t.client.ListPackageVariables(filter, cfg)
  2387  	if err != nil {
  2388  		return err
  2389  	}
  2390  	return t.printFilteredVariables("vars", vars, filter, cfg)
  2391  }
  2392  
  2393  func regs(t *Term, ctx callContext, args string) error {
  2394  	includeFp := false
  2395  	if args == "-a" {
  2396  		includeFp = true
  2397  	}
  2398  	var regs api.Registers
  2399  	var err error
  2400  	if ctx.Scope.GoroutineID < 0 && ctx.Scope.Frame == 0 {
  2401  		regs, err = t.client.ListThreadRegisters(0, includeFp)
  2402  	} else {
  2403  		regs, err = t.client.ListScopeRegisters(ctx.Scope, includeFp)
  2404  	}
  2405  	if err != nil {
  2406  		return err
  2407  	}
  2408  	fmt.Fprintln(t.stdout, regs)
  2409  	return nil
  2410  }
  2411  
  2412  func stackCommand(t *Term, ctx callContext, args string) error {
  2413  	sa, err := parseStackArgs(args)
  2414  	if err != nil {
  2415  		return err
  2416  	}
  2417  	if ctx.Prefix == onPrefix {
  2418  		ctx.Breakpoint.Stacktrace = sa.depth
  2419  		return nil
  2420  	}
  2421  	var cfg *api.LoadConfig
  2422  	if sa.full {
  2423  		cfg = &ShortLoadConfig
  2424  	}
  2425  	stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, sa.depth, sa.opts, cfg)
  2426  	if err != nil {
  2427  		return err
  2428  	}
  2429  	t.stdout.pw.PageMaybe(nil)
  2430  	printStack(t, t.stdout, stack, "", sa.offsets)
  2431  	if sa.ancestors > 0 {
  2432  		ancestors, err := t.client.Ancestors(ctx.Scope.GoroutineID, sa.ancestors, sa.ancestorDepth)
  2433  		if err != nil {
  2434  			return err
  2435  		}
  2436  		for _, ancestor := range ancestors {
  2437  			fmt.Fprintf(t.stdout, "Created by Goroutine %d:\n", ancestor.ID)
  2438  			if ancestor.Unreadable != "" {
  2439  				fmt.Fprintf(t.stdout, "\t%s\n", ancestor.Unreadable)
  2440  				continue
  2441  			}
  2442  			printStack(t, t.stdout, ancestor.Stack, "\t", false)
  2443  		}
  2444  	}
  2445  	return nil
  2446  }
  2447  
  2448  type stackArgs struct {
  2449  	depth   int
  2450  	full    bool
  2451  	offsets bool
  2452  	opts    api.StacktraceOptions
  2453  
  2454  	ancestors     int
  2455  	ancestorDepth int
  2456  }
  2457  
  2458  func parseStackArgs(argstr string) (stackArgs, error) {
  2459  	r := stackArgs{
  2460  		depth: 50,
  2461  		full:  false,
  2462  	}
  2463  	if argstr != "" {
  2464  		args := strings.Split(argstr, " ")
  2465  		for i := 0; i < len(args); i++ {
  2466  			numarg := func(name string) (int, error) {
  2467  				if i >= len(args) {
  2468  					return 0, fmt.Errorf("expected number after %s", name)
  2469  				}
  2470  				n, err := strconv.Atoi(args[i])
  2471  				if err != nil {
  2472  					return 0, fmt.Errorf("expected number after %s: %v", name, err)
  2473  				}
  2474  				return n, nil
  2475  			}
  2476  			switch args[i] {
  2477  			case "-full":
  2478  				r.full = true
  2479  			case "-offsets":
  2480  				r.offsets = true
  2481  			case "-defer":
  2482  				r.opts |= api.StacktraceReadDefers
  2483  			case "-mode":
  2484  				i++
  2485  				if i >= len(args) {
  2486  					return stackArgs{}, fmt.Errorf("expected normal, simple or fromg after -mode")
  2487  				}
  2488  				switch args[i] {
  2489  				case "normal":
  2490  					r.opts &^= api.StacktraceSimple
  2491  					r.opts &^= api.StacktraceG
  2492  				case "simple":
  2493  					r.opts |= api.StacktraceSimple
  2494  				case "fromg":
  2495  					r.opts |= api.StacktraceG | api.StacktraceSimple
  2496  				default:
  2497  					return stackArgs{}, fmt.Errorf("expected normal, simple or fromg after -mode")
  2498  				}
  2499  			case "-a":
  2500  				i++
  2501  				n, err := numarg("-a")
  2502  				if err != nil {
  2503  					return stackArgs{}, err
  2504  				}
  2505  				r.ancestors = n
  2506  			case "-adepth":
  2507  				i++
  2508  				n, err := numarg("-adepth")
  2509  				if err != nil {
  2510  					return stackArgs{}, err
  2511  				}
  2512  				r.ancestorDepth = n
  2513  			default:
  2514  				n, err := strconv.Atoi(args[i])
  2515  				if err != nil {
  2516  					return stackArgs{}, fmt.Errorf("depth must be a number")
  2517  				}
  2518  				r.depth = n
  2519  			}
  2520  		}
  2521  	}
  2522  	if r.ancestors > 0 && r.ancestorDepth == 0 {
  2523  		r.ancestorDepth = r.depth
  2524  	}
  2525  	return r, nil
  2526  }
  2527  
  2528  // getLocation returns the current location or the locations specified by the argument.
  2529  // getLocation is used to process the argument of list and edit commands.
  2530  func getLocation(t *Term, ctx callContext, args string, showContext bool) (file string, lineno int, showarrow bool, err error) {
  2531  	switch {
  2532  	case len(args) == 0 && !ctx.scoped():
  2533  		state, err := t.client.GetState()
  2534  		if err != nil {
  2535  			return "", 0, false, err
  2536  		}
  2537  		if showContext {
  2538  			printcontext(t, state)
  2539  		}
  2540  		if state.SelectedGoroutine != nil {
  2541  			return state.SelectedGoroutine.CurrentLoc.File, state.SelectedGoroutine.CurrentLoc.Line, true, nil
  2542  		}
  2543  		return state.CurrentThread.File, state.CurrentThread.Line, true, nil
  2544  
  2545  	case len(args) == 0 && ctx.scoped():
  2546  		locs, err := t.client.Stacktrace(ctx.Scope.GoroutineID, ctx.Scope.Frame, 0, nil)
  2547  		if err != nil {
  2548  			return "", 0, false, err
  2549  		}
  2550  		if ctx.Scope.Frame >= len(locs) {
  2551  			return "", 0, false, fmt.Errorf("Frame %d does not exist in goroutine %d", ctx.Scope.Frame, ctx.Scope.GoroutineID)
  2552  		}
  2553  		loc := locs[ctx.Scope.Frame]
  2554  		gid := ctx.Scope.GoroutineID
  2555  		if gid < 0 {
  2556  			state, err := t.client.GetState()
  2557  			if err != nil {
  2558  				return "", 0, false, err
  2559  			}
  2560  			if state.SelectedGoroutine != nil {
  2561  				gid = state.SelectedGoroutine.ID
  2562  			}
  2563  		}
  2564  		if showContext {
  2565  			fmt.Fprintf(t.stdout, "Goroutine %d frame %d at %s:%d (PC: %#x)\n", gid, ctx.Scope.Frame, loc.File, loc.Line, loc.PC)
  2566  		}
  2567  		return loc.File, loc.Line, true, nil
  2568  
  2569  	default:
  2570  		locs, _, err := t.client.FindLocation(ctx.Scope, args, false, t.substitutePathRules())
  2571  		if err != nil {
  2572  			return "", 0, false, err
  2573  		}
  2574  		if len(locs) > 1 {
  2575  			return "", 0, false, locspec.AmbiguousLocationError{Location: args, CandidatesLocation: locs}
  2576  		}
  2577  		loc := locs[0]
  2578  		if showContext {
  2579  			fmt.Fprintf(t.stdout, "Showing %s:%d (PC: %#x)\n", loc.File, loc.Line, loc.PC)
  2580  		}
  2581  		return loc.File, loc.Line, false, nil
  2582  	}
  2583  }
  2584  
  2585  func listCommand(t *Term, ctx callContext, args string) error {
  2586  	file, lineno, showarrow, err := getLocation(t, ctx, args, true)
  2587  	if err != nil {
  2588  		return err
  2589  	}
  2590  	return printfile(t, file, lineno, showarrow)
  2591  }
  2592  
  2593  func (c *Commands) sourceCommand(t *Term, ctx callContext, args string) error {
  2594  	if len(args) == 0 {
  2595  		return fmt.Errorf("wrong number of arguments: source <filename>")
  2596  	}
  2597  
  2598  	if args == "-" {
  2599  		return t.starlarkEnv.REPL()
  2600  	}
  2601  
  2602  	if runtime.GOOS != "windows" && strings.HasPrefix(args, "~") {
  2603  		home, err := os.UserHomeDir()
  2604  		if err == nil {
  2605  			if args == "~" {
  2606  				args = home
  2607  			} else if strings.HasPrefix(args, "~/") {
  2608  				args = filepath.Join(home, args[2:])
  2609  			}
  2610  		}
  2611  	}
  2612  
  2613  	if filepath.Ext(args) == ".star" {
  2614  		_, err := t.starlarkEnv.Execute(args, nil, "main", nil)
  2615  		return err
  2616  	}
  2617  
  2618  	return c.executeFile(t, args)
  2619  }
  2620  
  2621  var errDisasmUsage = errors.New("wrong number of arguments: disassemble [-a <start> <end>] [-l <locspec>]")
  2622  
  2623  func disassCommand(t *Term, ctx callContext, args string) error {
  2624  	var cmd, rest string
  2625  
  2626  	if args != "" {
  2627  		argv := config.Split2PartsBySpace(args)
  2628  		if len(argv) != 2 {
  2629  			return errDisasmUsage
  2630  		}
  2631  		cmd = argv[0]
  2632  		rest = argv[1]
  2633  	}
  2634  
  2635  	t.stdout.pw.PageMaybe(nil)
  2636  
  2637  	flavor := t.conf.GetDisassembleFlavour()
  2638  
  2639  	var disasm api.AsmInstructions
  2640  	var disasmErr error
  2641  
  2642  	switch cmd {
  2643  	case "":
  2644  		locs, _, err := t.client.FindLocation(ctx.Scope, "+0", true, t.substitutePathRules())
  2645  		if err != nil {
  2646  			return err
  2647  		}
  2648  		disasm, disasmErr = t.client.DisassemblePC(ctx.Scope, locs[0].PC, flavor)
  2649  	case "-a":
  2650  		v := config.Split2PartsBySpace(rest)
  2651  		if len(v) != 2 {
  2652  			return errDisasmUsage
  2653  		}
  2654  		startpc, err := strconv.ParseInt(v[0], 0, 64)
  2655  		if err != nil {
  2656  			return fmt.Errorf("wrong argument: %q is not a number", v[0])
  2657  		}
  2658  		endpc, err := strconv.ParseInt(v[1], 0, 64)
  2659  		if err != nil {
  2660  			return fmt.Errorf("wrong argument: %q is not a number", v[1])
  2661  		}
  2662  		disasm, disasmErr = t.client.DisassembleRange(ctx.Scope, uint64(startpc), uint64(endpc), flavor)
  2663  	case "-l":
  2664  		locs, _, err := t.client.FindLocation(ctx.Scope, rest, true, t.substitutePathRules())
  2665  		if err != nil {
  2666  			return err
  2667  		}
  2668  		if len(locs) != 1 {
  2669  			return errors.New("expression specifies multiple locations")
  2670  		}
  2671  		disasm, disasmErr = t.client.DisassemblePC(ctx.Scope, locs[0].PC, flavor)
  2672  	default:
  2673  		return errDisasmUsage
  2674  	}
  2675  
  2676  	if disasmErr != nil {
  2677  		return disasmErr
  2678  	}
  2679  
  2680  	disasmPrint(disasm, t.stdout, true)
  2681  
  2682  	return nil
  2683  }
  2684  
  2685  func libraries(t *Term, ctx callContext, args string) error {
  2686  	libs, err := t.client.ListDynamicLibraries()
  2687  	if err != nil {
  2688  		return err
  2689  	}
  2690  	d := digits(len(libs))
  2691  	for i := range libs {
  2692  		fmt.Fprintf(t.stdout, "%"+strconv.Itoa(d)+"d. %#x %s\n", i, libs[i].Address, libs[i].Path)
  2693  		if libs[i].LoadError != "" {
  2694  			fmt.Fprintf(t.stdout, "    Load error: %s\n", libs[i].LoadError)
  2695  		}
  2696  	}
  2697  	return nil
  2698  }
  2699  
  2700  func digits(n int) int {
  2701  	if n <= 0 {
  2702  		return 1
  2703  	}
  2704  	return int(math.Floor(math.Log10(float64(n)))) + 1
  2705  }
  2706  
  2707  func printStack(t *Term, out io.Writer, stack []api.Stackframe, ind string, offsets bool) {
  2708  	api.PrintStack(t.formatPath, out, stack, ind, offsets, t.stackTraceColors, func(api.Stackframe) bool { return true })
  2709  }
  2710  
  2711  func printcontext(t *Term, state *api.DebuggerState) {
  2712  	if t.IsTraceNonInteractive() {
  2713  		// If we're just running the `trace` subcommand there isn't any need
  2714  		// to print out the rest of the state below.
  2715  		for i := range state.Threads {
  2716  			if state.Threads[i].Breakpoint != nil {
  2717  				printcontextThread(t, state.Threads[i])
  2718  			}
  2719  		}
  2720  		return
  2721  	}
  2722  
  2723  	if state.Pid != t.oldPid {
  2724  		if t.oldPid != 0 {
  2725  			fmt.Fprintf(t.stdout, "Switch target process from %d to %d (%s)\n", t.oldPid, state.Pid, state.TargetCommandLine)
  2726  		}
  2727  		t.oldPid = state.Pid
  2728  	}
  2729  	for i := range state.Threads {
  2730  		if (state.CurrentThread != nil) && (state.Threads[i].ID == state.CurrentThread.ID) {
  2731  			continue
  2732  		}
  2733  		if state.Threads[i].Breakpoint != nil {
  2734  			printcontextThread(t, state.Threads[i])
  2735  		}
  2736  	}
  2737  
  2738  	if state.CurrentThread == nil {
  2739  		fmt.Fprintln(t.stdout, "No current thread available")
  2740  		return
  2741  	}
  2742  
  2743  	var th *api.Thread
  2744  	if state.SelectedGoroutine == nil {
  2745  		th = state.CurrentThread
  2746  	} else {
  2747  		for i := range state.Threads {
  2748  			if state.Threads[i].ID == state.SelectedGoroutine.ThreadID {
  2749  				th = state.Threads[i]
  2750  				break
  2751  			}
  2752  		}
  2753  		if th == nil {
  2754  			printcontextLocation(t, state.SelectedGoroutine.CurrentLoc)
  2755  			return
  2756  		}
  2757  	}
  2758  
  2759  	if th.File == "" {
  2760  		fmt.Fprintf(t.stdout, "Stopped at: 0x%x\n", state.CurrentThread.PC)
  2761  		t.stdout.ColorizePrint("", bytes.NewReader([]byte("no source available")), 1, 10, 1)
  2762  		return
  2763  	}
  2764  
  2765  	printcontextThread(t, th)
  2766  
  2767  	if state.When != "" {
  2768  		fmt.Fprintln(t.stdout, state.When)
  2769  	}
  2770  
  2771  	for _, watchpoint := range state.WatchOutOfScope {
  2772  		fmt.Fprintf(t.stdout, "%s went out of scope and was cleared\n", formatBreakpointName(watchpoint, true))
  2773  	}
  2774  }
  2775  
  2776  func printcontextLocation(t *Term, loc api.Location) {
  2777  	fmt.Fprintf(t.stdout, "> %s() %s:%d (PC: %#v)\n", loc.Function.Name(), t.formatPath(loc.File), loc.Line, loc.PC)
  2778  	if loc.Function != nil && loc.Function.Optimized {
  2779  		fmt.Fprintln(t.stdout, optimizedFunctionWarning)
  2780  	}
  2781  }
  2782  
  2783  func printReturnValues(t *Term, th *api.Thread) {
  2784  	if th.ReturnValues == nil {
  2785  		return
  2786  	}
  2787  	fmt.Fprintln(t.stdout, "Values returned:")
  2788  	for _, v := range th.ReturnValues {
  2789  		fmt.Fprintf(t.stdout, "\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
  2790  	}
  2791  	fmt.Fprintln(t.stdout)
  2792  }
  2793  
  2794  func printcontextThread(t *Term, th *api.Thread) {
  2795  	fn := th.Function
  2796  
  2797  	if th.Breakpoint == nil {
  2798  		printcontextLocation(t, api.Location{PC: th.PC, File: th.File, Line: th.Line, Function: th.Function})
  2799  		printReturnValues(t, th)
  2800  		return
  2801  	}
  2802  
  2803  	args := ""
  2804  	var hasReturnValue bool
  2805  	if th.BreakpointInfo != nil && th.Breakpoint.LoadArgs != nil && *th.Breakpoint.LoadArgs == ShortLoadConfig {
  2806  		var arg []string
  2807  		for _, ar := range th.BreakpointInfo.Arguments {
  2808  			// For AI compatibility return values are included in the
  2809  			// argument list. This is a relic of the dark ages when the
  2810  			// Go debug information did not distinguish between the two.
  2811  			// Filter them out here instead, so during trace operations
  2812  			// they are not printed as an argument.
  2813  			if (ar.Flags & api.VariableArgument) != 0 {
  2814  				arg = append(arg, ar.SinglelineString())
  2815  			}
  2816  			if (ar.Flags & api.VariableReturnArgument) != 0 {
  2817  				hasReturnValue = true
  2818  			}
  2819  		}
  2820  		args = strings.Join(arg, ", ")
  2821  	}
  2822  
  2823  	bpname := ""
  2824  	if th.Breakpoint.WatchExpr != "" {
  2825  		bpname = fmt.Sprintf("watchpoint on [%s] ", th.Breakpoint.WatchExpr)
  2826  	} else if th.Breakpoint.Name != "" {
  2827  		bpname = fmt.Sprintf("[%s] ", th.Breakpoint.Name)
  2828  	} else if !th.Breakpoint.Tracepoint {
  2829  		bpname = fmt.Sprintf("[Breakpoint %d] ", th.Breakpoint.ID)
  2830  	}
  2831  
  2832  	if th.Breakpoint.Tracepoint || th.Breakpoint.TraceReturn {
  2833  		printTracepoint(t, th, bpname, fn, args, hasReturnValue)
  2834  		return
  2835  	}
  2836  
  2837  	if hitCount, ok := th.Breakpoint.HitCount[strconv.FormatInt(th.GoroutineID, 10)]; ok {
  2838  		fmt.Fprintf(t.stdout, "> %s%s(%s) %s:%d (hits goroutine(%d):%d total:%d) (PC: %#v)\n",
  2839  			bpname,
  2840  			fn.Name(),
  2841  			args,
  2842  			t.formatPath(th.File),
  2843  			th.Line,
  2844  			th.GoroutineID,
  2845  			hitCount,
  2846  			th.Breakpoint.TotalHitCount,
  2847  			th.PC)
  2848  	} else {
  2849  		fmt.Fprintf(t.stdout, "> %s%s(%s) %s:%d (hits total:%d) (PC: %#v)\n",
  2850  			bpname,
  2851  			fn.Name(),
  2852  			args,
  2853  			t.formatPath(th.File),
  2854  			th.Line,
  2855  			th.Breakpoint.TotalHitCount,
  2856  			th.PC)
  2857  	}
  2858  	if th.Function != nil && th.Function.Optimized {
  2859  		fmt.Fprintln(t.stdout, optimizedFunctionWarning)
  2860  	}
  2861  
  2862  	printReturnValues(t, th)
  2863  	printBreakpointInfo(t, th, false)
  2864  }
  2865  
  2866  func printBreakpointInfo(t *Term, th *api.Thread, tracepointOnNewline bool) {
  2867  	if th.BreakpointInfo == nil {
  2868  		return
  2869  	}
  2870  	bp := th.Breakpoint
  2871  	bpi := th.BreakpointInfo
  2872  
  2873  	if bp.TraceReturn {
  2874  		return
  2875  	}
  2876  
  2877  	didprintnl := tracepointOnNewline
  2878  	tracepointnl := func() {
  2879  		if !bp.Tracepoint || didprintnl {
  2880  			return
  2881  		}
  2882  		didprintnl = true
  2883  		fmt.Fprintln(t.stdout)
  2884  	}
  2885  
  2886  	if bpi.Goroutine != nil {
  2887  		tracepointnl()
  2888  		writeGoroutineLong(t, t.stdout, bpi.Goroutine, "\t")
  2889  	}
  2890  
  2891  	for _, v := range bpi.Variables {
  2892  		tracepointnl()
  2893  		fmt.Fprintf(t.stdout, "\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
  2894  	}
  2895  
  2896  	for _, v := range bpi.Locals {
  2897  		tracepointnl()
  2898  		if *bp.LoadLocals == longLoadConfig {
  2899  			fmt.Fprintf(t.stdout, "\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
  2900  		} else {
  2901  			fmt.Fprintf(t.stdout, "\t%s: %s\n", v.Name, v.SinglelineString())
  2902  		}
  2903  	}
  2904  
  2905  	if bp.LoadArgs != nil && *bp.LoadArgs == longLoadConfig {
  2906  		for _, v := range bpi.Arguments {
  2907  			tracepointnl()
  2908  			fmt.Fprintf(t.stdout, "\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
  2909  		}
  2910  	}
  2911  
  2912  	if bpi.Stacktrace != nil {
  2913  		tracepointnl()
  2914  		fmt.Fprintf(t.stdout, "\tStack:\n")
  2915  		printStack(t, t.stdout, bpi.Stacktrace, "\t\t", false)
  2916  	}
  2917  }
  2918  
  2919  func printTracepoint(t *Term, th *api.Thread, bpname string, fn *api.Function, args string, hasReturnValue bool) {
  2920  	if t.conf.TraceShowTimestamp {
  2921  		fmt.Fprintf(t.stdout, "%s ", time.Now().Format(time.RFC3339Nano))
  2922  	}
  2923  
  2924  	if th.Breakpoint.Tracepoint {
  2925  		fmt.Fprintf(t.stdout, "> goroutine(%d): %s%s(%s)\n", th.GoroutineID, bpname, fn.Name(), args)
  2926  		printBreakpointInfo(t, th, !hasReturnValue)
  2927  	}
  2928  	if th.Breakpoint.TraceReturn {
  2929  		retVals := make([]string, 0, len(th.ReturnValues))
  2930  		for _, v := range th.ReturnValues {
  2931  			retVals = append(retVals, v.SinglelineString())
  2932  		}
  2933  		fmt.Fprintf(t.stdout, ">> goroutine(%d): %s => (%s)\n", th.GoroutineID, fn.Name(), strings.Join(retVals, ","))
  2934  	}
  2935  	if th.Breakpoint.TraceReturn || !hasReturnValue {
  2936  		if th.BreakpointInfo != nil && th.BreakpointInfo.Stacktrace != nil {
  2937  			fmt.Fprintf(t.stdout, "\tStack:\n")
  2938  			printStack(t, t.stdout, th.BreakpointInfo.Stacktrace, "\t\t", false)
  2939  		}
  2940  	}
  2941  }
  2942  
  2943  type printPosFlags uint8
  2944  
  2945  const (
  2946  	printPosShowArrow printPosFlags = 1 << iota
  2947  	printPosStepInstruction
  2948  )
  2949  
  2950  func printPos(t *Term, th *api.Thread, flags printPosFlags) error {
  2951  	if flags&printPosStepInstruction != 0 {
  2952  		if t.conf.Position == config.PositionSource {
  2953  			return printfile(t, th.File, th.Line, flags&printPosShowArrow != 0)
  2954  		}
  2955  		return printdisass(t, th.PC)
  2956  	}
  2957  	if t.conf.Position == config.PositionDisassembly {
  2958  		return printdisass(t, th.PC)
  2959  	}
  2960  	return printfile(t, th.File, th.Line, flags&printPosShowArrow != 0)
  2961  }
  2962  
  2963  func printfile(t *Term, filename string, line int, showArrow bool) error {
  2964  	if filename == "" {
  2965  		return nil
  2966  	}
  2967  
  2968  	lineCount := t.conf.GetSourceListLineCount()
  2969  	arrowLine := 0
  2970  	if showArrow {
  2971  		arrowLine = line
  2972  	}
  2973  
  2974  	var file *os.File
  2975  	path := t.substitutePath(filename)
  2976  	if _, err := os.Stat(path); os.IsNotExist(err) {
  2977  		foundPath, err := debuginfod.GetSource(t.client.BuildID(), filename)
  2978  		if err == nil {
  2979  			path = foundPath
  2980  		}
  2981  	}
  2982  	file, err := os.OpenFile(path, 0, os.ModePerm)
  2983  	if err != nil {
  2984  		return err
  2985  	}
  2986  	defer file.Close()
  2987  
  2988  	fi, _ := file.Stat()
  2989  	lastModExe := t.client.LastModified()
  2990  	if fi.ModTime().After(lastModExe) {
  2991  		fmt.Fprintln(t.stdout, "Warning: listing may not match stale executable")
  2992  	}
  2993  
  2994  	return t.stdout.ColorizePrint(file.Name(), file, line-lineCount, line+lineCount+1, arrowLine)
  2995  }
  2996  
  2997  func printdisass(t *Term, pc uint64) error {
  2998  	disasm, err := t.client.DisassemblePC(api.EvalScope{GoroutineID: -1, Frame: 0, DeferredCall: 0}, pc, t.conf.GetDisassembleFlavour())
  2999  	if err != nil {
  3000  		return err
  3001  	}
  3002  
  3003  	lineCount := t.conf.GetSourceListLineCount()
  3004  
  3005  	showHeader := true
  3006  	for i := range disasm {
  3007  		if disasm[i].AtPC {
  3008  			s := i - lineCount
  3009  			if s < 0 {
  3010  				s = 0
  3011  			}
  3012  			e := i + lineCount + 1
  3013  			if e > len(disasm) {
  3014  				e = len(disasm)
  3015  			}
  3016  			showHeader = s == 0
  3017  			disasm = disasm[s:e]
  3018  			break
  3019  		}
  3020  	}
  3021  
  3022  	disasmPrint(disasm, t.stdout, showHeader)
  3023  	return nil
  3024  }
  3025  
  3026  // ExitRequestError is returned when the user
  3027  // exits Delve.
  3028  type ExitRequestError struct{}
  3029  
  3030  func (ere ExitRequestError) Error() string {
  3031  	return ""
  3032  }
  3033  
  3034  func exitCommand(t *Term, ctx callContext, args string) error {
  3035  	if args == "-c" {
  3036  		if !t.client.IsMulticlient() {
  3037  			return errors.New("not connected to an --accept-multiclient server")
  3038  		}
  3039  		bps, _ := t.client.ListBreakpoints(false)
  3040  		hasUserBreakpoints := false
  3041  		for _, bp := range bps {
  3042  			if bp.ID >= 0 {
  3043  				hasUserBreakpoints = true
  3044  				break
  3045  			}
  3046  		}
  3047  		if hasUserBreakpoints {
  3048  			yes, _ := yesno(t.line, "There are breakpoints set, do you wish to quit and continue without clearing breakpoints? [Y/n] ", "yes")
  3049  			if !yes {
  3050  				return nil
  3051  			}
  3052  		}
  3053  		t.quitContinue = true
  3054  	}
  3055  	return ExitRequestError{}
  3056  }
  3057  
  3058  func getBreakpointByIDOrName(t *Term, arg string) (*api.Breakpoint, error) {
  3059  	if id, err := strconv.Atoi(arg); err == nil {
  3060  		return t.client.GetBreakpoint(id)
  3061  	}
  3062  	return t.client.GetBreakpointByName(arg)
  3063  }
  3064  
  3065  func (c *Commands) onCmd(t *Term, ctx callContext, argstr string) error {
  3066  	args := config.Split2PartsBySpace(argstr)
  3067  
  3068  	if len(args) < 2 {
  3069  		return errors.New("not enough arguments")
  3070  	}
  3071  
  3072  	bp, err := getBreakpointByIDOrName(t, args[0])
  3073  	if err != nil {
  3074  		return err
  3075  	}
  3076  
  3077  	ctx.Prefix = onPrefix
  3078  	ctx.Breakpoint = bp
  3079  
  3080  	if args[1] == "-edit" {
  3081  		f, err := os.CreateTemp("", "dlv-on-cmd-")
  3082  		if err != nil {
  3083  			return err
  3084  		}
  3085  		defer func() {
  3086  			_ = os.Remove(f.Name())
  3087  		}()
  3088  		attrs := formatBreakpointAttrs("", ctx.Breakpoint, true)
  3089  		_, err = f.Write([]byte(strings.Join(attrs, "\n")))
  3090  		if err != nil {
  3091  			return err
  3092  		}
  3093  		err = f.Close()
  3094  		if err != nil {
  3095  			return err
  3096  		}
  3097  
  3098  		err = runEditor(f.Name())
  3099  		if err != nil {
  3100  			return err
  3101  		}
  3102  
  3103  		fin, err := os.Open(f.Name())
  3104  		if err != nil {
  3105  			return err
  3106  		}
  3107  		defer fin.Close()
  3108  
  3109  		err = c.parseBreakpointAttrs(t, ctx, fin)
  3110  		if err != nil {
  3111  			return err
  3112  		}
  3113  	} else {
  3114  		err = c.CallWithContext(args[1], t, ctx)
  3115  		if err != nil {
  3116  			return err
  3117  		}
  3118  	}
  3119  	return t.client.AmendBreakpoint(ctx.Breakpoint)
  3120  }
  3121  
  3122  func (c *Commands) parseBreakpointAttrs(t *Term, ctx callContext, r io.Reader) error {
  3123  	ctx.Breakpoint.Tracepoint = false
  3124  	ctx.Breakpoint.Goroutine = false
  3125  	ctx.Breakpoint.Stacktrace = 0
  3126  	ctx.Breakpoint.Variables = ctx.Breakpoint.Variables[:0]
  3127  	ctx.Breakpoint.Cond = ""
  3128  	ctx.Breakpoint.HitCond = ""
  3129  
  3130  	scan := bufio.NewScanner(r)
  3131  	lineno := 0
  3132  	for scan.Scan() {
  3133  		lineno++
  3134  		err := c.CallWithContext(scan.Text(), t, ctx)
  3135  		if err != nil {
  3136  			fmt.Fprintf(t.stdout, "%d: %s\n", lineno, err.Error())
  3137  		}
  3138  	}
  3139  	return scan.Err()
  3140  }
  3141  
  3142  func conditionCmd(t *Term, ctx callContext, argstr string) error {
  3143  	args := config.Split2PartsBySpace(argstr)
  3144  
  3145  	if len(args) < 2 {
  3146  		return fmt.Errorf("not enough arguments")
  3147  	}
  3148  
  3149  	hitCondPerG := args[0] == "-per-g-hitcount"
  3150  	if args[0] == "-hitcount" || hitCondPerG {
  3151  		// hitcount breakpoint
  3152  
  3153  		if ctx.Prefix == onPrefix {
  3154  			ctx.Breakpoint.HitCond = args[1]
  3155  			ctx.Breakpoint.HitCondPerG = hitCondPerG
  3156  			return nil
  3157  		}
  3158  
  3159  		args = config.Split2PartsBySpace(args[1])
  3160  		if len(args) < 2 {
  3161  			return fmt.Errorf("not enough arguments")
  3162  		}
  3163  
  3164  		bp, err := getBreakpointByIDOrName(t, args[0])
  3165  		if err != nil {
  3166  			return err
  3167  		}
  3168  
  3169  		bp.HitCond = args[1]
  3170  		bp.HitCondPerG = hitCondPerG
  3171  
  3172  		return t.client.AmendBreakpoint(bp)
  3173  	}
  3174  
  3175  	if args[0] == "-clear" {
  3176  		bp, err := getBreakpointByIDOrName(t, args[1])
  3177  		if err != nil {
  3178  			return err
  3179  		}
  3180  		bp.Cond = ""
  3181  		return t.client.AmendBreakpoint(bp)
  3182  	}
  3183  
  3184  	if ctx.Prefix == onPrefix {
  3185  		ctx.Breakpoint.Cond = argstr
  3186  		return nil
  3187  	}
  3188  
  3189  	bp, err := getBreakpointByIDOrName(t, args[0])
  3190  	if err != nil {
  3191  		return err
  3192  	}
  3193  	bp.Cond = args[1]
  3194  
  3195  	return t.client.AmendBreakpoint(bp)
  3196  }
  3197  
  3198  func (c *Commands) executeFile(t *Term, name string) error {
  3199  	fh, err := os.Open(name)
  3200  	if err != nil {
  3201  		return err
  3202  	}
  3203  	defer fh.Close()
  3204  
  3205  	scanner := bufio.NewScanner(fh)
  3206  	lineno := 0
  3207  	for scanner.Scan() {
  3208  		line := strings.TrimSpace(scanner.Text())
  3209  		lineno++
  3210  
  3211  		if line == "" || line[0] == '#' {
  3212  			continue
  3213  		}
  3214  
  3215  		if err := c.Call(line, t); err != nil {
  3216  			if _, isExitRequest := err.(ExitRequestError); isExitRequest {
  3217  				return err
  3218  			}
  3219  			fmt.Fprintf(t.stdout, "%s:%d: %v\n", name, lineno, err)
  3220  		}
  3221  	}
  3222  
  3223  	return scanner.Err()
  3224  }
  3225  
  3226  func (c *Commands) rewind(t *Term, ctx callContext, args string) error {
  3227  	c.frame = 0
  3228  	stateChan := t.client.Rewind()
  3229  	var state *api.DebuggerState
  3230  	for state = range stateChan {
  3231  		if state.Err != nil {
  3232  			return state.Err
  3233  		}
  3234  		printcontext(t, state)
  3235  	}
  3236  	printPos(t, state.CurrentThread, printPosShowArrow)
  3237  	return nil
  3238  }
  3239  
  3240  func checkpoint(t *Term, ctx callContext, args string) error {
  3241  	if args == "" {
  3242  		state, err := t.client.GetState()
  3243  		if err != nil {
  3244  			return err
  3245  		}
  3246  		var loc api.Location = api.Location{PC: state.CurrentThread.PC, File: state.CurrentThread.File, Line: state.CurrentThread.Line, Function: state.CurrentThread.Function}
  3247  		if state.SelectedGoroutine != nil {
  3248  			loc = state.SelectedGoroutine.CurrentLoc
  3249  		}
  3250  		args = fmt.Sprintf("%s() %s:%d (%#x)", loc.Function.Name(), loc.File, loc.Line, loc.PC)
  3251  	}
  3252  
  3253  	cpid, err := t.client.Checkpoint(args)
  3254  	if err != nil {
  3255  		return err
  3256  	}
  3257  
  3258  	fmt.Fprintf(t.stdout, "Checkpoint c%d created.\n", cpid)
  3259  	return nil
  3260  }
  3261  
  3262  func checkpoints(t *Term, ctx callContext, args string) error {
  3263  	cps, err := t.client.ListCheckpoints()
  3264  	if err != nil {
  3265  		return err
  3266  	}
  3267  	w := new(tabwriter.Writer)
  3268  	w.Init(t.stdout, 4, 4, 2, ' ', 0)
  3269  	fmt.Fprintln(w, "ID\tWhen\tNote")
  3270  	for _, cp := range cps {
  3271  		fmt.Fprintf(w, "c%d\t%s\t%s\n", cp.ID, cp.When, cp.Where)
  3272  	}
  3273  	w.Flush()
  3274  	return nil
  3275  }
  3276  
  3277  func clearCheckpoint(t *Term, ctx callContext, args string) error {
  3278  	if len(args) == 0 {
  3279  		return errors.New("not enough arguments to clear-checkpoint")
  3280  	}
  3281  	if args[0] != 'c' {
  3282  		return errors.New("clear-checkpoint argument must be a checkpoint ID")
  3283  	}
  3284  	id, err := strconv.Atoi(args[1:])
  3285  	if err != nil {
  3286  		return errors.New("clear-checkpoint argument must be a checkpoint ID")
  3287  	}
  3288  	return t.client.ClearCheckpoint(id)
  3289  }
  3290  
  3291  func display(t *Term, ctx callContext, args string) error {
  3292  	const (
  3293  		addOption = "-a "
  3294  		delOption = "-d "
  3295  	)
  3296  	switch {
  3297  	case args == "":
  3298  		t.printDisplays()
  3299  
  3300  	case strings.HasPrefix(args, addOption):
  3301  		args = strings.TrimSpace(args[len(addOption):])
  3302  		fmtstr, args := parseFormatArg(args)
  3303  		if args == "" {
  3304  			return fmt.Errorf("not enough arguments")
  3305  		}
  3306  		t.addDisplay(args, fmtstr)
  3307  		t.printDisplay(len(t.displays) - 1)
  3308  
  3309  	case strings.HasPrefix(args, delOption):
  3310  		args = strings.TrimSpace(args[len(delOption):])
  3311  		n, err := strconv.Atoi(args)
  3312  		if err != nil {
  3313  			return fmt.Errorf("%q is not a number", args)
  3314  		}
  3315  		return t.removeDisplay(n)
  3316  
  3317  	default:
  3318  		return fmt.Errorf("wrong arguments")
  3319  	}
  3320  	return nil
  3321  }
  3322  
  3323  func dump(t *Term, ctx callContext, args string) error {
  3324  	if args == "" {
  3325  		return fmt.Errorf("not enough arguments")
  3326  	}
  3327  	dumpState, err := t.client.CoreDumpStart(args)
  3328  	if err != nil {
  3329  		return err
  3330  	}
  3331  	for {
  3332  		if dumpState.ThreadsDone != dumpState.ThreadsTotal {
  3333  			fmt.Fprintf(t.stdout, "\rDumping threads %d / %d...", dumpState.ThreadsDone, dumpState.ThreadsTotal)
  3334  		} else {
  3335  			fmt.Fprintf(t.stdout, "\rDumping memory %d / %d...", dumpState.MemDone, dumpState.MemTotal)
  3336  		}
  3337  		if !dumpState.Dumping {
  3338  			break
  3339  		}
  3340  		dumpState = t.client.CoreDumpWait(1000)
  3341  	}
  3342  	fmt.Fprintf(t.stdout, "\n")
  3343  	if dumpState.Err != "" {
  3344  		fmt.Fprintf(t.stdout, "error dumping: %s\n", dumpState.Err)
  3345  	} else if !dumpState.AllDone {
  3346  		fmt.Fprintf(t.stdout, "canceled\n")
  3347  	} else if dumpState.MemDone != dumpState.MemTotal {
  3348  		fmt.Fprintf(t.stdout, "Core dump could be incomplete\n")
  3349  	}
  3350  	return nil
  3351  }
  3352  
  3353  func transcript(t *Term, ctx callContext, args string) error {
  3354  	argv := strings.SplitN(args, " ", -1)
  3355  	truncate := false
  3356  	fileOnly := false
  3357  	disable := false
  3358  	path := ""
  3359  	for _, arg := range argv {
  3360  		switch arg {
  3361  		case "-x":
  3362  			fileOnly = true
  3363  		case "-t":
  3364  			truncate = true
  3365  		case "-off":
  3366  			disable = true
  3367  		default:
  3368  			if path != "" || strings.HasPrefix(arg, "-") {
  3369  				return fmt.Errorf("unrecognized option %q", arg)
  3370  			} else {
  3371  				path = arg
  3372  			}
  3373  		}
  3374  	}
  3375  
  3376  	if disable {
  3377  		if path != "" {
  3378  			return errors.New("-o option specified with an output path")
  3379  		}
  3380  		return t.stdout.CloseTranscript()
  3381  	}
  3382  
  3383  	if path == "" {
  3384  		return errors.New("no output path specified")
  3385  	}
  3386  
  3387  	flags := os.O_APPEND | os.O_WRONLY | os.O_CREATE
  3388  	if truncate {
  3389  		flags |= os.O_TRUNC
  3390  	}
  3391  	fh, err := os.OpenFile(path, flags, 0660)
  3392  	if err != nil {
  3393  		return err
  3394  	}
  3395  
  3396  	if err := t.stdout.CloseTranscript(); err != nil {
  3397  		return err
  3398  	}
  3399  
  3400  	t.stdout.TranscribeTo(fh, fileOnly)
  3401  	return nil
  3402  }
  3403  
  3404  func target(t *Term, ctx callContext, args string) error {
  3405  	argv := config.Split2PartsBySpace(args)
  3406  	switch argv[0] {
  3407  	case "list":
  3408  		tgts, err := t.client.ListTargets()
  3409  		if err != nil {
  3410  			return err
  3411  		}
  3412  		w := new(tabwriter.Writer)
  3413  		w.Init(t.stdout, 4, 4, 2, ' ', 0)
  3414  		for _, tgt := range tgts {
  3415  			selected := ""
  3416  			if tgt.Pid == t.oldPid {
  3417  				selected = "*"
  3418  			}
  3419  			fmt.Fprintf(w, "%s\t%d\t%s\n", selected, tgt.Pid, tgt.CmdLine)
  3420  		}
  3421  		w.Flush()
  3422  		return nil
  3423  	case "follow-exec":
  3424  		if len(argv) == 1 {
  3425  			if t.client.FollowExecEnabled() {
  3426  				fmt.Fprintf(t.stdout, "Follow exec is enabled.\n")
  3427  			} else {
  3428  				fmt.Fprintf(t.stdout, "Follow exec is disabled.\n")
  3429  			}
  3430  			return nil
  3431  		}
  3432  		argv = config.Split2PartsBySpace(argv[1])
  3433  		switch argv[0] {
  3434  		case "-on":
  3435  			var regex string
  3436  			if len(argv) == 2 {
  3437  				regex = argv[1]
  3438  			}
  3439  			t.client.FollowExec(true, regex)
  3440  		case "-off":
  3441  			if len(argv) > 1 {
  3442  				return errors.New("too many arguments")
  3443  			}
  3444  			t.client.FollowExec(false, "")
  3445  		default:
  3446  			return fmt.Errorf("unknown argument %q to 'target follow-exec'", argv[0])
  3447  		}
  3448  		return nil
  3449  	case "switch":
  3450  		tgts, err := t.client.ListTargets()
  3451  		if err != nil {
  3452  			return err
  3453  		}
  3454  		pid, err := strconv.Atoi(argv[1])
  3455  		if err != nil {
  3456  			return err
  3457  		}
  3458  		found := false
  3459  		for _, tgt := range tgts {
  3460  			if tgt.Pid == pid {
  3461  				found = true
  3462  				t.client.SwitchThread(tgt.CurrentThread.ID)
  3463  			}
  3464  		}
  3465  		if !found {
  3466  			return fmt.Errorf("could not find target %d", pid)
  3467  		}
  3468  		return nil
  3469  	case "":
  3470  		return errors.New("not enough arguments for 'target'")
  3471  	default:
  3472  		return fmt.Errorf("unknown command 'target %s'", argv[0])
  3473  	}
  3474  }
  3475  
  3476  func formatBreakpointName(bp *api.Breakpoint, upcase bool) string {
  3477  	thing := "breakpoint"
  3478  	if bp.Tracepoint {
  3479  		thing = "tracepoint"
  3480  	}
  3481  	if bp.WatchExpr != "" {
  3482  		thing = "watchpoint"
  3483  	}
  3484  	if upcase {
  3485  		thing = strings.Title(thing)
  3486  	}
  3487  	id := bp.Name
  3488  	if id == "" {
  3489  		id = strconv.Itoa(bp.ID)
  3490  	}
  3491  	if bp.WatchExpr != "" && bp.WatchExpr != bp.Name {
  3492  		return fmt.Sprintf("%s %s on [%s]", thing, id, bp.WatchExpr)
  3493  	}
  3494  	return fmt.Sprintf("%s %s", thing, id)
  3495  }
  3496  
  3497  func (t *Term) formatBreakpointLocation(bp *api.Breakpoint) string {
  3498  	var out bytes.Buffer
  3499  	if len(bp.Addrs) > 0 {
  3500  		for i, addr := range bp.Addrs {
  3501  			if i == 0 {
  3502  				fmt.Fprintf(&out, "%#x", addr)
  3503  			} else {
  3504  				fmt.Fprintf(&out, ",%#x", addr)
  3505  			}
  3506  		}
  3507  	} else {
  3508  		// In case we are connecting to an older version of delve that does not return the Addrs field.
  3509  		fmt.Fprintf(&out, "%#x", bp.Addr)
  3510  	}
  3511  	if bp.WatchExpr == "" {
  3512  		fmt.Fprintf(&out, " for ")
  3513  		p := t.formatPath(bp.File)
  3514  		if bp.FunctionName != "" {
  3515  			fmt.Fprintf(&out, "%s() ", bp.FunctionName)
  3516  		}
  3517  		fmt.Fprintf(&out, "%s:%d", p, bp.Line)
  3518  	}
  3519  	return out.String()
  3520  }
  3521  
  3522  func shouldAskToSuspendBreakpoint(t *Term) bool {
  3523  	fns, _ := t.client.ListFunctions(`^plugin\.Open$`)
  3524  	_, err := t.client.GetState()
  3525  	return len(fns) > 0 || isErrProcessExited(err) || t.client.FollowExecEnabled()
  3526  }