2015-10-28 02:38:57 +00:00
// Package terminal implements functions for responding to user
2015-03-20 21:11:11 +00:00
// input and dispatching to appropriate backend commands.
package terminal
import (
"bufio"
2016-02-06 06:00:48 +00:00
"errors"
2015-03-20 21:11:11 +00:00
"fmt"
2015-11-02 06:30:12 +00:00
"go/parser"
"go/scanner"
2015-10-16 06:42:02 +00:00
"io"
2015-09-17 08:42:34 +00:00
"math"
2015-03-20 21:11:11 +00:00
"os"
"regexp"
"sort"
"strconv"
"strings"
2015-07-29 22:49:23 +00:00
"text/tabwriter"
2015-03-20 21:11:11 +00:00
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api"
2015-07-29 14:24:52 +00:00
"github.com/derekparker/delve/service/debugger"
2015-03-20 21:11:11 +00:00
)
2016-02-23 14:12:04 +00:00
type cmdPrefix int
2015-08-28 20:06:29 +00:00
2016-02-23 14:12:04 +00:00
const (
noPrefix = cmdPrefix ( 0 )
scopePrefix = cmdPrefix ( 1 << iota )
onPrefix
)
type callContext struct {
Prefix cmdPrefix
Scope api . EvalScope
Breakpoint * api . Breakpoint
}
type cmdfunc func ( t * Term , ctx callContext , args string ) error
type filteringFunc func ( t * Term , ctx callContext , args string ) ( [ ] string , error )
2015-03-20 21:11:11 +00:00
type command struct {
2016-02-23 14:12:04 +00:00
aliases [ ] string
allowedPrefixes cmdPrefix
helpMsg string
cmdFn cmdfunc
2015-03-20 21:11:11 +00:00
}
// 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
}
2016-01-10 08:57:52 +00:00
// Commands represents the commands for Delve terminal process.
2015-03-20 21:11:11 +00:00
type Commands struct {
2015-10-04 17:58:32 +00:00
cmds [ ] command
lastCmd cmdfunc
client service . Client
2015-03-20 21:11:11 +00:00
}
2016-01-10 08:57:52 +00:00
// DebugCommands returns a Commands struct with default commands defined.
2015-03-20 21:11:11 +00:00
func DebugCommands ( client service . Client ) * Commands {
c := & Commands { client : client }
c . cmds = [ ] command {
2016-03-23 16:32:11 +00:00
{ aliases : [ ] string { "help" , "h" } , cmdFn : c . help , helpMsg : "Prints the help message." } ,
2016-01-24 08:16:45 +00:00
{ aliases : [ ] string { "break" , "b" } , cmdFn : breakpoint , helpMsg : "break [name] <linespec>" } ,
2015-07-29 22:49:23 +00:00
{ aliases : [ ] string { "trace" , "t" } , cmdFn : tracepoint , helpMsg : "Set tracepoint, takes the same arguments as break." } ,
2015-07-03 20:35:22 +00:00
{ aliases : [ ] string { "restart" , "r" } , cmdFn : restart , helpMsg : "Restart process." } ,
2015-03-20 21:11:11 +00:00
{ aliases : [ ] string { "continue" , "c" } , cmdFn : cont , helpMsg : "Run until breakpoint or program termination." } ,
2016-01-24 05:25:46 +00:00
{ aliases : [ ] string { "step" , "s" } , cmdFn : step , helpMsg : "Single step through program." } ,
{ aliases : [ ] string { "step-instruction" , "si" } , cmdFn : stepInstruction , helpMsg : "Single step a single cpu instruction." } ,
2015-03-20 21:11:11 +00:00
{ aliases : [ ] string { "next" , "n" } , cmdFn : next , helpMsg : "Step over to next source line." } ,
{ aliases : [ ] string { "threads" } , cmdFn : threads , helpMsg : "Print out info for every traced thread." } ,
2015-07-29 13:20:33 +00:00
{ aliases : [ ] string { "thread" , "tr" } , cmdFn : thread , helpMsg : "Switch to the specified thread." } ,
2015-03-20 21:11:11 +00:00
{ aliases : [ ] string { "clear" } , cmdFn : clear , helpMsg : "Deletes breakpoint." } ,
2015-10-04 17:59:53 +00:00
{ aliases : [ ] string { "clearall" } , cmdFn : clearAll , helpMsg : "clearall [<linespec>]. Deletes all breakpoints. If <linespec> is provided, only matching breakpoints will be deleted." } ,
2015-10-18 22:02:14 +00:00
{ aliases : [ ] string { "goroutines" } , cmdFn : goroutines , helpMsg : "goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)] Print out info for every goroutine." } ,
2016-02-23 14:12:04 +00:00
{ aliases : [ ] string { "goroutine" } , allowedPrefixes : onPrefix | scopePrefix , cmdFn : c . goroutine , helpMsg : "Sets current goroutine." } ,
2015-03-20 21:11:11 +00:00
{ aliases : [ ] string { "breakpoints" , "bp" } , cmdFn : breakpoints , helpMsg : "Print out info for active breakpoints." } ,
2016-02-23 14:12:04 +00:00
{ aliases : [ ] string { "print" , "p" } , allowedPrefixes : onPrefix | scopePrefix , cmdFn : printVar , helpMsg : "Evaluate a variable." } ,
{ aliases : [ ] string { "set" } , allowedPrefixes : scopePrefix , cmdFn : setVar , helpMsg : "Changes the value of a variable." } ,
2015-08-11 02:31:27 +00:00
{ aliases : [ ] string { "sources" } , cmdFn : filterSortAndOutput ( sources ) , helpMsg : "Print list of source files, optionally filtered by a regexp." } ,
{ aliases : [ ] string { "funcs" } , cmdFn : filterSortAndOutput ( funcs ) , helpMsg : "Print list of functions, optionally filtered by a regexp." } ,
2016-02-22 15:08:45 +00:00
{ aliases : [ ] string { "types" } , cmdFn : filterSortAndOutput ( types ) , helpMsg : "Print list of types, optionally filtered by a regexp." } ,
2016-02-23 14:12:04 +00:00
{ aliases : [ ] string { "args" } , allowedPrefixes : scopePrefix , cmdFn : filterSortAndOutput ( args ) , helpMsg : "Print function arguments, optionally filtered by a regexp." } ,
{ aliases : [ ] string { "locals" } , allowedPrefixes : scopePrefix , cmdFn : filterSortAndOutput ( locals ) , helpMsg : "Print function locals, optionally filtered by a regexp." } ,
2015-08-11 02:31:27 +00:00
{ aliases : [ ] string { "vars" } , cmdFn : filterSortAndOutput ( vars ) , helpMsg : "Print package variables, optionally filtered by a regexp." } ,
{ aliases : [ ] string { "regs" } , cmdFn : regs , helpMsg : "Print contents of CPU registers." } ,
2015-07-29 23:19:06 +00:00
{ aliases : [ ] string { "exit" , "quit" , "q" } , cmdFn : exitCommand , helpMsg : "Exit the debugger." } ,
2016-02-23 14:12:04 +00:00
{ aliases : [ ] string { "list" , "ls" } , allowedPrefixes : scopePrefix , cmdFn : listCommand , helpMsg : "list <linespec>. Show source around current point or provided linespec." } ,
{ aliases : [ ] string { "stack" , "bt" } , allowedPrefixes : scopePrefix | onPrefix , cmdFn : stackCommand , helpMsg : "stack [<depth>] [-full]. Prints stack." } ,
{ aliases : [ ] string { "frame" } , allowedPrefixes : scopePrefix , cmdFn : c . frame , helpMsg : "Sets current stack frame (0 is the top of the stack)" } ,
2015-09-29 16:40:12 +00:00
{ aliases : [ ] string { "source" } , cmdFn : c . sourceCommand , helpMsg : "Executes a file containing a list of delve commands" } ,
2016-02-23 14:12:04 +00:00
{ aliases : [ ] string { "disassemble" , "disass" } , allowedPrefixes : scopePrefix , cmdFn : disassCommand , helpMsg : "Displays disassembly of specific function or address range: disassemble [-a <start> <end>] [-l <locspec>]" } ,
{ aliases : [ ] string { "on" } , cmdFn : c . onCmd , helpMsg : "on <breakpoint name or id> <command>. Executes command when the specified breakpoint is hit (supported commands: print <expression>, stack [<depth>] [-full] and goroutine)" } ,
2016-01-24 08:16:45 +00:00
{ aliases : [ ] string { "condition" , "cond" } , cmdFn : conditionCmd , helpMsg : "cond <breakpoint name or id> <boolean expression>. Specifies that the breakpoint or tracepoint should break only if the boolean expression is true." } ,
2015-03-20 21:11:11 +00:00
}
return c
}
// Register custom commands. Expects cf to be a func of type cmdfunc,
// returning only an error.
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 } )
}
// Find will look up the command function for the given command input.
2015-07-16 18:07:34 +00:00
// If it cannot find the command it will default to noCmdAvailable().
2015-03-20 21:11:11 +00:00
// If the command is an empty string it will replay the last command.
2016-02-23 14:12:04 +00:00
func ( c * Commands ) Find ( cmdstr string , prefix cmdPrefix ) cmdfunc {
2015-03-20 21:11:11 +00:00
// If <enter> use last command, if there was one.
if cmdstr == "" {
if c . lastCmd != nil {
return c . lastCmd
}
return nullCommand
}
for _ , v := range c . cmds {
if v . match ( cmdstr ) {
2016-02-23 14:12:04 +00:00
if prefix != noPrefix && v . allowedPrefixes & prefix == 0 {
continue
}
2015-03-20 21:11:11 +00:00
c . lastCmd = v . cmdFn
return v . cmdFn
}
}
return noCmdAvailable
}
2016-02-23 14:12:04 +00:00
func ( c * Commands ) CallWithContext ( cmdstr , args string , t * Term , ctx callContext ) error {
return c . Find ( cmdstr , ctx . Prefix ) ( t , ctx , args )
}
func ( c * Commands ) Call ( cmdstr , args string , t * Term ) error {
ctx := callContext { Prefix : noPrefix , Scope : api . EvalScope { GoroutineID : - 1 , Frame : 0 } }
return c . CallWithContext ( cmdstr , args , t , ctx )
}
2015-08-17 03:41:25 +00:00
// Merge takes aliases defined in the config struct and merges them with the default aliases.
func ( c * Commands ) Merge ( allAliases map [ string ] [ ] string ) {
for i := range c . cmds {
if aliases , ok := allAliases [ c . cmds [ i ] . aliases [ 0 ] ] ; ok {
c . cmds [ i ] . aliases = append ( c . cmds [ i ] . aliases , aliases ... )
}
}
}
2016-02-23 14:12:04 +00:00
func noCmdAvailable ( t * Term , ctx callContext , args string ) error {
2015-05-04 22:31:13 +00:00
return fmt . Errorf ( "command not available" )
2015-03-20 21:11:11 +00:00
}
2016-02-23 14:12:04 +00:00
func nullCommand ( t * Term , ctx callContext , args string ) error {
2015-03-20 21:11:11 +00:00
return nil
}
2016-02-23 14:12:04 +00:00
func ( c * Commands ) help ( t * Term , ctx callContext , args string ) error {
2015-03-20 21:11:11 +00:00
fmt . Println ( "The following commands are available:" )
2015-07-29 22:49:23 +00:00
w := new ( tabwriter . Writer )
w . Init ( os . Stdout , 0 , 8 , 0 , '-' , 0 )
2015-03-20 21:11:11 +00:00
for _ , cmd := range c . cmds {
2015-07-29 22:49:23 +00:00
if len ( cmd . aliases ) > 1 {
fmt . Fprintf ( w , " %s (alias: %s) \t %s\n" , cmd . aliases [ 0 ] , strings . Join ( cmd . aliases [ 1 : ] , " | " ) , cmd . helpMsg )
} else {
fmt . Fprintf ( w , " %s \t %s\n" , cmd . aliases [ 0 ] , cmd . helpMsg )
}
2015-03-20 21:11:11 +00:00
}
2015-07-29 22:49:23 +00:00
return w . Flush ( )
2015-03-20 21:11:11 +00:00
}
2015-09-10 18:12:42 +00:00
type byThreadID [ ] * api . Thread
2015-09-10 18:04:59 +00:00
2015-09-10 18:12:42 +00:00
func ( a byThreadID ) Len ( ) int { return len ( a ) }
func ( a byThreadID ) Swap ( i , j int ) { a [ i ] , a [ j ] = a [ j ] , a [ i ] }
func ( a byThreadID ) Less ( i , j int ) bool { return a [ i ] . ID < a [ j ] . ID }
2015-09-10 18:04:59 +00:00
2016-02-23 14:12:04 +00:00
func threads ( t * Term , ctx callContext , args string ) error {
2015-09-30 05:42:06 +00:00
threads , err := t . client . ListThreads ( )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
2015-09-30 05:42:06 +00:00
state , err := t . client . GetState ( )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
2015-09-10 18:12:42 +00:00
sort . Sort ( byThreadID ( threads ) )
2015-03-20 21:11:11 +00:00
for _ , th := range threads {
prefix := " "
if state . CurrentThread != nil && state . CurrentThread . ID == th . ID {
prefix = "* "
}
if th . Function != nil {
fmt . Printf ( "%sThread %d at %#v %s:%d %s\n" ,
2015-12-28 02:58:00 +00:00
prefix , th . ID , th . PC , ShortenFilePath ( th . File ) ,
2015-03-20 21:11:11 +00:00
th . Line , th . Function . Name )
} else {
2015-08-28 20:06:29 +00:00
fmt . Printf ( "%sThread %s\n" , prefix , formatThread ( th ) )
2015-03-20 21:11:11 +00:00
}
}
return nil
}
2016-02-23 14:12:04 +00:00
func thread ( t * Term , ctx callContext , args string ) error {
2015-05-29 17:29:02 +00:00
if len ( args ) == 0 {
return fmt . Errorf ( "you must specify a thread" )
}
2015-11-02 06:30:12 +00:00
tid , err := strconv . Atoi ( args )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
2015-09-30 05:42:06 +00:00
oldState , err := t . client . GetState ( )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
2015-09-30 05:42:06 +00:00
newState , err := t . client . SwitchThread ( tid )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
oldThread := "<none>"
newThread := "<none>"
if oldState . CurrentThread != nil {
oldThread = strconv . Itoa ( oldState . CurrentThread . ID )
}
if newState . CurrentThread != nil {
newThread = strconv . Itoa ( newState . CurrentThread . ID )
}
fmt . Printf ( "Switched from %s to %s\n" , oldThread , newThread )
return nil
}
2015-09-10 18:12:42 +00:00
type byGoroutineID [ ] * api . Goroutine
func ( a byGoroutineID ) Len ( ) int { return len ( a ) }
func ( a byGoroutineID ) Swap ( i , j int ) { a [ i ] , a [ j ] = a [ j ] , a [ i ] }
func ( a byGoroutineID ) Less ( i , j int ) bool { return a [ i ] . ID < a [ j ] . ID }
2016-02-23 14:12:04 +00:00
func goroutines ( t * Term , ctx callContext , argstr string ) error {
2015-11-02 06:30:12 +00:00
args := strings . Split ( argstr , " " )
2015-10-16 06:42:02 +00:00
var fgl = fglUserCurrent
switch len ( args ) {
case 0 :
// nothing to do
case 1 :
switch args [ 0 ] {
case "-u" :
fgl = fglUserCurrent
case "-r" :
fgl = fglRuntimeCurrent
case "-g" :
fgl = fglGo
2016-01-19 14:24:01 +00:00
case "" :
// nothing to do
2015-10-16 06:42:02 +00:00
default :
2016-01-10 08:57:52 +00:00
return fmt . Errorf ( "wrong argument: '%s'" , args [ 0 ] )
2015-10-16 06:42:02 +00:00
}
default :
return fmt . Errorf ( "too many arguments" )
}
2015-09-30 05:42:06 +00:00
state , err := t . client . GetState ( )
2015-08-28 20:06:29 +00:00
if err != nil {
return err
}
2015-09-30 05:42:06 +00:00
gs , err := t . client . ListGoroutines ( )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
2015-09-10 18:12:42 +00:00
sort . Sort ( byGoroutineID ( gs ) )
2015-03-20 21:11:11 +00:00
fmt . Printf ( "[%d goroutines]\n" , len ( gs ) )
for _ , g := range gs {
2015-08-28 20:06:29 +00:00
prefix := " "
2015-10-03 12:29:46 +00:00
if state . SelectedGoroutine != nil && g . ID == state . SelectedGoroutine . ID {
2015-08-28 20:06:29 +00:00
prefix = "* "
}
2015-10-16 06:42:02 +00:00
fmt . Printf ( "%sGoroutine %s\n" , prefix , formatGoroutine ( g , fgl ) )
2015-08-28 20:06:29 +00:00
}
return nil
}
2016-02-23 14:12:04 +00:00
func ( c * Commands ) goroutine ( t * Term , ctx callContext , argstr string ) error {
args := strings . SplitN ( argstr , " " , 3 )
2015-08-28 20:06:29 +00:00
2016-02-23 14:12:04 +00:00
if ctx . Prefix == onPrefix {
if len ( args ) != 1 || args [ 0 ] != "" {
return errors . New ( "too many arguments to goroutine" )
2015-08-28 20:06:29 +00:00
}
2016-02-23 14:12:04 +00:00
ctx . Breakpoint . Goroutine = true
2015-08-28 20:06:29 +00:00
return nil
}
2016-02-23 14:12:04 +00:00
switch len ( args ) {
case 1 :
if ctx . Prefix == scopePrefix {
return errors . New ( "no command passed to goroutine" )
2015-11-02 06:30:12 +00:00
}
2016-02-23 14:12:04 +00:00
if args [ 0 ] == "" {
return printscope ( t )
2016-03-09 15:11:58 +00:00
}
gid , err := strconv . Atoi ( argstr )
if err != nil {
return err
}
2016-02-23 14:12:04 +00:00
2016-03-09 15:11:58 +00:00
oldState , err := t . client . GetState ( )
if err != nil {
return err
}
newState , err := t . client . SwitchGoroutine ( gid )
if err != nil {
return err
2015-08-28 20:06:29 +00:00
}
2016-03-09 15:11:58 +00:00
fmt . Printf ( "Switched from %d to %d (thread %d)\n" , oldState . SelectedGoroutine . ID , gid , newState . CurrentThread . ID )
return nil
2016-02-23 14:12:04 +00:00
case 2 :
args = append ( args , "" )
2015-08-28 20:06:29 +00:00
}
2016-02-23 14:12:04 +00:00
var err error
ctx . Prefix = scopePrefix
ctx . Scope . GoroutineID , err = strconv . Atoi ( args [ 0 ] )
if err != nil {
return err
}
return c . CallWithContext ( args [ 1 ] , args [ 2 ] , t , ctx )
}
func ( c * Commands ) frame ( t * Term , ctx callContext , args string ) error {
v := strings . SplitN ( args , " " , 3 )
switch len ( v ) {
case 0 , 1 :
return errors . New ( "not enough arguments" )
case 2 :
v = append ( v , "" )
}
2016-03-09 15:11:58 +00:00
var err error
2016-02-23 14:12:04 +00:00
ctx . Prefix = scopePrefix
ctx . Scope . Frame , err = strconv . Atoi ( v [ 0 ] )
if err != nil {
return err
}
return c . CallWithContext ( v [ 1 ] , v [ 2 ] , t , ctx )
2015-08-28 20:06:29 +00:00
}
2015-09-30 05:42:06 +00:00
func printscope ( t * Term ) error {
state , err := t . client . GetState ( )
2015-08-28 20:06:29 +00:00
if err != nil {
return err
2015-03-20 21:11:11 +00:00
}
2015-08-28 20:06:29 +00:00
2015-10-16 06:42:02 +00:00
fmt . Printf ( "Thread %s\n" , formatThread ( state . CurrentThread ) )
if state . SelectedGoroutine != nil {
writeGoroutineLong ( os . Stdout , state . SelectedGoroutine , "" )
}
2015-03-20 21:11:11 +00:00
return nil
}
2015-08-28 20:06:29 +00:00
func formatThread ( th * api . Thread ) string {
if th == nil {
return "<nil>"
}
2015-12-28 02:58:00 +00:00
return fmt . Sprintf ( "%d at %s:%d" , th . ID , ShortenFilePath ( th . File ) , th . Line )
2015-08-28 20:06:29 +00:00
}
2015-10-16 06:42:02 +00:00
type formatGoroutineLoc int
const (
fglRuntimeCurrent = formatGoroutineLoc ( iota )
fglUserCurrent
fglGo
)
func formatLocation ( loc api . Location ) string {
fname := ""
if loc . Function != nil {
fname = loc . Function . Name
}
2015-12-28 02:58:00 +00:00
return fmt . Sprintf ( "%s:%d %s (%#v)" , ShortenFilePath ( loc . File ) , loc . Line , fname , loc . PC )
2015-10-16 06:42:02 +00:00
}
func formatGoroutine ( g * api . Goroutine , fgl formatGoroutineLoc ) string {
2015-08-28 20:06:29 +00:00
if g == nil {
return "<nil>"
}
2015-10-16 06:42:02 +00:00
var locname string
var loc api . Location
switch fgl {
case fglRuntimeCurrent :
locname = "Runtime"
2015-10-18 22:02:14 +00:00
loc = g . CurrentLoc
2015-10-16 06:42:02 +00:00
case fglUserCurrent :
locname = "User"
2015-10-18 22:02:14 +00:00
loc = g . UserCurrentLoc
2015-10-16 06:42:02 +00:00
case fglGo :
locname = "Go"
2015-10-18 22:02:14 +00:00
loc = g . GoStatementLoc
2015-06-28 15:00:56 +00:00
}
2015-10-16 06:42:02 +00:00
return fmt . Sprintf ( "%d - %s: %s" , g . ID , locname , formatLocation ( loc ) )
}
func writeGoroutineLong ( w io . Writer , g * api . Goroutine , prefix string ) {
fmt . Fprintf ( w , "%sGoroutine %d:\n%s\tRuntime: %s\n%s\tUser: %s\n%s\tGo: %s\n" ,
prefix , g . ID ,
2015-10-18 22:02:14 +00:00
prefix , formatLocation ( g . CurrentLoc ) ,
prefix , formatLocation ( g . UserCurrentLoc ) ,
prefix , formatLocation ( g . GoStatementLoc ) )
2015-06-28 15:00:56 +00:00
}
2016-02-23 14:12:04 +00:00
func restart ( t * Term , ctx callContext , args string ) error {
2015-09-30 05:42:06 +00:00
if err := t . client . Restart ( ) ; err != nil {
2015-07-03 20:35:22 +00:00
return err
}
2015-09-30 05:42:06 +00:00
fmt . Println ( "Process restarted with PID" , t . client . ProcessPid ( ) )
2015-07-03 20:35:22 +00:00
return nil
}
2016-02-23 14:12:04 +00:00
func cont ( t * Term , ctx callContext , args string ) error {
2015-09-30 05:42:06 +00:00
stateChan := t . client . Continue ( )
2015-07-01 03:16:52 +00:00
for state := range stateChan {
2015-06-28 15:00:56 +00:00
if state . Err != nil {
return state . Err
}
2015-09-30 05:42:06 +00:00
printcontext ( t , state )
2015-03-20 21:11:11 +00:00
}
return nil
}
2016-02-23 14:12:04 +00:00
func step ( t * Term , ctx callContext , args string ) error {
2015-09-30 05:42:06 +00:00
state , err := t . client . Step ( )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
2015-09-30 05:42:06 +00:00
printcontext ( t , state )
2015-03-20 21:11:11 +00:00
return nil
}
2016-02-23 14:12:04 +00:00
func stepInstruction ( t * Term , ctx callContext , args string ) error {
2016-01-24 05:25:46 +00:00
state , err := t . client . StepInstruction ( )
if err != nil {
return err
}
printcontext ( t , state )
return nil
}
2016-02-23 14:12:04 +00:00
func next ( t * Term , ctx callContext , args string ) error {
2015-09-30 05:42:06 +00:00
state , err := t . client . Next ( )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
2015-09-30 05:42:06 +00:00
printcontext ( t , state )
2015-03-20 21:11:11 +00:00
return nil
}
2016-02-23 14:12:04 +00:00
func clear ( t * Term , ctx callContext , args string ) error {
2015-03-20 21:11:11 +00:00
if len ( args ) == 0 {
2015-05-04 22:31:13 +00:00
return fmt . Errorf ( "not enough arguments" )
2015-03-20 21:11:11 +00:00
}
2015-11-02 06:30:12 +00:00
id , err := strconv . Atoi ( args )
2016-01-24 08:16:45 +00:00
var bp * api . Breakpoint
if err == nil {
bp , err = t . client . ClearBreakpoint ( id )
} else {
bp , err = t . client . ClearBreakpointByName ( args )
2015-03-20 21:11:11 +00:00
}
if err != nil {
return err
}
2016-01-24 08:16:45 +00:00
fmt . Printf ( "%s cleared at %s\n" , formatBreakpointName ( bp , true ) , formatBreakpointLocation ( bp ) )
2015-03-20 21:11:11 +00:00
return nil
}
2016-02-23 14:12:04 +00:00
func clearAll ( t * Term , ctx callContext , args string ) error {
2015-09-30 05:42:06 +00:00
breakPoints , err := t . client . ListBreakpoints ( )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
2015-10-02 13:43:21 +00:00
var locPCs map [ uint64 ] struct { }
2015-11-02 06:30:12 +00:00
if args != "" {
2016-01-10 08:57:52 +00:00
locs , err := t . client . FindLocation ( api . EvalScope { GoroutineID : - 1 , Frame : 0 } , args )
2015-10-02 13:43:21 +00:00
if err != nil {
return err
}
locPCs = make ( map [ uint64 ] struct { } )
for _ , loc := range locs {
locPCs [ loc . PC ] = struct { } { }
}
}
2015-03-20 21:11:11 +00:00
for _ , bp := range breakPoints {
2015-10-02 13:43:21 +00:00
if locPCs != nil {
if _ , ok := locPCs [ bp . Addr ] ; ! ok {
continue
}
}
2016-03-06 17:54:43 +00:00
if bp . ID < 0 {
continue
}
2015-09-30 05:42:06 +00:00
_ , err := t . client . ClearBreakpoint ( bp . ID )
2015-03-20 21:11:11 +00:00
if err != nil {
2016-01-24 08:16:45 +00:00
fmt . Printf ( "Couldn't delete %s at %s: %s\n" , formatBreakpointName ( bp , false ) , formatBreakpointLocation ( bp ) , err )
2015-03-20 21:11:11 +00:00
}
2016-01-24 08:16:45 +00:00
fmt . Printf ( "%s cleared at %s\n" , formatBreakpointName ( bp , true ) , formatBreakpointLocation ( bp ) )
2015-03-20 21:11:11 +00:00
}
return nil
}
2016-01-10 08:57:52 +00:00
// ByID sorts breakpoints by ID.
type ByID [ ] * api . Breakpoint
2015-03-20 21:11:11 +00:00
2016-01-10 08:57:52 +00:00
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 }
2015-03-20 21:11:11 +00:00
2016-02-23 14:12:04 +00:00
func breakpoints ( t * Term , ctx callContext , args string ) error {
2015-09-30 05:42:06 +00:00
breakPoints , err := t . client . ListBreakpoints ( )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
2016-01-10 08:57:52 +00:00
sort . Sort ( ByID ( breakPoints ) )
2015-03-20 21:11:11 +00:00
for _ , bp := range breakPoints {
2016-01-24 08:16:45 +00:00
fmt . Printf ( "%s at %v (%d)\n" , formatBreakpointName ( bp , true ) , formatBreakpointLocation ( bp ) , bp . TotalHitCount )
2015-06-28 15:00:56 +00:00
var attrs [ ] string
2016-01-24 08:16:45 +00:00
if bp . Cond != "" {
attrs = append ( attrs , fmt . Sprintf ( "\tcond %s" , bp . Cond ) )
}
2015-06-28 15:00:56 +00:00
if bp . Stacktrace > 0 {
2016-01-24 08:16:45 +00:00
attrs = append ( attrs , fmt . Sprintf ( "\tstack %d" , bp . Stacktrace ) )
2015-06-28 15:00:56 +00:00
}
if bp . Goroutine {
2016-01-24 08:16:45 +00:00
attrs = append ( attrs , "\tgoroutine" )
2015-06-28 15:00:56 +00:00
}
2015-07-01 03:16:52 +00:00
for i := range bp . Variables {
2016-01-24 08:16:45 +00:00
attrs = append ( attrs , fmt . Sprintf ( "\tprint %s" , bp . Variables [ i ] ) )
2015-06-28 15:00:56 +00:00
}
if len ( attrs ) > 0 {
2016-01-24 08:16:45 +00:00
fmt . Printf ( "%s\n" , strings . Join ( attrs , "\n" ) )
2015-06-28 15:00:56 +00:00
}
2015-03-20 21:11:11 +00:00
}
return nil
}
2015-11-02 06:30:12 +00:00
func setBreakpoint ( t * Term , tracepoint bool , argstr string ) error {
2016-01-24 08:16:45 +00:00
args := strings . SplitN ( argstr , " " , 2 )
2015-03-20 21:11:11 +00:00
2016-01-24 08:16:45 +00:00
requestedBp := & api . Breakpoint { }
locspec := ""
switch len ( args ) {
case 1 :
locspec = argstr
case 2 :
if api . ValidBreakpointName ( args [ 0 ] ) == nil {
requestedBp . Name = args [ 0 ]
locspec = args [ 1 ]
} else {
locspec = argstr
2015-06-28 15:00:56 +00:00
}
2016-01-24 08:16:45 +00:00
default :
return fmt . Errorf ( "address required" )
2015-06-28 15:00:56 +00:00
}
requestedBp . Tracepoint = tracepoint
2016-01-24 08:16:45 +00:00
locs , err := t . client . FindLocation ( api . EvalScope { GoroutineID : - 1 , Frame : 0 } , locspec )
2015-03-20 21:11:11 +00:00
if err != nil {
2016-01-24 08:16:45 +00:00
if requestedBp . Name == "" {
return err
}
requestedBp . Name = ""
locspec = argstr
var err2 error
2016-03-09 15:11:58 +00:00
locs , err2 = t . client . FindLocation ( api . EvalScope { GoroutineID : - 1 , Frame : 0 } , locspec )
2016-01-24 08:16:45 +00:00
if err2 != nil {
return err
}
2015-06-28 15:00:56 +00:00
}
2015-08-07 16:50:14 +00:00
for _ , loc := range locs {
requestedBp . Addr = loc . PC
2015-09-30 05:42:06 +00:00
bp , err := t . client . CreateBreakpoint ( requestedBp )
2015-08-07 16:50:14 +00:00
if err != nil {
return err
}
2016-01-24 08:16:45 +00:00
fmt . Printf ( "%s set at %s\n" , formatBreakpointName ( bp , true ) , formatBreakpointLocation ( bp ) )
2015-08-07 16:50:14 +00:00
}
2015-03-20 21:11:11 +00:00
return nil
}
2016-02-23 14:12:04 +00:00
func breakpoint ( t * Term , ctx callContext , args string ) error {
2015-11-02 06:30:12 +00:00
return setBreakpoint ( t , false , args )
2015-06-28 15:00:56 +00:00
}
2016-02-23 14:12:04 +00:00
func tracepoint ( t * Term , ctx callContext , args string ) error {
2015-11-02 06:30:12 +00:00
return setBreakpoint ( t , true , args )
2015-06-28 15:00:56 +00:00
}
2016-02-23 14:12:04 +00:00
func printVar ( t * Term , ctx callContext , args string ) error {
2015-03-20 21:11:11 +00:00
if len ( args ) == 0 {
2015-05-04 22:31:13 +00:00
return fmt . Errorf ( "not enough arguments" )
2015-03-20 21:11:11 +00:00
}
2016-02-23 14:12:04 +00:00
if ctx . Prefix == onPrefix {
ctx . Breakpoint . Variables = append ( ctx . Breakpoint . Variables , args )
return nil
}
val , err := t . client . EvalVariable ( ctx . Scope , args )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
2015-10-18 17:37:13 +00:00
fmt . Println ( val . MultilineString ( "" ) )
2015-03-20 21:11:11 +00:00
return nil
}
2016-02-23 14:12:04 +00:00
func setVar ( t * Term , ctx callContext , args string ) error {
2015-11-02 06:30:12 +00:00
// HACK: in go '=' is not an operator, we detect the error and try to recover from it by splitting the input string
_ , err := parser . ParseExpr ( args )
if err == nil {
return fmt . Errorf ( "syntax error '=' not found" )
2015-09-28 10:01:18 +00:00
}
2015-11-02 06:30:12 +00:00
el , ok := err . ( scanner . ErrorList )
if ! ok || el [ 0 ] . Msg != "expected '==', found '='" {
return err
}
lexpr := args [ : el [ 0 ] . Pos . Offset ]
rexpr := args [ el [ 0 ] . Pos . Offset + 1 : ]
2016-02-23 14:12:04 +00:00
return t . client . SetVariable ( ctx . Scope , lexpr , rexpr )
2015-09-28 10:01:18 +00:00
}
2015-08-11 02:31:27 +00:00
func filterVariables ( vars [ ] api . Variable , filter string ) [ ] string {
reg , err := regexp . Compile ( filter )
if err != nil {
fmt . Fprintf ( os . Stderr , err . Error ( ) )
return nil
}
2015-03-20 21:11:11 +00:00
data := make ( [ ] string , 0 , len ( vars ) )
for _ , v := range vars {
2015-08-11 02:31:27 +00:00
if reg == nil || reg . Match ( [ ] byte ( v . Name ) ) {
2015-10-18 17:37:13 +00:00
data = append ( data , fmt . Sprintf ( "%s = %s" , v . Name , v . SinglelineString ( ) ) )
2015-03-20 21:11:11 +00:00
}
}
return data
}
2016-02-23 14:12:04 +00:00
func sources ( t * Term , ctx callContext , filter string ) ( [ ] string , error ) {
2015-09-30 05:42:06 +00:00
return t . client . ListSources ( filter )
2015-08-11 02:31:27 +00:00
}
2015-03-20 21:11:11 +00:00
2016-02-23 14:12:04 +00:00
func funcs ( t * Term , ctx callContext , filter string ) ( [ ] string , error ) {
2015-09-30 05:42:06 +00:00
return t . client . ListFunctions ( filter )
2015-08-11 02:31:27 +00:00
}
2015-03-20 21:11:11 +00:00
2016-02-23 14:12:04 +00:00
func types ( t * Term , ctx callContext , filter string ) ( [ ] string , error ) {
2016-02-22 15:08:45 +00:00
return t . client . ListTypes ( filter )
}
2016-02-23 14:12:04 +00:00
func args ( t * Term , ctx callContext , filter string ) ( [ ] string , error ) {
vars , err := t . client . ListFunctionArgs ( ctx . Scope )
2015-08-11 02:31:27 +00:00
if err != nil {
return nil , err
}
2016-04-06 16:26:10 +00:00
return describeNoVars ( "args" , filterVariables ( vars , filter ) ) , nil
2015-08-11 02:31:27 +00:00
}
2015-03-20 21:11:11 +00:00
2016-02-23 14:12:04 +00:00
func locals ( t * Term , ctx callContext , filter string ) ( [ ] string , error ) {
locals , err := t . client . ListLocalVariables ( ctx . Scope )
2015-08-11 02:31:27 +00:00
if err != nil {
return nil , err
}
2016-04-06 16:26:10 +00:00
return describeNoVars ( "locals" , filterVariables ( locals , filter ) ) , nil
2015-08-11 02:31:27 +00:00
}
2015-03-20 21:11:11 +00:00
2016-02-23 14:12:04 +00:00
func vars ( t * Term , ctx callContext , filter string ) ( [ ] string , error ) {
2015-09-30 05:42:06 +00:00
vars , err := t . client . ListPackageVariables ( filter )
2015-08-11 02:31:27 +00:00
if err != nil {
return nil , err
}
2016-04-06 16:26:10 +00:00
return describeNoVars ( "vars" , filterVariables ( vars , filter ) ) , nil
2015-08-11 02:31:27 +00:00
}
2015-03-20 21:11:11 +00:00
2016-02-23 14:12:04 +00:00
func regs ( t * Term , ctx callContext , args string ) error {
2015-09-30 05:42:06 +00:00
regs , err := t . client . ListRegisters ( )
2015-08-11 02:31:27 +00:00
if err != nil {
return err
}
fmt . Println ( regs )
return nil
}
2015-06-19 07:20:10 +00:00
2015-08-28 20:06:29 +00:00
func filterSortAndOutput ( fn filteringFunc ) cmdfunc {
2016-02-23 14:12:04 +00:00
return func ( t * Term , ctx callContext , args string ) error {
2015-08-11 02:31:27 +00:00
var filter string
2015-11-02 06:30:12 +00:00
if len ( args ) > 0 {
if _ , err := regexp . Compile ( args ) ; err != nil {
2015-08-11 02:31:27 +00:00
return fmt . Errorf ( "invalid filter argument: %s" , err . Error ( ) )
}
2015-11-02 06:30:12 +00:00
filter = args
2015-03-20 21:11:11 +00:00
}
2016-02-23 14:12:04 +00:00
data , err := fn ( t , ctx , filter )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
2015-08-11 02:31:27 +00:00
sort . Sort ( sort . StringSlice ( data ) )
for _ , d := range data {
fmt . Println ( d )
2015-03-20 21:11:11 +00:00
}
2015-08-11 02:31:27 +00:00
return nil
2015-03-20 21:11:11 +00:00
}
}
2016-02-23 14:12:04 +00:00
func stackCommand ( t * Term , ctx callContext , args string ) error {
2015-09-20 04:41:26 +00:00
depth , full , err := parseStackArgs ( args )
if err != nil {
return err
}
2016-02-23 14:12:04 +00:00
if ctx . Prefix == onPrefix {
ctx . Breakpoint . Stacktrace = depth
return nil
}
stack , err := t . client . Stacktrace ( ctx . Scope . GoroutineID , depth , full )
2015-09-20 04:41:26 +00:00
if err != nil {
return err
}
printStack ( stack , "" )
return nil
}
2015-06-17 17:11:57 +00:00
2015-11-02 06:30:12 +00:00
func parseStackArgs ( argstr string ) ( int , bool , error ) {
2015-09-20 04:41:26 +00:00
var (
depth = 10
full = false
)
2015-11-02 06:30:12 +00:00
if argstr != "" {
args := strings . Split ( argstr , " " )
for i := range args {
if args [ i ] == "-full" {
full = true
} else {
n , err := strconv . Atoi ( args [ i ] )
if err != nil {
return 0 , false , fmt . Errorf ( "depth must be a number" )
}
depth = n
2015-09-17 08:42:34 +00:00
}
2015-06-17 17:11:57 +00:00
}
}
2015-09-20 04:41:26 +00:00
return depth , full , nil
2015-06-28 15:00:56 +00:00
}
2016-02-23 14:12:04 +00:00
func listCommand ( t * Term , ctx callContext , args string ) error {
if ctx . Prefix == scopePrefix {
locs , err := t . client . Stacktrace ( ctx . Scope . GoroutineID , ctx . Scope . Frame , false )
if err != nil {
return err
}
if ctx . Scope . Frame >= len ( locs ) {
return fmt . Errorf ( "Frame %d does not exist in goroutine %d" , ctx . Scope . Frame , ctx . Scope . GoroutineID )
}
loc := locs [ ctx . Scope . Frame ]
return printfile ( t , loc . File , loc . Line , true )
}
2015-07-29 14:24:52 +00:00
if len ( args ) == 0 {
2015-09-30 05:42:06 +00:00
state , err := t . client . GetState ( )
2015-07-29 14:24:52 +00:00
if err != nil {
return err
}
2015-09-30 05:42:06 +00:00
printcontext ( t , state )
2015-07-29 14:24:52 +00:00
return nil
}
2016-01-10 08:57:52 +00:00
locs , err := t . client . FindLocation ( api . EvalScope { GoroutineID : - 1 , Frame : 0 } , args )
2015-07-29 14:24:52 +00:00
if err != nil {
return err
}
if len ( locs ) > 1 {
2015-11-02 06:30:12 +00:00
return debugger . AmbiguousLocationError { Location : args , CandidatesLocation : locs }
2015-07-29 14:24:52 +00:00
}
2015-09-30 05:42:06 +00:00
printfile ( t , locs [ 0 ] . File , locs [ 0 ] . Line , false )
2015-07-29 14:24:52 +00:00
return nil
}
2016-02-23 14:12:04 +00:00
func ( c * Commands ) sourceCommand ( t * Term , ctx callContext , args string ) error {
2015-12-15 14:16:17 +00:00
if len ( args ) == 0 {
2015-09-29 16:40:12 +00:00
return fmt . Errorf ( "wrong number of arguments: source <filename>" )
}
2016-01-10 08:57:52 +00:00
return c . executeFile ( t , args )
2015-09-29 16:40:12 +00:00
}
2016-02-06 06:00:48 +00:00
var disasmUsageError = errors . New ( "wrong number of arguments: disassemble [-a <start> <end>] [-l <locspec>]" )
2016-02-23 14:12:04 +00:00
func disassCommand ( t * Term , ctx callContext , args string ) error {
2016-02-06 06:00:48 +00:00
var cmd , rest string
if args != "" {
argv := strings . SplitN ( args , " " , 2 )
if len ( argv ) != 2 {
return disasmUsageError
}
cmd = argv [ 0 ]
rest = argv [ 1 ]
}
var disasm api . AsmInstructions
var disasmErr error
switch cmd {
case "" :
2016-02-23 14:12:04 +00:00
locs , err := t . client . FindLocation ( ctx . Scope , "+0" )
2016-02-06 06:00:48 +00:00
if err != nil {
return err
}
2016-02-23 14:12:04 +00:00
disasm , disasmErr = t . client . DisassemblePC ( ctx . Scope , locs [ 0 ] . PC , api . IntelFlavour )
2016-02-06 06:00:48 +00:00
case "-a" :
v := strings . SplitN ( rest , " " , 2 )
if len ( v ) != 2 {
return disasmUsageError
}
startpc , err := strconv . ParseInt ( v [ 0 ] , 0 , 64 )
if err != nil {
return fmt . Errorf ( "wrong argument: %s is not a number" , v [ 0 ] )
}
endpc , err := strconv . ParseInt ( v [ 1 ] , 0 , 64 )
if err != nil {
return fmt . Errorf ( "wrong argument: %s is not a number" , v [ 1 ] )
}
2016-02-23 14:12:04 +00:00
disasm , disasmErr = t . client . DisassembleRange ( ctx . Scope , uint64 ( startpc ) , uint64 ( endpc ) , api . IntelFlavour )
2016-02-06 06:00:48 +00:00
case "-l" :
2016-02-23 14:12:04 +00:00
locs , err := t . client . FindLocation ( ctx . Scope , rest )
2016-02-06 06:00:48 +00:00
if err != nil {
return err
}
if len ( locs ) != 1 {
return errors . New ( "expression specifies multiple locations" )
}
2016-02-23 14:12:04 +00:00
disasm , disasmErr = t . client . DisassemblePC ( ctx . Scope , locs [ 0 ] . PC , api . IntelFlavour )
2016-02-06 06:00:48 +00:00
default :
return disasmUsageError
}
if disasmErr != nil {
return disasmErr
}
fmt . Printf ( "printing\n" )
DisasmPrint ( disasm , os . Stdout )
return nil
}
2015-09-17 08:42:34 +00:00
func digits ( n int ) int {
2016-01-27 12:33:37 +00:00
if n <= 0 {
return 1
}
2015-09-17 08:42:34 +00:00
return int ( math . Floor ( math . Log10 ( float64 ( n ) ) ) ) + 1
}
func printStack ( stack [ ] api . Stackframe , ind string ) {
2016-01-28 07:47:04 +00:00
if len ( stack ) == 0 {
return
}
2015-09-17 08:42:34 +00:00
d := digits ( len ( stack ) - 1 )
fmtstr := "%s%" + strconv . Itoa ( d ) + "d 0x%016x in %s\n"
2015-09-20 04:58:18 +00:00
s := strings . Repeat ( " " , d + 2 + len ( ind ) )
2015-09-17 08:42:34 +00:00
2015-06-17 17:11:57 +00:00
for i := range stack {
name := "(nil)"
if stack [ i ] . Function != nil {
name = stack [ i ] . Function . Name
}
2015-09-17 08:42:34 +00:00
fmt . Printf ( fmtstr , ind , i , stack [ i ] . PC , name )
2015-12-28 02:58:00 +00:00
fmt . Printf ( "%sat %s:%d\n" , s , ShortenFilePath ( stack [ i ] . File ) , stack [ i ] . Line )
2015-09-17 08:42:34 +00:00
for j := range stack [ i ] . Arguments {
2015-10-18 17:37:13 +00:00
fmt . Printf ( "%s %s = %s\n" , s , stack [ i ] . Arguments [ j ] . Name , stack [ i ] . Arguments [ j ] . SinglelineString ( ) )
2015-09-17 08:42:34 +00:00
}
for j := range stack [ i ] . Locals {
2015-10-18 17:37:13 +00:00
fmt . Printf ( "%s %s = %s\n" , s , stack [ i ] . Locals [ j ] . Name , stack [ i ] . Locals [ j ] . SinglelineString ( ) )
2015-09-17 08:42:34 +00:00
}
2015-06-17 17:11:57 +00:00
}
}
2015-09-30 05:42:06 +00:00
func printcontext ( t * Term , state * api . DebuggerState ) error {
2015-10-29 09:59:22 +00:00
for i := range state . Threads {
if ( state . CurrentThread != nil ) && ( state . Threads [ i ] . ID == state . CurrentThread . ID ) {
continue
}
if state . Threads [ i ] . Breakpoint != nil {
printcontextThread ( t , state . Threads [ i ] )
}
}
2015-03-20 21:11:11 +00:00
if state . CurrentThread == nil {
fmt . Println ( "No current thread available" )
return nil
}
if len ( state . CurrentThread . File ) == 0 {
fmt . Printf ( "Stopped at: 0x%x\n" , state . CurrentThread . PC )
2015-09-30 05:42:06 +00:00
t . Println ( "=>" , "no source available" )
2015-03-20 21:11:11 +00:00
return nil
}
2015-10-29 09:59:22 +00:00
printcontextThread ( t , state . CurrentThread )
2016-02-13 20:00:21 +00:00
if state . CurrentThread . Breakpoint == nil || ! state . CurrentThread . Breakpoint . Tracepoint || state . CurrentThread . BreakpointInfo == nil {
2015-10-29 09:59:22 +00:00
return printfile ( t , state . CurrentThread . File , state . CurrentThread . Line , true )
2015-07-12 20:18:14 +00:00
}
2015-10-29 09:59:22 +00:00
return nil
}
2015-09-26 00:04:39 +00:00
2015-10-29 09:59:22 +00:00
func printcontextThread ( t * Term , th * api . Thread ) {
fn := th . Function
if th . Breakpoint == nil {
2016-01-31 12:03:53 +00:00
fmt . Printf ( "> %s() %s:%d (PC: %#v)\n" , fn . Name , ShortenFilePath ( th . File ) , th . Line , th . PC )
2015-10-29 09:59:22 +00:00
return
}
args := ""
2016-02-13 20:00:21 +00:00
if th . Breakpoint . Tracepoint && th . BreakpointInfo != nil {
2015-10-29 09:59:22 +00:00
var arg [ ] string
2016-01-28 07:47:04 +00:00
for _ , ar := range th . BreakpointInfo . Arguments {
2015-10-29 09:59:22 +00:00
arg = append ( arg , ar . SinglelineString ( ) )
2015-07-12 20:18:14 +00:00
}
2015-10-29 09:59:22 +00:00
args = strings . Join ( arg , ", " )
}
2016-01-24 08:16:45 +00:00
bpname := ""
if th . Breakpoint . Name != "" {
bpname = fmt . Sprintf ( "[%s] " , th . Breakpoint . Name )
}
2015-10-29 09:59:22 +00:00
if hitCount , ok := th . Breakpoint . HitCount [ strconv . Itoa ( th . GoroutineID ) ] ; ok {
2016-01-24 08:16:45 +00:00
fmt . Printf ( "> %s%s(%s) %s:%d (hits goroutine(%d):%d total:%d) (PC: %#v)\n" ,
bpname ,
2015-10-29 09:59:22 +00:00
fn . Name ,
args ,
ShortenFilePath ( th . File ) ,
th . Line ,
th . GoroutineID ,
hitCount ,
2016-01-31 12:03:53 +00:00
th . Breakpoint . TotalHitCount ,
th . PC )
2015-07-12 20:18:14 +00:00
} else {
2016-01-24 08:16:45 +00:00
fmt . Printf ( "> %s%s(%s) %s:%d (hits total:%d) (PC: %#v)\n" ,
bpname ,
2015-10-29 09:59:22 +00:00
fn . Name ,
args ,
ShortenFilePath ( th . File ) ,
th . Line ,
2016-01-31 12:03:53 +00:00
th . Breakpoint . TotalHitCount ,
th . PC )
2015-03-20 21:11:11 +00:00
}
2015-10-29 09:59:22 +00:00
if th . BreakpointInfo != nil {
bpi := th . BreakpointInfo
2015-06-28 15:00:56 +00:00
if bpi . Goroutine != nil {
2015-10-16 06:42:02 +00:00
writeGoroutineLong ( os . Stdout , bpi . Goroutine , "\t" )
2015-06-28 15:00:56 +00:00
}
2015-10-29 09:59:22 +00:00
if len ( bpi . Variables ) > 0 {
ss := make ( [ ] string , len ( bpi . Variables ) )
for i , v := range bpi . Variables {
ss [ i ] = fmt . Sprintf ( "%s: %s" , v . Name , v . MultilineString ( "" ) )
}
fmt . Printf ( "\t%s\n" , strings . Join ( ss , ", " ) )
2015-06-28 15:00:56 +00:00
}
if bpi . Stacktrace != nil {
fmt . Printf ( "\tStack:\n" )
printStack ( bpi . Stacktrace , "\t\t" )
}
}
2015-07-29 14:24:52 +00:00
}
2015-09-30 05:42:06 +00:00
func printfile ( t * Term , filename string , line int , showArrow bool ) error {
2015-07-29 14:24:52 +00:00
file , err := os . Open ( filename )
2015-03-20 21:11:11 +00:00
if err != nil {
return err
}
defer file . Close ( )
2015-09-30 05:42:06 +00:00
buf := bufio . NewScanner ( file )
2015-07-29 14:24:52 +00:00
l := line
2015-03-20 21:11:11 +00:00
for i := 1 ; i < l - 5 ; i ++ {
2015-09-30 05:42:06 +00:00
if ! buf . Scan ( ) {
return nil
2015-03-20 21:11:11 +00:00
}
}
2015-07-29 14:24:52 +00:00
s := l - 5
if s < 1 {
s = 1
}
for i := s ; i <= l + 5 ; i ++ {
2015-09-30 05:42:06 +00:00
if ! buf . Scan ( ) {
return nil
2015-03-20 21:11:11 +00:00
}
2015-09-30 15:37:14 +00:00
var prefix string
2015-07-29 14:24:52 +00:00
if showArrow {
2015-09-30 15:37:14 +00:00
prefix = " "
2015-07-29 14:24:52 +00:00
if i == l {
2015-09-30 15:37:14 +00:00
prefix = "=>"
2015-07-29 14:24:52 +00:00
}
2015-03-20 21:11:11 +00:00
}
2015-09-30 15:37:14 +00:00
prefix = fmt . Sprintf ( "%s%4d:\t" , prefix , i )
t . Println ( prefix , buf . Text ( ) )
2015-03-20 21:11:11 +00:00
}
return nil
}
2015-07-29 23:19:06 +00:00
2016-01-10 08:57:52 +00:00
// ExitRequestError is returned when the user
// exits Delve.
2015-07-29 23:19:06 +00:00
type ExitRequestError struct { }
func ( ere ExitRequestError ) Error ( ) string {
return ""
}
2016-02-23 14:12:04 +00:00
func exitCommand ( t * Term , ctx callContext , args string ) error {
2015-07-29 23:19:06 +00:00
return ExitRequestError { }
}
2015-08-19 22:38:53 +00:00
2016-03-09 15:11:58 +00:00
func getBreakpointByIDOrName ( t * Term , arg string ) ( * api . Breakpoint , error ) {
2016-01-24 08:16:45 +00:00
if id , err := strconv . Atoi ( arg ) ; err == nil {
2016-03-09 15:11:58 +00:00
return t . client . GetBreakpoint ( id )
2016-01-24 08:16:45 +00:00
}
2016-03-09 15:11:58 +00:00
return t . client . GetBreakpointByName ( arg )
2016-01-24 08:16:45 +00:00
}
2016-02-23 14:12:04 +00:00
func ( c * Commands ) onCmd ( t * Term , ctx callContext , argstr string ) error {
2016-01-24 08:16:45 +00:00
args := strings . SplitN ( argstr , " " , 3 )
if len ( args ) < 2 {
2016-02-23 14:12:04 +00:00
return errors . New ( "not enough arguments" )
}
if len ( args ) < 3 {
args = append ( args , "" )
2016-01-24 08:16:45 +00:00
}
bp , err := getBreakpointByIDOrName ( t , args [ 0 ] )
if err != nil {
return err
}
2016-02-23 14:12:04 +00:00
ctx . Prefix = onPrefix
ctx . Breakpoint = bp
err = c . CallWithContext ( args [ 1 ] , args [ 2 ] , t , ctx )
if err != nil {
return err
2016-01-24 08:16:45 +00:00
}
2016-02-23 14:12:04 +00:00
return t . client . AmendBreakpoint ( ctx . Breakpoint )
2016-01-24 08:16:45 +00:00
}
2016-02-23 14:12:04 +00:00
func conditionCmd ( t * Term , ctx callContext , argstr string ) error {
2016-01-24 08:16:45 +00:00
args := strings . SplitN ( argstr , " " , 2 )
if len ( args ) < 2 {
return fmt . Errorf ( "not enough arguments" )
}
bp , err := getBreakpointByIDOrName ( t , args [ 0 ] )
if err != nil {
return err
}
bp . Cond = args [ 1 ]
return t . client . AmendBreakpoint ( bp )
}
2016-01-10 08:57:52 +00:00
// ShortenFilePath take a full file path and attempts to shorten
// it by replacing the current directory to './'.
2015-12-28 02:58:00 +00:00
func ShortenFilePath ( fullPath string ) string {
2015-08-19 22:38:53 +00:00
workingDir , _ := os . Getwd ( )
return strings . Replace ( fullPath , workingDir , "." , 1 )
}
2015-09-29 16:40:12 +00:00
2016-01-10 08:57:52 +00:00
func ( c * Commands ) executeFile ( t * Term , name string ) error {
2015-09-29 16:40:12 +00:00
fh , err := os . Open ( name )
if err != nil {
return err
}
defer fh . Close ( )
scanner := bufio . NewScanner ( fh )
lineno := 0
for scanner . Scan ( ) {
line := strings . TrimSpace ( scanner . Text ( ) )
lineno ++
if line == "" || line [ 0 ] == '#' {
continue
}
cmdstr , args := parseCommand ( line )
2016-02-23 14:12:04 +00:00
if err := c . Call ( cmdstr , args , t ) ; err != nil {
2015-09-29 16:40:12 +00:00
fmt . Printf ( "%s:%d: %v\n" , name , lineno , err )
}
}
return scanner . Err ( )
}
2016-01-24 08:16:45 +00:00
func formatBreakpointName ( bp * api . Breakpoint , upcase bool ) string {
thing := "breakpoint"
if bp . Tracepoint {
thing = "tracepoint"
}
if upcase {
thing = strings . Title ( thing )
}
id := bp . Name
if id == "" {
id = strconv . Itoa ( bp . ID )
}
return fmt . Sprintf ( "%s %s" , thing , id )
}
func formatBreakpointLocation ( bp * api . Breakpoint ) string {
p := ShortenFilePath ( bp . File )
if bp . FunctionName != "" {
return fmt . Sprintf ( "%#v for %s() %s:%d" , bp . Addr , bp . FunctionName , p , bp . Line )
}
2016-03-09 15:11:58 +00:00
return fmt . Sprintf ( "%#v for %s:%d" , bp . Addr , p , bp . Line )
2016-01-24 08:16:45 +00:00
}
2016-04-06 16:26:10 +00:00
func describeNoVars ( varType string , data [ ] string ) [ ] string {
if len ( data ) == 0 {
return [ ] string { fmt . Sprintf ( "(no %s)" , varType ) }
}
return data
}