2009-08-10 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mono / metadata / mono-debug-debugger.c
index 4134060875adb89adcdcbd3971ca5bee5c72deaf..6755f63630980bd65f60e80a3cea1df44182be44 100644 (file)
@@ -1,3 +1,13 @@
+/*
+ * mono-debug-debugger.c: 
+ *
+ * Author:
+ *     Mono Project (http://www.mono-project.com)
+ *
+ * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
+ * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
+ */
+
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
@@ -8,9 +18,10 @@
 #include <mono/metadata/appdomain.h>
 #include <mono/metadata/gc-internal.h>
 #include <mono/metadata/threads.h>
-#include <mono/os/gc_wrapper.h>
+#include <mono/metadata/gc-internal.h>
 #include <mono/metadata/object-internals.h>
 #include <mono/metadata/class-internals.h>
+#include <mono/metadata/domain-internals.h>
 #include <mono/metadata/exception.h>
 #include <mono/metadata/mono-debug.h>
 #include <mono/metadata/mono-debug-debugger.h>
@@ -20,14 +31,34 @@ static guint32 debugger_lock_level = 0;
 static CRITICAL_SECTION debugger_lock_mutex;
 static gboolean mono_debugger_use_debugger = FALSE;
 static MonoObject *last_exception = NULL;
+volatile gint32 _mono_debugger_interruption_request = 0;
 
 void (*mono_debugger_event_handler) (MonoDebuggerEvent event, guint64 data, guint64 arg) = NULL;
 
+typedef struct
+{
+       guint32 index;
+       MonoMethod *method;
+       MonoDebugMethodAddressList *address_list;
+} MethodBreakpointInfo;
+
+typedef struct {
+       MonoImage *image;
+       guint64 index;
+       guint32 token;
+       gchar *name_space;
+       gchar *name;
+} ClassInitCallback;
+
 typedef struct {
-       gpointer stack_pointer;
-       MonoObject *exception_obj;
-       guint32 stop;
-} MonoDebuggerExceptionInfo;
+       guint32 id;
+       guint32 shadow_path_len;
+       gchar *shadow_path;
+       MonoDomain *domain;
+       MonoAppDomainSetup *setup;
+} AppDomainSetupInfo;
+
+static GPtrArray *class_init_callbacks = NULL;
 
 static int initialized = 0;
 
@@ -66,6 +97,28 @@ mono_debugger_event (MonoDebuggerEvent event, guint64 data, guint64 arg)
                (* mono_debugger_event_handler) (event, data, arg);
 }
 
+void
+mono_debugger_event_create_appdomain (MonoDomain *domain, gchar *shadow_path)
+{
+       AppDomainSetupInfo info;
+
+       info.id = mono_domain_get_id (domain);
+       info.shadow_path_len = shadow_path ? strlen (shadow_path) : 0;
+       info.shadow_path = shadow_path;
+
+       info.domain = domain;
+       info.setup = domain->setup;
+
+       mono_debugger_event (MONO_DEBUGGER_EVENT_CREATE_APPDOMAIN, (guint64) (gsize) &info, 0);
+}
+
+void
+mono_debugger_event_unload_appdomain (MonoDomain *domain)
+{
+       mono_debugger_event (MONO_DEBUGGER_EVENT_UNLOAD_APPDOMAIN,
+                            (guint64) (gsize) domain, (guint64) mono_domain_get_id (domain));
+}
+
 void
 mono_debugger_cleanup (void)
 {
@@ -73,124 +126,200 @@ mono_debugger_cleanup (void)
        mono_debugger_event_handler = NULL;
 }
 
