command.go 3.6 KB
Newer Older
D
Derek Parker 已提交
1 2
// Package command implements functions for responding to user
// input and dispatching to appropriate backend commands.
D
Derek Parker 已提交
3 4 5
package command

import (
6
	"bufio"
7
	"debug/gosym"
D
Derek Parker 已提交
8
	"fmt"
9 10
	"io"
	"os"
11 12 13 14 15
	"path/filepath"
	"strconv"
	"strings"

	"github.com/derekparker/dbg/proctl"
D
Derek Parker 已提交
16 17
)

18
type cmdfunc func(proc *proctl.DebuggedProcess, args ...string) error
D
Derek Parker 已提交
19 20 21 22 23

type Commands struct {
	cmds map[string]cmdfunc
}

24
// Returns a Commands struct with default commands defined.
D
Derek Parker 已提交
25 26
func DebugCommands() *Commands {
	cmds := map[string]cmdfunc{
27
		"continue": cont,
D
Derek Parker 已提交
28
		"next":     next,
D
Derek Parker 已提交
29
		"break":    breakpoint,
30 31 32
		"step":     step,
		"clear":    clear,
		"":         nullCommand,
D
Derek Parker 已提交
33 34 35 36 37
	}

	return &Commands{cmds}
}

D
Derek Parker 已提交
38 39
// Register custom commands. Expects cf to be a func of type cmdfunc,
// returning only an error.
D
Derek Parker 已提交
40 41 42 43
func (c *Commands) Register(cmdstr string, cf cmdfunc) {
	c.cmds[cmdstr] = cf
}

44 45 46
// 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.
D
Derek Parker 已提交
47 48 49 50 51 52
func (c *Commands) Find(cmdstr string) cmdfunc {
	cmd, ok := c.cmds[cmdstr]
	if !ok {
		return noCmdAvailable
	}

53 54 55
	// Allow <enter> to replay last command
	c.cmds[""] = cmd

D
Derek Parker 已提交
56 57 58
	return cmd
}

D
Derek Parker 已提交
59
func CommandFunc(fn func() error) cmdfunc {
60
	return func(p *proctl.DebuggedProcess, args ...string) error {
D
Derek Parker 已提交
61 62 63 64
		return fn()
	}
}

65
func noCmdAvailable(p *proctl.DebuggedProcess, ars ...string) error {
D
Derek Parker 已提交
66 67 68
	return fmt.Errorf("command not available")
}

69 70 71 72 73
func nullCommand(p *proctl.DebuggedProcess, ars ...string) error {
	return nil
}

func cont(p *proctl.DebuggedProcess, ars ...string) error {
74
	err := p.Continue()
75 76 77 78
	if err != nil {
		return err
	}

79 80 81 82 83
	return printcontext(p)
}

func step(p *proctl.DebuggedProcess, args ...string) error {
	err := p.Step()
84 85 86 87
	if err != nil {
		return err
	}

88
	return printcontext(p)
89 90
}

D
Derek Parker 已提交
91 92 93 94 95 96
func next(p *proctl.DebuggedProcess, args ...string) error {
	err := p.Next()
	if err != nil {
		return err
	}

97
	return printcontext(p)
D
Derek Parker 已提交
98 99
}

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
func clear(p *proctl.DebuggedProcess, args ...string) error {
	fname := args[0]
	fn := p.GoSymTable.LookupFunc(fname)
	if fn == nil {
		return fmt.Errorf("No function named %s", fname)
	}

	bp, err := p.Clear(fn.Entry)
	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
}

D
Derek Parker 已提交
117
func breakpoint(p *proctl.DebuggedProcess, args ...string) error {
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
	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)
143 144 145
		if fn == nil {
			return fmt.Errorf("No function named %s", fname)
		}
146

147
		pc = fn.Entry
148 149 150 151 152 153 154 155 156
	}

	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)

157 158
	return nil
}
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195

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)
	for i := 1; i <= l+5; i++ {
		line, err := buf.ReadString('\n')
		if err != nil && err != io.EOF {
			return err
		}

		if i >= (l - 5) {
			if i == l {
				line = "=>" + line
			}
			context = append(context, line)
		}
	}

	fmt.Println(strings.Join(context, " "))

	return nil
}