Merge pull request #273 from joncham/bug-getpid
authorAlex Rønne Petersen <alex@lycus.org>
Sat, 12 Apr 2014 08:29:38 +0000 (10:29 +0200)
committerAlex Rønne Petersen <alex@lycus.org>
Sat, 12 Apr 2014 08:29:38 +0000 (10:29 +0200)
Use getpid instead of GetCurrentProcessId as the latter calls into io-la...

1  2 
mono/mini/debugger-agent.c

index 27c785afc0c2c149ee7b0b49f83922da6424deca,b2c4572e5bae971fa7090d8ee5f2024da6b621c5..9eb0c294976101e19a897b123a68ea96c221124d
@@@ -69,30 -69,16 +69,30 @@@ int WSAAPI getnameinfo(const struct soc
  #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
  #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
@@@ -125,7 -115,6 +125,7 @@@ typedef struct 
        gboolean embedding;
        gboolean defer;
        int keepalive;
 +      gboolean setpgid;
  } AgentConfig;
  
  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];
        /*
@@@ -173,7 -156,6 +173,7 @@@ struct _InvokeDat
        MonoMethod *method;
        gpointer *args;
        guint32 suspend_count;
 +      int nmethods;
  
        InvokeData *last_invoke;
  };
  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;
         * 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
@@@ -285,7 -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,
        CMD_SET_METHOD = 22,
        CMD_SET_TYPE = 23,
        CMD_SET_MODULE = 24,
 +      CMD_SET_FIELD = 25,
        CMD_SET_EVENT = 64
  } CommandSet;
  
@@@ -369,9 -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 {
@@@ -413,8 -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 {
        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 {
@@@ -460,10 -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,
        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 {
        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 {
@@@ -537,7 -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{
@@@ -566,8 -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;
  
  /*
@@@ -690,9 -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);
@@@ -771,8 -739,6 +771,8 @@@ static void finish_agent_init (gboolea
  
  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);
@@@ -808,7 -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");
  }
  
@@@ -832,14 -797,14 +832,14 @@@ mono_debugger_agent_parse_options (cha
        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);
  
                        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);
                /* Waiting for deferred attachment */
                agent_config.defer = TRUE;
                if (agent_config.address == NULL) {
-                       agent_config.address = g_strdup_printf ("0.0.0.0:%u", 56000 + (GetCurrentProcessId () % 1000));
+                       agent_config.address = g_strdup_printf ("0.0.0.0:%u", 56000 + (getpid () % 1000));
                }
        }
  
@@@ -933,7 -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);
         */
        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
  }
  
  /*
@@@ -1083,30 -1046,15 +1083,30 @@@ socket_transport_recv (void *buf, int l
        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;
@@@ -1122,7 -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;
@@@ -1350,7 -1298,8 +1350,7 @@@ socket_transport_connect (const char *a
  #endif
        }
        
 -      disconnected = !transport_handshake ();
 -      if (disconnected)
 +      if (!transport_handshake ())
                exit (1);
  }
  
@@@ -1410,15 -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)
  {
@@@ -1481,12 -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)
  {
        guint8 buf [128];
        int res;
        
 +      disconnected = TRUE;
 +      
        /* Write handshake message */
        sprintf (handshake_msg, "DWP-Handshake");
        do {
         * 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,
        set_keepalive ();
  #endif
        
 +      disconnected = FALSE;
        return TRUE;
  }
  
@@@ -1582,7 -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);
  }
  
@@@ -1836,7 -1769,6 +1836,7 @@@ objrefs_cleanup (void
  }
  
  static GHashTable *obj_to_objref;
 +static MonoGHashTable *suspended_objs;
  
  /*
   * Return an ObjRef for OBJ.
@@@ -1853,18 -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 */
  
        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)
  {
@@@ -2205,7 -2113,6 +2205,7 @@@ decode_ptr_id (guint8 *buf, guint8 **en
        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;
        }
@@@ -2225,17 -2132,7 +2225,17 @@@ buffer_add_ptr_id (Buffer *buf, MonoDom
  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*
@@@ -2253,17 -2150,7 +2253,17 @@@ decode_moduleid (guint8 *buf, guint8 **
  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*
@@@ -2288,29 -2175,12 +2288,29 @@@ static inline voi
  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
@@@ -2369,6 -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
@@@ -2387,7 -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);
  }
@@@ -2417,6 -2290,7 +2417,6 @@@ get_last_frame (StackFrameInfo *info, M
                /* Store the context/lmf for the frame above the last frame */
                memcpy (&data->ctx, ctx, sizeof (MonoContext));
                data->lmf = info->lmf;
 -
                return TRUE;
        }
  }
@@@ -2475,7 -2349,7 +2475,7 @@@ thread_interrupt (DebuggerTlsData *tls
  
        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 {
                /* 
                                /* 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
                                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;
@@@ -2558,10 -2434,8 +2558,10 @@@ mono_debugger_agent_thread_interrupt (v
                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);
  }
@@@ -2577,23 -2451,15 +2577,23 @@@ static void CALLBACK notify_thread_apc 
  /*
   * 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);
 +      }
  }
  
  /*
@@@ -2641,28 -2507,20 +2641,28 @@@ notify_thread (gpointer key, gpointer v
                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
  }
@@@ -2672,7 -2530,6 +2672,7 @@@ process_suspend (DebuggerTlsData *tls, 
  {
        guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
        MonoJitInfo *ji;
 +      MonoMethod *method;
  
        if (mono_loader_lock_is_owned_by_self ()) {
                /*
        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);
@@@ -2739,12 -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 ();
  }
  
@@@ -2783,9 -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 ();
  }
  
@@@ -2811,7 -2658,7 +2811,7 @@@ resume_thread (MonoInternalThread *thre
  
        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;
  
@@@ -2921,7 -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
@@@ -3006,13 -2853,16 +3006,13 @@@ get_seq_points (MonoDomain *domain, Mon
        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));
  }
  
  /*
@@@ -3026,13 -2876,7 +3026,13 @@@ find_next_seq_point_for_native_offset (
        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;
  
@@@ -3055,11 -2899,9 +3055,11 @@@ find_prev_seq_point_for_native_offset (
        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)
@@@ -3106,7 -2948,7 +3106,7 @@@ process_frame (StackFrameInfo *info, Mo
  {
        ComputeFramesUserData *ud = user_data;
        StackFrame *frame;
 -      MonoMethod *method, *actual_method;
 +      MonoMethod *method, *actual_method, *api_method;
        SeqPoint *sp;
        int flags = 0;
  
        }
  
        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;
                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) {
@@@ -3381,9 -3220,7 +3381,9 @@@ create_event_list (EventKind event, GPt
                                        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;
  
                                        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)
                                                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);
                                                                /*
                                                                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);
                                        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;
 +                                      }
                                }
                        }
  
@@@ -3540,7 -3322,6 +3540,7 @@@ event_to_string (EventKind event
        case EVENT_KIND_USER_LOG: return "USER_LOG";
        default:
                g_assert_not_reached ();
 +              return "";
        }
  }
  
@@@ -3559,7 -3340,6 +3559,7 @@@ process_event (EventKind event, gpointe
        GSList *l;
        MonoDomain *domain = mono_domain_get ();
        MonoThread *thread = NULL;
 +      MonoObject *keepalive_obj = NULL;
        gboolean send_success = FALSE;
        static int ecount;
        int nevents;
                        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:
                 */
                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);
@@@ -3824,6 -3593,7 +3824,6 @@@ thread_startup (MonoProfiler *prof, uin
        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);
@@@ -4139,20 -3909,18 +4139,20 @@@ insert_breakpoint (MonoSeqPointInfo *se
        }
  
        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;
                }
        }
  
        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
  #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
