Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Delve debugger #384

Merged
merged 7 commits into from
Mar 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 42 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

bee
===

Expand Down Expand Up @@ -34,17 +35,20 @@ go get -u github.com/beego/bee
Bee provides a variety of commands which can be helpful at various stages of development. The top level commands include:

```
new Creates a Beego application
run Run the application by starting a local development server
pack Compresses a Beego application into a single file
api Creates a Beego API application
hprose Creates an RPC application based on Hprose and Beego frameworks
bale Transforms non-Go files to Go source files
version Prints the current Bee version
generate Source code generator
migrate Runs database migrations
api Creates a Beego API application
bale Transforms non-Go files to Go source files
fix Fixes your application by making it compatible with newer versions of Beego
dlv Start a debugging session using Delve
dockerize Generates a Dockerfile for your Beego application
generate Source code generator
hprose Creates an RPC application based on Hprose and Beego frameworks
new Creates a Beego application
pack Compresses a Beego application into a single file
rs Run customized scripts
run Run the application by starting a local development server

```

### bee version
Expand Down Expand Up @@ -298,6 +302,37 @@ ______

For more information on the usage, run `bee help dockerize`.

### bee dlv

Bee can also help with debugging your application. To start a debugging session:

```bash
______
| ___ \
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.8.0
2017/03/22 11:17:05 INFO ▶ 0001 Starting Delve Debugger...
Type 'help' for list of commands.
(dlv) break main.main
Breakpoint 1 set at 0x40100f for main.main() ./main.go:8

(dlv) continue
> main.main() ./main.go:8 (hits goroutine(1):1 total:1) (PC: 0x40100f)
3: import (
4: _ "github.com/user/myapp/routers"
5: "github.com/astaxie/beego"
6: )
7:
=> 8: func main() {
9: beego.Run()
10: }
11:
```

For more information on the usage, run `bee help dlv`.

## Shortcuts

Because you'll likely type these generator commands over and over, it makes sense to create aliases:
Expand Down
3 changes: 2 additions & 1 deletion cmd/bee.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
// License for the specific language governing permissions and limitations
// under the License.

// Bee is a tool for developing applications based on beego framework.
// Package cmd ...
package cmd

import (
"github.com/beego/bee/cmd/commands"
_ "github.com/beego/bee/cmd/commands/api"
_ "github.com/beego/bee/cmd/commands/bale"
_ "github.com/beego/bee/cmd/commands/beefix"
_ "github.com/beego/bee/cmd/commands/dlv"
_ "github.com/beego/bee/cmd/commands/dockerize"
_ "github.com/beego/bee/cmd/commands/generate"
_ "github.com/beego/bee/cmd/commands/hprose"
Expand Down
135 changes: 135 additions & 0 deletions cmd/commands/dlv/dlv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright 2017 bee authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

// Package dlv ...
package dlv

import (
"flag"
"fmt"
"net"
"os"
"path/filepath"

"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
"github.com/derekparker/delve/pkg/terminal"
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/rpc2"
"github.com/derekparker/delve/service/rpccommon"
)

var cmdDlv = &commands.Command{
CustomFlags: true,
UsageLine: "dlv [-package=\"\"] [-port=8181]",
Short: "Start a debugging session using Delve",
Long: `dlv command start a debugging session using debugging tool Delve.

To debug your application using Delve, use: {{"$ bee dlv" | bold}}

For more information on Delve: https://github.com/derekparker/delve
`,
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: runDlv,
}

var (
packageName string
port int
)

func init() {
fs := flag.NewFlagSet("dlv", flag.ContinueOnError)
fs.IntVar(&port, "port", 8181, "Port to listen to for clients")
fs.StringVar(&packageName, "package", "", "The package to debug (Must have a main package)")
cmdDlv.Flag = *fs
commands.AvailableCommands = append(commands.AvailableCommands, cmdDlv)
}

func runDlv(cmd *commands.Command, args []string) int {
if err := cmd.Flag.Parse(args); err != nil {
beeLogger.Log.Fatalf("Error parsing flags: %v", err.Error())
}

debugname := "debug"
addr := fmt.Sprintf("127.0.0.1:%d", port)
return runDelve(addr, debugname)
}

// runDelve runs the Delve debugger server
func runDelve(addr, debugname string) int {
beeLogger.Log.Info("Starting Delve Debugger...")

err := gobuild(debugname, packageName)
if err != nil {
beeLogger.Log.Fatalf("%v", err)
}

fp, err := filepath.Abs("./" + debugname)
if err != nil {
beeLogger.Log.Fatalf("%v", err)
}
defer os.Remove(fp)

abs, err := filepath.Abs(debugname)
if err != nil {
beeLogger.Log.Fatalf("%v", err)
}

// Create and start the debugger server
listener, err := net.Listen("tcp", addr)
if err != nil {
beeLogger.Log.Fatalf("Could not start listener: %s", err)
}
defer listener.Close()

server := rpccommon.NewServer(&service.Config{
Listener: listener,
AcceptMulti: true,
AttachPid: 0,
APIVersion: 2,
WorkingDir: "./",
ProcessArgs: []string{abs},
}, false)
if err := server.Run(); err != nil {
beeLogger.Log.Fatalf("Could not start debugger server: %v", err)
}

// Start the Delve client REPL
client := rpc2.NewClient(addr)
term := terminal.New(client, nil)

status, err := term.Run()
if err != nil {
beeLogger.Log.Fatalf("Could not start Delve REPL: %v", err)
}
defer term.Close()

// Stop and kill the debugger server once
// user quits the REPL
if err := server.Stop(true); err != nil {
beeLogger.Log.Fatalf("Could not stop Delve server: %v", err)
}
return status
}

// gobuild runs the "go build" command on the specified package
func gobuild(debugname, pkg string) error {
args := []string{"-gcflags", "-N -l", "-o", debugname}
args = append(args, utils.SplitQuotedFields("-ldflags='-linkmode internal'")...)
args = append(args, pkg)
return utils.GoCommand("build", args...)
}
67 changes: 67 additions & 0 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"runtime"
"strings"
"text/template"
"unicode"

beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
Expand Down Expand Up @@ -332,3 +333,69 @@ func PrintErrorAndExit(message, errorTemplate string) {
Tmpl(fmt.Sprintf(errorTemplate, message), nil)
os.Exit(2)
}

// GoCommand executes the passed command using Go tool
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()
}

// SplitQuotedFields is like strings.Fields but ignores spaces
// inside areas surrounded by single quotes.
// To specify a single quote use backslash to escape it: '\''
func SplitQuotedFields(in string) []string {
type stateEnum int
const (
inSpace stateEnum = iota
inField
inQuote
inQuoteEscaped
)
state := inSpace
r := []string{}
var buf bytes.Buffer

for _, ch := range in {
switch state {
case inSpace:
if ch == '\'' {
state = inQuote
} else if !unicode.IsSpace(ch) {
buf.WriteRune(ch)
state = inField
}

case inField:
if ch == '\'' {
state = inQuote
} else if unicode.IsSpace(ch) {
r = append(r, buf.String())
buf.Reset()
} else {
buf.WriteRune(ch)
}

case inQuote:
if ch == '\'' {
state = inField
} else if ch == '\\' {
state = inQuoteEscaped
} else {
buf.WriteRune(ch)
}

case inQuoteEscaped:
buf.WriteRune(ch)
state = inQuote
}
}

if buf.Len() != 0 {
r = append(r, buf.String())
}

return r
}
20 changes: 20 additions & 0 deletions vendor/github.com/derekparker/delve/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading