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] Return errors from debugger.c's details functions #30

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c863ed4
[wasm][debugger] Send var names also, to `mono_wasm_get_variables`
radical May 11, 2020
1aa03f8
[wasm][debugger] Add support for deref'ing pointers
radical May 12, 2020
40161e4
[wasm][debugger] Dereference the pointer in js, instead of debugger.c
radical May 14, 2020
64d3216
[wasm][debugger][tests] Fix setting async methods when setting
radical May 29, 2020
71bfa63
[wasm][debugger][tests] Update to set bp by method name+lineoffset
radical May 29, 2020
653f80e
Address review comments from Katelyn Gadd (@kg)
radical Jun 4, 2020
d49bcd7
Add test to check deref'ing invalid pointers
radical Jun 4, 2020
cabc4ec
Remove old unused test code
radical Jun 4, 2020
9107ccd
[wasm][debugger] Use JsonConvert instead of manually building the jso…
radical Jul 6, 2020
b3b1bfd
[wasm][debugger][tests] Fix negative pointer tests
radical Jun 30, 2020
955d6c8
[wasm][debugger][tests] Fix test to correctly check the valuetype local
radical Jun 22, 2020
2c98026
[wasm][debugger][tests] Make value checks consistent
radical Jun 25, 2020
23350be
[wasm][debugger] Add new `id` types, which have associated properties
radical Jun 30, 2020
a6ecdaa
[wasm][debugger] Update valuetype code to use the new `id`s
radical Jun 30, 2020
a762b5b
[wasm][debugger] Simplify array API in `mini-wasm-debugger.c`
radical Jun 22, 2020
5bb8509
[wasm][debugger] library_mono.js: improvements to valuetype code
radical Jun 30, 2020
fed771f
[wasm][debugger] runtime.c - extract object id lookup into a function
radical Jun 24, 2020
9462be8
[wasm][debugger][tests] Rename method param to be self descriptive
radical Jun 29, 2020
3706e5d
[wasm][debugger][tests] Rework cfo test for getters
radical Jun 30, 2020
fa5dbd8
[wasm][debugger][tests] Improve valuetype locals/method args tests
radical Jun 30, 2020
2f2c0d6
[wasm][debugger] Add support for invoking getters on valuetypes
radical Jun 30, 2020
c751082
[wasm][debugger] mono.js: fix warnings
radical Jun 30, 2020
ac31ffd
[wasm][debugger] mono.js: _split_object_id -> _parse_object_id
radical Jul 6, 2020
6152851
[wasm][debugger] Streamline accessing exported debugger.c functions
radical Jul 1, 2020
c0a51e7
[wasm][debugger] Return errors from debugger.c's details functions
radical Jul 1, 2020
dc66d20
[wasm][debugger] Small checks on inputs, and some negative tests
radical Jul 6, 2020
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
233 changes: 133 additions & 100 deletions mono/mini/mini-wasm-debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,19 @@ EMSCRIPTEN_KEEPALIVE int mono_wasm_set_breakpoint (const char *assembly_name, in
EMSCRIPTEN_KEEPALIVE int mono_wasm_remove_breakpoint (int bp_id);
EMSCRIPTEN_KEEPALIVE int mono_wasm_current_bp_id (void);
EMSCRIPTEN_KEEPALIVE void mono_wasm_enum_frames (void);
EMSCRIPTEN_KEEPALIVE void mono_wasm_get_var_info (int scope, int* pos, int len);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_local_vars (int scope, int* pos, int len);
EMSCRIPTEN_KEEPALIVE void mono_wasm_clear_all_breakpoints (void);
EMSCRIPTEN_KEEPALIVE int mono_wasm_setup_single_step (int kind);
EMSCRIPTEN_KEEPALIVE void mono_wasm_get_object_properties (int object_id, gboolean expand_value_types);
EMSCRIPTEN_KEEPALIVE void mono_wasm_get_array_values (int object_id);
EMSCRIPTEN_KEEPALIVE void mono_wasm_get_array_value_expanded (int object_id, int idx);
EMSCRIPTEN_KEEPALIVE void mono_wasm_invoke_getter_on_object (int object_id, const char* name);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_object_properties (int object_id, gboolean expand_value_types);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_array_values (int object_id, int start_idx, int count, gboolean expand_value_types);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_invoke_getter_on_object (int object_id, const char* name);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_invoke_getter_on_value (void *value, MonoClass *klass, const char *name);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_deref_ptr_value (void *value_addr, MonoClass *klass);

//JS functions imported that we use
extern void mono_wasm_add_frame (int il_offset, int method_token, const char *assembly_name, const char *method_name);
extern void mono_wasm_fire_bp (void);
extern void mono_wasm_add_obj_var (const char*, const char*, guint64);
extern void mono_wasm_add_value_type_unexpanded_var (const char*, const char*);
extern void mono_wasm_begin_value_type_var (const char*, const char*);
extern void mono_wasm_end_value_type_var (void);
extern void mono_wasm_add_enum_var (const char*, const char*, guint64);
extern void mono_wasm_add_func_var (const char*, const char*, guint64);
extern void mono_wasm_add_properties_var (const char*, gint32);
Expand Down Expand Up @@ -583,6 +581,22 @@ static int get_object_id(MonoObject *obj)
return ref->id;
}

static MonoObject*
get_object_from_id (int objectId)
{
ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
if (!ref) {
DEBUG_PRINTF (2, "get_object_from_id !ref: %d\n", objectId);
return NULL;
}

MonoObject *obj = mono_gchandle_get_target_internal (ref->handle);
if (!obj)
DEBUG_PRINTF (2, "get_object_from_id !obj: %d\n", objectId);

return obj;
}

static gboolean
list_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data)
{
Expand Down Expand Up @@ -808,11 +822,13 @@ static gboolean describe_value(MonoType * type, gpointer addr, gboolean expandVa
case MONO_TYPE_FNPTR: {
char *class_name = mono_type_full_name (type);
const void *val = *(const void **)addr;
char *val_str = g_strdup_printf ("(%s) %p", class_name, val);
char *descr = g_strdup_printf ("(%s) %p", class_name, val);

mono_wasm_add_typed_value ("pointer", val_str, (guint32)val);
EM_ASM ({
MONO.mono_wasm_add_typed_value ('pointer', $0, { ptr_addr: $1, klass_addr: $2 });
}, descr, val ? addr : 0, val ? mono_class_from_mono_type_internal (type) : 0);

g_free (val_str);
g_free (descr);
g_free (class_name);
break;
}
Expand Down Expand Up @@ -918,17 +934,28 @@ static gboolean describe_value(MonoType * type, gpointer addr, gboolean expandVa

mono_wasm_add_enum_var (class_name, enum_members->str, value__);
g_string_free (enum_members, TRUE);
} else if (expandValueType) {
char *to_string_val = get_to_string_description (class_name, klass, addr);
mono_wasm_begin_value_type_var (class_name, to_string_val);
g_free (to_string_val);

// FIXME: isAsyncLocalThis
describe_object_properties_for_klass ((MonoObject*)addr, klass, FALSE, expandValueType);
mono_wasm_end_value_type_var ();
} else {
char *to_string_val = get_to_string_description (class_name, klass, addr);
mono_wasm_add_value_type_unexpanded_var (class_name, to_string_val);

if (expandValueType) {
int32_t size = mono_class_value_size (klass, NULL);
void *value_buf = g_malloc0 (size);
mono_value_copy_internal (value_buf, addr, klass);

EM_ASM ({
MONO.mono_wasm_add_typed_value ($0, $1, { toString: $2, value_addr: $3, value_size: $4, klass: $5 });
}, "begin_vt", class_name, to_string_val, value_buf, size, klass);

g_free (value_buf);

// FIXME: isAsyncLocalThis
describe_object_properties_for_klass (addr, klass, FALSE, expandValueType);
mono_wasm_add_typed_value ("end_vt", NULL, 0);
} else {
EM_ASM ({
MONO.mono_wasm_add_typed_value ($0, $1, { toString: $2 });
}, "unexpanded_vt", class_name, to_string_val);
}
g_free (to_string_val);
}
g_free (class_name);
Expand Down Expand Up @@ -986,7 +1013,7 @@ describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAs
gboolean is_valuetype;
int pnum;
char *klass_name;
gboolean getters_allowed;
gboolean auto_invoke_getters;

g_assert (klass);
is_valuetype = m_class_is_valuetype(klass);
Expand Down Expand Up @@ -1019,7 +1046,7 @@ describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAs
}

klass_name = mono_class_full_name (klass);
getters_allowed = are_getters_allowed (klass_name);
auto_invoke_getters = are_getters_allowed (klass_name);

iter = NULL;
pnum = 0;
Expand All @@ -1031,29 +1058,24 @@ describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAs
mono_wasm_add_properties_var (p->name, pnum);
sig = mono_method_signature_internal (p->get);

// automatic properties will get skipped
if (!getters_allowed) {
gboolean vt_self_type_getter = is_valuetype && mono_class_from_mono_type_internal (sig->ret) == klass;
if (auto_invoke_getters && !vt_self_type_getter) {
invoke_and_describe_getter_value (obj, p);
} else {
// not allowed to call the getter here
char *ret_class_name = mono_class_full_name (mono_class_from_mono_type_internal (sig->ret));

// getters not supported for valuetypes, yet
gboolean invokable = !is_valuetype && sig->param_count == 0;
gboolean invokable = sig->param_count == 0;
mono_wasm_add_typed_value ("getter", ret_class_name, invokable);

g_free (ret_class_name);
continue;
}

if (is_valuetype && mono_class_from_mono_type_internal (sig->ret) == klass) {
// Property of the same valuetype, avoid endlessly recursion!
mono_wasm_add_typed_value ("getter", klass_name, 0);
continue;
}

invoke_and_describe_getter_value (obj, p);
}
pnum ++;
}

