Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to safely call functions #119

Closed
derekparker opened this issue May 3, 2015 · 13 comments
Closed

Add ability to safely call functions #119

derekparker opened this issue May 3, 2015 · 13 comments

Comments

@derekparker
Copy link
Member

Add support for calling functions within the current scope. Should allow specifying in-scope variables as arguments to these function calls.

call myFunc(localVar, 4, "foo")

@aarzilli
Copy link
Member

Comment moved here from PR #331:

I did think a bit about calling functions (without writing any code so this is purely philosophical) this is what I thought would be the pain points:

a. panics
b. breakpoints getting hit while evaluating a function
c. our goroutine getting scheduled out -- which makes (b) worse.

Regarding (a), one solution is to set a breakpoint on panic and abort the evaluation as soon as it is called, before it starts unwinding the stack. This is not ideal, the panic could be recovered by a frame below our dummy frame, but given how panics are generally used by go code it should be a big deal.

The other possibility would be to abort the call only when the stack unwind gets to our dummy frame, possibly modifying the g struct to slip in our own fake _defer object?

Regarding (b) and (c) I have two possible solution

Orthodox solution

For (b) we disable all user breakpoints. (c) is hard: I'm not sure it's possible to suppress co-operative scheduler calls, maybe abort on them? Even pre-emptive scheduler calls may be hard because they seem to be at least in part intertwined with stack resizes (which we can not suppress).

Unorthodox solution

We call Continue in the middle of Call, any breakpoint that gets hit is reported to the user normally, the rest of Call is executed when its temp breakpoint (which would have a condition on the goroutine id) gets hit, in a callback executed by Continue.

This is easier to implement because we don't have to worry about (b) and especially (c) but it's weird for the user.

> main.somefunction() somefile.go:10
(dlv) print a
3
(dlv) print double(a)
Command failed: function calls are not supported

The user must use a different command to enable function calls, this command will have a behaviour similar to continue and use a similar API, let's call this command exec:

(dlv) exec double(a)
> main.someotherfunction() somefile.go:50

the function call did not complete, the user got a breakpoint somewhere else in the program, the user can interact with the debugger:

(dlv) continue
> main.somefunction() somefile.go:10
    double(a) = 6
(dlv)

after another continue the original exec call completes and the user gets the evaluation result: double(a) = 6.

@ignatov
Copy link

ignatov commented Jul 17, 2017

@derekparker @aarzilli Do we need some changes in Go itself for this issue?

@aarzilli
Copy link
Member

@ignatov: likely yes. On top of the design problems in my previous message there is also another thing to be considered. Let's say we want to initiate a call, from the debugger, to a function F from goroutine G.
We shall call the activation frame currently on top of the stack for G the "dangerframe"

  1. We can't overwrite memory in the dangerframe because the garbage collector could run while F is executing and collect live object because we overwrote the dangerframe, so we need to add memory do G's stack (because goroutines stack are tight): how?

  2. Let's say there was enough space on G's stack to push our arguments, space for the return value and some fake return address. Now we change RIP and restart the process to execute F. If the garbage collector runs and tries to unwind G's stack it will get hopelessly confused once it gets to the dangerframe because we made dangerframe extra fat by adding one extra arguments area at the end of it.

  3. Even if this doesn't happen the dangerframe is not stopped at a safe point so the garbage collector wouldn't have a good pointer bitmap to determine which pointers on the dangerframe are live.

@ignatov
Copy link

ignatov commented Jul 21, 2017

@aarzilli maybe we should file an issue in https://github.com/golang/go/issues? We had a conversation with @rsc, and he told us that Go team is going to provide a good basement for debugger at least.

@komuw
Copy link

komuw commented Aug 28, 2017

@ignatov Did you create an issue at https://github.com/golang/go/issues ?
I'm new to Go and was trying to run a func in a delve session and I came across this issue.

I'm asking, since I would like to file an issue in the Go repo, if you haven't already. Especially now that Go is collecting feedback on stuff that should go into Go2, I think a better debugging experience should be of utmost importance and having an issue open with them would greatly help.
cc @derekparker @aarzilli

@ignatov
Copy link

ignatov commented Aug 28, 2017

@komuw

Did you create an issue at https://github.com/golang/go/issues ?

No, I didn't. Could you please do it?

@phemmer
Copy link

phemmer commented Nov 8, 2017

Pardon my jumping in here, but could we side-step all the GC related issues by disabling it during the function call?
I can think of 3 risks to doing this.

  1. function call goes nuts and consumes all memory. I think the risk here is pretty minimal, and acceptable if it gets us function calls.
  2. function call explicitly triggers a GC. I'm not sure how delve interacts with the Go runtime, but it seems like we might be able to set a breakpoint on runtime.GC() so that we can clean up the stack, or make it a no-op.
  3. function call re-enables the GC. Similar to # 2, set a breakpoint on debug.SetGCPercent() and no-op it.

@komuw
Copy link

