Merge pull request #273 from joncham/bug-getpid
[mono.git] / mono / mini / debugger-agent.c
index b2c4572e5bae971fa7090d8ee5f2024da6b621c5..9eb0c294976101e19a897b123a68ea96c221124d 100644 (file)
@@ -69,16 +69,30 @@ int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD,
 #include <mono/metadata/mono-debug-debugger.h>
 #include <mono/metadata/debug-mono-symfile.h>
 #include <mono/metadata/gc-internal.h>
+#include <mono/metadata/environment.h>
 #include <mono/metadata/threads-types.h>
 #include <mono/metadata/socket-io.h>
 #include <mono/metadata/assembly.h>
+#include <mono/metadata/runtime.h>
+#include <mono/metadata/threadpool.h>
+#include <mono/metadata/verify-internals.h>
 #include <mono/utils/mono-semaphore.h>
 #include <mono/utils/mono-error-internals.h>
 #include <mono/utils/mono-stack-unwinding.h>
 #include <mono/utils/mono-time.h>
+#include <mono/utils/mono-threads.h>
 #include "debugger-agent.h"
 #include "mini.h"
 
+/*
+On iOS we can't use System.Environment.Exit () as it will do the wrong
+shutdown sequence.
+*/
+#if !defined (TARGET_IOS)
+#define TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT
+#endif
+
+
 #ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
 #define DISABLE_DEBUGGER_AGENT 1
 #endif
@@ -87,13 +101,9 @@ int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD,
 #define DISABLE_DEBUGGER_AGENT 1
 #endif
 
-#if defined(__MACH__)
-#include <mono/utils/mono-threads.h>
-#endif
-
 #ifndef DISABLE_DEBUGGER_AGENT
 
-#include <mono/io-layer/mono-mutex.h>
+#include <mono/utils/mono-mutex.h>
 
 /* Definitions to make backporting to 2.6 easier */
 //#define MonoInternalThread MonoThread
@@ -115,6 +125,7 @@ typedef struct {
        gboolean embedding;
        gboolean defer;
        int keepalive;
+       gboolean setpgid;
 } AgentConfig;
 
 typedef struct
@@ -128,8 +139,14 @@ typedef struct
         * method.
         */
        MonoMethod *actual_method;
+       /*
+        * This is the method which is visible to debugger clients. Same as method,
+        * except for native-to-managed wrappers.
+        */
+       MonoMethod *api_method;
        MonoContext ctx;
        MonoDebugMethodJitInfo *jit;
+       MonoJitInfo *ji;
        int flags;
        mgreg_t *reg_locations [MONO_MAX_IREGS];
        /*
@@ -156,6 +173,7 @@ struct _InvokeData
        MonoMethod *method;
        gpointer *args;
        guint32 suspend_count;
+       int nmethods;
 
        InvokeData *last_invoke;
 };
@@ -163,7 +181,6 @@ struct _InvokeData
 typedef struct {
        MonoThreadUnwindState context;
 
-       gpointer resume_event;
        /* This is computed on demand when it is requested using the wire protocol */
        /* It is freed up when the thread is resumed */
        int frame_count;
@@ -242,9 +259,7 @@ typedef struct {
         * The context where single stepping should resume while the thread is suspended because
         * of an EXCEPTION event.
         */
-       MonoContext catch_ctx;
-
-       gboolean has_catch_ctx;
+       MonoThreadUnwindState catch_state;
 
        /*
         * The context which needs to be restored after handling a single step/breakpoint
@@ -270,7 +285,7 @@ typedef struct {
 #define HEADER_LENGTH 11
 
 #define MAJOR_VERSION 2
-#define MINOR_VERSION 17
+#define MINOR_VERSION 29
 
 typedef enum {
        CMD_SET_VM = 1,
@@ -285,6 +300,7 @@ typedef enum {
        CMD_SET_METHOD = 22,
        CMD_SET_TYPE = 23,
        CMD_SET_MODULE = 24,
+       CMD_SET_FIELD = 25,
        CMD_SET_EVENT = 64
 } CommandSet;
 
@@ -353,7 +369,9 @@ typedef enum {
 
 typedef enum {
        STEP_FILTER_NONE = 0,
-       STEP_FILTER_STATIC_CTOR = 1
+       STEP_FILTER_STATIC_CTOR = 1,
+       STEP_FILTER_DEBUGGER_HIDDEN = 2,
+       STEP_FILTER_DEBUGGER_STEP_THROUGH = 4
 } StepFilter;
 
 typedef enum {
@@ -395,7 +413,8 @@ typedef enum {
        CMD_VM_ABORT_INVOKE = 9,
        CMD_VM_SET_KEEPALIVE = 10,
        CMD_VM_GET_TYPES_FOR_SOURCE_FILE = 11,
-       CMD_VM_GET_TYPES = 12
+       CMD_VM_GET_TYPES = 12,
+       CMD_VM_INVOKE_METHODS = 13
 } CmdVM;
 
 typedef enum {
@@ -404,7 +423,8 @@ typedef enum {
        CMD_THREAD_GET_STATE = 3,
        CMD_THREAD_GET_INFO = 4,
        CMD_THREAD_GET_ID = 5,
-       CMD_THREAD_GET_TID = 6
+       CMD_THREAD_GET_TID = 6,
+       CMD_THREAD_SET_IP = 7
 } CmdThread;
 
 typedef enum {
@@ -440,6 +460,10 @@ typedef enum {
        CMD_MODULE_GET_INFO = 1,
 } CmdModule;
 
+typedef enum {
+       CMD_FIELD_GET_INFO = 1,
+} CmdField;
+
 typedef enum {
        CMD_METHOD_GET_NAME = 1,
        CMD_METHOD_GET_DECLARING_TYPE = 2,
@@ -449,6 +473,8 @@ typedef enum {
        CMD_METHOD_GET_INFO = 6,
        CMD_METHOD_GET_BODY = 7,
        CMD_METHOD_RESOLVE_TOKEN = 8,
+       CMD_METHOD_GET_CATTRS = 9,
+       CMD_METHOD_MAKE_GENERIC_METHOD = 10
 } CmdMethod;
 
 typedef enum {
@@ -469,6 +495,7 @@ typedef enum {
        CMD_TYPE_GET_METHODS_BY_NAME_FLAGS = 15,
        CMD_TYPE_GET_INTERFACES = 16,
        CMD_TYPE_GET_INTERFACE_MAP = 17,
+       CMD_TYPE_IS_INITIALIZED = 18
 } CmdType;
 
 typedef enum {
@@ -510,7 +537,7 @@ typedef struct {
                GHashTable *type_names; /* For kind == MONO_KIND_TYPE_NAME_ONLY */
                StepFilter filter; /* For kind == MOD_KIND_STEP */
        } data;
-       gboolean caught, uncaught; /* For kind == MOD_KIND_EXCEPTION_ONLY */
+       gboolean caught, uncaught, subclasses; /* For kind == MOD_KIND_EXCEPTION_ONLY */
 } Modifier;
 
 typedef struct{
@@ -539,6 +566,8 @@ typedef struct {
        gboolean global;
        /* The list of breakpoints used to implement step-over */
        GSList *bps;
+       /* The number of frames at the start of a step-over */
+       int nframes;
 } SingleStepReq;
 
 /*
@@ -661,6 +690,9 @@ static gboolean protocol_version_set;
 /* A hash table containing all active domains */
 static GHashTable *domains;
 
+/* The number of times the runtime is suspended */
+static gint32 suspend_count;
+
 static void transport_init (void);
 static void transport_connect (const char *address);
 static gboolean transport_handshake (void);
@@ -739,6 +771,8 @@ static void finish_agent_init (gboolean on_startup);
 
 static void process_profiler_event (EventKind event, gpointer arg);
 
+static void invalidate_frames (DebuggerTlsData *tls);
+
 #ifndef DISABLE_SOCKET_TRANSPORT
 static void
 register_socket_transport (void);
@@ -774,6 +808,7 @@ print_usage (void)
        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, "  keepalive=<n>\t\t\tSend keepalive events every n milliseconds.\n");
+       fprintf (stderr, "  setpgid=y/n\t\t\tWhether to call setpid(0, 0) after startup.\n");
        fprintf (stderr, "  help\t\t\t\tPrint this help.\n");
 }
 
@@ -797,14 +832,14 @@ mono_debugger_agent_parse_options (char *options)
        char **args, **ptr;
        char *host;
        int port;
-       char *extra;
+       const char *extra;
 
 #ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
        fprintf (stderr, "--debugger-agent is not supported on this platform.\n");
        exit (1);
 #endif
 
-       extra = getenv ("MONO_SDB_ENV_OPTIONS");
+       extra = g_getenv ("MONO_SDB_ENV_OPTIONS");
        if (extra)
                options = g_strdup_printf ("%s,%s", options, extra);
 
@@ -848,6 +883,8 @@ mono_debugger_agent_parse_options (char *options)
                        agent_config.embedding = atoi (arg + 10) == 1;
                } else if (strncmp (arg, "keepalive=", 10) == 0) {
                        agent_config.keepalive = atoi (arg + 10);
+               } else if (strncmp (arg, "setpgid=", 8) == 0) {
+                       agent_config.setpgid = parse_flag ("setpgid", arg + 8);
                } else {
                        print_usage ();
                        exit (1);
@@ -896,7 +933,7 @@ mono_debugger_agent_init (void)
 
        event_requests = g_ptr_array_new ();
 
-       mono_mutex_init (&debugger_thread_exited_mutex, NULL);
+       mono_mutex_init (&debugger_thread_exited_mutex);
        mono_cond_init (&debugger_thread_exited_cond, NULL);
 
        mono_profiler_install ((MonoProfiler*)&debugger_profiler, runtime_shutdown);
@@ -961,13 +998,13 @@ mono_debugger_agent_init (void)
         */
        mini_get_debug_options ()->load_aot_jit_info_eagerly = TRUE;
 
+#ifdef HAVE_SETPGID
+       if (agent_config.setpgid)
+               setpgid (0, 0);
+#endif
+
        if (!agent_config.onuncaught && !agent_config.onthrow)
                finish_agent_init (TRUE);
-
-       /* FIXME: Is this still needed ? */
-#if defined(__MACH__)
-       mono_thread_info_disable_new_interrupt (TRUE);
-#endif
 }
 
 /*
@@ -1046,15 +1083,30 @@ socket_transport_recv (void *buf, int len)
        int total = 0;
        int fd = conn_fd;
        int flags = 0;
+       static gint32 last_keepalive;
+       gint32 msecs;
 
        do {
        again:
                res = recv (fd, (char *) buf + total, len - total, flags);
                if (res > 0)
                        total += res;
-               if (agent_config.keepalive && res == -1 && get_last_sock_error () == MONO_EWOULDBLOCK) {
-                       process_profiler_event (EVENT_KIND_KEEPALIVE, NULL);
-                       goto again;
+               if (agent_config.keepalive) {
+                       gboolean need_keepalive = FALSE;
+                       if (res == -1 && get_last_sock_error () == MONO_EWOULDBLOCK) {
+                               need_keepalive = TRUE;
+                       } else if (res == -1) {
+                               /* This could happen if recv () is interrupted repeatedly */
+                               msecs = mono_msec_ticks ();
+                               if (msecs - last_keepalive >= agent_config.keepalive) {
+                                       need_keepalive = TRUE;
+                                       last_keepalive = msecs;
+                               }
+                       }
+                       if (need_keepalive) {
+                               process_profiler_event (EVENT_KIND_KEEPALIVE, NULL);
+                               goto again;
+                       }
                }
        } while ((res > 0 && total < len) || (res == -1 && get_last_sock_error () == MONO_EINTR));
        return total;
@@ -1070,7 +1122,7 @@ set_keepalive (void)
        struct timeval tv;
        int result;
 
-       if (!agent_config.keepalive)
+       if (!agent_config.keepalive || !conn_fd)
                return;
 
        tv.tv_sec = agent_config.keepalive / 1000;
@@ -1298,8 +1350,7 @@ socket_transport_connect (const char *address)
 #endif
        }
        
-       disconnected = !transport_handshake ();
-       if (disconnected)
+       if (!transport_handshake ())
                exit (1);
 }
 
@@ -1359,6 +1410,15 @@ static DebuggerTransport *transport;
 static DebuggerTransport transports [MAX_TRANSPORTS];
 static int ntransports;
 
+void
+mono_debugger_agent_register_transport (DebuggerTransport *trans);
+
+void
+mono_debugger_agent_register_transport (DebuggerTransport *trans)
+{
+       register_transport (trans);
+}
+
 static void
 register_transport (DebuggerTransport *trans)
 {
@@ -1421,6 +1481,12 @@ transport_recv (void *buf, int len)
        return transport->recv (buf, len);
 }
 
+gboolean
+mono_debugger_agent_transport_handshake (void)
+{
+       return transport_handshake ();
+}
+
 static gboolean
 transport_handshake (void)
 {
@@ -1428,6 +1494,8 @@ transport_handshake (void)
        guint8 buf [128];
        int res;
        
+       disconnected = TRUE;
+       
        /* Write handshake message */
        sprintf (handshake_msg, "DWP-Handshake");
        do {
@@ -1456,7 +1524,7 @@ transport_handshake (void)
         * Set TCP_NODELAY on the socket so the client receives events/command
         * results immediately.
         */
-       {
+       if (conn_fd) {
                int flag = 1;
                int result = setsockopt (conn_fd,
                                  IPPROTO_TCP,
@@ -1469,6 +1537,7 @@ transport_handshake (void)
        set_keepalive ();
 #endif
        
+       disconnected = FALSE;
        return TRUE;
 }
 
@@ -1513,9 +1582,7 @@ stop_debugger_thread (void)
 static void
 start_debugger_thread (void)
 {
-       gsize tid;
-
-       debugger_thread_handle = mono_create_thread (NULL, 0, debugger_thread, NULL, 0, &tid);
+       debugger_thread_handle = mono_threads_create_thread (debugger_thread, NULL, 0, 0, NULL);
        g_assert (debugger_thread_handle);
 }
 
@@ -1769,6 +1836,7 @@ objrefs_cleanup (void)
 }
 
 static GHashTable *obj_to_objref;
+static MonoGHashTable *suspended_objs;
 
 /*
  * Return an ObjRef for OBJ.
@@ -1785,8 +1853,18 @@ get_objref (MonoObject *obj)
 
        mono_loader_lock ();
 
-       if (!obj_to_objref)
+       if (!obj_to_objref) {
                obj_to_objref = g_hash_table_new (NULL, NULL);
+               suspended_objs = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC);
+               MONO_GC_REGISTER_ROOT_FIXED (suspended_objs);
+       }
+
+       if (suspend_count) {
+               /*
+                * Have to keep object refs created during suspensions alive for the duration of the suspension, so GCs during invokes don't collect them.
+                */
+               mono_g_hash_table_insert (suspended_objs, obj, NULL);
+       }
        
        /* FIXME: The tables can grow indefinitely */
 
@@ -1833,6 +1911,20 @@ get_objref (MonoObject *obj)
        return ref;
 }
 
+static gboolean
+true_pred (gpointer key, gpointer value, gpointer user_data)
+{
+       return TRUE;
+}
+
+static void
+clear_suspended_objs (void)
+{
+       mono_loader_lock ();
+       mono_g_hash_table_foreach_remove (suspended_objs, true_pred, NULL);
+       mono_loader_unlock ();
+}
+
 static inline int
 get_objid (MonoObject *obj)
 {
@@ -2113,6 +2205,7 @@ decode_ptr_id (guint8 *buf, guint8 **endbuf, guint8 *limit, IdType type, MonoDom
        mono_loader_unlock ();
 
        if (res->domain == NULL) {
+               DEBUG (0, fprintf (log_file, "ERR_UNLOADED, id=%d, type=%d.\n", id, type));
                *err = ERR_UNLOADED;
                return NULL;
        }
@@ -2132,7 +2225,17 @@ buffer_add_ptr_id (Buffer *buf, MonoDomain *domain, IdType type, gpointer val)
 static inline MonoClass*
 decode_typeid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
 {
-       return decode_ptr_id (buf, endbuf, limit, ID_TYPE, domain, err);
+       MonoClass *klass;
+
+       klass = decode_ptr_id (buf, endbuf, limit, ID_TYPE, domain, err);
+       if (G_UNLIKELY (log_level >= 2) && klass) {
+               char *s;
+
+               s = mono_type_full_name (&klass->byval_arg);
+               DEBUG(2, fprintf (log_file, "[dbg]   recv class [%s]\n", s));
+               g_free (s);
+       }
+       return klass;
 }
 
 static inline MonoAssembly*
@@ -2150,7 +2253,17 @@ decode_moduleid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domai
 static inline MonoMethod*
 decode_methodid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
 {
-       return decode_ptr_id (buf, endbuf, limit, ID_METHOD, domain, err);
+       MonoMethod *m;
+
+       m = decode_ptr_id (buf, endbuf, limit, ID_METHOD, domain, err);
+       if (G_UNLIKELY (log_level >= 2) && m) {
+               char *s;
+
+               s = mono_method_full_name (m, TRUE);
+               DEBUG(2, fprintf (log_file, "[dbg]   recv method [%s]\n", s));
+               g_free (s);
+       }
+       return m;
 }
 
 static inline MonoClassField*
@@ -2175,12 +2288,29 @@ static inline void
 buffer_add_typeid (Buffer *buf, MonoDomain *domain, MonoClass *klass)
 {
        buffer_add_ptr_id (buf, domain, ID_TYPE, klass);
+       if (G_UNLIKELY (log_level >= 2) && klass) {
+               char *s;
+
+               s = mono_type_full_name (&klass->byval_arg);
+               if (GetCurrentThreadId () == debugger_thread_id)
+                       DEBUG(2, fprintf (log_file, "[dbg]   send class [%s]\n", s));
+               else
+                       DEBUG(2, fprintf (log_file, "[%p]   send class [%s]\n", (gpointer)GetCurrentThreadId (), s));
+               g_free (s);
+       }
 }
 
 static inline void
 buffer_add_methodid (Buffer *buf, MonoDomain *domain, MonoMethod *method)
 {
        buffer_add_ptr_id (buf, domain, ID_METHOD, method);
+       if (G_UNLIKELY (log_level >= 2) && method) {
+               char *s;
+
+               s = mono_method_full_name (method, 1);
+               DEBUG(2, fprintf (log_file, "[dbg]   send method [%s]\n", s));
+               g_free (s);
+       }
 }
 
 static inline void
@@ -2239,9 +2369,6 @@ save_thread_context (MonoContext *ctx)
                mono_thread_state_init_from_current (&tls->context);
 }
 
-/* The number of times the runtime is suspended */
-static gint32 suspend_count;
-
 /* Number of threads suspended */
 /* 
  * If this is equal to the size of thread_to_tls, the runtime is considered
@@ -2260,7 +2387,7 @@ static MonoSemType suspend_sem;
 static void
 suspend_init (void)
 {
-       mono_mutex_init (&suspend_mutex, NULL);
+       mono_mutex_init (&suspend_mutex);
        mono_cond_init (&suspend_cond, NULL);   
        MONO_SEM_INIT (&suspend_sem, 0);
 }
@@ -2290,7 +2417,6 @@ get_last_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
                /* Store the context/lmf for the frame above the last frame */
                memcpy (&data->ctx, ctx, sizeof (MonoContext));
                data->lmf = info->lmf;
-
                return TRUE;
        }
 }
@@ -2349,7 +2475,7 @@ thread_interrupt (DebuggerTlsData *tls, MonoThreadInfo *info, void *sigctx, Mono
 
        if (ji) {
                /* Running managed code, will be suspended by the single step code */
-               DEBUG (1, fprintf (log_file, "[%p] Received interrupt while at %s(%p), continuing.\n", (gpointer)(gsize)tid, ji->method->name, ip));
+               DEBUG (1, fprintf (log_file, "[%p] Received interrupt while at %s(%p), continuing.\n", (gpointer)(gsize)tid, jinfo_get_method (ji)->name, ip));
                return TRUE;
        } else {
                /* 
@@ -2372,8 +2498,6 @@ thread_interrupt (DebuggerTlsData *tls, MonoThreadInfo *info, void *sigctx, Mono
                                /* Already terminated */
                                return TRUE;
 
-                       tls->context.valid = FALSE;
-
                        /*
                         * We are in a difficult position: we want to be able to provide stack
                         * traces for this thread, but we can't use the current ctx+lmf, since
@@ -2407,9 +2531,9 @@ thread_interrupt (DebuggerTlsData *tls, MonoThreadInfo *info, void *sigctx, Mono
                                tls->async_state.unwind_data [MONO_UNWIND_DATA_LMF] = data.lmf;
                                tls->async_state.unwind_data [MONO_UNWIND_DATA_JIT_TLS] = tls->thread->jit_data;
                        } else {
-                               /* No managed frames */
                                tls->async_state.valid = FALSE;
                        }
+
                        mono_memory_barrier ();
 
                        tls->suspended = TRUE;
@@ -2434,8 +2558,10 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
                return FALSE;
 
        tls = mono_native_tls_get_value (debugger_tls_id);
-       if (!tls)
-               return FALSE;
+       if (!tls) {
+               DEBUG (1, fprintf (log_file, "[%p] Received interrupt with no TLS, continuing.\n", (gpointer)GetCurrentThreadId ()));
+               return FALSE;
+       }
 
        return thread_interrupt (tls, NULL, sigctx, ji);
 }
@@ -2451,15 +2577,23 @@ static void CALLBACK notify_thread_apc (ULONG_PTR param)
 /*
  * reset_native_thread_suspend_state:
  * 
- *   Reset the suspended flag on native threads
+ *   Reset the suspended flag and state on native threads
  */
 static void
 reset_native_thread_suspend_state (gpointer key, gpointer value, gpointer user_data)
 {
        DebuggerTlsData *tls = value;
 
-       if (!tls->really_suspended && tls->suspended)
+       if (!tls->really_suspended && tls->suspended) {
                tls->suspended = FALSE;
+               /*
+                * The thread might still be running if it was executing native code, so the state won't be invalided by
+                * suspend_current ().
+                */
+               tls->context.valid = FALSE;
+               tls->async_state.valid = FALSE;
+               invalidate_frames (tls);
+       }
 }
 
 /*
@@ -2507,20 +2641,28 @@ notify_thread (gpointer key, gpointer value, gpointer user_data)
                MonoJitInfo *ji;
 
                info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gpointer)(gsize)thread->tid, FALSE);
-               g_assert (info);
-
-               ji = mono_jit_info_table_find (info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
+               if (!info) {
+                       DEBUG(1, fprintf (log_file, "[%p] mono_thread_info_suspend_sync () failed for %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid));
+                       /* 
+                        * Attached thread which died without detaching.
+                        */
+                       tls->terminated = TRUE;
+               } else {
+                       ji = mono_jit_info_table_find (info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
 
-               thread_interrupt (tls, info, NULL, ji);
+                       thread_interrupt (tls, info, NULL, ji);
 
-               mono_thread_info_resume (mono_thread_info_get_tid (info));
+                       mono_thread_info_resume (mono_thread_info_get_tid (info));
+               }
        } else {
                res = mono_thread_kill (thread, mono_thread_get_abort_signal ());
-               if (res)
+               if (res) {
+                       DEBUG(1, fprintf (log_file, "[%p] mono_thread_kill () failed for %p: %d...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid, res));
                        /* 
                         * Attached thread which died without detaching.
                         */
                        tls->terminated = TRUE;
+               }
        }
 #endif
 }
@@ -2530,6 +2672,7 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx)
 {
        guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
        MonoJitInfo *ji;
+       MonoMethod *method;
 
        if (mono_loader_lock_is_owned_by_self ()) {
                /*
@@ -2562,7 +2705,8 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx)
        ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
 
        /* Can't suspend in these methods */
-       if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy")))
+       method = jinfo_get_method (ji);
+       if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy")))
                return;
 
        save_thread_context (ctx);
@@ -2595,6 +2739,12 @@ suspend_vm (void)
 
        mono_mutex_unlock (&suspend_mutex);
 
+       if (suspend_count == 1)
+               /*
+                * Suspend creation of new threadpool threads, since they cannot run
+                */
+               mono_thread_pool_suspend ();
+
        mono_loader_unlock ();
 }
 
@@ -2633,6 +2783,9 @@ resume_vm (void)
        mono_mutex_unlock (&suspend_mutex);
        //g_assert (err == 0);
 
+       if (suspend_count == 0)
+               mono_thread_pool_resume ();
+
        mono_loader_unlock ();
 }
 
@@ -2658,7 +2811,7 @@ resume_thread (MonoInternalThread *thread)
 
        g_assert (suspend_count > 0);
 
-       DEBUG(1, fprintf (log_file, "[%p] Resuming thread...\n", (gpointer)(gssize)thread->tid));
+       DEBUG(1, fprintf (log_file, "[sdb] Resuming thread %p...\n", (gpointer)(gssize)thread->tid));
 
        tls->resume_count += suspend_count;
 
@@ -2768,7 +2921,7 @@ suspend_current (void)
        /* The frame info becomes invalid after a resume */
        tls->context.valid = FALSE;
        tls->async_state.valid = FALSE;
-       invalidate_frames (NULL);
+       invalidate_frames (tls);
 }
 
 static void
@@ -2853,16 +3006,13 @@ get_seq_points (MonoDomain *domain, MonoMethod *method)
        return seq_points;
 }
 
-static MonoSeqPointInfo*
-find_seq_points (MonoDomain *domain, MonoMethod *method)
+static void
+no_seq_points_found (MonoMethod *method)
 {
-       MonoSeqPointInfo *seq_points = get_seq_points (domain, method);
-
-       if (!seq_points)
-               printf ("Unable to find seq points for method '%s'.\n", mono_method_full_name (method, TRUE));
-       g_assert (seq_points);
-
-       return seq_points;
+       /*
+        * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space.
+        */
+       printf ("Unable to find seq points for method '%s'.\n", mono_method_full_name (method, TRUE));
 }
 
 /*
@@ -2876,7 +3026,13 @@ find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, g
        MonoSeqPointInfo *seq_points;
        int i;
 
-       seq_points = find_seq_points (domain, method);
+       seq_points = get_seq_points (domain, method);
+       if (!seq_points) {
+               if (info)
+                       *info = NULL;
+               return NULL;
+       }
+       g_assert (seq_points);
        if (info)
                *info = seq_points;
 
@@ -2899,9 +3055,11 @@ find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, g
        MonoSeqPointInfo *seq_points;
        int i;
 
-       seq_points = find_seq_points (domain, method);
+       seq_points = get_seq_points (domain, method);
        if (info)
                *info = seq_points;
+       if (!seq_points)
+               return NULL;
 
        for (i = seq_points->len - 1; i >= 0; --i) {
                if (seq_points->seq_points [i].native_offset <= native_offset)
@@ -2948,7 +3106,7 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
 {
        ComputeFramesUserData *ud = user_data;
        StackFrame *frame;
-       MonoMethod *method, *actual_method;
+       MonoMethod *method, *actual_method, *api_method;
        SeqPoint *sp;
        int flags = 0;
 
@@ -2962,10 +3120,11 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
        }
 
        if (info->ji)
-               method = info->ji->method;
+               method = jinfo_get_method (info->ji);
        else
                method = info->method;
        actual_method = info->actual_method;
+       api_method = method;
 
        if (!method)
                return FALSE;
@@ -2990,19 +3149,21 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
                if (!CHECK_PROTOCOL_VERSION (2, 17))
                        /* Older clients can't handle this flag */
                        return FALSE;
-               method = mono_marshal_method_from_wrapper (method);
-               if (!method)
+               api_method = mono_marshal_method_from_wrapper (method);
+               if (!api_method)
                        return FALSE;
-               actual_method = method;
+               actual_method = api_method;
                flags |= FRAME_FLAG_NATIVE_TRANSITION;
        }
 
        frame = g_new0 (StackFrame, 1);
        frame->method = method;
        frame->actual_method = actual_method;
+       frame->api_method = api_method;
        frame->il_offset = info->il_offset;
        frame->native_offset = info->native_offset;
        frame->flags = flags;
+       frame->ji = info->ji;
        if (info->reg_locations)
                memcpy (frame->reg_locations, info->reg_locations, MONO_MAX_IREGS * sizeof (mgreg_t*));
        if (ctx) {
@@ -3220,7 +3381,9 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo
                                        if (mod->data.thread != mono_thread_internal_current ())
                                                filtered = TRUE;
                                } else if (mod->kind == MOD_KIND_EXCEPTION_ONLY && ei) {
-                                       if (mod->data.exc_class && !mono_class_is_assignable_from (mod->data.exc_class, ei->exc->vtable->klass))
+                                       if (mod->data.exc_class && mod->subclasses && !mono_class_is_assignable_from (mod->data.exc_class, ei->exc->vtable->klass))
+                                               filtered = TRUE;
+                                       if (mod->data.exc_class && !mod->subclasses && mod->data.exc_class != ei->exc->vtable->klass)
                                                filtered = TRUE;
                                        if (ei->caught && !mod->caught)
                                                filtered = TRUE;
@@ -3233,7 +3396,7 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo
 
                                        if (assemblies) {
                                                for (k = 0; assemblies [k]; ++k)
-                                                       if (assemblies [k] == ji->method->klass->image->assembly)
+                                                       if (assemblies [k] == jinfo_get_method (ji)->klass->image->assembly)
                                                                found = TRUE;
                                        }
                                        if (!found)
@@ -3251,7 +3414,7 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo
                                                MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);
 
                                                if (minfo) {
-                                                       mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, NULL, NULL, NULL, NULL);
+                                                       mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, NULL, NULL, NULL, NULL, NULL);
                                                        for (i = 0; i < source_file_list->len; ++i) {
                                                                sinfo = g_ptr_array_index (source_file_list, i);
                                                                /*
@@ -3261,6 +3424,15 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo
                                                                s = strdup_tolower (sinfo->source_file);
                                                                if (g_hash_table_lookup (mod->data.source_files, s))
                                                                        found = TRUE;
+                                                               else {
+                                                                       char *s2 = g_path_get_basename (sinfo->source_file);
+                                                                       char *s3 = strdup_tolower (s2);
+
+                                                                       if (g_hash_table_lookup (mod->data.source_files, s3))
+                                                                               found = TRUE;
+                                                                       g_free (s2);
+                                                                       g_free (s3);
+                                                               }
                                                                g_free (s);
                                                        }
                                                        g_ptr_array_free (source_file_list, TRUE);
@@ -3277,9 +3449,55 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo
                                        g_free (s);
                                } else if (mod->kind == MOD_KIND_STEP) {
                                        if ((mod->data.filter & STEP_FILTER_STATIC_CTOR) && ji &&
-                                               (ji->method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
-                                               !strcmp (ji->method->name, ".cctor"))
+                                               (jinfo_get_method (ji)->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
+                                               !strcmp (jinfo_get_method (ji)->name, ".cctor"))
                                                filtered = TRUE;
+                                       if ((mod->data.filter & STEP_FILTER_DEBUGGER_HIDDEN) && ji) {
+                                               MonoCustomAttrInfo *ainfo;
+                                               static MonoClass *klass;
+
+                                               if (!klass) {
+                                                       klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggerHiddenAttribute");
+                                                       g_assert (klass);
+                                               }
+                                               if (!ji->dbg_hidden_inited) {
+                                                       ainfo = mono_custom_attrs_from_method (jinfo_get_method (ji));
+                                                       if (ainfo) {
+                                                               if (mono_custom_attrs_has_attr (ainfo, klass))
+                                                                       ji->dbg_hidden = TRUE;
+                                                               mono_custom_attrs_free (ainfo);
+                                                       }
+                                                       ji->dbg_hidden_inited = TRUE;
+                                               }
+                                               if (ji->dbg_hidden)
+                                                       filtered = TRUE;
+                                       }
+                                       if ((mod->data.filter & STEP_FILTER_DEBUGGER_STEP_THROUGH) && ji) {
+                                               MonoCustomAttrInfo *ainfo;
+                                               static MonoClass *klass;
+
+                                               if (!klass) {
+                                                       klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggerStepThroughAttribute");
+                                                       g_assert (klass);
+                                               }
+                                               if (!ji->dbg_step_through_inited) {
+                                                       ainfo = mono_custom_attrs_from_method (jinfo_get_method (ji));
+                                                       if (ainfo) {
+                                                               if (mono_custom_attrs_has_attr (ainfo, klass))
+                                                                       ji->dbg_step_through = TRUE;
+                                                               mono_custom_attrs_free (ainfo);
+                                                       }
+                                                       ainfo = mono_custom_attrs_from_class (jinfo_get_method (ji)->klass);
+                                                       if (ainfo) {
+                                                               if (mono_custom_attrs_has_attr (ainfo, klass))
+                                                                       ji->dbg_step_through = TRUE;
+                                                               mono_custom_attrs_free (ainfo);
+                                                       }
+                                                       ji->dbg_step_through_inited = TRUE;
+                                               }
+                                               if (ji->dbg_step_through)
+                                                       filtered = TRUE;
+                                       }
                                }
                        }
 
@@ -3322,6 +3540,7 @@ event_to_string (EventKind event)
        case EVENT_KIND_USER_LOG: return "USER_LOG";
        default:
                g_assert_not_reached ();
+               return "";
        }
 }
 
@@ -3340,6 +3559,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
        GSList *l;
        MonoDomain *domain = mono_domain_get ();
        MonoThread *thread = NULL;
+       MonoObject *keepalive_obj = NULL;
        gboolean send_success = FALSE;
        static int ecount;
        int nevents;
@@ -3438,10 +3658,17 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
                        buffer_add_domainid (&buf, mono_get_root_domain ());
                        break;
                case EVENT_KIND_VM_DEATH:
+                       if (CHECK_PROTOCOL_VERSION (2, 27))
+                               buffer_add_int (&buf, mono_environment_exitcode_get ());
                        break;
                case EVENT_KIND_EXCEPTION: {
                        EventInfo *ei = arg;
                        buffer_add_objid (&buf, ei->exc);
+                       /*
+                        * We are not yet suspending, so get_objref () will not keep this object alive. So we need to do it
+                        * later after the suspension. (#12494).
+                        */
+                       keepalive_obj = ei->exc;
                        break;
                }
                case EVENT_KIND_USER_BREAK:
@@ -3483,6 +3710,10 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
                 */
                save_thread_context (ctx);
                suspend_vm ();
+
+               if (keepalive_obj)
+                       /* This will keep this object alive */
+                       get_objref (keepalive_obj);
        }
 
        send_success = send_packet (CMD_SET_EVENT, CMD_COMPOSITE, &buf);
@@ -3593,7 +3824,6 @@ thread_startup (MonoProfiler *prof, uintptr_t tid)
        g_assert (!tls);
        // FIXME: Free this somewhere
        tls = g_new0 (DebuggerTlsData, 1);
-       tls->resume_event = CreateEvent (NULL, FALSE, FALSE, NULL);
        MONO_GC_REGISTER_ROOT_SINGLE (tls->thread);
        tls->thread = thread;
        mono_native_tls_set_value (debugger_tls_id, tls);
@@ -3909,18 +4139,20 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo
        }
 
        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);
+               char *s = g_strdup_printf ("Unable to insert breakpoint at %s:%d, seq_points=%d\n", mono_method_full_name (jinfo_get_method (ji), TRUE), bp->il_offset, seq_points->len);
 
                for (i = 0; i < seq_points->len; ++i)
-                       printf ("%d\n", seq_points->seq_points [i].il_offset);
+                       DEBUG (1, fprintf (log_file, "%d\n", seq_points->seq_points [i].il_offset));
 
                if (error) {
                        mono_error_set_error (error, MONO_ERROR_GENERIC, "%s", s);
+                       g_warning ("%s", s);
                        g_free (s);
                        return;
                } else {
-                       g_error ("%s", s);
+                       g_warning ("%s", s);
                        g_free (s);
+                       return;
                }
        }
 
@@ -3939,7 +4171,9 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo
        g_hash_table_insert (bp_locs, inst->ip, GINT_TO_POINTER (count + 1));
        mono_loader_unlock ();
 
-       if (count == 0) {
+       if (sp->native_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
+               DEBUG (1, fprintf (log_file, "[dbg] Attempting to insert seq point at dead IL offset %d, ignoring.\n", (int)bp->il_offset));
+       } else if (count == 0) {
 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
                mono_arch_set_breakpoint (ji, inst->ip);
 #else
@@ -3947,7 +4181,7 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo
 #endif
        }
 
-       DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x.\n", mono_method_full_name (ji->method, TRUE), (int)sp->il_offset));  
+       DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x.\n", mono_method_full_name (jinfo_get_method (ji), TRUE), (int)sp->il_offset));       
 }
 
 static void
@@ -3965,7 +4199,7 @@ remove_breakpoint (BreakpointInstance *inst)
 
        g_assert (count > 0);
 
-       if (count == 1) {
+       if (count == 1 && inst->native_offset != SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
                mono_arch_clear_breakpoint (ji, ip);
        }
 #else
@@ -4016,6 +4250,7 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
        int i, j;
        MonoSeqPointInfo *seq_points;
        MonoDomain *domain;
+       MonoMethod *jmethod;
 
        if (!breakpoints)
                return;
@@ -4039,10 +4274,11 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
                }
 
                if (!found) {
+                       jmethod = jinfo_get_method (ji);
                        mono_domain_lock (domain);
-                       seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, ji->method);
-                       if (!seq_points && ji->method->is_inflated)
-                               seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (ji->method));
+                       seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, jmethod);
+                       if (!seq_points && jmethod->is_inflated)
+                               seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (jmethod));
                        mono_domain_unlock (domain);
                        if (!seq_points)
                                /* Could be AOT code */
@@ -4235,6 +4471,66 @@ clear_breakpoints_for_domain (MonoDomain *domain)
        mono_loader_unlock ();
 }
 
