X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;ds=sidebyside;f=mono%2Fmini%2Fdebugger-agent.c;h=b9d5cd4491365a1ae2aa69eaccc58f9840ecae4c;hb=7c89014866fd7d162336970a78a89c9f0ffd7a4d;hp=b207335b23e2d84f916ef5785bddffb72daf3928;hpb=083ff1ec0dee175d3af5b4badf017f207d614cff;p=mono.git diff --git a/mono/mini/debugger-agent.c b/mono/mini/debugger-agent.c index b207335b23e..ebf77355785 100644 --- a/mono/mini/debugger-agent.c +++ b/mono/mini/debugger-agent.c @@ -72,13 +72,26 @@ int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD, #include #include #include +#include +#include +#include #include #include #include #include +#include #include "debugger-agent.h" #include "mini.h" +/* +On iOS we can't use System.Environment.Exit () as it will do the wrong +shutdown sequence. +*/ +#if !defined (TARGET_IOS) +#define TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT +#endif + + #ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED #define DISABLE_DEBUGGER_AGENT 1 #endif @@ -87,13 +100,9 @@ int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD, #define DISABLE_DEBUGGER_AGENT 1 #endif -#if defined(__MACH__) -#include -#endif - #ifndef DISABLE_DEBUGGER_AGENT -#include +#include /* Definitions to make backporting to 2.6 easier */ //#define MonoInternalThread MonoThread @@ -162,6 +171,7 @@ struct _InvokeData MonoMethod *method; gpointer *args; guint32 suspend_count; + int nmethods; InvokeData *last_invoke; }; @@ -169,7 +179,6 @@ struct _InvokeData typedef struct { MonoThreadUnwindState context; - gpointer resume_event; /* This is computed on demand when it is requested using the wire protocol */ /* It is freed up when the thread is resumed */ int frame_count; @@ -248,9 +257,7 @@ typedef struct { * The context where single stepping should resume while the thread is suspended because * of an EXCEPTION event. */ - MonoContext catch_ctx; - - gboolean has_catch_ctx; + MonoThreadUnwindState catch_state; /* * The context which needs to be restored after handling a single step/breakpoint @@ -276,7 +283,7 @@ typedef struct { #define HEADER_LENGTH 11 #define MAJOR_VERSION 2 -#define MINOR_VERSION 18 +#define MINOR_VERSION 24 typedef enum { CMD_SET_VM = 1, @@ -359,7 +366,8 @@ typedef enum { typedef enum { STEP_FILTER_NONE = 0, - STEP_FILTER_STATIC_CTOR = 1 + STEP_FILTER_STATIC_CTOR = 1, + STEP_FILTER_DEBUGGER_HIDDEN = 2 } StepFilter; typedef enum { @@ -401,7 +409,8 @@ typedef enum { CMD_VM_ABORT_INVOKE = 9, CMD_VM_SET_KEEPALIVE = 10, CMD_VM_GET_TYPES_FOR_SOURCE_FILE = 11, - CMD_VM_GET_TYPES = 12 + CMD_VM_GET_TYPES = 12, + CMD_VM_INVOKE_METHODS = 13 } CmdVM; typedef enum { @@ -455,6 +464,8 @@ typedef enum { CMD_METHOD_GET_INFO = 6, CMD_METHOD_GET_BODY = 7, CMD_METHOD_RESOLVE_TOKEN = 8, + CMD_METHOD_GET_CATTRS = 9, + CMD_METHOD_MAKE_GENERIC_METHOD = 10 } CmdMethod; typedef enum { @@ -475,6 +486,7 @@ typedef enum { CMD_TYPE_GET_METHODS_BY_NAME_FLAGS = 15, CMD_TYPE_GET_INTERFACES = 16, CMD_TYPE_GET_INTERFACE_MAP = 17, + CMD_TYPE_IS_INITIALIZED = 18 } CmdType; typedef enum { @@ -667,6 +679,9 @@ static gboolean protocol_version_set; /* A hash table containing all active domains */ static GHashTable *domains; +/* The number of times the runtime is suspended */ +static gint32 suspend_count; + static void transport_init (void); static void transport_connect (const char *address); static gboolean transport_handshake (void); @@ -904,7 +919,7 @@ mono_debugger_agent_init (void) event_requests = g_ptr_array_new (); - mono_mutex_init (&debugger_thread_exited_mutex, NULL); + mono_mutex_init (&debugger_thread_exited_mutex); mono_cond_init (&debugger_thread_exited_cond, NULL); mono_profiler_install ((MonoProfiler*)&debugger_profiler, runtime_shutdown); @@ -971,11 +986,6 @@ mono_debugger_agent_init (void) if (!agent_config.onuncaught && !agent_config.onthrow) finish_agent_init (TRUE); - - /* FIXME: Is this still needed ? */ -#if defined(__MACH__) - mono_thread_info_disable_new_interrupt (TRUE); -#endif } /* @@ -1054,15 +1064,30 @@ socket_transport_recv (void *buf, int len) int total = 0; int fd = conn_fd; int flags = 0; + static gint32 last_keepalive; + gint32 msecs; do { again: res = recv (fd, (char *) buf + total, len - total, flags); if (res > 0) total += res; - if (agent_config.keepalive && res == -1 && get_last_sock_error () == MONO_EWOULDBLOCK) { - process_profiler_event (EVENT_KIND_KEEPALIVE, NULL); - goto again; + if (agent_config.keepalive) { + gboolean need_keepalive = FALSE; + if (res == -1 && get_last_sock_error () == MONO_EWOULDBLOCK) { + need_keepalive = TRUE; + } else if (res == -1) { + /* This could happen if recv () is interrupted repeatedly */ + msecs = mono_msec_ticks (); + if (msecs - last_keepalive >= agent_config.keepalive) { + need_keepalive = TRUE; + last_keepalive = msecs; + } + } + if (need_keepalive) { + process_profiler_event (EVENT_KIND_KEEPALIVE, NULL); + goto again; + } } } while ((res > 0 && total < len) || (res == -1 && get_last_sock_error () == MONO_EINTR)); return total; @@ -1078,7 +1103,7 @@ set_keepalive (void) struct timeval tv; int result; - if (!agent_config.keepalive) + if (!agent_config.keepalive || !conn_fd) return; tv.tv_sec = agent_config.keepalive / 1000; @@ -1306,8 +1331,7 @@ socket_transport_connect (const char *address) #endif } - disconnected = !transport_handshake (); - if (disconnected) + if (!transport_handshake ()) exit (1); } @@ -1367,6 +1391,15 @@ static DebuggerTransport *transport; static DebuggerTransport transports [MAX_TRANSPORTS]; static int ntransports; +void +mono_debugger_agent_register_transport (DebuggerTransport *trans); + +void +mono_debugger_agent_register_transport (DebuggerTransport *trans) +{ + register_transport (trans); +} + static void register_transport (DebuggerTransport *trans) { @@ -1429,6 +1462,12 @@ transport_recv (void *buf, int len) return transport->recv (buf, len); } +gboolean +mono_debugger_agent_transport_handshake (void) +{ + return transport_handshake (); +} + static gboolean transport_handshake (void) { @@ -1436,6 +1475,8 @@ transport_handshake (void) guint8 buf [128]; int res; + disconnected = TRUE; + /* Write handshake message */ sprintf (handshake_msg, "DWP-Handshake"); do { @@ -1464,7 +1505,7 @@ transport_handshake (void) * Set TCP_NODELAY on the socket so the client receives events/command * results immediately. */ - { + if (conn_fd) { int flag = 1; int result = setsockopt (conn_fd, IPPROTO_TCP, @@ -1477,6 +1518,7 @@ transport_handshake (void) set_keepalive (); #endif + disconnected = FALSE; return TRUE; } @@ -1777,6 +1819,7 @@ objrefs_cleanup (void) } static GHashTable *obj_to_objref; +static MonoGHashTable *suspended_objs; /* * Return an ObjRef for OBJ. @@ -1793,8 +1836,18 @@ get_objref (MonoObject *obj) mono_loader_lock (); - if (!obj_to_objref) + if (!obj_to_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_GC_REGISTER_ROOT_FIXED (suspended_objs); + } + + if (suspend_count) { + /* + * Have to keep object refs created during suspensions alive for the duration of the suspension, so GCs during invokes don't collect them. + */ + mono_g_hash_table_insert (suspended_objs, obj, NULL); + } /* FIXME: The tables can grow indefinitely */ @@ -1841,6 +1894,20 @@ get_objref (MonoObject *obj) return ref; } +static gboolean +true_pred (gpointer key, gpointer value, gpointer user_data) +{ + return TRUE; +} + +static void +clear_suspended_objs (void) +{ + mono_loader_lock (); + mono_g_hash_table_foreach_remove (suspended_objs, true_pred, NULL); + mono_loader_unlock (); +} + static inline int get_objid (MonoObject *obj) { @@ -2140,7 +2207,17 @@ buffer_add_ptr_id (Buffer *buf, MonoDomain *domain, IdType type, gpointer val) static inline MonoClass* decode_typeid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err) { - return decode_ptr_id (buf, endbuf, limit, ID_TYPE, domain, err); + MonoClass *klass; + + klass = decode_ptr_id (buf, endbuf, limit, ID_TYPE, domain, err); + if (G_UNLIKELY (log_level >= 2) && klass) { + char *s; + + s = mono_type_full_name (&klass->byval_arg); + DEBUG(2, fprintf (log_file, "[dbg] recv class [%s]\n", s)); + g_free (s); + } + return klass; } static inline MonoAssembly* @@ -2183,12 +2260,29 @@ static inline void buffer_add_typeid (Buffer *buf, MonoDomain *domain, MonoClass *klass) { buffer_add_ptr_id (buf, domain, ID_TYPE, klass); + if (G_UNLIKELY (log_level >= 2) && klass) { + char *s; + + s = mono_type_full_name (&klass->byval_arg); + if (GetCurrentThreadId () == debugger_thread_id) + DEBUG(2, fprintf (log_file, "[dbg] send class [%s]\n", s)); + else + DEBUG(2, fprintf (log_file, "[%p] send class [%s]\n", (gpointer)GetCurrentThreadId (), s)); + g_free (s); + } } static inline void buffer_add_methodid (Buffer *buf, MonoDomain *domain, MonoMethod *method) { buffer_add_ptr_id (buf, domain, ID_METHOD, method); + if (G_UNLIKELY (log_level >= 2) && method) { + char *s; + + s = mono_method_full_name (method, 1); + DEBUG(2, fprintf (log_file, "[dbg] send method [%s]\n", s)); + g_free (s); + } } static inline void @@ -2247,9 +2341,6 @@ save_thread_context (MonoContext *ctx) mono_thread_state_init_from_current (&tls->context); } -/* The number of times the runtime is suspended */ -static gint32 suspend_count; - /* Number of threads suspended */ /* * If this is equal to the size of thread_to_tls, the runtime is considered @@ -2268,7 +2359,7 @@ static MonoSemType suspend_sem; static void suspend_init (void) { - mono_mutex_init (&suspend_mutex, NULL); + mono_mutex_init (&suspend_mutex); mono_cond_init (&suspend_cond, NULL); MONO_SEM_INIT (&suspend_sem, 0); } @@ -2441,8 +2532,10 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji) return FALSE; tls = mono_native_tls_get_value (debugger_tls_id); - if (!tls) - return FALSE; + if (!tls) { + DEBUG (1, fprintf (log_file, "[%p] Received interrupt with no TLS, continuing.\n", (gpointer)GetCurrentThreadId ())); + return FALSE; + } return thread_interrupt (tls, NULL, sigctx, ji); } @@ -2522,20 +2615,28 @@ notify_thread (gpointer key, gpointer value, gpointer user_data) MonoJitInfo *ji; info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gpointer)(gsize)thread->tid, FALSE); - g_assert (info); - - ji = mono_jit_info_table_find (info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx)); + if (!info) { + DEBUG(1, fprintf (log_file, "[%p] mono_thread_info_suspend_sync () failed for %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid)); + /* + * Attached thread which died without detaching. + */ + tls->terminated = TRUE; + } else { + ji = mono_jit_info_table_find (info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx)); - thread_interrupt (tls, info, NULL, ji); + thread_interrupt (tls, info, NULL, ji); - mono_thread_info_resume (mono_thread_info_get_tid (info)); + mono_thread_info_resume (mono_thread_info_get_tid (info)); + } } else { res = mono_thread_kill (thread, mono_thread_get_abort_signal ()); - if (res) + if (res) { + DEBUG(1, fprintf (log_file, "[%p] mono_thread_kill () failed for %p: %d...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid, res)); /* * Attached thread which died without detaching. */ tls->terminated = TRUE; + } } #endif } @@ -2610,6 +2711,12 @@ suspend_vm (void) mono_mutex_unlock (&suspend_mutex); + if (suspend_count == 1) + /* + * Suspend creation of new threadpool threads, since they cannot run + */ + mono_thread_pool_suspend (); + mono_loader_unlock (); } @@ -2648,6 +2755,9 @@ resume_vm (void) mono_mutex_unlock (&suspend_mutex); //g_assert (err == 0); + if (suspend_count == 0) + mono_thread_pool_resume (); + mono_loader_unlock (); } @@ -2868,16 +2978,13 @@ get_seq_points (MonoDomain *domain, MonoMethod *method) return seq_points; } -static MonoSeqPointInfo* -find_seq_points (MonoDomain *domain, MonoMethod *method) +static void +no_seq_points_found (MonoMethod *method) { - MonoSeqPointInfo *seq_points = get_seq_points (domain, method); - - if (!seq_points) - printf ("Unable to find seq points for method '%s'.\n", mono_method_full_name (method, TRUE)); - g_assert (seq_points); - - return seq_points; + /* + * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space. + */ + printf ("Unable to find seq points for method '%s'.\n", mono_method_full_name (method, TRUE)); } /* @@ -2891,7 +2998,13 @@ find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, g MonoSeqPointInfo *seq_points; int i; - seq_points = find_seq_points (domain, method); + seq_points = get_seq_points (domain, method); + if (!seq_points) { + if (info) + *info = NULL; + return NULL; + } + g_assert (seq_points); if (info) *info = seq_points; @@ -2914,9 +3027,11 @@ find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, g MonoSeqPointInfo *seq_points; int i; - seq_points = find_seq_points (domain, method); + seq_points = get_seq_points (domain, method); if (info) *info = seq_points; + if (!seq_points) + return NULL; for (i = seq_points->len - 1; i >= 0; --i) { if (seq_points->seq_points [i].native_offset <= native_offset) @@ -3269,7 +3384,7 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method); if (minfo) { - mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, NULL, NULL, NULL, NULL); + mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, NULL, NULL, NULL, NULL, NULL); for (i = 0; i < source_file_list->len; ++i) { sinfo = g_ptr_array_index (source_file_list, i); /* @@ -3279,6 +3394,15 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo s = strdup_tolower (sinfo->source_file); if (g_hash_table_lookup (mod->data.source_files, s)) found = TRUE; + else { + char *s2 = g_path_get_basename (sinfo->source_file); + char *s3 = strdup_tolower (s2); + + if (g_hash_table_lookup (mod->data.source_files, s3)) + found = TRUE; + g_free (s2); + g_free (s3); + } g_free (s); } g_ptr_array_free (source_file_list, TRUE); @@ -3298,6 +3422,26 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo (ji->method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && !strcmp (ji->method->name, ".cctor")) filtered = TRUE; + if ((mod->data.filter & STEP_FILTER_DEBUGGER_HIDDEN) && ji) { + MonoCustomAttrInfo *ainfo; + static MonoClass *klass; + + if (!klass) { + klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggerHiddenAttribute"); + g_assert (klass); + } + if (!ji->dbg_hidden_inited) { + ainfo = mono_custom_attrs_from_method (ji->method); + if (ainfo) { + if (mono_custom_attrs_has_attr (ainfo, klass)) + ji->dbg_hidden = TRUE; + mono_custom_attrs_free (ainfo); + } + ji->dbg_hidden_inited = TRUE; + } + if (ji->dbg_hidden) + filtered = TRUE; + } } } @@ -3358,6 +3502,7 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx GSList *l; MonoDomain *domain = mono_domain_get (); MonoThread *thread = NULL; + MonoObject *keepalive_obj = NULL; gboolean send_success = FALSE; static int ecount; int nevents; @@ -3460,6 +3605,11 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx case EVENT_KIND_EXCEPTION: { EventInfo *ei = arg; buffer_add_objid (&buf, ei->exc); + /* + * We are not yet suspending, so get_objref () will not keep this object alive. So we need to do it + * later after the suspension. (#12494). + */ + keepalive_obj = ei->exc; break; } case EVENT_KIND_USER_BREAK: @@ -3501,6 +3651,10 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx */ save_thread_context (ctx); suspend_vm (); + + if (keepalive_obj) + /* This will keep this object alive */ + get_objref (keepalive_obj); } send_success = send_packet (CMD_SET_EVENT, CMD_COMPOSITE, &buf); @@ -3611,7 +3765,6 @@ thread_startup (MonoProfiler *prof, uintptr_t tid) g_assert (!tls); // FIXME: Free this somewhere tls = g_new0 (DebuggerTlsData, 1); - tls->resume_event = CreateEvent (NULL, FALSE, FALSE, NULL); MONO_GC_REGISTER_ROOT_SINGLE (tls->thread); tls->thread = thread; mono_native_tls_set_value (debugger_tls_id, tls); @@ -3930,15 +4083,17 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo char *s = g_strdup_printf ("Unable to insert breakpoint at %s:%d, seq_points=%d\n", mono_method_full_name (ji->method, TRUE), bp->il_offset, seq_points->len); for (i = 0; i < seq_points->len; ++i) - printf ("%d\n", seq_points->seq_points [i].il_offset); + DEBUG (1, fprintf (log_file, "%d\n", seq_points->seq_points [i].il_offset)); if (error) { mono_error_set_error (error, MONO_ERROR_GENERIC, "%s", s); + g_warning ("%s", s); g_free (s); return; } else { - g_error ("%s", s); + g_warning ("%s", s); g_free (s); + return; } } @@ -3957,7 +4112,9 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo g_hash_table_insert (bp_locs, inst->ip, GINT_TO_POINTER (count + 1)); mono_loader_unlock (); - if (count == 0) { + if (sp->native_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) { + DEBUG (1, fprintf (log_file, "[dbg] Attempting to insert seq point at dead IL offset %d, ignoring.\n", (int)bp->il_offset)); + } else if (count == 0) { #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED mono_arch_set_breakpoint (ji, inst->ip); #else @@ -3983,7 +4140,7 @@ remove_breakpoint (BreakpointInstance *inst) g_assert (count > 0); - if (count == 1) { + if (count == 1 && inst->native_offset != SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) { mono_arch_clear_breakpoint (ji, ip); } #else @@ -4253,6 +4410,42 @@ clear_breakpoints_for_domain (MonoDomain *domain) mono_loader_unlock (); } +/* + * ss_update: + * + * Return FALSE if single stepping needs to continue because we are at the same line. + */ +static gboolean +ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp) +{ + MonoDebugMethodInfo *minfo; + MonoDebugSourceLocation *loc = NULL; + gboolean hit = TRUE; + + if (req->size != STEP_SIZE_LINE) + return TRUE; + + /* Have to check whenever a different source line was reached */ + minfo = mono_debug_lookup_method (ji->method); + + if (minfo) + loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset); + + if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line)) { + /* Have to continue single stepping */ + DEBUG(1, fprintf (log_file, "[%p] Same source line, continuing single stepping.\n", (gpointer)GetCurrentThreadId ())); + hit = FALSE; + } + + if (loc) { + ss_req->last_method = ji->method; + ss_req->last_line = loc->row; + mono_debug_free_source_location (loc); + } + + return hit; +} + static gboolean breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly) { @@ -4304,6 +4497,9 @@ process_breakpoint_inner (DebuggerTlsData *tls) * the offset recorded in the seq point map, so find the prev seq point before ip. */ sp = find_prev_seq_point_for_native_offset (mono_domain_get (), ji->method, native_offset, &info); + if (!sp) + no_seq_points_found (ji->method); + g_assert (sp); DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, ip=%p, offset=0x%x, sp il offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, ip, native_offset, sp ? sp->il_offset : -1)); @@ -4337,31 +4533,9 @@ process_breakpoint_inner (DebuggerTlsData *tls) for (i = 0; i < ss_reqs_orig->len; ++i) { EventRequest *req = g_ptr_array_index (ss_reqs_orig, i); SingleStepReq *ss_req = req->info; - gboolean hit = TRUE; - - if (ss_req->size == STEP_SIZE_LINE) { - /* Have to check whenever a different source line was reached */ - MonoDebugMethodInfo *minfo; - MonoDebugSourceLocation *loc = NULL; - - minfo = mono_debug_lookup_method (ji->method); - - if (minfo) - loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset); - - if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line)) { - /* Have to continue single stepping */ - DEBUG(1, fprintf (log_file, "[%p] Same source line, continuing single stepping.\n", (gpointer)GetCurrentThreadId ())); - hit = FALSE; - } - - if (loc) { - ss_req->last_method = ji->method; - ss_req->last_line = loc->row; - mono_debug_free_source_location (loc); - } - } + gboolean hit; + hit = ss_update (ss_req, ji, sp); if (hit) g_ptr_array_add (ss_reqs, req); @@ -4433,6 +4607,8 @@ resume_from_signal_handler (void *sigctx, void *func) /* Save the original context in TLS */ // FIXME: This might not work on an altstack ? tls = mono_native_tls_get_value (debugger_tls_id); + if (!tls) + fprintf (stderr, "Thread %p is not attached to the JIT.\n", (gpointer)GetCurrentThreadId ()); g_assert (tls); // FIXME: MonoContext usually doesn't include the fp registers, so these are @@ -4578,41 +4754,8 @@ process_single_step_inner (DebuggerTlsData *tls) return; il_offset = sp->il_offset; - // FIXME: No tests fail if this is disabled -#if 0 - if (ss_req->size == STEP_SIZE_LINE) { - // FIXME: - NOT_IMPLEMENTED; - - /* Step until a different source line is reached */ - MonoDebugMethodInfo *minfo; - - minfo = mono_debug_lookup_method (ji->method); - - if (minfo) { - MonoDebugSourceLocation *loc = mono_debug_symfile_lookup_location (minfo, il_offset); - - if (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line) { - mono_debug_free_source_location (loc); - return; - } - if (!loc) - /* - * Step until we reach a location with line number info, - * otherwise the client can't show a location. - * This can happen for example with statics initialized inline - * outside of a cctor. - */ - return; - - if (loc) { - ss_req->last_method = ji->method; - ss_req->last_line = loc->row; - mono_debug_free_source_location (loc); - } - } - } -#endif + if (!ss_update (ss_req, ji, sp)) + return; /* Start single stepping again from the current sequence point */ ss_start (ss_req, ji->method, sp, info, ctx, tls, FALSE); @@ -4750,6 +4893,8 @@ stop_single_stepping (void) if (val == 0) mono_arch_stop_single_stepping (); + if (ss_req != NULL) + ss_invoke_addr = NULL; #else g_assert_not_reached (); #endif @@ -4831,13 +4976,16 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointI // There could be method calls before the next seq point in the caller when using nested calls //enable_global = TRUE; } else { - while (sp && sp->next_len == 0) { + if (sp && sp->next_len == 0) { sp = NULL; - if (frame_index < tls->frame_count) { + while (frame_index < tls->frame_count) { StackFrame *frame = tls->frames [frame_index]; method = frame->method; sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info); + if (sp && sp->next_len != 0) + break; + sp = NULL; frame_index ++; } } @@ -4939,7 +5087,7 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ g_assert (tls->context.valid); ss_req->start_sp = ss_req->last_sp = MONO_CONTEXT_GET_SP (&tls->context.ctx); - if (tls->has_catch_ctx) { + if (tls->catch_state.valid) { gboolean res; StackFrameInfo frame; MonoContext new_ctx; @@ -4950,7 +5098,7 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ */ /* Find the the jit info for the catch context */ - res = mono_find_jit_info_ext (mono_domain_get (), thread->jit_data, NULL, &tls->catch_ctx, &new_ctx, NULL, &lmf, NULL, &frame); + res = mono_find_jit_info_ext (tls->catch_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], thread->jit_data, NULL, &tls->catch_state.ctx, &new_ctx, NULL, &lmf, NULL, &frame); g_assert (res); g_assert (frame.type == FRAME_TYPE_MANAGED); @@ -4959,6 +5107,8 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ * point after ip. */ sp = find_next_seq_point_for_native_offset (frame.domain, frame.method, frame.native_offset, &info); + if (!sp) + no_seq_points_found (frame.method); g_assert (sp); method = frame.method; @@ -5003,6 +5153,8 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ if (!method && frame->il_offset != -1) { /* FIXME: Sort the table and use a binary search */ sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info); + if (!sp) + no_seq_points_found (frame->method); g_assert (sp); method = frame->method; } @@ -5060,13 +5212,35 @@ mono_debugger_agent_debug_log_is_enabled (void) return agent_config.enabled; } +#ifdef PLATFORM_ANDROID +void +mono_debugger_agent_unhandled_exception (MonoException *exc) +{ + int suspend_policy; + GSList *events; + EventInfo ei; + + if (!inited) + return; + + memset (&ei, 0, sizeof (EventInfo)); + ei.exc = (MonoObject*)exc; + + mono_loader_lock (); + events = create_event_list (EVENT_KIND_EXCEPTION, NULL, NULL, &ei, &suspend_policy); + mono_loader_unlock (); + + process_event (EVENT_KIND_EXCEPTION, &ei, 0, NULL, events, suspend_policy); +} +#endif + void mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx) { - int suspend_policy; + int i, j, suspend_policy; GSList *events; - MonoJitInfo *ji; + MonoJitInfo *ji, *catch_ji; EventInfo ei; DebuggerTlsData *tls = NULL; @@ -5129,23 +5303,55 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx return; ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (throw_ctx), NULL); + if (catch_ctx) + catch_ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (catch_ctx), NULL); + else + catch_ji = NULL; ei.exc = (MonoObject*)exc; ei.caught = catch_ctx != NULL; mono_loader_lock (); + + /* Treat exceptions which are caught in non-user code as unhandled */ + for (i = 0; i < event_requests->len; ++i) { + EventRequest *req = g_ptr_array_index (event_requests, i); + if (req->event_kind != EVENT_KIND_EXCEPTION) + continue; + + for (j = 0; j < req->nmodifiers; ++j) { + Modifier *mod = &req->modifiers [j]; + + if (mod->kind == MOD_KIND_ASSEMBLY_ONLY && catch_ji) { + int k; + gboolean found = FALSE; + MonoAssembly **assemblies = mod->data.assemblies; + + if (assemblies) { + for (k = 0; assemblies [k]; ++k) + if (assemblies [k] == catch_ji->method->klass->image->assembly) + found = TRUE; + } + if (!found) + ei.caught = FALSE; + } + } + } + events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, &ei, &suspend_policy); mono_loader_unlock (); - if (tls && catch_ctx) { - tls->catch_ctx = *catch_ctx; - tls->has_catch_ctx = TRUE; + if (tls && ei.caught && catch_ctx) { + memset (&tls->catch_state, 0, sizeof (tls->catch_state)); + tls->catch_state.ctx = *catch_ctx; + tls->catch_state.unwind_data [MONO_UNWIND_DATA_DOMAIN] = mono_domain_get (); + tls->catch_state.valid = TRUE; } process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, suspend_policy); if (tls) - tls->has_catch_ctx = FALSE; + tls->catch_state.valid = FALSE; } void @@ -5216,6 +5422,12 @@ buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain, MonoObject *obj; if (t->byref) { + if (!(*(void**)addr)) { + /* This can happen with compiler generated locals */ + //printf ("%s\n", mono_type_full_name (t)); + buffer_add_byte (buf, VALUE_TYPE_ID_NULL); + return; + } g_assert (*(void**)addr); addr = *(void**)addr; } @@ -5363,7 +5575,7 @@ obj_is_of_type (MonoObject *obj, MonoType *t) { MonoClass *klass = obj->vtable->klass; if (!mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) { - if (klass == mono_defaults.transparent_proxy_class) { + if (mono_class_is_transparent_proxy (klass)) { klass = ((MonoTransparentProxy *)obj)->remote_class->proxy_class; if (mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) { return TRUE; @@ -5377,6 +5589,49 @@ obj_is_of_type (MonoObject *obj, MonoType *t) static ErrorCode decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit); +static ErrorCode +decode_vtype (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit) +{ + gboolean is_enum; + MonoClass *klass; + MonoClassField *f; + int nfields; + gpointer iter = NULL; + MonoDomain *d; + int err; + + is_enum = decode_byte (buf, &buf, limit); + /* Enums are sent as a normal vtype */ + if (is_enum) + return ERR_NOT_IMPLEMENTED; + klass = decode_typeid (buf, &buf, limit, &d, &err); + if (err) + return err; + + if (t && klass != mono_class_from_mono_type (t)) { + char *name = mono_type_full_name (t); + char *name2 = mono_type_full_name (&klass->byval_arg); + DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got %s.\n", (gpointer)GetCurrentThreadId (), name, name2)); + g_free (name); + g_free (name2); + return ERR_INVALID_ARGUMENT; + } + + nfields = decode_int (buf, &buf, limit); + while ((f = mono_class_get_fields (klass, &iter))) { + if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + if (mono_field_is_deleted (f)) + continue; + err = decode_value (f->type, domain, (guint8*)addr + f->offset - sizeof (MonoObject), buf, &buf, limit); + if (err) + return err; + nfields --; + } + g_assert (nfields == 0); + return 0; +} + static ErrorCode decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit) { @@ -5449,38 +5704,11 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, g_assert (type == MONO_TYPE_VALUETYPE); /* Fall through */ handle_vtype: - case MONO_TYPE_VALUETYPE: { - gboolean is_enum = decode_byte (buf, &buf, limit); - MonoClass *klass; - MonoClassField *f; - int nfields; - gpointer iter = NULL; - MonoDomain *d; - - /* Enums are sent as a normal vtype */ - if (is_enum) - return ERR_NOT_IMPLEMENTED; - klass = decode_typeid (buf, &buf, limit, &d, &err); + case MONO_TYPE_VALUETYPE: + err = decode_vtype (t, domain, addr,buf, &buf, limit); if (err) return err; - - if (klass != mono_class_from_mono_type (t)) - return ERR_INVALID_ARGUMENT; - - nfields = decode_int (buf, &buf, limit); - while ((f = mono_class_get_fields (klass, &iter))) { - if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) - continue; - if (mono_field_is_deleted (f)) - continue; - err = decode_value (f->type, domain, (guint8*)addr + f->offset - sizeof (MonoObject), buf, &buf, limit); - if (err) - return err; - nfields --; - } - g_assert (nfields == 0); break; - } handle_ref: default: if (MONO_TYPE_IS_REFERENCE (t)) { @@ -5505,7 +5733,44 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, mono_gc_wbarrier_generic_store (addr, obj); } else if (type == VALUE_TYPE_ID_NULL) { *(MonoObject**)addr = NULL; + } else if (type == MONO_TYPE_VALUETYPE) { + guint8 *buf2; + gboolean is_enum; + MonoClass *klass; + MonoDomain *d; + guint8 *vtype_buf; + int vtype_buf_size; + + /* This can happen when round-tripping boxed vtypes */ + /* + * Obtain vtype class. + * Same as the beginning of the handle_vtype case above. + */ + buf2 = buf; + is_enum = decode_byte (buf, &buf, limit); + if (is_enum) + return ERR_NOT_IMPLEMENTED; + klass = decode_typeid (buf, &buf, limit, &d, &err); + if (err) + return err; + + /* Decode the vtype into a temporary buffer, then box it. */ + vtype_buf_size = mono_class_value_size (klass, NULL); + vtype_buf = g_malloc0 (vtype_buf_size); + g_assert (vtype_buf); + + buf = buf2; + err = decode_vtype (NULL, domain, vtype_buf, buf, &buf, limit); + if (err) { + g_free (vtype_buf); + return err; + } + *(MonoObject**)addr = mono_value_box (d, klass, vtype_buf); + g_free (vtype_buf); } else { + char *name = mono_type_full_name (t); + DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got 0x%0x.\n", (gpointer)GetCurrentThreadId (), name, type)); + g_free (name); return ERR_INVALID_ARGUMENT; } } else { @@ -5561,11 +5826,11 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 } static void -add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype) +add_var (Buffer *buf, MonoDebugMethodJitInfo *jit, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype) { guint32 flags; int reg; - guint8 *addr; + guint8 *addr, *gaddr; mgreg_t reg_val; flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; @@ -5588,6 +5853,59 @@ add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, Mono case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD: NOT_IMPLEMENTED; break; + case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR: + /* Same as regoffset, but with an indirection */ + addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg); + addr += (gint32)var->offset; + + gaddr = *(gpointer*)addr; + g_assert (gaddr); + buffer_add_value_full (buf, t, gaddr, domain, as_vtype); + break; + case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: { + MonoDebugVarInfo *info_var = jit->gsharedvt_info_var; + MonoDebugVarInfo *locals_var = jit->gsharedvt_locals_var; + MonoGSharedVtMethodRuntimeInfo *info; + guint8 *locals; + int idx; + + idx = reg; + + g_assert (info_var); + g_assert (locals_var); + + flags = info_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; + reg = info_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; + if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET) { + addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg); + addr += (gint32)info_var->offset; + info = *(gpointer*)addr; + } else if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER) { + info = (gpointer)mono_arch_context_get_int_reg (ctx, reg); + } else { + g_assert_not_reached (); + } + g_assert (info); + + flags = locals_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; + reg = locals_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; + if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET) { + addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg); + addr += (gint32)locals_var->offset; + locals = *(gpointer*)addr; + } else if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER) { + locals = (gpointer)mono_arch_context_get_int_reg (ctx, reg); + } else { + g_assert_not_reached (); + } + g_assert (locals); + + addr = locals + GPOINTER_TO_INT (info->entries [idx]); + + buffer_add_value_full (buf, t, addr, domain, as_vtype); + break; + } + default: g_assert_not_reached (); } @@ -5598,7 +5916,7 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai { guint32 flags; int reg, size; - guint8 *addr; + guint8 *addr, *gaddr; flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; reg = var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; @@ -5672,6 +5990,16 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai // FIXME: Write barriers mono_gc_memmove (addr, val, size); break; + case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR: + /* Same as regoffset, but with an indirection */ + addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg); + addr += (gint32)var->offset; + + gaddr = *(gpointer*)addr; + g_assert (gaddr); + // FIXME: Write barriers + mono_gc_memmove (gaddr, val, size); + break; case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD: NOT_IMPLEMENTED; break; @@ -5796,9 +6124,8 @@ add_thread (gpointer key, gpointer value, gpointer user_data) } static ErrorCode -do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke) +do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp) { - guint8 *p = invoke->p; guint8 *end = invoke->endp; MonoMethod *m; int i, err, nargs; @@ -5835,8 +6162,10 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke) if (m->klass->valuetype && (m->flags & METHOD_ATTRIBUTE_STATIC)) { /* Should be null */ int type = decode_byte (p, &p, end); - if (type != VALUE_TYPE_ID_NULL) + if (type != VALUE_TYPE_ID_NULL) { + DEBUG (1, fprintf (log_file, "[%p] Error: Static vtype method invoked with this argument.\n", (gpointer)GetCurrentThreadId ())); return ERR_INVALID_ARGUMENT; + } memset (this_buf, 0, mono_class_instance_size (m->klass)); } else { err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end); @@ -5997,6 +6326,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke) mono_set_lmf ((gpointer)(((gssize)ext.lmf.previous_lmf) & ~3)); #endif + *endp = p; // FIXME: byref arguments // FIXME: varargs return ERR_NONE; @@ -6013,10 +6343,11 @@ invoke_method (void) DebuggerTlsData *tls; InvokeData *invoke; int id; - int i, err; + int i, err, mindex; Buffer buf; static void (*restore_context) (void *); MonoContext restore_ctx; + guint8 *p; if (!restore_context) restore_context = mono_get_restore_context (); @@ -6044,19 +6375,29 @@ invoke_method (void) id = invoke->id; - buffer_init (&buf, 128); + p = invoke->p; + err = 0; + for (mindex = 0; mindex < invoke->nmethods; ++mindex) { + buffer_init (&buf, 128); - err = do_invoke_method (tls, &buf, invoke); + if (err) { + /* Fail the other invokes as well */ + } else { + err = do_invoke_method (tls, &buf, invoke, p, &p); + } - /* Start suspending before sending the reply */ - if (!(invoke->flags & INVOKE_FLAG_SINGLE_THREADED)) { - for (i = 0; i < invoke->suspend_count; ++i) - suspend_vm (); - } + /* Start suspending before sending the reply */ + if (mindex == invoke->nmethods - 1) { + if (!(invoke->flags & INVOKE_FLAG_SINGLE_THREADED)) { + for (i = 0; i < invoke->suspend_count; ++i) + suspend_vm (); + } + } - send_reply_packet (id, err, &buf); + send_reply_packet (id, err, &buf); - buffer_free (&buf); + buffer_free (&buf); + } memcpy (&restore_ctx, &invoke->ctx, sizeof (MonoContext)); @@ -6068,7 +6409,7 @@ invoke_method (void) tls->resume_count -= invoke->suspend_count; } - DEBUG (1, fprintf (log_file, "[%p] Invoke finished, resume_count = %d.\n", (gpointer)GetCurrentThreadId (), tls->resume_count)); + DEBUG (1, fprintf (log_file, "[%p] Invoke finished (%d), resume_count = %d.\n", (gpointer)GetCurrentThreadId (), err, tls->resume_count)); /* * Take the loader lock to avoid race conditions with CMD_VM_ABORT_INVOKE: @@ -6126,7 +6467,7 @@ get_source_files_for_type (MonoClass *klass) GPtrArray *source_file_list; if (minfo) { - mono_debug_symfile_get_line_numbers_full (minfo, NULL, &source_file_list, NULL, NULL, NULL, NULL); + mono_debug_symfile_get_line_numbers_full (minfo, NULL, &source_file_list, NULL, NULL, NULL, NULL, NULL); for (j = 0; j < source_file_list->len; ++j) { sinfo = g_ptr_array_index (source_file_list, j); for (i = 0; i < files->len; ++i) @@ -6182,6 +6523,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) if (suspend_count == 0) return ERR_NOT_SUSPENDED; resume_vm (); + clear_suspended_objs (); break; case CMD_VM_DISPOSE: /* Clear all event requests */ @@ -6201,7 +6543,9 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) case CMD_VM_EXIT: { MonoInternalThread *thread; DebuggerTlsData *tls; +#ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT MonoClass *env_class; +#endif MonoMethod *exit_method = NULL; gpointer *args; int exit_code; @@ -6232,9 +6576,11 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) suspend_vm (); wait_for_suspend (); +#ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT env_class = mono_class_from_name (mono_defaults.corlib, "System", "Environment"); if (env_class) exit_method = mono_class_get_method_from_name (env_class, "Exit", 1); +#endif mono_loader_lock (); thread = mono_g_hash_table_find (tid_to_thread, is_really_suspended, NULL); @@ -6252,6 +6598,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) tls->pending_invoke = g_new0 (InvokeData, 1); tls->pending_invoke->method = exit_method; tls->pending_invoke->args = args; + tls->pending_invoke->nmethods = 1; while (suspend_count > 0) resume_vm (); @@ -6263,9 +6610,8 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) while (suspend_count > 0) resume_vm (); - mono_runtime_set_shutting_down (); - - mono_threads_set_shutting_down (); + if (!mono_runtime_try_shutdown ()) + break; /* Suspend all managed threads since the runtime is going away */ DEBUG(1, fprintf (log_file, "Suspending all threads...\n")); @@ -6279,11 +6625,12 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) } break; } - case CMD_VM_INVOKE_METHOD: { + case CMD_VM_INVOKE_METHOD: + case CMD_VM_INVOKE_METHODS: { int objid = decode_objid (p, &p, end); MonoThread *thread; DebuggerTlsData *tls; - int i, count, err, flags; + int i, count, err, flags, nmethods; err = get_object (objid, (MonoObject**)&thread); if (err) @@ -6291,6 +6638,11 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) flags = decode_int (p, &p, end); + if (command == CMD_VM_INVOKE_METHODS) + nmethods = decode_int (p, &p, end); + else + nmethods = 1; + // Wait for suspending if it already started if (suspend_count) wait_for_suspend (); @@ -6319,6 +6671,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) memcpy (tls->pending_invoke->p, p, end - p); tls->pending_invoke->endp = tls->pending_invoke->p + (end - p); tls->pending_invoke->suspend_count = suspend_count; + tls->pending_invoke->nmethods = nmethods; if (flags & INVOKE_FLAG_SINGLE_THREADED) { resume_thread (THREAD_TO_INTERNAL (thread)); @@ -7015,8 +7368,10 @@ buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass MonoArray *typed_args, *named_args; MonoType *t; CattrNamedArg *arginfo; + MonoError error; - mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo); + mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo, &error); + g_assert (mono_error_ok (&error)); buffer_add_methodid (buf, domain, attr->ctor); @@ -7495,6 +7850,15 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint } break; } + case CMD_TYPE_IS_INITIALIZED: { + MonoVTable *vtable = mono_class_vtable (domain, klass); + + if (vtable) + buffer_add_int (buf, (vtable->initialized || vtable->init_failed) ? 1 : 0); + else + buffer_add_int (buf, 0); + break; + } default: return ERR_NOT_IMPLEMENTED; } @@ -7529,6 +7893,7 @@ static ErrorCode method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, guint8 *p, guint8 *end, Buffer *buf) { MonoMethodHeader *header; + int err; switch (command) { case CMD_METHOD_GET_NAME: { @@ -7545,6 +7910,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g int i, j, n_il_offsets; int *il_offsets; int *line_numbers; + int *column_numbers; int *source_files; GPtrArray *source_file_list; @@ -7565,7 +7931,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g break; } - mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, &n_il_offsets, &il_offsets, &line_numbers, &source_files); + mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, &n_il_offsets, &il_offsets, &line_numbers, &column_numbers, &source_files); buffer_add_int (buf, header->code_size); if (CHECK_PROTOCOL_VERSION (2, 13)) { buffer_add_int (buf, source_file_list->len); @@ -7589,11 +7955,13 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g MonoDebugSourceInfo *sinfo = g_ptr_array_index (source_file_list, source_files [i]); srcfile = sinfo->source_file; } - DEBUG (10, fprintf (log_file, "IL%x -> %s:%d\n", il_offsets [i], srcfile, line_numbers [i])); + DEBUG (10, fprintf (log_file, "IL%x -> %s:%d %d\n", il_offsets [i], srcfile, line_numbers [i], column_numbers ? column_numbers [i] : -1)); buffer_add_int (buf, il_offsets [i]); buffer_add_int (buf, line_numbers [i]); if (CHECK_PROTOCOL_VERSION (2, 13)) buffer_add_int (buf, source_files [i]); + if (CHECK_PROTOCOL_VERSION (2, 19)) + buffer_add_int (buf, column_numbers ? column_numbers [i] : -1); } g_free (source_file); g_free (il_offsets); @@ -7845,6 +8213,54 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g } break; } + case CMD_METHOD_GET_CATTRS: { + MonoClass *attr_klass; + MonoCustomAttrInfo *cinfo; + + attr_klass = decode_typeid (p, &p, end, NULL, &err); + /* attr_klass can be NULL */ + if (err) + return err; + + cinfo = mono_custom_attrs_from_method (method); + + buffer_add_cattrs (buf, domain, method->klass->image, attr_klass, cinfo); + break; + } + case CMD_METHOD_MAKE_GENERIC_METHOD: { + MonoType **type_argv; + int i, type_argc; + MonoDomain *d; + MonoClass *klass; + MonoGenericInst *ginst; + MonoGenericContext tmp_context; + MonoMethod *inflated; + + type_argc = decode_int (p, &p, end); + type_argv = g_new0 (MonoType*, type_argc); + for (i = 0; i < type_argc; ++i) { + klass = decode_typeid (p, &p, end, &d, &err); + if (err) { + g_free (type_argv); + return err; + } + if (domain != d) { + g_free (type_argv); + return ERR_INVALID_ARGUMENT; + } + type_argv [i] = &klass->byval_arg; + } + ginst = mono_metadata_get_generic_inst (type_argc, type_argv); + g_free (type_argv); + tmp_context.class_inst = method->klass->generic_class ? method->klass->generic_class->context.class_inst : NULL; + tmp_context.method_inst = ginst; + + inflated = mono_class_inflate_generic_method (method, &tmp_context); + if (!mono_verifier_is_method_valid_generic_instantiation (inflated)) + return ERR_INVALID_ARGUMENT; + buffer_add_methodid (buf, domain, inflated); + break; + } default: return ERR_NOT_IMPLEMENTED; } @@ -8014,8 +8430,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) frame = tls->frames [frame_idx]; if (!frame->has_ctx) - // FIXME: - return ERR_INVALID_FRAMEID; + return ERR_ABSENT_INFORMATION; if (!frame->jit) { frame->jit = mono_debug_find_method (frame->api_method, frame->domain); @@ -8056,13 +8471,13 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) var = &jit->params [pos]; - add_var (buf, 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 { g_assert (pos >= 0 && pos < jit->num_locals); var = &jit->locals [pos]; - add_var (buf, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE); + add_var (buf, jit, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE); } } mono_metadata_free_mh (header); @@ -8074,14 +8489,14 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) MonoObject *p = NULL; buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &p, frame->domain); } else { - add_var (buf, &frame->actual_method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE); + 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, &frame->api_method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE); + add_var (buf, jit, &frame->api_method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE); } } break; @@ -8394,39 +8809,188 @@ command_set_to_string (CommandSet command_set) } } +static const char* vm_cmds_str [] = { + "VERSION", + "ALL_THREADS", + "SUSPEND", + "RESUME", + "EXIT", + "DISPOSE", + "INVOKE_METHOD", + "SET_PROTOCOL_VERSION", + "ABORT_INVOKE", + "SET_KEEPALIVE" + "GET_TYPES_FOR_SOURCE_FILE", + "GET_TYPES", + "INVOKE_METHODS" +}; + +static const char* thread_cmds_str[] = { + "GET_FRAME_INFO", + "GET_NAME", + "GET_STATE", + "GET_INFO", + "GET_ID", + "GET_TID" +}; + +static const char* event_cmds_str[] = { + "REQUEST_SET", + "REQUEST_CLEAR", + "REQUEST_CLEAR_ALL_BREAKPOINTS" +}; + +static const char* appdomain_cmds_str[] = { + "GET_ROOT_DOMAIN", + "GET_FRIENDLY_NAME", + "GET_ASSEMBLIES", + "GET_ENTRY_ASSEMBLY", + "CREATE_STRING", + "GET_CORLIB", + "CREATE_BOXED_VALUE" +}; + +static const char* assembly_cmds_str[] = { + "GET_LOCATION", + "GET_ENTRY_POINT", + "GET_MANIFEST_MODULE", + "GET_OBJECT", + "GET_TYPE", + "GET_NAME" +}; + +static const char* module_cmds_str[] = { + "GET_INFO", +}; + +static const char* method_cmds_str[] = { + "GET_NAME", + "GET_DECLARING_TYPE", + "GET_DEBUG_INFO", + "GET_PARAM_INFO", + "GET_LOCALS_INFO", + "GET_INFO", + "GET_BODY", + "RESOLVE_TOKEN", + "GET_CATTRS ", + "MAKE_GENERIC_METHOD" +}; + +static const char* type_cmds_str[] = { + "GET_INFO", + "GET_METHODS", + "GET_FIELDS", + "GET_VALUES", + "GET_OBJECT", + "GET_SOURCE_FILES", + "SET_VALUES", + "IS_ASSIGNABLE_FROM", + "GET_PROPERTIES ", + "GET_CATTRS", + "GET_FIELD_CATTRS", + "GET_PROPERTY_CATTRS", + "GET_SOURCE_FILES_2", + "GET_VALUES_2", + "GET_METHODS_BY_NAME_FLAGS", + "GET_INTERFACES", + "GET_INTERFACE_MAP", + "IS_INITIALIZED" +}; + +static const char* stack_frame_cmds_str[] = { + "GET_VALUES", + "GET_THIS", + "SET_VALUES" +}; + +static const char* array_cmds_str[] = { + "GET_LENGTH", + "GET_VALUES", + "SET_VALUES", +}; + +static const char* string_cmds_str[] = { + "GET_VALUE", + "GET_LENGTH", + "GET_CHARS" +}; + +static const char* object_cmds_str[] = { + "GET_TYPE", + "GET_VALUES", + "IS_COLLECTED", + "GET_ADDRESS", + "GET_DOMAIN", + "SET_VALUES", + "GET_INFO", +}; + static const char* cmd_to_string (CommandSet set, int command) { + const char **cmds; + int cmds_len = 0; + switch (set) { - case CMD_SET_VM: { - switch (command) { - case CMD_VM_VERSION: - return "VERSION"; - case CMD_VM_ALL_THREADS: - return "ALL_THREADS"; - case CMD_VM_SUSPEND: - return "SUSPEND"; - case CMD_VM_RESUME: - return "RESUME"; - case CMD_VM_EXIT: - return "EXIT"; - case CMD_VM_DISPOSE: - return "DISPOSE"; - case CMD_VM_INVOKE_METHOD: - return "INVOKE_METHOD"; - case CMD_VM_SET_PROTOCOL_VERSION: - return "SET_PROTOCOL_VERSION"; - case CMD_VM_ABORT_INVOKE: - return "ABORT_INVOKE"; - default: - break; - } + case CMD_SET_VM: + cmds = vm_cmds_str; + cmds_len = G_N_ELEMENTS (vm_cmds_str); + break; + case CMD_SET_OBJECT_REF: + cmds = object_cmds_str; + cmds_len = G_N_ELEMENTS (object_cmds_str); + break; + case CMD_SET_STRING_REF: + cmds = string_cmds_str; + cmds_len = G_N_ELEMENTS (string_cmds_str); + break; + case CMD_SET_THREAD: + cmds = thread_cmds_str; + cmds_len = G_N_ELEMENTS (thread_cmds_str); + break; + case CMD_SET_ARRAY_REF: + cmds = array_cmds_str; + cmds_len = G_N_ELEMENTS (array_cmds_str); + break; + case CMD_SET_EVENT_REQUEST: + cmds = event_cmds_str; + cmds_len = G_N_ELEMENTS (event_cmds_str); + break; + case CMD_SET_STACK_FRAME: + cmds = stack_frame_cmds_str; + cmds_len = G_N_ELEMENTS (stack_frame_cmds_str); + break; + case CMD_SET_APPDOMAIN: + cmds = appdomain_cmds_str; + cmds_len = G_N_ELEMENTS (appdomain_cmds_str); + break; + case CMD_SET_ASSEMBLY: + cmds = assembly_cmds_str; + cmds_len = G_N_ELEMENTS (assembly_cmds_str); + break; + case CMD_SET_METHOD: + cmds = method_cmds_str; + cmds_len = G_N_ELEMENTS (method_cmds_str); + break; + case CMD_SET_TYPE: + cmds = type_cmds_str; + cmds_len = G_N_ELEMENTS (type_cmds_str); + break; + case CMD_SET_MODULE: + cmds = module_cmds_str; + cmds_len = G_N_ELEMENTS (module_cmds_str); + break; + case CMD_SET_EVENT: + cmds = event_cmds_str; + cmds_len = G_N_ELEMENTS (event_cmds_str); break; - } default: break; } - return NULL; + if (command > 0 && command <= cmds_len) + return cmds [command - 1]; + else + return NULL; } static gboolean @@ -8500,8 +9064,10 @@ debugger_thread (void *arg) res = transport_recv (header, HEADER_LENGTH); /* This will break if the socket is closed during shutdown too */ - if (res != HEADER_LENGTH) + if (res != HEADER_LENGTH) { + DEBUG (1, fprintf (log_file, "[dbg] transport_recv () returned %d, expected %d.\n", res, HEADER_LENGTH)); break; + } p = header; end = header + HEADER_LENGTH; @@ -8524,15 +9090,17 @@ debugger_thread (void *arg) cmd_str = cmd_num; } - DEBUG (1, fprintf (log_file, "[dbg] Received command %s(%s), id=%d.\n", command_set_to_string (command_set), cmd_str, id)); + DEBUG (1, fprintf (log_file, "[dbg] Command %s(%s) [%d].\n", command_set_to_string (command_set), cmd_str, id)); } data = g_malloc (len - HEADER_LENGTH); if (len - HEADER_LENGTH > 0) { res = transport_recv (data, len - HEADER_LENGTH); - if (res != len - HEADER_LENGTH) + if (res != len - HEADER_LENGTH) { + DEBUG (1, fprintf (log_file, "[dbg] transport_recv () returned %d, expected %d.\n", res, len - HEADER_LENGTH)); break; + } } p = data; @@ -8594,7 +9162,7 @@ debugger_thread (void *arg) g_free (data); buffer_free (&buf); - if (command_set == CMD_SET_VM && command == CMD_VM_DISPOSE) + if (command_set == CMD_SET_VM && (command == CMD_VM_DISPOSE || command == CMD_VM_EXIT)) break; }