Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions src/mono/mono/component/debugger-agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious - why is this freed in this code path, but not for the others in this function?

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)
{
Expand Down Expand Up @@ -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;
Expand Down
80 changes: 68 additions & 12 deletions src/mono/mono/component/debugger-engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions src/mono/mono/component/debugger-protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/

#define MAJOR_VERSION 2
#define MINOR_VERSION 64
#define MINOR_VERSION 65

typedef enum {
MDBGPROT_CMD_COMPOSITE = 100
Expand Down Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions src/mono/mono/component/debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down