svn path=/trunk/mono/; revision=139311
[mono.git] / mono / mini / mini-exceptions.c
index 5b57cda36b1a9372c92247b577c779f5f7d53872..f7b56a8511916c3eb7bd236ecbb137bdb5291d7a 100644 (file)
@@ -46,6 +46,7 @@
 #include <mono/utils/mono-mmap.h>
 
 #include "mini.h"
+#include "debug-mini.h"
 #include "trace.h"
 
 #ifndef MONO_ARCH_CONTEXT_DEF
@@ -258,6 +259,7 @@ static gpointer
 get_generic_info_from_stack_frame (MonoJitInfo *ji, MonoContext *ctx)
 {
        MonoGenericJitInfo *gi;
+       gpointer info;
 
        if (!ji->has_generic_jit_info)
                return NULL;
@@ -266,10 +268,20 @@ get_generic_info_from_stack_frame (MonoJitInfo *ji, MonoContext *ctx)
                return NULL;
 
        if (gi->this_in_reg)
-               return mono_arch_context_get_int_reg (ctx, gi->this_reg);
+               info = mono_arch_context_get_int_reg (ctx, gi->this_reg);
        else
-               return *(gpointer*)(gpointer)((char*)mono_arch_context_get_int_reg (ctx, gi->this_reg) +
-                               gi->this_offset);
+               info = *(gpointer*)(gpointer)((char*)mono_arch_context_get_int_reg (ctx, gi->this_reg) +
+                                                                         gi->this_offset);
+       if (mono_method_get_context (ji->method)->method_inst) {
+               return info;
+       } else if ((ji->method->flags & METHOD_ATTRIBUTE_STATIC) || ji->method->klass->valuetype) {
+               return info;
+       } else {
+               /* Avoid returning a managed object */
+               MonoObject *this_obj = info;
+
+               return this_obj->vtable->klass;
+       }
 }
 
 static MonoGenericContext
@@ -292,20 +304,24 @@ get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_info)
 
                class = vtable->klass;
        } else {
-               MonoObject *this = generic_info;
-
-               class = this->vtable->klass;
+               class = generic_info;
        }
 
-       if (class->generic_class || class->generic_container)
-               context.class_inst = mini_class_get_context (class)->class_inst;
-
        g_assert (!ji->method->klass->generic_container);
        if (ji->method->klass->generic_class)
                method_container_class = ji->method->klass->generic_class->container_class;
        else
                method_container_class = ji->method->klass;
 
+       /* class might refer to a subclass of ji->method's class */
+       while (class->generic_class && class->generic_class->container_class != method_container_class) {
+               class = class->parent;
+               g_assert (class);
+       }
+
+       if (class->generic_class || class->generic_container)
+               context.class_inst = mini_class_get_context (class)->class_inst;
+
        if (class->generic_class)
                g_assert (mono_class_has_parent_and_ignore_generics (class->generic_class->container_class, method_container_class));
        else
@@ -319,7 +335,7 @@ get_method_from_stack_frame (MonoJitInfo *ji, gpointer generic_info)
 {
        MonoGenericContext context;
        MonoMethod *method;
-
+       
        if (!ji->has_generic_jit_info || !mono_jit_info_get_generic_jit_info (ji)->has_this)
                return ji->method;
        context = get_generic_context_from_stack_frame (ji, generic_info);
@@ -610,7 +626,7 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
 
        actual_method = get_method_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, &ji_ctx));
 
-       *method = mono_method_get_object (domain, actual_method, NULL);
+       mono_gc_wbarrier_generic_store (method, (MonoObject*) mono_method_get_object (domain, actual_method, NULL));
 
        location = mono_debug_lookup_source_location (ji->method, *native_offset, domain);
        if (location)
