command.go 55.1 KB
Newer Older
1
// Package terminal implements functions for responding to user
D
Dan Mace 已提交
2 3 4 5 6
// input and dispatching to appropriate backend commands.
package terminal

import (
	"bufio"
A
aarzilli 已提交
7
	"errors"
D
Dan Mace 已提交
8
	"fmt"
9 10
	"go/parser"
	"go/scanner"
11
	"io"
12
	"math"
D
Dan Mace 已提交
13
	"os"
B
Ben Cotterell 已提交
14
	"os/exec"
A
aarzilli 已提交
15
	"reflect"
D
Dan Mace 已提交
16 17 18 19
	"regexp"
	"sort"
	"strconv"
	"strings"
D
Derek Parker 已提交
20
	"text/tabwriter"
D
Dan Mace 已提交
21

22
	"github.com/cosiner/argv"
23 24 25
	"github.com/go-delve/delve/service"
	"github.com/go-delve/delve/service/api"
	"github.com/go-delve/delve/service/debugger"
D
Dan Mace 已提交
26 27
)

28 29
const optimizedFunctionWarning = "Warning: debugging optimized function"

30
type cmdPrefix int
31

32
const (
33 34
	noPrefix = cmdPrefix(0)
	onPrefix = cmdPrefix(1 << iota)
35
	deferredPrefix
36 37 38 39 40 41 42 43
)

type callContext struct {
	Prefix     cmdPrefix
	Scope      api.EvalScope
	Breakpoint *api.Breakpoint
}

44 45 46 47 48 49 50 51 52 53 54 55
func (ctx *callContext) scoped() bool {
	return ctx.Scope.GoroutineID >= 0 || ctx.Scope.Frame > 0
}

type frameDirection int

const (
	frameSet frameDirection = iota
	frameUp
	frameDown
)

56
type cmdfunc func(t *Term, ctx callContext, args string) error
D
Dan Mace 已提交
57 58

type command struct {
59
	aliases         []string
A
aarzilli 已提交
60
	builtinAliases  []string
61 62 63
	allowedPrefixes cmdPrefix
	helpMsg         string
	cmdFn           cmdfunc
D
Dan Mace 已提交
64 65 66 67 68 69 70 71 72 73 74 75
}

// Returns true if the command string matches one of the aliases for this command
func (c command) match(cmdstr string) bool {
	for _, v := range c.aliases {
		if v == cmdstr {
			return true
		}
	}
	return false
}

D
Derek Parker 已提交
76
// Commands represents the commands for Delve terminal process.
D
Dan Mace 已提交
77
type Commands struct {
78 79 80 81
	cmds    []command
	lastCmd cmdfunc
	client  service.Client
	frame   int // Current frame as set by frame/up/down commands.
D
Dan Mace 已提交
82 83
}

84
var (
85 86 87 88 89 90 91
	// LongLoadConfig loads more information:
	// * Follows pointers
	// * Loads more array values
	// * Does not limit struct fields
	LongLoadConfig = api.LoadConfig{true, 1, 64, 64, -1}
	// ShortLoadConfig loads less information, not following pointers
	// and limiting struct fields loaded to 3.
92 93 94
	ShortLoadConfig = api.LoadConfig{false, 0, 64, 0, 3}
)

95 96
// ByFirstAlias will sort by the first
// alias of a command.
97 98 99 100 101 102
type ByFirstAlias []command

func (a ByFirstAlias) Len() int           { return len(a) }
func (a ByFirstAlias) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByFirstAlias) Less(i, j int) bool { return a[i].aliases[0] < a[j].aliases[0] }

