From eb2bc2a7eeb94c17770fb332eebe552c56a4f0dd Mon Sep 17 00:00:00 2001 From: aarzilli Date: Tue, 29 Sep 2015 18:40:12 +0200 Subject: [PATCH] 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 --- _fixtures/bpfile | 8 ++++++++ cmd/dlv/main.go | 7 +++++++ proc/test/support.go | 14 +++++++++---- terminal/command.go | 44 +++++++++++++++++++++++++++++++++++++--- terminal/command_test.go | 33 ++++++++++++++++++++++++++++++ terminal/terminal.go | 8 ++++++++ 6 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 _fixtures/bpfile diff --git a/_fixtures/bpfile b/_fixtures/bpfile new file mode 100644 index 00000000..d1cfc135 --- /dev/null +++ b/_fixtures/bpfile @@ -0,0 +1,8 @@ +# comment line +trace main.main + +break main.sayhi + +# +# comment line +# diff --git a/cmd/dlv/main.go b/cmd/dlv/main.go index 51155ce4..8f0a1a0f 100644 --- a/cmd/dlv/main.go +++ b/cmd/dlv/main.go @@ -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) diff --git a/proc/test/support.go b/proc/test/support.go index 779e714b..d7b19724 100644 --- a/proc/test/support.go +++ b/proc/test/support.go @@ -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) diff --git a/terminal/command.go b/terminal/command.go index 67b21973..6b58c145 100644 --- a/terminal/command.go +++ b/terminal/command.go @@ -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 . Show source around current point or provided linespec."}, {aliases: []string{"stack", "bt"}, cmdFn: stackCommand, helpMsg: "stack [] [-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 ") + } + + 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() +} diff --git a/terminal/command_test.go b/terminal/command_test.go index fac36c27..7a9bfbd0 100644 --- a/terminal/command_test.go +++ b/terminal/command_test.go @@ -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) + } +} diff --git a/terminal/terminal.go b/terminal/terminal.go index 3a6ee15e..932af116 100644 --- a/terminal/terminal.go +++ b/terminal/terminal.go @@ -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() -- GitLab