Mon Mar 8 17:58:26 CET 2010 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / mini / debug-mini.c
index cc06068cf6d69785ffbd48179d14fab79f80a4a4..29131f9c3c6f8741a0a31ba2ce37bcf32921864e 100644 (file)
@@ -20,9 +20,7 @@
 #include <mono/metadata/mono-debug-debugger.h>
 #include "debug-mini.h"
 
-#ifdef HAVE_VALGRIND_H
-#include <valgrind/valgrind.h>
-#endif
+#include <mono/utils/valgrind.h>
 
 #ifdef MONO_DEBUGGER_SUPPORTED
 #include <libgc/include/libgc-mono-debugger.h>
@@ -41,6 +39,24 @@ typedef struct
        guint32 breakpoint_id;
 } MiniDebugMethodInfo;
 
+typedef struct {
+       MonoObject *last_exception;
+       guint32 stopped_on_exception : 1;
+       guint32 stopped_on_unhandled : 1;
+} MonoDebuggerExceptionState;
+
+typedef enum {
+       MONO_DEBUGGER_THREAD_FLAGS_NONE         = 0,
+       MONO_DEBUGGER_THREAD_FLAGS_INTERNAL     = 1,
+       MONO_DEBUGGER_THREAD_FLAGS_THREADPOOL   = 2
+} MonoDebuggerThreadFlags;
+
+typedef enum {
+       MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_NONE                = 0,
+       MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_IN_RUNTIME_INVOKE   = 1,
+       MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED     = 2
+} MonoDebuggerInternalThreadFlags;
+
 struct _MonoDebuggerThreadInfo {
        guint64 tid;
        guint64 lmf_addr;
@@ -59,13 +75,26 @@ struct _MonoDebuggerThreadInfo {
        guint32 stack_size;
        guint32 signal_stack_size;
 
+       guint32 thread_flags;
+
        /*
         * The debugger doesn't access anything beyond this point.
         */
+       MonoDebuggerExceptionState exception_state;
+
+       guint32 internal_flags;
+
        MonoJitTlsData *jit_tls;
-       MonoThread *thread;
+       MonoInternalThread *thread;
 };
 
+typedef struct {
+       gpointer stack_pointer;
+       MonoObject *exception_obj;
+       guint32 stop;
+       guint32 stop_unhandled;
+} MonoDebuggerExceptionInfo;
+
 MonoDebuggerThreadInfo *mono_debugger_thread_table = NULL;
 
 static inline void
@@ -107,7 +136,7 @@ mono_debug_open_method (MonoCompile *cfg)
 
        mono_class_init (cfg->method->klass);
 
-       header = mono_method_get_header (cfg->method);
+       header = cfg->header;
        g_assert (header);
        
        info->jit = jit = g_new0 (MonoDebugMethodJitInfo, 1);
@@ -123,10 +152,9 @@ write_variable (MonoInst *inst, MonoDebugVarInfo *var)
 
        if (inst->opcode == OP_REGVAR)
                var->index = inst->dreg | MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER;
-       else if (inst->flags & MONO_INST_IS_DEAD) {
-               // FIXME:
-               var->index = 0 | MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER;
-       } else {
+       else if (inst->flags & MONO_INST_IS_DEAD)
+               var->index = MONO_DEBUG_VAR_ADDRESS_MODE_DEAD;
+       else {
                /* the debug interface needs fixing to allow 0(%base) address */
                var->index = inst->inst_basereg | MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET;
                var->offset = inst->inst_offset;
@@ -231,6 +259,7 @@ mono_debug_add_vg_method (MonoMethod *method, MonoDebugMethodJitInfo *jit)
 
        g_free (addresses);
        g_free (lines);
+       mono_metadata_free_mh (header);
 #endif /* VALGRIND_ADD_LINE_INFO */
 }
 
@@ -253,7 +282,7 @@ mono_debug_close_method (MonoCompile *cfg)
        }
 
        method = cfg->method;
-       header = mono_method_get_header (method);
+       header = cfg->header;
        sig = mono_method_signature (method);
 
        jit = info->jit;
@@ -284,13 +313,10 @@ mono_debug_close_method (MonoCompile *cfg)
        for (i = 0; i < jit->num_line_numbers; i++)
                jit->line_numbers [i] = g_array_index (info->line_numbers, MonoDebugLineNumberEntry, i);
 
-       debug_info = mono_debug_add_method (method, jit, cfg->domain);
+       debug_info = mono_debug_add_method (cfg->method_to_register, jit, cfg->domain);
 
        mono_debug_add_vg_method (method, jit);
 
-       if (info->breakpoint_id)
-               mono_debugger_breakpoint_callback (method, info->breakpoint_id);
-
        mono_debugger_check_breakpoints (method, debug_info);
 
        mono_debug_free_method_jit_info (jit);
@@ -309,7 +335,7 @@ mono_debug_record_line_number (MonoCompile *cfg, MonoInst *ins, guint32 address)
        if (!info || !info->jit || !ins->cil_code)
                return;
 
-       header = mono_method_get_header (cfg->method);
+       header = cfg->header;
        g_assert (header);
 
        if ((ins->cil_code < header->code) ||
@@ -336,7 +362,7 @@ mono_debug_open_block (MonoCompile *cfg, MonoBasicBlock *bb, guint32 address)
        if (!info || !info->jit || !bb->cil_code)
                return;
 
-       header = mono_method_get_header (cfg->method);
+       header = cfg->header;
        g_assert (header);
 
        if ((bb->cil_code < header->code) ||
@@ -423,14 +449,16 @@ serialize_variable (MonoDebugVarInfo *var, guint8 *p, guint8 **endbuf)
 {
        guint32 flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
 
+       encode_value (var->index, p, &p);
+
        switch (flags) {
        case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
-               encode_value (var->index, p, &p);
                break;
        case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
-               encode_value (var->index, p, &p);
                encode_value (var->offset, p, &p);
                break;
+       case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
+               break;
        default:
                g_assert_not_reached ();
        }
@@ -501,6 +529,8 @@ deserialize_variable (MonoDebugVarInfo *var, guint8 *p, guint8 **endbuf)
        case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
                var->offset = decode_value (p, &p);
                break;
+       case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
+               break;
        default:
                g_assert_not_reached ();
        }
@@ -560,6 +590,7 @@ deserialize_debug_info (MonoMethod *method, guint8 *code_start, guint8 *buf, gui
                prev_native_offset = native_offset;
        }
 
+       mono_metadata_free_mh (header);
        return jit;
 }
 
@@ -728,7 +759,8 @@ mono_debugger_method_has_breakpoint (MonoMethod *method)
 {
        int i;
 
-       if (!breakpoints || (method->wrapper_type != MONO_WRAPPER_NONE))
+       if (!breakpoints || ((method->wrapper_type != MONO_WRAPPER_NONE) &&
+                                                (method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)))
                return 0;
 
        for (i = 0; i < breakpoints->len; i++) {
@@ -750,7 +782,7 @@ mono_debugger_breakpoint_callback (MonoMethod *method, guint32 index)
 }
 
 void
-mono_debugger_thread_created (gsize tid, MonoThread *thread, MonoJitTlsData *jit_tls)
+mono_debugger_thread_created (gsize tid, MonoThread *thread, MonoJitTlsData *jit_tls, gpointer func)
 {
 #ifdef MONO_DEBUGGER_SUPPORTED
        size_t stsize = 0;
@@ -766,7 +798,7 @@ mono_debugger_thread_created (gsize tid, MonoThread *thread, MonoJitTlsData *jit
 
        info = g_new0 (MonoDebuggerThreadInfo, 1);
        info->tid = tid;
-       info->thread = thread;
+       info->thread = thread->internal_thread;
        info->stack_start = (guint64) (gsize) staddr;
        info->signal_stack_start = (guint64) (gsize) jit_tls->signal_stack;
        info->stack_size = stsize;
@@ -775,6 +807,11 @@ mono_debugger_thread_created (gsize tid, MonoThread *thread, MonoJitTlsData *jit
        info->lmf_addr = (guint64) (gsize) mono_get_lmf_addr ();
        info->jit_tls = jit_tls;
 
+       if (func)
+               info->thread_flags = MONO_DEBUGGER_THREAD_FLAGS_INTERNAL;
+       if (thread->internal_thread->threadpool_thread)
+               info->thread_flags |= MONO_DEBUGGER_THREAD_FLAGS_THREADPOOL;
+
        info->next = mono_debugger_thread_table;
        mono_debugger_thread_table = info;
 
@@ -819,7 +856,7 @@ mono_debugger_extended_notification (MonoDebuggerEvent event, guint64 data, guin
 {
 #ifdef MONO_DEBUGGER_SUPPORTED
        MonoDebuggerThreadInfo **ptr;
-       MonoThread *thread = mono_thread_current ();
+       MonoInternalThread *thread = mono_thread_internal_current ();
 
        if (!mono_debug_using_mono_debugger ())
                return;
@@ -843,8 +880,335 @@ mono_debugger_extended_notification (MonoDebuggerEvent event, guint64 data, guin
 }
 
 void
-mono_debugger_trampoline_compiled (MonoMethod *method, const guint8 *code)
+mono_debugger_trampoline_compiled (const guint8 *trampoline, MonoMethod *method, const guint8 *code)
 {
-       mono_debugger_extended_notification (MONO_DEBUGGER_EVENT_TRAMPOLINE,
+#ifdef MONO_DEBUGGER_SUPPORTED
+       struct {
+               const guint8 * trampoline;
+               MonoMethod *method;
+               const guint8 *code;
+       } info = { trampoline, method, code };
+
+       mono_debugger_extended_notification (MONO_DEBUGGER_EVENT_OLD_TRAMPOLINE,
                                             (guint64) (gsize) method, (guint64) (gsize) code);
+       mono_debugger_extended_notification (MONO_DEBUGGER_EVENT_TRAMPOLINE,
+                                            (guint64) (gsize) &info, 0);
+#endif
+}
+
+#if MONO_DEBUGGER_SUPPORTED
+static MonoDebuggerThreadInfo *
+find_debugger_thread_info (MonoInternalThread *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_internal_current ());
+       if (!thread_info) {
+               mono_debugger_unlock ();
+               return MONO_DEBUGGER_EXCEPTION_ACTION_NONE;
+       }
+
+       if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED) != 0) {
+               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_internal_current ());
+       if (!thread_info) {
+               mono_debugger_unlock ();
+               return FALSE;
+       }
+
+       if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED) != 0) {
+               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
+}
+
+/*
+ * mono_debugger_call_exception_handler:
+ *
+ * Called from mono_handle_exception_internal() to tell the debugger that we're about
+ * to invoke an exception handler.
+ *
+ * The debugger may choose to set a breakpoint at @addr.  This is used if the user is
+ * single-stepping from a `try' into a `catch' block, for instance.
+ */
+
+void
+mono_debugger_call_exception_handler (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_internal_current ());
+       if (!thread_info) {
+               mono_debugger_unlock ();
+               return;
+       }
+
+       if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED) != 0) {
+               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_internal_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;
+
+       thread_info->internal_flags |= MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_IN_RUNTIME_INVOKE;
+
+       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->exception_state = saved_exception_state;
+       thread_info->internal_flags &= ~MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_IN_RUNTIME_INVOKE;
+
+       if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED) != 0) {
+               thread_info->internal_flags &= ~MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED;
+               mono_thread_internal_reset_abort (thread_info->thread);
+
+               mono_debugger_unlock ();
+
+               *exc = NULL;
+               return NULL;
+       }
+
+       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;
+}
+
+gboolean
+mono_debugger_abort_runtime_invoke ()
+{
+       MonoInternalThread *thread = mono_thread_internal_current ();
+       MonoDebuggerThreadInfo *thread_info;
+
+       mono_debugger_lock ();
+
+       thread_info = find_debugger_thread_info (thread);
+       if (!thread_info) {
+               mono_debugger_unlock ();
+               return FALSE;
+       }
+
+       if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_IN_RUNTIME_INVOKE) == 0) {
+               mono_debugger_unlock ();
+               return FALSE;
+       }
+
+       if ((thread_info->internal_flags & MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED) != 0) {
+               mono_debugger_unlock ();
+               return TRUE;
+       }
+
+       thread_info->internal_flags |= MONO_DEBUGGER_INTERNAL_THREAD_FLAGS_ABORT_REQUESTED;
+       ves_icall_System_Threading_Thread_Abort (thread_info->thread, NULL);
+
+       mono_debugger_unlock ();
+       return TRUE;
+}
+
+#endif