2010-01-16 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / debugger-agent.c
index 7d516ae35a5a3aecf637e5437029dd4dcca86625..3d544f8b1c15d8f7bc33fb5581feffcf053563d4 100644 (file)
 #include <pthread.h>
 #endif
 
+#ifdef HAVE_UCONTEXT_H
+#include <ucontext.h>
+#endif
+
+#ifdef HOST_WIN32
+#include <ws2tcpip.h>
+#ifdef __GNUC__
+/* cygwin's headers do not seem to define these */
+void WSAAPI freeaddrinfo (struct addrinfo*);
+int WSAAPI getaddrinfo (const char*,const char*,const struct addrinfo*,
+                        struct addrinfo**);
+int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD,
+                       char*,DWORD,int);
+#endif
+#endif
+
+#ifdef PLATFORM_ANDROID
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <sys/endian.h>
+#endif
+
 #include <mono/metadata/mono-debug.h>
 #include <mono/metadata/mono-debug-debugger.h>
 #include <mono/metadata/debug-mono-symfile.h>
 #include <mono/metadata/gc-internal.h>
 #include <mono/metadata/threads-types.h>
+#include <mono/metadata/socket-io.h>
 #include <mono/utils/mono-semaphore.h>
 #include "debugger-agent.h"
 #include "mini.h"
 #define DISABLE_DEBUGGER_AGENT 1
 #endif
 
+#ifdef DISABLE_SOFT_DEBUG
+#define DISABLE_DEBUGGER_AGENT 1
+#endif
+
 #ifndef DISABLE_DEBUGGER_AGENT
 #include <mono/io-layer/mono-mutex.h>
 
+/* Definitions to make backporting to 2.6 easier */
+//#define MonoInternalThread MonoThread
+//#define mono_thread_internal_current mono_thread_current
+#define THREAD_TO_INTERNAL(thread) (thread)->internal_thread
+
 typedef struct {
        gboolean enabled;
        char *transport;
@@ -60,6 +92,10 @@ typedef struct {
        char *log_file;
        gboolean suspend;
        gboolean server;
+       gboolean onuncaught;
+       GSList *onthrow;
+       int timeout;
+       char *launch;
 } AgentConfig;
 
 typedef struct
@@ -71,16 +107,28 @@ typedef struct
        MonoContext ctx;
        MonoDebugMethodJitInfo *jit;
        int flags;
+       /*
+        * Whenever ctx is set. This is FALSE for the last frame of running threads, since
+        * the frame can become invalid.
+        */
+       gboolean has_ctx;
 } StackFrame;
 
 typedef struct
 {
        int id;
+       int flags;
        guint8 *p;
        guint8 *endp;
        /* This is the context which needs to be restored after the invoke */
        MonoContext ctx;
        gboolean has_ctx;
+       /*
+        * If this is set, invoke this method with the arguments given by ARGS.
+        */
+       MonoMethod *method;
+       gpointer *args;
+       guint32 suspend_count;
 } InvokeData;
 
 typedef struct {
@@ -108,6 +156,15 @@ typedef struct {
         * native code.
         */
        gboolean suspended;
+       /*
+        * Signals whenever the thread is in the process of suspending, i.e. it will suspend
+        * within a finite amount of time.
+        */
+       gboolean suspending;
+       /*
+        * Set to TRUE if this thread is suspended in suspend_current ().
+        */
+       gboolean really_suspended;
        /* Used to pass the context to the breakpoint/single step handler */
        MonoContext handler_ctx;
        /* Whenever thread_stop () was called for this thread */
@@ -115,6 +172,39 @@ typedef struct {
 
        /* Number of thread interruptions not yet processed */
        gint32 interrupt_count;
+
+       /* Whenever to disable breakpoints (used during invokes) */
+       gboolean disable_breakpoints;
+
+       /*
+        * Number of times this thread has been resumed using resume_thread ().
+        */
+       guint32 resume_count;
+
+       MonoInternalThread *thread;
+
+       /*
+        * Information about the frame which transitioned to native code for running
+        * threads.
+        */
+       StackFrameInfo async_last_frame;
+
+       /*
+        * The context where the stack walk can be started for running threads.
+        */
+       MonoContext async_ctx;
+
+       gboolean has_async_ctx;
+
+       /*
+        * The lmf where the stack walk can be started for running threads.
+        */
+       gpointer async_lmf;
+
+       /*
+        * The callee address of the last mono_runtime_invoke call
+        */
+       gpointer invoke_addr;
 } DebuggerTlsData;
 
 /* 
@@ -123,8 +213,8 @@ typedef struct {
 
 #define HEADER_LENGTH 11
 
-#define MAJOR_VERSION 0
-#define MINOR_VERSION 2
+#define MAJOR_VERSION 2
+#define MINOR_VERSION 0
 
 typedef enum {
        CMD_SET_VM = 1,
@@ -213,6 +303,11 @@ typedef enum {
        FRAME_FLAG_DEBUGGER_INVOKE = 1
 } StackFrameFlags;
 
+typedef enum {
+       INVOKE_FLAG_DISABLE_BREAKPOINTS = 1,
+       INVOKE_FLAG_SINGLE_THREADED = 2
+} InvokeFlags;
+
 typedef enum {
        CMD_VM_VERSION = 1,
        CMD_VM_ALL_THREADS = 2,
@@ -255,6 +350,7 @@ typedef enum {
        CMD_ASSEMBLY_GET_MANIFEST_MODULE = 3,
        CMD_ASSEMBLY_GET_OBJECT = 4,
        CMD_ASSEMBLY_GET_TYPE = 5,
+       CMD_ASSEMBLY_GET_NAME = 6
 } CmdAssembly;
 
 typedef enum {
@@ -343,7 +439,11 @@ typedef struct {
        gpointer start_sp;
        MonoMethod *last_method;
        int last_line;
-} MonoSingleStepReq;
+       /* Whenever single stepping is performed using start/stop_single_stepping () */
+       gboolean global;
+       /* The list of breakpoints used to implement step-over */
+       GSList *bps;
+} SingleStepReq;
 
 /* Dummy structure used for the profiler callbacks */
 typedef struct {
@@ -358,7 +458,14 @@ typedef struct {
 
 static AgentConfig agent_config;
 
-static gboolean inited;
+/* 
+ * Whenever the agent is fully initialized.
+ * When using the onuncaught or onthrow options, only some parts of the agent are
+ * initialized on startup, and the full initialization which includes connection
+ * establishment and the startup of the agent thread is only done in response to
+ * an event.
+ */
+static gint32 inited;
 
 static int conn_fd;
 
@@ -403,22 +510,23 @@ static GPtrArray *pending_assembly_loads;
 static gboolean debugger_thread_exited;
 
 /* Cond variable used to wait for debugger_thread_exited becoming true */
-static mono_cond_t debugger_thread_exited_cond = MONO_COND_INITIALIZER;
+static mono_cond_t debugger_thread_exited_cond;
 
 /* Mutex for the cond var above */
-static mono_mutex_t debugger_thread_exited_mutex = MONO_MUTEX_INITIALIZER;
+static mono_mutex_t debugger_thread_exited_mutex;
 
 static DebuggerProfiler debugger_profiler;
 
 /* The single step request instance */
-static MonoSingleStepReq *ss_req = NULL;
+static SingleStepReq *ss_req = NULL;
+static gpointer ss_invoke_addr = NULL;
 
 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
 /* Number of single stepping operations in progress */
 static int ss_count;
 #endif
 
-static void transport_connect (char *host, int port);
+static void transport_connect (const char *host, int port);
 
 static guint32 WINAPI debugger_thread (void *arg);
 
@@ -464,8 +572,13 @@ static void ids_cleanup (void);
 
 static void suspend_init (void);
 
-static ErrorCode ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req);
-static void ss_stop (EventRequest *req);
+static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls);
+static ErrorCode ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req);
+static void ss_destroy (SingleStepReq *req);
+
+static void start_debugger_thread (void);
+
+static void finish_agent_init (gboolean on_startup);
 
 static int
 parse_address (char *address, char **host, int *port)
@@ -494,18 +607,22 @@ print_usage (void)
        fprintf (stderr, "  loglevel=<n>\t\t\tLog level (defaults to 0)\n");
        fprintf (stderr, "  logfile=<file>\t\tFile to log to (defaults to stdout)\n");
        fprintf (stderr, "  suspend=y/n\t\t\tWhenever to suspend after startup.\n");
+       fprintf (stderr, "  timeout=<n>\t\t\tTimeout for connecting in milliseconds.\n");
        fprintf (stderr, "  help\t\t\t\tPrint this help.\n");
 }
 
-static int
-parse_flag (char *flag)
+static gboolean
+parse_flag (const char *option, char *flag)
 {
        if (!strcmp (flag, "y"))
                return TRUE;
        else if (!strcmp (flag, "n"))
                return FALSE;
-       else
-               return -1;
+       else {
+               fprintf (stderr, "debugger-agent: The valid values for the '%s' option are 'y' and 'n'.\n", option);
+               exit (1);
+               return FALSE;
+       }
 }
 
 void
@@ -537,22 +654,23 @@ mono_debugger_agent_parse_options (char *options)
                } else if (strncmp (arg, "logfile=", 8) == 0) {
                        agent_config.log_file = g_strdup (arg + 8);
                } else if (strncmp (arg, "suspend=", 8) == 0) {
-                       int flag = parse_flag (arg + 8);
-                       if (flag == -1) {
-                               fprintf (stderr, "debugger-agent: The valid values for the 'suspend' option are 'y' and 'n'.\n");
-                               exit (1);
-                       }
-                       agent_config.suspend = flag ? TRUE : FALSE;
+                       agent_config.suspend = parse_flag ("suspend", arg + 8);
                } else if (strncmp (arg, "server=", 7) == 0) {
-                       int flag = parse_flag (arg + 7);
-                       if (flag == -1) {
-                               fprintf (stderr, "debugger-agent: The valid values for the 'server' option are 'y' and 'n'.\n");
-                               exit (1);
-                       }
-                       agent_config.server = flag ? TRUE : FALSE;
+                       agent_config.server = parse_flag ("server", arg + 7);
+               } else if (strncmp (arg, "onuncaught=", 11) == 0) {
+                       agent_config.onuncaught = parse_flag ("onuncaught", arg + 11);
+               } else if (strncmp (arg, "onthrow=", 8) == 0) {
+                       /* We support multiple onthrow= options */
+                       agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup (arg + 8));
+               } else if (strncmp (arg, "onthrow", 7) == 0) {
+                       agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup (""));
                } else if (strncmp (arg, "help", 4) == 0) {
                        print_usage ();
                        exit (0);
+               } else if (strncmp (arg, "timeout=", 8) == 0) {
+                       agent_config.timeout = atoi (arg + 8);
+               } else if (strncmp (arg, "launch=", 7) == 0) {
+                       agent_config.launch = g_strdup (arg + 7);
                } else {
                        print_usage ();
                        exit (1);
@@ -582,10 +700,6 @@ mono_debugger_agent_parse_options (char *options)
 void
 mono_debugger_agent_init (void)
 {
-       char *host;
-       int port;
-       int res;
-
        if (!agent_config.enabled)
                return;
 
@@ -594,6 +708,9 @@ mono_debugger_agent_init (void)
 
        event_requests = g_ptr_array_new ();
 
+       mono_mutex_init (&debugger_thread_exited_mutex, NULL);
+       mono_cond_init (&debugger_thread_exited_cond, NULL);
+
        mono_profiler_install ((MonoProfiler*)&debugger_profiler, runtime_shutdown);
        mono_profiler_set_events (MONO_PROFILE_APPDOMAIN_EVENTS | MONO_PROFILE_THREADS | MONO_PROFILE_ASSEMBLY_EVENTS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_METHOD_EVENTS);
        mono_profiler_install_runtime_initialized (runtime_initialized);
@@ -617,14 +734,6 @@ mono_debugger_agent_init (void)
        loaded_classes = g_hash_table_new (mono_aligned_addr_hash, NULL);
        pending_assembly_loads = g_ptr_array_new ();
 
-       if (agent_config.address) {
-               res = parse_address (agent_config.address, &host, &port);
-               g_assert (res == 0);
-       } else {
-               host = NULL;
-               port = 0;
-       }
-
        log_level = agent_config.log_level;
 
        if (agent_config.log_file) {
@@ -651,20 +760,71 @@ mono_debugger_agent_init (void)
        /* This is needed because we can't set local variables in registers yet */
        mono_disable_optimizations (MONO_OPT_LINEARS);
 
-       inited = TRUE;
+       if (!agent_config.onuncaught && !agent_config.onthrow)
+               finish_agent_init (TRUE);
+}
+
+/*
+ * finish_agent_init:
+ *
+ *   Finish the initialization of the agent. This involves connecting the transport
+ * and starting the agent thread. This is either done at startup, or
+ * in response to some event like an unhandled exception.
+ */
+static void
+finish_agent_init (gboolean on_startup)
+{
+       char *host;
+       int port;
+       int res;
+
+       if (InterlockedCompareExchange (&inited, 1, 0) == 1)
+               return;
+
+       if (agent_config.launch) {
+               char *argv [16];
+
+               // FIXME: Generated address
+               // FIXME: Races with transport_connect ()
+
+               argv [0] = agent_config.launch;
+               argv [1] = agent_config.transport;
+               argv [2] = agent_config.address;
+               argv [3] = NULL;
+
+               res = g_spawn_async_with_pipes (NULL, argv, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+               if (!res) {
+                       fprintf (stderr, "Failed to execute '%s'.\n", agent_config.launch);
+                       exit (1);
+               }
+       }
+
+       if (agent_config.address) {
+               res = parse_address (agent_config.address, &host, &port);
+               g_assert (res == 0);
+       } else {
+               host = NULL;
+               port = 0;
+       }
 
        transport_connect (host, port);
+
+       if (!on_startup) {
+               /* Do some which is usually done after sending the VMStart () event */
+               vm_start_event_sent = TRUE;
+               start_debugger_thread ();
+       }
 }
 
 static void
 mono_debugger_agent_cleanup (void)
 {
-       if (!agent_config.enabled)
+       if (!inited)
                return;
 
        /* This will interrupt the agent thread */
        /* Close the read part only so it can still send back replies */
-#ifdef PLATFORM_WIN32
+#ifdef HOST_WIN32
        shutdown (conn_fd, SD_RECEIVE);
 #else
        shutdown (conn_fd, SHUT_RD);
@@ -681,14 +841,32 @@ mono_debugger_agent_cleanup (void)
        //WaitForSingleObject (debugger_thread_handle, INFINITE);
        if (GetCurrentThreadId () != debugger_thread_id) {
                mono_mutex_lock (&debugger_thread_exited_mutex);
-               if (!debugger_thread_exited)
+               if (!debugger_thread_exited) {
+#ifdef HOST_WIN32
+                       if (WAIT_TIMEOUT == WaitForSingleObject(debugger_thread_exited_cond, 0)) {
+                               mono_mutex_unlock (&debugger_thread_exited_mutex);
+                               Sleep(0);
+                               mono_mutex_lock (&debugger_thread_exited_mutex);
+                       }
+#else
                        mono_cond_wait (&debugger_thread_exited_cond, &debugger_thread_exited_mutex);
+#endif
+               }
                mono_mutex_unlock (&debugger_thread_exited_mutex);
        }
 
        breakpoints_cleanup ();
        objrefs_cleanup ();
        ids_cleanup ();
+
+#ifdef HOST_WIN32
+       shutdown (conn_fd, SD_BOTH);
+#else
+       shutdown (conn_fd, SHUT_RDWR);
+#endif
+       
+       mono_mutex_destroy (&debugger_thread_exited_mutex);
+       mono_cond_destroy (&debugger_thread_exited_cond);
 }
 
 /*
@@ -697,7 +875,7 @@ mono_debugger_agent_cleanup (void)
  *   Connect/Listen on HOST:PORT. If HOST is NULL, generate an address and listen on it.
  */
 static void
-transport_connect (char *host, int port)
+transport_connect (const char *host, int port)
 {
        struct addrinfo hints;
        struct addrinfo *result, *rp;
@@ -711,6 +889,8 @@ transport_connect (char *host, int port)
        if (host) {
                sprintf (port_string, "%d", port);
 
+               mono_network_init ();
+
                /* Obtain address(es) matching host/port */
 
                memset (&hints, 0, sizeof (struct addrinfo));
@@ -748,12 +928,12 @@ transport_connect (char *host, int port)
                        res = getsockname (sfd, &addr, &addrlen);
                        g_assert (res == 0);
 
+                       host = "127.0.0.1";
+                       port = ntohs (addr.sin_port);
+
                        /* Emit the address to stdout */
                        /* FIXME: Should print another interface, not localhost */
-                       printf ("127.0.0.1:%d\n", ntohs (addr.sin_port));
-
-                       conn_fd = accept (sfd, NULL, NULL);
-                       g_assert (conn_fd != -1);
+                       printf ("%s:%d\n", host, port);
                } else {
                        /* Listen on the provided address */
                        for (rp = result; rp != NULL; rp = rp->ai_next) {
@@ -769,23 +949,47 @@ transport_connect (char *host, int port)
                                res = listen (sfd, 16);
                                if (res == -1)
                                        continue;
-
-                               DEBUG (1, fprintf (log_file, "Listening on %s:%d...\n", host, port));
-                               conn_fd = accept (sfd, NULL, NULL);
-                               if (conn_fd != -1)
-                                       break;
+                               break;
                        }
 
+#ifndef PLATFORM_WIN32
+                       /*
+                        * this function is not present on win2000 which we still support, and the
+                        * workaround described here:
+                        * http://msdn.microsoft.com/en-us/library/ms737931(VS.85).aspx
+                        * only works with MSVC.
+                        */
                        freeaddrinfo (result);
+#endif
+               }
 
-                       if (rp == 0) {
-                               fprintf (stderr, "debugger-agent: Unable to listen on %s:%d\n", host, port);
+               DEBUG (1, fprintf (log_file, "Listening on %s:%d (timeout=%d ms)...\n", host, port, agent_config.timeout));
+
+               if (agent_config.timeout) {
+                       fd_set readfds;
+                       struct timeval tv;
+
+                       tv.tv_sec = 0;
+                       tv.tv_usec = agent_config.timeout * 1000;
+                       FD_ZERO (&readfds);
+                       FD_SET (sfd, &readfds);
+                       res = select (sfd + 1, &readfds, NULL, NULL, &tv);
+                       if (res == 0) {
+                               fprintf (stderr, "debugger-agent: Timed out waiting to connect.\n");
                                exit (1);
                        }
                }
 
+               conn_fd = accept (sfd, NULL, NULL);
+               if (conn_fd == -1) {
+                       fprintf (stderr, "debugger-agent: Unable to listen on %s:%d\n", host, port);
+                       exit (1);
+               }
+
                DEBUG (1, fprintf (log_file, "Accepted connection from client, socket fd=%d.\n", conn_fd));
        } else {
+               /* Connect to the specified address */
+               /* FIXME: Respect the timeout */
                for (rp = result; rp != NULL; rp = rp->ai_next) {
                        sfd = socket (rp->ai_family, rp->ai_socktype,
                                                  rp->ai_protocol);
@@ -800,7 +1004,10 @@ transport_connect (char *host, int port)
 
                conn_fd = sfd;
 
+#ifndef PLATFORM_WIN32
+               /* See the comment above */
                freeaddrinfo (result);
+#endif
 
                if (rp == 0) {
                        fprintf (stderr, "debugger-agent: Unable to connect to %s:%d\n", host, port);
@@ -810,11 +1017,11 @@ transport_connect (char *host, int port)
        
        /* Write handshake message */
        sprintf (handshake_msg, "DWP-Handshake");
-       res = write (conn_fd, handshake_msg, strlen (handshake_msg));
+       res = send (conn_fd, handshake_msg, strlen (handshake_msg), 0);
        g_assert (res != -1);
 
        /* Read answer */
-       res = read (conn_fd, buf, strlen (handshake_msg));
+       res = recv (conn_fd, buf, strlen (handshake_msg), 0);
        if ((res != strlen (handshake_msg)) || (memcmp (buf, handshake_msg, strlen (handshake_msg) != 0))) {
                fprintf (stderr, "debugger-agent: DWP handshake failed.\n");
                exit (1);
@@ -840,7 +1047,7 @@ transport_send (guint8 *data, int len)
 {
        int res;
 
-       res = write (conn_fd, data, len);
+       res = send (conn_fd, data, len, 0);
        if (res != len)
                return FALSE;
        else
@@ -1463,6 +1670,12 @@ static void invoke_method (void);
  * SUSPEND/RESUME
  */
 
+/*
+ * save_thread_context:
+ *
+ *   Set CTX as the current threads context which is used for computing stack traces.
+ * This function is signal-safe.
+ */
 static void
 save_thread_context (MonoContext *ctx)
 {
@@ -1496,10 +1709,10 @@ static gint32 suspend_count;
  */
 static gint32 threads_suspend_count;
 
-static mono_mutex_t suspend_mutex = MONO_MUTEX_INITIALIZER;
+static mono_mutex_t suspend_mutex;
 
 /* Cond variable used to wait for suspend_count becoming 0 */
-static mono_cond_t suspend_cond = MONO_COND_INITIALIZER;
+static mono_cond_t suspend_cond;
 
 /* Semaphore used to wait for a thread becoming suspended */
 static MonoSemType suspend_sem;
@@ -1507,26 +1720,70 @@ static MonoSemType suspend_sem;
 static void
 suspend_init (void)
 {
+       mono_mutex_init (&suspend_mutex, NULL);
+       mono_cond_init (&suspend_cond, NULL);   
        MONO_SEM_INIT (&suspend_sem, 0);
 }
 
+typedef struct
+{
+       StackFrameInfo last_frame;
+       gboolean last_frame_set;
+       MonoContext ctx;
+       gpointer lmf;
+} GetLastFrameUserData;
+
+static gboolean
+get_last_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
+{
+       GetLastFrameUserData *data = user_data;
+
+       if (info->type == FRAME_TYPE_MANAGED_TO_NATIVE)
+               return FALSE;
+
+       if (!data->last_frame_set) {
+               /* Store the last frame */
+               memcpy (&data->last_frame, info, sizeof (StackFrameInfo));
+               data->last_frame_set = TRUE;
+               return FALSE;
+       } else {
+               /* Store the context/lmf for the frame above the last frame */
+               memcpy (&data->ctx, ctx, sizeof (MonoContext));
+               data->lmf = info->lmf;
+
+               return TRUE;
+       }
+}
+
 /*
  * mono_debugger_agent_thread_interrupt:
  *
  *   Called by the abort signal handler.
+ * Should be signal safe.
  */
 gboolean
-mono_debugger_agent_thread_interrupt (MonoJitInfo *ji)
+mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
 {
        DebuggerTlsData *tls;
 
-       if (!agent_config.enabled)
+       if (!inited)
                return FALSE;
 
        tls = TlsGetValue (debugger_tls_id);
        if (!tls)
                return FALSE;
 
+       /*
+        * OSX can (and will) coalesce signals, so sending multiple pthread_kills does not
+        * guarantee the signal handler will be called that many times.  Instead of tracking
+        * interrupt_count on osx, we use this as a boolean flag to determine if a interrupt
+        * has been requested that hasn't been handled yet, otherwise we can have threads
+        * refuse to die when VM_EXIT is called
+        */
+#if defined(__APPLE__)
+       if (InterlockedCompareExchange (&tls->interrupt_count, 0, 1) == 0)
+               return FALSE;
+#else
        /*
         * We use interrupt_count to determine whenever this interrupt should be processed
         * by us or the normal interrupt processing code in the signal handler.
@@ -1537,21 +1794,61 @@ mono_debugger_agent_thread_interrupt (MonoJitInfo *ji)
                return FALSE;
 
        InterlockedDecrement (&tls->interrupt_count);
+#endif
 
        // FIXME: Races when the thread leaves managed code before hitting a single step
        // event.
 
        if (ji) {
                /* Running managed code, will be suspended by the single step code */
-               //printf ("S1: %p\n", GetCurrentThreadId ());
+               DEBUG (1, printf ("[%p] Received interrupt while at %s(%p), continuing.\n", (gpointer)GetCurrentThreadId (), ji->method->name, mono_arch_ip_from_context (sigctx)));
                return TRUE;
        } else {
                /* 
                 * Running native code, will be suspended when it returns to/enters 
                 * managed code. Treat it as already suspended.
+                * This might interrupt the code in process_single_step_inner (), we use the
+                * tls->suspending flag to avoid races when that happens.
                 */
-               //printf ("S2: %p\n", GetCurrentThreadId ());
-               if (!tls->suspended) {
+               if (!tls->suspended && !tls->suspending) {
+                       MonoContext ctx;
+                       GetLastFrameUserData data;
+
+                       // FIXME: printf is not signal safe, but this is only used during
+                       // debugger debugging
+                       DEBUG (1, printf ("[%p] Received interrupt while at %p, treating as suspended.\n", (gpointer)GetCurrentThreadId (), mono_arch_ip_from_context (sigctx)));
+                       //save_thread_context (&ctx);
+
+                       if (!tls->thread)
+                               /* Already terminated */
+                               return TRUE;
+
+                       /*
+                        * 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
+                        * the thread is still running, so it might return to managed code,
+                        * making these invalid.
+                        * So we start a stack walk and save the first frame, along with the
+                        * parent frame's ctx+lmf. This (hopefully) works because the thread will be 
+                        * suspended when it returns to managed code, so the parent's ctx should
+                        * remain valid.
+                        */
+                       data.last_frame_set = FALSE;
+                       if (sigctx) {
+                               mono_arch_sigctx_to_monoctx (sigctx, &ctx);
+                               mono_jit_walk_stack_from_ctx_in_thread (get_last_frame, mono_domain_get (), &ctx, FALSE, tls->thread, mono_get_lmf (), &data);
+                       }
+                       if (data.last_frame_set) {
+                               memcpy (&tls->async_last_frame, &data.last_frame, sizeof (StackFrameInfo));
+                               memcpy (&tls->async_ctx, &data.ctx, sizeof (MonoContext));
+                               tls->async_lmf = data.lmf;
+                               tls->has_async_ctx = TRUE;
+                               tls->domain = mono_domain_get ();
+                               memcpy (&tls->ctx, &ctx, sizeof (MonoContext));
+                       } else {
+                               tls->has_async_ctx = FALSE;
+                       }
+
                        tls->suspended = TRUE;
                        MONO_SEM_POST (&suspend_sem);
                }
@@ -1559,6 +1856,28 @@ mono_debugger_agent_thread_interrupt (MonoJitInfo *ji)
        }
 }
 
+#ifdef HOST_WIN32
+static void CALLBACK notify_thread_apc (ULONG_PTR param)
+{
+       //DebugBreak ();
+       mono_debugger_agent_thread_interrupt (NULL, NULL);
+}
+#endif /* HOST_WIN32 */
+
+/*
+ * reset_native_thread_suspend_state:
+ * 
+ *   Reset the suspended flag 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)
+               tls->suspended = FALSE;
+}
+
 /*
  * notify_thread:
  *
@@ -1573,19 +1892,70 @@ notify_thread (gpointer key, gpointer value, gpointer user_data)
 
        if (GetCurrentThreadId () != tid) {
                DEBUG(1, fprintf (log_file, "[%p] Interrupting %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid));
+
+               /*
+                * OSX can (and will) coalesce signals, so sending multiple pthread_kills does not
+                * guarantee the signal handler will be called that many times.  Instead of tracking
+                * interrupt_count on osx, we use this as a boolean flag to determine if a interrupt
+                * has been requested that hasn't been handled yet, otherwise we can have threads
+                * refuse to die when VM_EXIT is called
+                */
+#if defined(__APPLE__)
+               if (InterlockedCompareExchange (&tls->interrupt_count, 1, 0) == 1)
+                       return;
+#else
                /*
                 * Maybe we could use the normal interrupt infrastructure, but that does a lot
                 * of things like breaking waits etc. which we don't want.
                 */
                InterlockedIncrement (&tls->interrupt_count);
-#ifdef PLATFORM_WIN32
-               /*FIXME: Abort thread */
+#endif
+
+               /* This is _not_ equivalent to ves_icall_System_Threading_Thread_Abort () */
+#ifdef HOST_WIN32
+               QueueUserAPC (notify_thread_apc, thread->handle, NULL);
 #else
                pthread_kill ((pthread_t) tid, mono_thread_get_abort_signal ());
 #endif
        }
 }
 
+static void
+process_suspend (DebuggerTlsData *tls, MonoContext *ctx)
+{
+       guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
+       MonoJitInfo *ji;
+
+       if (debugger_thread_id == GetCurrentThreadId ())
+               return;
+
+       /* Prevent races with mono_debugger_agent_thread_interrupt () */
+       if (suspend_count - tls->resume_count > 0)
+               tls->suspending = TRUE;
+
+       DEBUG(1, fprintf (log_file, "[%p] Received single step event for suspending.\n", (gpointer)GetCurrentThreadId ()));
+
+       if (suspend_count - tls->resume_count == 0) {
+               /* 
+                * We are executing a single threaded invoke but the single step for 
+                * suspending is still active.
+                * FIXME: This slows down single threaded invokes.
+                */
+               DEBUG(1, fprintf (log_file, "[%p] Ignored during single threaded invoke.\n", (gpointer)GetCurrentThreadId ()));
+               return;
+       }
+
+       ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+
+       /* Can't suspend in these methods */
+       if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy")))
+               return;
+
+       save_thread_context (ctx);
+
+       suspend_current ();
+}
+
 /*
  * suspend_vm:
  *
@@ -1639,13 +2009,55 @@ resume_vm (void)
        if (suspend_count == 0) {
                // FIXME: Is it safe to call this inside the lock ?
                stop_single_stepping ();
-               err = mono_cond_broadcast (&suspend_cond);
-               g_assert (err == 0);
+               mono_g_hash_table_foreach (thread_to_tls, reset_native_thread_suspend_state, NULL);
        }
 
-       err = mono_mutex_unlock (&suspend_mutex);
+       /* Signal this even when suspend_count > 0, since some threads might have resume_count > 0 */
+       err = mono_cond_broadcast (&suspend_cond);
        g_assert (err == 0);
 
+       mono_mutex_unlock (&suspend_mutex);
+       //g_assert (err == 0);
+
+       mono_loader_unlock ();
+}
+
+/*
+ * resume_thread:
+ *
+ *   Resume just one thread.
+ */
+static void
+resume_thread (MonoInternalThread *thread)
+{
+       int err;
+       DebuggerTlsData *tls;
+
+       g_assert (debugger_thread_id == GetCurrentThreadId ());
+
+       mono_loader_lock ();
+
+       tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+       g_assert (tls);
+       
+       mono_mutex_lock (&suspend_mutex);
+
+       g_assert (suspend_count > 0);
+
+       DEBUG(1, fprintf (log_file, "[%p] Resuming thread...\n", (gpointer)(gssize)thread->tid));
+
+       tls->resume_count += suspend_count;
+
+       /* 
+        * Signal suspend_count without decreasing suspend_count, the threads will wake up
+        * but only the one whose resume_count field is > 0 will be resumed.
+        */
+       err = mono_cond_broadcast (&suspend_cond);
+       g_assert (err == 0);
+
+       mono_mutex_unlock (&suspend_mutex);
+       //g_assert (err == 0);
+
        mono_loader_unlock ();
 }
 
@@ -1695,6 +2107,9 @@ suspend_current (void)
 
        mono_mutex_lock (&suspend_mutex);
 
+       tls->suspending = FALSE;
+       tls->really_suspended = TRUE;
+
        if (!tls->suspended) {
                tls->suspended = TRUE;
                MONO_SEM_POST (&suspend_sem);
@@ -1702,12 +2117,25 @@ suspend_current (void)
 
        DEBUG(1, fprintf (log_file, "[%p] Suspended.\n", (gpointer)GetCurrentThreadId ()));
 
-       while (suspend_count > 0) {
+       while (suspend_count - tls->resume_count > 0) {
+#ifdef HOST_WIN32
+               if (WAIT_TIMEOUT == WaitForSingleObject(suspend_cond, 0))
+               {
+                       mono_mutex_unlock (&suspend_mutex);
+                       Sleep(0);
+                       mono_mutex_lock (&suspend_mutex);
+               }
+               else
+               {
+               }
+#else
                err = mono_cond_wait (&suspend_cond, &suspend_mutex);
                g_assert (err == 0);
+#endif
        }
 
        tls->suspended = FALSE;
+       tls->really_suspended = FALSE;
 
        threads_suspend_count --;
 
@@ -1725,6 +2153,7 @@ suspend_current (void)
 
        /* The frame info becomes invalid after a resume */
        tls->has_context = FALSE;
+       tls->has_async_ctx = FALSE;
        invalidate_frames (NULL);
 }
 
@@ -1792,6 +2221,60 @@ is_suspended (void)
        return count_threads_to_wait_for () == 0;
 }
 
+/*
+ * find_seq_point_for_native_offset:
+ *
+ *   Find the sequence point corresponding to the native offset NATIVE_OFFSET, which
+ * should be the location of a sequence point.
+ */
+static SeqPoint*
+find_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info)
+{
+       MonoSeqPointInfo *seq_points;
+       int i;
+
+       mono_domain_lock (domain);
+       seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
+       mono_domain_unlock (domain);
+       g_assert (seq_points);
+
+       *info = seq_points;
+
+       for (i = 0; i < seq_points->len; ++i) {
+               if (seq_points->seq_points [i].native_offset == native_offset)
+                       return &seq_points->seq_points [i];
+       }
+
+       return NULL;
+}
+
+/*
+ * find_seq_point:
+ *
+ *   Find the sequence point corresponding to the IL offset IL_OFFSET, which
+ * should be the location of a sequence point.
+ */
+static SeqPoint*
+find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info)
+{
+       MonoSeqPointInfo *seq_points;
+       int i;
+
+       mono_domain_lock (domain);
+       seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
+       mono_domain_unlock (domain);
+       g_assert (seq_points);
+
+       *info = seq_points;
+
+       for (i = 0; i < seq_points->len; ++i) {
+               if (seq_points->seq_points [i].il_offset == il_offset)
+                       return &seq_points->seq_points [i];
+       }
+
+       return NULL;
+}
+
 /*
  * compute_il_offset:
  *
@@ -1803,7 +2286,7 @@ is_suspended (void)
 static gint32
 compute_il_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset)
 {
-       GPtrArray *seq_points;
+       MonoSeqPointInfo *seq_points;
        int i, last_il_offset, seq_il_offset, seq_native_offset;
 
        mono_domain_lock (domain);
@@ -1814,9 +2297,9 @@ compute_il_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset)
        last_il_offset = -1;
 
        /* Find the sequence point */
-       for (i = 0; i < seq_points->len; i += 2) {
-               seq_il_offset = GPOINTER_TO_UINT (g_ptr_array_index (seq_points, i));
-               seq_native_offset = GPOINTER_TO_UINT (g_ptr_array_index (seq_points, i + 1));
+       for (i = 0; i < seq_points->len; ++i) {
+               seq_il_offset = seq_points->seq_points [i].il_offset;
+               seq_native_offset = seq_points->seq_points [i].native_offset;
 
                if (seq_native_offset > native_offset)
                        break;
@@ -1875,7 +2358,10 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
        frame = g_new0 (StackFrame, 1);
        frame->method = method;
        frame->il_offset = info->il_offset;
-       frame->ctx = *ctx;
+       if (ctx) {
+               frame->ctx = *ctx;
+               frame->has_ctx = TRUE;
+       }
        frame->domain = info->domain;
 
        ud->frames = g_slist_append (ud->frames, frame);
@@ -1899,7 +2385,14 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls)
 
        user_data.tls = tls;
        user_data.frames = NULL;
-       if (tls->has_context) {
+       if (tls->terminated) {
+               tls->frame_count = 0;
+               return;
+       } if (!tls->really_suspended && tls->has_async_ctx) {
+               /* Have to use the state saved by the signal handler */
+               process_frame (&tls->async_last_frame, NULL, &user_data);
+               mono_jit_walk_stack_from_ctx_in_thread (process_frame, tls->domain, &tls->async_ctx, FALSE, thread, tls->async_lmf, &user_data);
+       } else if (tls->has_context) {
                mono_jit_walk_stack_from_ctx_in_thread (process_frame, tls->domain, &tls->ctx, FALSE, thread, tls->lmf, &user_data);
        } else {
                // FIXME:
@@ -1996,9 +2489,11 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, MonoExcept
                                        gboolean found = FALSE;
                                        MonoAssembly **assemblies = mod->data.assemblies;
 
-                                       for (k = 0; assemblies [k]; ++k)
-                                               if (assemblies [k] == ji->method->klass->image->assembly)
-                                                       found = TRUE;
+                                       if (assemblies) {
+                                               for (k = 0; assemblies [k]; ++k)
+                                                       if (assemblies [k] == ji->method->klass->image->assembly)
+                                                               found = TRUE;
+                                       }
                                        if (!found)
                                                filtered = TRUE;
                                }
@@ -2059,7 +2554,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx
        MonoDomain *domain = mono_domain_get ();
        MonoThread *thread;
 
-       if (!agent_config.enabled)
+       if (!inited)
                return;
 
        if (!vm_start_event_sent && event != EVENT_KIND_VM_START)
@@ -2255,6 +2750,8 @@ thread_startup (MonoProfiler *prof, gsize tid)
        // FIXME: Free this somewhere
        tls = g_new0 (DebuggerTlsData, 1);
        tls->resume_event = CreateEvent (NULL, FALSE, FALSE, NULL);
+       MONO_GC_REGISTER_ROOT (tls->thread);
+       tls->thread = thread;
        TlsSetValue (debugger_tls_id, tls);
 
        DEBUG (1, fprintf (log_file, "[%p] Thread started, obj=%p, tls=%p.\n", (gpointer)tid, thread, tls));
@@ -2287,6 +2784,8 @@ thread_end (MonoProfiler *prof, gsize tid)
                tls->terminated = TRUE;
                mono_g_hash_table_remove (tid_to_thread_obj, (gpointer)tid);
                /* Can't remove from tid_to_thread, as that would defeat the check in thread_start () */
+               MONO_GC_UNREGISTER_ROOT (tls->thread);
+               tls->thread = NULL;
        }
        mono_loader_unlock ();
 
@@ -2327,19 +2826,35 @@ assembly_unload (MonoProfiler *prof, MonoAssembly *assembly)
 static void
 start_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
 {
+#if defined(HOST_WIN32) && !defined(__GNUC__)
+       gpointer stackptr = ((guint64)_AddressOfReturnAddress () - sizeof (void*));
+#else
+       gpointer stackptr = __builtin_frame_address (1);
+#endif
+       MonoInternalThread *thread = mono_thread_internal_current ();
+       DebuggerTlsData *tls;
+
+       mono_loader_lock ();
+       
+       tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+       /* Could be the debugger thread with assembly/type load hooks */
+       if (tls)
+               tls->invoke_addr = stackptr;
+
+       mono_loader_unlock ();
 }
 
 static void
 end_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
 {
        int i;
-#ifdef PLATFORM_WIN32
+#if defined(HOST_WIN32) && !defined(__GNUC__)
        gpointer stackptr = ((guint64)_AddressOfReturnAddress () - sizeof (void*));
 #else
-       gpointer stackptr = __builtin_frame_address (0);
+       gpointer stackptr = __builtin_frame_address (1);
 #endif
 
-       if (ss_req == NULL || ss_req->start_sp > stackptr || ss_req->thread != mono_thread_internal_current ())
+       if (ss_req == NULL || stackptr != ss_invoke_addr || ss_req->thread != mono_thread_internal_current ())
                return;
 
        /*
@@ -2347,18 +2862,19 @@ end_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
         * a step out, it may return to native code, and thus never end.
         */
        mono_loader_lock ();
+       ss_invoke_addr = NULL;
+
        for (i = 0; i < event_requests->len; ++i) {
                EventRequest *req = g_ptr_array_index (event_requests, i);
 
                if (req->event_kind == EVENT_KIND_STEP) {
-                       ss_stop (req);
-                        g_ptr_array_remove_index_fast (event_requests, i);
-                        g_free (req);
+                       ss_destroy (req->info);
+                       g_ptr_array_remove_index_fast (event_requests, i);
+                       g_free (req);
                        break;
                }
        }
        mono_loader_unlock ();
-
 }
 
 static void
@@ -2473,16 +2989,16 @@ breakpoints_cleanup (void)
  * JI.
  */
 static void
-insert_breakpoint (GPtrArray *seq_points, MonoJitInfo *ji, MonoBreakpoint *bp)
+insert_breakpoint (MonoSeqPointInfo *seq_points, MonoJitInfo *ji, MonoBreakpoint *bp)
 {
        int i, count;
        gint32 il_offset, native_offset;
        BreakpointInstance *inst;
 
        native_offset = 0;
-       for (i = 0; i < seq_points->len; i += 2) {
-               il_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i));
-               native_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i + 1));
+       for (i = 0; i < seq_points->len; ++i) {
+               il_offset = seq_points->seq_points [i].il_offset;
+               native_offset = seq_points->seq_points [i].native_offset;
 
                if (il_offset == bp->il_offset)
                        break;
@@ -2546,7 +3062,7 @@ static void
 add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
 {
        int i;
-       GPtrArray *seq_points;
+       MonoSeqPointInfo *seq_points;
        MonoDomain *domain;
 
        if (!breakpoints)
@@ -2576,7 +3092,7 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
 }
 
 static void
-set_bp_in_method (MonoDomain *domain, MonoMethod *method, GPtrArray *seq_points, MonoBreakpoint *bp)
+set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_points, MonoBreakpoint *bp)
 {
        gpointer code;
        MonoJitInfo *ji;
@@ -2598,7 +3114,7 @@ static void
 set_bp_in_method_cb (gpointer key, gpointer value, gpointer user_data)
 {
        MonoMethod *method = key;
-       GPtrArray *seq_points = value;
+       MonoSeqPointInfo *seq_points = value;
        MonoBreakpoint *bp = user_data;
        MonoDomain *domain = mono_domain_get ();
 
@@ -2624,7 +3140,7 @@ set_bp_in_method_cb (gpointer key, gpointer value, gpointer user_data)
 static MonoBreakpoint*
 set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req)
 {
-       GPtrArray *seq_points;
+       MonoSeqPointInfo *seq_points;
        MonoDomain *domain;
        MonoBreakpoint *bp;
 
@@ -2641,7 +3157,7 @@ set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req)
        bp->req = req;
        bp->children = g_ptr_array_new ();
 
-       DEBUG(1, fprintf (log_file, "[dbg] Setting breakpoint at %s:0x%x.\n", mono_method_full_name (method, TRUE), (int)il_offset));
+       DEBUG(1, fprintf (log_file, "[dbg] Setting %sbreakpoint at %s:0x%x.\n", (req->event_kind == EVENT_KIND_STEP) ? "single step " : "", method ? mono_method_full_name (method, TRUE) : "<all>", (int)il_offset));
 
        domain = mono_domain_get ();
        mono_domain_lock (domain);
@@ -2693,7 +3209,7 @@ clear_breakpoint (MonoBreakpoint *bp)
 }
 
 static void
-process_breakpoint_inner (MonoContext *ctx)
+process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx)
 {
        MonoJitInfo *ji;
        guint8 *orig_ip, *ip;
@@ -2701,8 +3217,8 @@ process_breakpoint_inner (MonoContext *ctx)
        guint32 native_offset;
        MonoBreakpoint *bp;
        BreakpointInstance *inst;
-       GPtrArray *reqs;
-       GSList *events = NULL;
+       GPtrArray *bp_reqs, *ss_reqs_orig, *ss_reqs;
+       GSList *bp_events = NULL, *ss_events = NULL, *enter_leave_events = NULL;
        EventKind kind = EVENT_KIND_BREAKPOINT;
 
        // FIXME: Speed this up
@@ -2725,10 +3241,12 @@ process_breakpoint_inner (MonoContext *ctx)
         */
        mono_arch_skip_breakpoint (ctx);
 
-       if (ji->method->wrapper_type)
+       if (ji->method->wrapper_type || tls->disable_breakpoints)
                return;
 
-       reqs = g_ptr_array_new ();
+       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, offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, native_offset));
 
@@ -2743,12 +3261,17 @@ process_breakpoint_inner (MonoContext *ctx)
 
                for (j = 0; j < bp->children->len; ++j) {
                        inst = g_ptr_array_index (bp->children, j);
-                       if (inst->ji == ji && inst->native_offset == native_offset)
-                               g_ptr_array_add (reqs, bp->req);
+                       if (inst->ji == ji && inst->native_offset == native_offset) {
+                               if (bp->req->event_kind == EVENT_KIND_STEP) {
+                                       g_ptr_array_add (ss_reqs_orig, bp->req);
+                               } else {
+                                       g_ptr_array_add (bp_reqs, bp->req);
+                               }
+                       }
                }
        }
-       if (reqs->len == 0) {
-               GPtrArray *seq_points;
+       if (bp_reqs->len == 0 && ss_reqs_orig->len == 0) {
+               MonoSeqPointInfo *seq_points;
                int seq_il_offset, seq_native_offset;
                MonoDomain *domain = mono_domain_get ();
 
@@ -2763,9 +3286,9 @@ process_breakpoint_inner (MonoContext *ctx)
                }
                g_assert (seq_points);
 
-               for (i = 0; i < seq_points->len; i += 2) {
-                       seq_il_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i));
-                       seq_native_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i + 1));
+               for (i = 0; i < seq_points->len; ++i) {
+                       seq_il_offset = seq_points->seq_points [i].il_offset;
+                       seq_native_offset = seq_points->seq_points [i].native_offset;
 
                        if (native_offset == seq_native_offset) {
                                if (seq_il_offset == METHOD_ENTRY_IL_OFFSET)
@@ -2776,18 +3299,68 @@ process_breakpoint_inner (MonoContext *ctx)
                        }
                }
        }
