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-05-20 21:28:24 +00:00
"fmt"
2014-09-07 00:39:40 +00:00
"io"
"os"
2014-12-28 08:02:37 +00:00
"regexp"
"sort"
2015-03-13 19:47:04 +00:00
"strconv"
2014-06-25 19:58:45 +00:00
"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." } ,
2015-03-13 19:47:04 +00:00
command { aliases : [ ] string { "thread" , "t" } , cmdFn : thread , helpMsg : "Switch to the specified thread." } ,
2014-11-14 15:36:39 +00:00
command { aliases : [ ] string { "clear" } , cmdFn : clear , helpMsg : "Deletes breakpoint." } ,
command { aliases : [ ] string { "goroutines" } , cmdFn : goroutines , helpMsg : "Print out info for every goroutine." } ,
2015-03-06 14:01:41 +00:00
command { aliases : [ ] string { "breakpoints" , "bp" } , cmdFn : breakpoints , helpMsg : "Print out info for active breakpoints." } ,
2014-11-14 15:36:39 +00:00
command { aliases : [ ] string { "print" , "p" } , cmdFn : printVar , helpMsg : "Evaluate a variable." } ,
2015-02-13 17:15:40 +00:00
command { aliases : [ ] string { "info" } , cmdFn : info , helpMsg : "Provides info about args, funcs, locals, sources, or vars." } ,
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 ( )
}
}
2015-03-31 02:12:15 +00:00
func noCmdAvailable ( p * proctl . DebuggedProcess , args ... string ) error {
2015-05-04 22:31:13 +00:00
return fmt . Errorf ( "command not available" )
2014-05-20 21:28:24 +00:00
}
2015-03-31 02:12:15 +00:00
func nullCommand ( p * proctl . DebuggedProcess , args ... string ) error {
2014-06-25 19:58:45 +00:00
return nil
}
2015-03-31 02:12:15 +00:00
func ( c * Commands ) help ( p * proctl . DebuggedProcess , args ... string ) error {
2014-11-14 05:51:32 +00:00
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
2015-03-31 02:12:15 +00:00
func threads ( p * proctl . DebuggedProcess , args ... string ) error {
2015-03-11 23:08:20 +00:00
for _ , th := range p . Threads {
prefix := " "
if th == p . CurrentThread {
prefix = "* "
}
2015-04-23 15:40:33 +00:00
pc , err := th . PC ( )
2015-03-11 23:08:20 +00:00
if err != nil {
return err
}
2015-04-03 16:10:35 +00:00
f , l , fn := th . Process . PCToLine ( pc )
2015-03-11 23:08:20 +00:00
if fn != nil {
fmt . Printf ( "%sThread %d at %#v %s:%d %s\n" , prefix , th . Id , pc , f , l , fn . Name )
} else {
fmt . Printf ( "%sThread %d at %#v\n" , prefix , th . Id , pc )
}
}
return nil
2014-11-08 05:55:25 +00:00
}
2015-03-31 02:12:15 +00:00
func thread ( p * proctl . DebuggedProcess , args ... string ) error {
if len ( args ) == 0 {
2015-05-04 22:31:13 +00:00
return fmt . Errorf ( "you must specify a thread" )
2015-03-26 18:15:35 +00:00
}
2015-03-13 19:47:04 +00:00
oldTid := p . CurrentThread . Id
2015-03-31 02:12:15 +00:00
tid , err := strconv . Atoi ( args [ 0 ] )
2015-03-13 19:47:04 +00:00
if err != nil {
return err
}
err = p . SwitchThread ( tid )
if err != nil {
return err
}
fmt . Printf ( "Switched from %d to %d\n" , oldTid , tid )
return nil
}
2015-03-31 02:12:15 +00:00
func goroutines ( p * proctl . DebuggedProcess , args ... string ) error {
2015-04-09 14:53:02 +00:00
var fname string
gs , err := p . GoroutinesInfo ( )
if err != nil {
return err
}
fmt . Printf ( "[%d goroutines]\n" , len ( gs ) )
for _ , g := range gs {
if g . Func != nil {
fname = g . Func . Name
}
fmt . Printf ( "Goroutine %d - %s:%d %s\n" , g . Id , g . File , g . Line , fname )
}
return nil
2014-11-08 14:02:31 +00:00
}
2015-03-31 02:12:15 +00:00
func cont ( p * proctl . DebuggedProcess , args ... 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 {
2015-05-04 22:31:13 +00:00
return fmt . Errorf ( "not enough arguments" )
2014-11-12 23:40:44 +00:00
}
2015-01-01 13:23:55 +00:00
bp , err := p . ClearByLocation ( args [ 0 ] )
2014-06-25 19:58:45 +00:00
if err != nil {
return err
}
2015-01-01 13:23:55 +00:00
fmt . Printf ( "Breakpoint %d cleared at %#v for %s %s:%d\n" , bp . ID , bp . Addr , bp . FunctionName , bp . File , bp . Line )
2014-06-25 19:58:45 +00:00
return nil
}
2015-03-06 14:01:41 +00:00
type ById [ ] * proctl . BreakPoint
func ( a ById ) Len ( ) int { return len ( a ) }
func ( a ById ) Swap ( i , j int ) { a [ i ] , a [ j ] = a [ j ] , a [ i ] }
func ( a ById ) Less ( i , j int ) bool { return a [ i ] . ID < a [ j ] . ID }
func breakpoints ( p * proctl . DebuggedProcess , args ... string ) error {
bps := make ( [ ] * proctl . BreakPoint , 0 , len ( p . BreakPoints ) + 4 )
for _ , bp := range p . HWBreakPoints {
if bp == nil {
continue
}
bps = append ( bps , bp )
}
for _ , bp := range p . BreakPoints {
if bp . Temp {
continue
}
bps = append ( bps , bp )
}
sort . Sort ( ById ( bps ) )
for _ , bp := range bps {
fmt . Println ( bp )
}
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 {
2015-05-04 22:31:13 +00:00
return fmt . Errorf ( "not enough arguments" )
2014-11-12 23:40:44 +00:00
}
2015-01-01 13:23:55 +00:00
bp , err := p . BreakByLocation ( args [ 0 ] )
2014-06-25 19:58:45 +00:00
if err != nil {
return err
}
2015-01-01 13:23:55 +00:00
fmt . Printf ( "Breakpoint %d set at %#v for %s %s:%d\n" , bp . ID , bp . Addr , bp . FunctionName , bp . File , bp . Line )
2014-06-25 19:58:45 +00:00
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 {
2015-05-04 22:31:13 +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
}
2014-12-30 17:57:31 +00:00
func filterVariables ( vars [ ] * proctl . Variable , filter * regexp . Regexp ) [ ] string {
data := make ( [ ] string , 0 , len ( vars ) )
for _ , v := range vars {
if v == nil {
continue
}
if filter == nil || filter . Match ( [ ] byte ( v . Name ) ) {
data = append ( data , fmt . Sprintf ( "%s = %s" , v . Name , v . Value ) )
}
}
return data
}
2014-12-28 08:02:37 +00:00
func info ( p * proctl . DebuggedProcess , args ... string ) error {
if len ( args ) == 0 {
2015-05-04 22:31:13 +00:00
return fmt . Errorf ( "not enough arguments. expected info type [regex]." )
2014-12-28 08:02:37 +00:00
}
// 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 ( ) )
}
}
2014-12-28 08:14:17 +00:00
var data [ ] string
2014-12-28 08:02:37 +00:00
switch args [ 0 ] {
case "sources" :
2015-04-03 16:10:35 +00:00
data = make ( [ ] string , 0 , len ( p . Sources ( ) ) )
for f := range p . Sources ( ) {
2014-12-28 08:02:37 +00:00
if filter == nil || filter . Match ( [ ] byte ( f ) ) {
2014-12-28 08:14:17 +00:00
data = append ( data , f )
2014-12-28 08:02:37 +00:00
}
}
2014-12-29 05:06:04 +00:00
case "funcs" :
2015-04-03 16:10:35 +00:00
data = make ( [ ] string , 0 , len ( p . Funcs ( ) ) )
for _ , f := range p . Funcs ( ) {
2014-12-28 08:14:17 +00:00
if f . Sym != nil && ( filter == nil || filter . Match ( [ ] byte ( f . Name ) ) ) {
data = append ( data , f . Name )
}
2014-12-28 08:02:37 +00:00
}
2014-12-30 17:57:31 +00:00
case "args" :
vars , err := p . CurrentThread . FunctionArguments ( )
if err != nil {
return nil
}
data = filterVariables ( vars , filter )
case "locals" :
vars , err := p . CurrentThread . LocalVariables ( )
if err != nil {
return nil
}
data = filterVariables ( vars , filter )
2015-01-21 05:47:59 +00:00
case "vars" :
vars , err := p . CurrentThread . PackageVariables ( )
if err != nil {
return nil
}
data = filterVariables ( vars , filter )
2014-12-28 08:02:37 +00:00
default :
2015-05-04 22:31:13 +00:00
return fmt . Errorf ( "unsupported info type, must be args, funcs, locals, sources, or vars" )
2014-12-28 08:14:17 +00:00
}
// sort and output data
sort . Sort ( sort . StringSlice ( data ) )
for _ , d := range data {
2014-12-29 03:14:21 +00:00
fmt . Println ( d )
2014-12-28 08:02:37 +00:00
}
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
}
2015-04-03 16:10:35 +00:00
f , l , fn := p . 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
}