Revert "[mono][debugger] First PR to implement iCorDebug on mono (#20757)"
[mono-project.git] / mono / mini / debugger-engine.c
blob7cc46e797760b764943e2d051371ec3aa5d7173f
1 /**
2 * \file
3 * Debugger Engine shared code.
5 * Author:
6 * Zoltan Varga (vargaz@gmail.com)
7 * Rodrigo Kumpera (kumpera@gmail.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <config.h>
13 #include "mini-runtime.h"
15 #if !defined (DISABLE_SDB) || defined(TARGET_WASM)
17 #include <glib.h>
18 #include "seq-points.h"
19 #include "aot-runtime.h"
20 #include "debugger-engine.h"
21 #include "debugger-state-machine.h"
22 #include <mono/metadata/debug-internals.h>
24 static void mono_de_ss_start (SingleStepReq *ss_req, SingleStepArgs *ss_args);
25 static gboolean mono_de_ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, void *tls, MonoContext *ctx, MonoMethod* method);
28 static DebuggerEngineCallbacks rt_callbacks;
31 * Logging support
33 static int log_level;
34 static FILE *log_file;
38 * Locking
40 #define dbg_lock() mono_coop_mutex_lock (&debug_mutex)
41 #define dbg_unlock() mono_coop_mutex_unlock (&debug_mutex)
42 static MonoCoopMutex debug_mutex;
44 void
45 mono_de_lock (void)
47 dbg_lock ();
50 void
51 mono_de_unlock (void)
53 dbg_unlock ();
58 * Domain support
62 /* A hash table containing all active domains */
63 /* Protected by the loader lock */
64 static GHashTable *domains;
67 static void
68 domains_init (void)
70 domains = g_hash_table_new (mono_aligned_addr_hash, NULL);
73 static void
74 domains_cleanup (void)
76 //FIXME can we safely destroy `domains`?
80 * mono_de_foreach_domain:
82 * Iterate over all domains under debugging. Caller must take the loader lock.
84 * FIXME can we move the locking to here? Callers in sdb must be properly audited.
86 void
87 mono_de_foreach_domain (GHFunc func, gpointer user_data)
89 g_hash_table_foreach (domains, func, user_data);
93 * LOCKING: Takes the loader lock
95 void
96 mono_de_domain_remove (MonoDomain *domain)
98 mono_loader_lock ();
99 g_hash_table_remove (domains, domain);
100 mono_loader_unlock ();
104 * LOCKING: Takes the loader lock
106 void
107 mono_de_domain_add (MonoDomain *domain)
109 mono_loader_lock ();
110 g_hash_table_insert (domains, domain, domain);
111 mono_loader_unlock ();
115 * BREAKPOINTS
118 /* List of breakpoints */
119 /* Protected by the loader lock */
120 static GPtrArray *breakpoints;
121 /* Maps breakpoint locations to the number of breakpoints at that location */
122 static GHashTable *bp_locs;
124 static void
125 breakpoints_init (void)
127 breakpoints = g_ptr_array_new ();
128 bp_locs = g_hash_table_new (NULL, NULL);
132 * insert_breakpoint:
134 * Insert the breakpoint described by BP into the method described by
135 * JI.
137 static void
138 insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo *ji, MonoBreakpoint *bp, MonoError *error)
140 int count;
141 BreakpointInstance *inst;
142 SeqPointIterator it;
143 gboolean it_has_sp = FALSE;
145 if (error)
146 error_init (error);
148 mono_seq_point_iterator_init (&it, seq_points);
149 while (mono_seq_point_iterator_next (&it)) {
150 if (it.seq_point.il_offset == bp->il_offset) {
151 it_has_sp = TRUE;
152 break;
156 if (!it_has_sp) {
158 * The set of IL offsets with seq points doesn't completely match the
159 * info returned by CMD_METHOD_GET_DEBUG_INFO (#407).
161 mono_seq_point_iterator_init (&it, seq_points);
162 while (mono_seq_point_iterator_next (&it)) {
163 if (it.seq_point.il_offset != METHOD_ENTRY_IL_OFFSET &&
164 it.seq_point.il_offset != METHOD_EXIT_IL_OFFSET &&
165 it.seq_point.il_offset + 1 == bp->il_offset) {
166 it_has_sp = TRUE;
167 break;
172 if (!it_has_sp) {
173 char *s = g_strdup_printf ("Unable to insert breakpoint at %s:%ld", mono_method_full_name (jinfo_get_method (ji), TRUE), bp->il_offset);
175 mono_seq_point_iterator_init (&it, seq_points);
176 while (mono_seq_point_iterator_next (&it))
177 PRINT_DEBUG_MSG (1, "%d\n", it.seq_point.il_offset);
179 if (error) {
180 mono_error_set_error (error, MONO_ERROR_GENERIC, "%s", s);
181 g_warning ("%s", s);
182 g_free (s);
183 return;
184 } else {
185 g_warning ("%s", s);
186 g_free (s);
187 return;
191 inst = g_new0 (BreakpointInstance, 1);
192 inst->il_offset = it.seq_point.il_offset;
193 inst->native_offset = it.seq_point.native_offset;
194 inst->ip = (guint8*)ji->code_start + it.seq_point.native_offset;
195 inst->ji = ji;
196 inst->domain = domain;
198 mono_loader_lock ();
200 g_ptr_array_add (bp->children, inst);
202 mono_loader_unlock ();
204 dbg_lock ();
205 count = GPOINTER_TO_INT (g_hash_table_lookup (bp_locs, inst->ip));
206 g_hash_table_insert (bp_locs, inst->ip, GINT_TO_POINTER (count + 1));
207 dbg_unlock ();
209 if (it.seq_point.native_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
210 PRINT_DEBUG_MSG (1, "[dbg] Attempting to insert seq point at dead IL offset %d, ignoring.\n", (int)bp->il_offset);
211 } else if (count == 0) {
212 if (ji->is_interp) {
213 mini_get_interp_callbacks ()->set_breakpoint (ji, inst->ip);
214 } else {
215 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
216 mono_arch_set_breakpoint (ji, inst->ip);
217 #else
218 NOT_IMPLEMENTED;
219 #endif
223 PRINT_DEBUG_MSG (1, "[dbg] Inserted breakpoint at %s:[il=0x%x,native=0x%x] [%p](%d).\n", mono_method_full_name (jinfo_get_method (ji), TRUE), (int)it.seq_point.il_offset, (int)it.seq_point.native_offset, inst->ip, count);
226 static void
227 remove_breakpoint (BreakpointInstance *inst)
229 int count;
230 MonoJitInfo *ji = inst->ji;
231 guint8 *ip = inst->ip;
233 dbg_lock ();
234 count = GPOINTER_TO_INT (g_hash_table_lookup (bp_locs, ip));
235 g_hash_table_insert (bp_locs, ip, GINT_TO_POINTER (count - 1));
236 dbg_unlock ();
238 g_assert (count > 0);
240 if (count == 1 && inst->native_offset != SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
241 if (ji->is_interp) {
242 mini_get_interp_callbacks ()->clear_breakpoint (ji, ip);
243 } else {
244 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
245 mono_arch_clear_breakpoint (ji, ip);
246 #else
247 NOT_IMPLEMENTED;
248 #endif
250 PRINT_DEBUG_MSG (1, "[dbg] Clear breakpoint at %s [%p].\n", mono_method_full_name (jinfo_get_method (ji), TRUE), ip);
255 * This doesn't take any locks.
257 static gboolean
258 bp_matches_method (MonoBreakpoint *bp, MonoMethod *method)
260 int i;
262 if (!bp->method)
263 return TRUE;
264 if (method == bp->method)
265 return TRUE;
266 if (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method)
267 return TRUE;
269 if (bp->method->is_inflated && method->is_inflated) {
270 MonoMethodInflated *bpimethod = (MonoMethodInflated*)bp->method;
271 MonoMethodInflated *imethod = (MonoMethodInflated*)method;
273 /* Open generic methods should match closed generic methods of the same class */
274 if (bpimethod->declaring == imethod->declaring && bpimethod->context.class_inst == imethod->context.class_inst && bpimethod->context.method_inst && bpimethod->context.method_inst->is_open) {
275 for (i = 0; i < bpimethod->context.method_inst->type_argc; ++i) {
276 MonoType *t1 = bpimethod->context.method_inst->type_argv [i];
278 /* FIXME: Handle !mvar */
279 if (t1->type != MONO_TYPE_MVAR)
280 return FALSE;
282 return TRUE;
286 return FALSE;
290 * mono_de_add_pending_breakpoints:
292 * Insert pending breakpoints into the newly JITted method METHOD.
294 void
295 mono_de_add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
297 int i, j;
298 MonoSeqPointInfo *seq_points;
299 MonoDomain *domain;
301 if (!breakpoints)
302 return;
304 domain = mono_domain_get ();
306 mono_loader_lock ();
308 for (i = 0; i < breakpoints->len; ++i) {
309 MonoBreakpoint *bp = (MonoBreakpoint *)g_ptr_array_index (breakpoints, i);
310 gboolean found = FALSE;
312 if (!bp_matches_method (bp, method))
313 continue;
315 for (j = 0; j < bp->children->len; ++j) {
316 BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, j);
318 if (inst->ji == ji)
319 found = TRUE;
322 if (!found) {
323 seq_points = (MonoSeqPointInfo *) ji->seq_points;
325 if (!seq_points) {
326 MonoMethod *jmethod = jinfo_get_method (ji);
327 if (jmethod->is_inflated) {
328 MonoJitInfo *seq_ji;
329 MonoMethod *declaring = mono_method_get_declaring_generic_method (jmethod);
330 mono_jit_search_all_backends_for_jit_info (domain, declaring, &seq_ji);
331 seq_points = (MonoSeqPointInfo *) seq_ji->seq_points;
335 if (!seq_points)
336 /* Could be AOT code, or above "search_all_backends" call could have failed */
337 continue;
339 insert_breakpoint (seq_points, domain, ji, bp, NULL);
343 mono_loader_unlock ();
346 static void
347 set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_points, MonoBreakpoint *bp, MonoError *error)
349 MonoJitInfo *ji;
351 if (error)
352 error_init (error);
354 (void)mono_jit_search_all_backends_for_jit_info (domain, method, &ji);
355 g_assert (ji);
357 insert_breakpoint (seq_points, domain, ji, bp, error);
360 typedef struct {
361 MonoBreakpoint *bp;
362 GPtrArray *methods;
363 GPtrArray *method_domains;
364 GPtrArray *method_seq_points;
365 } CollectDomainData;
367 static void
368 collect_domain_bp (gpointer key, gpointer value, gpointer user_data)
370 GHashTableIter iter;
371 MonoSeqPointInfo *seq_points;
372 MonoDomain *domain = (MonoDomain*)key;
373 CollectDomainData *ud = (CollectDomainData*)user_data;
374 MonoMethod *m;
376 if (mono_domain_is_unloading (domain))
377 return;
379 mono_domain_lock (domain);
380 g_hash_table_iter_init (&iter, domain_jit_info (domain)->seq_points);
381 while (g_hash_table_iter_next (&iter, (void**)&m, (void**)&seq_points)) {
382 if (bp_matches_method (ud->bp, m)) {
383 /* Save the info locally to simplify the code inside the domain lock */
384 g_ptr_array_add (ud->methods, m);
385 g_ptr_array_add (ud->method_domains, domain);
386 g_ptr_array_add (ud->method_seq_points, seq_points);
389 mono_domain_unlock (domain);
392 void
393 mono_de_clear_all_breakpoints (void)
395 while (breakpoints->len)
396 mono_de_clear_breakpoint ((MonoBreakpoint*)g_ptr_array_index (breakpoints, 0));
400 * mono_de_set_breakpoint:
402 * Set a breakpoint at IL_OFFSET in METHOD.
403 * METHOD can be NULL, in which case a breakpoint is placed in all methods.
404 * METHOD can also be a generic method definition, in which case a breakpoint
405 * is placed in all instances of the method.
406 * If ERROR is non-NULL, then it is set and NULL is returnd if some breakpoints couldn't be
407 * inserted.
409 MonoBreakpoint*
410 mono_de_set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req, MonoError *error)
412 MonoBreakpoint *bp;
413 MonoDomain *domain;
414 MonoMethod *m;
415 MonoSeqPointInfo *seq_points;
416 GPtrArray *methods;
417 GPtrArray *method_domains;
418 GPtrArray *method_seq_points;
419 int i;
421 if (error)
422 error_init (error);
424 // FIXME:
425 // - suspend/resume the vm to prevent code patching problems
426 // - multiple breakpoints on the same location
427 // - dynamic methods
428 // - races
430 bp = g_new0 (MonoBreakpoint, 1);
431 bp->method = method;
432 bp->il_offset = il_offset;
433 bp->req = req;
434 bp->children = g_ptr_array_new ();
436 PRINT_DEBUG_MSG (1, "[dbg] Setting %sbreakpoint at %s:0x%x.\n", (req->event_kind == EVENT_KIND_STEP) ? "single step " : "", method ? mono_method_full_name (method, TRUE) : "<all>", (int)il_offset);
438 methods = g_ptr_array_new ();
439 method_domains = g_ptr_array_new ();
440 method_seq_points = g_ptr_array_new ();
442 mono_loader_lock ();
444 CollectDomainData user_data;
445 memset (&user_data, 0, sizeof (user_data));
446 user_data.bp = bp;
447 user_data.methods = methods;
448 user_data.method_domains = method_domains;
449 user_data.method_seq_points = method_seq_points;
450 mono_de_foreach_domain (collect_domain_bp, &user_data);
452 for (i = 0; i < methods->len; ++i) {
453 m = (MonoMethod *)g_ptr_array_index (methods, i);
454 domain = (MonoDomain *)g_ptr_array_index (method_domains, i);
455 seq_points = (MonoSeqPointInfo *)g_ptr_array_index (method_seq_points, i);
456 set_bp_in_method (domain, m, seq_points, bp, error);
459 g_ptr_array_add (breakpoints, bp);
460 mono_debugger_log_add_bp (bp, bp->method, bp->il_offset);
461 mono_loader_unlock ();
463 g_ptr_array_free (methods, TRUE);
464 g_ptr_array_free (method_domains, TRUE);
465 g_ptr_array_free (method_seq_points, TRUE);
467 if (error && !is_ok (error)) {
468 mono_de_clear_breakpoint (bp);
469 return NULL;
472 return bp;
475 MonoBreakpoint *
476 mono_de_get_breakpoint_by_id (int id)
478 for (int i = 0; i < breakpoints->len; ++i) {
479 MonoBreakpoint *bp = (MonoBreakpoint *)g_ptr_array_index (breakpoints, i);
480 if (bp->req->id == id)
481 return bp;
483 return NULL;
486 void
487 mono_de_clear_breakpoint (MonoBreakpoint *bp)
489 int i;
491 // FIXME: locking, races
492 for (i = 0; i < bp->children->len; ++i) {
493 BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, i);
495 remove_breakpoint (inst);
497 g_free (inst);
500 mono_loader_lock ();
501 mono_debugger_log_remove_bp (bp, bp->method, bp->il_offset);
502 g_ptr_array_remove (breakpoints, bp);
503 mono_loader_unlock ();
505 g_ptr_array_free (bp->children, TRUE);
506 g_free (bp);
509 void
510 mono_de_collect_breakpoints_by_sp (SeqPoint *sp, MonoJitInfo *ji, GPtrArray *ss_reqs, GPtrArray *bp_reqs)
512 for (int i = 0; i < breakpoints->len; ++i) {
513 MonoBreakpoint *bp = (MonoBreakpoint *)g_ptr_array_index (breakpoints, i);
515 if (!bp->method)
516 continue;
518 for (int j = 0; j < bp->children->len; ++j) {
519 BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, j);
520 if (inst->ji == ji && inst->il_offset == sp->il_offset && inst->native_offset == sp->native_offset) {
521 if (bp->req->event_kind == EVENT_KIND_STEP) {
522 if (ss_reqs)
523 g_ptr_array_add (ss_reqs, bp->req);
524 } else {
525 if (bp_reqs)
526 g_ptr_array_add (bp_reqs, bp->req);
533 static void
534 breakpoints_cleanup (void)
536 int i;
538 mono_loader_lock ();
540 for (i = 0; i < breakpoints->len; ++i)
541 g_free (g_ptr_array_index (breakpoints, i));
543 g_ptr_array_free (breakpoints, TRUE);
544 g_hash_table_destroy (bp_locs);
546 breakpoints = NULL;
547 bp_locs = NULL;
549 mono_loader_unlock ();
553 * mono_de_clear_breakpoints_for_domain:
555 * Clear breakpoint instances which reference DOMAIN.
557 void
558 mono_de_clear_breakpoints_for_domain (MonoDomain *domain)
560 int i, j;
562 /* This could be called after shutdown */
563 if (!breakpoints)
564 return;
566 mono_loader_lock ();
567 for (i = 0; i < breakpoints->len; ++i) {
568 MonoBreakpoint *bp = (MonoBreakpoint *)g_ptr_array_index (breakpoints, i);
570 j = 0;
571 while (j < bp->children->len) {
572 BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, j);
574 if (inst->domain == domain) {
575 remove_breakpoint (inst);
577 g_free (inst);
579 g_ptr_array_remove_index_fast (bp->children, j);
580 } else {
581 j ++;
585 mono_loader_unlock ();
588 /* Single stepping engine */
589 /* Number of single stepping operations in progress */
590 static int ss_count;
592 /* The single step request instances */
593 static GPtrArray *the_ss_reqs;
595 static void
596 ss_req_init (void)
598 the_ss_reqs = g_ptr_array_new ();
601 static void
602 ss_req_cleanup (void)
604 dbg_lock ();
606 g_ptr_array_free (the_ss_reqs, TRUE);
608 the_ss_reqs = NULL;
610 dbg_unlock ();
614 * mono_de_start_single_stepping:
616 * Turn on single stepping. Can be called multiple times, for example,
617 * by a single step event request + a suspend.
619 void
620 mono_de_start_single_stepping (void)
622 int val = mono_atomic_inc_i32 (&ss_count);
624 if (val == 1) {
625 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
626 mono_arch_start_single_stepping ();
627 #endif
628 mini_get_interp_callbacks ()->start_single_stepping ();
632 void
633 mono_de_stop_single_stepping (void)
635 int val = mono_atomic_dec_i32 (&ss_count);
637 if (val == 0) {
638 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
639 mono_arch_stop_single_stepping ();
640 #endif
641 mini_get_interp_callbacks ()->stop_single_stepping ();
645 static MonoJitInfo*
646 get_top_method_ji (gpointer ip, MonoDomain **domain, gpointer *out_ip)
648 MonoJitInfo *ji;
650 if (out_ip)
651 *out_ip = ip;
653 ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, domain);
654 if (!ji) {
655 /* Could be an interpreter method */
657 MonoLMF *lmf = mono_get_lmf ();
658 MonoInterpFrameHandle *frame;
660 g_assert (((gsize)lmf->previous_lmf) & 2);
661 MonoLMFExt *ext = (MonoLMFExt*)lmf;
663 g_assert (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX);
664 frame = (MonoInterpFrameHandle*)ext->interp_exit_data;
665 ji = mini_get_interp_callbacks ()->frame_get_jit_info (frame);
666 if (domain)
667 *domain = mono_domain_get ();
668 if (out_ip)
669 *out_ip = mini_get_interp_callbacks ()->frame_get_ip (frame);
671 return ji;
674 static void
675 no_seq_points_found (MonoMethod *method, int offset)
678 * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space.
680 PRINT_MSG ("Unable to find seq points for method '%s', offset 0x%x.\n", mono_method_full_name (method, TRUE), offset);
683 static const char*
684 ss_depth_to_string (StepDepth depth)
686 switch (depth) {
687 case STEP_DEPTH_OVER:
688 return "over";
689 case STEP_DEPTH_OUT:
690 return "out";
691 case STEP_DEPTH_INTO:
692 return "into";
693 default:
694 g_assert_not_reached ();
695 return NULL;
700 * ss_stop:
702 * Stop the single stepping operation given by SS_REQ.
704 static void
705 ss_stop (SingleStepReq *ss_req)
707 if (ss_req->bps) {
708 GSList *l;
710 for (l = ss_req->bps; l; l = l->next) {
711 mono_de_clear_breakpoint ((MonoBreakpoint *)l->data);
713 g_slist_free (ss_req->bps);
714 ss_req->bps = NULL;
717 ss_req->async_id = 0;
718 ss_req->async_stepout_method = NULL;
719 if (ss_req->global) {
720 mono_de_stop_single_stepping ();
721 ss_req->global = FALSE;
725 static void
726 ss_destroy (SingleStepReq *req)
728 PRINT_DEBUG_MSG (1, "[dbg] ss_destroy.\n");
730 ss_stop (req);
732 g_free (req);
735 static SingleStepReq*
736 ss_req_acquire (MonoInternalThread *thread)
738 SingleStepReq *req = NULL;
739 dbg_lock ();
740 int i;
741 for (i = 0; i < the_ss_reqs->len; ++i) {
742 SingleStepReq *current_req = (SingleStepReq *)g_ptr_array_index (the_ss_reqs, i);
743 if (current_req->thread == thread) {
744 current_req->refcount ++;
745 req = current_req;
748 dbg_unlock ();
749 return req;
752 static int
753 ss_req_count ()
755 return the_ss_reqs->len;
758 static void
759 mono_de_ss_req_release (SingleStepReq *req)
761 gboolean free = FALSE;
763 dbg_lock ();
764 g_assert (req->refcount);
765 req->refcount --;
766 if (req->refcount == 0)
767 free = TRUE;
768 if (free) {
769 g_ptr_array_remove (the_ss_reqs, req);
770 ss_destroy (req);
772 dbg_unlock ();
775 void
776 mono_de_cancel_ss (SingleStepReq *req)
778 if (the_ss_reqs) {
779 mono_de_ss_req_release (req);
783 void
784 mono_de_cancel_all_ss ()
786 int i;
787 for (i = 0; i < the_ss_reqs->len; ++i) {
788 SingleStepReq *current_req = (SingleStepReq *)g_ptr_array_index (the_ss_reqs, i);
789 mono_de_ss_req_release (current_req);
793 void
794 mono_de_process_single_step (void *tls, gboolean from_signal)
796 MonoJitInfo *ji;
797 guint8 *ip;
798 GPtrArray *reqs;
799 int il_offset;
800 MonoDomain *domain;
801 MonoContext *ctx = rt_callbacks.tls_get_restore_state (tls);
802 MonoMethod *method;
803 SeqPoint sp;
804 MonoSeqPointInfo *info;
805 SingleStepReq *ss_req;
807 /* Skip the instruction causing the single step */
808 rt_callbacks.begin_single_step_processing (ctx, from_signal);
810 if (rt_callbacks.try_process_suspend (tls, ctx, FALSE))
811 return;
814 * This can run concurrently with a clear_event_request () call, so needs locking/reference counts.
816 ss_req = ss_req_acquire (mono_thread_internal_current ());
818 if (!ss_req)
819 // FIXME: A suspend race
820 return;
821 ip = (guint8 *)MONO_CONTEXT_GET_IP (ctx);
823 ji = get_top_method_ji (ip, &domain, (gpointer*)&ip);
824 g_assert (ji && !ji->is_trampoline);
826 if (log_level > 0) {
827 PRINT_DEBUG_MSG (1, "[%p] Single step event (depth=%s) at %s (%p)[0x%x], sp %p, last sp %p\n", (gpointer) (gsize) mono_native_thread_id_get (), ss_depth_to_string (ss_req->depth), mono_method_full_name (jinfo_get_method (ji), TRUE), MONO_CONTEXT_GET_IP (ctx), (int)((guint8*)MONO_CONTEXT_GET_IP (ctx) - (guint8*)ji->code_start), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp);
830 method = jinfo_get_method (ji);
831 g_assert (method);
833 if (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
834 goto exit;
837 * FIXME:
838 * Stopping in memset makes half-initialized vtypes visible.
839 * Stopping in memcpy makes half-copied vtypes visible.
841 if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy")))
842 goto exit;
845 * This could be in mono_de_ss_update method, but mono_find_next_seq_point_for_native_offset is pretty expensive method,
846 * hence we prefer this check here.
848 if (ss_req->user_assemblies) {
849 gboolean found = FALSE;
850 for (int k = 0; ss_req->user_assemblies[k]; k++)
851 if (ss_req->user_assemblies[k] == m_class_get_image (method->klass)->assembly) {
852 found = TRUE;
853 break;
855 if (!found)
856 goto exit;
860 * The ip points to the instruction causing the single step event, which is before
861 * the offset recorded in the seq point map, so find the next seq point after ip.
863 if (!mono_find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info, &sp)) {
864 g_assert_not_reached ();
865 goto exit;
868 il_offset = sp.il_offset;
870 if (!mono_de_ss_update (ss_req, ji, &sp, tls, ctx, method))
871 goto exit;
873 /* Start single stepping again from the current sequence point */
875 SingleStepArgs args;
876 memset (&args, 0, sizeof (args));
877 args.method = method;
878 args.ctx = ctx;
879 args.tls = tls;
880 args.step_to_catch = FALSE;
881 args.sp = sp;
882 args.info = info;
883 args.frames = NULL;
884 args.nframes = 0;
885 mono_de_ss_start (ss_req, &args);
887 if ((ss_req->filter & STEP_FILTER_STATIC_CTOR) &&
888 (method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
889 !strcmp (method->name, ".cctor"))
890 goto exit;
892 // FIXME: Has to lock earlier
894 reqs = g_ptr_array_new ();
896 mono_loader_lock ();
898 g_ptr_array_add (reqs, ss_req->req);
900 void *bp_events;
901 bp_events = rt_callbacks.create_breakpoint_events (reqs, NULL, ji, EVENT_KIND_BREAKPOINT);
903 g_ptr_array_free (reqs, TRUE);
905 mono_loader_unlock ();
907 rt_callbacks.process_breakpoint_events (bp_events, method, ctx, il_offset);
909 exit:
910 mono_de_ss_req_release (ss_req);
914 * mono_de_ss_update:
916 * Return FALSE if single stepping needs to continue.
918 static gboolean
919 mono_de_ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, void *tls, MonoContext *ctx, MonoMethod* method)
921 MonoDebugMethodInfo *minfo;
922 MonoDebugSourceLocation *loc = NULL;
923 gboolean hit = TRUE;
925 if ((req->filter & STEP_FILTER_STATIC_CTOR)) {
926 DbgEngineStackFrame **frames;
927 int nframes;
928 rt_callbacks.ss_calculate_framecount (tls, ctx, TRUE, &frames, &nframes);
930 gboolean ret = FALSE;
931 gboolean method_in_stack = FALSE;
933 for (int i = 0; i < nframes; i++) {
934 MonoMethod *external_method = frames [i]->method;
935 if (method == external_method)
936 method_in_stack = TRUE;
938 if (!ret) {
939 ret = (external_method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME);
940 ret = ret && !strcmp (external_method->name, ".cctor");
941 ret = ret && (external_method != req->start_method);
945 if (!method_in_stack) {
946 PRINT_ERROR_MSG ("[%p] The instruction pointer of the currently executing method(%s) is not on the recorded stack. This is likely due to a runtime bug. The %d frames are as follow: \n", (gpointer)(gsize)mono_native_thread_id_get (), mono_method_full_name (method, TRUE), nframes);
947 /*PRINT_DEBUG_MSG (1, "[%p] The instruction pointer of the currently executing method(%s) is not on the recorded stack. This is likely due to a runtime bug. The %d frames are as follow: \n", (gpointer)(gsize)mono_native_thread_id_get (), mono_method_full_name (method, TRUE), tls->frame_count);*/
949 for (int i=0; i < nframes; i++)
950 PRINT_ERROR_MSG ("\t [%p] Frame (%d / %d): %s\n", (gpointer)(gsize)mono_native_thread_id_get (), i, nframes, mono_method_full_name (frames [i]->method, TRUE));
953 rt_callbacks.ss_discard_frame_context (tls);
955 if (ret)
956 return FALSE;
959 if (req->async_stepout_method == method) {
960 PRINT_DEBUG_MSG (1, "[%p] Breakpoint hit during async step-out at %s hit, continuing stepping out.\n", (gpointer)(gsize)mono_native_thread_id_get (), method->name);
961 return FALSE;
964 if (req->depth == STEP_DEPTH_OVER && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) && !(sp->flags & MONO_SEQ_POINT_FLAG_NESTED_CALL)) {
966 * These seq points are inserted by the JIT after calls, step over needs to skip them.
968 PRINT_DEBUG_MSG (1, "[%p] Seq point at nonempty stack %x while stepping over, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), sp->il_offset);
969 return FALSE;
972 if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit && !req->async_stepout_method) {
973 gboolean is_step_out = req->depth == STEP_DEPTH_OUT;
974 int nframes;
975 rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes);
977 // Because functions can call themselves recursively, we need to make sure we're stopping at the right stack depth.
978 // In case of step out, the target is the frame *enclosing* the one where the request was made.
979 int target_frames = req->nframes + (is_step_out ? -1 : 0);
980 if (req->nframes > 0 && nframes > 0 && nframes > target_frames) {
981 /* Hit the breakpoint in a recursive call, don't halt */
982 PRINT_DEBUG_MSG (1, "[%p] Breakpoint at lower frame while stepping %s, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), is_step_out ? "out" : "over");
983 return FALSE;
987 if (req->depth == STEP_DEPTH_INTO && req->size == STEP_SIZE_MIN && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) && req->start_method) {
988 int nframes;
989 rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes);
990 if (req->start_method == method && req->nframes && nframes == req->nframes) { //Check also frame count(could be recursion)
991 PRINT_DEBUG_MSG (1, "[%p] Seq point at nonempty stack %x while stepping in, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), sp->il_offset);
992 return FALSE;
996 MonoDebugMethodAsyncInfo* async_method = mono_debug_lookup_method_async_debug_info (method);
997 if (async_method) {
998 for (int i = 0; i < async_method->num_awaits; i++) {
999 if (async_method->yield_offsets[i] == sp->il_offset || async_method->resume_offsets[i] == sp->il_offset) {
1000 mono_debug_free_method_async_debug_info (async_method);
1001 return FALSE;
1004 mono_debug_free_method_async_debug_info (async_method);
1007 if (req->size != STEP_SIZE_LINE)
1008 return TRUE;
1010 /* Have to check whenever a different source line was reached */
1011 minfo = mono_debug_lookup_method (method);
1013 if (minfo)
1014 loc = mono_debug_method_lookup_location (minfo, sp->il_offset);
1016 if (!loc) {
1017 PRINT_DEBUG_MSG (1, "[%p] No line number info for il offset %x, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), sp->il_offset);
1018 req->last_method = method;
1019 hit = FALSE;
1020 } else if (loc && method == req->last_method && loc->row == req->last_line) {
1021 int nframes;
1022 rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes);
1023 if (nframes == req->nframes) { // If the frame has changed we're clearly not on the same source line.
1024 PRINT_DEBUG_MSG (1, "[%p] Same source line (%d), continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), loc->row);
1025 hit = FALSE;
1029 if (loc) {
1030 req->last_method = method;
1031 req->last_line = loc->row;
1032 mono_debug_free_source_location (loc);
1035 return hit;
1038 void
1039 mono_de_process_breakpoint (void *void_tls, gboolean from_signal)
1041 DebuggerTlsData *tls = (DebuggerTlsData*)void_tls;
1042 MonoJitInfo *ji;
1043 guint8 *ip;
1044 int i;
1045 guint32 native_offset;
1046 GPtrArray *bp_reqs, *ss_reqs_orig, *ss_reqs;
1047 EventKind kind = EVENT_KIND_BREAKPOINT;
1048 MonoContext *ctx = rt_callbacks.tls_get_restore_state (tls);
1049 MonoMethod *method;
1050 MonoSeqPointInfo *info;
1051 SeqPoint sp;
1052 gboolean found_sp;
1054 if (rt_callbacks.try_process_suspend (tls, ctx, TRUE))
1055 return;
1057 ip = (guint8 *)MONO_CONTEXT_GET_IP (ctx);
1059 ji = get_top_method_ji (ip, NULL, (gpointer*)&ip);
1060 g_assert (ji && !ji->is_trampoline);
1061 method = jinfo_get_method (ji);
1063 /* Compute the native offset of the breakpoint from the ip */
1064 native_offset = ip - (guint8*)ji->code_start;
1066 if (!rt_callbacks.begin_breakpoint_processing (tls, ctx, ji, from_signal))
1067 return;
1069 if (method->wrapper_type)
1070 return;
1072 bp_reqs = g_ptr_array_new ();
1073 ss_reqs = g_ptr_array_new ();
1074 ss_reqs_orig = g_ptr_array_new ();
1076 mono_loader_lock ();
1079 * The ip points to the instruction causing the breakpoint event, which is after
1080 * the offset recorded in the seq point map, so find the prev seq point before ip.
1082 found_sp = mono_find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info, &sp);
1084 if (!found_sp)
1085 no_seq_points_found (method, native_offset);
1087 g_assert (found_sp);
1089 PRINT_DEBUG_MSG (1, "[%p] Breakpoint hit, method=%s, ip=%p, [il=0x%x,native=0x%x].\n", (gpointer) (gsize) mono_native_thread_id_get (), method->name, ip, sp.il_offset, native_offset);
1091 mono_debugger_log_bp_hit (tls, method, sp.il_offset);
1093 mono_de_collect_breakpoints_by_sp (&sp, ji, ss_reqs_orig, bp_reqs);
1095 if (bp_reqs->len == 0 && ss_reqs_orig->len == 0) {
1096 /* Maybe a method entry/exit event */
1097 if (sp.il_offset == METHOD_ENTRY_IL_OFFSET)
1098 kind = EVENT_KIND_METHOD_ENTRY;
1099 else if (sp.il_offset == METHOD_EXIT_IL_OFFSET)
1100 kind = EVENT_KIND_METHOD_EXIT;
1103 /* Process single step requests */
1104 for (i = 0; i < ss_reqs_orig->len; ++i) {
1105 EventRequest *req = (EventRequest *)g_ptr_array_index (ss_reqs_orig, i);
1106 SingleStepReq *ss_req = (SingleStepReq *)req->info;
1107 gboolean hit;
1109 //if we hit async_stepout_method, it's our no matter which thread
1110 if ((ss_req->async_stepout_method != method) && (ss_req->async_id || mono_thread_internal_current () != ss_req->thread)) {
1111 DbgEngineStackFrame **frames;
1112 int nframes;
1113 //We have different thread and we don't have async stepping in progress
1114 //it's breakpoint in parallel thread, ignore it
1115 if (ss_req->async_id == 0)
1116 continue;
1118 rt_callbacks.ss_discard_frame_context (tls);
1119 rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, &frames, &nframes);
1120 //make sure we have enough data to get current async method instance id
1121 if (nframes == 0 || !rt_callbacks.ensure_jit (frames [0]))
1122 continue;
1124 //Check method is async before calling get_this_async_id
1125 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
1126 if (!asyncMethod)
1127 continue;
1128 else
1129 mono_debug_free_method_async_debug_info (asyncMethod);
1131 //breakpoint was hit in parallelly executing async method, ignore it
1132 if (ss_req->async_id != rt_callbacks.get_this_async_id (frames [0]))
1133 continue;
1136 //Update stepping request to new thread/frame_count that we are continuing on
1137 //so continuing with normal stepping works as expected
1138 if (ss_req->async_stepout_method || ss_req->async_id) {
1139 int nframes;
1140 rt_callbacks.ss_discard_frame_context (tls);
1141 rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes);
1142 ss_req->thread = mono_thread_internal_current ();
1143 ss_req->nframes = nframes;
1146 hit = mono_de_ss_update (ss_req, ji, &sp, tls, ctx, method);
1147 if (hit)
1148 g_ptr_array_add (ss_reqs, req);
1150 SingleStepArgs args;
1151 memset (&args, 0, sizeof (args));
1152 args.method = method;
1153 args.ctx = ctx;
1154 args.tls = tls;
1155 args.step_to_catch = FALSE;
1156 args.sp = sp;
1157 args.info = info;
1158 args.frames = NULL;
1159 args.nframes = 0;
1160 mono_de_ss_start (ss_req, &args);
1163 void *bp_events = rt_callbacks.create_breakpoint_events (ss_reqs, bp_reqs, ji, kind);
1165 mono_loader_unlock ();
1167 g_ptr_array_free (bp_reqs, TRUE);
1168 g_ptr_array_free (ss_reqs, TRUE);
1170 rt_callbacks.process_breakpoint_events (bp_events, method, ctx, 0);
1174 * ss_bp_is_unique:
1176 * Reject breakpoint if it is a duplicate of one already in list or hash table.
1178 static gboolean
1179 ss_bp_is_unique (GSList *bps, GHashTable *ss_req_bp_cache, MonoMethod *method, guint32 il_offset)
1181 if (ss_req_bp_cache) {
1182 MonoBreakpoint dummy = {method, (long)il_offset, NULL, NULL};
1183 return !g_hash_table_lookup (ss_req_bp_cache, &dummy);
1185 for (GSList *l = bps; l; l = l->next) {
1186 MonoBreakpoint *bp = (MonoBreakpoint *)l->data;
1187 if (bp->method == method && bp->il_offset == il_offset)
1188 return FALSE;
1190 return TRUE;
1194 * ss_bp_eq:
1196 * GHashTable equality for a MonoBreakpoint (only care about method and il_offset fields)
1198 static gint
1199 ss_bp_eq (gconstpointer ka, gconstpointer kb)
1201 const MonoBreakpoint *s1 = (const MonoBreakpoint *)ka;
1202 const MonoBreakpoint *s2 = (const MonoBreakpoint *)kb;
1203 return (s1->method == s2->method && s1->il_offset == s2->il_offset) ? 1 : 0;
1207 * ss_bp_eq:
1209 * GHashTable hash for a MonoBreakpoint (only care about method and il_offset fields)
1211 static guint
1212 ss_bp_hash (gconstpointer data)
1214 const MonoBreakpoint *s = (const MonoBreakpoint *)data;
1215 guint hash = (guint) (uintptr_t) s->method;
1216 hash ^= ((guint)s->il_offset) << 16; // Assume low bits are more interesting
1217 hash ^= ((guint)s->il_offset) >> 16;
1218 return hash;
1221 #define MAX_LINEAR_SCAN_BPS 7
1224 * ss_bp_add_one:
1226 * Create a new breakpoint and add it to a step request.
1227 * Will adjust the bp count and cache used by mono_de_ss_start.
1229 static void
1230 ss_bp_add_one (SingleStepReq *ss_req, int *ss_req_bp_count, GHashTable **ss_req_bp_cache,
1231 MonoMethod *method, guint32 il_offset)
1233 // This list is getting too long, switch to using the hash table
1234 if (!*ss_req_bp_cache && *ss_req_bp_count > MAX_LINEAR_SCAN_BPS) {
1235 *ss_req_bp_cache = g_hash_table_new (ss_bp_hash, ss_bp_eq);
1236 for (GSList *l = ss_req->bps; l; l = l->next)
1237 g_hash_table_insert (*ss_req_bp_cache, l->data, l->data);
1240 if (ss_bp_is_unique (ss_req->bps, *ss_req_bp_cache, method, il_offset)) {
1241 // Create and add breakpoint
1242 MonoBreakpoint *bp = mono_de_set_breakpoint (method, il_offset, ss_req->req, NULL);
1243 ss_req->bps = g_slist_append (ss_req->bps, bp);
1244 if (*ss_req_bp_cache)
1245 g_hash_table_insert (*ss_req_bp_cache, bp, bp);
1246 (*ss_req_bp_count)++;
1247 } else {
1248 PRINT_DEBUG_MSG (1, "[dbg] Candidate breakpoint at %s:[il=0x%x] is a duplicate for this step request, will not add.\n", mono_method_full_name (method, TRUE), (int)il_offset);
1252 static gboolean
1253 is_last_non_empty (SeqPoint* sp, MonoSeqPointInfo *info)
1255 if (!sp->next_len)
1256 return TRUE;
1257 SeqPoint* next = g_new (SeqPoint, sp->next_len);
1258 mono_seq_point_init_next (info, *sp, next);
1259 for (int i = 0; i < sp->next_len; i++) {
1260 if (next [i].flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK && !(next [i].flags & MONO_SEQ_POINT_FLAG_NESTED_CALL)) {
1261 if (!is_last_non_empty (&next [i], info)) {
1262 g_free (next);
1263 return FALSE;
1265 } else {
1266 g_free (next);
1267 return FALSE;
1270 g_free (next);
1271 return TRUE;
1275 * mono_de_ss_start:
1277 * Start the single stepping operation given by SS_REQ from the sequence point SP.
1278 * If CTX is not set, then this can target any thread. If CTX is set, then TLS should
1279 * belong to the same thread as CTX.
1280 * If FRAMES is not-null, use that instead of tls->frames for placing breakpoints etc.
1282 static void
1283 mono_de_ss_start (SingleStepReq *ss_req, SingleStepArgs *ss_args)
1285 int i, j, frame_index;
1286 SeqPoint *next_sp, *parent_sp = NULL;
1287 SeqPoint local_sp, local_parent_sp;
1288 gboolean found_sp;
1289 MonoSeqPointInfo *parent_info;
1290 MonoMethod *parent_sp_method = NULL;
1291 gboolean enable_global = FALSE;
1293 // When 8 or more entries are in bps, we build a hash table to serve as a set of breakpoints.
1294 // Recreating this on each pass is a little wasteful but at least keeps behavior linear.
1295 int ss_req_bp_count = g_slist_length (ss_req->bps);
1296 GHashTable *ss_req_bp_cache = NULL;
1298 /* Stop the previous operation */
1299 ss_stop (ss_req);
1301 gboolean locked = FALSE;
1303 void *tls = ss_args->tls;
1304 MonoMethod *method = ss_args->method;
1305 DbgEngineStackFrame **frames = ss_args->frames;
1306 int nframes = ss_args->nframes;
1307 SeqPoint *sp = &ss_args->sp;
1309 /* this can happen on a single step in a exception on android (Mono_UnhandledException_internal) and on IOS */
1310 if (!method)
1311 return;
1314 * Implement single stepping using breakpoints if possible.
1316 if (ss_args->step_to_catch) {
1317 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, sp->il_offset);
1318 } else {
1319 frame_index = 1;
1321 #ifndef TARGET_WASM
1322 if (ss_args->ctx && !frames) {
1323 #else
1324 if (!frames) {
1325 #endif
1326 mono_loader_lock ();
1327 locked = TRUE;
1329 /* Need parent frames */
1330 rt_callbacks.ss_calculate_framecount (tls, ss_args->ctx, FALSE, &frames, &nframes);
1333 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
1335 /* Need to stop in catch clauses as well */
1336 for (i = ss_req->depth == STEP_DEPTH_OUT ? 1 : 0; i < nframes; ++i) {
1337 DbgEngineStackFrame *frame = frames [i];
1339 if (frame->ji) {
1340 MonoJitInfo *jinfo = frame->ji;
1341 for (j = 0; j < jinfo->num_clauses; ++j) {
1342 // In case of async method we don't want to place breakpoint on last catch handler(which state machine added for whole method)
1343 if (asyncMethod && asyncMethod->num_awaits && i == 0 && j + 1 == jinfo->num_clauses)
1344 break;
1345 MonoJitExceptionInfo *ei = &jinfo->clauses [j];
1347 if (mono_find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL, &local_sp))
1348 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, frame->method, local_sp.il_offset);
1353 if (asyncMethod && asyncMethod->num_awaits && nframes && rt_callbacks.ensure_jit (frames [0])) {
1354 //asyncMethod has value and num_awaits > 0, this means we are inside async method with awaits
1356 // Check if we hit yield_offset during normal stepping, because if we did...
1357 // Go into special async stepping mode which places breakpoint on resumeOffset
1358 // of this await call and sets async_id so we can distinguish it from parallel executions
1359 for (i = 0; i < asyncMethod->num_awaits; i++) {
1360 if (sp->il_offset == asyncMethod->yield_offsets [i]) {
1361 ss_req->async_id = rt_callbacks.get_this_async_id (frames [0]);
1362 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, asyncMethod->resume_offsets [i]);
1363 g_hash_table_destroy (ss_req_bp_cache);
1364 mono_debug_free_method_async_debug_info (asyncMethod);
1365 if (locked)
1366 mono_loader_unlock ();
1367 goto cleanup;
1370 //If we are at end of async method and doing step-in or step-over...
1371 //Switch to step-out, so whole NotifyDebuggerOfWaitCompletion magic happens...
1372 if (is_last_non_empty (sp, ss_args->info)) {
1373 ss_req->depth = STEP_DEPTH_OUT;//setting depth to step-out is important, don't inline IF, because code later depends on this
1375 if (ss_req->depth == STEP_DEPTH_OUT) {
1376 //If we are inside `async void` method, do normal step-out
1377 if (rt_callbacks.set_set_notification_for_wait_completion_flag (frames [0])) {
1378 ss_req->async_id = rt_callbacks.get_this_async_id (frames [0]);
1379 ss_req->async_stepout_method = rt_callbacks.get_notify_debugger_of_wait_completion_method ();
1380 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, ss_req->async_stepout_method, 0);
1381 g_hash_table_destroy (ss_req_bp_cache);
1382 mono_debug_free_method_async_debug_info (asyncMethod);
1383 if (locked)
1384 mono_loader_unlock ();
1385 goto cleanup;
1390 if (asyncMethod)
1391 mono_debug_free_method_async_debug_info (asyncMethod);
1394 * Find the first sequence point in the current or in a previous frame which
1395 * is not the last in its method.
1397 if (ss_req->depth == STEP_DEPTH_OUT) {
1398 /* Ignore seq points in current method */
1399 while (frame_index < nframes) {
1400 DbgEngineStackFrame *frame = frames [frame_index];
1402 method = frame->method;
1403 found_sp = mono_find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &ss_args->info, &local_sp);
1404 sp = (found_sp)? &local_sp : NULL;
1405 frame_index ++;
1406 if (sp && sp->next_len != 0)
1407 break;
1409 // There could be method calls before the next seq point in the caller when using nested calls
1410 //enable_global = TRUE;
1411 } else {
1412 if (sp && sp->next_len == 0) {
1413 sp = NULL;
1414 while (frame_index < nframes) {
1415 DbgEngineStackFrame *frame = frames [frame_index];
1417 method = frame->method;
1418 found_sp = mono_find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &ss_args->info, &local_sp);
1419 sp = (found_sp)? &local_sp : NULL;
1420 if (sp && sp->next_len != 0)
1421 break;
1422 sp = NULL;
1423 frame_index ++;
1425 } else {
1426 /* Have to put a breakpoint into a parent frame since the seq points might not cover all control flow out of the method */
1427 while (frame_index < nframes) {
1428 DbgEngineStackFrame *frame = frames [frame_index];
1430 parent_sp_method = frame->method;
1431 found_sp = mono_find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &parent_info, &local_parent_sp);
1432 parent_sp = found_sp ? &local_parent_sp : NULL;
1433 if (found_sp && parent_sp->next_len != 0)
1434 break;
1435 parent_sp = NULL;
1436 frame_index ++;
1441 if (sp && sp->next_len > 0) {
1442 SeqPoint* next = g_new(SeqPoint, sp->next_len);
1444 mono_seq_point_init_next (ss_args->info, *sp, next);
1445 for (i = 0; i < sp->next_len; i++) {
1446 next_sp = &next[i];
1448 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, next_sp->il_offset);
1450 g_free (next);
1453 if (parent_sp) {
1454 SeqPoint* next = g_new(SeqPoint, parent_sp->next_len);
1456 mono_seq_point_init_next (parent_info, *parent_sp, next);
1457 for (i = 0; i < parent_sp->next_len; i++) {
1458 next_sp = &next[i];
1460 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, parent_sp_method, next_sp->il_offset);
1462 g_free (next);
1465 if (ss_req->nframes == 0)
1466 ss_req->nframes = nframes;
1468 if ((ss_req->depth == STEP_DEPTH_OVER) && (!sp && !parent_sp)) {
1469 PRINT_DEBUG_MSG (1, "[dbg] No parent frame for step over, transition to step into.\n");
1471 * This is needed since if we leave managed code, and later return to it, step over
1472 * is not going to stop.
1473 * This approach is a bit ugly, since we change the step depth, but it only affects
1474 * clients who reuse the same step request, and only in this special case.
1476 ss_req->depth = STEP_DEPTH_INTO;
1479 if (ss_req->depth == STEP_DEPTH_INTO) {
1480 /* Enable global stepping so we stop at method entry too */
1481 enable_global = TRUE;
1485 * The ctx/frame info computed above will become invalid when we continue.
1487 rt_callbacks.ss_discard_frame_context (tls);
1490 if (enable_global) {
1491 PRINT_DEBUG_MSG (1, "[dbg] Turning on global single stepping.\n");
1492 ss_req->global = TRUE;
1493 mono_de_start_single_stepping ();
1494 } else if (!ss_req->bps) {
1495 PRINT_DEBUG_MSG (1, "[dbg] Turning on global single stepping.\n");
1496 ss_req->global = TRUE;
1497 mono_de_start_single_stepping ();
1498 } else {
1499 ss_req->global = FALSE;
1502 g_hash_table_destroy (ss_req_bp_cache);
1504 if (locked)
1505 mono_loader_unlock ();
1507 cleanup:
1508 rt_callbacks.ss_args_destroy (ss_args);
1513 * Start single stepping of thread THREAD
1515 DbgEngineErrorCode
1516 mono_de_ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilter filter, EventRequest *req)
1518 int err = rt_callbacks.ensure_runtime_is_suspended ();
1519 if (err)
1520 return err;
1522 // FIXME: Multiple requests
1523 if (ss_req_count () > 1) {
1524 err = rt_callbacks.handle_multiple_ss_requests ();
1526 if (err == DE_ERR_NOT_IMPLEMENTED) {
1527 PRINT_DEBUG_MSG (0, "Received a single step request while the previous one was still active.\n");
1528 return DE_ERR_NOT_IMPLEMENTED;
1532 PRINT_DEBUG_MSG (1, "[dbg] Starting single step of thread %p (depth=%s).\n", thread, ss_depth_to_string (depth));
1534 SingleStepReq *ss_req = g_new0 (SingleStepReq, 1);
1535 ss_req->req = req;
1536 ss_req->thread = thread;
1537 ss_req->size = size;
1538 ss_req->depth = depth;
1539 ss_req->filter = filter;
1540 ss_req->refcount = 1;
1541 req->info = ss_req;
1543 for (int i = 0; i < req->nmodifiers; i++) {
1544 if (req->modifiers[i].kind == MOD_KIND_ASSEMBLY_ONLY) {
1545 ss_req->user_assemblies = req->modifiers[i].data.assemblies;
1546 break;
1550 SingleStepArgs args;
1551 err = rt_callbacks.ss_create_init_args (ss_req, &args);
1552 if (err)
1553 return err;
1554 g_ptr_array_add (the_ss_reqs, ss_req);
1556 mono_de_ss_start (ss_req, &args);
1558 return DE_ERR_NONE;
1562 * mono_de_set_log_level:
1564 * Configures logging level and output file. Must be called together with mono_de_init.
1566 void
1567 mono_de_set_log_level (int level, FILE *file)
1569 log_level = level;
1570 log_file = file;
1574 * mono_de_init:
1576 * Inits the shared debugger engine. Not reentrant.
1578 void
1579 mono_de_init (DebuggerEngineCallbacks *cbs)
1581 rt_callbacks = *cbs;
1582 mono_coop_mutex_init_recursive (&debug_mutex);
1584 domains_init ();
1585 breakpoints_init ();
1586 ss_req_init ();
1587 mono_debugger_log_init ();
1590 void
1591 mono_de_cleanup (void)
1593 breakpoints_cleanup ();
1594 domains_cleanup ();
1595 ss_req_cleanup ();
1598 void
1599 mono_debugger_free_objref (gpointer value)
1601 ObjRef *o = (ObjRef *)value;
1603 mono_gchandle_free_internal (o->handle);
1605 g_free (o);
1608 // Returns true if TaskBuilder has NotifyDebuggerOfWaitCompletion method
1609 // false if not(AsyncVoidBuilder)
1610 MonoClass *
1611 get_class_to_get_builder_field(DbgEngineStackFrame *frame)
1613 ERROR_DECL (error);
1614 gpointer this_addr = get_this_addr (frame);
1615 MonoClass *original_class = frame->method->klass;
1616 MonoClass *ret;
1617 if (!m_class_is_valuetype (original_class) && mono_class_is_open_constructed_type (m_class_get_byval_arg (original_class))) {
1618 MonoObject *this_obj = *(MonoObject**)this_addr;
1619 MonoGenericContext context;
1620 MonoType *inflated_type;
1622 if (!this_obj)
1623 return NULL;
1625 context = mono_get_generic_context_from_stack_frame (frame->ji, this_obj->vtable);
1626 inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (original_class), &context, error);
1627 mono_error_assert_ok (error); /* FIXME don't swallow the error */
1629 ret = mono_class_from_mono_type_internal (inflated_type);
1630 mono_metadata_free_type (inflated_type);
1631 return ret;
1633 return original_class;
1637 gboolean
1638 set_set_notification_for_wait_completion_flag (DbgEngineStackFrame *frame)
1640 MonoClassField *builder_field = mono_class_get_field_from_name_full (get_class_to_get_builder_field(frame), "<>t__builder", NULL);
1641 if (!builder_field)
1642 return FALSE;
1643 gpointer builder = get_async_method_builder (frame);
1644 if (!builder)
1645 return FALSE;
1647 MonoMethod* method = get_set_notification_method (mono_class_from_mono_type_internal (builder_field->type));
1648 if (method == NULL)
1649 return FALSE;
1650 gboolean arg = TRUE;
1651 ERROR_DECL (error);
1652 void *args [ ] = { &arg };
1653 mono_runtime_invoke_checked (method, builder, args, error);
1654 mono_error_assert_ok (error);
1655 return TRUE;
1658 MonoMethod*
1659 get_object_id_for_debugger_method (MonoClass* async_builder_class)
1661 ERROR_DECL (error);
1662 GPtrArray *array = mono_class_get_methods_by_name (async_builder_class, "get_ObjectIdForDebugger", 0x24, 1, FALSE, error);
1663 mono_error_assert_ok (error);
1664 if (array->len != 1) {
1665 g_ptr_array_free (array, TRUE);
1666 //if we don't find method get_ObjectIdForDebugger we try to find the property Task to continue async debug.
1667 MonoProperty *prop = mono_class_get_property_from_name_internal (async_builder_class, "Task");
1668 if (!prop) {
1669 PRINT_DEBUG_MSG (1, "Impossible to debug async methods.\n");
1670 return NULL;
1672 return prop->get;
1674 MonoMethod *method = (MonoMethod *)g_ptr_array_index (array, 0);
1675 g_ptr_array_free (array, TRUE);
1676 return method;
1679 gpointer
1680 get_this_addr (DbgEngineStackFrame *the_frame)
1682 StackFrame *frame = (StackFrame *)the_frame;
1683 if (frame->de.ji->is_interp)
1684 return mini_get_interp_callbacks ()->frame_get_this (frame->interp_frame);
1686 MonoDebugVarInfo *var = frame->jit->this_var;
1687 if ((var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS) != MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET)
1688 return NULL;
1690 guint8 *addr = (guint8 *)mono_arch_context_get_int_reg (&frame->ctx, var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS);
1691 addr += (gint32)var->offset;
1692 return addr;
1695 /* Return the address of the AsyncMethodBuilder struct belonging to the state machine method pointed to by FRAME */
1696 gpointer
1697 get_async_method_builder (DbgEngineStackFrame *frame)
1699 MonoObject *this_obj;
1700 MonoClassField *builder_field;
1701 gpointer builder;
1702 gpointer this_addr;
1703 MonoClass* klass = frame->method->klass;
1705 klass = get_class_to_get_builder_field(frame);
1706 builder_field = mono_class_get_field_from_name_full (klass, "<>t__builder", NULL);
1707 if (!builder_field)
1708 return NULL;
1710 this_addr = get_this_addr (frame);
1711 if (!this_addr)
1712 return NULL;
1714 if (m_class_is_valuetype (klass)) {
1715 builder = mono_vtype_get_field_addr (*(guint8**)this_addr, builder_field);
1716 } else {
1717 this_obj = *(MonoObject**)this_addr;
1718 builder = (char*)this_obj + builder_field->offset;
1721 return builder;
1724 MonoMethod*
1725 get_set_notification_method (MonoClass* async_builder_class)
1727 ERROR_DECL (error);
1728 GPtrArray* array = mono_class_get_methods_by_name (async_builder_class, "SetNotificationForWaitCompletion", 0x24, 1, FALSE, error);
1729 mono_error_assert_ok (error);
1730 if (array->len == 0) {
1731 g_ptr_array_free (array, TRUE);
1732 return NULL;
1734 MonoMethod* set_notification_method = (MonoMethod *)g_ptr_array_index (array, 0);
1735 g_ptr_array_free (array, TRUE);
1736 return set_notification_method;
1739 static MonoMethod* notify_debugger_of_wait_completion_method_cache;
1741 MonoMethod*
1742 get_notify_debugger_of_wait_completion_method (void)
1744 if (notify_debugger_of_wait_completion_method_cache != NULL)
1745 return notify_debugger_of_wait_completion_method_cache;
1746 ERROR_DECL (error);
1747 MonoClass* task_class = mono_class_load_from_name (mono_defaults.corlib, "System.Threading.Tasks", "Task");
1748 GPtrArray* array = mono_class_get_methods_by_name (task_class, "NotifyDebuggerOfWaitCompletion", 0x24, 1, FALSE, error);
1749 mono_error_assert_ok (error);
1750 g_assert (array->len == 1);
1751 notify_debugger_of_wait_completion_method_cache = (MonoMethod *)g_ptr_array_index (array, 0);
1752 g_ptr_array_free (array, TRUE);
1753 return notify_debugger_of_wait_completion_method_cache;
1757 #endif