@@@ -4199,7 -3965,7 +4199,7 @@@ remove_breakpoint (BreakpointInstance *
  
        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
@@@ -4250,7 -4016,6 +4250,7 @@@ add_pending_breakpoints (MonoMethod *me
        int i, j;
        MonoSeqPointInfo *seq_points;
        MonoDomain *domain;
 +      MonoMethod *jmethod;
  
        if (!breakpoints)
                return;
                }
  
                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 */
@@@ -4471,66 -4235,6 +4471,66 @@@ clear_breakpoints_for_domain (MonoDomai
        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)
  {
@@@ -4550,7 -4254,6 +4550,7 @@@ process_breakpoint_inner (DebuggerTlsDa
        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;
  
        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;   
         */
        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) {
        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)
         * 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 */
@@@ -4662,6 -4379,10 +4662,6 @@@ process_signal_event (void (*func) (Deb
  {
        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. */
        /* 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 ();
  }
  
@@@ -4692,8 -4413,6 +4692,8 @@@ resume_from_signal_handler (void *sigct
        /* 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 
@@@ -4722,6 -4441,7 +4722,6 @@@ mono_debugger_agent_breakpoint_hit (voi
         * 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);
  }
  
@@@ -4789,7 -4509,6 +4789,7 @@@ process_single_step_inner (DebuggerTlsD
        MonoDomain *domain;
        GSList *events;
        MonoContext *ctx = &tls->restore_ctx;
 +      MonoMethod *method;
        SeqPoint *sp;
        MonoSeqPointInfo *info;
  
        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
  
        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
@@@ -4980,8 -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
@@@ -5021,7 -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;
        } 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);
                                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 ++;
                                }
                        }
                        }
                }
  
 +              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;
@@@ -5176,7 -4897,7 +5176,7 @@@ ss_create (MonoInternalThread *thread, 
        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;
                 */
  
                /* 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);
  
                 * 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;
                        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;
                        }
@@@ -5301,35 -5018,13 +5301,35 @@@ mono_debugger_agent_debug_log_is_enable
        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;
  
                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
@@@ -5511,12 -5174,6 +5511,12 @@@ buffer_add_value_full (Buffer *buf, Mon
        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;
        }
@@@ -5659,75 -5316,11 +5659,75 @@@ buffer_add_value (Buffer *buf, MonoTyp
        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) &&
                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)) {
                                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 {
        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;
        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 ();
        }
@@@ -6009,7 -5493,7 +6009,7 @@@ set_var (MonoType *t, MonoDebugVarInfo 
  {
        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;
                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;
  
                        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 */
                }
                        
                // 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;
@@@ -6224,8 -5691,9 +6224,8 @@@ add_thread (gpointer key, gpointer valu
  }
  
  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;
        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);
        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)
                }
        }
  
 -      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);
  
                /* 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));
                        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);
                mono_set_lmf ((gpointer)(((gssize)ext.lmf.previous_lmf) & ~3));
  #endif
  
 +      *endp = p;
        // FIXME: byref arguments
        // FIXME: varargs
        return ERR_NONE;
@@@ -6419,10 -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);
  
        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));
  
                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:
@@@ -6539,7 -6019,7 +6539,7 @@@ get_source_files_for_type (MonoClass *k
                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)
@@@ -6595,7 -6075,6 +6595,7 @@@ vm_commands (int command, int id, guint
                if (suspend_count == 0)
                        return ERR_NOT_SUSPENDED;
                resume_vm ();
 +              clear_suspended_objs ();
                break;
        case CMD_VM_DISPOSE:
                /* Clear all event requests */
        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;
                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);
                        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 ();
                        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"));
                }
                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)
  
                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 ();
                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));
@@@ -7012,9 -6478,6 +7012,9 @@@ event_commands (int command, guint8 *p
                                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);
  
                                        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;
  
@@@ -7412,29 -6871,6 +7412,29 @@@ module_commands (int command, guint8 *p
        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)
  {
@@@ -7471,11 -6907,9 +7471,11 @@@ buffer_add_cattrs (Buffer *buf, MonoDom
                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);
  
                        } else {
                                buffer_add_int (buf, 0);
                        }
 +                      g_free (arginfo);
                }
        }
  }
@@@ -7955,15 -7388,6 +7955,15 @@@ type_commands_internal (int command, Mo
                }
                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;
        }
@@@ -7998,7 -7422,6 +7998,7 @@@ static ErrorCod
  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: {
                int i, j, n_il_offsets;
                int *il_offsets;
                int *line_numbers;
 +              int *column_numbers;
                int *source_files;
                GPtrArray *source_file_list;
  
                        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);
                        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 = "";
  
                                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);
                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: {
                }
                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;
        }
@@@ -8487,52 -7837,6 +8487,52 @@@ thread_commands (int command, guint8 *p
        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;
        }
@@@ -8581,17 -7885,18 +8581,17 @@@ frame_commands (int command, guint8 *p
        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;
  
                                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;
@@@ -8824,8 -8129,7 +8824,8 @@@ object_commands (int command, guint8 *p
  
        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);
                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:
@@@ -8934,225 -8238,65 +8934,225 @@@ command_set_to_string (CommandSet comma
        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
@@@ -9226,10 -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;
                                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;
                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;
                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;
        }