Browse Source

Improved the debugger's exception handling code.
2009-03-04 Martin Baulig <[email protected]>

* debug-mini.c (mono_debugger_runtime_invoke): Moved here from
../metadata/mono-debug-debugger.c; save and reset exception state.

2009-03-02 Martin Baulig <[email protected]>

* debug-mini.c: Moved the debugger exception handling here from
../metadata/mono-debug-debugger.c.

* debug-mini.h
(MonoDebuggerExceptionAction): New exception typedef.

* debug-mini.c
(MonoDebuggerThreadInfo): Added `MonoObject *last_exception'.

* exceptions-amd64.c
(mono_amd64_throw_exception): Use the new debugger exception
handling code.

* mini-exceptions.c
(mono_handle_exception_internal): Don't call
mono_debugger_unhandled_exception() here.

2009-03-04 Martin Baulig <[email protected]>

* mono-debug.h
(mono_debugger_runtime_invoke): Removed.

* mono-debug-debugger.c
(mono_runtime_invoke): Moved into ../mini/debug-mini.c.

2009-03-02 Martin Baulig <[email protected]>

* mono-debug.h
(mono_debugger_unhandled_exception): Removed.
(mono_debugger_handle_exception): Removed.
(mono_debugger_throw_exception): Removed.

* mono-debug.c
(mono_debug_debugger_version): Bump to 5.

* mono-debug-debugger.c: Moved the exception handling code to
../mini/debug-mini.c

svn path=/trunk/mono/; revision=128544

Martin Baulig 17 years ago
parent
commit
79fe383003

+ 21 - 0
mono/metadata/ChangeLog

@@ -1,3 +1,24 @@
+2009-03-04  Martin Baulig  <[email protected]>
+
+	* mono-debug.h
+	(mono_debugger_runtime_invoke): Removed.
+
+	* mono-debug-debugger.c
+	(mono_runtime_invoke): Moved into ../mini/debug-mini.c.
+
+2009-03-02  Martin Baulig  <[email protected]>
+
+	* mono-debug.h
+	(mono_debugger_unhandled_exception): Removed.
+	(mono_debugger_handle_exception): Removed.
+	(mono_debugger_throw_exception): Removed.
+
+	* mono-debug.c
+	(mono_debug_debugger_version): Bump to 5.
+
+	* mono-debug-debugger.c: Moved the exception handling code to
+	../mini/debug-mini.c
+
 2009-03-03  Zoltan Varga  <[email protected]>
 
 	* domain-internals.h (struct _MonoDomain): Add a separate lock for the

+ 0 - 129
mono/metadata/mono-debug-debugger.c

@@ -34,12 +34,6 @@ volatile gint32 _mono_debugger_interruption_request = 0;
 
 void (*mono_debugger_event_handler) (MonoDebuggerEvent event, guint64 data, guint64 arg) = NULL;
 
-typedef struct {
-	gpointer stack_pointer;
-	MonoObject *exception_obj;
-	guint32 stop;
-} MonoDebuggerExceptionInfo;
-
 typedef struct
 {
 	guint32 index;
@@ -101,129 +95,6 @@ mono_debugger_cleanup (void)
 	mono_debugger_event_handler = NULL;
 }
 
-gboolean
-mono_debugger_unhandled_exception (gpointer addr, gpointer stack, MonoObject *exc)
-{
-	const gchar *name;
-
-	if (!mono_debugger_use_debugger)
-		return FALSE;
-
-	name = mono_class_get_name (mono_object_get_class (exc));
-	if (!strcmp (name, "ThreadAbortException"))
-		return FALSE;
-
-	// Prevent the object from being finalized.
-	last_exception = exc;
-
-	mono_debugger_event (MONO_DEBUGGER_EVENT_UNHANDLED_EXCEPTION,
-			     (guint64) (gsize) exc, (guint64) (gsize) addr);
-	return TRUE;
-}
-
-void
-mono_debugger_handle_exception (gpointer addr, gpointer stack, MonoObject *exc)
-{
-	MonoDebuggerExceptionInfo info;
-
-	if (!mono_debugger_use_debugger)
-		return;
-
-	// Prevent the object from being finalized.
-	last_exception = exc;
-
-	info.stack_pointer = stack;
-	info.exception_obj = exc;
-	info.stop = 0;
-
-	mono_debugger_event (MONO_DEBUGGER_EVENT_HANDLE_EXCEPTION, (guint64) (gsize) &info,
-			     (guint64) (gsize) addr);
-}
-
-gboolean
-mono_debugger_throw_exception (gpointer addr, gpointer stack, MonoObject *exc)
-{
-	MonoDebuggerExceptionInfo info;
-
-	if (!mono_debugger_use_debugger)
-		return FALSE;
-
-	// Prevent the object from being finalized.
-	last_exception = exc;
-
-	info.stack_pointer = stack;
-	info.exception_obj = exc;
-	info.stop = 0;
-
-	mono_debugger_event (MONO_DEBUGGER_EVENT_THROW_EXCEPTION, (guint64) (gsize) &info,
-			     (guint64) (gsize) addr);
-	return info.stop != 0;
-}
-
-static gchar *
-get_exception_message (MonoObject *exc)
-{
-	char *message = NULL;
-	MonoString *str; 
-	MonoMethod *method;
-	MonoClass *klass;
-	gint i;
-
-	if (mono_object_isinst (exc, mono_defaults.exception_class)) {
-		klass = exc->vtable->klass;
-		method = NULL;
-		while (klass && method == NULL) {
-			for (i = 0; i < klass->method.count; ++i) {
-				method = klass->methods [i];
-				if (!strcmp ("ToString", method->name) &&
-				    mono_method_signature (method)->param_count == 0 &&
-				    method->flags & METHOD_ATTRIBUTE_VIRTUAL &&
-				    method->flags & METHOD_ATTRIBUTE_PUBLIC) {
-					break;
-				}
-				method = NULL;
-			}
-			
-			if (method == NULL)
-				klass = klass->parent;
-		}
-
-		g_assert (method);
-
-		str = (MonoString *) mono_runtime_invoke (method, exc, NULL, NULL);
-		if (str)
-			message = mono_string_to_utf8 (str);
-	}
-
-	return message;
-}
-
-MonoObject *
-mono_debugger_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc)
-{
-	MonoObject *retval;
-	gchar *message;
-
-	if (!strcmp (method->name, ".ctor")) {
-		retval = obj = mono_object_new (mono_domain_get (), method->klass);
-
-		mono_runtime_invoke (method, obj, params, exc);
-	} else
-		retval = mono_runtime_invoke (method, obj, params, exc);
-
-	if (!exc || (*exc == NULL))
-		return retval;
-
-	retval = *exc;
-	message = get_exception_message (*exc);
-	if (message) {
-		*exc = (MonoObject *) mono_string_new_wrapper (message);
-		g_free (message);
-	}
-
-	return retval;
-}
-
 void
 mono_debugger_check_interruption (void)
 {

+ 0 - 3
mono/metadata/mono-debug-debugger.h

@@ -53,9 +53,6 @@ void            mono_debugger_lock                          (void);
 void            mono_debugger_unlock                        (void);
 void            mono_debugger_event                         (MonoDebuggerEvent event, guint64 data, guint64 arg);
 
-MonoObject     *mono_debugger_runtime_invoke                (MonoMethod *method, void *obj,
-							     void **params, MonoObject **exc);
-
 void *
 mono_vtable_get_static_field_data (MonoVTable *vt);
 

+ 2 - 2
mono/metadata/mono-debug.c

@@ -106,7 +106,7 @@ typedef struct {
 
 MonoSymbolTable *mono_symbol_table = NULL;
 MonoDebugFormat mono_debug_format = MONO_DEBUG_FORMAT_NONE;
-gint32 mono_debug_debugger_version = 4;
+gint32 mono_debug_debugger_version = 5;
 gint32 _mono_debug_using_mono_debugger = 0;
 
 static gboolean mono_debug_initialized = FALSE;
@@ -926,7 +926,7 @@ mono_debug_lookup_method_addresses (MonoMethod *method)
 	GSList *list;
 	guint8 *ptr;
 
-	g_assert (mono_debug_debugger_version == 4);
+	g_assert ((mono_debug_debugger_version == 4) || (mono_debug_debugger_version == 5));
 
 	mono_debugger_lock ();
 

+ 1 - 4
mono/metadata/mono-debug.h

@@ -129,7 +129,7 @@ struct _MonoDebugVarInfo {
 };
 
 #define MONO_DEBUGGER_MAJOR_VERSION			81
-#define MONO_DEBUGGER_MINOR_VERSION			0
+#define MONO_DEBUGGER_MINOR_VERSION			1
 #define MONO_DEBUGGER_MAGIC				0x7aff65af4253d427ULL
 
 extern MonoSymbolTable *mono_symbol_table;
@@ -195,8 +195,5 @@ mono_debug_print_stack_frame (MonoMethod *method, guint32 native_offset, MonoDom
 
 int             mono_debugger_method_has_breakpoint       (MonoMethod *method);
 int             mono_debugger_insert_breakpoint           (const gchar *method_name, gboolean include_namespace);
-gboolean        mono_debugger_unhandled_exception         (gpointer addr, gpointer stack, MonoObject *exc);
-void            mono_debugger_handle_exception            (gpointer addr, gpointer stack, MonoObject *exc);
-gboolean        mono_debugger_throw_exception             (gpointer addr, gpointer stack, MonoObject *exc);
 
 #endif /* __MONO_DEBUG_H__ */

+ 24 - 0
mono/mini/ChangeLog

@@ -1,3 +1,27 @@
+2009-03-04  Martin Baulig  <[email protected]>
+
+	* debug-mini.c (mono_debugger_runtime_invoke): Moved here from
+	../metadata/mono-debug-debugger.c; save and reset exception state.
+
+2009-03-02  Martin Baulig  <[email protected]>
+
+	* debug-mini.c: Moved the debugger exception handling here from
+	../metadata/mono-debug-debugger.c.
+
+	* debug-mini.h
+	(MonoDebuggerExceptionAction): New exception typedef.
+
+	* debug-mini.c
+	(MonoDebuggerThreadInfo): Added `MonoObject *last_exception'.
+
+	* exceptions-amd64.c
+	(mono_amd64_throw_exception): Use the new debugger exception
+	handling code.
+
+	* mini-exceptions.c
+	(mono_handle_exception_internal): Don't call
+	mono_debugger_unhandled_exception() here.
+
 2009-03-03  Zoltan Varga  <[email protected]>
 
 	* mini.c aot-compiler.c: Update after the changes to 

