Mixed mode exception handling (#4777)
authorZoltan Varga <vargaz@gmail.com>
Wed, 3 May 2017 04:45:59 +0000 (00:45 -0400)
committerGitHub <noreply@github.com>
Wed, 3 May 2017 04:45:59 +0000 (00:45 -0400)
* [mixed] Add beginnings of unwinding support for interp->jit transitions by pushing an LMF frame on the stack when exiting interpreted code.

* [runtime] Add a mono_debug_lookup_source_location_by_il () helper function to lookup a source location using an IL offset.

* [mixed] Add support for mixed mode managed stack walks.

* [interp] Generate line number info for interpreted code, using the same MonoDebugMethodJitInfo structure used by the JIT.

* [interp] Pass the clause index to the MINT_ENDFINALLY opcode. Not yet used.

* [interp] Create a MonoJitInfo structure for each interpreted method. Not yet used.

* [interp] Add a mono_interp_set_resume_state () function which can be used to set the frame/ip the interpreter will resume execution from when execution returns to it.

* [mixed] Add support for mixed mode exception handling by extending the normal JIT exception handling code in mini-exceptions.c to handle interpreted frames as well.

* [jit] Add an Unwinder type to reduce the amount of code duplication when unwinding though interpreter frames.

* [interp] Fix the !ENABLE_INTERPRETER build.

* [interp] Disable an assert which is hit even when running without --interpreter.

* Fix a comment.

* [interp] Fix an assertion. Fix/add comments.

16 files changed:
mono/metadata/debug-internals.h
mono/metadata/mono-debug.c
mono/metadata/object.c
mono/mini/exceptions-amd64.c
mono/mini/interp/interp-internals.h
mono/mini/interp/interp.c
mono/mini/interp/interp.h
mono/mini/interp/mintops.def
mono/mini/interp/transform.c
mono/mini/mini-amd64.h
mono/mini/mini-exceptions.c
mono/mini/mini-generic-sharing.c
mono/mini/mini-runtime.c
mono/mini/mini.h
mono/mini/mixed.cs
mono/utils/mono-stack-unwinding.h

index 1d5e33cf2504f07ff6924f3900e62d296bba4400..5ac4f103adf48a874e10f09a3377cc0fbe05511e 100644 (file)
@@ -84,4 +84,7 @@ mono_debug_free_method_async_debug_info (MonoDebugMethodAsyncInfo *info);
 gboolean
 mono_debug_image_has_debug_info (MonoImage *image);
 
+MonoDebugSourceLocation *
+mono_debug_lookup_source_location_by_il (MonoMethod *method, guint32 il_offset, MonoDomain *domain);
+
 #endif /* __DEBUG_INTERNALS_H__ */
index b1a40b6ae5f2eb1c21f210c680971e67d8b916b0..4230399ec10242c2c3d339e80ed8271981bb4f96 100644 (file)
@@ -793,6 +793,40 @@ mono_debug_lookup_source_location (MonoMethod *method, guint32 address, MonoDoma
        return location;
 }
 
+/**
+ * mono_debug_lookup_source_location_by_il:
+ *
+ *   Same as mono_debug_lookup_source_location but take an IL_OFFSET argument.
+ */
+MonoDebugSourceLocation *
+mono_debug_lookup_source_location_by_il (MonoMethod *method, guint32 il_offset, MonoDomain *domain)
+{
+       MonoDebugMethodInfo *minfo;
+       MonoDebugSourceLocation *location;
+
+       if (mono_debug_format == MONO_DEBUG_FORMAT_NONE)
+               return NULL;
+
+       mono_debugger_lock ();
+       minfo = mono_debug_lookup_method_internal (method);
+       if (!minfo || !minfo->handle) {
+               mono_debugger_unlock ();
+               return NULL;
+       }
+
+       if (!minfo->handle->ppdb && (!minfo->handle->symfile || !mono_debug_symfile_is_loaded (minfo->handle->symfile))) {
+               mono_debugger_unlock ();
+               return NULL;
+       }
+
+       if (minfo->handle->ppdb)
+               location = mono_ppdb_lookup_location (minfo, il_offset);
+       else
+               location = mono_debug_symfile_lookup_location (minfo, il_offset);
+       mono_debugger_unlock ();
+       return location;
+}
+
 MonoDebugSourceLocation *
 mono_debug_method_lookup_location (MonoDebugMethodInfo *minfo, int il_offset)
 {
index c23c56bafb87838a93505e21cb009b262b8edace..6c3466099d95879377f875348b1a844ffe66c7b6 100644 (file)
@@ -7826,7 +7826,7 @@ mono_delegate_ctor_with_method (MonoObject *this_obj, MonoObject *target, gpoint
                g_assert (method);
                method = mono_marshal_get_remoting_invoke (method);
 #ifdef ENABLE_INTERPRETER
-               g_error ("need RuntimeMethod in method_ptr when using interpreter");
+               //g_error ("need RuntimeMethod in method_ptr when using interpreter");
 #endif
                delegate->method_ptr = mono_compile_method_checked (method, error);
                return_val_if_nok (error, FALSE);
index 846ad9744f59c5f30b8987af7d8ac4f822ba4ec2..e02cdb8b8b25e4069eb0410d71210844740119ae 100644 (file)
@@ -582,20 +582,24 @@ mono_arch_unwind_frame (MonoDomain *domain, MonoJitTlsData *jit_tls,
                guint64 rip;
 
                if (((guint64)(*lmf)->previous_lmf) & 2) {
-                       /* 
-                        * This LMF entry is created by the soft debug code to mark transitions to
-                        * managed code done during invokes.
-                        */
                        MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
 
-                       g_assert (ext->debugger_invoke);
-
-                       memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
+                       if (ext->debugger_invoke) {
+                               /*
+                                * This LMF entry is created by the soft debug code to mark transitions to
+                                * managed code done during invokes.
+                                */
+                               frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
+                               memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
+                       } else if (ext->interp_exit) {
+                               frame->type = FRAME_TYPE_INTERP_TO_MANAGED;
+                               frame->interp_exit_data = ext->interp_exit_data;
+                       } else {
+                               g_assert_not_reached ();
+                       }
 
                        *lmf = (MonoLMF *)(((guint64)(*lmf)->previous_lmf) & ~7);
 
-                       frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
-
                        return TRUE;
                }
 
index 9769855efac02efd040d90d6e8e1a3ac1125080c..692925187816f4a93216e88ced07b2036277fbfc 100644 (file)
@@ -7,6 +7,7 @@
 #include <mono/metadata/object.h>
 #include <mono/metadata/domain-internals.h>
 #include <mono/metadata/class-internals.h>
+#include <mono/metadata/debug-internals.h>
 #include "config.h"
 
 enum {
@@ -88,6 +89,7 @@ typedef struct _RuntimeMethod
        gpointer jit_entry;
        MonoType *rtype;
        MonoType **param_types;
+       MonoJitInfo *jinfo;
 } RuntimeMethod;
 
 struct _MonoInvocation {
@@ -115,6 +117,13 @@ typedef struct {
        jmp_buf *current_env;
        unsigned char search_for_handler;
        unsigned char managed_code;
+
+       /* Resume state for resuming execution in mixed mode */
+       gboolean       has_resume_state;
+       /* Frame to resume execution at */
+       MonoInvocation *handler_frame;
+       /* IP to resume execution at */
+       gpointer handler_ip;
 } ThreadContext;
 
 extern int mono_interp_traceopt;
index 0c0b651f563c3665e203acbd56013d220fdc17ab..1b74cc75f5eaafedd33bf643f81cc3511d4b8547 100644 (file)
@@ -107,7 +107,7 @@ void ves_exec_method (MonoInvocation *frame);
 static char* dump_stack (stackval *stack, stackval *sp);
 static char* dump_frame (MonoInvocation *inv);
 static MonoArray *get_trace_ips (MonoDomain *domain, MonoInvocation *top);
-static void ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, unsigned short *start_with_ip, MonoException *filter_exception);
+static void ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, unsigned short *start_with_ip, MonoException *filter_exception, int exit_at_finally);
 
 typedef void (*ICallMethod) (MonoInvocation *frame);
 
@@ -148,7 +148,8 @@ db_match_method (gpointer data, gpointer user_data)
                break_on_method = 1;
 }
 
-static void debug_enter (MonoInvocation *frame, int *tracing)
+static void
+debug_enter (MonoInvocation *frame, int *tracing)
 {
        if (db_methods) {
                g_list_foreach (db_methods, db_match_method, (gpointer)frame->runtime_method->method);
@@ -198,6 +199,17 @@ static void debug_enter (MonoInvocation *frame, int *tracing)
 
 #endif
 
+/* Set the current execution state to the resume state in context */
+#define SET_RESUME_STATE(context) do { \
+               ip = (context)->handler_ip;                                             \
+               sp->data.p = frame->ex;                                                                                 \
+               ++sp;                                                                                                                   \
+               frame->ex = NULL;                                                                                               \
+               (context)->has_resume_state = 0;                                                                \
+               (context)->handler_frame = NULL;                                                                \
+               goto main_loop;                                                                                                 \
+       } while (0)
+
 static void
 interp_ex_handler (MonoException *ex) {
        MonoError error;
@@ -902,6 +914,7 @@ ves_pinvoke_method (MonoInvocation *frame, MonoMethodSignature *sig, MonoFuncV a
        MonoInvocation *old_frame = context->current_frame;
        MonoInvocation *old_env_frame = context->env_frame;
        jmp_buf *old_env = context->current_env;
+       MonoLMFExt ext;
 
        if (setjmp (env)) {
                context->current_frame = old_frame;
@@ -932,8 +945,20 @@ ves_pinvoke_method (MonoInvocation *frame, MonoMethodSignature *sig, MonoFuncV a
        context->current_frame = frame;
        context->managed_code = 0;
 
+       /*
+        * Push an LMF frame on the LMF stack
+        * to mark the transition to native code.
+        */
+       memset (&ext, 0, sizeof (ext));
+       ext.interp_exit = TRUE;
+       ext.interp_exit_data = frame;
+
+       mono_push_lmf (&ext);
+
        mono_interp_enter_icall_trampoline (addr, margs);
 
+       mono_pop_lmf (&ext.lmf);
+
        context->managed_code = 1;
        /* domain can only be changed by native code */
        context->domain = mono_domain_get ();
@@ -1301,12 +1326,10 @@ mono_interp_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoOb
 
        if (context == NULL) {
                context = &context_struct;
+               memset (context, 0, sizeof (ThreadContext));
                context_struct.base_frame = &frame;
-               context_struct.current_frame = NULL;
                context_struct.env_frame = &frame;
                context_struct.current_env = &env;
-               context_struct.search_for_handler = 0;
-               context_struct.managed_code = 0;
                mono_native_tls_set_value (thread_context_id, context);
        }
        else
@@ -1427,7 +1450,7 @@ handle_enum:
        if (exc)
                frame.invoke_trap = 1;
        context->managed_code = 1;
-       ves_exec_method_with_context (&frame, context, NULL, NULL);
+       ves_exec_method_with_context (&frame, context, NULL, NULL, -1);
        context->managed_code = 0;
        if (context == &context_struct)
                mono_native_tls_set_value (thread_context_id, NULL);
@@ -1567,7 +1590,7 @@ interp_entry (InterpEntryData *data)
                }
        }
 
-       init_frame (&frame, context->current_frame, data->rmethod, args, &result);
+       init_frame (&frame, NULL, data->rmethod, args, &result);
        context->managed_code = 1;
 
        type = rmethod->rtype;
@@ -1583,7 +1606,7 @@ interp_entry (InterpEntryData *data)
                break;
        }
 
-       ves_exec_method_with_context (&frame, context, NULL, NULL);
+       ves_exec_method_with_context (&frame, context, NULL, NULL, -1);
        context->managed_code = 0;
        if (context == &context_struct)
                mono_native_tls_set_value (thread_context_id, NULL);
@@ -2013,8 +2036,11 @@ static int opcode_counts[512];
 #define MINT_IN_DEFAULT default:
 #endif
 
+/*
+ * If EXIT_AT_FINALLY is not -1, exit after exiting the finally clause with that index.
+ */
 static void 
-ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, unsigned short *start_with_ip, MonoException *filter_exception)
+ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, unsigned short *start_with_ip, MonoException *filter_exception, int exit_at_finally)
 {
        MonoInvocation child_frame;
        GSList *finally_ips = NULL;
@@ -2266,10 +2292,17 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, uns
                                }
                        }
 
-                       ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+                       ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
 
                        context->current_frame = frame;
 
+                       if (context->has_resume_state) {
+                               if (frame == context->handler_frame)
+                                       SET_RESUME_STATE (context);
+                               else
+                                       goto exit_frame;
+                       }
+
                        if (child_frame.ex) {
                                /*
                                 * An exception occurred, need to run finally, fault and catch handlers..
@@ -2310,6 +2343,13 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, uns
 
                        context->current_frame = frame;
 
+                       if (context->has_resume_state) {
+                               if (frame == context->handler_frame)
+                                       SET_RESUME_STATE (context);
+                               else
+                                       goto exit_frame;
+                       }
+
                        if (child_frame.ex) {
                                /*
                                 * An exception occurred, need to run finally, fault and catch handlers..
@@ -2350,10 +2390,17 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, uns
                                mono_error_cleanup (&error); /* FIXME: don't swallow the error */
                        }
 
-                       ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+                       ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
 
                        context->current_frame = frame;
 
+                       if (context->has_resume_state) {
+                               if (frame == context->handler_frame)
+                                       SET_RESUME_STATE (context);
+                               else
+                                       goto exit_frame;
+                       }
+
                        if (child_frame.ex) {
                                /*
                                 * An exception occurred, need to run finally, fault and catch handlers..
@@ -2390,10 +2437,17 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, uns
                                mono_error_cleanup (&error); /* FIXME: don't swallow the error */
                        }
 
-                       ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+                       ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
 
                        context->current_frame = frame;
 
+                       if (context->has_resume_state) {
+                               if (frame == context->handler_frame)
+                                       SET_RESUME_STATE (context);
+                               else
+                                       goto exit_frame;
+                       }
+
                        if (child_frame.ex) {
                                /*
                                 * An exception occurred, need to run finally, fault and catch handlers..
@@ -2410,6 +2464,7 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, uns
                        MonoFtnDesc ftndesc;
                        guint8 res_buf [256];
                        MonoType *type;
+                       MonoLMFExt ext;
 
                        //printf ("%s\n", mono_method_full_name (rmethod->method, 1));
 
@@ -2506,6 +2561,16 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, uns
                                }
                        }
 
+                       /*
+                        * Push an LMF frame on the LMF stack
+                        * to mark the transition to compiled code.
+                        */
+                       memset (&ext, 0, sizeof (ext));
+                       ext.interp_exit = TRUE;
+                       ext.interp_exit_data = frame;
+
+                       mono_push_lmf (&ext);
+
                        switch (pindex) {
                        case 0: {
                                void (*func)(gpointer) = rmethod->jit_wrapper;
@@ -2560,6 +2625,21 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, uns
                                break;
                        }
 
+                       mono_pop_lmf (&ext.lmf);
+
+                       if (context->has_resume_state) {
+                               /*
+                                * If this bit is set, it means the call has thrown the exception, and we
+                                * reached this point because the EH code in mono_handle_exception ()
+                                * unwound all the JITted frames below us. mono_interp_set_resume_state ()
+                                * has set the fields in context to indicate where we have to resume execution.
+                                */
+                               if (frame == context->handler_frame)
+                                       SET_RESUME_STATE (context);
+                               else
+                                       goto exit_frame;
+                       }
+
                        MonoType *rtype = rmethod->rtype;
                        switch (rtype->type) {
                        case MONO_TYPE_VOID:
@@ -2640,10 +2720,17 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, uns
                                sp [0].data.p = unboxed;
                        }
 
-                       ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+                       ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
 
                        context->current_frame = frame;
 
+                       if (context->has_resume_state) {
+                               if (frame == context->handler_frame)
+                                       SET_RESUME_STATE (context);
+                               else
+                                       goto exit_frame;
+                       }
+
                        if (child_frame.ex) {
                                /*
                                 * An exception occurred, need to run finally, fault and catch handlers..
@@ -2687,10 +2774,17 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, uns
                                sp [0].data.p = unboxed;
                        }
 
-                       ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+                       ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
 
                        context->current_frame = frame;
 
+                       if (context->has_resume_state) {
+                               if (frame == context->handler_frame)
+                                       SET_RESUME_STATE (context);
+                               else
+                                       goto exit_frame;
+                       }
+
                        if (child_frame.ex) {
                                /*
                                 * An exception occurred, need to run finally, fault and catch handlers..
@@ -3477,10 +3571,17 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, uns
 
                        g_assert (csig->call_convention == MONO_CALL_DEFAULT);
 
-                       ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+                       ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
 
                        context->current_frame = frame;
 
+                       if (context->has_resume_state) {
+                               if (frame == context->handler_frame)
+                                       SET_RESUME_STATE (context);
+                               else
+                                       goto exit_frame;
+                       }
+
                        if (child_frame.ex) {
                                /*
                                 * An exception occurred, need to run finally, fault and catch handlers..
@@ -4266,6 +4367,10 @@ array_constructed:
                        BINOP_CAST(l, -, guint64);
                        MINT_IN_BREAK;
                MINT_IN_CASE(MINT_ENDFINALLY)
+                       ip ++;
+                       int clause_index = *ip;
+                       if (clause_index == exit_at_finally)
+                               goto exit_frame;
                        while (sp > frame->stack) {
                                --sp;
                        }
@@ -4739,7 +4844,7 @@ array_constructed:
                                        stackval retval;
                                        memcpy (&dup_frame, inv, sizeof (MonoInvocation));
                                        dup_frame.retval = &retval;
-                                       ves_exec_method_with_context (&dup_frame, context, inv->runtime_method->code + clause->data.filter_offset, frame->ex);
+                                       ves_exec_method_with_context (&dup_frame, context, inv->runtime_method->code + clause->data.filter_offset, frame->ex, -1);
                                        if (dup_frame.retval->data.i) {
 #if DEBUG_INTERP
                                                if (tracing)
@@ -4917,7 +5022,7 @@ ves_exec_method (MonoInvocation *frame)
        frame->runtime_method = mono_interp_get_runtime_method (context->domain, frame->method, &error);
        mono_error_cleanup (&error); /* FIXME: don't swallow the error */
        context->managed_code = 1;
-       ves_exec_method_with_context (frame, context, NULL, NULL);
+       ves_exec_method_with_context (frame, context, NULL, NULL, -1);
        context->managed_code = 0;
        if (frame->ex) {
                if (context != &context_struct && context->current_env) {
@@ -5264,3 +5369,77 @@ mono_interp_regression_list (int verbose, int count, char *images [])
        return total;
 }
 
+/*
+ * mono_interp_set_resume_state:
+ *
+ *   Set the state the interpeter will continue to execute from after execution returns to the interpreter.
+ */
+void
+mono_interp_set_resume_state (MonoException *ex, StackFrameInfo *frame, gpointer handler_ip)
+{
+       ThreadContext *context = mono_native_tls_get_value (thread_context_id);
+
+       context->has_resume_state = TRUE;
+       context->handler_frame = frame->interp_frame;
+       /* This is on the stack, so it doesn't need a wbarrier */
+       context->handler_frame->ex = ex;
+       context->handler_ip = handler_ip;
+}
+
+/*
+ * mono_interp_run_finally:
+ *
+ *   Run the finally clause identified by CLAUSE_INDEX in the intepreter frame given by
+ * frame->interp_frame.
+ */
+void
+mono_interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip)
+{
+       MonoInvocation *iframe = frame->interp_frame;
+       ThreadContext *context = mono_native_tls_get_value (thread_context_id);
+
+       ves_exec_method_with_context (iframe, context, handler_ip, NULL, clause_index);
+}
+
+typedef struct {
+       MonoInvocation *current;
+} StackIter;
+
+/*
+ * mono_interp_frame_iter_init:
+ *
+ *   Initialize an iterator for iterating through interpreted frames.
+ */
+void
+mono_interp_frame_iter_init (MonoInterpStackIter *iter, gpointer interp_exit_data)
+{
+       StackIter *stack_iter = (StackIter*)iter;
+
+       stack_iter->current = (MonoInvocation*)interp_exit_data;
+}
+
+gboolean
+mono_interp_frame_iter_next (MonoInterpStackIter *iter, StackFrameInfo *frame)
+{
+       StackIter *stack_iter = (StackIter*)iter;
+       MonoInvocation *iframe = stack_iter->current;
+
+       memset (frame, 0, sizeof (StackFrameInfo));
+       /* pinvoke frames doesn't have runtime_method set */
+       while (iframe && !iframe->runtime_method)
+               iframe = iframe->parent;
+       if (!iframe)
+               return FALSE;
+
+       frame->type = FRAME_TYPE_INTERP;
+       frame->interp_frame = iframe;
+       frame->method = iframe->runtime_method->method;
+       frame->actual_method = frame->method;
+       /* This is the offset in the interpreter IR */
+       frame->native_offset = iframe->ip - iframe->runtime_method->code;
+       frame->ji = iframe->runtime_method->jinfo;
+
+       stack_iter->current = iframe->parent;
+
+       return TRUE;
+}
index 951c47c85e1a416fa8bcbad7e87d0263ccff3228..8d83bf401495426f661669c63ce4efc86c3aa531 100644 (file)
@@ -6,6 +6,13 @@
 #define __MONO_MINI_INTERPRETER_H__
 #include <mono/mini/mini.h>
 
+typedef struct _MonoInterpStackIter MonoInterpStackIter;
+
+/* Needed for stack allocation */
+struct _MonoInterpStackIter {
+       gpointer dummy [8];
+};
+
 int
 mono_interp_regression_list (int verbose, int count, char *images []);
 
@@ -29,4 +36,17 @@ mono_interp_parse_options (const char *options);
 
 void
 interp_walk_stack_with_ctx (MonoInternalStackWalk func, MonoContext *ctx, MonoUnwindOptions options, void *user_data);
+
+void
+mono_interp_set_resume_state (MonoException *ex, StackFrameInfo *frame, gpointer handler_ip);
+
+void
+mono_interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip);
+
+void
+mono_interp_frame_iter_init (MonoInterpStackIter *iter, gpointer interp_exit_data);
+
+gboolean
+mono_interp_frame_iter_next (MonoInterpStackIter *iter, StackFrameInfo *frame);
+
 #endif /* __MONO_MINI_INTERPRETER_H__ */
index 6e3e4dbd9e17adfddc57cc1da259a2d607a77933..68a559f6db5c0c969d75dfdc5728760236af5d0b 100644 (file)
@@ -172,7 +172,7 @@ OPDEF(MINT_LEAVE_S, "leave.s", 2, MintOpShortBranch)
 
 OPDEF(MINT_THROW, "throw", 1, MintOpNoArgs)
 OPDEF(MINT_RETHROW, "rethrow", 1, MintOpNoArgs)
-OPDEF(MINT_ENDFINALLY, "endfinally", 1, MintOpNoArgs)
+OPDEF(MINT_ENDFINALLY, "endfinally", 2, MintOpNoArgs)
 
 OPDEF(MINT_BRFALSE_I4, "brfalse.i4", 3, MintOpBranch)
 OPDEF(MINT_BRFALSE_I8, "brfalse.i8", 3, MintOpBranch)
index d76c9af55a7106f54ad06fd220174e5a14bdf4fd..182ef3ac84f5a9d52b604c6c6931d7ac6a9c5aa1 100644 (file)
@@ -64,6 +64,7 @@ typedef struct
        int max_data_items;
        void **data_items;
        GHashTable *data_hash;
+       int *clause_indexes;
 } TransformData;
 
 #define MINT_TYPE_I1 0
@@ -932,6 +933,35 @@ interp_field_from_token (MonoMethod *method, guint32 token, MonoClass **klass, M
        return field;
 }
 
+static void
+interp_save_debug_info (RuntimeMethod *rtm, MonoMethodHeader *header, TransformData *td, GArray *line_numbers)
+{
+       MonoDebugMethodJitInfo *dinfo;
+       int i;
+
+       if (!mono_debug_enabled ())
+               return;
+
+       /*
+        * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code.
+        */
+
+       dinfo = g_new0 (MonoDebugMethodJitInfo, 1);
+       dinfo->num_locals = header->num_locals;
+       dinfo->locals = g_new0 (MonoDebugVarInfo, header->num_locals);
+       dinfo->code_start = (guint8*)rtm->code;
+       dinfo->code_size = td->new_ip - td->new_code;
+       dinfo->epilogue_begin = 0;
+       dinfo->has_var_info = FALSE;
+       dinfo->num_line_numbers = line_numbers->len;
+       dinfo->line_numbers = g_new0 (MonoDebugLineNumberEntry, dinfo->num_line_numbers);
+       for (i = 0; i < dinfo->num_line_numbers; i++)
+               dinfo->line_numbers [i] = g_array_index (line_numbers, MonoDebugLineNumberEntry, i);
+       mono_debug_add_method (rtm->method, dinfo, mono_domain_get ());
+
+       mono_debug_free_method_jit_info (dinfo);
+}
+
 static void
 generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, MonoGenericContext *generic_context)
 {
@@ -952,6 +982,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo
        guint32 token;
        TransformData td;
        int generating_code = 1;
+       GArray *line_numbers;
 
        memset(&td, 0, sizeof(td));
        td.method = method;
@@ -972,10 +1003,12 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo
        td.max_data_items = 0;
        td.data_items = NULL;
        td.data_hash = g_hash_table_new (NULL, NULL);
+       td.clause_indexes = g_malloc (header->code_size * sizeof (int));
        rtm->data_items = td.data_items;
        for (i = 0; i < header->code_size; i++) {
                td.forward_refs [i] = -1;
                td.stack_height [i] = -1;
+               td.clause_indexes [i] = -1;
        }
        td.new_ip = td.new_code;
        td.last_new_ip = NULL;
@@ -984,6 +1017,8 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo
        td.sp = td.stack;
        td.max_stack_height = 0;
 
+       line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
+
        for (i = 0; i < header->num_clauses; i++) {
                MonoExceptionClause *c = header->clauses + i;
                td.stack_height [c->handler_offset] = 0;
@@ -1005,6 +1040,13 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo
                        td.stack_state [c->data.filter_offset][0].type = STACK_TYPE_O;
                        td.stack_state [c->data.filter_offset][0].klass = NULL; /*FIX*/
                }
+
+               if ((c->flags & MONO_EXCEPTION_CLAUSE_FINALLY) || (c->flags & MONO_EXCEPTION_CLAUSE_FAULT)) {
+                       for (int j = c->handler_offset; j < c->handler_offset + c->handler_len; ++j) {
+                               if (td.clause_indexes [j] == -1)
+                                       td.clause_indexes [j] = i;
+                       }
+               }
        }
 
        td.ip = header->code;
@@ -1043,6 +1085,12 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo
                td.in_offsets [in_offset] = td.new_ip - td.new_code;
                new_in_start_offset = td.new_ip - td.new_code;
                td.in_start = td.ip;
+
+               MonoDebugLineNumberEntry lne;
+               lne.native_offset = td.new_ip - td.new_code;
+               lne.il_offset = td.ip - header->code;
+               g_array_append_val (line_numbers, lne);
+
                while (td.forward_refs [in_offset] >= 0) {
                        int j = td.forward_refs [in_offset];
                        int slot;
@@ -2823,8 +2871,10 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo
                        ++td.ip;
                        break;
                case CEE_ENDFINALLY:
+                       g_assert (td.clause_indexes [in_offset] != -1);
                        td.sp = td.stack;
                        SIMPLE_OP (td, MINT_ENDFINALLY);
+                       ADD_CODE (&td, td.clause_indexes [in_offset]);
                        generating_code = 0;
                        break;
                case CEE_LEAVE:
@@ -3278,6 +3328,8 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo
        }
        g_assert (td.max_stack_height <= (header->max_stack + 1));
 
+       int code_len = td.new_ip - td.new_code;
+
        rtm->clauses = mono_domain_alloc0 (domain, header->num_clauses * sizeof (MonoExceptionClause));
        memcpy (rtm->clauses, header->clauses, header->num_clauses * sizeof(MonoExceptionClause));
        rtm->code = mono_domain_alloc0 (domain, (td.new_ip - td.new_code) * sizeof (gushort));
@@ -3300,6 +3352,29 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo
        rtm->alloca_size = rtm->locals_size + rtm->args_size + rtm->vt_stack_size + rtm->stack_size;
        rtm->data_items = mono_domain_alloc0 (domain, td.n_data_items * sizeof (td.data_items [0]));
        memcpy (rtm->data_items, td.data_items, td.n_data_items * sizeof (td.data_items [0]));
+
+       /* Save debug info */
+       interp_save_debug_info (rtm, header, &td, line_numbers);
+
+       /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
+       int jinfo_len = mono_jit_info_size (0, header->num_clauses, 0);
+       MonoJitInfo *jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, jinfo_len);
+       rtm->jinfo = jinfo;
+       mono_jit_info_init (jinfo, method, (guint8*)rtm->code, code_len, 0, header->num_clauses, 0);
+       for (i = 0; i < jinfo->num_clauses; ++i) {
+               MonoJitExceptionInfo *ei = &jinfo->clauses [i];
+               MonoExceptionClause *c = rtm->clauses + i;
+
+               ei->flags = c->flags;
+               ei->try_start = rtm->code + c->try_offset;
+               ei->try_end = rtm->code + c->try_offset + c->try_len;
+               ei->handler_start = rtm->code + c->handler_offset;
+               if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
+               } else {
+                       ei->data.catch_class = c->data.catch_class;
+               }
+       }
+
        g_free (td.in_offsets);
        g_free (td.forward_refs);
        for (i = 0; i < header->code_size; ++i)
