Implement passing of this as the first argument for ia64 correctly.
[mono.git] / mono / mini / debugger-agent.c
index de673d642bc720755700d335c7697cd8afa4e96a..86315e332230fc0320f999ba32301853d5016f39 100644 (file)
@@ -1,10 +1,10 @@
 /*
- * debugger-agent.c: Debugger back-end module
+ * debugger-agent.c: Soft Debugger back-end module
  *
  * Author:
  *   Zoltan Varga (vargaz@gmail.com)
  *
- * (C) 2009 Novell, Inc.
+ * Copyright 2009-2010 Novell, Inc.
  */
 
 #include <config.h>
@@ -14,6 +14,9 @@
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 #endif
@@ -41,6 +44,9 @@
 #endif
 
 #ifdef HOST_WIN32
+#ifdef _MSC_VER
+#include <winsock2.h>
+#endif
 #include <ws2tcpip.h>
 #ifdef __GNUC__
 /* cygwin's headers do not seem to define these */
@@ -64,6 +70,7 @@ int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD,
 #include <mono/metadata/gc-internal.h>
 #include <mono/metadata/threads-types.h>
 #include <mono/metadata/socket-io.h>
+#include <mono/metadata/assembly.h>
 #include <mono/utils/mono-semaphore.h>
 #include "debugger-agent.h"
 #include "mini.h"
@@ -96,6 +103,7 @@ typedef struct {
        GSList *onthrow;
        int timeout;
        char *launch;
+       gboolean embedding;
 } AgentConfig;
 
 typedef struct
@@ -114,7 +122,9 @@ typedef struct
        gboolean has_ctx;
 } StackFrame;
 
-typedef struct
+typedef struct _InvokeData InvokeData;
+
+struct _InvokeData
 {
        int id;
        int flags;
@@ -128,7 +138,10 @@ typedef struct
         */
        MonoMethod *method;
        gpointer *args;
-} InvokeData;
+       guint32 suspend_count;
+
+       InvokeData *last_invoke;
+};
 
 typedef struct {
        MonoContext ctx;
@@ -149,7 +162,7 @@ typedef struct {
         * Points to data about a pending invoke which needs to be executed after the thread
         * resumes.
         */
-       InvokeData *invoke;
+       InvokeData *pending_invoke;
        /*
         * Set to TRUE if this thread is suspended in suspend_current () or it is executing
         * native code.
@@ -199,6 +212,18 @@ typedef struct {
         * The lmf where the stack walk can be started for running threads.
         */
        gpointer async_lmf;
+
+       /*
+        * The callee address of the last mono_runtime_invoke call
+        */
+       gpointer invoke_addr;
+
+       gboolean abort_requested;
+
+       /*
+        * The current mono_runtime_invoke invocation.
+        */
+       InvokeData *invoke;
 } DebuggerTlsData;
 
 /* 
@@ -208,7 +233,7 @@ typedef struct {
 #define HEADER_LENGTH 11
 
 #define MAJOR_VERSION 2
-#define MINOR_VERSION 0
+#define MINOR_VERSION 2
 
 typedef enum {
        CMD_SET_VM = 1,
@@ -257,7 +282,9 @@ typedef enum {
        ERR_NOT_IMPLEMENTED = 100,
        ERR_NOT_SUSPENDED = 101,
        ERR_INVALID_ARGUMENT = 102,
-       ERR_UNLOADED = 103
+       ERR_UNLOADED = 103,
+       ERR_NO_INVOCATION = 104,
+       ERR_ABSENT_INFORMATION = 105
 } ErrorCode;
 
 typedef enum {
@@ -309,14 +336,17 @@ typedef enum {
        CMD_VM_RESUME = 4,
        CMD_VM_EXIT = 5,
        CMD_VM_DISPOSE = 6,
-       CMD_VM_INVOKE_METHOD = 7
+       CMD_VM_INVOKE_METHOD = 7,
+       CMD_VM_SET_PROTOCOL_VERSION = 8,
+       CMD_VM_ABORT_INVOKE = 9
 } CmdVM;
 
 typedef enum {
        CMD_THREAD_GET_FRAME_INFO = 1,
        CMD_THREAD_GET_NAME = 2,
        CMD_THREAD_GET_STATE = 3,
-       CMD_THREAD_GET_INFO = 4
+       CMD_THREAD_GET_INFO = 4,
+       CMD_THREAD_GET_ID = 5
 } CmdThread;
 
 typedef enum {
@@ -335,7 +365,8 @@ typedef enum {
        CMD_APPDOMAIN_GET_ASSEMBLIES = 3,
        CMD_APPDOMAIN_GET_ENTRY_ASSEMBLY = 4,
        CMD_APPDOMAIN_CREATE_STRING = 5,
-       CMD_APPDOMAIN_GET_CORLIB = 6
+       CMD_APPDOMAIN_GET_CORLIB = 6,
+       CMD_APPDOMAIN_CREATE_BOXED_VALUE = 7,
 } CmdAppDomain;
 
 typedef enum {
@@ -374,7 +405,8 @@ typedef enum {
        CMD_TYPE_GET_PROPERTIES = 9,
        CMD_TYPE_GET_CATTRS = 10,
        CMD_TYPE_GET_FIELD_CATTRS = 11,
-       CMD_TYPE_GET_PROPERTY_CATTRS = 12
+       CMD_TYPE_GET_PROPERTY_CATTRS = 12,
+       CMD_TYPE_GET_SOURCE_FILES_2 = 13,
 } CmdType;
 
 typedef enum {
@@ -410,6 +442,7 @@ typedef struct {
                MonoClass *exc_class; /* For kind == MONO_KIND_EXCEPTION_ONLY */
                MonoAssembly **assemblies; /* For kind == MONO_KIND_ASSEMBLY_ONLY */
        } data;
+       gboolean caught, uncaught; /* For kind == MOD_KIND_EXCEPTION_ONLY */
 } Modifier;
 
 typedef struct{
@@ -433,7 +466,21 @@ typedef struct {
        gpointer start_sp;
        MonoMethod *last_method;
        int last_line;
-} MonoSingleStepReq;
+       /* Whenever single stepping is performed using start/stop_single_stepping () */
+       gboolean global;
+       /* The list of breakpoints used to implement step-over */
+       GSList *bps;
+} SingleStepReq;
+
+/*
+ * Contains additional information for an event
+ */
+typedef struct {
+       /* For EVENT_KIND_EXCEPTION */
+       MonoObject *exc;
+       MonoContext catch_ctx;
+       gboolean caught;
+} EventInfo;
 
 /* Dummy structure used for the profiler callbacks */
 typedef struct {
@@ -488,6 +535,8 @@ static HANDLE debugger_thread_handle;
 
 static int log_level;
 
+static gboolean embedding;
+
 static FILE *log_file;
 
 /* Classes whose class load event has been sent */
@@ -496,6 +545,9 @@ static GHashTable *loaded_classes;
 /* Assemblies whose assembly load event has no been sent yet */
 static GPtrArray *pending_assembly_loads;
 
+/* Types whose type load event has no been sent yet */
+static GPtrArray *pending_type_loads;
+
 /* Whenever the debugger thread has exited */
 static gboolean debugger_thread_exited;
 
@@ -508,13 +560,23 @@ static mono_mutex_t debugger_thread_exited_mutex;
 static DebuggerProfiler debugger_profiler;
 
 /* The single step request instance */
-static MonoSingleStepReq *ss_req = NULL;
+static SingleStepReq *ss_req = NULL;
+static gpointer ss_invoke_addr = NULL;
 
 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
 /* Number of single stepping operations in progress */
 static int ss_count;
 #endif
 
+/* The protocol version of the client */
+static int major_version, minor_version;
+
+/* Whenever the variables above are set by the client */
+static gboolean protocol_version_set;
+
+/* A hash table containing all active domains */
+static GHashTable *domains;
+
 static void transport_connect (const char *host, int port);
 
 static guint32 WINAPI debugger_thread (void *arg);
@@ -523,14 +585,16 @@ static void runtime_initialized (MonoProfiler *prof);
 
 static void runtime_shutdown (MonoProfiler *prof);
 
-static void thread_startup (MonoProfiler *prof, gsize tid);
+static void thread_startup (MonoProfiler *prof, uintptr_t tid);
 
-static void thread_end (MonoProfiler *prof, gsize tid);
+static void thread_end (MonoProfiler *prof, uintptr_t tid);
 
 static void appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result);
 
 static void appdomain_unload (MonoProfiler *prof, MonoDomain *domain);
 
+static void invalidate_each_thread (gpointer key, gpointer value, gpointer user_data);
+
 static void assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result);
 
 static void assembly_unload (MonoProfiler *prof, MonoAssembly *assembly);
@@ -549,6 +613,12 @@ static void stop_single_stepping (void);
 
 static void suspend_current (void);
 
+static void clear_event_requests_for_assembly (MonoAssembly *assembly);
+
+static void clear_breakpoints_for_domain (MonoDomain *domain);
+
+static void clear_types_for_assembly (MonoAssembly *assembly);
+
 /* Submodule init/cleanup */
 static void breakpoints_init (void);
 static void breakpoints_cleanup (void);
@@ -561,8 +631,9 @@ static void ids_cleanup (void);
 
 static void suspend_init (void);
 
-static ErrorCode ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req);
-static void ss_stop (EventRequest *req);
+static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls);
+static ErrorCode ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req);
+static void ss_destroy (SingleStepReq *req);
 
 static void start_debugger_thread (void);
 
@@ -650,6 +721,8 @@ mono_debugger_agent_parse_options (char *options)
                } else if (strncmp (arg, "onthrow=", 8) == 0) {
                        /* We support multiple onthrow= options */
                        agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup (arg + 8));
+               } else if (strncmp (arg, "onthrow", 7) == 0) {
+                       agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup (""));
                } else if (strncmp (arg, "help", 4) == 0) {
                        print_usage ();
                        exit (0);
@@ -657,6 +730,8 @@ mono_debugger_agent_parse_options (char *options)
                        agent_config.timeout = atoi (arg + 8);
                } else if (strncmp (arg, "launch=", 7) == 0) {
                        agent_config.launch = g_strdup (arg + 7);
+               } else if (strncmp (arg, "embedding=", 10) == 0) {
+                       agent_config.embedding = atoi (arg + 10) == 1;
                } else {
                        print_usage ();
                        exit (1);
@@ -700,7 +775,7 @@ mono_debugger_agent_init (void)
        mono_profiler_install ((MonoProfiler*)&debugger_profiler, runtime_shutdown);
        mono_profiler_set_events (MONO_PROFILE_APPDOMAIN_EVENTS | MONO_PROFILE_THREADS | MONO_PROFILE_ASSEMBLY_EVENTS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_METHOD_EVENTS);
        mono_profiler_install_runtime_initialized (runtime_initialized);
-       mono_profiler_install_appdomain (NULL, appdomain_load, appdomain_unload, NULL);
+       mono_profiler_install_appdomain (NULL, appdomain_load, NULL, appdomain_unload);
        mono_profiler_install_thread (thread_startup, thread_end);
        mono_profiler_install_assembly (NULL, assembly_load, assembly_unload, NULL);
        mono_profiler_install_jit_end (jit_end);
@@ -708,20 +783,24 @@ mono_debugger_agent_init (void)
 
        debugger_tls_id = TlsAlloc ();
 
-       thread_to_tls = mono_g_hash_table_new (NULL, NULL);
-       MONO_GC_REGISTER_ROOT (thread_to_tls);
+       thread_to_tls = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC);
+       MONO_GC_REGISTER_ROOT_FIXED (thread_to_tls);
 
        tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
-       MONO_GC_REGISTER_ROOT (tid_to_thread);
+       MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread);
 
        tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
-       MONO_GC_REGISTER_ROOT (tid_to_thread_obj);
+       MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread_obj);
 
        loaded_classes = g_hash_table_new (mono_aligned_addr_hash, NULL);
        pending_assembly_loads = g_ptr_array_new ();
+       pending_type_loads = g_ptr_array_new ();
+       domains = g_hash_table_new (mono_aligned_addr_hash, NULL);
 
        log_level = agent_config.log_level;
 
+       embedding = agent_config.embedding;
+
        if (agent_config.log_file) {
                log_file = fopen (agent_config.log_file, "w+");
                if (!log_file) {
@@ -827,8 +906,17 @@ mono_debugger_agent_cleanup (void)
        //WaitForSingleObject (debugger_thread_handle, INFINITE);
        if (GetCurrentThreadId () != debugger_thread_id) {
                mono_mutex_lock (&debugger_thread_exited_mutex);
-               if (!debugger_thread_exited)
+               while (!debugger_thread_exited) {
+#ifdef HOST_WIN32
+                       if (WAIT_TIMEOUT == WaitForSingleObject(debugger_thread_exited_cond, 0)) {
+                               mono_mutex_unlock (&debugger_thread_exited_mutex);
+                               Sleep(0);
+                               mono_mutex_lock (&debugger_thread_exited_mutex);
+                       }
+#else
                        mono_cond_wait (&debugger_thread_exited_cond, &debugger_thread_exited_mutex);
+#endif
+               }
                mono_mutex_unlock (&debugger_thread_exited_mutex);
        }
 
@@ -846,6 +934,24 @@ mono_debugger_agent_cleanup (void)
        mono_cond_destroy (&debugger_thread_exited_cond);
 }
 
+/*
+ * recv_length:
+ *
+ * recv() + handle incomplete reads and EINTR
+ */
+static int
+recv_length (int fd, void *buf, int len, int flags)
+{
+       int res;
+       int total = 0;
+
+       do {
+               res = recv (fd, (char *) buf + total, len - total, flags);
+               if (res > 0)
+                       total += res;
+       } while ((res > 0 && total < len) || (res == -1 && errno == EINTR));
+       return total;
+}
 /*
  * transport_connect:
  *
@@ -929,7 +1035,15 @@ transport_connect (const char *host, int port)
                                break;
                        }
 
+#ifndef HOST_WIN32
+                       /*
+                        * this function is not present on win2000 which we still support, and the
+                        * workaround described here:
+                        * http://msdn.microsoft.com/en-us/library/ms737931(VS.85).aspx
+                        * only works with MSVC.
+                        */
                        freeaddrinfo (result);
+#endif
                }
 
                DEBUG (1, fprintf (log_file, "Listening on %s:%d (timeout=%d ms)...\n", host, port, agent_config.timeout));
@@ -973,7 +1087,10 @@ transport_connect (const char *host, int port)
 
                conn_fd = sfd;
 
+#ifndef HOST_WIN32
+               /* See the comment above */
                freeaddrinfo (result);
+#endif
 
                if (rp == 0) {
                        fprintf (stderr, "debugger-agent: Unable to connect to %s:%d\n", host, port);
@@ -983,16 +1100,26 @@ transport_connect (const char *host, int port)
        
        /* Write handshake message */
        sprintf (handshake_msg, "DWP-Handshake");
-       res = send (conn_fd, handshake_msg, strlen (handshake_msg), 0);
+       do {
+               res = send (conn_fd, handshake_msg, strlen (handshake_msg), 0);
+       } while (res == -1 && errno == EINTR);
        g_assert (res != -1);
 
        /* Read answer */
-       res = recv (conn_fd, buf, strlen (handshake_msg), 0);
+       res = recv_length (conn_fd, buf, strlen (handshake_msg), 0);
        if ((res != strlen (handshake_msg)) || (memcmp (buf, handshake_msg, strlen (handshake_msg) != 0))) {
                fprintf (stderr, "debugger-agent: DWP handshake failed.\n");
                exit (1);
        }
 
+       /*
+        * To support older clients, the client sends its protocol version after connecting
+        * using a command. Until that is received, default to our protocol version.
+        */
+       major_version = MAJOR_VERSION;
+       minor_version = MINOR_VERSION;
+       protocol_version_set = FALSE;
+
        /* 
         * Set TCP_NODELAY on the socket so the client receives events/command
         * results immediately.
@@ -1013,7 +1140,9 @@ transport_send (guint8 *data, int len)
 {
        int res;
 
-       res = send (conn_fd, data, len, 0);
+       do {
+               res = send (conn_fd, data, len, 0);
+       } while (res == -1 && errno == EINTR);
        if (res != len)
                return FALSE;
        else
@@ -1273,26 +1402,42 @@ static ObjRef*
 get_objref (MonoObject *obj)
 {
        ObjRef *ref;
+       GSList *reflist = NULL, *l;
+       int hash = 0;
 
        if (obj == NULL)
                return 0;
 
-#ifdef HAVE_SGEN_GC
-       NOT_IMPLEMENTED;
-#endif
-
-       /* Use a hash table with masked pointers to internalize object references */
-       /* FIXME: This can grow indefinitely */
        mono_loader_lock ();
 
        if (!obj_to_objref)
                obj_to_objref = g_hash_table_new (NULL, NULL);
+       
+       /* FIXME: The tables can grow indefinitely */
 
-       ref = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
-       /* ref might refer to a different object with the same addr which was GCd */
-       if (ref && mono_gchandle_get_target (ref->handle) == obj) {
-               mono_loader_unlock ();
-               return ref;
+       if (mono_gc_is_moving ()) {
+               /*
+                * Objects can move, so use a hash table mapping hash codes to lists of
+                * ObjRef structures.
+                */
+               hash = mono_object_hash (obj);
+
+               reflist = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (hash));
+               for (l = reflist; l; l = l->next) {
+                       ref = l->data;
+                       if (ref && mono_gchandle_get_target (ref->handle) == obj) {
+                               mono_loader_unlock ();
+                               return ref;
+                       }
+               }
+       } else {
+               /* Use a hash table with masked pointers to internalize object references */
+               ref = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
+               /* ref might refer to a different object with the same addr which was GCd */
+               if (ref && mono_gchandle_get_target (ref->handle) == obj) {
+                       mono_loader_unlock ();
+                       return ref;
+               }
        }
 
        ref = g_new0 (ObjRef, 1);
@@ -1300,7 +1445,13 @@ get_objref (MonoObject *obj)
        ref->handle = mono_gchandle_new_weakref (obj, FALSE);
 
        g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref);
-       g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
+
+       if (mono_gc_is_moving ()) {
+               reflist = g_slist_append (reflist, ref);
+               g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (hash), reflist);
+       } else {
+               g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
+       }
 
        mono_loader_unlock ();
 
@@ -1463,6 +1614,10 @@ mono_debugger_agent_free_domain_info (MonoDomain *domain)
                        }
                }
        }
+
+       mono_loader_lock ();
+       g_hash_table_remove (domains, domain);
+       mono_loader_unlock ();
 }
 
 static int
@@ -1739,6 +1894,17 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
        if (!tls)
                return FALSE;
 
+       /*
+        * OSX can (and will) coalesce signals, so sending multiple pthread_kills does not
+        * guarantee the signal handler will be called that many times.  Instead of tracking
+        * interrupt_count on osx, we use this as a boolean flag to determine if a interrupt
+        * has been requested that hasn't been handled yet, otherwise we can have threads
+        * refuse to die when VM_EXIT is called
+        */
+#if defined(__APPLE__)
+       if (InterlockedCompareExchange (&tls->interrupt_count, 0, 1) == 0)
+               return FALSE;
+#else
        /*
         * We use interrupt_count to determine whenever this interrupt should be processed
         * by us or the normal interrupt processing code in the signal handler.
@@ -1749,6 +1915,7 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
                return FALSE;
 
        InterlockedDecrement (&tls->interrupt_count);
+#endif
 
        // FIXME: Races when the thread leaves managed code before hitting a single step
        // event.
@@ -1768,10 +1935,10 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
                        MonoContext ctx;
                        GetLastFrameUserData data;
 
-                       mono_arch_sigctx_to_monoctx (sigctx, &ctx);
                        // FIXME: printf is not signal safe, but this is only used during
                        // debugger debugging
-                       DEBUG (1, printf ("[%p] Received interrupt while at %p, treating as suspended.\n", (gpointer)GetCurrentThreadId (), mono_arch_ip_from_context (sigctx)));
+                       if (sigctx)
+                               DEBUG (1, printf ("[%p] Received interrupt while at %p, treating as suspended.\n", (gpointer)GetCurrentThreadId (), mono_arch_ip_from_context (sigctx)));
                        //save_thread_context (&ctx);
 
                        if (!tls->thread)
@@ -1789,7 +1956,10 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
                         * remain valid.
                         */
                        data.last_frame_set = FALSE;
-                       mono_jit_walk_stack_from_ctx_in_thread (get_last_frame, mono_domain_get (), &ctx, FALSE, tls->thread, mono_get_lmf (), &data);
+                       if (sigctx) {
+                               mono_arch_sigctx_to_monoctx (sigctx, &ctx);
+                               mono_jit_walk_stack_from_ctx_in_thread (get_last_frame, mono_domain_get (), &ctx, FALSE, tls->thread, mono_get_lmf (), &data);
+                       }
                        if (data.last_frame_set) {
                                memcpy (&tls->async_last_frame, &data.last_frame, sizeof (StackFrameInfo));
                                memcpy (&tls->async_ctx, &data.ctx, sizeof (MonoContext));
@@ -1801,6 +1971,8 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
                                tls->has_async_ctx = FALSE;
                        }
 
+                       mono_memory_barrier ();
+
                        tls->suspended = TRUE;
                        MONO_SEM_POST (&suspend_sem);
                }
@@ -1816,6 +1988,20 @@ static void CALLBACK notify_thread_apc (ULONG_PTR param)
 }
 #endif /* HOST_WIN32 */
 
+/*
+ * reset_native_thread_suspend_state:
+ * 
+ *   Reset the suspended flag on native threads
+ */
+static void
+reset_native_thread_suspend_state (gpointer key, gpointer value, gpointer user_data)
+{
+       DebuggerTlsData *tls = value;
+
+       if (!tls->really_suspended && tls->suspended)
+               tls->suspended = FALSE;
+}
+
 /*
  * notify_thread:
  *
@@ -1828,20 +2014,35 @@ notify_thread (gpointer key, gpointer value, gpointer user_data)
        DebuggerTlsData *tls = value;
        gsize tid = thread->tid;
 
-       if (GetCurrentThreadId () != tid) {
-               DEBUG(1, fprintf (log_file, "[%p] Interrupting %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid));
-               /*
-                * Maybe we could use the normal interrupt infrastructure, but that does a lot
-                * of things like breaking waits etc. which we don't want.
-                */
-               InterlockedIncrement (&tls->interrupt_count);
-               /* This is _not_ equivalent to ves_icall_System_Threading_Thread_Abort () */
+       if (GetCurrentThreadId () == tid || tls->terminated)
+               return;
+
+       DEBUG(1, fprintf (log_file, "[%p] Interrupting %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid));
+
+       /*
+        * OSX can (and will) coalesce signals, so sending multiple pthread_kills does not
+        * guarantee the signal handler will be called that many times.  Instead of tracking
+        * interrupt_count on osx, we use this as a boolean flag to determine if a interrupt
+        * has been requested that hasn't been handled yet, otherwise we can have threads
+        * refuse to die when VM_EXIT is called
+        */
+#if defined(__APPLE__)
+       if (InterlockedCompareExchange (&tls->interrupt_count, 1, 0) == 1)
+               return;
+#else
+       /*
+        * Maybe we could use the normal interrupt infrastructure, but that does a lot
+        * of things like breaking waits etc. which we don't want.
+        */
+       InterlockedIncrement (&tls->interrupt_count);
+#endif
+
+       /* This is _not_ equivalent to ves_icall_System_Threading_Thread_Abort () */
 #ifdef HOST_WIN32
-               QueueUserAPC (notify_thread_apc, thread->handle, NULL);
+       QueueUserAPC (notify_thread_apc, thread->handle, NULL);
 #else
-               pthread_kill ((pthread_t) tid, mono_thread_get_abort_signal ());
+       mono_thread_kill (thread, mono_thread_get_abort_signal ());
 #endif
-       }
 }
 
 static void
@@ -1869,7 +2070,7 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx)
                return;
        }
 
-       ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+       ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
 
        /* Can't suspend in these methods */
        if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy")))
@@ -1933,6 +2134,7 @@ resume_vm (void)
        if (suspend_count == 0) {
                // FIXME: Is it safe to call this inside the lock ?
                stop_single_stepping ();
+               mono_g_hash_table_foreach (thread_to_tls, reset_native_thread_suspend_state, NULL);
        }
 
        /* Signal this even when suspend_count > 0, since some threads might have resume_count > 0 */
@@ -1969,7 +2171,7 @@ resume_thread (MonoInternalThread *thread)
 
        DEBUG(1, fprintf (log_file, "[%p] Resuming thread...\n", (gpointer)(gssize)thread->tid));
 
-       tls->resume_count ++;
+       tls->resume_count += suspend_count;
 
        /* 
         * Signal suspend_count without decreasing suspend_count, the threads will wake up
@@ -2066,10 +2268,10 @@ suspend_current (void)
 
        DEBUG(1, fprintf (log_file, "[%p] Resumed.\n", (gpointer)GetCurrentThreadId ()));
 
-       if (tls->invoke) {
+       if (tls->pending_invoke) {
                /* Save the original context */
-               tls->invoke->has_ctx = TRUE;
-               memcpy (&tls->invoke->ctx, &tls->ctx, sizeof (MonoContext));
+               tls->pending_invoke->has_ctx = TRUE;
+               memcpy (&tls->pending_invoke->ctx, &tls->ctx, sizeof (MonoContext));
 
                invoke_method ();
        }
@@ -2144,6 +2346,60 @@ is_suspended (void)
        return count_threads_to_wait_for () == 0;
 }
 
+/*
+ * find_seq_point_for_native_offset:
+ *
+ *   Find the sequence point corresponding to the native offset NATIVE_OFFSET, which
+ * should be the location of a sequence point.
+ */
+static SeqPoint*
+find_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info)
+{
+       MonoSeqPointInfo *seq_points;
+       int i;
+
+       mono_domain_lock (domain);
+       seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
+       mono_domain_unlock (domain);
+       g_assert (seq_points);
+
+       *info = seq_points;
+
+       for (i = 0; i < seq_points->len; ++i) {
+               if (seq_points->seq_points [i].native_offset == native_offset)
+                       return &seq_points->seq_points [i];
+       }
+
+       return NULL;
+}
+
+/*
+ * find_seq_point:
+ *
+ *   Find the sequence point corresponding to the IL offset IL_OFFSET, which
+ * should be the location of a sequence point.
+ */
+static SeqPoint*
+find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info)
+{
+       MonoSeqPointInfo *seq_points;
+       int i;
+
+       mono_domain_lock (domain);
+       seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
+       mono_domain_unlock (domain);
+       g_assert (seq_points);
+
+       *info = seq_points;
+
+       for (i = 0; i < seq_points->len; ++i) {
+               if (seq_points->seq_points [i].il_offset == il_offset)
+                       return &seq_points->seq_points [i];
+       }
+
+       return NULL;
+}
+
 /*
  * compute_il_offset:
  *
@@ -2155,7 +2411,7 @@ is_suspended (void)
 static gint32
 compute_il_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset)
 {
-       GPtrArray *seq_points;
+       MonoSeqPointInfo *seq_points;
        int i, last_il_offset, seq_il_offset, seq_native_offset;
 
        mono_domain_lock (domain);
@@ -2166,9 +2422,9 @@ compute_il_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset)
        last_il_offset = -1;
 
        /* Find the sequence point */
-       for (i = 0; i < seq_points->len; i += 2) {
-               seq_il_offset = GPOINTER_TO_UINT (g_ptr_array_index (seq_points, i));
-               seq_native_offset = GPOINTER_TO_UINT (g_ptr_array_index (seq_points, i + 1));
+       for (i = 0; i < seq_points->len; ++i) {
+               seq_il_offset = seq_points->seq_points [i].il_offset;
+               seq_native_offset = seq_points->seq_points [i].native_offset;
 
                if (seq_native_offset > native_offset)
                        break;
@@ -2194,7 +2450,7 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
                if (info->type == FRAME_TYPE_DEBUGGER_INVOKE) {
                        /* Mark the last frame as an invoke frame */
                        if (ud->frames)
-                               ((StackFrame*)ud->frames->data)->flags |= FRAME_FLAG_DEBUGGER_INVOKE;
+                               ((StackFrame*)g_slist_last (ud->frames)->data)->flags |= FRAME_FLAG_DEBUGGER_INVOKE;
                }
                return FALSE;
        }
@@ -2212,7 +2468,7 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
                info->il_offset = mono_debug_il_offset_from_address (method, info->domain, info->native_offset);
        }
 
-       DEBUG (1, fprintf (stderr, "\tFrame: %s %d %d %d\n", mono_method_full_name (method, TRUE), info->native_offset, info->il_offset, info->managed));
+       DEBUG (1, fprintf (log_file, "\tFrame: %s %d %d %d\n", mono_method_full_name (method, TRUE), info->native_offset, info->il_offset, info->managed));
 
        if (!info->managed && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD) {
                /*
@@ -2316,7 +2572,7 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls)
  * LOCKING: Assumes the loader lock is held.
  */
 static GSList*
-create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, MonoException *exc, int *suspend_policy)
+create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo *ei, int *suspend_policy)
 {
        int i, j;
        GSList *events = NULL;
@@ -2350,8 +2606,12 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, MonoExcept
                                } else if (mod->kind == MOD_KIND_THREAD_ONLY) {
                                        if (mod->data.thread != mono_thread_internal_current ())
                                                filtered = TRUE;
-                               } else if (mod->kind == MOD_KIND_EXCEPTION_ONLY && exc) {
-                                       if (mod->data.exc_class && !mono_class_is_assignable_from (mod->data.exc_class, exc->object.vtable->klass))
+                               } else if (mod->kind == MOD_KIND_EXCEPTION_ONLY && ei) {
+                                       if (mod->data.exc_class && !mono_class_is_assignable_from (mod->data.exc_class, ei->exc->vtable->klass))
+                                               filtered = TRUE;
+                                       if (ei->caught && !mod->caught)
+                                               filtered = TRUE;
+                                       if (!ei->caught && !mod->uncaught)
                                                filtered = TRUE;
                                } else if (mod->kind == MOD_KIND_ASSEMBLY_ONLY && ji) {
                                        int k;
@@ -2492,9 +2752,11 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
                        break;
                case EVENT_KIND_VM_DEATH:
                        break;
-               case EVENT_KIND_EXCEPTION:
-                       buffer_add_objid (&buf, (MonoObject*)arg);
+               case EVENT_KIND_EXCEPTION: {
+                       EventInfo *ei = arg;
+                       buffer_add_objid (&buf, ei->exc);
                        break;
+               }
                default:
                        g_assert_not_reached ();
                }
@@ -2578,7 +2840,7 @@ runtime_shutdown (MonoProfiler *prof)
 }
 
 static void
-thread_startup (MonoProfiler *prof, gsize tid)
+thread_startup (MonoProfiler *prof, uintptr_t tid)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
        MonoInternalThread *old_thread;
@@ -2619,7 +2881,7 @@ thread_startup (MonoProfiler *prof, gsize tid)
        // FIXME: Free this somewhere
        tls = g_new0 (DebuggerTlsData, 1);
        tls->resume_event = CreateEvent (NULL, FALSE, FALSE, NULL);
-       MONO_GC_REGISTER_ROOT (tls->thread);
+       MONO_GC_REGISTER_ROOT_SINGLE (tls->thread);
        tls->thread = thread;
        TlsSetValue (debugger_tls_id, tls);
 
@@ -2640,7 +2902,7 @@ thread_startup (MonoProfiler *prof, gsize tid)
 }
 
 static void
-thread_end (MonoProfiler *prof, gsize tid)
+thread_end (MonoProfiler *prof, uintptr_t tid)
 {
        MonoInternalThread *thread;
        DebuggerTlsData *tls = NULL;
@@ -2668,15 +2930,34 @@ thread_end (MonoProfiler *prof, gsize tid)
 static void
 appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result)
 {
+       mono_loader_lock ();
+       g_hash_table_insert (domains, domain, domain);
+       mono_loader_unlock ();
+
        process_profiler_event (EVENT_KIND_APPDOMAIN_CREATE, domain);
 }
 
 static void
 appdomain_unload (MonoProfiler *prof, MonoDomain *domain)
 {
+       /* Invalidate each thread's frame stack */
+       mono_g_hash_table_foreach (thread_to_tls, invalidate_each_thread, NULL);
+       clear_breakpoints_for_domain (domain);
        process_profiler_event (EVENT_KIND_APPDOMAIN_UNLOAD, domain);
 }
 
+/*
+ * invalidate_each_thread:
+ *
+ *   A GHFunc to invalidate frames.
+ *   value must be a DebuggerTlsData*
+ */
+static void
+invalidate_each_thread (gpointer key, gpointer value, gpointer user_data)
+{
+       invalidate_frames (value);
+}
+
 static void
 assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result)
 {
@@ -2690,11 +2971,30 @@ static void
 assembly_unload (MonoProfiler *prof, MonoAssembly *assembly)
 {
        process_profiler_event (EVENT_KIND_ASSEMBLY_UNLOAD, assembly);
+
+       clear_event_requests_for_assembly (assembly);
+       clear_types_for_assembly (assembly);
 }
 
 static void
 start_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
 {
+#if defined(HOST_WIN32) && !defined(__GNUC__)
+       gpointer stackptr = ((guint64)_AddressOfReturnAddress () - sizeof (void*));
+#else
+       gpointer stackptr = __builtin_frame_address (1);
+#endif
+       MonoInternalThread *thread = mono_thread_internal_current ();
+       DebuggerTlsData *tls;
+
+       mono_loader_lock ();
+       
+       tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+       /* Could be the debugger thread with assembly/type load hooks */
+       if (tls)
+               tls->invoke_addr = stackptr;
+
+       mono_loader_unlock ();
 }
 
 static void
@@ -2704,10 +3004,10 @@ end_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
 #if defined(HOST_WIN32) && !defined(__GNUC__)
        gpointer stackptr = ((guint64)_AddressOfReturnAddress () - sizeof (void*));
 #else
-       gpointer stackptr = __builtin_frame_address (0);
+       gpointer stackptr = __builtin_frame_address (1);
 #endif
 
-       if (ss_req == NULL || ss_req->start_sp > stackptr || ss_req->thread != mono_thread_internal_current ())
+       if (!embedding || ss_req == NULL || stackptr != ss_invoke_addr || ss_req->thread != mono_thread_internal_current ())
                return;
 
        /*
@@ -2715,18 +3015,34 @@ end_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
         * a step out, it may return to native code, and thus never end.
         */
        mono_loader_lock ();
+       ss_invoke_addr = NULL;
+
        for (i = 0; i < event_requests->len; ++i) {
                EventRequest *req = g_ptr_array_index (event_requests, i);
 
                if (req->event_kind == EVENT_KIND_STEP) {
-                       ss_stop (req);
-                        g_ptr_array_remove_index_fast (event_requests, i);
-                        g_free (req);
+                       ss_destroy (req->info);
+                       g_ptr_array_remove_index_fast (event_requests, i);
+                       g_free (req);
                        break;
                }
        }
        mono_loader_unlock ();
+}
+
+static void
+send_type_load (MonoClass *klass)
+{
+       gboolean type_load = FALSE;
 
+       mono_loader_lock ();
+       if (!g_hash_table_lookup (loaded_classes, klass)) {
+               type_load = TRUE;
+               g_hash_table_insert (loaded_classes, klass, klass);
+       }
+       mono_loader_unlock ();
+       if (type_load)
+               process_profiler_event (EVENT_KIND_TYPE_LOAD, klass);
 }
 
 static void
@@ -2738,8 +3054,6 @@ jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result)
         * loader lock held. They could also occur in the debugger thread.
         * Same for assembly load events.
         */
-       gboolean type_load = FALSE;
-
        while (TRUE) {
                MonoAssembly *assembly = NULL;
 
@@ -2757,14 +3071,30 @@ jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result)
                        break;
        }
 
-       mono_loader_lock ();
-       if (!g_hash_table_lookup (loaded_classes, method->klass)) {
-               type_load = TRUE;
-               g_hash_table_insert (loaded_classes, method->klass, method->klass);
+       if (!vm_start_event_sent) {
+               /* Save these so they can be sent after the vm start event */
+               mono_loader_lock ();
+               g_ptr_array_add (pending_type_loads, method->klass);
+               mono_loader_unlock ();
+       } else {
+               /* Send all pending type load events */
+               MonoClass *klass;
+               while (TRUE) {
+                       klass = NULL;
+                       mono_loader_lock ();
+                       if (pending_type_loads->len > 0) {
+                               klass = g_ptr_array_index (pending_type_loads, 0);
+                               g_ptr_array_remove_index (pending_type_loads, 0);
+                       }
+                       mono_loader_unlock ();
+                       if (klass)
+                               send_type_load (klass);
+                       else
+                               break;
+               }
+
+               send_type_load (method->klass);
        }
-       mono_loader_unlock ();
-       if (type_load)
-               process_profiler_event (EVENT_KIND_TYPE_LOAD, method->klass);
 
        if (!result)
                add_pending_breakpoints (method, jinfo);
@@ -2780,8 +3110,8 @@ jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result)
 typedef struct {
        long il_offset, native_offset;
        guint8 *ip;
-       gboolean pending, entry;
        MonoJitInfo *ji;
+       MonoDomain *domain;
 } BreakpointInstance;
 
 /*
@@ -2796,7 +3126,6 @@ typedef struct {
         */
        MonoMethod *method;
        long il_offset;
-       gboolean pending, entry;
        EventRequest *req;
        /* 
         * A list of BreakpointInstance structures describing where the breakpoint
@@ -2818,22 +3147,6 @@ breakpoints_init (void)
        bp_locs = g_hash_table_new (NULL, NULL);
 }      
 
-static void
-breakpoints_cleanup (void)
-{
-       int i;
-
-       mono_loader_lock ();
-
-       for (i = 0; i < breakpoints->len; ++i)
-               g_free (g_ptr_array_index (breakpoints, i));
-
-       g_ptr_array_free (breakpoints, TRUE);
-       g_hash_table_destroy (bp_locs);
-
-       mono_loader_unlock ();
-}
-
 /*
  * insert_breakpoint:
  *
@@ -2841,29 +3154,31 @@ breakpoints_cleanup (void)
  * JI.
  */
 static void
-insert_breakpoint (GPtrArray *seq_points, MonoJitInfo *ji, MonoBreakpoint *bp)
+insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo *ji, MonoBreakpoint *bp)
 {
        int i, count;
-       gint32 il_offset, native_offset;
+       gint32 il_offset = -1, native_offset;
        BreakpointInstance *inst;
 
        native_offset = 0;
-       for (i = 0; i < seq_points->len; i += 2) {
-               il_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i));
-               native_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i + 1));
+       for (i = 0; i < seq_points->len; ++i) {
+               il_offset = seq_points->seq_points [i].il_offset;
+               native_offset = seq_points->seq_points [i].native_offset;
 
                if (il_offset == bp->il_offset)
                        break;
        }
 
-       if (i == seq_points->len)
+       if (i == seq_points->len) {
                /* Have to handle this somehow */
-               NOT_IMPLEMENTED;
+               g_error ("Unable to insert breakpoint at %s:%d, seq_points=%d\n", mono_method_full_name (ji->method, TRUE), bp->il_offset, seq_points->len);
+       }
 
        inst = g_new0 (BreakpointInstance, 1);
        inst->native_offset = native_offset;
        inst->ip = (guint8*)ji->code_start + native_offset;
        inst->ji = ji;
+       inst->domain = domain;
 
        mono_loader_lock ();
 
@@ -2880,6 +3195,8 @@ insert_breakpoint (GPtrArray *seq_points, MonoJitInfo *ji, MonoBreakpoint *bp)
                NOT_IMPLEMENTED;
 #endif
        }
+
+       DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x.\n", mono_method_full_name (ji->method, TRUE), (int)il_offset));      
 }
 
 static void
@@ -2905,6 +3222,12 @@ remove_breakpoint (BreakpointInstance *inst)
 #endif
 }      
 
+static inline gboolean
+bp_matches_method (MonoBreakpoint *bp, MonoMethod *method)
+{
+       return (!bp->method || method == bp->method || (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method));
+}
+
 /*
  * add_pending_breakpoints:
  *
@@ -2913,8 +3236,8 @@ remove_breakpoint (BreakpointInstance *inst)
 static void
 add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
 {
-       int i;
-       GPtrArray *seq_points;
+       int i, j;
+       MonoSeqPointInfo *seq_points;
        MonoDomain *domain;
 
        if (!breakpoints)
@@ -2926,8 +3249,19 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
 
        for (i = 0; i < breakpoints->len; ++i) {
                MonoBreakpoint *bp = g_ptr_array_index (breakpoints, i);
+               gboolean found = FALSE;
+
+               if (!bp_matches_method (bp, method))
+                       continue;
+
+               for (j = 0; j < bp->children->len; ++j) {
+                       BreakpointInstance *inst = g_ptr_array_index (bp->children, j);
+
+                       if (inst->ji == ji)
+                               found = TRUE;
+               }
 
-               if (bp->pending && (bp->method == method || !bp->method || (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method))) {
+               if (!found) {
                        mono_domain_lock (domain);
                        seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, ji->method);
                        mono_domain_unlock (domain);
@@ -2936,7 +3270,7 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
                                continue;
                        g_assert (seq_points);
 
-                       insert_breakpoint (seq_points, ji, bp);
+                       insert_breakpoint (seq_points, domain, ji, bp);
                }
        }
 
@@ -2944,7 +3278,7 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
 }
 
 static void
-set_bp_in_method (MonoDomain *domain, MonoMethod *method, GPtrArray *seq_points, MonoBreakpoint *bp)
+set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_points, MonoBreakpoint *bp)
 {
        gpointer code;
        MonoJitInfo *ji;
@@ -2959,26 +3293,41 @@ set_bp_in_method (MonoDomain *domain, MonoMethod *method, GPtrArray *seq_points,
        }
        g_assert (code);
 
-       insert_breakpoint (seq_points, ji, bp);
+       insert_breakpoint (seq_points, domain, ji, bp);
 }
 
+typedef struct
+{
+       MonoBreakpoint *bp;
+       MonoDomain *domain;
+} SetBpUserData;
+
 static void
 set_bp_in_method_cb (gpointer key, gpointer value, gpointer user_data)
 {
        MonoMethod *method = key;
-       GPtrArray *seq_points = value;
-       MonoBreakpoint *bp = user_data;
-       MonoDomain *domain = mono_domain_get ();
+       MonoSeqPointInfo *seq_points = value;
+       SetBpUserData *ud = user_data;
+       MonoBreakpoint *bp = ud->bp;
+       MonoDomain *domain = ud->domain;
 
-       if (bp->method) {
-               if (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method) {
-                       /* Generic instance */
-                       set_bp_in_method (domain, method, seq_points, bp);
-               }
-       } else {
-               /* Method entry/exit */
+       if (bp_matches_method (bp, method))
                set_bp_in_method (domain, method, seq_points, bp);
-       }
+}
+
+static void
+set_bp_in_domain (gpointer key, gpointer value, gpointer user_data)
+{
+       MonoDomain *domain = key;
+       MonoBreakpoint *bp = user_data;
+       SetBpUserData ud;
+
+       ud.bp = bp;
+       ud.domain = domain;
+
+       mono_domain_lock (domain);
+       g_hash_table_foreach (domain_jit_info (domain)->seq_points, set_bp_in_method_cb, &ud);
+       mono_domain_unlock (domain);
 }
 
 /*
@@ -2992,13 +3341,10 @@ set_bp_in_method_cb (gpointer key, gpointer value, gpointer user_data)
 static MonoBreakpoint*
 set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req)
 {
-       GPtrArray *seq_points;
-       MonoDomain *domain;
        MonoBreakpoint *bp;
 
-       // FIXME: 
+       // FIXME:
        // - suspend/resume the vm to prevent code patching problems
-       // - appdomains
        // - multiple breakpoints on the same location
        // - dynamic methods
        // - races
@@ -3009,27 +3355,13 @@ set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req)
        bp->req = req;
        bp->children = g_ptr_array_new ();
 
-       DEBUG(1, fprintf (log_file, "[dbg] Setting breakpoint at %s:0x%x.\n", method ? mono_method_full_name (method, TRUE) : "<all>", (int)il_offset));
+       DEBUG(1, fprintf (log_file, "[dbg] Setting %sbreakpoint at %s:0x%x.\n", (req->event_kind == EVENT_KIND_STEP) ? "single step " : "", method ? mono_method_full_name (method, TRUE) : "<all>", (int)il_offset));
 
-       domain = mono_domain_get ();
-       mono_domain_lock (domain);
-       if (method) {
-               seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
-               if (seq_points) {
-                       set_bp_in_method (domain, method, seq_points, bp);
-               } else {
-                       if (method->is_generic)
-                               /* There might be already JITted instances */
-                               g_hash_table_foreach (domain_jit_info (domain)->seq_points, set_bp_in_method_cb, bp);
+       mono_loader_lock ();
 
-                       /* Not yet JITted */
-                       bp->pending = TRUE;
-               }
-       } else {
-               g_hash_table_foreach (domain_jit_info (domain)->seq_points, set_bp_in_method_cb, bp);
-               bp->pending = TRUE;
-       }
-       mono_domain_unlock (domain);
+       g_hash_table_foreach (domains, set_bp_in_domain, bp);
+
+       mono_loader_unlock ();
 
        mono_loader_lock ();
        g_ptr_array_add (breakpoints, bp);
@@ -3061,22 +3393,95 @@ clear_breakpoint (MonoBreakpoint *bp)
 }
 
 static void
-process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx)
+breakpoints_cleanup (void)
 {
-       MonoJitInfo *ji;
-       guint8 *orig_ip, *ip;
-       int i, j, suspend_policy;
-       guint32 native_offset;
-       MonoBreakpoint *bp;
-       BreakpointInstance *inst;
-       GPtrArray *reqs;
-       GSList *events = NULL;
+       int i;
+
+       mono_loader_lock ();
+       i = 0;
+       while (i < event_requests->len) {
+               EventRequest *req = g_ptr_array_index (event_requests, i);
+
+               if (req->event_kind == EVENT_KIND_BREAKPOINT) {
+                       clear_breakpoint (req->info);
+                       g_ptr_array_remove_index_fast (event_requests, i);
+                       g_free (req);
+               } else {
+                       i ++;
+               }
+       }
+
+       for (i = 0; i < breakpoints->len; ++i)
+               g_free (g_ptr_array_index (breakpoints, i));
+
+       g_ptr_array_free (breakpoints, TRUE);
+       g_hash_table_destroy (bp_locs);
+
+       breakpoints = NULL;
+       bp_locs = NULL;
+
+       mono_loader_unlock ();
+}
+
+/*
+ * clear_breakpoints_for_domain:
+ *
+ *   Clear breakpoint instances which reference DOMAIN.
+ */
+static void
+clear_breakpoints_for_domain (MonoDomain *domain)
+{
+       int i, j;
+
+       /* This could be called after shutdown */
+       if (!breakpoints)
+               return;
+
+       mono_loader_lock ();
+       for (i = 0; i < breakpoints->len; ++i) {
+               MonoBreakpoint *bp = g_ptr_array_index (breakpoints, i);
+
+               j = 0;
+               while (j < bp->children->len) {
+                       BreakpointInstance *inst = g_ptr_array_index (bp->children, j);
+
+                       if (inst->domain == domain) {
+                               remove_breakpoint (inst);
+
+                               g_free (inst);
+
+                               g_ptr_array_remove_index_fast (bp->children, j);
+                       } else {
+                               j ++;
+                       }
+               }
+       }
+       mono_loader_unlock ();
+}
+
+static gboolean
+breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly)
+{
+       return bp->method && bp->method->klass->image->assembly == assembly;
+}
+
+static void
+process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx)
+{
+       MonoJitInfo *ji;
+       guint8 *orig_ip, *ip;
+       int i, j, suspend_policy;
+       guint32 native_offset;
+       MonoBreakpoint *bp;
+       BreakpointInstance *inst;
+       GPtrArray *bp_reqs, *ss_reqs_orig, *ss_reqs;
+       GSList *bp_events = NULL, *ss_events = NULL, *enter_leave_events = NULL;
        EventKind kind = EVENT_KIND_BREAKPOINT;
 
        // FIXME: Speed this up
 
        orig_ip = ip = MONO_CONTEXT_GET_IP (ctx);
-       ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+       ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
        g_assert (ji);
        g_assert (ji->method);
 
@@ -3096,7 +3501,9 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx)
        if (ji->method->wrapper_type || tls->disable_breakpoints)
                return;
 
-       reqs = g_ptr_array_new ();
+       bp_reqs = g_ptr_array_new ();
+       ss_reqs = g_ptr_array_new ();
+       ss_reqs_orig = g_ptr_array_new ();
 
        DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, native_offset));
 
@@ -3111,12 +3518,17 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx)
 
                for (j = 0; j < bp->children->len; ++j) {
                        inst = g_ptr_array_index (bp->children, j);
-                       if (inst->ji == ji && inst->native_offset == native_offset)
-                               g_ptr_array_add (reqs, bp->req);
+                       if (inst->ji == ji && inst->native_offset == native_offset) {
+                               if (bp->req->event_kind == EVENT_KIND_STEP) {
+                                       g_ptr_array_add (ss_reqs_orig, bp->req);
+                               } else {
+                                       g_ptr_array_add (bp_reqs, bp->req);
+                               }
+                       }
                }
        }
-       if (reqs->len == 0) {
-               GPtrArray *seq_points;
+       if (bp_reqs->len == 0 && ss_reqs_orig->len == 0) {
+               MonoSeqPointInfo *seq_points;
                int seq_il_offset, seq_native_offset;
                MonoDomain *domain = mono_domain_get ();
 
@@ -3131,9 +3543,9 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx)
                }
                g_assert (seq_points);
 
-               for (i = 0; i < seq_points->len; i += 2) {
-                       seq_il_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i));
-                       seq_native_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i + 1));
+               for (i = 0; i < seq_points->len; ++i) {
+                       seq_il_offset = seq_points->seq_points [i].il_offset;
+                       seq_native_offset = seq_points->seq_points [i].native_offset;
 
                        if (native_offset == seq_native_offset) {
                                if (seq_il_offset == METHOD_ENTRY_IL_OFFSET)
@@ -3144,18 +3556,68 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx)
                        }
                }
        }
-       
-       if (reqs->len > 0)
-               events = create_event_list (EVENT_KIND_BREAKPOINT, reqs, ji, NULL, &suspend_policy);
-       else if (kind != EVENT_KIND_BREAKPOINT)
-               events = create_event_list (kind, NULL, ji, NULL, &suspend_policy);
 
-       g_ptr_array_free (reqs, TRUE);
+       /* Process single step requests */
+       for (i = 0; i < ss_reqs_orig->len; ++i) {
+               EventRequest *req = g_ptr_array_index (ss_reqs_orig, i);
+               SingleStepReq *ss_req = bp->req->info;
+               gboolean hit = TRUE;
+               MonoSeqPointInfo *info;
+               SeqPoint *sp;
+
+               sp = find_seq_point_for_native_offset (mono_domain_get (), ji->method, native_offset, &info);
+               g_assert (sp);
+
+               if (ss_req->size == STEP_SIZE_LINE) {
+                       /* Have to check whenever a different source line was reached */
+                       MonoDebugMethodInfo *minfo;
+                       MonoDebugSourceLocation *loc = NULL;
+
+                       minfo = mono_debug_lookup_method (ji->method);
+
+                       if (minfo)
+                               loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset);
+
+                       if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line))
+                               /* Have to continue single stepping */
+                               hit = FALSE;
+                               
+                       if (loc) {
+                               ss_req->last_method = ji->method;
+                               ss_req->last_line = loc->row;
+                               mono_debug_free_source_location (loc);
+                       }
+               }
+
+               if (hit)
+                       g_ptr_array_add (ss_reqs, req);
+
+               /* Start single stepping again from the current sequence point */
+               ss_start (ss_req, ji->method, sp, info, ctx, NULL);
+       }
+       
+       if (ss_reqs->len > 0)
+               ss_events = create_event_list (EVENT_KIND_STEP, ss_reqs, ji, NULL, &suspend_policy);
+       if (bp_reqs->len > 0)
+               bp_events = create_event_list (EVENT_KIND_BREAKPOINT, bp_reqs, ji, NULL, &suspend_policy);
+       if (kind != EVENT_KIND_BREAKPOINT)
+               enter_leave_events = create_event_list (kind, NULL, ji, NULL, &suspend_policy);
 
        mono_loader_unlock ();
 
-       if (events)
-               process_event (kind, ji->method, 0, ctx, events, suspend_policy);
+       g_ptr_array_free (bp_reqs, TRUE);
+       g_ptr_array_free (ss_reqs, TRUE);
+
+       /* 
+        * FIXME: The first event will suspend, so the second will only be sent after the
+        * resume.
+        */
+       if (ss_events)
+               process_event (EVENT_KIND_STEP, ji->method, 0, ctx, ss_events, suspend_policy);
+       if (bp_events)
+               process_event (kind, ji->method, 0, ctx, bp_events, suspend_policy);
+       if (enter_leave_events)
+               process_event (kind, ji->method, 0, ctx, enter_leave_events, suspend_policy);
 }
 
 static void
@@ -3196,8 +3658,11 @@ resume_from_signal_handler (void *sigctx, void *func)
        mono_arch_sigctx_to_monoctx (sigctx, &ctx);
        memcpy (&tls->handler_ctx, &ctx, sizeof (MonoContext));
        MONO_CONTEXT_SET_IP (&ctx, func);
-
        mono_arch_monoctx_to_sigctx (&ctx, sigctx);
+
+#ifdef PPC_USES_FUNCTION_DESCRIPTOR
+       mono_ppc_set_func_into_sigctx (sigctx, func);
+#endif
 }
 
 void
@@ -3219,7 +3684,7 @@ process_single_step_inner (DebuggerTlsData *tls, MonoContext *ctx)
        guint8 *ip;
        GPtrArray *reqs;
        int il_offset, suspend_policy;
-       MonoDomain *domain = mono_domain_get ();
+       MonoDomain *domain;
        GSList *events;
 
        // FIXME: Speed this up
@@ -3244,7 +3709,7 @@ process_single_step_inner (DebuggerTlsData *tls, MonoContext *ctx)
        if (log_level > 0) {
                const char *depth = NULL;
 
-               ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+               ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
 
                switch (ss_req->depth) {
                case STEP_DEPTH_OVER:
@@ -3281,7 +3746,7 @@ process_single_step_inner (DebuggerTlsData *tls, MonoContext *ctx)
                ss_req->last_sp = MONO_CONTEXT_GET_SP (ctx);
        }
 
-       ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+       ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
        g_assert (ji);
        g_assert (ji->method);
 
@@ -3427,6 +3892,17 @@ start_single_stepping (void)
 
        if (val == 1)
                mono_arch_start_single_stepping ();
+
+       if (ss_req != NULL && ss_invoke_addr == NULL) {
+               DebuggerTlsData *tls;
+       
+               mono_loader_lock ();
+       
+               tls = mono_g_hash_table_lookup (thread_to_tls, ss_req->thread);
+               ss_invoke_addr = tls->invoke_addr;
+               
+               mono_loader_unlock ();
+       }
 #else
        g_assert_not_reached ();
 #endif
@@ -3445,13 +3921,101 @@ stop_single_stepping (void)
 #endif
 }
 
+/*
+ * ss_stop:
+ *
+ *   Stop the single stepping operation given by SS_REQ.
+ */
+static void
+ss_stop (SingleStepReq *ss_req)
+{
+       gboolean use_bps = FALSE;
+
+       if (ss_req->bps) {
+               GSList *l;
+
+               use_bps = TRUE;
+
+               for (l = ss_req->bps; l; l = l->next) {
+                       clear_breakpoint (l->data);
+               }
+               g_slist_free (ss_req->bps);
+               ss_req->bps = NULL;
+       }
+
+       if (ss_req->global) {
+               stop_single_stepping ();
+               ss_req->global = FALSE;
+       }
+}
+
+/*
+ * ss_start:
+ *
+ *   Start the single stepping operation given by SS_REQ from the sequence point SP.
+ */
+static void
+ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls)
+{
+       gboolean use_bp = FALSE;
+       int i, frame_index;
+       SeqPoint *next_sp;
+       MonoBreakpoint *bp;
+
+       /* Stop the previous operation */
+       ss_stop (ss_req);
+
+       /*
+        * Implement single stepping using breakpoints if possible.
+        */
+       if (ss_req->depth == STEP_DEPTH_OVER) {
+               frame_index = 1;
+               /*
+                * Find the first sequence point in the current or in a previous frame which
+                * is not the last in its method.
+                */
+               while (sp && sp->next_len == 0) {
+                       sp = NULL;
+                       if (tls && frame_index < tls->frame_count) {
+                               StackFrame *frame = tls->frames [frame_index];
+
+                               method = frame->method;
+                               if (frame->il_offset != -1) {
+                                       sp = find_seq_point (frame->domain, frame->method, frame->il_offset, &info);
+                               }
+                               frame_index ++;
+                       }
+               }
+
+               if (sp && sp->next_len > 0) {
+                       use_bp = TRUE;
+                       for (i = 0; i < sp->next_len; ++i) {
+                               next_sp = &info->seq_points [sp->next [i]];
+
+                               bp = set_breakpoint (method, next_sp->il_offset, ss_req->req);
+                               ss_req->bps = g_slist_append (ss_req->bps, bp);
+                       }
+               }
+       }
+
+       if (!ss_req->bps) {
+               ss_req->global = TRUE;
+               start_single_stepping ();
+       } else {
+               ss_req->global = FALSE;
+       }
+}
+
 /*
  * Start single stepping of thread THREAD
  */
 static ErrorCode
-ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req)
+ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req)
 {
        DebuggerTlsData *tls;
+       MonoSeqPointInfo *info;
+       SeqPoint *sp = NULL;
+       MonoMethod *method = NULL;
 
        if (suspend_count == 0)
                return ERR_NOT_SUSPENDED;
@@ -3464,7 +4028,7 @@ ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventReque
                return ERR_NOT_IMPLEMENTED;
        }
 
-       ss_req = g_new0 (MonoSingleStepReq, 1);
+       ss_req = g_new0 (SingleStepReq, 1);
        ss_req->req = req;
        ss_req->thread = thread;
        ss_req->size = size;
@@ -3502,33 +4066,76 @@ ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventReque
                }
        }
 
-       start_single_stepping ();
+       if (ss_req->depth == STEP_DEPTH_OVER) {
+               StackFrame *frame;
+
+               compute_frame_info (thread, tls);
+
+               g_assert (tls->frame_count);
+               frame = tls->frames [0];
+
+               if (frame->il_offset != -1) {
+                       /* FIXME: Sort the table and use a binary search */
+                       sp = find_seq_point (frame->domain, frame->method, frame->il_offset, &info);
+                       g_assert (sp);
+                       method = frame->method;
+               }
+       }
+
+       ss_start (ss_req, method, sp, info, NULL, tls);
 
        return 0;
 }
 
 static void
-ss_stop (EventRequest *req)
+ss_destroy (SingleStepReq *req)
 {
        // FIXME: Locking
-       g_assert (ss_req);
-       g_assert (ss_req->req == req);
+       g_assert (ss_req == req);
+
+       ss_stop (ss_req);
 
        g_free (ss_req);
        ss_req = NULL;
-
-       stop_single_stepping ();
 }
 
 void
-mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *ctx)
+mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, 
+                                     MonoContext *catch_ctx)
 {
        int suspend_policy;
        GSList *events;
        MonoJitInfo *ji;
+       EventInfo ei;
+
+       if (thread_to_tls != NULL) {
+               MonoInternalThread *thread = mono_thread_internal_current ();
+               DebuggerTlsData *tls;
+
+               mono_loader_lock ();
+               tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+               mono_loader_unlock ();
+
+               if (tls && tls->abort_requested)
+                       return;
+       }
+
+       memset (&ei, 0, sizeof (EventInfo));
 
        /* Just-In-Time debugging */
-       if (agent_config.onthrow && !inited) {
+       if (!catch_ctx) {
+               if (agent_config.onuncaught && !inited) {
+                       finish_agent_init (FALSE);
+
+                       /*
+                        * Send an unsolicited EXCEPTION event with a dummy request id.
+                        */
+                       events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff));
+                       ei.exc = (MonoObject*)exc;
+                       process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, SUSPEND_POLICY_ALL);
+                       return;
+               }
+       } else if (agent_config.onthrow && !inited) {
                GSList *l;
                gboolean found = FALSE;
 
@@ -3536,7 +4143,7 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *ctx)
                        char *ex_type = l->data;
                        char *f = mono_type_full_name (&exc->object.vtable->klass->byval_arg);
 
-                       if (!strcmp (ex_type, f))
+                       if (!strcmp (ex_type, "") || !strcmp (ex_type, f))
                                found = TRUE;
 
                        g_free (f);
@@ -3549,7 +4156,8 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *ctx)
                         * Send an unsolicited EXCEPTION event with a dummy request id.
                         */
                        events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff));
-                       process_event (EVENT_KIND_EXCEPTION, exc, 0, ctx, events, SUSPEND_POLICY_ALL);
+                       ei.exc = (MonoObject*)exc;
+                       process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, SUSPEND_POLICY_ALL);
                        return;
                }
        }
@@ -3557,39 +4165,28 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *ctx)
        if (!inited)
                return;
 
-       ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (ctx), NULL);
+       ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (throw_ctx), NULL);
+
+       ei.exc = (MonoObject*)exc;
+       ei.caught = catch_ctx != NULL;
 
        mono_loader_lock ();
-       events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, exc, &suspend_policy);
+       events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, &ei, &suspend_policy);
        mono_loader_unlock ();
 
-       process_event (EVENT_KIND_EXCEPTION, exc, 0, ctx, events, suspend_policy);
-}
-
-void
-mono_debugger_agent_handle_unhandled_exception (MonoException *exc, MonoContext *ctx)
-{
-       GSList *events;
-
-       if (!agent_config.onuncaught)
-               return;
-
-       finish_agent_init (FALSE);
-
-       /*
-        * Send an unsolicited EXCEPTION event with a dummy request id.
-        */
-       events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff));
-       process_event (EVENT_KIND_EXCEPTION, exc, 0, ctx, events, SUSPEND_POLICY_ALL);
+       process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, suspend_policy);
 }
 
 /*
- * buffer_add_value:
+ * buffer_add_value_full:
  *
  *   Add the encoding of the value at ADDR described by T to the buffer.
+ * AS_VTYPE determines whenever to treat primitive types as primitive types or
+ * vtypes.
  */
 static void
-buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
+buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain,
+                                          gboolean as_vtype)
 {
        MonoObject *obj;
 
@@ -3598,6 +4195,30 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
                addr = *(void**)addr;
        }
 
+       if (as_vtype) {
+               switch (t->type) {
+               case MONO_TYPE_BOOLEAN:
+               case MONO_TYPE_I1:
+               case MONO_TYPE_U1:
+               case MONO_TYPE_CHAR:
+               case MONO_TYPE_I2:
+               case MONO_TYPE_U2:
+               case MONO_TYPE_I4:
+               case MONO_TYPE_U4:
+               case MONO_TYPE_R4:
+               case MONO_TYPE_I8:
+               case MONO_TYPE_U8:
+               case MONO_TYPE_R8:
+               case MONO_TYPE_I:
+               case MONO_TYPE_U:
+               case MONO_TYPE_PTR:
+                       goto handle_vtype;
+                       break;
+               default:
+                       break;
+               }
+       }
+
        switch (t->type) {
        case MONO_TYPE_VOID:
                buffer_add_byte (buf, t->type);
@@ -3628,6 +4249,8 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
                break;
        case MONO_TYPE_I:
        case MONO_TYPE_U:
+               /* Treat it as a vtype */
+               goto handle_vtype;
        case MONO_TYPE_PTR: {
                gssize val = *(gssize*)addr;
                
@@ -3688,7 +4311,7 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
                                continue;
                        if (mono_field_is_deleted (f))
                                continue;
-                       buffer_add_value (buf, f->type, (guint8*)addr + f->offset - sizeof (MonoObject), domain);
+                       buffer_add_value_full (buf, f->type, (guint8*)addr + f->offset - sizeof (MonoObject), domain, FALSE);
                }
                break;
        }
@@ -3704,14 +4327,26 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
        }
 }
 
+static void
+buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
+{
+       buffer_add_value_full (buf, t, addr, domain, FALSE);
+}
+
 static ErrorCode
 decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
 {
        int err;
        int type = decode_byte (buf, &buf, limit);
 
-       if (type != t->type && !MONO_TYPE_IS_REFERENCE (t)) {
-               DEBUG(1, fprintf (log_file, "Expected value of type %d, got %d.\n", t->type, type));
+       if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) &&
+               !(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) &&
+               !(t->type == MONO_TYPE_U && type == MONO_TYPE_VALUETYPE) &&
+               !(t->type == MONO_TYPE_PTR && type == MONO_TYPE_I8) &&
+               !(t->type == MONO_TYPE_GENERICINST && type == MONO_TYPE_VALUETYPE)) {
+               char *name = mono_type_full_name (t);
+               DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got 0x%0x.\n", (gpointer)GetCurrentThreadId (), name, type));
+               g_free (name);
                return ERR_INVALID_ARGUMENT;
        }
 
@@ -3752,6 +4387,25 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8
        case MONO_TYPE_R8:
                *(guint64*)addr = decode_long (buf, &buf, limit);
                break;
+       case MONO_TYPE_PTR:
+               /* We send these as I8, so we get them back as such */
+               g_assert (type == MONO_TYPE_I8);
+               *(gssize*)addr = decode_long (buf, &buf, limit);
+               break;
+       case MONO_TYPE_GENERICINST:
+               if (MONO_TYPE_ISSTRUCT (t)) {
+                       /* The client sends these as a valuetype */
+                       goto handle_vtype;
+               } else {
+                       goto handle_ref;
+               }
+               break;
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+               /* We send these as vtypes, so we get them back as such */
+               g_assert (type == MONO_TYPE_VALUETYPE);
+               /* Fall through */
+               handle_vtype:
        case MONO_TYPE_VALUETYPE: {
                gboolean is_enum = decode_byte (buf, &buf, limit);
                MonoClass *klass;
@@ -3784,6 +4438,7 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8
                g_assert (nfields == 0);
                break;
        }
+       handle_ref:
        default:
                if (MONO_TYPE_IS_REFERENCE (t)) {
                        if (type == MONO_TYPE_OBJECT) {
@@ -3818,7 +4473,7 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8
 }
 
 static void
-add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain)
+add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype)
 {
        guint32 flags;
        int reg;
@@ -3832,7 +4487,7 @@ add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, Mono
        case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
                reg_val = mono_arch_context_get_int_reg (ctx, reg);
 
-               buffer_add_value (buf, t, &reg_val, domain);
+               buffer_add_value_full (buf, t, &reg_val, domain, as_vtype);
                break;
        case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
                addr = mono_arch_context_get_int_reg (ctx, reg);
@@ -3840,7 +4495,7 @@ add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, Mono
 
                //printf ("[R%d+%d] = %p\n", reg, var->offset, addr);
 
-               buffer_add_value (buf, t, addr, domain);
+               buffer_add_value_full (buf, t, addr, domain, as_vtype);
                break;
        case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
                NOT_IMPLEMENTED;
@@ -3900,7 +4555,7 @@ clear_event_request (int req_id, int etype)
                        if (req->event_kind == EVENT_KIND_BREAKPOINT)
                                clear_breakpoint (req->info);
                        if (req->event_kind == EVENT_KIND_STEP)
-                               ss_stop (req);
+                               ss_destroy (req->info);
                        if (req->event_kind == EVENT_KIND_METHOD_ENTRY)
                                clear_breakpoint (req->info);
                        if (req->event_kind == EVENT_KIND_METHOD_EXIT)
@@ -3913,6 +4568,82 @@ clear_event_request (int req_id, int etype)
        mono_loader_unlock ();
 }
 
+static gboolean
+event_req_matches_assembly (EventRequest *req, MonoAssembly *assembly)
+{
+       if (req->event_kind == EVENT_KIND_BREAKPOINT)
+               return breakpoint_matches_assembly (req->info, assembly);
+       else {
+               int i, j;
+
+               for (i = 0; i < req->nmodifiers; ++i) {
+                       Modifier *m = &req->modifiers [i];
+
+                       if (m->kind == MOD_KIND_EXCEPTION_ONLY && m->data.exc_class && m->data.exc_class->image->assembly == assembly)
+                               return TRUE;
+                       if (m->kind == MOD_KIND_ASSEMBLY_ONLY && m->data.assemblies) {
+                               for (j = 0; m->data.assemblies [j]; ++j)
+                                       if (m->data.assemblies [j] == assembly)
+                                               return TRUE;
+                       }
+               }
+       }
+
+       return FALSE;
+}
+
+/*
+ * clear_event_requests_for_assembly:
+ *
+ *   Clear all events requests which reference ASSEMBLY.
+ */
+static void
+clear_event_requests_for_assembly (MonoAssembly *assembly)
+{
+       int i;
+       gboolean found;
+
+       mono_loader_lock ();
+       found = TRUE;
+       while (found) {
+               found = FALSE;
+               for (i = 0; i < event_requests->len; ++i) {
+                       EventRequest *req = g_ptr_array_index (event_requests, i);
+
+                       if (event_req_matches_assembly (req, assembly)) {
+                               clear_event_request (req->id, req->event_kind);
+                               found = TRUE;
+                               break;
+                       }
+               }
+       }
+       mono_loader_unlock ();
+}
+
+/*
+ * type_comes_from_assembly:
+ *
+ *   GHRFunc that returns TRUE if klass comes from assembly
+ */
+static gboolean
+type_comes_from_assembly (gpointer klass, gpointer also_klass, gpointer assembly)
+{
+       return (mono_class_get_image ((MonoClass*)klass) == mono_assembly_get_image ((MonoAssembly*)assembly));
+}
+
+/*
+ * clear_types_for_assembly:
+ *
+ *   Clears types from loaded_classes for a given assembly
+ */
+static void
+clear_types_for_assembly (MonoAssembly *assembly)
+{
+       mono_loader_lock ();
+       g_hash_table_foreach_remove (loaded_classes, type_comes_from_assembly, assembly);
+       mono_loader_unlock ();
+}
+
 static void
 add_thread (gpointer key, gpointer value, gpointer user_data)
 {
@@ -3935,7 +4666,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
        MonoObject *this, *res, *exc;
        MonoDomain *domain;
        guint8 *this_buf;
-#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
        MonoLMFExt ext;
 #endif
 
@@ -3958,9 +4689,17 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
                this_buf = g_alloca (mono_class_instance_size (m->klass));
        else
                this_buf = g_alloca (sizeof (MonoObject*));
-       err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end);
-       if (err)
-               return err;
+       if (m->klass->valuetype && (m->flags & METHOD_ATTRIBUTE_STATIC)) {
+               /* Should be null */
+               int type = decode_byte (p, &p, end);
+               if (type != VALUE_TYPE_ID_NULL)
+                       return ERR_INVALID_ARGUMENT;
+               memset (this_buf, 0, mono_class_instance_size (m->klass));
+       } else {
+               err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end);
+               if (err)
+                       return err;
+       }
 
        if (!m->klass->valuetype)
                this = *(MonoObject**)this_buf;
@@ -4022,7 +4761,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
         * Add an LMF frame to link the stack frames on the invoke method with our caller.
         */
        /* FIXME: Move this to arch specific code */
-#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
        if (invoke->has_ctx) {
                MonoLMF **lmf_addr;
 
@@ -4045,6 +4784,11 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
                /* Mark that this is a MonoLMFExt */
                ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
                ext.lmf.ebp = (gssize)&ext;
+#elif defined(TARGET_POWERPC)
+               ext.lmf.previous_lmf = *(lmf_addr);
+               /* Mark that this is a MonoLMFExt */
+               ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
+               ext.lmf.ebp = (gssize)&ext;
 #else
                g_assert_not_reached ();
 #endif
@@ -4083,7 +4827,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
 
        tls->disable_breakpoints = FALSE;
 
-#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
        if (invoke->has_ctx)
                mono_set_lmf ((gpointer)(((gssize)ext.lmf.previous_lmf) & ~3));
 #endif
@@ -4096,7 +4840,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
 /*
  * invoke_method:
  *
- *   Invoke the method given by tls->invoke in the current thread.
+ *   Invoke the method given by tls->pending_invoke in the current thread.
  */
 static void
 invoke_method (void)
@@ -4115,9 +4859,21 @@ invoke_method (void)
        tls = TlsGetValue (debugger_tls_id);
        g_assert (tls);
 
-       invoke = tls->invoke;
+       /*
+        * Store the `InvokeData *' in `tls->invoke' until we're done with
+        * the invocation, so CMD_VM_ABORT_INVOKE can check it.
+        */
+
+       mono_loader_lock ();
+
+       invoke = tls->pending_invoke;
        g_assert (invoke);
-       tls->invoke = NULL;
+       tls->pending_invoke = NULL;
+
+       invoke->last_invoke = tls->invoke;
+       tls->invoke = invoke;
+
+       mono_loader_unlock ();
 
        tls->frames_up_to_date = FALSE;
 
@@ -4142,11 +4898,29 @@ invoke_method (void)
 
        if (invoke->flags & INVOKE_FLAG_SINGLE_THREADED) {
                g_assert (tls->resume_count);
-               tls->resume_count --;
+               tls->resume_count -= invoke->suspend_count;
        }
 
        DEBUG (1, printf ("[%p] Invoke finished, resume_count = %d.\n", (gpointer)GetCurrentThreadId (), tls->resume_count));
 
+       /*
+        * Take the loader lock to avoid race conditions with CMD_VM_ABORT_INVOKE:
+        *
+        * It is possible that ves_icall_System_Threading_Thread_Abort () was called
+        * after the mono_runtime_invoke() already returned, but it doesn't matter
+        * because we reset the abort here.
+        */
+
+       mono_loader_lock ();
+
+       if (tls->abort_requested)
+               mono_thread_internal_reset_abort (tls->thread);
+
+       tls->invoke = tls->invoke->last_invoke;
+       tls->abort_requested = FALSE;
+
+       mono_loader_unlock ();
+
        g_free (invoke->p);
        g_free (invoke);
 
@@ -4186,6 +4960,13 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                g_free (version);
                break;
        }
+       case CMD_VM_SET_PROTOCOL_VERSION: {
+               major_version = decode_int (p, &p, end);
+               minor_version = decode_int (p, &p, end);
+               protocol_version_set = TRUE;
+               DEBUG(1, fprintf (log_file, "[dbg] Protocol version %d.%d, client protocol version %d.%d.\n", MAJOR_VERSION, MINOR_VERSION, major_version, minor_version));
+               break;
+       }
        case CMD_VM_ALL_THREADS: {
                // FIXME: Domains
                mono_loader_lock ();
@@ -4269,9 +5050,9 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                        args [0] = g_malloc (sizeof (int));
                        *(int*)(args [0]) = exit_code;
 
-                       tls->invoke = g_new0 (InvokeData, 1);
-                       tls->invoke->method = exit_method;
-                       tls->invoke->args = args;
+                       tls->pending_invoke = g_new0 (InvokeData, 1);
+                       tls->pending_invoke->method = exit_method;
+                       tls->pending_invoke->args = args;
 
                        while (suspend_count > 0)
                                resume_vm ();
@@ -4334,14 +5115,15 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                 * Store the invoke data into tls, the thread will execute it after it is
                 * resumed.
                 */
-               if (tls->invoke)
+               if (tls->pending_invoke)
                        NOT_IMPLEMENTED;
-               tls->invoke = g_new0 (InvokeData, 1);
-               tls->invoke->id = id;
-               tls->invoke->flags = flags;
-               tls->invoke->p = g_malloc (end - p);
-               memcpy (tls->invoke->p, p, end - p);
-               tls->invoke->endp = tls->invoke->p + (end - p);
+               tls->pending_invoke = g_new0 (InvokeData, 1);
+               tls->pending_invoke->id = id;
+               tls->pending_invoke->flags = flags;
+               tls->pending_invoke->p = g_malloc (end - p);
+               memcpy (tls->pending_invoke->p, p, end - p);
+               tls->pending_invoke->endp = tls->pending_invoke->p + (end - p);
+               tls->pending_invoke->suspend_count = suspend_count;
 
                if (flags & INVOKE_FLAG_SINGLE_THREADED)
                        resume_thread (THREAD_TO_INTERNAL (thread));
@@ -4349,6 +5131,49 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                        resume_vm ();
                break;
        }
+       case CMD_VM_ABORT_INVOKE: {
+               int objid = decode_objid (p, &p, end);
+               MonoThread *thread;
+               DebuggerTlsData *tls;
+               int invoke_id, err;
+
+               err = get_object (objid, (MonoObject**)&thread);
+               if (err)
+                       return err;
+
+               invoke_id = decode_int (p, &p, end);
+
+               mono_loader_lock ();
+               tls = mono_g_hash_table_lookup (thread_to_tls, THREAD_TO_INTERNAL (thread));
+               g_assert (tls);
+
+               if (tls->abort_requested) {
+                       mono_loader_unlock ();
+                       break;
+               }
+
+               /*
+                * Check whether we're still inside the mono_runtime_invoke() and that it's
+                * actually the correct invocation.
+                *
+                * Careful, we do not stop the thread that's doing the invocation, so we can't
+                * inspect its stack.  However, invoke_method() also acquires the loader lock
+                * when it's done, so we're safe here.
+                *
+                */
+
+               if (!tls->invoke || (tls->invoke->id != invoke_id)) {
+                       mono_loader_unlock ();
+                       return ERR_NO_INVOCATION;
+               }
+
+               tls->abort_requested = TRUE;
+
+               ves_icall_System_Threading_Thread_Abort (THREAD_TO_INTERNAL (thread), NULL);
+               mono_loader_unlock ();
+               break;
+       }
+
        default:
                return ERR_NOT_IMPLEMENTED;
        }
@@ -4410,6 +5235,9 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
                                if (err)
                                        return err;
+                               req->modifiers [i].caught = decode_byte (p, &p, end);
+                               req->modifiers [i].uncaught = decode_byte (p, &p, end);
+                               DEBUG(1, fprintf (log_file, "[dbg] \tEXCEPTION_ONLY filter (%s%s%s).\n", exc_class ? exc_class->name : "all", req->modifiers [i].caught ? ", caught" : "", req->modifiers [i].uncaught ? ", uncaught" : ""));
                                if (exc_class) {
                                        req->modifiers [i].data.exc_class = exc_class;
 
@@ -4449,7 +5277,7 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                                return err;
                        }
 
-                       err = ss_start (THREAD_TO_INTERNAL (step_thread), size, depth, req);
+                       err = ss_create (THREAD_TO_INTERNAL (step_thread), size, depth, req);
                        if (err) {
                                g_free (req);
                                return err;
@@ -4578,6 +5406,30 @@ domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                buffer_add_objid (buf, (MonoObject*)o);
                break;
        }
+       case CMD_APPDOMAIN_CREATE_BOXED_VALUE: {
+               MonoClass *klass;
+               MonoDomain *domain2;
+               MonoObject *o;
+
+               domain = decode_domainid (p, &p, end, NULL, &err);
+               if (err)
+                       return err;
+               klass = decode_typeid (p, &p, end, &domain2, &err);
+               if (err)
+                       return err;
+
+               // FIXME:
+               g_assert (domain == domain2);
+
+               o = mono_object_new (domain, klass);
+
+               err = decode_value (&klass->byval_arg, domain, mono_object_unbox (o), p, &p, end);
+               if (err)
+                       return err;
+
+               buffer_add_objid (buf, o);
+               break;
+       }
        default:
                return ERR_NOT_IMPLEMENTED;
        }
@@ -4779,10 +5631,8 @@ buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass
 }
 
 static ErrorCode
-type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint8 *p, guint8 *end, Buffer *buf)
 {
-       MonoClass *klass;
-       MonoDomain *domain;
        MonoClass *nested;
        MonoType *type;
        gpointer iter;
@@ -4790,10 +5640,6 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
        int err, nnested;
        char *name;
 
-       klass = decode_typeid (p, &p, end, &domain, &err);
-       if (err)
-               return err;
-
        switch (command) {
        case CMD_TYPE_GET_INFO: {
                buffer_add_string (buf, klass->name_space);
@@ -4823,6 +5669,8 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                        b |= (1 << 2);
                if (type->type == MONO_TYPE_VALUETYPE)
                        b |= (1 << 3);
+               if (klass->enumtype)
+                       b |= (1 << 4);
                buffer_add_byte (buf, b);
                nnested = 0;
                iter = NULL;
@@ -4840,6 +5688,8 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                gpointer iter = NULL;
                MonoMethod *m;
 
+               mono_class_setup_methods (klass);
+
                nmethods = mono_class_num_methods (klass);
 
                buffer_add_int (buf, nmethods);
@@ -5012,7 +5862,10 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                                g_free (val);
                                return err;
                        }
-                       mono_field_static_set_value (vtable, f, val);
+                       if (MONO_TYPE_IS_REFERENCE (f->type))
+                               mono_field_static_set_value (vtable, f, *(gpointer*)val);
+                       else
+                               mono_field_static_set_value (vtable, f, val);
                        g_free (val);
                }
                break;
@@ -5022,7 +5875,8 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                buffer_add_objid (buf, o);
                break;
        }
-       case CMD_TYPE_GET_SOURCE_FILES: {
+       case CMD_TYPE_GET_SOURCE_FILES:
+       case CMD_TYPE_GET_SOURCE_FILES_2: {
                gpointer iter = NULL;
                MonoMethod *method;
                char *source_file, *base;
@@ -5036,6 +5890,8 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
                        if (minfo) {
                                mono_debug_symfile_get_line_numbers (minfo, &source_file, NULL, NULL, NULL);
+                               if (!source_file)
+                                       continue;
 
                                for (i = 0; i < files->len; ++i)
                                        if (!strcmp (g_ptr_array_index (files, i), source_file))
@@ -5049,9 +5905,13 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                buffer_add_int (buf, files->len);
                for (i = 0; i < files->len; ++i) {
                        source_file = g_ptr_array_index (files, i);
-                       base = g_path_get_basename (source_file);
-                       buffer_add_string (buf, base);
-                       g_free (base);
+                       if (command == CMD_TYPE_GET_SOURCE_FILES_2) {
+                               buffer_add_string (buf, source_file);
+                       } else {
+                               base = g_path_get_basename (source_file);
+                               buffer_add_string (buf, base);
+                               g_free (base);
+                       }
                        g_free (source_file);
                }
                g_ptr_array_free (files, TRUE);
@@ -5076,17 +5936,33 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 }
 
 static ErrorCode
-method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 {
-       int err;
+       MonoClass *klass;
+       MonoDomain *old_domain;
        MonoDomain *domain;
-       MonoMethod *method;
-       MonoMethodHeader *header;
+       int err;
 
-       method = decode_methodid (p, &p, end, &domain, &err);
+       klass = decode_typeid (p, &p, end, &domain, &err);
        if (err)
                return err;
 
+       old_domain = mono_domain_get ();
+
+       mono_domain_set (domain, TRUE);
+
+       err = type_commands_internal (command, klass, domain, p, end, buf);
+
+       mono_domain_set (old_domain, TRUE);
+
+       return err;
+}
+
+static ErrorCode
+method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, guint8 *p, guint8 *end, Buffer *buf)
+{
+       MonoMethodHeader *header;
+
        switch (command) {
        case CMD_METHOD_GET_NAME: {
                buffer_add_string (buf, method->name);
@@ -5116,6 +5992,7 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                        buffer_add_int (buf, header->code_size);
                        buffer_add_string (buf, "");
                        buffer_add_int (buf, 0);
+                       mono_metadata_free_mh (header);
                        break;
                }
 
@@ -5132,6 +6009,7 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                g_free (source_file);
                g_free (il_offsets);
                g_free (line_numbers);
+               mono_metadata_free_mh (header);
                break;
        }
        case CMD_METHOD_GET_PARAM_INFO: {
@@ -5162,8 +6040,7 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
        }
        case CMD_METHOD_GET_LOCALS_INFO: {
                int i, j, num_locals;
-               char **local_names;
-               int *local_indexes;
+               MonoDebugLocalsInfo *locals;
 
                header = mono_method_get_header (method);
                g_assert (header);
@@ -5175,25 +6052,38 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                        buffer_add_typeid (buf, domain, mono_class_from_mono_type (header->locals [i]));
 
                /* Names */
-               num_locals = mono_debug_lookup_locals (method, &local_names, &local_indexes);
+               locals = mono_debug_lookup_locals (method);
+               if (locals)
+                       num_locals = locals->num_locals;
+               else
+                       num_locals = 0;
                for (i = 0; i < header->num_locals; ++i) {
                        for (j = 0; j < num_locals; ++j)
-                               if (local_indexes [j] == i)
+                               if (locals->locals [j].index == i)
                                        break;
                        if (j < num_locals)
-                               buffer_add_string (buf, local_names [j]);
+                               buffer_add_string (buf, locals->locals [j].name);
                        else
                                buffer_add_string (buf, "");
                }
-               g_free (local_names);
-               g_free (local_indexes);
 
-               /* Live ranges */
-               /* FIXME: This works because we set debug_options.mdb_optimizations */
+               /* Scopes */
                for (i = 0; i < header->num_locals; ++i) {
-                       buffer_add_int (buf, 0);
-                       buffer_add_int (buf, header->code_size);
+                       for (j = 0; j < num_locals; ++j)
+                               if (locals->locals [j].index == i)
+                                       break;
+                       if (j < num_locals && locals->locals [j].block) {
+                               buffer_add_int (buf, locals->locals [j].block->start_offset);
+                               buffer_add_int (buf, locals->locals [j].block->end_offset);
+                       } else {
+                               buffer_add_int (buf, 0);
+                               buffer_add_int (buf, header->code_size);
+                       }
                }
+               mono_metadata_free_mh (header);
+
+               if (locals)
+                       mono_debug_symfile_free_locals (locals);
 
                break;
        }
@@ -5213,6 +6103,7 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                        for (i = 0; i < header->code_size; ++i)
                                buffer_add_byte (buf, header->code [i]);
                }
+               mono_metadata_free_mh (header);
                break;
        }
        case CMD_METHOD_RESOLVE_TOKEN: {
@@ -5283,6 +6174,29 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
        return ERR_NONE;
 }
 
+static ErrorCode
+method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       int err;
+       MonoDomain *old_domain;
+       MonoDomain *domain;
+       MonoMethod *method;
+
+       method = decode_methodid (p, &p, end, &domain, &err);
+       if (err)
+               return err;
+
+       old_domain = mono_domain_get ();
+
+       mono_domain_set (domain, TRUE);
+
+       err = method_commands_internal (command, method, domain, p, end, buf);
+
+       mono_domain_set (old_domain, TRUE);
+
+       return err;
+}
+
 static ErrorCode
 thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 {
@@ -5361,6 +6275,9 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
        case CMD_THREAD_GET_INFO:
                buffer_add_byte (buf, thread->threadpool_thread);
                break;
+       case CMD_THREAD_GET_ID:
+               buffer_add_long (buf, (guint64)(gsize)thread);
+               break;
        default:
                return ERR_NOT_IMPLEMENTED;
        }
@@ -5413,7 +6330,9 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
        if (!frame->jit) {
                frame->jit = mono_debug_find_method (frame->method, frame->domain);
-               g_assert (frame->jit);
+               if (!frame->jit)
+                       /* This could happen for aot images with no jit debug info */
+                       return ERR_ABSENT_INFORMATION;
        }
        jit = frame->jit;
 
@@ -5434,15 +6353,16 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
                                var = &jit->params [pos];
 
-                               add_var (buf, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain);
+                               add_var (buf, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain, FALSE);
                        } else {
                                g_assert (pos >= 0 && pos < jit->num_locals);
 
                                var = &jit->locals [pos];
                                
-                               add_var (buf, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain);
+                               add_var (buf, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE);
                        }
                }
+               mono_metadata_free_mh (header);
                break;
        }
        case CMD_STACK_FRAME_GET_THIS: {
@@ -5451,14 +6371,14 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                                MonoObject *p = NULL;
                                buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &p, frame->domain);
                        } else {
-                               add_var (buf, &frame->method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain);
+                               add_var (buf, &frame->method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
                        }
                } else {
                        if (!sig->hasthis) {
                                MonoObject *p = NULL;
                                buffer_add_value (buf, &frame->method->klass->byval_arg, &p, frame->domain);
                        } else {
-                               add_var (buf, &frame->method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain);
+                               add_var (buf, &frame->method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
                        }
                }
                break;
@@ -5498,6 +6418,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
                        set_var (t, var, &frame->ctx, frame->domain, val_buf);
                }
+               mono_metadata_free_mh (header);
                break;
        }
        default:
@@ -5778,7 +6699,7 @@ debugger_thread (void *arg)
        mono_set_is_debugger_attached (TRUE);
 
        while (TRUE) {
-               res = recv (conn_fd, header, HEADER_LENGTH, 0);
+               res = recv_length (conn_fd, header, HEADER_LENGTH, 0);
 
                /* This will break if the socket is closed during shutdown too */
                if (res != HEADER_LENGTH)
@@ -5800,7 +6721,7 @@ debugger_thread (void *arg)
                data = g_malloc (len - HEADER_LENGTH);
                if (len - HEADER_LENGTH > 0)
                {
-                       res = recv (conn_fd, data, len - HEADER_LENGTH, 0);
+                       res = recv_length (conn_fd, data, len - HEADER_LENGTH, 0);
                        if (res != len - HEADER_LENGTH)
                                break;
                }
@@ -5915,14 +6836,9 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
 }
 
 void
-mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *ctx)
-{
-}
-
-void
-mono_debugger_agent_handle_unhandled_exception (MonoException *exc, MonoContext *ctx)
+mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *throw_ctx,
+                                                                         MonoContext *catch_ctx)
 {
-       
 }
 #endif