@@ -620,7 +636,7 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
 
        if (need_file_info) {
                if (location) {
-                       *file = mono_string_new (domain, location->source_file);
+                       mono_gc_wbarrier_generic_store (file, (MonoObject*) mono_string_new (domain, location->source_file));
                        *line = location->row;
                        *column = location->column;
                } else {
@@ -844,7 +860,7 @@ get_exception_catch_class (MonoJitExceptionInfo *ei, MonoJitInfo *ji, MonoContex
  * the first filter clause which caught the exception.
  */
 static gboolean
-mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer original_ip, gboolean test_only, gint32 *out_filter_idx)
+mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer original_ip, gboolean test_only, gint32 *out_filter_idx, MonoJitInfo **out_ji)
 {
        MonoDomain *domain = mono_domain_get ();
        MonoJitInfo *ji, rji;
@@ -911,32 +927,21 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
        if (!test_only) {
                MonoContext ctx_cp = *ctx;
                if (mono_trace_is_enabled ())
-                       g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
+                       g_print ("[%p:] EXCEPTION handling: %s\n", (void*)GetCurrentThreadId (), mono_object_class (obj)->name);
                mono_profiler_exception_thrown (obj);
-               if (!mono_handle_exception_internal (&ctx_cp, obj, original_ip, TRUE, &first_filter_idx)) {
+               if (!mono_handle_exception_internal (&ctx_cp, obj, original_ip, TRUE, &first_filter_idx, out_ji)) {
                        if (mono_break_on_exc)
                                G_BREAKPOINT ();
                        // FIXME: This runs managed code so it might cause another stack overflow when
                        // we are handling a stack overflow
                        mono_unhandled_exception (obj);
-
-                       if (mono_debugger_unhandled_exception (original_ip, MONO_CONTEXT_GET_SP (ctx), obj)) {
-                               /*
-                                * If this returns true, then we're running inside the
-                                * Mono Debugger and the debugger wants us to restore the
-                                * context and continue (normally, the debugger inserts
-                                * a breakpoint on the `original_ip', so it regains control
-                                * immediately after restoring the context).
-                                */
-                               MONO_CONTEXT_SET_IP (ctx, original_ip);
-                               restore_context (ctx);
-                               g_assert_not_reached ();
-                       }
                }
        }
 
        if (out_filter_idx)
                *out_filter_idx = -1;
+       if (out_ji)
+               *out_ji = NULL;
        filter_idx = 0;
        initial_ctx = *ctx;
        memset (&rji, 0, sizeof (rji));
@@ -1021,12 +1026,14 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
                                                }
 
                                                if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
-                                                       // mono_debugger_handle_exception (ei->data.filter, MONO_CONTEXT_GET_SP (ctx), obj);
                                                        if (test_only) {
                                                                mono_perfcounters->exceptions_filters++;
+                                                               mono_debugger_call_exception_handler (ei->data.filter, MONO_CONTEXT_GET_SP (ctx), obj);
                                                                filtered = call_filter (ctx, ei->data.filter);
                                                                if (filtered && out_filter_idx)
                                                                        *out_filter_idx = filter_idx;
+                                                               if (out_ji)
+                                                                       *out_ji = ji;
                                                        }
                                                        else {
                                                                /* 
@@ -1055,7 +1062,7 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
                                                        if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
                                                                g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
                                                        mono_profiler_exception_clause_handler (ji->method, ei->flags, i);
-                                                       mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
+                                                       mono_debugger_call_exception_handler (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
                                                        MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
                                                        *(mono_get_lmf_addr ()) = lmf;
                                                        mono_perfcounters->exceptions_depth += frame_count;
@@ -1070,7 +1077,7 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
                                                        if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
                                                                g_print ("EXCEPTION: fault clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
                                                        mono_profiler_exception_clause_handler (ji->method, ei->flags, i);
-                                                       mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
+                                                       mono_debugger_call_exception_handler (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
                                                        call_filter (ctx, ei->handler_start);
                                                }
                                                if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
@@ -1079,7 +1086,7 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
                                                        if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
                                                                g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
                                                        mono_profiler_exception_clause_handler (ji->method, ei->flags, i);
-                                                       mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
+                                                       mono_debugger_call_exception_handler (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
                                                        mono_perfcounters->exceptions_finallys++;
                                                        call_filter (ctx, ei->handler_start);
                                                }
@@ -1117,6 +1124,72 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
        g_assert_not_reached ();
 }
 
+/*
+ * mono_debugger_handle_exception:
+ *
+ *  Notify the debugger about exceptions.  Returns TRUE if the debugger wants us to stop
+ *  at the exception and FALSE to resume with the normal exception handling.
+ *
+ *  The arch code is responsible to setup @ctx in a way that MONO_CONTEXT_GET_IP () and
+ *  MONO_CONTEXT_GET_SP () point to the throw instruction; ie. before executing the
+ *  `callq throw' instruction.
+ */
+gboolean
+mono_debugger_handle_exception (MonoContext *ctx, MonoObject *obj)
+{
+       MonoDebuggerExceptionAction action;
+
+       if (!mono_debug_using_mono_debugger ())
+               return FALSE;
+
+       if (!obj) {
+               MonoException *ex = mono_get_exception_null_reference ();
+               MONO_OBJECT_SETREF (ex, message, mono_string_new (mono_domain_get (), "Object reference not set to an instance of an object"));
+               obj = (MonoObject *)ex;
+       }
+
+       action = _mono_debugger_throw_exception (MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), obj);
+
+       if (action == MONO_DEBUGGER_EXCEPTION_ACTION_STOP) {
+               /*
+                * The debugger wants us to stop on the `throw' instruction.
+                * By the time we get here, it already inserted a breakpoint there.
+                */
+               return TRUE;
+       } else if (action == MONO_DEBUGGER_EXCEPTION_ACTION_STOP_UNHANDLED) {
+               MonoContext ctx_cp = *ctx;
+               MonoJitInfo *ji = NULL;
+               gboolean ret;
+
+               /*
+                * The debugger wants us to stop only if this exception is user-unhandled.
+                */
+
+               ret = mono_handle_exception_internal (&ctx_cp, obj, MONO_CONTEXT_GET_IP (ctx), TRUE, NULL, &ji);
+               if (ret && (ji != NULL) && (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE)) {
+                       /*
+                        * The exception is handled in a runtime-invoke wrapper, that means that it's unhandled
+                        * inside the method being invoked, so we handle it like a user-unhandled exception.
+                        */
+                       ret = FALSE;
+               }
+
+               if (!ret) {
+                       /*
+                        * The exception is user-unhandled - tell the debugger to stop.
+                        */
+                       return _mono_debugger_unhandled_exception (MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), obj);
+               }
+
+               /*
+                * The exception is catched somewhere - resume with the normal exception handling and don't
+                * stop in the debugger.
+                */
+       }
+
+       return FALSE;
+}
+
 /**
  * mono_debugger_run_finally:
  * @start_ctx: saved processor state
@@ -1170,7 +1243,7 @@ mono_handle_exception (MonoContext *ctx, gpointer obj, gpointer original_ip, gbo
 {
        if (!test_only)
                mono_perfcounters->exceptions_thrown++;
-       return mono_handle_exception_internal (ctx, obj, original_ip, test_only, NULL);
+       return mono_handle_exception_internal (ctx, obj, original_ip, test_only, NULL, NULL);
 }
 
 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
@@ -1302,7 +1375,7 @@ restore_stack_protection (void)
 }
 
 gpointer
-mono_altstack_restore_prot (gssize *regs, guint8 *code, gpointer *tramp_data, guint8* tramp)
+mono_altstack_restore_prot (mgreg_t *regs, guint8 *code, gpointer *tramp_data, guint8* tramp)
 {
        void (*func)(void) = (gpointer)tramp_data;
        func ();
@@ -1453,8 +1526,6 @@ mono_handle_native_sigsegv (int signal, void *ctx)
                int res;
                int stdout_pipe [2] = { -1, -1 };
                pid_t pid;
-               const char *argv [16];
-               char buf1 [128];
                int status;
                char buffer [1024];
 
@@ -1466,7 +1537,8 @@ mono_handle_native_sigsegv (int signal, void *ctx)
                 * glibc fork acquires some locks, so if the crash happened inside malloc/free,
                 * it will deadlock. Call the syscall directly instead.
                 */
-               pid = syscall (SYS_fork);
+               pid = mono_runtime_syscall_fork ();
+
                if (pid == 0) {
                        close (stdout_pipe [0]);
                        dup2 (stdout_pipe [1], STDOUT_FILENO);
@@ -1474,23 +1546,9 @@ mono_handle_native_sigsegv (int signal, void *ctx)
                        for (i = getdtablesize () - 1; i >= 3; i--)
                                close (i);
 
-                       argv [0] = g_find_program_in_path ("gdb");
-                       if (argv [0] == NULL) {
+                       if (!mono_gdb_render_native_backtraces ())
                                close (STDOUT_FILENO);
-                               exit (1);
-                       }
 
-                       argv [1] = "-ex";
-                       sprintf (buf1, "attach %ld", (long)getpid ());
-                       argv [2] = buf1;
-                       argv [3] = "--ex";
-                       argv [4] = "info threads";
-                       argv [5] = "--ex";
-                       argv [6] = "thread apply all bt";
-                       argv [7] = "--batch";
-                       argv [8] = 0;
-
-                       execv (argv [0], (char**)argv);
                        exit (1);
                }