Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@vargaz
Copy link
Contributor

@vargaz vargaz commented Oct 20, 2017

Instead of catching all exceptions in the native-to-managed wrapper and calling the callback from the catch clause, call it directly from the EH code. The
previous approach caught all exceptions, which messed up debugging, since the debugger wouldn't break on uncaught exceptions raised from native-to-managed
callbacks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to remove the finally clause here, as there are 2 possible cases:

  • there is an exception (and we assume there is no ftnptr_eh_callback): we are going to switch GC unsafe -> safe here so we end up unwinding in GC safe mode which is not allowed.
  • there is no exception: it works properly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean remove the clause but keep the detach call right ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to call mono_threads_detach_coop here, otherwise the ftnptr_eh_callback will unwind in GC unsafe mode and that can lead to suspend locks (the runtime tries to suspend a thread but it's in GC unsafe mode and it doesn't hit any safe point)

@rolfbjarne
Copy link
Member

What happens if there are no other managed frames further up the stack? Will mono abort with an unhandled exception message?

luhenry
luhenry previously approved these changes Oct 30, 2017
@luhenry
Copy link
Contributor

luhenry commented Oct 30, 2017

@rolfbjarne if there is no managed frames up the stack, there will be an unhandled exception, and that is no different than today.

@rolfbjarne
Copy link
Member

@luhenry there might be a difference if there's a ftnptr_eh_callback, in which case the managed exception might be converted to a native exception, which native code might catch.

It looks like this PR will cause mono to abort the process before calling ftnptr_eh_callback in that scenario, since it will detect the "no managed frames up the stack" condition first.

@ghost ghost removed the cla-already-signed label Nov 16, 2017
@luhenry luhenry self-assigned this Dec 6, 2017
@luhenry
Copy link
Contributor

luhenry commented Dec 6, 2017

@rolfbjarne how should we treat exceptions which: 1. are not handled by a managed frame (because there is "no managed frames up the stack" for example), and 2. are not handled by a native frame? Should we have the native unhandled exception mechanism take precedence over the managed one (ie: report a native unhandled exception over a managed unhandled exception)?

Instead of catching all exceptions in the native-to-managed wrapper and calling the callback from the catch clause, call it directly from the EH code. The
previous approach caught all exceptions, which messed up debugging, since the debugger wouldn't break on uncaught exceptions raised from native-to-managed
callbacks.
@rolfbjarne
Copy link
Member

@luhenry

If a managed exception is not handled by a managed frame, and there's no ftnptr_eh_callback callback (and nobody else wants the exception, for instance mono_runtime_invoke can request any managed exceptions, and in this case the mono runtime should consider the exception "handled"), then the mono runtime should start the unhandled managed exception logic:

  1. If there's a debugger attached, notify the debugger.
  2. If no debugger (or the debugger said to continue executing), it should start the unhandled managed exception logic:
    mono_invoke_unhandled_exception_hook (MonoObject *exc)
    . This will terminate the process.

@luhenry
Copy link
Contributor

luhenry commented Jan 9, 2018

@rolfbjarne ok for the case you presented, but my question is about the case where: 1. the managed exception is not handled by a managed frame, 2. there IS a ftnptr_eh_callback (so the managed exception is wrapped into a native exception and it is passed to the callback), and 3. the native exception is not handled by a native frame?

@luhenry luhenry dismissed their stale review January 11, 2018 10:42

We need to do the check in the first pass, as pointed out by #5837 (comment)

@lambdageek
Copy link
Member