D
Derek Parker 已提交
103
// DebugCommands returns a Commands struct with default commands defined.
D
Dan Mace 已提交
104 105 106 107
func DebugCommands(client service.Client) *Commands {
	c := &Commands{client: client}

	c.cmds = []command{
108 109 110
		{aliases: []string{"help", "h"}, cmdFn: c.help, helpMsg: `Prints the help message.

	help [command]
111

112 113 114 115 116
Type "help" followed by the name of a command for more information about it.`},
		{aliases: []string{"break", "b"}, cmdFn: breakpoint, helpMsg: `Sets a breakpoint.

	break [name] <linespec>

117
See $GOPATH/src/github.com/go-delve/delve/Documentation/cli/locspec.md for the syntax of linespec.
118 119 120 121 122

See also: "help on", "help cond" and "help clear"`},
		{aliases: []string{"trace", "t"}, cmdFn: tracepoint, helpMsg: `Set tracepoint.

	trace [name] <linespec>
123

124
A tracepoint is a breakpoint that does not stop the execution of the program, instead when the tracepoint is hit a notification is displayed. See $GOPATH/src/github.com/go-delve/delve/Documentation/cli/locspec.md for the syntax of linespec.
125 126

See also: "help on", "help cond" and "help clear"`},
127 128
		{aliases: []string{"restart", "r"}, cmdFn: restart, helpMsg: `Restart process.

129 130
	restart [checkpoint]
	restart [-noargs] newargv...
131

132 133 134
For recorded processes restarts from the start or from the specified
checkpoint.  For normal processes restarts the process, optionally changing
the arguments.  With -noargs, the process starts with an empty commandline.
135
`},
136 137 138 139 140
		{aliases: []string{"continue", "c"}, cmdFn: c.cont, helpMsg: "Run until breakpoint or program termination."},
		{aliases: []string{"step", "s"}, cmdFn: c.step, helpMsg: "Single step through program."},
		{aliases: []string{"step-instruction", "si"}, cmdFn: c.stepInstruction, helpMsg: "Single step a single cpu instruction."},
		{aliases: []string{"next", "n"}, cmdFn: c.next, helpMsg: "Step over to next source line."},
		{aliases: []string{"stepout"}, cmdFn: c.stepout, helpMsg: "Step out of the current function."},
141 142
		{aliases: []string{"call"}, cmdFn: c.call, helpMsg: `Resumes process, injecting a function call (EXPERIMENTAL!!!)
	
143 144
	call [-unsafe] <function call expression>
	
145 146
Current limitations:
- only pointers to stack-allocated objects can be passed as argument.
A
aarzilli 已提交
147
- only some automatic type conversions are supported.
148 149 150 151 152 153 154 155 156
- functions can only be called on running goroutines that are not
  executing the runtime.
- the current goroutine needs to have at least 256 bytes of free space on
  the stack.
- functions can only be called when the goroutine is stopped at a safe
  point.
- calling a function will resume execution of all goroutines.
- only supported on linux's native backend.
`},
D
Dan Mace 已提交
157
		{aliases: []string{"threads"}, cmdFn: threads, helpMsg: "Print out info for every traced thread."},
158 159 160 161 162 163 164 165 166
		{aliases: []string{"thread", "tr"}, cmdFn: thread, helpMsg: `Switch to the specified thread.

	thread <id>`},
		{aliases: []string{"clear"}, cmdFn: clear, helpMsg: `Deletes breakpoint.

	clear <breakpoint name or id>`},
		{aliases: []string{"clearall"}, cmdFn: clearAll, helpMsg: `Deletes multiple breakpoints.

	clearall [<linespec>]
167

168
If called with the linespec argument it will delete all the breakpoints matching the linespec. If linespec is omitted all breakpoints are deleted.`},
169
		{aliases: []string{"goroutines", "grs"}, cmdFn: goroutines, helpMsg: `List program goroutines.
170

171
	goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)|-s (start location)] [ -t (stack trace)]
172 173 174 175 176 177

Print out info for every goroutine. The flag controls what information is shown along with each goroutine:

	-u	displays location of topmost stackframe in user code
	-r	displays location of topmost stackframe (including frames inside private runtime functions)
	-g	displays location of go instruction that created the goroutine
178
	-s	displays location of the start function
179
	-t	displays stack trace of goroutine
180

181
If no flag is specified the default is -u.`},
182
		{aliases: []string{"goroutine", "gr"}, allowedPrefixes: onPrefix, cmdFn: c.goroutine, helpMsg: `Shows or changes current goroutine
183 184 185 186 187 188 189 190

	goroutine
	goroutine <id>
	goroutine <id> <command>

Called without arguments it will show information about the current goroutine.
Called with a single argument it will switch to the specified goroutine.
Called with more arguments it will execute a command on the specified goroutine.`},
D
Dan Mace 已提交
191
		{aliases: []string{"breakpoints", "bp"}, cmdFn: breakpoints, helpMsg: "Print out info for active breakpoints."},
192
		{aliases: []string{"print", "p"}, allowedPrefixes: onPrefix | deferredPrefix, cmdFn: printVar, helpMsg: `Evaluate an expression.
193 194 195

	[goroutine <n>] [frame <m>] print <expression>

196
See $GOPATH/src/github.com/go-delve/delve/Documentation/cli/expr.md for a description of supported expressions.`},
197
		{aliases: []string{"whatis"}, cmdFn: whatisCommand, helpMsg: `Prints type of an expression.
198

C
chainhelen 已提交
199
	whatis <expression>`},
200
		{aliases: []string{"set"}, cmdFn: setVar, helpMsg: `Changes the value of a variable.
201 202 203

	[goroutine <n>] [frame <m>] set <variable> = <value>

204
See $GOPATH/src/github.com/go-delve/delve/Documentation/cli/expr.md for a description of supported expressions. Only numerical variables and pointers can be changed.`},
205 206 207 208 209 210 211 212 213 214 215 216 217 218
		{aliases: []string{"sources"}, cmdFn: sources, helpMsg: `Print list of source files.

	sources [<regex>]

If regex is specified only the source files matching it will be returned.`},
		{aliases: []string{"funcs"}, cmdFn: funcs, helpMsg: `Print list of functions.

	funcs [<regex>]

If regex is specified only the functions matching it will be returned.`},
		{aliases: []string{"types"}, cmdFn: types, helpMsg: `Print list of types

	types [<regex>]

219
If regex is specified only the types matching it will be returned.`},
220
		{aliases: []string{"args"}, allowedPrefixes: onPrefix | deferredPrefix, cmdFn: args, helpMsg: `Print function arguments.
221 222 223 224

	[goroutine <n>] [frame <m>] args [-v] [<regex>]

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.`},
225
		{aliases: []string{"locals"}, allowedPrefixes: onPrefix | deferredPrefix, cmdFn: locals, helpMsg: `Print local variables.
226 227

	[goroutine <n>] [frame <m>] locals [-v] [<regex>]
228

A
aarzilli 已提交
229
The name of variables that are shadowed in the current scope will be shown in parenthesis.
230 231 232 233 234 235 236

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.`},
		{aliases: []string{"vars"}, cmdFn: vars, helpMsg: `Print package variables.

	vars [-v] [<regex>]

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.`},
A
aarzilli 已提交
237 238 239
		{aliases: []string{"regs"}, cmdFn: regs, helpMsg: `Print contents of CPU registers.

	regs [-a]
240

A
aarzilli 已提交
241
Argument -a shows more registers.`},
242 243 244 245 246
		{aliases: []string{"exit", "quit", "q"}, cmdFn: exitCommand, helpMsg: `Exit the debugger.
		
	exit [-c]
	
When connected to a headless instance started with the --accept-multiclient, pass -c to resume the execution of the target process before disconnecting.`},
247
		{aliases: []string{"list", "ls", "l"}, cmdFn: listCommand, helpMsg: `Show source code.
248 249 250 251

	[goroutine <n>] [frame <m>] list [<linespec>]

Show source around current point or provided linespec.`},
252
		{aliases: []string{"stack", "bt"}, allowedPrefixes: onPrefix, cmdFn: stackCommand, helpMsg: `Print stack trace.
253

254
	[goroutine <n>] [frame <m>] stack [<depth>] [-full] [-offsets] [-defer] [-a <n>] [-adepth <depth>]
255

256
	-full		every stackframe is decorated with the value of its local variables and arguments.
257 258
	-offsets	prints frame offset of each frame.
	-defer		prints deferred function call stack for each frame.
259 260
	-a <n>		prints stacktrace of n ancestors of the selected goroutine (target process must have tracebackancestors enabled)
	-adepth <depth>	configures depth of ancestor stacktrace
261
`},
262 263 264 265 266 267
		{aliases: []string{"frame"},
			cmdFn: func(t *Term, ctx callContext, arg string) error {
				return c.frameCommand(t, ctx, arg, frameSet)
			},
			helpMsg: `Set the current frame, or execute command on a different frame.

268 269
	frame <m>
	frame <m> <command>
270 271 272 273 274 275 276 277 278

The first form sets frame used by subsequent commands such as "print" or "set".
The second form runs the command on the given frame.`},
		{aliases: []string{"up"},
			cmdFn: func(t *Term, ctx callContext, arg string) error {
				return c.frameCommand(t, ctx, arg, frameUp)
			},
			helpMsg: `Move the current frame up.

279 280
	up [<m>]
	up [<m>] <command>
281 282 283 284 285 286 287 288

Move the current frame up by <m>. The second form runs the command on the given frame.`},
		{aliases: []string{"down"},
			cmdFn: func(t *Term, ctx callContext, arg string) error {
				return c.frameCommand(t, ctx, arg, frameDown)
			},
			helpMsg: `Move the current frame down.

289 290
	down [<m>]
	down [<m>] <command>
291 292

Move the current frame down by <m>. The second form runs the command on the given frame.`},
293 294 295 296 297
		{aliases: []string{"deferred"}, cmdFn: c.deferredCommand, helpMsg: `Executes command in the context of a deferred call.

	deferred <n> <command>

Executes the specified command (print, args, locals) in the context of the n-th deferred call in the current frame.`},
298 299 300
		{aliases: []string{"source"}, cmdFn: c.sourceCommand, helpMsg: `Executes a file containing a list of delve commands

	source <path>`},
301
		{aliases: []string{"disassemble", "disass"}, cmdFn: disassCommand, helpMsg: `Disassembler.
302 303 304 305

	[goroutine <n>] [frame <m>] disassemble [-a <start> <end>] [-l <locspec>]

If no argument is specified the function being executed in the selected stack frame will be executed.
306

307 308 309 310 311
	-a <start> <end>	disassembles the specified address range
	-l <locspec>		disassembles the specified function`},
		{aliases: []string{"on"}, cmdFn: c.onCmd, helpMsg: `Executes a command when a breakpoint is hit.

	on <breakpoint name or id> <command>.
312

313 314 315 316
Supported commands: print, stack and goroutine)`},
		{aliases: []string{"condition", "cond"}, cmdFn: conditionCmd, helpMsg: `Set breakpoint condition.

	condition <breakpoint name or id> <boolean expression>.
317

318
Specifies that the breakpoint or tracepoint should break only if the boolean expression is true.`},
A
aarzilli 已提交
319
		{aliases: []string{"config"}, cmdFn: configureCmd, helpMsg: `Changes configuration parameters.
320

A
aarzilli 已提交
321
	config -list
322

A
aarzilli 已提交
323 324 325 326 327 328 329
Show all configuration parameters.

	config -save

Saves the configuration file to disk, overwriting the current configuration file.

	config <parameter> <value>
330

A
aarzilli 已提交
331 332
Changes the value of a configuration parameter.

J
Josh Soref 已提交
333 334
	config substitute-path <from> <to>
	config substitute-path <from>
335

J
Josh Soref 已提交
336
Adds or removes a path substitution rule.
A
aarzilli 已提交
337 338 339

	config alias <command> <alias>
	config alias <alias>
340

A
aarzilli 已提交
341
Defines <alias> as an alias to <command> or removes an alias.`},
B
Ben Cotterell 已提交
342

343 344 345 346 347
		{aliases: []string{"edit", "ed"}, cmdFn: edit, helpMsg: `Open where you are in $DELVE_EDITOR or $EDITOR

	edit [locspec]
	
If locspec is omitted edit will open the current source file in the editor, otherwise it will open the specified location.`},
348
		{aliases: []string{"libraries"}, cmdFn: libraries, helpMsg: `List loaded dynamic libraries`},
D
Dan Mace 已提交
349 350
	}

351 352 353 354 355 356 357 358 359 360
	if client == nil || client.Recorded() {
		c.cmds = append(c.cmds, command{
			aliases: []string{"rewind", "rw"},
			cmdFn:   rewind,
			helpMsg: "Run backwards until breakpoint or program termination.",
		})
		c.cmds = append(c.cmds, command{
			aliases: []string{"check", "checkpoint"},
			cmdFn:   checkpoint,
			helpMsg: `Creates a checkpoint at the current position.
361

362 363 364
	checkpoint [note]

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.`,
365 366 367 368 369 370 371 372 373 374
		})
		c.cmds = append(c.cmds, command{
			aliases: []string{"checkpoints"},
			cmdFn:   checkpoints,
			helpMsg: "Print out info for existing checkpoints.",
		})
		c.cmds = append(c.cmds, command{
			aliases: []string{"clear-checkpoint", "clearcheck"},
			cmdFn:   clearCheckpoint,
			helpMsg: `Deletes checkpoint.
375

376
	clear-checkpoint <id>`,
377 378 379 380 381
		})
		for i := range c.cmds {
			v := &c.cmds[i]
			if v.match("restart") {
				v.helpMsg = `Restart process from a checkpoint or event.
382 383

  restart [event number or checkpoint id]`
384 385 386 387
			}
		}
	}

388
	sort.Sort(ByFirstAlias(c.cmds))
D
Dan Mace 已提交
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
	return c
}

// Register custom commands. Expects cf to be a func of type cmdfunc,
// returning only an error.
func (c *Commands) Register(cmdstr string, cf cmdfunc, helpMsg string) {
	for _, v := range c.cmds {
		if v.match(cmdstr) {
			v.cmdFn = cf
			return
		}
	}

	c.cmds = append(c.cmds, command{aliases: []string{cmdstr}, cmdFn: cf, helpMsg: helpMsg})
}

// Find will look up the command function for the given command input.
D
Derek Parker 已提交
406
// If it cannot find the command it will default to noCmdAvailable().
D
Dan Mace 已提交
407
// If the command is an empty string it will replay the last command.
408
func (c *Commands) Find(cmdstr string, prefix cmdPrefix) cmdfunc {
D
Dan Mace 已提交
409 410 411 412 413 414 415 416 417 418
	// If <enter> use last command, if there was one.
	if cmdstr == "" {
		if c.lastCmd != nil {
			return c.lastCmd
		}
		return nullCommand
	}

	for _, v := range c.cmds {
		if v.match(cmdstr) {
419 420 421
			if prefix != noPrefix && v.allowedPrefixes&prefix == 0 {
				continue
			}
D
Dan Mace 已提交
422 423 424 425 426 427 428 429
			c.lastCmd = v.cmdFn
			return v.cmdFn
		}
	}

	return noCmdAvailable
}

430
// CallWithContext takes a command and a context that command should be executed in.
431 432 433 434 435 436 437 438
func (c *Commands) CallWithContext(cmdstr string, t *Term, ctx callContext) error {
	vals := strings.SplitN(strings.TrimSpace(cmdstr), " ", 2)
	cmdname := vals[0]
	var args string
	if len(vals) > 1 {
		args = strings.TrimSpace(vals[1])
	}
	return c.Find(cmdname, ctx.Prefix)(t, ctx, args)
439 440
}

441
// Call takes a command to execute.
442
func (c *Commands) Call(cmdstr string, t *Term) error {
443
	ctx := callContext{Prefix: noPrefix, Scope: api.EvalScope{GoroutineID: -1, Frame: c.frame, DeferredCall: 0}}
444
	return c.CallWithContext(cmdstr, t, ctx)
445 446
}

447 448
// Merge takes aliases defined in the config struct and merges them with the default aliases.
func (c *Commands) Merge(allAliases map[string][]string) {
A
aarzilli 已提交
449 450 451 452 453
	for i := range c.cmds {
		if c.cmds[i].builtinAliases != nil {
			c.cmds[i].aliases = append(c.cmds[i].aliases[:0], c.cmds[i].builtinAliases...)
		}
	}
454 455
	for i := range c.cmds {
		if aliases, ok := allAliases[c.cmds[i].aliases[0]]; ok {
A
aarzilli 已提交
456 457 458 459
			if c.cmds[i].builtinAliases == nil {
				c.cmds[i].builtinAliases = make([]string, len(c.cmds[i].aliases))
				copy(c.cmds[i].builtinAliases, c.cmds[i].aliases)
			}
460 461 462 463 464
			c.cmds[i].aliases = append(c.cmds[i].aliases, aliases...)
		}
	}
}

