Merge pull request #517 from getsometoast/master
[mono.git] / mono / mini / debugger-agent.c
index b207335b23e2d84f916ef5785bddffb72daf3928..9c42a1f78c29bfa3f369539a6c0f23b9bd20e546 100644 (file)
@@ -76,9 +76,19 @@ int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD,
 #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,10 +97,6 @@ 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>
@@ -162,6 +168,7 @@ struct _InvokeData
        MonoMethod *method;
        gpointer *args;
        guint32 suspend_count;
+       int nmethods;
 
        InvokeData *last_invoke;
 };
@@ -169,7 +176,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;
@@ -276,7 +282,7 @@ typedef struct {
 #define HEADER_LENGTH 11
 
 #define MAJOR_VERSION 2
-#define MINOR_VERSION 18
+#define MINOR_VERSION 22
 
 typedef enum {
        CMD_SET_VM = 1,
@@ -359,7 +365,8 @@ typedef enum {
 
 typedef enum {
        STEP_FILTER_NONE = 0,
-       STEP_FILTER_STATIC_CTOR = 1
+       STEP_FILTER_STATIC_CTOR = 1,
+       STEP_FILTER_DEBUGGER_HIDDEN = 2
 } StepFilter;
 
 typedef enum {
@@ -401,7 +408,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 {
@@ -455,6 +463,7 @@ typedef enum {
        CMD_METHOD_GET_INFO = 6,
        CMD_METHOD_GET_BODY = 7,
        CMD_METHOD_RESOLVE_TOKEN = 8,
+       CMD_METHOD_GET_CATTRS = 9,
 } CmdMethod;
 
 typedef enum {
@@ -667,6 +676,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);
@@ -971,11 +983,6 @@ mono_debugger_agent_init (void)
 
        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
 }
 
 /*
@@ -1054,15 +1061,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;
@@ -1078,7 +1100,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;
@@ -1306,8 +1328,7 @@ socket_transport_connect (const char *address)
 #endif
        }
        
-       disconnected = !transport_handshake ();
-       if (disconnected)
+       if (!transport_handshake ())
                exit (1);
 }
 
@@ -1367,6 +1388,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)
 {
@@ -1429,6 +1459,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)
 {
@@ -1436,6 +1472,8 @@ transport_handshake (void)
        guint8 buf [128];
        int res;
        
+       disconnected = TRUE;
+       
        /* Write handshake message */
        sprintf (handshake_msg, "DWP-Handshake");
        do {
@@ -1464,7 +1502,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,
@@ -1477,6 +1515,7 @@ transport_handshake (void)
        set_keepalive ();
 #endif
        
+       disconnected = FALSE;
        return TRUE;
 }
 
@@ -1777,6 +1816,7 @@ objrefs_cleanup (void)
 }
 
 static GHashTable *obj_to_objref;
+static MonoGHashTable *suspended_objs;
 
 /*
  * Return an ObjRef for OBJ.
@@ -1793,8 +1833,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 */
 
@@ -1841,6 +1891,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)
 {
@@ -2247,9 +2311,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
@@ -2441,8 +2502,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);
 }
@@ -2522,20 +2585,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
 }
@@ -2892,6 +2963,7 @@ find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, g
        int i;
 
        seq_points = find_seq_points (domain, method);
+       g_assert (seq_points);
        if (info)
                *info = seq_points;
 
@@ -2914,9 +2986,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)
@@ -3269,7 +3343,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);
                                                                /*
@@ -3298,6 +3372,26 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo
                                                (ji->method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
                                                !strcmp (ji->method->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 (ji->method);
+                                                       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;
+                                       }
                                }
                        }
 
@@ -3611,7 +3705,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);
@@ -3930,7 +4023,7 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo
                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);
 
                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);
@@ -4304,6 +4397,7 @@ process_breakpoint_inner (DebuggerTlsData *tls)
         * 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);
+       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 (), ji->method->name, ip, native_offset, sp ? sp->il_offset : -1));
 
@@ -5060,13 +5154,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;
 
@@ -5129,15 +5245,45 @@ 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] == catch_ji->method->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) {
+       if (tls && ei.caught && catch_ctx) {
                tls->catch_ctx = *catch_ctx;
                tls->has_catch_ctx = TRUE;
        }
@@ -5796,9 +5942,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;
@@ -5997,6 +6142,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;
@@ -6013,10 +6159,11 @@ 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;
+       guint8 *p;
 
        if (!restore_context)
                restore_context = mono_get_restore_context ();
@@ -6044,19 +6191,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));
 
@@ -6126,7 +6283,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)
@@ -6182,6 +6339,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 */
@@ -6232,9 +6390,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);
@@ -6252,6 +6412,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 ();
@@ -6279,11 +6440,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)
@@ -6291,6 +6453,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 ();
@@ -6319,6 +6486,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));
@@ -7529,6 +7697,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: {
@@ -7545,6 +7714,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;
 
@@ -7565,7 +7735,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);
@@ -7589,11 +7759,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, fprintf (log_file, "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);
@@ -7845,6 +8017,20 @@ 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;
+       }
        default:
                return ERR_NOT_IMPLEMENTED;
        }
@@ -8418,6 +8604,8 @@ cmd_to_string (CommandSet set, int command)
                        return "SET_PROTOCOL_VERSION";
                case CMD_VM_ABORT_INVOKE:
                        return "ABORT_INVOKE";
+               case CMD_VM_SET_KEEPALIVE:
+                       return "SET_KEEPALIVE";
                default:
                        break;
                }
@@ -8500,8 +8688,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;
@@ -8531,8 +8721,10 @@ debugger_thread (void *arg)
                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;