-gboolean
-mono_debugger_unhandled_exception (gpointer addr, gpointer stack, MonoObject *exc)
+void
+mono_debugger_check_interruption (void)
+{
+       if (!_mono_debugger_interruption_request)
+               return;
+
+       mono_debugger_lock ();
+       mono_debugger_event (MONO_DEBUGGER_EVENT_INTERRUPTION_REQUEST, 0, 0);
+       mono_debugger_unlock ();
+}
+
+/*
+ * Debugger breakpoint interface.
+ *
+ * This interface is used to insert breakpoints on methods which are not yet JITed.
+ * The debugging code keeps a list of all such breakpoints and automatically inserts the
+ * breakpoint when the method is JITed.
+ */
+
+static GPtrArray *method_breakpoints = NULL;
+
+MonoDebugMethodAddressList *
+mono_debugger_insert_method_breakpoint (MonoMethod *method, guint64 index)
 {
-       const gchar *name;
+       MethodBreakpointInfo *info;
+
+       info = g_new0 (MethodBreakpointInfo, 1);
+       info->method = method;
+       info->index = index;
 
-       if (!mono_debugger_use_debugger)
-               return FALSE;
+       info->address_list = mono_debug_lookup_method_addresses (method);
 
-       name = mono_class_get_name (mono_object_get_class (exc));
-       if (!strcmp (name, "ThreadAbortException"))
-               return FALSE;
+       if (!method_breakpoints)
+               method_breakpoints = g_ptr_array_new ();
 
-       // Prevent the object from being finalized.
-       last_exception = exc;
+       g_ptr_array_add (method_breakpoints, info);
 
-       mono_debugger_event (MONO_DEBUGGER_EVENT_UNHANDLED_EXCEPTION,
-                            (guint64) (gsize) exc, (guint64) (gsize) addr);
-       return TRUE;
+       return info->address_list;
 }
 
-void
-mono_debugger_handle_exception (gpointer addr, gpointer stack, MonoObject *exc)
+int
+mono_debugger_remove_method_breakpoint (guint64 index)
 {
-       MonoDebuggerExceptionInfo info;
+       int i;
 
-       if (!mono_debugger_use_debugger)
-               return;
+       if (!method_breakpoints)
+               return 0;
 
-       // Prevent the object from being finalized.
-       last_exception = exc;
+       for (i = 0; i < method_breakpoints->len; i++) {
+               MethodBreakpointInfo *info = g_ptr_array_index (method_breakpoints, i);
 
-       info.stack_pointer = stack;
-       info.exception_obj = exc;
-       info.stop = 0;
+               if (info->index != index)
+                       continue;
+
+               g_ptr_array_remove (method_breakpoints, info);
+               g_free (info->address_list);
+               g_free (info);
+               return 1;
+       }
 
-       mono_debugger_event (MONO_DEBUGGER_EVENT_HANDLE_EXCEPTION, (guint64) (gsize) &info,
-                            (guint64) (gsize) addr);
+       return 0;
 }
 
-gboolean
-mono_debugger_throw_exception (gpointer addr, gpointer stack, MonoObject *exc)
+void
+mono_debugger_check_breakpoints (MonoMethod *method, MonoDebugMethodAddress *debug_info)
 {
-       MonoDebuggerExceptionInfo info;
+       int i;
+
+       if (method->is_inflated)
+               method = ((MonoMethodInflated *) method)->declaring;
+
+       if (method_breakpoints) {
+               for (i = 0; i < method_breakpoints->len; i++) {
+                       MethodBreakpointInfo *info = g_ptr_array_index (method_breakpoints, i);
+
+                       if (method != info->method)
+                               continue;
 
-       if (!mono_debugger_use_debugger)
-               return FALSE;
+                       mono_debugger_event (MONO_DEBUGGER_EVENT_JIT_BREAKPOINT,
+                                            (guint64) (gsize) debug_info, info->index);
+               }
+       }
 
-       // Prevent the object from being finalized.
-       last_exception = exc;
+       if (class_init_callbacks) {
+               for (i = 0; i < class_init_callbacks->len; i++) {
+                       ClassInitCallback *info = g_ptr_array_index (class_init_callbacks, i);
 
-       info.stack_pointer = stack;
-       info.exception_obj = exc;
-       info.stop = 0;
+                       if ((method->token != info->token) || (method->klass->image != info->image))
+                               continue;
 
-       mono_debugger_event (MONO_DEBUGGER_EVENT_THROW_EXCEPTION, (guint64) (gsize) &info,
-                            (guint64) (gsize) addr);
-       return info.stop != 0;
+                       mono_debugger_event (MONO_DEBUGGER_EVENT_JIT_BREAKPOINT,
+                                            (guint64) (gsize) debug_info, info->index);
+               }
+       }
 }
 
