-
Notifications
You must be signed in to change notification settings - Fork 3.8k
[runtime] Rework how the native-to-managed eh callback is called. #5837
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
Changes from all commits
e1735dc
3fdef26
fe6852c
bf33699
10fa108
06da2cf
a20760c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -112,6 +112,8 @@ static gpointer restore_context_func, call_filter_func; | |
| static gpointer throw_exception_func, rethrow_exception_func; | ||
| static gpointer throw_corlib_exception_func; | ||
|
|
||
| static MonoFtnPtrEHCallback ftnptr_eh_callback; | ||
|
|
||
| static void mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain *domain, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data); | ||
| static void mono_raise_exception_with_ctx (MonoException *exc, MonoContext *ctx); | ||
| static void mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data); | ||
|
|
@@ -1602,14 +1604,24 @@ setup_stack_trace (MonoException *mono_ex, GSList *dynamic_methods, GList **trac | |
| *trace_ips = NULL; | ||
| } | ||
|
|
||
| typedef enum { | ||
| MONO_FIRST_PASS_UNHANDLED, | ||
| MONO_FIRST_PASS_CALLBACK_TO_NATIVE, | ||
| MONO_FIRST_PASS_HANDLED, | ||
| } MonoFirstPassResult; | ||
|
|
||
| /* | ||
| * handle_exception_first_pass: | ||
| * | ||
| * The first pass of exception handling. Unwind the stack until a catch clause which can catch | ||
| * OBJ is found. Store the index of the filter clause which caught the exception into | ||
| * OUT_FILTER_IDX. Return TRUE if the exception is caught, FALSE otherwise. | ||
| * The first pass of exception handling. Unwind the stack until a catch | ||
| * clause which can catch OBJ is found. Store the index of the filter clause | ||
| * which caught the exception into OUT_FILTER_IDX. Return | ||
| * \c MONO_FIRST_PASS_HANDLED if the exception is caught, | ||
| * \c MONO_FIRST_PASS_UNHANDLED otherwise, unless there is a native-to-managed | ||
| * wrapper and an exception handling callback is installed (in which case | ||
| * return \c MONO_FIRST_PASS_CALLBACK_TO_NATIVE). | ||
| */ | ||
| static gboolean | ||
| static MonoFirstPassResult | ||
| handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filter_idx, MonoJitInfo **out_ji, MonoJitInfo **out_prev_ji, MonoObject *non_exception, StackFrameInfo *catch_frame) | ||
| { | ||
| ERROR_DECL (error); | ||
|
|
@@ -1631,6 +1643,8 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt | |
| Unwinder unwinder; | ||
| gboolean in_interp; | ||
|
|
||
| MonoFirstPassResult result = MONO_FIRST_PASS_UNHANDLED; | ||
|
|
||
| g_assert (ctx != NULL); | ||
|
|
||
| if (obj == (MonoObject *)domain->stack_overflow_ex) | ||
|
|
@@ -1686,7 +1700,7 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt | |
| if (!unwind_res) { | ||
| setup_stack_trace (mono_ex, dynamic_methods, &trace_ips); | ||
| g_slist_free (dynamic_methods); | ||
| return FALSE; | ||
| return result; | ||
| } | ||
|
|
||
| switch (frame.type) { | ||
|
|
@@ -1740,6 +1754,11 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt | |
| } else { | ||
| free_stack = 0xffffff; | ||
| } | ||
|
|
||
| if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED && ftnptr_eh_callback) { | ||
| result = MONO_FIRST_PASS_CALLBACK_TO_NATIVE; | ||
| } | ||
|
|
||
|
|
||
| for (i = clause_index_start; i < ji->num_clauses; i++) { | ||
| MonoJitExceptionInfo *ei = &ji->clauses [i]; | ||
|
|
@@ -1818,7 +1837,8 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt | |
| MONO_CONTEXT_SET_IP (ctx, ei->handler_start); | ||
| frame.native_offset = (char*)ei->handler_start - (char*)ji->code_start; | ||
| *catch_frame = frame; | ||
| return TRUE; | ||
| result = MONO_FIRST_PASS_HANDLED; | ||
| return result; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1836,7 +1856,8 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt | |
| MONO_CONTEXT_SET_IP (ctx, ei->handler_start); | ||
| frame.native_offset = (char*)ei->handler_start - (char*)ji->code_start; | ||
| *catch_frame = frame; | ||
| return TRUE; | ||
| result = MONO_FIRST_PASS_HANDLED; | ||
| return result; | ||
| } | ||
| mono_error_cleanup (&isinst_error); | ||
| } | ||
|
|
@@ -1962,8 +1983,6 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu | |
| memcpy (&jit_tls->orig_ex_ctx, ctx, sizeof (MonoContext)); | ||
|
|
||
| if (!resume) { | ||
| gboolean res; | ||
|
|
||
| MonoContext ctx_cp = *ctx; | ||
| if (mono_trace_is_enabled ()) { | ||
| MonoMethod *system_exception_get_message = mono_class_get_method_from_name (mono_defaults.exception_class, "get_Message", 0); | ||
|
|
@@ -2003,9 +2022,10 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu | |
| jit_tls->orig_ex_ctx_set = FALSE; | ||
|
|
||
| StackFrameInfo catch_frame; | ||
| MonoFirstPassResult res; | ||
| res = handle_exception_first_pass (&ctx_cp, obj, &first_filter_idx, &ji, &prev_ji, non_exception, &catch_frame); | ||
|
|
||
| if (!res) { | ||
| if (res == MONO_FIRST_PASS_UNHANDLED) { | ||
| if (mini_get_debug_options ()->break_on_exc) | ||
| G_BREAKPOINT (); | ||
| mono_debugger_agent_handle_exception ((MonoException *)obj, ctx, NULL, NULL); | ||
|
|
@@ -2037,7 +2057,7 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu | |
|
|
||
| if (unhandled) | ||
| mono_debugger_agent_handle_exception ((MonoException *)obj, ctx, NULL, NULL); | ||
| else | ||
| else if (res != MONO_FIRST_PASS_CALLBACK_TO_NATIVE) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we got here, res is
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think so - this is all in the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yes, misread the code. |
||
| mono_debugger_agent_handle_exception ((MonoException *)obj, ctx, &ctx_cp, &catch_frame); | ||
| } | ||
| } | ||
|
|
@@ -2108,6 +2128,15 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu | |
| free_stack = 0xffffff; | ||
| } | ||
|
|
||
| if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED && ftnptr_eh_callback) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
| guint32 handle = mono_gchandle_new (obj, FALSE); | ||
| gpointer stackptr; | ||
|
|
||
| mono_threads_enter_gc_safe_region_unbalanced (&stackptr); | ||
| ftnptr_eh_callback (handle); | ||
| g_error ("Did not expect ftnptr_eh_callback to return."); | ||
| } | ||
|
|
||
| for (i = clause_index_start; i < ji->num_clauses; i++) { | ||
| MonoJitExceptionInfo *ei = &ji->clauses [i]; | ||
| gboolean filtered = FALSE; | ||
|
|
@@ -3189,6 +3218,21 @@ mono_jinfo_get_epilog_size (MonoJitInfo *ji) | |
| return info->epilog_size; | ||
| } | ||
|
|
||
| /* | ||
| * mono_install_ftnptr_eh_callback: | ||
| * | ||
| * Install a callback that should be called when there is a managed exception | ||
| * in a native-to-managed wrapper. This is mainly used by iOS to convert a | ||
| * managed exception to a native exception, to properly unwind the native | ||
| * stack; this native exception will then be converted back to a managed | ||
| * exception in their managed-to-native wrapper. | ||
| */ | ||
| void | ||
| mono_install_ftnptr_eh_callback (MonoFtnPtrEHCallback callback) | ||
| { | ||
| ftnptr_eh_callback = callback; | ||
| } | ||
|
|
||
| /* | ||
| * LLVM/Bitcode exception handling. | ||
| */ | ||
|
|
||
There was a problem hiding this comment.
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.