Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm][debugger] Fix stepping out from an async method #42227

Merged
merged 2 commits into from
Sep 15, 2020
Merged
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
158 changes: 0 additions & 158 deletions src/mono/mono/mini/debugger-agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,35 +136,6 @@ typedef struct {
gboolean setpgid;
} AgentConfig;

typedef struct
{
//Must be the first field to ensure pointer equivalence
DbgEngineStackFrame de;
int id;
guint32 il_offset;
/*
* If method is gshared, this is the actual instance, otherwise this is equal to
* method.
*/
MonoMethod *actual_method;
/*
* This is the method which is visible to debugger clients. Same as method,
* except for native-to-managed wrappers.
*/
MonoMethod *api_method;
MonoContext ctx;
MonoDebugMethodJitInfo *jit;
MonoInterpFrameHandle interp_frame;
gpointer frame_addr;
int flags;
host_mgreg_t *reg_locations [MONO_MAX_IREGS];
/*
* 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 _InvokeData InvokeData;

struct _InvokeData
Expand Down Expand Up @@ -740,8 +711,6 @@ static void ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force
static gboolean ensure_jit (DbgEngineStackFrame* the_frame);
static int ensure_runtime_is_suspended (void);
static int get_this_async_id (DbgEngineStackFrame *frame);
static gboolean set_set_notification_for_wait_completion_flag (DbgEngineStackFrame *frame);
static MonoMethod* get_notify_debugger_of_wait_completion_method (void);
static void* create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind);
static void process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offset);
static int ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *args);
Expand Down Expand Up @@ -4551,37 +4520,6 @@ breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly)
return bp->method && m_class_get_image (bp->method->klass)->assembly == assembly;
}

static gpointer
get_this_addr (DbgEngineStackFrame *the_frame)
{
StackFrame *frame = (StackFrame *)the_frame;
if (frame->de.ji->is_interp)
return mini_get_interp_callbacks ()->frame_get_this (frame->interp_frame);

MonoDebugVarInfo *var = frame->jit->this_var;
if ((var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS) != MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET)
return NULL;

guint8 *addr = (guint8 *)mono_arch_context_get_int_reg (&frame->ctx, var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS);
addr += (gint32)var->offset;
return addr;
}

static MonoMethod*
get_set_notification_method (MonoClass* async_builder_class)
{
ERROR_DECL (error);
GPtrArray* array = mono_class_get_methods_by_name (async_builder_class, "SetNotificationForWaitCompletion", 0x24, 1, FALSE, error);
mono_error_assert_ok (error);
if (array->len == 0) {
g_ptr_array_free (array, TRUE);
return NULL;
}
MonoMethod* set_notification_method = (MonoMethod *)g_ptr_array_index (array, 0);
g_ptr_array_free (array, TRUE);
return set_notification_method;
}

static MonoMethod*
get_object_id_for_debugger_method (MonoClass* async_builder_class)
{
Expand All @@ -4603,62 +4541,6 @@ get_object_id_for_debugger_method (MonoClass* async_builder_class)
return method;
}

static MonoClass *
get_class_to_get_builder_field(DbgEngineStackFrame *frame)
{
ERROR_DECL (error);
gpointer this_addr = get_this_addr (frame);
MonoClass *original_class = frame->method->klass;
MonoClass *ret;
if (!m_class_is_valuetype (original_class) && mono_class_is_open_constructed_type (m_class_get_byval_arg (original_class))) {
MonoObject *this_obj = *(MonoObject**)this_addr;
MonoGenericContext context;
MonoType *inflated_type;

if (!this_obj)
return NULL;

context = mono_get_generic_context_from_stack_frame (frame->ji, this_obj->vtable);
inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (original_class), &context, error);
mono_error_assert_ok (error); /* FIXME don't swallow the error */

ret = mono_class_from_mono_type_internal (inflated_type);
mono_metadata_free_type (inflated_type);
return ret;
}
return original_class;
}


/* Return the address of the AsyncMethodBuilder struct belonging to the state machine method pointed to by FRAME */
static gpointer
get_async_method_builder (DbgEngineStackFrame *frame)
{
MonoObject *this_obj;
MonoClassField *builder_field;
gpointer builder;
gpointer this_addr;
MonoClass* klass = frame->method->klass;

klass = get_class_to_get_builder_field(frame);
builder_field = mono_class_get_field_from_name_full (klass, "<>t__builder", NULL);
if (!builder_field)
return NULL;

this_addr = get_this_addr (frame);
if (!this_addr)
return NULL;

if (m_class_is_valuetype (klass)) {
builder = mono_vtype_get_field_addr (*(guint8**)this_addr, builder_field);
} else {
this_obj = *(MonoObject**)this_addr;
builder = (char*)this_obj + builder_field->offset;
}

return builder;
}

