Introduce MonoThreadUnwindState and a bunch overloads to mono_walk_stack.
[mono.git] / mono / mini / debugger-agent.c
index 58e426a969ba649a86b866f5651c0539aa387be2..7a8a42b81df0f2e648372b9e374e11388d99b198 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,
@@ -281,7 +303,9 @@ typedef enum {
        ERR_NOT_SUSPENDED = 101,
        ERR_INVALID_ARGUMENT = 102,
        ERR_UNLOADED = 103,
-       ERR_NO_INVOCATION = 104
+       ERR_NO_INVOCATION = 104,
+       ERR_ABSENT_INFORMATION = 105,
+       ERR_NO_SEQ_POINT_AT_IL_OFFSET = 106
 } ErrorCode;
 
 typedef enum {
@@ -342,7 +366,9 @@ 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,
+       CMD_THREAD_GET_TID = 6
 } CmdThread;
 
 typedef enum {
@@ -401,7 +427,9 @@ 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,
+       CMD_TYPE_GET_VALUES_2 = 14
 } CmdType;
 
 typedef enum {
@@ -500,6 +528,7 @@ static AgentConfig agent_config;
 static gint32 inited;
 
 static int conn_fd;
+static int listen_fd;
 
 static int packet_id = 0;
 
@@ -530,10 +559,9 @@ 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;
@@ -564,6 +592,9 @@ 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);
@@ -572,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);
@@ -602,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);
@@ -614,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);
 
@@ -648,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");
 }
 
@@ -682,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 ++) {
@@ -713,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);
@@ -764,20 +823,23 @@ 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 ();
+       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) {
@@ -864,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);
@@ -929,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:
  *
@@ -937,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);
@@ -952,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 */
@@ -961,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;
@@ -982,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));
@@ -1009,6 +1110,7 @@ transport_connect (const char *host, int port)
                                res = listen (sfd, 16);
                                if (res == -1)
                                        continue;
+                               listen_fd = sfd;
                                break;
                        }
 
@@ -1019,10 +1121,15 @@ 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
                }
 
+               if (agent_config.defer)
+                       return;
+
                DEBUG (1, fprintf (log_file, "Listening on %s:%d (timeout=%d ms)...\n", host, port, agent_config.timeout));
 
                if (agent_config.timeout) {
@@ -1040,15 +1147,17 @@ transport_connect (const char *host, int port)
                        }
                }
 
-               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,
@@ -1062,54 +1171,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
@@ -1126,6 +1213,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)
 {
@@ -1379,26 +1520,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);
@@ -1406,7 +1563,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 ();
 
@@ -1515,6 +1678,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 */
@@ -1554,6 +1719,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);
        }
 
@@ -1569,6 +1735,43 @@ mono_debugger_agent_free_domain_info (MonoDomain *domain)
                        }
                }
        }
+
+       mono_loader_lock ();
+       g_hash_table_remove (domains, 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
@@ -1584,9 +1787,8 @@ get_id (MonoDomain *domain, IdType type, gpointer val)
 
        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;
+       info = get_agent_domain_info (domain);
+
        if (info->val_to_id [type] == NULL)
                info->val_to_id [type] = g_hash_table_new (mono_aligned_addr_hash, NULL);
 
@@ -1873,7 +2075,7 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
 
        if (ji) {
                /* Running managed code, will be suspended by the single step code */
-               DEBUG (1, printf ("[%p] Received interrupt while at %s(%p), continuing.\n", (gpointer)GetCurrentThreadId (), ji->method->name, mono_arch_ip_from_context (sigctx)));
+               DEBUG (1, fprintf (log_file, "[%p] Received interrupt while at %s(%p), continuing.\n", (gpointer)GetCurrentThreadId (), ji->method->name, mono_arch_ip_from_context (sigctx)));
                return TRUE;
        } else {
                /* 
@@ -1888,7 +2090,8 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
 
                        // 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, fprintf (log_file, "[%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)
@@ -1908,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));
@@ -1991,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
 }
 
@@ -2001,6 +2204,15 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx)
        guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
        MonoJitInfo *ji;
 
+       if (mono_loader_lock_is_owned_by_self ()) {
+               /*
+                * Shortcut for the check in suspend_current (). This speeds up processing
+                * when executing long running code inside the loader lock, i.e. assembly load
+                * hooks.
+                */
+               return;
+       }
+
        if (debugger_thread_id == GetCurrentThreadId ())
                return;
 
@@ -2197,7 +2409,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
@@ -2323,6 +2535,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:
  *
@@ -2394,7 +2658,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) {
@@ -2409,6 +2673,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;
@@ -2418,7 +2683,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) {
                /*
@@ -2432,7 +2697,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;
@@ -2444,6 +2711,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)
 {
@@ -2466,9 +2752,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;
@@ -2507,6 +2805,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
  */
@@ -2631,30 +2978,48 @@ 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 ();
+       } else {
+               if (debugger_thread_id == GetCurrentThreadId () && event != EVENT_KIND_VM_DEATH)
+                       // FIXME: Send these with a NULL thread, don't suspend the current thread
+                       return;
+       }
 
        buffer_init (&buf, 128);
        buffer_add_byte (&buf, suspend_policy);
@@ -2664,12 +3029,11 @@ 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 (!thread)
+                       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
 
@@ -2714,12 +3078,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;
        }
 
@@ -2736,17 +3100,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:
@@ -2779,7 +3152,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)
@@ -2790,7 +3165,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;
@@ -2831,7 +3206,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);
 
@@ -2852,7 +3227,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;
@@ -2880,14 +3255,23 @@ thread_end (MonoProfiler *prof, intptr_t 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)
 {
+       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);
 }
 
@@ -2918,6 +3302,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
@@ -2951,7 +3336,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;
 
        /*
@@ -2974,6 +3359,29 @@ end_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
        mono_loader_unlock ();
 }
 
+static void
+send_type_load (MonoClass *klass)
+{
+       gboolean type_load = FALSE;
+       MonoDomain *domain = mono_domain_get ();
+       AgentDomainInfo *info = NULL;
+
+       mono_loader_lock ();
+       mono_domain_lock (domain);
+
+       info = get_agent_domain_info (domain);
+
+       if (!g_hash_table_lookup (info->loaded_classes, klass)) {
+               type_load = TRUE;
+               g_hash_table_insert (info->loaded_classes, klass, klass);
+       }
+
+       mono_domain_unlock (domain);
+       mono_loader_unlock ();
+       if (type_load)
+               emit_type_load (klass, klass, NULL);
+}
+
 static void
 jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result)
 {
@@ -2983,8 +3391,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;
 
@@ -2996,20 +3402,14 @@ 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;
+               }
        }
 
-       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);
-       }
-       mono_loader_unlock ();
-       if (type_load)
-               process_profiler_event (EVENT_KIND_TYPE_LOAD, method->klass);
+       send_type_load (method->klass);
 
        if (!result)
                add_pending_breakpoints (method, jinfo);
@@ -3026,6 +3426,7 @@ typedef struct {
        long il_offset, native_offset;
        guint8 *ip;
        MonoJitInfo *ji;
+       MonoDomain *domain;
 } BreakpointInstance;
 
 /*
@@ -3061,22 +3462,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:
  *
@@ -3084,12 +3469,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, native_offset;
+       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;
@@ -3099,14 +3487,23 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoJitInfo *ji, MonoBreakpoint
                        break;
        }
 
-       if (i == seq_points->len)
-               /* Have to handle this somehow */
-               NOT_IMPLEMENTED;
+       if (i == 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 ();
 
@@ -3198,7 +3595,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);
                }
        }
 
@@ -3206,11 +3603,14 @@ 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 */
@@ -3221,20 +3621,11 @@ set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_
        }
        g_assert (code);
 
-       insert_breakpoint (seq_points, ji, bp);
+       insert_breakpoint (seq_points, domain, ji, bp, error);
 }
 
 static void