@@ -3310,6 +3385,8 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo
        g_free (td.data_items);
        g_free (td.stack);
        g_hash_table_destroy (td.data_hash);
+       g_free (td.clause_indexes);
+       g_array_free (line_numbers, TRUE);
 }
 
 static mono_mutex_t calc_section;
index d13bca95ee1aa08a09ac3cde6c6ec05c3bbc4f13..b42ac52a33bfe7b7c18cc175d219cfa4b7dfd991 100644 (file)
@@ -447,6 +447,7 @@ typedef struct {
 #define MONO_ARCH_HAVE_PATCH_CODE_NEW 1
 #define MONO_ARCH_HAVE_OP_GENERIC_CLASS_INIT 1
 #define MONO_ARCH_HAVE_GENERAL_RGCTX_LAZY_FETCH_TRAMPOLINE 1
+#define MONO_ARCH_HAVE_INIT_LMF_EXT 1
 
 #if defined(TARGET_OSX) || defined(__linux__)
 #define MONO_ARCH_HAVE_UNWIND_BACKTRACE 1
index 45ceb0d6629df7c7f288e115d55daf5bd90d3a30..90d85b88ab41d1127f82f118f2e1b0995858bb3e 100644 (file)
@@ -58,6 +58,7 @@
 #include <mono/metadata/object-internals.h>
 #include <mono/metadata/reflection-internals.h>
 #include <mono/metadata/gc-internals.h>
+#include <mono/metadata/debug-internals.h>
 #include <mono/metadata/mono-debug.h>
 #include <mono/metadata/profiler.h>
 #include <mono/metadata/mono-endian.h>
@@ -567,7 +568,7 @@ mono_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls,
        if (!err)
                return FALSE;
 
-       if (*lmf && ((*lmf) != jit_tls->first_lmf) && ((gpointer)MONO_CONTEXT_GET_SP (new_ctx) >= (gpointer)(*lmf))) {
+       if (frame->type != FRAME_TYPE_INTERP_TO_MANAGED && *lmf && ((*lmf) != jit_tls->first_lmf) && ((gpointer)MONO_CONTEXT_GET_SP (new_ctx) >= (gpointer)(*lmf))) {
                /*
                 * Remove any unused lmf.
                 * Mask out the lower bits which might be used to hold additional information.
@@ -630,6 +631,44 @@ mono_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls,
        return TRUE;
 }
 
+typedef struct {
+       gboolean in_interp;
+       MonoInterpStackIter interp_iter;
+} Unwinder;
+
+static void
+unwinder_init (Unwinder *unwinder)
+{
+       memset (unwinder, 0, sizeof (Unwinder));
+}
+
+static gboolean
+unwinder_unwind_frame (Unwinder *unwinder,
+                                          MonoDomain *domain, MonoJitTlsData *jit_tls,
+                                          MonoJitInfo *prev_ji, MonoContext *ctx,
+                                          MonoContext *new_ctx, char **trace, MonoLMF **lmf,
+                                          mgreg_t **save_locations,
+                                          StackFrameInfo *frame)
+{
+       if (unwinder->in_interp) {
+               unwinder->in_interp = mono_interp_frame_iter_next (&unwinder->interp_iter, frame);
+               if (!unwinder->in_interp) {
+                       return unwinder_unwind_frame (unwinder, domain, jit_tls, prev_ji, ctx, new_ctx, trace, lmf, save_locations, frame);
+               }
+               return TRUE;
+       } else {
+               gboolean res = mono_find_jit_info_ext (domain, jit_tls, prev_ji, ctx, new_ctx, trace, lmf,
+                                                                                          save_locations, frame);
+               if (!res)
+                       return FALSE;
+               if (frame->type == FRAME_TYPE_INTERP_TO_MANAGED) {
+                       unwinder->in_interp = TRUE;
+                       mono_interp_frame_iter_init (&unwinder->interp_iter, frame->interp_exit_data);
+               }
+               return TRUE;
+       }
+}
+
 /*
  * This function is async-safe.
  */
@@ -1119,6 +1158,8 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
        MonoMethod *jmethod = NULL, *actual_method;
        StackFrameInfo frame;
        gboolean res;
+       Unwinder unwinder;
+       int il_offset = -1;
 
        MONO_ARCH_CONTEXT_DEF;
 
@@ -1160,29 +1201,43 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
                MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_get_frame_info);
 #endif
 
+               unwinder_init (&unwinder);
+
                new_ctx = ctx;
                do {
                        ctx = new_ctx;
-                       res = mono_find_jit_info_ext (domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, NULL, &frame);
+                       res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, NULL, &frame);
                        if (!res)
                                return FALSE;
-
-                       if (frame.type == FRAME_TYPE_MANAGED_TO_NATIVE ||
-                               frame.type == FRAME_TYPE_DEBUGGER_INVOKE ||
-                               frame.type == FRAME_TYPE_TRAMPOLINE)
+                       switch (frame.type) {
+                       case FRAME_TYPE_MANAGED_TO_NATIVE:
+                       case FRAME_TYPE_DEBUGGER_INVOKE:
+                       case FRAME_TYPE_TRAMPOLINE:
+                       case FRAME_TYPE_INTERP_TO_MANAGED:
                                continue;
+                       case FRAME_TYPE_INTERP:
+                               skip--;
+                               break;
+                       default:
+                               ji = frame.ji;
+                               *native_offset = frame.native_offset;
 
-                       ji = frame.ji;
-                       *native_offset = frame.native_offset;
-
-                       /* The skip count passed by the caller depends on us not filtering out MANAGED_TO_NATIVE */
-                       jmethod = jinfo_get_method (ji);
-                       if (jmethod->wrapper_type != MONO_WRAPPER_NONE && jmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD && jmethod->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE)
-                               continue;
-                       skip--;
+                               /* The skip count passed by the caller depends on us not filtering out MANAGED_TO_NATIVE */
+                               jmethod = jinfo_get_method (ji);
+                               if (jmethod->wrapper_type != MONO_WRAPPER_NONE && jmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD && jmethod->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE)
+                                       continue;
+                               skip--;
+                               break;
+                       }
                } while (skip >= 0);
 
-               actual_method = get_method_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, &ctx));
+               if (frame.type == FRAME_TYPE_INTERP) {
+                       jmethod = frame.method;
+                       actual_method = frame.actual_method;
+                       *native_offset = frame.native_offset;
+               } else {
+                       actual_method = get_method_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, &ctx));
+               }
        }
 
        MonoReflectionMethod *rm = mono_method_get_object_checked (domain, actual_method, NULL, &error);