+ 265 - 0
mono/mini/debug-mini.c

@@ -41,6 +41,12 @@ typedef struct
 	guint32 breakpoint_id;
 } MiniDebugMethodInfo;
 
+typedef struct {
+	MonoObject *last_exception;
+	guint32 stopped_on_exception : 1;
+	guint32 stopped_on_unhandled : 1;
+} MonoDebuggerExceptionState;
+
 struct _MonoDebuggerThreadInfo {
 	guint64 tid;
 	guint64 lmf_addr;
@@ -59,6 +65,8 @@ struct _MonoDebuggerThreadInfo {
 	guint32 stack_size;
 	guint32 signal_stack_size;
 
+	MonoDebuggerExceptionState exception_state;
+
 	/*
 	 * The debugger doesn't access anything beyond this point.
 	 */
@@ -66,6 +74,13 @@ struct _MonoDebuggerThreadInfo {
 	MonoThread *thread;
 };
 
+typedef struct {
+	gpointer stack_pointer;
+	MonoObject *exception_obj;
+	guint32 stop;
+	guint32 stop_unhandled;
+} MonoDebuggerExceptionInfo;
+
 MonoDebuggerThreadInfo *mono_debugger_thread_table = NULL;
 
 static inline void
@@ -851,3 +866,253 @@ mono_debugger_trampoline_compiled (MonoMethod *method, const guint8 *code)
 	mono_debugger_extended_notification (MONO_DEBUGGER_EVENT_TRAMPOLINE,
 					     (guint64) (gsize) method, (guint64) (gsize) code);
 }
+
+#if MONO_DEBUGGER_SUPPORTED
+static MonoDebuggerThreadInfo *
+find_debugger_thread_info (MonoThread *thread)
+{
+	MonoDebuggerThreadInfo **ptr;
+
+	for (ptr = &mono_debugger_thread_table; *ptr; ptr = &(*ptr)->next) {
+		MonoDebuggerThreadInfo *info = *ptr;
+
+		if (info->thread == thread)
+			return info;
+	}
+
+	return NULL;
+}
+#endif
+
+MonoDebuggerExceptionAction
+mono_debugger_throw_exception (gpointer addr, gpointer stack, MonoObject *exc)
+{
+#ifdef MONO_DEBUGGER_SUPPORTED
+	MonoDebuggerExceptionInfo exc_info;
+	MonoDebuggerThreadInfo *thread_info;
+
+	if (!mono_debug_using_mono_debugger ())
+		return MONO_DEBUGGER_EXCEPTION_ACTION_NONE;
+
+	mono_debugger_lock ();
+
+	thread_info = find_debugger_thread_info (mono_thread_current ());
+	if (!thread_info) {
+		mono_debugger_unlock ();
+		return MONO_DEBUGGER_EXCEPTION_ACTION_NONE;
+	}
+
+	if (thread_info->exception_state.stopped_on_exception ||
+	    thread_info->exception_state.stopped_on_unhandled) {
+		thread_info->exception_state.stopped_on_exception = 0;
+		mono_debugger_unlock ();
+		return MONO_DEBUGGER_EXCEPTION_ACTION_NONE;
+	}
+
+	/* Protect the exception object from being garbage collected. */
+
+	thread_info->exception_state.stopped_on_unhandled = 0;
+	thread_info->exception_state.stopped_on_exception = 1;
+	thread_info->exception_state.last_exception = exc;
+
+	/*
+	 * Backwards compatibility:
+	 *
+	 * Older debugger versions only know `exc_info.stop' and older runtime versions check
+	 * `exc_info.stop != 0'.
+	 *
+	 * The debugger must check for `mono_debug_debugger_version >= 5' before accessing the
+	 * `stop_unhandled' field.
+	 */
+
+	exc_info.stack_pointer = stack;
+	exc_info.exception_obj = exc;
+	exc_info.stop = 0;
+	exc_info.stop_unhandled = 0;
+
+	mono_debugger_event (MONO_DEBUGGER_EVENT_THROW_EXCEPTION, (guint64) (gsize) &exc_info,
+			     (guint64) (gsize) addr);
+
+	if (!exc_info.stop) {
+		thread_info->exception_state.stopped_on_exception = 0;
+		thread_info->exception_state.last_exception = NULL;
+	} 
+
+	mono_debugger_unlock ();
+
+	if (exc_info.stop)
+		return MONO_DEBUGGER_EXCEPTION_ACTION_STOP;
+	else if (exc_info.stop_unhandled)
+		return MONO_DEBUGGER_EXCEPTION_ACTION_STOP_UNHANDLED;
+#endif
+
+	return MONO_DEBUGGER_EXCEPTION_ACTION_NONE;
+}
+
+gboolean
+mono_debugger_unhandled_exception (gpointer addr, gpointer stack, MonoObject *exc)
+{
+#ifdef MONO_DEBUGGER_SUPPORTED
+	MonoDebuggerThreadInfo *thread_info;
+
+	if (!mono_debug_using_mono_debugger ())
+		return FALSE;
+
+	if (exc) {
+		const gchar *name = mono_class_get_name (mono_object_get_class (exc));
+		if (!strcmp (name, "ThreadAbortException"))
+			return FALSE;
+	}
+
+	mono_debugger_lock ();
+
+	thread_info = find_debugger_thread_info (mono_thread_current ());
+	if (!thread_info) {
+		mono_debugger_unlock ();
+		return FALSE;
+	}
+
+	if (thread_info->exception_state.stopped_on_unhandled) {
+		thread_info->exception_state.stopped_on_unhandled = 0;
+		mono_debugger_unlock ();
+		return FALSE;
+	}
+
+	thread_info->exception_state.stopped_on_unhandled = 1;
+	thread_info->exception_state.last_exception = exc;
+
+	mono_debugger_event (MONO_DEBUGGER_EVENT_UNHANDLED_EXCEPTION,
+			     (guint64) (gsize) exc, (guint64) (gsize) addr);
+
+	return TRUE;
+#else
+	return FALSE;
+#endif
+}
+
+void
+mono_debugger_handle_exception (gpointer addr, gpointer stack, MonoObject *exc)
+{
+#ifdef MONO_DEBUGGER_SUPPORTED
+	MonoDebuggerThreadInfo *thread_info;
+	MonoDebuggerExceptionInfo exc_info;
+
+	if (!mono_debug_using_mono_debugger ())
+		return;
+
+	mono_debugger_lock ();
+
+	thread_info = find_debugger_thread_info (mono_thread_current ());
+	if (!thread_info) {
+		mono_debugger_unlock ();
+		return;
+	}
+
+	// Prevent the object from being finalized.
+	thread_info->exception_state.last_exception = exc;
+
+	exc_info.stack_pointer = stack;
+	exc_info.exception_obj = exc;
+	exc_info.stop = 0;
+	exc_info.stop_unhandled = 0;
+
+	mono_debugger_event (MONO_DEBUGGER_EVENT_HANDLE_EXCEPTION, (guint64) (gsize) &exc_info,
+			     (guint64) (gsize) addr);
+
+	mono_debugger_unlock ();
+#endif
+}
+
+#ifdef MONO_DEBUGGER_SUPPORTED
+
+static gchar *
+get_exception_message (MonoObject *exc)
+{
+	char *message = NULL;
+	MonoString *str; 
+	MonoMethod *method;
+	MonoClass *klass;
+	gint i;
+
+	if (mono_object_isinst (exc, mono_defaults.exception_class)) {
+		klass = exc->vtable->klass;
+		method = NULL;
+		while (klass && method == NULL) {
+			for (i = 0; i < klass->method.count; ++i) {
+				method = klass->methods [i];
+				if (!strcmp ("ToString", method->name) &&
+				    mono_method_signature (method)->param_count == 0 &&
+				    method->flags & METHOD_ATTRIBUTE_VIRTUAL &&
+				    method->flags & METHOD_ATTRIBUTE_PUBLIC) {
+					break;
+				}
+				method = NULL;
+			}
+			
+			if (method == NULL)
+				klass = klass->parent;
+		}
+
+		g_assert (method);
+
+		str = (MonoString *) mono_runtime_invoke (method, exc, NULL, NULL);
+		if (str)
+			message = mono_string_to_utf8 (str);
+	}
+
+	return message;
+}
+
+MonoObject *
+mono_debugger_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc)
+{
+	MonoDebuggerThreadInfo *thread_info;
+	MonoDebuggerExceptionState saved_exception_state;
+	MonoObject *retval;
+	gchar *message;
+
+	mono_debugger_lock ();
+
+	thread_info = find_debugger_thread_info (mono_thread_current ());
+	if (!thread_info) {
+		mono_debugger_unlock ();
+		return NULL;
+	}
+
+	saved_exception_state = thread_info->exception_state;
+
+	thread_info->exception_state.last_exception = NULL;
+	thread_info->exception_state.stopped_on_unhandled = 0;
+	thread_info->exception_state.stopped_on_exception = 0;
+
+	mono_debugger_unlock ();
+
+	if (!strcmp (method->name, ".ctor")) {
+		retval = obj = mono_object_new (mono_domain_get (), method->klass);
+
+		mono_runtime_invoke (method, obj, params, exc);
+	} else
+		retval = mono_runtime_invoke (method, obj, params, exc);
+
+	mono_debugger_lock ();
+
+	thread_info = find_debugger_thread_info (mono_thread_current ());
+	if (thread_info)
+		thread_info->exception_state = saved_exception_state;
+
+	mono_debugger_unlock ();
+
+	if (!exc || (*exc == NULL))
+		return retval;
+
+	retval = *exc;
+	message = get_exception_message (*exc);
+	if (message) {
+		*exc = (MonoObject *) mono_string_new_wrapper (message);
+		g_free (message);
+	}
+
+	return retval;
+}
+
+#endif

