[interp] Fix DEBUG_INTERP build (#16057)
[mono-project.git] / mono / mini / mini-wasm-debugger.c
blobcc8e147f00674a1c7e6382dc7e43b0ce4bd74001
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, int len);
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_number_var (double);
41 extern void mono_wasm_add_string_var (const char*);
42 extern void mono_wasm_add_obj_var (const char*, guint64);
43 extern void mono_wasm_add_array_var (const char*, guint64);
44 extern void mono_wasm_add_properties_var (const char*);
45 extern void mono_wasm_add_array_item (int);
47 G_END_DECLS
49 //FIXME move all of those fields to the profiler object
50 static gboolean debugger_enabled;
52 static int event_request_id;
53 static GHashTable *objrefs;
54 static GHashTable *obj_to_objref;
55 static int objref_id = 0;
57 #define THREAD_TO_INTERNAL(thread) (thread)->internal_thread
59 static void
60 inplace_tolower (char *c)
62 int i;
63 for (i = strlen (c) - 1; i >= 0; --i)
64 c [i] = tolower (c [i]);
67 static void
68 jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo)
70 mono_de_add_pending_breakpoints (method, jinfo);
73 static void
74 appdomain_load (MonoProfiler *prof, MonoDomain *domain)
76 mono_de_domain_add (domain);
79 /* Frame state handling */
80 static GPtrArray *frames;
82 static void
83 free_frame (DbgEngineStackFrame *frame)
85 g_free (frame);
88 static gboolean
89 collect_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data)
91 SeqPoint sp;
92 MonoMethod *method;
94 //skip wrappers
95 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP)
96 return FALSE;
98 if (info->ji)
99 method = jinfo_get_method (info->ji);
100 else
101 method = info->method;
103 if (!method)
104 return FALSE;
106 DEBUG_PRINTF (2, "Reporting method %s native_offset %d\n", method->name, info->native_offset);
108 if (!mono_find_prev_seq_point_for_native_offset (mono_get_root_domain (), method, info->native_offset, NULL, &sp))
109 DEBUG_PRINTF (1, "Failed to lookup sequence point\n");
111 DbgEngineStackFrame *frame = g_new0 (DbgEngineStackFrame, 1);
113 frame->ji = info->ji;
114 frame->domain = info->domain;
115 frame->method = method;
116 frame->native_offset = info->native_offset;
118 g_ptr_array_add (frames, frame);
120 return FALSE;
123 static void
124 free_frame_state (void)
126 if (frames) {
127 int i;
128 for (i = 0; i < frames->len; ++i)
129 free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i));
130 g_ptr_array_set_size (frames, 0);
134 static void
135 compute_frames (void) {
136 if (frames) {
137 int i;
138 for (i = 0; i < frames->len; ++i)
139 free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i));
140 g_ptr_array_set_size (frames, 0);
141 } else {
142 frames = g_ptr_array_new ();
145 mono_walk_stack_with_ctx (collect_frames, NULL, MONO_UNWIND_NONE, NULL);
147 static MonoContext*
148 tls_get_restore_state (void *tls)
150 return NULL;
153 static gboolean
154 try_process_suspend (void *tls, MonoContext *ctx)
156 return FALSE;
159 static gboolean
160 begin_breakpoint_processing (void *tls, MonoContext *ctx, MonoJitInfo *ji, gboolean from_signal)
162 return TRUE;
165 static void
166 begin_single_step_processing (MonoContext *ctx, gboolean from_signal)
170 static void
171 ss_discard_frame_context (void *the_tls)
173 free_frame_state ();
176 static void
177 ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***out_frames, int *nframes)
179 compute_frames ();
180 if (out_frames)
181 *out_frames = (DbgEngineStackFrame **)frames->pdata;
182 if (nframes)
183 *nframes = frames->len;
186 static gboolean
187 ensure_jit (DbgEngineStackFrame* the_frame)
189 return TRUE;
192 static int
193 ensure_runtime_is_suspended (void)
195 return DE_ERR_NONE;
198 static int
199 get_this_async_id (DbgEngineStackFrame *f)
201 g_error ("get_this_async_id");
202 return 0;
205 static gboolean
206 set_set_notification_for_wait_completion_flag (DbgEngineStackFrame *f)
208 g_error ("set_set_notification_for_wait_completion_flag");
209 return FALSE;
212 static MonoMethod*
213 get_notify_debugger_of_wait_completion_method (void)
215 g_error ("get_notify_debugger_of_wait_completion_method");
218 typedef struct {
219 gboolean is_ss; //do I need this?
220 } BpEvents;
222 static void*
223 create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind)
225 printf ("ss_reqs %d bp_reqs %d\n", ss_reqs->len, bp_reqs->len);
226 if ((ss_reqs && ss_reqs->len) || (bp_reqs && bp_reqs->len)) {
227 BpEvents *evts = g_new0 (BpEvents, 1); //just a non-null value to make sure we can raise it on process_breakpoint_events
228 evts->is_ss = (ss_reqs && ss_reqs->len);
229 return evts;
231 return NULL;
234 static void
235 process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offsets)
237 BpEvents *evts = (BpEvents*)_evts;
238 if (evts) {
239 if (evts->is_ss)
240 mono_de_cancel_ss ();
241 mono_wasm_fire_bp ();
242 g_free (evts);
246 static void
247 no_seq_points_found (MonoMethod *method, int offset)
250 * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space.
252 printf ("Unable to find seq points for method '%s', offset 0x%x.\n", mono_method_full_name (method, TRUE), offset);
255 #define DBG_NOT_SUSPENDED 1
257 static int
258 ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *ss_args)
260 printf ("ss_create_init_args\n");
261 int dummy = 0;
262 ss_req->start_sp = ss_req->last_sp = &dummy;
263 compute_frames ();
264 memset (ss_args, 0, sizeof (*ss_args));
266 //BIG WTF, should not happen maybe should assert?
267 if (frames->len == 0) {
268 DEBUG_PRINTF (1, "SINGLE STEPPING FOUND NO FRAMES");
269 return DBG_NOT_SUSPENDED;
272 DbgEngineStackFrame *frame = (DbgEngineStackFrame*)g_ptr_array_index (frames, 0);
273 ss_req->start_method = ss_args->method = frame->method;
274 gboolean found_sp = mono_find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &ss_args->info, &ss_args->sp);
275 if (!found_sp)
276 no_seq_points_found (frame->method, frame->native_offset);
277 g_assert (found_sp);
279 ss_args->frames = (DbgEngineStackFrame**)frames->pdata;
280 ss_args->nframes = frames->len;
281 //XXX do sp
283 return DE_ERR_NONE;
286 static void
287 ss_args_destroy (SingleStepArgs *ss_args)
289 //nothing to do
292 void
293 mono_wasm_debugger_init (void)
295 if (!debugger_enabled)
296 return;
298 DebuggerEngineCallbacks cbs = {
299 .tls_get_restore_state = tls_get_restore_state,
300 .try_process_suspend = try_process_suspend,
301 .begin_breakpoint_processing = begin_breakpoint_processing,
302 .begin_single_step_processing = begin_single_step_processing,
303 .ss_discard_frame_context = ss_discard_frame_context,
304 .ss_calculate_framecount = ss_calculate_framecount,
305 .ensure_jit = ensure_jit,
306 .ensure_runtime_is_suspended = ensure_runtime_is_suspended,
307 .get_this_async_id = get_this_async_id,
308 .set_set_notification_for_wait_completion_flag = set_set_notification_for_wait_completion_flag,
309 .get_notify_debugger_of_wait_completion_method = get_notify_debugger_of_wait_completion_method,
310 .create_breakpoint_events = create_breakpoint_events,
311 .process_breakpoint_events = process_breakpoint_events,
312 .ss_create_init_args = ss_create_init_args,
313 .ss_args_destroy = ss_args_destroy,
316 mono_debug_init (MONO_DEBUG_FORMAT_MONO);
317 mono_de_init (&cbs);
318 mono_de_set_log_level (1, stdout);
320 mini_debug_options.gen_sdb_seq_points = TRUE;
321 mini_debug_options.mdb_optimizations = TRUE;
322 mono_disable_optimizations (MONO_OPT_LINEARS);
323 mini_debug_options.load_aot_jit_info_eagerly = TRUE;
325 MonoProfilerHandle prof = mono_profiler_create (NULL);
326 mono_profiler_set_jit_done_callback (prof, jit_done);
327 //FIXME support multiple appdomains
328 mono_profiler_set_domain_loaded_callback (prof, appdomain_load);
330 obj_to_objref = g_hash_table_new (NULL, NULL);
331 objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref);
334 MONO_API void
335 mono_wasm_enable_debugging (void)
337 DEBUG_PRINTF (1, "DEBUGGING ENABLED\n");
338 debugger_enabled = TRUE;
341 EMSCRIPTEN_KEEPALIVE void
342 mono_wasm_setup_single_step (int kind)
344 int nmodifiers = 1;
346 printf (">>>> mono_wasm_setup_single_step %d\n", kind);
347 EventRequest *req = (EventRequest *)g_malloc0 (sizeof (EventRequest) + (nmodifiers * sizeof (Modifier)));
348 req->id = ++event_request_id;
349 req->event_kind = EVENT_KIND_STEP;
350 // DE doesn't care about suspend_policy
351 // req->suspend_policy = SUSPEND_POLICY_ALL;
352 req->nmodifiers = nmodifiers;
354 StepSize size = STEP_SIZE_MIN;
356 //FIXME I DON'T KNOW WHAT I'M DOING!!!!! filter all the things.
357 StepFilter filter = (StepFilter)(STEP_FILTER_STATIC_CTOR | STEP_FILTER_DEBUGGER_HIDDEN | STEP_FILTER_DEBUGGER_STEP_THROUGH | STEP_FILTER_DEBUGGER_NON_USER_CODE);
358 req->modifiers [0].data.filter = filter;
360 StepDepth depth;
361 switch (kind) {
362 case 0: //into
363 depth = STEP_DEPTH_INTO;
364 break;
365 case 1: //out
366 depth = STEP_DEPTH_OUT;
367 break;
368 case 2: //over
369 depth = STEP_DEPTH_OVER;
370 break;
371 default:
372 g_error ("dunno step kind %d", kind);
375 DbgEngineErrorCode err = mono_de_ss_create (THREAD_TO_INTERNAL (mono_thread_current ()), size, depth, filter, req);
376 if (err != DE_ERR_NONE) {
377 DEBUG_PRINTF (1, "[dbg] Failed to setup single step request");
379 printf ("ss is in place, now ahat?\n");
382 EMSCRIPTEN_KEEPALIVE void
383 mono_wasm_clear_all_breakpoints (void)
385 DEBUG_PRINTF (1, "CLEAR BREAKPOINTS\n");
386 mono_de_clear_all_breakpoints ();
389 EMSCRIPTEN_KEEPALIVE int
390 mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset)
392 int i;
393 ERROR_DECL (error);
394 DEBUG_PRINTF (1, "SET BREAKPOINT: assembly %s method %x offset %x\n", assembly_name, method_token, il_offset);
397 //we get 'foo.dll' but mono_assembly_load expects 'foo' so we strip the last dot
398 char *lookup_name = g_strdup (assembly_name);
399 for (i = strlen (lookup_name) - 1; i >= 0; --i) {
400 if (lookup_name [i] == '.') {
401 lookup_name [i] = 0;
402 break;
406 //resolve the assembly
407 MonoImageOpenStatus status;
408 MonoAssemblyName* aname = mono_assembly_name_new (lookup_name);
409 MonoAssembly *assembly = mono_assembly_load (aname, NULL, &status);
410 g_free (lookup_name);
411 if (!assembly) {
412 DEBUG_PRINTF (1, "Could not resolve assembly %s\n", assembly_name);
413 return -1;
416 mono_assembly_name_free (aname);
418 MonoMethod *method = mono_get_method_checked (assembly->image, MONO_TOKEN_METHOD_DEF | method_token, NULL, NULL, error);
419 if (!method) {
420 //FIXME don't swallow the error
421 DEBUG_PRINTF (1, "Could not find method due to %s\n", mono_error_get_message (error));
422 mono_error_cleanup (error);
423 return -1;
426 //FIXME right now none of the EventRequest fields are used by debugger-engine
427 EventRequest *req = g_new0 (EventRequest, 1);
428 req->id = ++event_request_id;
429 req->event_kind = EVENT_KIND_BREAKPOINT;
430 //DE doesn't care about suspend_policy
431 // req->suspend_policy = SUSPEND_POLICY_ALL;
432 req->nmodifiers = 0; //funny thing,
434 // BreakPointRequest *req = breakpoint_request_new (assembly, method, il_offset);
435 MonoBreakpoint *bp = mono_de_set_breakpoint (method, il_offset, req, error);
437 if (!bp) {
438 DEBUG_PRINTF (1, "Could not set breakpoint to %s\n", mono_error_get_message (error));
439 mono_error_cleanup (error);
440 return 0;
443 DEBUG_PRINTF (1, "NEW BP %p has id %d\n", req, req->id);
444 return req->id;
447 EMSCRIPTEN_KEEPALIVE int
448 mono_wasm_remove_breakpoint (int bp_id)
450 MonoBreakpoint *bp = mono_de_get_breakpoint_by_id (bp_id);
451 if (!bp)
452 return 0;
454 mono_de_clear_breakpoint (bp);
455 return 1;
458 void
459 mono_wasm_single_step_hit (void)
461 mono_de_process_single_step (NULL, FALSE);
464 void
465 mono_wasm_breakpoint_hit (void)
467 mono_de_process_breakpoint (NULL, FALSE);
468 // mono_wasm_fire_bp ();
471 EMSCRIPTEN_KEEPALIVE int
472 mono_wasm_current_bp_id (void)
474 DEBUG_PRINTF (1, "COMPUTING breapoint ID\n");
475 //FIXME handle compiled case
477 /* Interpreter */
478 MonoLMF *lmf = mono_get_lmf ();
480 g_assert (((guint64)lmf->previous_lmf) & 2);
481 MonoLMFExt *ext = (MonoLMFExt*)lmf;
483 g_assert (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX);
484 MonoInterpFrameHandle *frame = (MonoInterpFrameHandle*)ext->interp_exit_data;
485 MonoJitInfo *ji = mini_get_interp_callbacks ()->frame_get_jit_info (frame);
486 guint8 *ip = (guint8*)mini_get_interp_callbacks ()->frame_get_ip (frame);
488 g_assert (ji && !ji->is_trampoline);
489 MonoMethod *method = jinfo_get_method (ji);
491 /* Compute the native offset of the breakpoint from the ip */
492 guint32 native_offset = ip - (guint8*)ji->code_start;
494 MonoSeqPointInfo *info = NULL;
495 SeqPoint sp;
496 gboolean found_sp = mono_find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info, &sp);
497 if (!found_sp)
498 DEBUG_PRINTF (1, "Could not find SP\n");
501 GPtrArray *bp_reqs = g_ptr_array_new ();
502 mono_de_collect_breakpoints_by_sp (&sp, ji, NULL, bp_reqs);
504 if (bp_reqs->len == 0) {
505 DEBUG_PRINTF (1, "BP NOT FOUND for method %s JI %p il_offset %d\n", method->name, ji, sp.il_offset);
506 return -1;
509 if (bp_reqs->len > 1)
510 DEBUG_PRINTF (1, "Multiple breakpoints (%d) at the same location, returning the first one.", bp_reqs->len);
512 EventRequest *evt = (EventRequest *)g_ptr_array_index (bp_reqs, 0);
513 g_ptr_array_free (bp_reqs, TRUE);
515 DEBUG_PRINTF (1, "Found BP %p with id %d\n", evt, evt->id);
516 return evt->id;
519 static int get_object_id(MonoObject *obj)
521 ObjRef *ref = (ObjRef *)g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
522 if (ref)
523 return ref->id;
524 ref = g_new0 (ObjRef, 1);
525 ref->id = mono_atomic_inc_i32 (&objref_id);
526 ref->handle = mono_gchandle_new_weakref_internal (obj, FALSE);
527 g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref);
528 g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
529 return ref->id;
532 static gboolean
533 list_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data)
535 SeqPoint sp;
536 MonoMethod *method;
538 //skip wrappers
539 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP)
540 return FALSE;
543 if (info->ji)
544 method = jinfo_get_method (info->ji);
545 else
546 method = info->method;
548 if (!method)
549 return FALSE;
551 DEBUG_PRINTF (2, "Reporting method %s native_offset %d\n", method->name, info->native_offset);
553 if (!mono_find_prev_seq_point_for_native_offset (mono_get_root_domain (), method, info->native_offset, NULL, &sp))
554 DEBUG_PRINTF (1, "Failed to lookup sequence point\n");
556 while (method->is_inflated)
557 method = ((MonoMethodInflated*)method)->declaring;
559 char *assembly_name = g_strdup (m_class_get_image (method->klass)->module_name);
560 inplace_tolower (assembly_name);
562 if (method->wrapper_type == MONO_WRAPPER_NONE) {
563 DEBUG_PRINTF (2, "adding off %d token %d assembly name %s\n", sp.il_offset, mono_metadata_token_index (method->token), assembly_name);
564 mono_wasm_add_frame (sp.il_offset, mono_metadata_token_index (method->token), assembly_name);
567 g_free (assembly_name);
569 return FALSE;
572 EMSCRIPTEN_KEEPALIVE void
573 mono_wasm_enum_frames (void)
575 mono_walk_stack_with_ctx (list_frames, NULL, MONO_UNWIND_NONE, NULL);
578 typedef struct {
579 int cur_frame;
580 int target_frame;
581 int variable;
582 } FrameDescData;
584 static gboolean describe_value(MonoType * type, gpointer addr)
586 ERROR_DECL (error);
587 switch (type->type) {
588 case MONO_TYPE_BOOLEAN:
589 mono_wasm_add_bool_var (*(gint8*)addr);
590 break;
591 case MONO_TYPE_I1:
592 mono_wasm_add_number_var (*(gint8*)addr);
593 break;
594 case MONO_TYPE_U1:
595 mono_wasm_add_number_var (*(guint8*)addr);
596 break;
597 case MONO_TYPE_CHAR:
598 case MONO_TYPE_U2:
599 mono_wasm_add_number_var (*(guint16*)addr);
600 break;
601 case MONO_TYPE_I2:
602 mono_wasm_add_number_var (*(gint16*)addr);
603 break;
604 case MONO_TYPE_I4:
605 case MONO_TYPE_I:
606 mono_wasm_add_number_var (*(gint32*)addr);
607 break;
608 case MONO_TYPE_U4:
609 case MONO_TYPE_U:
610 mono_wasm_add_number_var (*(guint32*)addr);
611 break;
612 case MONO_TYPE_I8:
613 mono_wasm_add_number_var (*(gint64*)addr);
614 break;
615 case MONO_TYPE_U8:
616 mono_wasm_add_number_var (*(guint64*)addr);
617 break;
618 case MONO_TYPE_R4:
619 mono_wasm_add_number_var (*(float*)addr);
620 break;
621 case MONO_TYPE_R8:
622 mono_wasm_add_number_var (*(double*)addr);
623 break;
624 case MONO_TYPE_STRING: {
625 MonoString *str_obj = *(MonoString **)addr;
626 if (!str_obj) {
627 mono_wasm_add_string_var (NULL);
628 } else {
629 char *str = mono_string_to_utf8_checked_internal (str_obj, error);
630 mono_error_assert_ok (error); /* FIXME report error */
631 mono_wasm_add_string_var (str);
632 g_free (str);
634 break;
636 case MONO_TYPE_GENERICINST:
637 case MONO_TYPE_SZARRAY:
638 case MONO_TYPE_ARRAY:
639 case MONO_TYPE_OBJECT:
640 case MONO_TYPE_CLASS: {
641 MonoObject *obj = *(MonoObject**)addr;
642 if (!obj) {
643 mono_wasm_add_string_var (NULL);
644 } else {
645 GString *class_name;
646 class_name = g_string_new ("");
647 if (*(obj->vtable->klass->name_space)) {
648 g_string_append (class_name, obj->vtable->klass->name_space);
649 g_string_append_c (class_name, '.');
651 g_string_append (class_name, obj->vtable->klass->name);
652 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)
653 mono_wasm_add_array_var (class_name->str, get_object_id(obj));
654 else
655 mono_wasm_add_obj_var (class_name->str, get_object_id(obj));
656 g_string_free(class_name, FALSE);
657 break;
659 break;
661 default: {
662 char *type_name = mono_type_full_name (type);
663 char *msg = g_strdup_printf("can't handle type %s [%p, %x]", type_name, type, type->type);
664 mono_wasm_add_string_var (msg);
665 g_free (msg);
666 g_free (type_name);
669 return TRUE;
672 static gboolean
673 describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis)
675 MonoClassField *f;
676 MonoProperty *p;
677 MonoObject *exc;
678 MonoObject *res;
679 MonoMethodSignature *sig;
680 gpointer iter = NULL;
681 ERROR_DECL (error);
682 DEBUG_PRINTF (2, "describe_object_properties %d\n", objectId);
683 ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
684 if (!ref) {
685 DEBUG_PRINTF (2, "describe_object_properties !ref\n");
686 return FALSE;
689 MonoObject *obj = mono_gchandle_get_target_internal (ref->handle);
690 if (!obj) {
691 DEBUG_PRINTF (2, "describe_object_properties !obj\n");
692 return FALSE;
695 while (obj && (f = mono_class_get_fields_internal (obj->vtable->klass, &iter))) {
696 DEBUG_PRINTF (2, "mono_class_get_fields_internal - %s - %x\n", f->name, f->type->type);
697 if (isAsyncLocalThis && (f->name[0] != '<' || (f->name[0] == '<' && f->name[1] == '>'))) {
698 continue;
700 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
701 continue;
702 if (mono_field_is_deleted (f))
703 continue;
704 mono_wasm_add_properties_var(f->name);
705 gpointer field_value = (guint8*)obj + f->offset;
707 describe_value(f->type, field_value);
710 iter = NULL;
711 while ((p = mono_class_get_properties (obj->vtable->klass, &iter))) {
712 DEBUG_PRINTF (2, "mono_class_get_properties - %s - %s\n", p->name, p->get->name);
713 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
714 if (isAsyncLocalThis && (p->name[0] != '<' || (p->name[0] == '<' && p->name[1] == '>'))) {
715 continue;
717 mono_wasm_add_properties_var(p->name);
718 sig = mono_method_signature_internal (p->get);
719 res = mono_runtime_try_invoke (p->get, obj, NULL, &exc, error);
720 if (!mono_error_ok (error) && exc == NULL)
721 exc = (MonoObject*) mono_error_convert_to_exception (error);
722 if (exc)
723 describe_value (mono_get_object_type (), &exc);
724 else if (!m_class_is_valuetype (mono_object_class (res)))
725 describe_value(sig->ret, &res);
726 else
727 describe_value(sig->ret, mono_object_unbox_internal (res));
730 return TRUE;
734 static gboolean
735 describe_array_values (guint64 objectId)
737 int esize;
738 gpointer elem;
739 ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
740 if (!ref) {
741 return FALSE;
743 MonoArray *arr = (MonoArray *)mono_gchandle_get_target_internal (ref->handle);
744 MonoObject *obj = &arr->obj;
745 if (!obj) {
746 return FALSE;
748 esize = mono_array_element_size (obj->vtable->klass);
749 for (int i = 0; i < arr->max_length; i++) {
750 mono_wasm_add_array_item(i);
751 elem = (gpointer*)((char*)arr->vector + (i * esize));
752 describe_value(m_class_get_byval_arg (m_class_get_element_class (arr->obj.vtable->klass)), elem);
754 return TRUE;
757 static gboolean
758 describe_async_method_locals (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud)
760 //Async methods are special in the way that local variables can be lifted to generated class fields
761 FrameDescData *data = (FrameDescData*)ud;
763 //skip wrappers
764 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) {
765 return FALSE;
768 if (data->cur_frame < data->target_frame) {
769 ++data->cur_frame;
770 return FALSE;
773 InterpFrame *frame = (InterpFrame*)info->interp_frame;
774 g_assert (frame);
775 MonoMethod *method = frame->imethod->method;
776 g_assert (method);
777 gpointer addr = NULL;
778 if (mono_debug_lookup_method_async_debug_info (method)) {
779 addr = mini_get_interp_callbacks ()->frame_get_this (frame);
780 MonoObject *obj = *(MonoObject**)addr;
781 describe_object_properties(get_object_id(obj), TRUE);
783 return TRUE;
786 static gboolean
787 describe_this (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud)
789 //Async methods are special in the way that local variables can be lifted to generated class fields
790 FrameDescData *data = (FrameDescData*)ud;
792 //skip wrappers
793 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) {
794 return FALSE;
797 if (data->cur_frame < data->target_frame) {
798 ++data->cur_frame;
799 return FALSE;
802 InterpFrame *frame = (InterpFrame*)info->interp_frame;
803 g_assert (frame);
804 MonoMethod *method = frame->imethod->method;
805 g_assert (method);
806 gpointer addr = NULL;
807 if (mono_method_signature_internal (method)->hasthis) {
808 addr = mini_get_interp_callbacks ()->frame_get_this (frame);
809 MonoObject *obj = *(MonoObject**)addr;
810 mono_wasm_add_properties_var("this");
811 GString *class_name;
812 class_name = g_string_new ("");
813 if (*(obj->vtable->klass->name_space)) {
814 g_string_append (class_name, obj->vtable->klass->name_space);
815 g_string_append_c (class_name, '.');
817 g_string_append (class_name, obj->vtable->klass->name);
818 mono_wasm_add_obj_var (class_name->str, get_object_id(obj));
819 g_string_free(class_name, FALSE);
821 return TRUE;
825 static gboolean
826 describe_variable (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud)
828 ERROR_DECL (error);
829 MonoMethodHeader *header = NULL;
831 FrameDescData *data = (FrameDescData*)ud;
833 //skip wrappers
834 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) {
835 return FALSE;
838 if (data->cur_frame < data->target_frame) {
839 ++data->cur_frame;
840 return FALSE;
843 InterpFrame *frame = (InterpFrame*)info->interp_frame;
844 g_assert (frame);
845 MonoMethod *method = frame->imethod->method;
846 g_assert (method);
848 MonoType *type = NULL;
849 gpointer addr = NULL;
850 int pos = data->variable;
851 if (pos < 0) {
852 pos = -pos - 1;
853 type = mono_method_signature_internal (method)->params [pos];
854 addr = mini_get_interp_callbacks ()->frame_get_arg (frame, pos);
855 } else {
856 header = mono_method_get_header_checked (method, error);
857 mono_error_assert_ok (error); /* FIXME report error */
859 type = header->locals [pos];
860 addr = mini_get_interp_callbacks ()->frame_get_local (frame, pos);
863 DEBUG_PRINTF (2, "adding val %p type [%p] %s\n", addr, type, mono_type_full_name (type));
865 describe_value(type, addr);
866 if (header)
867 mono_metadata_free_mh (header);
869 return TRUE;
872 //FIXME this doesn't support getting the return value pseudo-var
873 EMSCRIPTEN_KEEPALIVE void
874 mono_wasm_get_var_info (int scope, int* pos, int len)
876 FrameDescData data;
877 data.cur_frame = 0;
878 data.target_frame = scope;
879 for (int i = 0; i < len; i++)
881 DEBUG_PRINTF (2, "getting var %d of scope %d - %d\n", pos[i], scope, len);
882 data.variable = pos[i];
883 mono_walk_stack_with_ctx (describe_variable, NULL, MONO_UNWIND_NONE, &data);
885 mono_walk_stack_with_ctx (describe_async_method_locals, NULL, MONO_UNWIND_NONE, &data);
886 mono_walk_stack_with_ctx (describe_this, NULL, MONO_UNWIND_NONE, &data);
889 EMSCRIPTEN_KEEPALIVE void
890 mono_wasm_get_object_properties (int object_id)
892 DEBUG_PRINTF (2, "getting properties of object %d\n", object_id);
894 describe_object_properties(object_id, FALSE);
897 EMSCRIPTEN_KEEPALIVE void
898 mono_wasm_get_array_values (int object_id)
900 DEBUG_PRINTF (2, "getting array values %d\n", object_id);
902 describe_array_values(object_id);
905 // Functions required by debugger-state-machine.
906 gsize
907 mono_debugger_tls_thread_id (DebuggerTlsData *debuggerTlsData)
909 return 1;
912 #else // HOST_WASM
914 void
915 mono_wasm_single_step_hit (void)
919 void
920 mono_wasm_breakpoint_hit (void)
924 void
925 mono_wasm_debugger_init (void)
929 #endif // HOST_WASM