[tests] Disable tests that crash on android sdks (#17255)
[mono-project.git] / mono / mini / debugger-engine.c
blobfc5624c70d10b788378e348012482e8285400664
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;
36 #ifdef HOST_ANDROID
37 #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { g_print (__VA_ARGS__); } } while (0)
38 #else
39 #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { fprintf (log_file, __VA_ARGS__); fflush (log_file); } } while (0)
40 #endif
43 * Locking
45 #define dbg_lock() mono_coop_mutex_lock (&debug_mutex)
46 #define dbg_unlock() mono_coop_mutex_unlock (&debug_mutex)
47 static MonoCoopMutex debug_mutex;
49 void
50 mono_de_lock (void)
52 dbg_lock ();
55 void
56 mono_de_unlock (void)
58 dbg_unlock ();
63 * Domain support
67 /* A hash table containing all active domains */
68 /* Protected by the loader lock */
69 static GHashTable *domains;
72 static void
73 domains_init (void)
75 domains = g_hash_table_new (mono_aligned_addr_hash, NULL);
78 static void
79 domains_cleanup (void)
81 //FIXME can we safely destroy `domains`?
85 * mono_de_foreach_domain:
87 * Iterate over all domains under debugging. Caller must take the loader lock.
89 * FIXME can we move the locking to here? Callers in sdb must be properly audited.
91 void
92 mono_de_foreach_domain (GHFunc func, gpointer user_data)
94 g_hash_table_foreach (domains, func, user_data);
98 * LOCKING: Takes the loader lock
100 void
101 mono_de_domain_remove (MonoDomain *domain)
103 mono_loader_lock ();
104 g_hash_table_remove (domains, domain);
105 mono_loader_unlock ();
109 * LOCKING: Takes the loader lock
111 void
112 mono_de_domain_add (MonoDomain *domain)
114 mono_loader_lock ();
115 g_hash_table_insert (domains, domain, domain);
116 mono_loader_unlock ();
120 * BREAKPOINTS
123 /* List of breakpoints */
124 /* Protected by the loader lock */
125 static GPtrArray *breakpoints;
126 /* Maps breakpoint locations to the number of breakpoints at that location */
127 static GHashTable *bp_locs;
129 static void
130 breakpoints_init (void)
132 breakpoints = g_ptr_array_new ();
133 bp_locs = g_hash_table_new (NULL, NULL);
137 * insert_breakpoint:
139 * Insert the breakpoint described by BP into the method described by
140 * JI.
142 static void
143 insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo *ji, MonoBreakpoint *bp, MonoError *error)
145 int count;
146 BreakpointInstance *inst;
147 SeqPointIterator it;
148 gboolean it_has_sp = FALSE;
150 if (error)
151 error_init (error);
153 mono_seq_point_iterator_init (&it, seq_points);
154 while (mono_seq_point_iterator_next (&it)) {
155 if (it.seq_point.il_offset == bp->il_offset) {
156 it_has_sp = TRUE;
157 break;
161 if (!it_has_sp) {
163 * The set of IL offsets with seq points doesn't completely match the
164 * info returned by CMD_METHOD_GET_DEBUG_INFO (#407).
166 mono_seq_point_iterator_init (&it, seq_points);
167 while (mono_seq_point_iterator_next (&it)) {
168 if (it.seq_point.il_offset != METHOD_ENTRY_IL_OFFSET &&
169 it.seq_point.il_offset != METHOD_EXIT_IL_OFFSET &&
170 it.seq_point.il_offset + 1 == bp->il_offset) {
171 it_has_sp = TRUE;
172 break;
177 if (!it_has_sp) {
178 char *s = g_strdup_printf ("Unable to insert breakpoint at %s:%ld", mono_method_full_name (jinfo_get_method (ji), TRUE), bp->il_offset);
180 mono_seq_point_iterator_init (&it, seq_points);
181 while (mono_seq_point_iterator_next (&it))
182 DEBUG_PRINTF (1, "%d\n", it.seq_point.il_offset);
184 if (error) {
185 mono_error_set_error (error, MONO_ERROR_GENERIC, "%s", s);
186 g_warning ("%s", s);
187 g_free (s);
188 return;
189 } else {
190 g_warning ("%s", s);
191 g_free (s);
192 return;
196 inst = g_new0 (BreakpointInstance, 1);
197 inst->il_offset = it.seq_point.il_offset;
198 inst->native_offset = it.seq_point.native_offset;
199 inst->ip = (guint8*)ji->code_start + it.seq_point.native_offset;
200 inst->ji = ji;
201 inst->domain = domain;
203 mono_loader_lock ();
205 g_ptr_array_add (bp->children, inst);
207 mono_loader_unlock ();
209 dbg_lock ();
210 count = GPOINTER_TO_INT (g_hash_table_lookup (bp_locs, inst->ip));
211 g_hash_table_insert (bp_locs, inst->ip, GINT_TO_POINTER (count + 1));
212 dbg_unlock ();
214 if (it.seq_point.native_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
215 DEBUG_PRINTF (1, "[dbg] Attempting to insert seq point at dead IL offset %d, ignoring.\n", (int)bp->il_offset);
216 } else if (count == 0) {
217 if (ji->is_interp) {
218 mini_get_interp_callbacks ()->set_breakpoint (ji, inst->ip);
219 } else {
220 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
221 mono_arch_set_breakpoint (ji, inst->ip);
222 #else
223 NOT_IMPLEMENTED;
224 #endif
228 DEBUG_PRINTF (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);
231 static void
232 remove_breakpoint (BreakpointInstance *inst)
234 int count;
235 MonoJitInfo *ji = inst->ji;
236 guint8 *ip = inst->ip;
238 dbg_lock ();
239 count = GPOINTER_TO_INT (g_hash_table_lookup (bp_locs, ip));
240 g_hash_table_insert (bp_locs, ip, GINT_TO_POINTER (count - 1));
241 dbg_unlock ();
243 g_assert (count > 0);
245 if (count == 1 && inst->native_offset != SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
246 if (ji->is_interp) {
247 mini_get_interp_callbacks ()->clear_breakpoint (ji, ip);
248 } else {
249 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
250 mono_arch_clear_breakpoint (ji, ip);
251 #else
252 NOT_IMPLEMENTED;
253 #endif
255 DEBUG_PRINTF (1, "[dbg] Clear breakpoint at %s [%p].\n", mono_method_full_name (jinfo_get_method (ji), TRUE), ip);
260 * This doesn't take any locks.
262 static gboolean
263 bp_matches_method (MonoBreakpoint *bp, MonoMethod *method)
265 int i;
267 if (!bp->method)
268 return TRUE;
269 if (method == bp->method)
270 return TRUE;
271 if (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method)
272 return TRUE;
274 if (bp->method->is_inflated && method->is_inflated) {
275 MonoMethodInflated *bpimethod = (MonoMethodInflated*)bp->method;
276 MonoMethodInflated *imethod = (MonoMethodInflated*)method;
278 /* Open generic methods should match closed generic methods of the same class */
279 if (bpimethod->declaring == imethod->declaring && bpimethod->context.class_inst == imethod->context.class_inst && bpimethod->context.method_inst && bpimethod->context.method_inst->is_open) {
280 for (i = 0; i < bpimethod->context.method_inst->type_argc; ++i) {
281 MonoType *t1 = bpimethod->context.method_inst->type_argv [i];
283 /* FIXME: Handle !mvar */
284 if (t1->type != MONO_TYPE_MVAR)
285 return FALSE;
287 return TRUE;
291 return FALSE;
295 * mono_de_add_pending_breakpoints:
297 * Insert pending breakpoints into the newly JITted method METHOD.
299 void
300 mono_de_add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
302 int i, j;
303 MonoSeqPointInfo *seq_points;
304 MonoDomain *domain;
306 if (!breakpoints)
307 return;
309 domain = mono_domain_get ();
311 mono_loader_lock ();
313 for (i = 0; i < breakpoints->len; ++i) {
314 MonoBreakpoint *bp = (MonoBreakpoint *)g_ptr_array_index (breakpoints, i);
315 gboolean found = FALSE;
317 if (!bp_matches_method (bp, method))
318 continue;
320 for (j = 0; j < bp->children->len; ++j) {
321 BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, j);
323 if (inst->ji == ji)
324 found = TRUE;
327 if (!found) {
328 seq_points = (MonoSeqPointInfo *) ji->seq_points;
330 if (!seq_points) {
331 MonoMethod *jmethod = jinfo_get_method (ji);
332 if (jmethod->is_inflated) {
333 MonoJitInfo *seq_ji;
334 MonoMethod *declaring = mono_method_get_declaring_generic_method (jmethod);
335 mono_jit_search_all_backends_for_jit_info (domain, declaring, &seq_ji);
336 seq_points = (MonoSeqPointInfo *) seq_ji->seq_points;
340 if (!seq_points)
341 /* Could be AOT code, or above "search_all_backends" call could have failed */
342 continue;
344 insert_breakpoint (seq_points, domain, ji, bp, NULL);
348 mono_loader_unlock ();
351 static void
352 set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_points, MonoBreakpoint *bp, MonoError *error)
354 MonoJitInfo *ji;
356 if (error)
357 error_init (error);
359 (void)mono_jit_search_all_backends_for_jit_info (domain, method, &ji);
360 g_assert (ji);
362 insert_breakpoint (seq_points, domain, ji, bp, error);
365 typedef struct {
366 MonoBreakpoint *bp;
367 GPtrArray *methods;
368 GPtrArray *method_domains;
369 GPtrArray *method_seq_points;
370 } CollectDomainData;
372 static void
373 collect_domain_bp (gpointer key, gpointer value, gpointer user_data)
375 GHashTableIter iter;
376 MonoSeqPointInfo *seq_points;
377 MonoDomain *domain = (MonoDomain*)key;
378 CollectDomainData *ud = (CollectDomainData*)user_data;
379 MonoMethod *m;
381 mono_domain_lock (domain);
382 g_hash_table_iter_init (&iter, domain_jit_info (domain)->seq_points);
383 while (g_hash_table_iter_next (&iter, (void**)&m, (void**)&seq_points)) {
384 if (bp_matches_method (ud->bp, m)) {
385 /* Save the info locally to simplify the code inside the domain lock */
386 g_ptr_array_add (ud->methods, m);
387 g_ptr_array_add (ud->method_domains, domain);
388 g_ptr_array_add (ud->method_seq_points, seq_points);
391 mono_domain_unlock (domain);
394 void
395 mono_de_clear_all_breakpoints (void)
397 while (breakpoints->len)
398 mono_de_clear_breakpoint ((MonoBreakpoint*)g_ptr_array_index (breakpoints, 0));
402 * mono_de_set_breakpoint:
404 * Set a breakpoint at IL_OFFSET in METHOD.
405 * METHOD can be NULL, in which case a breakpoint is placed in all methods.
406 * METHOD can also be a generic method definition, in which case a breakpoint
407 * is placed in all instances of the method.
408 * If ERROR is non-NULL, then it is set and NULL is returnd if some breakpoints couldn't be
409 * inserted.
411 MonoBreakpoint*
412 mono_de_set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req, MonoError *error)
414 MonoBreakpoint *bp;
415 MonoDomain *domain;
416 MonoMethod *m;
417 MonoSeqPointInfo *seq_points;
418 GPtrArray *methods;
419 GPtrArray *method_domains;
420 GPtrArray *method_seq_points;
421 int i;
423 if (error)
424 error_init (error);
426 // FIXME:
427 // - suspend/resume the vm to prevent code patching problems
428 // - multiple breakpoints on the same location
429 // - dynamic methods
430 // - races
432 bp = g_new0 (MonoBreakpoint, 1);
433 bp->method = method;
434 bp->il_offset = il_offset;
435 bp->req = req;
436 bp->children = g_ptr_array_new ();
438 DEBUG_PRINTF (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);
440 methods = g_ptr_array_new ();
441 method_domains = g_ptr_array_new ();
442 method_seq_points = g_ptr_array_new ();
444 mono_loader_lock ();
446 CollectDomainData user_data;
447 memset (&user_data, 0, sizeof (user_data));
448 user_data.bp = bp;
449 user_data.methods = methods;
450 user_data.method_domains = method_domains;
451 user_data.method_seq_points = method_seq_points;
452 mono_de_foreach_domain (collect_domain_bp, &user_data);
454 for (i = 0; i < methods->len; ++i) {
455 m = (MonoMethod *)g_ptr_array_index (methods, i);
456 domain = (MonoDomain *)g_ptr_array_index (method_domains, i);
457 seq_points = (MonoSeqPointInfo *)g_ptr_array_index (method_seq_points, i);
458 set_bp_in_method (domain, m, seq_points, bp, error);
461 g_ptr_array_add (breakpoints, bp);
462 mono_debugger_log_add_bp (bp, bp->method, bp->il_offset);
463 mono_loader_unlock ();
465 g_ptr_array_free (methods, TRUE);
466 g_ptr_array_free (method_domains, TRUE);
467 g_ptr_array_free (method_seq_points, TRUE);
469 if (error && !is_ok (error)) {
470 mono_de_clear_breakpoint (bp);
471 return NULL;
474 return bp;
477 MonoBreakpoint *
478 mono_de_get_breakpoint_by_id (int id)
480 for (int i = 0; i < breakpoints->len; ++i) {
481 MonoBreakpoint *bp = (MonoBreakpoint *)g_ptr_array_index (breakpoints, i);
482 if (bp->req->id == id)
483 return bp;
485 return NULL;
488 void
489 mono_de_clear_breakpoint (MonoBreakpoint *bp)
491 int i;
493 // FIXME: locking, races
494 for (i = 0; i < bp->children->len; ++i) {
495 BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, i);
497 remove_breakpoint (inst);
499 g_free (inst);
502 mono_loader_lock ();
503 mono_debugger_log_remove_bp (bp, bp->method, bp->il_offset);
504 g_ptr_array_remove (breakpoints, bp);
505 mono_loader_unlock ();
507 g_ptr_array_free (bp->children, TRUE);
508 g_free (bp);
511 void
512 mono_de_collect_breakpoints_by_sp (SeqPoint *sp, MonoJitInfo *ji, GPtrArray *ss_reqs, GPtrArray *bp_reqs)
514 for (int i = 0; i < breakpoints->len; ++i) {
515 MonoBreakpoint *bp = (MonoBreakpoint *)g_ptr_array_index (breakpoints, i);
517 if (!bp->method)
518 continue;
520 for (int j = 0; j < bp->children->len; ++j) {
521 BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, j);
522 if (inst->ji == ji && inst->il_offset == sp->il_offset && inst->native_offset == sp->native_offset) {
523 if (bp->req->event_kind == EVENT_KIND_STEP) {
524 if (ss_reqs)
525 g_ptr_array_add (ss_reqs, bp->req);
526 } else {
527 if (bp_reqs)
528 g_ptr_array_add (bp_reqs, bp->req);
535 static void
536 breakpoints_cleanup (void)
538 int i;
540 mono_loader_lock ();
542 for (i = 0; i < breakpoints->len; ++i)
543 g_free (g_ptr_array_index (breakpoints, i));
545 g_ptr_array_free (breakpoints, TRUE);
546 g_hash_table_destroy (bp_locs);
548 breakpoints = NULL;
549 bp_locs = NULL;
551 mono_loader_unlock ();
555 * mono_de_clear_breakpoints_for_domain:
557 * Clear breakpoint instances which reference DOMAIN.
559 void
560 mono_de_clear_breakpoints_for_domain (MonoDomain *domain)
562 int i, j;
564 /* This could be called after shutdown */
565 if (!breakpoints)
566 return;
568 mono_loader_lock ();
569 for (i = 0; i < breakpoints->len; ++i) {
570 MonoBreakpoint *bp = (MonoBreakpoint *)g_ptr_array_index (breakpoints, i);
572 j = 0;
573 while (j < bp->children->len) {
574 BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, j);
576 if (inst->domain == domain) {
577 remove_breakpoint (inst);
579 g_free (inst);
581 g_ptr_array_remove_index_fast (bp->children, j);
582 } else {
583 j ++;
587 mono_loader_unlock ();
590 /* Single stepping engine */
591 /* Number of single stepping operations in progress */
592 static int ss_count;
594 /* The single step request instance */
595 static SingleStepReq *the_ss_req;
598 * mono_de_start_single_stepping:
600 * Turn on single stepping. Can be called multiple times, for example,
601 * by a single step event request + a suspend.
603 void
604 mono_de_start_single_stepping (void)
606 int val = mono_atomic_inc_i32 (&ss_count);
608 if (val == 1) {
609 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
610 mono_arch_start_single_stepping ();
611 #endif
612 mini_get_interp_callbacks ()->start_single_stepping ();
616 void
617 mono_de_stop_single_stepping (void)
619 int val = mono_atomic_dec_i32 (&ss_count);
621 if (val == 0) {
622 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
623 mono_arch_stop_single_stepping ();
624 #endif
625 mini_get_interp_callbacks ()->stop_single_stepping ();
629 static MonoJitInfo*
630 get_top_method_ji (gpointer ip, MonoDomain **domain, gpointer *out_ip)
632 MonoJitInfo *ji;
634 if (out_ip)
635 *out_ip = ip;
637 ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, domain);
638 if (!ji) {
639 /* Could be an interpreter method */
641 MonoLMF *lmf = mono_get_lmf ();
642 MonoInterpFrameHandle *frame;
644 g_assert (((gsize)lmf->previous_lmf) & 2);
645 MonoLMFExt *ext = (MonoLMFExt*)lmf;
647 g_assert (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX);
648 frame = (MonoInterpFrameHandle*)ext->interp_exit_data;
649 ji = mini_get_interp_callbacks ()->frame_get_jit_info (frame);
650 if (domain)
651 *domain = mono_domain_get ();
652 if (out_ip)
653 *out_ip = mini_get_interp_callbacks ()->frame_get_ip (frame);
655 return ji;
658 static void
659 no_seq_points_found (MonoMethod *method, int offset)
662 * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space.
664 printf ("Unable to find seq points for method '%s', offset 0x%x.\n", mono_method_full_name (method, TRUE), offset);
667 static const char*
668 ss_depth_to_string (StepDepth depth)
670 switch (depth) {
671 case STEP_DEPTH_OVER:
672 return "over";
673 case STEP_DEPTH_OUT:
674 return "out";
675 case STEP_DEPTH_INTO:
676 return "into";
677 default:
678 g_assert_not_reached ();
679 return NULL;
684 * ss_stop:
686 * Stop the single stepping operation given by SS_REQ.
688 static void
689 ss_stop (SingleStepReq *ss_req)
691 if (ss_req->bps) {
692 GSList *l;
694 for (l = ss_req->bps; l; l = l->next) {
695 mono_de_clear_breakpoint ((MonoBreakpoint *)l->data);
697 g_slist_free (ss_req->bps);
698 ss_req->bps = NULL;
701 ss_req->async_id = 0;
702 ss_req->async_stepout_method = NULL;
703 if (ss_req->global) {
704 mono_de_stop_single_stepping ();
705 ss_req->global = FALSE;
709 static void
710 ss_destroy (SingleStepReq *req)
712 DEBUG_PRINTF (1, "[dbg] ss_destroy.\n");
714 ss_stop (req);
716 g_free (req);
719 static SingleStepReq*
720 ss_req_acquire (void)
722 SingleStepReq *req;
724 dbg_lock ();
725 req = the_ss_req;
726 if (req)
727 req->refcount ++;
728 dbg_unlock ();
729 return req;
732 static void
733 mono_de_ss_req_release (SingleStepReq *req)
735 gboolean free = FALSE;
737 dbg_lock ();
738 g_assert (req->refcount);
739 req->refcount --;
740 if (req->refcount == 0)
741 free = TRUE;
742 dbg_unlock ();
743 if (free) {
744 if (req == the_ss_req)
745 the_ss_req = NULL;
746 ss_destroy (req);
750 void
751 mono_de_cancel_ss (void)
753 if (the_ss_req) {
754 mono_de_ss_req_release (the_ss_req);
755 the_ss_req = NULL;
760 void
761 mono_de_process_single_step (void *tls, gboolean from_signal)
763 MonoJitInfo *ji;
764 guint8 *ip;
765 GPtrArray *reqs;
766 int il_offset;
767 MonoDomain *domain;
768 MonoContext *ctx = rt_callbacks.tls_get_restore_state (tls);
769 MonoMethod *method;
770 SeqPoint sp;
771 MonoSeqPointInfo *info;
772 SingleStepReq *ss_req;
774 /* Skip the instruction causing the single step */
775 rt_callbacks.begin_single_step_processing (ctx, from_signal);
777 if (rt_callbacks.try_process_suspend (tls, ctx))
778 return;
781 * This can run concurrently with a clear_event_request () call, so needs locking/reference counts.
783 ss_req = ss_req_acquire ();
785 if (!ss_req)
786 // FIXME: A suspend race
787 return;
789 if (mono_thread_internal_current () != ss_req->thread)
790 goto exit;
792 ip = (guint8 *)MONO_CONTEXT_GET_IP (ctx);
794 ji = get_top_method_ji (ip, &domain, (gpointer*)&ip);
795 g_assert (ji && !ji->is_trampoline);
797 if (log_level > 0) {
798 DEBUG_PRINTF (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);
801 method = jinfo_get_method (ji);
802 g_assert (method);
804 if (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
805 goto exit;
808 * FIXME:
809 * Stopping in memset makes half-initialized vtypes visible.
810 * Stopping in memcpy makes half-copied vtypes visible.
812 if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy")))
813 goto exit;
816 * This could be in mono_de_ss_update method, but mono_find_next_seq_point_for_native_offset is pretty expensive method,
817 * hence we prefer this check here.
819 if (ss_req->user_assemblies) {
820 gboolean found = FALSE;
821 for (int k = 0; ss_req->user_assemblies[k]; k++)
822 if (ss_req->user_assemblies[k] == m_class_get_image (method->klass)->assembly) {
823 found = TRUE;
824 break;
826 if (!found)
827 goto exit;
831 * The ip points to the instruction causing the single step event, which is before
832 * the offset recorded in the seq point map, so find the next seq point after ip.
834 if (!mono_find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info, &sp)) {
835 g_assert_not_reached ();
836 goto exit;
839 il_offset = sp.il_offset;
841 if (!mono_de_ss_update (ss_req, ji, &sp, tls, ctx, method))
842 goto exit;
844 /* Start single stepping again from the current sequence point */
846 SingleStepArgs args;
847 memset (&args, 0, sizeof (args));
848 args.method = method;
849 args.ctx = ctx;
850 args.tls = tls;
851 args.step_to_catch = FALSE;
852 args.sp = sp;
853 args.info = info;
854 args.frames = NULL;
855 args.nframes = 0;
856 mono_de_ss_start (ss_req, &args);
858 if ((ss_req->filter & STEP_FILTER_STATIC_CTOR) &&
859 (method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
860 !strcmp (method->name, ".cctor"))
861 goto exit;
863 // FIXME: Has to lock earlier
865 reqs = g_ptr_array_new ();
867 mono_loader_lock ();
869 g_ptr_array_add (reqs, ss_req->req);
871 void *bp_events;
872 bp_events = rt_callbacks.create_breakpoint_events (reqs, NULL, ji, EVENT_KIND_BREAKPOINT);
874 g_ptr_array_free (reqs, TRUE);
876 mono_loader_unlock ();
878 rt_callbacks.process_breakpoint_events (bp_events, method, ctx, il_offset);
880 exit:
881 mono_de_ss_req_release (ss_req);
885 * mono_de_ss_update:
887 * Return FALSE if single stepping needs to continue.
889 static gboolean
890 mono_de_ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, void *tls, MonoContext *ctx, MonoMethod* method)
892 MonoDebugMethodInfo *minfo;
893 MonoDebugSourceLocation *loc = NULL;
894 gboolean hit = TRUE;
896 if ((req->filter & STEP_FILTER_STATIC_CTOR)) {
897 DbgEngineStackFrame **frames;
898 int nframes;
899 rt_callbacks.ss_calculate_framecount (tls, ctx, TRUE, &frames, &nframes);
901 gboolean ret = FALSE;
902 gboolean method_in_stack = FALSE;
904 for (int i = 0; i < nframes; i++) {
905 MonoMethod *external_method = frames [i]->method;
906 if (method == external_method)
907 method_in_stack = TRUE;
909 if (!ret) {
910 ret = (external_method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME);
911 ret = ret && !strcmp (external_method->name, ".cctor");
912 ret = ret && (external_method != req->start_method);
916 if (!method_in_stack) {
917 g_printerr ("[%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);
918 /*DEBUG_PRINTF (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);*/
920 for (int i=0; i < nframes; i++)
921 g_printerr ("\t [%p] Frame (%d / %d): %s\n", (gpointer)(gsize)mono_native_thread_id_get (), i, nframes, mono_method_full_name (frames [i]->method, TRUE));
924 rt_callbacks.ss_discard_frame_context (tls);
926 if (ret)
927 return FALSE;
930 if (req->async_stepout_method == method) {
931 DEBUG_PRINTF (1, "[%p] Breakpoint hit during async step-out at %s hit, continuing stepping out.\n", (gpointer)(gsize)mono_native_thread_id_get (), method->name);
932 return FALSE;
935 if (req->depth == STEP_DEPTH_OVER && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) && !(sp->flags & MONO_SEQ_POINT_FLAG_NESTED_CALL)) {
937 * These seq points are inserted by the JIT after calls, step over needs to skip them.
939 DEBUG_PRINTF (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);
940 return FALSE;
943 if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit && !req->async_stepout_method) {
944 gboolean is_step_out = req->depth == STEP_DEPTH_OUT;
945 int nframes;
946 rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes);
948 // Because functions can call themselves recursively, we need to make sure we're stopping at the right stack depth.
949 // In case of step out, the target is the frame *enclosing* the one where the request was made.
950 int target_frames = req->nframes + (is_step_out ? -1 : 0);
951 if (req->nframes > 0 && nframes > 0 && nframes > target_frames) {
952 /* Hit the breakpoint in a recursive call, don't halt */
953 DEBUG_PRINTF (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");
954 return FALSE;
958 if (req->depth == STEP_DEPTH_INTO && req->size == STEP_SIZE_MIN && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) && req->start_method) {
959 int nframes;
960 rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes);
961 if (req->start_method == method && req->nframes && nframes == req->nframes) { //Check also frame count(could be recursion)
962 DEBUG_PRINTF (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);
963 return FALSE;
967 MonoDebugMethodAsyncInfo* async_method = mono_debug_lookup_method_async_debug_info (method);
968 if (async_method) {
969 for (int i = 0; i < async_method->num_awaits; i++) {
970 if (async_method->yield_offsets[i] == sp->il_offset || async_method->resume_offsets[i] == sp->il_offset) {
971 mono_debug_free_method_async_debug_info (async_method);
972 return FALSE;
975 mono_debug_free_method_async_debug_info (async_method);
978 if (req->size != STEP_SIZE_LINE)
979 return TRUE;
981 /* Have to check whenever a different source line was reached */
982 minfo = mono_debug_lookup_method (method);
984 if (minfo)
985 loc = mono_debug_method_lookup_location (minfo, sp->il_offset);
987 if (!loc) {
988 DEBUG_PRINTF (1, "[%p] No line number info for il offset %x, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), sp->il_offset);
989 req->last_method = method;
990 hit = FALSE;
991 } else if (loc && method == req->last_method && loc->row == req->last_line) {
992 int nframes;
993 rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes);
994 if (nframes == req->nframes) { // If the frame has changed we're clearly not on the same source line.
995 DEBUG_PRINTF (1, "[%p] Same source line (%d), continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), loc->row);
996 hit = FALSE;
1000 if (loc) {
1001 req->last_method = method;
1002 req->last_line = loc->row;
1003 mono_debug_free_source_location (loc);
1006 return hit;
1009 void
1010 mono_de_process_breakpoint (void *void_tls, gboolean from_signal)
1012 DebuggerTlsData *tls = (DebuggerTlsData*)void_tls;
1013 MonoJitInfo *ji;
1014 guint8 *ip;
1015 int i;
1016 guint32 native_offset;
1017 GPtrArray *bp_reqs, *ss_reqs_orig, *ss_reqs;
1018 EventKind kind = EVENT_KIND_BREAKPOINT;
1019 MonoContext *ctx = rt_callbacks.tls_get_restore_state (tls);
1020 MonoMethod *method;
1021 MonoSeqPointInfo *info;
1022 SeqPoint sp;
1023 gboolean found_sp;
1025 if (rt_callbacks.try_process_suspend (tls, ctx))
1026 return;
1028 ip = (guint8 *)MONO_CONTEXT_GET_IP (ctx);
1030 ji = get_top_method_ji (ip, NULL, (gpointer*)&ip);
1031 g_assert (ji && !ji->is_trampoline);
1032 method = jinfo_get_method (ji);
1034 /* Compute the native offset of the breakpoint from the ip */
1035 native_offset = ip - (guint8*)ji->code_start;
1037 if (!rt_callbacks.begin_breakpoint_processing (tls, ctx, ji, from_signal))
1038 return;
1040 if (method->wrapper_type)
1041 return;
1043 bp_reqs = g_ptr_array_new ();
1044 ss_reqs = g_ptr_array_new ();
1045 ss_reqs_orig = g_ptr_array_new ();
1047 mono_loader_lock ();
1050 * The ip points to the instruction causing the breakpoint event, which is after
1051 * the offset recorded in the seq point map, so find the prev seq point before ip.
1053 found_sp = mono_find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info, &sp);
1055 if (!found_sp)
1056 no_seq_points_found (method, native_offset);
1058 g_assert (found_sp);
1060 DEBUG_PRINTF (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);
1062 mono_debugger_log_bp_hit (tls, method, sp.il_offset);
1064 mono_de_collect_breakpoints_by_sp (&sp, ji, ss_reqs_orig, bp_reqs);
1066 if (bp_reqs->len == 0 && ss_reqs_orig->len == 0) {
1067 /* Maybe a method entry/exit event */
1068 if (sp.il_offset == METHOD_ENTRY_IL_OFFSET)
1069 kind = EVENT_KIND_METHOD_ENTRY;
1070 else if (sp.il_offset == METHOD_EXIT_IL_OFFSET)
1071 kind = EVENT_KIND_METHOD_EXIT;
1074 /* Process single step requests */
1075 for (i = 0; i < ss_reqs_orig->len; ++i) {
1076 EventRequest *req = (EventRequest *)g_ptr_array_index (ss_reqs_orig, i);
1077 SingleStepReq *ss_req = (SingleStepReq *)req->info;
1078 gboolean hit;
1080 //if we hit async_stepout_method, it's our no matter which thread
1081 if ((ss_req->async_stepout_method != method) && (ss_req->async_id || mono_thread_internal_current () != ss_req->thread)) {
1082 DbgEngineStackFrame **frames;
1083 int nframes;
1084 //We have different thread and we don't have async stepping in progress
1085 //it's breakpoint in parallel thread, ignore it
1086 if (ss_req->async_id == 0)
1087 continue;
1089 rt_callbacks.ss_discard_frame_context (tls);
1090 rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, &frames, &nframes);
1091 //make sure we have enough data to get current async method instance id
1092 if (nframes == 0 || !rt_callbacks.ensure_jit (frames [0]))
1093 continue;
1095 //Check method is async before calling get_this_async_id
1096 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
1097 if (!asyncMethod)
1098 continue;
1099 else
1100 mono_debug_free_method_async_debug_info (asyncMethod);
1102 //breakpoint was hit in parallelly executing async method, ignore it
1103 if (ss_req->async_id != rt_callbacks.get_this_async_id (frames [0]))
1104 continue;
1107 //Update stepping request to new thread/frame_count that we are continuing on
1108 //so continuing with normal stepping works as expected
1109 if (ss_req->async_stepout_method || ss_req->async_id) {
1110 int nframes;
1111 rt_callbacks.ss_discard_frame_context (tls);
1112 rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes);
1113 ss_req->thread = mono_thread_internal_current ();
1114 ss_req->nframes = nframes;
1117 hit = mono_de_ss_update (ss_req, ji, &sp, tls, ctx, method);
1118 if (hit)
1119 g_ptr_array_add (ss_reqs, req);
1121 SingleStepArgs args;
1122 memset (&args, 0, sizeof (args));
1123 args.method = method;
1124 args.ctx = ctx;
1125 args.tls = tls;
1126 args.step_to_catch = FALSE;
1127 args.sp = sp;
1128 args.info = info;
1129 args.frames = NULL;
1130 args.nframes = 0;
1131 mono_de_ss_start (ss_req, &args);
1134 void *bp_events = rt_callbacks.create_breakpoint_events (ss_reqs, bp_reqs, ji, kind);
1136 mono_loader_unlock ();
1138 g_ptr_array_free (bp_reqs, TRUE);
1139 g_ptr_array_free (ss_reqs, TRUE);
1141 rt_callbacks.process_breakpoint_events (bp_events, method, ctx, 0);
1145 * ss_bp_is_unique:
1147 * Reject breakpoint if it is a duplicate of one already in list or hash table.
1149 static gboolean
1150 ss_bp_is_unique (GSList *bps, GHashTable *ss_req_bp_cache, MonoMethod *method, guint32 il_offset)
1152 if (ss_req_bp_cache) {
1153 MonoBreakpoint dummy = {method, (long)il_offset, NULL, NULL};
1154 return !g_hash_table_lookup (ss_req_bp_cache, &dummy);
1156 for (GSList *l = bps; l; l = l->next) {
1157 MonoBreakpoint *bp = (MonoBreakpoint *)l->data;
1158 if (bp->method == method && bp->il_offset == il_offset)
1159 return FALSE;
1161 return TRUE;
1165 * ss_bp_eq:
1167 * GHashTable equality for a MonoBreakpoint (only care about method and il_offset fields)
1169 static gint
1170 ss_bp_eq (gconstpointer ka, gconstpointer kb)
1172 const MonoBreakpoint *s1 = (const MonoBreakpoint *)ka;
1173 const MonoBreakpoint *s2 = (const MonoBreakpoint *)kb;
1174 return (s1->method == s2->method && s1->il_offset == s2->il_offset) ? 1 : 0;
1178 * ss_bp_eq:
1180 * GHashTable hash for a MonoBreakpoint (only care about method and il_offset fields)
1182 static guint
1183 ss_bp_hash (gconstpointer data)
1185 const MonoBreakpoint *s = (const MonoBreakpoint *)data;
1186 guint hash = (guint) (uintptr_t) s->method;
1187 hash ^= ((guint)s->il_offset) << 16; // Assume low bits are more interesting
1188 hash ^= ((guint)s->il_offset) >> 16;
1189 return hash;
1192 #define MAX_LINEAR_SCAN_BPS 7
1195 * ss_bp_add_one:
1197 * Create a new breakpoint and add it to a step request.
1198 * Will adjust the bp count and cache used by mono_de_ss_start.
1200 static void
1201 ss_bp_add_one (SingleStepReq *ss_req, int *ss_req_bp_count, GHashTable **ss_req_bp_cache,
1202 MonoMethod *method, guint32 il_offset)
1204 // This list is getting too long, switch to using the hash table
1205 if (!*ss_req_bp_cache && *ss_req_bp_count > MAX_LINEAR_SCAN_BPS) {
1206 *ss_req_bp_cache = g_hash_table_new (ss_bp_hash, ss_bp_eq);
1207 for (GSList *l = ss_req->bps; l; l = l->next)
1208 g_hash_table_insert (*ss_req_bp_cache, l->data, l->data);
1211 if (ss_bp_is_unique (ss_req->bps, *ss_req_bp_cache, method, il_offset)) {
1212 // Create and add breakpoint
1213 MonoBreakpoint *bp = mono_de_set_breakpoint (method, il_offset, ss_req->req, NULL);
1214 ss_req->bps = g_slist_append (ss_req->bps, bp);
1215 if (*ss_req_bp_cache)
1216 g_hash_table_insert (*ss_req_bp_cache, bp, bp);
1217 (*ss_req_bp_count)++;
1218 } else {
1219 DEBUG_PRINTF (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);
1223 static gboolean
1224 is_last_non_empty (SeqPoint* sp, MonoSeqPointInfo *info)
1226 if (!sp->next_len)
1227 return TRUE;
1228 SeqPoint* next = g_new (SeqPoint, sp->next_len);
1229 mono_seq_point_init_next (info, *sp, next);
1230 for (int i = 0; i < sp->next_len; i++) {
1231 if (next [i].flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK && !(next [i].flags & MONO_SEQ_POINT_FLAG_NESTED_CALL)) {
1232 if (!is_last_non_empty (&next [i], info)) {
1233 g_free (next);
1234 return FALSE;
1236 } else {
1237 g_free (next);
1238 return FALSE;
1241 g_free (next);
1242 return TRUE;
1246 * mono_de_ss_start:
1248 * Start the single stepping operation given by SS_REQ from the sequence point SP.
1249 * If CTX is not set, then this can target any thread. If CTX is set, then TLS should
1250 * belong to the same thread as CTX.
1251 * If FRAMES is not-null, use that instead of tls->frames for placing breakpoints etc.
1253 static void
1254 mono_de_ss_start (SingleStepReq *ss_req, SingleStepArgs *ss_args)
1256 int i, j, frame_index;
1257 SeqPoint *next_sp, *parent_sp = NULL;
1258 SeqPoint local_sp, local_parent_sp;
1259 gboolean found_sp;
1260 MonoSeqPointInfo *parent_info;
1261 MonoMethod *parent_sp_method = NULL;
1262 gboolean enable_global = FALSE;
1264 // When 8 or more entries are in bps, we build a hash table to serve as a set of breakpoints.
1265 // Recreating this on each pass is a little wasteful but at least keeps behavior linear.
1266 int ss_req_bp_count = g_slist_length (ss_req->bps);
1267 GHashTable *ss_req_bp_cache = NULL;
1269 /* Stop the previous operation */
1270 ss_stop (ss_req);
1272 gboolean locked = FALSE;
1274 void *tls = ss_args->tls;
1275 MonoMethod *method = ss_args->method;
1276 DbgEngineStackFrame **frames = ss_args->frames;
1277 int nframes = ss_args->nframes;
1278 SeqPoint *sp = &ss_args->sp;
1280 /* this can happen on a single step in a exception on android (Mono_UnhandledException_internal) and on IOS */
1281 if (!method)
1282 return;
1285 * Implement single stepping using breakpoints if possible.
1287 if (ss_args->step_to_catch) {
1288 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, sp->il_offset);
1289 } else {
1290 frame_index = 1;
1292 if (ss_args->ctx && !frames) {
1294 mono_loader_lock ();
1295 locked = TRUE;
1297 /* Need parent frames */
1298 rt_callbacks.ss_calculate_framecount (tls, ss_args->ctx, FALSE, &frames, &nframes);
1301 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
1303 /* Need to stop in catch clauses as well */
1304 for (i = ss_req->depth == STEP_DEPTH_OUT ? 1 : 0; i < nframes; ++i) {
1305 DbgEngineStackFrame *frame = frames [i];
1307 if (frame->ji) {
1308 MonoJitInfo *jinfo = frame->ji;
1309 for (j = 0; j < jinfo->num_clauses; ++j) {
1310 // In case of async method we don't want to place breakpoint on last catch handler(which state machine added for whole method)
1311 if (asyncMethod && asyncMethod->num_awaits && i == 0 && j + 1 == jinfo->num_clauses)
1312 break;
1313 MonoJitExceptionInfo *ei = &jinfo->clauses [j];
1315 if (mono_find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL, &local_sp))
1316 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, frame->method, local_sp.il_offset);
1321 if (asyncMethod && asyncMethod->num_awaits && nframes && rt_callbacks.ensure_jit (frames [0])) {
1322 //asyncMethod has value and num_awaits > 0, this means we are inside async method with awaits
1324 // Check if we hit yield_offset during normal stepping, because if we did...
1325 // Go into special async stepping mode which places breakpoint on resumeOffset
1326 // of this await call and sets async_id so we can distinguish it from parallel executions
1327 for (i = 0; i < asyncMethod->num_awaits; i++) {
1328 if (sp->il_offset == asyncMethod->yield_offsets [i]) {
1329 ss_req->async_id = rt_callbacks.get_this_async_id (frames [0]);
1330 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, asyncMethod->resume_offsets [i]);
1331 g_hash_table_destroy (ss_req_bp_cache);
1332 mono_debug_free_method_async_debug_info (asyncMethod);
1333 if (locked)
1334 mono_loader_unlock ();
1335 goto cleanup;
1338 //If we are at end of async method and doing step-in or step-over...
1339 //Switch to step-out, so whole NotifyDebuggerOfWaitCompletion magic happens...
1340 if (is_last_non_empty (sp, ss_args->info)) {
1341 ss_req->depth = STEP_DEPTH_OUT;//setting depth to step-out is important, don't inline IF, because code later depends on this
1343 if (ss_req->depth == STEP_DEPTH_OUT) {
1344 //If we are inside `async void` method, do normal step-out
1345 if (rt_callbacks.set_set_notification_for_wait_completion_flag (frames [0])) {
1346 ss_req->async_id = rt_callbacks.get_this_async_id (frames [0]);
1347 ss_req->async_stepout_method = rt_callbacks.get_notify_debugger_of_wait_completion_method ();
1348 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, ss_req->async_stepout_method, 0);
1349 g_hash_table_destroy (ss_req_bp_cache);
1350 mono_debug_free_method_async_debug_info (asyncMethod);
1351 if (locked)
1352 mono_loader_unlock ();
1353 goto cleanup;
1358 if (asyncMethod)
1359 mono_debug_free_method_async_debug_info (asyncMethod);
1362 * Find the first sequence point in the current or in a previous frame which
1363 * is not the last in its method.
1365 if (ss_req->depth == STEP_DEPTH_OUT) {
1366 /* Ignore seq points in current method */
1367 while (frame_index < nframes) {
1368 DbgEngineStackFrame *frame = frames [frame_index];
1370 method = frame->method;
1371 found_sp = mono_find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &ss_args->info, &local_sp);
1372 sp = (found_sp)? &local_sp : NULL;
1373 frame_index ++;
1374 if (sp && sp->next_len != 0)
1375 break;
1377 // There could be method calls before the next seq point in the caller when using nested calls
1378 //enable_global = TRUE;
1379 } else {
1380 if (sp && sp->next_len == 0) {
1381 sp = NULL;
1382 while (frame_index < nframes) {
1383 DbgEngineStackFrame *frame = frames [frame_index];
1385 method = frame->method;
1386 found_sp = mono_find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &ss_args->info, &local_sp);
1387 sp = (found_sp)? &local_sp : NULL;
1388 if (sp && sp->next_len != 0)
1389 break;
1390 sp = NULL;
1391 frame_index ++;
1393 } else {
1394 /* Have to put a breakpoint into a parent frame since the seq points might not cover all control flow out of the method */
1395 while (frame_index < nframes) {
1396 DbgEngineStackFrame *frame = frames [frame_index];
1398 parent_sp_method = frame->method;
1399 found_sp = mono_find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &parent_info, &local_parent_sp);
1400 parent_sp = found_sp ? &local_parent_sp : NULL;
1401 if (found_sp && parent_sp->next_len != 0)
1402 break;
1403 parent_sp = NULL;
1404 frame_index ++;
1409 if (sp && sp->next_len > 0) {
1410 SeqPoint* next = g_new(SeqPoint, sp->next_len);
1412 mono_seq_point_init_next (ss_args->info, *sp, next);
1413 for (i = 0; i < sp->next_len; i++) {
1414 next_sp = &next[i];
1416 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, next_sp->il_offset);
1418 g_free (next);
1421 if (parent_sp) {
1422 SeqPoint* next = g_new(SeqPoint, parent_sp->next_len);
1424 mono_seq_point_init_next (parent_info, *parent_sp, next);
1425 for (i = 0; i < parent_sp->next_len; i++) {
1426 next_sp = &next[i];
1428 ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, parent_sp_method, next_sp->il_offset);
1430 g_free (next);
1433 if (ss_req->nframes == 0)
1434 ss_req->nframes = nframes;
1436 if ((ss_req->depth == STEP_DEPTH_OVER) && (!sp && !parent_sp)) {
1437 DEBUG_PRINTF (1, "[dbg] No parent frame for step over, transition to step into.\n");
1439 * This is needed since if we leave managed code, and later return to it, step over
1440 * is not going to stop.
1441 * This approach is a bit ugly, since we change the step depth, but it only affects
1442 * clients who reuse the same step request, and only in this special case.
1444 ss_req->depth = STEP_DEPTH_INTO;
1447 if (ss_req->depth == STEP_DEPTH_INTO) {
1448 /* Enable global stepping so we stop at method entry too */
1449 enable_global = TRUE;
1453 * The ctx/frame info computed above will become invalid when we continue.
1455 rt_callbacks.ss_discard_frame_context (tls);
1458 if (enable_global) {
1459 DEBUG_PRINTF (1, "[dbg] Turning on global single stepping.\n");
1460 ss_req->global = TRUE;
1461 mono_de_start_single_stepping ();
1462 } else if (!ss_req->bps) {
1463 DEBUG_PRINTF (1, "[dbg] Turning on global single stepping.\n");
1464 ss_req->global = TRUE;
1465 mono_de_start_single_stepping ();
1466 } else {
1467 ss_req->global = FALSE;
1470 g_hash_table_destroy (ss_req_bp_cache);
1472 if (locked)
1473 mono_loader_unlock ();
1475 cleanup:
1476 rt_callbacks.ss_args_destroy (ss_args);
1481 * Start single stepping of thread THREAD
1483 DbgEngineErrorCode
1484 mono_de_ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilter filter, EventRequest *req)
1486 int err = rt_callbacks.ensure_runtime_is_suspended ();
1487 if (err)
1488 return err;
1490 // FIXME: Multiple requests
1491 if (the_ss_req) {
1492 DEBUG_PRINTF (0, "Received a single step request while the previous one was still active.\n");
1493 return DE_ERR_NOT_IMPLEMENTED;
1496 DEBUG_PRINTF (1, "[dbg] Starting single step of thread %p (depth=%s).\n", thread, ss_depth_to_string (depth));
1498 SingleStepReq *ss_req = g_new0 (SingleStepReq, 1);
1499 ss_req->req = req;
1500 ss_req->thread = thread;
1501 ss_req->size = size;
1502 ss_req->depth = depth;
1503 ss_req->filter = filter;
1504 ss_req->refcount = 1;
1505 req->info = ss_req;
1507 for (int i = 0; i < req->nmodifiers; i++) {
1508 if (req->modifiers[i].kind == MOD_KIND_ASSEMBLY_ONLY) {
1509 ss_req->user_assemblies = req->modifiers[i].data.assemblies;
1510 break;
1514 SingleStepArgs args;
1515 err = rt_callbacks.ss_create_init_args (ss_req, &args);
1516 if (err)
1517 return err;
1519 the_ss_req = ss_req;
1521 mono_de_ss_start (ss_req, &args);
1523 return DE_ERR_NONE;
1527 * mono_de_set_log_level:
1529 * Configures logging level and output file. Must be called together with mono_de_init.
1531 void
1532 mono_de_set_log_level (int level, FILE *file)
1534 log_level = level;
1535 log_file = file;
1539 * mono_de_init:
1541 * Inits the shared debugger engine. Not reentrant.
1543 void
1544 mono_de_init (DebuggerEngineCallbacks *cbs)
1546 rt_callbacks = *cbs;
1547 mono_coop_mutex_init_recursive (&debug_mutex);
1549 domains_init ();
1550 breakpoints_init ();
1552 mono_debugger_log_init ();
1555 void
1556 mono_de_cleanup (void)
1558 breakpoints_cleanup ();
1559 domains_cleanup ();
1562 void
1563 mono_debugger_free_objref (gpointer value)
1565 ObjRef *o = (ObjRef *)value;
1567 mono_gchandle_free_internal (o->handle);
1569 g_free (o);
1571 #endif