+/*
+ * ss_update:
+ *
+ * Return FALSE if single stepping needs to continue.
+ */
+static gboolean
+ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx)
+{
+       MonoDebugMethodInfo *minfo;
+       MonoDebugSourceLocation *loc = NULL;
+       gboolean hit = TRUE;
+       MonoMethod *method;
+
+       if (req->depth == STEP_DEPTH_OVER && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK)) {
+               /*
+                * These seq points are inserted by the JIT after calls, step over needs to skip them.
+                */
+               DEBUG (1, fprintf (log_file, "[%p] Seq point at nonempty stack %x while stepping over, continuing single stepping.\n", (gpointer)GetCurrentThreadId (), sp->il_offset));
+               return FALSE;
+       }
+
+       if (req->depth == STEP_DEPTH_OVER && hit) {
+               if (!tls->context.valid)
+                       mono_thread_state_init_from_monoctx (&tls->context, ctx);
+               compute_frame_info (tls->thread, tls);
+               if (req->nframes && tls->frame_count && tls->frame_count > req->nframes) {
+                       /* Hit the breakpoint in a recursive call */
+                       DEBUG (1, fprintf (log_file, "[%p] Breakpoint at lower frame while stepping over, continuing single stepping.\n", (gpointer)GetCurrentThreadId ()));
+                       return FALSE;
+               }
+       }
+
+       if (req->size != STEP_SIZE_LINE)
+               return TRUE;
+
+       /* Have to check whenever a different source line was reached */
+       method = jinfo_get_method (ji);
+       minfo = mono_debug_lookup_method (method);
+
+       if (minfo)
+               loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset);
+
+       if (!loc || (loc && method == ss_req->last_method && loc->row == ss_req->last_line)) {
+               /* Have to continue single stepping */
+               if (!loc)
+                       DEBUG(1, fprintf (log_file, "[%p] No line number info for il offset %x, continuing single stepping.\n", (gpointer)GetCurrentThreadId (), sp->il_offset));
+               else
+                       DEBUG(1, fprintf (log_file, "[%p] Same source line (%d), continuing single stepping.\n", (gpointer)GetCurrentThreadId (), loc->row));
+               hit = FALSE;
+       }
+                               
+       if (loc) {
+               ss_req->last_method = method;
+               ss_req->last_line = loc->row;
+               mono_debug_free_source_location (loc);
+       }
+
+       return hit;
+}
+
 static gboolean
 breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly)
 {
@@ -4254,6 +4550,7 @@ process_breakpoint_inner (DebuggerTlsData *tls)
        GSList *bp_events = NULL, *ss_events = NULL, *enter_leave_events = NULL;
        EventKind kind = EVENT_KIND_BREAKPOINT;
        MonoContext *ctx = &tls->restore_ctx;
+       MonoMethod *method;
        MonoSeqPointInfo *info;
        SeqPoint *sp;
 
@@ -4262,7 +4559,7 @@ process_breakpoint_inner (DebuggerTlsData *tls)
        ip = MONO_CONTEXT_GET_IP (ctx);
        ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
        g_assert (ji);
-       g_assert (ji->method);
+       method = jinfo_get_method (ji);
 
        /* Compute the native offset of the breakpoint from the ip */
        native_offset = ip - (guint8*)ji->code_start;   
@@ -4272,22 +4569,25 @@ process_breakpoint_inner (DebuggerTlsData *tls)
         */
        mono_arch_skip_breakpoint (ctx, ji);
 
-       if (ji->method->wrapper_type || tls->disable_breakpoints)
+       if (method->wrapper_type || tls->disable_breakpoints)
                return;
 
        bp_reqs = g_ptr_array_new ();
        ss_reqs = g_ptr_array_new ();
        ss_reqs_orig = g_ptr_array_new ();
 
-       DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, ip=%p, offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, ip, native_offset));
-
        mono_loader_lock ();
 
        /*
         * The ip points to the instruction causing the breakpoint event, which is after
         * the offset recorded in the seq point map, so find the prev seq point before ip.
         */
-       sp = find_prev_seq_point_for_native_offset (mono_domain_get (), ji->method, native_offset, &info);
+       sp = find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info);
+       if (!sp)
+               no_seq_points_found (method);
+       g_assert (sp);
+
+       DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, ip=%p, offset=0x%x, sp il offset=0x%x.\n", (gpointer)GetCurrentThreadId (), method->name, ip, native_offset, sp ? sp->il_offset : -1));
 
        bp = NULL;
        for (i = 0; i < breakpoints->len; ++i) {
@@ -4319,34 +4619,17 @@ process_breakpoint_inner (DebuggerTlsData *tls)
        for (i = 0; i < ss_reqs_orig->len; ++i) {
                EventRequest *req = g_ptr_array_index (ss_reqs_orig, i);
                SingleStepReq *ss_req = req->info;
-               gboolean hit = TRUE;
-
-               if (ss_req->size == STEP_SIZE_LINE) {
-                       /* Have to check whenever a different source line was reached */
-                       MonoDebugMethodInfo *minfo;
-                       MonoDebugSourceLocation *loc = NULL;
-
-                       minfo = mono_debug_lookup_method (ji->method);
-
-                       if (minfo)
-                               loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset);
+               gboolean hit;
 
-                       if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line))
-                               /* Have to continue single stepping */
-                               hit = FALSE;
-                               
-                       if (loc) {
-                               ss_req->last_method = ji->method;
-                               ss_req->last_line = loc->row;
-                               mono_debug_free_source_location (loc);
-                       }
-               }
+               if (mono_thread_internal_current () != ss_req->thread)
+                       continue;
 
+               hit = ss_update (ss_req, ji, sp, tls, ctx);
                if (hit)
                        g_ptr_array_add (ss_reqs, req);
 
                /* Start single stepping again from the current sequence point */
-               ss_start (ss_req, ji->method, sp, info, ctx, tls, FALSE);
+               ss_start (ss_req, method, sp, info, ctx, tls, FALSE);
        }
        
        if (ss_reqs->len > 0)
@@ -4366,11 +4649,11 @@ process_breakpoint_inner (DebuggerTlsData *tls)
         * resume.
         */
        if (ss_events)
-               process_event (EVENT_KIND_STEP, ji->method, 0, ctx, ss_events, suspend_policy);
+               process_event (EVENT_KIND_STEP, method, 0, ctx, ss_events, suspend_policy);
        if (bp_events)
-               process_event (kind, ji->method, 0, ctx, bp_events, suspend_policy);
+               process_event (kind, method, 0, ctx, bp_events, suspend_policy);
        if (enter_leave_events)
-               process_event (kind, ji->method, 0, ctx, enter_leave_events, suspend_policy);
+               process_event (kind, method, 0, ctx, enter_leave_events, suspend_policy);
 }
 
 /* Process a breakpoint/single step event after resuming from a signal handler */
@@ -4379,10 +4662,6 @@ process_signal_event (void (*func) (DebuggerTlsData*))
 {
        DebuggerTlsData *tls;
        MonoContext orig_restore_ctx, ctx;
-       static void (*restore_context) (void *);
-
-       if (!restore_context)
-               restore_context = mono_get_restore_context ();
 
        tls = mono_native_tls_get_value (debugger_tls_id);
        /* Have to save/restore the restore_ctx as we can be called recursively during invokes etc. */
@@ -4394,7 +4673,7 @@ process_signal_event (void (*func) (DebuggerTlsData*))
        /* This is called when resuming from a signal handler, so it shouldn't return */
        memcpy (&ctx, &tls->restore_ctx, sizeof (MonoContext));
        memcpy (&tls->restore_ctx, &orig_restore_ctx, sizeof (MonoContext));
-       restore_context (&ctx);
+       mono_restore_context (&ctx);
        g_assert_not_reached ();
 }
 
@@ -4413,6 +4692,8 @@ resume_from_signal_handler (void *sigctx, void *func)
        /* Save the original context in TLS */
        // FIXME: This might not work on an altstack ?
        tls = mono_native_tls_get_value (debugger_tls_id);
+       if (!tls)
+               fprintf (stderr, "Thread %p is not attached to the JIT.\n", (gpointer)GetCurrentThreadId ());
        g_assert (tls);
 
        // FIXME: MonoContext usually doesn't include the fp registers, so these are 
@@ -4441,7 +4722,6 @@ mono_debugger_agent_breakpoint_hit (void *sigctx)
         * problems, like the original signal is disabled, libgc can't handle altstack, etc.
         * So set up the signal context to return to the real breakpoint handler function.
         */
-
        resume_from_signal_handler (sigctx, process_breakpoint);
 }
 
@@ -4509,6 +4789,7 @@ process_single_step_inner (DebuggerTlsData *tls)
        MonoDomain *domain;
        GSList *events;
        MonoContext *ctx = &tls->restore_ctx;
+       MonoMethod *method;
        SeqPoint *sp;
        MonoSeqPointInfo *info;
 
@@ -4532,75 +4813,43 @@ process_single_step_inner (DebuggerTlsData *tls)
        if (log_level > 0) {
                ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
 
-               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));
+               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 (jinfo_get_method (ji), TRUE), MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp));
        }
 
        ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
        g_assert (ji);
-       g_assert (ji->method);
+       method = jinfo_get_method (ji);
+       g_assert (method);
 