-set_bp_in_method_cb (gpointer key, gpointer value, gpointer user_data)
-{
-       MonoMethod *method = key;
-       MonoSeqPointInfo *seq_points = value;
-       MonoBreakpoint *bp = user_data;
-       MonoDomain *domain = mono_domain_get ();
-
-       if (bp_matches_method (bp, method))
-               set_bp_in_method (domain, method, seq_points, bp);
-}
+clear_breakpoint (MonoBreakpoint *bp);
 
 /*
  * set_breakpoint:
@@ -3243,14 +3634,22 @@ set_bp_in_method_cb (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)
 {
-       MonoDomain *domain;
        MonoBreakpoint *bp;
+       GHashTableIter iter, iter2;
+       MonoDomain *domain;
+       MonoMethod *m;
+       MonoSeqPointInfo *seq_points;
 
-       // FIXME: 
+       if (error)
+               mono_error_init (error);
+
+       // FIXME:
        // - suspend/resume the vm to prevent code patching problems
        // - multiple breakpoints on the same location
        // - dynamic methods
@@ -3264,38 +3663,122 @@ set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req)
 
        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);
-       g_hash_table_foreach (domain_jit_info (domain)->seq_points, set_bp_in_method_cb, bp);
-       mono_domain_unlock (domain);
+       mono_loader_lock ();
+
+       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 ();
 
        mono_loader_lock ();
        g_ptr_array_add (breakpoints, bp);
        mono_loader_unlock ();
 
-       return bp;
+       if (error && !mono_error_ok (error)) {
+               clear_breakpoint (bp);
+               return NULL;
+       }
+
+       return bp;
+}
+
+static void
+clear_breakpoint (MonoBreakpoint *bp)
+{
+       int i;
+
+       // FIXME: locking, races
+       for (i = 0; i < bp->children->len; ++i) {
+               BreakpointInstance *inst = g_ptr_array_index (bp->children, i);
+
+               remove_breakpoint (inst);
+
+               g_free (inst);
+       }
+
+       mono_loader_lock ();
+       g_ptr_array_remove (breakpoints, bp);
+       mono_loader_unlock ();
+
+       g_ptr_array_free (bp->children, TRUE);
+       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_breakpoint (MonoBreakpoint *bp)
+clear_breakpoints_for_domain (MonoDomain *domain)
 {
-       int i;
+       int i, j;
 
-       // FIXME: locking, races
-       for (i = 0; i < bp->children->len; ++i) {
-               BreakpointInstance *inst = g_ptr_array_index (bp->children, i);
+       /* This could be called after shutdown */
+       if (!breakpoints)
+               return;
 
-               remove_breakpoint (inst);
+       mono_loader_lock ();
+       for (i = 0; i < breakpoints->len; ++i) {
+               MonoBreakpoint *bp = g_ptr_array_index (breakpoints, i);
 
-               g_free (inst);
-       }
+               j = 0;
+               while (j < bp->children->len) {
+                       BreakpointInstance *inst = g_ptr_array_index (bp->children, j);
 
-       mono_loader_lock ();
-       g_ptr_array_remove (breakpoints, bp);
-       mono_loader_unlock ();
+                       if (inst->domain == domain) {
+                               remove_breakpoint (inst);
 
-       g_ptr_array_free (bp->children, TRUE);
-       g_free (bp);
+                               g_free (inst);
+
+                               g_ptr_array_remove_index_fast (bp->children, j);
+                       } else {
+                               j ++;
+                       }
+               }
+       }
+       mono_loader_unlock ();
 }
 
 static gboolean
@@ -3399,7 +3882,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;
@@ -3432,7 +3915,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)
@@ -3496,7 +3979,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
@@ -3516,6 +4003,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)
 {
@@ -3546,25 +4049,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));
        }
 
        /*
@@ -3794,9 +4281,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;
@@ -3807,7 +4293,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
@@ -3827,17 +4316,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, fprintf (log_file, "[dbg] Turning on global single stepping.\n"));
                ss_req->global = TRUE;
                start_single_stepping ();
        } else {
@@ -3852,9 +4341,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;
@@ -3863,10 +4354,12 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ
 
        // FIXME: Multiple requests
        if (ss_req) {
-               DEBUG (0, printf ("Received a single step request while the previous one was still active.\n"));
+               DEBUG (0, fprintf (log_file, "Received a single step request while the previous one was still active.\n"));
                return ERR_NOT_IMPLEMENTED;
        }
 
+       DEBUG (1, fprintf (log_file, "[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;
@@ -3881,9 +4374,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);
@@ -3905,7 +4426,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);
@@ -3913,15 +4434,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;
 }
@@ -3946,10 +4467,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);
@@ -3957,6 +4478,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));
@@ -4013,7 +4536,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;
 }
 
 /*
@@ -4459,6 +5048,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)
 {
@@ -4481,7 +5098,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
 
@@ -4490,7 +5107,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
                 * Invoke this method directly, currently only Environment.Exit () is supported.
                 */
                this = NULL;
