dlv: Take package name in certain commands

This patch allows certain commands (`debug`, `test`, and `trace`) to
take an argument specifying a package name. This allows a user to
specify a package other than that of the current package in the working
directory.

If a package name is not specified, the behavior remains the same, as in
Delve will look in the current working directory for a `main` package to
compile.

Fixes #423
This commit is contained in:
Derek Parker 2016-02-15 20:45:06 -08:00
parent 3360a2e997
commit 8f85b6cbae

@ -21,10 +21,11 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
const version string = "0.11.0-alpha" const (
version = "0.11.0-alpha"
// Build is the current git hash. debugname = "debug"
var Build string testdebugname = "debug.test"
)
var ( var (
// Log is whether to log debug statements. // Log is whether to log debug statements.
@ -37,6 +38,8 @@ 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
// Build is the current git hash.
Build string
traceAttachPid int traceAttachPid int
traceStackDepth int traceStackDepth int
@ -97,7 +100,7 @@ func init() {
// 'debug' subcommand. // 'debug' subcommand.
debugCommand := &cobra.Command{ debugCommand := &cobra.Command{
Use: "debug", Use: "debug [package]",
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.`,
@ -123,15 +126,9 @@ starts and attaches to it, and enables you to immediately begin debugging your p
// 'trace' subcommand. // 'trace' subcommand.
traceCommand := &cobra.Command{ traceCommand := &cobra.Command{
Use: "trace [regexp]", Use: "trace [package] 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 the provided regular expression and output information when tracepoint is hit.",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("you must provide a function to trace")
}
return nil
},
Run: traceCmd, Run: traceCmd,
} }
traceCommand.Flags().IntVarP(&traceAttachPid, "pid", "p", 0, "Pid to attach to.") traceCommand.Flags().IntVarP(&traceAttachPid, "pid", "p", 0, "Pid to attach to.")
@ -140,7 +137,7 @@ starts and attaches to it, and enables you to immediately begin debugging your p
// 'test' subcommand. // 'test' subcommand.
testCommand := &cobra.Command{ testCommand := &cobra.Command{
Use: "test", Use: "test [package]",
Short: "Compile test binary and begin debugging program.", 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.`, Long: `Compiles a test binary with optimizations disabled, starts and attaches to it, and enable you to immediately begin debugging your program.`,
Run: testCmd, Run: testCmd,
@ -149,7 +146,7 @@ starts and attaches to it, and enables you to immediately begin debugging your p
// 'attach' subcommand. // 'attach' subcommand.
attachCommand := &cobra.Command{ attachCommand := &cobra.Command{
Use: "attach [pid]", Use: "attach pid",
Short: "Attach to running process and begin debugging.", Short: "Attach to running process and begin debugging.",
Long: "Attach to running process and begin debugging.", Long: "Attach to running process and begin debugging.",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
@ -164,7 +161,7 @@ starts and attaches to it, and enables you to immediately begin debugging your p
// 'connect' subcommand. // 'connect' subcommand.
connectCommand := &cobra.Command{ connectCommand := &cobra.Command{
Use: "connect [addr]", Use: "connect addr",
Short: "Connect to a headless debug server.", Short: "Connect to a headless debug server.",
Long: "Connect to a headless debug server.", Long: "Connect to a headless debug server.",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
@ -176,7 +173,6 @@ starts and attaches to it, and enables you to immediately begin debugging your p
Run: connectCmd, Run: connectCmd,
} }
rootCommand.AddCommand(connectCommand) rootCommand.AddCommand(connectCommand)
} }
func main() { func main() {
@ -188,19 +184,25 @@ func main() {
func debugCmd(cmd *cobra.Command, args []string) { func debugCmd(cmd *cobra.Command, args []string) {
status := func() int { status := func() int {
const debugname = "debug" var pkg string
err := gobuild(debugname) dlvArgs, targetArgs := splitArgs(cmd, args)
if len(dlvArgs) > 0 {
pkg = args[0]
}
err := gobuild(debugname, pkg)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return 1 return 1
} }
fp, err := filepath.Abs("./" + debugname) fp, err := filepath.Abs("./" + debugname)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, err.Error()) fmt.Fprintf(os.Stderr, "%v\n", err)
return 1 return 1
} }
defer os.Remove(fp) defer os.Remove(fp)
processArgs := append([]string{"./" + debugname}, args...) processArgs := append([]string{"./" + debugname}, targetArgs...)
return execute(0, processArgs, conf) return execute(0, processArgs, conf)
}() }()
os.Exit(status) os.Exit(status)
@ -208,20 +210,26 @@ func debugCmd(cmd *cobra.Command, args []string) {
func traceCmd(cmd *cobra.Command, args []string) { func traceCmd(cmd *cobra.Command, args []string) {
status := func() int { status := func() int {
const debugname = "debug" var regexp string
var processArgs []string var processArgs []string
if traceAttachPid == 0 {
if err := gobuild(debugname); 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...) dlvArgs, targetArgs := splitArgs(cmd, args)
if traceAttachPid == 0 {
var pkg string
switch len(dlvArgs) {
case 1:
regexp = args[0]
case 2:
pkg = args[0]
regexp = args[1]
}
if err := gobuild(debugname, pkg); err != nil {
return 1
}
defer os.Remove("./" + debugname)
processArgs = append([]string{"./" + debugname}, targetArgs...)
} }
// Make a TCP listener // Make a TCP listener
listener, err := net.Listen("tcp", Addr) listener, err := net.Listen("tcp", Addr)
@ -242,7 +250,7 @@ func traceCmd(cmd *cobra.Command, args []string) {
return 1 return 1
} }
client := rpc.NewClient(listener.Addr().String()) client := rpc.NewClient(listener.Addr().String())
funcs, err := client.ListFunctions(args[0]) funcs, err := client.ListFunctions(regexp)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
return 1 return 1
@ -270,23 +278,18 @@ func traceCmd(cmd *cobra.Command, args []string) {
func testCmd(cmd *cobra.Command, args []string) { func testCmd(cmd *cobra.Command, args []string) {
status := func() int { status := func() int {
wd, err := os.Getwd() var pkg string
if err != nil { dlvArgs, targetArgs := splitArgs(cmd, args)
fmt.Fprintf(os.Stderr, err.Error())
return 1 if len(dlvArgs) > 0 {
pkg = args[0]
} }
base := filepath.Base(wd) err := gotestbuild(pkg)
err = gotestbuild()
if err != nil { if err != nil {
return 1 return 1
} }
debugname := "./" + base + ".test" defer os.Remove("./" + testdebugname)
// On Windows, "go test" generates an executable with the ".exe" extension processArgs := append([]string{"./" + testdebugname}, targetArgs...)
if runtime.GOOS == "windows" {
debugname += ".exe"
}
defer os.Remove(debugname)
processArgs := append([]string{debugname}, args...)
return execute(0, processArgs, conf) return execute(0, processArgs, conf)
}() }()
@ -311,6 +314,13 @@ func connectCmd(cmd *cobra.Command, args []string) {
os.Exit(connect(addr, conf)) os.Exit(connect(addr, conf))
} }
func splitArgs(cmd *cobra.Command, args []string) ([]string, []string) {
if cmd.ArgsLenAtDash() >= 0 {
return args[:cmd.ArgsLenAtDash()], args[cmd.ArgsLenAtDash():]
}
return args, []string{}
}
func connect(addr string, conf *config.Config) int { func connect(addr string, conf *config.Config) int {
// Create and start a terminal - attach to running instance // Create and start a terminal - attach to running instance
var client service.Client var client service.Client
@ -369,16 +379,26 @@ func execute(attachPid int, processArgs []string, conf *config.Config) int {
return status return status
} }
func gobuild(debugname string) error { func gobuild(debugname, pkg string) error {
return gocommand("build", "-o", debugname, BuildFlags) args := []string{"-gcflags", "-N -l", "-o", debugname}
if BuildFlags != "" {
args = append(args, BuildFlags)
}
args = append(args, pkg)
return gocommand("build", args...)
} }
func gotestbuild() error { func gotestbuild(pkg string) error {
return gocommand("test", "-c", BuildFlags) args := []string{"-gcflags", "-N -l", "-c", "-o", testdebugname}
if BuildFlags != "" {
args = append(args, BuildFlags)
}
args = append(args, pkg)
return gocommand("test", args...)
} }
func gocommand(command string, args ...string) error { func gocommand(command string, args ...string) error {
allargs := []string{command, "-gcflags", "-N -l"} allargs := []string{command}
allargs = append(allargs, args...) allargs = append(allargs, args...)
goBuild := exec.Command("go", allargs...) goBuild := exec.Command("go", allargs...)
goBuild.Stderr = os.Stderr goBuild.Stderr = os.Stderr