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