//This ID is used to figure out if breakpoint hit on resumeOffset belongs to us or not
//since thread probably changed...
static int
Expand Down Expand Up @@ -4706,46 +4588,6 @@ get_this_async_id (DbgEngineStackFrame *frame)
return get_objid (obj);
}

// Returns true if TaskBuilder has NotifyDebuggerOfWaitCompletion method
// false if not(AsyncVoidBuilder)
static gboolean
set_set_notification_for_wait_completion_flag (DbgEngineStackFrame *frame)
{
MonoClassField *builder_field = mono_class_get_field_from_name_full (get_class_to_get_builder_field(frame), "<>t__builder", NULL);
if (!builder_field)
return FALSE;
gpointer builder = get_async_method_builder (frame);
if (!builder)
return FALSE;

MonoMethod* method = get_set_notification_method (mono_class_from_mono_type_internal (builder_field->type));
if (method == NULL)
return FALSE;
gboolean arg = TRUE;
ERROR_DECL (error);
void *args [ ] = { &arg };
mono_runtime_invoke_checked (method, builder, args, error);
mono_error_assert_ok (error);
return TRUE;
}

static MonoMethod* notify_debugger_of_wait_completion_method_cache;

static MonoMethod*
get_notify_debugger_of_wait_completion_method (void)
{
if (notify_debugger_of_wait_completion_method_cache != NULL)
return notify_debugger_of_wait_completion_method_cache;
ERROR_DECL (error);
MonoClass* task_class = mono_class_load_from_name (mono_defaults.corlib, "System.Threading.Tasks", "Task");
GPtrArray* array = mono_class_get_methods_by_name (task_class, "NotifyDebuggerOfWaitCompletion", 0x24, 1, FALSE, error);
mono_error_assert_ok (error);
g_assert (array->len == 1);
notify_debugger_of_wait_completion_method_cache = (MonoMethod *)g_ptr_array_index (array, 0);
g_ptr_array_free (array, TRUE);
return notify_debugger_of_wait_completion_method_cache;
}

static gboolean
begin_breakpoint_processing (void *the_tls, MonoContext *ctx, MonoJitInfo *ji, gboolean from_signal)
{
Expand Down
129 changes: 129 additions & 0 deletions src/mono/mono/mini/debugger-engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -1606,4 +1606,133 @@ mono_debugger_free_objref (gpointer value)

g_free (o);
}

// Returns true if TaskBuilder has NotifyDebuggerOfWaitCompletion method
// false if not(AsyncVoidBuilder)
MonoClass *
get_class_to_get_builder_field(DbgEngineStackFrame *frame)
{
ERROR_DECL (error);
gpointer this_addr = get_this_addr (frame);
MonoClass *original_class = frame->method->klass;
MonoClass *ret;
if (!m_class_is_valuetype (original_class) && mono_class_is_open_constructed_type (m_class_get_byval_arg (original_class))) {
MonoObject *this_obj = *(MonoObject**)this_addr;
MonoGenericContext context;
MonoType *inflated_type;

if (!this_obj)
return NULL;

context = mono_get_generic_context_from_stack_frame (frame->ji, this_obj->vtable);
inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (original_class), &context, error);
mono_error_assert_ok (error); /* FIXME don't swallow the error */

ret = mono_class_from_mono_type_internal (inflated_type);
mono_metadata_free_type (inflated_type);
return ret;
}
return original_class;
}


gboolean
set_set_notification_for_wait_completion_flag (DbgEngineStackFrame *frame)
{
MonoClassField *builder_field = mono_class_get_field_from_name_full (get_class_to_get_builder_field(frame), "<>t__builder", NULL);
if (!builder_field)
return FALSE;
gpointer builder = get_async_method_builder (frame);
if (!builder)
return FALSE;

MonoMethod* method = get_set_notification_method (mono_class_from_mono_type_internal (builder_field->type));
if (method == NULL)
return FALSE;
gboolean arg = TRUE;
ERROR_DECL (error);
void *args [ ] = { &arg };
mono_runtime_invoke_checked (method, builder, args, error);
mono_error_assert_ok (error);
return TRUE;
}

gpointer
get_this_addr (DbgEngineStackFrame *the_frame)
{
StackFrame *frame = (StackFrame *)the_frame;
if (frame->de.ji->is_interp)
return mini_get_interp_callbacks ()->frame_get_this (frame->interp_frame);

MonoDebugVarInfo *var = frame->jit->this_var;
if ((var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS) != MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET)
return NULL;

guint8 *addr = (guint8 *)mono_arch_context_get_int_reg (&frame->ctx, var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS);
addr += (gint32)var->offset;
return addr;
}