+ 18 - 0
mono/mini/debug-mini.h

@@ -9,6 +9,12 @@
 typedef struct _MonoDebuggerThreadInfo MonoDebuggerThreadInfo;
 extern MonoDebuggerThreadInfo *mono_debugger_thread_table;
 
+typedef enum {
+	MONO_DEBUGGER_EXCEPTION_ACTION_NONE		= 0,
+	MONO_DEBUGGER_EXCEPTION_ACTION_STOP		= 1,
+	MONO_DEBUGGER_EXCEPTION_ACTION_STOP_UNHANDLED	= 2
+} MonoDebuggerExceptionAction;
+
 void
 mono_debugger_thread_created (gsize tid, MonoThread *thread, MonoJitTlsData *jit_tls);
 
@@ -21,6 +27,18 @@ mono_debugger_extended_notification (MonoDebuggerEvent event, guint64 data, guin
 void
 mono_debugger_trampoline_compiled (MonoMethod *method, const guint8 *code);
 
+gboolean
+mono_debugger_unhandled_exception (gpointer addr, gpointer stack, MonoObject *exc);
+
+void
+mono_debugger_handle_exception (gpointer addr, gpointer stack, MonoObject *exc);
+
+MonoDebuggerExceptionAction
+mono_debugger_throw_exception (gpointer addr, gpointer stack, MonoObject *exc);
+
+MonoObject *
+mono_debugger_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc);
+
 /*
  * This is the old breakpoint interface.
  * It isn't used by the debugger anymore, but still when using the `--break' command

+ 68 - 25
mono/mini/exceptions-amd64.c

@@ -28,6 +28,7 @@
 
 #include "mini.h"
 #include "mini-amd64.h"
+#include "debug-mini.h"
 
 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
 
@@ -281,6 +282,43 @@ mono_arch_get_call_filter_full (guint32 *code_size, MonoJumpInfo **ji, gboolean
 	return start;
 }
 
+static gboolean
+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;
+
+		/*
+		 * The debugger wants us to stop only if this exception is user-unhandled.
+		 */
+
+		if (!mono_handle_exception (&ctx_cp, obj, MONO_CONTEXT_GET_IP (ctx), TRUE)) {
+			return mono_debugger_unhandled_exception (MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), obj);
+		}
+	}
+
+	return FALSE;
+}
+
 /* 
  * The first few arguments are dummy, to force the other arguments to be passed on
  * the stack, this avoids overwriting the argument registers in the throw trampoline.
@@ -314,37 +352,32 @@ mono_amd64_throw_exception (guint64 dummy1, guint64 dummy2, guint64 dummy3, guin
 	ctx.rcx = rcx;
 	ctx.rdx = rdx;
 
-	if (!rethrow && mono_debugger_throw_exception ((gpointer)(rip - 8), (gpointer)rsp, exc)) {
-		/*
-		 * The debugger wants us to stop on the `throw' instruction.
-		 * By the time we get here, it already inserted a breakpoint on
-		 * eip - 8 (which is the address of the `mov %r15,%rdi ; callq throw').
-		 */
+	if (mono_object_isinst (exc, mono_defaults.exception_class)) {
+		MonoException *mono_ex = (MonoException*)exc;
+		if (!rethrow)
+			mono_ex->stack_trace = NULL;
+	}
 
