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