dlv: Misc refactors

Mainly just cleans up the code in cmd/dlv/main.go
This commit is contained in:
Derek Parker
2016-02-01 00:30:40 +01:00
committed by aarzilli
parent 8a1f36a1ce
commit 94a265f098
2 changed files with 183 additions and 151 deletions

View File

@@ -37,31 +37,37 @@ var (
InitFile string InitFile string
// BuildFlags is the flags passed during compiler invocation. // BuildFlags is the flags passed during compiler invocation.
BuildFlags string BuildFlags string
traceAttachPid int
traceStackDepth int
conf *config.Config
rootCommand *cobra.Command
) )
func main() { const dlvCommandLongDesc = `Delve is a source level debugger for Go programs.
// Config setup and load.
conf := config.LoadConfig()
// Main dlv root command.
rootCommand := &cobra.Command{
Use: "dlv",
Short: "Delve is a debugger for the Go programming language.",
Long: `Delve is a source level debugger for Go programs.
Delve enables you to interact with your program by controlling the execution of the process, Delve enables you to interact with your program by controlling the execution of the process,
evaluating variables, and providing information of thread / goroutine state, CPU register state and more. evaluating variables, and providing information of thread / goroutine state, CPU register state and more.
The goal of this tool is to provide a simple yet powerful interface for debugging Go programs. The goal of this tool is to provide a simple yet powerful interface for debugging Go programs.
`, `
}
func init() {
buildFlagsDefault := "" buildFlagsDefault := ""
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
// Work-around for https://github.com/golang/go/issues/13154 // Work-around for https://github.com/golang/go/issues/13154
buildFlagsDefault = "-ldflags=-linkmode internal" buildFlagsDefault = "-ldflags=-linkmode internal"
} }
// Main dlv root command.
rootCommand = &cobra.Command{
Use: "dlv",
Short: "Delve is a debugger for the Go programming language.",
Long: dlvCommandLongDesc,
}
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.")
@@ -95,27 +101,7 @@ The goal of this tool is to provide a simple yet powerful interface for debuggin
Short: "Compile and begin debugging program.", Short: "Compile and begin debugging program.",
Long: `Compiles your program with optimizations disabled, Long: `Compiles your program with optimizations disabled,
starts and attaches to it, and enables you to immediately begin debugging your program.`, starts and attaches to it, and enables you to immediately begin debugging your program.`,
Run: func(cmd *cobra.Command, args []string) { Run: debugCmd,
status := func() int {
const debugname = "debug"
goBuild := exec.Command("go", "build", "-o", debugname, "-gcflags", "-N -l", BuildFlags)
goBuild.Stderr = os.Stderr
err := goBuild.Run()
if err != nil {
return 1
}
fp, err := filepath.Abs("./" + debugname)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
return 1
}
defer os.Remove(fp)
processArgs := append([]string{"./" + debugname}, args...)
return execute(0, processArgs, conf)
}()
os.Exit(status)
},
} }
rootCommand.AddCommand(debugCommand) rootCommand.AddCommand(debugCommand)
@@ -136,24 +122,96 @@ starts and attaches to it, and enables you to immediately begin debugging your p
rootCommand.AddCommand(execCommand) rootCommand.AddCommand(execCommand)
// 'trace' subcommand. // 'trace' subcommand.
var traceAttachPid, traceStackDepth int
traceCommand := &cobra.Command{ traceCommand := &cobra.Command{
Use: "trace [regexp]", Use: "trace [regexp]",
Short: "Compile and begin tracing program.", Short: "Compile and begin tracing program.",
Long: "Trace program execution. Will set a tracepoint on every function matching [regexp] and output information when tracepoint is hit.", Long: "Trace program execution. Will set a tracepoint on every function matching [regexp] and output information when tracepoint is hit.",
Run: func(cmd *cobra.Command, args []string) { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
status := func() int {
if len(args) == 0 { if len(args) == 0 {
fmt.Fprintln(os.Stderr, "You must provide a function to trace.") return errors.New("you must provide a function to trace")
}
return nil
},
Run: traceCmd,
}
traceCommand.Flags().IntVarP(&traceAttachPid, "pid", "p", 0, "Pid to attach to.")
traceCommand.Flags().IntVarP(&traceStackDepth, "stack", "s", 0, "Show stack trace with given depth.")
rootCommand.AddCommand(traceCommand)
// 'test' subcommand.
testCommand := &cobra.Command{
Use: "test",
Short: "Compile test binary and begin debugging program.",
Long: `Compiles a test binary with optimizations disabled, starts and attaches to it, and enable you to immediately begin debugging your program.`,
Run: testCmd,
}
rootCommand.AddCommand(testCommand)
// 'attach' subcommand.
attachCommand := &cobra.Command{
Use: "attach [pid]",
Short: "Attach to running process and begin debugging.",
Long: "Attach to running process and begin debugging.",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("you must provide a PID")
}
return nil
},
Run: attachCmd,
}
rootCommand.AddCommand(attachCommand)
// 'connect' subcommand.
connectCommand := &cobra.Command{
Use: "connect [addr]",
Short: "Connect to a headless debug server.",
Long: "Connect to a headless debug server.",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("you must provide an address as the first argument")
}
return nil
},
Run: connectCmd,
}
rootCommand.AddCommand(connectCommand)
}
func main() {
// Config setup and load.
conf = config.LoadConfig()
rootCommand.Execute()
}
func debugCmd(cmd *cobra.Command, args []string) {
status := func() int {
const debugname = "debug"
err := gobuild(debugname)
if err != nil {
return 1 return 1
} }
fp, err := filepath.Abs("./" + debugname)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
return 1
}
defer os.Remove(fp)
processArgs := append([]string{"./" + debugname}, args...)
return execute(0, processArgs, conf)
}()
os.Exit(status)
}
func traceCmd(cmd *cobra.Command, args []string) {
status := func() int {
const debugname = "debug" const debugname = "debug"
var processArgs []string var processArgs []string
if traceAttachPid == 0 { if traceAttachPid == 0 {
goBuild := exec.Command("go", "build", "-o", debugname, "-gcflags", "-N -l", BuildFlags) if err := gobuild(debugname); err != nil {
goBuild.Stderr = os.Stderr
err := goBuild.Run()
if err != nil {
return 1 return 1
} }
fp, err := filepath.Abs("./" + debugname) fp, err := filepath.Abs("./" + debugname)
@@ -173,7 +231,7 @@ starts and attaches to it, and enables you to immediately begin debugging your p
} }
defer listener.Close() defer listener.Close()
// Create and start a debugger server // Create and start a debug server
server := rpc.NewServer(&service.Config{ server := rpc.NewServer(&service.Config{
Listener: listener, Listener: listener,
ProcessArgs: processArgs, ProcessArgs: processArgs,
@@ -183,8 +241,6 @@ starts and attaches to it, and enables you to immediately begin debugging your p
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
return 1 return 1
} }
sigChan := make(chan os.Signal)
signal.Notify(sigChan, syscall.SIGINT)
client := rpc.NewClient(listener.Addr().String()) client := rpc.NewClient(listener.Addr().String())
funcs, err := client.ListFunctions(args[0]) funcs, err := client.ListFunctions(args[0])
if err != nil { if err != nil {
@@ -192,7 +248,7 @@ starts and attaches to it, and enables you to immediately begin debugging your p
return 1 return 1
} }
for i := range funcs { for i := range funcs {
_, err := client.CreateBreakpoint(&api.Breakpoint{FunctionName: funcs[i], Line: -1, Tracepoint: true, Stacktrace: traceStackDepth}) _, err := client.CreateBreakpoint(&api.Breakpoint{FunctionName: funcs[i], Tracepoint: true, Line: -1, Stacktrace: traceStackDepth})
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
return 1 return 1
@@ -208,19 +264,9 @@ starts and attaches to it, and enables you to immediately begin debugging your p
return 0 return 0
}() }()
os.Exit(status) os.Exit(status)
}, }
}
traceCommand.Flags().IntVarP(&traceAttachPid, "pid", "p", 0, "Pid to attach to.")
traceCommand.Flags().IntVarP(&traceStackDepth, "stack", "s", 0, "Show stack trace with given depth.")
rootCommand.AddCommand(traceCommand)
// 'test' subcommand. func testCmd(cmd *cobra.Command, args []string) {
testCommand := &cobra.Command{
Use: "test",
Short: "Compile test binary and begin debugging program.",
Long: `Compiles a test binary with optimizations disabled,
starts and attaches to it, and enable you to immediately begin debugging your program.`,
Run: func(cmd *cobra.Command, args []string) {
status := func() int { status := func() int {
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
@@ -228,9 +274,7 @@ starts and attaches to it, and enable you to immediately begin debugging your pr
return 1 return 1
} }
base := filepath.Base(wd) base := filepath.Base(wd)
goTest := exec.Command("go", "test", "-c", "-gcflags", "-N -l", BuildFlags) err = gotestbuild()
goTest.Stderr = os.Stderr
err = goTest.Run()
if err != nil { if err != nil {
return 1 return 1
} }
@@ -245,53 +289,24 @@ starts and attaches to it, and enable you to immediately begin debugging your pr
return execute(0, processArgs, conf) return execute(0, processArgs, conf)
}() }()
os.Exit(status) os.Exit(status)
}, }
}
rootCommand.AddCommand(testCommand)
// 'attach' subcommand. func attachCmd(cmd *cobra.Command, args []string) {
attachCommand := &cobra.Command{
Use: "attach [pid]",
Short: "Attach to running process and begin debugging.",
Long: "Attach to running process and begin debugging.",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("you must provide a PID")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
pid, err := strconv.Atoi(args[0]) pid, err := strconv.Atoi(args[0])
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Invalid pid: %s\n", args[0]) fmt.Fprintf(os.Stderr, "Invalid pid: %s\n", args[0])
os.Exit(1) os.Exit(1)
} }
os.Exit(execute(pid, nil, conf)) os.Exit(execute(pid, nil, conf))
}, }
}
rootCommand.AddCommand(attachCommand)
// 'connect' subcommand. func connectCmd(cmd *cobra.Command, args []string) {
connectCommand := &cobra.Command{
Use: "connect [addr]",
Short: "Connect to a headless debug server.",
Long: "Connect to a headless debug server.",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
fmt.Fprintf(os.Stderr, "An address was not provided. You must provide an address as the first argument.\n")
os.Exit(1)
}
addr := args[0] addr := args[0]
if addr == "" { if addr == "" {
fmt.Fprintf(os.Stderr, "An empty address was provided. You must provide an address as the first argument.\n") fmt.Fprintf(os.Stderr, "An empty address was provided. You must provide an address as the first argument.\n")
os.Exit(1) os.Exit(1)
} }
os.Exit(connect(addr, conf)) os.Exit(connect(addr, conf))
},
}
rootCommand.AddCommand(connectCommand)
rootCommand.Execute()
} }
func connect(addr string, conf *config.Config) int { func connect(addr string, conf *config.Config) int {
@@ -331,18 +346,18 @@ func execute(attachPid int, processArgs []string, conf *config.Config) int {
} }
var status int var status int
if !Headless { if Headless {
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGINT)
<-ch
err = server.Stop(true)
} else {
// Create and start a terminal // Create and start a terminal
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 term.InitFile = InitFile
status, err = term.Run() status, err = term.Run()
} else {
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGINT)
<-ch
err = server.Stop(true)
} }
if err != nil { if err != nil {
@@ -351,3 +366,19 @@ func execute(attachPid int, processArgs []string, conf *config.Config) int {
return status return status
} }
func gobuild(debugname string) error {
return gocommand("build", "-o", debugname, BuildFlags)
}
func gotestbuild() error {
return gocommand("test", "-c", BuildFlags)
}
func gocommand(command string, args ...string) error {
allargs := []string{command, "-gcflags", "-N -l"}
allargs = append(allargs, args...)
goBuild := exec.Command("go", allargs...)
goBuild.Stderr = os.Stderr
return goBuild.Run()
}

View File

@@ -65,6 +65,7 @@ func (s *ServerImpl) Run() error {
if err != nil { if err != nil {
panic(err) panic(err)
} }
defer s.s.listener.Close()
rpcs := grpc.NewServer() rpcs := grpc.NewServer()
rpcs.Register(s.s) rpcs.Register(s.s)