-		/* FIXME FIXME
-		 *
-		 * In case of a rethrow, the JIT is emitting code like this:
-		 *
-		 *    mov    0xffffffffffffffd0(%rbp),%rax'
-		 *    mov    %rax,%rdi
-		 *    callq  throw
-		 *
-		 * Here, restore_context() wouldn't restore the %rax register correctly.
-		 */
-		ctx.rip = rip - 8;
-		ctx.rsp = rsp + 8;
-		restore_context (&ctx);
-		g_assert_not_reached ();
+	if (mono_debug_using_mono_debugger ()) {
+		guint8 buf [16], *code;
+
+		mono_breakpoint_clean_code (NULL, (gpointer)rip, 8, buf, sizeof (buf));
+		code = buf + 8;
+
+		if (buf [3] == 0xe8) {
+			MonoContext ctx_cp = ctx;
+			ctx_cp.rip = rip - 5;
+
+			if (debugger_handle_exception (&ctx_cp, exc)) {
+				restore_context (&ctx_cp);
+				g_assert_not_reached ();
+			}
+		}
 	}
 
 	/* adjust eip so that it point into the call instruction */
 	ctx.rip -= 1;
 
-	if (mono_object_isinst (exc, mono_defaults.exception_class)) {
-		MonoException *mono_ex = (MonoException*)exc;
-		if (!rethrow)
-			mono_ex->stack_trace = NULL;
-	}
 	mono_handle_exception (&ctx, exc, (gpointer)rip, FALSE);
 	restore_context (&ctx);
 
