docs: Move wiki docs into Documentation dir

Going forward, all documentation should be placed in the Documentation
directory in the root of the project. This switch allows maintainers to
approve updates to documentation before they are committed, as opposed
to the pre-existing wiki which anybody could modify.

Currently the Documentation directory includes docs on building, usage,
and minimal docs around the API. This is just the initial commit, and
documentation will continue to improve over time.

Some changes have been made (and will continue to be made) to `cmd/dlv` to
ensure we can auto-generate documentation for all commands from the
newly provided script `scripts/gen-usage-docs.go`, which can be invoked
via `go run scripts/gen-usage-docs.go`.

Additionally, version has been split into its own package. This was a
bit of housekeeping related to the changes made the `cmd/dlv`.
This commit is contained in:
Derek Parker 2016-02-19 10:32:24 -08:00
parent 460f0c910c
commit 63a660820e
24 changed files with 872 additions and 408 deletions

8
Documentation/README.md Normal file

@ -0,0 +1,8 @@
# Delve Documentation
Documentation for the project will reside in this directory.
- [Installation](installation)
- [Usage](usage)
- [API](api)
- [Internal](internal)

@ -0,0 +1,31 @@
# API Documentation
Delve exposes an API interface so that other programs, mostly IDEs and editors, can interact with Delve programmatically. The API is used by the terminal client, so will always stay up to date in lockstep regardless of new features.
## Usage
In order to run Delve in "API mode", simply invoke with one of the standard commands, providing the `--headless` flag, like so:
```
$ dlv debug --headless --log --listen=127.0.0.1:8181
```
This will start the debugger in a non-interactive mode, listening on the specified address, and will enable logging. The last two flags are optional, of course.
Optionally, you may also specify the `--accept-multiclient` flag if you would like to connect multiple clients to the API.
You can connect the headless debugger from Delve itself using the `connect` subcommand:
```
$ dlv connect 127.0.0.1:8181
```
This can be useful for remote debugging.
## API Interfaces
Delve has been architected in such a way as to allow multiple client/server implementations. All of the "business logic" as it were is abstracted away from the actual client/server implementations, allowing for easy implementation of new API interfaces.
### Current API Interfaces
- [JSON-RPC](json-rpc/README.md)