g_free (klass_name);
}

/*
Expand Down Expand Up @@ -1082,17 +1104,10 @@ static gboolean
describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis, gboolean expandValueType)
{
DEBUG_PRINTF (2, "describe_object_properties %llu\n", objectId);
ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
if (!ref) {
DEBUG_PRINTF (2, "describe_object_properties !ref\n");
return FALSE;
}

MonoObject *obj = mono_gchandle_get_target_internal (ref->handle);
if (!obj) {
DEBUG_PRINTF (2, "describe_object_properties !obj\n");
MonoObject *obj = get_object_from_id (objectId);
if (!obj)
return FALSE;
}

if (m_class_is_delegate (mono_object_class (obj))) {
// delegates get the same id format as regular objects
Expand All @@ -1105,80 +1120,70 @@ describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis, gboolea
}

static gboolean
invoke_getter_on_object (guint64 objectId, const char *name)
invoke_getter (void *obj_or_value, MonoClass *klass, const char *name)
{
ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
if (!ref) {
DEBUG_PRINTF (1, "invoke_getter_on_object no objRef found for id %llu\n", objectId);
return FALSE;
}

MonoObject *obj = mono_gchandle_get_target_internal (ref->handle);
if (!obj) {
DEBUG_PRINTF (1, "invoke_getter_on_object !obj\n");
if (!obj_or_value || !klass || !name) {
DEBUG_PRINTF (2, "invoke_getter: none of the arguments can be null");
return FALSE;
}

MonoClass *klass = mono_object_class (obj);
gpointer iter = NULL;
MonoProperty *p;
while ((p = mono_class_get_properties (klass, &iter))) {
//if get doesn't have name means that doesn't have a getter implemented and we don't want to show value, like VS debug
if (!p->get->name || strcasecmp (p->name, name) != 0)
continue;

invoke_and_describe_getter_value (obj, p);
invoke_and_describe_getter_value (obj_or_value, p);
return TRUE;
}

return FALSE;
}

static gboolean
describe_array_values (guint64 objectId)
describe_array_values (guint64 objectId, int startIdx, int count, gboolean expandValueType)
{
if (count == 0)
return TRUE;

int esize;
gpointer elem;
ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
if (!ref) {
MonoArray *arr = (MonoArray*) get_object_from_id (objectId);
if (!arr)
return FALSE;
}
MonoArray *arr = (MonoArray *)mono_gchandle_get_target_internal (ref->handle);
MonoObject *obj = &arr->obj;
if (!obj) {

MonoClass *klass = mono_object_class (arr);
MonoTypeEnum type = m_class_get_byval_arg (klass)->type;
if (type != MONO_TYPE_SZARRAY && type != MONO_TYPE_ARRAY) {
DEBUG_PRINTF (1, "describe_array_values: object is not an array. type: 0x%x\n", type);
return FALSE;
}
esize = mono_array_element_size (obj->vtable->klass);
for (int i = 0; i < arr->max_length; i++) {
mono_wasm_add_array_item(i);
elem = (gpointer*)((char*)arr->vector + (i * esize));
describe_value (m_class_get_byval_arg (m_class_get_element_class (arr->obj.vtable->klass)), elem, FALSE);

int len = arr->max_length;
if (len == 0 && startIdx == 0 && count <= 0) {
// Nothing to do
return TRUE;
}
return TRUE;
}

/* Expands valuetypes */
static gboolean
describe_array_value_expanded (guint64 objectId, guint64 idx)
{
int esize;
gpointer elem;
ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
if (!ref) {
if (startIdx < 0 || (len > 0 && startIdx >= len)) {
DEBUG_PRINTF (1, "describe_array_values: invalid startIdx (%d) for array of length %d\n", startIdx, len);
return FALSE;
}
MonoArray *arr = (MonoArray *)mono_gchandle_get_target_internal (ref->handle);
MonoObject *obj = &arr->obj;
if (!obj) {

if (count > 0 && (startIdx + count) > len) {
DEBUG_PRINTF (1, "describe_array_values: invalid count (%d) for startIdx: %d, and array of length %d\n", count, startIdx, len);
return FALSE;
}
if (idx >= arr->max_length)
return FALSE;

esize = mono_array_element_size (obj->vtable->klass);
elem = (gpointer*)((char*)arr->vector + (idx * esize));
describe_value (m_class_get_byval_arg (m_class_get_element_class (arr->obj.vtable->klass)), elem, TRUE);
esize = mono_array_element_size (klass);
int endIdx = count < 0 ? len : startIdx + count;

for (int i = startIdx; i < endIdx; i ++) {
mono_wasm_add_array_item(i);
elem = (gpointer*)((char*)arr->vector + (i * esize));
describe_value (m_class_get_byval_arg (m_class_get_element_class (klass)), elem, expandValueType);
}
return TRUE;
}

Expand Down Expand Up @@ -1281,9 +1286,23 @@ describe_variables_on_frame (MonoStackFrameInfo *info, MonoContext *ctx, gpointe
return TRUE;
}

EMSCRIPTEN_KEEPALIVE gboolean
mono_wasm_get_deref_ptr_value (void *value_addr, MonoClass *klass)
{
MonoType *type = m_class_get_byval_arg (klass);
if (type->type != MONO_TYPE_PTR && type->type != MONO_TYPE_FNPTR) {
DEBUG_PRINTF (2, "BUG: mono_wasm_get_deref_ptr_value: Expected to get a ptr type, but got 0x%x\n", type->type);
return FALSE;
}

mono_wasm_add_properties_var ("deref", -1);
describe_value (type->data.type, value_addr, TRUE);
return TRUE;
}

//FIXME this doesn't support getting the return value pseudo-var
EMSCRIPTEN_KEEPALIVE void
mono_wasm_get_var_info (int scope, int* pos, int len)
EMSCRIPTEN_KEEPALIVE gboolean
mono_wasm_get_local_vars (int scope, int* pos, int len)
{
FrameDescData data;
data.target_frame = scope;
Expand All @@ -1292,37 +1311,51 @@ mono_wasm_get_var_info (int scope, int* pos, int len)
data.pos = pos;

mono_walk_stack_with_ctx (describe_variables_on_frame, NULL, MONO_UNWIND_NONE, &data);

return TRUE;
}

EMSCRIPTEN_KEEPALIVE void
EMSCRIPTEN_KEEPALIVE gboolean
mono_wasm_get_object_properties (int object_id, gboolean expand_value_types)
{
DEBUG_PRINTF (2, "getting properties of object %d\n", object_id);

describe_object_properties (object_id, FALSE, expand_value_types);
return describe_object_properties (object_id, FALSE, expand_value_types);
}

EMSCRIPTEN_KEEPALIVE void
mono_wasm_get_array_values (int object_id)
EMSCRIPTEN_KEEPALIVE gboolean
mono_wasm_get_array_values (int object_id, int start_idx, int count, gboolean expand_value_types)
{
DEBUG_PRINTF (2, "getting array values %d\n", object_id);
DEBUG_PRINTF (2, "getting array values %d, startIdx: %d, count: %d, expandValueType: %d\n", object_id, start_idx, count, expand_value_types);

describe_array_values(object_id);
return describe_array_values (object_id, start_idx, count, expand_value_types);
}

EMSCRIPTEN_KEEPALIVE void
mono_wasm_get_array_value_expanded (int object_id, int idx)
EMSCRIPTEN_KEEPALIVE
mono_wasm_invoke_getter_on_object (int object_id, const char* name)
{
DEBUG_PRINTF (2, "getting array value %d for idx %d\n", object_id, idx);
MonoObject *obj = get_object_from_id (object_id);
if (!obj)
return FALSE;

describe_array_value_expanded (object_id, idx);
return invoke_getter (obj, mono_object_class (obj), name);
}

EMSCRIPTEN_KEEPALIVE void
mono_wasm_invoke_getter_on_object (int object_id, const char* name)
EMSCRIPTEN_KEEPALIVE gboolean
mono_wasm_invoke_getter_on_value (void *value, MonoClass *klass, const char *name)
{
invoke_getter_on_object (object_id, name);
DEBUG_PRINTF (2, "mono_wasm_invoke_getter_on_value: v: %p klass: %p, name: %s\n", value, klass, name);
if (!klass || !value)
return FALSE;

if (!m_class_is_valuetype (klass)) {
DEBUG_PRINTF (2, "mono_wasm_invoke_getter_on_value: klass is not a valuetype. name: %s\n", mono_class_full_name (klass));
return FALSE;
}

return invoke_getter (value, klass, name);
}

// Functions required by debugger-state-machine.
gsize
mono_debugger_tls_thread_id (DebuggerTlsData *debuggerTlsData)
Expand Down
Loading