komuw commented Apr 5, 2018

update:
I had opened an issue on the Go issue tracker and it got a lot of traction: golang/go#21678

Additionally, there is a new proposal; golang/go#24543 that if adopted would have side benefits for debugger function calls

aarzilli added a commit to aarzilli/delve that referenced this issue May 8, 2018
aarzilli added a commit to aarzilli/delve that referenced this issue May 8, 2018
aarzilli added a commit to aarzilli/delve that referenced this issue May 8, 2018
aarzilli added a commit to aarzilli/delve that referenced this issue May 14, 2018
aarzilli added a commit to aarzilli/delve that referenced this issue May 17, 2018
aarzilli added a commit to aarzilli/delve that referenced this issue May 21, 2018
gopherbot referenced this issue in golang/go May 22, 2018
This adds a mechanism for debuggers to safely inject calls to Go
functions on amd64. Debuggers must participate in a protocol with the
runtime, and need to know how to lay out a call frame, but the runtime
support takes care of the details of handling live pointers in
registers, stack growth, and detecting the trickier conditions when it
is unsafe to inject a user function call.

Fixes #21678.
Updates derekparker/delve#119.

Change-Id: I56d8ca67700f1f77e19d89e7fc92ab337b228834
Reviewed-on: https://go-review.googlesource.com/109699
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
aarzilli added a commit to aarzilli/delve that referenced this issue May 23, 2018
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue May 25, 2018
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue May 25, 2018
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue Jun 12, 2018
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue Jun 18, 2018
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue Jun 26, 2018
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue Jul 13, 2018
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue Jul 13, 2018
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 go-delve#119
derekparker pushed a commit that referenced this issue Jul 13, 2018
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
@marat-rkh
Copy link

I am implementing IDE integration with delve. It would be helpful to have an option to disable breakpoints while a function call is executed (as @aarzilli mentioned). Are there any plans regarding this?

aarzilli added a commit to aarzilli/delve that referenced this issue Feb 27, 2019
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue Feb 27, 2019
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue Feb 27, 2019
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue Feb 27, 2019
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 go-delve#119
@coriolinus
Copy link

coriolinus commented Mar 7, 2019

Given that https://go-review.googlesource.com/c/go/+/109699/ is merged into go 1.12, which has been released, does there now exist a reasonable estimate when this feature will be ready?

@derekparker
Copy link
Member Author

@coriolinus this feature is already implemented in the latest release of Delve, however it has been left open as there are still some improvements to be made.

@coriolinus
Copy link

coriolinus commented Mar 7, 2019 via email

aarzilli added a commit to aarzilli/delve that referenced this issue May 2, 2019
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue May 2, 2019
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue May 9, 2019
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue May 9, 2019
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 go-delve#119
aarzilli added a commit to aarzilli/delve that referenced this issue May 9, 2019
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 go-delve#119
derekparker pushed a commit that referenced this issue May 9, 2019
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
aarzilli added a commit to aarzilli/delve that referenced this issue May 9, 2019
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 go-delve#119
derekparker pushed a commit that referenced this issue May 30, 2019
* 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
@derekparker
Copy link
Member Author

Going to officially close this out as we now have this functionality. We will continue adding more improvements and @aarzilli has done a ton already.

nclifton pushed a commit to nclifton/delve that referenced this issue Feb 24, 2021
go-delve#119)

* added dev env container for webhook worker

* using ContainerName

* Squashed commit of the following:

commit 504164e
Author: Elliot <elliot@burstsms.com>
Date:   Thu Jan 28 16:23:29 2021 +1100

    RPC infra simplification (go-delve#118)

    * init

    * mm7 port

    * rpcbuilder listen host

    * container names fix for jaeger

    * revert port mapping in dev

    * template

    * container_port, container_name struct fields

    * rpc_service -> rpc_address env vars/ struct fields

    * charts

    * trigger build rerun

    * struct fields

    * more fields and naming

* removed extra blank line

* de-lint

* changing the webhook message queue exchange name to "webhook-post"

* remove ports not required until the health check PR is  created
cgxxv pushed a commit to cgxxv/delve that referenced this issue Mar 25, 2022
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 go-delve#119
cgxxv pushed a commit to cgxxv/delve that referenced this issue Mar 25, 2022
)

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 go-delve#119
cgxxv pushed a commit to cgxxv/delve that referenced this issue Mar 25, 2022
* 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 go-delve#119

* proc: bugfix: closure addr was wrong for non-closure functions
abner-chenc pushed a commit to loongson/delve that referenced this issue Mar 1, 2024
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 go-delve#119
abner-chenc pushed a commit to loongson/delve that referenced this issue Mar 1, 2024
)

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 go-delve#119
abner-chenc pushed a commit to loongson/delve that referenced this issue Mar 1, 2024
* 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 go-delve#119

* proc: bugfix: closure addr was wrong for non-closure functions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants