Don't use a fixed file name for LLVM compilation.
[mono.git] / mono / mini / debugger-agent.c
index 4d07c8de11816a8c019c2fa43006c52d30609cad..2c47fa815020437f13709f6fb4c3748bd5d7279a 100644 (file)
@@ -76,6 +76,7 @@ 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"
 
@@ -87,10 +88,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>
@@ -128,8 +125,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 +159,7 @@ struct _InvokeData
        MonoMethod *method;
        gpointer *args;
        guint32 suspend_count;
+       int nmethods;
 
        InvokeData *last_invoke;
 };
@@ -163,7 +167,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;
@@ -270,7 +273,7 @@ typedef struct {
 #define HEADER_LENGTH 11
 
 #define MAJOR_VERSION 2
-#define MINOR_VERSION 17
+#define MINOR_VERSION 22
 
 typedef enum {
        CMD_SET_VM = 1,
@@ -353,7 +356,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 {
@@ -395,7 +399,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 {
@@ -449,6 +454,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 {
@@ -739,6 +745,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);
@@ -862,6 +870,8 @@ mono_debugger_agent_parse_options (char *options)
                }
        }
 
+       //agent_config.log_level = 0;
+
        if (agent_config.transport == NULL) {
                fprintf (stderr, "debugger-agent: The 'transport' option is mandatory.\n");
                exit (1);
@@ -951,13 +961,16 @@ mono_debugger_agent_init (void)
        mono_disable_optimizations (MONO_OPT_LINEARS);
 #endif
 
+       /*
+        * The stack walk done from thread_interrupt () needs to be signal safe, but it
+        * isn't, since it can call into mono_aot_find_jit_info () which is not signal
+        * safe (#3411). So load AOT info eagerly when the debugger is running as a
+        * workaround.
+        */
+       mini_get_debug_options ()->load_aot_jit_info_eagerly = TRUE;
+
        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
 }
 
 /*
@@ -1036,15 +1049,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;
@@ -1060,7 +1088,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;
@@ -1175,7 +1203,7 @@ socket_transport_connect (const char *address)
 
                        addrlen = sizeof (addr);
                        memset (&addr, 0, sizeof (addr));
-                       res = getsockname (sfd, &addr, &addrlen);
+                       res = getsockname (sfd, (struct sockaddr*)&addr, &addrlen);
                        g_assert (res == 0);
 
                        host = (char*)"127.0.0.1";
@@ -1288,8 +1316,7 @@ socket_transport_connect (const char *address)
 #endif
        }
        
-       disconnected = !transport_handshake ();
-       if (disconnected)
+       if (!transport_handshake ())
                exit (1);
 }
 
@@ -1418,6 +1445,8 @@ transport_handshake (void)
        guint8 buf [128];
        int res;
        
+       disconnected = TRUE;
+       
        /* Write handshake message */
        sprintf (handshake_msg, "DWP-Handshake");
        do {
@@ -1446,7 +1475,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,
@@ -1459,6 +1488,7 @@ transport_handshake (void)
        set_keepalive ();
 #endif
        
+       disconnected = FALSE;
        return TRUE;
 }
 
@@ -2280,7 +2310,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;
        }
 }
@@ -2332,7 +2361,7 @@ thread_interrupt (DebuggerTlsData *tls, MonoThreadInfo *info, void *sigctx, Mono
        if (info)
                tid = mono_thread_info_get_tid (info);
        else
-               tid = GetCurrentThreadId ();
+               tid = (MonoNativeThreadId)GetCurrentThreadId ();
 
        // FIXME: Races when the thread leaves managed code before hitting a single step
        // event.
@@ -2424,8 +2453,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);
 }
@@ -2441,15 +2472,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);
+       }
 }
 
 /*
@@ -2506,11 +2545,13 @@ notify_thread (gpointer key, gpointer value, gpointer user_data)
                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
 }
@@ -2608,7 +2649,7 @@ resume_vm (void)
        g_assert (suspend_count > 0);
        suspend_count --;
 
-       DEBUG(1, fprintf (log_file, "[%p] Resuming vm...\n", (gpointer)GetCurrentThreadId ()));
+       DEBUG(1, fprintf (log_file, "[%p] Resuming vm, suspend count=%d...\n", (gpointer)GetCurrentThreadId (), suspend_count));
 
        if (suspend_count == 0) {
                // FIXME: Is it safe to call this inside the lock ?
@@ -2758,7 +2799,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
@@ -2938,7 +2979,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;
 
@@ -2956,11 +2997,12 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
        else
                method = info->method;
        actual_method = info->actual_method;
+       api_method = method;
 
        if (!method)
                return FALSE;
 
-       if (!method || !info->managed || (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD && method->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE))
+       if (!method || (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD && method->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE))
                return FALSE;
 
        if (info->il_offset == -1) {
@@ -2980,16 +3022,23 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
                if (!CHECK_PROTOCOL_VERSION (2, 17))
                        /* Older clients can't handle this flag */
                        return FALSE;
+               api_method = mono_marshal_method_from_wrapper (method);
+               if (!api_method)
+                       return FALSE;
+               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;
-       memcpy (frame->reg_locations, info->reg_locations, MONO_MAX_IREGS * sizeof (mgreg_t*));
+       frame->ji = info->ji;
+       if (info->reg_locations)
+               memcpy (frame->reg_locations, info->reg_locations, MONO_MAX_IREGS * sizeof (mgreg_t*));
        if (ctx) {
                frame->ctx = *ctx;
                frame->has_ctx = TRUE;
@@ -3236,7 +3285,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);
                                                                /*
@@ -3265,6 +3314,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;
+                                       }
                                }
                        }
 
@@ -3578,7 +3647,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);
@@ -3608,13 +3676,15 @@ thread_end (MonoProfiler *prof, uintptr_t tid)
        mono_loader_lock ();
        thread = mono_g_hash_table_lookup (tid_to_thread, (gpointer)tid);
        if (thread) {
-               tls = mono_g_hash_table_lookup (thread_to_tls, thread);
-               /* FIXME: Maybe we need to free this instead, but some code can't handle that */
-               tls->terminated = TRUE;
                mono_g_hash_table_remove (tid_to_thread_obj, (gpointer)tid);
-               /* Can't remove from tid_to_thread, as that would defeat the check in thread_start () */
-               MONO_GC_UNREGISTER_ROOT (tls->thread);
-               tls->thread = NULL;
+               tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+               if (tls) {
+                       /* FIXME: Maybe we need to free this instead, but some code can't handle that */
+                       tls->terminated = TRUE;
+                       /* Can't remove from tid_to_thread, as that would defeat the check in thread_start () */
+                       MONO_GC_UNREGISTER_ROOT (tls->thread);
+                       tls->thread = NULL;
+               }
        }
        mono_loader_unlock ();
 
@@ -3895,7 +3965,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);
@@ -4262,8 +4332,6 @@ process_breakpoint_inner (DebuggerTlsData *tls)
        ss_reqs = g_ptr_array_new ();
        ss_reqs_orig = g_ptr_array_new ();
 
-       DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, native_offset));
-
        mono_loader_lock ();
 
        /*
@@ -4272,6 +4340,8 @@ process_breakpoint_inner (DebuggerTlsData *tls)
         */
        sp = find_prev_seq_point_for_native_offset (mono_domain_get (), ji->method, native_offset, &info);
 
+       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));
+
        bp = NULL;
        for (i = 0; i < breakpoints->len; ++i) {
                bp = g_ptr_array_index (breakpoints, i);
@@ -4314,9 +4384,11 @@ process_breakpoint_inner (DebuggerTlsData *tls)
                        if (minfo)
                                loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset);
 
-                       if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line))
+                       if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line)) {
                                /* Have to continue single stepping */
+                               DEBUG(1, fprintf (log_file, "[%p] Same source line, continuing single stepping.\n", (gpointer)GetCurrentThreadId ()));
                                hit = FALSE;
+                       }
                                
                        if (loc) {
                                ss_req->last_method = ji->method;
@@ -4424,7 +4496,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);
 }
 
@@ -4753,7 +4824,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;
@@ -4770,7 +4841,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);
@@ -4792,6 +4863,8 @@ 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) {
                                sp = NULL;
@@ -4814,6 +4887,27 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointI
                        }
                }
 
+               if (ss_req->depth == STEP_DEPTH_OVER) {
+                       /* 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;
@@ -5299,11 +5393,29 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
        buffer_add_value_full (buf, t, addr, domain, FALSE);
 }
 
+static gboolean
+obj_is_of_type (MonoObject *obj, MonoType *t)
+{
+       MonoClass *klass = obj->vtable->klass;
+       if (!mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) {
+               if (klass == mono_defaults.transparent_proxy_class) {
+                       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)
+decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit);
+
+static ErrorCode
+decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
 {
        int err;
-       int type = decode_byte (buf, &buf, limit);
 
        if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) &&
                !(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) &&
@@ -5416,8 +5528,12 @@ 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;
 
@@ -5438,6 +5554,47 @@ 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)
 {
@@ -5512,6 +5669,9 @@ 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 */
@@ -5537,8 +5697,15 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai
 
                //printf ("[R%d+%d] = %p\n", reg, var->offset, addr);
 
+               if (t->byref) {
+                       addr = *(guint8**)addr;
+
+                       if (!addr)
+                               break;
+               }
+                       
                // FIXME: Write barriers
-               memcpy (addr, val, size);
+               mono_gc_memmove (addr, val, size);
                break;
        case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
                NOT_IMPLEMENTED;
@@ -5664,9 +5831,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;
@@ -5733,7 +5899,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);
@@ -5841,12 +6007,14 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
                                buffer_add_value (buf, &mono_defaults.void_class->byval_arg, NULL, domain);
                } else if (MONO_TYPE_IS_REFERENCE (sig->ret)) {
                        buffer_add_value (buf, sig->ret, &res, domain);
-               } else if (mono_class_from_mono_type (sig->ret)->valuetype) {
+               } 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);
@@ -5863,6 +6031,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;
@@ -5879,10 +6048,11 @@ invoke_method (void)
        DebuggerTlsData *tls;
        InvokeData *invoke;
        int id;
-       int 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 ();
@@ -5910,17 +6080,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))
-               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));
 
@@ -5990,7 +6172,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)
@@ -6116,6 +6298,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 ();
@@ -6143,11 +6326,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 err, flags;
+               int i, count, err, flags, nmethods;
 
                err = get_object (objid, (MonoObject**)&thread);
                if (err)
@@ -6155,6 +6339,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 ();
@@ -6175,7 +6364,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                 * resumed.
                 */
                if (tls->pending_invoke)
-                       NOT_IMPLEMENTED;
+                       return ERR_NOT_SUSPENDED;
                tls->pending_invoke = g_new0 (InvokeData, 1);
                tls->pending_invoke->id = id;
                tls->pending_invoke->flags = flags;
@@ -6183,11 +6372,16 @@ 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)
+               if (flags & INVOKE_FLAG_SINGLE_THREADED) {
                        resume_thread (THREAD_TO_INTERNAL (thread));
-               else
-                       resume_vm ();
+               }
+               else {
+                       count = suspend_count;
+                       for (i = 0; i < count; ++i)
+                               resume_vm ();
+               }
                break;
        }
        case CMD_VM_ABORT_INVOKE: {
@@ -6757,7 +6951,7 @@ assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                break;
        }
        case CMD_ASSEMBLY_GET_OBJECT: {
-               MonoObject *o = (MonoObject*)mono_assembly_get_object (mono_domain_get (), ass);
+               MonoObject *o = (MonoObject*)mono_assembly_get_object (domain, ass);
                buffer_add_objid (buf, o);
                break;
        }
@@ -7241,7 +7435,7 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint
                break;
        }
        case CMD_TYPE_GET_OBJECT: {
-               MonoObject *o = (MonoObject*)mono_type_get_object (mono_domain_get (), &klass->byval_arg);
+               MonoObject *o = (MonoObject*)mono_type_get_object (domain, &klass->byval_arg);
                buffer_add_objid (buf, o);
                break;
        }
@@ -7389,6 +7583,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: {
@@ -7405,6 +7600,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;
 
@@ -7425,7 +7621,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);
@@ -7441,7 +7637,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 = "";
 
@@ -7449,11 +7645,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);
@@ -7494,7 +7692,8 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g
                MonoDebugLocalsInfo *locals;
 
                header = mono_method_get_header (method);
-               g_assert (header);
+               if (!header)
+                       return ERR_INVALID_ARGUMENT;
 
                buffer_add_int (buf, header->num_locals);
 
@@ -7610,12 +7809,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: {
@@ -7682,6 +7903,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;
        }
@@ -7855,14 +8090,14 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                return ERR_INVALID_FRAMEID;
 
        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;
@@ -7906,7 +8141,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                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);
@@ -7918,7 +8153,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                                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, &frame->api_method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
                        }
                }
                break;
@@ -8095,7 +8330,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);
@@ -8185,7 +8421,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:
@@ -8254,6 +8490,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;
                }