465 466
var noCmdError = errors.New("command not available")

467
func noCmdAvailable(t *Term, ctx callContext, args string) error {
468
	return noCmdError
D
Dan Mace 已提交
469 470
}

471
func nullCommand(t *Term, ctx callContext, args string) error {
D
Dan Mace 已提交
472 473 474
	return nil
}

475
func (c *Commands) help(t *Term, ctx callContext, args string) error {
476 477 478 479 480 481 482 483 484 485 486 487
	if args != "" {
		for _, cmd := range c.cmds {
			for _, alias := range cmd.aliases {
				if alias == args {
					fmt.Println(cmd.helpMsg)
					return nil
				}
			}
		}
		return noCmdError
	}

D
Dan Mace 已提交
488
	fmt.Println("The following commands are available:")
D
Derek Parker 已提交
489 490
	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 8, 0, '-', 0)
D
Dan Mace 已提交
491
	for _, cmd := range c.cmds {
492 493 494 495
		h := cmd.helpMsg
		if idx := strings.Index(h, "\n"); idx >= 0 {
			h = h[:idx]
		}
D
Derek Parker 已提交
496
		if len(cmd.aliases) > 1 {
497
			fmt.Fprintf(w, "    %s (alias: %s) \t %s\n", cmd.aliases[0], strings.Join(cmd.aliases[1:], " | "), h)
D
Derek Parker 已提交
498
		} else {
499
			fmt.Fprintf(w, "    %s \t %s\n", cmd.aliases[0], h)
D
Derek Parker 已提交
500
		}
D
Dan Mace 已提交
501
	}
502 503 504 505 506
	if err := w.Flush(); err != nil {
		return err
	}
	fmt.Println("Type help followed by a command for full documentation.")
	return nil
D
Dan Mace 已提交
507 508
}

I
Ilia Choly 已提交
509
type byThreadID []*api.Thread
I
Ilia Choly 已提交
510

I
Ilia Choly 已提交
511 512 513
func (a byThreadID) Len() int           { return len(a) }
func (a byThreadID) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byThreadID) Less(i, j int) bool { return a[i].ID < a[j].ID }
I
Ilia Choly 已提交
514

515
func threads(t *Term, ctx callContext, args string) error {
516
	threads, err := t.client.ListThreads()
D
Dan Mace 已提交
517 518 519
	if err != nil {
		return err
	}
520
	state, err := t.client.GetState()
D
Dan Mace 已提交
521 522 523
	if err != nil {
		return err
	}
I
Ilia Choly 已提交
524
	sort.Sort(byThreadID(threads))
D
Dan Mace 已提交
525 526 527 528 529 530 531
	for _, th := range threads {
		prefix := "  "
		if state.CurrentThread != nil && state.CurrentThread.ID == th.ID {
			prefix = "* "
		}
		if th.Function != nil {
			fmt.Printf("%sThread %d at %#v %s:%d %s\n",
532
				prefix, th.ID, th.PC, ShortenFilePath(th.File),
533
				th.Line, th.Function.Name())
D
Dan Mace 已提交
534
		} else {
535
			fmt.Printf("%sThread %s\n", prefix, formatThread(th))
D
Dan Mace 已提交
536 537 538 539 540
		}
	}
	return nil
}

541
func thread(t *Term, ctx callContext, args string) error {
542 543 544
	if len(args) == 0 {
		return fmt.Errorf("you must specify a thread")
	}
545
	tid, err := strconv.Atoi(args)
D
Dan Mace 已提交
546 547 548
	if err != nil {
		return err
	}
549
	oldState, err := t.client.GetState()
D
Dan Mace 已提交
550 551 552
	if err != nil {
		return err
	}
553
	newState, err := t.client.SwitchThread(tid)
D
Dan Mace 已提交
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
	if err != nil {
		return err
	}

	oldThread := "<none>"
	newThread := "<none>"
	if oldState.CurrentThread != nil {
		oldThread = strconv.Itoa(oldState.CurrentThread.ID)
	}
	if newState.CurrentThread != nil {
		newThread = strconv.Itoa(newState.CurrentThread.ID)
	}
	fmt.Printf("Switched from %s to %s\n", oldThread, newThread)
	return nil
}

I
Ilia Choly 已提交
570 571 572 573 574 575
type byGoroutineID []*api.Goroutine

func (a byGoroutineID) Len() int           { return len(a) }
func (a byGoroutineID) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byGoroutineID) Less(i, j int) bool { return a[i].ID < a[j].ID }

576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
// The number of goroutines we're going to request on each RPC call
const goroutineBatchSize = 10000

func printGoroutines(t *Term, gs []*api.Goroutine, fgl formatGoroutineLoc, bPrintStack bool, state *api.DebuggerState) error {
	for _, g := range gs {
		prefix := "  "
		if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID {
			prefix = "* "
		}
		fmt.Printf("%sGoroutine %s\n", prefix, formatGoroutine(g, fgl))
		if bPrintStack {
			stack, err := t.client.Stacktrace(g.ID, 10, false, nil)
			if err != nil {
				return err
			}
			printStack(stack, "\t", false)
		}
	}
	return nil
}

597
func goroutines(t *Term, ctx callContext, argstr string) error {
598
	args := strings.Split(argstr, " ")
599
	var fgl = fglUserCurrent
600
	bPrintStack := false
601 602 603 604

	switch len(args) {
	case 0:
		// nothing to do
605 606 607 608 609 610 611 612 613
	case 1, 2:
		for _, arg := range args {
			switch arg {
			case "-u":
				fgl = fglUserCurrent
			case "-r":
				fgl = fglRuntimeCurrent
			case "-g":
				fgl = fglGo
614 615
			case "-s":
				fgl = fglStart
616 617 618 619 620 621 622
			case "-t":
				bPrintStack = true
			case "":
				// nothing to do
			default:
				return fmt.Errorf("wrong argument: '%s'", arg)
			}
623 624 625 626
		}
	default:
		return fmt.Errorf("too many arguments")
	}
627
	state, err := t.client.GetState()
628 629 630
	if err != nil {
		return err
	}
631 632 633 634 635 636 637 638 639
	var (
		start = 0
		gslen = 0
		gs    []*api.Goroutine
	)
	for start >= 0 {
		gs, start, err = t.client.ListGoroutines(start, goroutineBatchSize)
		if err != nil {
			return err
640
		}
641 642 643 644
		sort.Sort(byGoroutineID(gs))
		err = printGoroutines(t, gs, fgl, bPrintStack, state)
		if err != nil {
			return err
645
		}
646
		gslen += len(gs)
647
	}
648
	fmt.Printf("[%d goroutines]\n", gslen)
649 650 651
	return nil
}

652 653 654 655 656 657 658
func selectedGID(state *api.DebuggerState) int {
	if state.SelectedGoroutine == nil {
		return 0
	}
	return state.SelectedGoroutine.ID
}

659
func (c *Commands) goroutine(t *Term, ctx callContext, argstr string) error {
660
	args := strings.SplitN(argstr, " ", 2)
661

662 663 664
	if ctx.Prefix == onPrefix {
		if len(args) != 1 || args[0] != "" {
			return errors.New("too many arguments to goroutine")
665
		}
666
		ctx.Breakpoint.Goroutine = true
667 668 669
		return nil
	}

670
	if len(args) == 1 {
671 672
		if args[0] == "" {
			return printscope(t)
H
Hubert Krauze 已提交
673 674 675 676 677
		}
		gid, err := strconv.Atoi(argstr)
		if err != nil {
			return err
		}
678

H
Hubert Krauze 已提交
679 680 681 682 683 684 685
		oldState, err := t.client.GetState()
		if err != nil {
			return err
		}
		newState, err := t.client.SwitchGoroutine(gid)
		if err != nil {
			return err
686
		}
687
		c.frame = 0
688
		fmt.Printf("Switched from %d to %d (thread %d)\n", selectedGID(oldState), gid, newState.CurrentThread.ID)
H
Hubert Krauze 已提交
689
		return nil
690 691
	}

692 693 694 695 696
	var err error
	ctx.Scope.GoroutineID, err = strconv.Atoi(args[0])
	if err != nil {
		return err
	}
697
	return c.CallWithContext(args[1], t, ctx)
698 699
}

700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
// Handle "frame", "up", "down" commands.
func (c *Commands) frameCommand(t *Term, ctx callContext, argstr string, direction frameDirection) error {
	frame := 1
	arg := ""
	if len(argstr) == 0 {
		if direction == frameSet {
			return errors.New("not enough arguments")
		}
	} else {
		args := strings.SplitN(argstr, " ", 2)
		var err error
		if frame, err = strconv.Atoi(args[0]); err != nil {
			return err
		}
		if len(args) > 1 {
			arg = args[1]
		}
717
	}
718 719 720 721 722 723 724 725 726 727 728 729 730
	switch direction {
	case frameUp:
		frame = c.frame + frame
	case frameDown:
		frame = c.frame - frame
	}
	if len(arg) > 0 {
		ctx.Scope.Frame = frame
		return c.CallWithContext(arg, t, ctx)
	}
	if frame < 0 {
		return fmt.Errorf("Invalid frame %d", frame)
	}
731
	stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, frame, false, nil)
732 733 734
	if err != nil {
		return err
	}
735 736 737 738 739 740 741 742 743 744 745 746 747
	if frame >= len(stack) {
		return fmt.Errorf("Invalid frame %d", frame)
	}
	c.frame = frame
	state, err := t.client.GetState()
	if err != nil {
		return err
	}
	printcontext(t, state)
	th := stack[frame]
	fmt.Printf("Frame %d: %s:%d (PC: %x)\n", frame, ShortenFilePath(th.File), th.Line, th.PC)
	printfile(t, th.File, th.Line, true)
	return nil
748 749
}

750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
func (c *Commands) deferredCommand(t *Term, ctx callContext, argstr string) error {
	ctx.Prefix = deferredPrefix

	space := strings.Index(argstr, " ")

	var err error
	ctx.Scope.DeferredCall, err = strconv.Atoi(argstr[:space])
	if err != nil {
		return err
	}
	if ctx.Scope.DeferredCall <= 0 {
		return errors.New("argument of deferred must be a number greater than 0 (use 'stack -defer' to see the list of deferred calls)")
	}
	return c.CallWithContext(argstr[space:], t, ctx)
}

