* proc: support new Go 1.17 panic/defer mechanism
Go 1.17 will create wrappers for deferred calls that take arguments.
Change defer reading code so that wrappers are automatically unwrapped.
Also the deferred function is called directly by runtime.gopanic, without going through runtime.callN which means that sometimes when a panic happens the stack is either:
0. deferred function call
1. deferred call wrapper
2. runtime.gopanic
or:
0. deferred function call
1. runtime.gopanic
instead of always being:
0. deferred function call
1. runtime.callN
2. runtime.gopanic
the isPanicCall check is changed accordingly.
* test: miscellaneous minor test fixes for Go 1.17
* proc: resolve inlined calls when stepping out of runtime.breakpoint
Calls to runtime.Breakpoint are inlined in Go 1.17 when inlining is
enabled, resolve inlined calls in stepInstructionOut.
* proc: add support for debugCallV2 with regabi
This change adds support for the new debug call protocol which had to
change for the new register ABI introduced in Go 1.17.
Summary of changes:
- Abstracts over the debug call version depending on the Go version
found in the binary.
- Uses R12 instead of RAX as the debug protocol register when the binary
is from Go 1.17 or later.
- Creates a variable directly from the DWARF entry for function
arguments to support passing arguments however the ABI expects.
- Computes a very conservative stack frame size for the call when
injecting a call into a Go process whose version is >=1.17.
Co-authored-by: Michael Anthony Knyszek <mknyszek@google.com>
Co-authored-by: Alessandro Arzilli <alessandro.arzilli@gmail.com>
* TeamCity: enable tests on go-tip
* goversion: version compatibility bump
* TeamCity: fix go-tip builds on macOS/arm64
Co-authored-by: Michael Anthony Knyszek <mknyszek@google.com>
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
evalFunctionCall needs to remove the breakpoint from the current thread
after starting the function call injection, otherwise Continue will
think that the thread is stopped at a breakpoint and return to the user
instead of continuing the call injection.
Removes the restriction that the DWARF type for the receiver of a method
must be a TypeDef. This seems reasonable in practice, but it turns out
Go DWARF does not consider
```
type X int
```
to be a typedef. This patch also allows for calling a method where the
receiver is not used or passed in, such as:
```
func (_ X) Method() { println("why") }
```
Trust argument order to determine argument frame layout when calling
functions, this allows calling optimized functions and removes the
special cases for runtime.mallocgc.
Fixes#1589
Allow changing the value of a string variable to a new literal string,
which requires calling runtime.mallocgc to allocate the string into the
target process.
This means that a command like:
call f("some string")
is now supported.
Additionally the command:
call s = "some string"
is also supported.
Fixes#826
* proc: support nested function calls
Changes the code in fncall.go to support nested function calls.
This changes delays argument evaluation until after we have used
the call injection protocol to allocate an argument frame. When
evaluating the parse tree of an expression we'll initiate each
function call we find on the way down and then complete the function
call on the way up.
For example. in:
f(g(x))
we will:
1. initiate the call injection protocol for f(...)
2. progress it until the point where we have space for the arguments
of 'f' (i.e. when we receive the debugCallAXCompleteCall message
from the target runtime)
3. inititate the call injection protocol for g(...)
4. progress it until the point where we have space for the arguments
of 'g'
5. copy the value of x into the argument frame of 'g'
6. finish the call to g(...)
7. copy the return value of g(x) into the argument frame of 'f'
8. finish the call to f(...)
Updates #119
* proc: bugfix: closure addr was wrong for non-closure functions
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
Normally variables that have a named struct as a type will get a
typedef entry as their type, sometimes however the Go linker will
decide to use the DW_TAG_structure_type entry instead.
For consistency always wrap a struct type into a typedef when we are
creating a new variables (see comment in newVariable for exceptions).
This fixes a bug where it would be impossible to call methods on a
global variable.
Implements the function call injection protocol introduced in go 1.11
by https://go-review.googlesource.com/c/go/+/109699.
This is only the basic support, see TODO comments in pkg/proc/fncall.go
for a list of missing features.
Updates #119