proc: allow function calls to appear inside an expression (#1503)

The initial implementation of the 'call' command required the
function call to be the root expression, i.e. something like:

	double(3) + 1

was not allowed, because the root expression was the binary operator
'+', not the function call.

With this change expressions like the one above and others are
allowed.

This is the first step necessary to implement nested function calls
(where the result of a function call is used as argument to another
function call).

This is implemented by replacing proc.CallFunction with
proc.EvalExpressionWithCalls. EvalExpressionWithCalls will run
proc.(*EvalScope).EvalExpression in a different goroutine. This
goroutine, the 'eval' goroutine, will communicate with the main
goroutine of the debugger by means of two channels: continueRequest
and continueCompleted.

The eval goroutine evaluates the expression recursively, when
a function call is encountered it takes care of setting up the
function call on the target program and writes a request to the
continueRequest channel, this causes the 'main' goroutine to restart
the target program by calling proc.Continue.

Whenever Continue encounters a breakpoint that belongs to the
function call injection protocol (runtime.debugCallV1 and associated
functions) it writes to continueCompleted which resumes the 'eval'
goroutine.

The 'eval' goroutine takes care of implementing the function call
injection protocol.

When the expression is fully evaluated the 'eval' goroutine will
write a special message to 'continueRequest' signaling that the
expression evaluation is terminated which will cause Continue to
return to the user.

Updates #119
This commit is contained in:
Alessandro Arzilli
2019-05-09 17:29:58 +02:00
committed by Derek Parker
parent f3b149bda7
commit c30a333f7b
11 changed files with 347 additions and 90 deletions

View File

@ -221,6 +221,20 @@ type EvalScope struct {
frameOffset int64
aordr *dwarf.Reader // extra reader to load DW_AT_abstract_origin entries, do not initialize
// When the following pointer is not nil this EvalScope was created
// by CallFunction and the expression evaluation is executing on a
// different goroutine from the debugger's main goroutine.
// Under this circumstance the expression evaluator can make function
// calls by setting up the runtime.debugCallV1 call and then writing a
// value to the continueRequest channel.
// When a value is written to continueRequest the debugger's main goroutine
// will call Continue, when the runtime in the target process sends us a
// request in the function call protocol the debugger's main goroutine will
// write a value to the continueCompleted channel.
// The goroutine executing the expression evaluation shall signal that the
// evaluation is complete by closing the continueRequest channel.
callCtx *callContext
}
// IsNilErr is returned when a variable is nil.