766 767
func printscope(t *Term) error {
	state, err := t.client.GetState()
768 769
	if err != nil {
		return err
D
Dan Mace 已提交
770
	}
771

772 773 774 775
	fmt.Printf("Thread %s\n", formatThread(state.CurrentThread))
	if state.SelectedGoroutine != nil {
		writeGoroutineLong(os.Stdout, state.SelectedGoroutine, "")
	}
D
Dan Mace 已提交
776 777 778
	return nil
}

779 780 781 782
func formatThread(th *api.Thread) string {
	if th == nil {
		return "<nil>"
	}
783
	return fmt.Sprintf("%d at %s:%d", th.ID, ShortenFilePath(th.File), th.Line)
784 785
}

786 787 788 789 790 791
type formatGoroutineLoc int

const (
	fglRuntimeCurrent = formatGoroutineLoc(iota)
	fglUserCurrent
	fglGo
792
	fglStart
793 794 795
)

func formatLocation(loc api.Location) string {
796
	return fmt.Sprintf("%s:%d %s (%#v)", ShortenFilePath(loc.File), loc.Line, loc.Function.Name(), loc.PC)
797 798 799
}

func formatGoroutine(g *api.Goroutine, fgl formatGoroutineLoc) string {
800 801 802
	if g == nil {
		return "<nil>"
	}
803 804 805
	if g.Unreadable != "" {
		return fmt.Sprintf("(unreadable %s)", g.Unreadable)
	}
806 807 808 809 810
	var locname string
	var loc api.Location
	switch fgl {
	case fglRuntimeCurrent:
		locname = "Runtime"
811
		loc = g.CurrentLoc
812 813
	case fglUserCurrent:
		locname = "User"
814
		loc = g.UserCurrentLoc
815 816
	case fglGo:
		locname = "Go"
817
		loc = g.GoStatementLoc
818 819 820
	case fglStart:
		locname = "Start"
		loc = g.StartLoc
A
aarzilli 已提交
821
	}
822 823 824 825 826
	thread := ""
	if g.ThreadID != 0 {
		thread = fmt.Sprintf(" (thread %d)", g.ThreadID)
	}
	return fmt.Sprintf("%d - %s: %s%s", g.ID, locname, formatLocation(loc), thread)
827 828 829
}

func writeGoroutineLong(w io.Writer, g *api.Goroutine, prefix string) {
830
	fmt.Fprintf(w, "%sGoroutine %d:\n%s\tRuntime: %s\n%s\tUser: %s\n%s\tGo: %s\n%s\tStart: %s\n",
831
		prefix, g.ID,
832 833
		prefix, formatLocation(g.CurrentLoc),
		prefix, formatLocation(g.UserCurrentLoc),
834 835
		prefix, formatLocation(g.GoStatementLoc),
		prefix, formatLocation(g.StartLoc))
A
aarzilli 已提交
836 837
}

838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
func parseArgs(args string) ([]string, error) {
	if args == "" {
		return nil, nil
	}
	v, err := argv.Argv([]rune(args), argv.ParseEnv(os.Environ()),
		func(s []rune, _ map[string]string) ([]rune, error) {
			return nil, fmt.Errorf("Backtick not supported in '%s'", string(s))
		})
	if err != nil {
		return nil, err
	}
	if len(v) != 1 {
		return nil, fmt.Errorf("Illegal commandline '%s'", args)
	}
	return v[0], nil
}

855
func restart(t *Term, ctx callContext, args string) error {
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
	v, err := parseArgs(args)
	if err != nil {
		return err
	}
	var restartPos string
	var resetArgs bool
	if t.client.Recorded() {
		if len(v) > 1 {
			return fmt.Errorf("restart: illegal position '%v'", v)
		}
		if len(v) == 1 {
			restartPos = v[0]
			v = nil
		}
	} else if len(v) > 0 {
		resetArgs = true
		if v[0] == "-noargs" {
			if len(v) > 1 {
				return fmt.Errorf("restart: -noargs does not take any arg")
			}
			v = nil
		}
	}
	discarded, err := t.client.RestartFrom(restartPos, resetArgs, v)
880
	if err != nil {
D
Derek Parker 已提交
881 882
		return err
	}
883 884 885
	if !t.client.Recorded() {
		fmt.Println("Process restarted with PID", t.client.ProcessPid())
	}
886
	for i := range discarded {
887
		fmt.Printf("Discarded %s at %s: %v\n", formatBreakpointName(discarded[i].Breakpoint, false), formatBreakpointLocation(discarded[i].Breakpoint), discarded[i].Reason)
888
	}
889 890 891 892 893 894 895 896
	if t.client.Recorded() {
		state, err := t.client.GetState()
		if err != nil {
			return err
		}
		printcontext(t, state)
		printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
	}
D
Derek Parker 已提交
897 898 899
	return nil
}

900 901 902 903
func printcontextNoState(t *Term) {
	state, _ := t.client.GetState()
	if state == nil || state.CurrentThread == nil {
		return
904
	}
905
	printcontext(t, state)
906 907
}

908 909
func (c *Commands) cont(t *Term, ctx callContext, args string) error {
	c.frame = 0
910
	stateChan := t.client.Continue()
911 912
	var state *api.DebuggerState
	for state = range stateChan {
A
aarzilli 已提交
913
		if state.Err != nil {
914
			printcontextNoState(t)
A
aarzilli 已提交
915 916
			return state.Err
		}
917
		printcontext(t, state)
D
Dan Mace 已提交
918
	}
919
	printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
D
Dan Mace 已提交
920 921 922
	return nil
}

923 924 925 926 927 928
func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string) error {
	if !state.NextInProgress {
		printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
		return nil
	}
	for {
929
		fmt.Printf("\tbreakpoint hit during %s, continuing...\n", op)
930 931 932 933
		stateChan := t.client.Continue()
		var state *api.DebuggerState
		for state = range stateChan {
			if state.Err != nil {
934
				printcontextNoState(t)
935 936 937 938 939 940 941 942 943 944 945
				return state.Err
			}
			printcontext(t, state)
		}
		if !state.NextInProgress {
			printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
			return nil
		}
	}
}

946 947 948 949 950 951 952 953 954 955
func scopePrefixSwitch(t *Term, ctx callContext) error {
	if ctx.Scope.GoroutineID > 0 {
		_, err := t.client.SwitchGoroutine(ctx.Scope.GoroutineID)
		if err != nil {
			return err
		}
	}
	return nil
}

956 957 958 959 960 961 962
func exitedToError(state *api.DebuggerState, err error) (*api.DebuggerState, error) {
	if err == nil && state.Exited {
		return nil, fmt.Errorf("Process has exited with status %d", state.ExitStatus)
	}
	return state, err
}

963
func (c *Commands) step(t *Term, ctx callContext, args string) error {
964 965 966
	if err := scopePrefixSwitch(t, ctx); err != nil {
		return err
	}
967
	c.frame = 0
968
	state, err := exitedToError(t.client.Step())
D
Dan Mace 已提交
969
	if err != nil {
970
		printcontextNoState(t)
D
Dan Mace 已提交
971 972
		return err
	}
973
	printcontext(t, state)
974
	return continueUntilCompleteNext(t, state, "step")
D
Dan Mace 已提交
975 976
}

977 978
var notOnFrameZeroErr = errors.New("not on topmost frame")

979
func (c *Commands) stepInstruction(t *Term, ctx callContext, args string) error {
980 981 982
	if err := scopePrefixSwitch(t, ctx); err != nil {
		return err
	}
983 984 985
	if c.frame != 0 {
		return notOnFrameZeroErr
	}
986
	state, err := exitedToError(t.client.StepInstruction())
987
	if err != nil {
988
		printcontextNoState(t)
989 990 991
		return err
	}
	printcontext(t, state)
992
	printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
993 994 995
	return nil
}

996
func (c *Commands) next(t *Term, ctx callContext, args string) error {
997 998
	if err := scopePrefixSwitch(t, ctx); err != nil {
		return err
999
	}
1000 1001 1002
	if c.frame != 0 {
		return notOnFrameZeroErr
	}
1003
	state, err := exitedToError(t.client.Next())
D
Dan Mace 已提交
1004
	if err != nil {
1005
		printcontextNoState(t)
D
Dan Mace 已提交
1006 1007
		return err
	}
1008
	printcontext(t, state)
1009
	return continueUntilCompleteNext(t, state, "next")
D
Dan Mace 已提交
1010 1011
}

1012
func (c *Commands) stepout(t *Term, ctx callContext, args string) error {
A
aarzilli 已提交
1013 1014 1015
	if err := scopePrefixSwitch(t, ctx); err != nil {
		return err
	}
1016 1017 1018
	if c.frame != 0 {
		return notOnFrameZeroErr
	}
1019
	state, err := exitedToError(t.client.StepOut())
A
aarzilli 已提交
1020
	if err != nil {
1021
		printcontextNoState(t)
A
aarzilli 已提交
1022 1023 1024 1025 1026 1027
		return err
	}
	printcontext(t, state)
	return continueUntilCompleteNext(t, state, "stepout")
}

1028 1029 1030 1031
func (c *Commands) call(t *Term, ctx callContext, args string) error {
	if err := scopePrefixSwitch(t, ctx); err != nil {
		return err
	}
1032 1033 1034 1035 1036 1037
	const unsafePrefix = "-unsafe "
	unsafe := false
	if strings.HasPrefix(args, unsafePrefix) {
		unsafe = true
		args = args[len(unsafePrefix):]
	}
1038
	state, err := exitedToError(t.client.Call(ctx.Scope.GoroutineID, args, unsafe))
1039 1040
	c.frame = 0
	if err != nil {
1041
		printcontextNoState(t)
1042 1043 1044 1045 1046 1047
		return err
	}
	printcontext(t, state)
	return continueUntilCompleteNext(t, state, "call")
}

1048
func clear(t *Term, ctx callContext, args string) error {
D
Dan Mace 已提交
1049
	if len(args) == 0 {
D
Derek Parker 已提交
1050
		return fmt.Errorf("not enough arguments")
D
Dan Mace 已提交
1051
	}
1052
	id, err := strconv.Atoi(args)
1053 1054 1055 1056 1057
	var bp *api.Breakpoint
	if err == nil {
		bp, err = t.client.ClearBreakpoint(id)
	} else {
		bp, err = t.client.ClearBreakpointByName(args)
D
Dan Mace 已提交
1058 1059 1060 1061
	}
	if err != nil {
		return err
	}
1062
	fmt.Printf("%s cleared at %s\n", formatBreakpointName(bp, true), formatBreakpointLocation(bp))
D
Dan Mace 已提交
1063 1064 1065
	return nil
}

1066
func clearAll(t *Term, ctx callContext, args string) error {
1067
	breakPoints, err := t.client.ListBreakpoints()
D
Dan Mace 已提交
1068 1069 1070
	if err != nil {
		return err
	}
1071 1072

	var locPCs map[uint64]struct{}
1073
	if args != "" {
D
Derek Parker 已提交
1074
		locs, err := t.client.FindLocation(api.EvalScope{GoroutineID: -1, Frame: 0}, args)
1075 1076 1077 1078 1079 1080 1081 1082 1083
		if err != nil {
			return err
		}
		locPCs = make(map[uint64]struct{})
		for _, loc := range locs {
			locPCs[loc.PC] = struct{}{}
		}
	}

D
Dan Mace 已提交
1084
	for _, bp := range breakPoints {
1085 1086 1087 1088 1089 1090
		if locPCs != nil {
			if _, ok := locPCs[bp.Addr]; !ok {
				continue
			}
		}

1091 1092 1093 1094
		if bp.ID < 0 {
			continue
		}

1095
		_, err := t.client.ClearBreakpoint(bp.ID)
D
Dan Mace 已提交
1096
		if err != nil {
1097
			fmt.Printf("Couldn't delete %s at %s: %s\n", formatBreakpointName(bp, false), formatBreakpointLocation(bp), err)
D
Dan Mace 已提交
1098
		}
1099
		fmt.Printf("%s cleared at %s\n", formatBreakpointName(bp, true), formatBreakpointLocation(bp))
D
Dan Mace 已提交
1100 1101 1102 1103
	}
	return nil
}

D
Derek Parker 已提交
1104 1105
// ByID sorts breakpoints by ID.
type ByID []*api.Breakpoint
D
Dan Mace 已提交
1106

D
Derek Parker 已提交
1107 1108 1109
func (a ByID) Len() int           { return len(a) }
func (a ByID) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByID) Less(i, j int) bool { return a[i].ID < a[j].ID }
D
Dan Mace 已提交
1110

1111
func breakpoints(t *Term, ctx callContext, args string) error {
1112
	breakPoints, err := t.client.ListBreakpoints()
D
Dan Mace 已提交
1113 1114 1115
	if err != nil {
		return err
	}
D
Derek Parker 已提交
1116
	sort.Sort(ByID(breakPoints))
D
Dan Mace 已提交
1117
	for _, bp := range breakPoints {
1118
		fmt.Printf("%s at %v (%d)\n", formatBreakpointName(bp, true), formatBreakpointLocation(bp), bp.TotalHitCount)
A
aarzilli 已提交
1119 1120

		var attrs []string
1121 1122 1123
		if bp.Cond != "" {
			attrs = append(attrs, fmt.Sprintf("\tcond %s", bp.Cond))
		}
A
aarzilli 已提交
1124
		if bp.Stacktrace > 0 {
1125
			attrs = append(attrs, fmt.Sprintf("\tstack %d", bp.Stacktrace))
A
aarzilli 已提交
1126 1127
		}
		if bp.Goroutine {
1128
			attrs = append(attrs, "\tgoroutine")
A
aarzilli 已提交
1129
		}
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143
		if bp.LoadArgs != nil {
			if *(bp.LoadArgs) == LongLoadConfig {
				attrs = append(attrs, "\targs -v")
			} else {
				attrs = append(attrs, "\targs")
			}
		}
		if bp.LoadLocals != nil {
			if *(bp.LoadLocals) == LongLoadConfig {
				attrs = append(attrs, "\tlocals -v")
			} else {
				attrs = append(attrs, "\tlocals")
			}
		}
D
Derek Parker 已提交
1144
		for i := range bp.Variables {
1145
			attrs = append(attrs, fmt.Sprintf("\tprint %s", bp.Variables[i]))
A
aarzilli 已提交
1146 1147
		}
		if len(attrs) > 0 {
1148
			fmt.Printf("%s\n", strings.Join(attrs, "\n"))
A
aarzilli 已提交
1149
		}
D
Dan Mace 已提交
1150 1151 1152 1153
	}
	return nil
}

1154
func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) error {
1155
	args := strings.SplitN(argstr, " ", 2)
D
Dan Mace 已提交
1156

1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167
	requestedBp := &api.Breakpoint{}
	locspec := ""
	switch len(args) {
	case 1:
		locspec = argstr
	case 2:
		if api.ValidBreakpointName(args[0]) == nil {
			requestedBp.Name = args[0]
			locspec = args[1]
		} else {
			locspec = argstr
A
aarzilli 已提交
1168
		}
1169 1170
	default:
		return fmt.Errorf("address required")
A
aarzilli 已提交
1171 1172 1173
	}

	requestedBp.Tracepoint = tracepoint
1174
	locs, err := t.client.FindLocation(ctx.Scope, locspec)
D
Dan Mace 已提交
1175
	if err != nil {
1176 1177 1178 1179 1180 1181
		if requestedBp.Name == "" {
			return err
		}
		requestedBp.Name = ""
		locspec = argstr
		var err2 error
1182
		locs, err2 = t.client.FindLocation(ctx.Scope, locspec)
1183 1184 1185
		if err2 != nil {
			return err
		}
A
aarzilli 已提交
1186
	}
1187 1188 1189
	for _, loc := range locs {
		requestedBp.Addr = loc.PC

1190
		bp, err := t.client.CreateBreakpoint(requestedBp)
1191 1192 1193 1194
		if err != nil {
			return err
		}

1195
		fmt.Printf("%s set at %s\n", formatBreakpointName(bp, true), formatBreakpointLocation(bp))
1196
	}
D
Dan Mace 已提交
1197 1198 1199
	return nil
}

1200
func breakpoint(t *Term, ctx callContext, args string) error {
1201
	return setBreakpoint(t, ctx, false, args)
A
aarzilli 已提交
1202 1203
}

1204
func tracepoint(t *Term, ctx callContext, args string) error {
1205
	return setBreakpoint(t, ctx, true, args)
A
aarzilli 已提交
1206 1207
}

B
Ben Cotterell 已提交
1208
func edit(t *Term, ctx callContext, args string) error {
1209
	file, lineno, _, err := getLocation(t, ctx, args, false)
A
aarzilli 已提交
1210 1211 1212
	if err != nil {
		return err
	}
B
Ben Cotterell 已提交
1213 1214 1215 1216 1217 1218 1219 1220

	var editor string
	if editor = os.Getenv("DELVE_EDITOR"); editor == "" {
		if editor = os.Getenv("EDITOR"); editor == "" {
			return fmt.Errorf("Neither DELVE_EDITOR or EDITOR is set")
		}
	}

1221
	cmd := exec.Command(editor, fmt.Sprintf("+%d", lineno), file)
1222 1223 1224
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
B
Ben Cotterell 已提交
1225 1226 1227
	return cmd.Run()
}

1228
func printVar(t *Term, ctx callContext, args string) error {
D
Dan Mace 已提交
1229
	if len(args) == 0 {
D
Derek Parker 已提交
1230
		return fmt.Errorf("not enough arguments")
D
Dan Mace 已提交
1231
	}
1232 1233 1234 1235
	if ctx.Prefix == onPrefix {
		ctx.Breakpoint.Variables = append(ctx.Breakpoint.Variables, args)
		return nil
	}
A
aarzilli 已提交
1236
	val, err := t.client.EvalVariable(ctx.Scope, args, t.loadConfig())
D
Dan Mace 已提交
1237 1238 1239
	if err != nil {
		return err
	}
1240 1241

	fmt.Println(val.MultilineString(""))
D
Dan Mace 已提交
1242 1243 1244
	return nil
}

A
aarzilli 已提交
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
func whatisCommand(t *Term, ctx callContext, args string) error {
	if len(args) == 0 {
		return fmt.Errorf("not enough arguments")
	}
	val, err := t.client.EvalVariable(ctx.Scope, args, ShortLoadConfig)
	if err != nil {
		return err
	}
	if val.Type != "" {
		fmt.Println(val.Type)
	}
	if val.RealType != val.Type {
		fmt.Printf("Real type: %s\n", val.RealType)
	}
	if val.Kind == reflect.Interface && len(val.Children) > 0 {
		fmt.Printf("Concrete type: %s\n", val.Children[0].Type)
	}
1262 1263 1264
	if t.conf.ShowLocationExpr && val.LocationExpr != "" {
		fmt.Printf("location: %s\n", val.LocationExpr)
	}
A
aarzilli 已提交
1265 1266 1267
	return nil
}

1268
func setVar(t *Term, ctx callContext, args string) error {
1269 1270 1271 1272
	// HACK: in go '=' is not an operator, we detect the error and try to recover from it by splitting the input string
	_, err := parser.ParseExpr(args)
	if err == nil {
		return fmt.Errorf("syntax error '=' not found")
1273 1274
	}

1275 1276 1277 1278 1279 1280 1281
	el, ok := err.(scanner.ErrorList)
	if !ok || el[0].Msg != "expected '==', found '='" {
		return err
	}

	lexpr := args[:el[0].Pos.Offset]
	rexpr := args[el[0].Pos.Offset+1:]
1282
	return t.client.SetVariable(ctx.Scope, lexpr, rexpr)
1283 1284
}