-               DEBUG (1, printf ("[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (invoke->method, TRUE), this ? this->vtable->klass->name : "<null>"));
+               DEBUG (1, fprintf (log_file, "[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (invoke->method, TRUE), this ? this->vtable->klass->name : "<null>"));
                mono_runtime_invoke (invoke->method, NULL, invoke->args, &exc);
                g_assert_not_reached ();
        }
@@ -4521,7 +5138,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
        else
                this = NULL;
 
-       DEBUG (1, printf ("[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (m, TRUE), this ? this->vtable->klass->name : "<null>"));
+       DEBUG (1, fprintf (log_file, "[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (m, TRUE), this ? this->vtable->klass->name : "<null>"));
 
        if (this && this->vtable->domain != domain)
                NOT_IMPLEMENTED;
@@ -4576,7 +5193,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;
 
@@ -4598,7 +5215,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 */
@@ -4633,8 +5249,15 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
                } else if (MONO_TYPE_IS_REFERENCE (sig->ret)) {
                        buffer_add_value (buf, sig->ret, &res, domain);
                } else if (mono_class_from_mono_type (sig->ret)->valuetype) {
-                       g_assert (res);
-                       buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain);
+                       if (mono_class_is_nullable (mono_class_from_mono_type (sig->ret))) {
+                               if (!res)
+                                       buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &res, domain);
+                               else
+                                       buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain);
+                       } else {
+                               g_assert (res);
+                               buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain);
+                       }
                } else {
                        NOT_IMPLEMENTED;
                }
@@ -4642,7 +5265,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
@@ -4716,7 +5339,7 @@ invoke_method (void)
                tls->resume_count -= invoke->suspend_count;
        }
 
-       DEBUG (1, printf ("[%p] Invoke finished, resume_count = %d.\n", (gpointer)GetCurrentThreadId (), tls->resume_count));
+       DEBUG (1, fprintf (log_file, "[%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:
@@ -4809,9 +5432,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;
@@ -4848,15 +5472,14 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                wait_for_suspend ();
 
                env_class = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
-               g_assert (env_class);
-               exit_method = mono_class_get_method_from_name (env_class, "Exit", 1);
-               g_assert (exit_method);
+               if (env_class)
+                       exit_method = mono_class_get_method_from_name (env_class, "Exit", 1);
 
                mono_loader_lock ();
                thread = mono_g_hash_table_find (tid_to_thread, is_really_suspended, NULL);
                mono_loader_unlock ();
 
-               if (thread) {
+               if (thread && exit_method) {
                        mono_loader_lock ();
                        tls = mono_g_hash_table_lookup (thread_to_tls, thread);
                        mono_loader_unlock ();
@@ -5000,6 +5623,7 @@ static ErrorCode
 event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 {
        int err;
+       MonoError error;
 
        switch (command) {
        case CMD_EVENT_REQUEST_SET: {
@@ -5052,6 +5676,7 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                                        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;
 
@@ -5081,7 +5706,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);
 
@@ -5097,9 +5728,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) {
@@ -5445,10 +6076,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;
@@ -5456,10 +6085,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);
@@ -5508,6 +6133,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);
@@ -5603,13 +6230,28 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                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) {
@@ -5619,8 +6261,11 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
                        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;
@@ -5635,7 +6280,7 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
                        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);
                }
@@ -5693,7 +6338,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;
@@ -5722,9 +6368,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);
@@ -5749,17 +6399,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);
@@ -5837,8 +6503,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);
@@ -5850,27 +6515,39 @@ 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;
        }
        case CMD_METHOD_GET_INFO:
@@ -5931,7 +6608,10 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
                        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);
@@ -5960,6 +6640,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)
 {
@@ -6019,7 +6722,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
@@ -6038,6 +6741,12 @@ 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;
+       case CMD_THREAD_GET_TID:
+               buffer_add_long (buf, (guint64)thread->tid);
+               break;
        default:
                return ERR_NOT_IMPLEMENTED;
        }
@@ -6090,16 +6799,18 @@ 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;
 
-       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);
@@ -6129,12 +6840,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);
                        }
@@ -6147,7 +6858,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);
@@ -6430,6 +7141,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:
  *
@@ -6445,6 +7217,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 ()));
 
@@ -6455,8 +7228,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 */
@@ -6474,7 +7257,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)
@@ -6548,14 +7342,19 @@ debugger_thread (void *arg)
        }
 
        mono_set_is_debugger_attached (FALSE);
-
+       
        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, fprintf (log_file, "[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;
 }
 
@@ -6564,7 +7363,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
@@ -6598,5 +7397,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