2014-05-21 15:23:14 +00:00
// Package command implements functions for responding to user
// input and dispatching to appropriate backend commands.
2014-05-20 21:28:24 +00:00
package command
import (
2014-09-07 00:39:40 +00:00
"bufio"
2014-06-25 19:58:45 +00:00
"debug/gosym"
2014-05-20 21:28:24 +00:00
"fmt"
2014-09-07 00:39:40 +00:00
"io"
"os"
2014-06-25 19:58:45 +00:00
"path/filepath"
2014-12-28 08:02:37 +00:00
"regexp"
"sort"
2014-06-25 19:58:45 +00:00
"strconv"
"strings"
2014-10-15 14:21:46 +00:00
"github.com/derekparker/delve/proctl"
2014-05-20 21:28:24 +00:00
)
2014-06-25 19:58:45 +00:00
type cmdfunc func ( proc * proctl . DebuggedProcess , args ... string ) error
2014-05-20 21:28:24 +00:00
2014-11-14 05:51:32 +00:00
type command struct {
aliases [ ] string
helpMsg string
cmdFn cmdfunc
}
// Returns true if the command string matches one of the aliases for this command
func ( c command ) match ( cmdstr string ) bool {
for _ , v := range c . aliases {
if v == cmdstr {
return true
}
}
return false
}
2014-05-20 21:28:24 +00:00
type Commands struct {
2014-11-14 05:51:32 +00:00
cmds [ ] command
lastCmd cmdfunc
2014-05-20 21:28:24 +00:00
}
2014-05-21 15:18:54 +00:00
// Returns a Commands struct with default commands defined.
2014-05-20 21:28:24 +00:00
func DebugCommands ( ) * Commands {
2014-11-14 05:51:32 +00:00
c := & Commands { }
c . cmds = [ ] command {
2014-11-14 15:36:39 +00:00
command { aliases : [ ] string { "help" } , cmdFn : c . help , helpMsg : "Prints the help message." } ,
command { aliases : [ ] string { "break" , "b" } , cmdFn : breakpoint , helpMsg : "Set break point at the entry point of a function, or at a specific file/line. Example: break foo.go:13" } ,
command { aliases : [ ] string { "continue" , "c" } , cmdFn : cont , helpMsg : "Run until breakpoint or program termination." } ,
command { aliases : [ ] string { "step" , "si" } , cmdFn : step , helpMsg : "Single step through program." } ,
command { aliases : [ ] string { "next" , "n" } , cmdFn : next , helpMsg : "Step over to next source line." } ,
command { aliases : [ ] string { "threads" } , cmdFn : threads , helpMsg : "Print out info for every traced thread." } ,
command { aliases : [ ] string { "clear" } , cmdFn : clear , helpMsg : "Deletes breakpoint." } ,
command { aliases : [ ] string { "goroutines" } , cmdFn : goroutines , helpMsg : "Print out info for every goroutine." } ,
command { aliases : [ ] string { "print" , "p" } , cmdFn : printVar , helpMsg : "Evaluate a variable." } ,
2014-12-28 08:02:37 +00:00
command { aliases : [ ] string { "info" } , cmdFn : info , helpMsg : "Provides list of source files with symbols." } ,
2014-11-14 15:36:39 +00:00
command { aliases : [ ] string { "exit" } , cmdFn : nullCommand , helpMsg : "Exit the debugger." } ,
2014-05-20 21:28:24 +00:00
}
2014-11-14 05:51:32 +00:00
return c
2014-05-20 21:28:24 +00:00
}
2014-05-21 15:23:14 +00:00
// Register custom commands. Expects cf to be a func of type cmdfunc,
// returning only an error.
2014-11-14 05:51:32 +00:00
func ( c * Commands ) Register ( cmdstr string , cf cmdfunc , helpMsg string ) {
for _ , v := range c . cmds {
if v . match ( cmdstr ) {
v . cmdFn = cf
return
}
}
c . cmds = append ( c . cmds , command { aliases : [ ] string { cmdstr } , cmdFn : cf , helpMsg : helpMsg } )
2014-05-20 23:09:34 +00:00
}
2014-05-21 15:15:58 +00:00
// Find will look up the command function for the given command input.
// If it cannot find the command it will defualt to noCmdAvailable().
// If the command is an empty string it will replay the last command.
2014-05-20 21:28:24 +00:00
func ( c * Commands ) Find ( cmdstr string ) cmdfunc {
2014-11-14 05:51:32 +00:00
// If <enter> use last command, if there was one.
if cmdstr == "" {
if c . lastCmd != nil {
return c . lastCmd
}
return nullCommand
2014-05-20 21:28:24 +00:00
}
2014-11-14 05:51:32 +00:00
for _ , v := range c . cmds {
if v . match ( cmdstr ) {
c . lastCmd = v . cmdFn
return v . cmdFn
}
}
2014-05-21 15:15:58 +00:00
2014-11-14 05:51:32 +00:00
return noCmdAvailable
2014-05-20 21:28:24 +00:00
}
2014-05-27 15:44:29 +00:00
func CommandFunc ( fn func ( ) error ) cmdfunc {
2014-06-25 19:58:45 +00:00
return func ( p * proctl . DebuggedProcess , args ... string ) error {
2014-05-27 15:44:29 +00:00
return fn ( )
}
}
2014-06-25 19:58:45 +00:00
func noCmdAvailable ( p * proctl . DebuggedProcess , ars ... string ) error {
2014-05-20 21:28:24 +00:00
return fmt . Errorf ( "command not available" )
}
2014-06-25 19:58:45 +00:00
func nullCommand ( p * proctl . DebuggedProcess , ars ... string ) error {
return nil
}
2014-11-14 05:51:32 +00:00
func ( c * Commands ) help ( p * proctl . DebuggedProcess , ars ... string ) error {
fmt . Println ( "The following commands are available:" )
for _ , cmd := range c . cmds {
2014-11-14 15:36:39 +00:00
fmt . Printf ( "\t%s - %s\n" , strings . Join ( cmd . aliases , "|" ) , cmd . helpMsg )
2014-11-14 05:51:32 +00:00
}
2014-10-20 17:06:36 +00:00
return nil
}
2014-11-08 05:55:25 +00:00
func threads ( p * proctl . DebuggedProcess , ars ... string ) error {
return p . PrintThreadInfo ( )
}
2014-11-08 14:02:31 +00:00
func goroutines ( p * proctl . DebuggedProcess , ars ... string ) error {
return p . PrintGoroutinesInfo ( )
}
2014-06-25 19:58:45 +00:00
func cont ( p * proctl . DebuggedProcess , ars ... string ) error {
2014-09-07 00:39:40 +00:00
err := p . Continue ( )
2014-06-25 19:58:45 +00:00
if err != nil {
return err
}
2014-09-07 00:39:40 +00:00
return printcontext ( p )
}
func step ( p * proctl . DebuggedProcess , args ... string ) error {
err := p . Step ( )
2014-06-25 19:58:45 +00:00
if err != nil {
return err
}
2014-09-07 00:39:40 +00:00
return printcontext ( p )
2014-06-25 19:58:45 +00:00
}
2014-07-11 21:18:07 +00:00
func next ( p * proctl . DebuggedProcess , args ... string ) error {
err := p . Next ( )
if err != nil {
return err
}
2014-09-07 00:39:40 +00:00
return printcontext ( p )
2014-07-11 21:18:07 +00:00
}
2014-06-25 19:58:45 +00:00
func clear ( p * proctl . DebuggedProcess , args ... string ) error {
2014-11-12 23:40:44 +00:00
if len ( args ) == 0 {
return fmt . Errorf ( "not enough arguments" )
}
2014-10-17 13:29:44 +00:00
var (
fn * gosym . Func
pc uint64
fname = args [ 0 ]
)
if strings . ContainsRune ( fname , ':' ) {
fl := strings . Split ( fname , ":" )
f , err := filepath . Abs ( fl [ 0 ] )
if err != nil {
return err
}
l , err := strconv . Atoi ( fl [ 1 ] )
if err != nil {
return err
}
pc , fn , err = p . GoSymTable . LineToPC ( f , l )
if err != nil {
return err
}
} else {
fn = p . GoSymTable . LookupFunc ( fname )
if fn == nil {
return fmt . Errorf ( "No function named %s" , fname )
}
pc = fn . Entry
2014-06-25 19:58:45 +00:00
}
2014-10-17 13:29:44 +00:00
bp , err := p . Clear ( pc )
2014-06-25 19:58:45 +00:00
if err != nil {
return err
}
fmt . Printf ( "Breakpoint cleared at %#v for %s %s:%d\n" , bp . Addr , bp . FunctionName , bp . File , bp . Line )
return nil
}
2014-06-27 01:36:39 +00:00
func breakpoint ( p * proctl . DebuggedProcess , args ... string ) error {
2014-11-12 23:40:44 +00:00
if len ( args ) == 0 {
return fmt . Errorf ( "not enough arguments" )
}
2014-06-25 19:58:45 +00:00
var (
fn * gosym . Func
pc uint64
fname = args [ 0 ]
)
if strings . ContainsRune ( fname , ':' ) {
fl := strings . Split ( fname , ":" )
f , err := filepath . Abs ( fl [ 0 ] )
if err != nil {
return err
}
l , err := strconv . Atoi ( fl [ 1 ] )
if err != nil {
return err
}
pc , fn , err = p . GoSymTable . LineToPC ( f , l )
if err != nil {
return err
}
} else {
fn = p . GoSymTable . LookupFunc ( fname )
2014-08-07 21:13:11 +00:00
if fn == nil {
return fmt . Errorf ( "No function named %s" , fname )
}
2014-06-25 19:58:45 +00:00
2014-08-07 21:13:11 +00:00
pc = fn . Entry
2014-06-25 19:58:45 +00:00
}
bp , err := p . Break ( uintptr ( pc ) )
if err != nil {
return err
}
fmt . Printf ( "Breakpoint set at %#v for %s %s:%d\n" , bp . Addr , bp . FunctionName , bp . File , bp . Line )
2014-05-21 15:15:58 +00:00
return nil
}
2014-09-07 00:39:40 +00:00
2014-10-07 22:03:20 +00:00
func printVar ( p * proctl . DebuggedProcess , args ... string ) error {
2014-10-17 13:25:40 +00:00
if len ( args ) == 0 {
2014-11-12 23:40:44 +00:00
return fmt . Errorf ( "not enough arguments" )
2014-10-17 13:25:40 +00:00
}
2014-10-07 22:03:20 +00:00
val , err := p . EvalSymbol ( args [ 0 ] )
if err != nil {
return err
}
fmt . Println ( val . Value )
return nil
2014-12-28 08:02:37 +00:00
}
func info ( p * proctl . DebuggedProcess , args ... string ) error {
if len ( args ) == 0 {
return fmt . Errorf ( "not enough arguments" )
}
// Allow for optional regex
var filter * regexp . Regexp
if len ( args ) >= 2 {
var err error
if filter , err = regexp . Compile ( args [ 1 ] ) ; err != nil {
return fmt . Errorf ( "invalid filter argument: %s" , err . Error ( ) )
}
}
switch args [ 0 ] {
case "sources" :
files := make ( [ ] string , 0 , len ( p . GoSymTable . Files ) )
for f := range p . GoSymTable . Files {
if filter == nil || filter . Match ( [ ] byte ( f ) ) {
files = append ( files , f )
}
}
sort . Sort ( sort . StringSlice ( files ) )
for _ , f := range files {
fmt . Printf ( "%s\n" , f )
}
default :
return fmt . Errorf ( "unsupported info type, must be sources" )
}
return nil
2014-10-07 22:03:20 +00:00
}
2014-09-07 00:39:40 +00:00
func printcontext ( p * proctl . DebuggedProcess ) error {
var context [ ] string
regs , err := p . Registers ( )
if err != nil {
return err
}
2014-11-13 11:04:47 +00:00
f , l , fn := p . GoSymTable . PCToLine ( regs . PC ( ) )
2014-09-07 00:39:40 +00:00
2014-11-13 11:04:47 +00:00
if fn != nil {
2014-12-02 18:40:53 +00:00
fmt . Printf ( "current loc: %s %s:%d\n" , fn . Name , f , l )
2014-11-13 11:04:47 +00:00
file , err := os . Open ( f )
if err != nil {
2014-09-07 00:39:40 +00:00
return err
}
2014-11-13 11:04:47 +00:00
defer file . Close ( )
2014-09-07 00:39:40 +00:00
2014-11-13 11:04:47 +00:00
buf := bufio . NewReader ( file )
for i := 1 ; i < l - 5 ; i ++ {
_ , err := buf . ReadString ( '\n' )
if err != nil && err != io . EOF {
2014-10-17 19:33:40 +00:00
return err
2014-09-07 00:39:40 +00:00
}
2014-11-13 11:04:47 +00:00
}
for i := l - 5 ; i <= l + 5 ; i ++ {
line , err := buf . ReadString ( '\n' )
if err != nil {
if err != io . EOF {
return err
}
2014-10-17 19:33:40 +00:00
2014-11-13 11:04:47 +00:00
if err == io . EOF {
break
}
2014-10-17 19:33:40 +00:00
}
2014-12-02 03:13:08 +00:00
arrow := " "
2014-11-13 11:04:47 +00:00
if i == l {
2014-12-02 03:13:08 +00:00
arrow = "=>"
2014-11-13 11:04:47 +00:00
}
2014-10-17 19:33:40 +00:00
2014-12-02 03:13:08 +00:00
context = append ( context , fmt . Sprintf ( "\033[34m%s %d\033[0m: %s" , arrow , i , line ) )
2014-11-13 11:04:47 +00:00
}
} else {
fmt . Printf ( "Stopped at: 0x%x\n" , regs . PC ( ) )
context = append ( context , "\033[34m=>\033[0m no source available" )
2014-09-07 00:39:40 +00:00
}
2014-10-17 19:33:40 +00:00
fmt . Println ( strings . Join ( context , "" ) )
2014-09-07 00:39:40 +00:00
return nil
}