3 * Debugger Engine shared code.
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.
13 #include "mini-runtime.h"
15 #if !defined (DISABLE_SDB) || defined(TARGET_WASM)
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
;
34 static FILE *log_file
;
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
;
62 /* A hash table containing all active domains */
63 /* Protected by the loader lock */
64 static GHashTable
*domains
;
70 domains
= g_hash_table_new (mono_aligned_addr_hash
, NULL
);
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.
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
96 mono_de_domain_remove (MonoDomain
*domain
)
99 g_hash_table_remove (domains
, domain
);
100 mono_loader_unlock ();
104 * LOCKING: Takes the loader lock
107 mono_de_domain_add (MonoDomain
*domain
)
110 g_hash_table_insert (domains
, domain
, domain
);
111 mono_loader_unlock ();
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
;
125 breakpoints_init (void)
127 breakpoints
= g_ptr_array_new ();
128 bp_locs
= g_hash_table_new (NULL
, NULL
);
134 * Insert the breakpoint described by BP into the method described by
138 insert_breakpoint (MonoSeqPointInfo
*seq_points
, MonoDomain
*domain
, MonoJitInfo
*ji
, MonoBreakpoint
*bp
, MonoError
*error
)
141 BreakpointInstance
*inst
;
143 gboolean it_has_sp
= FALSE
;
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
) {
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
) {
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
);
180 mono_error_set_error (error
, MONO_ERROR_GENERIC
, "%s", s
);
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
;
196 inst
->domain
= domain
;
200 g_ptr_array_add (bp
->children
, inst
);
202 mono_loader_unlock ();
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));
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) {
213 mini_get_interp_callbacks ()->set_breakpoint (ji
, inst
->ip
);
215 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
216 mono_arch_set_breakpoint (ji
, inst
->ip
);
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
);
227 remove_breakpoint (BreakpointInstance
*inst
)
230 MonoJitInfo
*ji
= inst
->ji
;
231 guint8
*ip
= inst
->ip
;
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));
238 g_assert (count
> 0);
240 if (count
== 1 && inst
->native_offset
!= SEQ_POINT_NATIVE_OFFSET_DEAD_CODE
) {
242 mini_get_interp_callbacks ()->clear_breakpoint (ji
, ip
);
244 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
245 mono_arch_clear_breakpoint (ji
, ip
);
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.
258 bp_matches_method (MonoBreakpoint
*bp
, MonoMethod
*method
)
264 if (method
== bp
->method
)
266 if (method
->is_inflated
&& ((MonoMethodInflated
*)method
)->declaring
== bp
->method
)
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
)
290 * mono_de_add_pending_breakpoints:
292 * Insert pending breakpoints into the newly JITted method METHOD.
295 mono_de_add_pending_breakpoints (MonoMethod
*method
, MonoJitInfo
*ji
)
298 MonoSeqPointInfo
*seq_points
;
304 domain
= mono_domain_get ();
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
))
315 for (j
= 0; j
< bp
->children
->len
; ++j
) {
316 BreakpointInstance
*inst
= (BreakpointInstance
*)g_ptr_array_index (bp
->children
, j
);
323 seq_points
= (MonoSeqPointInfo
*) ji
->seq_points
;
326 MonoMethod
*jmethod
= jinfo_get_method (ji
);
327 if (jmethod
->is_inflated
) {
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
;
336 /* Could be AOT code, or above "search_all_backends" call could have failed */
339 insert_breakpoint (seq_points
, domain
, ji
, bp
, NULL
);
343 mono_loader_unlock ();
347 set_bp_in_method (MonoDomain
*domain
, MonoMethod
*method
, MonoSeqPointInfo
*seq_points
, MonoBreakpoint
*bp
, MonoError
*error
)
354 (void)mono_jit_search_all_backends_for_jit_info (domain
, method
, &ji
);
357 insert_breakpoint (seq_points
, domain
, ji
, bp
, error
);
363 GPtrArray
*method_domains
;
364 GPtrArray
*method_seq_points
;
368 collect_domain_bp (gpointer key
, gpointer value
, gpointer user_data
)
371 MonoSeqPointInfo
*seq_points
;
372 MonoDomain
*domain
= (MonoDomain
*)key
;
373 CollectDomainData
*ud
= (CollectDomainData
*)user_data
;
376 if (mono_domain_is_unloading (domain
))
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
);
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
410 mono_de_set_breakpoint (MonoMethod
*method
, long il_offset
, EventRequest
*req
, MonoError
*error
)
415 MonoSeqPointInfo
*seq_points
;
417 GPtrArray
*method_domains
;
418 GPtrArray
*method_seq_points
;
425 // - suspend/resume the vm to prevent code patching problems
426 // - multiple breakpoints on the same location
430 bp
= g_new0 (MonoBreakpoint
, 1);
432 bp
->il_offset
= il_offset
;
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 ();
444 CollectDomainData user_data
;
445 memset (&user_data
, 0, sizeof (user_data
));
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
);
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
)
487 mono_de_clear_breakpoint (MonoBreakpoint
*bp
)
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
);
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
);
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
);
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
) {
523 g_ptr_array_add (ss_reqs
, bp
->req
);
526 g_ptr_array_add (bp_reqs
, bp
->req
);
534 breakpoints_cleanup (void)
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
);
549 mono_loader_unlock ();
553 * mono_de_clear_breakpoints_for_domain:
555 * Clear breakpoint instances which reference DOMAIN.
558 mono_de_clear_breakpoints_for_domain (MonoDomain
*domain
)
562 /* This could be called after shutdown */
567 for (i
= 0; i
< breakpoints
->len
; ++i
) {
568 MonoBreakpoint
*bp
= (MonoBreakpoint
*)g_ptr_array_index (breakpoints
, i
);
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
);
579 g_ptr_array_remove_index_fast (bp
->children
, j
);
585 mono_loader_unlock ();
588 /* Single stepping engine */
589 /* Number of single stepping operations in progress */
592 /* The single step request instances */
593 static GPtrArray
*the_ss_reqs
;
598 the_ss_reqs
= g_ptr_array_new ();
602 ss_req_cleanup (void)
606 g_ptr_array_free (the_ss_reqs
, TRUE
);
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.
620 mono_de_start_single_stepping (void)
622 int val
= mono_atomic_inc_i32 (&ss_count
);
625 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
626 mono_arch_start_single_stepping ();
628 mini_get_interp_callbacks ()->start_single_stepping ();
633 mono_de_stop_single_stepping (void)
635 int val
= mono_atomic_dec_i32 (&ss_count
);
638 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
639 mono_arch_stop_single_stepping ();
641 mini_get_interp_callbacks ()->stop_single_stepping ();
646 get_top_method_ji (gpointer ip
, MonoDomain
**domain
, gpointer
*out_ip
)
653 ji
= mini_jit_info_table_find (mono_domain_get (), (char*)ip
, domain
);
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
);
667 *domain
= mono_domain_get ();
669 *out_ip
= mini_get_interp_callbacks ()->frame_get_ip (frame
);
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
);
684 ss_depth_to_string (StepDepth depth
)
687 case STEP_DEPTH_OVER
:
691 case STEP_DEPTH_INTO
:
694 g_assert_not_reached ();
702 * Stop the single stepping operation given by SS_REQ.
705 ss_stop (SingleStepReq
*ss_req
)
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
);
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
;
726 ss_destroy (SingleStepReq
*req
)
728 PRINT_DEBUG_MSG (1, "[dbg] ss_destroy.\n");
735 static SingleStepReq
*
736 ss_req_acquire (MonoInternalThread
*thread
)
738 SingleStepReq
*req
= NULL
;
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
++;
755 return the_ss_reqs
->len
;
759 mono_de_ss_req_release (SingleStepReq
*req
)
761 gboolean free
= FALSE
;
764 g_assert (req
->refcount
);
766 if (req
->refcount
== 0)
769 g_ptr_array_remove (the_ss_reqs
, req
);
776 mono_de_cancel_ss (SingleStepReq
*req
)
779 mono_de_ss_req_release (req
);
784 mono_de_cancel_all_ss ()
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
);
794 mono_de_process_single_step (void *tls
, gboolean from_signal
)
801 MonoContext
*ctx
= rt_callbacks
.tls_get_restore_state (tls
);
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
))
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 ());
819 // FIXME: A suspend race
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
);
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
);
833 if (method
->wrapper_type
&& method
->wrapper_type
!= MONO_WRAPPER_DYNAMIC_METHOD
)
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")))
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
) {
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 ();
868 il_offset
= sp
.il_offset
;
870 if (!mono_de_ss_update (ss_req
, ji
, &sp
, tls
, ctx
, method
))
873 /* Start single stepping again from the current sequence point */
876 memset (&args
, 0, sizeof (args
));
877 args
.method
= method
;
880 args
.step_to_catch
= FALSE
;
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"))
892 // FIXME: Has to lock earlier
894 reqs
= g_ptr_array_new ();
898 g_ptr_array_add (reqs
, ss_req
->req
);
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
);
910 mono_de_ss_req_release (ss_req
);
916 * Return FALSE if single stepping needs to continue.
919 mono_de_ss_update (SingleStepReq
*req
, MonoJitInfo
*ji
, SeqPoint
*sp
, void *tls
, MonoContext
*ctx
, MonoMethod
* method
)
921 MonoDebugMethodInfo
*minfo
;
922 MonoDebugSourceLocation
*loc
= NULL
;
925 if ((req
->filter
& STEP_FILTER_STATIC_CTOR
)) {
926 DbgEngineStackFrame
**frames
;
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
;
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
);
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
);
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
);
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
;
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");
987 if (req
->depth
== STEP_DEPTH_INTO
&& req
->size
== STEP_SIZE_MIN
&& (sp
->flags
& MONO_SEQ_POINT_FLAG_NONEMPTY_STACK
) && req
->start_method
) {
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
);
996 MonoDebugMethodAsyncInfo
* async_method
= mono_debug_lookup_method_async_debug_info (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
);
1004 mono_debug_free_method_async_debug_info (async_method
);
1007 if (req
->size
!= STEP_SIZE_LINE
)
1010 /* Have to check whenever a different source line was reached */
1011 minfo
= mono_debug_lookup_method (method
);
1014 loc
= mono_debug_method_lookup_location (minfo
, sp
->il_offset
);
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
;
1020 } else if (loc
&& method
== req
->last_method
&& loc
->row
== req
->last_line
) {
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
);
1030 req
->last_method
= method
;
1031 req
->last_line
= loc
->row
;
1032 mono_debug_free_source_location (loc
);
1039 mono_de_process_breakpoint (void *void_tls
, gboolean from_signal
)
1041 DebuggerTlsData
*tls
= (DebuggerTlsData
*)void_tls
;
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
);
1050 MonoSeqPointInfo
*info
;
1054 if (rt_callbacks
.try_process_suspend (tls
, ctx
, TRUE
))
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
))
1069 if (method
->wrapper_type
)
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
);
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
;
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
;
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)
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]))
1124 //Check method is async before calling get_this_async_id
1125 MonoDebugMethodAsyncInfo
* asyncMethod
= mono_debug_lookup_method_async_debug_info (method
);
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]))
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
) {
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
);
1148 g_ptr_array_add (ss_reqs
, req
);
1150 SingleStepArgs args
;
1151 memset (&args
, 0, sizeof (args
));
1152 args
.method
= method
;
1155 args
.step_to_catch
= FALSE
;
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);
1176 * Reject breakpoint if it is a duplicate of one already in list or hash table.
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
)
1196 * GHashTable equality for a MonoBreakpoint (only care about method and il_offset fields)
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;
1209 * GHashTable hash for a MonoBreakpoint (only care about method and il_offset fields)
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;
1221 #define MAX_LINEAR_SCAN_BPS 7
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.
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
)++;
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
);
1253 is_last_non_empty (SeqPoint
* sp
, MonoSeqPointInfo
*info
)
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
)) {
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.
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
;
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 */
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 */
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
);
1322 if (ss_args
->ctx
&& !frames
) {
1326 mono_loader_lock ();
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
];
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
)
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
);
1366 mono_loader_unlock ();
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
);
1384 mono_loader_unlock ();
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
;
1406 if (sp
&& sp
->next_len
!= 0)
1409 // There could be method calls before the next seq point in the caller when using nested calls
1410 //enable_global = TRUE;
1412 if (sp
&& sp
->next_len
== 0) {
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)
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)
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
++) {
1448 ss_bp_add_one (ss_req
, &ss_req_bp_count
, &ss_req_bp_cache
, method
, next_sp
->il_offset
);
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
++) {
1460 ss_bp_add_one (ss_req
, &ss_req_bp_count
, &ss_req_bp_cache
, parent_sp_method
, next_sp
->il_offset
);
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 ();
1499 ss_req
->global
= FALSE
;
1502 g_hash_table_destroy (ss_req_bp_cache
);
1505 mono_loader_unlock ();
1508 rt_callbacks
.ss_args_destroy (ss_args
);
1513 * Start single stepping of thread THREAD
1516 mono_de_ss_create (MonoInternalThread
*thread
, StepSize size
, StepDepth depth
, StepFilter filter
, EventRequest
*req
)
1518 int err
= rt_callbacks
.ensure_runtime_is_suspended ();
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);
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;
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
;
1550 SingleStepArgs args
;
1551 err
= rt_callbacks
.ss_create_init_args (ss_req
, &args
);
1554 g_ptr_array_add (the_ss_reqs
, ss_req
);
1556 mono_de_ss_start (ss_req
, &args
);
1562 * mono_de_set_log_level:
1564 * Configures logging level and output file. Must be called together with mono_de_init.
1567 mono_de_set_log_level (int level
, FILE *file
)
1576 * Inits the shared debugger engine. Not reentrant.
1579 mono_de_init (DebuggerEngineCallbacks
*cbs
)
1581 rt_callbacks
= *cbs
;
1582 mono_coop_mutex_init_recursive (&debug_mutex
);
1585 breakpoints_init ();
1587 mono_debugger_log_init ();
1591 mono_de_cleanup (void)
1593 breakpoints_cleanup ();
1599 mono_debugger_free_objref (gpointer value
)
1601 ObjRef
*o
= (ObjRef
*)value
;
1603 mono_gchandle_free_internal (o
->handle
);
1608 // Returns true if TaskBuilder has NotifyDebuggerOfWaitCompletion method
1609 // false if not(AsyncVoidBuilder)
1611 get_class_to_get_builder_field(DbgEngineStackFrame
*frame
)
1614 gpointer this_addr
= get_this_addr (frame
);
1615 MonoClass
*original_class
= frame
->method
->klass
;
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
;
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
);
1633 return original_class
;
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
);
1643 gpointer builder
= get_async_method_builder (frame
);
1647 MonoMethod
* method
= get_set_notification_method (mono_class_from_mono_type_internal (builder_field
->type
));
1650 gboolean arg
= TRUE
;
1652 void *args
[ ] = { &arg
};
1653 mono_runtime_invoke_checked (method
, builder
, args
, error
);
1654 mono_error_assert_ok (error
);
1659 get_object_id_for_debugger_method (MonoClass
* async_builder_class
)
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");
1669 PRINT_DEBUG_MSG (1, "Impossible to debug async methods.\n");
1674 MonoMethod
*method
= (MonoMethod
*)g_ptr_array_index (array
, 0);
1675 g_ptr_array_free (array
, TRUE
);
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
)
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
;
1695 /* Return the address of the AsyncMethodBuilder struct belonging to the state machine method pointed to by FRAME */
1697 get_async_method_builder (DbgEngineStackFrame
*frame
)
1699 MonoObject
*this_obj
;
1700 MonoClassField
*builder_field
;
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
);
1710 this_addr
= get_this_addr (frame
);
1714 if (m_class_is_valuetype (klass
)) {
1715 builder
= mono_vtype_get_field_addr (*(guint8
**)this_addr
, builder_field
);
1717 this_obj
= *(MonoObject
**)this_addr
;
1718 builder
= (char*)this_obj
+ builder_field
->offset
;
1725 get_set_notification_method (MonoClass
* async_builder_class
)
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
);
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
;
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
;
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
;