2015-03-20 21:11:11 +00:00
|
|
|
// Package command implements functions for responding to user
|
|
|
|
// input and dispatching to appropriate backend commands.
|
|
|
|
package terminal
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
2015-09-17 08:42:34 +00:00
|
|
|
"math"
|
2015-03-20 21:11:11 +00:00
|
|
|
"os"
|
|
|
|
"regexp"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2015-07-29 22:49:23 +00:00
|
|
|
"text/tabwriter"
|
2015-03-20 21:11:11 +00:00
|
|
|
|
|
|
|
"github.com/derekparker/delve/service"
|
|
|
|
"github.com/derekparker/delve/service/api"
|
2015-07-29 14:24:52 +00:00
|
|
|
"github.com/derekparker/delve/service/debugger"
|
2015-03-20 21:11:11 +00:00
|
|
|
)
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
type cmdfunc func(t *Term, args ...string) error
|
|
|
|
type scopedCmdfunc func(t *Term, scope api.EvalScope, args ...string) error
|
2015-08-28 20:06:29 +00:00
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
type filteringFunc func(t *Term, filter string) ([]string, error)
|
|
|
|
type scopedFilteringFunc func(t *Term, scope api.EvalScope, filter string) ([]string, error)
|
2015-03-20 21:11:11 +00:00
|
|
|
|
|
|
|
type command struct {
|
|
|
|
aliases []string
|
|
|
|
helpMsg string
|
|
|
|
cmdFn cmdfunc
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
type Commands struct {
|
2015-09-29 16:40:12 +00:00
|
|
|
cmds []command
|
|
|
|
lastCmd cmdfunc
|
|
|
|
client service.Client
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a Commands struct with default commands defined.
|
|
|
|
func DebugCommands(client service.Client) *Commands {
|
|
|
|
c := &Commands{client: client}
|
|
|
|
|
|
|
|
c.cmds = []command{
|
|
|
|
{aliases: []string{"help"}, cmdFn: c.help, helpMsg: "Prints the help message."},
|
2015-08-07 16:50:14 +00:00
|
|
|
{aliases: []string{"break", "b"}, cmdFn: breakpoint, helpMsg: "break <linespec> [-stack <n>|-goroutine|<variable name>]*"},
|
2015-07-29 22:49:23 +00:00
|
|
|
{aliases: []string{"trace", "t"}, cmdFn: tracepoint, helpMsg: "Set tracepoint, takes the same arguments as break."},
|
2015-07-03 20:35:22 +00:00
|
|
|
{aliases: []string{"restart", "r"}, cmdFn: restart, helpMsg: "Restart process."},
|
2015-03-20 21:11:11 +00:00
|
|
|
{aliases: []string{"continue", "c"}, cmdFn: cont, helpMsg: "Run until breakpoint or program termination."},
|
|
|
|
{aliases: []string{"step", "si"}, cmdFn: step, helpMsg: "Single step through program."},
|
|
|
|
{aliases: []string{"next", "n"}, cmdFn: next, helpMsg: "Step over to next source line."},
|
|
|
|
{aliases: []string{"threads"}, cmdFn: threads, helpMsg: "Print out info for every traced thread."},
|
2015-07-29 13:20:33 +00:00
|
|
|
{aliases: []string{"thread", "tr"}, cmdFn: thread, helpMsg: "Switch to the specified thread."},
|
2015-03-20 21:11:11 +00:00
|
|
|
{aliases: []string{"clear"}, cmdFn: clear, helpMsg: "Deletes breakpoint."},
|
|
|
|
{aliases: []string{"clearall"}, cmdFn: clearAll, helpMsg: "Deletes all breakpoints."},
|
|
|
|
{aliases: []string{"goroutines"}, cmdFn: goroutines, helpMsg: "Print out info for every goroutine."},
|
2015-08-28 20:06:29 +00:00
|
|
|
{aliases: []string{"goroutine"}, cmdFn: goroutine, helpMsg: "Sets current goroutine."},
|
2015-03-20 21:11:11 +00:00
|
|
|
{aliases: []string{"breakpoints", "bp"}, cmdFn: breakpoints, helpMsg: "Print out info for active breakpoints."},
|
2015-08-28 20:06:29 +00:00
|
|
|
{aliases: []string{"print", "p"}, cmdFn: g0f0(printVar), helpMsg: "Evaluate a variable."},
|
2015-09-28 10:01:18 +00:00
|
|
|
{aliases: []string{"set"}, cmdFn: g0f0(setVar), helpMsg: "Changes the value of a variable."},
|
2015-08-11 02:31:27 +00:00
|
|
|
{aliases: []string{"sources"}, cmdFn: filterSortAndOutput(sources), helpMsg: "Print list of source files, optionally filtered by a regexp."},
|
|
|
|
{aliases: []string{"funcs"}, cmdFn: filterSortAndOutput(funcs), helpMsg: "Print list of functions, optionally filtered by a regexp."},
|
2015-08-28 20:06:29 +00:00
|
|
|
{aliases: []string{"args"}, cmdFn: filterSortAndOutput(g0f0filter(args)), helpMsg: "Print function arguments, optionally filtered by a regexp."},
|
|
|
|
{aliases: []string{"locals"}, cmdFn: filterSortAndOutput(g0f0filter(locals)), helpMsg: "Print function locals, optionally filtered by a regexp."},
|
2015-08-11 02:31:27 +00:00
|
|
|
{aliases: []string{"vars"}, cmdFn: filterSortAndOutput(vars), helpMsg: "Print package variables, optionally filtered by a regexp."},
|
|
|
|
{aliases: []string{"regs"}, cmdFn: regs, helpMsg: "Print contents of CPU registers."},
|
2015-07-29 23:19:06 +00:00
|
|
|
{aliases: []string{"exit", "quit", "q"}, cmdFn: exitCommand, helpMsg: "Exit the debugger."},
|
2015-08-14 13:58:17 +00:00
|
|
|
{aliases: []string{"list", "ls"}, cmdFn: listCommand, helpMsg: "list <linespec>. Show source around current point or provided linespec."},
|
2015-09-20 04:41:26 +00:00
|
|
|
{aliases: []string{"stack", "bt"}, cmdFn: stackCommand, helpMsg: "stack [<depth>] [-full]. Prints stack."},
|
2015-08-28 20:06:29 +00:00
|
|
|
{aliases: []string{"frame"}, cmdFn: frame, helpMsg: "Sets current stack frame (0 is the top of the stack)"},
|
2015-09-29 16:40:12 +00:00
|
|
|
{aliases: []string{"source"}, cmdFn: c.sourceCommand, helpMsg: "Executes a file containing a list of delve commands"},
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2015-07-16 18:07:34 +00:00
|
|
|
// If it cannot find the command it will default to noCmdAvailable().
|
2015-03-20 21:11:11 +00:00
|
|
|
// If the command is an empty string it will replay the last command.
|
|
|
|
func (c *Commands) Find(cmdstr string) cmdfunc {
|
|
|
|
// 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) {
|
|
|
|
c.lastCmd = v.cmdFn
|
|
|
|
return v.cmdFn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return noCmdAvailable
|
|
|
|
}
|
|
|
|
|
2015-08-17 03:41:25 +00:00
|
|
|
// Merge takes aliases defined in the config struct and merges them with the default aliases.
|
|
|
|
func (c *Commands) Merge(allAliases map[string][]string) {
|
|
|
|
for i := range c.cmds {
|
|
|
|
if aliases, ok := allAliases[c.cmds[i].aliases[0]]; ok {
|
|
|
|
c.cmds[i].aliases = append(c.cmds[i].aliases, aliases...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-20 21:11:11 +00:00
|
|
|
func CommandFunc(fn func() error) cmdfunc {
|
2015-09-30 05:42:06 +00:00
|
|
|
return func(t *Term, args ...string) error {
|
2015-03-20 21:11:11 +00:00
|
|
|
return fn()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func noCmdAvailable(t *Term, args ...string) error {
|
2015-05-04 22:31:13 +00:00
|
|
|
return fmt.Errorf("command not available")
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func nullCommand(t *Term, args ...string) error {
|
2015-03-20 21:11:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func (c *Commands) help(t *Term, args ...string) error {
|
2015-03-20 21:11:11 +00:00
|
|
|
fmt.Println("The following commands are available:")
|
2015-07-29 22:49:23 +00:00
|
|
|
w := new(tabwriter.Writer)
|
|
|
|
w.Init(os.Stdout, 0, 8, 0, '-', 0)
|
2015-03-20 21:11:11 +00:00
|
|
|
for _, cmd := range c.cmds {
|
2015-07-29 22:49:23 +00:00
|
|
|
if len(cmd.aliases) > 1 {
|
|
|
|
fmt.Fprintf(w, " %s (alias: %s) \t %s\n", cmd.aliases[0], strings.Join(cmd.aliases[1:], " | "), cmd.helpMsg)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, " %s \t %s\n", cmd.aliases[0], cmd.helpMsg)
|
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
2015-07-29 22:49:23 +00:00
|
|
|
return w.Flush()
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
|
2015-09-10 18:12:42 +00:00
|
|
|
type byThreadID []*api.Thread
|
2015-09-10 18:04:59 +00:00
|
|
|
|
2015-09-10 18:12:42 +00:00
|
|
|
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 }
|
2015-09-10 18:04:59 +00:00
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func threads(t *Term, args ...string) error {
|
|
|
|
threads, err := t.client.ListThreads()
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
state, err := t.client.GetState()
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-10 18:12:42 +00:00
|
|
|
sort.Sort(byThreadID(threads))
|
2015-03-20 21:11:11 +00:00
|
|
|
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",
|
2015-08-19 22:38:53 +00:00
|
|
|
prefix, th.ID, th.PC, shortenFilePath(th.File),
|
2015-03-20 21:11:11 +00:00
|
|
|
th.Line, th.Function.Name)
|
|
|
|
} else {
|
2015-08-28 20:06:29 +00:00
|
|
|
fmt.Printf("%sThread %s\n", prefix, formatThread(th))
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func thread(t *Term, args ...string) error {
|
2015-05-29 17:29:02 +00:00
|
|
|
if len(args) == 0 {
|
|
|
|
return fmt.Errorf("you must specify a thread")
|
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
tid, err := strconv.Atoi(args[0])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
oldState, err := t.client.GetState()
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
newState, err := t.client.SwitchThread(tid)
|
2015-03-20 21:11:11 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-09-10 18:12:42 +00:00
|
|
|
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 }
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func goroutines(t *Term, args ...string) error {
|
|
|
|
state, err := t.client.GetState()
|
2015-08-28 20:06:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
gs, err := t.client.ListGoroutines()
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-10 18:12:42 +00:00
|
|
|
sort.Sort(byGoroutineID(gs))
|
2015-03-20 21:11:11 +00:00
|
|
|
fmt.Printf("[%d goroutines]\n", len(gs))
|
|
|
|
for _, g := range gs {
|
2015-08-28 20:06:29 +00:00
|
|
|
prefix := " "
|
2015-10-03 12:29:46 +00:00
|
|
|
if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID {
|
2015-08-28 20:06:29 +00:00
|
|
|
prefix = "* "
|
|
|
|
}
|
|
|
|
fmt.Printf("%sGoroutine %s\n", prefix, formatGoroutine(g))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func goroutine(t *Term, args ...string) error {
|
2015-08-28 20:06:29 +00:00
|
|
|
switch len(args) {
|
|
|
|
case 0:
|
2015-09-30 05:42:06 +00:00
|
|
|
return printscope(t)
|
2015-08-28 20:06:29 +00:00
|
|
|
|
|
|
|
case 1:
|
|
|
|
gid, err := strconv.Atoi(args[0])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
oldState, err := t.client.GetState()
|
2015-08-28 20:06:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
newState, err := t.client.SwitchGoroutine(gid)
|
2015-08-28 20:06:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf("Switched from %d to %d (thread %d)\n", oldState.SelectedGoroutine.ID, gid, newState.CurrentThread.ID)
|
|
|
|
return nil
|
|
|
|
|
|
|
|
default:
|
2015-09-30 05:42:06 +00:00
|
|
|
return scopePrefix(t, "goroutine", args...)
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func frame(t *Term, args ...string) error {
|
|
|
|
return scopePrefix(t, "frame", args...)
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func scopePrefix(t *Term, cmdname string, pargs ...string) error {
|
2015-08-28 20:06:29 +00:00
|
|
|
fullargs := make([]string, 0, len(pargs)+1)
|
|
|
|
fullargs = append(fullargs, cmdname)
|
|
|
|
fullargs = append(fullargs, pargs...)
|
|
|
|
|
|
|
|
scope := api.EvalScope{-1, 0}
|
|
|
|
lastcmd := ""
|
|
|
|
|
|
|
|
callFilterSortAndOutput := func(fn scopedFilteringFunc, fnargs []string) error {
|
2015-09-30 05:42:06 +00:00
|
|
|
outfn := filterSortAndOutput(func(t *Term, filter string) ([]string, error) {
|
|
|
|
return fn(t, scope, filter)
|
2015-08-28 20:06:29 +00:00
|
|
|
})
|
2015-09-30 05:42:06 +00:00
|
|
|
return outfn(t, fnargs...)
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < len(fullargs); i++ {
|
|
|
|
lastcmd = fullargs[i]
|
|
|
|
switch fullargs[i] {
|
|
|
|
case "goroutine":
|
|
|
|
if i+1 >= len(fullargs) {
|
|
|
|
return fmt.Errorf("goroutine command needs an argument")
|
|
|
|
}
|
|
|
|
n, err := strconv.Atoi(fullargs[i+1])
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid argument to goroutine, expected integer")
|
|
|
|
}
|
|
|
|
scope.GoroutineID = int(n)
|
|
|
|
i++
|
|
|
|
case "frame":
|
|
|
|
if i+1 >= len(fullargs) {
|
|
|
|
return fmt.Errorf("frame command needs an argument")
|
|
|
|
}
|
|
|
|
n, err := strconv.Atoi(fullargs[i+1])
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid argument to frame, expected integer")
|
|
|
|
}
|
|
|
|
scope.Frame = int(n)
|
|
|
|
i++
|
2015-09-10 17:24:49 +00:00
|
|
|
case "list", "ls":
|
|
|
|
frame, gid := scope.Frame, scope.GoroutineID
|
2015-09-30 05:42:06 +00:00
|
|
|
locs, err := t.client.Stacktrace(gid, frame, false)
|
2015-09-10 17:24:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if frame >= len(locs) {
|
|
|
|
return fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
|
|
|
|
}
|
|
|
|
loc := locs[frame]
|
2015-09-30 05:42:06 +00:00
|
|
|
return printfile(t, loc.File, loc.Line, true)
|
2015-09-20 04:41:26 +00:00
|
|
|
case "stack", "bt":
|
|
|
|
depth, full, err := parseStackArgs(fullargs[i+1:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
stack, err := t.client.Stacktrace(scope.GoroutineID, depth, full)
|
2015-09-20 04:41:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
printStack(stack, "")
|
|
|
|
return nil
|
2015-08-28 20:06:29 +00:00
|
|
|
case "locals":
|
|
|
|
return callFilterSortAndOutput(locals, fullargs[i+1:])
|
|
|
|
case "args":
|
|
|
|
return callFilterSortAndOutput(args, fullargs[i+1:])
|
|
|
|
case "print", "p":
|
2015-09-30 05:42:06 +00:00
|
|
|
return printVar(t, scope, fullargs[i+1:]...)
|
2015-08-28 20:06:29 +00:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("unknown command %s", fullargs[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Errorf("no command passed to %s", lastcmd)
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func printscope(t *Term) error {
|
|
|
|
state, err := t.client.GetState()
|
2015-08-28 20:06:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
2015-08-28 20:06:29 +00:00
|
|
|
|
|
|
|
fmt.Printf("Thread %s\nGoroutine %s\n", formatThread(state.CurrentThread), formatGoroutine(state.SelectedGoroutine))
|
2015-03-20 21:11:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
func formatThread(th *api.Thread) string {
|
|
|
|
if th == nil {
|
|
|
|
return "<nil>"
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%d at %s:%d", th.ID, shortenFilePath(th.File), th.Line)
|
|
|
|
}
|
|
|
|
|
2015-06-28 15:00:56 +00:00
|
|
|
func formatGoroutine(g *api.Goroutine) string {
|
2015-08-28 20:06:29 +00:00
|
|
|
if g == nil {
|
|
|
|
return "<nil>"
|
|
|
|
}
|
2015-06-28 15:00:56 +00:00
|
|
|
fname := ""
|
|
|
|
if g.Function != nil {
|
|
|
|
fname = g.Function.Name
|
|
|
|
}
|
2015-08-19 22:38:53 +00:00
|
|
|
return fmt.Sprintf("%d - %s:%d %s (%#v)", g.ID, shortenFilePath(g.File), g.Line, fname, g.PC)
|
2015-06-28 15:00:56 +00:00
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func restart(t *Term, args ...string) error {
|
|
|
|
if err := t.client.Restart(); err != nil {
|
2015-07-03 20:35:22 +00:00
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
fmt.Println("Process restarted with PID", t.client.ProcessPid())
|
2015-07-03 20:35:22 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func cont(t *Term, args ...string) error {
|
|
|
|
stateChan := t.client.Continue()
|
2015-07-01 03:16:52 +00:00
|
|
|
for state := range stateChan {
|
2015-06-28 15:00:56 +00:00
|
|
|
if state.Err != nil {
|
|
|
|
return state.Err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
printcontext(t, state)
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func step(t *Term, args ...string) error {
|
|
|
|
state, err := t.client.Step()
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
printcontext(t, state)
|
2015-03-20 21:11:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func next(t *Term, args ...string) error {
|
|
|
|
state, err := t.client.Next()
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
printcontext(t, state)
|
2015-03-20 21:11:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func clear(t *Term, args ...string) error {
|
2015-03-20 21:11:11 +00:00
|
|
|
if len(args) == 0 {
|
2015-05-04 22:31:13 +00:00
|
|
|
return fmt.Errorf("not enough arguments")
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
id, err := strconv.Atoi(args[0])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
bp, err := t.client.ClearBreakpoint(id)
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-08-19 22:38:53 +00:00
|
|
|
fmt.Printf("Breakpoint %d cleared at %#v for %s %s:%d\n", bp.ID, bp.Addr, bp.FunctionName, shortenFilePath(bp.File), bp.Line)
|
2015-03-20 21:11:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func clearAll(t *Term, args ...string) error {
|
|
|
|
breakPoints, err := t.client.ListBreakpoints()
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, bp := range breakPoints {
|
2015-09-30 05:42:06 +00:00
|
|
|
_, err := t.client.ClearBreakpoint(bp.ID)
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
2015-08-19 22:38:53 +00:00
|
|
|
fmt.Printf("Couldn't delete breakpoint %d at %#v %s:%d: %s\n", bp.ID, bp.Addr, shortenFilePath(bp.File), bp.Line, err)
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
2015-08-19 22:38:53 +00:00
|
|
|
fmt.Printf("Breakpoint %d cleared at %#v for %s %s:%d\n", bp.ID, bp.Addr, bp.FunctionName, shortenFilePath(bp.File), bp.Line)
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-06-12 19:32:32 +00:00
|
|
|
type ById []*api.Breakpoint
|
2015-03-20 21:11:11 +00:00
|
|
|
|
|
|
|
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 }
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func breakpoints(t *Term, args ...string) error {
|
|
|
|
breakPoints, err := t.client.ListBreakpoints()
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sort.Sort(ById(breakPoints))
|
|
|
|
for _, bp := range breakPoints {
|
2015-06-28 15:00:56 +00:00
|
|
|
thing := "Breakpoint"
|
|
|
|
if bp.Tracepoint {
|
|
|
|
thing = "Tracepoint"
|
|
|
|
}
|
2015-08-19 22:38:53 +00:00
|
|
|
fmt.Printf("%s %d at %#v %s:%d\n", thing, bp.ID, bp.Addr, shortenFilePath(bp.File), bp.Line)
|
2015-06-28 15:00:56 +00:00
|
|
|
|
|
|
|
var attrs []string
|
|
|
|
if bp.Stacktrace > 0 {
|
|
|
|
attrs = append(attrs, "-stack")
|
|
|
|
attrs = append(attrs, strconv.Itoa(bp.Stacktrace))
|
|
|
|
}
|
|
|
|
if bp.Goroutine {
|
|
|
|
attrs = append(attrs, "-goroutine")
|
|
|
|
}
|
2015-07-01 03:16:52 +00:00
|
|
|
for i := range bp.Variables {
|
|
|
|
attrs = append(attrs, bp.Variables[i])
|
2015-06-28 15:00:56 +00:00
|
|
|
}
|
|
|
|
if len(attrs) > 0 {
|
|
|
|
fmt.Printf("\t%s\n", strings.Join(attrs, " "))
|
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func setBreakpoint(t *Term, tracepoint bool, args ...string) error {
|
2015-06-28 15:00:56 +00:00
|
|
|
if len(args) < 1 {
|
|
|
|
return fmt.Errorf("address required, specify either a function name or <file:line>")
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
2015-06-12 19:32:32 +00:00
|
|
|
requestedBp := &api.Breakpoint{}
|
2015-03-20 21:11:11 +00:00
|
|
|
|
2015-06-28 15:00:56 +00:00
|
|
|
for i := 1; i < len(args); i++ {
|
|
|
|
switch args[i] {
|
|
|
|
case "-stack":
|
|
|
|
i++
|
|
|
|
n, err := strconv.Atoi(args[i])
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("argument of -stack must be a number")
|
|
|
|
}
|
|
|
|
requestedBp.Stacktrace = n
|
|
|
|
case "-goroutine":
|
|
|
|
requestedBp.Goroutine = true
|
|
|
|
default:
|
2015-07-01 03:16:52 +00:00
|
|
|
requestedBp.Variables = append(requestedBp.Variables, args[i])
|
2015-06-28 15:00:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
requestedBp.Tracepoint = tracepoint
|
2015-09-30 05:42:06 +00:00
|
|
|
locs, err := t.client.FindLocation(api.EvalScope{-1, 0}, args[0])
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-06-28 15:00:56 +00:00
|
|
|
thing := "Breakpoint"
|
|
|
|
if tracepoint {
|
|
|
|
thing = "Tracepoint"
|
|
|
|
}
|
2015-08-07 16:50:14 +00:00
|
|
|
for _, loc := range locs {
|
|
|
|
requestedBp.Addr = loc.PC
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
bp, err := t.client.CreateBreakpoint(requestedBp)
|
2015-08-07 16:50:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-08-19 22:38:53 +00:00
|
|
|
fmt.Printf("%s %d set at %#v for %s %s:%d\n", thing, bp.ID, bp.Addr, bp.FunctionName, shortenFilePath(bp.File), bp.Line)
|
2015-08-07 16:50:14 +00:00
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func breakpoint(t *Term, args ...string) error {
|
|
|
|
return setBreakpoint(t, false, args...)
|
2015-06-28 15:00:56 +00:00
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func tracepoint(t *Term, args ...string) error {
|
|
|
|
return setBreakpoint(t, true, args...)
|
2015-06-28 15:00:56 +00:00
|
|
|
}
|
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
func g0f0(fn scopedCmdfunc) cmdfunc {
|
2015-09-30 05:42:06 +00:00
|
|
|
return func(t *Term, args ...string) error {
|
|
|
|
return fn(t, api.EvalScope{-1, 0}, args...)
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func g0f0filter(fn scopedFilteringFunc) filteringFunc {
|
2015-09-30 05:42:06 +00:00
|
|
|
return func(t *Term, filter string) ([]string, error) {
|
|
|
|
return fn(t, api.EvalScope{-1, 0}, filter)
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func printVar(t *Term, scope api.EvalScope, args ...string) error {
|
2015-03-20 21:11:11 +00:00
|
|
|
if len(args) == 0 {
|
2015-05-04 22:31:13 +00:00
|
|
|
return fmt.Errorf("not enough arguments")
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
val, err := t.client.EvalVariable(scope, args[0])
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fmt.Println(val.Value)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func setVar(t *Term, scope api.EvalScope, args ...string) error {
|
2015-09-28 10:01:18 +00:00
|
|
|
if len(args) != 2 {
|
|
|
|
return fmt.Errorf("wrong number of arguments")
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
return t.client.SetVariable(scope, args[0], args[1])
|
2015-09-28 10:01:18 +00:00
|
|
|
}
|
|
|
|
|
2015-08-11 02:31:27 +00:00
|
|
|
func filterVariables(vars []api.Variable, filter string) []string {
|
|
|
|
reg, err := regexp.Compile(filter)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, err.Error())
|
|
|
|
return nil
|
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
data := make([]string, 0, len(vars))
|
|
|
|
for _, v := range vars {
|
2015-08-11 02:31:27 +00:00
|
|
|
if reg == nil || reg.Match([]byte(v.Name)) {
|
2015-03-20 21:11:11 +00:00
|
|
|
data = append(data, fmt.Sprintf("%s = %s", v.Name, v.Value))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func sources(t *Term, filter string) ([]string, error) {
|
|
|
|
return t.client.ListSources(filter)
|
2015-08-11 02:31:27 +00:00
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func funcs(t *Term, filter string) ([]string, error) {
|
|
|
|
return t.client.ListFunctions(filter)
|
2015-08-11 02:31:27 +00:00
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func args(t *Term, scope api.EvalScope, filter string) ([]string, error) {
|
|
|
|
vars, err := t.client.ListFunctionArgs(scope)
|
2015-08-11 02:31:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return filterVariables(vars, filter), nil
|
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func locals(t *Term, scope api.EvalScope, filter string) ([]string, error) {
|
|
|
|
locals, err := t.client.ListLocalVariables(scope)
|
2015-08-11 02:31:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return filterVariables(locals, filter), nil
|
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func vars(t *Term, filter string) ([]string, error) {
|
|
|
|
vars, err := t.client.ListPackageVariables(filter)
|
2015-08-11 02:31:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return filterVariables(vars, filter), nil
|
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func regs(t *Term, args ...string) error {
|
|
|
|
regs, err := t.client.ListRegisters()
|
2015-08-11 02:31:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fmt.Println(regs)
|
|
|
|
return nil
|
|
|
|
}
|
2015-06-19 07:20:10 +00:00
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
func filterSortAndOutput(fn filteringFunc) cmdfunc {
|
2015-09-30 05:42:06 +00:00
|
|
|
return func(t *Term, args ...string) error {
|
2015-08-11 02:31:27 +00:00
|
|
|
var filter string
|
|
|
|
if len(args) == 1 {
|
|
|
|
if _, err := regexp.Compile(args[0]); err != nil {
|
|
|
|
return fmt.Errorf("invalid filter argument: %s", err.Error())
|
|
|
|
}
|
|
|
|
filter = args[0]
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
data, err := fn(t, filter)
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-08-11 02:31:27 +00:00
|
|
|
sort.Sort(sort.StringSlice(data))
|
|
|
|
for _, d := range data {
|
|
|
|
fmt.Println(d)
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
2015-08-11 02:31:27 +00:00
|
|
|
return nil
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func stackCommand(t *Term, args ...string) error {
|
2015-09-20 04:41:26 +00:00
|
|
|
var (
|
|
|
|
err error
|
|
|
|
goroutineid = -1
|
|
|
|
)
|
|
|
|
depth, full, err := parseStackArgs(args)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
stack, err := t.client.Stacktrace(goroutineid, depth, full)
|
2015-09-20 04:41:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
printStack(stack, "")
|
|
|
|
return nil
|
|
|
|
}
|
2015-06-17 17:11:57 +00:00
|
|
|
|
2015-09-20 04:41:26 +00:00
|
|
|
func parseStackArgs(args []string) (int, bool, error) {
|
|
|
|
var (
|
|
|
|
depth = 10
|
|
|
|
full = false
|
|
|
|
)
|
2015-09-17 08:42:34 +00:00
|
|
|
for i := range args {
|
|
|
|
if args[i] == "-full" {
|
|
|
|
full = true
|
|
|
|
} else {
|
|
|
|
n, err := strconv.Atoi(args[i])
|
|
|
|
if err != nil {
|
2015-09-20 04:41:26 +00:00
|
|
|
return 0, false, fmt.Errorf("depth must be a number")
|
2015-09-17 08:42:34 +00:00
|
|
|
}
|
2015-09-20 04:41:26 +00:00
|
|
|
depth = n
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
}
|
2015-09-20 04:41:26 +00:00
|
|
|
return depth, full, nil
|
2015-06-28 15:00:56 +00:00
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func listCommand(t *Term, args ...string) error {
|
2015-07-29 14:24:52 +00:00
|
|
|
if len(args) == 0 {
|
2015-09-30 05:42:06 +00:00
|
|
|
state, err := t.client.GetState()
|
2015-07-29 14:24:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
printcontext(t, state)
|
2015-07-29 14:24:52 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
locs, err := t.client.FindLocation(api.EvalScope{-1, 0}, args[0])
|
2015-07-29 14:24:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(locs) > 1 {
|
|
|
|
return debugger.AmbiguousLocationError{Location: args[0], CandidatesLocation: locs}
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
printfile(t, locs[0].File, locs[0].Line, false)
|
2015-07-29 14:24:52 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-29 16:40:12 +00:00
|
|
|
func (cmds *Commands) sourceCommand(t *Term, args ...string) error {
|
|
|
|
if len(args) != 1 {
|
|
|
|
return fmt.Errorf("wrong number of arguments: source <filename>")
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmds.executeFile(t, args[0])
|
|
|
|
}
|
|
|
|
|
2015-09-17 08:42:34 +00:00
|
|
|
func digits(n int) int {
|
|
|
|
return int(math.Floor(math.Log10(float64(n)))) + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
func printStack(stack []api.Stackframe, ind string) {
|
|
|
|
d := digits(len(stack) - 1)
|
|
|
|
fmtstr := "%s%" + strconv.Itoa(d) + "d 0x%016x in %s\n"
|
2015-09-20 04:58:18 +00:00
|
|
|
s := strings.Repeat(" ", d+2+len(ind))
|
2015-09-17 08:42:34 +00:00
|
|
|
|
2015-06-17 17:11:57 +00:00
|
|
|
for i := range stack {
|
|
|
|
name := "(nil)"
|
|
|
|
if stack[i].Function != nil {
|
|
|
|
name = stack[i].Function.Name
|
|
|
|
}
|
2015-09-17 08:42:34 +00:00
|
|
|
fmt.Printf(fmtstr, ind, i, stack[i].PC, name)
|
|
|
|
fmt.Printf("%sat %s:%d\n", s, shortenFilePath(stack[i].File), stack[i].Line)
|
|
|
|
|
|
|
|
for j := range stack[i].Arguments {
|
|
|
|
fmt.Printf("%s %s = %s\n", s, stack[i].Arguments[j].Name, stack[i].Arguments[j].Value)
|
|
|
|
}
|
|
|
|
for j := range stack[i].Locals {
|
|
|
|
fmt.Printf("%s %s = %s\n", s, stack[i].Locals[j].Name, stack[i].Locals[j].Value)
|
|
|
|
}
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func printcontext(t *Term, state *api.DebuggerState) error {
|
2015-03-20 21:11:11 +00:00
|
|
|
if state.CurrentThread == nil {
|
|
|
|
fmt.Println("No current thread available")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if len(state.CurrentThread.File) == 0 {
|
|
|
|
fmt.Printf("Stopped at: 0x%x\n", state.CurrentThread.PC)
|
2015-09-30 05:42:06 +00:00
|
|
|
t.Println("=>", "no source available")
|
2015-03-20 21:11:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
2015-07-12 20:18:14 +00:00
|
|
|
var fn *api.Function
|
2015-03-20 21:11:11 +00:00
|
|
|
if state.CurrentThread.Function != nil {
|
2015-07-12 20:18:14 +00:00
|
|
|
fn = state.CurrentThread.Function
|
|
|
|
}
|
|
|
|
if state.Breakpoint != nil && state.Breakpoint.Tracepoint {
|
|
|
|
var args []string
|
|
|
|
for _, arg := range state.CurrentThread.Function.Args {
|
|
|
|
args = append(args, arg.Value)
|
|
|
|
}
|
2015-08-19 22:38:53 +00:00
|
|
|
fmt.Printf("> %s(%s) %s:%d\n", fn.Name, strings.Join(args, ", "), shortenFilePath(state.CurrentThread.File), state.CurrentThread.Line)
|
2015-07-12 20:18:14 +00:00
|
|
|
} else {
|
2015-08-19 22:38:53 +00:00
|
|
|
fmt.Printf("> %s() %s:%d\n", fn.Name, shortenFilePath(state.CurrentThread.File), state.CurrentThread.Line)
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
|
2015-06-28 15:00:56 +00:00
|
|
|
if state.BreakpointInfo != nil {
|
|
|
|
bpi := state.BreakpointInfo
|
|
|
|
|
|
|
|
if bpi.Goroutine != nil {
|
|
|
|
fmt.Printf("\tGoroutine %s\n", formatGoroutine(bpi.Goroutine))
|
|
|
|
}
|
|
|
|
|
|
|
|
ss := make([]string, len(bpi.Variables))
|
|
|
|
for i, v := range bpi.Variables {
|
|
|
|
ss[i] = fmt.Sprintf("%s: <%v>", v.Name, v.Value)
|
|
|
|
}
|
|
|
|
fmt.Printf("\t%s\n", strings.Join(ss, ", "))
|
|
|
|
|
|
|
|
if bpi.Stacktrace != nil {
|
|
|
|
fmt.Printf("\tStack:\n")
|
|
|
|
printStack(bpi.Stacktrace, "\t\t")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if state.Breakpoint != nil && state.Breakpoint.Tracepoint {
|
|
|
|
return nil
|
|
|
|
}
|
2015-09-30 05:42:06 +00:00
|
|
|
return printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
2015-07-29 14:24:52 +00:00
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func printfile(t *Term, filename string, line int, showArrow bool) error {
|
2015-07-29 14:24:52 +00:00
|
|
|
file, err := os.Open(filename)
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
buf := bufio.NewScanner(file)
|
2015-07-29 14:24:52 +00:00
|
|
|
l := line
|
2015-03-20 21:11:11 +00:00
|
|
|
for i := 1; i < l-5; i++ {
|
2015-09-30 05:42:06 +00:00
|
|
|
if !buf.Scan() {
|
|
|
|
return nil
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-29 14:24:52 +00:00
|
|
|
s := l - 5
|
|
|
|
if s < 1 {
|
|
|
|
s = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := s; i <= l+5; i++ {
|
2015-09-30 05:42:06 +00:00
|
|
|
if !buf.Scan() {
|
|
|
|
return nil
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
|
2015-09-30 15:37:14 +00:00
|
|
|
var prefix string
|
2015-07-29 14:24:52 +00:00
|
|
|
if showArrow {
|
2015-09-30 15:37:14 +00:00
|
|
|
prefix = " "
|
2015-07-29 14:24:52 +00:00
|
|
|
if i == l {
|
2015-09-30 15:37:14 +00:00
|
|
|
prefix = "=>"
|
2015-07-29 14:24:52 +00:00
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
|
2015-09-30 15:37:14 +00:00
|
|
|
prefix = fmt.Sprintf("%s%4d:\t", prefix, i)
|
|
|
|
t.Println(prefix, buf.Text())
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2015-07-29 23:19:06 +00:00
|
|
|
|
|
|
|
type ExitRequestError struct{}
|
|
|
|
|
|
|
|
func (ere ExitRequestError) Error() string {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2015-09-30 05:42:06 +00:00
|
|
|
func exitCommand(t *Term, args ...string) error {
|
2015-07-29 23:19:06 +00:00
|
|
|
return ExitRequestError{}
|
|
|
|
}
|
2015-08-19 22:38:53 +00:00
|
|
|
|
|
|
|
func shortenFilePath(fullPath string) string {
|
|
|
|
workingDir, _ := os.Getwd()
|
|
|
|
return strings.Replace(fullPath, workingDir, ".", 1)
|
|
|
|
}
|
2015-09-29 16:40:12 +00:00
|
|
|
|
|
|
|
func (cmds *Commands) executeFile(t *Term, name string) error {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
cmdstr, args := parseCommand(line)
|
|
|
|
cmd := cmds.Find(cmdstr)
|
|
|
|
err := cmd(t, args...)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("%s:%d: %v\n", name, lineno, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return scanner.Err()
|
|
|
|
}
|