main.go 4.4 KB
Newer Older
D
Derek Parker 已提交
1 2 3 4
package main

import (
	"fmt"
D
Dan Mace 已提交
5
	"net"
D
Derek Parker 已提交
6
	"os"
D
Dan Mace 已提交
7
	"os/exec"
D
Dan Mace 已提交
8
	"os/signal"
D
Dan Mace 已提交
9 10
	"path/filepath"
	"strconv"
D
Derek Parker 已提交
11

12 13
	sys "golang.org/x/sys/unix"

D
Derek Parker 已提交
14 15
	"github.com/derekparker/delve/service"
	"github.com/derekparker/delve/service/rpc"
D
Dan Mace 已提交
16
	"github.com/derekparker/delve/terminal"
17
	"github.com/spf13/cobra"
D
Derek Parker 已提交
18 19
)

D
Derek Parker 已提交
20
const version string = "0.6.0.beta"
D
Derek Parker 已提交
21

22 23 24 25 26
var (
	Log      bool
	Headless bool
	Addr     string
)
27

D
Derek Parker 已提交
28
func main() {
29 30 31 32
	// Main dlv root command.
	rootCommand := &cobra.Command{
		Use:   "dlv",
		Short: "Delve is a debugger for the Go programming language.",
D
Derek Parker 已提交
33 34 35 36 37 38 39
		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.
`,
D
Derek Parker 已提交
40
	}
41 42 43 44 45 46 47 48 49 50 51
	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)
		},
D
Derek Parker 已提交
52
	}
53 54 55 56 57 58 59
	rootCommand.AddCommand(versionCommand)

	// 'run' subcommand.
	runCommand := &cobra.Command{
		Use:   "run",
		Short: "Compile and begin debugging program.",
		Long: `Compiles your program with optimizations disabled, 
D
Derek Parker 已提交
60
starts and attaches to it, and enables you to immediately begin debugging your program.`,
61
		Run: func(cmd *cobra.Command, args []string) {
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
			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)
			}()
80 81
			os.Exit(status)
		},
M
Matt Butcher 已提交
82
	}
83 84 85 86 87 88 89 90 91
	rootCommand.AddCommand(runCommand)

	// '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) {
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
			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)
			}()
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
			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)
M
Matt Butcher 已提交
131

132
	rootCommand.Execute()
D
Derek Parker 已提交
133 134
}

135
func execute(attachPid int, processArgs []string) int {
D
Dan Mace 已提交
136
	// Make a TCP listener
137
	listener, err := net.Listen("tcp", Addr)
D
Dan Mace 已提交
138 139
	if err != nil {
		fmt.Printf("couldn't start listener: %s\n", err)
D
Derek Parker 已提交
140
		return 1
D
Dan Mace 已提交
141
	}
142
	defer listener.Close()
D
Dan Mace 已提交
143

D
Derek Parker 已提交
144
	// Create and start a debugger server
145
	server := rpc.NewServer(&service.Config{
146 147 148
		Listener:    listener,
		ProcessArgs: processArgs,
		AttachPid:   attachPid,
149
	}, Log)
150 151 152 153
	if err := server.Run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}
D
Dan Mace 已提交
154

D
Derek Parker 已提交
155
	var status int
156
	if !Headless {
D
Dan Mace 已提交
157
		// Create and start a terminal
D
Derek Parker 已提交
158
		var client service.Client
159
		client = rpc.NewClient(listener.Addr().String())
D
Dan Mace 已提交
160 161 162 163 164 165 166 167 168
		term := terminal.New(client)
		err, status = term.Run()
	} else {
		ch := make(chan os.Signal)
		signal.Notify(ch, sys.SIGINT)
		<-ch
		err = server.Stop(true)
	}

D
Dan Mace 已提交
169 170 171
	if err != nil {
		fmt.Println(err)
	}
D
Derek Parker 已提交
172 173

	return status
D
Derek Parker 已提交
174
}