1285
func printFilteredVariables(varType string, vars []api.Variable, filter string, cfg api.LoadConfig) error {
D
Derek Parker 已提交
1286 1287
	reg, err := regexp.Compile(filter)
	if err != nil {
1288
		return err
D
Derek Parker 已提交
1289
	}
1290
	match := false
D
Dan Mace 已提交
1291
	for _, v := range vars {
D
Derek Parker 已提交
1292
		if reg == nil || reg.Match([]byte(v.Name)) {
1293
			match = true
A
aarzilli 已提交
1294 1295 1296 1297
			name := v.Name
			if v.Flags&api.VariableShadowed != 0 {
				name = "(" + name + ")"
			}
1298
			if cfg == ShortLoadConfig {
A
aarzilli 已提交
1299
				fmt.Printf("%s = %s\n", name, v.SinglelineString())
1300
			} else {
A
aarzilli 已提交
1301
				fmt.Printf("%s = %s\n", name, v.MultilineString(""))
1302
			}
D
Dan Mace 已提交
1303 1304
		}
	}
1305 1306 1307 1308
	if !match {
		fmt.Printf("(no %s)\n", varType)
	}
	return nil
D
Dan Mace 已提交
1309 1310
}

1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327
func printSortedStrings(v []string, err error) error {
	if err != nil {
		return err
	}
	sort.Strings(v)
	for _, d := range v {
		fmt.Println(d)
	}
	return nil
}

func sources(t *Term, ctx callContext, args string) error {
	return printSortedStrings(t.client.ListSources(args))
}

func funcs(t *Term, ctx callContext, args string) error {
	return printSortedStrings(t.client.ListFunctions(args))
D
Derek Parker 已提交
1328
}
D
Dan Mace 已提交
1329

1330 1331
func types(t *Term, ctx callContext, args string) error {
	return printSortedStrings(t.client.ListTypes(args))
D
Derek Parker 已提交
1332
}
D
Dan Mace 已提交
1333

A
aarzilli 已提交
1334
func parseVarArguments(args string, t *Term) (filter string, cfg api.LoadConfig) {
1335 1336
	if v := strings.SplitN(args, " ", 2); len(v) >= 1 && v[0] == "-v" {
		if len(v) == 2 {
A
aarzilli 已提交
1337
			return v[1], t.loadConfig()
1338
		} else {
A
aarzilli 已提交
1339
			return "", t.loadConfig()
1340 1341 1342
		}
	}
	return args, ShortLoadConfig
A
aarzilli 已提交
1343 1344
}

1345
func args(t *Term, ctx callContext, args string) error {
A
aarzilli 已提交
1346
	filter, cfg := parseVarArguments(args, t)
1347 1348 1349 1350 1351 1352 1353 1354
	if ctx.Prefix == onPrefix {
		if filter != "" {
			return fmt.Errorf("filter not supported on breakpoint")
		}
		ctx.Breakpoint.LoadArgs = &cfg
		return nil
	}
	vars, err := t.client.ListFunctionArgs(ctx.Scope, cfg)
D
Derek Parker 已提交
1355
	if err != nil {
1356
		return err
D
Derek Parker 已提交
1357
	}
1358
	return printFilteredVariables("args", vars, filter, cfg)
D
Derek Parker 已提交
1359
}
D
Dan Mace 已提交
1360

1361
func locals(t *Term, ctx callContext, args string) error {
A
aarzilli 已提交
1362
	filter, cfg := parseVarArguments(args, t)
1363 1364 1365 1366 1367 1368 1369 1370
	if ctx.Prefix == onPrefix {
		if filter != "" {
			return fmt.Errorf("filter not supported on breakpoint")
		}
		ctx.Breakpoint.LoadLocals = &cfg
		return nil
	}
	locals, err := t.client.ListLocalVariables(ctx.Scope, cfg)
D
Derek Parker 已提交
1371
	if err != nil {
1372
		return err
D
Derek Parker 已提交
1373
	}
1374
	return printFilteredVariables("locals", locals, filter, cfg)
D
Derek Parker 已提交
1375
}
D
Dan Mace 已提交
1376

1377
func vars(t *Term, ctx callContext, args string) error {
A
aarzilli 已提交
1378
	filter, cfg := parseVarArguments(args, t)
1379
	vars, err := t.client.ListPackageVariables(filter, cfg)
D
Derek Parker 已提交
1380
	if err != nil {
1381
		return err
D
Derek Parker 已提交
1382
	}
1383
	return printFilteredVariables("vars", vars, filter, cfg)
D
Derek Parker 已提交
1384
}
D
Dan Mace 已提交
1385

1386
func regs(t *Term, ctx callContext, args string) error {
A
aarzilli 已提交
1387 1388 1389 1390 1391
	includeFp := false
	if args == "-a" {
		includeFp = true
	}
	regs, err := t.client.ListRegisters(0, includeFp)
D
Derek Parker 已提交
1392 1393 1394 1395 1396 1397
	if err != nil {
		return err
	}
	fmt.Println(regs)
	return nil
}
1398

1399
func stackCommand(t *Term, ctx callContext, args string) error {
1400
	sa, err := parseStackArgs(args)
1401 1402 1403
	if err != nil {
		return err
	}
1404
	if ctx.Prefix == onPrefix {
1405
		ctx.Breakpoint.Stacktrace = sa.depth
1406 1407
		return nil
	}
1408
	var cfg *api.LoadConfig
1409
	if sa.full {
1410 1411
		cfg = &ShortLoadConfig
	}
1412
	stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, sa.depth, sa.readDefers, cfg)
1413 1414 1415
	if err != nil {
		return err
	}
1416
	printStack(stack, "", sa.offsets)
1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430
	if sa.ancestors > 0 {
		ancestors, err := t.client.Ancestors(ctx.Scope.GoroutineID, sa.ancestors, sa.ancestorDepth)
		if err != nil {
			return err
		}
		for _, ancestor := range ancestors {
			fmt.Printf("Created by Goroutine %d:\n", ancestor.ID)
			if ancestor.Unreadable != "" {
				fmt.Printf("\t%s\n", ancestor.Unreadable)
				continue
			}
			printStack(ancestor.Stack, "\t", false)
		}
	}
1431 1432
	return nil
}
A
aarzilli 已提交
1433

1434
type stackArgs struct {
1435 1436 1437 1438
	depth      int
	full       bool
	offsets    bool
	readDefers bool
1439 1440 1441

	ancestors     int
	ancestorDepth int
1442 1443 1444 1445
}

func parseStackArgs(argstr string) (stackArgs, error) {
	r := stackArgs{
1446
		depth: 50,
1447 1448
		full:  false,
	}
1449 1450
	if argstr != "" {
		args := strings.Split(argstr, " ")
1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462
		for i := 0; i < len(args); i++ {
			numarg := func(name string) (int, error) {
				if i >= len(args) {
					return 0, fmt.Errorf("expected number after %s", name)
				}
				n, err := strconv.Atoi(args[i])
				if err != nil {
					return 0, fmt.Errorf("expected number after %s: %v", name, err)
				}
				return n, nil

			}
1463 1464 1465 1466 1467
			switch args[i] {
			case "-full":
				r.full = true
			case "-offsets":
				r.offsets = true
1468 1469
			case "-defer":
				r.readDefers = true
1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483
			case "-a":
				i++
				n, err := numarg("-a")
				if err != nil {
					return stackArgs{}, err
				}
				r.ancestors = n
			case "-adepth":
				i++
				n, err := numarg("-adepth")
				if err != nil {
					return stackArgs{}, err
				}
				r.ancestorDepth = n
1484
			default:
1485 1486
				n, err := strconv.Atoi(args[i])
				if err != nil {
1487
					return stackArgs{}, fmt.Errorf("depth must be a number")
1488
				}
1489
				r.depth = n
1490
			}
A
aarzilli 已提交
1491 1492
		}
	}
1493 1494 1495
	if r.ancestors > 0 && r.ancestorDepth == 0 {
		r.ancestorDepth = r.depth
	}
1496
	return r, nil
A
aarzilli 已提交
1497 1498
}

1499 1500 1501
// getLocation returns the current location or the locations specified by the argument.
// getLocation is used to process the argument of list and edit commands.
func getLocation(t *Term, ctx callContext, args string, showContext bool) (file string, lineno int, showarrow bool, err error) {
A
aarzilli 已提交
1502
	switch {
1503
	case len(args) == 0 && !ctx.scoped():
A
aarzilli 已提交
1504 1505
		state, err := t.client.GetState()
		if err != nil {
1506 1507 1508 1509
			return "", 0, false, err
		}
		if showContext {
			printcontext(t, state)
A
aarzilli 已提交
1510
		}
1511
		if state.SelectedGoroutine != nil {
1512
			return state.SelectedGoroutine.CurrentLoc.File, state.SelectedGoroutine.CurrentLoc.Line, true, nil
1513
		}
1514
		return state.CurrentThread.File, state.CurrentThread.Line, true, nil
A
aarzilli 已提交
1515

1516
	case len(args) == 0 && ctx.scoped():
1517
		locs, err := t.client.Stacktrace(ctx.Scope.GoroutineID, ctx.Scope.Frame, false, nil)
1518
		if err != nil {
1519
			return "", 0, false, err
1520 1521
		}
		if ctx.Scope.Frame >= len(locs) {
1522
			return "", 0, false, fmt.Errorf("Frame %d does not exist in goroutine %d", ctx.Scope.Frame, ctx.Scope.GoroutineID)
1523 1524
		}
		loc := locs[ctx.Scope.Frame]
A
aarzilli 已提交
1525 1526 1527 1528
		gid := ctx.Scope.GoroutineID
		if gid < 0 {
			state, err := t.client.GetState()
			if err != nil {
1529
				return "", 0, false, err
A
aarzilli 已提交
1530 1531 1532 1533 1534
			}
			if state.SelectedGoroutine != nil {
				gid = state.SelectedGoroutine.ID
			}
		}
1535 1536 1537 1538
		if showContext {
			fmt.Printf("Goroutine %d frame %d at %s:%d (PC: %#x)\n", gid, ctx.Scope.Frame, loc.File, loc.Line, loc.PC)
		}
		return loc.File, loc.Line, true, nil
1539

A
aarzilli 已提交
1540 1541
	default:
		locs, err := t.client.FindLocation(ctx.Scope, args)
1542
		if err != nil {
1543
			return "", 0, false, err
1544
		}
A
aarzilli 已提交
1545
		if len(locs) > 1 {
1546
			return "", 0, false, debugger.AmbiguousLocationError{Location: args, CandidatesLocation: locs}
A
aarzilli 已提交
1547 1548
		}
		loc := locs[0]
1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
		if showContext {
			fmt.Printf("Showing %s:%d (PC: %#x)\n", loc.File, loc.Line, loc.PC)
		}
		return loc.File, loc.Line, false, nil
	}
}

