Skip to content

Commit

Permalink
[wasm][debugger] Add support for visualizing valuetypes (#33604)
Browse files Browse the repository at this point in the history
Co-authored-by: radical <[email protected]>
  • Loading branch information
monojenkins and radical authored Apr 2, 2020
1 parent b927233 commit 90689b2
Showing 1 changed file with 176 additions and 45 deletions.
221 changes: 176 additions & 45 deletions src/mono/mono/mini/mini-wasm-debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <mono/metadata/assembly-internals.h>
#include <mono/metadata/metadata.h>
#include <mono/metadata/metadata-internals.h>
#include <mono/metadata/mono-endian.h>
#include <mono/metadata/seq-points-data.h>
#include <mono/mini/aot-runtime.h>
#include <mono/mini/seq-points.h>
Expand Down Expand Up @@ -33,7 +34,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_enum_frames (void);
EMSCRIPTEN_KEEPALIVE void mono_wasm_get_var_info (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);
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);

//JS functions imported that we use
Expand All @@ -43,13 +44,19 @@ extern void mono_wasm_add_bool_var (gint8);
extern void mono_wasm_add_number_var (double);
extern void mono_wasm_add_string_var (const char*);
extern void mono_wasm_add_obj_var (const char*, guint64);
extern void mono_wasm_add_value_type_unexpanded_var (const char*);
extern void mono_wasm_begin_value_type_var (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*, guint64);
extern void mono_wasm_add_array_var (const char*, guint64);
extern void mono_wasm_add_properties_var (const char*);
extern void mono_wasm_add_properties_var (const char*, gint32);
extern void mono_wasm_add_array_item (int);

G_END_DECLS

static void describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, gboolean expandValueType);

//FIXME move all of those fields to the profiler object
static gboolean debugger_enabled;

Expand Down Expand Up @@ -613,7 +620,43 @@ typedef struct {
int *pos;
} FrameDescData;

static gboolean describe_value(MonoType * type, gpointer addr)
static guint64
read_enum_value (const char *mem, int type)
{
switch (type) {
case MONO_TYPE_BOOLEAN:
case MONO_TYPE_U1:
return *(guint8*)mem;
case MONO_TYPE_I1:
return *(gint8*)mem;
case MONO_TYPE_CHAR:
case MONO_TYPE_U2:
return read16 (mem);
case MONO_TYPE_I2:
return (gint16) read16 (mem);
case MONO_TYPE_U4:
case MONO_TYPE_R4:
return read32 (mem);
case MONO_TYPE_I4:
return (gint32) read32 (mem);
case MONO_TYPE_U8:
case MONO_TYPE_I8:
case MONO_TYPE_R8:
return read64 (mem);
case MONO_TYPE_U:
case MONO_TYPE_I:
#if SIZEOF_REGISTER == 8
return read64 (mem);
#else
return read32 (mem);
#endif
default:
g_assert_not_reached ();
}
return 0;
}

static gboolean describe_value(MonoType * type, gpointer addr, gboolean expandValueType)
{
ERROR_DECL (error);
switch (type->type) {
Expand Down Expand Up @@ -665,17 +708,23 @@ static gboolean describe_value(MonoType * type, gpointer addr)
}
break;
}
case MONO_TYPE_GENERICINST:
case MONO_TYPE_GENERICINST: {
if (mono_type_generic_inst_is_valuetype (type))
goto handle_vtype;
/*
* else fallthrough
*/
}