+
+       /* Process single step requests */
+       for (i = 0; i < ss_reqs_orig->len; ++i) {
+               EventRequest *req = g_ptr_array_index (ss_reqs_orig, i);
+               SingleStepReq *ss_req = bp->req->info;
+               gboolean hit = TRUE;
+               MonoSeqPointInfo *info;
+               SeqPoint *sp;
+
+               sp = find_seq_point_for_native_offset (mono_domain_get (), ji->method, native_offset, &info);
+               g_assert (sp);
+
+               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);
+
+                       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 (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, NULL);
+       }
        
-       if (reqs->len > 0)
-               events = create_event_list (EVENT_KIND_BREAKPOINT, reqs, ji, NULL, &suspend_policy);
-       else if (kind != EVENT_KIND_BREAKPOINT)
-               events = create_event_list (kind, NULL, ji, NULL, &suspend_policy);
-
-       g_ptr_array_free (reqs, TRUE);
+       if (ss_reqs->len > 0)
+               ss_events = create_event_list (EVENT_KIND_STEP, ss_reqs, ji, NULL, &suspend_policy);
+       if (bp_reqs->len > 0)
+               bp_events = create_event_list (EVENT_KIND_BREAKPOINT, bp_reqs, ji, NULL, &suspend_policy);
+       if (kind != EVENT_KIND_BREAKPOINT)
+               enter_leave_events = create_event_list (kind, NULL, ji, NULL, &suspend_policy);
 
        mono_loader_unlock ();
 
-       if (events)
-               process_event (kind, ji->method, 0, ctx, events, suspend_policy);
+       g_ptr_array_free (bp_reqs, TRUE);
+       g_ptr_array_free (ss_reqs, TRUE);
+
+       /* 
+        * FIXME: The first event will suspend, so the second will only be sent after the
+        * resume.
+        */
+       if (ss_events)
+               process_event (EVENT_KIND_STEP, ji->method, 0, ctx, ss_events, suspend_policy);
+       if (bp_events)
+               process_event (kind, ji->method, 0, ctx, bp_events, suspend_policy);
+       if (enter_leave_events)
+               process_event (kind, ji->method, 0, ctx, enter_leave_events, suspend_policy);
 }
 
 static void
@@ -2803,39 +3376,52 @@ process_breakpoint (void)
        tls = TlsGetValue (debugger_tls_id);
        memcpy (&ctx, &tls->handler_ctx, sizeof (MonoContext));
 
-       process_breakpoint_inner (&ctx);
+       process_breakpoint_inner (tls, &ctx);
 
        /* This is called when resuming from a signal handler, so it shouldn't return */
        restore_context (&ctx);
        g_assert_not_reached ();
 }
 
-void
-mono_debugger_agent_breakpoint_hit (void *sigctx)
+static void
+resume_from_signal_handler (void *sigctx, void *func)
 {
        DebuggerTlsData *tls;
        MonoContext ctx;
 
-       /*
-        * We are called from a signal handler, and running code there causes all kinds of
-        * 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.
-        */
-
+       /* Save the original context in TLS */
        // FIXME: This might not work on an altstack ?
        tls = TlsGetValue (debugger_tls_id);
        g_assert (tls);
 
+       // FIXME: MonoContext usually doesn't include the fp registers, so these are 
+       // clobbered by a single step/breakpoint event. If this turns out to be a problem,
+       // clob:c could be added to op_seq_point.
+
        mono_arch_sigctx_to_monoctx (sigctx, &ctx);
        memcpy (&tls->handler_ctx, &ctx, sizeof (MonoContext));
-       
-       MONO_CONTEXT_SET_IP (&ctx, process_breakpoint);
-
+       MONO_CONTEXT_SET_IP (&ctx, func);
        mono_arch_monoctx_to_sigctx (&ctx, sigctx);
+
+#ifdef PPC_USES_FUNCTION_DESCRIPTOR
+       mono_ppc_set_func_into_sigctx (sigctx, func);
+#endif
+}
+
+void
+mono_debugger_agent_breakpoint_hit (void *sigctx)
+{
+       /*
+        * We are called from a signal handler, and running code there causes all kinds of
+        * 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);
 }
 
 static void
-process_single_step_inner (MonoContext *ctx)
+process_single_step_inner (DebuggerTlsData *tls, MonoContext *ctx)
 {
        MonoJitInfo *ji;
        guint8 *ip;
@@ -2852,19 +3438,7 @@ process_single_step_inner (MonoContext *ctx)
        mono_arch_skip_single_step (ctx);
 
        if (suspend_count > 0) {
-               if (debugger_thread_id == GetCurrentThreadId ())
-                       return;
-
-               DEBUG(1, fprintf (log_file, "[%p] Received single step event for suspending.\n", (gpointer)GetCurrentThreadId ()));
-
-               ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
-
-               /* See the comment below */
-               if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy")))
-                       return;
-
-               save_thread_context (ctx);
-               suspend_current ();
+               process_suspend (tls, ctx);
                return;
        }
 
