#include "debugger-agent.h"
#include "mini.h"
+/*
+On iOS we can't use System.Environment.Exit () as it will do the wrong
+shutdown sequence.
+*/
+#if !defined (TARGET_IOS)
+#define TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT
+#endif
+
+
#ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
#define DISABLE_DEBUGGER_AGENT 1
#endif
/* A hash table containing all active domains */
static GHashTable *domains;
+/* The number of times the runtime is suspended */
+static gint32 suspend_count;
+
static void transport_init (void);
static void transport_connect (const char *address);
static gboolean transport_handshake (void);
#endif
}
- disconnected = !transport_handshake ();
- if (disconnected)
+ if (!transport_handshake ())
exit (1);
}
static DebuggerTransport transports [MAX_TRANSPORTS];
static int ntransports;
+void
+mono_debugger_agent_register_transport (DebuggerTransport *trans);
+
+void
+mono_debugger_agent_register_transport (DebuggerTransport *trans)
+{
+ register_transport (trans);
+}
+
static void
register_transport (DebuggerTransport *trans)
{
return transport->recv (buf, len);
}
+gboolean
+mono_debugger_agent_transport_handshake (void)
+{
+ return transport_handshake ();
+}
+
static gboolean
transport_handshake (void)
{
guint8 buf [128];
int res;
+ disconnected = TRUE;
+
/* Write handshake message */
sprintf (handshake_msg, "DWP-Handshake");
do {
* Set TCP_NODELAY on the socket so the client receives events/command
* results immediately.
*/
- {
+ if (conn_fd) {
int flag = 1;
int result = setsockopt (conn_fd,
IPPROTO_TCP,
set_keepalive ();
#endif
+ disconnected = FALSE;
return TRUE;
}
}
static GHashTable *obj_to_objref;
+static MonoGHashTable *suspended_objs;
/*
* Return an ObjRef for OBJ.
mono_loader_lock ();
- if (!obj_to_objref)
+ if (!obj_to_objref) {
obj_to_objref = g_hash_table_new (NULL, NULL);
+ suspended_objs = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC);
+ MONO_GC_REGISTER_ROOT_FIXED (suspended_objs);
+ }
+
+ if (suspend_count) {
+ /*
+ * Have to keep object refs created during suspensions alive for the duration of the suspension, so GCs during invokes don't collect them.
+ */
+ mono_g_hash_table_insert (suspended_objs, obj, NULL);
+ }
/* FIXME: The tables can grow indefinitely */
return ref;
}
+static gboolean
+true_pred (gpointer key, gpointer value, gpointer user_data)
+{
+ return TRUE;
+}
+
+static void
+clear_suspended_objs (void)
+{
+ mono_loader_lock ();
+ mono_g_hash_table_foreach_remove (suspended_objs, true_pred, NULL);
+ mono_loader_unlock ();
+}
+
static inline int
get_objid (MonoObject *obj)
{
mono_thread_state_init_from_current (&tls->context);
}
-/* The number of times the runtime is suspended */
-static gint32 suspend_count;
-
/* Number of threads suspended */
/*
* If this is equal to the size of thread_to_tls, the runtime is considered
MonoJitInfo *ji;
info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gpointer)(gsize)thread->tid, FALSE);
- g_assert (info);
-
- ji = mono_jit_info_table_find (info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
+ if (!info) {
+ DEBUG(1, fprintf (log_file, "[%p] mono_thread_info_suspend_sync () failed for %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid));
+ /*
+ * Attached thread which died without detaching.
+ */
+ tls->terminated = TRUE;
+ } else {
+ ji = mono_jit_info_table_find (info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
- thread_interrupt (tls, info, NULL, ji);
+ thread_interrupt (tls, info, NULL, ji);
- mono_thread_info_resume (mono_thread_info_get_tid (info));
+ mono_thread_info_resume (mono_thread_info_get_tid (info));
+ }
} else {
res = mono_thread_kill (thread, mono_thread_get_abort_signal ());
if (res) {
int i;
seq_points = find_seq_points (domain, method);
+ g_assert (seq_points);
if (info)
*info = seq_points;
MonoSeqPointInfo *seq_points;
int i;
- seq_points = find_seq_points (domain, method);
+ seq_points = get_seq_points (domain, method);
if (info)
*info = seq_points;
+ if (!seq_points)
+ return NULL;
for (i = seq_points->len - 1; i >= 0; --i) {
if (seq_points->seq_points [i].native_offset <= native_offset)
* the offset recorded in the seq point map, so find the prev seq point before ip.
*/
sp = find_prev_seq_point_for_native_offset (mono_domain_get (), ji->method, native_offset, &info);
+ g_assert (sp);
DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, ip=%p, offset=0x%x, sp il offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, ip, native_offset, sp ? sp->il_offset : -1));
return agent_config.enabled;
}
+#ifdef PLATFORM_ANDROID
+void
+mono_debugger_agent_unhandled_exception (MonoException *exc)
+{
+ int suspend_policy;
+ GSList *events;
+ EventInfo ei;
+
+ if (!inited)
+ return;
+
+ memset (&ei, 0, sizeof (EventInfo));
+ ei.exc = (MonoObject*)exc;
+
+ mono_loader_lock ();
+ events = create_event_list (EVENT_KIND_EXCEPTION, NULL, NULL, &ei, &suspend_policy);
+ mono_loader_unlock ();
+
+ process_event (EVENT_KIND_EXCEPTION, &ei, 0, NULL, events, suspend_policy);
+}
+#endif
+
void
mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx,
MonoContext *catch_ctx)
{
- int suspend_policy;
+ int i, j, suspend_policy;
GSList *events;
- MonoJitInfo *ji;
+ MonoJitInfo *ji, *catch_ji;
EventInfo ei;
DebuggerTlsData *tls = NULL;
return;
ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (throw_ctx), NULL);
+ if (catch_ctx)
+ catch_ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (catch_ctx), NULL);
+ else
+ catch_ji = NULL;
ei.exc = (MonoObject*)exc;
ei.caught = catch_ctx != NULL;
mono_loader_lock ();
+
+ /* Treat exceptions which are caught in non-user code as unhandled */
+ for (i = 0; i < event_requests->len; ++i) {
+ EventRequest *req = g_ptr_array_index (event_requests, i);
+ if (req->event_kind != EVENT_KIND_EXCEPTION)
+ continue;
+
+ for (j = 0; j < req->nmodifiers; ++j) {
+ Modifier *mod = &req->modifiers [j];
+
+ if (mod->kind == MOD_KIND_ASSEMBLY_ONLY && catch_ji) {
+ int k;
+ gboolean found = FALSE;
+ MonoAssembly **assemblies = mod->data.assemblies;
+
+ if (assemblies) {
+ for (k = 0; assemblies [k]; ++k)
+ if (assemblies [k] == catch_ji->method->klass->image->assembly)
+ found = TRUE;
+ }
+ if (!found)
+ ei.caught = FALSE;
+ }
+ }
+ }
+
events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, &ei, &suspend_policy);
mono_loader_unlock ();
- if (tls && catch_ctx) {
+ if (tls && ei.caught && catch_ctx) {
tls->catch_ctx = *catch_ctx;
tls->has_catch_ctx = TRUE;
}
if (suspend_count == 0)
return ERR_NOT_SUSPENDED;
resume_vm ();
+ clear_suspended_objs ();
break;
case CMD_VM_DISPOSE:
/* Clear all event requests */
suspend_vm ();
wait_for_suspend ();
+#ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT
env_class = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
if (env_class)
exit_method = mono_class_get_method_from_name (env_class, "Exit", 1);
+#endif
mono_loader_lock ();
thread = mono_g_hash_table_find (tid_to_thread, is_really_suspended, NULL);
res = transport_recv (header, HEADER_LENGTH);
/* This will break if the socket is closed during shutdown too */
- if (res != HEADER_LENGTH)
+ if (res != HEADER_LENGTH) {
+ DEBUG (1, fprintf (log_file, "[dbg] transport_recv () returned %d, expected %d.\n", res, HEADER_LENGTH));
break;
+ }
p = header;
end = header + HEADER_LENGTH;
if (len - HEADER_LENGTH > 0)
{
res = transport_recv (data, len - HEADER_LENGTH);
- if (res != len - HEADER_LENGTH)
+ if (res != len - HEADER_LENGTH) {
+ DEBUG (1, fprintf (log_file, "[dbg] transport_recv () returned %d, expected %d.\n", res, len - HEADER_LENGTH));
break;
+ }
}
p = data;