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
|
Log bool
|
||||||
Headless bool
|
Headless bool
|
||||||
Addr string
|
Addr string
|
||||||
|
InitFile string
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
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().StringVarP(&Addr, "listen", "l", "localhost:0", "Debugging server listen address.")
|
||||||
rootCommand.PersistentFlags().BoolVarP(&Log, "log", "", false, "Enable debugging server logging.")
|
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().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.
|
// 'version' subcommand.
|
||||||
versionCommand := &cobra.Command{
|
versionCommand := &cobra.Command{
|
||||||
@ -292,6 +294,10 @@ func execute(attachPid int, processArgs []string, conf *config.Config) int {
|
|||||||
}
|
}
|
||||||
defer listener.Close()
|
defer listener.Close()
|
||||||
|
|
||||||
|
if Headless && (InitFile != "") {
|
||||||
|
fmt.Fprintf(os.Stderr, "Warning: init file ignored\n")
|
||||||
|
}
|
||||||
|
|
||||||
// Create and start a debugger server
|
// Create and start a debugger server
|
||||||
server := rpc.NewServer(&service.Config{
|
server := rpc.NewServer(&service.Config{
|
||||||
Listener: listener,
|
Listener: listener,
|
||||||
@ -309,6 +315,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config) int {
|
|||||||
var client service.Client
|
var client service.Client
|
||||||
client = rpc.NewClient(listener.Addr().String())
|
client = rpc.NewClient(listener.Addr().String())
|
||||||
term := terminal.New(client, conf)
|
term := terminal.New(client, conf)
|
||||||
|
term.InitFile = InitFile
|
||||||
err, status = term.Run()
|
err, status = term.Run()
|
||||||
} else {
|
} else {
|
||||||
ch := make(chan os.Signal)
|
ch := make(chan os.Signal)
|
||||||
|
@ -23,10 +23,7 @@ type Fixture struct {
|
|||||||
// Fixtures is a map of Fixture.Name to Fixture.
|
// Fixtures is a map of Fixture.Name to Fixture.
|
||||||
var Fixtures map[string]Fixture = make(map[string]Fixture)
|
var Fixtures map[string]Fixture = make(map[string]Fixture)
|
||||||
|
|
||||||
func BuildFixture(name string) Fixture {
|
func FindFixturesDir() string {
|
||||||
if f, ok := Fixtures[name]; ok {
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
parent := ".."
|
parent := ".."
|
||||||
fixturesDir := "_fixtures"
|
fixturesDir := "_fixtures"
|
||||||
for depth := 0; depth < 10; depth++ {
|
for depth := 0; depth < 10; depth++ {
|
||||||
@ -35,6 +32,15 @@ func BuildFixture(name string) Fixture {
|
|||||||
}
|
}
|
||||||
fixturesDir = filepath.Join(parent, fixturesDir)
|
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
|
// Make a (good enough) random temporary file name
|
||||||
r := make([]byte, 4)
|
r := make([]byte, 4)
|
||||||
|
@ -41,9 +41,9 @@ func (c command) match(cmdstr string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Commands struct {
|
type Commands struct {
|
||||||
cmds []command
|
cmds []command
|
||||||
lastCmd cmdfunc
|
lastCmd cmdfunc
|
||||||
client service.Client
|
client service.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a Commands struct with default commands defined.
|
// 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{"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{"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{"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
|
return c
|
||||||
@ -699,6 +700,14 @@ func listCommand(t *Term, args ...string) error {
|
|||||||
return nil
|
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 {
|
func digits(n int) int {
|
||||||
return int(math.Floor(math.Log10(float64(n)))) + 1
|
return int(math.Floor(math.Log10(float64(n)))) + 1
|
||||||
}
|
}
|
||||||
@ -826,3 +835,32 @@ func shortenFilePath(fullPath string) string {
|
|||||||
workingDir, _ := os.Getwd()
|
workingDir, _ := os.Getwd()
|
||||||
return strings.Replace(fullPath, workingDir, ".", 1)
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/derekparker/delve/proc/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCommandDefault(t *testing.T) {
|
func TestCommandDefault(t *testing.T) {
|
||||||
@ -65,3 +68,33 @@ func TestCommandThread(t *testing.T) {
|
|||||||
t.Fatal("wrong command output: ", err.Error())
|
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
|
line *liner.State
|
||||||
conf *config.Config
|
conf *config.Config
|
||||||
dumb bool
|
dumb bool
|
||||||
|
InitFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(client service.Client, conf *config.Config) *Term {
|
func New(client service.Client, conf *config.Config) *Term {
|
||||||
@ -85,6 +86,13 @@ func (t *Term) Run() (error, int) {
|
|||||||
f.Close()
|
f.Close()
|
||||||
fmt.Println("Type 'help' for list of commands.")
|
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
|
var status int
|
||||||
for {
|
for {
|
||||||
cmdstr, err := t.promptForInput()
|
cmdstr, err := t.promptForInput()
|
||||||
|
Loading…
Reference in New Issue
Block a user