func listCommand(t *Term, ctx callContext, args string) error {
	file, lineno, showarrow, err := getLocation(t, ctx, args, true)
	if err != nil {
		return err
1560
	}
1561
	return printfile(t, file, lineno, showarrow)
1562 1563
}

1564
func (c *Commands) sourceCommand(t *Term, ctx callContext, args string) error {
1565
	if len(args) == 0 {
1566 1567 1568
		return fmt.Errorf("wrong number of arguments: source <filename>")
	}

D
Derek Parker 已提交
1569
	return c.executeFile(t, args)
1570 1571
}

A
aarzilli 已提交
1572 1573
var disasmUsageError = errors.New("wrong number of arguments: disassemble [-a <start> <end>] [-l <locspec>]")

1574
func disassCommand(t *Term, ctx callContext, args string) error {
A
aarzilli 已提交
1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590
	var cmd, rest string

	if args != "" {
		argv := strings.SplitN(args, " ", 2)
		if len(argv) != 2 {
			return disasmUsageError
		}
		cmd = argv[0]
		rest = argv[1]
	}

	var disasm api.AsmInstructions
	var disasmErr error

	switch cmd {
	case "":
1591
		locs, err := t.client.FindLocation(ctx.Scope, "+0")
A
aarzilli 已提交
1592 1593 1594
		if err != nil {
			return err
		}
1595
		disasm, disasmErr = t.client.DisassemblePC(ctx.Scope, locs[0].PC, api.IntelFlavour)
A
aarzilli 已提交
1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608
	case "-a":
		v := strings.SplitN(rest, " ", 2)
		if len(v) != 2 {
			return disasmUsageError
		}
		startpc, err := strconv.ParseInt(v[0], 0, 64)
		if err != nil {
			return fmt.Errorf("wrong argument: %s is not a number", v[0])
		}
		endpc, err := strconv.ParseInt(v[1], 0, 64)
		if err != nil {
			return fmt.Errorf("wrong argument: %s is not a number", v[1])
		}
1609
		disasm, disasmErr = t.client.DisassembleRange(ctx.Scope, uint64(startpc), uint64(endpc), api.IntelFlavour)
A
aarzilli 已提交
1610
	case "-l":
1611
		locs, err := t.client.FindLocation(ctx.Scope, rest)
A
aarzilli 已提交
1612 1613 1614 1615 1616 1617
		if err != nil {
			return err
		}
		if len(locs) != 1 {
			return errors.New("expression specifies multiple locations")
		}
1618
		disasm, disasmErr = t.client.DisassemblePC(ctx.Scope, locs[0].PC, api.IntelFlavour)
A
aarzilli 已提交
1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631
	default:
		return disasmUsageError
	}

	if disasmErr != nil {
		return disasmErr
	}

	DisasmPrint(disasm, os.Stdout)

	return nil
}

1632 1633 1634 1635 1636 1637 1638
func libraries(t *Term, ctx callContext, args string) error {
	libs, err := t.client.ListDynamicLibraries()
	if err != nil {
		return err
	}
	d := digits(len(libs))
	for i := range libs {
1639
		fmt.Printf("%"+strconv.Itoa(d)+"d. %#x %s\n", i, libs[i].Address, libs[i].Path)
1640 1641 1642 1643
	}
	return nil
}

1644
func digits(n int) int {
1645 1646 1647
	if n <= 0 {
		return 1
	}
1648 1649 1650
	return int(math.Floor(math.Log10(float64(n)))) + 1
}

1651 1652
const stacktraceTruncatedMessage = "(truncated)"

1653
func printStack(stack []api.Stackframe, ind string, offsets bool) {
1654 1655 1656
	if len(stack) == 0 {
		return
	}
1657 1658 1659 1660 1661 1662 1663 1664 1665

	extranl := offsets
	for i := range stack {
		if extranl {
			break
		}
		extranl = extranl || (len(stack[i].Defers) > 0) || (len(stack[i].Arguments) > 0) || (len(stack[i].Locals) > 0)
	}

1666 1667
	d := digits(len(stack) - 1)
	fmtstr := "%s%" + strconv.Itoa(d) + "d  0x%016x in %s\n"
1668
	s := ind + strings.Repeat(" ", d+2+len(ind))
1669

A
aarzilli 已提交
1670
	for i := range stack {
1671 1672 1673 1674
		if stack[i].Err != "" {
			fmt.Printf("%serror: %s\n", s, stack[i].Err)
			continue
		}
1675
		fmt.Printf(fmtstr, ind, i, stack[i].PC, stack[i].Function.Name())
1676
		fmt.Printf("%sat %s:%d\n", s, ShortenFilePath(stack[i].File), stack[i].Line)
1677

1678 1679 1680 1681
		if offsets {
			fmt.Printf("%sframe: %+#x frame pointer %+#x\n", s, stack[i].FrameOffset, stack[i].FramePointerOffset)
		}

1682
		for j, d := range stack[i].Defers {
1683
			deferHeader := fmt.Sprintf("%s    defer %d: ", s, j+1)
1684 1685 1686 1687 1688 1689 1690 1691 1692 1693
			s2 := strings.Repeat(" ", len(deferHeader))
			if d.Unreadable != "" {
				fmt.Printf("%s(unreadable defer: %s)\n", deferHeader, d.Unreadable)
				continue
			}
			fmt.Printf("%s%#016x in %s\n", deferHeader, d.DeferredLoc.PC, d.DeferredLoc.Function.Name())
			fmt.Printf("%sat %s:%d\n", s2, d.DeferredLoc.File, d.DeferredLoc.Line)
			fmt.Printf("%sdeferred by %s at %s:%d\n", s2, d.DeferLoc.Function.Name(), d.DeferLoc.File, d.DeferLoc.Line)
		}

1694
		for j := range stack[i].Arguments {
1695
			fmt.Printf("%s    %s = %s\n", s, stack[i].Arguments[j].Name, stack[i].Arguments[j].SinglelineString())
1696 1697
		}
		for j := range stack[i].Locals {
1698
			fmt.Printf("%s    %s = %s\n", s, stack[i].Locals[j].Name, stack[i].Locals[j].SinglelineString())
1699
		}
1700 1701 1702 1703

		if extranl {
			fmt.Println()
		}
A
aarzilli 已提交
1704
	}
1705 1706 1707 1708

	if len(stack) > 0 && !stack[len(stack)-1].Bottom {
		fmt.Printf("%s"+stacktraceTruncatedMessage+"\n", ind)
	}
A
aarzilli 已提交
1709 1710
}

1711
func printcontext(t *Term, state *api.DebuggerState) {
1712 1713 1714 1715 1716 1717 1718 1719 1720
	for i := range state.Threads {
		if (state.CurrentThread != nil) && (state.Threads[i].ID == state.CurrentThread.ID) {
			continue
		}
		if state.Threads[i].Breakpoint != nil {
			printcontextThread(t, state.Threads[i])
		}
	}

D
Dan Mace 已提交
1721 1722
	if state.CurrentThread == nil {
		fmt.Println("No current thread available")
1723
		return
D
Dan Mace 已提交
1724
	}
1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737

	var th *api.Thread
	if state.SelectedGoroutine == nil {
		th = state.CurrentThread
	} else {
		for i := range state.Threads {
			if state.Threads[i].ID == state.SelectedGoroutine.ThreadID {
				th = state.Threads[i]
				break
			}
		}
		if th == nil {
			printcontextLocation(state.SelectedGoroutine.CurrentLoc)
1738
			return
1739 1740 1741 1742
		}
	}

	if th.File == "" {
D
Dan Mace 已提交
1743
		fmt.Printf("Stopped at: 0x%x\n", state.CurrentThread.PC)
1744
		t.Println("=>", "no source available")
1745
		return
D
Dan Mace 已提交
1746
	}
1747

1748
	printcontextThread(t, th)
1749

1750 1751 1752
	if state.When != "" {
		fmt.Println(state.When)
	}
1753
}
1754

1755
func printcontextLocation(loc api.Location) {
1756
	fmt.Printf("> %s() %s:%d (PC: %#v)\n", loc.Function.Name(), ShortenFilePath(loc.File), loc.Line, loc.PC)
1757 1758 1759 1760 1761 1762
	if loc.Function != nil && loc.Function.Optimized {
		fmt.Println(optimizedFunctionWarning)
	}
	return
}

1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773
func printReturnValues(th *api.Thread) {
	if th.ReturnValues == nil {
		return
	}
	fmt.Println("Values returned:")
	for _, v := range th.ReturnValues {
		fmt.Printf("\t%s: %s\n", v.Name, v.MultilineString("\t"))
	}
	fmt.Println()
}