@@ -683,6 +716,9 @@ mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
 
 	mono_arch_sigctx_to_monoctx (sigctx, &mctx);
 
+	if (debugger_handle_exception (&mctx, (MonoObject *)obj))
+		return TRUE;
+
 	mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), test_only);
 
 	mono_arch_monoctx_to_sigctx (&mctx, sigctx);
@@ -834,6 +870,13 @@ altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
 
 	restore_context = mono_get_restore_context ();
 	mono_arch_sigctx_to_monoctx (sigctx, &mctx);
+
+	if (debugger_handle_exception (&mctx, (MonoObject *)obj)) {
+		if (stack_ovf)
+			prepare_for_guard_pages (&mctx);
+		restore_context (&mctx);
+	}
+
 	mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), FALSE);
 	if (stack_ovf)
 		prepare_for_guard_pages (&mctx);

+ 68 - 15
mono/mini/exceptions-x86.c

@@ -361,6 +361,43 @@ mono_arch_get_call_filter (void)
 	return start;
 }
 
+static gboolean
+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;
+
+		/*
+		 * The debugger wants us to stop only if this exception is user-unhandled.
+		 */
+
+		if (!mono_handle_exception (&ctx_cp, obj, MONO_CONTEXT_GET_IP (ctx), TRUE)) {
+			return mono_debugger_unhandled_exception (MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), obj);
+		}
+	}
+
+	return FALSE;
+}
+
 static void
 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
 		 unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
