X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fdebugger-agent.c;h=016423072bd74048996f6c85363e28b6c52d90d7;hb=e91a66fc94957b893cc896e32439fc957059a2de;hp=98381c28f19073d5cd037a10bbff5b98279ca717;hpb=863ddf25bfb4e88db7225dd4968a4403840c42af;p=mono.git diff --git a/mono/mini/debugger-agent.c b/mono/mini/debugger-agent.c index 98381c28f19..016423072bd 100644 --- a/mono/mini/debugger-agent.c +++ b/mono/mini/debugger-agent.c @@ -1,5 +1,6 @@ -/* - * debugger-agent.c: Soft Debugger back-end module +/** + * \file + * Soft Debugger back-end module * * Author: * Zoltan Varga (vargaz@gmail.com) @@ -46,24 +47,23 @@ #include #endif -#ifdef PLATFORM_ANDROID +#ifdef HOST_ANDROID #include #include #include #endif #include -#include -#include +#include #include #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -72,10 +72,11 @@ #include #include #include +#include #include "debugger-agent.h" #include "mini.h" #include "seq-points.h" -#include +#include "interp/interp.h" /* * On iOS we can't use System.Environment.Exit () as it will do the wrong @@ -137,6 +138,7 @@ typedef struct MonoContext ctx; MonoDebugMethodJitInfo *jit; MonoJitInfo *ji; + MonoInterpFrameHandle interp_frame; int flags; mgreg_t *reg_locations [MONO_MAX_IREGS]; /* @@ -273,7 +275,7 @@ typedef struct { #define HEADER_LENGTH 11 #define MAJOR_VERSION 2 -#define MINOR_VERSION 44 +#define MINOR_VERSION 45 typedef enum { CMD_SET_VM = 1, @@ -450,7 +452,8 @@ typedef enum { CMD_ASSEMBLY_GET_MANIFEST_MODULE = 3, CMD_ASSEMBLY_GET_OBJECT = 4, CMD_ASSEMBLY_GET_TYPE = 5, - CMD_ASSEMBLY_GET_NAME = 6 + CMD_ASSEMBLY_GET_NAME = 6, + CMD_ASSEMBLY_GET_DOMAIN = 7 } CmdAssembly; typedef enum { @@ -571,6 +574,10 @@ typedef struct { int nframes; /* If set, don't stop in methods that are not part of user assemblies */ MonoAssembly** user_assemblies; + /* Used to distinguish stepping breakpoint hits in parallel tasks executions */ + int async_id; + /* Used to know if we are in process of async step-out and distishing from exception breakpoints */ + MonoMethod* async_stepout_method; } SingleStepReq; /* @@ -588,11 +595,6 @@ typedef struct { MonoClass *klass; } EventInfo; -/* Dummy structure used for the profiler callbacks */ -typedef struct { - void* dummy; -} DebuggerProfiler; - typedef struct { guint8 *buf, *p, *end; } Buffer; @@ -605,7 +607,7 @@ typedef struct ReplyPacket { #define DEBUG(level,s) do { if (G_UNLIKELY ((level) <= log_level)) { s; fflush (log_file); } } while (0) -#ifdef PLATFORM_ANDROID +#ifdef HOST_ANDROID #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { g_print (__VA_ARGS__); } } while (0) #else #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { fprintf (log_file, __VA_ARGS__); fflush (log_file); } } while (0) @@ -693,8 +695,6 @@ static MonoCoopCond debugger_thread_exited_cond; /* Mutex for the cond var above */ static MonoCoopMutex debugger_thread_exited_mutex; -static DebuggerProfiler debugger_profiler; - /* The single step request instance */ static SingleStepReq *ss_req; @@ -742,7 +742,7 @@ static void thread_startup (MonoProfiler *prof, uintptr_t tid); static void thread_end (MonoProfiler *prof, uintptr_t tid); -static void appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result); +static void appdomain_load (MonoProfiler *prof, MonoDomain *domain); static void appdomain_start_unload (MonoProfiler *prof, MonoDomain *domain); @@ -754,7 +754,7 @@ static void emit_thread_start (gpointer key, gpointer value, gpointer user_data) static void invalidate_each_thread (gpointer key, gpointer value, gpointer user_data); -static void assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result); +static void assembly_load (MonoProfiler *prof, MonoAssembly *assembly); static void assembly_unload (MonoProfiler *prof, MonoAssembly *assembly); @@ -762,7 +762,11 @@ static void emit_assembly_load (gpointer assembly, gpointer user_data); static void emit_type_load (gpointer key, gpointer type, gpointer user_data); -static void jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result); +static void jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo); + +static void jit_failed (MonoProfiler *prof, MonoMethod *method); + +static void jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo); static void add_pending_breakpoints (MonoMethod *method, MonoJitInfo *jinfo); @@ -814,7 +818,13 @@ register_socket_transport (void); static inline gboolean is_debugger_thread (void) { - return mono_native_thread_id_equals (mono_native_thread_id_get (), debugger_thread_id); + MonoInternalThread *internal; + + internal = mono_thread_internal_current (); + if (!internal) + return FALSE; + + return internal->debugger_thread; } static int @@ -825,9 +835,10 @@ parse_address (char *address, char **host, int *port) if (pos == NULL || pos == address) return 1; - *host = (char *)g_malloc (pos - address + 1); - strncpy (*host, address, pos - address); - (*host) [pos - address] = '\0'; + size_t len = pos - address; + *host = (char *)g_malloc (len + 1); + memcpy (*host, address, len); + (*host) [len] = '\0'; *port = atoi (pos + 1); @@ -871,7 +882,7 @@ mono_debugger_agent_parse_options (char *options) char **args, **ptr; char *host; int port; - const char *extra; + char *extra; #ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED fprintf (stderr, "--debugger-agent is not supported on this platform.\n"); @@ -879,8 +890,10 @@ mono_debugger_agent_parse_options (char *options) #endif extra = g_getenv ("MONO_SDB_ENV_OPTIONS"); - if (extra) + if (extra) { options = g_strdup_printf ("%s,%s", options, extra); + g_free (extra); + } agent_config.enabled = TRUE; agent_config.suspend = TRUE; @@ -979,20 +992,25 @@ mono_debugger_agent_init (void) mono_coop_mutex_init (&debugger_thread_exited_mutex); mono_coop_cond_init (&debugger_thread_exited_cond); - mono_profiler_install ((MonoProfiler*)&debugger_profiler, runtime_shutdown); - mono_profiler_set_events ((MonoProfileFlags)(MONO_PROFILE_APPDOMAIN_EVENTS | MONO_PROFILE_THREADS | MONO_PROFILE_ASSEMBLY_EVENTS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_METHOD_EVENTS)); - mono_profiler_install_runtime_initialized (runtime_initialized); - mono_profiler_install_appdomain (NULL, appdomain_load, appdomain_start_unload, appdomain_unload); - mono_profiler_install_thread (thread_startup, thread_end); - mono_profiler_install_assembly (NULL, assembly_load, assembly_unload, NULL); - mono_profiler_install_jit_end (jit_end); + MonoProfilerHandle prof = mono_profiler_create (NULL); + mono_profiler_set_runtime_shutdown_end_callback (prof, runtime_shutdown); + mono_profiler_set_runtime_initialized_callback (prof, runtime_initialized); + mono_profiler_set_domain_loaded_callback (prof, appdomain_load); + mono_profiler_set_domain_unloading_callback (prof, appdomain_start_unload); + mono_profiler_set_domain_unloaded_callback (prof, appdomain_unload); + mono_profiler_set_thread_started_callback (prof, thread_startup); + mono_profiler_set_thread_stopped_callback (prof, thread_end); + mono_profiler_set_assembly_loaded_callback (prof, assembly_load); + mono_profiler_set_assembly_unloading_callback (prof, assembly_unload); + mono_profiler_set_jit_done_callback (prof, jit_done); + mono_profiler_set_jit_failed_callback (prof, jit_failed); mono_native_tls_alloc (&debugger_tls_id, NULL); /* Needed by the hash_table_new_type () call below */ mono_gc_base_init (); - thread_to_tls = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "thread-to-tls table"); + thread_to_tls = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "thread-to-tls table"); tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread table"); @@ -1629,7 +1647,13 @@ stop_debugger_thread (void) static void start_debugger_thread (void) { - debugger_thread_handle = mono_threads_create_thread (debugger_thread, NULL, NULL, NULL); + MonoError error; + MonoInternalThread *thread; + + thread = mono_thread_create_internal (mono_get_root_domain (), debugger_thread, NULL, MONO_THREAD_CREATE_FLAGS_DEBUGGER, &error); + mono_error_assert_ok (&error); + + debugger_thread_handle = mono_threads_open_thread_handle (thread->handle); g_assert (debugger_thread_handle); } @@ -1933,7 +1957,7 @@ objrefs_init (void) { objrefs = g_hash_table_new_full (NULL, NULL, NULL, free_objref); obj_to_objref = g_hash_table_new (NULL, NULL); - suspended_objs = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "suspended objects table"); + suspended_objs = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "suspended objects table"); } static void @@ -2460,6 +2484,35 @@ static void invoke_method (void); * SUSPEND/RESUME */ +static MonoJitInfo* +get_top_method_ji (gpointer ip, MonoDomain **domain, gpointer *out_ip) +{ + MonoJitInfo *ji; + + if (out_ip) + *out_ip = ip; + + ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, domain); + if (!ji) { + /* Could be an interpreter method */ + + MonoLMF *lmf = mono_get_lmf (); + MonoInterpFrameHandle *frame; + + g_assert (((guint64)lmf->previous_lmf) & 2); + MonoLMFExt *ext = (MonoLMFExt*)lmf; + + g_assert (ext->interp_exit); + frame = ext->interp_exit_data; + ji = mono_interp_frame_get_jit_info (frame); + if (domain) + *domain = mono_domain_get (); + if (out_ip) + *out_ip = mono_interp_frame_get_ip (frame); + } + return ji; +} + /* * save_thread_context: * @@ -2599,13 +2652,23 @@ thread_interrupt (DebuggerTlsData *tls, MonoThreadInfo *info, MonoJitInfo *ji) * suspended when it returns to managed code, so the parent's ctx should * remain valid. */ + MonoThreadUnwindState *state = mono_thread_info_get_suspend_state (info); + data.last_frame_set = FALSE; - mono_get_eh_callbacks ()->mono_walk_stack_with_state (get_last_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &data); + mono_get_eh_callbacks ()->mono_walk_stack_with_state (get_last_frame, state, MONO_UNWIND_SIGNAL_SAFE, &data); if (data.last_frame_set) { gpointer jit_tls = ((MonoThreadInfo*)tls->thread->thread_info)->jit_data; memcpy (&tls->async_last_frame, &data.last_frame, sizeof (StackFrameInfo)); + if (data.last_frame.type == FRAME_TYPE_INTERP_TO_MANAGED) { + /* + * Store the current lmf instead of the parent one, since that + * contains the interp exit data. + */ + data.lmf = state->unwind_data [MONO_UNWIND_DATA_LMF]; + } + copy_unwind_state_from_frame_data (&tls->async_state, &data, jit_tls); copy_unwind_state_from_frame_data (&tls->context, &data, jit_tls); } else { @@ -2677,7 +2740,7 @@ notify_thread (gpointer key, gpointer value, gpointer user_data) DebuggerTlsData *tls = (DebuggerTlsData *)value; MonoNativeThreadId tid = MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid); - if (mono_native_thread_id_equals (mono_native_thread_id_get (), tid) || tls->terminated) + if (mono_thread_internal_is_current (thread) || tls->terminated) return; DEBUG_PRINTF (1, "[%p] Interrupting %p...\n", (gpointer) (gsize) mono_native_thread_id_get (), (gpointer)tid); @@ -2731,8 +2794,8 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx) return; } - ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL); - + ji = get_top_method_ji (ip, NULL, NULL); + g_assert (ji); /* Can't suspend in these methods */ method = jinfo_get_method (ji); if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy"))) @@ -2949,7 +3012,7 @@ count_thread (gpointer key, gpointer value, gpointer user_data) { DebuggerTlsData *tls = (DebuggerTlsData *)value; - if (!tls->suspended && !tls->terminated) + if (!tls->suspended && !tls->terminated && !mono_thread_internal_is_current (tls->thread)) *(int*)user_data = *(int*)user_data + 1; } @@ -3031,7 +3094,7 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) SeqPoint sp; int flags = 0; - if (info->type != FRAME_TYPE_MANAGED) { + if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) { if (info->type == FRAME_TYPE_DEBUGGER_INVOKE) { /* Mark the last frame as an invoke frame */ if (ud->frames) @@ -3084,6 +3147,7 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) frame->native_offset = info->native_offset; frame->flags = flags; frame->ji = info->ji; + frame->interp_frame = info->interp_frame; if (info->reg_locations) memcpy (frame->reg_locations, info->reg_locations, MONO_MAX_IREGS * sizeof (mgreg_t*)); if (ctx) { @@ -3243,8 +3307,8 @@ emit_appdomain_load (gpointer key, gpointer value, gpointer user_data) static void emit_thread_start (gpointer key, gpointer value, gpointer user_data) { - if (!mono_native_thread_id_equals (MONO_UINT_TO_NATIVE_THREAD_ID (GPOINTER_TO_UINT (key)), debugger_thread_id)) - process_profiler_event (EVENT_KIND_THREAD_START, value); + g_assert (!mono_native_thread_id_equals (MONO_UINT_TO_NATIVE_THREAD_ID (GPOINTER_TO_UINT (key)), debugger_thread_id)); + process_profiler_event (EVENT_KIND_THREAD_START, value); } /* @@ -3805,7 +3869,7 @@ thread_startup (MonoProfiler *prof, uintptr_t tid) MonoInternalThread *old_thread; DebuggerTlsData *tls; - if (mono_native_thread_id_equals (MONO_UINT_TO_NATIVE_THREAD_ID (tid), debugger_thread_id)) + if (is_debugger_thread ()) return; g_assert (mono_native_thread_id_equals (MONO_UINT_TO_NATIVE_THREAD_ID (tid), MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))); @@ -3884,8 +3948,7 @@ thread_end (MonoProfiler *prof, uintptr_t tid) if (thread) { DEBUG_PRINTF (1, "[%p] Thread terminated, obj=%p, tls=%p.\n", (gpointer)tid, thread, tls); - if (mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (tid)) - && !mono_native_tls_get_value (debugger_tls_id) + if (mono_thread_internal_is_current (thread) && !mono_native_tls_get_value (debugger_tls_id) ) { /* * This can happen on darwin since we deregister threads using pthread dtors. @@ -3899,7 +3962,7 @@ thread_end (MonoProfiler *prof, uintptr_t tid) } static void -appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result) +appdomain_load (MonoProfiler *prof, MonoDomain *domain) { mono_loader_lock (); g_hash_table_insert (domains, domain, domain); @@ -3961,7 +4024,7 @@ invalidate_each_thread (gpointer key, gpointer value, gpointer user_data) } static void -assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result) +assembly_load (MonoProfiler *prof, MonoAssembly *assembly) { /* Sent later in jit_end () */ dbg_lock (); @@ -4011,18 +4074,57 @@ send_type_load (MonoClass *klass) static void send_types_for_domain (MonoDomain *domain, void *user_data) { + MonoDomain* old_domain; AgentDomainInfo *info = NULL; info = get_agent_domain_info (domain); g_assert (info); + + old_domain = mono_domain_get (); + + mono_domain_set (domain, TRUE); mono_loader_lock (); g_hash_table_foreach (info->loaded_classes, emit_type_load, NULL); mono_loader_unlock (); + + mono_domain_set (old_domain, TRUE); } static void -jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result) +send_assemblies_for_domain (MonoDomain *domain, void *user_data) +{ + GSList *tmp; + MonoDomain* old_domain; + + old_domain = mono_domain_get (); + + mono_domain_set (domain, TRUE); + + mono_domain_assemblies_lock (domain); + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + MonoAssembly* ass = (MonoAssembly *)tmp->data; + emit_assembly_load (ass, NULL); + } + mono_domain_assemblies_unlock (domain); + + mono_domain_set (old_domain, TRUE); +} + +static void +jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo) +{ + jit_end (prof, method, jinfo); +} + +static void +jit_failed (MonoProfiler *prof, MonoMethod *method) +{ + jit_end (prof, method, NULL); +} + +static void +jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo) { /* * We emit type load events when the first method of the type is JITted, @@ -4050,7 +4152,7 @@ jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result) send_type_load (method->klass); - if (!result) + if (jinfo) add_pending_breakpoints (method, jinfo); } @@ -4117,7 +4219,7 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo gboolean it_has_sp = FALSE; if (error) - mono_error_init (error); + error_init (error); mono_seq_point_iterator_init (&it, seq_points); while (mono_seq_point_iterator_next (&it)) { @@ -4183,11 +4285,15 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo if (it.seq_point.native_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) { DEBUG_PRINTF (1, "[dbg] Attempting to insert seq point at dead IL offset %d, ignoring.\n", (int)bp->il_offset); } else if (count == 0) { + if (ji->is_interp) { + mono_interp_set_breakpoint (ji, inst->ip); + } else { #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED - mono_arch_set_breakpoint (ji, inst->ip); + mono_arch_set_breakpoint (ji, inst->ip); #else - NOT_IMPLEMENTED; + NOT_IMPLEMENTED; #endif + } } 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); @@ -4209,7 +4315,10 @@ remove_breakpoint (BreakpointInstance *inst) g_assert (count > 0); if (count == 1 && inst->native_offset != SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) { - mono_arch_clear_breakpoint (ji, ip); + if (ji->is_interp) + mono_interp_clear_breakpoint (ji, ip); + else + mono_arch_clear_breakpoint (ji, ip); DEBUG_PRINTF (1, "[dbg] Clear breakpoint at %s [%p].\n", mono_method_full_name (jinfo_get_method (ji), TRUE), ip); } #else @@ -4317,7 +4426,7 @@ set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_ MonoJitInfo *ji; if (error) - mono_error_init (error); + error_init (error); code = mono_jit_find_compiled_method_with_jit_info (domain, method, &ji); if (!code) { @@ -4326,12 +4435,15 @@ set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_ /* Might be AOTed code */ mono_class_init (method->klass); code = mono_aot_get_method_checked (domain, method, &oerror); - g_assert (code); - mono_error_assert_ok (&oerror); - ji = mono_jit_info_table_find (domain, (char *)code); + if (code) { + mono_error_assert_ok (&oerror); + ji = mono_jit_info_table_find (domain, (char *)code); + } else { + /* Might be interpreted */ + ji = mono_interp_find_jit_info (domain, method); + } g_assert (ji); } - g_assert (code); insert_breakpoint (seq_points, domain, ji, bp, error); } @@ -4363,7 +4475,7 @@ set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req, MonoError int i; if (error) - mono_error_init (error); + error_init (error); // FIXME: // - suspend/resume the vm to prevent code patching problems @@ -4522,18 +4634,42 @@ static void ss_calculate_framecount (DebuggerTlsData *tls, MonoContext *ctx) compute_frame_info (tls->thread, tls); } +static gboolean +ensure_jit (StackFrame* frame) +{ + if (!frame->jit) { + frame->jit = mono_debug_find_method (frame->api_method, frame->domain); + if (!frame->jit && frame->api_method->is_inflated) + frame->jit = mono_debug_find_method(mono_method_get_declaring_generic_method (frame->api_method), frame->domain); + if (!frame->jit) { + char *s; + + /* This could happen for aot images with no jit debug info */ + s = mono_method_full_name (frame->api_method, TRUE); + DEBUG_PRINTF(1, "[dbg] No debug information found for '%s'.\n", s); + g_free (s); + return FALSE; + } + } + return TRUE; +} + /* * ss_update: * * Return FALSE if single stepping needs to continue. */ static gboolean -ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx) +ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx, MonoMethod* method) { MonoDebugMethodInfo *minfo; MonoDebugSourceLocation *loc = NULL; gboolean hit = TRUE; - MonoMethod *method; + + if (req->async_stepout_method == method) { + 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); + return FALSE; + } if (req->depth == STEP_DEPTH_OVER && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK)) { /* @@ -4543,7 +4679,7 @@ ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *t return FALSE; } - if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit) { + if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit && !req->async_stepout_method) { gboolean is_step_out = req->depth == STEP_DEPTH_OUT; ss_calculate_framecount (tls, ctx); @@ -4559,7 +4695,6 @@ ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *t } if (req->depth == STEP_DEPTH_INTO && req->size == STEP_SIZE_MIN && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) && ss_req->start_method){ - method = jinfo_get_method (ji); ss_calculate_framecount (tls, ctx); if (ss_req->start_method == method && req->nframes && tls->frame_count == req->nframes) {//Check also frame count(could be recursion) 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); @@ -4567,11 +4702,21 @@ ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *t } } + MonoDebugMethodAsyncInfo* async_method = mono_debug_lookup_method_async_debug_info (method); + if (async_method) { + for (int i = 0; i < async_method->num_awaits; i++) { + if (async_method->yield_offsets[i] == sp->il_offset || async_method->resume_offsets[i] == sp->il_offset) { + mono_debug_free_method_async_debug_info (async_method); + return FALSE; + } + } + mono_debug_free_method_async_debug_info (async_method); + } + if (req->size != STEP_SIZE_LINE) return TRUE; /* Have to check whenever a different source line was reached */ - method = jinfo_get_method (ji); minfo = mono_debug_lookup_method (method); if (minfo) @@ -4604,8 +4749,79 @@ breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly) return bp->method && bp->method->klass->image->assembly == assembly; } +static MonoObject* +get_this (StackFrame *frame) +{ + //Logic inspiered by "add_var" method and took out path that happens in async method for getting this + MonoDebugVarInfo *var = frame->jit->this_var; + if ((var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS) != MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET) + return NULL; + + guint8 * addr = (guint8 *)mono_arch_context_get_int_reg (&frame->ctx, var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS); + addr += (gint32)var->offset; + return *(MonoObject**)addr; +} + +//This ID is used to figure out if breakpoint hit on resumeOffset belongs to us or not +//since thread probably changed... +static int +get_this_async_id (StackFrame *frame) +{ + return get_objid (get_this (frame)); +} + +static MonoMethod* +get_set_notification_method (MonoClass* async_builder_class) +{ + MonoError error; + GPtrArray* array = mono_class_get_methods_by_name (async_builder_class, "SetNotificationForWaitCompletion", 0x24, FALSE, FALSE, &error); + mono_error_assert_ok (&error); + g_assert (array->len == 1); + MonoMethod* set_notification_method = (MonoMethod *)g_ptr_array_index (array, 0); + g_ptr_array_free (array, TRUE); + return set_notification_method; +} + static void -process_breakpoint_inner (DebuggerTlsData *tls, gboolean from_signal) +set_set_notification_for_wait_completion_flag (StackFrame *frame) +{ + MonoObject* obj = get_this (frame); + g_assert (obj); + MonoClassField *builder_field = mono_class_get_field_from_name (obj->vtable->klass, "<>t__builder"); + g_assert (builder_field); + MonoObject* builder; + MonoError error; + builder = mono_field_get_value_object_checked (frame->domain, builder_field, obj, &error); + mono_error_assert_ok (&error); + g_assert (builder); + + void* args [1]; + gboolean arg = TRUE; + args [0] = &arg; + mono_runtime_invoke_checked (get_set_notification_method (builder->vtable->klass), mono_object_unbox (builder), args, &error); + mono_error_assert_ok (&error); + mono_field_set_value (obj, builder_field, mono_object_unbox (builder)); +} + +static MonoMethod* notify_debugger_of_wait_completion_method_cache = NULL; + +static MonoMethod* +get_notify_debugger_of_wait_completion_method (void) +{ + if (notify_debugger_of_wait_completion_method_cache != NULL) + return notify_debugger_of_wait_completion_method_cache; + MonoError error; + MonoClass* task_class = mono_class_load_from_name (mono_defaults.corlib, "System.Threading.Tasks", "Task"); + GPtrArray* array = mono_class_get_methods_by_name (task_class, "NotifyDebuggerOfWaitCompletion", 0x24, FALSE, FALSE, &error); + mono_error_assert_ok (&error); + g_assert (array->len == 1); + notify_debugger_of_wait_completion_method_cache = (MonoMethod *)g_ptr_array_index (array, 0); + g_ptr_array_free (array, TRUE); + return notify_debugger_of_wait_completion_method_cache; +} + +static void +process_breakpoint (DebuggerTlsData *tls, gboolean from_signal) { MonoJitInfo *ji; guint8 *ip; @@ -4626,11 +4842,27 @@ process_breakpoint_inner (DebuggerTlsData *tls, gboolean from_signal) ip = (guint8 *)MONO_CONTEXT_GET_IP (ctx); ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL); + + if (!ji) { + /* Interpreter */ + // FIXME: Pass a flag instead to detect this + MonoLMF *lmf = mono_get_lmf (); + MonoInterpFrameHandle *frame; + + g_assert (((guint64)lmf->previous_lmf) & 2); + MonoLMFExt *ext = (MonoLMFExt*)lmf; + + g_assert (ext->interp_exit); + frame = ext->interp_exit_data; + ji = mono_interp_frame_get_jit_info (frame); + ip = mono_interp_frame_get_ip (frame); + } + g_assert (ji && !ji->is_trampoline); method = jinfo_get_method (ji); /* Compute the native offset of the breakpoint from the ip */ - native_offset = ip - (guint8*)ji->code_start; + native_offset = ip - (guint8*)ji->code_start; /* * Skip the instruction causing the breakpoint signal. @@ -4692,10 +4924,45 @@ process_breakpoint_inner (DebuggerTlsData *tls, gboolean from_signal) SingleStepReq *ss_req = (SingleStepReq *)req->info; gboolean hit; - if (mono_thread_internal_current () != ss_req->thread) - continue; + //if we hit async_stepout_method, it's our no matter which thread + if ((ss_req->async_stepout_method != method) && (ss_req->async_id || mono_thread_internal_current () != ss_req->thread)) { + //We have different thread and we don't have async stepping in progress + //it's breakpoint in parallel thread, ignore it + if (ss_req->async_id == 0) + continue; - hit = ss_update (ss_req, ji, &sp, tls, ctx); + tls->context.valid = FALSE; + tls->async_state.valid = FALSE; + invalidate_frames (tls); + ss_calculate_framecount(tls, ctx); + //make sure we have enough data to get current async method instance id + if (tls->frame_count == 0 || !ensure_jit (tls->frames [0])) + continue; + + //Check method is async before calling get_this_async_id + MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method); + if (!asyncMethod) + continue; + else + mono_debug_free_method_async_debug_info (asyncMethod); + + //breakpoint was hit in parallelly executing async method, ignore it + if (ss_req->async_id != get_this_async_id (tls->frames [0])) + continue; + } + + //Update stepping request to new thread/frame_count that we are continuing on + //so continuing with normal stepping works as expected + if (ss_req->async_stepout_method || ss_req->async_id) { + tls->context.valid = FALSE; + tls->async_state.valid = FALSE; + invalidate_frames (tls); + ss_calculate_framecount (tls, ctx); + ss_req->thread = mono_thread_internal_current (); + ss_req->nframes = tls->frame_count; + } + + hit = ss_update (ss_req, ji, &sp, tls, ctx, method); if (hit) g_ptr_array_add (ss_reqs, req); @@ -4750,9 +5017,9 @@ process_signal_event (void (*func) (DebuggerTlsData*, gboolean)) } static void -process_breakpoint (void) +process_breakpoint_from_signal (void) { - process_signal_event (process_breakpoint_inner); + process_signal_event (process_breakpoint); } static void @@ -4794,19 +5061,30 @@ mono_debugger_agent_breakpoint_hit (void *sigctx) * problems, like the original signal is disabled, libgc can't handle altstack, etc. * So set up the signal context to return to the real breakpoint handler function. */ - resume_from_signal_handler (sigctx, process_breakpoint); + resume_from_signal_handler (sigctx, process_breakpoint_from_signal); } +typedef struct { + gboolean found; + MonoContext *ctx; +} UserBreakCbData; + static gboolean -user_break_cb (StackFrameInfo *frame, MonoContext *ctx, gpointer data) +user_break_cb (StackFrameInfo *frame, MonoContext *ctx, gpointer user_data) { + UserBreakCbData *data = user_data; + + if (frame->type == FRAME_TYPE_INTERP_TO_MANAGED) { + data->found = TRUE; + return TRUE; + } if (frame->managed) { - *(MonoContext*)data = *ctx; + data->found = TRUE; + *data->ctx = *ctx; return TRUE; - } else { - return FALSE; } + return FALSE; } /* @@ -4819,11 +5097,15 @@ mono_debugger_agent_user_break (void) MonoContext ctx; int suspend_policy; GSList *events; + UserBreakCbData data; + + memset (&data, 0, sizeof (UserBreakCbData)); + data.ctx = &ctx; /* Obtain a context */ MONO_CONTEXT_SET_IP (&ctx, NULL); - mono_walk_stack_with_ctx (user_break_cb, NULL, (MonoUnwindOptions)0, &ctx); - g_assert (MONO_CONTEXT_GET_IP (&ctx) != NULL); + mono_walk_stack_with_ctx (user_break_cb, NULL, (MonoUnwindOptions)0, &data); + g_assert (data.found); mono_loader_lock (); events = create_event_list (EVENT_KIND_USER_BREAK, NULL, NULL, NULL, &suspend_policy); @@ -4865,8 +5147,6 @@ process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal) SeqPoint sp; MonoSeqPointInfo *info; - ip = (guint8 *)MONO_CONTEXT_GET_IP (ctx); - /* Skip the instruction causing the single step */ if (from_signal) mono_arch_skip_single_step (ctx); @@ -4886,14 +5166,15 @@ process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal) if (mono_thread_internal_current () != ss_req->thread) return; - if (log_level > 0) { - ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain); + ip = (guint8 *)MONO_CONTEXT_GET_IP (ctx); + ji = get_top_method_ji (ip, &domain, (gpointer*)&ip); + g_assert (ji && !ji->is_trampoline); + + if (log_level > 0) { 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); } - ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain); - g_assert (ji && !ji->is_trampoline); method = jinfo_get_method (ji); g_assert (method); @@ -4928,12 +5209,14 @@ process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal) * The ip points to the instruction causing the single step event, which is before * the offset recorded in the seq point map, so find the next seq point after ip. */ - if (!mono_find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info, &sp)) + if (!mono_find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info, &sp)) { + g_assert_not_reached (); return; + } il_offset = sp.il_offset; - if (!ss_update (ss_req, ji, &sp, tls, ctx)) + if (!ss_update (ss_req, ji, &sp, tls, ctx, method)) return; /* Start single stepping again from the current sequence point */ @@ -5044,7 +5327,7 @@ debugger_agent_breakpoint_from_context (MonoContext *ctx) mono_thread_state_init_from_monoctx (&tls->restore_state, ctx); memcpy (&tls->handler_ctx, ctx, sizeof (MonoContext)); - process_breakpoint_inner (tls, FALSE); + process_breakpoint (tls, FALSE); memcpy (ctx, &tls->restore_state.ctx, sizeof (MonoContext)); memcpy (&tls->restore_state, &orig_restore_state, sizeof (MonoThreadUnwindState)); @@ -5064,8 +5347,10 @@ start_single_stepping (void) #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED int val = InterlockedIncrement (&ss_count); - if (val == 1) + if (val == 1) { mono_arch_start_single_stepping (); + mono_interp_start_single_stepping (); + } #else g_assert_not_reached (); #endif @@ -5077,8 +5362,10 @@ stop_single_stepping (void) #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED int val = InterlockedDecrement (&ss_count); - if (val == 0) + if (val == 0) { mono_arch_stop_single_stepping (); + mono_interp_stop_single_stepping (); + } #else g_assert_not_reached (); #endif @@ -5102,6 +5389,8 @@ ss_stop (SingleStepReq *ss_req) ss_req->bps = NULL; } + ss_req->async_id = 0; + ss_req->async_stepout_method = NULL; if (ss_req->global) { stop_single_stepping (); ss_req->global = FALSE; @@ -5187,6 +5476,28 @@ ss_bp_add_one (SingleStepReq *ss_req, int *ss_req_bp_count, GHashTable **ss_req_ } } +static gboolean +is_last_non_empty (SeqPoint* sp, MonoSeqPointInfo *info) +{ + if (!sp->next_len) + return TRUE; + SeqPoint* next = g_new (SeqPoint, sp->next_len); + mono_seq_point_init_next (info, *sp, next); + for (int i = 0; i < sp->next_len; i++) { + if (next [i].flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) { + if (!is_last_non_empty (&next [i], info)) { + g_free (next); + return FALSE; + } + } else { + g_free (next); + return FALSE; + } + } + g_free (next); + return TRUE; +} + /* * ss_start: * @@ -5232,6 +5543,8 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointI nframes = tls->frame_count; } + MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method); + /* Need to stop in catch clauses as well */ for (i = ss_req->depth == STEP_DEPTH_OUT ? 1 : 0; i < nframes; ++i) { StackFrame *frame = frames [i]; @@ -5239,6 +5552,9 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointI if (frame->ji) { MonoJitInfo *jinfo = frame->ji; for (j = 0; j < jinfo->num_clauses; ++j) { + // In case of async method we don't want to place breakpoint on last catch handler(which state machine added for whole method) + if (asyncMethod && asyncMethod->num_awaits && i == 0 && j + 1 == jinfo->num_clauses) + break; MonoJitExceptionInfo *ei = &jinfo->clauses [j]; if (mono_find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL, &local_sp)) @@ -5247,10 +5563,46 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointI } } + if (asyncMethod && asyncMethod->num_awaits && nframes && ensure_jit (frames [0])) { + //asyncMethod has value and num_awaits > 0, this means we are inside async method with awaits + + // Check if we hit yield_offset during normal stepping, because if we did... + // Go into special async stepping mode which places breakpoint on resumeOffset + // of this await call and sets async_id so we can distinguish it from parallel executions + for (i = 0; i < asyncMethod->num_awaits; i++) { + if (sp->il_offset == asyncMethod->yield_offsets [i]) { + ss_req->async_id = get_this_async_id (frames [0]); + ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, asyncMethod->resume_offsets [i]); + if (ss_req_bp_cache) + g_hash_table_destroy (ss_req_bp_cache); + mono_debug_free_method_async_debug_info (asyncMethod); + return; + } + } + //If we are at end of async method and doing step-in or step-over... + //Switch to step-out, so whole NotifyDebuggerOfWaitCompletion magic happens... + if (is_last_non_empty (sp, info)) { + ss_req->depth = STEP_DEPTH_OUT;//setting depth to step-out is important, don't inline IF, because code later depends on this + } + if (ss_req->depth == STEP_DEPTH_OUT) { + set_set_notification_for_wait_completion_flag (frames [0]); + ss_req->async_id = get_this_async_id (frames [0]); + ss_req->async_stepout_method = get_notify_debugger_of_wait_completion_method (); + ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, ss_req->async_stepout_method, 0); + if (ss_req_bp_cache) + g_hash_table_destroy (ss_req_bp_cache); + mono_debug_free_method_async_debug_info (asyncMethod); + return; + } + } + + if (asyncMethod) + mono_debug_free_method_async_debug_info (asyncMethod); + /* - * Find the first sequence point in the current or in a previous frame which - * is not the last in its method. - */ + * Find the first sequence point in the current or in a previous frame which + * is not the last in its method. + */ if (ss_req->depth == STEP_DEPTH_OUT) { /* Ignore seq points in current method */ while (frame_index < nframes) { @@ -5589,7 +5941,7 @@ mono_debugger_agent_debug_log_is_enabled (void) return agent_config.enabled; } -#if defined(PLATFORM_ANDROID) || defined(TARGET_ANDROID) +#if defined(HOST_ANDROID) || defined(TARGET_ANDROID) void mono_debugger_agent_unhandled_exception (MonoException *exc) { @@ -6456,6 +6808,24 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai } } +static void +set_interp_var (MonoType *t, gpointer addr, guint8 *val_buf) +{ + int size; + + if (t->byref) { + addr = *(gpointer*)addr; + g_assert (addr); + } + + if (MONO_TYPE_IS_REFERENCE (t)) + size = sizeof (gpointer); + else + size = mono_class_value_size (mono_class_from_mono_type (t), NULL); + + memcpy (addr, val_buf, size); +} + static void clear_event_request (int req_id, int etype) { @@ -6740,11 +7110,17 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 args [i] = arg_buf [i]; } } else { - arg_buf [i] = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type (sig->params [i]))); + MonoClass *arg_class = mono_class_from_mono_type (sig->params [i]); + arg_buf [i] = (guint8 *)g_alloca (mono_class_instance_size (arg_class)); err = decode_value (sig->params [i], domain, arg_buf [i], p, &p, end); if (err != ERR_NONE) break; - args [i] = arg_buf [i]; + if (mono_class_is_nullable (arg_class)) { + args [i] = mono_nullable_box (arg_buf [i], arg_class, &error); + mono_error_assert_ok (&error); + } else { + args [i] = arg_buf [i]; + } } } @@ -6797,7 +7173,9 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 if ((invoke->flags & INVOKE_FLAG_RETURN_OUT_ARGS) && CHECK_PROTOCOL_VERSION (2, 35)) out_args = TRUE; buffer_add_byte (buf, 1 + (out_this ? 2 : 0) + (out_args ? 4 : 0)); - if (sig->ret->type == MONO_TYPE_VOID) { + if (m->string_ctor) { + buffer_add_value (buf, &mono_get_string_class ()->byval_arg, &res, domain); + } else if (sig->ret->type == MONO_TYPE_VOID && !m->string_ctor) { if (!strcmp (m->name, ".ctor")) { if (!m->klass->valuetype) buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &this_arg, domain); @@ -7603,7 +7981,7 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) break; case EVENT_KIND_ASSEMBLY_LOAD: /* Emit load events for currently loaded assemblies */ - mono_assembly_foreach (emit_assembly_load, NULL); + mono_domain_foreach (send_assemblies_for_domain, NULL); break; case EVENT_KIND_THREAD_START: /* Emit start events for currently started threads */ @@ -7717,13 +8095,19 @@ domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf) case CMD_APPDOMAIN_CREATE_STRING: { char *s; MonoString *o; + MonoError error; domain = decode_domainid (p, &p, end, NULL, &err); if (err != ERR_NONE) return err; s = decode_string (p, &p, end); - o = mono_string_new (domain, s); + o = mono_string_new_checked (domain, s, &error); + if (!is_ok (&error)) { + DEBUG_PRINTF (1, "[dbg] Failed to allocate String object '%s': %s\n", s, mono_error_get_message (&error)); + mono_error_cleanup (&error); + return ERR_INVALID_OBJECT; + } buffer_add_objid (buf, (MonoObject*)o); break; } @@ -7765,7 +8149,7 @@ get_assembly_object_command (MonoDomain *domain, MonoAssembly *ass, Buffer *buf, { HANDLE_FUNCTION_ENTER(); ErrorCode err = ERR_NONE; - mono_error_init (error); + error_init (error); MonoReflectionAssemblyHandle o = mono_assembly_get_object_handle (domain, ass, error); if (MONO_HANDLE_IS_NULL (o)) { err = ERR_INVALID_OBJECT; @@ -7823,6 +8207,10 @@ assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf) mono_error_cleanup (&error); return err; } + case CMD_ASSEMBLY_GET_DOMAIN: { + buffer_add_domainid (buf, domain); + break; + } case CMD_ASSEMBLY_GET_TYPE: { MonoError error; char *s = decode_string (p, &p, end); @@ -8412,7 +8800,7 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint MonoError error; GPtrArray *array; - mono_error_init (&error); + error_init (&error); array = mono_class_get_methods_by_name (klass, name, flags & ~BINDING_FLAGS_IGNORE_CASE, (flags & BINDING_FLAGS_IGNORE_CASE) != 0, TRUE, &error); if (!is_ok (&error)) { mono_error_cleanup (&error); @@ -9115,7 +9503,13 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) // FIXME: Check that the ip change is safe DEBUG_PRINTF (1, "[dbg] Setting IP to %s:0x%0x(0x%0x)\n", tls->frames [0]->actual_method->name, (int)sp.il_offset, (int)sp.native_offset); - MONO_CONTEXT_SET_IP (&tls->restore_state.ctx, (guint8*)tls->frames [0]->ji->code_start + sp.native_offset); + + if (tls->frames [0]->ji->is_interp) { + MonoJitTlsData *jit_data = ((MonoThreadInfo*)thread->thread_info)->jit_data; + mono_interp_set_resume_state (jit_data, NULL, tls->frames [0]->interp_frame, (guint8*)tls->frames [0]->ji->code_start + sp.native_offset); + } else { + MONO_CONTEXT_SET_IP (&tls->restore_state.ctx, (guint8*)tls->frames [0]->ji->code_start + sp.native_offset); + } break; } default: @@ -9174,25 +9568,14 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (!frame->has_ctx) return ERR_ABSENT_INFORMATION; - if (!frame->jit) { - frame->jit = mono_debug_find_method (frame->api_method, frame->domain); - if (!frame->jit && frame->api_method->is_inflated) - frame->jit = mono_debug_find_method (mono_method_get_declaring_generic_method (frame->api_method), frame->domain); - if (!frame->jit) { - char *s; + if (!ensure_jit (frame)) + return ERR_ABSENT_INFORMATION; - /* This could happen for aot images with no jit debug info */ - s = mono_method_full_name (frame->api_method, TRUE); - DEBUG_PRINTF (1, "[dbg] No debug information found for '%s'.\n", s); - g_free (s); - return ERR_ABSENT_INFORMATION; - } - } jit = frame->jit; sig = mono_method_signature (frame->actual_method); - if (!jit->has_var_info || !mono_get_seq_points (frame->domain, frame->actual_method)) + if (!(jit->has_var_info || frame->ji->is_interp) || !mono_get_seq_points (frame->domain, frame->actual_method)) /* * The method is probably from an aot image compiled without soft-debug, variables might be dead, etc. */ @@ -9213,9 +9596,17 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) DEBUG_PRINTF (4, "[dbg] send arg %d.\n", pos); - g_assert (pos >= 0 && pos < jit->num_params); + if (frame->ji->is_interp) { + guint8 *addr; + + addr = mono_interp_frame_get_arg (frame->interp_frame, pos); + + buffer_add_value_full (buf, sig->params [pos], addr, frame->domain, FALSE, NULL); + } else { + g_assert (pos >= 0 && pos < jit->num_params); - add_var (buf, jit, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain, FALSE); + add_var (buf, jit, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain, FALSE); + } } else { MonoDebugLocalsInfo *locals; @@ -9225,30 +9616,57 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) pos = locals->locals [pos].index; mono_debug_free_locals (locals); } - g_assert (pos >= 0 && pos < jit->num_locals); DEBUG_PRINTF (4, "[dbg] send local %d.\n", pos); - add_var (buf, jit, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE); + if (frame->ji->is_interp) { + guint8 *addr; + + addr = mono_interp_frame_get_local (frame->interp_frame, pos); + + buffer_add_value_full (buf, header->locals [pos], addr, frame->domain, FALSE, NULL); + } else { + g_assert (pos >= 0 && pos < jit->num_locals); + + add_var (buf, jit, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE); + } } } mono_metadata_free_mh (header); break; } case CMD_STACK_FRAME_GET_THIS: { + if (frame->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) + return ERR_ABSENT_INFORMATION; if (frame->api_method->klass->valuetype) { if (!sig->hasthis) { MonoObject *p = NULL; buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &p, frame->domain); } else { - add_var (buf, jit, &frame->actual_method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE); + if (frame->ji->is_interp) { + guint8 *addr; + + addr = mono_interp_frame_get_this (frame->interp_frame); + + buffer_add_value_full (buf, &frame->actual_method->klass->this_arg, addr, frame->domain, FALSE, NULL); + } else { + add_var (buf, jit, &frame->actual_method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE); + } } } else { if (!sig->hasthis) { MonoObject *p = NULL; buffer_add_value (buf, &frame->actual_method->klass->byval_arg, &p, frame->domain); } else { - add_var (buf, jit, &frame->api_method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE); + if (frame->ji->is_interp) { + guint8 *addr; + + addr = mono_interp_frame_get_this (frame->interp_frame); + + buffer_add_value_full (buf, &frame->api_method->klass->byval_arg, addr, frame->domain, FALSE, NULL); + } else { + add_var (buf, jit, &frame->api_method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE); + } } } break; @@ -9257,7 +9675,8 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) MonoError error; guint8 *val_buf; MonoType *t; - MonoDebugVarInfo *var; + MonoDebugVarInfo *var = NULL; + gboolean is_arg = FALSE; len = decode_int (p, &p, end); header = mono_method_get_header_checked (frame->actual_method, &error); @@ -9273,6 +9692,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) t = sig->params [pos]; var = &jit->params [pos]; + is_arg = TRUE; } else { MonoDebugLocalsInfo *locals; @@ -9296,7 +9716,17 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (err != ERR_NONE) return err; - set_var (t, var, &frame->ctx, frame->domain, val_buf, frame->reg_locations, &tls->restore_state.ctx); + if (frame->ji->is_interp) { + guint8 *addr; + + if (is_arg) + addr = mono_interp_frame_get_arg (frame->interp_frame, pos); + else + addr = mono_interp_frame_get_local (frame->interp_frame, pos); + set_interp_var (t, addr, val_buf); + } else { + set_var (t, var, &frame->ctx, frame->domain, val_buf, frame->reg_locations, &tls->restore_state.ctx); + } } mono_metadata_free_mh (header); break; @@ -9314,15 +9744,23 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) t = &frame->actual_method->klass->byval_arg; /* Checked by the sender */ g_assert (MONO_TYPE_ISSTRUCT (t)); - var = jit->this_var; - g_assert (var); val_buf = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type (t))); err = decode_value (t, frame->domain, val_buf, p, &p, end); if (err != ERR_NONE) return err; - set_var (&frame->actual_method->klass->this_arg, var, &frame->ctx, frame->domain, val_buf, frame->reg_locations, &tls->restore_state.ctx); + if (frame->ji->is_interp) { + guint8 *addr; + + addr = mono_interp_frame_get_this (frame->interp_frame); + set_interp_var (&frame->actual_method->klass->this_arg, addr, val_buf); + } else { + var = jit->this_var; + g_assert (var); + + set_var (&frame->actual_method->klass->this_arg, var, &frame->ctx, frame->domain, val_buf, frame->reg_locations, &tls->restore_state.ctx); + } break; } default: @@ -9694,7 +10132,8 @@ static const char* assembly_cmds_str[] = { "GET_MANIFEST_MODULE", "GET_OBJECT", "GET_TYPE", - "GET_NAME" + "GET_NAME", + "GET_DOMAIN" }; static const char* module_cmds_str[] = { @@ -9895,12 +10334,14 @@ debugger_thread (void *arg) debugger_thread_id = mono_native_thread_id_get (); - MonoThread *thread = mono_thread_attach (mono_get_root_domain ()); - mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Debugger agent"), TRUE, &error); + MonoInternalThread *internal = mono_thread_internal_current (); + MonoString *str = mono_string_new_checked (mono_domain_get (), "Debugger agent", &error); + mono_error_assert_ok (&error); + mono_thread_set_name_internal (internal, str, TRUE, FALSE, &error); mono_error_assert_ok (&error); - thread->internal_thread->state |= ThreadState_Background; - thread->internal_thread->flags |= MONO_THREAD_FLAG_DONT_MANAGE; + internal->state |= ThreadState_Background; + internal->flags |= MONO_THREAD_FLAG_DONT_MANAGE; if (agent_config.defer) { if (!wait_for_attach ()) {