2014-05-20 21:29:01 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-03-20 21:11:11 +00:00
|
|
|
"net"
|
2014-05-20 21:29:01 +00:00
|
|
|
"os"
|
2015-03-20 21:11:11 +00:00
|
|
|
"os/exec"
|
2015-04-17 20:49:49 +00:00
|
|
|
"os/signal"
|
2015-03-20 21:11:11 +00:00
|
|
|
"path/filepath"
|
|
|
|
"strconv"
|
2015-07-12 20:18:14 +00:00
|
|
|
"strings"
|
2014-05-20 21:29:01 +00:00
|
|
|
|
2015-05-08 22:26:09 +00:00
|
|
|
sys "golang.org/x/sys/unix"
|
|
|
|
|
2015-06-21 03:47:44 +00:00
|
|
|
"github.com/derekparker/delve/service"
|
2015-07-12 20:18:14 +00:00
|
|
|
"github.com/derekparker/delve/service/api"
|
2015-06-21 03:47:44 +00:00
|
|
|
"github.com/derekparker/delve/service/rpc"
|
2015-03-20 21:11:11 +00:00
|
|
|
"github.com/derekparker/delve/terminal"
|
2015-07-11 14:28:08 +00:00
|
|
|
"github.com/spf13/cobra"
|
2014-05-20 21:29:01 +00:00
|
|
|
)
|
|
|
|
|
2015-07-11 14:29:40 +00:00
|
|
|
const version string = "0.6.0.beta"
|
2014-11-10 12:53:33 +00:00
|
|
|
|
2015-07-11 14:28:08 +00:00
|
|
|
var (
|
|
|
|
Log bool
|
|
|
|
Headless bool
|
|
|
|
Addr string
|
|
|
|
)
|
2014-05-23 20:27:08 +00:00
|
|
|
|
2014-11-23 16:22:04 +00:00
|
|
|
func main() {
|
2015-07-11 14:28:08 +00:00
|
|
|
// Main dlv root command.
|
|
|
|
rootCommand := &cobra.Command{
|
|
|
|
Use: "dlv",
|
|
|
|
Short: "Delve is a debugger for the Go programming language.",
|
2015-07-11 15:31:15 +00:00
|
|
|
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,
|
|
|
|
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.
|
|
|
|
`,
|
2014-05-20 23:11:00 +00:00
|
|
|
}
|
2015-07-11 14:28:08 +00:00
|
|
|
rootCommand.Flags().StringVarP(&Addr, "listen", "l", "localhost:0", "Debugging server listen address.")
|
|
|
|
rootCommand.Flags().BoolVarP(&Log, "log", "", false, "Enable debugging server logging.")
|
|
|
|
rootCommand.Flags().BoolVarP(&Headless, "headless", "", false, "Run debug server only, in headless mode.")
|
|
|
|
|
|
|
|
// 'version' subcommand.
|
|
|
|
versionCommand := &cobra.Command{
|
|
|
|
Use: "version",
|
|
|
|
Short: "Prints version.",
|
|
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
|
|
fmt.Println("Delve version: " + version)
|
|
|
|
},
|
2014-11-10 12:53:33 +00:00
|
|
|
}
|
2015-07-11 14:28:08 +00:00
|
|
|
rootCommand.AddCommand(versionCommand)
|
|
|
|
|
|
|
|
// 'run' subcommand.
|
|
|
|
runCommand := &cobra.Command{
|
|
|
|
Use: "run",
|
|
|
|
Short: "Compile and begin debugging program.",
|
|
|
|
Long: `Compiles your program with optimizations disabled,
|
2015-07-11 14:34:08 +00:00
|
|
|
starts and attaches to it, and enables you to immediately begin debugging your program.`,
|
2015-07-11 14:28:08 +00:00
|
|
|
Run: func(cmd *cobra.Command, args []string) {
|
2015-07-11 19:51:54 +00:00
|
|
|
status := func() int {
|
|
|
|
const debugname = "debug"
|
|
|
|
goBuild := exec.Command("go", "build", "-o", debugname, "-gcflags", "-N -l")
|
|
|
|
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)
|
|
|
|
}()
|
2015-07-11 14:28:08 +00:00
|
|
|
os.Exit(status)
|
|
|
|
},
|
2015-04-25 14:49:12 +00:00
|
|
|
}
|
2015-07-11 14:28:08 +00:00
|
|
|
rootCommand.AddCommand(runCommand)
|
|
|
|
|
2015-07-12 20:18:14 +00:00
|
|
|
// 'trace' subcommand.
|
2015-07-13 21:34:51 +00:00
|
|
|
var traceAttachPid int
|
2015-07-12 20:18:14 +00:00
|
|
|
traceCommand := &cobra.Command{
|
2015-07-13 21:34:51 +00:00
|
|
|
Use: "trace [regexp] [flags]",
|
2015-07-12 20:18:14 +00:00
|
|
|
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.",
|
|
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
|
|
status := func() int {
|
|
|
|
const debugname = "debug"
|
2015-07-13 21:34:51 +00:00
|
|
|
var processArgs []string
|
|
|
|
if traceAttachPid == 0 {
|
|
|
|
goBuild := exec.Command("go", "build", "-o", debugname, "-gcflags", "-N -l")
|
|
|
|
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)
|
2015-07-12 20:18:14 +00:00
|
|
|
|
2015-07-13 21:34:51 +00:00
|
|
|
processArgs = append([]string{"./" + debugname}, args...)
|
|
|
|
}
|
2015-07-12 20:18:14 +00:00
|
|
|
// Make a TCP listener
|
|
|
|
listener, err := net.Listen("tcp", Addr)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("couldn't start listener: %s\n", err)
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
defer listener.Close()
|
|
|
|
|
|
|
|
// Create and start a debugger server
|
|
|
|
server := rpc.NewServer(&service.Config{
|
|
|
|
Listener: listener,
|
|
|
|
ProcessArgs: processArgs,
|
2015-07-13 21:34:51 +00:00
|
|
|
AttachPid: traceAttachPid,
|
2015-07-12 20:18:14 +00:00
|
|
|
}, Log)
|
|
|
|
if err := server.Run(); err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
sigChan := make(chan os.Signal)
|
|
|
|
signal.Notify(sigChan, sys.SIGINT)
|
|
|
|
client := rpc.NewClient(listener.Addr().String())
|
|
|
|
funcs, err := client.ListFunctions(args[0])
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
for i := range funcs {
|
|
|
|
_, err := client.CreateBreakpoint(&api.Breakpoint{FunctionName: funcs[i], Tracepoint: true})
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stateChan := client.Continue()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case state := <-stateChan:
|
|
|
|
if state.Err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, state.Err)
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
var args []string
|
|
|
|
var fname string
|
|
|
|
if state.CurrentThread != nil && state.CurrentThread.Function != nil {
|
|
|
|
fname = state.CurrentThread.Function.Name
|
|
|
|
}
|
|
|
|
if state.BreakpointInfo != nil {
|
|
|
|
for _, arg := range state.BreakpointInfo.Arguments {
|
|
|
|
args = append(args, arg.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Printf("%s(%s) %s:%d\n", fname, strings.Join(args, ", "), state.CurrentThread.File, state.CurrentThread.Line)
|
|
|
|
case <-sigChan:
|
2015-07-13 21:34:51 +00:00
|
|
|
server.Stop(traceAttachPid == 0)
|
2015-07-12 20:18:14 +00:00
|
|
|
return 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}()
|
|
|
|
os.Exit(status)
|
|
|
|
},
|
|
|
|
}
|
2015-07-13 21:34:51 +00:00
|
|
|
traceCommand.Flags().IntVarP(&traceAttachPid, "pid", "p", 0, "Pid to attach to.")
|
2015-07-12 20:18:14 +00:00
|
|
|
rootCommand.AddCommand(traceCommand)
|
|
|
|
|
2015-07-11 14:28:08 +00:00
|
|
|
// '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: func(cmd *cobra.Command, args []string) {
|
2015-07-11 19:51:54 +00:00
|
|
|
status := func() int {
|
|
|
|
wd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, err.Error())
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
base := filepath.Base(wd)
|
|
|
|
goTest := exec.Command("go", "test", "-c", "-gcflags", "-N -l")
|
|
|
|
goTest.Stderr = os.Stderr
|
|
|
|
err = goTest.Run()
|
|
|
|
if err != nil {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
debugname := "./" + base + ".test"
|
|
|
|
defer os.Remove(debugname)
|
|
|
|
processArgs := append([]string{debugname}, args...)
|
|
|
|
|
|
|
|
return execute(0, processArgs)
|
|
|
|
}()
|
2015-07-11 14:28:08 +00:00
|
|
|
os.Exit(status)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
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.",
|
|
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
|
|
pid, err := strconv.Atoi(args[0])
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "Invalid pid: %d", args[0])
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
os.Exit(execute(pid, nil))
|
|
|
|
},
|
|
|
|
}
|
|
|
|
rootCommand.AddCommand(attachCommand)
|
2015-04-25 14:49:12 +00:00
|
|
|
|
2015-07-11 14:28:08 +00:00
|
|
|
rootCommand.Execute()
|
2015-06-13 23:16:09 +00:00
|
|
|
}
|
|
|
|
|
2015-07-11 14:28:08 +00:00
|
|
|
func execute(attachPid int, processArgs []string) int {
|
2015-03-20 21:11:11 +00:00
|
|
|
// Make a TCP listener
|
2015-07-11 14:28:08 +00:00
|
|
|
listener, err := net.Listen("tcp", Addr)
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("couldn't start listener: %s\n", err)
|
2015-06-13 23:16:09 +00:00
|
|
|
return 1
|
2015-03-20 21:11:11 +00:00
|
|
|
}
|
2015-06-27 04:05:15 +00:00
|
|
|
defer listener.Close()
|
2015-03-20 21:11:11 +00:00
|
|
|
|
2015-06-21 03:47:44 +00:00
|
|
|
// Create and start a debugger server
|
2015-07-11 19:51:54 +00:00
|
|
|
server := rpc.NewServer(&service.Config{
|
2015-06-24 13:08:48 +00:00
|
|
|
Listener: listener,
|
|
|
|
ProcessArgs: processArgs,
|
|
|
|
AttachPid: attachPid,
|
2015-07-11 14:28:08 +00:00
|
|
|
}, Log)
|
2015-06-27 04:05:15 +00:00
|
|
|
if err := server.Run(); err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
|
|
return 1
|
|
|
|
}
|
2015-03-20 21:11:11 +00:00
|
|
|
|
2015-06-13 23:16:09 +00:00
|
|
|
var status int
|
2015-07-11 14:28:08 +00:00
|
|
|
if !Headless {
|
2015-04-17 20:49:49 +00:00
|
|
|
// Create and start a terminal
|
2015-06-21 03:47:44 +00:00
|
|
|
var client service.Client
|
2015-06-24 13:08:48 +00:00
|
|
|
client = rpc.NewClient(listener.Addr().String())
|
2015-04-17 20:49:49 +00:00
|
|
|
term := terminal.New(client)
|
|
|
|
err, status = term.Run()
|
|
|
|
} else {
|
|
|
|
ch := make(chan os.Signal)
|
|
|
|
signal.Notify(ch, sys.SIGINT)
|
|
|
|
<-ch
|
|
|
|
err = server.Stop(true)
|
|
|
|
}
|
|
|
|
|
2015-03-20 21:11:11 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
2015-06-13 23:16:09 +00:00
|
|
|
|
|
|
|
return status
|
2014-05-20 21:29:01 +00:00
|
|
|
}
|