case MONO_TYPE_SZARRAY:
case MONO_TYPE_ARRAY:
case MONO_TYPE_OBJECT:
case MONO_TYPE_VALUETYPE:
case MONO_TYPE_CLASS: {
MonoObject *obj = *(MonoObject**)addr;
MonoClass *klass = type->data.klass;

char *class_name = mono_type_full_name (type);
int obj_id = type->type == MONO_TYPE_VALUETYPE ? 0 : get_object_id (obj);
int obj_id = get_object_id (obj);

if (type->type == MONO_TYPE_SZARRAY || type->type == MONO_TYPE_ARRAY) {
mono_wasm_add_array_var (class_name, obj_id);
Expand All @@ -687,6 +736,56 @@ static gboolean describe_value(MonoType * type, gpointer addr)
g_free (class_name);
break;
}

handle_vtype:
case MONO_TYPE_VALUETYPE: {
g_assert (addr);
MonoClass *klass = mono_class_from_mono_type_internal (type);
char *class_name = mono_type_full_name (type);

if (m_class_is_enumtype (klass)) {
MonoClassField *field;
gpointer iter = NULL;
const char *p;
MonoTypeEnum def_type;
guint64 field_value;
guint64 value__ = 0xDEAD;
GString *enum_members = g_string_new ("");
int base_type = mono_class_enum_basetype_internal (klass)->type;

while ((field = mono_class_get_fields_internal (klass, &iter))) {
if (strcmp ("value__", mono_field_get_name (field)) == 0) {
value__ = read_enum_value (mono_vtype_get_field_addr (addr, field), base_type);
continue;
}

if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
continue;
if (mono_field_is_deleted (field))
continue;

p = mono_class_get_field_default_value (field, &def_type);
/* this is to correctly increment `p` in the blob */
/* len = */ mono_metadata_decode_blob_size (p, &p);

field_value = read_enum_value (p, base_type);

g_string_append_printf (enum_members, ",%s:%llu", mono_field_get_name (field), field_value);
}

mono_wasm_add_enum_var (class_name, enum_members->str, value__);
g_string_free (enum_members, TRUE);
} else if (expandValueType) {
mono_wasm_begin_value_type_var (class_name);
// FIXME: isAsyncLocalThis
describe_object_properties_for_klass ((MonoObject*)addr, klass, FALSE, expandValueType);
mono_wasm_end_value_type_var ();
} else {
mono_wasm_add_value_type_unexpanded_var (class_name);
}
g_free (class_name);
break;
}
default: {
char *type_name = mono_type_full_name (type);
char *msg = g_strdup_printf("can't handle type %s [%p, %x]", type_name, type, type->type);
Expand All @@ -698,8 +797,8 @@ static gboolean describe_value(MonoType * type, gpointer addr)
return TRUE;
}

static gboolean
describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis)
static void
describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, gboolean expandValueType)
{
MonoClassField *f;
MonoProperty *p;
Expand All @@ -708,28 +807,19 @@ describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis)
MonoMethodSignature *sig;
gpointer iter = NULL;
ERROR_DECL (error);
DEBUG_PRINTF (2, "describe_object_properties %d\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");
return FALSE;
}
gboolean is_valuetype;
int pnum;

g_assert (klass);
is_valuetype = m_class_is_valuetype(klass);

while (obj && (f = mono_class_get_fields_internal (obj->vtable->klass, &iter))) {
DEBUG_PRINTF (2, "mono_class_get_fields_internal - %s - %x\n", f->name, f->type->type);
while (obj && (f = mono_class_get_fields_internal (klass, &iter))) {
if (isAsyncLocalThis && f->name[0] == '<' && f->name[1] == '>') {
if (g_str_has_suffix (f->name, "__this")) {
char *class_name = mono_class_full_name (obj->vtable->klass);
mono_wasm_add_properties_var ("this");
mono_wasm_add_properties_var ("this", f->offset);
gpointer field_value = (guint8*)obj + f->offset;

describe_value (f->type, field_value);
describe_value (f->type, field_value, is_valuetype | expandValueType);
}

continue;
Expand All @@ -738,35 +828,76 @@ describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis)
continue;
if (mono_field_is_deleted (f))
continue;
mono_wasm_add_properties_var(f->name);
gpointer field_value = (guint8*)obj + f->offset;

mono_wasm_add_properties_var (f->name, f->offset);

gpointer field_addr;
if (is_valuetype)
field_addr = mono_vtype_get_field_addr (obj, f);
else
field_addr = (guint8*)obj + f->offset;

describe_value(f->type, field_value);
describe_value (f->type, field_addr, is_valuetype | expandValueType);
}

iter = NULL;
while ((p = mono_class_get_properties (obj->vtable->klass, &iter))) {
pnum = 0;
while ((p = mono_class_get_properties (klass, &iter))) {
DEBUG_PRINTF (2, "mono_class_get_properties - %s - %s\n", p->name, p->get->name);
if (p->get->name) { //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 (isAsyncLocalThis && (p->name[0] != '<' || (p->name[0] == '<' && p->name[1] == '>'))) {
char *class_name;
if (isAsyncLocalThis && (p->name[0] != '<' || (p->name[0] == '<' && p->name[1] == '>')))
continue;
}
mono_wasm_add_properties_var(p->name);

mono_wasm_add_properties_var (p->name, pnum);
sig = mono_method_signature_internal (p->get);

// automatic properties will get skipped
class_name = mono_class_full_name (mono_class_from_mono_type_internal (sig->ret));
mono_wasm_add_string_var (class_name);
g_free (class_name);

#if false // Disabled for now, as we don't want to invoke getters
if (is_valuetype && mono_class_from_mono_type_internal (sig->ret) == klass) {
// Property of the same valuetype, avoid endlessly recursion!
mono_wasm_add_string_var (mono_class_full_name (klass));
continue;
}

res = mono_runtime_try_invoke (p->get, obj, NULL, &exc, error);
if (!is_ok (error) && exc == NULL)
exc = (MonoObject*) mono_error_convert_to_exception (error);
if (exc)
describe_value (mono_get_object_type (), &exc);
else if (!m_class_is_valuetype (mono_object_class (res)))
describe_value(sig->ret, &res);
describe_value (mono_get_object_type (), &exc, TRUE);
else if (!res || !m_class_is_valuetype (mono_object_class (res)))
describe_value (sig->ret, &res, TRUE);
else
describe_value(sig->ret, mono_object_unbox_internal (res));
describe_value (sig->ret, mono_object_unbox_internal (res), TRUE);
#endif
}
pnum ++;
}
return TRUE;
}

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");
return FALSE;
}

