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
;
37 #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { g_print (__VA_ARGS__); } } while (0)
39 #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { fprintf (log_file, __VA_ARGS__); fflush (log_file); } } while (0)
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
;
67 /* A hash table containing all active domains */
68 /* Protected by the loader lock */
69 static GHashTable
*domains
;
75 domains
= g_hash_table_new (mono_aligned_addr_hash
, NULL
);
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.
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
101 mono_de_domain_remove (MonoDomain
*domain
)
104 g_hash_table_remove (domains
, domain
);
105 mono_loader_unlock ();
109 * LOCKING: Takes the loader lock
112 mono_de_domain_add (MonoDomain
*domain
)
115 g_hash_table_insert (domains
, domain
, domain
);
116 mono_loader_unlock ();
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
;
130 breakpoints_init (void)
132 breakpoints
= g_ptr_array_new ();
133 bp_locs
= g_hash_table_new (NULL
, NULL
);
139 * Insert the breakpoint described by BP into the method described by
143 insert_breakpoint (MonoSeqPointInfo
*seq_points
, MonoDomain
*domain
, MonoJitInfo
*ji
, MonoBreakpoint
*bp
, MonoError
*error
)
146 BreakpointInstance
*inst
;
148 gboolean it_has_sp
= FALSE
;
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
) {
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
) {
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
);
185 mono_error_set_error (error
, MONO_ERROR_GENERIC
, "%s", s
);
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
;
201 inst
->domain
= domain
;
205 g_ptr_array_add (bp
->children
, inst
);
207 mono_loader_unlock ();
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));
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) {
218 mini_get_interp_callbacks ()->set_breakpoint (ji
, inst
->ip
);
220 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
221 mono_arch_set_breakpoint (ji
, inst
->ip
);
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
);
232 remove_breakpoint (BreakpointInstance
*inst
)
235 MonoJitInfo
*ji
= inst
->ji
;
236 guint8
*ip
= inst
->ip
;
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));
243 g_assert (count
> 0);
245 if (count
== 1 && inst
->native_offset
!= SEQ_POINT_NATIVE_OFFSET_DEAD_CODE
) {
247 mini_get_interp_callbacks ()->clear_breakpoint (ji
, ip
);
249 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
250 mono_arch_clear_breakpoint (ji
, ip
);
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 inline gboolean
263 bp_matches_method (MonoBreakpoint
*bp
, MonoMethod
*method
)
269 if (method
== bp
->method
)
271 if (method
->is_inflated
&& ((MonoMethodInflated
*)method
)->declaring
== bp
->method
)
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
)
295 * mono_de_add_pending_breakpoints:
297 * Insert pending breakpoints into the newly JITted method METHOD.
300 mono_de_add_pending_breakpoints (MonoMethod
*method
, MonoJitInfo
*ji
)
303 MonoSeqPointInfo
*seq_points
;
309 domain
= mono_domain_get ();
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
))
320 for (j
= 0; j
< bp
->children
->len
; ++j
) {
321 BreakpointInstance
*inst
= (BreakpointInstance
*)g_ptr_array_index (bp
->children
, j
);
328 seq_points
= (MonoSeqPointInfo
*) ji
->seq_points
;
331 MonoMethod
*jmethod
= jinfo_get_method (ji
);
332 if (jmethod
->is_inflated
) {
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
;
341 /* Could be AOT code, or above "search_all_backends" call could have failed */
344 insert_breakpoint (seq_points
, domain
, ji
, bp
, NULL
);
348 mono_loader_unlock ();
352 set_bp_in_method (MonoDomain
*domain
, MonoMethod
*method
, MonoSeqPointInfo
*seq_points
, MonoBreakpoint
*bp
, MonoError
*error
)
360 code
= mono_jit_search_all_backends_for_jit_info (domain
, method
, &ji
);
363 insert_breakpoint (seq_points
, domain
, ji
, bp
, error
);
369 GPtrArray
*method_domains
;
370 GPtrArray
*method_seq_points
;
374 collect_domain_bp (gpointer key
, gpointer value
, gpointer user_data
)
377 MonoSeqPointInfo
*seq_points
;
378 MonoDomain
*domain
= (MonoDomain
*)key
;
379 CollectDomainData
*ud
= (CollectDomainData
*)user_data
;
382 mono_domain_lock (domain
);
383 g_hash_table_iter_init (&iter
, domain_jit_info (domain
)->seq_points
);
384 while (g_hash_table_iter_next (&iter
, (void**)&m
, (void**)&seq_points
)) {
385 if (bp_matches_method (ud
->bp
, m
)) {
386 /* Save the info locally to simplify the code inside the domain lock */
387 g_ptr_array_add (ud
->methods
, m
);
388 g_ptr_array_add (ud
->method_domains
, domain
);
389 g_ptr_array_add (ud
->method_seq_points
, seq_points
);
392 mono_domain_unlock (domain
);
396 mono_de_clear_all_breakpoints (void)
398 while (breakpoints
->len
)
399 mono_de_clear_breakpoint ((MonoBreakpoint
*)g_ptr_array_index (breakpoints
, 0));
403 * mono_de_set_breakpoint:
405 * Set a breakpoint at IL_OFFSET in METHOD.
406 * METHOD can be NULL, in which case a breakpoint is placed in all methods.
407 * METHOD can also be a generic method definition, in which case a breakpoint
408 * is placed in all instances of the method.
409 * If ERROR is non-NULL, then it is set and NULL is returnd if some breakpoints couldn't be
413 mono_de_set_breakpoint (MonoMethod
*method
, long il_offset
, EventRequest
*req
, MonoError
*error
)
418 MonoSeqPointInfo
*seq_points
;
420 GPtrArray
*method_domains
;
421 GPtrArray
*method_seq_points
;
428 // - suspend/resume the vm to prevent code patching problems
429 // - multiple breakpoints on the same location
433 bp
= g_new0 (MonoBreakpoint
, 1);
435 bp
->il_offset
= il_offset
;
437 bp
->children
= g_ptr_array_new ();
439 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
);
441 methods
= g_ptr_array_new ();
442 method_domains
= g_ptr_array_new ();
443 method_seq_points
= g_ptr_array_new ();
447 CollectDomainData user_data
;
448 memset (&user_data
, 0, sizeof (user_data
));
450 user_data
.methods
= methods
;
451 user_data
.method_domains
= method_domains
;
452 user_data
.method_seq_points
= method_seq_points
;
453 mono_de_foreach_domain (collect_domain_bp
, &user_data
);
455 for (i
= 0; i
< methods
->len
; ++i
) {
456 m
= (MonoMethod
*)g_ptr_array_index (methods
, i
);
457 domain
= (MonoDomain
*)g_ptr_array_index (method_domains
, i
);
458 seq_points
= (MonoSeqPointInfo
*)g_ptr_array_index (method_seq_points
, i
);
459 set_bp_in_method (domain
, m
, seq_points
, bp
, error
);
462 g_ptr_array_add (breakpoints
, bp
);
463 mono_debugger_log_add_bp (bp
, bp
->method
, bp
->il_offset
);
464 mono_loader_unlock ();
466 g_ptr_array_free (methods
, TRUE
);
467 g_ptr_array_free (method_domains
, TRUE
);
468 g_ptr_array_free (method_seq_points
, TRUE
);
470 if (error
&& !mono_error_ok (error
)) {
471 mono_de_clear_breakpoint (bp
);
479 mono_de_get_breakpoint_by_id (int id
)
481 for (int i
= 0; i
< breakpoints
->len
; ++i
) {
482 MonoBreakpoint
*bp
= (MonoBreakpoint
*)g_ptr_array_index (breakpoints
, i
);
483 if (bp
->req
->id
== id
)
490 mono_de_clear_breakpoint (MonoBreakpoint
*bp
)
494 // FIXME: locking, races
495 for (i
= 0; i
< bp
->children
->len
; ++i
) {
496 BreakpointInstance
*inst
= (BreakpointInstance
*)g_ptr_array_index (bp
->children
, i
);
498 remove_breakpoint (inst
);
504 mono_debugger_log_remove_bp (bp
, bp
->method
, bp
->il_offset
);
505 g_ptr_array_remove (breakpoints
, bp
);
506 mono_loader_unlock ();
508 g_ptr_array_free (bp
->children
, TRUE
);
513 mono_de_collect_breakpoints_by_sp (SeqPoint
*sp
, MonoJitInfo
*ji
, GPtrArray
*ss_reqs
, GPtrArray
*bp_reqs
)
515 for (int i
= 0; i
< breakpoints
->len
; ++i
) {
516 MonoBreakpoint
*bp
= (MonoBreakpoint
*)g_ptr_array_index (breakpoints
, i
);
521 for (int j
= 0; j
< bp
->children
->len
; ++j
) {
522 BreakpointInstance
*inst
= (BreakpointInstance
*)g_ptr_array_index (bp
->children
, j
);
523 if (inst
->ji
== ji
&& inst
->il_offset
== sp
->il_offset
&& inst
->native_offset
== sp
->native_offset
) {
524 if (bp
->req
->event_kind
== EVENT_KIND_STEP
) {
526 g_ptr_array_add (ss_reqs
, bp
->req
);
529 g_ptr_array_add (bp_reqs
, bp
->req
);
537 breakpoints_cleanup (void)
543 for (i
= 0; i
< breakpoints
->len
; ++i
)
544 g_free (g_ptr_array_index (breakpoints
, i
));
546 g_ptr_array_free (breakpoints
, TRUE
);
547 g_hash_table_destroy (bp_locs
);
552 mono_loader_unlock ();
556 * mono_de_clear_breakpoints_for_domain:
558 * Clear breakpoint instances which reference DOMAIN.
561 mono_de_clear_breakpoints_for_domain (MonoDomain
*domain
)
565 /* This could be called after shutdown */
570 for (i
= 0; i
< breakpoints
->len
; ++i
) {
571 MonoBreakpoint
*bp
= (MonoBreakpoint
*)g_ptr_array_index (breakpoints
, i
);
574 while (j
< bp
->children
->len
) {
575 BreakpointInstance
*inst
= (BreakpointInstance
*)g_ptr_array_index (bp
->children
, j
);
577 if (inst
->domain
== domain
) {
578 remove_breakpoint (inst
);
582 g_ptr_array_remove_index_fast (bp
->children
, j
);
588 mono_loader_unlock ();
591 /* Single stepping engine */
592 /* Number of single stepping operations in progress */
595 /* The single step request instance */
596 static SingleStepReq
*the_ss_req
;
599 * mono_de_start_single_stepping:
601 * Turn on single stepping. Can be called multiple times, for example,
602 * by a single step event request + a suspend.
605 mono_de_start_single_stepping (void)
607 int val
= mono_atomic_inc_i32 (&ss_count
);
610 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
611 mono_arch_start_single_stepping ();
613 mini_get_interp_callbacks ()->start_single_stepping ();
618 mono_de_stop_single_stepping (void)
620 int val
= mono_atomic_dec_i32 (&ss_count
);
623 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
624 mono_arch_stop_single_stepping ();
626 mini_get_interp_callbacks ()->stop_single_stepping ();
631 get_top_method_ji (gpointer ip
, MonoDomain
**domain
, gpointer
*out_ip
)
638 ji
= mini_jit_info_table_find (mono_domain_get (), (char*)ip
, domain
);
640 /* Could be an interpreter method */
642 MonoLMF
*lmf
= mono_get_lmf ();
643 MonoInterpFrameHandle
*frame
;
645 g_assert (((gsize
)lmf
->previous_lmf
) & 2);
646 MonoLMFExt
*ext
= (MonoLMFExt
*)lmf
;
648 g_assert (ext
->kind
== MONO_LMFEXT_INTERP_EXIT
|| ext
->kind
== MONO_LMFEXT_INTERP_EXIT_WITH_CTX
);
649 frame
= (MonoInterpFrameHandle
*)ext
->interp_exit_data
;
650 ji
= mini_get_interp_callbacks ()->frame_get_jit_info (frame
);
652 *domain
= mono_domain_get ();
654 *out_ip
= mini_get_interp_callbacks ()->frame_get_ip (frame
);
660 no_seq_points_found (MonoMethod
*method
, int offset
)
663 * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space.
665 printf ("Unable to find seq points for method '%s', offset 0x%x.\n", mono_method_full_name (method
, TRUE
), offset
);
669 ss_depth_to_string (StepDepth depth
)
672 case STEP_DEPTH_OVER
:
676 case STEP_DEPTH_INTO
:
679 g_assert_not_reached ();
687 * Stop the single stepping operation given by SS_REQ.
690 ss_stop (SingleStepReq
*ss_req
)
695 for (l
= ss_req
->bps
; l
; l
= l
->next
) {
696 mono_de_clear_breakpoint ((MonoBreakpoint
*)l
->data
);
698 g_slist_free (ss_req
->bps
);
702 ss_req
->async_id
= 0;
703 ss_req
->async_stepout_method
= NULL
;
704 if (ss_req
->global
) {
705 mono_de_stop_single_stepping ();
706 ss_req
->global
= FALSE
;
711 ss_destroy (SingleStepReq
*req
)
713 DEBUG_PRINTF (1, "[dbg] ss_destroy.\n");
720 static SingleStepReq
*
721 ss_req_acquire (void)
734 mono_de_ss_req_release (SingleStepReq
*req
)
736 gboolean free
= FALSE
;
739 g_assert (req
->refcount
);
741 if (req
->refcount
== 0)
745 if (req
== the_ss_req
)
752 mono_de_cancel_ss (void)
755 mono_de_ss_req_release (the_ss_req
);
762 mono_de_process_single_step (void *tls
, gboolean from_signal
)
769 MonoContext
*ctx
= rt_callbacks
.tls_get_restore_state (tls
);
772 MonoSeqPointInfo
*info
;
773 SingleStepReq
*ss_req
;
775 /* Skip the instruction causing the single step */
776 rt_callbacks
.begin_single_step_processing (ctx
, from_signal
);
778 if (rt_callbacks
.try_process_suspend (tls
, ctx
))
782 * This can run concurrently with a clear_event_request () call, so needs locking/reference counts.
784 ss_req
= ss_req_acquire ();
787 // FIXME: A suspend race
790 if (mono_thread_internal_current () != ss_req
->thread
)
793 ip
= (guint8
*)MONO_CONTEXT_GET_IP (ctx
);
795 ji
= get_top_method_ji (ip
, &domain
, (gpointer
*)&ip
);
796 g_assert (ji
&& !ji
->is_trampoline
);
799 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
);
802 method
= jinfo_get_method (ji
);
805 if (method
->wrapper_type
&& method
->wrapper_type
!= MONO_WRAPPER_DYNAMIC_METHOD
)
810 * Stopping in memset makes half-initialized vtypes visible.
811 * Stopping in memcpy makes half-copied vtypes visible.
813 if (method
->klass
== mono_defaults
.string_class
&& (!strcmp (method
->name
, "memset") || strstr (method
->name
, "memcpy")))
817 * This could be in mono_de_ss_update method, but mono_find_next_seq_point_for_native_offset is pretty expensive method,
818 * hence we prefer this check here.
820 if (ss_req
->user_assemblies
) {
821 gboolean found
= FALSE
;
822 for (int k
= 0; ss_req
->user_assemblies
[k
]; k
++)
823 if (ss_req
->user_assemblies
[k
] == m_class_get_image (method
->klass
)->assembly
) {
832 * The ip points to the instruction causing the single step event, which is before
833 * the offset recorded in the seq point map, so find the next seq point after ip.
835 if (!mono_find_next_seq_point_for_native_offset (domain
, method
, (guint8
*)ip
- (guint8
*)ji
->code_start
, &info
, &sp
)) {
836 g_assert_not_reached ();
840 il_offset
= sp
.il_offset
;
842 if (!mono_de_ss_update (ss_req
, ji
, &sp
, tls
, ctx
, method
))
845 /* Start single stepping again from the current sequence point */
848 memset (&args
, 0, sizeof (args
));
849 args
.method
= method
;
852 args
.step_to_catch
= FALSE
;
857 mono_de_ss_start (ss_req
, &args
);
859 if ((ss_req
->filter
& STEP_FILTER_STATIC_CTOR
) &&
860 (method
->flags
& METHOD_ATTRIBUTE_SPECIAL_NAME
) &&
861 !strcmp (method
->name
, ".cctor"))
864 // FIXME: Has to lock earlier
866 reqs
= g_ptr_array_new ();
870 g_ptr_array_add (reqs
, ss_req
->req
);
873 bp_events
= rt_callbacks
.create_breakpoint_events (reqs
, NULL
, ji
, EVENT_KIND_BREAKPOINT
);
875 g_ptr_array_free (reqs
, TRUE
);
877 mono_loader_unlock ();
879 rt_callbacks
.process_breakpoint_events (bp_events
, method
, ctx
, il_offset
);
882 mono_de_ss_req_release (ss_req
);
888 * Return FALSE if single stepping needs to continue.
891 mono_de_ss_update (SingleStepReq
*req
, MonoJitInfo
*ji
, SeqPoint
*sp
, void *tls
, MonoContext
*ctx
, MonoMethod
* method
)
893 MonoDebugMethodInfo
*minfo
;
894 MonoDebugSourceLocation
*loc
= NULL
;
897 if ((req
->filter
& STEP_FILTER_STATIC_CTOR
)) {
898 DbgEngineStackFrame
**frames
;
900 rt_callbacks
.ss_calculate_framecount (tls
, ctx
, TRUE
, &frames
, &nframes
);
902 gboolean ret
= FALSE
;
903 gboolean method_in_stack
= FALSE
;
905 for (int i
= 0; i
< nframes
; i
++) {
906 MonoMethod
*external_method
= frames
[i
]->method
;
907 if (method
== external_method
)
908 method_in_stack
= TRUE
;
911 ret
= (external_method
->flags
& METHOD_ATTRIBUTE_SPECIAL_NAME
);
912 ret
= ret
&& !strcmp (external_method
->name
, ".cctor");
913 ret
= ret
&& (external_method
!= req
->start_method
);
917 if (!method_in_stack
) {
918 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
);
919 /*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);*/
921 for (int i
=0; i
< nframes
; i
++)
922 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
));
925 rt_callbacks
.ss_discard_frame_context (tls
);
931 if (req
->async_stepout_method
== method
) {
932 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
);
936 if (req
->depth
== STEP_DEPTH_OVER
&& (sp
->flags
& MONO_SEQ_POINT_FLAG_NONEMPTY_STACK
) && !(sp
->flags
& MONO_SEQ_POINT_FLAG_NESTED_CALL
)) {
938 * These seq points are inserted by the JIT after calls, step over needs to skip them.
940 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
);
944 if ((req
->depth
== STEP_DEPTH_OVER
|| req
->depth
== STEP_DEPTH_OUT
) && hit
&& !req
->async_stepout_method
) {
945 gboolean is_step_out
= req
->depth
== STEP_DEPTH_OUT
;
947 rt_callbacks
.ss_calculate_framecount (tls
, ctx
, FALSE
, NULL
, &nframes
);
949 // Because functions can call themselves recursively, we need to make sure we're stopping at the right stack depth.
950 // In case of step out, the target is the frame *enclosing* the one where the request was made.
951 int target_frames
= req
->nframes
+ (is_step_out
? -1 : 0);
952 if (req
->nframes
> 0 && nframes
> 0 && nframes
> target_frames
) {
953 /* Hit the breakpoint in a recursive call, don't halt */
954 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");
959 if (req
->depth
== STEP_DEPTH_INTO
&& req
->size
== STEP_SIZE_MIN
&& (sp
->flags
& MONO_SEQ_POINT_FLAG_NONEMPTY_STACK
) && req
->start_method
) {
961 rt_callbacks
.ss_calculate_framecount (tls
, ctx
, FALSE
, NULL
, &nframes
);
962 if (req
->start_method
== method
&& req
->nframes
&& nframes
== req
->nframes
) { //Check also frame count(could be recursion)
963 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
);
968 MonoDebugMethodAsyncInfo
* async_method
= mono_debug_lookup_method_async_debug_info (method
);
970 for (int i
= 0; i
< async_method
->num_awaits
; i
++) {
971 if (async_method
->yield_offsets
[i
] == sp
->il_offset
|| async_method
->resume_offsets
[i
] == sp
->il_offset
) {
972 mono_debug_free_method_async_debug_info (async_method
);
976 mono_debug_free_method_async_debug_info (async_method
);
979 if (req
->size
!= STEP_SIZE_LINE
)
982 /* Have to check whenever a different source line was reached */
983 minfo
= mono_debug_lookup_method (method
);
986 loc
= mono_debug_method_lookup_location (minfo
, sp
->il_offset
);
989 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
);
990 req
->last_method
= method
;
992 } else if (loc
&& method
== req
->last_method
&& loc
->row
== req
->last_line
) {
994 rt_callbacks
.ss_calculate_framecount (tls
, ctx
, FALSE
, NULL
, &nframes
);
995 if (nframes
== req
->nframes
) { // If the frame has changed we're clearly not on the same source line.
996 DEBUG_PRINTF (1, "[%p] Same source line (%d), continuing single stepping.\n", (gpointer
) (gsize
) mono_native_thread_id_get (), loc
->row
);
1002 req
->last_method
= method
;
1003 req
->last_line
= loc
->row
;
1004 mono_debug_free_source_location (loc
);
1011 mono_de_process_breakpoint (void *void_tls
, gboolean from_signal
)
1013 DebuggerTlsData
*tls
= (DebuggerTlsData
*)void_tls
;
1017 guint32 native_offset
;
1018 GPtrArray
*bp_reqs
, *ss_reqs_orig
, *ss_reqs
;
1019 EventKind kind
= EVENT_KIND_BREAKPOINT
;
1020 MonoContext
*ctx
= rt_callbacks
.tls_get_restore_state (tls
);
1022 MonoSeqPointInfo
*info
;
1026 if (rt_callbacks
.try_process_suspend (tls
, ctx
))
1029 ip
= (guint8
*)MONO_CONTEXT_GET_IP (ctx
);
1031 ji
= get_top_method_ji (ip
, NULL
, (gpointer
*)&ip
);
1032 g_assert (ji
&& !ji
->is_trampoline
);
1033 method
= jinfo_get_method (ji
);
1035 /* Compute the native offset of the breakpoint from the ip */
1036 native_offset
= ip
- (guint8
*)ji
->code_start
;
1038 if (!rt_callbacks
.begin_breakpoint_processing (tls
, ctx
, ji
, from_signal
))
1041 if (method
->wrapper_type
)
1044 bp_reqs
= g_ptr_array_new ();
1045 ss_reqs
= g_ptr_array_new ();
1046 ss_reqs_orig
= g_ptr_array_new ();
1048 mono_loader_lock ();
1051 * The ip points to the instruction causing the breakpoint event, which is after
1052 * the offset recorded in the seq point map, so find the prev seq point before ip.
1054 found_sp
= mono_find_prev_seq_point_for_native_offset (mono_domain_get (), method
, native_offset
, &info
, &sp
);
1057 no_seq_points_found (method
, native_offset
);
1059 g_assert (found_sp
);
1061 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
);
1063 mono_debugger_log_bp_hit (tls
, method
, sp
.il_offset
);
1065 mono_de_collect_breakpoints_by_sp (&sp
, ji
, ss_reqs_orig
, bp_reqs
);
1067 if (bp_reqs
->len
== 0 && ss_reqs_orig
->len
== 0) {
1068 /* Maybe a method entry/exit event */
1069 if (sp
.il_offset
== METHOD_ENTRY_IL_OFFSET
)
1070 kind
= EVENT_KIND_METHOD_ENTRY
;
1071 else if (sp
.il_offset
== METHOD_EXIT_IL_OFFSET
)
1072 kind
= EVENT_KIND_METHOD_EXIT
;
1075 /* Process single step requests */
1076 for (i
= 0; i
< ss_reqs_orig
->len
; ++i
) {
1077 EventRequest
*req
= (EventRequest
*)g_ptr_array_index (ss_reqs_orig
, i
);
1078 SingleStepReq
*ss_req
= (SingleStepReq
*)req
->info
;
1081 //if we hit async_stepout_method, it's our no matter which thread
1082 if ((ss_req
->async_stepout_method
!= method
) && (ss_req
->async_id
|| mono_thread_internal_current () != ss_req
->thread
)) {
1083 DbgEngineStackFrame
**frames
;
1085 //We have different thread and we don't have async stepping in progress
1086 //it's breakpoint in parallel thread, ignore it
1087 if (ss_req
->async_id
== 0)
1090 rt_callbacks
.ss_discard_frame_context (tls
);
1091 rt_callbacks
.ss_calculate_framecount (tls
, ctx
, FALSE
, &frames
, &nframes
);
1092 //make sure we have enough data to get current async method instance id
1093 if (nframes
== 0 || !rt_callbacks
.ensure_jit (frames
[0]))
1096 //Check method is async before calling get_this_async_id
1097 MonoDebugMethodAsyncInfo
* asyncMethod
= mono_debug_lookup_method_async_debug_info (method
);
1101 mono_debug_free_method_async_debug_info (asyncMethod
);
1103 //breakpoint was hit in parallelly executing async method, ignore it
1104 if (ss_req
->async_id
!= rt_callbacks
.get_this_async_id (frames
[0]))
1108 //Update stepping request to new thread/frame_count that we are continuing on
1109 //so continuing with normal stepping works as expected
1110 if (ss_req
->async_stepout_method
|| ss_req
->async_id
) {
1112 rt_callbacks
.ss_discard_frame_context (tls
);
1113 rt_callbacks
.ss_calculate_framecount (tls
, ctx
, FALSE
, NULL
, &nframes
);
1114 ss_req
->thread
= mono_thread_internal_current ();
1115 ss_req
->nframes
= nframes
;
1118 hit
= mono_de_ss_update (ss_req
, ji
, &sp
, tls
, ctx
, method
);
1120 g_ptr_array_add (ss_reqs
, req
);
1122 SingleStepArgs args
;
1123 memset (&args
, 0, sizeof (args
));
1124 args
.method
= method
;
1127 args
.step_to_catch
= FALSE
;
1132 mono_de_ss_start (ss_req
, &args
);
1135 void *bp_events
= rt_callbacks
.create_breakpoint_events (ss_reqs
, bp_reqs
, ji
, kind
);
1137 mono_loader_unlock ();
1139 g_ptr_array_free (bp_reqs
, TRUE
);
1140 g_ptr_array_free (ss_reqs
, TRUE
);
1142 rt_callbacks
.process_breakpoint_events (bp_events
, method
, ctx
, 0);
1148 * Reject breakpoint if it is a duplicate of one already in list or hash table.
1151 ss_bp_is_unique (GSList
*bps
, GHashTable
*ss_req_bp_cache
, MonoMethod
*method
, guint32 il_offset
)
1153 if (ss_req_bp_cache
) {
1154 MonoBreakpoint dummy
= {method
, (long)il_offset
, NULL
, NULL
};
1155 return !g_hash_table_lookup (ss_req_bp_cache
, &dummy
);
1157 for (GSList
*l
= bps
; l
; l
= l
->next
) {
1158 MonoBreakpoint
*bp
= (MonoBreakpoint
*)l
->data
;
1159 if (bp
->method
== method
&& bp
->il_offset
== il_offset
)
1168 * GHashTable equality for a MonoBreakpoint (only care about method and il_offset fields)
1171 ss_bp_eq (gconstpointer ka
, gconstpointer kb
)
1173 const MonoBreakpoint
*s1
= (const MonoBreakpoint
*)ka
;
1174 const MonoBreakpoint
*s2
= (const MonoBreakpoint
*)kb
;
1175 return (s1
->method
== s2
->method
&& s1
->il_offset
== s2
->il_offset
) ? 1 : 0;
1181 * GHashTable hash for a MonoBreakpoint (only care about method and il_offset fields)
1184 ss_bp_hash (gconstpointer data
)
1186 const MonoBreakpoint
*s
= (const MonoBreakpoint
*)data
;
1187 guint hash
= (guint
) (uintptr_t) s
->method
;
1188 hash
^= ((guint
)s
->il_offset
) << 16; // Assume low bits are more interesting
1189 hash
^= ((guint
)s
->il_offset
) >> 16;
1193 #define MAX_LINEAR_SCAN_BPS 7
1198 * Create a new breakpoint and add it to a step request.
1199 * Will adjust the bp count and cache used by mono_de_ss_start.
1202 ss_bp_add_one (SingleStepReq
*ss_req
, int *ss_req_bp_count
, GHashTable
**ss_req_bp_cache
,
1203 MonoMethod
*method
, guint32 il_offset
)
1205 // This list is getting too long, switch to using the hash table
1206 if (!*ss_req_bp_cache
&& *ss_req_bp_count
> MAX_LINEAR_SCAN_BPS
) {
1207 *ss_req_bp_cache
= g_hash_table_new (ss_bp_hash
, ss_bp_eq
);
1208 for (GSList
*l
= ss_req
->bps
; l
; l
= l
->next
)
1209 g_hash_table_insert (*ss_req_bp_cache
, l
->data
, l
->data
);
1212 if (ss_bp_is_unique (ss_req
->bps
, *ss_req_bp_cache
, method
, il_offset
)) {
1213 // Create and add breakpoint
1214 MonoBreakpoint
*bp
= mono_de_set_breakpoint (method
, il_offset
, ss_req
->req
, NULL
);
1215 ss_req
->bps
= g_slist_append (ss_req
->bps
, bp
);
1216 if (*ss_req_bp_cache
)
1217 g_hash_table_insert (*ss_req_bp_cache
, bp
, bp
);
1218 (*ss_req_bp_count
)++;
1220 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
);
1225 is_last_non_empty (SeqPoint
* sp
, MonoSeqPointInfo
*info
)
1229 SeqPoint
* next
= g_new (SeqPoint
, sp
->next_len
);
1230 mono_seq_point_init_next (info
, *sp
, next
);
1231 for (int i
= 0; i
< sp
->next_len
; i
++) {
1232 if (next
[i
].flags
& MONO_SEQ_POINT_FLAG_NONEMPTY_STACK
&& !(next
[i
].flags
& MONO_SEQ_POINT_FLAG_NESTED_CALL
)) {
1233 if (!is_last_non_empty (&next
[i
], info
)) {
1249 * Start the single stepping operation given by SS_REQ from the sequence point SP.
1250 * If CTX is not set, then this can target any thread. If CTX is set, then TLS should
1251 * belong to the same thread as CTX.
1252 * If FRAMES is not-null, use that instead of tls->frames for placing breakpoints etc.
1255 mono_de_ss_start (SingleStepReq
*ss_req
, SingleStepArgs
*ss_args
)
1257 int i
, j
, frame_index
;
1258 SeqPoint
*next_sp
, *parent_sp
= NULL
;
1259 SeqPoint local_sp
, local_parent_sp
;
1261 MonoSeqPointInfo
*parent_info
;
1262 MonoMethod
*parent_sp_method
= NULL
;
1263 gboolean enable_global
= FALSE
;
1265 // When 8 or more entries are in bps, we build a hash table to serve as a set of breakpoints.
1266 // Recreating this on each pass is a little wasteful but at least keeps behavior linear.
1267 int ss_req_bp_count
= g_slist_length (ss_req
->bps
);
1268 GHashTable
*ss_req_bp_cache
= NULL
;
1270 /* Stop the previous operation */
1273 gboolean locked
= FALSE
;
1275 void *tls
= ss_args
->tls
;
1276 MonoMethod
*method
= ss_args
->method
;
1277 DbgEngineStackFrame
**frames
= ss_args
->frames
;
1278 int nframes
= ss_args
->nframes
;
1279 SeqPoint
*sp
= &ss_args
->sp
;
1282 * Implement single stepping using breakpoints if possible.
1284 if (ss_args
->step_to_catch
) {
1285 ss_bp_add_one (ss_req
, &ss_req_bp_count
, &ss_req_bp_cache
, method
, sp
->il_offset
);
1289 if (ss_args
->ctx
&& !frames
) {
1291 mono_loader_lock ();
1294 /* Need parent frames */
1295 rt_callbacks
.ss_calculate_framecount (tls
, ss_args
->ctx
, FALSE
, &frames
, &nframes
);
1298 MonoDebugMethodAsyncInfo
* asyncMethod
= mono_debug_lookup_method_async_debug_info (method
);
1300 /* Need to stop in catch clauses as well */
1301 for (i
= ss_req
->depth
== STEP_DEPTH_OUT
? 1 : 0; i
< nframes
; ++i
) {
1302 DbgEngineStackFrame
*frame
= frames
[i
];
1305 MonoJitInfo
*jinfo
= frame
->ji
;
1306 for (j
= 0; j
< jinfo
->num_clauses
; ++j
) {
1307 // In case of async method we don't want to place breakpoint on last catch handler(which state machine added for whole method)
1308 if (asyncMethod
&& asyncMethod
->num_awaits
&& i
== 0 && j
+ 1 == jinfo
->num_clauses
)
1310 MonoJitExceptionInfo
*ei
= &jinfo
->clauses
[j
];
1312 if (mono_find_next_seq_point_for_native_offset (frame
->domain
, frame
->method
, (char*)ei
->handler_start
- (char*)jinfo
->code_start
, NULL
, &local_sp
))
1313 ss_bp_add_one (ss_req
, &ss_req_bp_count
, &ss_req_bp_cache
, frame
->method
, local_sp
.il_offset
);
1318 if (asyncMethod
&& asyncMethod
->num_awaits
&& nframes
&& rt_callbacks
.ensure_jit (frames
[0])) {
1319 //asyncMethod has value and num_awaits > 0, this means we are inside async method with awaits
1321 // Check if we hit yield_offset during normal stepping, because if we did...
1322 // Go into special async stepping mode which places breakpoint on resumeOffset
1323 // of this await call and sets async_id so we can distinguish it from parallel executions
1324 for (i
= 0; i
< asyncMethod
->num_awaits
; i
++) {
1325 if (sp
->il_offset
== asyncMethod
->yield_offsets
[i
]) {
1326 ss_req
->async_id
= rt_callbacks
.get_this_async_id (frames
[0]);
1327 ss_bp_add_one (ss_req
, &ss_req_bp_count
, &ss_req_bp_cache
, method
, asyncMethod
->resume_offsets
[i
]);
1328 g_hash_table_destroy (ss_req_bp_cache
);
1329 mono_debug_free_method_async_debug_info (asyncMethod
);
1331 mono_loader_unlock ();
1335 //If we are at end of async method and doing step-in or step-over...
1336 //Switch to step-out, so whole NotifyDebuggerOfWaitCompletion magic happens...
1337 if (is_last_non_empty (sp
, ss_args
->info
)) {
1338 ss_req
->depth
= STEP_DEPTH_OUT
;//setting depth to step-out is important, don't inline IF, because code later depends on this
1340 if (ss_req
->depth
== STEP_DEPTH_OUT
) {
1341 //If we are inside `async void` method, do normal step-out
1342 if (rt_callbacks
.set_set_notification_for_wait_completion_flag (frames
[0])) {
1343 ss_req
->async_id
= rt_callbacks
.get_this_async_id (frames
[0]);
1344 ss_req
->async_stepout_method
= rt_callbacks
.get_notify_debugger_of_wait_completion_method ();
1345 ss_bp_add_one (ss_req
, &ss_req_bp_count
, &ss_req_bp_cache
, ss_req
->async_stepout_method
, 0);
1346 g_hash_table_destroy (ss_req_bp_cache
);
1347 mono_debug_free_method_async_debug_info (asyncMethod
);
1349 mono_loader_unlock ();
1356 mono_debug_free_method_async_debug_info (asyncMethod
);
1359 * Find the first sequence point in the current or in a previous frame which
1360 * is not the last in its method.
1362 if (ss_req
->depth
== STEP_DEPTH_OUT
) {
1363 /* Ignore seq points in current method */
1364 while (frame_index
< nframes
) {
1365 DbgEngineStackFrame
*frame
= frames
[frame_index
];
1367 method
= frame
->method
;
1368 found_sp
= mono_find_prev_seq_point_for_native_offset (frame
->domain
, frame
->method
, frame
->native_offset
, &ss_args
->info
, &local_sp
);
1369 sp
= (found_sp
)? &local_sp
: NULL
;
1371 if (sp
&& sp
->next_len
!= 0)
1374 // There could be method calls before the next seq point in the caller when using nested calls
1375 //enable_global = TRUE;
1377 if (sp
&& sp
->next_len
== 0) {
1379 while (frame_index
< nframes
) {
1380 DbgEngineStackFrame
*frame
= frames
[frame_index
];
1382 method
= frame
->method
;
1383 found_sp
= mono_find_prev_seq_point_for_native_offset (frame
->domain
, frame
->method
, frame
->native_offset
, &ss_args
->info
, &local_sp
);
1384 sp
= (found_sp
)? &local_sp
: NULL
;
1385 if (sp
&& sp
->next_len
!= 0)
1391 /* Have to put a breakpoint into a parent frame since the seq points might not cover all control flow out of the method */
1392 while (frame_index
< nframes
) {
1393 DbgEngineStackFrame
*frame
= frames
[frame_index
];
1395 parent_sp_method
= frame
->method
;
1396 found_sp
= mono_find_prev_seq_point_for_native_offset (frame
->domain
, frame
->method
, frame
->native_offset
, &parent_info
, &local_parent_sp
);
1397 parent_sp
= found_sp
? &local_parent_sp
: NULL
;
1398 if (found_sp
&& parent_sp
->next_len
!= 0)
1406 if (sp
&& sp
->next_len
> 0) {
1407 SeqPoint
* next
= g_new(SeqPoint
, sp
->next_len
);
1409 mono_seq_point_init_next (ss_args
->info
, *sp
, next
);
1410 for (i
= 0; i
< sp
->next_len
; i
++) {
1413 ss_bp_add_one (ss_req
, &ss_req_bp_count
, &ss_req_bp_cache
, method
, next_sp
->il_offset
);
1419 SeqPoint
* next
= g_new(SeqPoint
, parent_sp
->next_len
);
1421 mono_seq_point_init_next (parent_info
, *parent_sp
, next
);
1422 for (i
= 0; i
< parent_sp
->next_len
; i
++) {
1425 ss_bp_add_one (ss_req
, &ss_req_bp_count
, &ss_req_bp_cache
, parent_sp_method
, next_sp
->il_offset
);
1430 if (ss_req
->nframes
== 0)
1431 ss_req
->nframes
= nframes
;
1433 if ((ss_req
->depth
== STEP_DEPTH_OVER
) && (!sp
&& !parent_sp
)) {
1434 DEBUG_PRINTF (1, "[dbg] No parent frame for step over, transition to step into.\n");
1436 * This is needed since if we leave managed code, and later return to it, step over
1437 * is not going to stop.
1438 * This approach is a bit ugly, since we change the step depth, but it only affects
1439 * clients who reuse the same step request, and only in this special case.
1441 ss_req
->depth
= STEP_DEPTH_INTO
;
1444 if (ss_req
->depth
== STEP_DEPTH_INTO
) {
1445 /* Enable global stepping so we stop at method entry too */
1446 enable_global
= TRUE
;
1450 * The ctx/frame info computed above will become invalid when we continue.
1452 rt_callbacks
.ss_discard_frame_context (tls
);
1455 if (enable_global
) {
1456 DEBUG_PRINTF (1, "[dbg] Turning on global single stepping.\n");
1457 ss_req
->global
= TRUE
;
1458 mono_de_start_single_stepping ();
1459 } else if (!ss_req
->bps
) {
1460 DEBUG_PRINTF (1, "[dbg] Turning on global single stepping.\n");
1461 ss_req
->global
= TRUE
;
1462 mono_de_start_single_stepping ();
1464 ss_req
->global
= FALSE
;
1467 g_hash_table_destroy (ss_req_bp_cache
);
1470 mono_loader_unlock ();
1473 rt_callbacks
.ss_args_destroy (ss_args
);
1478 * Start single stepping of thread THREAD
1481 mono_de_ss_create (MonoInternalThread
*thread
, StepSize size
, StepDepth depth
, StepFilter filter
, EventRequest
*req
)
1483 int err
= rt_callbacks
.ensure_runtime_is_suspended ();
1487 // FIXME: Multiple requests
1489 DEBUG_PRINTF (0, "Received a single step request while the previous one was still active.\n");
1490 return DE_ERR_NOT_IMPLEMENTED
;
1493 DEBUG_PRINTF (1, "[dbg] Starting single step of thread %p (depth=%s).\n", thread
, ss_depth_to_string (depth
));
1495 SingleStepReq
*ss_req
= g_new0 (SingleStepReq
, 1);
1497 ss_req
->thread
= thread
;
1498 ss_req
->size
= size
;
1499 ss_req
->depth
= depth
;
1500 ss_req
->filter
= filter
;
1501 ss_req
->refcount
= 1;
1504 for (int i
= 0; i
< req
->nmodifiers
; i
++) {
1505 if (req
->modifiers
[i
].kind
== MOD_KIND_ASSEMBLY_ONLY
) {
1506 ss_req
->user_assemblies
= req
->modifiers
[i
].data
.assemblies
;
1511 SingleStepArgs args
;
1512 err
= rt_callbacks
.ss_create_init_args (ss_req
, &args
);
1516 the_ss_req
= ss_req
;
1518 mono_de_ss_start (ss_req
, &args
);
1524 * mono_de_set_log_level:
1526 * Configures logging level and output file. Must be called together with mono_de_init.
1529 mono_de_set_log_level (int level
, FILE *file
)
1538 * Inits the shared debugger engine. Not reentrant.
1541 mono_de_init (DebuggerEngineCallbacks
*cbs
)
1543 rt_callbacks
= *cbs
;
1544 mono_coop_mutex_init_recursive (&debug_mutex
);
1547 breakpoints_init ();
1549 mono_debugger_log_init ();
1553 mono_de_cleanup (void)
1555 breakpoints_cleanup ();
1560 mono_debugger_free_objref (gpointer value
)
1562 ObjRef
*o
= (ObjRef
*)value
;
1564 mono_gchandle_free_internal (o
->handle
);