static gboolean transport_handshake (void);
static void register_transport (DebuggerTransport *trans);
-static guint32 WINAPI debugger_thread (void *arg);
+static gsize WINAPI debugger_thread (void *arg);
static void runtime_initialized (MonoProfiler *prof);
static void
start_debugger_thread (void)
{
- debugger_thread_handle = mono_threads_create_thread (debugger_thread, NULL, 0, 0, NULL);
+ MonoThreadParm tp;
+
+ tp.priority = MONO_THREAD_PRIORITY_NORMAL;
+ tp.stack_size = 0;
+ tp.creation_flags = 0;
+ debugger_thread_handle = mono_threads_create_thread (debugger_thread, NULL, &tp, NULL);
g_assert (debugger_thread_handle);
}
static void
resume_vm (void)
{
- int err;
-
g_assert (is_debugger_thread ());
mono_loader_lock ();
}
/* Signal this even when suspend_count > 0, since some threads might have resume_count > 0 */
- err = mono_coop_cond_broadcast (&suspend_cond);
- g_assert (err == 0);
+ mono_coop_cond_broadcast (&suspend_cond);
mono_coop_mutex_unlock (&suspend_mutex);
//g_assert (err == 0);
static void
resume_thread (MonoInternalThread *thread)
{
- int err;
DebuggerTlsData *tls;
g_assert (is_debugger_thread ());
* 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_coop_cond_broadcast (&suspend_cond);
- g_assert (err == 0);
+ mono_coop_cond_broadcast (&suspend_cond);
mono_coop_mutex_unlock (&suspend_mutex);
//g_assert (err == 0);
suspend_current (void)
{
DebuggerTlsData *tls;
- int err;
g_assert (!is_debugger_thread ());
DEBUG_PRINTF (1, "[%p] Suspended.\n", (gpointer) (gsize) mono_native_thread_id_get ());
while (suspend_count - tls->resume_count > 0) {
- err = mono_coop_cond_wait (&suspend_cond, &suspend_mutex);
- g_assert (err == 0);
+ mono_coop_cond_wait (&suspend_cond, &suspend_mutex);
}
tls->suspended = FALSE;
MonoError oerror;
/* Might be AOTed code */
+ mono_class_init (method->klass);
code = mono_aot_get_method_checked (domain, method, &oerror);
g_assert (code);
mono_error_assert_ok (&oerror);
mono_loader_unlock ();
}
+/*
+ * ss_calculate_framecount:
+ *
+ * Ensure DebuggerTlsData fields are filled out.
+ */
+static void ss_calculate_framecount (DebuggerTlsData *tls, MonoContext *ctx)
+{
+ if (!tls->context.valid)
+ mono_thread_state_init_from_monoctx (&tls->context, ctx);
+ compute_frame_info (tls->thread, tls);
+}
+
/*
* ss_update:
*
return FALSE;
}
- if (req->depth == STEP_DEPTH_OVER && hit) {
- if (!tls->context.valid)
- mono_thread_state_init_from_monoctx (&tls->context, ctx);
- compute_frame_info (tls->thread, tls);
- if (req->nframes && tls->frame_count && tls->frame_count > req->nframes) {
- /* Hit the breakpoint in a recursive call */
- DEBUG_PRINTF (1, "[%p] Breakpoint at lower frame while stepping over, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get ());
+ if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit) {
+ gboolean is_step_out = req->depth == STEP_DEPTH_OUT;
+
+ ss_calculate_framecount (tls, ctx);
+
+ // Because functions can call themselves recursively, we need to make sure we're stopping at the right stack depth.
+ // In case of step out, the target is the frame *enclosing* the one where the request was made.
+ int target_frames = req->nframes + (is_step_out ? -1 : 0);
+ if (req->nframes > 0 && tls->frame_count > 0 && tls->frame_count > target_frames) {
+ /* Hit the breakpoint in a recursive call, don't halt */
+ DEBUG_PRINTF (1, "[%p] Breakpoint at lower frame while stepping %s, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), is_step_out ? "out" : "over");
return FALSE;
}
}
if (req->depth == STEP_DEPTH_INTO && req->size == STEP_SIZE_MIN && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) && ss_req->start_method){
method = jinfo_get_method (ji);
- if (!tls->context.valid)
- mono_thread_state_init_from_monoctx (&tls->context, ctx);
- compute_frame_info (tls->thread, tls);
+ ss_calculate_framecount (tls, ctx);
if (ss_req->start_method == method && req->nframes && tls->frame_count == req->nframes) {//Check also frame count(could be recursion)
DEBUG_PRINTF (1, "[%p] Seq point at nonempty stack %x while stepping in, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), sp->il_offset);
return FALSE;
ss_req->last_method = method;
hit = FALSE;
} else if (loc && method == ss_req->last_method && loc->row == ss_req->last_line) {
- DEBUG_PRINTF (1, "[%p] Same source line (%d), continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), loc->row);
- hit = FALSE;
+ ss_calculate_framecount (tls, ctx);
+ if (tls->frame_count == req->nframes) { // If the frame has changed we're clearly not on the same source line.
+ DEBUG_PRINTF (1, "[%p] Same source line (%d), continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), loc->row);
+ hit = FALSE;
+ }
}
if (loc) {
}
}
+/*
+ * ss_bp_is_unique:
+ *
+ * Reject breakpoint if it is a duplicate of one already in list or hash table.
+ */
+static gboolean
+ss_bp_is_unique (GSList *bps, GHashTable *ss_req_bp_cache, MonoMethod *method, guint32 il_offset)
+{
+ if (ss_req_bp_cache) {
+ MonoBreakpoint dummy = {method, il_offset, NULL, NULL};
+ return !g_hash_table_lookup (ss_req_bp_cache, &dummy);
+ }
+ for (GSList *l = bps; l; l = l->next) {
+ MonoBreakpoint *bp = (MonoBreakpoint *)l->data;
+ if (bp->method == method && bp->il_offset == il_offset)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * ss_bp_eq:
+ *
+ * GHashTable equality for a MonoBreakpoint (only care about method and il_offset fields)
+ */
+static gint
+ss_bp_eq (gconstpointer ka, gconstpointer kb)
+{
+ const MonoBreakpoint *s1 = (const MonoBreakpoint *)ka;
+ const MonoBreakpoint *s2 = (const MonoBreakpoint *)kb;
+ return (s1->method == s2->method && s1->il_offset == s2->il_offset) ? 1 : 0;
+}
+
+/*
+ * ss_bp_eq:
+ *
+ * GHashTable hash for a MonoBreakpoint (only care about method and il_offset fields)
+ */
+static guint
+ss_bp_hash (gconstpointer data)
+{
+ const MonoBreakpoint *s = (const MonoBreakpoint *)data;
+ guint hash = (guint) (uintptr_t) s->method;
+ hash ^= ((guint)s->il_offset) << 16; // Assume low bits are more interesting
+ hash ^= ((guint)s->il_offset) >> 16;
+ return hash;
+}
+
+#define MAX_LINEAR_SCAN_BPS 7
+
+/*
+ * ss_bp_add_one:
+ *
+ * Create a new breakpoint and add it to a step request.
+ * Will adjust the bp count and cache used by ss_start.
+ */
+static void
+ss_bp_add_one (SingleStepReq *ss_req, int *ss_req_bp_count, GHashTable **ss_req_bp_cache,
+ MonoMethod *method, guint32 il_offset)
+{
+ // This list is getting too long, switch to using the hash table
+ if (!*ss_req_bp_cache && *ss_req_bp_count > MAX_LINEAR_SCAN_BPS) {
+ *ss_req_bp_cache = g_hash_table_new (ss_bp_hash, ss_bp_eq);
+ for (GSList *l = ss_req->bps; l; l = l->next)
+ g_hash_table_insert (*ss_req_bp_cache, l->data, l->data);
+ }
+
+ if (ss_bp_is_unique (ss_req->bps, *ss_req_bp_cache, method, il_offset)) {
+ // Create and add breakpoint
+ MonoBreakpoint *bp = set_breakpoint (method, il_offset, ss_req->req, NULL);
+ ss_req->bps = g_slist_append (ss_req->bps, bp);
+ if (*ss_req_bp_cache)
+ g_hash_table_insert (*ss_req_bp_cache, bp, bp);
+ (*ss_req_bp_count)++;
+ } else {
+ DEBUG_PRINTF (1, "[dbg] Candidate breakpoint at %s:[il=0x%x] is a duplicate for this step request, will not add.\n", mono_method_full_name (method, TRUE), (int)il_offset);
+ }
+}
+
/*
* ss_start:
*
SeqPoint *next_sp, *parent_sp = NULL;
SeqPoint local_sp, local_parent_sp;
gboolean found_sp;
- MonoBreakpoint *bp;
MonoSeqPointInfo *parent_info;
MonoMethod *parent_sp_method = NULL;
gboolean enable_global = FALSE;
+ // When 8 or more entries are in bps, we build a hash table to serve as a set of breakpoints.
+ // Recreating this on each pass is a little wasteful but at least keeps behavior linear.
+ int ss_req_bp_count = g_slist_length (ss_req->bps);
+ GHashTable *ss_req_bp_cache = NULL;
+
/* Stop the previous operation */
ss_stop (ss_req);
* Implement single stepping using breakpoints if possible.
*/
if (step_to_catch) {
- bp = set_breakpoint (method, sp->il_offset, ss_req->req, NULL);
- ss_req->bps = g_slist_append (ss_req->bps, bp);
+ ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, sp->il_offset);
} else {
frame_index = 1;
for (i = 0; i < sp->next_len; i++) {
next_sp = &next[i];
- bp = set_breakpoint (method, next_sp->il_offset, ss_req->req, NULL);
- ss_req->bps = g_slist_append (ss_req->bps, bp);
+ ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, next_sp->il_offset);
}
g_free (next);
}
for (i = 0; i < parent_sp->next_len; i++) {
next_sp = &next[i];
- bp = set_breakpoint (parent_sp_method, next_sp->il_offset, ss_req->req, NULL);
- ss_req->bps = g_slist_append (ss_req->bps, bp);
+ ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, parent_sp_method, next_sp->il_offset);
}
g_free (next);
}
found_sp = mono_find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL, &local_sp);
sp = (found_sp)? &local_sp : NULL;
- if (sp) {
- bp = set_breakpoint (frame->method, sp->il_offset, ss_req->req, NULL);
- ss_req->bps = g_slist_append (ss_req->bps, bp);
- }
+
+ if (found_sp)
+ ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, frame->method, sp->il_offset);
}
}
}
} else {
ss_req->global = FALSE;
}
+
+ if (ss_req_bp_cache)
+ g_hash_table_destroy (ss_req_bp_cache);
}
/*
case CMD_TYPE_GET_METHODS_BY_NAME_FLAGS: {
char *name = decode_string (p, &p, end);
int i, flags = decode_int (p, &p, end);
- MonoException *ex = NULL;
- GPtrArray *array = mono_class_get_methods_by_name (klass, name, flags & ~BINDING_FLAGS_IGNORE_CASE, (flags & BINDING_FLAGS_IGNORE_CASE) != 0, TRUE, &ex);
+ MonoError error;
+ GPtrArray *array;
- if (!array)
+ mono_error_init (&error);
+ array = mono_class_get_methods_by_name (klass, name, flags & ~BINDING_FLAGS_IGNORE_CASE, (flags & BINDING_FLAGS_IGNORE_CASE) != 0, TRUE, &error);
+ if (!is_ok (&error)) {
+ mono_error_cleanup (&error);
return ERR_LOADER_ERROR;
+ }
buffer_add_int (buf, array->len);
for (i = 0; i < array->len; ++i) {
MonoMethod *method = (MonoMethod *)g_ptr_array_index (array, i);
sig = mono_method_signature (frame->actual_method);
- if (!mono_get_seq_points (frame->domain, frame->actual_method))
+ if (!jit->has_var_info || !mono_get_seq_points (frame->domain, frame->actual_method))
/*
* The method is probably from an aot image compiled without soft-debug, variables might be dead, etc.
*/
g_free (val);
} else {
guint8 *field_value = NULL;
- void *field_storage = NULL;
if (remote_obj) {
#ifndef DISABLE_REMOTING
+ void *field_storage = NULL;
field_value = mono_load_remote_field_checked(obj, obj_type, f, &field_storage, &error);
if (!is_ok (&error)) {
mono_error_cleanup (&error); /* FIXME report the error */
* This thread handles communication with the debugger client using a JDWP
* like protocol.
*/
-static guint32 WINAPI
+static gsize WINAPI
debugger_thread (void *arg)
{
MonoError error;