X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fdebugger-agent.c;h=e18cb98d643bd015f474be8bec56aa36e03706b6;hb=30d60224a85b2e36406bf58e9ab7bfe7ffa6509f;hp=ec40de9a8bc83cb65a81e1d54d372fc64adda781;hpb=8784e2e1cbbd88b4872536d3a8077702dc547216;p=mono.git diff --git a/mono/mini/debugger-agent.c b/mono/mini/debugger-agent.c index ec40de9a8bc..e18cb98d643 100644 --- a/mono/mini/debugger-agent.c +++ b/mono/mini/debugger-agent.c @@ -53,17 +53,16 @@ #endif #include -#include -#include +#include #include #include #include -#include -#include +#include #include #include #include #include +#include #include #include #include @@ -75,6 +74,7 @@ #include "debugger-agent.h" #include "mini.h" #include "seq-points.h" +#include /* * On iOS we can't use System.Environment.Exit () as it will do the wrong @@ -272,7 +272,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, @@ -449,7 +449,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 { @@ -568,6 +569,12 @@ typedef struct { GSList *bps; /* The number of frames at the start of a step-over */ 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; /* @@ -811,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 @@ -822,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); @@ -989,14 +1003,11 @@ mono_debugger_agent_init (void) /* 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"); - MONO_GC_REGISTER_ROOT_FIXED (thread_to_tls, 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"); - MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread table"); tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread object table"); - MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread_obj, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread object table"); pending_assembly_loads = g_ptr_array_new (); domains = g_hash_table_new (mono_aligned_addr_hash, NULL); @@ -1629,7 +1640,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,8 +1950,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"); - MONO_GC_REGISTER_ROOT_FIXED (suspended_objs, 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 @@ -2309,7 +2325,7 @@ decode_ptr_id (guint8 *buf, guint8 **endbuf, guint8 *limit, IdType type, MonoDom res = (Id *)g_ptr_array_index (ids [type], GPOINTER_TO_INT (id - 1)); dbg_unlock (); - if (res->domain == NULL) { + if (res->domain == NULL || res->domain->state == MONO_APPDOMAIN_UNLOADED) { DEBUG_PRINTF (1, "ERR_UNLOADED, id=%d, type=%d.\n", id, type); *err = ERR_UNLOADED; return NULL; @@ -2678,7 +2694,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); @@ -2773,7 +2789,7 @@ suspend_vm (void) /* * Suspend creation of new threadpool threads, since they cannot run */ - mono_threadpool_ms_suspend (); + mono_threadpool_suspend (); mono_loader_unlock (); } @@ -2811,7 +2827,7 @@ resume_vm (void) //g_assert (err == 0); if (suspend_count == 0) - mono_threadpool_ms_resume (); + mono_threadpool_resume (); mono_loader_unlock (); } @@ -2950,7 +2966,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; } @@ -3244,8 +3260,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); } /* @@ -3806,7 +3822,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))); @@ -3885,8 +3901,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. @@ -4012,14 +4027,41 @@ 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 +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 @@ -4118,7 +4160,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)) { @@ -4318,7 +4360,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) { @@ -4364,7 +4406,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 @@ -4523,18 +4565,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)) { /* @@ -4544,7 +4610,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); @@ -4560,7 +4626,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); @@ -4568,11 +4633,22 @@ ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *t } } + MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method); + if (asyncMethod) { + for (int i = 0; i < asyncMethod->num_awaits; i++) + { + if (asyncMethod->yield_offsets[i] == sp->il_offset || asyncMethod->resume_offsets[i] == sp->il_offset) { + mono_debug_free_method_async_debug_info (asyncMethod); + return FALSE; + } + } + mono_debug_free_method_async_debug_info (asyncMethod); + } + 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) @@ -4605,6 +4681,82 @@ 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* set_notification_method_cache = NULL; + +static MonoMethod* +get_set_notification_method () +{ + if(set_notification_method_cache != NULL) + return set_notification_method_cache; + MonoError error; + MonoClass* async_builder_class = mono_class_load_from_name (mono_defaults.corlib, "System.Runtime.CompilerServices", "AsyncTaskMethodBuilder"); + 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); + set_notification_method_cache = (MonoMethod *)g_ptr_array_index (array, 0); + g_ptr_array_free (array, TRUE); + return set_notification_method_cache; +} + +static void +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(), 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 () +{ + 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_inner (DebuggerTlsData *tls, gboolean from_signal) { @@ -4693,10 +4845,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; + + 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; - hit = ss_update (ss_req, ji, &sp, tls, ctx); + //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); @@ -4909,6 +5096,22 @@ process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal) if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy"))) return; + /* + * This could be in ss_update method, but mono_find_next_seq_point_for_native_offset is pretty expensive method, + * hence we prefer this check here. + */ + if (ss_req->user_assemblies) { + gboolean found = FALSE; + for (int k = 0; ss_req->user_assemblies[k]; k++) + if (ss_req->user_assemblies[k] == method->klass->image->assembly) { + found = TRUE; + break; + } + if (!found) + return; + } + + /* * 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. @@ -4918,7 +5121,7 @@ process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal) 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 */ @@ -5087,6 +5290,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; @@ -5172,6 +5377,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: * @@ -5217,10 +5444,66 @@ 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]; + + 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)) + ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, frame->method, local_sp.il_offset); + } + } + } + + 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) { @@ -5303,26 +5586,6 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint* sp, MonoSeqPointI ss_req->depth = STEP_DEPTH_INTO; } - if (ss_req->depth == STEP_DEPTH_OVER) { - /* Need to stop in catch clauses as well */ - for (i = 0; i < nframes; ++i) { - StackFrame *frame = frames [i]; - - if (frame->ji) { - MonoJitInfo *jinfo = frame->ji; - for (j = 0; j < jinfo->num_clauses; ++j) { - MonoJitExceptionInfo *ei = &jinfo->clauses [j]; - - found_sp = mono_find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL, &local_sp); - sp = (found_sp)? &local_sp : NULL; - - if (found_sp) - ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, frame->method, sp->il_offset); - } - } - } - } - if (ss_req->depth == STEP_DEPTH_INTO) { /* Enable global stepping so we stop at method entry too */ enable_global = TRUE; @@ -5391,11 +5654,21 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, StepFilte ss_req->filter = filter; req->info = ss_req; + for (int i = 0; i < req->nmodifiers; i++) { + if (req->modifiers[i].kind == MOD_KIND_ASSEMBLY_ONLY) { + ss_req->user_assemblies = req->modifiers[i].data.assemblies; + break; + } + } + mono_loader_lock (); tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread); mono_loader_unlock (); g_assert (tls); - g_assert (tls->context.valid); + if (!tls->context.valid) { + DEBUG_PRINTF (1, "Received a single step request on a thread with no managed frames."); + return ERR_INVALID_ARGUMENT; + } if (tls->restore_state.valid && MONO_CONTEXT_GET_IP (&tls->context.ctx) != MONO_CONTEXT_GET_IP (&tls->restore_state.ctx)) { /* @@ -6480,7 +6753,8 @@ clear_assembly_from_modifier (EventRequest *req, Modifier *m, MonoAssembly *asse } if (match_count) { - newassemblies = g_new0 (MonoAssembly*, count - match_count); + // +1 because we don't know length and we use last element to check for end + newassemblies = g_new0 (MonoAssembly*, count - match_count + 1); pos = 0; for (i = 0; i < count; ++i) @@ -7490,7 +7764,8 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) int n = decode_int (p, &p, end); int j; - req->modifiers [i].data.assemblies = g_new0 (MonoAssembly*, n); + // +1 because we don't know length and we use last element to check for end + req->modifiers [i].data.assemblies = g_new0 (MonoAssembly*, n + 1); for (j = 0; j < n; ++j) { req->modifiers [i].data.assemblies [j] = decode_assemblyid (p, &p, end, &domain, &err); if (err != ERR_NONE) { @@ -7581,7 +7856,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 */ @@ -7738,6 +8013,23 @@ domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return ERR_NONE; } +static ErrorCode +get_assembly_object_command (MonoDomain *domain, MonoAssembly *ass, Buffer *buf, MonoError *error) +{ + HANDLE_FUNCTION_ENTER(); + ErrorCode err = ERR_NONE; + error_init (error); + MonoReflectionAssemblyHandle o = mono_assembly_get_object_handle (domain, ass, error); + if (MONO_HANDLE_IS_NULL (o)) { + err = ERR_INVALID_OBJECT; + goto leave; + } + buffer_add_objid (buf, MONO_HANDLE_RAW (MONO_HANDLE_CAST (MonoObject, o))); +leave: + HANDLE_FUNCTION_RETURN_VAL (err); +} + + static ErrorCode assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf) { @@ -7780,12 +8072,12 @@ assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf) } case CMD_ASSEMBLY_GET_OBJECT: { MonoError error; - MonoObject *o = (MonoObject*)mono_assembly_get_object_checked (domain, ass, &error); - if (!o) { - mono_error_cleanup (&error); /* FIXME don't swallow the error */ - return ERR_INVALID_OBJECT; - } - buffer_add_objid (buf, o); + err = get_assembly_object_command (domain, ass, buf, &error); + mono_error_cleanup (&error); + return err; + } + case CMD_ASSEMBLY_GET_DOMAIN: { + buffer_add_domainid (buf, domain); break; } case CMD_ASSEMBLY_GET_TYPE: { @@ -8377,7 +8669,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); @@ -9139,20 +9431,9 @@ 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); @@ -9659,7 +9940,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[] = { @@ -9860,12 +10142,12 @@ 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 (); + mono_thread_set_name_internal (internal, mono_string_new (mono_domain_get (), "Debugger agent"), 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 ()) {