1774 1775 1776 1777
func printcontextThread(t *Term, th *api.Thread) {
	fn := th.Function

	if th.Breakpoint == nil {
1778
		printcontextLocation(api.Location{PC: th.PC, File: th.File, Line: th.Line, Function: th.Function})
1779
		printReturnValues(th)
1780 1781 1782 1783
		return
	}

	args := ""
1784
	if th.BreakpointInfo != nil && th.Breakpoint.LoadArgs != nil && *th.Breakpoint.LoadArgs == ShortLoadConfig {
1785
		var arg []string
1786
		for _, ar := range th.BreakpointInfo.Arguments {
D
Derek Parker 已提交
1787 1788 1789 1790 1791 1792 1793 1794
			// For AI compatibility return values are included in the
			// argument list. This is a relic of the dark ages when the
			// Go debug information did not distinguish between the two.
			// Filter them out here instead, so during trace operations
			// they are not printed as an argument.
			if (ar.Flags & api.VariableArgument) != 0 {
				arg = append(arg, ar.SinglelineString())
			}
D
Derek Parker 已提交
1795
		}
1796 1797 1798
		args = strings.Join(arg, ", ")
	}

1799 1800 1801 1802 1803
	bpname := ""
	if th.Breakpoint.Name != "" {
		bpname = fmt.Sprintf("[%s] ", th.Breakpoint.Name)
	}

1804
	if hitCount, ok := th.Breakpoint.HitCount[strconv.Itoa(th.GoroutineID)]; ok {
1805 1806
		fmt.Printf("> %s%s(%s) %s:%d (hits goroutine(%d):%d total:%d) (PC: %#v)\n",
			bpname,
1807
			fn.Name(),
1808 1809 1810 1811 1812
			args,
			ShortenFilePath(th.File),
			th.Line,
			th.GoroutineID,
			hitCount,
A
aarzilli 已提交
1813 1814
			th.Breakpoint.TotalHitCount,
			th.PC)
D
Derek Parker 已提交
1815
	} else {
1816 1817
		fmt.Printf("> %s%s(%s) %s:%d (hits total:%d) (PC: %#v)\n",
			bpname,
1818
			fn.Name(),
1819 1820 1821
			args,
			ShortenFilePath(th.File),
			th.Line,
A
aarzilli 已提交
1822 1823
			th.Breakpoint.TotalHitCount,
			th.PC)
D
Dan Mace 已提交
1824
	}
1825 1826 1827
	if th.Function != nil && th.Function.Optimized {
		fmt.Println(optimizedFunctionWarning)
	}
D
Dan Mace 已提交
1828

1829 1830
	printReturnValues(th)

1831
	if th.BreakpointInfo != nil {
1832
		bp := th.Breakpoint
1833
		bpi := th.BreakpointInfo
A
aarzilli 已提交
1834 1835

		if bpi.Goroutine != nil {
1836
			writeGoroutineLong(os.Stdout, bpi.Goroutine, "\t")
A
aarzilli 已提交
1837 1838
		}

1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853
		for _, v := range bpi.Variables {
			fmt.Printf("\t%s: %s\n", v.Name, v.MultilineString("\t"))
		}

		for _, v := range bpi.Locals {
			if *bp.LoadLocals == LongLoadConfig {
				fmt.Printf("\t%s: %s\n", v.Name, v.MultilineString("\t"))
			} else {
				fmt.Printf("\t%s: %s\n", v.Name, v.SinglelineString())
			}
		}

		if bp.LoadArgs != nil && *bp.LoadArgs == LongLoadConfig {
			for _, v := range bpi.Arguments {
				fmt.Printf("\t%s: %s\n", v.Name, v.MultilineString("\t"))
1854
			}
A
aarzilli 已提交
1855 1856 1857 1858
		}

		if bpi.Stacktrace != nil {
			fmt.Printf("\tStack:\n")
1859
			printStack(bpi.Stacktrace, "\t\t", false)
A
aarzilli 已提交
1860 1861
		}
	}
1862 1863
}

1864
func printfile(t *Term, filename string, line int, showArrow bool) error {
1865 1866 1867
	if filename == "" {
		return nil
	}
1868
	file, err := os.Open(t.substitutePath(filename))
D
Dan Mace 已提交
1869 1870 1871 1872 1873
	if err != nil {
		return err
	}
	defer file.Close()

1874 1875 1876 1877 1878 1879
	fi, _ := file.Stat()
	lastModExe := t.client.LastModified()
	if fi.ModTime().After(lastModExe) {
		fmt.Println("Warning: listing may not match stale executable")
	}

1880
	buf := bufio.NewScanner(file)
1881
	l := line
D
Dan Mace 已提交
1882
	for i := 1; i < l-5; i++ {
1883 1884
		if !buf.Scan() {
			return nil
D
Dan Mace 已提交
1885 1886 1887
		}
	}

1888 1889 1890 1891 1892 1893
	s := l - 5
	if s < 1 {
		s = 1
	}

	for i := s; i <= l+5; i++ {
1894 1895
		if !buf.Scan() {
			return nil
D
Dan Mace 已提交
1896 1897
		}

1898
		var prefix string
1899
		if showArrow {
1900
			prefix = "  "
1901
			if i == l {
1902
				prefix = "=>"
1903
			}
D
Dan Mace 已提交
1904 1905
		}

1906 1907
		prefix = fmt.Sprintf("%s%4d:\t", prefix, i)
		t.Println(prefix, buf.Text())
D
Dan Mace 已提交
1908 1909 1910
	}
	return nil
}
D
Derek Parker 已提交
1911

D
Derek Parker 已提交
1912 1913
// ExitRequestError is returned when the user
// exits Delve.
D
Derek Parker 已提交
1914 1915 1916 1917 1918 1919
type ExitRequestError struct{}

func (ere ExitRequestError) Error() string {
	return ""
}

1920
func exitCommand(t *Term, ctx callContext, args string) error {
1921 1922 1923 1924 1925 1926
	if args == "-c" {
		if !t.client.IsMulticlient() {
			return errors.New("not connected to an --accept-multiclient server")
		}
		t.quitContinue = true
	}
D
Derek Parker 已提交
1927 1928
	return ExitRequestError{}
}
1929

H
Hubert Krauze 已提交
1930
func getBreakpointByIDOrName(t *Term, arg string) (*api.Breakpoint, error) {
1931
	if id, err := strconv.Atoi(arg); err == nil {
H
Hubert Krauze 已提交
1932
		return t.client.GetBreakpoint(id)
1933
	}
H
Hubert Krauze 已提交
1934
	return t.client.GetBreakpointByName(arg)
1935 1936
}

1937
func (c *Commands) onCmd(t *Term, ctx callContext, argstr string) error {
1938
	args := strings.SplitN(argstr, " ", 2)
1939 1940

	if len(args) < 2 {
1941 1942 1943
		return errors.New("not enough arguments")
	}

1944 1945 1946 1947 1948
	bp, err := getBreakpointByIDOrName(t, args[0])
	if err != nil {
		return err
	}

1949 1950
	ctx.Prefix = onPrefix
	ctx.Breakpoint = bp
1951
	err = c.CallWithContext(args[1], t, ctx)
1952 1953
	if err != nil {
		return err
1954
	}
1955
	return t.client.AmendBreakpoint(ctx.Breakpoint)
1956 1957
}

1958
func conditionCmd(t *Term, ctx callContext, argstr string) error {
1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973
	args := strings.SplitN(argstr, " ", 2)

	if len(args) < 2 {
		return fmt.Errorf("not enough arguments")
	}

	bp, err := getBreakpointByIDOrName(t, args[0])
	if err != nil {
		return err
	}
	bp.Cond = args[1]

	return t.client.AmendBreakpoint(bp)
}

D
Derek Parker 已提交
1974 1975
// ShortenFilePath take a full file path and attempts to shorten
// it by replacing the current directory to './'.
1976
func ShortenFilePath(fullPath string) string {
1977 1978 1979
	workingDir, _ := os.Getwd()
	return strings.Replace(fullPath, workingDir, ".", 1)
}
1980

D
Derek Parker 已提交
1981
func (c *Commands) executeFile(t *Term, name string) error {
1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997
	fh, err := os.Open(name)
	if err != nil {
		return err
	}
	defer fh.Close()

	scanner := bufio.NewScanner(fh)
	lineno := 0
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		lineno++

		if line == "" || line[0] == '#' {
			continue
		}

1998
		if err := c.Call(line, t); err != nil {
1999 2000 2001
			if _, isExitRequest := err.(ExitRequestError); isExitRequest {
				return err
			}
2002 2003 2004 2005 2006 2007
			fmt.Printf("%s:%d: %v\n", name, lineno, err)
		}
	}

	return scanner.Err()
}
2008

2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031
func rewind(t *Term, ctx callContext, args string) error {
	stateChan := t.client.Rewind()
	var state *api.DebuggerState
	for state = range stateChan {
		if state.Err != nil {
			return state.Err
		}
		printcontext(t, state)
	}
	printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
	return nil
}

func checkpoint(t *Term, ctx callContext, args string) error {
	if args == "" {
		state, err := t.client.GetState()
		if err != nil {
			return err
		}
		var loc api.Location = api.Location{PC: state.CurrentThread.PC, File: state.CurrentThread.File, Line: state.CurrentThread.Line, Function: state.CurrentThread.Function}
		if state.SelectedGoroutine != nil {
			loc = state.SelectedGoroutine.CurrentLoc
		}
2032
		args = fmt.Sprintf("%s() %s:%d (%#x)", loc.Function.Name(), loc.File, loc.Line, loc.PC)
2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050
	}

	cpid, err := t.client.Checkpoint(args)
	if err != nil {
		return err
	}

	fmt.Printf("Checkpoint c%d created.\n", cpid)
	return nil
}

func checkpoints(t *Term, ctx callContext, args string) error {
	cps, err := t.client.ListCheckpoints()
	if err != nil {
		return err
	}
	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 4, 4, 2, ' ', 0)
2051
	fmt.Fprintln(w, "ID\tWhen\tNote")
2052 2053 2054 2055 2056 2057 2058 2059
	for _, cp := range cps {
		fmt.Fprintf(w, "c%d\t%s\t%s\n", cp.ID, cp.When, cp.Where)
	}
	w.Flush()
	return nil
}

func clearCheckpoint(t *Term, ctx callContext, args string) error {
D
dpapastamos 已提交
2060
	if len(args) == 0 {
2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072
		return errors.New("not enough arguments to clear-checkpoint")
	}
	if args[0] != 'c' {
		return errors.New("clear-checkpoint argument must be a checkpoint ID")
	}
	id, err := strconv.Atoi(args[1:])
	if err != nil {
		return errors.New("clear-checkpoint argument must be a checkpoint ID")
	}
	return t.client.ClearCheckpoint(id)
}

2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092
func formatBreakpointName(bp *api.Breakpoint, upcase bool) string {
	thing := "breakpoint"
	if bp.Tracepoint {
		thing = "tracepoint"
	}
	if upcase {
		thing = strings.Title(thing)
	}
	id := bp.Name
	if id == "" {
		id = strconv.Itoa(bp.ID)
	}
	return fmt.Sprintf("%s %s", thing, id)
}

func formatBreakpointLocation(bp *api.Breakpoint) string {
	p := ShortenFilePath(bp.File)
	if bp.FunctionName != "" {
		return fmt.Sprintf("%#v for %s() %s:%d", bp.Addr, bp.FunctionName, p, bp.Line)
	}
H
Hubert Krauze 已提交
2093
	return fmt.Sprintf("%#v for %s:%d", bp.Addr, p, bp.Line)
2094
}