@@ -3009,7 +3583,7 @@ process_single_step (void)
        tls = TlsGetValue (debugger_tls_id);
        memcpy (&ctx, &tls->handler_ctx, sizeof (MonoContext));
 
-       process_single_step_inner (&ctx);
+       process_single_step_inner (tls, &ctx);
 
        /* This is called when resuming from a signal handler, so it shouldn't return */
        restore_context (&ctx);
@@ -3024,25 +3598,27 @@ process_single_step (void)
 void
 mono_debugger_agent_single_step_event (void *sigctx)
 {
-       DebuggerTlsData *tls;
-       MonoContext ctx;
-
        /* Resume to process_single_step through the signal context */
 
        // FIXME: Since step out/over is implemented using step in, the step in case should
        // be as fast as possible. Move the relevant code from process_single_step_inner ()
        // here
 
-       /* Save the original context in TLS */
-       // FIXME: This might not work on an altstack ?
-       tls = TlsGetValue (debugger_tls_id);
-       g_assert (tls);
-       mono_arch_sigctx_to_monoctx (sigctx, &ctx);
-       memcpy (&tls->handler_ctx, &ctx, sizeof (MonoContext));
-       
-       MONO_CONTEXT_SET_IP (&ctx, process_single_step);
+       if (GetCurrentThreadId () == debugger_thread_id) {
+               /* 
+                * This could happen despite our best effors when the runtime calls 
+                * assembly/type resolve hooks.
+                * FIXME: Breakpoints too.
+                */
+               MonoContext ctx;
 
-       mono_arch_monoctx_to_sigctx (&ctx, sigctx);
+               mono_arch_sigctx_to_monoctx (sigctx, &ctx);
+               mono_arch_skip_single_step (&ctx);
+               mono_arch_monoctx_to_sigctx (&ctx, sigctx);
+               return;
+       }
+
+       resume_from_signal_handler (sigctx, process_single_step);
 }
 
 /*
@@ -3059,6 +3635,17 @@ start_single_stepping (void)
 
        if (val == 1)
                mono_arch_start_single_stepping ();
+
+       if (ss_req != NULL && ss_invoke_addr == NULL) {
+               DebuggerTlsData *tls;
+       
+               mono_loader_lock ();
+       
+               tls = mono_g_hash_table_lookup (thread_to_tls, ss_req->thread);
+               ss_invoke_addr = tls->invoke_addr;
+               
+               mono_loader_unlock ();
+       }
 #else
        g_assert_not_reached ();
 #endif
@@ -3077,13 +3664,101 @@ stop_single_stepping (void)
 #endif
 }
 
+/*
+ * ss_stop:
+ *
+ *   Stop the single stepping operation given by SS_REQ.
+ */
+static void
+ss_stop (SingleStepReq *ss_req)
+{
+       gboolean use_bps = FALSE;
+
+       if (ss_req->bps) {
+               GSList *l;
+
+               use_bps = TRUE;
+
+               for (l = ss_req->bps; l; l = l->next) {
+                       clear_breakpoint (l->data);
+               }
+               g_slist_free (ss_req->bps);
+               ss_req->bps = NULL;
+       }
+
+       if (ss_req->global) {
+               stop_single_stepping ();
+               ss_req->global = FALSE;
+       }
+}
+
+/*
+ * ss_start:
+ *
+ *   Start the single stepping operation given by SS_REQ from the sequence point SP.
+ */
+static void
+ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls)
+{
+       gboolean use_bp = FALSE;
+       int i, frame_index;
+       SeqPoint *next_sp;
+       MonoBreakpoint *bp;
+
+       /* Stop the previous operation */
+       ss_stop (ss_req);
+
+       /*
+        * Implement single stepping using breakpoints if possible.
+        */
+       if (ss_req->depth == STEP_DEPTH_OVER) {
+               frame_index = 1;
+               /*
+                * Find the first sequence point in the current or in a previous frame which
+                * is not the last in its method.
+                */
+               while (sp && sp->next_len == 0) {
+                       sp = NULL;
+                       if (tls && frame_index < tls->frame_count) {
+                               StackFrame *frame = tls->frames [frame_index];
+
+                               method = frame->method;
+                               if (frame->il_offset != -1) {
+                                       sp = find_seq_point (frame->domain, frame->method, frame->il_offset, &info);
+                               }
+                               frame_index ++;
+                       }
+               }
+
+               if (sp && sp->next_len > 0) {
+                       use_bp = TRUE;
+                       for (i = 0; i < sp->next_len; ++i) {
+                               next_sp = &info->seq_points [sp->next [i]];
+
+                               bp = set_breakpoint (method, next_sp->il_offset, ss_req->req);
+                               ss_req->bps = g_slist_append (ss_req->bps, bp);
+                       }
+               }
+       }
+
+       if (!ss_req->bps) {
+               ss_req->global = TRUE;
+               start_single_stepping ();
+       } else {
+               ss_req->global = FALSE;
+       }
+}
+
 /*
  * Start single stepping of thread THREAD
  */
 static ErrorCode
-ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req)
+ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req)
 {
        DebuggerTlsData *tls;
+       MonoSeqPointInfo *info;
+       SeqPoint *sp = NULL;
+       MonoMethod *method = NULL;
 
        if (suspend_count == 0)
                return ERR_NOT_SUSPENDED;
@@ -3091,10 +3766,12 @@ ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventReque
        wait_for_suspend ();
 
        // FIXME: Multiple requests
-       if (ss_req)
+       if (ss_req) {
+               DEBUG (0, printf ("Received a single step request while the previous one was still active.\n"));
                return ERR_NOT_IMPLEMENTED;
+       }
 
-       ss_req = g_new0 (MonoSingleStepReq, 1);
+       ss_req = g_new0 (SingleStepReq, 1);
        ss_req->req = req;
        ss_req->thread = thread;
        ss_req->size = size;
@@ -3132,22 +3809,37 @@ ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventReque
                }
        }
 
-       start_single_stepping ();
+       if (ss_req->depth == STEP_DEPTH_OVER) {
+               StackFrame *frame;
+
+               compute_frame_info (thread, tls);
+
+               g_assert (tls->frame_count);
+               frame = tls->frames [0];
+
+               if (frame->il_offset != -1) {
+                       /* FIXME: Sort the table and use a binary search */
+                       sp = find_seq_point (frame->domain, frame->method, frame->il_offset, &info);
+                       g_assert (sp);
+                       method = frame->method;
+               }
+       }
+
+       ss_start (ss_req, method, sp, info, NULL, tls);
 
        return 0;
 }
 
 static void
-ss_stop (EventRequest *req)
+ss_destroy (SingleStepReq *req)
 {
        // FIXME: Locking
-       g_assert (ss_req);
-       g_assert (ss_req->req == req);
+       g_assert (ss_req == req);
+
+       ss_stop (ss_req);
 
        g_free (ss_req);
        ss_req = NULL;
-
-       stop_single_stepping ();
 }
 
 void
@@ -3155,24 +3847,74 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *ctx)
 {
        int suspend_policy;
        GSList *events;
+       MonoJitInfo *ji;
 
-       if (!agent_config.enabled)
+       /* Just-In-Time debugging */
+       if (agent_config.onthrow && !inited) {
+               GSList *l;
+               gboolean found = FALSE;
+
+               for (l = agent_config.onthrow; l; l = l->next) {
+                       char *ex_type = l->data;
+                       char *f = mono_type_full_name (&exc->object.vtable->klass->byval_arg);
+
+                       if (!strcmp (ex_type, "") || !strcmp (ex_type, f))
+                               found = TRUE;
+
+                       g_free (f);
+               }
+
+               if (found) {
+                       finish_agent_init (FALSE);
+
+                       /*
+                        * Send an unsolicited EXCEPTION event with a dummy request id.
+                        */
+                       events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff));
+                       process_event (EVENT_KIND_EXCEPTION, exc, 0, ctx, events, SUSPEND_POLICY_ALL);
+                       return;
+               }
+       }
+
+       if (!inited)
                return;
 
+       ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (ctx), NULL);
+
        mono_loader_lock ();
-       events = create_event_list (EVENT_KIND_EXCEPTION, NULL, NULL, exc, &suspend_policy);
+       events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, exc, &suspend_policy);
        mono_loader_unlock ();
 
        process_event (EVENT_KIND_EXCEPTION, exc, 0, ctx, events, suspend_policy);
 }
 
+void
+mono_debugger_agent_handle_unhandled_exception (MonoException *exc, MonoContext *ctx)
+{
+       GSList *events;
+
+       if (!agent_config.onuncaught)
+               return;
+
+       finish_agent_init (FALSE);
+
+       /*
+        * Send an unsolicited EXCEPTION event with a dummy request id.
+        */
+       events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff));
+       process_event (EVENT_KIND_EXCEPTION, exc, 0, ctx, events, SUSPEND_POLICY_ALL);
+}
+
 /*
- * buffer_add_value:
+ * buffer_add_value_full:
  *
  *   Add the encoding of the value at ADDR described by T to the buffer.
+ * AS_VTYPE determines whenever to treat primitive types as primitive types or
+ * vtypes.
  */
 static void
-buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
+buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain,
+                                          gboolean as_vtype)
 {
        MonoObject *obj;
 
@@ -3181,6 +3923,30 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
                addr = *(void**)addr;
        }
 
+       if (as_vtype) {
+               switch (t->type) {
+               case MONO_TYPE_BOOLEAN:
+               case MONO_TYPE_I1:
+               case MONO_TYPE_U1:
+               case MONO_TYPE_CHAR:
+               case MONO_TYPE_I2:
+               case MONO_TYPE_U2:
+               case MONO_TYPE_I4:
+               case MONO_TYPE_U4:
+               case MONO_TYPE_R4:
+               case MONO_TYPE_I8:
+               case MONO_TYPE_U8:
+               case MONO_TYPE_R8:
+               case MONO_TYPE_I:
+               case MONO_TYPE_U:
+               case MONO_TYPE_PTR:
+                       goto handle_vtype;
+                       break;
+               default:
+                       break;
+               }
+       }
+
        switch (t->type) {
        case MONO_TYPE_VOID:
                buffer_add_byte (buf, t->type);
@@ -3211,6 +3977,8 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
                break;
        case MONO_TYPE_I:
        case MONO_TYPE_U:
+               /* Treat it as a vtype */
+               goto handle_vtype;
        case MONO_TYPE_PTR: {
                gssize val = *(gssize*)addr;
                
@@ -3271,7 +4039,7 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
                                continue;
                        if (mono_field_is_deleted (f))
                                continue;
-                       buffer_add_value (buf, f->type, (guint8*)addr + f->offset - sizeof (MonoObject), domain);
+                       buffer_add_value_full (buf, f->type, (guint8*)addr + f->offset - sizeof (MonoObject), domain, FALSE);
                }
                break;
        }
@@ -3287,14 +4055,23 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
        }
 }
 
+static void
+buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
+{
+       buffer_add_value_full (buf, t, addr, domain, FALSE);
+}
+
 static ErrorCode
 decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
 {
        int err;
        int type = decode_byte (buf, &buf, limit);
 
-       if (type != t->type && !MONO_TYPE_IS_REFERENCE (t)) {
-               DEBUG(1, fprintf (log_file, "Expected value of type %d, got %d.\n", t->type, type));
+       if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) &&
+               !(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) &&
+               !(t->type == MONO_TYPE_U && type == MONO_TYPE_VALUETYPE) &&
+               !(t->type == MONO_TYPE_PTR && type == MONO_TYPE_I8)) {
+               DEBUG(1, fprintf (log_file, "[%p] Expected value of type 0x%0x, got 0x%0x.\n", (gpointer)GetCurrentThreadId (), t->type, type));
                return ERR_INVALID_ARGUMENT;
        }
 
@@ -3335,6 +4112,16 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8
        case MONO_TYPE_R8:
                *(guint64*)addr = decode_long (buf, &buf, limit);
                break;
+       case MONO_TYPE_PTR:
+               /* We send these as I8, so we get them back as such */
+               g_assert (type == MONO_TYPE_I8);
+               *(gssize*)addr = decode_long (buf, &buf, limit);
+               break;
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+               /* We send these as vtypes, so we get them back as such */
+               g_assert (type == MONO_TYPE_VALUETYPE);
+               /* Fall through */
        case MONO_TYPE_VALUETYPE: {
                gboolean is_enum = decode_byte (buf, &buf, limit);
                MonoClass *klass;
@@ -3401,7 +4188,7 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8
 }
 
 static void
-add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain)
+add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype)
 {
        guint32 flags;
        int reg;
@@ -3415,7 +4202,7 @@ add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, Mono
        case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
                reg_val = mono_arch_context_get_int_reg (ctx, reg);
 
-               buffer_add_value (buf, t, &reg_val, domain);
+               buffer_add_value_full (buf, t, &reg_val, domain, as_vtype);
                break;
        case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
                addr = mono_arch_context_get_int_reg (ctx, reg);
@@ -3423,7 +4210,7 @@ add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, Mono
 
                //printf ("[R%d+%d] = %p\n", reg, var->offset, addr);
 
-               buffer_add_value (buf, t, addr, domain);
+               buffer_add_value_full (buf, t, addr, domain, as_vtype);
                break;
        case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
                NOT_IMPLEMENTED;
@@ -3483,7 +4270,11 @@ clear_event_request (int req_id, int etype)
                        if (req->event_kind == EVENT_KIND_BREAKPOINT)
                                clear_breakpoint (req->info);
                        if (req->event_kind == EVENT_KIND_STEP)
-                               ss_stop (req);
+                               ss_destroy (req->info);
+                       if (req->event_kind == EVENT_KIND_METHOD_ENTRY)
+                               clear_breakpoint (req->info);
+                       if (req->event_kind == EVENT_KIND_METHOD_EXIT)
+                               clear_breakpoint (req->info);
                        g_ptr_array_remove_index_fast (event_requests, i);
                        g_free (req);
                        break;
@@ -3502,7 +4293,7 @@ add_thread (gpointer key, gpointer value, gpointer user_data)
 }
 
 static ErrorCode
-do_invoke_method (Buffer *buf, InvokeData *invoke)
+do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
 {
        guint8 *p = invoke->p;
        guint8 *end = invoke->endp;
@@ -3518,6 +4309,16 @@ do_invoke_method (Buffer *buf, InvokeData *invoke)
        MonoLMFExt ext;
 #endif
 
+       if (invoke->method) {
+               /* 
+                * Invoke this method directly, currently only Environment.Exit () is supported.
+                */
+               this = NULL;
+               DEBUG (1, printf ("[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (invoke->method, TRUE), this ? this->vtable->klass->name : "<null>"));
+               mono_runtime_invoke (invoke->method, NULL, invoke->args, &exc);
+               g_assert_not_reached ();
+       }
+
        m = decode_methodid (p, &p, end, &domain, &err);
        if (err)
                return err;
@@ -3582,6 +4383,11 @@ do_invoke_method (Buffer *buf, InvokeData *invoke)
        if (i < nargs)
                return err;
 
+       if (invoke->flags & INVOKE_FLAG_DISABLE_BREAKPOINTS)
+               tls->disable_breakpoints = TRUE;
+       else
+               tls->disable_breakpoints = FALSE;
+
        /* 
         * Add an LMF frame to link the stack frames on the invoke method with our caller.
         */
@@ -3609,6 +4415,11 @@ do_invoke_method (Buffer *buf, InvokeData *invoke)
                /* Mark that this is a MonoLMFExt */
                ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
                ext.lmf.ebp = (gssize)&ext;
+#elif defined(TARGET_POWERPC)
+               ext.lmf.previous_lmf = *(lmf_addr);
+               /* Mark that this is a MonoLMFExt */
+               ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
+               ext.lmf.ebp = (gssize)&ext;
 #else
                g_assert_not_reached ();
 #endif
@@ -3645,6 +4456,8 @@ do_invoke_method (Buffer *buf, InvokeData *invoke)
                }
        }
 
+       tls->disable_breakpoints = FALSE;
+
 #ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
        if (invoke->has_ctx)
                mono_set_lmf ((gpointer)(((gssize)ext.lmf.previous_lmf) & ~3));
@@ -3687,10 +4500,11 @@ invoke_method (void)
 
        buffer_init (&buf, 128);
 
-       err = do_invoke_method (&buf, invoke);
+       err = do_invoke_method (tls, &buf, invoke);
 
        /* Start suspending before sending the reply */
-       suspend_vm ();
+       if (!(invoke->flags & INVOKE_FLAG_SINGLE_THREADED))
+               suspend_vm ();
 
        send_reply_packet (id, err, &buf);
        
@@ -3701,12 +4515,35 @@ invoke_method (void)
        if (invoke->has_ctx)
                save_thread_context (&restore_ctx);
 
+       if (invoke->flags & INVOKE_FLAG_SINGLE_THREADED) {
+               g_assert (tls->resume_count);
+               tls->resume_count -= invoke->suspend_count;
+       }
+
+       DEBUG (1, printf ("[%p] Invoke finished, resume_count = %d.\n", (gpointer)GetCurrentThreadId (), tls->resume_count));
+
        g_free (invoke->p);
        g_free (invoke);
 
        suspend_current ();
 }
 