-       if (ji->method->wrapper_type && ji->method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
+       if (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
                return;
 
        /* 
-        * FIXME: 
+        * FIXME:
         * Stopping in memset makes half-initialized vtypes visible.
         * Stopping in memcpy makes half-copied vtypes visible.
         */
-       if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy")))
+       if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy")))
                return;
 
        /*
         * The ip points to the instruction causing the single step event, which is before
         * the offset recorded in the seq point map, so find the next seq point after ip.
         */
-       sp = find_next_seq_point_for_native_offset (domain, ji->method, (guint8*)ip - (guint8*)ji->code_start, &info);
+       sp = find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info);
        if (!sp)
                return;
        il_offset = sp->il_offset;
 
-       // FIXME: No tests fail if this is disabled
-#if 0
-       if (ss_req->size == STEP_SIZE_LINE) {
-               // FIXME:
-               NOT_IMPLEMENTED;
-
-               /* Step until a different source line is reached */
-               MonoDebugMethodInfo *minfo;
-
-               minfo = mono_debug_lookup_method (ji->method);
-
-               if (minfo) {
-                       MonoDebugSourceLocation *loc = mono_debug_symfile_lookup_location (minfo, il_offset);
-
-                       if (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line) {
-                               mono_debug_free_source_location (loc);
-                               return;
-                       }
-                       if (!loc)
-                               /*
-                                * Step until we reach a location with line number info, 
-                                * otherwise the client can't show a location.
-                                * This can happen for example with statics initialized inline
-                                * outside of a cctor.
-                                */
-                               return;
-
-                       if (loc) {
-                               ss_req->last_method = ji->method;
-                               ss_req->last_line = loc->row;
-                               mono_debug_free_source_location (loc);
-                       }
-               }
-       }
-#endif
+       if (!ss_update (ss_req, ji, sp, tls, ctx))
+               return;
 
        /* Start single stepping again from the current sequence point */
-       ss_start (ss_req, ji->method, sp, info, ctx, tls, FALSE);
+       ss_start (ss_req, method, sp, info, ctx, tls, FALSE);
 
        if ((ss_req->filter & STEP_FILTER_STATIC_CTOR) &&
-               (ji->method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
-               !strcmp (ji->method->name, ".cctor"))
+               (method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
+               !strcmp (method->name, ".cctor"))
                return;
 
        // FIXME: Has to lock earlier
@@ -4617,7 +4866,7 @@ process_single_step_inner (DebuggerTlsData *tls)
 
        mono_loader_unlock ();
 
-       process_event (EVENT_KIND_STEP, ji->method, il_offset, ctx, events, suspend_policy);
+       process_event (EVENT_KIND_STEP, jinfo_get_method (ji), il_offset, ctx, events, suspend_policy);
 }
 
 static void
@@ -4731,6 +4980,8 @@ stop_single_stepping (void)
 
        if (val == 0)
                mono_arch_stop_single_stepping ();
+       if (ss_req != NULL)
+               ss_invoke_addr = NULL;
 #else
        g_assert_not_reached ();
 #endif
@@ -4770,7 +5021,7 @@ ss_stop (SingleStepReq *ss_req)
 static void
 ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch)
 {
-       int i, frame_index;
+       int i, j, frame_index;
        SeqPoint *next_sp;
        MonoBreakpoint *bp;
        gboolean enable_global = FALSE;
@@ -4787,7 +5038,7 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointI
        } else {
                frame_index = 1;
 
-               if ((!sp || sp->next_len == 0 || ss_req->depth == STEP_DEPTH_OUT) && ctx) {
+               if ((!sp || sp->next_len == 0 || ss_req->depth == STEP_DEPTH_OUT || ss_req->depth == STEP_DEPTH_OVER) && ctx) {
                        /* Need parent frames */
                        if (!tls->context.valid)
                                mono_thread_state_init_from_monoctx (&tls->context, ctx);
@@ -4809,14 +5060,19 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointI
                                if (sp && sp->next_len != 0)
                                        break;
                        }
+                       // There could be method calls before the next seq point in the caller when using nested calls
+                       //enable_global = TRUE;
                } else {
-                       while (sp && sp->next_len == 0) {
+                       if (sp && sp->next_len == 0) {
                                sp = NULL;
-                               if (frame_index < tls->frame_count) {
+                               while (frame_index < tls->frame_count) {
                                        StackFrame *frame = tls->frames [frame_index];
 
                                        method = frame->method;
                                        sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info);
+                                       if (sp && sp->next_len != 0)
+                                               break;
+                                       sp = NULL;
                                        frame_index ++;
                                }
                        }
@@ -4831,6 +5087,29 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointI
                        }
                }
 
+               if (ss_req->depth == STEP_DEPTH_OVER) {
+                       if (ss_req->nframes == 0)
+                               ss_req->nframes = tls->frame_count;
+                       /* Need to stop in catch clauses as well */
+                       for (i = 0; i < tls->frame_count; ++i) {
+                               StackFrame *frame = tls->frames [i];
+
+                               if (frame->ji) {
+                                       MonoJitInfo *jinfo = frame->ji;
+                                       for (j = 0; j < jinfo->num_clauses; ++j) {
+                                               MonoJitExceptionInfo *ei = &jinfo->clauses [j];
+
+                                               sp = find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL);
+                                               if (sp) {
+                                                       bp = set_breakpoint (frame->method, sp->il_offset, ss_req->req, NULL);
+                                                       ss_req->bps = g_slist_append (ss_req->bps, bp);
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+
                if (ss_req->depth == STEP_DEPTH_INTO) {
                        /* Enable global stepping so we stop at method entry too */
                        enable_global = TRUE;
@@ -4897,7 +5176,7 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ
        g_assert (tls->context.valid);
        ss_req->start_sp = ss_req->last_sp = MONO_CONTEXT_GET_SP (&tls->context.ctx);
 
-       if (tls->has_catch_ctx) {
+       if (tls->catch_state.valid) {
                gboolean res;
                StackFrameInfo frame;
                MonoContext new_ctx;
@@ -4908,7 +5187,7 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ
                 */
 
                /* 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);
+               res = mono_find_jit_info_ext (tls->catch_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], thread->jit_data, NULL, &tls->catch_state.ctx, &new_ctx, NULL, &lmf, NULL, &frame);
                g_assert (res);
                g_assert (frame.type == FRAME_TYPE_MANAGED);
 
@@ -4917,6 +5196,8 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ
                 * point after ip.
                 */
                sp = find_next_seq_point_for_native_offset (frame.domain, frame.method, frame.native_offset, &info);
+               if (!sp)
+                       no_seq_points_found (frame.method);
                g_assert (sp);
 
                method = frame.method;
@@ -4961,6 +5242,8 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ
                        if (!method && frame->il_offset != -1) {
                                /* FIXME: Sort the table and use a binary search */
                                sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info);
+                               if (!sp)
+                                       no_seq_points_found (frame->method);
                                g_assert (sp);
                                method = frame->method;
                        }
@@ -5018,13 +5301,35 @@ mono_debugger_agent_debug_log_is_enabled (void)
        return agent_config.enabled;
 }
 
+#ifdef PLATFORM_ANDROID
+void
+mono_debugger_agent_unhandled_exception (MonoException *exc)
+{
+       int suspend_policy;
+       GSList *events;
+       EventInfo ei;
+
+       if (!inited)
+               return;
+
+       memset (&ei, 0, sizeof (EventInfo));
+       ei.exc = (MonoObject*)exc;
+
+       mono_loader_lock ();
+       events = create_event_list (EVENT_KIND_EXCEPTION, NULL, NULL, &ei, &suspend_policy);
+       mono_loader_unlock ();
+
+       process_event (EVENT_KIND_EXCEPTION, &ei, 0, NULL, events, suspend_policy);
+}
+#endif
+
 void
 mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, 
                                      MonoContext *catch_ctx)
 {
-       int suspend_policy;
+       int i, j, suspend_policy;
        GSList *events;
-       MonoJitInfo *ji;
+       MonoJitInfo *ji, *catch_ji;
        EventInfo ei;
        DebuggerTlsData *tls = NULL;
 
@@ -5087,23 +5392,55 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx
                return;
 
        ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (throw_ctx), NULL);
+       if (catch_ctx)
+               catch_ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (catch_ctx), NULL);
+       else
+               catch_ji = NULL;
 
        ei.exc = (MonoObject*)exc;
        ei.caught = catch_ctx != NULL;
 
        mono_loader_lock ();
+
+       /* Treat exceptions which are caught in non-user code as unhandled */
+       for (i = 0; i < event_requests->len; ++i) {
+               EventRequest *req = g_ptr_array_index (event_requests, i);
+               if (req->event_kind != EVENT_KIND_EXCEPTION)
+                       continue;
+
+               for (j = 0; j < req->nmodifiers; ++j) {
+                       Modifier *mod = &req->modifiers [j];
+
+                       if (mod->kind == MOD_KIND_ASSEMBLY_ONLY && catch_ji) {
+                               int k;
+                               gboolean found = FALSE;
+                               MonoAssembly **assemblies = mod->data.assemblies;
+
+                               if (assemblies) {
+                                       for (k = 0; assemblies [k]; ++k)
+                                               if (assemblies [k] == jinfo_get_method (catch_ji)->klass->image->assembly)
+                                                       found = TRUE;
+                               }
+                               if (!found)
+                                       ei.caught = FALSE;
+                       }
+               }
+       }
+
        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;
+       if (tls && ei.caught && catch_ctx) {
+               memset (&tls->catch_state, 0, sizeof (tls->catch_state));
+               tls->catch_state.ctx = *catch_ctx;
+               tls->catch_state.unwind_data [MONO_UNWIND_DATA_DOMAIN] = mono_domain_get ();
+               tls->catch_state.valid = TRUE;
        }
 
        process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, suspend_policy);
 
        if (tls)
-               tls->has_catch_ctx = FALSE;
+               tls->catch_state.valid = FALSE;
 }
 
 void
@@ -5174,6 +5511,12 @@ buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain,
        MonoObject *obj;
 
        if (t->byref) {
+               if (!(*(void**)addr)) {
+                       /* This can happen with compiler generated locals */
+                       //printf ("%s\n", mono_type_full_name (t));
+                       buffer_add_byte (buf, VALUE_TYPE_ID_NULL);
+                       return;
+               }
                g_assert (*(void**)addr);
                addr = *(void**)addr;
        }
@@ -5316,11 +5659,75 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
        buffer_add_value_full (buf, t, addr, domain, FALSE);
 }
 
-static ErrorCode
-decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
+static gboolean
+obj_is_of_type (MonoObject *obj, MonoType *t)
 {
-       int err;
-       int type = decode_byte (buf, &buf, limit);
+       MonoClass *klass = obj->vtable->klass;
+       if (!mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) {
+               if (mono_class_is_transparent_proxy (klass)) {
+                       klass = ((MonoTransparentProxy *)obj)->remote_class->proxy_class;
+                       if (mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) {
+                               return TRUE;
+                       }
+               }
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static ErrorCode
+decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit);
+
+static ErrorCode
+decode_vtype (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+       gboolean is_enum;
+       MonoClass *klass;
+       MonoClassField *f;
+       int nfields;
+       gpointer iter = NULL;
+       MonoDomain *d;
+       int err;
+
+       is_enum = decode_byte (buf, &buf, limit);
+       /* Enums are sent as a normal vtype */
+       if (is_enum)
+               return ERR_NOT_IMPLEMENTED;
+       klass = decode_typeid (buf, &buf, limit, &d, &err);
+       if (err)
+               return err;
+
+       if (t && klass != mono_class_from_mono_type (t)) {
+               char *name = mono_type_full_name (t);
+               char *name2 = mono_type_full_name (&klass->byval_arg);
+               DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got %s.\n", (gpointer)GetCurrentThreadId (), name, name2));
+               g_free (name);
+               g_free (name2);
+               return ERR_INVALID_ARGUMENT;
+       }
+
+       nfields = decode_int (buf, &buf, limit);
+       while ((f = mono_class_get_fields (klass, &iter))) {
+               if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
+                       continue;
+               if (mono_field_is_deleted (f))
+                       continue;
+               err = decode_value (f->type, domain, (guint8*)addr + f->offset - sizeof (MonoObject), buf, &buf, limit);
+               if (err)
+                       return err;
+               nfields --;
+       }
+       g_assert (nfields == 0);
+
+       *endbuf = buf;
+
+       return 0;
+}
+
+static ErrorCode
+decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+       int err;
 
        if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) &&
                !(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) &&
@@ -5389,38 +5796,11 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8
                g_assert (type == MONO_TYPE_VALUETYPE);
                /* Fall through */
                handle_vtype:
-       case MONO_TYPE_VALUETYPE: {
-               gboolean is_enum = decode_byte (buf, &buf, limit);
-               MonoClass *klass;
-               MonoClassField *f;
-               int nfields;
-               gpointer iter = NULL;
-               MonoDomain *d;
-
-               /* Enums are sent as a normal vtype */
-               if (is_enum)
-                       return ERR_NOT_IMPLEMENTED;
-               klass = decode_typeid (buf, &buf, limit, &d, &err);
+       case MONO_TYPE_VALUETYPE:
+               err = decode_vtype (t, domain, addr,buf, &buf, limit);
                if (err)
                        return err;
-
-               if (klass != mono_class_from_mono_type (t))
-                       return ERR_INVALID_ARGUMENT;
-
-               nfields = decode_int (buf, &buf, limit);
-               while ((f = mono_class_get_fields (klass, &iter))) {
-                       if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
-                               continue;
-                       if (mono_field_is_deleted (f))
-                               continue;
-                       err = decode_value (f->type, domain, (guint8*)addr + f->offset - sizeof (MonoObject), buf, &buf, limit);
-                       if (err)
-                               return err;
-                       nfields --;
-               }
-               g_assert (nfields == 0);
                break;
-       }
        handle_ref:
        default:
                if (MONO_TYPE_IS_REFERENCE (t)) {
@@ -5433,15 +5813,56 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8
                                if (err)
                                        return err;
 
-                               if (obj && !mono_class_is_assignable_from (mono_class_from_mono_type (t), obj->vtable->klass))
-                                       return ERR_INVALID_ARGUMENT;
+                               if (obj) {
+                                       if (!obj_is_of_type (obj, t)) {
+                                               DEBUG (1, fprintf (log_file, "Expected type '%s', got '%s'\n", mono_type_full_name (t), obj->vtable->klass->name));
+                                               return ERR_INVALID_ARGUMENT;
+                                       }
+                               }
                                if (obj && obj->vtable->domain != domain)
                                        return ERR_INVALID_ARGUMENT;
 
                                mono_gc_wbarrier_generic_store (addr, obj);
                        } else if (type == VALUE_TYPE_ID_NULL) {
                                *(MonoObject**)addr = NULL;
+                       } else if (type == MONO_TYPE_VALUETYPE) {
+                               guint8 *buf2;
+                               gboolean is_enum;
+                               MonoClass *klass;
+                               MonoDomain *d;
+                               guint8 *vtype_buf;
+                               int vtype_buf_size;
+
+                               /* This can happen when round-tripping boxed vtypes */
+                               /*
+                                * Obtain vtype class.
+                                * Same as the beginning of the handle_vtype case above.
+                                */
+                               buf2 = buf;
+                               is_enum = decode_byte (buf, &buf, limit);
+                               if (is_enum)
+                                       return ERR_NOT_IMPLEMENTED;
+                               klass = decode_typeid (buf, &buf, limit, &d, &err);
+                               if (err)
+                                       return err;
+
+                               /* Decode the vtype into a temporary buffer, then box it. */
+                               vtype_buf_size = mono_class_value_size (klass, NULL);
+                               vtype_buf = g_malloc0 (vtype_buf_size);
+                               g_assert (vtype_buf);
+
+                               buf = buf2;
+                               err = decode_vtype (NULL, domain, vtype_buf, buf, &buf, limit);
+                               if (err) {
+                                       g_free (vtype_buf);
+                                       return err;
+                               }
+                               *(MonoObject**)addr = mono_value_box (d, klass, vtype_buf);
+                               g_free (vtype_buf);
                        } else {
+                               char *name = mono_type_full_name (t);
+                               DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got 0x%0x.\n", (gpointer)GetCurrentThreadId (), name, type));
+                               g_free (name);
                                return ERR_INVALID_ARGUMENT;
                        }
                } else {
@@ -5455,12 +5876,53 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8
        return 0;
 }
 
+static ErrorCode
+decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+       int err;
+       int type = decode_byte (buf, &buf, limit);
+
+       if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) {
+               MonoType *targ = t->data.generic_class->context.class_inst->type_argv [0];
+               guint8 *nullable_buf;
+
+               /*
+                * First try decoding it as a Nullable`1
+                */
+               err = decode_value_internal (t, type, domain, addr, buf, endbuf, limit);
+               if (!err)
+                       return err;
+
+               /*
+                * Then try decoding as a primitive value or null.
+                */
+               if (targ->type == type) {
+                       nullable_buf = g_malloc (mono_class_instance_size (mono_class_from_mono_type (targ)));
+                       err = decode_value_internal (targ, type, domain, nullable_buf, buf, endbuf, limit);
+                       if (err) {
+                               g_free (nullable_buf);
+                               return err;
+                       }
+                       mono_nullable_init (addr, mono_value_box (domain, mono_class_from_mono_type (targ), nullable_buf), mono_class_from_mono_type (t));
+                       g_free (nullable_buf);
+                       *endbuf = buf;
+                       return ERR_NONE;
+               } else if (type == VALUE_TYPE_ID_NULL) {
+                       mono_nullable_init (addr, NULL, mono_class_from_mono_type (t));
+                       *endbuf = buf;
+                       return ERR_NONE;
+               }
+       }
+
+       return decode_value_internal (t, type, domain, addr, buf, endbuf, limit);
+}
+
 static void
-add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype)
+add_var (Buffer *buf, MonoDebugMethodJitInfo *jit, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype)
 {
        guint32 flags;
        int reg;
-       guint8 *addr;
+       guint8 *addr, *gaddr;
        mgreg_t reg_val;
 
        flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
@@ -5483,6 +5945,60 @@ add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, Mono
        case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
                NOT_IMPLEMENTED;
                break;
+       case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR:
+       case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR:
+               /* Same as regoffset, but with an indirection */
+               addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+               addr += (gint32)var->offset;
+
+               gaddr = *(gpointer*)addr;
+               g_assert (gaddr);
+               buffer_add_value_full (buf, t, gaddr, domain, as_vtype);
+               break;
+       case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: {
+               MonoDebugVarInfo *info_var = jit->gsharedvt_info_var;
+               MonoDebugVarInfo *locals_var = jit->gsharedvt_locals_var;
+               MonoGSharedVtMethodRuntimeInfo *info;
+               guint8 *locals;
+               int idx;
+
+               idx = reg;
+
+               g_assert (info_var);
+               g_assert (locals_var);
+
+               flags = info_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+               reg = info_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+               if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET) {
+                       addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+                       addr += (gint32)info_var->offset;
+                       info = *(gpointer*)addr;
+               } else if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER) {
+                       info = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+               } else {
+                       g_assert_not_reached ();
+               }
+               g_assert (info);
+
+               flags = locals_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+               reg = locals_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+               if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET) {
+                       addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+                       addr += (gint32)locals_var->offset;
+                       locals = *(gpointer*)addr;
+               } else if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER) {
+                       locals = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+               } else {
+                       g_assert_not_reached ();
+               }
+               g_assert (locals);
+
+               addr = locals + GPOINTER_TO_INT (info->entries [idx]);
+
+               buffer_add_value_full (buf, t, addr, domain, as_vtype);
+               break;
+       }
+
        default:
                g_assert_not_reached ();
        }
@@ -5493,7 +6009,7 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai
 {
        guint32 flags;
        int reg, size;
-       guint8 *addr;
+       guint8 *addr, *gaddr;
 
        flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
        reg = var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
@@ -5509,6 +6025,16 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai
                mgreg_t v;
                gboolean is_signed = FALSE;
 
+               if (t->byref) {
+                       addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+
+                       if (addr) {
+                               // FIXME: Write barriers
+                               mono_gc_memmove_atomic (addr, val, size);
+                       }
+                       break;
+               }
+
                if (!t->byref && (t->type == MONO_TYPE_I1 || t->type == MONO_TYPE_I2 || t->type == MONO_TYPE_I4 || t->type == MONO_TYPE_I8))
                        is_signed = TRUE;
 
@@ -5529,9 +6055,6 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai
                        g_assert_not_reached ();
                }
 
-               if (t->byref)
-                       NOT_IMPLEMENTED;
-
                /* Set value on the stack or in the return ctx */
                if (reg_locations [reg]) {
                        /* Saved on the stack */
@@ -5565,7 +6088,17 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai
                }
                        
                // FIXME: Write barriers
-               memcpy (addr, val, size);
+               mono_gc_memmove_atomic (addr, val, size);
+               break;
+       case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR:
+               /* Same as regoffset, but with an indirection */
+               addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+               addr += (gint32)var->offset;
+
+               gaddr = *(gpointer*)addr;
+               g_assert (gaddr);
+               // FIXME: Write barriers
+               mono_gc_memmove_atomic (gaddr, val, size);
                break;
        case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
                NOT_IMPLEMENTED;
@@ -5691,9 +6224,8 @@ add_thread (gpointer key, gpointer value, gpointer user_data)
 }
 
 static ErrorCode
-do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
+do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp)
 {
-       guint8 *p = invoke->p;
        guint8 *end = invoke->endp;
        MonoMethod *m;
        int i, err, nargs;
@@ -5730,8 +6262,10 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
        if (m->klass->valuetype && (m->flags & METHOD_ATTRIBUTE_STATIC)) {
                /* Should be null */
                int type = decode_byte (p, &p, end);
-               if (type != VALUE_TYPE_ID_NULL)
+               if (type != VALUE_TYPE_ID_NULL) {
+                       DEBUG (1, fprintf (log_file, "[%p] Error: Static vtype method invoked with this argument.\n", (gpointer)GetCurrentThreadId ()));
                        return ERR_INVALID_ARGUMENT;
+               }
                memset (this_buf, 0, mono_class_instance_size (m->klass));
        } else {
                err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end);
@@ -5744,6 +6278,14 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
        else
                this = NULL;
 
+       if (MONO_CLASS_IS_INTERFACE (m->klass)) {
+               if (!this) {
+                       DEBUG (1, fprintf (log_file, "[%p] Error: Interface method invoked without this argument.\n", (gpointer)GetCurrentThreadId ()));
+                       return ERR_INVALID_ARGUMENT;
+               }
+               m = mono_object_get_virtual_method (this, m);
+       }
+
        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)
@@ -5760,7 +6302,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
                }
        }
 
-       if (this && !mono_class_is_assignable_from (m->klass, this->vtable->klass))
+       if (this && !obj_is_of_type (this, &m->klass->byval_arg))
                return ERR_INVALID_ARGUMENT;
 
        nargs = decode_int (p, &p, end);
@@ -5807,39 +6349,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
 
                /* Setup our lmf */
                memset (&ext, 0, sizeof (ext));
-#ifdef TARGET_AMD64
-               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.rsp = (gssize)&ext;
-#elif defined(TARGET_X86)
-               ext.lmf.previous_lmf = (gsize)*(lmf_addr);
-               /* Mark that this is a MonoLMFExt */
-               ext.lmf.previous_lmf = (gsize)(gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
-               ext.lmf.ebp = (gssize)&ext;
-#elif defined(TARGET_ARM)
-               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.sp = (gssize)&ext;
-#elif defined(TARGET_POWERPC)
-               ext.lmf.previous_lmf = *(lmf_addr);
-               /* Mark that this is a MonoLMFExt */
-               ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
-               ext.lmf.ebp = (gssize)&ext;
-#elif defined(TARGET_S390X)
-               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_MIPS)
-               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.iregs [mips_sp] = (gssize)&ext;
-#else
-               g_assert_not_reached ();
-#endif
+               mono_arch_init_lmf_ext (&ext, *lmf_addr);
 
                ext.debugger_invoke = TRUE;
                memcpy (&ext.ctx, &invoke->ctx, sizeof (MonoContext));
@@ -5870,10 +6380,12 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
                        buffer_add_value (buf, sig->ret, &res, domain);
                } else if (mono_class_from_mono_type (sig->ret)->valuetype || sig->ret->type == MONO_TYPE_PTR || sig->ret->type == MONO_TYPE_FNPTR) {
                        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);
+                               MonoClass *k = mono_class_from_mono_type (sig->ret);
+                               guint8 *nullable_buf = g_alloca (mono_class_value_size (k, NULL));
+
+                               g_assert (nullable_buf);
+                               mono_nullable_init (nullable_buf, res, k);
+                               buffer_add_value (buf, sig->ret, nullable_buf, domain);
                        } else {
                                g_assert (res);
                                buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain);
@@ -5890,6 +6402,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
                mono_set_lmf ((gpointer)(((gssize)ext.lmf.previous_lmf) & ~3));
 #endif
 
+       *endp = p;
        // FIXME: byref arguments
        // FIXME: varargs
        return ERR_NONE;
@@ -5906,13 +6419,10 @@ invoke_method (void)
        DebuggerTlsData *tls;
        InvokeData *invoke;
        int id;
-       int i, err;
+       int i, err, mindex;
        Buffer buf;
-       static void (*restore_context) (void *);
        MonoContext restore_ctx;
-
-       if (!restore_context)
-               restore_context = mono_get_restore_context ();
+       guint8 *p;
 
        tls = mono_native_tls_get_value (debugger_tls_id);
        g_assert (tls);
@@ -5937,19 +6447,29 @@ invoke_method (void)
 
        id = invoke->id;
 
-       buffer_init (&buf, 128);
+       p = invoke->p;
+       err = 0;
+       for (mindex = 0; mindex < invoke->nmethods; ++mindex) {
+               buffer_init (&buf, 128);
 
-       err = do_invoke_method (tls, &buf, invoke);
+               if (err) {
+                       /* Fail the other invokes as well */
+               } else {
+                       err = do_invoke_method (tls, &buf, invoke, p, &p);
+               }
 
-       /* Start suspending before sending the reply */
-       if (!(invoke->flags & INVOKE_FLAG_SINGLE_THREADED)) {
-               for (i = 0; i < invoke->suspend_count; ++i)
-                       suspend_vm ();
-       }
+               /* Start suspending before sending the reply */
+               if (mindex == invoke->nmethods - 1) {
+                       if (!(invoke->flags & INVOKE_FLAG_SINGLE_THREADED)) {
+                               for (i = 0; i < invoke->suspend_count; ++i)
+                                       suspend_vm ();
+                       }
+               }
 
-       send_reply_packet (id, err, &buf);
+               send_reply_packet (id, err, &buf);
        
-       buffer_free (&buf);
+               buffer_free (&buf);
+       }
 
        memcpy (&restore_ctx, &invoke->ctx, sizeof (MonoContext));
 
@@ -5961,7 +6481,7 @@ invoke_method (void)
                tls->resume_count -= invoke->suspend_count;
        }
 
-       DEBUG (1, fprintf (log_file, "[%p] Invoke finished, resume_count = %d.\n", (gpointer)GetCurrentThreadId (), tls->resume_count));
+       DEBUG (1, fprintf (log_file, "[%p] Invoke finished (%d), resume_count = %d.\n", (gpointer)GetCurrentThreadId (), err, tls->resume_count));
 
        /*
         * Take the loader lock to avoid race conditions with CMD_VM_ABORT_INVOKE:
@@ -6019,7 +6539,7 @@ get_source_files_for_type (MonoClass *klass)
                GPtrArray *source_file_list;
 
                if (minfo) {
-                       mono_debug_symfile_get_line_numbers_full (minfo, NULL, &source_file_list, NULL, NULL, NULL, NULL);
+                       mono_debug_symfile_get_line_numbers_full (minfo, NULL, &source_file_list, NULL, NULL, NULL, NULL, NULL);
                        for (j = 0; j < source_file_list->len; ++j) {
                                sinfo = g_ptr_array_index (source_file_list, j);
                                for (i = 0; i < files->len; ++i)
@@ -6075,6 +6595,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                if (suspend_count == 0)
                        return ERR_NOT_SUSPENDED;
                resume_vm ();
+               clear_suspended_objs ();
                break;
        case CMD_VM_DISPOSE:
                /* Clear all event requests */
@@ -6094,7 +6615,9 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
        case CMD_VM_EXIT: {
                MonoInternalThread *thread;
                DebuggerTlsData *tls;
+#ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT
                MonoClass *env_class;
+#endif
                MonoMethod *exit_method = NULL;
                gpointer *args;
                int exit_code;
@@ -6125,9 +6648,11 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                suspend_vm ();
                wait_for_suspend ();
 
+#ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT
                env_class = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
                if (env_class)
                        exit_method = mono_class_get_method_from_name (env_class, "Exit", 1);
+#endif
 
                mono_loader_lock ();
                thread = mono_g_hash_table_find (tid_to_thread, is_really_suspended, NULL);
@@ -6145,6 +6670,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                        tls->pending_invoke = g_new0 (InvokeData, 1);
                        tls->pending_invoke->method = exit_method;
                        tls->pending_invoke->args = args;
+                       tls->pending_invoke->nmethods = 1;
 
                        while (suspend_count > 0)
                                resume_vm ();
@@ -6156,9 +6682,10 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                        while (suspend_count > 0)
                                resume_vm ();
 
-                       mono_runtime_set_shutting_down ();
+                       if (!mono_runtime_try_shutdown ())
+                               break;
 
-                       mono_threads_set_shutting_down ();
+                       mono_environment_exitcode_set (exit_code);
 
                        /* Suspend all managed threads since the runtime is going away */
                        DEBUG(1, fprintf (log_file, "Suspending all threads...\n"));
@@ -6172,11 +6699,12 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                }
                break;
        }               