@ -0,0 +1,3 @@
# JSON-RPC interface
Delve exposes a [JSON-RPC](http://json-rpc.org/) API interface. Here is an (incomplete) [list of language implementations](http://json-rpc.org/wiki/implementations).

@ -0,0 +1,7 @@
# Installation
Directions for installing Delve on all supported platforms is provided here. Please note you *must* have Go 1.5 or higher installed. Also, if using Go 1.5 you must set `GO15VENDOREXPERIMENT=1` before attempting to install.
- [OSX](osx/install.md)
- [Linux](linux/install.md)
- [Windows](windows/install.md)

@ -0,0 +1,17 @@
# Installation on Linux
Please use the following steps to build and install Delve on Linux.
There are two ways to install on Linux. First is the standard `go get` method:
```
go get github.com/derekparker/delve/cmd/dlv
```
Alternatively, you can clone the repo and run:
```
$ make install
```
Note: If you are using Go 1.5 you must set `GO15VENDOREXPERIMENT=1` before continuing. The `GO15VENDOREXPERIMENT` env var simply opts into the [Go 1.5 Vendor Experiment](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo/).

@ -0,0 +1,54 @@
# Installation on OSX
Please use the following steps to build and install Delve on OSX.
## 1) Create a self-signed certificate
You must create a self signed certificate and sign the binary with it:
* Open application “Keychain Access” (/Applications/Utilities/Keychain Access.app)
* Open menu /Keychain Access/Certificate Assistant/Create a Certificate...
* Choose a name (dlv-cert in the example), set “Identity Type” to “Self Signed Root”, set “Certificate Type” to “Code Signing” and select the “Let me override defaults”. Click “Continue”. You might want to extend the predefined 365 days period to 3650 days.
* Click several times on “Continue” until you get to the “Specify a Location For The Certificate” screen, then set “Keychain to System”.
* If you can't store the certificate in the “System” keychain, create it in the “login” keychain, then export it. You can then import it into the “System” keychain.
* In keychains select “System”, and you should find your new certificate. Use the context menu for the certificate, select “Get Info”, open the “Trust” item, and set “Code Signing” to “Always Trust”.
* [At least on Yosemite:] In keychains select category Keys -> dlv-cert -> right click -> GetInfo -> Access Control -> select "Allow all applications to access this item" -> Save Changes.
* You must quit “Keychain Access” application in order to use the certificate and restart “taskgated” service by killing the current running “taskgated” process. Alternatively you can restart your computer.
* Run the following: `GO15VENDOREXPERIMENT=1 CERT=dlv-cert make install`, which will install the binary and codesign it.
## 2) Install the binary
Note: If you are using Go 1.5 you must set `GO15VENDOREXPERIMENT=1` before continuing. The `GO15VENDOREXPERIMENT` env var simply opts into the [Go 1.5 Vendor Experiment](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo/).
All `make` commands assume a CERT environment variables that contains the name of the cert you created above.
Now, simply run:
```
$ CERT=mycert make install
```
The Makefile also assumes that `GOPATH` is single-valued, not colon-separated.
The makefile is only necessary to help facilitate the process of building and codesigning.
## Notes
### Eliminating codesign authorization prompt during builds
If you're prompted for authorization when running `make` using your self-signed certificate, try the following:
* Open application “Keychain Access” (/Applications/Utilities/Keychain Access.app)
* Double-click on the private key corresponding to your self-signed certificate (dlv-cert in the example)
* Choose the "Access Control" tab
* Click the [+] under "Always allow access by these applications", and choose `/usr/bin/codesign` from the Finder dialog
* Click the "Save changes" button
#### Eliminating "Developer tools access" prompt running delve
If you are prompted with this when running `dlv`:
"Developer tools access needs to take control of another process for debugging to continue. Type your password to allow this"
Try running `DevToolsSecurity -enable` to eliminate the prompt. See `man DevToolsSecurity` for more information.

@ -0,0 +1,27 @@
# Installation on Windows
Please use the following steps to build and install Delve on Windows.
If you have a unix-y shell on Windows ([MSYS2](http://sourceforge.net/p/msys2/wiki/MSYS2%20installation/), [CYGWIN](https://cygwin.com/install.html) or other), follow the Linux installation directions.
From a standard Windows `cmd` shell, use the following steps:
## 1) Install MinGW
Install [MinGW-W64](http://sourceforge.net/projects/mingw-w64/) to get a GCC toolchain which is required to [build with CGO on Windows](https://github.com/golang/go/wiki/cgo#windows).
You should select:
* Version: Latest available (`5.3.0` at time of writing)
* Architecture: `x86_64`
* Threads: `posix` (shouldn't actually matter)
* Exception: `seh` (shouldn't actually matter)
* Build revision: Latest available (`0` at time of writing)
## 2) Make and install the `dlv` binary
```
$ mingw32-make install
```
Note: If you are using Go 1.5 you must set `GO15VENDOREXPERIMENT=1` before continuing. The `GO15VENDOREXPERIMENT` env var simply opts into the [Go 1.5 Vendor Experiment](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo/).

@ -0,0 +1,5 @@
# Internal Documentation
TODO(derekparker)
This directory will hold documentation around the internals of the debugger and how it works.

@ -0,0 +1,5 @@
# Using Delve
You can invoke Delve in multiple ways, depending on your usage needs. Delve makes every attempt to be user-friendly, ensuring the user has to do the least amount of work possible to begin debugging their program.
Refer to the [main usage document](dlv.md) to further explore commands.

@ -0,0 +1,37 @@
## dlv
Delve is a debugger for the Go programming language.
### Synopsis
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.
### Options
```
--accept-multiclient[=false]: Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate
--build-flags="": Build flags, to be passed to the compiler.
--headless[=false]: Run debug server only, in headless mode.
--init="": Init file, executed by the terminal client.
-l, --listen="localhost:0": Debugging server listen address.
--log[=false]: Enable debugging server logging.
```
### SEE ALSO
* [dlv attach](dlv_attach.md) - Attach to running process and begin debugging.
* [dlv connect](dlv_connect.md) - Connect to a headless debug server.
* [dlv debug](dlv_debug.md) - Compile and begin debugging program.
* [dlv exec](dlv_exec.md) - Runs precompiled binary, attaches and begins debug session.
* [dlv run](dlv_run.md) - Deprecated command. Use 'debug' instead.
* [dlv test](dlv_test.md) - Compile test binary and begin debugging program.
* [dlv trace](dlv_trace.md) - Compile and begin tracing program.
* [dlv version](dlv_version.md) - Prints version.
###### Auto generated by spf13/cobra on 19-Feb-2016

@ -0,0 +1,28 @@
## dlv attach
Attach to running process and begin debugging.
### Synopsis
Attach to running process and begin debugging.
```
dlv attach pid
```
### Options inherited from parent commands
```
--accept-multiclient[=false]: Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate
--build-flags="": Build flags, to be passed to the compiler.
--headless[=false]: Run debug server only, in headless mode.
--init="": Init file, executed by the terminal client.
-l, --listen="localhost:0": Debugging server listen address.
--log[=false]: Enable debugging server logging.
```
### SEE ALSO
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
###### Auto generated by spf13/cobra on 19-Feb-2016

@ -0,0 +1,28 @@
## dlv connect
Connect to a headless debug server.
### Synopsis
Connect to a headless debug server.
```
dlv connect addr
```
### Options inherited from parent commands
```
--accept-multiclient[=false]: Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate
--build-flags="": Build flags, to be passed to the compiler.
--headless[=false]: Run debug server only, in headless mode.
--init="": Init file, executed by the terminal client.
-l, --listen="localhost:0": Debugging server listen address.
--log[=false]: Enable debugging server logging.
```
### SEE ALSO
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
###### Auto generated by spf13/cobra on 19-Feb-2016

@ -0,0 +1,29 @@
## dlv debug
Compile and begin debugging program.
### Synopsis
Compiles your program with optimizations disabled,
starts and attaches to it, and enables you to immediately begin debugging your program.
```
dlv debug [package]
```
### Options inherited from parent commands
```
--accept-multiclient[=false]: Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate
--build-flags="": Build flags, to be passed to the compiler.
--headless[=false]: Run debug server only, in headless mode.
--init="": Init file, executed by the terminal client.
-l, --listen="localhost:0": Debugging server listen address.
--log[=false]: Enable debugging server logging.
```
### SEE ALSO
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
###### Auto generated by spf13/cobra on 19-Feb-2016

@ -0,0 +1,28 @@
## dlv exec
Runs precompiled binary, attaches and begins debug session.
### Synopsis
Runs precompiled binary, attaches and begins debug session.
```
dlv exec [./path/to/binary]
```
### Options inherited from parent commands
```
--accept-multiclient[=false]: Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate
--build-flags="": Build flags, to be passed to the compiler.
--headless[=false]: Run debug server only, in headless mode.
--init="": Init file, executed by the terminal client.
-l, --listen="localhost:0": Debugging server listen address.
--log[=false]: Enable debugging server logging.
```
### SEE ALSO
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
###### Auto generated by spf13/cobra on 19-Feb-2016

@ -0,0 +1,28 @@
## dlv run
Deprecated command. Use 'debug' instead.
### Synopsis
Deprecated command. Use 'debug' instead.
```
dlv run
```
### Options inherited from parent commands
```
--accept-multiclient[=false]: Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate
--build-flags="": Build flags, to be passed to the compiler.
--headless[=false]: Run debug server only, in headless mode.
--init="": Init file, executed by the terminal client.
-l, --listen="localhost:0": Debugging server listen address.
--log[=false]: Enable debugging server logging.
```
### SEE ALSO
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
###### Auto generated by spf13/cobra on 19-Feb-2016

@ -0,0 +1,28 @@
## dlv test
Compile test binary and begin debugging program.
### Synopsis
Compiles a test binary with optimizations disabled, starts and attaches to it, and enable you to immediately begin debugging your program.
```
dlv test [package]
```
### Options inherited from parent commands
```
--accept-multiclient[=false]: Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate
--build-flags="": Build flags, to be passed to the compiler.
--headless[=false]: Run debug server only, in headless mode.
--init="": Init file, executed by the terminal client.
-l, --listen="localhost:0": Debugging server listen address.
--log[=false]: Enable debugging server logging.
```
### SEE ALSO
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
###### Auto generated by spf13/cobra on 19-Feb-2016

@ -0,0 +1,35 @@
## dlv trace
Compile and begin tracing program.
### Synopsis
Trace program execution. Will set a tracepoint on every function matching the provided regular expression and output information when tracepoint is hit.
```
dlv trace [package] regexp
```
### Options
```
-p, --pid=0: Pid to attach to.
-s, --stack=0: Show stack trace with given depth.
```
### Options inherited from parent commands
```
--accept-multiclient[=false]: Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate
--build-flags="": Build flags, to be passed to the compiler.
--headless[=false]: Run debug server only, in headless mode.
--init="": Init file, executed by the terminal client.
-l, --listen="localhost:0": Debugging server listen address.
--log[=false]: Enable debugging server logging.
```
### SEE ALSO
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
###### Auto generated by spf13/cobra on 19-Feb-2016

@ -0,0 +1,28 @@
## dlv version
Prints version.
### Synopsis
Prints version.
```
dlv version
```
### Options inherited from parent commands
```
--accept-multiclient[=false]: Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate
--build-flags="": Build flags, to be passed to the compiler.
--headless[=false]: Run debug server only, in headless mode.
--init="": Init file, executed by the terminal client.
-l, --listen="localhost:0": Debugging server listen address.
--log[=false]: Enable debugging server logging.
```
### SEE ALSO
* [dlv](dlv.md) - Delve is a debugger for the Go programming language.
###### Auto generated by spf13/cobra on 19-Feb-2016

@ -12,11 +12,8 @@ The Github issue tracker is for **bugs** only. Please use the [developer mailing
### About Delve
- [Building](https://github.com/derekparker/delve/wiki/Building)
- [Usage](https://github.com/derekparker/delve/wiki/Usage)
- [Command reference](https://github.com/derekparker/delve/wiki/Commands)
- [Installation Tips & Troubleshooting](https://github.com/derekparker/delve/wiki/Tips-&-Troubleshooting)
- [Upcoming Features](https://github.com/derekparker/delve/wiki/Upcoming-Features)
- [Contributing](https://github.com/derekparker/delve/blob/master/CONTRIBUTING.md)
- [Documentation](Documentation)
- [Building](Documentation/installation)
- [Contributing](CONTRIBUTING.md)
Delve is a debugger for the Go programming language. The goal of the project is to provide a simple, full featured debugging tool for Go. Delve should be easy to invoke and easy to use. Chances are if you're using a debugger, most likely things aren't going your way. With that in mind, Delve should stay out of your way as much as possible.

406
cmd/dlv/cmds/commands.go Normal file

@ -0,0 +1,406 @@
package cmds
import (
"errors"
"fmt"
"net"
"os"
"os/exec"
"os/signal"
"path/filepath"
"runtime"
"strconv"
"syscall"
"github.com/derekparker/delve/config"
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api"
"github.com/derekparker/delve/service/rpc"
"github.com/derekparker/delve/terminal"
"github.com/derekparker/delve/version"
"github.com/spf13/cobra"
)
var (
// Log is whether to log debug statements.
Log bool
// Headless is whether to run without terminal.
Headless bool
// AcceptMulti allows multiple clients to connect to the same server
AcceptMulti bool
// Addr is the debugging server listen address.
Addr string
// InitFile is the path to initialization file.
InitFile string
// BuildFlags is the flags passed during compiler invocation.
BuildFlags string
// RootCommand is the root of the command tree.
RootCommand *cobra.Command
traceAttachPid int
traceStackDepth int
conf *config.Config
)
const (
debugname = "debug"
testdebugname = "debug.test"
)
const dlvCommandLongDesc = `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.
`
// New returns an initialized command tree.
func New() *cobra.Command {
// Config setup and load.
conf = config.LoadConfig()
buildFlagsDefault := ""
if runtime.GOOS == "windows" {
// Work-around for https://github.com/golang/go/issues/13154
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().BoolVarP(&Log, "log", "", false, "Enable debugging server logging.")
RootCommand.PersistentFlags().BoolVarP(&Headless, "headless", "", false, "Run debug server only, in headless mode.")
RootCommand.PersistentFlags().BoolVarP(&AcceptMulti, "accept-multiclient", "", false, "Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate")
RootCommand.PersistentFlags().StringVar(&InitFile, "init", "", "Init file, executed by the terminal client.")
RootCommand.PersistentFlags().StringVar(&BuildFlags, "build-flags", buildFlagsDefault, "Build flags, to be passed to the compiler.")
// 'version' subcommand.
versionCommand := &cobra.Command{
Use: "version",
Short: "Prints version.",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Delve Debugger\n%s\n", version.DelveVersion)
},
}
RootCommand.AddCommand(versionCommand)
// Deprecated 'run' subcommand.
runCommand := &cobra.Command{
Use: "run",
Short: "Deprecated command. Use 'debug' instead.",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("This command is deprecated, please use 'debug' instead.")
os.Exit(0)
},
}
RootCommand.AddCommand(runCommand)
// 'debug' subcommand.
debugCommand := &cobra.Command{
Use: "debug [package]",
Short: "Compile and begin debugging program.",
Long: `Compiles your program with optimizations disabled,
starts and attaches to it, and enables you to immediately begin debugging your program.`,
Run: debugCmd,
}
RootCommand.AddCommand(debugCommand)
// 'exec' subcommand.
execCommand := &cobra.Command{
Use: "exec [./path/to/binary]",
Short: "Runs precompiled binary, attaches and begins debug session.",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("you must provide a path to a binary")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
os.Exit(execute(0, args, conf))
},
}
RootCommand.AddCommand(execCommand)
// 'trace' subcommand.
traceCommand := &cobra.Command{
Use: "trace [package] regexp",
Short: "Compile and begin tracing program.",
Long: "Trace program execution. Will set a tracepoint on every function matching the provided regular expression and output information when tracepoint is hit.",
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 [package]",
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)
return RootCommand
}
func debugCmd(cmd *cobra.Command, args []string) {
status := func() int {
var pkg string
dlvArgs, targetArgs := splitArgs(cmd, args)
if len(dlvArgs) > 0 {
pkg = args[0]
}
err := gobuild(debugname, pkg)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return 1
}
fp, err := filepath.Abs("./" + debugname)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return 1
}
defer os.Remove(fp)
processArgs := append([]string{"./" + debugname}, targetArgs...)
return execute(0, processArgs, conf)
}()
os.Exit(status)
}
func traceCmd(cmd *cobra.Command, args []string) {
status := func() int {
var regexp string
var processArgs []string
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
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 debug server
server := rpc.NewServer(&service.Config{
Listener: listener,
ProcessArgs: processArgs,
AttachPid: traceAttachPid,
}, Log)
if err := server.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
client := rpc.NewClient(listener.Addr().String())
funcs, err := client.ListFunctions(regexp)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
for i := range funcs {
_, err := client.CreateBreakpoint(&api.Breakpoint{FunctionName: funcs[i], Tracepoint: true, Line: -1, Stacktrace: traceStackDepth})
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
}
cmds := terminal.DebugCommands(client)
cmd := cmds.Find("continue")
t := terminal.New(client, nil)
defer t.Close()
err = cmd(t, "")
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
return 0
}()
os.Exit(status)
}
func testCmd(cmd *cobra.Command, args []string) {
status := func() int {
var pkg string
dlvArgs, targetArgs := splitArgs(cmd, args)
if len(dlvArgs) > 0 {
pkg = args[0]
}
err := gotestbuild(pkg)
if err != nil {
return 1
}
defer os.Remove("./" + testdebugname)
processArgs := append([]string{"./" + testdebugname}, targetArgs...)
return execute(0, processArgs, conf)
}()
os.Exit(status)
}
func attachCmd(cmd *cobra.Command, args []string) {
pid, err := strconv.Atoi(args[0])
if err != nil {
fmt.Fprintf(os.Stderr, "Invalid pid: %s\n", args[0])
os.Exit(1)
}
os.Exit(execute(pid, nil, conf))
}
func connectCmd(cmd *cobra.Command, args []string) {
addr := args[0]
if addr == "" {
fmt.Fprintf(os.Stderr, "An empty address was provided. You must provide an address as the first argument.\n")
os.Exit(1)
}
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 {
// Create and start a terminal - attach to running instance
var client service.Client
client = rpc.NewClient(addr)
term := terminal.New(client, conf)
status, err := term.Run()
if err != nil {
fmt.Println(err)
}
return status
}
func execute(attachPid int, processArgs []string, conf *config.Config) int {
// 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()
if Headless && (InitFile != "") {
fmt.Fprintf(os.Stderr, "Warning: init file ignored\n")
}
// Create and start a debugger server
server := rpc.NewServer(&service.Config{
Listener: listener,
ProcessArgs: processArgs,
AttachPid: attachPid,
AcceptMulti: AcceptMulti,
}, Log)
if err := server.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
var status int
if Headless {
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGINT)
<-ch
err = server.Stop(true)
} else {
// Create and start a terminal
var client service.Client
client = rpc.NewClient(listener.Addr().String())
term := terminal.New(client, conf)
term.InitFile = InitFile
status, err = term.Run()
}
if err != nil {
fmt.Println(err)
}
return status
}
func gobuild(debugname, pkg string) error {
args := []string{"-gcflags", "-N -l", "-o", debugname}
if BuildFlags != "" {
args = append(args, BuildFlags)
}
args = append(args, pkg)
return gocommand("build", args...)
}
func gotestbuild(pkg string) error {
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 {
allargs := []string{command}
allargs = append(allargs, args...)
goBuild := exec.Command("go", allargs...)
goBuild.Stderr = os.Stderr
return goBuild.Run()
}

@ -1,410 +1,14 @@
package main
import (
"errors"
"fmt"
"net"
"os"
"os/exec"
"os/signal"
"path/filepath"
"runtime"
"strconv"
"syscall"
"github.com/derekparker/delve/config"
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api"
"github.com/derekparker/delve/service/rpc"
"github.com/derekparker/delve/terminal"
"github.com/spf13/cobra"
"github.com/derekparker/delve/cmd/dlv/cmds"
"github.com/derekparker/delve/version"
)
const (
version = "0.11.0-alpha"
debugname = "debug"
testdebugname = "debug.test"
)
var (
// Log is whether to log debug statements.
Log bool
// Headless is whether to run without terminal.
Headless bool
// Allows multiple clients to connect to the same server
AcceptMulti bool
// Addr is the debugging server listen address.
Addr string
// InitFile is the path to initialization file.
InitFile string
// BuildFlags is the flags passed during compiler invocation.
BuildFlags string
// Build is the current git hash.
Build string
traceAttachPid int
traceStackDepth int
conf *config.Config
rootCommand *cobra.Command
)
const dlvCommandLongDesc = `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.
`
func init() {
buildFlagsDefault := ""
if runtime.GOOS == "windows" {
// Work-around for https://github.com/golang/go/issues/13154
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().BoolVarP(&Log, "log", "", false, "Enable debugging server logging.")
rootCommand.PersistentFlags().BoolVarP(&Headless, "headless", "", false, "Run debug server only, in headless mode.")
rootCommand.PersistentFlags().BoolVarP(&AcceptMulti, "accept-multiclient", "", false, "Allows a headless server to accept multiple client connection. Note that the server API is not reentrant and clients will have to coordinate")
rootCommand.PersistentFlags().StringVar(&InitFile, "init", "", "Init file, executed by the terminal client.")
rootCommand.PersistentFlags().StringVar(&BuildFlags, "build-flags", buildFlagsDefault, "Build flags, to be passed to the compiler.")
// 'version' subcommand.
versionCommand := &cobra.Command{
Use: "version",
Short: "Prints version.",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Delve Debugger\nVersion: %s\nBuild: %s\n", version, Build)
},
}
rootCommand.AddCommand(versionCommand)
// Deprecated 'run' subcommand.
runCommand := &cobra.Command{
Use: "run",
Short: "Deprecated command. Use 'debug' instead.",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("This command is deprecated, please use 'debug' instead.")
os.Exit(0)
},
}
rootCommand.AddCommand(runCommand)
// 'debug' subcommand.
debugCommand := &cobra.Command{
Use: "debug [package]",
Short: "Compile and begin debugging program.",
Long: `Compiles your program with optimizations disabled,
starts and attaches to it, and enables you to immediately begin debugging your program.`,
Run: debugCmd,
}
rootCommand.AddCommand(debugCommand)
// 'exec' subcommand.
execCommand := &cobra.Command{
Use: "exec [./path/to/binary]",
Short: "Runs precompiled binary, attaches and begins debug session.",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("you must provide a path to a binary")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
os.Exit(execute(0, args, conf))
},
}
rootCommand.AddCommand(execCommand)
// 'trace' subcommand.
traceCommand := &cobra.Command{
Use: "trace [package] regexp",
Short: "Compile and begin tracing program.",
Long: "Trace program execution. Will set a tracepoint on every function matching the provided regular expression and output information when tracepoint is hit.",
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 [package]",
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)
}
// Build is the git sha of this binaries build.
var Build string
func main() {
// Config setup and load.
conf = config.LoadConfig()
rootCommand.Execute()
}
func debugCmd(cmd *cobra.Command, args []string) {
status := func() int {
var pkg string
dlvArgs, targetArgs := splitArgs(cmd, args)
if len(dlvArgs) > 0 {
pkg = args[0]
}
err := gobuild(debugname, pkg)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return 1
}
fp, err := filepath.Abs("./" + debugname)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return 1
}
defer os.Remove(fp)
processArgs := append([]string{"./" + debugname}, targetArgs...)
return execute(0, processArgs, conf)
}()
os.Exit(status)
}
func traceCmd(cmd *cobra.Command, args []string) {
status := func() int {
var regexp string
var processArgs []string
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
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 debug server
server := rpc.NewServer(&service.Config{
Listener: listener,
ProcessArgs: processArgs,
AttachPid: traceAttachPid,
}, Log)
if err := server.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
client := rpc.NewClient(listener.Addr().String())
funcs, err := client.ListFunctions(regexp)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
for i := range funcs {
_, err := client.CreateBreakpoint(&api.Breakpoint{FunctionName: funcs[i], Tracepoint: true, Line: -1, Stacktrace: traceStackDepth})
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
}
cmds := terminal.DebugCommands(client)
cmd := cmds.Find("continue")
t := terminal.New(client, nil)
defer t.Close()
err = cmd(t, "")
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
return 0
}()
os.Exit(status)
}
func testCmd(cmd *cobra.Command, args []string) {
status := func() int {
var pkg string
dlvArgs, targetArgs := splitArgs(cmd, args)
if len(dlvArgs) > 0 {
pkg = args[0]
}
err := gotestbuild(pkg)
if err != nil {
return 1
}
defer os.Remove("./" + testdebugname)
processArgs := append([]string{"./" + testdebugname}, targetArgs...)
return execute(0, processArgs, conf)
}()
os.Exit(status)
}
func attachCmd(cmd *cobra.Command, args []string) {
pid, err := strconv.Atoi(args[0])
if err != nil {
fmt.Fprintf(os.Stderr, "Invalid pid: %s\n", args[0])
os.Exit(1)
}
os.Exit(execute(pid, nil, conf))
}
func connectCmd(cmd *cobra.Command, args []string) {
addr := args[0]
if addr == "" {
fmt.Fprintf(os.Stderr, "An empty address was provided. You must provide an address as the first argument.\n")
os.Exit(1)
}
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 {
// Create and start a terminal - attach to running instance
var client service.Client
client = rpc.NewClient(addr)
term := terminal.New(client, conf)
status, err := term.Run()
if err != nil {
fmt.Println(err)
}
return status
}
func execute(attachPid int, processArgs []string, conf *config.Config) int {
// 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()
if Headless && (InitFile != "") {
fmt.Fprintf(os.Stderr, "Warning: init file ignored\n")
}
// Create and start a debugger server
server := rpc.NewServer(&service.Config{
Listener: listener,
ProcessArgs: processArgs,
AttachPid: attachPid,
AcceptMulti: AcceptMulti,
}, Log)
if err := server.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
var status int
if Headless {
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGINT)
<-ch
err = server.Stop(true)
} else {
// Create and start a terminal
var client service.Client
client = rpc.NewClient(listener.Addr().String())
term := terminal.New(client, conf)
term.InitFile = InitFile
status, err = term.Run()
}
if err != nil {
fmt.Println(err)
}
return status
}
func gobuild(debugname, pkg string) error {
args := []string{"-gcflags", "-N -l", "-o", debugname}
if BuildFlags != "" {
args = append(args, BuildFlags)
}
args = append(args, pkg)
return gocommand("build", args...)
}
func gotestbuild(pkg string) error {
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 {
allargs := []string{command}
allargs = append(allargs, args...)
goBuild := exec.Command("go", allargs...)
goBuild.Stderr = os.Stderr
return goBuild.Run()
version.DelveVersion.Build = Build
cmds.New().Execute()
}

10
scripts/gen-usage-docs.go Normal file

@ -0,0 +1,10 @@
package main
import (
"github.com/derekparker/delve/cmd/dlv/cmds"
"github.com/spf13/cobra/doc"
)
func main() {
doc.GenMarkdownTree(cmds.New(), "./Documentation/usage")
}

21
version/version.go Normal file

@ -0,0 +1,21 @@
package version
import "fmt"
// Version represents the current version of Delve.
type Version struct {
Major string
Minor string
Patch string
Metadata string
Build string
}
var (
// DelveVersion is the current version of Delve.
DelveVersion = Version{Major: "0", Minor: "11", Patch: "0", Metadata: "alpha"}
)
func (v Version) String() string {
return fmt.Sprintf("Version: %s.%s.%s-%s\nBuild: %s", v.Major, v.Minor, v.Patch, v.Metadata, v.Build)
}