@@ -383,26 +420,32 @@ throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsign
 	ctx.ecx = ecx;
 	ctx.eax = eax;
 
-	if (mono_debugger_throw_exception ((gpointer)(eip - 5), (gpointer)esp, exc)) {
-		/*
-		 * The debugger wants us to stop on the `throw' instruction.
-		 * By the time we get here, it already inserted a breakpoint on
-		 * eip - 5 (which is the address of the call).
-		 */
-		ctx.eip = eip - 5;
-		ctx.esp = esp + sizeof (gpointer);
-		restore_context (&ctx);
-		g_assert_not_reached ();
-	}
-
-	/* adjust eip so that it point into the call instruction */
-	ctx.eip -= 1;
-
 	if (mono_object_isinst (exc, mono_defaults.exception_class)) {
 		MonoException *mono_ex = (MonoException*)exc;
 		if (!rethrow)
 			mono_ex->stack_trace = NULL;
 	}
+
+	if (mono_debug_using_mono_debugger ()) {
+		guint8 buf [16], *code;
+
+		mono_breakpoint_clean_code (NULL, (gpointer)eip, 8, buf, sizeof (buf));
+		code = buf + 8;
+
+		if (buf [3] == 0xe8) {
+			MonoContext ctx_cp = ctx;
+			ctx_cp.eip = eip - 5;
+
+			if (debugger_handle_exception (&ctx_cp, exc)) {
+				restore_context (&ctx_cp);
+				g_assert_not_reached ();
+			}
+		}
+	}
+
+	/* adjust eip so that it point into the call instruction */
+	ctx.eip -= 1;
+
 	mono_handle_exception (&ctx, exc, (gpointer)eip, FALSE);
 	restore_context (&ctx);
 
@@ -791,6 +834,9 @@ mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
 
 	mono_arch_sigctx_to_monoctx (sigctx, &mctx);
 
+	if (debugger_handle_exception (&mctx, (MonoObject *)obj))
+		return TRUE;
+
 	mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
 
 	mono_arch_monoctx_to_sigctx (&mctx, sigctx);
@@ -832,6 +878,13 @@ altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
 
 	restore_context = mono_arch_get_restore_context ();
 	mono_arch_sigctx_to_monoctx (sigctx, &mctx);
+
+	if (debugger_handle_exception (&mctx, (MonoObject *)obj)) {
+		if (stack_ovf)
+			prepare_for_guard_pages (&mctx);
+		restore_context (&mctx);
+	}
+
 	mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, FALSE);
 	if (stack_ovf)
 		prepare_for_guard_pages (&mctx);

+ 1 - 13
mono/mini/mini-exceptions.c

@@ -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
@@ -919,19 +920,6 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
 			// 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 ();
-			}
 		}
 	}