-       case CMD_VM_INVOKE_METHOD: {
+       case CMD_VM_INVOKE_METHOD:
+       case CMD_VM_INVOKE_METHODS: {
                int objid = decode_objid (p, &p, end);
                MonoThread *thread;
                DebuggerTlsData *tls;
-               int i, count, err, flags;
+               int i, count, err, flags, nmethods;
 
                err = get_object (objid, (MonoObject**)&thread);
                if (err)
@@ -6184,6 +6712,11 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
 
                flags = decode_int (p, &p, end);
 
+               if (command == CMD_VM_INVOKE_METHODS)
+                       nmethods = decode_int (p, &p, end);
+               else
+                       nmethods = 1;
+
                // Wait for suspending if it already started
                if (suspend_count)
                        wait_for_suspend ();
@@ -6212,6 +6745,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                memcpy (tls->pending_invoke->p, p, end - p);
                tls->pending_invoke->endp = tls->pending_invoke->p + (end - p);
                tls->pending_invoke->suspend_count = suspend_count;
+               tls->pending_invoke->nmethods = nmethods;
 
                if (flags & INVOKE_FLAG_SINGLE_THREADED) {
                        resume_thread (THREAD_TO_INTERNAL (thread));
@@ -6478,6 +7012,9 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                                if (CHECK_PROTOCOL_VERSION (2, 16))
                                        filter = decode_int (p, &p, end);
                                req->modifiers [i].data.filter = filter;
+                               if (!CHECK_PROTOCOL_VERSION (2, 26) && (req->modifiers [i].data.filter & STEP_FILTER_DEBUGGER_HIDDEN))
+                                       /* Treat STEP_THOUGH the same as HIDDEN */
+                                       req->modifiers [i].data.filter |= STEP_FILTER_DEBUGGER_STEP_THROUGH;
                        } else if (mod == MOD_KIND_THREAD_ONLY) {
                                int id = decode_id (p, &p, end);
 
@@ -6493,7 +7030,11 @@ 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 (CHECK_PROTOCOL_VERSION (2, 25))
+                                       req->modifiers [i].subclasses = decode_byte (p, &p, end);
+                               else
+                                       req->modifiers [i].subclasses = TRUE;
+                               DEBUG(1, fprintf (log_file, "[dbg] \tEXCEPTION_ONLY filter (%s%s%s%s).\n", exc_class ? exc_class->name : "all", req->modifiers [i].caught ? ", caught" : "", req->modifiers [i].uncaught ? ", uncaught" : "", req->modifiers [i].subclasses ? ", include-subclasses" : ""));
                                if (exc_class) {
                                        req->modifiers [i].data.exc_class = exc_class;
 
@@ -6871,6 +7412,29 @@ module_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
        return ERR_NONE;
 }
 
+static ErrorCode
+field_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       int err;
+       MonoDomain *domain;
+
+       switch (command) {
+       case CMD_FIELD_GET_INFO: {
+               MonoClassField *f = decode_fieldid (p, &p, end, &domain, &err);
+
+               buffer_add_string (buf, f->name);
+               buffer_add_typeid (buf, domain, f->parent);
+               buffer_add_typeid (buf, domain, mono_class_from_mono_type (f->type));
+               buffer_add_int (buf, f->type->attrs);
+               break;
+       }
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
 static void
 buffer_add_cattr_arg (Buffer *buf, MonoType *t, MonoDomain *domain, MonoObject *val)
 {
@@ -6907,9 +7471,11 @@ buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass
                if (!attr_klass || mono_class_has_parent (attr->ctor->klass, attr_klass)) {
                        MonoArray *typed_args, *named_args;
                        MonoType *t;
-                       CattrNamedArg *arginfo;
+                       CattrNamedArg *arginfo = NULL;
+                       MonoError error;
 
-                       mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo);
+                       mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo, &error);
+                       g_assert (mono_error_ok (&error));
 
                        buffer_add_methodid (buf, domain, attr->ctor);
 
@@ -6949,6 +7515,7 @@ buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass
                        } else {
                                buffer_add_int (buf, 0);
                        }
+                       g_free (arginfo);
                }
        }
 }
@@ -7388,6 +7955,15 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint
                }
                break;
        }
+       case CMD_TYPE_IS_INITIALIZED: {
+               MonoVTable *vtable = mono_class_vtable (domain, klass);
+
+               if (vtable)
+                       buffer_add_int (buf, (vtable->initialized || vtable->init_failed) ? 1 : 0);
+               else
+                       buffer_add_int (buf, 0);
+               break;
+       }
        default:
                return ERR_NOT_IMPLEMENTED;
        }
@@ -7422,6 +7998,7 @@ static ErrorCode
 method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, guint8 *p, guint8 *end, Buffer *buf)
 {
        MonoMethodHeader *header;
+       int err;
 
        switch (command) {
        case CMD_METHOD_GET_NAME: {
@@ -7438,6 +8015,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
                int i, j, n_il_offsets;
                int *il_offsets;
                int *line_numbers;
+               int *column_numbers;
                int *source_files;
                GPtrArray *source_file_list;
 
@@ -7458,7 +8036,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
                        break;
                }
 
-               mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, &n_il_offsets, &il_offsets, &line_numbers, &source_files);
+               mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, &n_il_offsets, &il_offsets, &line_numbers, &column_numbers, &source_files);
                buffer_add_int (buf, header->code_size);
                if (CHECK_PROTOCOL_VERSION (2, 13)) {
                        buffer_add_int (buf, source_file_list->len);
@@ -7474,7 +8052,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
                        buffer_add_string (buf, source_file);
                }
                buffer_add_int (buf, n_il_offsets);
-               DEBUG (10, printf ("Line number table for method %s:\n", mono_method_full_name (method,  TRUE)));
+               DEBUG (10, fprintf (log_file, "Line number table for method %s:\n", mono_method_full_name (method,  TRUE)));
                for (i = 0; i < n_il_offsets; ++i) {
                        const char *srcfile = "";
 
@@ -7482,11 +8060,13 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
                                MonoDebugSourceInfo *sinfo = g_ptr_array_index (source_file_list, source_files [i]);
                                srcfile = sinfo->source_file;
                        }
-                       DEBUG (10, printf ("IL%x -> %s:%d\n", il_offsets [i], srcfile, line_numbers [i]));
+                       DEBUG (10, fprintf (log_file, "IL%x -> %s:%d %d\n", il_offsets [i], srcfile, line_numbers [i], column_numbers ? column_numbers [i] : -1));
                        buffer_add_int (buf, il_offsets [i]);
                        buffer_add_int (buf, line_numbers [i]);
                        if (CHECK_PROTOCOL_VERSION (2, 13))
                                buffer_add_int (buf, source_files [i]);
+                       if (CHECK_PROTOCOL_VERSION (2, 19))
+                               buffer_add_int (buf, column_numbers ? column_numbers [i] : -1);
                }
                g_free (source_file);
                g_free (il_offsets);
@@ -7644,12 +8224,34 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
                header = mono_method_get_header (method);
                if (!header) {
                        buffer_add_int (buf, 0);
+
+                       if (CHECK_PROTOCOL_VERSION (2, 18))
+                               buffer_add_int (buf, 0);
                } else {
                        buffer_add_int (buf, header->code_size);
                        for (i = 0; i < header->code_size; ++i)
                                buffer_add_byte (buf, header->code [i]);
+
+                       if (CHECK_PROTOCOL_VERSION (2, 18)) {
+                               buffer_add_int (buf, header->num_clauses);
+                               for (i = 0; i < header->num_clauses; ++i) {
+                                       MonoExceptionClause *clause = &header->clauses [i];
+
+                                       buffer_add_int (buf, clause->flags);
+                                       buffer_add_int (buf, clause->try_offset);
+                                       buffer_add_int (buf, clause->try_len);
+                                       buffer_add_int (buf, clause->handler_offset);
+                                       buffer_add_int (buf, clause->handler_len);
+                                       if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE)
+                                               buffer_add_typeid (buf, domain, clause->data.catch_class);
+                                       else if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER)
+                                               buffer_add_int (buf, clause->data.filter_offset);
+                               }
+                       }
+
+                       mono_metadata_free_mh (header);
                }
-               mono_metadata_free_mh (header);
+
                break;
        }
        case CMD_METHOD_RESOLVE_TOKEN: {
@@ -7716,6 +8318,54 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
                }
                break;
        }
+       case CMD_METHOD_GET_CATTRS: {
+               MonoClass *attr_klass;
+               MonoCustomAttrInfo *cinfo;
+
+               attr_klass = decode_typeid (p, &p, end, NULL, &err);
+               /* attr_klass can be NULL */
+               if (err)
+                       return err;
+
+               cinfo = mono_custom_attrs_from_method (method);
+
+               buffer_add_cattrs (buf, domain, method->klass->image, attr_klass, cinfo);
+               break;
+       }
+       case CMD_METHOD_MAKE_GENERIC_METHOD: {
+               MonoType **type_argv;
+               int i, type_argc;
+               MonoDomain *d;
+               MonoClass *klass;
+               MonoGenericInst *ginst;
+               MonoGenericContext tmp_context;
+               MonoMethod *inflated;
+
+               type_argc = decode_int (p, &p, end);
+               type_argv = g_new0 (MonoType*, type_argc);
+               for (i = 0; i < type_argc; ++i) {
+                       klass = decode_typeid (p, &p, end, &d, &err);
+                       if (err) {
+                               g_free (type_argv);
+                               return err;
+                       }
+                       if (domain != d) {
+                               g_free (type_argv);
+                               return ERR_INVALID_ARGUMENT;
+                       }
+                       type_argv [i] = &klass->byval_arg;
+               }
+               ginst = mono_metadata_get_generic_inst (type_argc, type_argv);
+               g_free (type_argv);
+               tmp_context.class_inst = method->klass->generic_class ? method->klass->generic_class->context.class_inst : NULL;
+               tmp_context.method_inst = ginst;
+
+               inflated = mono_class_inflate_generic_method (method, &tmp_context);
+               if (!mono_verifier_is_method_valid_generic_instantiation (inflated))
+                       return ERR_INVALID_ARGUMENT;
+               buffer_add_methodid (buf, domain, inflated);
+               break;
+       }
        default:
                return ERR_NOT_IMPLEMENTED;
        }
@@ -7837,6 +8487,52 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
        case CMD_THREAD_GET_TID:
                buffer_add_long (buf, (guint64)thread->tid);
                break;
+       case CMD_THREAD_SET_IP: {
+               DebuggerTlsData *tls;
+               MonoMethod *method;
+               MonoDomain *domain;
+               MonoSeqPointInfo *seq_points;
+               SeqPoint *sp = NULL;
+               gint64 il_offset;
+               int i;
+
+               method = decode_methodid (p, &p, end, &domain, &err);
+               if (err)
+                       return err;
+               il_offset = decode_long (p, &p, end);
+
+               while (!is_suspended ()) {
+                       if (suspend_count)
+                               wait_for_suspend ();
+               }
+
+               mono_loader_lock ();
+               tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+               mono_loader_unlock ();
+               g_assert (tls);
+
+               compute_frame_info (thread, tls);
+               if (tls->frame_count == 0 || tls->frames [0]->actual_method != method)
+                       return ERR_INVALID_ARGUMENT;
+
+               seq_points = get_seq_points (domain, method);
+               g_assert (seq_points);
+
+               for (i = 0; i < seq_points->len; ++i) {
+                       sp = &seq_points->seq_points [i];
+
+                       if (sp->il_offset == il_offset)
+                               break;
+               }
+               if (i == seq_points->len)
+                       return ERR_INVALID_ARGUMENT;
+
+               // FIXME: Check that the ip change is safe
+
+               DEBUG (1, fprintf (log_file, "[dbg] Setting IP to %s:0x%0x(0x%0x)\n", tls->frames [0]->actual_method->name, (int)sp->il_offset, (int)sp->native_offset));
+               MONO_CONTEXT_SET_IP (&tls->restore_ctx, (guint8*)tls->frames [0]->ji->code_start + sp->native_offset);
+               break;
+       }
        default:
                return ERR_NOT_IMPLEMENTED;
        }
