X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fdebugger-agent.c;h=67f6a3db79e4b7e635cce91236c5eb409a74db2b;hb=62b8aa3b7e8b6d7dda7191184324c980260458f6;hp=4b34c20f59cd9db215a487b5dad7589a2ef855e5;hpb=42079727b3aa6527843d7ac0d25f125964c1da88;p=mono.git diff --git a/mono/mini/debugger-agent.c b/mono/mini/debugger-agent.c index 4b34c20f59c..67f6a3db79e 100644 --- a/mono/mini/debugger-agent.c +++ b/mono/mini/debugger-agent.c @@ -41,6 +41,9 @@ #endif #ifdef HOST_WIN32 +#ifdef _MSC_VER +#include +#endif #include #ifdef __GNUC__ /* cygwin's headers do not seem to define these */ @@ -107,6 +110,11 @@ typedef struct MonoContext ctx; MonoDebugMethodJitInfo *jit; int flags; + /* + * Whenever ctx is set. This is FALSE for the last frame of running threads, since + * the frame can become invalid. + */ + gboolean has_ctx; } StackFrame; typedef struct @@ -123,6 +131,7 @@ typedef struct */ MonoMethod *method; gpointer *args; + guint32 suspend_count; } InvokeData; typedef struct { @@ -174,6 +183,31 @@ typedef struct { * Number of times this thread has been resumed using resume_thread (). */ guint32 resume_count; + + MonoInternalThread *thread; + + /* + * Information about the frame which transitioned to native code for running + * threads. + */ + StackFrameInfo async_last_frame; + + /* + * The context where the stack walk can be started for running threads. + */ + MonoContext async_ctx; + + gboolean has_async_ctx; + + /* + * The lmf where the stack walk can be started for running threads. + */ + gpointer async_lmf; + + /* + * The callee address of the last mono_runtime_invoke call + */ + gpointer invoke_addr; } DebuggerTlsData; /* @@ -183,7 +217,7 @@ typedef struct { #define HEADER_LENGTH 11 #define MAJOR_VERSION 2 -#define MINOR_VERSION 0 +#define MINOR_VERSION 1 typedef enum { CMD_SET_VM = 1, @@ -310,7 +344,8 @@ typedef enum { CMD_APPDOMAIN_GET_ASSEMBLIES = 3, CMD_APPDOMAIN_GET_ENTRY_ASSEMBLY = 4, CMD_APPDOMAIN_CREATE_STRING = 5, - CMD_APPDOMAIN_GET_CORLIB = 6 + CMD_APPDOMAIN_GET_CORLIB = 6, + CMD_APPDOMAIN_CREATE_BOXED_VALUE = 7, } CmdAppDomain; typedef enum { @@ -385,6 +420,7 @@ typedef struct { MonoClass *exc_class; /* For kind == MONO_KIND_EXCEPTION_ONLY */ MonoAssembly **assemblies; /* For kind == MONO_KIND_ASSEMBLY_ONLY */ } data; + gboolean caught, uncaught; /* For kind == MOD_KIND_EXCEPTION_ONLY */ } Modifier; typedef struct{ @@ -408,7 +444,21 @@ typedef struct { gpointer start_sp; MonoMethod *last_method; int last_line; -} MonoSingleStepReq; + /* Whenever single stepping is performed using start/stop_single_stepping () */ + gboolean global; + /* The list of breakpoints used to implement step-over */ + GSList *bps; +} SingleStepReq; + +/* + * Contains additional information for an event + */ +typedef struct { + /* For EVENT_KIND_EXCEPTION */ + MonoObject *exc; + MonoContext catch_ctx; + gboolean caught; +} EventInfo; /* Dummy structure used for the profiler callbacks */ typedef struct { @@ -483,7 +533,8 @@ static mono_mutex_t debugger_thread_exited_mutex; static DebuggerProfiler debugger_profiler; /* The single step request instance */ -static MonoSingleStepReq *ss_req = NULL; +static SingleStepReq *ss_req = NULL; +static gpointer ss_invoke_addr = NULL; #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED /* Number of single stepping operations in progress */ @@ -498,9 +549,9 @@ static void runtime_initialized (MonoProfiler *prof); static void runtime_shutdown (MonoProfiler *prof); -static void thread_startup (MonoProfiler *prof, gsize tid); +static void thread_startup (MonoProfiler *prof, intptr_t tid); -static void thread_end (MonoProfiler *prof, gsize tid); +static void thread_end (MonoProfiler *prof, intptr_t tid); static void appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result); @@ -536,8 +587,9 @@ static void ids_cleanup (void); static void suspend_init (void); -static ErrorCode ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req); -static void ss_stop (EventRequest *req); +static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls); +static ErrorCode ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req); +static void ss_destroy (SingleStepReq *req); static void start_debugger_thread (void); @@ -625,6 +677,8 @@ mono_debugger_agent_parse_options (char *options) } else if (strncmp (arg, "onthrow=", 8) == 0) { /* We support multiple onthrow= options */ agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup (arg + 8)); + } else if (strncmp (arg, "onthrow", 7) == 0) { + agent_config.onthrow = g_slist_append (agent_config.onthrow, g_strdup ("")); } else if (strncmp (arg, "help", 4) == 0) { print_usage (); exit (0); @@ -675,7 +729,7 @@ mono_debugger_agent_init (void) mono_profiler_install ((MonoProfiler*)&debugger_profiler, runtime_shutdown); mono_profiler_set_events (MONO_PROFILE_APPDOMAIN_EVENTS | MONO_PROFILE_THREADS | MONO_PROFILE_ASSEMBLY_EVENTS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_METHOD_EVENTS); mono_profiler_install_runtime_initialized (runtime_initialized); - mono_profiler_install_appdomain (NULL, appdomain_load, appdomain_unload, NULL); + mono_profiler_install_appdomain (NULL, appdomain_load, NULL, appdomain_unload); mono_profiler_install_thread (thread_startup, thread_end); mono_profiler_install_assembly (NULL, assembly_load, assembly_unload, NULL); mono_profiler_install_jit_end (jit_end); @@ -802,8 +856,17 @@ mono_debugger_agent_cleanup (void) //WaitForSingleObject (debugger_thread_handle, INFINITE); if (GetCurrentThreadId () != debugger_thread_id) { mono_mutex_lock (&debugger_thread_exited_mutex); - if (!debugger_thread_exited) + if (!debugger_thread_exited) { +#ifdef HOST_WIN32 + if (WAIT_TIMEOUT == WaitForSingleObject(debugger_thread_exited_cond, 0)) { + mono_mutex_unlock (&debugger_thread_exited_mutex); + Sleep(0); + mono_mutex_lock (&debugger_thread_exited_mutex); + } +#else mono_cond_wait (&debugger_thread_exited_cond, &debugger_thread_exited_mutex); +#endif + } mono_mutex_unlock (&debugger_thread_exited_mutex); } @@ -821,6 +884,24 @@ mono_debugger_agent_cleanup (void) mono_cond_destroy (&debugger_thread_exited_cond); } +/* + * recv_length: + * + * recv() + handle incomplete reads and EINTR + */ +static int +recv_length (int fd, void *buf, int len, int flags) +{ + int res; + int total = 0; + + do { + res = recv (fd, (char *) buf + total, len - total, flags); + if (res > 0) + total += res; + } while ((res > 0 && total < len) || (res == -1 && errno == EINTR)); + return total; +} /* * transport_connect: * @@ -904,7 +985,15 @@ transport_connect (const char *host, int port) break; } +#ifndef HOST_WIN32 + /* + * this function is not present on win2000 which we still support, and the + * workaround described here: + * http://msdn.microsoft.com/en-us/library/ms737931(VS.85).aspx + * only works with MSVC. + */ freeaddrinfo (result); +#endif } DEBUG (1, fprintf (log_file, "Listening on %s:%d (timeout=%d ms)...\n", host, port, agent_config.timeout)); @@ -948,7 +1037,10 @@ transport_connect (const char *host, int port) conn_fd = sfd; +#ifndef HOST_WIN32 + /* See the comment above */ freeaddrinfo (result); +#endif if (rp == 0) { fprintf (stderr, "debugger-agent: Unable to connect to %s:%d\n", host, port); @@ -958,11 +1050,13 @@ transport_connect (const char *host, int port) /* Write handshake message */ sprintf (handshake_msg, "DWP-Handshake"); - res = send (conn_fd, handshake_msg, strlen (handshake_msg), 0); + do { + res = send (conn_fd, handshake_msg, strlen (handshake_msg), 0); + } while (res == -1 && errno == EINTR); g_assert (res != -1); /* Read answer */ - res = recv (conn_fd, buf, strlen (handshake_msg), 0); + res = recv_length (conn_fd, buf, strlen (handshake_msg), 0); if ((res != strlen (handshake_msg)) || (memcmp (buf, handshake_msg, strlen (handshake_msg) != 0))) { fprintf (stderr, "debugger-agent: DWP handshake failed.\n"); exit (1); @@ -988,7 +1082,9 @@ transport_send (guint8 *data, int len) { int res; - res = send (conn_fd, data, len, 0); + do { + res = send (conn_fd, data, len, 0); + } while (res == -1 && errno == EINTR); if (res != len) return FALSE; else @@ -1666,6 +1762,36 @@ suspend_init (void) MONO_SEM_INIT (&suspend_sem, 0); } +typedef struct +{ + StackFrameInfo last_frame; + gboolean last_frame_set; + MonoContext ctx; + gpointer lmf; +} GetLastFrameUserData; + +static gboolean +get_last_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) +{ + GetLastFrameUserData *data = user_data; + + if (info->type == FRAME_TYPE_MANAGED_TO_NATIVE) + return FALSE; + + if (!data->last_frame_set) { + /* Store the last frame */ + memcpy (&data->last_frame, info, sizeof (StackFrameInfo)); + data->last_frame_set = TRUE; + return FALSE; + } else { + /* Store the context/lmf for the frame above the last frame */ + memcpy (&data->ctx, ctx, sizeof (MonoContext)); + data->lmf = info->lmf; + + return TRUE; + } +} + /* * mono_debugger_agent_thread_interrupt: * @@ -1684,6 +1810,17 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji) if (!tls) return FALSE; + /* + * OSX can (and will) coalesce signals, so sending multiple pthread_kills does not + * guarantee the signal handler will be called that many times. Instead of tracking + * interrupt_count on osx, we use this as a boolean flag to determine if a interrupt + * has been requested that hasn't been handled yet, otherwise we can have threads + * refuse to die when VM_EXIT is called + */ +#if defined(__APPLE__) + if (InterlockedCompareExchange (&tls->interrupt_count, 0, 1) == 0) + return FALSE; +#else /* * We use interrupt_count to determine whenever this interrupt should be processed * by us or the normal interrupt processing code in the signal handler. @@ -1694,6 +1831,7 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji) return FALSE; InterlockedDecrement (&tls->interrupt_count); +#endif // FIXME: Races when the thread leaves managed code before hitting a single step // event. @@ -1711,18 +1849,44 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji) */ if (!tls->suspended && !tls->suspending) { MonoContext ctx; + GetLastFrameUserData data; - /* - * FIXME: This is dangerous as the thread is not really suspended, but - * without this, we can't print stack traces for threads executing - * native code. - * Maybe do a stack walk now, and save its result ? - */ - mono_arch_sigctx_to_monoctx (sigctx, &ctx); // FIXME: printf is not signal safe, but this is only used during // debugger debugging DEBUG (1, printf ("[%p] Received interrupt while at %p, treating as suspended.\n", (gpointer)GetCurrentThreadId (), mono_arch_ip_from_context (sigctx))); - save_thread_context (&ctx); + //save_thread_context (&ctx); + + if (!tls->thread) + /* Already terminated */ + return TRUE; + + /* + * 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 + * the thread is still running, so it might return to managed code, + * making these invalid. + * So we start a stack walk and save the first frame, along with the + * parent frame's ctx+lmf. This (hopefully) works because the thread will be + * suspended when it returns to managed code, so the parent's ctx should + * remain valid. + */ + data.last_frame_set = FALSE; + if (sigctx) { + mono_arch_sigctx_to_monoctx (sigctx, &ctx); + mono_jit_walk_stack_from_ctx_in_thread (get_last_frame, mono_domain_get (), &ctx, FALSE, tls->thread, mono_get_lmf (), &data); + } + if (data.last_frame_set) { + memcpy (&tls->async_last_frame, &data.last_frame, sizeof (StackFrameInfo)); + memcpy (&tls->async_ctx, &data.ctx, sizeof (MonoContext)); + tls->async_lmf = data.lmf; + tls->has_async_ctx = TRUE; + tls->domain = mono_domain_get (); + memcpy (&tls->ctx, &ctx, sizeof (MonoContext)); + } else { + tls->has_async_ctx = FALSE; + } + + mono_memory_barrier (); tls->suspended = TRUE; MONO_SEM_POST (&suspend_sem); @@ -1739,6 +1903,20 @@ static void CALLBACK notify_thread_apc (ULONG_PTR param) } #endif /* HOST_WIN32 */ +/* + * reset_native_thread_suspend_state: + * + * Reset the suspended flag 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) + tls->suspended = FALSE; +} + /* * notify_thread: * @@ -1751,20 +1929,35 @@ notify_thread (gpointer key, gpointer value, gpointer user_data) DebuggerTlsData *tls = value; gsize tid = thread->tid; - if (GetCurrentThreadId () != tid) { - DEBUG(1, fprintf (log_file, "[%p] Interrupting %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid)); - /* - * Maybe we could use the normal interrupt infrastructure, but that does a lot - * of things like breaking waits etc. which we don't want. - */ - InterlockedIncrement (&tls->interrupt_count); - /* This is _not_ equivalent to ves_icall_System_Threading_Thread_Abort () */ + if (GetCurrentThreadId () == tid || tls->terminated) + return; + + DEBUG(1, fprintf (log_file, "[%p] Interrupting %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid)); + + /* + * OSX can (and will) coalesce signals, so sending multiple pthread_kills does not + * guarantee the signal handler will be called that many times. Instead of tracking + * interrupt_count on osx, we use this as a boolean flag to determine if a interrupt + * has been requested that hasn't been handled yet, otherwise we can have threads + * refuse to die when VM_EXIT is called + */ +#if defined(__APPLE__) + if (InterlockedCompareExchange (&tls->interrupt_count, 1, 0) == 1) + return; +#else + /* + * Maybe we could use the normal interrupt infrastructure, but that does a lot + * of things like breaking waits etc. which we don't want. + */ + InterlockedIncrement (&tls->interrupt_count); +#endif + + /* This is _not_ equivalent to ves_icall_System_Threading_Thread_Abort () */ #ifdef HOST_WIN32 - QueueUserAPC (notify_thread_apc, thread->handle, NULL); + QueueUserAPC (notify_thread_apc, thread->handle, NULL); #else - pthread_kill ((pthread_t) tid, mono_thread_get_abort_signal ()); + pthread_kill ((pthread_t) tid, mono_thread_get_abort_signal ()); #endif - } } static void @@ -1792,7 +1985,7 @@ process_suspend (DebuggerTlsData *tls, MonoContext *ctx) return; } - ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip); + 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"))) @@ -1856,6 +2049,7 @@ resume_vm (void) if (suspend_count == 0) { // FIXME: Is it safe to call this inside the lock ? stop_single_stepping (); + mono_g_hash_table_foreach (thread_to_tls, reset_native_thread_suspend_state, NULL); } /* Signal this even when suspend_count > 0, since some threads might have resume_count > 0 */ @@ -1892,7 +2086,7 @@ resume_thread (MonoInternalThread *thread) DEBUG(1, fprintf (log_file, "[%p] Resuming thread...\n", (gpointer)(gssize)thread->tid)); - tls->resume_count ++; + tls->resume_count += suspend_count; /* * Signal suspend_count without decreasing suspend_count, the threads will wake up @@ -1954,10 +2148,10 @@ suspend_current (void) mono_mutex_lock (&suspend_mutex); tls->suspending = FALSE; + tls->really_suspended = TRUE; if (!tls->suspended) { tls->suspended = TRUE; - tls->really_suspended = TRUE; MONO_SEM_POST (&suspend_sem); } @@ -1999,6 +2193,7 @@ suspend_current (void) /* The frame info becomes invalid after a resume */ tls->has_context = FALSE; + tls->has_async_ctx = FALSE; invalidate_frames (NULL); } @@ -2066,6 +2261,60 @@ is_suspended (void) return count_threads_to_wait_for () == 0; } +/* + * find_seq_point_for_native_offset: + * + * Find the sequence point corresponding to the native offset NATIVE_OFFSET, which + * should be the location of a sequence point. + */ +static SeqPoint* +find_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info) +{ + MonoSeqPointInfo *seq_points; + int i; + + mono_domain_lock (domain); + seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method); + mono_domain_unlock (domain); + g_assert (seq_points); + + *info = seq_points; + + for (i = 0; i < seq_points->len; ++i) { + if (seq_points->seq_points [i].native_offset == native_offset) + return &seq_points->seq_points [i]; + } + + return NULL; +} + +/* + * find_seq_point: + * + * Find the sequence point corresponding to the IL offset IL_OFFSET, which + * should be the location of a sequence point. + */ +static SeqPoint* +find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info) +{ + MonoSeqPointInfo *seq_points; + int i; + + mono_domain_lock (domain); + seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method); + mono_domain_unlock (domain); + g_assert (seq_points); + + *info = seq_points; + + for (i = 0; i < seq_points->len; ++i) { + if (seq_points->seq_points [i].il_offset == il_offset) + return &seq_points->seq_points [i]; + } + + return NULL; +} + /* * compute_il_offset: * @@ -2077,7 +2326,7 @@ is_suspended (void) static gint32 compute_il_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset) { - GPtrArray *seq_points; + MonoSeqPointInfo *seq_points; int i, last_il_offset, seq_il_offset, seq_native_offset; mono_domain_lock (domain); @@ -2088,9 +2337,9 @@ compute_il_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset) last_il_offset = -1; /* Find the sequence point */ - for (i = 0; i < seq_points->len; i += 2) { - seq_il_offset = GPOINTER_TO_UINT (g_ptr_array_index (seq_points, i)); - seq_native_offset = GPOINTER_TO_UINT (g_ptr_array_index (seq_points, i + 1)); + for (i = 0; i < seq_points->len; ++i) { + seq_il_offset = seq_points->seq_points [i].il_offset; + seq_native_offset = seq_points->seq_points [i].native_offset; if (seq_native_offset > native_offset) break; @@ -2149,7 +2398,10 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) frame = g_new0 (StackFrame, 1); frame->method = method; frame->il_offset = info->il_offset; - frame->ctx = *ctx; + if (ctx) { + frame->ctx = *ctx; + frame->has_ctx = TRUE; + } frame->domain = info->domain; ud->frames = g_slist_append (ud->frames, frame); @@ -2173,7 +2425,14 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls) user_data.tls = tls; user_data.frames = NULL; - if (tls->has_context) { + if (tls->terminated) { + tls->frame_count = 0; + return; + } if (!tls->really_suspended && tls->has_async_ctx) { + /* Have to use the state saved by the signal handler */ + process_frame (&tls->async_last_frame, NULL, &user_data); + mono_jit_walk_stack_from_ctx_in_thread (process_frame, tls->domain, &tls->async_ctx, FALSE, thread, tls->async_lmf, &user_data); + } else if (tls->has_context) { mono_jit_walk_stack_from_ctx_in_thread (process_frame, tls->domain, &tls->ctx, FALSE, thread, tls->lmf, &user_data); } else { // FIXME: @@ -2228,7 +2487,7 @@ compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls) * LOCKING: Assumes the loader lock is held. */ static GSList* -create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, MonoException *exc, int *suspend_policy) +create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, EventInfo *ei, int *suspend_policy) { int i, j; GSList *events = NULL; @@ -2262,8 +2521,12 @@ create_event_list (EventKind event, GPtrArray *reqs, MonoJitInfo *ji, MonoExcept } else if (mod->kind == MOD_KIND_THREAD_ONLY) { if (mod->data.thread != mono_thread_internal_current ()) filtered = TRUE; - } else if (mod->kind == MOD_KIND_EXCEPTION_ONLY && exc) { - if (mod->data.exc_class && !mono_class_is_assignable_from (mod->data.exc_class, exc->object.vtable->klass)) + } 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)) + filtered = TRUE; + if (ei->caught && !mod->caught) + filtered = TRUE; + if (!ei->caught && !mod->uncaught) filtered = TRUE; } else if (mod->kind == MOD_KIND_ASSEMBLY_ONLY && ji) { int k; @@ -2404,9 +2667,11 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx break; case EVENT_KIND_VM_DEATH: break; - case EVENT_KIND_EXCEPTION: - buffer_add_objid (&buf, (MonoObject*)arg); + case EVENT_KIND_EXCEPTION: { + EventInfo *ei = arg; + buffer_add_objid (&buf, ei->exc); break; + } default: g_assert_not_reached (); } @@ -2490,7 +2755,7 @@ runtime_shutdown (MonoProfiler *prof) } static void -thread_startup (MonoProfiler *prof, gsize tid) +thread_startup (MonoProfiler *prof, intptr_t tid) { MonoInternalThread *thread = mono_thread_internal_current (); MonoInternalThread *old_thread; @@ -2531,6 +2796,8 @@ thread_startup (MonoProfiler *prof, gsize tid) // FIXME: Free this somewhere tls = g_new0 (DebuggerTlsData, 1); tls->resume_event = CreateEvent (NULL, FALSE, FALSE, NULL); + MONO_GC_REGISTER_ROOT (tls->thread); + tls->thread = thread; TlsSetValue (debugger_tls_id, tls); DEBUG (1, fprintf (log_file, "[%p] Thread started, obj=%p, tls=%p.\n", (gpointer)tid, thread, tls)); @@ -2550,7 +2817,7 @@ thread_startup (MonoProfiler *prof, gsize tid) } static void -thread_end (MonoProfiler *prof, gsize tid) +thread_end (MonoProfiler *prof, intptr_t tid) { MonoInternalThread *thread; DebuggerTlsData *tls = NULL; @@ -2563,6 +2830,8 @@ thread_end (MonoProfiler *prof, gsize tid) tls->terminated = TRUE; mono_g_hash_table_remove (tid_to_thread_obj, (gpointer)tid); /* Can't remove from tid_to_thread, as that would defeat the check in thread_start () */ + MONO_GC_UNREGISTER_ROOT (tls->thread); + tls->thread = NULL; } mono_loader_unlock (); @@ -2603,6 +2872,22 @@ assembly_unload (MonoProfiler *prof, MonoAssembly *assembly) static void start_runtime_invoke (MonoProfiler *prof, MonoMethod *method) { +#if defined(HOST_WIN32) && !defined(__GNUC__) + gpointer stackptr = ((guint64)_AddressOfReturnAddress () - sizeof (void*)); +#else + gpointer stackptr = __builtin_frame_address (1); +#endif + MonoInternalThread *thread = mono_thread_internal_current (); + DebuggerTlsData *tls; + + mono_loader_lock (); + + tls = mono_g_hash_table_lookup (thread_to_tls, thread); + /* Could be the debugger thread with assembly/type load hooks */ + if (tls) + tls->invoke_addr = stackptr; + + mono_loader_unlock (); } static void @@ -2612,10 +2897,10 @@ end_runtime_invoke (MonoProfiler *prof, MonoMethod *method) #if defined(HOST_WIN32) && !defined(__GNUC__) gpointer stackptr = ((guint64)_AddressOfReturnAddress () - sizeof (void*)); #else - gpointer stackptr = __builtin_frame_address (0); + gpointer stackptr = __builtin_frame_address (1); #endif - if (ss_req == NULL || ss_req->start_sp > stackptr || ss_req->thread != mono_thread_internal_current ()) + if (ss_req == NULL || stackptr != ss_invoke_addr || ss_req->thread != mono_thread_internal_current ()) return; /* @@ -2623,18 +2908,19 @@ end_runtime_invoke (MonoProfiler *prof, MonoMethod *method) * a step out, it may return to native code, and thus never end. */ mono_loader_lock (); + ss_invoke_addr = NULL; + for (i = 0; i < event_requests->len; ++i) { EventRequest *req = g_ptr_array_index (event_requests, i); if (req->event_kind == EVENT_KIND_STEP) { - ss_stop (req); - g_ptr_array_remove_index_fast (event_requests, i); - g_free (req); + ss_destroy (req->info); + g_ptr_array_remove_index_fast (event_requests, i); + g_free (req); break; } } mono_loader_unlock (); - } static void @@ -2749,16 +3035,16 @@ breakpoints_cleanup (void) * JI. */ static void -insert_breakpoint (GPtrArray *seq_points, MonoJitInfo *ji, MonoBreakpoint *bp) +insert_breakpoint (MonoSeqPointInfo *seq_points, MonoJitInfo *ji, MonoBreakpoint *bp) { int i, count; gint32 il_offset, native_offset; BreakpointInstance *inst; native_offset = 0; - for (i = 0; i < seq_points->len; i += 2) { - il_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i)); - native_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i + 1)); + for (i = 0; i < seq_points->len; ++i) { + il_offset = seq_points->seq_points [i].il_offset; + native_offset = seq_points->seq_points [i].native_offset; if (il_offset == bp->il_offset) break; @@ -2822,7 +3108,7 @@ static void add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji) { int i; - GPtrArray *seq_points; + MonoSeqPointInfo *seq_points; MonoDomain *domain; if (!breakpoints) @@ -2852,7 +3138,7 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji) } static void -set_bp_in_method (MonoDomain *domain, MonoMethod *method, GPtrArray *seq_points, MonoBreakpoint *bp) +set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_points, MonoBreakpoint *bp) { gpointer code; MonoJitInfo *ji; @@ -2874,7 +3160,7 @@ static void set_bp_in_method_cb (gpointer key, gpointer value, gpointer user_data) { MonoMethod *method = key; - GPtrArray *seq_points = value; + MonoSeqPointInfo *seq_points = value; MonoBreakpoint *bp = user_data; MonoDomain *domain = mono_domain_get (); @@ -2900,7 +3186,7 @@ set_bp_in_method_cb (gpointer key, gpointer value, gpointer user_data) static MonoBreakpoint* set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req) { - GPtrArray *seq_points; + MonoSeqPointInfo *seq_points; MonoDomain *domain; MonoBreakpoint *bp; @@ -2917,7 +3203,7 @@ set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req) bp->req = req; bp->children = g_ptr_array_new (); - DEBUG(1, fprintf (log_file, "[dbg] Setting breakpoint at %s:0x%x.\n", mono_method_full_name (method, TRUE), (int)il_offset)); + DEBUG(1, fprintf (log_file, "[dbg] Setting %sbreakpoint at %s:0x%x.\n", (req->event_kind == EVENT_KIND_STEP) ? "single step " : "", method ? mono_method_full_name (method, TRUE) : "", (int)il_offset)); domain = mono_domain_get (); mono_domain_lock (domain); @@ -2977,14 +3263,14 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx) guint32 native_offset; MonoBreakpoint *bp; BreakpointInstance *inst; - GPtrArray *reqs; - GSList *events = NULL; + GPtrArray *bp_reqs, *ss_reqs_orig, *ss_reqs; + GSList *bp_events = NULL, *ss_events = NULL, *enter_leave_events = NULL; EventKind kind = EVENT_KIND_BREAKPOINT; // FIXME: Speed this up orig_ip = ip = MONO_CONTEXT_GET_IP (ctx); - ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip); + ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL); g_assert (ji); g_assert (ji->method); @@ -3004,7 +3290,9 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx) if (ji->method->wrapper_type || tls->disable_breakpoints) return; - reqs = g_ptr_array_new (); + 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, offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, native_offset)); @@ -3019,12 +3307,17 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx) for (j = 0; j < bp->children->len; ++j) { inst = g_ptr_array_index (bp->children, j); - if (inst->ji == ji && inst->native_offset == native_offset) - g_ptr_array_add (reqs, bp->req); + if (inst->ji == ji && inst->native_offset == native_offset) { + if (bp->req->event_kind == EVENT_KIND_STEP) { + g_ptr_array_add (ss_reqs_orig, bp->req); + } else { + g_ptr_array_add (bp_reqs, bp->req); + } + } } } - if (reqs->len == 0) { - GPtrArray *seq_points; + if (bp_reqs->len == 0 && ss_reqs_orig->len == 0) { + MonoSeqPointInfo *seq_points; int seq_il_offset, seq_native_offset; MonoDomain *domain = mono_domain_get (); @@ -3039,9 +3332,9 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx) } g_assert (seq_points); - for (i = 0; i < seq_points->len; i += 2) { - seq_il_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i)); - seq_native_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i + 1)); + for (i = 0; i < seq_points->len; ++i) { + seq_il_offset = seq_points->seq_points [i].il_offset; + seq_native_offset = seq_points->seq_points [i].native_offset; if (native_offset == seq_native_offset) { if (seq_il_offset == METHOD_ENTRY_IL_OFFSET) @@ -3052,18 +3345,68 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx) } } } - - if (reqs->len > 0) - events = create_event_list (EVENT_KIND_BREAKPOINT, reqs, ji, NULL, &suspend_policy); - else if (kind != EVENT_KIND_BREAKPOINT) - events = create_event_list (kind, NULL, ji, NULL, &suspend_policy); - g_ptr_array_free (reqs, TRUE); + /* Process single step requests */ + for (i = 0; i < ss_reqs_orig->len; ++i) { + EventRequest *req = g_ptr_array_index (ss_reqs_orig, i); + SingleStepReq *ss_req = bp->req->info; + gboolean hit = TRUE; + MonoSeqPointInfo *info; + SeqPoint *sp; + + sp = find_seq_point_for_native_offset (mono_domain_get (), ji->method, native_offset, &info); + g_assert (sp); + + 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 */ + hit = FALSE; + + if (loc) { + ss_req->last_method = ji->method; + ss_req->last_line = loc->row; + mono_debug_free_source_location (loc); + } + } + + 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, NULL); + } + + if (ss_reqs->len > 0) + ss_events = create_event_list (EVENT_KIND_STEP, ss_reqs, ji, NULL, &suspend_policy); + if (bp_reqs->len > 0) + bp_events = create_event_list (EVENT_KIND_BREAKPOINT, bp_reqs, ji, NULL, &suspend_policy); + if (kind != EVENT_KIND_BREAKPOINT) + enter_leave_events = create_event_list (kind, NULL, ji, NULL, &suspend_policy); mono_loader_unlock (); - if (events) - process_event (kind, ji->method, 0, ctx, events, suspend_policy); + g_ptr_array_free (bp_reqs, TRUE); + g_ptr_array_free (ss_reqs, TRUE); + + /* + * FIXME: The first event will suspend, so the second will only be sent after the + * resume. + */ + if (ss_events) + process_event (EVENT_KIND_STEP, ji->method, 0, ctx, ss_events, suspend_policy); + if (bp_events) + process_event (kind, ji->method, 0, ctx, bp_events, suspend_policy); + if (enter_leave_events) + process_event (kind, ji->method, 0, ctx, enter_leave_events, suspend_policy); } static void @@ -3104,8 +3447,11 @@ resume_from_signal_handler (void *sigctx, void *func) mono_arch_sigctx_to_monoctx (sigctx, &ctx); memcpy (&tls->handler_ctx, &ctx, sizeof (MonoContext)); MONO_CONTEXT_SET_IP (&ctx, func); - mono_arch_monoctx_to_sigctx (&ctx, sigctx); + +#ifdef PPC_USES_FUNCTION_DESCRIPTOR + mono_ppc_set_func_into_sigctx (sigctx, func); +#endif } void @@ -3127,7 +3473,7 @@ process_single_step_inner (DebuggerTlsData *tls, MonoContext *ctx) guint8 *ip; GPtrArray *reqs; int il_offset, suspend_policy; - MonoDomain *domain = mono_domain_get (); + MonoDomain *domain; GSList *events; // FIXME: Speed this up @@ -3152,7 +3498,7 @@ process_single_step_inner (DebuggerTlsData *tls, MonoContext *ctx) if (log_level > 0) { const char *depth = NULL; - ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip); + ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain); switch (ss_req->depth) { case STEP_DEPTH_OVER: @@ -3189,7 +3535,7 @@ process_single_step_inner (DebuggerTlsData *tls, MonoContext *ctx) ss_req->last_sp = MONO_CONTEXT_GET_SP (ctx); } - ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip); + ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain); g_assert (ji); g_assert (ji->method); @@ -3335,6 +3681,17 @@ start_single_stepping (void) if (val == 1) mono_arch_start_single_stepping (); + + if (ss_req != NULL && ss_invoke_addr == NULL) { + DebuggerTlsData *tls; + + mono_loader_lock (); + + tls = mono_g_hash_table_lookup (thread_to_tls, ss_req->thread); + ss_invoke_addr = tls->invoke_addr; + + mono_loader_unlock (); + } #else g_assert_not_reached (); #endif @@ -3353,13 +3710,101 @@ stop_single_stepping (void) #endif } +/* + * ss_stop: + * + * Stop the single stepping operation given by SS_REQ. + */ +static void +ss_stop (SingleStepReq *ss_req) +{ + gboolean use_bps = FALSE; + + if (ss_req->bps) { + GSList *l; + + use_bps = TRUE; + + for (l = ss_req->bps; l; l = l->next) { + clear_breakpoint (l->data); + } + g_slist_free (ss_req->bps); + ss_req->bps = NULL; + } + + if (ss_req->global) { + stop_single_stepping (); + ss_req->global = FALSE; + } +} + +/* + * ss_start: + * + * Start the single stepping operation given by SS_REQ from the sequence point SP. + */ +static void +ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls) +{ + gboolean use_bp = FALSE; + int i, frame_index; + SeqPoint *next_sp; + MonoBreakpoint *bp; + + /* Stop the previous operation */ + ss_stop (ss_req); + + /* + * Implement single stepping using breakpoints if possible. + */ + if (ss_req->depth == STEP_DEPTH_OVER) { + frame_index = 1; + /* + * Find the first sequence point in the current or in a previous frame which + * is not the last in its method. + */ + while (sp && sp->next_len == 0) { + sp = NULL; + if (tls && frame_index < tls->frame_count) { + StackFrame *frame = tls->frames [frame_index]; + + method = frame->method; + if (frame->il_offset != -1) { + sp = find_seq_point (frame->domain, frame->method, frame->il_offset, &info); + } + frame_index ++; + } + } + + if (sp && sp->next_len > 0) { + use_bp = TRUE; + for (i = 0; i < sp->next_len; ++i) { + next_sp = &info->seq_points [sp->next [i]]; + + bp = set_breakpoint (method, next_sp->il_offset, ss_req->req); + ss_req->bps = g_slist_append (ss_req->bps, bp); + } + } + } + + if (!ss_req->bps) { + ss_req->global = TRUE; + start_single_stepping (); + } else { + ss_req->global = FALSE; + } +} + /* * Start single stepping of thread THREAD */ static ErrorCode -ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req) +ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req) { DebuggerTlsData *tls; + MonoSeqPointInfo *info; + SeqPoint *sp = NULL; + MonoMethod *method = NULL; if (suspend_count == 0) return ERR_NOT_SUSPENDED; @@ -3372,7 +3817,7 @@ ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventReque return ERR_NOT_IMPLEMENTED; } - ss_req = g_new0 (MonoSingleStepReq, 1); + ss_req = g_new0 (SingleStepReq, 1); ss_req->req = req; ss_req->thread = thread; ss_req->size = size; @@ -3410,33 +3855,64 @@ ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventReque } } - start_single_stepping (); + if (ss_req->depth == STEP_DEPTH_OVER) { + StackFrame *frame; + + compute_frame_info (thread, tls); + + g_assert (tls->frame_count); + frame = tls->frames [0]; + + if (frame->il_offset != -1) { + /* FIXME: Sort the table and use a binary search */ + sp = find_seq_point (frame->domain, frame->method, frame->il_offset, &info); + g_assert (sp); + method = frame->method; + } + } + + ss_start (ss_req, method, sp, info, NULL, tls); return 0; } static void -ss_stop (EventRequest *req) +ss_destroy (SingleStepReq *req) { // FIXME: Locking - g_assert (ss_req); - g_assert (ss_req->req == req); + g_assert (ss_req == req); + + ss_stop (ss_req); g_free (ss_req); ss_req = NULL; - - stop_single_stepping (); } void -mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *ctx) +mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx, + MonoContext *catch_ctx) { int suspend_policy; GSList *events; MonoJitInfo *ji; + EventInfo ei; + + memset (&ei, 0, sizeof (EventInfo)); /* Just-In-Time debugging */ - if (agent_config.onthrow && !inited) { + if (!catch_ctx) { + if (agent_config.onuncaught && !inited) { + finish_agent_init (FALSE); + + /* + * Send an unsolicited EXCEPTION event with a dummy request id. + */ + events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff)); + ei.exc = (MonoObject*)exc; + process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, SUSPEND_POLICY_ALL); + return; + } + } else if (agent_config.onthrow && !inited) { GSList *l; gboolean found = FALSE; @@ -3444,7 +3920,7 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *ctx) char *ex_type = l->data; char *f = mono_type_full_name (&exc->object.vtable->klass->byval_arg); - if (!strcmp (ex_type, f)) + if (!strcmp (ex_type, "") || !strcmp (ex_type, f)) found = TRUE; g_free (f); @@ -3457,7 +3933,8 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *ctx) * Send an unsolicited EXCEPTION event with a dummy request id. */ events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff)); - process_event (EVENT_KIND_EXCEPTION, exc, 0, ctx, events, SUSPEND_POLICY_ALL); + ei.exc = (MonoObject*)exc; + process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, SUSPEND_POLICY_ALL); return; } } @@ -3465,39 +3942,28 @@ mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *ctx) if (!inited) return; - ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (ctx), NULL); + ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (throw_ctx), NULL); + + ei.exc = (MonoObject*)exc; + ei.caught = catch_ctx != NULL; mono_loader_lock (); - events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, exc, &suspend_policy); + events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, &ei, &suspend_policy); mono_loader_unlock (); - process_event (EVENT_KIND_EXCEPTION, exc, 0, ctx, events, suspend_policy); -} - -void -mono_debugger_agent_handle_unhandled_exception (MonoException *exc, MonoContext *ctx) -{ - GSList *events; - - if (!agent_config.onuncaught) - return; - - finish_agent_init (FALSE); - - /* - * Send an unsolicited EXCEPTION event with a dummy request id. - */ - events = g_slist_append (NULL, GUINT_TO_POINTER (0xffffff)); - process_event (EVENT_KIND_EXCEPTION, exc, 0, ctx, events, SUSPEND_POLICY_ALL); + process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, suspend_policy); } /* - * buffer_add_value: + * buffer_add_value_full: * * Add the encoding of the value at ADDR described by T to the buffer. + * AS_VTYPE determines whenever to treat primitive types as primitive types or + * vtypes. */ static void -buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain) +buffer_add_value_full (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain, + gboolean as_vtype) { MonoObject *obj; @@ -3506,6 +3972,30 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain) addr = *(void**)addr; } + if (as_vtype) { + switch (t->type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R8: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + goto handle_vtype; + break; + default: + break; + } + } + switch (t->type) { case MONO_TYPE_VOID: buffer_add_byte (buf, t->type); @@ -3536,6 +4026,8 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain) break; case MONO_TYPE_I: case MONO_TYPE_U: + /* Treat it as a vtype */ + goto handle_vtype; case MONO_TYPE_PTR: { gssize val = *(gssize*)addr; @@ -3596,7 +4088,7 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain) continue; if (mono_field_is_deleted (f)) continue; - buffer_add_value (buf, f->type, (guint8*)addr + f->offset - sizeof (MonoObject), domain); + buffer_add_value_full (buf, f->type, (guint8*)addr + f->offset - sizeof (MonoObject), domain, FALSE); } break; } @@ -3612,14 +4104,26 @@ buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain) } } +static void +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) { int err; int type = decode_byte (buf, &buf, limit); - if (type != t->type && !MONO_TYPE_IS_REFERENCE (t)) { - DEBUG(1, fprintf (log_file, "Expected value of type %d, got %d.\n", t->type, type)); + if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) && + !(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) && + !(t->type == MONO_TYPE_U && type == MONO_TYPE_VALUETYPE) && + !(t->type == MONO_TYPE_PTR && type == MONO_TYPE_I8) && + !(t->type == MONO_TYPE_GENERICINST && type == MONO_TYPE_VALUETYPE)) { + 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; } @@ -3660,6 +4164,25 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 case MONO_TYPE_R8: *(guint64*)addr = decode_long (buf, &buf, limit); break; + case MONO_TYPE_PTR: + /* We send these as I8, so we get them back as such */ + g_assert (type == MONO_TYPE_I8); + *(gssize*)addr = decode_long (buf, &buf, limit); + break; + case MONO_TYPE_GENERICINST: + if (MONO_TYPE_ISSTRUCT (t)) { + /* The client sends these as a valuetype */ + goto handle_vtype; + } else { + goto handle_ref; + } + break; + case MONO_TYPE_I: + case MONO_TYPE_U: + /* We send these as vtypes, so we get them back as such */ + g_assert (type == MONO_TYPE_VALUETYPE); + /* Fall through */ + handle_vtype: case MONO_TYPE_VALUETYPE: { gboolean is_enum = decode_byte (buf, &buf, limit); MonoClass *klass; @@ -3692,6 +4215,7 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 g_assert (nfields == 0); break; } + handle_ref: default: if (MONO_TYPE_IS_REFERENCE (t)) { if (type == MONO_TYPE_OBJECT) { @@ -3726,7 +4250,7 @@ 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) +add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype) { guint32 flags; int reg; @@ -3740,7 +4264,7 @@ add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, Mono case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: reg_val = mono_arch_context_get_int_reg (ctx, reg); - buffer_add_value (buf, t, ®_val, domain); + buffer_add_value_full (buf, t, ®_val, domain, as_vtype); break; case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET: addr = mono_arch_context_get_int_reg (ctx, reg); @@ -3748,7 +4272,7 @@ add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, Mono //printf ("[R%d+%d] = %p\n", reg, var->offset, addr); - buffer_add_value (buf, t, addr, domain); + buffer_add_value_full (buf, t, addr, domain, as_vtype); break; case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD: NOT_IMPLEMENTED; @@ -3808,7 +4332,11 @@ clear_event_request (int req_id, int etype) if (req->event_kind == EVENT_KIND_BREAKPOINT) clear_breakpoint (req->info); if (req->event_kind == EVENT_KIND_STEP) - ss_stop (req); + ss_destroy (req->info); + if (req->event_kind == EVENT_KIND_METHOD_ENTRY) + clear_breakpoint (req->info); + if (req->event_kind == EVENT_KIND_METHOD_EXIT) + clear_breakpoint (req->info); g_ptr_array_remove_index_fast (event_requests, i); g_free (req); break; @@ -3862,9 +4390,17 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke) this_buf = g_alloca (mono_class_instance_size (m->klass)); else this_buf = g_alloca (sizeof (MonoObject*)); - err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end); - if (err) - return err; + 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) + 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); + if (err) + return err; + } if (!m->klass->valuetype) this = *(MonoObject**)this_buf; @@ -3949,6 +4485,11 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke) /* 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_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; #else g_assert_not_reached (); #endif @@ -4046,7 +4587,7 @@ invoke_method (void) if (invoke->flags & INVOKE_FLAG_SINGLE_THREADED) { g_assert (tls->resume_count); - tls->resume_count --; + tls->resume_count -= invoke->suspend_count; } DEBUG (1, printf ("[%p] Invoke finished, resume_count = %d.\n", (gpointer)GetCurrentThreadId (), tls->resume_count)); @@ -4230,6 +4771,10 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) mono_loader_unlock (); g_assert (tls); + if (!tls->really_suspended) + /* The thread is still running native code, can't do invokes */ + return ERR_NOT_SUSPENDED; + /* * Store the invoke data into tls, the thread will execute it after it is * resumed. @@ -4242,6 +4787,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) tls->invoke->p = g_malloc (end - p); memcpy (tls->invoke->p, p, end - p); tls->invoke->endp = tls->invoke->p + (end - p); + tls->invoke->suspend_count = suspend_count; if (flags & INVOKE_FLAG_SINGLE_THREADED) resume_thread (THREAD_TO_INTERNAL (thread)); @@ -4310,6 +4856,8 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (err) return err; + req->modifiers [i].caught = decode_byte (p, &p, end); + req->modifiers [i].uncaught = decode_byte (p, &p, end); if (exc_class) { req->modifiers [i].data.exc_class = exc_class; @@ -4349,7 +4897,7 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return err; } - err = ss_start (THREAD_TO_INTERNAL (step_thread), size, depth, req); + err = ss_create (THREAD_TO_INTERNAL (step_thread), size, depth, req); if (err) { g_free (req); return err; @@ -4478,6 +5026,30 @@ domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf) buffer_add_objid (buf, (MonoObject*)o); break; } + case CMD_APPDOMAIN_CREATE_BOXED_VALUE: { + MonoClass *klass; + MonoDomain *domain2; + MonoObject *o; + + domain = decode_domainid (p, &p, end, NULL, &err); + if (err) + return err; + klass = decode_typeid (p, &p, end, &domain2, &err); + if (err) + return err; + + // FIXME: + g_assert (domain == domain2); + + o = mono_object_new (domain, klass); + + err = decode_value (&klass->byval_arg, domain, mono_object_unbox (o), p, &p, end); + if (err) + return err; + + buffer_add_objid (buf, o); + break; + } default: return ERR_NOT_IMPLEMENTED; } @@ -4723,6 +5295,8 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf) b |= (1 << 2); if (type->type == MONO_TYPE_VALUETYPE) b |= (1 << 3); + if (klass->enumtype) + b |= (1 << 4); buffer_add_byte (buf, b); nnested = 0; iter = NULL; @@ -4912,7 +5486,10 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf) g_free (val); return err; } - mono_field_static_set_value (vtable, f, val); + if (MONO_TYPE_IS_REFERENCE (f->type)) + mono_field_static_set_value (vtable, f, *(gpointer*)val); + else + mono_field_static_set_value (vtable, f, val); g_free (val); } break; @@ -5307,6 +5884,10 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) frame = tls->frames [i]; + if (!frame->has_ctx) + // FIXME: + return ERR_INVALID_FRAMEID; + if (!frame->jit) { frame->jit = mono_debug_find_method (frame->method, frame->domain); g_assert (frame->jit); @@ -5330,13 +5911,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); + add_var (buf, 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); + add_var (buf, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE); } } break; @@ -5347,14 +5928,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->method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain); + add_var (buf, &frame->method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE); } } else { if (!sig->hasthis) { MonoObject *p = NULL; buffer_add_value (buf, &frame->method->klass->byval_arg, &p, frame->domain); } else { - add_var (buf, &frame->method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain); + add_var (buf, &frame->method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE); } } break; @@ -5674,7 +6255,7 @@ debugger_thread (void *arg) mono_set_is_debugger_attached (TRUE); while (TRUE) { - res = recv (conn_fd, header, HEADER_LENGTH, 0); + res = recv_length (conn_fd, header, HEADER_LENGTH, 0); /* This will break if the socket is closed during shutdown too */ if (res != HEADER_LENGTH) @@ -5696,7 +6277,7 @@ debugger_thread (void *arg) data = g_malloc (len - HEADER_LENGTH); if (len - HEADER_LENGTH > 0) { - res = recv (conn_fd, data, len - HEADER_LENGTH, 0); + res = recv_length (conn_fd, data, len - HEADER_LENGTH, 0); if (res != len - HEADER_LENGTH) break; } @@ -5811,14 +6392,9 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji) } void -mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *ctx) +mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *throw_ctx, + MonoContext *catch_ctx) { -} - -void -mono_debugger_agent_handle_unhandled_exception (MonoException *exc, MonoContext *ctx) -{ - } #endif