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