main.go 4.0 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 40 41 42 43 44
	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 已提交
45
	}
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
	rootCommand.AddCommand(versionCommand)

	// 'run' subcommand.
	runCommand := &cobra.Command{
		Use:   "run",
		Short: "Compile and begin debugging program.",
		Long: `Compiles your program with optimizations disabled, 
starts and attaches to it, and enable you to immediately begin debugging your program.`,
		Run: func(cmd *cobra.Command, args []string) {
			const debugname = "debug"
			goBuild := exec.Command("go", "build", "-o", debugname, "-gcflags", "-N -l")
			goBuild.Stderr = os.Stderr
			err := goBuild.Run()
			if err != nil {
				os.Exit(1)
			}
			fp, err := filepath.Abs("./" + debugname)
			if err != nil {
				fmt.Fprintf(os.Stderr, err.Error())
				os.Exit(1)
			}

			processArgs := append([]string{"./" + debugname}, args...)
			status := execute(0, processArgs)
			os.Remove(fp)
			os.Exit(status)
		},
M
Matt Butcher 已提交
73
	}
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
	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) {
			wd, err := os.Getwd()
			if err != nil {
				fmt.Fprintf(os.Stderr, err.Error())
				os.Exit(1)
			}
			base := filepath.Base(wd)
			goTest := exec.Command("go", "test", "-c", "-gcflags", "-N -l")
			goTest.Stderr = os.Stderr
			err = goTest.Run()
			if err != nil {
				os.Exit(1)
			}
			debugname := "./" + base + ".test"
			processArgs := append([]string{debugname}, args...)

			status := execute(0, processArgs)
			os.Remove(debugname)
			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 已提交
120

121
	rootCommand.Execute()
D
Derek Parker 已提交
122 123
}

124
func execute(attachPid int, processArgs []string) int {
D
Dan Mace 已提交
125
	// Make a TCP listener
126
	listener, err := net.Listen("tcp", Addr)
D
Dan Mace 已提交
127 128
	if err != nil {
		fmt.Printf("couldn't start listener: %s\n", err)
D
Derek Parker 已提交
129
		return 1
D
Dan Mace 已提交
130
	}
131
	defer listener.Close()
D
Dan Mace 已提交
132

D
Derek Parker 已提交
133 134
	// Create and start a debugger server
	var server service.Server
135 136 137 138
	server = rpc.NewServer(&service.Config{
		Listener:    listener,
		ProcessArgs: processArgs,
		AttachPid:   attachPid,
139
	}, Log)
140 141 142 143
	if err := server.Run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}
D
Dan Mace 已提交
144

D
Derek Parker 已提交
145
	var status int
146
	if !Headless {
D
Dan Mace 已提交
147
		// Create and start a terminal
D
Derek Parker 已提交
148
		var client service.Client
149
		client = rpc.NewClient(listener.Addr().String())
D
Dan Mace 已提交
150 151 152 153 154 155 156 157 158
		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 已提交
159 160 161
	if err != nil {
		fmt.Println(err)
	}
D
Derek Parker 已提交
162 163

	return status
D
Derek Parker 已提交
164
}