-static gchar *
-get_exception_message (MonoObject *exc)
+MonoClass *
+mono_debugger_register_class_init_callback (MonoImage *image, const gchar *full_name,
+                                           guint32 method_token, guint32 index)
 {
-       char *message = NULL;
-       MonoString *str; 
-       MonoMethod *method;
+       ClassInitCallback *info;
        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;
-               }
+       gchar *name_space, *name, *pos;
 
-               g_assert (method);
+       name = g_strdup (full_name);
 
-               str = (MonoString *) mono_runtime_invoke (method, exc, NULL, NULL);
-               if (str)
-                       message = mono_string_to_utf8 (str);
+       pos = strrchr (name, '.');
+       if (pos) {
+               name_space = name;
+               *pos = 0;
+               name = pos + 1;
+       } else {
+               name_space = NULL;
        }
 
-       return message;
+       mono_loader_lock ();
+
+       klass = mono_class_from_name (image, name_space ? name_space : "", name);
+
+       info = g_new0 (ClassInitCallback, 1);
+       info->image = image;
+       info->index = index;
+       info->token = method_token;
+       info->name_space = name_space;
+       info->name = name;
+
+       if (!class_init_callbacks)
+               class_init_callbacks = g_ptr_array_new ();
+
+       g_ptr_array_add (class_init_callbacks, info);
+       mono_loader_unlock ();
+       return klass;
 }
 
-MonoObject *
-mono_debugger_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc)
+void
+mono_debugger_remove_class_init_callback (int index)
 {
-       MonoObject *retval;
-       gchar *message;
+       int i;
 
-       if (!strcmp (method->name, ".ctor")) {
-               retval = obj = mono_object_new (mono_domain_get (), method->klass);
+       if (!class_init_callbacks)
+               return;
 
-               mono_runtime_invoke (method, obj, params, exc);
-       } else
-               retval = mono_runtime_invoke (method, obj, params, exc);
+       for (i = 0; i < class_init_callbacks->len; i++) {
+               ClassInitCallback *info = g_ptr_array_index (class_init_callbacks, i);
 
-       if (!exc || (*exc == NULL))
-               return retval;
+               if (info->index != index)
+                       continue;
 
-       message = get_exception_message (*exc);
-       if (message) {
-               *exc = (MonoObject *) mono_string_new_wrapper (message);
-               g_free (message);
+               g_ptr_array_remove (class_init_callbacks, info);
+               if (info->name_space)
+                       g_free (info->name_space);
+               else
+                       g_free (info->name);
+               g_free (info);
        }
+}
+
+void
+mono_debugger_class_initialized (MonoClass *klass)
+{
+       int i;
+
+       if (!class_init_callbacks)
+               return;
+
+ again:
+       for (i = 0; i < class_init_callbacks->len; i++) {
+               ClassInitCallback *info = g_ptr_array_index (class_init_callbacks, i);
 
-       return retval;
+               if (info->name_space && strcmp (info->name_space, klass->name_space))
+                       continue;
+               if (strcmp (info->name, klass->name))
+                       continue;
+
+               mono_debugger_event (MONO_DEBUGGER_EVENT_CLASS_INITIALIZED,
+                                    (guint64) (gsize) klass, info->index);
+
+               if (info->token) {
+                       int j;
+
+                       for (j = 0; j < klass->method.count; j++) {
+                               if (klass->methods [j]->token != info->token)
+                                       continue;
+
+                               mono_debugger_insert_method_breakpoint (klass->methods [j], info->index);
+                       }
+               }
+
+               g_ptr_array_remove (class_init_callbacks, info);
+               if (info->name_space)
+                       g_free (info->name_space);
+               else
+                       g_free (info->name);
+               g_free (info);
+               goto again;
+       }
 }