terminal: add display command (#1917)

Implements #1256
This commit is contained in:
Alessandro Arzilli 2020-03-19 19:58:40 +01:00 committed by GitHub
parent 4aee281520
commit c6de961be8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 104 additions and 2 deletions

@ -39,6 +39,7 @@ Command | Description
Command | Description Command | Description
--------|------------ --------|------------
[args](#args) | Print function arguments. [args](#args) | Print function arguments.
[display](#display) | Print value of an expression every time the program stops.
[examinemem](#examinemem) | Examine memory: [examinemem](#examinemem) | Examine memory:
[locals](#locals) | Print local variables. [locals](#locals) | Print local variables.
[print](#print) | Evaluate an expression. [print](#print) | Evaluate an expression.
@ -225,6 +226,17 @@ If no argument is specified the function being executed in the selected stack fr
Aliases: disass Aliases: disass
## display
Print value of an expression every time the program stops.
display -a <expression>
display -d <number>
The '-a' option adds an expression to the list of expression printed every time the program stops. The '-d' option removes the specified expression from the list.
If display is called without arguments it will print the value of all expression in the list.
## down ## down
Move the current frame down. Move the current frame down.
@ -246,7 +258,7 @@ Aliases: ed
## examinemem ## examinemem
Examine memory: Examine memory:
examinemem [-fmt <format>] [-len <length>] <address> examinemem [-fmt <format>] [-len <length>] <address>
Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal),. Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal),.
Length is the number of bytes (default 1) and must be less than or equal to 1000. Length is the number of bytes (default 1) and must be less than or equal to 1000.

@ -375,7 +375,7 @@ If locspec is omitted edit will open the current source file in the editor, othe
{aliases: []string{"examinemem", "x"}, group: dataCmds, cmdFn: examineMemoryCmd, helpMsg: `Examine memory: {aliases: []string{"examinemem", "x"}, group: dataCmds, cmdFn: examineMemoryCmd, helpMsg: `Examine memory:
examinemem [-fmt <format>] [-len <length>] <address> examinemem [-fmt <format>] [-len <length>] <address>
Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal),. Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal),.
Length is the number of bytes (default 1) and must be less than or equal to 1000. Length is the number of bytes (default 1) and must be less than or equal to 1000.
@ -384,6 +384,15 @@ Address is the memory location of the target to examine.
For example: For example:
x -fmt hex -len 20 0xc00008af38`}, x -fmt hex -len 20 0xc00008af38`},
{aliases: []string{"display"}, group: dataCmds, cmdFn: display, helpMsg: `Print value of an expression every time the program stops.
display -a <expression>
display -d <number>
The '-a' option adds an expression to the list of expression printed every time the program stops. The '-d' option removes the specified expression from the list.
If display is called without arguments it will print the value of all expression in the list.`},
} }
if client == nil || client.Recorded() { if client == nil || client.Recorded() {
@ -981,6 +990,7 @@ func restartRecorded(t *Term, ctx callContext, args string) error {
} }
printcontext(t, state) printcontext(t, state)
printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true) printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
t.onStop()
return nil return nil
} }
@ -1057,6 +1067,7 @@ func (c *Commands) cont(t *Term, ctx callContext, args string) error {
if ctx.Prefix == revPrefix { if ctx.Prefix == revPrefix {
return c.rewind(t, ctx, args) return c.rewind(t, ctx, args)
} }
defer t.onStop()
c.frame = 0 c.frame = 0
stateChan := t.client.Continue() stateChan := t.client.Continue()
var state *api.DebuggerState var state *api.DebuggerState
@ -1072,6 +1083,7 @@ func (c *Commands) cont(t *Term, ctx callContext, args string) error {
} }
func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, shouldPrintFile bool) error { func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, shouldPrintFile bool) error {
defer t.onStop()
if !state.NextInProgress { if !state.NextInProgress {
if shouldPrintFile { if shouldPrintFile {
printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true) printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
@ -1141,6 +1153,8 @@ func (c *Commands) stepInstruction(t *Term, ctx callContext, args string) error
return notOnFrameZeroErr return notOnFrameZeroErr
} }
defer t.onStop()
var fn func() (*api.DebuggerState, error) var fn func() (*api.DebuggerState, error)
if ctx.Prefix == revPrefix { if ctx.Prefix == revPrefix {
fn = t.client.ReverseStepInstruction fn = t.client.ReverseStepInstruction
@ -2386,6 +2400,37 @@ func clearCheckpoint(t *Term, ctx callContext, args string) error {
return t.client.ClearCheckpoint(id) return t.client.ClearCheckpoint(id)
} }
func display(t *Term, ctx callContext, args string) error {
const (
addOption = "-a "
delOption = "-d "
)
switch {
case args == "":
t.printDisplays()
case strings.HasPrefix(args, addOption):
args = strings.TrimSpace(args[len(addOption):])
if args == "" {
return fmt.Errorf("not enough arguments")
}
t.addDisplay(args)
t.printDisplay(len(t.displays) - 1)
case strings.HasPrefix(args, delOption):
args = strings.TrimSpace(args[len(delOption):])
n, err := strconv.Atoi(args)
if err != nil {
return fmt.Errorf("%q is not a number", args)
}
return t.removeDisplay(n)
default:
return fmt.Errorf("wrong arguments")
}
return nil
}
func formatBreakpointName(bp *api.Breakpoint, upcase bool) string { func formatBreakpointName(bp *api.Breakpoint, upcase bool) string {
thing := "breakpoint" thing := "breakpoint"
if bp.Tracepoint { if bp.Tracepoint {

@ -54,6 +54,7 @@ type Term struct {
dumb bool dumb bool
stdout io.Writer stdout io.Writer
InitFile string InitFile string
displays []string
starlarkEnv *starbind.Env starlarkEnv *starbind.Env
@ -432,6 +433,50 @@ func (t *Term) loadConfig() api.LoadConfig {
return r return r
} }
func (t *Term) removeDisplay(n int) error {
if n < 0 || n >= len(t.displays) {
return fmt.Errorf("%d is out of range", n)
}
t.displays[n] = ""
for i := len(t.displays) - 1; i >= 0; i-- {
if t.displays[i] != "" {
t.displays = t.displays[:i+1]
return nil
}
}
t.displays = t.displays[:0]
return nil
}
func (t *Term) addDisplay(expr string) {
t.displays = append(t.displays, expr)
}
func (t *Term) printDisplay(i int) {
expr := t.displays[i]
val, err := t.client.EvalVariable(api.EvalScope{GoroutineID: -1}, expr, ShortLoadConfig)
if err != nil {
if isErrProcessExited(err) {
return
}
fmt.Printf("%d: %s = error %v\n", i, expr, err)
return
}
fmt.Printf("%d: %s = %s\n", i, val.Name, val.SinglelineString())
}
func (t *Term) printDisplays() {
for i := range t.displays {
if t.displays[i] != "" {
t.printDisplay(i)
}
}
}
func (t *Term) onStop() {
t.printDisplays()
}
// isErrProcessExited returns true if `err` is an RPC error equivalent of proc.ErrProcessExited // isErrProcessExited returns true if `err` is an RPC error equivalent of proc.ErrProcessExited
func isErrProcessExited(err error) bool { func isErrProcessExited(err error) bool {
rpcError, ok := err.(rpc.ServerError) rpcError, ok := err.(rpc.ServerError)