Implement 'trace' subcommand
Allows a user to execute `dlv trace [regexp]` and Delve will execute the program and output information on functions matching [regexp].
This commit is contained in:
parent
c6ca18ff07
commit
3cee10d8bc
@ -8,10 +8,12 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
sys "golang.org/x/sys/unix"
|
sys "golang.org/x/sys/unix"
|
||||||
|
|
||||||
"github.com/derekparker/delve/service"
|
"github.com/derekparker/delve/service"
|
||||||
|
"github.com/derekparker/delve/service/api"
|
||||||
"github.com/derekparker/delve/service/rpc"
|
"github.com/derekparker/delve/service/rpc"
|
||||||
"github.com/derekparker/delve/terminal"
|
"github.com/derekparker/delve/terminal"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -82,6 +84,91 @@ starts and attaches to it, and enables you to immediately begin debugging your p
|
|||||||
}
|
}
|
||||||
rootCommand.AddCommand(runCommand)
|
rootCommand.AddCommand(runCommand)
|
||||||
|
|
||||||
|
// 'trace' subcommand.
|
||||||
|
traceCommand := &cobra.Command{
|
||||||
|
Use: "trace [regexp]",
|
||||||
|
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.",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
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...)
|
||||||
|
// 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 debugger server
|
||||||
|
server := rpc.NewServer(&service.Config{
|
||||||
|
Listener: listener,
|
||||||
|
ProcessArgs: processArgs,
|
||||||
|
}, Log)
|
||||||
|
if err := server.Run(); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
sigChan := make(chan os.Signal)
|
||||||
|
signal.Notify(sigChan, sys.SIGINT)
|
||||||
|
client := rpc.NewClient(listener.Addr().String())
|
||||||
|
funcs, err := client.ListFunctions(args[0])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
for i := range funcs {
|
||||||
|
_, err := client.CreateBreakpoint(&api.Breakpoint{FunctionName: funcs[i], Tracepoint: true})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stateChan := client.Continue()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case state := <-stateChan:
|
||||||
|
if state.Err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, state.Err)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var args []string
|
||||||
|
var fname string
|
||||||
|
if state.CurrentThread != nil && state.CurrentThread.Function != nil {
|
||||||
|
fname = state.CurrentThread.Function.Name
|
||||||
|
}
|
||||||
|
if state.BreakpointInfo != nil {
|
||||||
|
for _, arg := range state.BreakpointInfo.Arguments {
|
||||||
|
args = append(args, arg.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("%s(%s) %s:%d\n", fname, strings.Join(args, ", "), state.CurrentThread.File, state.CurrentThread.Line)
|
||||||
|
case <-sigChan:
|
||||||
|
server.Stop(true)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}()
|
||||||
|
os.Exit(status)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rootCommand.AddCommand(traceCommand)
|
||||||
|
|
||||||
// 'test' subcommand.
|
// 'test' subcommand.
|
||||||
testCommand := &cobra.Command{
|
testCommand := &cobra.Command{
|
||||||
Use: "test",
|
Use: "test",
|
||||||
|
@ -55,7 +55,7 @@ func (reader *Reader) SeekToFunction(pc uint64) (*dwarf.Entry, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if lowpc <= pc && highpc >= pc {
|
if lowpc <= pc && highpc > pc {
|
||||||
return entry, nil
|
return entry, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -770,7 +770,6 @@ func (thread *Thread) readMemory(addr uintptr, size int) ([]byte, error) {
|
|||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, size)
|
buf := make([]byte, size)
|
||||||
_, err := readMemory(thread, addr, buf)
|
_, err := readMemory(thread, addr, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,6 +2,8 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/gosym"
|
"debug/gosym"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/derekparker/delve/proc"
|
"github.com/derekparker/delve/proc"
|
||||||
)
|
)
|
||||||
@ -11,7 +13,7 @@ func ConvertBreakpoint(bp *proc.Breakpoint) *Breakpoint {
|
|||||||
return &Breakpoint{
|
return &Breakpoint{
|
||||||
ID: bp.ID,
|
ID: bp.ID,
|
||||||
FunctionName: bp.FunctionName,
|
FunctionName: bp.FunctionName,
|
||||||
File: bp.File,
|
File: shortenFilePath(bp.File),
|
||||||
Line: bp.Line,
|
Line: bp.Line,
|
||||||
Addr: bp.Addr,
|
Addr: bp.Addr,
|
||||||
Tracepoint: bp.Tracepoint,
|
Tracepoint: bp.Tracepoint,
|
||||||
@ -41,7 +43,7 @@ func ConvertThread(th *proc.Thread) *Thread {
|
|||||||
return &Thread{
|
return &Thread{
|
||||||
ID: th.Id,
|
ID: th.Id,
|
||||||
PC: pc,
|
PC: pc,
|
||||||
File: file,
|
File: shortenFilePath(file),
|
||||||
Line: line,
|
Line: line,
|
||||||
Function: function,
|
Function: function,
|
||||||
}
|
}
|
||||||
@ -74,7 +76,7 @@ func ConvertGoroutine(g *proc.G) *Goroutine {
|
|||||||
return &Goroutine{
|
return &Goroutine{
|
||||||
ID: g.Id,
|
ID: g.Id,
|
||||||
PC: g.PC,
|
PC: g.PC,
|
||||||
File: g.File,
|
File: shortenFilePath(g.File),
|
||||||
Line: g.Line,
|
Line: g.Line,
|
||||||
Function: ConvertFunction(g.Func),
|
Function: ConvertFunction(g.Func),
|
||||||
}
|
}
|
||||||
@ -83,8 +85,13 @@ func ConvertGoroutine(g *proc.G) *Goroutine {
|
|||||||
func ConvertLocation(loc proc.Location) Location {
|
func ConvertLocation(loc proc.Location) Location {
|
||||||
return Location{
|
return Location{
|
||||||
PC: loc.PC,
|
PC: loc.PC,
|
||||||
File: loc.File,
|
File: shortenFilePath(loc.File),
|
||||||
Line: loc.Line,
|
Line: loc.Line,
|
||||||
Function: ConvertFunction(loc.Fn),
|
Function: ConvertFunction(loc.Fn),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shortenFilePath(fullPath string) string {
|
||||||
|
workingDir, _ := os.Getwd()
|
||||||
|
return strings.Replace(fullPath, workingDir, ".", 1)
|
||||||
|
}
|
||||||
|
@ -112,6 +112,7 @@ type BreakpointInfo struct {
|
|||||||
Stacktrace []Location `json:"stacktrace,omitempty"`
|
Stacktrace []Location `json:"stacktrace,omitempty"`
|
||||||
Goroutine *Goroutine `json:"goroutine,omitempty"`
|
Goroutine *Goroutine `json:"goroutine,omitempty"`
|
||||||
Variables []Variable `json:"variables,omitempty"`
|
Variables []Variable `json:"variables,omitempty"`
|
||||||
|
Arguments []Variable `json:"arguments,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -272,6 +272,10 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
|||||||
}
|
}
|
||||||
bpi.Variables[i] = api.ConvertVar(v)
|
bpi.Variables[i] = api.ConvertVar(v)
|
||||||
}
|
}
|
||||||
|
vars, err := d.FunctionArguments(d.process.CurrentThread.Id)
|
||||||
|
if err == nil {
|
||||||
|
bpi.Arguments = vars
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -370,7 +374,7 @@ func (d *Debugger) FunctionArguments(threadID int) ([]api.Variable, error) {
|
|||||||
for _, v := range pv {
|
for _, v := range pv {
|
||||||
vars = append(vars, api.ConvertVar(v))
|
vars = append(vars, api.ConvertVar(v))
|
||||||
}
|
}
|
||||||
return vars, err
|
return vars, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Debugger) EvalVariableInThread(threadID int, symbol string) (*api.Variable, error) {
|
func (d *Debugger) EvalVariableInThread(threadID int, symbol string) (*api.Variable, error) {
|
||||||
|
@ -66,7 +66,7 @@ func (c *RPCClient) Continue() <-chan *api.DebuggerState {
|
|||||||
state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus)
|
state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus)
|
||||||
}
|
}
|
||||||
ch <- state
|
ch <- state
|
||||||
if err != nil || state.Breakpoint == nil || !state.Breakpoint.Tracepoint {
|
if err != nil || state.Exited || state.Breakpoint == nil || !state.Breakpoint.Tracepoint {
|
||||||
close(ch)
|
close(ch)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -537,11 +537,19 @@ func printcontext(state *api.DebuggerState) error {
|
|||||||
|
|
||||||
var context []string
|
var context []string
|
||||||
|
|
||||||
fn := ""
|
var fn *api.Function
|
||||||
if state.CurrentThread.Function != nil {
|
if state.CurrentThread.Function != nil {
|
||||||
fn = state.CurrentThread.Function.Name
|
fn = state.CurrentThread.Function
|
||||||
|
}
|
||||||
|
if state.Breakpoint != nil && state.Breakpoint.Tracepoint {
|
||||||
|
var args []string
|
||||||
|
for _, arg := range state.CurrentThread.Function.Args {
|
||||||
|
args = append(args, arg.Value)
|
||||||
|
}
|
||||||
|
fmt.Printf("%s(%s) %s:%d\n", fn.Name, strings.Join(args, ", "), state.CurrentThread.File, state.CurrentThread.Line)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s() %s:%d\n", fn.Name, state.CurrentThread.File, state.CurrentThread.Line)
|
||||||
}
|
}
|
||||||
fmt.Printf("current loc: %s %s:%d\n", fn, state.CurrentThread.File, state.CurrentThread.Line)
|
|
||||||
|
|
||||||
if state.BreakpointInfo != nil {
|
if state.BreakpointInfo != nil {
|
||||||
bpi := state.BreakpointInfo
|
bpi := state.BreakpointInfo
|
||||||
|
Loading…
Reference in New Issue
Block a user