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
--------|------------
[args](#args) | Print function arguments.
[display](#display) | Print value of an expression every time the program stops.
[examinemem](#examinemem) | Examine memory:
[locals](#locals) | Print local variables.
[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
## 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
Move the current frame down.

@ -384,6 +384,15 @@ Address is the memory location of the target to examine.
For example:
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() {
@ -981,6 +990,7 @@ func restartRecorded(t *Term, ctx callContext, args string) error {
}
printcontext(t, state)
printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
t.onStop()
return nil
}
@ -1057,6 +1067,7 @@ func (c *Commands) cont(t *Term, ctx callContext, args string) error {
if ctx.Prefix == revPrefix {
return c.rewind(t, ctx, args)
}
defer t.onStop()
c.frame = 0
stateChan := t.client.Continue()
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 {
defer t.onStop()
if !state.NextInProgress {
if shouldPrintFile {
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
}
defer t.onStop()
var fn func() (*api.DebuggerState, error)
if ctx.Prefix == revPrefix {
fn = t.client.ReverseStepInstruction
@ -2386,6 +2400,37 @@ func clearCheckpoint(t *Term, ctx callContext, args string) error {
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 {
thing := "breakpoint"
if bp.Tracepoint {

@ -54,6 +54,7 @@ type Term struct {
dumb bool
stdout io.Writer
InitFile string
displays []string
starlarkEnv *starbind.Env
@ -432,6 +433,50 @@ func (t *Term) loadConfig() api.LoadConfig {
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
func isErrProcessExited(err error) bool {
rpcError, ok := err.(rpc.ServerError)