describe_object_properties_for_klass (obj, obj->vtable->klass, isAsyncLocalThis, expandValueType);
return TRUE;
}

static gboolean
describe_array_values (guint64 objectId)
Expand All @@ -786,7 +917,7 @@ describe_array_values (guint64 objectId)
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);
describe_value (m_class_get_byval_arg (m_class_get_element_class (arr->obj.vtable->klass)), elem, FALSE);
}
return TRUE;
}
Expand All @@ -799,7 +930,7 @@ describe_async_method_locals (InterpFrame *frame, MonoMethod *method)
if (mono_debug_lookup_method_async_debug_info (method)) {
addr = mini_get_interp_callbacks ()->frame_get_this (frame);
MonoObject *obj = *(MonoObject**)addr;
describe_object_properties(get_object_id(obj), TRUE);
describe_object_properties (get_object_id(obj), TRUE, FALSE);
}
}

Expand All @@ -815,14 +946,14 @@ describe_non_async_this (InterpFrame *frame, MonoMethod *method)
MonoObject *obj = *(MonoObject**)addr;
char *class_name = mono_class_full_name (obj->vtable->klass);

mono_wasm_add_properties_var("this");
mono_wasm_add_properties_var ("this", -1);
mono_wasm_add_obj_var (class_name, get_object_id(obj));
g_free (class_name);
}
}

static gboolean
describe_variable (InterpFrame *frame, MonoMethod *method, int pos)
describe_variable (InterpFrame *frame, MonoMethod *method, int pos, gboolean expandValueType)
{
ERROR_DECL (error);
MonoMethodHeader *header = NULL;
Expand All @@ -843,7 +974,7 @@ describe_variable (InterpFrame *frame, MonoMethod *method, int pos)

DEBUG_PRINTF (2, "adding val %p type [%p] %s\n", addr, type, mono_type_full_name (type));

describe_value(type, addr);
describe_value(type, addr, expandValueType);
if (header)
mono_metadata_free_mh (header);

Expand Down Expand Up @@ -872,7 +1003,7 @@ describe_variables_on_frame (MonoStackFrameInfo *info, MonoContext *ctx, gpointe

for (int i = 0; i < data->len; i++)
{
describe_variable (frame, method, data->pos[i]);
describe_variable (frame, method, data->pos[i], TRUE);
}

describe_async_method_locals (frame, method);
Expand All @@ -895,11 +1026,11 @@ mono_wasm_get_var_info (int scope, int* pos, int len)
}

EMSCRIPTEN_KEEPALIVE void
mono_wasm_get_object_properties (int object_id)
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);
describe_object_properties (object_id, FALSE, expand_value_types);
}

EMSCRIPTEN_KEEPALIVE void
Expand Down

0 comments on commit 90689b2

Please sign in to comment.