diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index 91a2ee0fe3d8ae..1b8ad2bf5d8261 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -7521,7 +7521,111 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) return ERR_NONE; } +static void +set_jmc_breakpoint_on_methods (MonoClass *klass, GPtrArray *method_array, Modifier *modifier, EventRequest *req) +{ + MonoMethod *m; + mono_class_setup_methods (klass); + gpointer iter = NULL; + while ((m = mono_class_get_methods (klass, &iter))) { + bool found = FALSE; + guint n = 0; + while (n < method_array->len) { + if (m == (MonoMethod *)g_ptr_array_index (method_array, n)) { + found = TRUE; + break; + } + } + if (!found) { + MonoBreakpoint* bp = mono_de_set_breakpoint (m, 0, req, NULL); + modifier->data.jmc->bps = g_slist_append (modifier->data.jmc->bps, bp); + } + } +} +static ErrorCode +jmc_modifier (int *step_thread_id, StepSize *size, StepDepth *depth, Modifier *modifier, EventRequest *req, guint8 *p, guint8 *end, Buffer *buf) +{ + ErrorCode err; + ERROR_DECL (error); + MonoDomain *domain = NULL; + MonoThread *step_thread; + *step_thread_id = decode_id (p, &p, end); + *size = (StepSize)decode_int (p, &p, end); + *depth = (StepDepth)decode_int (p, &p, end); + int module_len = decode_int (p, &p, end); + MonoMethod *paused_method = NULL; + modifier->data.jmc = (JMC_Modifier *)g_malloc0 (sizeof (JMC_Modifier)); + if (*depth == STEP_DEPTH_OVER) { + g_assert (*step_thread_id); + err = get_object (*step_thread_id, (MonoObject**)&step_thread); + if (err != ERR_NONE) { + g_free (req); + return err; + } + GET_TLS_DATA_FROM_THREAD (THREAD_TO_INTERNAL(step_thread)); + + if (tls == NULL) + return ERR_UNLOADED; + if (tls->frame_count > 0) + paused_method = tls->frames [0]->actual_method; + } + + for (int j = 0 ; j < module_len; j++) { + MonoImage *image = decode_moduleid (p, &p, end, &domain, &err); + bool module_jmc = decode_byte (p, &p, end); + int type_len = decode_int (p, &p, end); + GPtrArray *type_array = g_ptr_array_new (); + GPtrArray *method_array = g_ptr_array_new (); + for (int k = 0 ; k < type_len; k++) { + MonoClass *klass = decode_typeid (p, &p, end, &domain, &err); + bool type_jmc = decode_byte (p, &p, end); + int method_len = decode_int (p, &p, end); + for (int l = 0 ; l < method_len; l++) { + MonoMethod *current_method = decode_methodid (p, &p, end, &domain, &err); + bool method_jmc = decode_byte (p, &p, end); + if (current_method == paused_method && method_jmc == FALSE) { + modifier->data.jmc->current_method = paused_method; + *depth = STEP_DEPTH_OUT; + } + if (!type_jmc && method_jmc) { + MonoBreakpoint* bp = mono_de_set_breakpoint (current_method, 0, req, NULL); + modifier->data.jmc->bps = g_slist_append (modifier->data.jmc->bps, bp); + } + if (type_jmc && !method_jmc) + g_ptr_array_add (method_array, current_method); + } + if (!module_jmc && type_jmc) { + set_jmc_breakpoint_on_methods (klass, method_array, modifier, req); + } + if (module_jmc && !type_jmc) + g_ptr_array_add (type_array, klass); + } + if (module_jmc) { + int rows = table_info_get_rows (&image->tables [MONO_TABLE_TYPEDEF]); + for (int q = 1; q <= rows; q++) { + MonoClass *klass = mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF | q, error); + mono_error_cleanup (error); + if (!klass) + continue; + guint o = 0; + while (o < type_array->len) { + bool found = FALSE; + if (klass == (MonoClass*) g_ptr_array_index (type_array, o)) { + found = TRUE; + break; + } + if (!found) { + set_jmc_breakpoint_on_methods (klass, method_array, modifier, req); + } + } + } + } + g_ptr_array_free (type_array, TRUE); + g_ptr_array_free (method_array, TRUE); + } + return ERR_NONE; +} static ErrorCode event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) { @@ -7653,6 +7757,9 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (s) g_hash_table_insert (modifier->data.type_names, s, s); } + } else if (mod == MDBGPROT_MOD_KIND_STEP_JMC) { + modifier = &req->modifiers [i]; + jmc_modifier (&step_thread_id, &size, &depth, modifier, req, p, end, buf); } else { g_free (req); return ERR_NOT_IMPLEMENTED; diff --git a/src/mono/mono/component/debugger-engine.c b/src/mono/mono/component/debugger-engine.c index 02f7eb090f6aec..2aae86368824df 100644 --- a/src/mono/mono/component/debugger-engine.c +++ b/src/mono/mono/component/debugger-engine.c @@ -572,6 +572,22 @@ ss_req_cleanup (void) dbg_unlock (); } +static gboolean +mono_de_is_jmc_stepping (void) +{ + gboolean jmc = FALSE; + dbg_lock (); + for (guint i = 0; i < the_ss_reqs->len; ++i) { + SingleStepReq *current_req = (SingleStepReq *)g_ptr_array_index (the_ss_reqs, i); + if (current_req->req && current_req->req->nmodifiers == 1 && current_req->req->modifiers[0].kind == MDBGPROT_MOD_KIND_STEP_JMC) { + jmc = TRUE; + break; + } + } + dbg_unlock (); + return jmc; +} + /* * mono_de_start_single_stepping: * @@ -581,6 +597,8 @@ ss_req_cleanup (void) void mono_de_start_single_stepping (void) { + if (mono_de_is_jmc_stepping()) + return; int val = mono_atomic_inc_i32 (&ss_count); if (val == 1) { @@ -594,6 +612,8 @@ mono_de_start_single_stepping (void) void mono_de_stop_single_stepping (void) { + if (mono_de_is_jmc_stepping() && ss_count == 0) + return; int val = mono_atomic_dec_i32 (&ss_count); if (val == 0) { @@ -693,6 +713,15 @@ ss_destroy (SingleStepReq *req) ss_stop (req); + if (req->req->nmodifiers == 1 && req->req->modifiers[0].kind == MDBGPROT_MOD_KIND_STEP_JMC) { + GSList *l; + for (l = req->req->modifiers[0].data.jmc->bps; l; l = l->next) { + mono_de_clear_breakpoint ((MonoBreakpoint *)l->data); + } + g_slist_free (req->req->modifiers[0].data.jmc->bps); + req->req->modifiers[0].data.jmc->bps = NULL; + } + g_free (req); } @@ -883,10 +912,10 @@ mono_de_ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, void *tls, MonoDebugMethodInfo *minfo; MonoDebugSourceLocation *loc = NULL; gboolean hit = TRUE; + int nframes = 0; + DbgEngineStackFrame **frames = NULL; if ((req->filter & STEP_FILTER_STATIC_CTOR)) { - DbgEngineStackFrame **frames; - int nframes; rt_callbacks.ss_calculate_framecount (tls, ctx, TRUE, &frames, &nframes); gboolean ret = FALSE; @@ -931,24 +960,51 @@ mono_de_ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, void *tls, return FALSE; } + //process jmc stepping + gboolean is_JMC_bp = FALSE; + MonoMethod *method_to_step_out = NULL; + GSList *jmc_bps = NULL; + if (req->req->nmodifiers == 1 && req->req->modifiers[0].kind == MDBGPROT_MOD_KIND_STEP_JMC) { + jmc_bps = req->req->modifiers[0].data.jmc->bps; + method_to_step_out = req->req->modifiers[0].data.jmc->current_method; + } + if (method_to_step_out) { + if (frames == NULL) + rt_callbacks.ss_calculate_framecount (tls, ctx, TRUE, &frames, &nframes); + for (int i = 0; i < nframes; i++) { + MonoMethod *external_method = frames [i]->method; + if (method_to_step_out == external_method) + return FALSE; + } + } + if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit && !req->async_stepout_method) { gboolean is_step_out = req->depth == STEP_DEPTH_OUT; - int nframes; - rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes); - // 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. + if (frames == NULL) + rt_callbacks.ss_calculate_framecount (tls, ctx, TRUE, &frames, &nframes); int target_frames = req->nframes + (is_step_out ? -1 : 0); if (req->nframes > 0 && nframes > 0 && nframes > target_frames) { - /* Hit the breakpoint in a recursive call, don't halt */ - PRINT_DEBUG_MSG (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 (!is_step_out) { + do { + MonoBreakpoint* bp = (MonoBreakpoint *)jmc_bps->data; + if (bp->method == method && bp->il_offset == sp->il_offset) { + is_JMC_bp = TRUE; + break; + } + jmc_bps = jmc_bps->next; + } while (jmc_bps); + } + if (!is_JMC_bp && !method_to_step_out) { + /* Hit the breakpoint in a recursive call, don't halt */ + PRINT_DEBUG_MSG (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) && req->start_method) { - int nframes; - rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes); if (req->start_method == method && req->nframes && nframes == req->nframes) { //Check also frame count(could be recursion) PRINT_DEBUG_MSG (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; @@ -982,8 +1038,8 @@ mono_de_ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, void *tls, req->last_column = -1; return hit; } else if (loc && method == req->last_method && loc->row == req->last_line && loc->column == req->last_column) { - int nframes; - rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes); + if (frames == NULL) + rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes); if (nframes == req->nframes) { // If the frame has changed we're clearly not on the same source line. PRINT_DEBUG_MSG (1, "[%p] Same source line (%d), continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), loc->row); hit = FALSE; diff --git a/src/mono/mono/component/debugger-protocol.h b/src/mono/mono/component/debugger-protocol.h index 9a8cc5e1c53d3b..c18a5188f052e6 100644 --- a/src/mono/mono/component/debugger-protocol.h +++ b/src/mono/mono/component/debugger-protocol.h @@ -11,7 +11,7 @@ */ #define MAJOR_VERSION 2 -#define MINOR_VERSION 64 +#define MINOR_VERSION 65 typedef enum { MDBGPROT_CMD_COMPOSITE = 100 @@ -327,7 +327,8 @@ typedef enum { MDBGPROT_MOD_KIND_ASSEMBLY_ONLY = 11, MDBGPROT_MOD_KIND_SOURCE_FILE_ONLY = 12, MDBGPROT_MOD_KIND_TYPE_NAME_ONLY = 13, - MDBGPROT_MOD_KIND_NONE = 14 + MDBGPROT_MOD_KIND_NONE = 14, + MDBGPROT_MOD_KIND_STEP_JMC = 15 } MdbgProtModifierKind; typedef enum { diff --git a/src/mono/mono/component/debugger.h b/src/mono/mono/component/debugger.h index 7dca4d2d30317d..13bb7fb2dad2e3 100644 --- a/src/mono/mono/component/debugger.h +++ b/src/mono/mono/component/debugger.h @@ -14,6 +14,12 @@ #include "debugger-protocol.h" #include "mono/metadata/seq-points-data.h" +typedef struct { + GSList *bps; + MonoMethod *current_method; + gboolean force_step_out; +} JMC_Modifier; + typedef struct { MdbgProtModifierKind kind; union { @@ -24,6 +30,7 @@ typedef struct { GHashTable *source_files; /* For kind == MONO_KIND_SOURCE_FILE_ONLY */ GHashTable *type_names; /* For kind == MONO_KIND_TYPE_NAME_ONLY */ MdbgProtStepFilter filter; /* For kind == MOD_KIND_STEP */ + JMC_Modifier *jmc; /* For kind == MOD_KIND_JMC */ } data; gboolean caught, uncaught, subclasses, not_filtered_feature, everything_else; /* For kind == MOD_KIND_EXCEPTION_ONLY */ } Modifier;