@@ -1192,7 +1247,11 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
        }
        mono_gc_wbarrier_generic_store (method, (MonoObject*) rm);
 
-       location = mono_debug_lookup_source_location (jmethod, *native_offset, domain);
+       if (il_offset != -1) {
+               location = mono_debug_lookup_source_location_by_il (jmethod, il_offset, domain);
+       } else {
+               location = mono_debug_lookup_source_location (jmethod, *native_offset, domain);
+       }
        if (location)
                *iloffset = location->il_offset;
        else
@@ -1469,6 +1528,8 @@ mono_handle_exception_internal_first_pass (MonoContext *ctx, MonoObject *obj, gi
        gint32 filter_idx;
        int i;
        MonoObject *ex_obj;
+       Unwinder unwinder;
+       gboolean in_interp;
 
        g_assert (ctx != NULL);
 
@@ -1508,6 +1569,8 @@ mono_handle_exception_internal_first_pass (MonoContext *ctx, MonoObject *obj, gi
        filter_idx = 0;
        initial_ctx = *ctx;
 
+       unwinder_init (&unwinder);
+
        while (1) {
                MonoContext new_ctx;
                guint32 free_stack;
@@ -1519,24 +1582,37 @@ mono_handle_exception_internal_first_pass (MonoContext *ctx, MonoObject *obj, gi
                if (out_prev_ji)
                        *out_prev_ji = ji;
 
-               unwind_res = mono_find_jit_info_ext (domain, jit_tls, NULL, ctx, &new_ctx, NULL, &lmf, NULL, &frame);
-               if (unwind_res) {
-                       if (frame.type == FRAME_TYPE_DEBUGGER_INVOKE ||
-                                       frame.type == FRAME_TYPE_MANAGED_TO_NATIVE ||
-                                       frame.type == FRAME_TYPE_TRAMPOLINE) {
-                               *ctx = new_ctx;
-                               continue;
-                       }
-                       g_assert (frame.type == FRAME_TYPE_MANAGED);
-                       ji = frame.ji;
-               }
-
+               unwind_res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, ctx, &new_ctx, NULL, &lmf, NULL, &frame);
                if (!unwind_res) {
                        setup_stack_trace (mono_ex, dynamic_methods, &trace_ips);
                        g_slist_free (dynamic_methods);
                        return FALSE;
                }
 
+               switch (frame.type) {
+               case FRAME_TYPE_DEBUGGER_INVOKE:
+               case FRAME_TYPE_MANAGED_TO_NATIVE:
+               case FRAME_TYPE_TRAMPOLINE:
+               case FRAME_TYPE_INTERP_TO_MANAGED:
+                       *ctx = new_ctx;
+                       continue;
+               case FRAME_TYPE_INTERP:
+               case FRAME_TYPE_MANAGED:
+                       break;
+               default:
+                       g_assert_not_reached ();
+                       break;
+               }
+
+               in_interp = frame.type == FRAME_TYPE_INTERP;
+               ji = frame.ji;
+
+               gpointer ip;
+               if (in_interp)
+                       ip = (guint16*)ji->code_start + frame.native_offset;
+               else
+                       ip = MONO_CONTEXT_GET_IP (ctx);
+
                frame_count ++;
                method = jinfo_get_method (ji);
                //printf ("M: %s %d.\n", mono_method_full_name (method, TRUE), frame_count);
@@ -1578,7 +1654,7 @@ mono_handle_exception_internal_first_pass (MonoContext *ctx, MonoObject *obj, gi
                        if (free_stack <= (64 * 1024))
                                continue;
 
-                       if (is_address_protected (ji, ei, MONO_CONTEXT_GET_IP (ctx))) {
+                       if (is_address_protected (ji, ei, ip)) {
                                /* catch block */
                                MonoClass *catch_class = get_exception_catch_class (ei, ji, ctx);
 
@@ -1650,7 +1726,8 @@ mono_handle_exception_internal_first_pass (MonoContext *ctx, MonoObject *obj, gi
                                                *out_ji = ji;
 
                                        /* mono_debugger_agent_handle_exception () needs this */
-                                       MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
+                                       if (!in_interp)
+                                               MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
                                        return TRUE;
                                }
                                mono_error_cleanup (&isinst_error);
@@ -1687,6 +1764,8 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu
        int i;
        MonoObject *ex_obj;
        MonoObject *non_exception = NULL;
+       Unwinder unwinder;
+       gboolean in_interp;
 
        g_assert (ctx != NULL);
        if (!obj) {
@@ -1849,11 +1928,15 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu
        filter_idx = 0;
        initial_ctx = *ctx;
 
+       unwinder_init (&unwinder);
+
        while (1) {
                MonoContext new_ctx;
                guint32 free_stack;
                int clause_index_start = 0;
                gboolean unwind_res = TRUE;
+               StackFrameInfo frame;
+               gpointer ip;
                
                if (resume) {
                        resume = FALSE;
@@ -1864,27 +1947,36 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu
                        first_filter_idx = jit_tls->resume_state.first_filter_idx;
                        filter_idx = jit_tls->resume_state.filter_idx;
                } else {
-                       StackFrameInfo frame;
-
-                       unwind_res = mono_find_jit_info_ext (domain, jit_tls, NULL, ctx, &new_ctx, NULL, &lmf, NULL, &frame);
-                       if (unwind_res) {
-                               if (frame.type == FRAME_TYPE_DEBUGGER_INVOKE ||
-                                               frame.type == FRAME_TYPE_MANAGED_TO_NATIVE ||
-                                               frame.type == FRAME_TYPE_TRAMPOLINE) {
-                                       *ctx = new_ctx;
-                                       continue;
-                               }
-                               g_assert (frame.type == FRAME_TYPE_MANAGED);
-                               ji = frame.ji;
+                       unwind_res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, ctx, &new_ctx, NULL, &lmf, NULL, &frame);
+                       if (!unwind_res) {
+                               *(mono_get_lmf_addr ()) = lmf;
+
+                               jit_tls->abort_func (obj);
+                               g_assert_not_reached ();
+                       }
+                       switch (frame.type) {
+                       case FRAME_TYPE_DEBUGGER_INVOKE:
+                       case FRAME_TYPE_MANAGED_TO_NATIVE:
+                       case FRAME_TYPE_TRAMPOLINE:
+                               *ctx = new_ctx;
+                               continue;
+                       case FRAME_TYPE_INTERP_TO_MANAGED:
+                               continue;
+                       case FRAME_TYPE_INTERP:
+                       case FRAME_TYPE_MANAGED:
+                               break;
+                       default:
+                               g_assert_not_reached ();
+                               break;
                        }
+                       in_interp = frame.type == FRAME_TYPE_INTERP;
+                       ji = frame.ji;
                }
 
-               if (!unwind_res) {
-                       *(mono_get_lmf_addr ()) = lmf;
-
-                       jit_tls->abort_func (obj);
-                       g_assert_not_reached ();
-               }
+               if (in_interp)
+                       ip = (guint16*)ji->code_start + frame.native_offset;
+               else
+                       ip = MONO_CONTEXT_GET_IP (ctx);
 
                method = jinfo_get_method (ji);
                frame_count ++;
@@ -1910,7 +2002,7 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu
                        if (free_stack <= (64 * 1024))
                                continue;
 
-                       if (is_address_protected (ji, ei, MONO_CONTEXT_GET_IP (ctx))) {
+                       if (is_address_protected (ji, ei, ip)) {
                                /* catch block */
                                MonoClass *catch_class = get_exception_catch_class (ei, ji, ctx);
 
@@ -1993,7 +2085,27 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu
                                        mono_profiler_exception_clause_handler (method, ei->flags, i);
                                        jit_tls->orig_ex_ctx_set = FALSE;
                                        mini_set_abort_threshold (ctx);
-                                       MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
+
+                                       if (in_interp) {
+                                               /*
+                                                * ctx->pc points into the interpreter, after the call which transitioned to
+                                                * JITted code. Store the unwind state into the
+                                                * interpeter state, then resume, the interpreter will unwind itself until
+                                                * it reaches the target frame and will continue execution from there.
+                                                * The resuming is kinda hackish, from the native code standpoint, it looks
+                                                * like the call which transitioned to JITted code has succeeded, but the
+                                                * return value register etc. is not set, so we have to be careful.
+                                                */
+                                               mono_interp_set_resume_state (mono_ex, &frame, ei->handler_start);
+                                               /* Undo the IP adjustment done by mono_arch_unwind_frame () */
+#ifdef TARGET_AMD64
+                                               ctx->gregs [AMD64_RIP] ++;
+#else
+                                               NOT_IMPLEMENTED;
+#endif
+                                       } else {
+                                               MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
+                                       }
                                        mono_set_lmf (lmf);
 #ifndef DISABLE_PERFCOUNTERS
                                        mono_perfcounters->exceptions_depth += frame_count;
@@ -2045,7 +2157,10 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu
                                                return 0;
                                        } else {
                                                mini_set_abort_threshold (ctx);
-                                               call_filter (ctx, ei->handler_start);
+                                               if (in_interp)
+                                                       mono_interp_run_finally (&frame, i, ei->handler_start);
+                                               else
+                                                       call_filter (ctx, ei->handler_start);
                                        }
                                }
                        }
@@ -3251,3 +3366,31 @@ mono_debug_personality (void)
        g_assert_not_reached ();
 }
 #endif
+
+#ifndef ENABLE_INTERPRETER
+/* Stubs of interpreter functions */
+void
+mono_interp_set_resume_state (MonoException *ex, StackFrameInfo *frame, gpointer handler_ip)
+{
+       g_assert_not_reached ();
+}
+
+void
+mono_interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip)
+{
+       g_assert_not_reached ();
+}
+
+void
+mono_interp_frame_iter_init (MonoInterpStackIter *iter, gpointer interp_exit_data)
+{
+       g_assert_not_reached ();
+}
+
+gboolean
+mono_interp_frame_iter_next (MonoInterpStackIter *iter, StackFrameInfo *frame)
+{
+       g_assert_not_reached ();
+       return FALSE;
+}
+#endif
index 587d368a4186ce564c123e0969c783d24a0fe667..d7475aebd3aff13266a7d01c0419a0fb04b675b2 100644 (file)
@@ -1493,7 +1493,8 @@ mini_get_interp_in_wrapper (MonoMethodSignature *sig)
 
        mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_UNKNOWN);
 
-       // FIXME: save lmf
+       /* This is needed to be able to unwind out of interpreted code */
+       mb->method->save_lmf = 1;
 
 #ifndef DISABLE_JIT
        if (sig->ret->type != MONO_TYPE_VOID)
index 719dceaf97e39dbfe0b39739584e9c85dfc23c60..5085233d27ab9376bac85309f92de891999adf9a 100644 (file)
@@ -791,6 +791,38 @@ mono_set_lmf_addr (gpointer lmf_addr)
                mono_thread_info_tls_set (info, TLS_KEY_LMF_ADDR, lmf_addr);
 }
 
+/*
+ * mono_push_lmf:
+ *
+ *   Push an MonoLMFExt frame on the LMF stack.
+ */
+void
+mono_push_lmf (MonoLMFExt *ext)
+{
+#ifdef MONO_ARCH_HAVE_INIT_LMF_EXT
+       MonoLMF **lmf_addr;
+
+       lmf_addr = mono_get_lmf_addr ();
+
+       mono_arch_init_lmf_ext (ext, *lmf_addr);
+
+       mono_set_lmf ((MonoLMF*)ext);
+#else
+       NOT_IMPLEMENTED;
+#endif
+}
+
+/*
+ * mono_push_lmf:
+ *
+ *   Pop the last frame from the LMF stack.
+ */
+void
+mono_pop_lmf (MonoLMF *lmf)
+{
+       mono_set_lmf ((MonoLMF *)(((gssize)lmf->previous_lmf) & ~3));
+}
+
 /*
  * mono_jit_thread_attach:
  *
index e33dcf85d7f6e355d000bf3be5e818a2c6d59c93..8e830f91f9dab865a63968fe911e32fa09730696 100644 (file)
@@ -1216,7 +1216,10 @@ typedef struct {
 typedef struct {
        struct MonoLMF lmf;
        gboolean debugger_invoke;
+       gboolean interp_exit;
        MonoContext ctx; /* if debugger_invoke is TRUE */
+       /* If interp_exit is TRUE */
+       gpointer interp_exit_data;
 } MonoLMFExt;
 
 /* Generic sharing */
@@ -2435,6 +2438,8 @@ gpointer  mono_jit_compile_method_inner     (MonoMethod *method, MonoDomain *tar
 MonoLMF * mono_get_lmf                      (void);
 MonoLMF** mono_get_lmf_addr                 (void);
 void      mono_set_lmf                      (MonoLMF *lmf);
+void      mono_push_lmf                     (MonoLMFExt *ext);
+void      mono_pop_lmf                      (MonoLMF *lmf);
 MonoJitTlsData* mono_get_jit_tls            (void);
 MONO_API MonoDomain* mono_jit_thread_attach (MonoDomain *domain);
 MONO_API void      mono_jit_set_domain      (MonoDomain *domain);
index be88673cb1848c545d422a8f63a902ca68d655a5..568589ebe2758600e2b68e95d7b813c3903bafa1 100644 (file)
@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Runtime.CompilerServices;
+using System.Diagnostics;
 
 /*
  * Regression tests for the mixed-mode execution.
@@ -73,6 +74,16 @@ class InterpClass
                return i;
        }
 
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static StackTrace get_stacktrace_interp () {
+               var o = new object ();
+               return new StackTrace (true);
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static void throw_ex () {
+               JitClass.throw_ex ();
+       }
 }
 
 /* The methods in this class will always be JITted */
@@ -140,6 +151,16 @@ class JitClass
        public static void exit_byref (ref int i) {
                i += 1;
        }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static void throw_ex () {
+               throw new Exception ();
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static StackTrace get_stacktrace_jit () {
+               return InterpClass.get_stacktrace_interp ();
+       }
 }
 
 #if __MOBILE__
@@ -179,4 +200,64 @@ class Tests
                        return 4;
                return 0;
        }
+
+       public static int test_0_throw () {
+               // Throw an exception from jitted code, catch it in interpreted code
+               try {
+                       JitClass.throw_ex ();
+               } catch {
+                       return 0;
+               }
+               return 1;
+       }
+
+       public static int test_0_throw_child () {
+               try {
+                       InterpClass.throw_ex ();
+               } catch {
+                       return 0;
+               }
+               return 1;
+       }
+
+       static bool finally_called;
+
+       public static void call_finally () {
+               try {
+                       JitClass.throw_ex ();
+               } finally {
+                       finally_called = true;
+               }
+       }
+
+       public static int test_0_eh2 () {
+               finally_called = false;
+
+               // Throw an exception from jitted code, execute finally in interpreted code
+               try {
+                       call_finally ();
+               } catch {
+                       return 0;
+               }
+               if (!finally_called)
+                       return 2;
+               return 1;
+       }
+
+       public static int test_0_stack_traces () {
+               //
+               // Get a stacktrace for an interp->jit->interp call stack
+               //
+               StackTrace st = JitClass.get_stacktrace_jit ();
+               var frame = st.GetFrame (0);
+               if (frame.GetMethod ().Name != "get_stacktrace_interp")
+                       return 1;
+               frame = st.GetFrame (1);
+               if (frame.GetMethod ().Name != "get_stacktrace_jit")
+                       return 2;
+               frame = st.GetFrame (2);
+               if (frame.GetMethod ().Name != "test_0_stack_traces")
+                       return 3;
+               return 0;
+       }
 }
\ No newline at end of file
index 8a6e1aa244efbc20e1570c5a49e600ee4073abd8..82f6127a8a67023cbee08f2ed67bacb081f0b2e5 100644 (file)
@@ -22,7 +22,11 @@ typedef enum {
        /* Frame for transitioning to native code */
        FRAME_TYPE_MANAGED_TO_NATIVE = 2,
        FRAME_TYPE_TRAMPOLINE = 3,
-       FRAME_TYPE_NUM = 4
+       /* Interpreter frame */
+       FRAME_TYPE_INTERP = 4,
+       /* Frame for transitioning from interpreter to managed code */
+       FRAME_TYPE_INTERP_TO_MANAGED = 5,
+       FRAME_TYPE_NUM = 6
 } MonoStackFrameType;
 
 typedef enum {
@@ -74,6 +78,12 @@ typedef struct {
         */
        int il_offset;
 
+       /* For FRAME_TYPE_INTERP_EXIT */
+       gpointer interp_exit_data;
+
+       /* For FRAME_TYPE_INTERP */
+       gpointer interp_frame;
+
        /* The next fields are only useful for the jit */
        gpointer lmf;
        guint32 unwind_info_len;