/* Return the address of the AsyncMethodBuilder struct belonging to the state machine method pointed to by FRAME */
gpointer
get_async_method_builder (DbgEngineStackFrame *frame)
{
MonoObject *this_obj;
MonoClassField *builder_field;
gpointer builder;
gpointer this_addr;
MonoClass* klass = frame->method->klass;

klass = get_class_to_get_builder_field(frame);
builder_field = mono_class_get_field_from_name_full (klass, "<>t__builder", NULL);
if (!builder_field)
return NULL;

this_addr = get_this_addr (frame);
if (!this_addr)
return NULL;

if (m_class_is_valuetype (klass)) {
builder = mono_vtype_get_field_addr (*(guint8**)this_addr, builder_field);
} else {
this_obj = *(MonoObject**)this_addr;
builder = (char*)this_obj + builder_field->offset;
}

return builder;
}

MonoMethod*
get_set_notification_method (MonoClass* async_builder_class)
{
ERROR_DECL (error);
GPtrArray* array = mono_class_get_methods_by_name (async_builder_class, "SetNotificationForWaitCompletion", 0x24, 1, FALSE, error);
mono_error_assert_ok (error);
if (array->len == 0) {
g_ptr_array_free (array, TRUE);
return NULL;
}
MonoMethod* set_notification_method = (MonoMethod *)g_ptr_array_index (array, 0);
g_ptr_array_free (array, TRUE);
return set_notification_method;
}

static MonoMethod* notify_debugger_of_wait_completion_method_cache;

MonoMethod*
get_notify_debugger_of_wait_completion_method (void)
{
if (notify_debugger_of_wait_completion_method_cache != NULL)
return notify_debugger_of_wait_completion_method_cache;
ERROR_DECL (error);
MonoClass* task_class = mono_class_load_from_name (mono_defaults.corlib, "System.Threading.Tasks", "Task");
GPtrArray* array = mono_class_get_methods_by_name (task_class, "NotifyDebuggerOfWaitCompletion", 0x24, 1, FALSE, error);
mono_error_assert_ok (error);
g_assert (array->len == 1);
notify_debugger_of_wait_completion_method_cache = (MonoMethod *)g_ptr_array_index (array, 0);
g_ptr_array_free (array, TRUE);
return notify_debugger_of_wait_completion_method_cache;
}


#endif
36 changes: 36 additions & 0 deletions src/mono/mono/mini/debugger-engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include "mini.h"
#include <mono/metadata/seq-points-data.h>
#include <mono/mini/debugger-state-machine.h>
#include <mono/metadata/mono-debug.h>
#include <mono/mini/interp/interp-internals.h>

/*
FIXME:
Expand Down Expand Up @@ -206,6 +208,34 @@ typedef struct {
MonoGCHandle handle;
} ObjRef;

typedef struct
{
//Must be the first field to ensure pointer equivalence
DbgEngineStackFrame de;
int id;
guint32 il_offset;
/*
* If method is gshared, this is the actual instance, otherwise this is equal to
* method.
*/
MonoMethod *actual_method;
/*
* This is the method which is visible to debugger clients. Same as method,
* except for native-to-managed wrappers.
*/
MonoMethod *api_method;
MonoContext ctx;
MonoDebugMethodJitInfo *jit;
MonoInterpFrameHandle interp_frame;
gpointer frame_addr;
int flags;
host_mgreg_t *reg_locations [MONO_MAX_IREGS];
/*
* Whenever ctx is set. This is FALSE for the last frame of running threads, since
* the frame can become invalid.
*/
gboolean has_ctx;
} StackFrame;

void mono_debugger_free_objref (gpointer value);

Expand Down Expand Up @@ -287,4 +317,10 @@ DbgEngineErrorCode mono_de_ss_create (MonoInternalThread *thread, StepSize size,
void mono_de_cancel_ss (SingleStepReq *req);
void mono_de_cancel_all_ss (void);

gboolean set_set_notification_for_wait_completion_flag (DbgEngineStackFrame *frame);
MonoClass * get_class_to_get_builder_field(DbgEngineStackFrame *frame);
gpointer get_this_addr (DbgEngineStackFrame *the_frame);
gpointer get_async_method_builder (DbgEngineStackFrame *frame);
MonoMethod* get_set_notification_method (MonoClass* async_builder_class);
MonoMethod* get_notify_debugger_of_wait_completion_method (void);
#endif
Loading