@@ -7885,18 +8581,17 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
        frame = tls->frames [frame_idx];
 
        if (!frame->has_ctx)
-               // FIXME:
-               return ERR_INVALID_FRAMEID;
+               return ERR_ABSENT_INFORMATION;
 
        if (!frame->jit) {
-               frame->jit = mono_debug_find_method (frame->method, frame->domain);
-               if (!frame->jit && frame->method->is_inflated)
-                       frame->jit = mono_debug_find_method (mono_method_get_declaring_generic_method (frame->method), frame->domain);
+               frame->jit = mono_debug_find_method (frame->api_method, frame->domain);
+               if (!frame->jit && frame->api_method->is_inflated)
+                       frame->jit = mono_debug_find_method (mono_method_get_declaring_generic_method (frame->api_method), frame->domain);
                if (!frame->jit) {
                        char *s;
 
                        /* This could happen for aot images with no jit debug info */
-                       s = mono_method_full_name (frame->method, TRUE);
+                       s = mono_method_full_name (frame->api_method, TRUE);
                        DEBUG (1, fprintf (log_file, "[dbg] No debug information found for '%s'.\n", s));
                        g_free (s);
                        return ERR_ABSENT_INFORMATION;
@@ -7927,32 +8622,32 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
                                var = &jit->params [pos];
 
-                               add_var (buf, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain, FALSE);
+                               add_var (buf, jit, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain, FALSE);
                        } else {
                                g_assert (pos >= 0 && pos < jit->num_locals);
 
                                var = &jit->locals [pos];
                                
-                               add_var (buf, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE);
+                               add_var (buf, jit, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE);
                        }
                }
                mono_metadata_free_mh (header);
                break;
        }
        case CMD_STACK_FRAME_GET_THIS: {
-               if (frame->method->klass->valuetype) {
+               if (frame->api_method->klass->valuetype) {
                        if (!sig->hasthis) {
                                MonoObject *p = NULL;
                                buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &p, frame->domain);
                        } else {
-                               add_var (buf, &frame->actual_method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
+                               add_var (buf, jit, &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->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);
+                               add_var (buf, jit, &frame->api_method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
                        }
                }
                break;
@@ -8129,7 +8824,8 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
        switch (command) {
        case CMD_OBJECT_REF_GET_TYPE:
-               buffer_add_typeid (buf, obj->vtable->domain, obj->vtable->klass);
+               /* This handles transparent proxies too */
+               buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type (((MonoReflectionType*)obj->vtable->type)->type));
                break;
        case CMD_OBJECT_REF_GET_VALUES:
                len = decode_int (p, &p, end);
@@ -8219,7 +8915,7 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                buffer_add_domainid (buf, obj->vtable->domain);
                break;
        case CMD_OBJECT_REF_GET_INFO:
-               buffer_add_typeid (buf, obj->vtable->domain, obj->vtable->klass);
+               buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type (((MonoReflectionType*)obj->vtable->type)->type));
                buffer_add_domainid (buf, obj->vtable->domain);
                break;
        default:
@@ -8238,65 +8934,225 @@ command_set_to_string (CommandSet command_set)
        case CMD_SET_OBJECT_REF:
                return "OBJECT_REF";
        case CMD_SET_STRING_REF:
-               return "STRING_REF"; 
+               return "STRING_REF";
        case CMD_SET_THREAD:
-               return "THREAD"; 
+               return "THREAD";
        case CMD_SET_ARRAY_REF:
-               return "ARRAY_REF"; 
+               return "ARRAY_REF";
        case CMD_SET_EVENT_REQUEST:
-               return "EVENT_REQUEST"; 
+               return "EVENT_REQUEST";
        case CMD_SET_STACK_FRAME:
-               return "STACK_FRAME"; 
+               return "STACK_FRAME";
        case CMD_SET_APPDOMAIN:
-               return "APPDOMAIN"; 
+               return "APPDOMAIN";
        case CMD_SET_ASSEMBLY:
-               return "ASSEMBLY"; 
+               return "ASSEMBLY";
        case CMD_SET_METHOD:
-               return "METHOD"; 
+               return "METHOD";
        case CMD_SET_TYPE:
-               return "TYPE"; 
+               return "TYPE";
        case CMD_SET_MODULE:
-               return "MODULE"; 
+               return "MODULE";
+       case CMD_SET_FIELD:
+               return "FIELD";
        case CMD_SET_EVENT:
-               return "EVENT"; 
+               return "EVENT";
        default:
                return "";
        }
 }
 
+static const char* vm_cmds_str [] = {
+       "VERSION",
+       "ALL_THREADS",
+       "SUSPEND",
+       "RESUME",
+       "EXIT",
+       "DISPOSE",
+       "INVOKE_METHOD",
+       "SET_PROTOCOL_VERSION",
+       "ABORT_INVOKE",
+       "SET_KEEPALIVE"
+       "GET_TYPES_FOR_SOURCE_FILE",
+       "GET_TYPES",
+       "INVOKE_METHODS"
+};
+
+static const char* thread_cmds_str[] = {
+       "GET_FRAME_INFO",
+       "GET_NAME",
+       "GET_STATE",
+       "GET_INFO",
+       "GET_ID",
+       "GET_TID",
+       "SET_IP"
+};
+
+static const char* event_cmds_str[] = {
+       "REQUEST_SET",
+       "REQUEST_CLEAR",
+       "REQUEST_CLEAR_ALL_BREAKPOINTS"
+};
+
+static const char* appdomain_cmds_str[] = {
+       "GET_ROOT_DOMAIN",
+       "GET_FRIENDLY_NAME",
+       "GET_ASSEMBLIES",
+       "GET_ENTRY_ASSEMBLY",
+       "CREATE_STRING",
+       "GET_CORLIB",
+       "CREATE_BOXED_VALUE"
+};
+
+static const char* assembly_cmds_str[] = {
+       "GET_LOCATION",
+       "GET_ENTRY_POINT",
+       "GET_MANIFEST_MODULE",
+       "GET_OBJECT",
+       "GET_TYPE",
+       "GET_NAME"
+};
+
+static const char* module_cmds_str[] = {
+       "GET_INFO",
+};
+
+static const char* field_cmds_str[] = {
+       "GET_INFO",
+};
+
+static const char* method_cmds_str[] = {
+       "GET_NAME",
+       "GET_DECLARING_TYPE",
+       "GET_DEBUG_INFO",
+       "GET_PARAM_INFO",
+       "GET_LOCALS_INFO",
+       "GET_INFO",
+       "GET_BODY",
+       "RESOLVE_TOKEN",
+       "GET_CATTRS ",
+       "MAKE_GENERIC_METHOD"
+};
+
+static const char* type_cmds_str[] = {
+       "GET_INFO",
+       "GET_METHODS",
+       "GET_FIELDS",
+       "GET_VALUES",
+       "GET_OBJECT",
+       "GET_SOURCE_FILES",
+       "SET_VALUES",
+       "IS_ASSIGNABLE_FROM",
+       "GET_PROPERTIES ",
+       "GET_CATTRS",
+       "GET_FIELD_CATTRS",
+       "GET_PROPERTY_CATTRS",
+       "GET_SOURCE_FILES_2",
+       "GET_VALUES_2",
+       "GET_METHODS_BY_NAME_FLAGS",
+       "GET_INTERFACES",
+       "GET_INTERFACE_MAP",
+       "IS_INITIALIZED"
+};
+
+static const char* stack_frame_cmds_str[] = {
+       "GET_VALUES",
+       "GET_THIS",
+       "SET_VALUES"
+};
+
+static const char* array_cmds_str[] = {
+       "GET_LENGTH",
+       "GET_VALUES",
+       "SET_VALUES",
+};
+
+static const char* string_cmds_str[] = {
+       "GET_VALUE",
+       "GET_LENGTH",
+       "GET_CHARS"
+};
+
+static const char* object_cmds_str[] = {
+       "GET_TYPE",
+       "GET_VALUES",
+       "IS_COLLECTED",
+       "GET_ADDRESS",
+       "GET_DOMAIN",
+       "SET_VALUES",
+       "GET_INFO",
+};
+
 static const char*
 cmd_to_string (CommandSet set, int command)
 {
+       const char **cmds;
+       int cmds_len = 0;
+
        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;
-               }
+       case CMD_SET_VM:
+               cmds = vm_cmds_str;
+               cmds_len = G_N_ELEMENTS (vm_cmds_str);
                break;
-       }
-       default:
+       case CMD_SET_OBJECT_REF:
+               cmds = object_cmds_str;
+               cmds_len = G_N_ELEMENTS (object_cmds_str);
+               break;
+       case CMD_SET_STRING_REF:
+               cmds = string_cmds_str;
+               cmds_len = G_N_ELEMENTS (string_cmds_str);
+               break;
+       case CMD_SET_THREAD:
+               cmds = thread_cmds_str;
+               cmds_len = G_N_ELEMENTS (thread_cmds_str);
+               break;
+       case CMD_SET_ARRAY_REF:
+               cmds = array_cmds_str;
+               cmds_len = G_N_ELEMENTS (array_cmds_str);
+               break;
+       case CMD_SET_EVENT_REQUEST:
+               cmds = event_cmds_str;
+               cmds_len = G_N_ELEMENTS (event_cmds_str);
+               break;
+       case CMD_SET_STACK_FRAME:
+               cmds = stack_frame_cmds_str;
+               cmds_len = G_N_ELEMENTS (stack_frame_cmds_str);
+               break;
+       case CMD_SET_APPDOMAIN:
+               cmds = appdomain_cmds_str;
+               cmds_len = G_N_ELEMENTS (appdomain_cmds_str);
                break;
+       case CMD_SET_ASSEMBLY:
+               cmds = assembly_cmds_str;
+               cmds_len = G_N_ELEMENTS (assembly_cmds_str);
+               break;
+       case CMD_SET_METHOD:
+               cmds = method_cmds_str;
+               cmds_len = G_N_ELEMENTS (method_cmds_str);
+               break;
+       case CMD_SET_TYPE:
+               cmds = type_cmds_str;
+               cmds_len = G_N_ELEMENTS (type_cmds_str);
+               break;
+       case CMD_SET_MODULE:
+               cmds = module_cmds_str;
+               cmds_len = G_N_ELEMENTS (module_cmds_str);
+               break;
+       case CMD_SET_FIELD:
+               cmds = field_cmds_str;
+               cmds_len = G_N_ELEMENTS (field_cmds_str);
+               break;
+       case CMD_SET_EVENT:
+               cmds = event_cmds_str;
+               cmds_len = G_N_ELEMENTS (event_cmds_str);
+               break;
+       default:
+               return NULL;
        }
-       return NULL;
+       if (command > 0 && command <= cmds_len)
+               return cmds [command - 1];
+       else
+               return NULL;
 }
 
 static gboolean
@@ -8370,8 +9226,10 @@ debugger_thread (void *arg)
                res = transport_recv (header, HEADER_LENGTH);
 
                /* This will break if the socket is closed during shutdown too */
-               if (res != HEADER_LENGTH)
+               if (res != HEADER_LENGTH) {
+                       DEBUG (1, fprintf (log_file, "[dbg] transport_recv () returned %d, expected %d.\n", res, HEADER_LENGTH));
                        break;
+               }
 
                p = header;
                end = header + HEADER_LENGTH;
@@ -8394,15 +9252,17 @@ debugger_thread (void *arg)
                                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));
+                       DEBUG (1, fprintf (log_file, "[dbg] Command %s(%s) [%d].\n", command_set_to_string (command_set), cmd_str, id));
                }
 
                data = g_malloc (len - HEADER_LENGTH);
                if (len - HEADER_LENGTH > 0)
                {
                        res = transport_recv (data, len - HEADER_LENGTH);
-                       if (res != len - HEADER_LENGTH)
+                       if (res != len - HEADER_LENGTH) {
+                               DEBUG (1, fprintf (log_file, "[dbg] transport_recv () returned %d, expected %d.\n", res, len - HEADER_LENGTH));
                                break;
+                       }
                }
 
                p = data;
@@ -8433,6 +9293,9 @@ debugger_thread (void *arg)
                case CMD_SET_MODULE:
                        err = module_commands (command, p, end, &buf);
                        break;
+               case CMD_SET_FIELD:
+                       err = field_commands (command, p, end, &buf);
+                       break;
                case CMD_SET_TYPE:
                        err = type_commands (command, p, end, &buf);
                        break;
@@ -8464,7 +9327,7 @@ debugger_thread (void *arg)
                g_free (data);
                buffer_free (&buf);
 
-               if (command_set == CMD_SET_VM && command == CMD_VM_DISPOSE)
+               if (command_set == CMD_SET_VM && (command == CMD_VM_DISPOSE || command == CMD_VM_EXIT))
                        break;
        }