X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fdebugger-agent.c;h=9eb0c294976101e19a897b123a68ea96c221124d;hb=90d6059c5475419ddf6e0fc4b49098158010cab0;hp=b2c4572e5bae971fa7090d8ee5f2024da6b621c5;hpb=12085044cdefad6907fa12a5862a717c7bb16490;p=mono.git diff --git a/mono/mini/debugger-agent.c b/mono/mini/debugger-agent.c index b2c4572e5ba..9eb0c294976 100644 --- a/mono/mini/debugger-agent.c +++ b/mono/mini/debugger-agent.c @@ -69,16 +69,30 @@ int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD, #include #include #include +#include #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 +101,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 @@ -115,6 +125,7 @@ typedef struct { gboolean embedding; gboolean defer; int keepalive; + gboolean setpgid; } AgentConfig; typedef struct @@ -128,8 +139,14 @@ typedef struct * method. */ MonoMethod *actual_method; + /* + * This is the method which is visible to debugger clients. Same as method, + * except for native-to-managed wrappers. + */ + MonoMethod *api_method; MonoContext ctx; MonoDebugMethodJitInfo *jit; + MonoJitInfo *ji; int flags; mgreg_t *reg_locations [MONO_MAX_IREGS]; /* @@ -156,6 +173,7 @@ struct _InvokeData MonoMethod *method; gpointer *args; guint32 suspend_count; + int nmethods; InvokeData *last_invoke; }; @@ -163,7 +181,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; @@ -242,9 +259,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 @@ -270,7 +285,7 @@ typedef struct { #define HEADER_LENGTH 11 #define MAJOR_VERSION 2 -#define MINOR_VERSION 17 +#define MINOR_VERSION 29 typedef enum { CMD_SET_VM = 1, @@ -285,6 +300,7 @@ typedef enum { CMD_SET_METHOD = 22, CMD_SET_TYPE = 23, CMD_SET_MODULE = 24, + CMD_SET_FIELD = 25, CMD_SET_EVENT = 64 } CommandSet; @@ -353,7 +369,9 @@ typedef enum { typedef enum { STEP_FILTER_NONE = 0, - STEP_FILTER_STATIC_CTOR = 1 + STEP_FILTER_STATIC_CTOR = 1, + STEP_FILTER_DEBUGGER_HIDDEN = 2, + STEP_FILTER_DEBUGGER_STEP_THROUGH = 4 } StepFilter; typedef enum { @@ -395,7 +413,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 { @@ -404,7 +423,8 @@ typedef enum { CMD_THREAD_GET_STATE = 3, CMD_THREAD_GET_INFO = 4, CMD_THREAD_GET_ID = 5, - CMD_THREAD_GET_TID = 6 + CMD_THREAD_GET_TID = 6, + CMD_THREAD_SET_IP = 7 } CmdThread; typedef enum { @@ -440,6 +460,10 @@ typedef enum { CMD_MODULE_GET_INFO = 1, } CmdModule; +typedef enum { + CMD_FIELD_GET_INFO = 1, +} CmdField; + typedef enum { CMD_METHOD_GET_NAME = 1, CMD_METHOD_GET_DECLARING_TYPE = 2, @@ -449,6 +473,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 { @@ -469,6 +495,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 { @@ -510,7 +537,7 @@ typedef struct { GHashTable *type_names; /* For kind == MONO_KIND_TYPE_NAME_ONLY */ StepFilter filter; /* For kind == MOD_KIND_STEP */ } data; - gboolean caught, uncaught; /* For kind == MOD_KIND_EXCEPTION_ONLY */ + gboolean caught, uncaught, subclasses; /* For kind == MOD_KIND_EXCEPTION_ONLY */ } Modifier; typedef struct{ @@ -539,6 +566,8 @@ typedef struct { gboolean global; /* The list of breakpoints used to implement step-over */ GSList *bps; + /* The number of frames at the start of a step-over */ + int nframes; } SingleStepReq; /* @@ -661,6 +690,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); @@ -739,6 +771,8 @@ static void finish_agent_init (gboolean on_startup); static void process_profiler_event (EventKind event, gpointer arg); +static void invalidate_frames (DebuggerTlsData *tls); + #ifndef DISABLE_SOCKET_TRANSPORT static void register_socket_transport (void); @@ -774,6 +808,7 @@ print_usage (void) fprintf (stderr, " timeout=\t\t\tTimeout for connecting in milliseconds.\n"); fprintf (stderr, " server=y/n\t\t\tWhether to listen for a client connection.\n"); fprintf (stderr, " keepalive=\t\t\tSend keepalive events every n milliseconds.\n"); + fprintf (stderr, " setpgid=y/n\t\t\tWhether to call setpid(0, 0) after startup.\n"); fprintf (stderr, " help\t\t\t\tPrint this help.\n"); } @@ -797,14 +832,14 @@ mono_debugger_agent_parse_options (char *options) char **args, **ptr; char *host; int port; - char *extra; + const char *extra; #ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED fprintf (stderr, "--debugger-agent is not supported on this platform.\n"); exit (1); #endif - extra = getenv ("MONO_SDB_ENV_OPTIONS"); + extra = g_getenv ("MONO_SDB_ENV_OPTIONS"); if (extra) options = g_strdup_printf ("%s,%s", options, extra); @@ -848,6 +883,8 @@ mono_debugger_agent_parse_options (char *options) agent_config.embedding = atoi (arg + 10) == 1; } else if (strncmp (arg, "keepalive=", 10) == 0) { agent_config.keepalive = atoi (arg + 10); + } else if (strncmp (arg, "setpgid=", 8) == 0) { + agent_config.setpgid = parse_flag ("setpgid", arg + 8); } else { print_usage (); exit (1); @@ -896,7 +933,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); @@ -961,13 +998,13 @@ mono_debugger_agent_init (void) */ mini_get_debug_options ()->load_aot_jit_info_eagerly = TRUE; +#ifdef HAVE_SETPGID + if (agent_config.setpgid) + setpgid (0, 0); +#endif + 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 } /* @@ -1046,15 +1083,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; @@ -1070,7 +1122,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; @@ -1298,8 +1350,7 @@ socket_transport_connect (const char *address) #endif } - disconnected = !transport_handshake (); - if (disconnected) + if (!transport_handshake ()) exit (1); } @@ -1359,6 +1410,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) { @@ -1421,6 +1481,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) { @@ -1428,6 +1494,8 @@ transport_handshake (void) guint8 buf [128]; int res; + disconnected = TRUE; + /* Write handshake message */ sprintf (handshake_msg, "DWP-Handshake"); do { @@ -1456,7 +1524,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, @@ -1469,6 +1537,7 @@ transport_handshake (void) set_keepalive (); #endif + disconnected = FALSE; return TRUE; } @@ -1513,9 +1582,7 @@ stop_debugger_thread (void) static void start_debugger_thread (void) { - gsize tid; - - debugger_thread_handle = mono_create_thread (NULL, 0, debugger_thread, NULL, 0, &tid); + debugger_thread_handle = mono_threads_create_thread (debugger_thread, NULL, 0, 0, NULL); g_assert (debugger_thread_handle); } @@ -1769,6 +1836,7 @@ objrefs_cleanup (void) } static GHashTable *obj_to_objref; +static MonoGHashTable *suspended_objs; /* * Return an ObjRef for OBJ. @@ -1785,8 +1853,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 */ @@ -1833,6 +1911,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) { @@ -2113,6 +2205,7 @@ decode_ptr_id (guint8 *buf, guint8 **endbuf, guint8 *limit, IdType type, MonoDom mono_loader_unlock (); if (res->domain == NULL) { + DEBUG (0, fprintf (log_file, "ERR_UNLOADED, id=%d, type=%d.\n", id, type)); *err = ERR_UNLOADED; return NULL; } @@ -2132,7 +2225,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* @@ -2150,7 +2253,17 @@ decode_moduleid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domai static inline MonoMethod* decode_methodid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err) { - return decode_ptr_id (buf, endbuf, limit, ID_METHOD, domain, err); + MonoMethod *m; + + m = decode_ptr_id (buf, endbuf, limit, ID_METHOD, domain, err); + if (G_UNLIKELY (log_level >= 2) && m) { + char *s; + + s = mono_method_full_name (m, TRUE); + DEBUG(2, fprintf (log_file, "[dbg] recv method [%s]\n", s)); + g_free (s); + } + return m; } static inline MonoClassField* @@ -2175,12 +2288,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 @@ -2239,9 +2369,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 @@ -2260,7 +2387,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); } @@ -2290,7 +2417,6 @@ get_last_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) /* Store the context/lmf for the frame above the last frame */ memcpy (&data->ctx, ctx, sizeof (MonoContext)); data->lmf = info->lmf; - return TRUE; } } @@ -2349,7 +2475,7 @@ thread_interrupt (DebuggerTlsData *tls, MonoThreadInfo *info, void *sigctx, Mono if (ji) { /* Running managed code, will be suspended by the single step code */ - DEBUG (1, fprintf (log_file, "[%p] Received interrupt while at %s(%p), continuing.\n", (gpointer)(gsize)tid, ji->method->name, ip)); + DEBUG (1, fprintf (log_file, "[%p] Received interrupt while at %s(%p), continuing.\n", (gpointer)(gsize)tid, jinfo_get_method (ji)->name, ip)); return TRUE; } else { /* @@ -2372,8 +2498,6 @@ thread_interrupt (DebuggerTlsData *tls, MonoThreadInfo *info, void *sigctx, Mono /* Already terminated */ return TRUE; - tls->context.valid = FALSE; - /* * We are in a difficult position: we want to be able to provide stack * traces for this thread, but we can't use the current ctx+lmf, since @@ -2407,9 +2531,9 @@ thread_interrupt (DebuggerTlsData *tls, MonoThreadInfo *info, void *sigctx, Mono tls->async_state.unwind_data [MONO_UNWIND_DATA_LMF] = data.lmf; tls->async_state.unwind_data [MONO_UNWIND_DATA_JIT_TLS] = tls->thread->jit_data; } else { - /* No managed frames */ tls->async_state.valid = FALSE; } + mono_memory_barrier (); tls->suspended = TRUE; @@ -2434,8 +2558,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); } @@ -2451,15 +2577,23 @@ static void CALLBACK notify_thread_apc (ULONG_PTR param) /* * reset_native_thread_suspend_state: * - * Reset the suspended flag on native threads + * Reset the suspended flag and state on native threads */ static void reset_native_thread_suspend_state (gpointer key, gpointer value, gpointer user_data) { DebuggerTlsData *tls = value; - if (!tls->really_suspended && tls->suspended) + if (!tls->really_suspended && tls->suspended) { tls->suspended = FALSE; + /* + * The thread might still be running if it was executing native code, so the state won't be invalided by + * suspend_current (). + */ + tls->context.valid = FALSE; + tls->async_state.valid = FALSE; + invalidate_frames (tls); + } } /* @@ -2507,20 +2641,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 } @@ -2530,6 +2672,7 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx) { guint8 *ip = MONO_CONTEXT_GET_IP (ctx); MonoJitInfo *ji; + MonoMethod *method; if (mono_loader_lock_is_owned_by_self ()) { /* @@ -2562,7 +2705,8 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx) ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL); /* Can't suspend in these methods */ - if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy"))) + method = jinfo_get_method (ji); + if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy"))) return; save_thread_context (ctx); @@ -2595,6 +2739,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 (); } @@ -2633,6 +2783,9 @@ resume_vm (void) mono_mutex_unlock (&suspend_mutex); //g_assert (err == 0); + if (suspend_count == 0) + mono_thread_pool_resume (); + mono_loader_unlock (); } @@ -2658,7 +2811,7 @@ resume_thread (MonoInternalThread *thread) g_assert (suspend_count > 0); - DEBUG(1, fprintf (log_file, "[%p] Resuming thread...\n", (gpointer)(gssize)thread->tid)); + DEBUG(1, fprintf (log_file, "[sdb] Resuming thread %p...\n", (gpointer)(gssize)thread->tid)); tls->resume_count += suspend_count; @@ -2768,7 +2921,7 @@ suspend_current (void) /* The frame info becomes invalid after a resume */ tls->context.valid = FALSE; tls->async_state.valid = FALSE; - invalidate_frames (NULL); + invalidate_frames (tls); } static void @@ -2853,16 +3006,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)); } /* @@ -2876,7 +3026,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; @@ -2899,9 +3055,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) @@ -2948,7 +3106,7 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) { ComputeFramesUserData *ud = user_data; StackFrame *frame; - MonoMethod *method, *actual_method; + MonoMethod *method, *actual_method, *api_method; SeqPoint *sp; int flags = 0; @@ -2962,10 +3120,11 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) } if (info->ji) - method = info->ji->method; + method = jinfo_get_method (info->ji); else method = info->method; actual_method = info->actual_method; + api_method = method; if (!method) return FALSE; @@ -2990,19 +3149,21 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) if (!CHECK_PROTOCOL_VERSION (2, 17)) /* Older clients can't handle this flag */ return FALSE; - method = mono_marshal_method_from_wrapper (method); - if (!method) + api_method = mono_marshal_method_from_wrapper (method); + if (!api_method) return FALSE; - actual_method = method; + actual_method = api_method; flags |= FRAME_FLAG_NATIVE_TRANSITION; } frame = g_new0 (StackFrame, 1); frame->method = method; frame->actual_method = actual_method; + frame->api_method = api_method; frame->il_offset = info->il_offset; frame->native_offset = info->native_offset; frame->flags = flags; + frame->ji = info->ji; if (info->reg_locations) memcpy (frame->reg_locations, info->reg_locations, MONO_MAX_IREGS * sizeof (mgreg_t*)); if (ctx) { @@ -3220,7 +3381,9 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo if (mod->data.thread != mono_thread_internal_current ()) filtered = TRUE; } else if (mod->kind == MOD_KIND_EXCEPTION_ONLY && ei) { - if (mod->data.exc_class && !mono_class_is_assignable_from (mod->data.exc_class, ei->exc->vtable->klass)) + if (mod->data.exc_class && mod->subclasses && !mono_class_is_assignable_from (mod->data.exc_class, ei->exc->vtable->klass)) + filtered = TRUE; + if (mod->data.exc_class && !mod->subclasses && mod->data.exc_class != ei->exc->vtable->klass) filtered = TRUE; if (ei->caught && !mod->caught) filtered = TRUE; @@ -3233,7 +3396,7 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo if (assemblies) { for (k = 0; assemblies [k]; ++k) - if (assemblies [k] == ji->method->klass->image->assembly) + if (assemblies [k] == jinfo_get_method (ji)->klass->image->assembly) found = TRUE; } if (!found) @@ -3251,7 +3414,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); /* @@ -3261,6 +3424,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); @@ -3277,9 +3449,55 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo g_free (s); } else if (mod->kind == MOD_KIND_STEP) { if ((mod->data.filter & STEP_FILTER_STATIC_CTOR) && ji && - (ji->method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && - !strcmp (ji->method->name, ".cctor")) + (jinfo_get_method (ji)->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && + !strcmp (jinfo_get_method (ji)->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 (jinfo_get_method (ji)); + 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; + } + if ((mod->data.filter & STEP_FILTER_DEBUGGER_STEP_THROUGH) && ji) { + MonoCustomAttrInfo *ainfo; + static MonoClass *klass; + + if (!klass) { + klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggerStepThroughAttribute"); + g_assert (klass); + } + if (!ji->dbg_step_through_inited) { + ainfo = mono_custom_attrs_from_method (jinfo_get_method (ji)); + if (ainfo) { + if (mono_custom_attrs_has_attr (ainfo, klass)) + ji->dbg_step_through = TRUE; + mono_custom_attrs_free (ainfo); + } + ainfo = mono_custom_attrs_from_class (jinfo_get_method (ji)->klass); + if (ainfo) { + if (mono_custom_attrs_has_attr (ainfo, klass)) + ji->dbg_step_through = TRUE; + mono_custom_attrs_free (ainfo); + } + ji->dbg_step_through_inited = TRUE; + } + if (ji->dbg_step_through) + filtered = TRUE; + } } } @@ -3322,6 +3540,7 @@ event_to_string (EventKind event) case EVENT_KIND_USER_LOG: return "USER_LOG"; default: g_assert_not_reached (); + return ""; } } @@ -3340,6 +3559,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; @@ -3438,10 +3658,17 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx buffer_add_domainid (&buf, mono_get_root_domain ()); break; case EVENT_KIND_VM_DEATH: + if (CHECK_PROTOCOL_VERSION (2, 27)) + buffer_add_int (&buf, mono_environment_exitcode_get ()); break; 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: @@ -3483,6 +3710,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); @@ -3593,7 +3824,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); @@ -3909,18 +4139,20 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo } if (i == seq_points->len) { - 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); + char *s = g_strdup_printf ("Unable to insert breakpoint at %s:%d, seq_points=%d\n", mono_method_full_name (jinfo_get_method (ji), 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; } } @@ -3939,7 +4171,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 @@ -3947,7 +4181,7 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo #endif } - DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x.\n", mono_method_full_name (ji->method, TRUE), (int)sp->il_offset)); + DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x.\n", mono_method_full_name (jinfo_get_method (ji), TRUE), (int)sp->il_offset)); } static void @@ -3965,7 +4199,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 @@ -4016,6 +4250,7 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji) int i, j; MonoSeqPointInfo *seq_points; MonoDomain *domain; + MonoMethod *jmethod; if (!breakpoints) return; @@ -4039,10 +4274,11 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji) } if (!found) { + jmethod = jinfo_get_method (ji); mono_domain_lock (domain); - seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, ji->method); - if (!seq_points && ji->method->is_inflated) - seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (ji->method)); + seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, jmethod); + if (!seq_points && jmethod->is_inflated) + seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (jmethod)); mono_domain_unlock (domain); if (!seq_points) /* Could be AOT code */ @@ -4235,6 +4471,66 @@ clear_breakpoints_for_domain (MonoDomain *domain) mono_loader_unlock (); } +/* + * ss_update: + * + * Return FALSE if single stepping needs to continue. + */ +static gboolean +ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx) +{ + MonoDebugMethodInfo *minfo; + MonoDebugSourceLocation *loc = NULL; + gboolean hit = TRUE; + MonoMethod *method; + + if (req->depth == STEP_DEPTH_OVER && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK)) { + /* + * These seq points are inserted by the JIT after calls, step over needs to skip them. + */ + DEBUG (1, fprintf (log_file, "[%p] Seq point at nonempty stack %x while stepping over, continuing single stepping.\n", (gpointer)GetCurrentThreadId (), sp->il_offset)); + return FALSE; + } + + if (req->depth == STEP_DEPTH_OVER && hit) { + if (!tls->context.valid) + mono_thread_state_init_from_monoctx (&tls->context, ctx); + compute_frame_info (tls->thread, tls); + if (req->nframes && tls->frame_count && tls->frame_count > req->nframes) { + /* Hit the breakpoint in a recursive call */ + DEBUG (1, fprintf (log_file, "[%p] Breakpoint at lower frame while stepping over, continuing single stepping.\n", (gpointer)GetCurrentThreadId ())); + return FALSE; + } + } + + 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) + loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset); + + if (!loc || (loc && method == ss_req->last_method && loc->row == ss_req->last_line)) { + /* Have to continue single stepping */ + if (!loc) + DEBUG(1, fprintf (log_file, "[%p] No line number info for il offset %x, continuing single stepping.\n", (gpointer)GetCurrentThreadId (), sp->il_offset)); + else + DEBUG(1, fprintf (log_file, "[%p] Same source line (%d), continuing single stepping.\n", (gpointer)GetCurrentThreadId (), loc->row)); + hit = FALSE; + } + + if (loc) { + ss_req->last_method = method; + ss_req->last_line = loc->row; + mono_debug_free_source_location (loc); + } + + return hit; +} + static gboolean breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly) { @@ -4254,6 +4550,7 @@ process_breakpoint_inner (DebuggerTlsData *tls) GSList *bp_events = NULL, *ss_events = NULL, *enter_leave_events = NULL; EventKind kind = EVENT_KIND_BREAKPOINT; MonoContext *ctx = &tls->restore_ctx; + MonoMethod *method; MonoSeqPointInfo *info; SeqPoint *sp; @@ -4262,7 +4559,7 @@ process_breakpoint_inner (DebuggerTlsData *tls) ip = MONO_CONTEXT_GET_IP (ctx); ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL); g_assert (ji); - g_assert (ji->method); + method = jinfo_get_method (ji); /* Compute the native offset of the breakpoint from the ip */ native_offset = ip - (guint8*)ji->code_start; @@ -4272,22 +4569,25 @@ process_breakpoint_inner (DebuggerTlsData *tls) */ mono_arch_skip_breakpoint (ctx, ji); - if (ji->method->wrapper_type || tls->disable_breakpoints) + if (method->wrapper_type || tls->disable_breakpoints) return; bp_reqs = g_ptr_array_new (); ss_reqs = g_ptr_array_new (); ss_reqs_orig = g_ptr_array_new (); - DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, ip=%p, offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, ip, native_offset)); - mono_loader_lock (); /* * The ip points to the instruction causing the breakpoint event, which is after * 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); + sp = find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info); + if (!sp) + no_seq_points_found (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 (), method->name, ip, native_offset, sp ? sp->il_offset : -1)); bp = NULL; for (i = 0; i < breakpoints->len; ++i) { @@ -4319,34 +4619,17 @@ 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); + gboolean hit; - if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line)) - /* Have to continue single stepping */ - hit = FALSE; - - if (loc) { - ss_req->last_method = ji->method; - ss_req->last_line = loc->row; - mono_debug_free_source_location (loc); - } - } + if (mono_thread_internal_current () != ss_req->thread) + continue; + hit = ss_update (ss_req, ji, sp, tls, ctx); if (hit) g_ptr_array_add (ss_reqs, req); /* Start single stepping again from the current sequence point */ - ss_start (ss_req, ji->method, sp, info, ctx, tls, FALSE); + ss_start (ss_req, method, sp, info, ctx, tls, FALSE); } if (ss_reqs->len > 0) @@ -4366,11 +4649,11 @@ process_breakpoint_inner (DebuggerTlsData *tls) * resume. */ if (ss_events) - process_event (EVENT_KIND_STEP, ji->method, 0, ctx, ss_events, suspend_policy); + process_event (EVENT_KIND_STEP, method, 0, ctx, ss_events, suspend_policy); if (bp_events) - process_event (kind, ji->method, 0, ctx, bp_events, suspend_policy); + process_event (kind, method, 0, ctx, bp_events, suspend_policy); if (enter_leave_events) - process_event (kind, ji->method, 0, ctx, enter_leave_events, suspend_policy); + process_event (kind, method, 0, ctx, enter_leave_events, suspend_policy); } /* Process a breakpoint/single step event after resuming from a signal handler */ @@ -4379,10 +4662,6 @@ process_signal_event (void (*func) (DebuggerTlsData*)) { DebuggerTlsData *tls; MonoContext orig_restore_ctx, ctx; - static void (*restore_context) (void *); - - if (!restore_context) - restore_context = mono_get_restore_context (); tls = mono_native_tls_get_value (debugger_tls_id); /* Have to save/restore the restore_ctx as we can be called recursively during invokes etc. */ @@ -4394,7 +4673,7 @@ process_signal_event (void (*func) (DebuggerTlsData*)) /* This is called when resuming from a signal handler, so it shouldn't return */ memcpy (&ctx, &tls->restore_ctx, sizeof (MonoContext)); memcpy (&tls->restore_ctx, &orig_restore_ctx, sizeof (MonoContext)); - restore_context (&ctx); + mono_restore_context (&ctx); g_assert_not_reached (); } @@ -4413,6 +4692,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 @@ -4441,7 +4722,6 @@ 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); } @@ -4509,6 +4789,7 @@ process_single_step_inner (DebuggerTlsData *tls) MonoDomain *domain; GSList *events; MonoContext *ctx = &tls->restore_ctx; + MonoMethod *method; SeqPoint *sp; MonoSeqPointInfo *info; @@ -4532,75 +4813,43 @@ process_single_step_inner (DebuggerTlsData *tls) if (log_level > 0) { ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain); - DEBUG (1, fprintf (log_file, "[%p] Single step event (depth=%s) at %s (%p), sp %p, last sp %p\n", (gpointer)GetCurrentThreadId (), ss_depth_to_string (ss_req->depth), mono_method_full_name (ji->method, TRUE), MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp)); + DEBUG (1, fprintf (log_file, "[%p] Single step event (depth=%s) at %s (%p), sp %p, last sp %p\n", (gpointer)GetCurrentThreadId (), ss_depth_to_string (ss_req->depth), mono_method_full_name (jinfo_get_method (ji), TRUE), MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp)); } ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain); g_assert (ji); - g_assert (ji->method); + method = jinfo_get_method (ji); + g_assert (method); - if (ji->method->wrapper_type && ji->method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD) + if (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD) return; /* - * FIXME: + * FIXME: * Stopping in memset makes half-initialized vtypes visible. * Stopping in memcpy makes half-copied vtypes visible. */ - if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy"))) + if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy"))) 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. */ - sp = find_next_seq_point_for_native_offset (domain, ji->method, (guint8*)ip - (guint8*)ji->code_start, &info); + sp = find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info); if (!sp) 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, tls, ctx)) + return; /* Start single stepping again from the current sequence point */ - ss_start (ss_req, ji->method, sp, info, ctx, tls, FALSE); + ss_start (ss_req, method, sp, info, ctx, tls, FALSE); if ((ss_req->filter & STEP_FILTER_STATIC_CTOR) && - (ji->method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && - !strcmp (ji->method->name, ".cctor")) + (method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && + !strcmp (method->name, ".cctor")) return; // FIXME: Has to lock earlier @@ -4617,7 +4866,7 @@ process_single_step_inner (DebuggerTlsData *tls) mono_loader_unlock (); - process_event (EVENT_KIND_STEP, ji->method, il_offset, ctx, events, suspend_policy); + process_event (EVENT_KIND_STEP, jinfo_get_method (ji), il_offset, ctx, events, suspend_policy); } static void @@ -4731,6 +4980,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 @@ -4770,7 +5021,7 @@ ss_stop (SingleStepReq *ss_req) static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch) { - int i, frame_index; + int i, j, frame_index; SeqPoint *next_sp; MonoBreakpoint *bp; gboolean enable_global = FALSE; @@ -4787,7 +5038,7 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointI } else { frame_index = 1; - if ((!sp || sp->next_len == 0 || ss_req->depth == STEP_DEPTH_OUT) && ctx) { + if ((!sp || sp->next_len == 0 || ss_req->depth == STEP_DEPTH_OUT || ss_req->depth == STEP_DEPTH_OVER) && ctx) { /* Need parent frames */ if (!tls->context.valid) mono_thread_state_init_from_monoctx (&tls->context, ctx); @@ -4809,14 +5060,19 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointI if (sp && sp->next_len != 0) break; } + // 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 ++; } } @@ -4831,6 +5087,29 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointI } } + if (ss_req->depth == STEP_DEPTH_OVER) { + if (ss_req->nframes == 0) + ss_req->nframes = tls->frame_count; + /* Need to stop in catch clauses as well */ + for (i = 0; i < tls->frame_count; ++i) { + StackFrame *frame = tls->frames [i]; + + if (frame->ji) { + MonoJitInfo *jinfo = frame->ji; + for (j = 0; j < jinfo->num_clauses; ++j) { + MonoJitExceptionInfo *ei = &jinfo->clauses [j]; + + sp = find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL); + if (sp) { + bp = set_breakpoint (frame->method, sp->il_offset, ss_req->req, NULL); + ss_req->bps = g_slist_append (ss_req->bps, bp); + } + } + } + } + } + + if (ss_req->depth == STEP_DEPTH_INTO) { /* Enable global stepping so we stop at method entry too */ enable_global = TRUE; @@ -4897,7 +5176,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; @@ -4908,7 +5187,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); @@ -4917,6 +5196,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; @@ -4961,6 +5242,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; } @@ -5018,13 +5301,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; @@ -5087,23 +5392,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] == jinfo_get_method (catch_ji)->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 @@ -5174,6 +5511,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; } @@ -5316,11 +5659,75 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain) buffer_add_value_full (buf, t, addr, domain, FALSE); } -static ErrorCode -decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit) +static gboolean +obj_is_of_type (MonoObject *obj, MonoType *t) { - int err; - int type = decode_byte (buf, &buf, limit); + MonoClass *klass = obj->vtable->klass; + if (!mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) { + 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; + } + } + return FALSE; + } + return TRUE; +} + +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); + + *endbuf = buf; + + return 0; +} + +static ErrorCode +decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit) +{ + int err; if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) && !(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) && @@ -5389,38 +5796,11 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 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)) { @@ -5433,15 +5813,56 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 if (err) return err; - if (obj && !mono_class_is_assignable_from (mono_class_from_mono_type (t), obj->vtable->klass)) - return ERR_INVALID_ARGUMENT; + if (obj) { + if (!obj_is_of_type (obj, t)) { + DEBUG (1, fprintf (log_file, "Expected type '%s', got '%s'\n", mono_type_full_name (t), obj->vtable->klass->name)); + return ERR_INVALID_ARGUMENT; + } + } if (obj && obj->vtable->domain != domain) return ERR_INVALID_ARGUMENT; 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 { @@ -5455,12 +5876,53 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 return 0; } +static ErrorCode +decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit) +{ + int err; + int type = decode_byte (buf, &buf, limit); + + if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) { + MonoType *targ = t->data.generic_class->context.class_inst->type_argv [0]; + guint8 *nullable_buf; + + /* + * First try decoding it as a Nullable`1 + */ + err = decode_value_internal (t, type, domain, addr, buf, endbuf, limit); + if (!err) + return err; + + /* + * Then try decoding as a primitive value or null. + */ + if (targ->type == type) { + nullable_buf = g_malloc (mono_class_instance_size (mono_class_from_mono_type (targ))); + err = decode_value_internal (targ, type, domain, nullable_buf, buf, endbuf, limit); + if (err) { + g_free (nullable_buf); + return err; + } + mono_nullable_init (addr, mono_value_box (domain, mono_class_from_mono_type (targ), nullable_buf), mono_class_from_mono_type (t)); + g_free (nullable_buf); + *endbuf = buf; + return ERR_NONE; + } else if (type == VALUE_TYPE_ID_NULL) { + mono_nullable_init (addr, NULL, mono_class_from_mono_type (t)); + *endbuf = buf; + return ERR_NONE; + } + } + + return decode_value_internal (t, type, domain, addr, buf, endbuf, limit); +} + 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; @@ -5483,6 +5945,60 @@ 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: + case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR: + /* 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 (); } @@ -5493,7 +6009,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; @@ -5509,6 +6025,16 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai mgreg_t v; gboolean is_signed = FALSE; + if (t->byref) { + addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg); + + if (addr) { + // FIXME: Write barriers + mono_gc_memmove_atomic (addr, val, size); + } + break; + } + if (!t->byref && (t->type == MONO_TYPE_I1 || t->type == MONO_TYPE_I2 || t->type == MONO_TYPE_I4 || t->type == MONO_TYPE_I8)) is_signed = TRUE; @@ -5529,9 +6055,6 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai g_assert_not_reached (); } - if (t->byref) - NOT_IMPLEMENTED; - /* Set value on the stack or in the return ctx */ if (reg_locations [reg]) { /* Saved on the stack */ @@ -5565,7 +6088,17 @@ set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domai } // FIXME: Write barriers - memcpy (addr, val, size); + mono_gc_memmove_atomic (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_atomic (gaddr, val, size); break; case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD: NOT_IMPLEMENTED; @@ -5691,9 +6224,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; @@ -5730,8 +6262,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); @@ -5744,6 +6278,14 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke) else this = NULL; + if (MONO_CLASS_IS_INTERFACE (m->klass)) { + if (!this) { + DEBUG (1, fprintf (log_file, "[%p] Error: Interface method invoked without this argument.\n", (gpointer)GetCurrentThreadId ())); + return ERR_INVALID_ARGUMENT; + } + m = mono_object_get_virtual_method (this, m); + } + DEBUG (1, fprintf (log_file, "[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (m, TRUE), this ? this->vtable->klass->name : "")); if (this && this->vtable->domain != domain) @@ -5760,7 +6302,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke) } } - if (this && !mono_class_is_assignable_from (m->klass, this->vtable->klass)) + if (this && !obj_is_of_type (this, &m->klass->byval_arg)) return ERR_INVALID_ARGUMENT; nargs = decode_int (p, &p, end); @@ -5807,39 +6349,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke) /* Setup our lmf */ memset (&ext, 0, sizeof (ext)); -#ifdef TARGET_AMD64 - ext.lmf.previous_lmf = *(lmf_addr); - /* Mark that this is a MonoLMFExt */ - ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2); - ext.lmf.rsp = (gssize)&ext; -#elif defined(TARGET_X86) - ext.lmf.previous_lmf = (gsize)*(lmf_addr); - /* Mark that this is a MonoLMFExt */ - ext.lmf.previous_lmf = (gsize)(gpointer)(((gssize)ext.lmf.previous_lmf) | 2); - ext.lmf.ebp = (gssize)&ext; -#elif defined(TARGET_ARM) - ext.lmf.previous_lmf = *(lmf_addr); - /* Mark that this is a MonoLMFExt */ - ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2); - ext.lmf.sp = (gssize)&ext; -#elif defined(TARGET_POWERPC) - ext.lmf.previous_lmf = *(lmf_addr); - /* Mark that this is a MonoLMFExt */ - ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2); - ext.lmf.ebp = (gssize)&ext; -#elif defined(TARGET_S390X) - ext.lmf.previous_lmf = *(lmf_addr); - /* Mark that this is a MonoLMFExt */ - ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2); - ext.lmf.ebp = (gssize)&ext; -#elif defined(TARGET_MIPS) - ext.lmf.previous_lmf = *(lmf_addr); - /* Mark that this is a MonoLMFExt */ - ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2); - ext.lmf.iregs [mips_sp] = (gssize)&ext; -#else - g_assert_not_reached (); -#endif + mono_arch_init_lmf_ext (&ext, *lmf_addr); ext.debugger_invoke = TRUE; memcpy (&ext.ctx, &invoke->ctx, sizeof (MonoContext)); @@ -5870,10 +6380,12 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke) buffer_add_value (buf, sig->ret, &res, domain); } else if (mono_class_from_mono_type (sig->ret)->valuetype || sig->ret->type == MONO_TYPE_PTR || sig->ret->type == MONO_TYPE_FNPTR) { if (mono_class_is_nullable (mono_class_from_mono_type (sig->ret))) { - if (!res) - buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &res, domain); - else - buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain); + MonoClass *k = mono_class_from_mono_type (sig->ret); + guint8 *nullable_buf = g_alloca (mono_class_value_size (k, NULL)); + + g_assert (nullable_buf); + mono_nullable_init (nullable_buf, res, k); + buffer_add_value (buf, sig->ret, nullable_buf, domain); } else { g_assert (res); buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain); @@ -5890,6 +6402,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; @@ -5906,13 +6419,10 @@ 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; - - if (!restore_context) - restore_context = mono_get_restore_context (); + guint8 *p; tls = mono_native_tls_get_value (debugger_tls_id); g_assert (tls); @@ -5937,19 +6447,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)); @@ -5961,7 +6481,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: @@ -6019,7 +6539,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) @@ -6075,6 +6595,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 */ @@ -6094,7 +6615,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; @@ -6125,9 +6648,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); @@ -6145,6 +6670,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 (); @@ -6156,9 +6682,10 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) while (suspend_count > 0) resume_vm (); - mono_runtime_set_shutting_down (); + if (!mono_runtime_try_shutdown ()) + break; - mono_threads_set_shutting_down (); + mono_environment_exitcode_set (exit_code); /* Suspend all managed threads since the runtime is going away */ DEBUG(1, fprintf (log_file, "Suspending all threads...\n")); @@ -6172,11 +6699,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) @@ -6184,6 +6712,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 (); @@ -6212,6 +6745,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)); @@ -6478,6 +7012,9 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (CHECK_PROTOCOL_VERSION (2, 16)) filter = decode_int (p, &p, end); req->modifiers [i].data.filter = filter; + if (!CHECK_PROTOCOL_VERSION (2, 26) && (req->modifiers [i].data.filter & STEP_FILTER_DEBUGGER_HIDDEN)) + /* Treat STEP_THOUGH the same as HIDDEN */ + req->modifiers [i].data.filter |= STEP_FILTER_DEBUGGER_STEP_THROUGH; } else if (mod == MOD_KIND_THREAD_ONLY) { int id = decode_id (p, &p, end); @@ -6493,7 +7030,11 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return err; req->modifiers [i].caught = decode_byte (p, &p, end); req->modifiers [i].uncaught = decode_byte (p, &p, end); - DEBUG(1, fprintf (log_file, "[dbg] \tEXCEPTION_ONLY filter (%s%s%s).\n", exc_class ? exc_class->name : "all", req->modifiers [i].caught ? ", caught" : "", req->modifiers [i].uncaught ? ", uncaught" : "")); + if (CHECK_PROTOCOL_VERSION (2, 25)) + req->modifiers [i].subclasses = decode_byte (p, &p, end); + else + req->modifiers [i].subclasses = TRUE; + DEBUG(1, fprintf (log_file, "[dbg] \tEXCEPTION_ONLY filter (%s%s%s%s).\n", exc_class ? exc_class->name : "all", req->modifiers [i].caught ? ", caught" : "", req->modifiers [i].uncaught ? ", uncaught" : "", req->modifiers [i].subclasses ? ", include-subclasses" : "")); if (exc_class) { req->modifiers [i].data.exc_class = exc_class; @@ -6871,6 +7412,29 @@ module_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return ERR_NONE; } +static ErrorCode +field_commands (int command, guint8 *p, guint8 *end, Buffer *buf) +{ + int err; + MonoDomain *domain; + + switch (command) { + case CMD_FIELD_GET_INFO: { + MonoClassField *f = decode_fieldid (p, &p, end, &domain, &err); + + buffer_add_string (buf, f->name); + buffer_add_typeid (buf, domain, f->parent); + buffer_add_typeid (buf, domain, mono_class_from_mono_type (f->type)); + buffer_add_int (buf, f->type->attrs); + break; + } + default: + return ERR_NOT_IMPLEMENTED; + } + + return ERR_NONE; +} + static void buffer_add_cattr_arg (Buffer *buf, MonoType *t, MonoDomain *domain, MonoObject *val) { @@ -6907,9 +7471,11 @@ buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass if (!attr_klass || mono_class_has_parent (attr->ctor->klass, attr_klass)) { MonoArray *typed_args, *named_args; MonoType *t; - CattrNamedArg *arginfo; + CattrNamedArg *arginfo = NULL; + 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); @@ -6949,6 +7515,7 @@ buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass } else { buffer_add_int (buf, 0); } + g_free (arginfo); } } } @@ -7388,6 +7955,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; } @@ -7422,6 +7998,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: { @@ -7438,6 +8015,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; @@ -7458,7 +8036,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); @@ -7474,7 +8052,7 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g buffer_add_string (buf, source_file); } buffer_add_int (buf, n_il_offsets); - DEBUG (10, printf ("Line number table for method %s:\n", mono_method_full_name (method, TRUE))); + DEBUG (10, fprintf (log_file, "Line number table for method %s:\n", mono_method_full_name (method, TRUE))); for (i = 0; i < n_il_offsets; ++i) { const char *srcfile = ""; @@ -7482,11 +8060,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, printf ("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); @@ -7644,12 +8224,34 @@ method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, g header = mono_method_get_header (method); if (!header) { buffer_add_int (buf, 0); + + if (CHECK_PROTOCOL_VERSION (2, 18)) + buffer_add_int (buf, 0); } else { buffer_add_int (buf, header->code_size); for (i = 0; i < header->code_size; ++i) buffer_add_byte (buf, header->code [i]); + + if (CHECK_PROTOCOL_VERSION (2, 18)) { + buffer_add_int (buf, header->num_clauses); + for (i = 0; i < header->num_clauses; ++i) { + MonoExceptionClause *clause = &header->clauses [i]; + + buffer_add_int (buf, clause->flags); + buffer_add_int (buf, clause->try_offset); + buffer_add_int (buf, clause->try_len); + buffer_add_int (buf, clause->handler_offset); + buffer_add_int (buf, clause->handler_len); + if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE) + buffer_add_typeid (buf, domain, clause->data.catch_class); + else if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) + buffer_add_int (buf, clause->data.filter_offset); + } + } + + mono_metadata_free_mh (header); } - mono_metadata_free_mh (header); + break; } case CMD_METHOD_RESOLVE_TOKEN: { @@ -7716,6 +8318,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; } @@ -7837,6 +8487,52 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) case CMD_THREAD_GET_TID: buffer_add_long (buf, (guint64)thread->tid); break; + case CMD_THREAD_SET_IP: { + DebuggerTlsData *tls; + MonoMethod *method; + MonoDomain *domain; + MonoSeqPointInfo *seq_points; + SeqPoint *sp = NULL; + gint64 il_offset; + int i; + + method = decode_methodid (p, &p, end, &domain, &err); + if (err) + return err; + il_offset = decode_long (p, &p, end); + + while (!is_suspended ()) { + if (suspend_count) + wait_for_suspend (); + } + + mono_loader_lock (); + tls = mono_g_hash_table_lookup (thread_to_tls, thread); + mono_loader_unlock (); + g_assert (tls); + + compute_frame_info (thread, tls); + if (tls->frame_count == 0 || tls->frames [0]->actual_method != method) + return ERR_INVALID_ARGUMENT; + + seq_points = get_seq_points (domain, method); + g_assert (seq_points); + + for (i = 0; i < seq_points->len; ++i) { + sp = &seq_points->seq_points [i]; + + if (sp->il_offset == il_offset) + break; + } + if (i == seq_points->len) + return ERR_INVALID_ARGUMENT; + + // FIXME: Check that the ip change is safe + + DEBUG (1, fprintf (log_file, "[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_ctx, (guint8*)tls->frames [0]->ji->code_start + sp->native_offset); + break; + } default: return ERR_NOT_IMPLEMENTED; } @@ -7885,18 +8581,17 @@ 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->method, frame->domain); - if (!frame->jit && frame->method->is_inflated) - frame->jit = mono_debug_find_method (mono_method_get_declaring_generic_method (frame->method), frame->domain); + 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->method, TRUE); + s = mono_method_full_name (frame->api_method, TRUE); DEBUG (1, fprintf (log_file, "[dbg] No debug information found for '%s'.\n", s)); g_free (s); return ERR_ABSENT_INFORMATION; @@ -7927,32 +8622,32 @@ 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); break; } case CMD_STACK_FRAME_GET_THIS: { - if (frame->method->klass->valuetype) { + 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, &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->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; @@ -8129,7 +8824,8 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) switch (command) { case CMD_OBJECT_REF_GET_TYPE: - buffer_add_typeid (buf, obj->vtable->domain, obj->vtable->klass); + /* This handles transparent proxies too */ + buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type (((MonoReflectionType*)obj->vtable->type)->type)); break; case CMD_OBJECT_REF_GET_VALUES: len = decode_int (p, &p, end); @@ -8219,7 +8915,7 @@ object_commands (int command, guint8 *p, guint8 *end, Buffer *buf) buffer_add_domainid (buf, obj->vtable->domain); break; case CMD_OBJECT_REF_GET_INFO: - buffer_add_typeid (buf, obj->vtable->domain, obj->vtable->klass); + buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type (((MonoReflectionType*)obj->vtable->type)->type)); buffer_add_domainid (buf, obj->vtable->domain); break; default: @@ -8238,65 +8934,225 @@ command_set_to_string (CommandSet command_set) case CMD_SET_OBJECT_REF: return "OBJECT_REF"; case CMD_SET_STRING_REF: - return "STRING_REF"; + return "STRING_REF"; case CMD_SET_THREAD: - return "THREAD"; + return "THREAD"; case CMD_SET_ARRAY_REF: - return "ARRAY_REF"; + return "ARRAY_REF"; case CMD_SET_EVENT_REQUEST: - return "EVENT_REQUEST"; + return "EVENT_REQUEST"; case CMD_SET_STACK_FRAME: - return "STACK_FRAME"; + return "STACK_FRAME"; case CMD_SET_APPDOMAIN: - return "APPDOMAIN"; + return "APPDOMAIN"; case CMD_SET_ASSEMBLY: - return "ASSEMBLY"; + return "ASSEMBLY"; case CMD_SET_METHOD: - return "METHOD"; + return "METHOD"; case CMD_SET_TYPE: - return "TYPE"; + return "TYPE"; case CMD_SET_MODULE: - return "MODULE"; + return "MODULE"; + case CMD_SET_FIELD: + return "FIELD"; case CMD_SET_EVENT: - return "EVENT"; + return "EVENT"; default: return ""; } } +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", + "SET_IP" +}; + +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* field_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; - } - default: + 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_FIELD: + cmds = field_cmds_str; + cmds_len = G_N_ELEMENTS (field_cmds_str); + break; + case CMD_SET_EVENT: + cmds = event_cmds_str; + cmds_len = G_N_ELEMENTS (event_cmds_str); + break; + default: + return NULL; } - return NULL; + if (command > 0 && command <= cmds_len) + return cmds [command - 1]; + else + return NULL; } static gboolean @@ -8370,8 +9226,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; @@ -8394,15 +9252,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; @@ -8433,6 +9293,9 @@ debugger_thread (void *arg) case CMD_SET_MODULE: err = module_commands (command, p, end, &buf); break; + case CMD_SET_FIELD: + err = field_commands (command, p, end, &buf); + break; case CMD_SET_TYPE: err = type_commands (command, p, end, &buf); break; @@ -8464,7 +9327,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; }