[wasm] Add a --runtime-arg= argument to runtime-tests.js to allow setting runtime...
[mono-project.git] / mono / mini / mini-wasm-debugger.c
blob5d2dea0335bfa8e0fba04bf3e49cda6b3829de12
1 #include "mini.h"
2 #include "mini-runtime.h"
3 #include <mono/metadata/mono-debug.h>
4 #include <mono/metadata/assembly.h>
5 #include <mono/metadata/metadata.h>
6 #include <mono/metadata/seq-points-data.h>
7 #include <mono/mini/aot-runtime.h>
8 #include <mono/mini/seq-points.h>
9 #include <mono/mini/debugger-engine.h>
11 //XXX This is dirty, extend ee.h to support extracting info from MonoInterpFrameHandle
12 #include <mono/mini/interp/interp-internals.h>
14 #ifdef HOST_WASM
16 #include <emscripten.h>
19 static int log_level = 1;
21 #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { fprintf (stdout, __VA_ARGS__); } } while (0)
23 //functions exported to be used by JS
24 G_BEGIN_DECLS
26 EMSCRIPTEN_KEEPALIVE int mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset);
27 EMSCRIPTEN_KEEPALIVE int mono_wasm_remove_breakpoint (int bp_id);
28 EMSCRIPTEN_KEEPALIVE int mono_wasm_current_bp_id (void);
29 EMSCRIPTEN_KEEPALIVE void mono_wasm_enum_frames (void);
30 EMSCRIPTEN_KEEPALIVE void mono_wasm_get_var_info (int scope, int pos);
31 EMSCRIPTEN_KEEPALIVE void mono_wasm_clear_all_breakpoints (void);
32 EMSCRIPTEN_KEEPALIVE void mono_wasm_setup_single_step (int kind);
33 EMSCRIPTEN_KEEPALIVE void mono_wasm_get_object_properties (int object_id);
34 EMSCRIPTEN_KEEPALIVE void mono_wasm_get_array_values (int object_id);
36 //JS functions imported that we use
37 extern void mono_wasm_add_frame (int il_offset, int method_token, const char *assembly_name);
38 extern void mono_wasm_fire_bp (void);
39 extern void mono_wasm_add_bool_var (gint8);
40 extern void mono_wasm_add_int_var (gint32);
41 extern void mono_wasm_add_long_var (gint64);
42 extern void mono_wasm_add_float_var (float);
43 extern void mono_wasm_add_double_var (double);
44 extern void mono_wasm_add_string_var (const char*);
45 extern void mono_wasm_add_obj_var (const char*, guint64);
46 extern void mono_wasm_add_array_var (const char*, guint64);
47 extern void mono_wasm_add_properties_var (const char*);
48 extern void mono_wasm_add_array_item (int);
50 G_END_DECLS
52 //FIXME move all of those fields to the profiler object
53 static gboolean debugger_enabled;
55 static int event_request_id;
56 static GHashTable *objrefs;
57 static GHashTable *obj_to_objref;
58 static int objref_id = 0;
60 #define THREAD_TO_INTERNAL(thread) (thread)->internal_thread
62 static void
63 inplace_tolower (char *c)
65 int i;
66 for (i = strlen (c) - 1; i >= 0; --i)
67 c [i] = tolower (c [i]);
70 static void
71 jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo)
73 mono_de_add_pending_breakpoints (method, jinfo);
76 static void
77 appdomain_load (MonoProfiler *prof, MonoDomain *domain)
79 mono_de_domain_add (domain);
82 /* Frame state handling */
83 static GPtrArray *frames;
85 static void
86 free_frame (DbgEngineStackFrame *frame)
88 g_free (frame);
91 static gboolean
92 collect_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data)
94 SeqPoint sp;
95 MonoMethod *method;
97 //skip wrappers
98 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP)
99 return FALSE;
101 if (info->ji)
102 method = jinfo_get_method (info->ji);
103 else
104 method = info->method;
106 if (!method)
107 return FALSE;
109 DEBUG_PRINTF (2, "Reporting method %s native_offset %d\n", method->name, info->native_offset);
111 if (!mono_find_prev_seq_point_for_native_offset (mono_get_root_domain (), method, info->native_offset, NULL, &sp))
112 DEBUG_PRINTF (1, "Failed to lookup sequence point\n");
114 DbgEngineStackFrame *frame = g_new0 (DbgEngineStackFrame, 1);
116 frame->ji = info->ji;
117 frame->domain = info->domain;
118 frame->method = method;
119 frame->native_offset = info->native_offset;
121 g_ptr_array_add (frames, frame);
123 return FALSE;
126 static void
127 free_frame_state (void)
129 if (frames) {
130 int i;
131 for (i = 0; i < frames->len; ++i)
132 free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i));
133 g_ptr_array_set_size (frames, 0);
137 static void
138 compute_frames (void) {
139 if (frames) {
140 int i;
141 for (i = 0; i < frames->len; ++i)
142 free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i));
143 g_ptr_array_set_size (frames, 0);
144 } else {
145 frames = g_ptr_array_new ();
148 mono_walk_stack_with_ctx (collect_frames, NULL, MONO_UNWIND_NONE, NULL);
150 static MonoContext*
151 tls_get_restore_state (void *tls)
153 return NULL;
156 static gboolean
157 try_process_suspend (void *tls, MonoContext *ctx)
159 return FALSE;
162 static gboolean
163 begin_breakpoint_processing (void *tls, MonoContext *ctx, MonoJitInfo *ji, gboolean from_signal)
165 return TRUE;
168 static void
169 begin_single_step_processing (MonoContext *ctx, gboolean from_signal)
173 static void
174 ss_discard_frame_context (void *the_tls)
176 free_frame_state ();
179 static void
180 ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***out_frames, int *nframes)
182 compute_frames ();
183 if (out_frames)
184 *out_frames = (DbgEngineStackFrame **)frames->pdata;
185 if (nframes)
186 *nframes = frames->len;
189 static gboolean
190 ensure_jit (DbgEngineStackFrame* the_frame)
192 return TRUE;
195 static int
196 ensure_runtime_is_suspended (void)
198 return DE_ERR_NONE;
201 static int
202 get_this_async_id (DbgEngineStackFrame *f)
204 g_error ("get_this_async_id");
205 return 0;
208 static gboolean
209 set_set_notification_for_wait_completion_flag (DbgEngineStackFrame *f)
211 g_error ("set_set_notification_for_wait_completion_flag");
212 return FALSE;
215 static MonoMethod*
216 get_notify_debugger_of_wait_completion_method (void)
218 g_error ("get_notify_debugger_of_wait_completion_method");
221 typedef struct {
222 gboolean is_ss; //do I need this?
223 } BpEvents;
225 static void*
226 create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind)
228 printf ("ss_reqs %d bp_reqs %d\n", ss_reqs->len, bp_reqs->len);
229 if ((ss_reqs && ss_reqs->len) || (bp_reqs && bp_reqs->len)) {
230 BpEvents *evts = g_new0 (BpEvents, 1); //just a non-null value to make sure we can raise it on process_breakpoint_events
231 evts->is_ss = (ss_reqs && ss_reqs->len);
232 return evts;
234 return NULL;
237 static void
238 process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offsets)
240 BpEvents *evts = (BpEvents*)_evts;
241 if (evts) {
242 if (evts->is_ss)
243 mono_de_cancel_ss ();
244 mono_wasm_fire_bp ();
245 g_free (evts);
249 static void
250 no_seq_points_found (MonoMethod *method, int offset)
253 * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space.
255 printf ("Unable to find seq points for method '%s', offset 0x%x.\n", mono_method_full_name (method, TRUE), offset);
258 #define DBG_NOT_SUSPENDED 1
260 static int
261 ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *ss_args)
263 printf ("ss_create_init_args\n");
264 int dummy = 0;
265 ss_req->start_sp = ss_req->last_sp = &dummy;
266 compute_frames ();
267 memset (ss_args, 0, sizeof (*ss_args));
269 //BIG WTF, should not happen maybe should assert?
270 if (frames->len == 0) {
271 DEBUG_PRINTF (1, "SINGLE STEPPING FOUND NO FRAMES");
272 return DBG_NOT_SUSPENDED;
275 DbgEngineStackFrame *frame = (DbgEngineStackFrame*)g_ptr_array_index (frames, 0);
276 ss_req->start_method = ss_args->method = frame->method;
277 gboolean found_sp = mono_find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &ss_args->info, &ss_args->sp);
278 if (!found_sp)
279 no_seq_points_found (frame->method, frame->native_offset);
280 g_assert (found_sp);
282 ss_args->frames = (DbgEngineStackFrame**)frames->pdata;
283 ss_args->nframes = frames->len;
284 //XXX do sp
286 return DE_ERR_NONE;
289 static void
290 ss_args_destroy (SingleStepArgs *ss_args)
292 //nothing to do
295 void
296 mono_wasm_debugger_init (void)
298 if (!debugger_enabled)
299 return;
301 DebuggerEngineCallbacks cbs = {
302 .tls_get_restore_state = tls_get_restore_state,
303 .try_process_suspend = try_process_suspend,
304 .begin_breakpoint_processing = begin_breakpoint_processing,
305 .begin_single_step_processing = begin_single_step_processing,
306 .ss_discard_frame_context = ss_discard_frame_context,
307 .ss_calculate_framecount = ss_calculate_framecount,
308 .ensure_jit = ensure_jit,
309 .ensure_runtime_is_suspended = ensure_runtime_is_suspended,
310 .get_this_async_id = get_this_async_id,
311 .set_set_notification_for_wait_completion_flag = set_set_notification_for_wait_completion_flag,
312 .get_notify_debugger_of_wait_completion_method = get_notify_debugger_of_wait_completion_method,
313 .create_breakpoint_events = create_breakpoint_events,
314 .process_breakpoint_events = process_breakpoint_events,
315 .ss_create_init_args = ss_create_init_args,
316 .ss_args_destroy = ss_args_destroy,
319 mono_debug_init (MONO_DEBUG_FORMAT_MONO);
320 mono_de_init (&cbs);
321 mono_de_set_log_level (1, stdout);
323 mini_get_debug_options ()->gen_sdb_seq_points = TRUE;
324 mini_get_debug_options ()->mdb_optimizations = TRUE;
325 mono_disable_optimizations (MONO_OPT_LINEARS);
326 mini_get_debug_options ()->load_aot_jit_info_eagerly = TRUE;
328 MonoProfilerHandle prof = mono_profiler_create (NULL);
329 mono_profiler_set_jit_done_callback (prof, jit_done);
330 //FIXME support multiple appdomains
331 mono_profiler_set_domain_loaded_callback (prof, appdomain_load);
333 obj_to_objref = g_hash_table_new (NULL, NULL);
334 objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref);
337 MONO_API void
338 mono_wasm_enable_debugging (void)
340 DEBUG_PRINTF (1, "DEBUGGING ENABLED\n");
341 debugger_enabled = TRUE;
344 EMSCRIPTEN_KEEPALIVE void
345 mono_wasm_setup_single_step (int kind)
347 int nmodifiers = 1;
349 printf (">>>> mono_wasm_setup_single_step %d\n", kind);
350 EventRequest *req = (EventRequest *)g_malloc0 (sizeof (EventRequest) + (nmodifiers * sizeof (Modifier)));
351 req->id = ++event_request_id;
352 req->event_kind = EVENT_KIND_STEP;
353 // DE doesn't care about suspend_policy
354 // req->suspend_policy = SUSPEND_POLICY_ALL;
355 req->nmodifiers = nmodifiers;
357 StepSize size = STEP_SIZE_MIN;
359 //FIXME I DON'T KNOW WHAT I'M DOING!!!!! filter all the things.
360 StepFilter filter = (StepFilter)(STEP_FILTER_STATIC_CTOR | STEP_FILTER_DEBUGGER_HIDDEN | STEP_FILTER_DEBUGGER_STEP_THROUGH | STEP_FILTER_DEBUGGER_NON_USER_CODE);
361 req->modifiers [0].data.filter = filter;
363 StepDepth depth;
364 switch (kind) {
365 case 0: //into
366 depth = STEP_DEPTH_INTO;
367 break;
368 case 1: //out
369 depth = STEP_DEPTH_OUT;
370 break;
371 case 2: //over
372 depth = STEP_DEPTH_OVER;
373 break;
374 default:
375 g_error ("dunno step kind %d", kind);
378 DbgEngineErrorCode err = mono_de_ss_create (THREAD_TO_INTERNAL (mono_thread_current ()), size, depth, filter, req);
379 if (err != DE_ERR_NONE) {
380 DEBUG_PRINTF (1, "[dbg] Failed to setup single step request");
382 printf ("ss is in place, now ahat?\n");
385 EMSCRIPTEN_KEEPALIVE void
386 mono_wasm_clear_all_breakpoints (void)
388 DEBUG_PRINTF (1, "CLEAR BREAKPOINTS\n");
389 mono_de_clear_all_breakpoints ();
392 EMSCRIPTEN_KEEPALIVE int
393 mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset)
395 int i;
396 ERROR_DECL (error);
397 DEBUG_PRINTF (1, "SET BREAKPOINT: assembly %s method %x offset %x\n", assembly_name, method_token, il_offset);
400 //we get 'foo.dll' but mono_assembly_load expects 'foo' so we strip the last dot
401 char *lookup_name = g_strdup (assembly_name);
402 for (i = strlen (lookup_name) - 1; i >= 0; --i) {
403 if (lookup_name [i] == '.') {
404 lookup_name [i] = 0;
405 break;
409 //resolve the assembly
410 MonoImageOpenStatus status;
411 MonoAssemblyName* aname = mono_assembly_name_new (lookup_name);
412 MonoAssembly *assembly = mono_assembly_load (aname, NULL, &status);
413 g_free (lookup_name);
414 if (!assembly) {
415 DEBUG_PRINTF (1, "Could not resolve assembly %s\n", assembly_name);
416 return -1;
419 mono_assembly_name_free (aname);
421 MonoMethod *method = mono_get_method_checked (assembly->image, MONO_TOKEN_METHOD_DEF | method_token, NULL, NULL, error);
422 if (!method) {
423 //FIXME don't swallow the error
424 DEBUG_PRINTF (1, "Could not find method due to %s\n", mono_error_get_message (error));
425 mono_error_cleanup (error);
426 return -1;
429 //FIXME right now none of the EventRequest fields are used by debugger-engine
430 EventRequest *req = g_new0 (EventRequest, 1);
431 req->id = ++event_request_id;
432 req->event_kind = EVENT_KIND_BREAKPOINT;
433 //DE doesn't care about suspend_policy
434 // req->suspend_policy = SUSPEND_POLICY_ALL;
435 req->nmodifiers = 0; //funny thing,
437 // BreakPointRequest *req = breakpoint_request_new (assembly, method, il_offset);
438 MonoBreakpoint *bp = mono_de_set_breakpoint (method, il_offset, req, error);
440 if (!bp) {
441 DEBUG_PRINTF (1, "Could not set breakpoint to %s\n", mono_error_get_message (error));
442 mono_error_cleanup (error);
443 return 0;
446 DEBUG_PRINTF (1, "NEW BP %p has id %d\n", req, req->id);
447 return req->id;
450 EMSCRIPTEN_KEEPALIVE int
451 mono_wasm_remove_breakpoint (int bp_id)
453 MonoBreakpoint *bp = mono_de_get_breakpoint_by_id (bp_id);
454 if (!bp)
455 return 0;
457 mono_de_clear_breakpoint (bp);
458 return 1;
461 void
462 mono_wasm_single_step_hit (void)
464 mono_de_process_single_step (NULL, FALSE);
467 void
468 mono_wasm_breakpoint_hit (void)
470 mono_de_process_breakpoint (NULL, FALSE);
471 // mono_wasm_fire_bp ();
474 EMSCRIPTEN_KEEPALIVE int
475 mono_wasm_current_bp_id (void)
477 DEBUG_PRINTF (1, "COMPUTING breapoint ID\n");
478 //FIXME handle compiled case
480 /* Interpreter */
481 MonoLMF *lmf = mono_get_lmf ();
483 g_assert (((guint64)lmf->previous_lmf) & 2);
484 MonoLMFExt *ext = (MonoLMFExt*)lmf;
486 g_assert (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX);
487 MonoInterpFrameHandle *frame = (MonoInterpFrameHandle*)ext->interp_exit_data;
488 MonoJitInfo *ji = mini_get_interp_callbacks ()->frame_get_jit_info (frame);
489 guint8 *ip = (guint8*)mini_get_interp_callbacks ()->frame_get_ip (frame);
491 g_assert (ji && !ji->is_trampoline);
492 MonoMethod *method = jinfo_get_method (ji);
494 /* Compute the native offset of the breakpoint from the ip */
495 guint32 native_offset = ip - (guint8*)ji->code_start;
497 MonoSeqPointInfo *info = NULL;
498 SeqPoint sp;
499 gboolean found_sp = mono_find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info, &sp);
500 if (!found_sp)
501 DEBUG_PRINTF (1, "Could not find SP\n");
504 GPtrArray *bp_reqs = g_ptr_array_new ();
505 mono_de_collect_breakpoints_by_sp (&sp, ji, NULL, bp_reqs);
507 if (bp_reqs->len == 0) {
508 DEBUG_PRINTF (1, "BP NOT FOUND for method %s JI %p il_offset %d\n", method->name, ji, sp.il_offset);
509 return -1;
512 if (bp_reqs->len > 1)
513 DEBUG_PRINTF (1, "Multiple breakpoints (%d) at the same location, returning the first one.", bp_reqs->len);
515 EventRequest *evt = (EventRequest *)g_ptr_array_index (bp_reqs, 0);
516 g_ptr_array_free (bp_reqs, TRUE);
518 DEBUG_PRINTF (1, "Found BP %p with id %d\n", evt, evt->id);
519 return evt->id;
522 static int get_object_id(MonoObject *obj)
524 ObjRef *ref = (ObjRef *)g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
525 if (ref)
526 return ref->id;
527 ref = g_new0 (ObjRef, 1);
528 ref->id = mono_atomic_inc_i32 (&objref_id);
529 ref->handle = mono_gchandle_new_weakref_internal (obj, FALSE);
530 g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref);
531 g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
532 return ref->id;
535 static gboolean
536 list_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data)
538 SeqPoint sp;
539 MonoMethod *method;
541 //skip wrappers
542 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP)
543 return FALSE;
546 if (info->ji)
547 method = jinfo_get_method (info->ji);
548 else
549 method = info->method;
551 if (!method)
552 return FALSE;
554 DEBUG_PRINTF (2, "Reporting method %s native_offset %d\n", method->name, info->native_offset);
556 if (!mono_find_prev_seq_point_for_native_offset (mono_get_root_domain (), method, info->native_offset, NULL, &sp))
557 DEBUG_PRINTF (1, "Failed to lookup sequence point\n");
559 while (method->is_inflated)
560 method = ((MonoMethodInflated*)method)->declaring;
562 char *assembly_name = g_strdup (m_class_get_image (method->klass)->module_name);
563 inplace_tolower (assembly_name);
565 if (method->wrapper_type == MONO_WRAPPER_NONE) {
566 DEBUG_PRINTF (2, "adding off %d token %d assembly name %s\n", sp.il_offset, mono_metadata_token_index (method->token), assembly_name);
567 mono_wasm_add_frame (sp.il_offset, mono_metadata_token_index (method->token), assembly_name);
570 g_free (assembly_name);
572 return FALSE;
575 EMSCRIPTEN_KEEPALIVE void
576 mono_wasm_enum_frames (void)
578 mono_walk_stack_with_ctx (list_frames, NULL, MONO_UNWIND_NONE, NULL);
581 typedef struct {
582 int cur_frame;
583 int target_frame;
584 int variable;
585 } FrameDescData;
587 static gboolean describe_value(MonoType * type, gpointer addr)
589 ERROR_DECL (error);
590 switch (type->type) {
591 case MONO_TYPE_BOOLEAN:
592 mono_wasm_add_bool_var (*(gint8*)addr);
593 break;
594 case MONO_TYPE_I1:
595 case MONO_TYPE_U1:
596 mono_wasm_add_int_var (*(gint8*)addr);
597 break;
598 case MONO_TYPE_CHAR:
599 case MONO_TYPE_I2:
600 case MONO_TYPE_U2:
601 mono_wasm_add_int_var (*(gint16*)addr);
602 break;
603 case MONO_TYPE_I4:
604 case MONO_TYPE_U4:
605 case MONO_TYPE_I:
606 case MONO_TYPE_U:
607 mono_wasm_add_int_var (*(gint32*)addr);
608 break;
609 case MONO_TYPE_I8:
610 case MONO_TYPE_U8:
611 mono_wasm_add_long_var (*(gint32*)addr);
612 break;
613 case MONO_TYPE_R4:
614 mono_wasm_add_float_var (*(float*)addr);
615 break;
616 case MONO_TYPE_R8:
617 mono_wasm_add_float_var (*(double*)addr);
618 break;
619 case MONO_TYPE_STRING: {
620 MonoString *str_obj = *(MonoString **)addr;
621 if (!str_obj) {
622 mono_wasm_add_string_var (NULL);
623 } else {
624 char *str = mono_string_to_utf8_checked_internal (str_obj, error);
625 mono_error_assert_ok (error); /* FIXME report error */
626 mono_wasm_add_string_var (str);
627 g_free (str);
629 break;
631 case MONO_TYPE_GENERICINST:
632 case MONO_TYPE_SZARRAY:
633 case MONO_TYPE_ARRAY:
634 case MONO_TYPE_OBJECT:
635 case MONO_TYPE_CLASS: {
636 MonoObject *obj = *(MonoObject**)addr;
637 if (!obj) {
638 mono_wasm_add_string_var (NULL);
639 } else {
640 GString *class_name;
641 class_name = g_string_new ("");
642 if (*(obj->vtable->klass->name_space)) {
643 g_string_append (class_name, obj->vtable->klass->name_space);
644 g_string_append_c (class_name, '.');
646 g_string_append (class_name, obj->vtable->klass->name);
647 if (m_class_get_byval_arg (obj->vtable->klass)->type == MONO_TYPE_SZARRAY || m_class_get_byval_arg (obj->vtable->klass)->type == MONO_TYPE_ARRAY)
648 mono_wasm_add_array_var (class_name->str, get_object_id(obj));
649 else
650 mono_wasm_add_obj_var (class_name->str, get_object_id(obj));
651 g_string_free(class_name, FALSE);
652 break;
654 break;
656 default: {
657 char *type_name = mono_type_full_name (type);
658 char *msg = g_strdup_printf("can't handle type %s [%p, %x]", type_name, type, type->type);
659 mono_wasm_add_string_var (msg);
660 g_free (msg);
661 g_free (type_name);
664 return TRUE;
667 static gboolean
668 describe_object_properties (guint64 objectId)
670 MonoClassField *f;
671 MonoProperty *p;
672 MonoObject *exc;
673 MonoObject *res;
674 MonoMethodSignature *sig;
675 gpointer iter = NULL;
676 ERROR_DECL (error);
677 DEBUG_PRINTF (2, "describe_object_properties %d\n", objectId);
678 ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
679 if (!ref) {
680 DEBUG_PRINTF (2, "describe_object_properties !ref\n");
681 return FALSE;
684 MonoObject *obj = mono_gchandle_get_target_internal (ref->handle);
685 if (!obj) {
686 DEBUG_PRINTF (2, "describe_object_properties !obj\n");
687 return FALSE;
690 while (obj && (f = mono_class_get_fields_internal (obj->vtable->klass, &iter))) {
691 DEBUG_PRINTF (2, "mono_class_get_fields_internal - %s - %x\n", f->name, f->type->type);
692 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
693 continue;
694 if (mono_field_is_deleted (f))
695 continue;
696 mono_wasm_add_properties_var(f->name);
697 gpointer field_value = (guint8*)obj + f->offset;
699 describe_value(f->type, field_value);
702 iter = NULL;
703 while ((p = mono_class_get_properties (obj->vtable->klass, &iter))) {
704 DEBUG_PRINTF (2, "mono_class_get_properties - %s - %s\n", p->name, p->get->name);
705 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
706 mono_wasm_add_properties_var(p->name);
707 sig = mono_method_signature_internal (p->get);
708 res = mono_runtime_try_invoke (p->get, obj, NULL, &exc, error);
709 if (!mono_error_ok (error) && exc == NULL)
710 exc = (MonoObject*) mono_error_convert_to_exception (error);
711 if (exc)
712 describe_value (mono_get_object_type (), &exc);
713 else if (!m_class_is_valuetype (mono_object_class (res)))
714 describe_value(sig->ret, &res);
715 else
716 describe_value(sig->ret, mono_object_unbox_internal (res));
719 return TRUE;
723 static gboolean
724 describe_array_values (guint64 objectId)
726 int esize;
727 gpointer elem;
728 ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
729 if (!ref) {
730 return FALSE;
732 MonoArray *arr = (MonoArray *)mono_gchandle_get_target_internal (ref->handle);
733 MonoObject *obj = &arr->obj;
734 if (!obj) {
735 return FALSE;
737 esize = mono_array_element_size (obj->vtable->klass);
738 for (int i = 0; i < arr->max_length; i++) {
739 mono_wasm_add_array_item(i);
740 elem = (gpointer*)((char*)arr->vector + (i * esize));
741 describe_value(m_class_get_byval_arg (m_class_get_element_class (arr->obj.vtable->klass)), elem);
743 return TRUE;
746 static gboolean
747 describe_variable (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud)
749 ERROR_DECL (error);
750 MonoMethodHeader *header = NULL;
752 FrameDescData *data = (FrameDescData*)ud;
754 //skip wrappers
755 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) {
756 return FALSE;
759 if (data->cur_frame < data->target_frame) {
760 ++data->cur_frame;
761 return FALSE;
764 InterpFrame *frame = (InterpFrame*)info->interp_frame;
765 g_assert (frame);
766 MonoMethod *method = frame->imethod->method;
767 g_assert (method);
769 MonoType *type = NULL;
770 gpointer addr = NULL;
771 int pos = data->variable;
772 if (pos < 0) {
773 pos = -pos - 1;
774 type = mono_method_signature_internal (method)->params [pos];
775 addr = mini_get_interp_callbacks ()->frame_get_arg (frame, pos);
776 } else {
777 header = mono_method_get_header_checked (method, error);
778 mono_error_assert_ok (error); /* FIXME report error */
780 type = header->locals [pos];
781 addr = mini_get_interp_callbacks ()->frame_get_local (frame, pos);
784 DEBUG_PRINTF (2, "adding val %p type [%p] %s\n", addr, type, mono_type_full_name (type));
786 describe_value(type, addr);
787 if (header)
788 mono_metadata_free_mh (header);
790 return TRUE;
793 //FIXME this doesn't support getting the return value pseudo-var
794 EMSCRIPTEN_KEEPALIVE void
795 mono_wasm_get_var_info (int scope, int pos)
797 DEBUG_PRINTF (2, "getting var %d of scope %d\n", pos, scope);
799 FrameDescData data;
800 data.cur_frame = 0;
801 data.target_frame = scope;
802 data.variable = pos;
804 mono_walk_stack_with_ctx (describe_variable, NULL, MONO_UNWIND_NONE, &data);
807 EMSCRIPTEN_KEEPALIVE void
808 mono_wasm_get_object_properties (int object_id)
810 DEBUG_PRINTF (2, "getting properties of object %d\n", object_id);
812 describe_object_properties(object_id);
815 EMSCRIPTEN_KEEPALIVE void
816 mono_wasm_get_array_values (int object_id)
818 DEBUG_PRINTF (2, "getting array values %d\n", object_id);
820 describe_array_values(object_id);
823 // Functions required by debugger-state-machine.
824 gsize
825 mono_debugger_tls_thread_id (DebuggerTlsData *debuggerTlsData)
827 return 1;
830 #else // HOST_WASM
832 void
833 mono_wasm_single_step_hit (void)
837 void
838 mono_wasm_breakpoint_hit (void)
842 void
843 mono_wasm_debugger_init (void)
847 #endif // HOST_WASM