@luhenry it looks like @vargaz updated the PR to call out to the hook in the first pass. Is there anything else that needs to change? (I'm ready to push to resolve the merge conflict)

@marek-safar marek-safar mentioned this pull request Mar 20, 2018
23 tasks
@lambdageek
Copy link
Member

@monojenkins build coop

@luhenry
Copy link
Contributor

luhenry commented Mar 20, 2018

I don't remember all of it, but looks like it's ok from what the comments and looking at the code. @rolfbjarne could you confirm it looks good to you too?

@lambdageek
Copy link
Member

lambdageek commented Mar 20, 2018

Linux x64 Interpreter looks relavant - pinvoke3 asserts: * Assertion at interp/interp.c:1661, condition `frame.ex == NULL' not met

Edit: Apparently that's a known/expected interp failure. See #7739

@rolfbjarne
Copy link
Member

@luhenry yes, from what I can tell (and remember) this looks good now

@lambdageek
Copy link
Member

@monojenkins build failed

@lambdageek
Copy link
Member

Merged master into this PR one more time and enabled all the tests that were disabled under coop on PRs.

This should be good to go after CI and a review.

@lambdageek
Copy link
Member

If we run the installed handler in the first pass, we don't run finally clauses.

@lambdageek
Copy link
Member

lambdageek commented Mar 21, 2018

So I think we need to refine the first pass result. Instead of just a boolean we should return some enum:

  • handled - caught by a managed frame (with a matching filter if one was present).
  • unhandled-delegate-to-native - there's a native to managed wrapper and a ftnptr_eh_callback is installed.
  • unhandled - first pass unwound all the way and there was no wrapper or there was no callback installed.

Then the second pass will treat unhandled-delegate-to-native as "handled" (ie, start unwinding, run finally clauses, etc) until we reach the wrapper again at which point we call the callback.

I'm assuming that first pass will need to stop if it sees the wrapper and there's a callback installed.

Does this make sense?

Also: does ftnptr_eh_callback ever return to its caller? I'm assuming no - its always going to be some kind of native throw, right? so we wouldn't ever need to keep unwinding after the wrapper frame?

…econd pass.

The constraints are:
1. We must allow managed exceptions to be caught and finally blocks must run
2. We must not call the unmanaged exception handler if the first unwinding pass
   detects that there is a native-to-managed wrapper and a callback is
   installed that will handle managed exceptions (by unwinding the native code
   and either re-raising the managed exception or otherwise dealing with it).

So we change the return value of handle_exception_first_pass to be:
- MONO_FIRST_PASS_HANDLED - caught by a managed frame (with a matching filter if one was present).
- MONO_FIRST_PASS_CALLBACK_TO_NATIVE - there's a native-to-managed wrapper and a ftnptr_eh_callback is installed.
- MONO_FIRST_PASS_UNHANDLED - first pass unwound all the way and there was no wrapper or there was no callback installed.

In mono_handle_exception_internal, we treat MONO_FIRST_PASS_CALLBACK_TO_NATIVE
as handled and unwind the managed stack, invoking finally clauses as we go,
until we reach the managed-to-native frame, at which point we invoke the callback.
@lambdageek
Copy link
Member

lambdageek commented Mar 22, 2018

Updated:
I added the three kinds of first pass results from my comment, above.

Also added a test case using setjmp/longjmp for the native callbacks.

@lambdageek lambdageek self-assigned this Mar 22, 2018
@lambdageek
Copy link
Member

The new test needs some love to work in the FullAOT configuration.

@lambdageek
Copy link
Member

Linux ARMv5, Linux ARMv7 and Linux i386 failures are all #7578.

@lambdageek
Copy link
Member

@monojenkins bulid failed

@lambdageek
Copy link
Member

@luhenry One more review?

}

if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED && ftnptr_eh_callback) {
result = MONO_FIRST_PASS_CALLBACK_TO_NATIVE;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know the code enough but shouldn't we return here? It seems to me that, since we found a native-to-managed frame, we don't want to continue looking for a clause that could handle the exception. We don't want to call the callback just yet either because we want to run the finalizers, but the first pass should be finished here IIUC.

@vargaz @alexanderkyte @kumpera could you please confirm? Thank you.

Copy link
Contributor

@luhenry luhenry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could go for returning TRUE in handle_exception_first_pass and simply keep checks for the wrspper_type in both first and second pass (the first pass would just stop trying to find a catch clause, the second would call the callback)

if (unhandled)
mono_debugger_agent_handle_exception ((MonoException *)obj, ctx, NULL, NULL);
else
else if (res != MONO_FIRST_PASS_CALLBACK_TO_NATIVE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we got here, res is MONO_FIRST_PASS_UNHANDLED so this check seems redundant.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so - this is all in the else branch of if (res == MONO_FIRST_PASS_UNHANDLED), so it could be handled or passed to native.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, misread the code.

free_stack = 0xffffff;
}

if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED && ftnptr_eh_callback) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MONO_FIRST_PASS_CALLBACK_TO_NATIVE doesn't seem necessary since returning TRUE from handle_exception_first_pass and checking for the wrapper_type again has the same effect.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the difference might be in how/when we signal the debugger... we don't want MONO_FIRST_PASS_CALLBACK_TO_NATIVE to tell the debugger that we're handling the exception since the callback is kind of internal to the embedder.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But obviously I haven't written a test for that part, so I'm just guessing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I based this comment on https://github.com/mono/mono/pull/5837/files#r176922871 which was wrong, so please ignore this review.

free_stack = 0xffffff;
}

if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED && ftnptr_eh_callback) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I based this comment on https://github.com/mono/mono/pull/5837/files#r176922871 which was wrong, so please ignore this review.

@lambdageek
Copy link
Member

@monojenkins build failed

@lambdageek lambdageek merged commit b20d5ce into mono:master Mar 26, 2018
lewurm pushed a commit to lewurm/mono that referenced this pull request May 30, 2018
…no#5837)

* [runtime] Rework how the native-to-managed eh callback is called.

Instead of catching all exceptions in the native-to-managed wrapper and calling the callback from the catch clause, call it directly from the EH code. The
previous approach caught all exceptions, which messed up debugging, since the debugger wouldn't break on uncaught exceptions raised from native-to-managed
callbacks.

* [coop] Enable tests bug-58782-plain-throw and bug-58782-capture-and-throw for PRs

* [mini] Installed native-to-managed exception handling callback from second pass.

The constraints are:
1. We must allow managed exceptions to be caught and finally blocks must run
2. We must not call the unmanaged exception handler if the first unwinding pass
   detects that there is a native-to-managed wrapper and a callback is
   installed that will handle managed exceptions (by unwinding the native code
   and either re-raising the managed exception or otherwise dealing with it).

So we change the return value of handle_exception_first_pass to be:
- MONO_FIRST_PASS_HANDLED - caught by a managed frame (with a matching filter if one was present).
- MONO_FIRST_PASS_CALLBACK_TO_NATIVE - there's a native-to-managed wrapper and a ftnptr_eh_callback is installed.
- MONO_FIRST_PASS_UNHANDLED - first pass unwound all the way and there was no wrapper or there was no callback installed.

In mono_handle_exception_internal, we treat MONO_FIRST_PASS_CALLBACK_TO_NATIVE
as handled and unwind the managed stack, invoking finally clauses as we go,
until we reach the managed-to-native frame, at which point we invoke the callback.

* [test] Test for mono_install_ftnptr_eh_callback

* [test] AOT-friendly test for mono_install_ftnptr_eh_callback
lewurm pushed a commit to lewurm/mono that referenced this pull request Jun 1, 2018
…no#5837)

* [runtime] Rework how the native-to-managed eh callback is called.

Instead of catching all exceptions in the native-to-managed wrapper and calling the callback from the catch clause, call it directly from the EH code. The
previous approach caught all exceptions, which messed up debugging, since the debugger wouldn't break on uncaught exceptions raised from native-to-managed
callbacks.

* [coop] Enable tests bug-58782-plain-throw and bug-58782-capture-and-throw for PRs

* [mini] Installed native-to-managed exception handling callback from second pass.

The constraints are:
1. We must allow managed exceptions to be caught and finally blocks must run
2. We must not call the unmanaged exception handler if the first unwinding pass
   detects that there is a native-to-managed wrapper and a callback is
   installed that will handle managed exceptions (by unwinding the native code
   and either re-raising the managed exception or otherwise dealing with it).

So we change the return value of handle_exception_first_pass to be:
- MONO_FIRST_PASS_HANDLED - caught by a managed frame (with a matching filter if one was present).
- MONO_FIRST_PASS_CALLBACK_TO_NATIVE - there's a native-to-managed wrapper and a ftnptr_eh_callback is installed.
- MONO_FIRST_PASS_UNHANDLED - first pass unwound all the way and there was no wrapper or there was no callback installed.

In mono_handle_exception_internal, we treat MONO_FIRST_PASS_CALLBACK_TO_NATIVE
as handled and unwind the managed stack, invoking finally clauses as we go,
until we reach the managed-to-native frame, at which point we invoke the callback.

* [test] Test for mono_install_ftnptr_eh_callback

* [test] AOT-friendly test for mono_install_ftnptr_eh_callback
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…no/mono#5837)

* [runtime] Rework how the native-to-managed eh callback is called.

Instead of catching all exceptions in the native-to-managed wrapper and calling the callback from the catch clause, call it directly from the EH code. The
previous approach caught all exceptions, which messed up debugging, since the debugger wouldn't break on uncaught exceptions raised from native-to-managed
callbacks.

* [coop] Enable tests bug-58782-plain-throw and bug-58782-capture-and-throw for PRs

* [mini] Installed native-to-managed exception handling callback from second pass.

The constraints are:
1. We must allow managed exceptions to be caught and finally blocks must run
2. We must not call the unmanaged exception handler if the first unwinding pass
   detects that there is a native-to-managed wrapper and a callback is
   installed that will handle managed exceptions (by unwinding the native code
   and either re-raising the managed exception or otherwise dealing with it).

So we change the return value of handle_exception_first_pass to be:
- MONO_FIRST_PASS_HANDLED - caught by a managed frame (with a matching filter if one was present).
- MONO_FIRST_PASS_CALLBACK_TO_NATIVE - there's a native-to-managed wrapper and a ftnptr_eh_callback is installed.
- MONO_FIRST_PASS_UNHANDLED - first pass unwound all the way and there was no wrapper or there was no callback installed.

In mono_handle_exception_internal, we treat MONO_FIRST_PASS_CALLBACK_TO_NATIVE
as handled and unwind the managed stack, invoking finally clauses as we go,
until we reach the managed-to-native frame, at which point we invoke the callback.

* [test] Test for mono_install_ftnptr_eh_callback

* [test] AOT-friendly test for mono_install_ftnptr_eh_callback


Commit migrated from mono/mono@b20d5ce
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants