[Cleanup] Don't force unrequested events to be sent.
[mono.git] / mono / mini / debugger-agent.c
index 989a28b9754e209cd23b36603475d756e549cba5..b50fb91d80b62232dce74936b191d2f9479ad3b5 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>
@@ -70,7 +70,9 @@ 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 <mono/utils/mono-error-internals.h>
 #include "debugger-agent.h"
 #include "mini.h"
 
@@ -83,6 +85,7 @@ int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD,
 #endif
 
 #ifndef DISABLE_DEBUGGER_AGENT
+
 #include <mono/io-layer/mono-mutex.h>
 
 /* Definitions to make backporting to 2.6 easier */
@@ -102,14 +105,21 @@ typedef struct {
        GSList *onthrow;
        int timeout;
        char *launch;
+       gboolean embedding;
+       gboolean defer;
 } AgentConfig;
 
 typedef struct
 {
        int id;
-       guint32 il_offset;
+       guint32 il_offset, native_offset;
        MonoDomain *domain;
        MonoMethod *method;
+       /*
+        * If method is gshared, this is the actual instance, otherwise this is equal to
+        * method.
+        */
+       MonoMethod *actual_method;
        MonoContext ctx;
        MonoDebugMethodJitInfo *jit;
        int flags;
@@ -206,6 +216,10 @@ typedef struct {
 
        gboolean has_async_ctx;
 
+       gboolean has_filter_ctx;
+       MonoContext filter_ctx;
+       MonoLMF *filter_lmf;
+
        /*
         * The lmf where the stack walk can be started for running threads.
         */
@@ -222,6 +236,14 @@ typedef struct {
         * The current mono_runtime_invoke invocation.
         */
        InvokeData *invoke;
+
+       /*
+        * The context where single stepping should resume while the thread is suspended because
+        * of an EXCEPTION event.
+        */
+       MonoContext catch_ctx;
+
+       gboolean has_catch_ctx;
 } DebuggerTlsData;
 
 /* 
@@ -231,7 +253,7 @@ typedef struct {
 #define HEADER_LENGTH 11
 
 #define MAJOR_VERSION 2
-#define MINOR_VERSION 1
+#define MINOR_VERSION 3
 
 typedef enum {
        CMD_SET_VM = 1,
@@ -282,7 +304,8 @@ typedef enum {
        ERR_INVALID_ARGUMENT = 102,
        ERR_UNLOADED = 103,
        ERR_NO_INVOCATION = 104,
-       ERR_ABSENT_INFORMATION = 105
+       ERR_ABSENT_INFORMATION = 105,
+       ERR_NO_SEQ_POINT_AT_IL_OFFSET = 106
 } ErrorCode;
 
 typedef enum {
@@ -344,7 +367,8 @@ typedef enum {
        CMD_THREAD_GET_NAME = 2,
        CMD_THREAD_GET_STATE = 3,
        CMD_THREAD_GET_INFO = 4,
-       CMD_THREAD_GET_ID = 5
+       CMD_THREAD_GET_ID = 5,
+       CMD_THREAD_GET_TID = 6
 } CmdThread;
 
 typedef enum {
@@ -405,6 +429,7 @@ typedef enum {
        CMD_TYPE_GET_FIELD_CATTRS = 11,
        CMD_TYPE_GET_PROPERTY_CATTRS = 12,
        CMD_TYPE_GET_SOURCE_FILES_2 = 13,
+       CMD_TYPE_GET_VALUES_2 = 14
 } CmdType;
 
 typedef enum {
@@ -503,6 +528,7 @@ static AgentConfig agent_config;
 static gint32 inited;
 
 static int conn_fd;
+static int listen_fd;
 
 static int packet_id = 0;
 
@@ -533,17 +559,13 @@ static HANDLE debugger_thread_handle;
 
 static int log_level;
 
-static FILE *log_file;
+static gboolean embedding;
 
-/* Classes whose class load event has been sent */
-static GHashTable *loaded_classes;
+static FILE *log_file;
 
 /* 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;
 
@@ -581,20 +603,28 @@ static void runtime_initialized (MonoProfiler *prof);
 
 static void runtime_shutdown (MonoProfiler *prof);
 
-static void thread_startup (MonoProfiler *prof, intptr_t tid);
+static void thread_startup (MonoProfiler *prof, uintptr_t tid);
 
-static void thread_end (MonoProfiler *prof, intptr_t 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 emit_appdomain_load (gpointer key, gpointer value, gpointer user_data);
+
+static void emit_thread_start (gpointer key, gpointer value, gpointer user_data);
+
 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);
 
+static void emit_assembly_load (gpointer assembly, gpointer user_data);
+
+static void emit_type_load (gpointer key, gpointer type, gpointer user_data);
+
 static void start_runtime_invoke (MonoProfiler *prof, MonoMethod *method);
 
 static void end_runtime_invoke (MonoProfiler *prof, MonoMethod *method);
@@ -611,6 +641,12 @@ static void suspend_current (void);
 
 static void clear_event_requests_for_assembly (MonoAssembly *assembly);
 
+static void clear_types_for_assembly (MonoAssembly *assembly);
+
+static void clear_breakpoints_for_domain (MonoDomain *domain);
+
+static void process_profiler_event (EventKind event, gpointer arg);
+
 /* Submodule init/cleanup */
 static void breakpoints_init (void);
 static void breakpoints_cleanup (void);
@@ -623,11 +659,12 @@ static void ids_cleanup (void);
 
 static void suspend_init (void);
 
-static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls);
+static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch);
 static ErrorCode ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req);
 static void ss_destroy (SingleStepReq *req);
 
 static void start_debugger_thread (void);
+static void stop_debugger_thread (void);
 
 static void finish_agent_init (gboolean on_startup);
 
@@ -657,8 +694,9 @@ print_usage (void)
        fprintf (stderr, "  address=<hostname>:<port>\tAddress to connect to (mandatory)\n");
        fprintf (stderr, "  loglevel=<n>\t\t\tLog level (defaults to 0)\n");
        fprintf (stderr, "  logfile=<file>\t\tFile to log to (defaults to stdout)\n");
-       fprintf (stderr, "  suspend=y/n\t\t\tWhenever to suspend after startup.\n");
+       fprintf (stderr, "  suspend=y/n\t\t\tWhether to suspend after startup.\n");
        fprintf (stderr, "  timeout=<n>\t\t\tTimeout for connecting in milliseconds.\n");
+       fprintf (stderr, "  server=y/n\t\t\tWhether to listen for a client connection.\n");
        fprintf (stderr, "  help\t\t\t\tPrint this help.\n");
 }
 
@@ -691,6 +729,8 @@ mono_debugger_agent_parse_options (char *options)
        agent_config.enabled = TRUE;
        agent_config.suspend = TRUE;
        agent_config.server = FALSE;
+       agent_config.defer = FALSE;
+       agent_config.address = NULL;
 
        args = g_strsplit (options, ",", -1);
        for (ptr = args; ptr && *ptr; ptr ++) {
@@ -722,12 +762,22 @@ 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);
                }
        }
 
+       if (agent_config.server && !agent_config.suspend) {
+               /* Waiting for deferred attachment */
+               agent_config.defer = TRUE;
+               if (agent_config.address == NULL) {
+                       agent_config.address = g_strdup_printf ("0.0.0.0:%u", 56000 + (GetCurrentProcessId () % 1000));
+               }
+       }
+
        if (agent_config.transport == NULL) {
                fprintf (stderr, "debugger-agent: The 'transport' option is mandatory.\n");
                exit (1);
@@ -774,21 +824,22 @@ mono_debugger_agent_init (void)
        debugger_tls_id = TlsAlloc ();
 
        thread_to_tls = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC);
-       MONO_GC_REGISTER_ROOT (thread_to_tls);
+       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;
+       disconnected = TRUE;
+
        if (agent_config.log_file) {
                log_file = fopen (agent_config.log_file, "w+");
                if (!log_file) {
@@ -875,48 +926,11 @@ mono_debugger_agent_cleanup (void)
        if (!inited)
                return;
 
-       /* This will interrupt the agent thread */
-       /* Close the read part only so it can still send back replies */
-#ifdef HOST_WIN32
-       shutdown (conn_fd, SD_RECEIVE);
-#else
-       shutdown (conn_fd, SHUT_RD);
-#endif
-
-       /* 
-        * Wait for the thread to exit.
-        *
-        * If we continue with the shutdown without waiting for it, then the client might
-        * not receive an answer to its last command like a resume.
-        * The WaitForSingleObject infrastructure doesn't seem to work during shutdown, so
-        * use pthreads.
-        */
-       //WaitForSingleObject (debugger_thread_handle, INFINITE);
-       if (GetCurrentThreadId () != debugger_thread_id) {
-               mono_mutex_lock (&debugger_thread_exited_mutex);
-               if (!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);
-       }
+       stop_debugger_thread ();
 
        breakpoints_cleanup ();
        objrefs_cleanup ();
        ids_cleanup ();
-
-#ifdef HOST_WIN32
-       shutdown (conn_fd, SD_BOTH);
-#else
-       shutdown (conn_fd, SHUT_RDWR);
-#endif
        
        mono_mutex_destroy (&debugger_thread_exited_mutex);
        mono_cond_destroy (&debugger_thread_exited_cond);
@@ -940,6 +954,70 @@ recv_length (int fd, void *buf, int len, int flags)
        } while ((res > 0 && total < len) || (res == -1 && errno == EINTR));
        return total;
 }
+
+#ifndef TARGET_PS3
+#define HAVE_GETADDRINFO 1
+#endif
+static int
+transport_accept (int socket_fd)
+{
+       conn_fd = accept (socket_fd, NULL, NULL);
+       if (conn_fd == -1) {
+               fprintf (stderr, "debugger-agent: Unable to listen on %d\n", socket_fd);
+       } else {
+               DEBUG (1, fprintf (log_file, "Accepted connection from client, connection fd=%d.\n", conn_fd));
+       }
+       
+       return conn_fd;
+}
+
+static gboolean
+transport_handshake (void)
+{
+       char handshake_msg [128];
+       guint8 buf [128];
+       int res;
+       
+       /* Write handshake message */
+       sprintf (handshake_msg, "DWP-Handshake");
+       do {
+               res = send (conn_fd, handshake_msg, strlen (handshake_msg), 0);
+       } while (res == -1 && errno == EINTR);
+       g_assert (res != -1);
+
+       /* Read answer */
+       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");
+               return FALSE;
+       }
+
+       /*
+        * 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.
+        */
+       {
+               int flag = 1;
+               int result = setsockopt (conn_fd,
+                                 IPPROTO_TCP,
+                                 TCP_NODELAY,
+                                 (char *) &flag,
+                                 sizeof(int));
+               g_assert (result >= 0);
+       }
+       
+       return TRUE;
+}
+
 /*
  * transport_connect:
  *
@@ -948,14 +1026,17 @@ recv_length (int fd, void *buf, int len, int flags)
 static void
 transport_connect (const char *host, int port)
 {
+#ifdef HAVE_GETADDRINFO
        struct addrinfo hints;
        struct addrinfo *result, *rp;
+#else
+       struct hostent *result;
+#endif
        int sfd, s, res;
        char port_string [128];
-       char handshake_msg [128];
-       guint8 buf [128];
 
        conn_fd = -1;
+       listen_fd = -1;
 
        if (host) {
                sprintf (port_string, "%d", port);
@@ -963,7 +1044,7 @@ transport_connect (const char *host, int port)
                mono_network_init ();
 
                /* Obtain address(es) matching host/port */
-
+#ifdef HAVE_GETADDRINFO
                memset (&hints, 0, sizeof (struct addrinfo));
                hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
                hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
@@ -972,12 +1053,20 @@ transport_connect (const char *host, int port)
 
                s = getaddrinfo (host, port_string, &hints, &result);
                if (s != 0) {
-                       fprintf (stderr, "debugger-agent: Unable to connect to %s:%d: %s\n", host, port, gai_strerror (s));
+                       fprintf (stderr, "debugger-agent: Unable to resolve %s:%d: %s\n", host, port, gai_strerror (s));
                        exit (1);
                }
+#else
+               /* The PS3 doesn't even have _r or hstrerror () */
+               result = gethostbyname (host);
+               if (!result) {
+                       fprintf (stderr, "debugger-agent: Unable to resolve %s:%d: %d\n", host, port, h_errno);
+               }
+#endif
        }
 
        if (agent_config.server) {
+#ifdef HAVE_GETADDRINFO
                /* Wait for a connection */
                if (!host) {
                        struct sockaddr_in addr;
@@ -993,6 +1082,7 @@ transport_connect (const char *host, int port)
                                fprintf (stderr, "debugger-agent: Unable to setup listening socket: %s\n", strerror (errno));
                                exit (1);
                        }
+                       listen_fd = sfd;
 
                        addrlen = sizeof (addr);
                        memset (&addr, 0, sizeof (addr));
@@ -1020,6 +1110,7 @@ transport_connect (const char *host, int port)
                                res = listen (sfd, 16);
                                if (res == -1)
                                        continue;
+                               listen_fd = sfd;
                                break;
                        }
 
@@ -1030,12 +1121,17 @@ transport_connect (const char *host, int port)
                         * http://msdn.microsoft.com/en-us/library/ms737931(VS.85).aspx
                         * only works with MSVC.
                         */
+#ifdef HAVE_GETADDRINFO
                        freeaddrinfo (result);
+#endif
 #endif
                }
 
                DEBUG (1, fprintf (log_file, "Listening on %s:%d (timeout=%d ms)...\n", host, port, agent_config.timeout));
 
+               if (agent_config.defer)
+                       return;
+
                if (agent_config.timeout) {
                        fd_set readfds;
                        struct timeval tv;
@@ -1047,19 +1143,22 @@ transport_connect (const char *host, int port)
                        res = select (sfd + 1, &readfds, NULL, NULL, &tv);
                        if (res == 0) {
                                fprintf (stderr, "debugger-agent: Timed out waiting to connect.\n");
-                               exit (1);
+                               if (!agent_config.defer)
+                                       exit (1);
                        }
                }
 
-               conn_fd = accept (sfd, NULL, NULL);
-               if (conn_fd == -1) {
-                       fprintf (stderr, "debugger-agent: Unable to listen on %s:%d\n", host, port);
+               conn_fd = transport_accept (sfd);
+               if (conn_fd == -1)
                        exit (1);
-               }
 
                DEBUG (1, fprintf (log_file, "Accepted connection from client, socket fd=%d.\n", conn_fd));
+#else
+               NOT_IMPLEMENTED;
+#endif /* HAVE_GETADDRINFO */
        } else {
                /* Connect to the specified address */
+#ifdef HAVE_GETADDRINFO
                /* FIXME: Respect the timeout */
                for (rp = result; rp != NULL; rp = rp->ai_next) {
                        sfd = socket (rp->ai_family, rp->ai_socktype,
@@ -1073,54 +1172,32 @@ transport_connect (const char *host, int port)
                        close (sfd);
                }
 
+               if (rp == 0) {
+                       fprintf (stderr, "debugger-agent: Unable to connect to %s:%d\n", host, port);
+                       exit (1);
+               }
+#else
+                       sfd = socket (result->h_addrtype, SOCK_STREAM, 0);
+                       if (sfd == -1)
+                               g_assert_not_reached ();
+                       res = connect (sfd, (void*)result->h_addr_list [0], result->h_length);
+                       if (res == -1)
+                               g_assert_not_reached ();
+#endif
+
                conn_fd = sfd;
 
 #ifndef HOST_WIN32
                /* See the comment above */
+#ifdef HAVE_GETADDRINFO
                freeaddrinfo (result);
 #endif
-
-               if (rp == 0) {
-                       fprintf (stderr, "debugger-agent: Unable to connect to %s:%d\n", host, port);
-                       exit (1);
-               }
+#endif
        }
        
-       /* Write handshake message */
-       sprintf (handshake_msg, "DWP-Handshake");
-       do {
-               res = send (conn_fd, handshake_msg, strlen (handshake_msg), 0);
-       } while (res == -1 && errno == EINTR);
-       g_assert (res != -1);
-
-       /* Read answer */
-       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");
+       disconnected = !transport_handshake ();
+       if (disconnected)
                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.
-        */
-       {
-               int flag = 1;
-               int result = setsockopt(conn_fd,
-                                 IPPROTO_TCP,
-                                 TCP_NODELAY,
-                                 (char *) &flag,
-                                 sizeof(int));
-               g_assert (result >= 0);
-       }
 }
 
 static gboolean
@@ -1137,6 +1214,60 @@ transport_send (guint8 *data, int len)
                return TRUE;
 }
 
+static void
+stop_debugger_thread (void)
+{
+       if (!inited)
+               return;
+
+       /* This will interrupt the agent thread */
+       /* Close the read part only so it can still send back replies */
+       /* Also shut down the connection listener so that we can exit normally */
+#ifdef HOST_WIN32
+       /* SD_RECEIVE doesn't break the recv in the debugger thread */
+       shutdown (conn_fd, SD_BOTH);
+       shutdown (listen_fd, SD_BOTH);
+       closesocket (listen_fd);
+#else
+       shutdown (conn_fd, SHUT_RD);
+       shutdown (listen_fd, SHUT_RDWR);
+       close (listen_fd);
+#endif
+
+       /* 
+        * Wait for the thread to exit.
+        *
+        * If we continue with the shutdown without waiting for it, then the client might
+        * not receive an answer to its last command like a resume.
+        * The WaitForSingleObject infrastructure doesn't seem to work during shutdown, so
+        * use pthreads.
+        */
+       //WaitForSingleObject (debugger_thread_handle, INFINITE);
+       if (GetCurrentThreadId () != debugger_thread_id) {
+               do {
+                       mono_mutex_lock (&debugger_thread_exited_mutex);
+                       if (!debugger_thread_exited) {
+#ifdef HOST_WIN32
+                               if (WAIT_TIMEOUT == WaitForSingleObject(debugger_thread_exited_cond, 0)) {
+                                       mono_mutex_unlock (&debugger_thread_exited_mutex);
+                                       Sleep(1);
+                                       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);
+               } while (!debugger_thread_exited);
+       }
+
+#ifdef HOST_WIN32
+       shutdown (conn_fd, SD_BOTH);
+#else
+       shutdown (conn_fd, SHUT_RDWR);
+#endif
+}
+
 static void
 start_debugger_thread (void)
 {
@@ -1390,26 +1521,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);
@@ -1417,7 +1564,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 ();
 
@@ -1526,6 +1679,8 @@ typedef struct {
 typedef struct {
        /* Maps runtime structure -> Id */
        GHashTable *val_to_id [ID_NUM];
+       /* Classes whose class load event has been sent */
+       GHashTable *loaded_classes;
 } AgentDomainInfo;
 
 /* Maps id -> Id */
@@ -1565,6 +1720,7 @@ mono_debugger_agent_free_domain_info (MonoDomain *domain)
                for (i = 0; i < ID_NUM; ++i)
                        if (info->val_to_id [i])
                                g_hash_table_destroy (info->val_to_id [i]);
+               g_hash_table_destroy (info->loaded_classes);
                g_free (info);
        }
 
@@ -1586,6 +1742,38 @@ mono_debugger_agent_free_domain_info (MonoDomain *domain)
        mono_loader_unlock ();
 }
 
+/*
+ * Called when deferred debugger session is attached, 
+ * after the VM start event has been sent successfully
+ */
+static void
+mono_debugger_agent_on_attach (void)
+{
+       /* Emit load events for currently loaded appdomains, assemblies, and types */
+       mono_loader_lock ();
+       g_hash_table_foreach (domains, emit_appdomain_load, NULL);
+       mono_g_hash_table_foreach (tid_to_thread, emit_thread_start, NULL);
+       mono_assembly_foreach (emit_assembly_load, NULL);
+       mono_loader_unlock ();
+}
+
+static AgentDomainInfo* get_agent_domain_info (MonoDomain *domain)
+{
+       AgentDomainInfo *info = NULL;
+
+       mono_domain_lock (domain);
+
+       info = domain_jit_info (domain)->agent_info;
+       if (!info) {
+               info = domain_jit_info (domain)->agent_info = g_new0 (AgentDomainInfo, 1);
+               info->loaded_classes = g_hash_table_new (mono_aligned_addr_hash, NULL);
+       }
+
+       mono_domain_unlock (domain);
+
+       return info;
+}
+
 static int
 get_id (MonoDomain *domain, IdType type, gpointer val)
 {
@@ -1597,11 +1785,10 @@ get_id (MonoDomain *domain, IdType type, gpointer val)
 
        mono_loader_lock ();
 
+       info = get_agent_domain_info (domain);
+
        mono_domain_lock (domain);
 
-       if (!domain_jit_info (domain)->agent_info)
-               domain_jit_info (domain)->agent_info = g_new0 (AgentDomainInfo, 1);
-       info = domain_jit_info (domain)->agent_info;
        if (info->val_to_id [type] == NULL)
                info->val_to_id [type] = g_hash_table_new (mono_aligned_addr_hash, NULL);
 
@@ -1924,7 +2111,7 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
                        data.last_frame_set = FALSE;
                        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);
+                               mono_walk_stack (get_last_frame, mono_domain_get (), &ctx, MONO_UNWIND_DEFAULT, tls->thread, mono_get_lmf (), &data);
                        }
                        if (data.last_frame_set) {
                                memcpy (&tls->async_last_frame, &data.last_frame, sizeof (StackFrameInfo));
@@ -2007,7 +2194,7 @@ notify_thread (gpointer key, gpointer value, gpointer user_data)
 #ifdef HOST_WIN32
        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
 }
 
@@ -2213,7 +2400,7 @@ suspend_current (void)
                if (WAIT_TIMEOUT == WaitForSingleObject(suspend_cond, 0))
                {
                        mono_mutex_unlock (&suspend_mutex);
-                       Sleep(0);
+                       Sleep(1);
                        mono_mutex_lock (&suspend_mutex);
                }
                else
@@ -2339,6 +2526,58 @@ find_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32
        return NULL;
 }
 
+/*
+ * find_next_seq_point_for_native_offset:
+ *
+ *   Find the first sequence point after NATIVE_OFFSET.
+ */
+static SeqPoint*
+find_next_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_prev_seq_point_for_native_offset:
+ *
+ *   Find the first sequence point before NATIVE_OFFSET.
+ */
+static SeqPoint*
+find_prev_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 = seq_points->len - 1; i >= 0; --i) {
+               if (seq_points->seq_points [i].native_offset <= native_offset)
+                       return &seq_points->seq_points [i];
+       }
+
+       return NULL;
+}
+
 /*
  * find_seq_point:
  *
@@ -2410,7 +2649,7 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
 {
        ComputeFramesUserData *ud = user_data;
        StackFrame *frame;
-       MonoMethod *method;
+       MonoMethod *method, *actual_method;
 
        if (info->type != FRAME_TYPE_MANAGED) {
                if (info->type == FRAME_TYPE_DEBUGGER_INVOKE) {
@@ -2425,6 +2664,7 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
                method = info->ji->method;
        else
                method = info->method;
+       actual_method = info->actual_method;
 
        if (!method || (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD))
                return FALSE;
@@ -2434,7 +2674,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 (log_file, "\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:%x(%x) %d\n", mono_method_full_name (method, TRUE), info->il_offset, info->native_offset, info->managed));
 
        if (!info->managed && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD) {
                /*
@@ -2448,7 +2688,9 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
 
        frame = g_new0 (StackFrame, 1);
        frame->method = method;
+       frame->actual_method = actual_method;
        frame->il_offset = info->il_offset;
+       frame->native_offset = info->native_offset;
        if (ctx) {
                frame->ctx = *ctx;
                frame->has_ctx = TRUE;
@@ -2460,6 +2702,25 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
        return FALSE;
 }
 
+static gboolean
+process_filter_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
+{
+       ComputeFramesUserData *ud = user_data;
+
+       /*
+        * 'tls->filter_ctx' is the location of the throw site.
+        *
+        * mono_walk_stack() will never actually hit the throw site, but unwind
+        * directly from the filter to the call site; we abort stack unwinding here
+        * once this happens and resume from the throw site.
+        */
+
+       if (MONO_CONTEXT_GET_SP (ctx) >= MONO_CONTEXT_GET_SP (&ud->tls->filter_ctx))
+               return TRUE;
+
+       return process_frame (info, ctx, user_data);
+}
+
 static void
 compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls)
 {
@@ -2482,9 +2743,21 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls)
        } if (!tls->really_suspended && tls->has_async_ctx) {
                /* Have to use the state saved by the signal handler */
                process_frame (&tls->async_last_frame, NULL, &user_data);
-               mono_jit_walk_stack_from_ctx_in_thread (process_frame, tls->domain, &tls->async_ctx, FALSE, thread, tls->async_lmf, &user_data);
+               mono_walk_stack (process_frame, tls->domain, &tls->async_ctx, MONO_UNWIND_DEFAULT, thread, tls->async_lmf, &user_data);
+       } else if (tls->has_filter_ctx) {
+               /*
+                * We are inside an exception filter.
+                *
+                * First we add all the frames from inside the filter; 'tls->ctx' has the current context.
+                */
+               if (tls->has_context)
+                       mono_walk_stack (process_filter_frame, tls->domain, &tls->ctx, MONO_UNWIND_DEFAULT, thread, tls->lmf, &user_data);
+               /*
+                * After that, we resume unwinding from the location where the exception has been thrown.
+                */
+               mono_walk_stack (process_frame, tls->domain, &tls->filter_ctx, MONO_UNWIND_DEFAULT, thread, tls->filter_lmf, &user_data);
        } else if (tls->has_context) {
-               mono_jit_walk_stack_from_ctx_in_thread (process_frame, tls->domain, &tls->ctx, FALSE, thread, tls->lmf, &user_data);
+               mono_walk_stack (process_frame, tls->domain, &tls->ctx, MONO_UNWIND_DEFAULT, thread, tls->lmf, &user_data);
        } else {
                // FIXME:
                tls->frame_count = 0;
@@ -2523,6 +2796,55 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls)
        tls->frames_up_to_date = TRUE;
 }
 
+/*
+ * GHFunc to emit an appdomain creation event
+ * @param key Don't care
+ * @param value A loaded appdomain
+ * @param user_data Don't care
+ */
+static void
+emit_appdomain_load (gpointer key, gpointer value, gpointer user_data)
+{
+       process_profiler_event (EVENT_KIND_APPDOMAIN_CREATE, value);
+       g_hash_table_foreach (get_agent_domain_info (value)->loaded_classes, emit_type_load, NULL);
+}
+
+/*
+ * GHFunc to emit a thread start event
+ * @param key A thread id
+ * @param value A thread object
+ * @param user_data Don't care
+ */
+static void
+emit_thread_start (gpointer key, gpointer value, gpointer user_data)
+{
+       if (GPOINTER_TO_INT (key) != debugger_thread_id)
+               process_profiler_event (EVENT_KIND_THREAD_START, value);
+}
+
+/*
+ * GFunc to emit an assembly load event
+ * @param value A loaded assembly
+ * @param user_data Don't care
+ */
+static void
+emit_assembly_load (gpointer value, gpointer user_data)
+{
+       process_profiler_event (EVENT_KIND_ASSEMBLY_LOAD, value);
+}
+
+/*
+ * GFunc to emit a type load event
+ * @param value A loaded type
+ * @param user_data Don't care
+ */
+static void
+emit_type_load (gpointer key, gpointer value, gpointer user_data)
+{
+       process_profiler_event (EVENT_KIND_TYPE_LOAD, value);
+}
+
+
 /*
  * EVENT HANDLING
  */
@@ -2647,30 +2969,44 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
        Buffer buf;
        GSList *l;
        MonoDomain *domain = mono_domain_get ();
-       MonoThread *thread;
+       MonoThread *thread = NULL;
+       gboolean send_success = FALSE;
 
-       if (!inited)
+       if (!inited) {
+               DEBUG (2, fprintf (log_file, "Debugger agent not initialized yet: dropping %s\n", event_to_string (event)));
                return;
+       }
 
-       if (!vm_start_event_sent && event != EVENT_KIND_VM_START)
+       if (!vm_start_event_sent && event != EVENT_KIND_VM_START) {
                // FIXME: We miss those events
+               DEBUG (2, fprintf (log_file, "VM start event not sent yet: dropping %s\n", event_to_string (event)));
                return;
+       }
 
-       if (vm_death_event_sent)
+       if (vm_death_event_sent) {
+               DEBUG (2, fprintf (log_file, "VM death event has been sent: dropping %s\n", event_to_string (event)));
                return;
+       }
 
-       if (mono_runtime_is_shutting_down () && event != EVENT_KIND_VM_DEATH)
+       if (mono_runtime_is_shutting_down () && event != EVENT_KIND_VM_DEATH) {
+               DEBUG (2, fprintf (log_file, "Mono runtime is shutting down: dropping %s\n", event_to_string (event)));
                return;
+       }
 
-       if (disconnected)
+       if (disconnected) {
+               DEBUG (2, fprintf (log_file, "Debugger client is not connected: dropping %s\n", event_to_string (event)));
                return;
+       }
 
        if (events == NULL)
                return;
-
-       if (debugger_thread_id == GetCurrentThreadId () && event != EVENT_KIND_VM_DEATH)
-               // FIXME: Send these with a NULL thread, don't suspend the current thread
-               return;
+       
+       if (agent_config.defer) {
+               /* Make sure the thread id is always set when doing deferred debugging */
+               if (debugger_thread_id == GetCurrentThreadId ())
+                       thread = mono_thread_get_main ();
+               else thread = mono_thread_current ();
+       }
 
        buffer_init (&buf, 128);
        buffer_add_byte (&buf, suspend_policy);
@@ -2680,12 +3016,8 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
                buffer_add_byte (&buf, event); // event kind
                buffer_add_int (&buf, GPOINTER_TO_INT (l->data)); // request id
 
-               thread = mono_thread_current ();
-
-               if (event == EVENT_KIND_VM_START)
+               if (event == EVENT_KIND_VM_START && arg != NULL)
                        thread = arg;
-               else if (event == EVENT_KIND_THREAD_START)
-                       g_assert (mono_thread_internal_current () == arg);
 
                buffer_add_objid (&buf, (MonoObject*)thread); // thread
 
@@ -2730,12 +3062,12 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
 
        if (event == EVENT_KIND_VM_START) {
                suspend_policy = agent_config.suspend ? SUSPEND_POLICY_ALL : SUSPEND_POLICY_NONE;
-               start_debugger_thread ();
+               if (!agent_config.defer)
+                       start_debugger_thread ();
        }
    
        if (event == EVENT_KIND_VM_DEATH) {
                vm_death_event_sent = TRUE;
-
                suspend_policy = SUSPEND_POLICY_NONE;
        }
 
@@ -2752,17 +3084,26 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
                suspend_vm ();
        }
 
-       send_packet (CMD_SET_EVENT, CMD_COMPOSITE, &buf);
+       send_success = send_packet (CMD_SET_EVENT, CMD_COMPOSITE, &buf);
+
+       buffer_free (&buf);
 
        g_slist_free (events);
        events = NULL;
 
-       if (event == EVENT_KIND_VM_START)
+       if (!send_success) {
+               DEBUG (2, fprintf (log_file, "Sending command %s failed.\n", event_to_string (event)));
+               return;
+       }
+       
+       if (event == EVENT_KIND_VM_START) {
                vm_start_event_sent = TRUE;
-
+               if (agent_config.defer)
+                       mono_debugger_agent_on_attach ();
+       }
+       
        DEBUG (1, fprintf (log_file, "[%p] Sent event %s, suspend=%d.\n", (gpointer)GetCurrentThreadId (), event_to_string (event), suspend_policy));
 
-       buffer_free (&buf);
 
        switch (suspend_policy) {
        case SUSPEND_POLICY_NONE:
@@ -2795,7 +3136,9 @@ static void
 runtime_initialized (MonoProfiler *prof)
 {
        process_profiler_event (EVENT_KIND_VM_START, mono_thread_current ());
-}      
+       if (agent_config.defer)
+               start_debugger_thread ();
+}
 
 static void
 runtime_shutdown (MonoProfiler *prof)
@@ -2806,7 +3149,7 @@ runtime_shutdown (MonoProfiler *prof)
 }
 
 static void
-thread_startup (MonoProfiler *prof, intptr_t tid)
+thread_startup (MonoProfiler *prof, uintptr_t tid)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
        MonoInternalThread *old_thread;
@@ -2847,7 +3190,7 @@ thread_startup (MonoProfiler *prof, intptr_t 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);
 
@@ -2868,7 +3211,7 @@ thread_startup (MonoProfiler *prof, intptr_t tid)
 }
 
 static void
-thread_end (MonoProfiler *prof, intptr_t tid)
+thread_end (MonoProfiler *prof, uintptr_t tid)
 {
        MonoInternalThread *thread;
        DebuggerTlsData *tls = NULL;
@@ -2906,8 +3249,13 @@ appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result)
 static void
 appdomain_unload (MonoProfiler *prof, MonoDomain *domain)
 {
+       clear_breakpoints_for_domain (domain);
+       
+       mono_loader_lock ();
        /* Invalidate each thread's frame stack */
        mono_g_hash_table_foreach (thread_to_tls, invalidate_each_thread, NULL);
+       mono_loader_unlock ();
+       
        process_profiler_event (EVENT_KIND_APPDOMAIN_UNLOAD, domain);
 }
 
@@ -2938,6 +3286,7 @@ 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
@@ -2971,7 +3320,7 @@ end_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
        gpointer stackptr = __builtin_frame_address (1);
 #endif
 
-       if (ss_req == NULL || stackptr != ss_invoke_addr || ss_req->thread != mono_thread_internal_current ())
+       if (!embedding || ss_req == NULL || stackptr != ss_invoke_addr || ss_req->thread != mono_thread_internal_current ())
                return;
 
        /*
@@ -2998,15 +3347,22 @@ static void
 send_type_load (MonoClass *klass)
 {
        gboolean type_load = FALSE;
+       MonoDomain *domain = mono_domain_get ();
+       AgentDomainInfo *info = NULL;
 
        mono_loader_lock ();
-       if (!g_hash_table_lookup (loaded_classes, klass)) {
+       info = get_agent_domain_info (domain);
+       mono_domain_lock (domain);
+
+       if (!g_hash_table_lookup (info->loaded_classes, klass)) {
                type_load = TRUE;
-               g_hash_table_insert (loaded_classes, klass, klass);
+               g_hash_table_insert (info->loaded_classes, klass, klass);
        }
+
+       mono_domain_unlock (domain);
        mono_loader_unlock ();
        if (type_load)
-               process_profiler_event (EVENT_KIND_TYPE_LOAD, klass);
+               emit_type_load (klass, klass, NULL);
 }
 
 static void
@@ -3029,37 +3385,15 @@ jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result)
                }
                mono_loader_unlock ();
 
-               if (assembly)
+               if (assembly) {
                        process_profiler_event (EVENT_KIND_ASSEMBLY_LOAD, assembly);
-               else
+               } else {
                        break;
-       }
-
-       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);
        }
 
+       send_type_load (method->klass);
+
        if (!result)
                add_pending_breakpoints (method, jinfo);
 }
@@ -3075,6 +3409,7 @@ typedef struct {
        long il_offset, native_offset;
        guint8 *ip;
        MonoJitInfo *ji;
+       MonoDomain *domain;
 } BreakpointInstance;
 
 /*
@@ -3110,22 +3445,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:
  *
@@ -3133,12 +3452,15 @@ breakpoints_cleanup (void)
  * JI.
  */
 static void
-insert_breakpoint (MonoSeqPointInfo *seq_points, MonoJitInfo *ji, MonoBreakpoint *bp)
+insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo *ji, MonoBreakpoint *bp, MonoError *error)
 {
        int i, count;
        gint32 il_offset = -1, native_offset;
        BreakpointInstance *inst;
 
+       if (error)
+               mono_error_init (error);
+
        native_offset = 0;
        for (i = 0; i < seq_points->len; ++i) {
                il_offset = seq_points->seq_points [i].il_offset;
@@ -3149,14 +3471,22 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoJitInfo *ji, MonoBreakpoint
        }
 
        if (i == seq_points->len) {
-               /* Have to handle this somehow */
-               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);
+               char *s = g_strdup_printf ("Unable to insert breakpoint at %s:%d, seq_points=%d\n", mono_method_full_name (ji->method, TRUE), bp->il_offset, seq_points->len);
+               if (error) {
+                       mono_error_set_error (error, MONO_ERROR_GENERIC, "%s", s);
+                       g_free (s);
+                       return;
+               } else {
+                       g_error ("%s", s);
+                       g_free (s);
+               }
        }
 
        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 ();
 
@@ -3248,7 +3578,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, NULL);
                }
        }
 
@@ -3256,57 +3586,29 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
 }
 
 static void
-set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_points, MonoBreakpoint *bp)
+set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_points, MonoBreakpoint *bp, MonoError *error)
 {
        gpointer code;
        MonoJitInfo *ji;
 
+       if (error)
+               mono_error_init (error);
+
        code = mono_jit_find_compiled_method_with_jit_info (domain, method, &ji);
        if (!code) {
                /* Might be AOTed code */
-               code = mono_aot_get_method (domain, method);
-               g_assert (code);
-               ji = mono_jit_info_table_find (domain, code);
-               g_assert (ji);
-       }
-       g_assert (code);
-
-       insert_breakpoint (seq_points, 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;
-       MonoSeqPointInfo *seq_points = value;
-       SetBpUserData *ud = user_data;
-       MonoBreakpoint *bp = ud->bp;
-       MonoDomain *domain = ud->domain;
+               code = mono_aot_get_method (domain, method);
+               g_assert (code);
+               ji = mono_jit_info_table_find (domain, code);
+               g_assert (ji);
+       }
+       g_assert (code);
 
-       if (bp_matches_method (bp, method))
-               set_bp_in_method (domain, method, seq_points, bp);
+       insert_breakpoint (seq_points, domain, ji, bp, error);
 }
 
 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);
-}
+clear_breakpoint (MonoBreakpoint *bp);
 
 /*
  * set_breakpoint:
@@ -3315,11 +3617,20 @@ set_bp_in_domain (gpointer key, gpointer value, gpointer user_data)
  * METHOD can be NULL, in which case a breakpoint is placed in all methods.
  * METHOD can also be a generic method definition, in which case a breakpoint
  * is placed in all instances of the method.
+ * If ERROR is non-NULL, then it is set and NULL is returnd if some breakpoints couldn't be
+ * inserted.
  */
 static MonoBreakpoint*
-set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req)
+set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req, MonoError *error)
 {
        MonoBreakpoint *bp;
+       GHashTableIter iter, iter2;
+       MonoDomain *domain;
+       MonoMethod *m;
+       MonoSeqPointInfo *seq_points;
+
+       if (error)
+               mono_error_init (error);
 
        // FIXME:
        // - suspend/resume the vm to prevent code patching problems
@@ -3337,7 +3648,18 @@ set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req)
 
        mono_loader_lock ();
 
-       g_hash_table_foreach (domains, set_bp_in_domain, bp);
+       g_hash_table_iter_init (&iter, domains);
+       while (g_hash_table_iter_next (&iter, (void**)&domain, NULL)) {
+               mono_domain_lock (domain);
+
+               g_hash_table_iter_init (&iter2, domain_jit_info (domain)->seq_points);
+               while (g_hash_table_iter_next (&iter2, (void**)&m, (void**)&seq_points)) {
+                       if (bp_matches_method (bp, m))
+                               set_bp_in_method (domain, m, seq_points, bp, error);
+               }
+
+               mono_domain_unlock (domain);
+       }
 
        mono_loader_unlock ();
 
@@ -3345,6 +3667,11 @@ set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req)
        g_ptr_array_add (breakpoints, bp);
        mono_loader_unlock ();
 
+       if (error && !mono_error_ok (error)) {
+               clear_breakpoint (bp);
+               return NULL;
+       }
+
        return bp;
 }
 
@@ -3370,6 +3697,73 @@ clear_breakpoint (MonoBreakpoint *bp)
        g_free (bp);
 }
 
+static void
+breakpoints_cleanup (void)
+{
+       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)
 {
@@ -3471,7 +3865,7 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx)
        /* 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;
+               SingleStepReq *ss_req = req->info;
                gboolean hit = TRUE;
                MonoSeqPointInfo *info;
                SeqPoint *sp;
@@ -3504,7 +3898,7 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx)
                        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);
+               ss_start (ss_req, ji->method, sp, info, ctx, NULL, FALSE);
        }
        
        if (ss_reqs->len > 0)
@@ -3568,7 +3962,11 @@ resume_from_signal_handler (void *sigctx, void *func)
 
        mono_arch_sigctx_to_monoctx (sigctx, &ctx);
        memcpy (&tls->handler_ctx, &ctx, sizeof (MonoContext));
+#ifdef MONO_ARCH_HAVE_SETUP_RESUME_FROM_SIGNAL_HANDLER_CTX
+       mono_arch_setup_resume_sighandler_ctx (&ctx, func);
+#else
        MONO_CONTEXT_SET_IP (&ctx, func);
+#endif
        mono_arch_monoctx_to_sigctx (&ctx, sigctx);
 
 #ifdef PPC_USES_FUNCTION_DESCRIPTOR
@@ -3588,6 +3986,22 @@ mono_debugger_agent_breakpoint_hit (void *sigctx)
        resume_from_signal_handler (sigctx, process_breakpoint);
 }
 
+static const char*
+ss_depth_to_string (StepDepth depth)
+{
+       switch (depth) {
+       case STEP_DEPTH_OVER:
+               return "over";
+       case STEP_DEPTH_OUT:
+               return "out";
+       case STEP_DEPTH_INTO:
+               return "into";
+       default:
+               g_assert_not_reached ();
+               return NULL;
+       }
+}
+
 static void
 process_single_step_inner (DebuggerTlsData *tls, MonoContext *ctx)
 {
@@ -3618,25 +4032,9 @@ process_single_step_inner (DebuggerTlsData *tls, MonoContext *ctx)
                return;
 
        if (log_level > 0) {
-               const char *depth = NULL;
-
                ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
 
-               switch (ss_req->depth) {
-               case STEP_DEPTH_OVER:
-                       depth = "over";
-                       break;
-               case STEP_DEPTH_OUT:
-                       depth = "out";
-                       break;
-               case STEP_DEPTH_INTO:
-                       depth = "into";
-                       break;
-               default:
-                       g_assert_not_reached ();
-               }
-                       
-               DEBUG (1, fprintf (log_file, "[%p] Single step event (depth=%s) at %s (%p), sp %p, last sp %p\n", (gpointer)GetCurrentThreadId (), ss_req->depth == STEP_DEPTH_OVER ? "over" : "out", mono_method_full_name (ji->method, TRUE), MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp));
+               DEBUG (1, fprintf (log_file, "[%p] Single step event (depth=%s) at %s (%p), sp %p, last sp %p\n", (gpointer)GetCurrentThreadId (), ss_depth_to_string (ss_req->depth), mono_method_full_name (ji->method, TRUE), MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp));
        }
 
        /*
@@ -3866,9 +4264,8 @@ ss_stop (SingleStepReq *ss_req)
  *   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)
+ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch)
 {
-       gboolean use_bp = FALSE;
        int i, frame_index;
        SeqPoint *next_sp;
        MonoBreakpoint *bp;
@@ -3879,7 +4276,10 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointI
        /*
         * Implement single stepping using breakpoints if possible.
         */
-       if (ss_req->depth == STEP_DEPTH_OVER) {
+       if (step_to_catch) {
+               bp = set_breakpoint (method, sp->il_offset, ss_req->req, NULL);
+               ss_req->bps = g_slist_append (ss_req->bps, bp);
+       } else if (ss_req->depth == STEP_DEPTH_OVER) {
                frame_index = 1;
                /*
                 * Find the first sequence point in the current or in a previous frame which
@@ -3899,17 +4299,17 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointI
                }
 
                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);
+                               bp = set_breakpoint (method, next_sp->il_offset, ss_req->req, NULL);
                                ss_req->bps = g_slist_append (ss_req->bps, bp);
                        }
                }
        }
 
        if (!ss_req->bps) {
+               DEBUG (1, printf ("[dbg] Turning on global single stepping.\n"));
                ss_req->global = TRUE;
                start_single_stepping ();
        } else {
@@ -3924,9 +4324,11 @@ static ErrorCode
 ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req)
 {
        DebuggerTlsData *tls;
-       MonoSeqPointInfo *info;
+       MonoSeqPointInfo *info = NULL;
        SeqPoint *sp = NULL;
        MonoMethod *method = NULL;
+       MonoDebugMethodInfo *minfo;
+       gboolean step_to_catch = FALSE;
 
        if (suspend_count == 0)
                return ERR_NOT_SUSPENDED;
@@ -3939,6 +4341,8 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ
                return ERR_NOT_IMPLEMENTED;
        }
 
+       DEBUG (1, printf ("[dbg] Starting single step of thread %p (depth=%s).\n", thread, ss_depth_to_string (depth)));
+
        ss_req = g_new0 (SingleStepReq, 1);
        ss_req->req = req;
        ss_req->thread = thread;
@@ -3953,9 +4357,37 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ
        g_assert (tls->has_context);
        ss_req->start_sp = ss_req->last_sp = MONO_CONTEXT_GET_SP (&tls->ctx);
 
-       if (ss_req->size == STEP_SIZE_LINE) {
+       if (tls->has_catch_ctx) {
+               gboolean res;
+               StackFrameInfo frame;
+               MonoContext new_ctx;
+               MonoLMF *lmf = NULL;
+
+               /*
+                * We are stopped at a throw site. Stepping should go to the catch site.
+                */
+
+               /* Find the the jit info for the catch context */
+               res = mono_find_jit_info_ext (mono_domain_get (), thread->jit_data, NULL, &tls->catch_ctx, &new_ctx, NULL, &lmf, NULL, &frame);
+               g_assert (res);
+               g_assert (frame.type == FRAME_TYPE_MANAGED);
+
+               /*
+                * Find the seq point corresponding to the landing site ip, which is the first seq
+                * point after ip.
+                */
+               sp = find_next_seq_point_for_native_offset (frame.domain, frame.method, frame.native_offset, &info);
+               g_assert (sp);
+
+               method = frame.method;
+
+               step_to_catch = TRUE;
+               /* This make sure the seq point is not skipped by process_single_step () */
+               ss_req->last_sp = NULL;
+       }
+
+       if (!step_to_catch && ss_req->size == STEP_SIZE_LINE) {
                StackFrame *frame;
-               MonoDebugMethodInfo *minfo;
 
                /* Compute the initial line info */
                compute_frame_info (thread, tls);
@@ -3977,7 +4409,7 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ
                }
        }
 
-       if (ss_req->depth == STEP_DEPTH_OVER) {
+       if (!step_to_catch && ss_req->depth == STEP_DEPTH_OVER) {
                StackFrame *frame;
 
                compute_frame_info (thread, tls);
@@ -3985,15 +4417,15 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ
                g_assert (tls->frame_count);
                frame = tls->frames [0];
 
-               if (frame->il_offset != -1) {
+               if (!method && 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);
+                       sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info);
                        g_assert (sp);
                        method = frame->method;
                }
        }
 
-       ss_start (ss_req, method, sp, info, NULL, tls);
+       ss_start (ss_req, method, sp, info, NULL, tls, step_to_catch);
 
        return 0;
 }
@@ -4018,10 +4450,10 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx
        GSList *events;
        MonoJitInfo *ji;
        EventInfo ei;
+       DebuggerTlsData *tls = NULL;
 
        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);
@@ -4029,6 +4461,8 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx
 
                if (tls && tls->abort_requested)
                        return;
+               if (tls && tls->disable_breakpoints)
+                       return;
        }
 
        memset (&ei, 0, sizeof (EventInfo));
@@ -4085,7 +4519,73 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx
        events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, &ei, &suspend_policy);
        mono_loader_unlock ();
 
+       if (tls && catch_ctx) {
+               tls->catch_ctx = *catch_ctx;
+               tls->has_catch_ctx = TRUE;
+       }
+
        process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, suspend_policy);
+
+       if (tls)
+               tls->has_catch_ctx = FALSE;
+}
+
+void
+mono_debugger_agent_begin_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx)
+{
+       DebuggerTlsData *tls;
+
+       if (!inited)
+               return;
+
+       tls = TlsGetValue (debugger_tls_id);
+       if (!tls)
+               return;
+
+       /*
+        * We're about to invoke an exception filter during the first pass of exception handling.
+        *
+        * 'ctx' is the context that'll get passed to the filter ('call_filter (ctx, ei->data.filter)'),
+        * 'orig_ctx' is the context where the exception has been thrown.
+        *
+        *
+        * See mcs/class/Mono.Debugger.Soft/Tests/dtest-excfilter.il for an example.
+        *
+        * If we're stopped in Filter(), normal stack unwinding would first unwind to
+        * the call site (line 37) and then continue to Main(), but it would never
+        * include the throw site (line 32).
+        *
+        * Since exception filters are invoked during the first pass of exception handling,
+        * the stack frames of the throw site are still intact, so we should include them
+        * in a stack trace.
+        *
+        * We do this here by saving the context of the throw site in 'tls->filter_ctx'.
+        *
+        * Exception filters are used by MonoDroid, where we want to stop inside a call filter,
+        * but report the location of the 'throw' to the user.
+        *
+        */
+
+       memcpy (&tls->filter_ctx, orig_ctx, sizeof (MonoContext));
+       tls->has_filter_ctx = TRUE;
+
+       tls->filter_lmf = mono_get_lmf ();
+       tls->domain = mono_domain_get ();
+}
+
+void
+mono_debugger_agent_end_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx)
+{
+       DebuggerTlsData *tls;
+
+       if (!inited)
+               return;
+
+       tls = TlsGetValue (debugger_tls_id);
+       if (!tls)
+               return;
+
+       tls->has_filter_ctx = FALSE;
 }
 
 /*
@@ -4531,6 +5031,34 @@ clear_event_requests_for_assembly (MonoAssembly *assembly)
        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)
+{
+       MonoDomain *domain = mono_domain_get ();
+       AgentDomainInfo *info = NULL;
+
+       mono_loader_lock ();
+       info = get_agent_domain_info (domain);
+       g_hash_table_foreach_remove (info->loaded_classes, type_comes_from_assembly, assembly);
+       mono_loader_unlock ();
+}
+
 static void
 add_thread (gpointer key, gpointer value, gpointer user_data)
 {
@@ -4553,7 +5081,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
 
@@ -4648,7 +5176,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;
 
@@ -4670,7 +5198,6 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
                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;
 #elif defined(TARGET_POWERPC)
                ext.lmf.previous_lmf = *(lmf_addr);
                /* Mark that this is a MonoLMFExt */
@@ -4714,7 +5241,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
@@ -4881,9 +5408,10 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                }
                mono_loader_unlock ();
 
-               // FIXME: Count resumes
-               resume_vm ();
+               while (suspend_count > 0)
+                       resume_vm ();
                disconnected = TRUE;
+               vm_start_event_sent = FALSE;
                break;
        case CMD_VM_EXIT: {
                MonoInternalThread *thread;
@@ -5072,6 +5600,7 @@ static ErrorCode
 event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 {
        int err;
+       MonoError error;
 
        switch (command) {
        case CMD_EVENT_REQUEST_SET: {
@@ -5154,7 +5683,13 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                if (req->event_kind == EVENT_KIND_BREAKPOINT) {
                        g_assert (method);
 
-                       req->info = set_breakpoint (method, location, req);
+                       req->info = set_breakpoint (method, location, req, &error);
+                       if (!mono_error_ok (&error)) {
+                               g_free (req);
+                               DEBUG(1, fprintf (log_file, "[dbg] Failed to set breakpoint: %s\n", mono_error_get_message (&error)));
+                               mono_error_cleanup (&error);
+                               return ERR_NO_SEQ_POINT_AT_IL_OFFSET;
+                       }
                } else if (req->event_kind == EVENT_KIND_STEP) {
                        g_assert (step_thread_id);
 
@@ -5170,9 +5705,9 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                                return err;
                        }
                } else if (req->event_kind == EVENT_KIND_METHOD_ENTRY) {
-                       req->info = set_breakpoint (NULL, METHOD_ENTRY_IL_OFFSET, req);
+                       req->info = set_breakpoint (NULL, METHOD_ENTRY_IL_OFFSET, req, NULL);
                } else if (req->event_kind == EVENT_KIND_METHOD_EXIT) {
-                       req->info = set_breakpoint (NULL, METHOD_EXIT_IL_OFFSET, req);
+                       req->info = set_breakpoint (NULL, METHOD_EXIT_IL_OFFSET, req, NULL);
                } else if (req->event_kind == EVENT_KIND_EXCEPTION) {
                } else {
                        if (req->nmodifiers) {
@@ -5672,13 +6207,28 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint
                buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo);
                break;
        }
-       case CMD_TYPE_GET_VALUES: {
+       case CMD_TYPE_GET_VALUES:
+       case CMD_TYPE_GET_VALUES_2: {
                guint8 *val;
                MonoClassField *f;
                MonoVTable *vtable;
                MonoClass *k;
                int len, i;
                gboolean found;
+               MonoThread *thread_obj;
+               MonoInternalThread *thread = NULL;
+               guint32 special_static_type;
+
+               if (command == CMD_TYPE_GET_VALUES_2) {
+                       int objid = decode_objid (p, &p, end);
+                       int err;
+
+                       err = get_object (objid, (MonoObject**)&thread_obj);
+                       if (err)
+                               return err;
+
+                       thread = THREAD_TO_INTERNAL (thread_obj);
+               }
 
                len = decode_int (p, &p, end);
                for (i = 0; i < len; ++i) {
@@ -5688,8 +6238,11 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint
 
                        if (!(f->type->attrs & FIELD_ATTRIBUTE_STATIC))
                                return ERR_INVALID_FIELDID;
-                       if (mono_class_field_is_special_static (f))
-                               return ERR_INVALID_FIELDID;
+                       special_static_type = mono_class_field_get_special_static_type (f);
+                       if (special_static_type != SPECIAL_STATIC_NONE) {
+                               if (!(thread && special_static_type == SPECIAL_STATIC_THREAD))
+                                       return ERR_INVALID_FIELDID;
+                       }
 
                        /* Check that the field belongs to the object */
                        found = FALSE;
@@ -5704,7 +6257,7 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint
 
                        vtable = mono_class_vtable (domain, f->parent);
                        val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
-                       mono_field_static_get_value (vtable, f, val);
+                       mono_field_static_get_value_for_thread (thread ? thread : mono_thread_internal_current (), vtable, f, val);
                        buffer_add_value (buf, f->type, val, domain);
                        g_free (val);
                }
@@ -5927,8 +6480,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
        }
        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);
@@ -5940,27 +6492,39 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
                        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;
        }
        case CMD_METHOD_GET_INFO:
@@ -6021,7 +6585,10 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
 
                        if (handle_class == mono_defaults.typehandle_class) {
                                buffer_add_byte (buf, TOKEN_TYPE_TYPE);
-                               buffer_add_typeid (buf, domain, mono_class_from_mono_type ((MonoType*)val));
+                               if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
+                                       buffer_add_typeid (buf, domain, (MonoClass *) val);
+                               else
+                                       buffer_add_typeid (buf, domain, mono_class_from_mono_type ((MonoType*)val));
                        } else if (handle_class == mono_defaults.fieldhandle_class) {
                                buffer_add_byte (buf, TOKEN_TYPE_FIELD);
                                buffer_add_fieldid (buf, domain, val);
@@ -6132,7 +6699,7 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                buffer_add_int (buf, tls->frame_count);
                for (i = 0; i < tls->frame_count; ++i) {
                        buffer_add_int (buf, tls->frames [i]->id);
-                       buffer_add_methodid (buf, tls->frames [i]->domain, tls->frames [i]->method);
+                       buffer_add_methodid (buf, tls->frames [i]->domain, tls->frames [i]->actual_method);
                        buffer_add_int (buf, tls->frames [i]->il_offset);
                        /*
                         * Instead of passing the frame type directly to the client, we associate
@@ -6154,6 +6721,9 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
        case CMD_THREAD_GET_ID:
                buffer_add_long (buf, (guint64)(gsize)thread);
                break;
+       case CMD_THREAD_GET_TID:
+               buffer_add_long (buf, (guint64)thread->tid);
+               break;
        default:
                return ERR_NOT_IMPLEMENTED;
        }
@@ -6212,12 +6782,12 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
        }
        jit = frame->jit;
 
-       sig = mono_method_signature (frame->method);
+       sig = mono_method_signature (frame->actual_method);
 
        switch (command) {
        case CMD_STACK_FRAME_GET_VALUES: {
                len = decode_int (p, &p, end);
-               header = mono_method_get_header (frame->method);
+               header = mono_method_get_header (frame->actual_method);
 
                for (i = 0; i < len; ++i) {
                        pos = decode_int (p, &p, end);
@@ -6247,12 +6817,12 @@ 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, TRUE);
+                               add_var (buf, &frame->actual_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);
+                               buffer_add_value (buf, &frame->actual_method->klass->byval_arg, &p, frame->domain);
                        } else {
                                add_var (buf, &frame->method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
                        }
@@ -6265,7 +6835,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                MonoDebugVarInfo *var;
 
                len = decode_int (p, &p, end);
-               header = mono_method_get_header (frame->method);
+               header = mono_method_get_header (frame->actual_method);
 
                for (i = 0; i < len; ++i) {
                        pos = decode_int (p, &p, end);
@@ -6548,6 +7118,67 @@ command_set_to_string (CommandSet command_set)
        }
 }
 
+static const char*
+cmd_to_string (CommandSet set, int command)
+{
+       switch (set) {
+       case CMD_SET_VM: {
+               switch (command) {
+               case CMD_VM_VERSION:
+                       return "VERSION";
+               case CMD_VM_ALL_THREADS:
+                       return "ALL_THREADS";
+               case CMD_VM_SUSPEND:
+                       return "SUSPEND";
+               case CMD_VM_RESUME:
+                       return "RESUME";
+               case CMD_VM_EXIT:
+                       return "EXIT";
+               case CMD_VM_DISPOSE:
+                       return "DISPOSE";
+               case CMD_VM_INVOKE_METHOD:
+                       return "INVOKE_METHOD";
+               case CMD_VM_SET_PROTOCOL_VERSION:
+                       return "SET_PROTOCOL_VERSION";
+               case CMD_VM_ABORT_INVOKE:
+                       return "ABORT_INVOKE";
+               default:
+                       break;
+               }
+               break;
+       }
+       default:
+               break;
+       }
+       return NULL;
+}
+
+static gboolean
+wait_for_attach (void)
+{
+       if (listen_fd == -1) {
+               DEBUG (1, fprintf (log_file, "[dbg] Invalid listening socket\n"));
+               return FALSE;
+       }
+
+       /* Block and wait for client connection */
+       conn_fd = transport_accept (listen_fd);
+       DEBUG (1, fprintf (log_file, "Accepted connection on %d\n", conn_fd));
+       if (conn_fd == -1) {
+               DEBUG (1, fprintf (log_file, "[dbg] Bad client connection\n"));
+               return FALSE;
+       }
+       
+       /* Handshake */
+       disconnected = !transport_handshake ();
+       if (disconnected) {
+               DEBUG (1, fprintf (log_file, "Transport handshake failed!\n"));
+               return FALSE;
+       }
+       
+       return TRUE;
+}
+
 /*
  * debugger_thread:
  *
@@ -6563,6 +7194,7 @@ debugger_thread (void *arg)
        Buffer buf;
        ErrorCode err;
        gboolean no_reply;
+       gboolean attach_failed = FALSE;
 
        DEBUG (1, fprintf (log_file, "[dbg] Agent thread started, pid=%p\n", (gpointer)GetCurrentThreadId ()));
 
@@ -6573,8 +7205,18 @@ debugger_thread (void *arg)
        mono_thread_internal_current ()->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
 
        mono_set_is_debugger_attached (TRUE);
-
-       while (TRUE) {
+       
+       if (agent_config.defer) {
+               if (!wait_for_attach ()) {
+                       DEBUG (1, fprintf (log_file, "[dbg] Can't attach, aborting debugger thread.\n"));
+                       attach_failed = TRUE; // Don't abort process when we can't listen
+               } else {
+                       /* Send start event to client */
+                       process_profiler_event (EVENT_KIND_VM_START, mono_thread_get_main ());
+               }
+       }
+       
+       while (!attach_failed) {
                res = recv_length (conn_fd, header, HEADER_LENGTH, 0);
 
                /* This will break if the socket is closed during shutdown too */
@@ -6592,7 +7234,18 @@ debugger_thread (void *arg)
 
                g_assert (flags == 0);
 
-               DEBUG (1, fprintf (log_file, "[dbg] Received command %s(%d), id=%d.\n", command_set_to_string (command_set), command, id));
+               if (log_level) {
+                       const char *cmd_str;
+                       char cmd_num [256];
+
+                       cmd_str = cmd_to_string (command_set, command);
+                       if (!cmd_str) {
+                               sprintf (cmd_num, "%d", command);
+                               cmd_str = cmd_num;
+                       }
+                       
+                       DEBUG (1, fprintf (log_file, "[dbg] Received command %s(%s), id=%d.\n", command_set_to_string (command_set), cmd_str, id));
+               }
 
                data = g_malloc (len - HEADER_LENGTH);
                if (len - HEADER_LENGTH > 0)
@@ -6666,14 +7319,24 @@ debugger_thread (void *arg)
        }
 
        mono_set_is_debugger_attached (FALSE);
+       
+#ifdef TARGET_WIN32
+       if (! (vm_death_event_sent || mono_runtime_is_shutting_down ())) 
+#endif
+       {
+               mono_mutex_lock (&debugger_thread_exited_mutex);
+               debugger_thread_exited = TRUE;
+               mono_cond_signal (&debugger_thread_exited_cond);
+               mono_mutex_unlock (&debugger_thread_exited_mutex);
 
-       mono_mutex_lock (&debugger_thread_exited_mutex);
-       debugger_thread_exited = TRUE;
-       mono_cond_signal (&debugger_thread_exited_cond);
-       mono_mutex_unlock (&debugger_thread_exited_mutex);
-
-       DEBUG (1, printf ("[dbg] Debugger thread exited.\n"));
-
+               DEBUG (1, printf ("[dbg] Debugger thread exited.\n"));
+               
+               if (!attach_failed && command_set == CMD_SET_VM && command == CMD_VM_DISPOSE && !(vm_death_event_sent || mono_runtime_is_shutting_down ())) {
+                       DEBUG (2, fprintf (log_file, "[dbg] Detached - restarting clean debugger thread.\n"));
+                       start_debugger_thread ();
+               }
+       }
+       
        return 0;
 }
 
@@ -6682,7 +7345,7 @@ debugger_thread (void *arg)
 void
 mono_debugger_agent_parse_options (char *options)
 {
-       g_error ("This runtime is configure with the debugger agent disabled.");
+       g_error ("This runtime is configured with the debugger agent disabled.");
 }
 
 void
@@ -6716,5 +7379,16 @@ mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *throw_ctx
                                                                          MonoContext *catch_ctx)
 {
 }
+
+void
+mono_debugger_agent_begin_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx)
+{
+}
+
+void
+mono_debugger_agent_end_exception_filter (MonoException *exc, MonoContext *ctx, MonoContext *orig_ctx)
+{
+}
+
 #endif