delve/command/command.go

267 lines
5.1 KiB
Go
Raw Normal View History

2014-05-21 15:23:14 +00:00
// Package command implements functions for responding to user
// input and dispatching to appropriate backend commands.
2014-05-20 21:28:24 +00:00
package command
import (
"bufio"
"debug/gosym"
2014-05-20 21:28:24 +00:00
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
2014-10-15 14:21:46 +00:00
"github.com/derekparker/delve/proctl"
2014-05-20 21:28:24 +00:00
)
type cmdfunc func(proc *proctl.DebuggedProcess, args ...string) error
2014-05-20 21:28:24 +00:00
type Commands struct {
cmds map[string]cmdfunc
}
// Returns a Commands struct with default commands defined.
2014-05-20 21:28:24 +00:00
func DebugCommands() *Commands {
cmds := map[string]cmdfunc{
2014-10-20 17:06:36 +00:00
"help": help,
"continue": cont,
2014-07-11 21:18:07 +00:00
"next": next,
2014-06-27 01:36:39 +00:00
"break": breakpoint,
"step": step,
"clear": clear,
2014-10-07 22:03:20 +00:00
"print": printVar,
"threads": threads,
"": nullCommand,
2014-05-20 21:28:24 +00:00
}
return &Commands{cmds}
}
2014-05-21 15:23:14 +00:00
// Register custom commands. Expects cf to be a func of type cmdfunc,
// returning only an error.
2014-05-20 23:09:34 +00:00
func (c *Commands) Register(cmdstr string, cf cmdfunc) {
c.cmds[cmdstr] = cf
}
// Find will look up the command function for the given command input.
// If it cannot find the command it will defualt to noCmdAvailable().
// If the command is an empty string it will replay the last command.
2014-05-20 21:28:24 +00:00
func (c *Commands) Find(cmdstr string) cmdfunc {
cmd, ok := c.cmds[cmdstr]
if !ok {
return noCmdAvailable
}
// Allow <enter> to replay last command
c.cmds[""] = cmd
2014-05-20 21:28:24 +00:00
return cmd
}
2014-05-27 15:44:29 +00:00
func CommandFunc(fn func() error) cmdfunc {
return func(p *proctl.DebuggedProcess, args ...string) error {
2014-05-27 15:44:29 +00:00
return fn()
}
}
func noCmdAvailable(p *proctl.DebuggedProcess, ars ...string) error {
2014-05-20 21:28:24 +00:00
return fmt.Errorf("command not available")
}
func nullCommand(p *proctl.DebuggedProcess, ars ...string) error {
return nil
}
2014-10-20 17:06:36 +00:00
func help(p *proctl.DebuggedProcess, ars ...string) error {
fmt.Println(`The following commands are available:
break - Set break point at the entry point of a function, or at a specific file/line. Example: break foo.go:13.
continue - Run until breakpoint or program termination.
step - Single step through program.
next - Step over to next source line.
2014-11-08 14:58:09 +00:00
threads - Print out info for every traced thread.
2014-10-20 17:06:36 +00:00
print $var - Evaluate a variable.`)
return nil
}
func threads(p *proctl.DebuggedProcess, ars ...string) error {
return p.PrintThreadInfo()
}
func cont(p *proctl.DebuggedProcess, ars ...string) error {
err := p.Continue()
if err != nil {
return err
}
return printcontext(p)
}
func step(p *proctl.DebuggedProcess, args ...string) error {
err := p.Step()
if err != nil {
return err
}
return printcontext(p)
}
2014-07-11 21:18:07 +00:00
func next(p *proctl.DebuggedProcess, args ...string) error {
err := p.Next()
if err != nil {
return err
}
return printcontext(p)
2014-07-11 21:18:07 +00:00
}
func clear(p *proctl.DebuggedProcess, args ...string) error {
var (
fn *gosym.Func
pc uint64
fname = args[0]
)
if strings.ContainsRune(fname, ':') {
fl := strings.Split(fname, ":")
f, err := filepath.Abs(fl[0])
if err != nil {
return err
}
l, err := strconv.Atoi(fl[1])
if err != nil {
return err
}
pc, fn, err = p.GoSymTable.LineToPC(f, l)
if err != nil {
return err
}
} else {
fn = p.GoSymTable.LookupFunc(fname)
if fn == nil {
return fmt.Errorf("No function named %s", fname)
}
pc = fn.Entry
}
bp, err := p.Clear(pc)
if err != nil {
return err
}
fmt.Printf("Breakpoint cleared at %#v for %s %s:%d\n", bp.Addr, bp.FunctionName, bp.File, bp.Line)
return nil
}
2014-06-27 01:36:39 +00:00
func breakpoint(p *proctl.DebuggedProcess, args ...string) error {
var (
fn *gosym.Func
pc uint64
fname = args[0]
)
if strings.ContainsRune(fname, ':') {
fl := strings.Split(fname, ":")
f, err := filepath.Abs(fl[0])
if err != nil {
return err
}
l, err := strconv.Atoi(fl[1])
if err != nil {
return err
}
pc, fn, err = p.GoSymTable.LineToPC(f, l)
if err != nil {
return err
}
} else {
fn = p.GoSymTable.LookupFunc(fname)
2014-08-07 21:13:11 +00:00
if fn == nil {
return fmt.Errorf("No function named %s", fname)
}
2014-08-07 21:13:11 +00:00
pc = fn.Entry
}
bp, err := p.Break(uintptr(pc))
if err != nil {
return err
}
fmt.Printf("Breakpoint set at %#v for %s %s:%d\n", bp.Addr, bp.FunctionName, bp.File, bp.Line)
return nil
}
2014-10-07 22:03:20 +00:00
func printVar(p *proctl.DebuggedProcess, args ...string) error {
if len(args) == 0 {
return fmt.Errorf("Not enough arguments to print command")
}
2014-10-07 22:03:20 +00:00
val, err := p.EvalSymbol(args[0])
if err != nil {
return err
}
fmt.Println(val.Value)
return nil
}
func printcontext(p *proctl.DebuggedProcess) error {
var context []string
regs, err := p.Registers()
if err != nil {
return err
}
f, l, _ := p.GoSymTable.PCToLine(regs.PC())
fmt.Printf("Stopped at: %s:%d\n", f, l)
file, err := os.Open(f)
if err != nil {
return err
}
defer file.Close()
buf := bufio.NewReader(file)
2014-10-17 19:33:40 +00:00
for i := 1; i < l-5; i++ {
_, err := buf.ReadString('\n')
if err != nil && err != io.EOF {
return err
}
2014-10-17 19:33:40 +00:00
}
2014-10-17 19:33:40 +00:00
for i := l - 5; i <= l+5; i++ {
line, err := buf.ReadString('\n')
if err != nil {
if err != io.EOF {
return err
}
2014-10-17 19:33:40 +00:00
if err == io.EOF {
break
}
}
if i == l {
2014-10-17 19:47:30 +00:00
line = "\033[34m=>\033[0m" + line
}
2014-10-17 19:33:40 +00:00
2014-10-27 12:27:28 +00:00
context = append(context, fmt.Sprintf("\033[34m%d\033[0m: %s", i, line))
}
2014-10-17 19:33:40 +00:00
fmt.Println(strings.Join(context, ""))
return nil
}