terminal: Implements init file and source command
The 'source' command reads the file specified as argument and executes it as a list of delve commands. Additionally a flag '--init' can be passed to delve specifying a file containing a list of commands to execute on startup. Issue #96
This commit is contained in:
parent
bc9ac0ec78
commit
eb2bc2a7ee
8
_fixtures/bpfile
Normal file
8
_fixtures/bpfile
Normal file
@ -0,0 +1,8 @@
|
||||
# comment line
|
||||
trace main.main
|
||||
|
||||
break main.sayhi
|
||||
|
||||
#
|
||||
# comment line
|
||||
#
|
@ -27,6 +27,7 @@ var (
|
||||
Log bool
|
||||
Headless bool
|
||||
Addr string
|
||||
InitFile string
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -48,6 +49,7 @@ The goal of this tool is to provide a simple yet powerful interface for debuggin
|
||||
rootCommand.PersistentFlags().StringVarP(&Addr, "listen", "l", "localhost:0", "Debugging server listen address.")
|
||||
rootCommand.PersistentFlags().BoolVarP(&Log, "log", "", false, "Enable debugging server logging.")
|
||||
rootCommand.PersistentFlags().BoolVarP(&Headless, "headless", "", false, "Run debug server only, in headless mode.")
|
||||
rootCommand.PersistentFlags().StringVar(&InitFile, "init", "", "Init file, executed by the terminal client")
|
||||
|
||||
// 'version' subcommand.
|
||||
versionCommand := &cobra.Command{
|
||||
@ -292,6 +294,10 @@ func execute(attachPid int, processArgs []string, conf *config.Config) int {
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
if Headless && (InitFile != "") {
|
||||
fmt.Fprintf(os.Stderr, "Warning: init file ignored\n")
|
||||
}
|
||||
|
||||
// Create and start a debugger server
|
||||
server := rpc.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
@ -309,6 +315,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config) int {
|
||||
var client service.Client
|
||||
client = rpc.NewClient(listener.Addr().String())
|
||||
term := terminal.New(client, conf)
|
||||
term.InitFile = InitFile
|
||||
err, status = term.Run()
|
||||
} else {
|
||||
ch := make(chan os.Signal)
|
||||
|
@ -23,10 +23,7 @@ type Fixture struct {
|
||||
// Fixtures is a map of Fixture.Name to Fixture.
|
||||
var Fixtures map[string]Fixture = make(map[string]Fixture)
|
||||
|
||||
func BuildFixture(name string) Fixture {
|
||||
if f, ok := Fixtures[name]; ok {
|
||||
return f
|
||||
}
|
||||
func FindFixturesDir() string {
|
||||
parent := ".."
|
||||
fixturesDir := "_fixtures"
|
||||
for depth := 0; depth < 10; depth++ {
|
||||
@ -35,6 +32,15 @@ func BuildFixture(name string) Fixture {
|
||||
}
|
||||
fixturesDir = filepath.Join(parent, fixturesDir)
|
||||
}
|
||||
return fixturesDir
|
||||
}
|
||||
|
||||
func BuildFixture(name string) Fixture {
|
||||
if f, ok := Fixtures[name]; ok {
|
||||
return f
|
||||
}
|
||||
|
||||
fixturesDir := FindFixturesDir()
|
||||
|
||||
// Make a (good enough) random temporary file name
|
||||
r := make([]byte, 4)
|
||||
|
@ -41,9 +41,9 @@ func (c command) match(cmdstr string) bool {
|
||||
}
|
||||
|
||||
type Commands struct {
|
||||
cmds []command
|
||||
lastCmd cmdfunc
|
||||
client service.Client
|
||||
cmds []command
|
||||
lastCmd cmdfunc
|
||||
client service.Client
|
||||
}
|
||||
|
||||
// Returns a Commands struct with default commands defined.
|
||||
@ -77,6 +77,7 @@ func DebugCommands(client service.Client) *Commands {
|
||||
{aliases: []string{"list", "ls"}, cmdFn: listCommand, helpMsg: "list <linespec>. Show source around current point or provided linespec."},
|
||||
{aliases: []string{"stack", "bt"}, cmdFn: stackCommand, helpMsg: "stack [<depth>] [-full]. Prints stack."},
|
||||
{aliases: []string{"frame"}, cmdFn: frame, helpMsg: "Sets current stack frame (0 is the top of the stack)"},
|
||||
{aliases: []string{"source"}, cmdFn: c.sourceCommand, helpMsg: "Executes a file containing a list of delve commands"},
|
||||
}
|
||||
|
||||
return c
|
||||
@ -699,6 +700,14 @@ func listCommand(t *Term, args ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
func digits(n int) int {
|
||||
return int(math.Floor(math.Log10(float64(n)))) + 1
|
||||
}
|
||||
@ -826,3 +835,32 @@ func shortenFilePath(fullPath string) string {
|
||||
workingDir, _ := os.Getwd()
|
||||
return strings.Replace(fullPath, workingDir, ".", 1)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
@ -2,7 +2,10 @@ package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/derekparker/delve/proc/test"
|
||||
)
|
||||
|
||||
func TestCommandDefault(t *testing.T) {
|
||||
@ -65,3 +68,33 @@ func TestCommandThread(t *testing.T) {
|
||||
t.Fatal("wrong command output: ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteFile(t *testing.T) {
|
||||
breakCount := 0
|
||||
traceCount := 0
|
||||
c := &Commands{
|
||||
client: nil,
|
||||
cmds: []command{
|
||||
{aliases: []string{"trace"}, cmdFn: func(t *Term, args ...string) error {
|
||||
traceCount++
|
||||
return nil
|
||||
}},
|
||||
{aliases: []string{"break"}, cmdFn: func(t *Term, args ...string) error {
|
||||
breakCount++
|
||||
return nil
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
fixturesDir := test.FindFixturesDir()
|
||||
|
||||
err := c.executeFile(nil, filepath.Join(fixturesDir, "bpfile"))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("executeFile: %v", err)
|
||||
}
|
||||
|
||||
if breakCount != 1 || traceCount != 1 {
|
||||
t.Fatalf("Wrong counts break: %d trace: %d\n", breakCount, traceCount)
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ type Term struct {
|
||||
line *liner.State
|
||||
conf *config.Config
|
||||
dumb bool
|
||||
InitFile string
|
||||
}
|
||||
|
||||
func New(client service.Client, conf *config.Config) *Term {
|
||||
@ -85,6 +86,13 @@ func (t *Term) Run() (error, int) {
|
||||
f.Close()
|
||||
fmt.Println("Type 'help' for list of commands.")
|
||||
|
||||
if t.InitFile != "" {
|
||||
err := cmds.executeFile(t, t.InitFile)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error executing init file: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
var status int
|
||||
for {
|
||||
cmdstr, err := t.promptForInput()
|
||||
|
Loading…
Reference in New Issue
Block a user