+static gboolean
+is_really_suspended (gpointer key, gpointer value, gpointer user_data)
+{
+       MonoThread *thread = value;
+       DebuggerTlsData *tls;
+       gboolean res;
+
+       mono_loader_lock ();
+       tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+       g_assert (tls);
+       res = tls->really_suspended;
+       mono_loader_unlock ();
+
+       return res;
+}
+
 static ErrorCode
 vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
 {
@@ -3756,7 +4593,14 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                disconnected = TRUE;
                break;
        case CMD_VM_EXIT: {
-               int exit_code = decode_int (p, &p, end);
+               MonoInternalThread *thread;
+               DebuggerTlsData *tls;
+               MonoClass *env_class;
+               MonoMethod *exit_method;
+               gpointer *args;
+               int exit_code;
+
+               exit_code = decode_int (p, &p, end);
 
                // FIXME: What if there is a VM_DEATH event request with SUSPEND_ALL ?
 
@@ -3772,43 +4616,80 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                }
                mono_loader_unlock ();
 
-               /* FIXME: Races with normal shutdown */
-               while (suspend_count > 0)
-                       resume_vm ();
-
                /*
                 * The JDWP documentation says that the shutdown is not orderly. It doesn't
                 * specify whenever a VM_DEATH event is sent. We currently do an orderly
-                * shutdown similar to Environment.Exit ().
+                * shutdown by hijacking a thread to execute Environment.Exit (). This is
+                * better than doing the shutdown ourselves, since it avoids various races.
                 */
-               mono_runtime_set_shutting_down ();
 
-               mono_threads_set_shutting_down ();
+               suspend_vm ();
+               wait_for_suspend ();
+
+               env_class = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
+               g_assert (env_class);
+               exit_method = mono_class_get_method_from_name (env_class, "Exit", 1);
+               g_assert (exit_method);
+
+               mono_loader_lock ();
+               thread = mono_g_hash_table_find (tid_to_thread, is_really_suspended, NULL);
+               mono_loader_unlock ();
+
+               if (thread) {
+                       mono_loader_lock ();
+                       tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+                       mono_loader_unlock ();
+
+                       args = g_new0 (gpointer, 1);
+                       args [0] = g_malloc (sizeof (int));
+                       *(int*)(args [0]) = exit_code;
+
+                       tls->invoke = g_new0 (InvokeData, 1);
+                       tls->invoke->method = exit_method;
+                       tls->invoke->args = args;
+
+                       while (suspend_count > 0)
+                               resume_vm ();
+               } else {
+                       /* 
+                        * No thread found, do it ourselves.
+                        * FIXME: This can race with normal shutdown etc.
+                        */
+                       while (suspend_count > 0)
+                               resume_vm ();
 
-               /* Suspend all managed threads since the runtime is going away */
-               DEBUG(1, fprintf (log_file, "Suspending all threads...\n"));
-               mono_thread_suspend_all_other_threads ();
-               DEBUG(1, fprintf (log_file, "Shutting down the runtime...\n"));
-               mono_runtime_quit ();
-#ifdef PLATFORM_WIN32
-               shutdown (conn_fd, SD_BOTH);
+                       mono_runtime_set_shutting_down ();
+
+                       mono_threads_set_shutting_down ();
+
+                       /* Suspend all managed threads since the runtime is going away */
+                       DEBUG(1, fprintf (log_file, "Suspending all threads...\n"));
+                       mono_thread_suspend_all_other_threads ();
+                       DEBUG(1, fprintf (log_file, "Shutting down the runtime...\n"));
+                       mono_runtime_quit ();
+#ifdef HOST_WIN32
+                       shutdown (conn_fd, SD_BOTH);
 #else
-               shutdown (conn_fd, SHUT_RDWR);
+                       shutdown (conn_fd, SHUT_RDWR);
 #endif
-               DEBUG(1, fprintf (log_file, "Exiting...\n"));
+                       DEBUG(1, fprintf (log_file, "Exiting...\n"));
 
-               exit (exit_code);
+                       exit (exit_code);
+               }
+               break;
        }               
        case CMD_VM_INVOKE_METHOD: {
                int objid = decode_objid (p, &p, end);
                MonoThread *thread;
                DebuggerTlsData *tls;
-               int err;
+               int err, flags;
 
                err = get_object (objid, (MonoObject**)&thread);
                if (err)
                        return err;
 
+               flags = decode_int (p, &p, end);
+
                // Wait for suspending if it already started
                if (suspend_count)
                        wait_for_suspend ();
@@ -3816,10 +4697,14 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                        return ERR_NOT_SUSPENDED;
 
                mono_loader_lock ();
-               tls = mono_g_hash_table_lookup (thread_to_tls, thread->internal_thread);
+               tls = mono_g_hash_table_lookup (thread_to_tls, THREAD_TO_INTERNAL (thread));
                mono_loader_unlock ();
                g_assert (tls);
 
+               if (!tls->really_suspended)
+                       /* The thread is still running native code, can't do invokes */
+                       return ERR_NOT_SUSPENDED;
+
                /* 
                 * Store the invoke data into tls, the thread will execute it after it is
                 * resumed.
@@ -3828,11 +4713,16 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                        NOT_IMPLEMENTED;
                tls->invoke = g_new0 (InvokeData, 1);
                tls->invoke->id = id;
+               tls->invoke->flags = flags;
                tls->invoke->p = g_malloc (end - p);
                memcpy (tls->invoke->p, p, end - p);
                tls->invoke->endp = tls->invoke->p + (end - p);
+               tls->invoke->suspend_count = suspend_count;
 
-               resume_vm ();
+               if (flags & INVOKE_FLAG_SINGLE_THREADED)
+                       resume_thread (THREAD_TO_INTERNAL (thread));
+               else
+                       resume_vm ();
                break;
        }
        default:
@@ -3852,9 +4742,9 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                EventRequest *req;
                int i, event_kind, suspend_policy, nmodifiers, mod;
                MonoMethod *method;
-               long location;
+               long location = 0;
                MonoThread *step_thread;
-               int size, depth, step_thread_id;
+               int size = 0, depth = 0, step_thread_id = 0;
                MonoDomain *domain;
 
                event_kind = decode_byte (p, &p, end);
@@ -3935,7 +4825,7 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                                return err;
                        }
 
-                       err = ss_start (step_thread->internal_thread, size, depth, req);
+                       err = ss_create (THREAD_TO_INTERNAL (step_thread), size, depth, req);
                        if (err) {
                                g_free (req);
                                return err;
@@ -4133,6 +5023,22 @@ assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
                break;
        }
+       case CMD_ASSEMBLY_GET_NAME: {
+               gchar *name;
+               MonoAssembly *mass = ass;
+
+               name = g_strdup_printf (
+                 "%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s%s",
+                 mass->aname.name,
+                 mass->aname.major, mass->aname.minor, mass->aname.build, mass->aname.revision,
+                 mass->aname.culture && *mass->aname.culture? mass->aname.culture: "neutral",
+                 mass->aname.public_key_token [0] ? (char *)mass->aname.public_key_token : "null",
+                 (mass->aname.flags & ASSEMBLYREF_RETARGETABLE_FLAG) ? ", Retargetable=Yes" : "");
+
+               buffer_add_string (buf, name);
+               g_free (name);
+               break;
+       }
        default:
                return ERR_NOT_IMPLEMENTED;
        }
@@ -4239,7 +5145,7 @@ buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass
                                                g_assert_not_reached ();
                                        }
 
-                                       buffer_add_cattr_arg (buf, t, domain, val);
+                                       buffer_add_cattr_arg (buf, arginfo [j].type, domain, val);
                                }
                        } else {
                                buffer_add_int (buf, 0);
@@ -4765,7 +5671,7 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
        if (err)
                return err;
 
-       thread = thread_obj->internal_thread;
+       thread = THREAD_TO_INTERNAL (thread_obj);
           
        switch (command) {
        case CMD_THREAD_GET_NAME: {
@@ -4859,7 +5765,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
        if (err)
                return err;
 
-       thread = thread_obj->internal_thread;
+       thread = THREAD_TO_INTERNAL (thread_obj);
 
        id = decode_id (p, &p, end);
 
@@ -4877,6 +5783,10 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
        frame = tls->frames [i];
 
+       if (!frame->has_ctx)
+               // FIXME:
+               return ERR_INVALID_FRAMEID;
+
        if (!frame->jit) {
                frame->jit = mono_debug_find_method (frame->method, frame->domain);
                g_assert (frame->jit);
@@ -4900,13 +5810,13 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
 
                                var = &jit->params [pos];
 
-                               add_var (buf, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain);
+                               add_var (buf, 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);
+                               add_var (buf, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE);
                        }
                }
                break;
@@ -4917,14 +5827,14 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
                                MonoObject *p = NULL;
                                buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &p, frame->domain);
                        } else {
-                               add_var (buf, &frame->method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain);
+                               add_var (buf, &frame->method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
                        }
                } else {
                        if (!sig->hasthis) {
                                MonoObject *p = NULL;
                                buffer_add_value (buf, &frame->method->klass->byval_arg, &p, frame->domain);
                        } else {
-                               add_var (buf, &frame->method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain);
+                               add_var (buf, &frame->method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
                        }
                }
                break;
@@ -5241,8 +6151,10 @@ debugger_thread (void *arg)
 
        mono_thread_internal_current ()->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
 
+       mono_set_is_debugger_attached (TRUE);
+
        while (TRUE) {
-               res = read (conn_fd, header, HEADER_LENGTH);
+               res = recv (conn_fd, header, HEADER_LENGTH, 0);
 
                /* This will break if the socket is closed during shutdown too */
                if (res != HEADER_LENGTH)
@@ -5262,9 +6174,12 @@ debugger_thread (void *arg)
                DEBUG (1, fprintf (log_file, "[dbg] Received command %s(%d), id=%d.\n", command_set_to_string (command_set), command, id));
 
                data = g_malloc (len - HEADER_LENGTH);
-               res = read (conn_fd, data, len - HEADER_LENGTH);
-               if (res != len - HEADER_LENGTH)
-                       break;
+               if (len - HEADER_LENGTH > 0)
+               {
+                       res = recv (conn_fd, data, len - HEADER_LENGTH, 0);
+                       if (res != len - HEADER_LENGTH)
+                               break;
+               }
 
                p = data;
                end = data + (len - HEADER_LENGTH);
@@ -5329,16 +6244,14 @@ debugger_thread (void *arg)
                        break;
        }
 
+       mono_set_is_debugger_attached (FALSE);
+
        mono_mutex_lock (&debugger_thread_exited_mutex);
        debugger_thread_exited = TRUE;
        mono_cond_signal (&debugger_thread_exited_cond);
        mono_mutex_unlock (&debugger_thread_exited_mutex);
 
-#ifdef PLATFORM_WIN32
-       shutdown (conn_fd, SD_BOTH);
-#else
-       shutdown (conn_fd, SHUT_RDWR);
-#endif
+       DEBUG (1, printf ("[dbg] Debugger thread exited.\n"));
 
        return 0;
 }
@@ -5356,11 +6269,6 @@ mono_debugger_agent_init (void)
 {
 }
 
-void
-mono_debugger_agent_cleanup (void)
-{
-}
-
 void
 mono_debugger_agent_breakpoint_hit (void *sigctx)
 {
@@ -5376,8 +6284,10 @@ mono_debugger_agent_free_domain_info (MonoDomain *domain)
 {
 }
 
-gboolean mono_debugger_agent_thread_interrupt (MonoJitInfo *ji)
+gboolean
+mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
 {
+       return FALSE;
 }
 
 void
@@ -5385,5 +6295,10 @@ mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *ctx)
 {
 }
 
+void
+mono_debugger_agent_handle_unhandled_exception (MonoException *exc, MonoContext *ctx)
+{
+       
+}
 #endif