#include <ucontext.h>
#endif
+#ifdef HOST_WIN32
+#ifdef _MSC_VER
+#include <winsock2.h>
+#endif
+#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 <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;
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 {
* 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 */
/* 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;
/*
#define HEADER_LENGTH 11
-#define MAJOR_VERSION 0
-#define MINOR_VERSION 2
+#define MAJOR_VERSION 2
+#define MINOR_VERSION 1
typedef enum {
CMD_SET_VM = 1,
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,
CMD_APPDOMAIN_GET_ASSEMBLIES = 3,
CMD_APPDOMAIN_GET_ENTRY_ASSEMBLY = 4,
CMD_APPDOMAIN_CREATE_STRING = 5,
- CMD_APPDOMAIN_GET_CORLIB = 6
+ CMD_APPDOMAIN_GET_CORLIB = 6,
+ CMD_APPDOMAIN_CREATE_BOXED_VALUE = 7,
} CmdAppDomain;
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 {
MonoClass *exc_class; /* For kind == MONO_KIND_EXCEPTION_ONLY */
MonoAssembly **assemblies; /* For kind == MONO_KIND_ASSEMBLY_ONLY */
} data;
+ gboolean caught, uncaught; /* For kind == MOD_KIND_EXCEPTION_ONLY */
} Modifier;
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;
+
+/*
+ * Contains additional information for an event
+ */
+typedef struct {
+ /* For EVENT_KIND_EXCEPTION */
+ MonoObject *exc;
+ MonoContext catch_ctx;
+ gboolean caught;
+} EventInfo;
/* Dummy structure used for the profiler callbacks */
typedef struct {
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 void runtime_shutdown (MonoProfiler *prof);
-static void thread_startup (MonoProfiler *prof, gsize tid);
+static void thread_startup (MonoProfiler *prof, intptr_t tid);
-static void thread_end (MonoProfiler *prof, gsize tid);
+static void thread_end (MonoProfiler *prof, intptr_t tid);
static void appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result);
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);
} 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);
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);
- mono_profiler_install_appdomain (NULL, appdomain_load, appdomain_unload, NULL);
+ mono_profiler_install_appdomain (NULL, appdomain_load, NULL, appdomain_unload);
mono_profiler_install_thread (thread_startup, thread_end);
mono_profiler_install_assembly (NULL, assembly_load, assembly_unload, NULL);
mono_profiler_install_jit_end (jit_end);
/* 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);
//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);
}
+/*
+ * recv_length:
+ *
+ * recv() + handle incomplete reads and EINTR
+ */
+static int
+recv_length (int fd, void *buf, int len, int flags)
+{
+ int res;
+ int total = 0;
+
+ do {
+ res = recv (fd, (char *) buf + total, len - total, flags);
+ if (res > 0)
+ total += res;
+ } while ((res > 0 && total < len) || (res == -1 && errno == EINTR));
+ return total;
+}
/*
* transport_connect:
*
if (host) {
sprintf (port_string, "%d", port);
+ mono_network_init ();
+
/* Obtain address(es) matching host/port */
memset (&hints, 0, sizeof (struct addrinfo));
break;
}
+#ifndef HOST_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
}
DEBUG (1, fprintf (log_file, "Listening on %s:%d (timeout=%d ms)...\n", host, port, agent_config.timeout));
conn_fd = sfd;
+#ifndef HOST_WIN32
+ /* See the comment above */
freeaddrinfo (result);
+#endif
if (rp == 0) {
fprintf (stderr, "debugger-agent: Unable to connect to %s:%d\n", host, port);
/* Write handshake message */
sprintf (handshake_msg, "DWP-Handshake");
- res = write (conn_fd, handshake_msg, strlen (handshake_msg));
+ do {
+ res = send (conn_fd, handshake_msg, strlen (handshake_msg), 0);
+ } while (res == -1 && errno == EINTR);
g_assert (res != -1);
/* Read answer */
- res = read (conn_fd, buf, strlen (handshake_msg));
+ res = recv_length (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);
{
int res;
- res = write (conn_fd, data, len);
+ do {
+ res = send (conn_fd, data, len, 0);
+ } while (res == -1 && errno == EINTR);
if (res != len)
return FALSE;
else
* 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)
{
*/
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;
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 (!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.
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;
+ }
+
+ mono_memory_barrier ();
+
tls->suspended = TRUE;
MONO_SEM_POST (&suspend_sem);
}
}
}
+#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:
*
DebuggerTlsData *tls = value;
gsize tid = thread->tid;
- if (GetCurrentThreadId () != tid) {
- DEBUG(1, fprintf (log_file, "[%p] Interrupting %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid));
- /*
- * 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 */
+ if (GetCurrentThreadId () == tid || tls->terminated)
+ return;
+
+ 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);
+#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 ());
+ 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 = 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")))
+ return;
+
+ save_thread_context (ctx);
+
+ suspend_current ();
}
/*
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 ();
}
mono_mutex_lock (&suspend_mutex);
+ tls->suspending = FALSE;
+ tls->really_suspended = TRUE;
+
if (!tls->suspended) {
tls->suspended = TRUE;
MONO_SEM_POST (&suspend_sem);
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 --;
/* The frame info becomes invalid after a resume */
tls->has_context = FALSE;
+ tls->has_async_ctx = FALSE;
invalidate_frames (NULL);
}
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:
*
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);
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;
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);
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:
* LOCKING: Assumes the loader lock is held.
*/
static GSList*
-create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, MonoException *exc, int *suspend_policy)
+create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo *ei, int *suspend_policy)
{
int i, j;
GSList *events = NULL;
} else if (mod->kind == MOD_KIND_THREAD_ONLY) {
if (mod->data.thread != mono_thread_internal_current ())
filtered = TRUE;
- } else if (mod->kind == MOD_KIND_EXCEPTION_ONLY && exc) {
- if (mod->data.exc_class && !mono_class_is_assignable_from (mod->data.exc_class, exc->object.vtable->klass))
+ } 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))
+ filtered = TRUE;
+ if (ei->caught && !mod->caught)
+ filtered = TRUE;
+ if (!ei->caught && !mod->uncaught)
filtered = TRUE;
} else if (mod->kind == MOD_KIND_ASSEMBLY_ONLY && ji) {
int k;
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;
}
break;
case EVENT_KIND_VM_DEATH:
break;
- case EVENT_KIND_EXCEPTION:
- buffer_add_objid (&buf, (MonoObject*)arg);
+ case EVENT_KIND_EXCEPTION: {
+ EventInfo *ei = arg;
+ buffer_add_objid (&buf, ei->exc);
break;
+ }
default:
g_assert_not_reached ();
}
}
static void
-thread_startup (MonoProfiler *prof, gsize tid)
+thread_startup (MonoProfiler *prof, intptr_t tid)
{
MonoInternalThread *thread = mono_thread_internal_current ();
MonoInternalThread *old_thread;
// 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));
}
static void
-thread_end (MonoProfiler *prof, gsize tid)
+thread_end (MonoProfiler *prof, intptr_t tid)
{
MonoInternalThread *thread;
DebuggerTlsData *tls = NULL;
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 ();
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;
/*
* 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
* 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;
add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
{
int i;
- GPtrArray *seq_points;
+ MonoSeqPointInfo *seq_points;
MonoDomain *domain;
if (!breakpoints)
}
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;
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 ();
static MonoBreakpoint*
set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req)
{
- GPtrArray *seq_points;
+ MonoSeqPointInfo *seq_points;
MonoDomain *domain;
MonoBreakpoint *bp;
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);
}
static void
-process_breakpoint_inner (MonoContext *ctx)
+process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx)
{
MonoJitInfo *ji;
guint8 *orig_ip, *ip;
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
orig_ip = ip = MONO_CONTEXT_GET_IP (ctx);
- ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+ ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
g_assert (ji);
g_assert (ji->method);
*/
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));
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 ();
}
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)
}
}
}
-
- 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);
+ /* 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 (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
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);
mono_arch_sigctx_to_monoctx (sigctx, &ctx);
memcpy (&tls->handler_ctx, &ctx, sizeof (MonoContext));
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
}
static void
-process_single_step_inner (MonoContext *ctx)
+process_single_step_inner (DebuggerTlsData *tls, MonoContext *ctx)
{
MonoJitInfo *ji;
guint8 *ip;
GPtrArray *reqs;
int il_offset, suspend_policy;
- MonoDomain *domain = mono_domain_get ();
+ MonoDomain *domain;
GSList *events;
// FIXME: Speed this up
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;
}
if (log_level > 0) {
const char *depth = NULL;
- ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+ ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
switch (ss_req->depth) {
case STEP_DEPTH_OVER:
ss_req->last_sp = MONO_CONTEXT_GET_SP (ctx);
}
- ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+ ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
g_assert (ji);
g_assert (ji->method);
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);
// be as fast as possible. Move the relevant code from process_single_step_inner ()
// here
+ 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_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);
}
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
#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;
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;
}
}
- 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
-mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *ctx)
+mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx,
+ MonoContext *catch_ctx)
{
int suspend_policy;
GSList *events;
+ MonoJitInfo *ji;
+ EventInfo ei;
+
+ memset (&ei, 0, sizeof (EventInfo));
/* Just-In-Time debugging */
- if (agent_config.onthrow && !inited) {
+ if (!catch_ctx) {
+ if (agent_config.onuncaught && !inited) {
+ finish_agent_init (FALSE);
+
+ /*
+ * Send an unsolicited EXCEPTION event with a dummy request id.
+ */
+ events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff));
+ ei.exc = (MonoObject*)exc;
+ process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, SUSPEND_POLICY_ALL);
+ return;
+ }
+ } else if (agent_config.onthrow && !inited) {
GSList *l;
gboolean found = FALSE;
char *ex_type = l->data;
char *f = mono_type_full_name (&exc->object.vtable->klass->byval_arg);
- if (!strcmp (ex_type, f))
+ if (!strcmp (ex_type, "") || !strcmp (ex_type, f))
found = TRUE;
g_free (f);
* 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);
+ ei.exc = (MonoObject*)exc;
+ process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, SUSPEND_POLICY_ALL);
return;
}
}
if (!inited)
return;
- mono_loader_lock ();
- events = create_event_list (EVENT_KIND_EXCEPTION, NULL, NULL, 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;
+ ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (throw_ctx), NULL);
- if (!agent_config.onuncaught)
- return;
+ ei.exc = (MonoObject*)exc;
+ ei.caught = catch_ctx != NULL;
- finish_agent_init (FALSE);
+ mono_loader_lock ();
+ events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, &ei, &suspend_policy);
+ mono_loader_unlock ();
- /*
- * 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);
+ process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, suspend_policy);
}
/*
- * 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;
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);
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;
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;
}
}
}
+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) &&
+ !(t->type == MONO_TYPE_GENERICINST && type == MONO_TYPE_VALUETYPE)) {
+ 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;
}
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_GENERICINST:
+ if (MONO_TYPE_ISSTRUCT (t)) {
+ /* The client sends these as a valuetype */
+ goto handle_vtype;
+ } else {
+ goto handle_ref;
+ }
+ 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 */
+ handle_vtype:
case MONO_TYPE_VALUETYPE: {
gboolean is_enum = decode_byte (buf, &buf, limit);
MonoClass *klass;
g_assert (nfields == 0);
break;
}
+ handle_ref:
default:
if (MONO_TYPE_IS_REFERENCE (t)) {
if (type == MONO_TYPE_OBJECT) {
}
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;
case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
reg_val = mono_arch_context_get_int_reg (ctx, reg);
- buffer_add_value (buf, t, ®_val, domain);
+ buffer_add_value_full (buf, t, ®_val, domain, as_vtype);
break;
case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
addr = mono_arch_context_get_int_reg (ctx, reg);
//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;
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;
}
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;
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;
this_buf = g_alloca (mono_class_instance_size (m->klass));
else
this_buf = g_alloca (sizeof (MonoObject*));
- err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end);
- if (err)
- return err;
+ 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)
+ 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);
+ if (err)
+ return err;
+ }
if (!m->klass->valuetype)
this = *(MonoObject**)this_buf;
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.
*/
/* 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
}
}
+ 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));
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);
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)
{
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 ?
}
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 ();
- /* 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);
+ 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 ();
+
+ 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 ();
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.
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:
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);
if (err)
return err;
+ req->modifiers [i].caught = decode_byte (p, &p, end);
+ req->modifiers [i].uncaught = decode_byte (p, &p, end);
if (exc_class) {
req->modifiers [i].data.exc_class = exc_class;
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;
buffer_add_objid (buf, (MonoObject*)o);
break;
}
+ case CMD_APPDOMAIN_CREATE_BOXED_VALUE: {
+ MonoClass *klass;
+ MonoDomain *domain2;
+ MonoObject *o;
+
+ domain = decode_domainid (p, &p, end, NULL, &err);
+ if (err)
+ return err;
+ klass = decode_typeid (p, &p, end, &domain2, &err);
+ if (err)
+ return err;
+
+ // FIXME:
+ g_assert (domain == domain2);
+
+ o = mono_object_new (domain, klass);
+
+ err = decode_value (&klass->byval_arg, domain, mono_object_unbox (o), p, &p, end);
+ if (err)
+ return err;
+
+ buffer_add_objid (buf, o);
+ break;
+ }
default:
return ERR_NOT_IMPLEMENTED;
}
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;
}
b |= (1 << 2);
if (type->type == MONO_TYPE_VALUETYPE)
b |= (1 << 3);
+ if (klass->enumtype)
+ b |= (1 << 4);
buffer_add_byte (buf, b);
nnested = 0;
iter = NULL;
g_free (val);
return err;
}
- mono_field_static_set_value (vtable, f, val);
+ if (MONO_TYPE_IS_REFERENCE (f->type))
+ mono_field_static_set_value (vtable, f, *(gpointer*)val);
+ else
+ mono_field_static_set_value (vtable, f, val);
g_free (val);
}
break;
if (err)
return err;
- thread = thread_obj->internal_thread;
+ thread = THREAD_TO_INTERNAL (thread_obj);
switch (command) {
case CMD_THREAD_GET_NAME: {
if (err)
return err;
- thread = thread_obj->internal_thread;
+ thread = THREAD_TO_INTERNAL (thread_obj);
id = decode_id (p, &p, end);
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);
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;
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;
mono_set_is_debugger_attached (TRUE);
while (TRUE) {
- res = read (conn_fd, header, HEADER_LENGTH);
+ res = recv_length (conn_fd, header, HEADER_LENGTH, 0);
/* This will break if the socket is closed during shutdown too */
if (res != HEADER_LENGTH)
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_length (conn_fd, data, len - HEADER_LENGTH, 0);
+ if (res != len - HEADER_LENGTH)
+ break;
+ }
p = data;
end = data + (len - HEADER_LENGTH);
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;
}
{
}
-void
-mono_debugger_agent_cleanup (void)
-{
-}
-
void
mono_debugger_agent_breakpoint_hit (void *sigctx)
{
{
}
-gboolean mono_debugger_agent_thread_interrupt (MonoJitInfo *ji)
-{
-}
-
-void
-mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *ctx)
+gboolean
+mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji)
{
+ return FALSE;
}
void
-mono_debugger_agent_handle_unhandled_exception (MonoException *exc, MonoContext *ctx)
+mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *throw_ctx,
+ MonoContext *catch_ctx)
{
-
}
#endif