[interp] Fix interp logging (#17636)
[mono-project.git] / mono / mini / mini-wasm-debugger.c
blob42554d29de6258384fb3b5282110b16179fdd1c7
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>
18 #include "mono/metadata/assembly-internals.h"
20 static int log_level = 1;
22 #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { fprintf (stdout, __VA_ARGS__); } } while (0)
24 //functions exported to be used by JS
25 G_BEGIN_DECLS
27 EMSCRIPTEN_KEEPALIVE int mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset);
28 EMSCRIPTEN_KEEPALIVE int mono_wasm_remove_breakpoint (int bp_id);
29 EMSCRIPTEN_KEEPALIVE int mono_wasm_current_bp_id (void);
30 EMSCRIPTEN_KEEPALIVE void mono_wasm_enum_frames (void);
31 EMSCRIPTEN_KEEPALIVE void mono_wasm_get_var_info (int scope, int* pos, int len);
32 EMSCRIPTEN_KEEPALIVE void mono_wasm_clear_all_breakpoints (void);
33 EMSCRIPTEN_KEEPALIVE void mono_wasm_setup_single_step (int kind);
34 EMSCRIPTEN_KEEPALIVE void mono_wasm_get_object_properties (int object_id);
35 EMSCRIPTEN_KEEPALIVE void mono_wasm_get_array_values (int object_id);
37 //JS functions imported that we use
38 extern void mono_wasm_add_frame (int il_offset, int method_token, const char *assembly_name);
39 extern void mono_wasm_fire_bp (void);
40 extern void mono_wasm_add_bool_var (gint8);
41 extern void mono_wasm_add_number_var (double);
42 extern void mono_wasm_add_string_var (const char*);
43 extern void mono_wasm_add_obj_var (const char*, guint64);
44 extern void mono_wasm_add_array_var (const char*, guint64);
45 extern void mono_wasm_add_properties_var (const char*);
46 extern void mono_wasm_add_array_item (int);
48 G_END_DECLS
50 //FIXME move all of those fields to the profiler object
51 static gboolean debugger_enabled;
53 static int event_request_id;
54 static GHashTable *objrefs;
55 static GHashTable *obj_to_objref;
56 static int objref_id = 0;
58 #define THREAD_TO_INTERNAL(thread) (thread)->internal_thread
60 static void
61 inplace_tolower (char *c)
63 int i;
64 for (i = strlen (c) - 1; i >= 0; --i)
65 c [i] = tolower (c [i]);
68 static void
69 jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo)
71 mono_de_add_pending_breakpoints (method, jinfo);
74 static void
75 appdomain_load (MonoProfiler *prof, MonoDomain *domain)
77 mono_de_domain_add (domain);
80 /* Frame state handling */
81 static GPtrArray *frames;
83 static void
84 free_frame (DbgEngineStackFrame *frame)
86 g_free (frame);
89 static gboolean
90 collect_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data)
92 SeqPoint sp;
93 MonoMethod *method;
95 //skip wrappers
96 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP)
97 return FALSE;
99 if (info->ji)
100 method = jinfo_get_method (info->ji);
101 else
102 method = info->method;
104 if (!method)
105 return FALSE;
107 DEBUG_PRINTF (2, "Reporting method %s native_offset %d\n", method->name, info->native_offset);
109 if (!mono_find_prev_seq_point_for_native_offset (mono_get_root_domain (), method, info->native_offset, NULL, &sp))
110 DEBUG_PRINTF (1, "Failed to lookup sequence point\n");
112 DbgEngineStackFrame *frame = g_new0 (DbgEngineStackFrame, 1);
114 frame->ji = info->ji;
115 frame->domain = info->domain;
116 frame->method = method;
117 frame->native_offset = info->native_offset;
119 g_ptr_array_add (frames, frame);
121 return FALSE;
124 static void
125 free_frame_state (void)
127 if (frames) {
128 int i;
129 for (i = 0; i < frames->len; ++i)
130 free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i));
131 g_ptr_array_set_size (frames, 0);
135 static void
136 compute_frames (void) {
137 if (frames) {
138 int i;
139 for (i = 0; i < frames->len; ++i)
140 free_frame ((DbgEngineStackFrame*)g_ptr_array_index (frames, i));
141 g_ptr_array_set_size (frames, 0);
142 } else {
143 frames = g_ptr_array_new ();
146 mono_walk_stack_with_ctx (collect_frames, NULL, MONO_UNWIND_NONE, NULL);
148 static MonoContext*
149 tls_get_restore_state (void *tls)
151 return NULL;
154 static gboolean
155 try_process_suspend (void *tls, MonoContext *ctx)
157 return FALSE;
160 static gboolean
161 begin_breakpoint_processing (void *tls, MonoContext *ctx, MonoJitInfo *ji, gboolean from_signal)
163 return TRUE;
166 static void
167 begin_single_step_processing (MonoContext *ctx, gboolean from_signal)
171 static void
172 ss_discard_frame_context (void *the_tls)
174 free_frame_state ();
177 static void
178 ss_calculate_framecount (void *tls, MonoContext *ctx, gboolean force_use_ctx, DbgEngineStackFrame ***out_frames, int *nframes)
180 compute_frames ();
181 if (out_frames)
182 *out_frames = (DbgEngineStackFrame **)frames->pdata;
183 if (nframes)
184 *nframes = frames->len;
187 static gboolean
188 ensure_jit (DbgEngineStackFrame* the_frame)
190 return TRUE;
193 static int
194 ensure_runtime_is_suspended (void)
196 return DE_ERR_NONE;
199 static int
200 get_this_async_id (DbgEngineStackFrame *f)
202 g_error ("get_this_async_id");
203 return 0;
206 static gboolean
207 set_set_notification_for_wait_completion_flag (DbgEngineStackFrame *f)
209 g_error ("set_set_notification_for_wait_completion_flag");
210 return FALSE;
213 static MonoMethod*
214 get_notify_debugger_of_wait_completion_method (void)
216 g_error ("get_notify_debugger_of_wait_completion_method");
219 typedef struct {
220 gboolean is_ss; //do I need this?
221 } BpEvents;
223 static void*
224 create_breakpoint_events (GPtrArray *ss_reqs, GPtrArray *bp_reqs, MonoJitInfo *ji, EventKind kind)
226 printf ("ss_reqs %d bp_reqs %d\n", ss_reqs->len, bp_reqs->len);
227 if ((ss_reqs && ss_reqs->len) || (bp_reqs && bp_reqs->len)) {
228 BpEvents *evts = g_new0 (BpEvents, 1); //just a non-null value to make sure we can raise it on process_breakpoint_events
229 evts->is_ss = (ss_reqs && ss_reqs->len);
230 return evts;
232 return NULL;
235 static void
236 process_breakpoint_events (void *_evts, MonoMethod *method, MonoContext *ctx, int il_offsets)
238 BpEvents *evts = (BpEvents*)_evts;
239 if (evts) {
240 if (evts->is_ss)
241 mono_de_cancel_ss ();
242 mono_wasm_fire_bp ();
243 g_free (evts);
247 static void
248 no_seq_points_found (MonoMethod *method, int offset)
251 * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space.
253 printf ("Unable to find seq points for method '%s', offset 0x%x.\n", mono_method_full_name (method, TRUE), offset);
256 #define DBG_NOT_SUSPENDED 1
258 static int
259 ss_create_init_args (SingleStepReq *ss_req, SingleStepArgs *ss_args)
261 printf ("ss_create_init_args\n");
262 int dummy = 0;
263 ss_req->start_sp = ss_req->last_sp = &dummy;
264 compute_frames ();
265 memset (ss_args, 0, sizeof (*ss_args));
267 //BIG WTF, should not happen maybe should assert?
268 if (frames->len == 0) {
269 DEBUG_PRINTF (1, "SINGLE STEPPING FOUND NO FRAMES");
270 return DBG_NOT_SUSPENDED;
273 DbgEngineStackFrame *frame = (DbgEngineStackFrame*)g_ptr_array_index (frames, 0);
274 ss_req->start_method = ss_args->method = frame->method;
275 gboolean found_sp = mono_find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &ss_args->info, &ss_args->sp);
276 if (!found_sp)
277 no_seq_points_found (frame->method, frame->native_offset);
278 g_assert (found_sp);
280 ss_args->frames = (DbgEngineStackFrame**)frames->pdata;
281 ss_args->nframes = frames->len;
282 //XXX do sp
284 return DE_ERR_NONE;
287 static void
288 ss_args_destroy (SingleStepArgs *ss_args)
290 //nothing to do
293 void
294 mono_wasm_debugger_init (void)
296 if (!debugger_enabled)
297 return;
299 DebuggerEngineCallbacks cbs = {
300 .tls_get_restore_state = tls_get_restore_state,
301 .try_process_suspend = try_process_suspend,
302 .begin_breakpoint_processing = begin_breakpoint_processing,
303 .begin_single_step_processing = begin_single_step_processing,
304 .ss_discard_frame_context = ss_discard_frame_context,
305 .ss_calculate_framecount = ss_calculate_framecount,
306 .ensure_jit = ensure_jit,
307 .ensure_runtime_is_suspended = ensure_runtime_is_suspended,
308 .get_this_async_id = get_this_async_id,
309 .set_set_notification_for_wait_completion_flag = set_set_notification_for_wait_completion_flag,
310 .get_notify_debugger_of_wait_completion_method = get_notify_debugger_of_wait_completion_method,
311 .create_breakpoint_events = create_breakpoint_events,
312 .process_breakpoint_events = process_breakpoint_events,
313 .ss_create_init_args = ss_create_init_args,
314 .ss_args_destroy = ss_args_destroy,
317 mono_debug_init (MONO_DEBUG_FORMAT_MONO);
318 mono_de_init (&cbs);
319 mono_de_set_log_level (1, stdout);
321 mini_debug_options.gen_sdb_seq_points = TRUE;
322 mini_debug_options.mdb_optimizations = TRUE;
323 mono_disable_optimizations (MONO_OPT_LINEARS);
324 mini_debug_options.load_aot_jit_info_eagerly = TRUE;
326 MonoProfilerHandle prof = mono_profiler_create (NULL);
327 mono_profiler_set_jit_done_callback (prof, jit_done);
328 //FIXME support multiple appdomains
329 mono_profiler_set_domain_loaded_callback (prof, appdomain_load);
331 obj_to_objref = g_hash_table_new (NULL, NULL);
332 objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref);
335 MONO_API void
336 mono_wasm_enable_debugging (void)
338 DEBUG_PRINTF (1, "DEBUGGING ENABLED\n");
339 debugger_enabled = TRUE;
342 EMSCRIPTEN_KEEPALIVE void
343 mono_wasm_setup_single_step (int kind)
345 int nmodifiers = 1;
347 printf (">>>> mono_wasm_setup_single_step %d\n", kind);
348 EventRequest *req = (EventRequest *)g_malloc0 (sizeof (EventRequest) + (nmodifiers * sizeof (Modifier)));
349 req->id = ++event_request_id;
350 req->event_kind = EVENT_KIND_STEP;
351 // DE doesn't care about suspend_policy
352 // req->suspend_policy = SUSPEND_POLICY_ALL;
353 req->nmodifiers = nmodifiers;
355 StepSize size = STEP_SIZE_MIN;
357 //FIXME I DON'T KNOW WHAT I'M DOING!!!!! filter all the things.
358 StepFilter filter = (StepFilter)(STEP_FILTER_STATIC_CTOR | STEP_FILTER_DEBUGGER_HIDDEN | STEP_FILTER_DEBUGGER_STEP_THROUGH | STEP_FILTER_DEBUGGER_NON_USER_CODE);
359 req->modifiers [0].data.filter = filter;
361 StepDepth depth;
362 switch (kind) {
363 case 0: //into
364 depth = STEP_DEPTH_INTO;
365 break;
366 case 1: //out
367 depth = STEP_DEPTH_OUT;
368 break;
369 case 2: //over
370 depth = STEP_DEPTH_OVER;
371 break;
372 default:
373 g_error ("dunno step kind %d", kind);
376 DbgEngineErrorCode err = mono_de_ss_create (THREAD_TO_INTERNAL (mono_thread_current ()), size, depth, filter, req);
377 if (err != DE_ERR_NONE) {
378 DEBUG_PRINTF (1, "[dbg] Failed to setup single step request");
380 printf ("ss is in place, now ahat?\n");
383 EMSCRIPTEN_KEEPALIVE void
384 mono_wasm_clear_all_breakpoints (void)
386 DEBUG_PRINTF (1, "CLEAR BREAKPOINTS\n");
387 mono_de_clear_all_breakpoints ();
390 EMSCRIPTEN_KEEPALIVE int
391 mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset)
393 int i;
394 ERROR_DECL (error);
395 DEBUG_PRINTF (1, "SET BREAKPOINT: assembly %s method %x offset %x\n", assembly_name, method_token, il_offset);
398 //we get 'foo.dll' but mono_assembly_load expects 'foo' so we strip the last dot
399 char *lookup_name = g_strdup (assembly_name);
400 for (i = strlen (lookup_name) - 1; i >= 0; --i) {
401 if (lookup_name [i] == '.') {
402 lookup_name [i] = 0;
403 break;
407 //resolve the assembly
408 MonoImageOpenStatus status;
409 MonoAssemblyName* aname = mono_assembly_name_new (lookup_name);
410 MonoAssembly *assembly = mono_assembly_load (aname, NULL, &status);
411 g_free (lookup_name);
412 if (!assembly) {
413 DEBUG_PRINTF (1, "Could not resolve assembly %s\n", assembly_name);
414 return -1;
417 mono_assembly_name_free_internal (aname);
419 MonoMethod *method = mono_get_method_checked (assembly->image, MONO_TOKEN_METHOD_DEF | method_token, NULL, NULL, error);
420 if (!method) {
421 //FIXME don't swallow the error
422 DEBUG_PRINTF (1, "Could not find method due to %s\n", mono_error_get_message (error));
423 mono_error_cleanup (error);
424 return -1;
427 //FIXME right now none of the EventRequest fields are used by debugger-engine
428 EventRequest *req = g_new0 (EventRequest, 1);
429 req->id = ++event_request_id;
430 req->event_kind = EVENT_KIND_BREAKPOINT;
431 //DE doesn't care about suspend_policy
432 // req->suspend_policy = SUSPEND_POLICY_ALL;
433 req->nmodifiers = 0; //funny thing,
435 // BreakPointRequest *req = breakpoint_request_new (assembly, method, il_offset);
436 MonoBreakpoint *bp = mono_de_set_breakpoint (method, il_offset, req, error);
438 if (!bp) {
439 DEBUG_PRINTF (1, "Could not set breakpoint to %s\n", mono_error_get_message (error));
440 mono_error_cleanup (error);
441 return 0;
444 DEBUG_PRINTF (1, "NEW BP %p has id %d\n", req, req->id);
445 return req->id;
448 EMSCRIPTEN_KEEPALIVE int
449 mono_wasm_remove_breakpoint (int bp_id)
451 MonoBreakpoint *bp = mono_de_get_breakpoint_by_id (bp_id);
452 if (!bp)
453 return 0;
455 mono_de_clear_breakpoint (bp);
456 return 1;
459 void
460 mono_wasm_single_step_hit (void)
462 mono_de_process_single_step (NULL, FALSE);
465 void
466 mono_wasm_breakpoint_hit (void)
468 mono_de_process_breakpoint (NULL, FALSE);
469 // mono_wasm_fire_bp ();
472 EMSCRIPTEN_KEEPALIVE int
473 mono_wasm_current_bp_id (void)
475 DEBUG_PRINTF (1, "COMPUTING breapoint ID\n");
476 //FIXME handle compiled case
478 /* Interpreter */
479 MonoLMF *lmf = mono_get_lmf ();
481 g_assert (((guint64)lmf->previous_lmf) & 2);
482 MonoLMFExt *ext = (MonoLMFExt*)lmf;
484 g_assert (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX);
485 MonoInterpFrameHandle *frame = (MonoInterpFrameHandle*)ext->interp_exit_data;
486 MonoJitInfo *ji = mini_get_interp_callbacks ()->frame_get_jit_info (frame);
487 guint8 *ip = (guint8*)mini_get_interp_callbacks ()->frame_get_ip (frame);
489 g_assert (ji && !ji->is_trampoline);
490 MonoMethod *method = jinfo_get_method (ji);
492 /* Compute the native offset of the breakpoint from the ip */
493 guint32 native_offset = ip - (guint8*)ji->code_start;
495 MonoSeqPointInfo *info = NULL;
496 SeqPoint sp;
497 gboolean found_sp = mono_find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info, &sp);
498 if (!found_sp)
499 DEBUG_PRINTF (1, "Could not find SP\n");
502 GPtrArray *bp_reqs = g_ptr_array_new ();
503 mono_de_collect_breakpoints_by_sp (&sp, ji, NULL, bp_reqs);
505 if (bp_reqs->len == 0) {
506 DEBUG_PRINTF (1, "BP NOT FOUND for method %s JI %p il_offset %d\n", method->name, ji, sp.il_offset);
507 return -1;
510 if (bp_reqs->len > 1)
511 DEBUG_PRINTF (1, "Multiple breakpoints (%d) at the same location, returning the first one.", bp_reqs->len);
513 EventRequest *evt = (EventRequest *)g_ptr_array_index (bp_reqs, 0);
514 g_ptr_array_free (bp_reqs, TRUE);
516 DEBUG_PRINTF (1, "Found BP %p with id %d\n", evt, evt->id);
517 return evt->id;
520 static int get_object_id(MonoObject *obj)
522 ObjRef *ref = (ObjRef *)g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
523 if (ref)
524 return ref->id;
525 ref = g_new0 (ObjRef, 1);
526 ref->id = mono_atomic_inc_i32 (&objref_id);
527 ref->handle = mono_gchandle_new_weakref_internal (obj, FALSE);
528 g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref);
529 g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
530 return ref->id;
533 static gboolean
534 list_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data)
536 SeqPoint sp;
537 MonoMethod *method;
539 //skip wrappers
540 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP)
541 return FALSE;
544 if (info->ji)
545 method = jinfo_get_method (info->ji);
546 else
547 method = info->method;
549 if (!method)
550 return FALSE;
552 DEBUG_PRINTF (2, "Reporting method %s native_offset %d\n", method->name, info->native_offset);
554 if (!mono_find_prev_seq_point_for_native_offset (mono_get_root_domain (), method, info->native_offset, NULL, &sp))
555 DEBUG_PRINTF (1, "Failed to lookup sequence point\n");
557 while (method->is_inflated)
558 method = ((MonoMethodInflated*)method)->declaring;
560 char *assembly_name = g_strdup (m_class_get_image (method->klass)->module_name);
561 inplace_tolower (assembly_name);
563 if (method->wrapper_type == MONO_WRAPPER_NONE) {
564 DEBUG_PRINTF (2, "adding off %d token %d assembly name %s\n", sp.il_offset, mono_metadata_token_index (method->token), assembly_name);
565 mono_wasm_add_frame (sp.il_offset, mono_metadata_token_index (method->token), assembly_name);
568 g_free (assembly_name);
570 return FALSE;
573 EMSCRIPTEN_KEEPALIVE void
574 mono_wasm_enum_frames (void)
576 mono_walk_stack_with_ctx (list_frames, NULL, MONO_UNWIND_NONE, NULL);
579 typedef struct {
580 int cur_frame;
581 int target_frame;
582 int variable;
583 } FrameDescData;
585 static gboolean describe_value(MonoType * type, gpointer addr)
587 ERROR_DECL (error);
588 switch (type->type) {
589 case MONO_TYPE_BOOLEAN:
590 mono_wasm_add_bool_var (*(gint8*)addr);
591 break;
592 case MONO_TYPE_I1:
593 mono_wasm_add_number_var (*(gint8*)addr);
594 break;
595 case MONO_TYPE_U1:
596 mono_wasm_add_number_var (*(guint8*)addr);
597 break;
598 case MONO_TYPE_CHAR:
599 case MONO_TYPE_U2:
600 mono_wasm_add_number_var (*(guint16*)addr);
601 break;
602 case MONO_TYPE_I2:
603 mono_wasm_add_number_var (*(gint16*)addr);
604 break;
605 case MONO_TYPE_I4:
606 case MONO_TYPE_I:
607 mono_wasm_add_number_var (*(gint32*)addr);
608 break;
609 case MONO_TYPE_U4:
610 case MONO_TYPE_U:
611 mono_wasm_add_number_var (*(guint32*)addr);
612 break;
613 case MONO_TYPE_I8:
614 mono_wasm_add_number_var (*(gint64*)addr);
615 break;
616 case MONO_TYPE_U8:
617 mono_wasm_add_number_var (*(guint64*)addr);
618 break;
619 case MONO_TYPE_R4:
620 mono_wasm_add_number_var (*(float*)addr);
621 break;
622 case MONO_TYPE_R8:
623 mono_wasm_add_number_var (*(double*)addr);
624 break;
625 case MONO_TYPE_STRING: {
626 MonoString *str_obj = *(MonoString **)addr;
627 if (!str_obj) {
628 mono_wasm_add_string_var (NULL);
629 } else {
630 char *str = mono_string_to_utf8_checked_internal (str_obj, error);
631 mono_error_assert_ok (error); /* FIXME report error */
632 mono_wasm_add_string_var (str);
633 g_free (str);
635 break;
637 case MONO_TYPE_GENERICINST:
638 case MONO_TYPE_SZARRAY:
639 case MONO_TYPE_ARRAY:
640 case MONO_TYPE_OBJECT:
641 case MONO_TYPE_CLASS: {
642 MonoObject *obj = *(MonoObject**)addr;
643 if (!obj) {
644 mono_wasm_add_string_var (NULL);
645 } else {
646 GString *class_name;
647 class_name = g_string_new ("");
648 if (*(obj->vtable->klass->name_space)) {
649 g_string_append (class_name, obj->vtable->klass->name_space);
650 g_string_append_c (class_name, '.');
652 g_string_append (class_name, obj->vtable->klass->name);
653 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)
654 mono_wasm_add_array_var (class_name->str, get_object_id(obj));
655 else
656 mono_wasm_add_obj_var (class_name->str, get_object_id(obj));
657 g_string_free(class_name, FALSE);
658 break;
660 break;
662 default: {
663 char *type_name = mono_type_full_name (type);
664 char *msg = g_strdup_printf("can't handle type %s [%p, %x]", type_name, type, type->type);
665 mono_wasm_add_string_var (msg);
666 g_free (msg);
667 g_free (type_name);
670 return TRUE;
673 static gboolean
674 describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis)
676 MonoClassField *f;
677 MonoProperty *p;
678 MonoObject *exc;
679 MonoObject *res;
680 MonoMethodSignature *sig;
681 gpointer iter = NULL;
682 ERROR_DECL (error);
683 DEBUG_PRINTF (2, "describe_object_properties %d\n", objectId);
684 ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
685 if (!ref) {
686 DEBUG_PRINTF (2, "describe_object_properties !ref\n");
687 return FALSE;
690 MonoObject *obj = mono_gchandle_get_target_internal (ref->handle);
691 if (!obj) {
692 DEBUG_PRINTF (2, "describe_object_properties !obj\n");
693 return FALSE;
696 while (obj && (f = mono_class_get_fields_internal (obj->vtable->klass, &iter))) {
697 DEBUG_PRINTF (2, "mono_class_get_fields_internal - %s - %x\n", f->name, f->type->type);
698 if (isAsyncLocalThis && (f->name[0] != '<' || (f->name[0] == '<' && f->name[1] == '>'))) {
699 continue;
701 if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
702 continue;
703 if (mono_field_is_deleted (f))
704 continue;
705 mono_wasm_add_properties_var(f->name);
706 gpointer field_value = (guint8*)obj + f->offset;
708 describe_value(f->type, field_value);
711 iter = NULL;
712 while ((p = mono_class_get_properties (obj->vtable->klass, &iter))) {
713 DEBUG_PRINTF (2, "mono_class_get_properties - %s - %s\n", p->name, p->get->name);
714 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
715 if (isAsyncLocalThis && (p->name[0] != '<' || (p->name[0] == '<' && p->name[1] == '>'))) {
716 continue;
718 mono_wasm_add_properties_var(p->name);
719 sig = mono_method_signature_internal (p->get);
720 res = mono_runtime_try_invoke (p->get, obj, NULL, &exc, error);
721 if (!is_ok (error) && exc == NULL)
722 exc = (MonoObject*) mono_error_convert_to_exception (error);
723 if (exc)
724 describe_value (mono_get_object_type (), &exc);
725 else if (!m_class_is_valuetype (mono_object_class (res)))
726 describe_value(sig->ret, &res);
727 else
728 describe_value(sig->ret, mono_object_unbox_internal (res));
731 return TRUE;
735 static gboolean
736 describe_array_values (guint64 objectId)
738 int esize;
739 gpointer elem;
740 ObjRef *ref = (ObjRef *)g_hash_table_lookup (objrefs, GINT_TO_POINTER (objectId));
741 if (!ref) {
742 return FALSE;
744 MonoArray *arr = (MonoArray *)mono_gchandle_get_target_internal (ref->handle);
745 MonoObject *obj = &arr->obj;
746 if (!obj) {
747 return FALSE;
749 esize = mono_array_element_size (obj->vtable->klass);
750 for (int i = 0; i < arr->max_length; i++) {
751 mono_wasm_add_array_item(i);
752 elem = (gpointer*)((char*)arr->vector + (i * esize));
753 describe_value(m_class_get_byval_arg (m_class_get_element_class (arr->obj.vtable->klass)), elem);
755 return TRUE;
758 static gboolean
759 describe_async_method_locals (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud)
761 //Async methods are special in the way that local variables can be lifted to generated class fields
762 FrameDescData *data = (FrameDescData*)ud;
764 //skip wrappers
765 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) {
766 return FALSE;
769 if (data->cur_frame < data->target_frame) {
770 ++data->cur_frame;
771 return FALSE;
774 InterpFrame *frame = (InterpFrame*)info->interp_frame;
775 g_assert (frame);
776 MonoMethod *method = frame->imethod->method;
777 g_assert (method);
778 gpointer addr = NULL;
779 if (mono_debug_lookup_method_async_debug_info (method)) {
780 addr = mini_get_interp_callbacks ()->frame_get_this (frame);
781 MonoObject *obj = *(MonoObject**)addr;
782 describe_object_properties(get_object_id(obj), TRUE);
784 return TRUE;
787 static gboolean
788 describe_this (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud)
790 //Async methods are special in the way that local variables can be lifted to generated class fields
791 FrameDescData *data = (FrameDescData*)ud;
793 //skip wrappers
794 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) {
795 return FALSE;
798 if (data->cur_frame < data->target_frame) {
799 ++data->cur_frame;
800 return FALSE;
803 InterpFrame *frame = (InterpFrame*)info->interp_frame;
804 g_assert (frame);
805 MonoMethod *method = frame->imethod->method;
806 g_assert (method);
807 gpointer addr = NULL;
808 if (mono_method_signature_internal (method)->hasthis) {
809 addr = mini_get_interp_callbacks ()->frame_get_this (frame);
810 MonoObject *obj = *(MonoObject**)addr;
811 mono_wasm_add_properties_var("this");
812 GString *class_name;
813 class_name = g_string_new ("");
814 if (*(obj->vtable->klass->name_space)) {
815 g_string_append (class_name, obj->vtable->klass->name_space);
816 g_string_append_c (class_name, '.');
818 g_string_append (class_name, obj->vtable->klass->name);
819 mono_wasm_add_obj_var (class_name->str, get_object_id(obj));
820 g_string_free(class_name, FALSE);
822 return TRUE;
826 static gboolean
827 describe_variable (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud)
829 ERROR_DECL (error);
830 MonoMethodHeader *header = NULL;
832 FrameDescData *data = (FrameDescData*)ud;
834 //skip wrappers
835 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) {
836 return FALSE;
839 if (data->cur_frame < data->target_frame) {
840 ++data->cur_frame;
841 return FALSE;
844 InterpFrame *frame = (InterpFrame*)info->interp_frame;
845 g_assert (frame);
846 MonoMethod *method = frame->imethod->method;
847 g_assert (method);
849 MonoType *type = NULL;
850 gpointer addr = NULL;
851 int pos = data->variable;
852 if (pos < 0) {
853 pos = -pos - 1;
854 type = mono_method_signature_internal (method)->params [pos];
855 addr = mini_get_interp_callbacks ()->frame_get_arg (frame, pos);
856 } else {
857 header = mono_method_get_header_checked (method, error);
858 mono_error_assert_ok (error); /* FIXME report error */
860 type = header->locals [pos];
861 addr = mini_get_interp_callbacks ()->frame_get_local (frame, pos);
864 DEBUG_PRINTF (2, "adding val %p type [%p] %s\n", addr, type, mono_type_full_name (type));
866 describe_value(type, addr);
867 if (header)
868 mono_metadata_free_mh (header);
870 return TRUE;
873 //FIXME this doesn't support getting the return value pseudo-var
874 EMSCRIPTEN_KEEPALIVE void
875 mono_wasm_get_var_info (int scope, int* pos, int len)
877 FrameDescData data;
878 data.cur_frame = 0;
879 data.target_frame = scope;
880 for (int i = 0; i < len; i++)
882 DEBUG_PRINTF (2, "getting var %d of scope %d - %d\n", pos[i], scope, len);
883 data.variable = pos[i];
884 mono_walk_stack_with_ctx (describe_variable, NULL, MONO_UNWIND_NONE, &data);
886 mono_walk_stack_with_ctx (describe_async_method_locals, NULL, MONO_UNWIND_NONE, &data);
887 mono_walk_stack_with_ctx (describe_this, NULL, MONO_UNWIND_NONE, &data);
890 EMSCRIPTEN_KEEPALIVE void
891 mono_wasm_get_object_properties (int object_id)
893 DEBUG_PRINTF (2, "getting properties of object %d\n", object_id);
895 describe_object_properties(object_id, FALSE);
898 EMSCRIPTEN_KEEPALIVE void
899 mono_wasm_get_array_values (int object_id)
901 DEBUG_PRINTF (2, "getting array values %d\n", object_id);
903 describe_array_values(object_id);
906 // Functions required by debugger-state-machine.
907 gsize
908 mono_debugger_tls_thread_id (DebuggerTlsData *debuggerTlsData)
910 return 1;
913 #else // HOST_WASM
915 void
916 mono_wasm_single_step_hit (void)
920 void
921 mono_wasm_breakpoint_hit (void)
925 void
926 mono_wasm_debugger_init (void)
930 #endif // HOST_WASM