X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fdebugger-agent.c;h=e205ede5c8f1f64f423e1b88ec7e284f4d92459b;hb=e98d5e33453448134f34f5d85c9e9a470a6bf007;hp=3982519e7b96075c4626ea39038665f6ffc3471b;hpb=3b18bdf7c1b3e739e9538f02892cdcbd5688e4b0;p=mono.git diff --git a/mono/mini/debugger-agent.c b/mono/mini/debugger-agent.c index 3982519e7b9..e205ede5c8f 100644 --- a/mono/mini/debugger-agent.c +++ b/mono/mini/debugger-agent.c @@ -1,10 +1,10 @@ /* - * debugger-agent.c: Debugger back-end module + * debugger-agent.c: Soft Debugger back-end module * * Author: * Zoltan Varga (vargaz@gmail.com) * - * (C) 2009 Novell, Inc. + * Copyright 2009-2010 Novell, Inc. */ #include @@ -14,6 +14,9 @@ #ifdef HAVE_SYS_TYPES_H #include #endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif #ifdef HAVE_SYS_SOCKET_H #include #endif @@ -41,6 +44,9 @@ #endif #ifdef HOST_WIN32 +#ifdef _MSC_VER +#include +#endif #include #ifdef __GNUC__ /* cygwin's headers do not seem to define these */ @@ -64,6 +70,7 @@ int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD, #include #include #include +#include #include #include "debugger-agent.h" #include "mini.h" @@ -96,6 +103,7 @@ typedef struct { GSList *onthrow; int timeout; char *launch; + gboolean embedding; } AgentConfig; typedef struct @@ -114,7 +122,9 @@ typedef struct gboolean has_ctx; } StackFrame; -typedef struct +typedef struct _InvokeData InvokeData; + +struct _InvokeData { int id; int flags; @@ -129,7 +139,9 @@ typedef struct MonoMethod *method; gpointer *args; guint32 suspend_count; -} InvokeData; + + InvokeData *last_invoke; +}; typedef struct { MonoContext ctx; @@ -150,7 +162,7 @@ typedef struct { * Points to data about a pending invoke which needs to be executed after the thread * resumes. */ - InvokeData *invoke; + InvokeData *pending_invoke; /* * Set to TRUE if this thread is suspended in suspend_current () or it is executing * native code. @@ -205,6 +217,13 @@ typedef struct { * The callee address of the last mono_runtime_invoke call */ gpointer invoke_addr; + + gboolean abort_requested; + + /* + * The current mono_runtime_invoke invocation. + */ + InvokeData *invoke; } DebuggerTlsData; /* @@ -214,7 +233,7 @@ typedef struct { #define HEADER_LENGTH 11 #define MAJOR_VERSION 2 -#define MINOR_VERSION 0 +#define MINOR_VERSION 2 typedef enum { CMD_SET_VM = 1, @@ -263,7 +282,9 @@ typedef enum { ERR_NOT_IMPLEMENTED = 100, ERR_NOT_SUSPENDED = 101, ERR_INVALID_ARGUMENT = 102, - ERR_UNLOADED = 103 + ERR_UNLOADED = 103, + ERR_NO_INVOCATION = 104, + ERR_ABSENT_INFORMATION = 105 } ErrorCode; typedef enum { @@ -315,14 +336,17 @@ typedef enum { CMD_VM_RESUME = 4, CMD_VM_EXIT = 5, CMD_VM_DISPOSE = 6, - CMD_VM_INVOKE_METHOD = 7 + CMD_VM_INVOKE_METHOD = 7, + CMD_VM_SET_PROTOCOL_VERSION = 8, + CMD_VM_ABORT_INVOKE = 9 } CmdVM; typedef enum { CMD_THREAD_GET_FRAME_INFO = 1, CMD_THREAD_GET_NAME = 2, CMD_THREAD_GET_STATE = 3, - CMD_THREAD_GET_INFO = 4 + CMD_THREAD_GET_INFO = 4, + CMD_THREAD_GET_ID = 5 } CmdThread; typedef enum { @@ -341,7 +365,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 { @@ -380,7 +405,8 @@ typedef enum { CMD_TYPE_GET_PROPERTIES = 9, CMD_TYPE_GET_CATTRS = 10, CMD_TYPE_GET_FIELD_CATTRS = 11, - CMD_TYPE_GET_PROPERTY_CATTRS = 12 + CMD_TYPE_GET_PROPERTY_CATTRS = 12, + CMD_TYPE_GET_SOURCE_FILES_2 = 13, } CmdType; typedef enum { @@ -416,6 +442,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{ @@ -445,6 +472,16 @@ typedef struct { 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 { void* dummy; @@ -498,6 +535,8 @@ static HANDLE debugger_thread_handle; static int log_level; +static gboolean embedding; + static FILE *log_file; /* Classes whose class load event has been sent */ @@ -506,6 +545,9 @@ static GHashTable *loaded_classes; /* Assemblies whose assembly load event has no been sent yet */ static GPtrArray *pending_assembly_loads; +/* Types whose type load event has no been sent yet */ +static GPtrArray *pending_type_loads; + /* Whenever the debugger thread has exited */ static gboolean debugger_thread_exited; @@ -526,6 +568,15 @@ static gpointer ss_invoke_addr = NULL; static int ss_count; #endif +/* The protocol version of the client */ +static int major_version, minor_version; + +/* Whenever the variables above are set by the client */ +static gboolean protocol_version_set; + +/* A hash table containing all active domains */ +static GHashTable *domains; + static void transport_connect (const char *host, int port); static guint32 WINAPI debugger_thread (void *arg); @@ -534,14 +585,16 @@ 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, uintptr_t tid); -static void thread_end (MonoProfiler *prof, gsize tid); +static void thread_end (MonoProfiler *prof, uintptr_t tid); static void appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result); static void appdomain_unload (MonoProfiler *prof, MonoDomain *domain); +static void invalidate_each_thread (gpointer key, gpointer value, gpointer user_data); + static void assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result); static void assembly_unload (MonoProfiler *prof, MonoAssembly *assembly); @@ -560,6 +613,12 @@ static void stop_single_stepping (void); static void suspend_current (void); +static void clear_event_requests_for_assembly (MonoAssembly *assembly); + +static void clear_breakpoints_for_domain (MonoDomain *domain); + +static void clear_types_for_assembly (MonoAssembly *assembly); + /* Submodule init/cleanup */ static void breakpoints_init (void); static void breakpoints_cleanup (void); @@ -572,7 +631,7 @@ static void ids_cleanup (void); static void suspend_init (void); -static void ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info); +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); @@ -662,6 +721,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); @@ -669,6 +730,8 @@ mono_debugger_agent_parse_options (char *options) agent_config.timeout = atoi (arg + 8); } else if (strncmp (arg, "launch=", 7) == 0) { agent_config.launch = g_strdup (arg + 7); + } else if (strncmp (arg, "embedding=", 10) == 0) { + agent_config.embedding = atoi (arg + 10) == 1; } else { print_usage (); exit (1); @@ -712,7 +775,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); @@ -720,20 +783,24 @@ mono_debugger_agent_init (void) debugger_tls_id = TlsAlloc (); - thread_to_tls = mono_g_hash_table_new (NULL, NULL); - MONO_GC_REGISTER_ROOT (thread_to_tls); + thread_to_tls = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC); + MONO_GC_REGISTER_ROOT_FIXED (thread_to_tls); tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC); - MONO_GC_REGISTER_ROOT (tid_to_thread); + MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread); tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC); - MONO_GC_REGISTER_ROOT (tid_to_thread_obj); + MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread_obj); loaded_classes = g_hash_table_new (mono_aligned_addr_hash, NULL); pending_assembly_loads = g_ptr_array_new (); + pending_type_loads = g_ptr_array_new (); + domains = g_hash_table_new (mono_aligned_addr_hash, NULL); log_level = agent_config.log_level; + embedding = agent_config.embedding; + if (agent_config.log_file) { log_file = fopen (agent_config.log_file, "w+"); if (!log_file) { @@ -839,7 +906,7 @@ 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) { + while (!debugger_thread_exited) { #ifdef HOST_WIN32 if (WAIT_TIMEOUT == WaitForSingleObject(debugger_thread_exited_cond, 0)) { mono_mutex_unlock (&debugger_thread_exited_mutex); @@ -867,6 +934,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: * @@ -950,7 +1035,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)); @@ -994,7 +1087,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); @@ -1004,16 +1100,26 @@ 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); } + /* + * To support older clients, the client sends its protocol version after connecting + * using a command. Until that is received, default to our protocol version. + */ + major_version = MAJOR_VERSION; + minor_version = MINOR_VERSION; + protocol_version_set = FALSE; + /* * Set TCP_NODELAY on the socket so the client receives events/command * results immediately. @@ -1034,7 +1140,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 @@ -1294,26 +1402,42 @@ static ObjRef* get_objref (MonoObject *obj) { ObjRef *ref; + GSList *reflist = NULL, *l; + int hash = 0; if (obj == NULL) return 0; -#ifdef HAVE_SGEN_GC - NOT_IMPLEMENTED; -#endif - - /* Use a hash table with masked pointers to internalize object references */ - /* FIXME: This can grow indefinitely */ mono_loader_lock (); if (!obj_to_objref) obj_to_objref = g_hash_table_new (NULL, NULL); + + /* FIXME: The tables can grow indefinitely */ - ref = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj))); - /* ref might refer to a different object with the same addr which was GCd */ - if (ref && mono_gchandle_get_target (ref->handle) == obj) { - mono_loader_unlock (); - return ref; + if (mono_gc_is_moving ()) { + /* + * Objects can move, so use a hash table mapping hash codes to lists of + * ObjRef structures. + */ + hash = mono_object_hash (obj); + + reflist = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (hash)); + for (l = reflist; l; l = l->next) { + ref = l->data; + if (ref && mono_gchandle_get_target (ref->handle) == obj) { + mono_loader_unlock (); + return ref; + } + } + } else { + /* Use a hash table with masked pointers to internalize object references */ + ref = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj))); + /* ref might refer to a different object with the same addr which was GCd */ + if (ref && mono_gchandle_get_target (ref->handle) == obj) { + mono_loader_unlock (); + return ref; + } } ref = g_new0 (ObjRef, 1); @@ -1321,7 +1445,13 @@ get_objref (MonoObject *obj) ref->handle = mono_gchandle_new_weakref (obj, FALSE); g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref); - g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref); + + if (mono_gc_is_moving ()) { + reflist = g_slist_append (reflist, ref); + g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (hash), reflist); + } else { + g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref); + } mono_loader_unlock (); @@ -1484,6 +1614,10 @@ mono_debugger_agent_free_domain_info (MonoDomain *domain) } } } + + mono_loader_lock (); + g_hash_table_remove (domains, domain); + mono_loader_unlock (); } static int @@ -1803,7 +1937,8 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji) // 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))); + if (sigctx) + 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); if (!tls->thread) @@ -1836,6 +1971,8 @@ mono_debugger_agent_thread_interrupt (void *sigctx, MonoJitInfo *ji) tls->has_async_ctx = FALSE; } + mono_memory_barrier (); + tls->suspended = TRUE; MONO_SEM_POST (&suspend_sem); } @@ -1877,34 +2014,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)); + if (GetCurrentThreadId () == tid || tls->terminated) + return; - /* - * 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 - */ + 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; + 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); + /* + * 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 () */ + /* 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 @@ -1932,7 +2070,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"))) @@ -2130,10 +2268,10 @@ suspend_current (void) DEBUG(1, fprintf (log_file, "[%p] Resumed.\n", (gpointer)GetCurrentThreadId ())); - if (tls->invoke) { + if (tls->pending_invoke) { /* Save the original context */ - tls->invoke->has_ctx = TRUE; - memcpy (&tls->invoke->ctx, &tls->ctx, sizeof (MonoContext)); + tls->pending_invoke->has_ctx = TRUE; + memcpy (&tls->pending_invoke->ctx, &tls->ctx, sizeof (MonoContext)); invoke_method (); } @@ -2312,7 +2450,7 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) if (info->type == FRAME_TYPE_DEBUGGER_INVOKE) { /* Mark the last frame as an invoke frame */ if (ud->frames) - ((StackFrame*)ud->frames->data)->flags |= FRAME_FLAG_DEBUGGER_INVOKE; + ((StackFrame*)g_slist_last (ud->frames)->data)->flags |= FRAME_FLAG_DEBUGGER_INVOKE; } return FALSE; } @@ -2330,7 +2468,7 @@ process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data) info->il_offset = mono_debug_il_offset_from_address (method, info->domain, info->native_offset); } - DEBUG (1, fprintf (stderr, "\tFrame: %s %d %d %d\n", mono_method_full_name (method, TRUE), info->native_offset, info->il_offset, info->managed)); + DEBUG (1, fprintf (log_file, "\tFrame: %s %d %d %d\n", mono_method_full_name (method, TRUE), info->native_offset, info->il_offset, info->managed)); if (!info->managed && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD) { /* @@ -2434,7 +2572,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; @@ -2468,8 +2606,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; @@ -2610,9 +2752,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 (); } @@ -2696,7 +2840,7 @@ runtime_shutdown (MonoProfiler *prof) } static void -thread_startup (MonoProfiler *prof, gsize tid) +thread_startup (MonoProfiler *prof, uintptr_t tid) { MonoInternalThread *thread = mono_thread_internal_current (); MonoInternalThread *old_thread; @@ -2737,7 +2881,7 @@ 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); + MONO_GC_REGISTER_ROOT_SINGLE (tls->thread); tls->thread = thread; TlsSetValue (debugger_tls_id, tls); @@ -2758,7 +2902,7 @@ thread_startup (MonoProfiler *prof, gsize tid) } static void -thread_end (MonoProfiler *prof, gsize tid) +thread_end (MonoProfiler *prof, uintptr_t tid) { MonoInternalThread *thread; DebuggerTlsData *tls = NULL; @@ -2786,15 +2930,34 @@ thread_end (MonoProfiler *prof, gsize tid) static void appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result) { + mono_loader_lock (); + g_hash_table_insert (domains, domain, domain); + mono_loader_unlock (); + process_profiler_event (EVENT_KIND_APPDOMAIN_CREATE, domain); } static void appdomain_unload (MonoProfiler *prof, MonoDomain *domain) { + /* Invalidate each thread's frame stack */ + mono_g_hash_table_foreach (thread_to_tls, invalidate_each_thread, NULL); + clear_breakpoints_for_domain (domain); process_profiler_event (EVENT_KIND_APPDOMAIN_UNLOAD, domain); } +/* + * invalidate_each_thread: + * + * A GHFunc to invalidate frames. + * value must be a DebuggerTlsData* + */ +static void +invalidate_each_thread (gpointer key, gpointer value, gpointer user_data) +{ + invalidate_frames (value); +} + static void assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result) { @@ -2808,6 +2971,9 @@ static void assembly_unload (MonoProfiler *prof, MonoAssembly *assembly) { process_profiler_event (EVENT_KIND_ASSEMBLY_UNLOAD, assembly); + + clear_event_requests_for_assembly (assembly); + clear_types_for_assembly (assembly); } static void @@ -2841,7 +3007,7 @@ end_runtime_invoke (MonoProfiler *prof, MonoMethod *method) gpointer stackptr = __builtin_frame_address (1); #endif - if (ss_req == NULL || stackptr != ss_invoke_addr || ss_req->thread != mono_thread_internal_current ()) + if (!embedding || ss_req == NULL || stackptr != ss_invoke_addr || ss_req->thread != mono_thread_internal_current ()) return; /* @@ -2864,6 +3030,21 @@ end_runtime_invoke (MonoProfiler *prof, MonoMethod *method) mono_loader_unlock (); } +static void +send_type_load (MonoClass *klass) +{ + gboolean type_load = FALSE; + + mono_loader_lock (); + if (!g_hash_table_lookup (loaded_classes, klass)) { + type_load = TRUE; + g_hash_table_insert (loaded_classes, klass, klass); + } + mono_loader_unlock (); + if (type_load) + process_profiler_event (EVENT_KIND_TYPE_LOAD, klass); +} + static void jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result) { @@ -2873,8 +3054,6 @@ jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result) * loader lock held. They could also occur in the debugger thread. * Same for assembly load events. */ - gboolean type_load = FALSE; - while (TRUE) { MonoAssembly *assembly = NULL; @@ -2892,14 +3071,30 @@ jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result) break; } - mono_loader_lock (); - if (!g_hash_table_lookup (loaded_classes, method->klass)) { - type_load = TRUE; - g_hash_table_insert (loaded_classes, method->klass, method->klass); + if (!vm_start_event_sent) { + /* Save these so they can be sent after the vm start event */ + mono_loader_lock (); + g_ptr_array_add (pending_type_loads, method->klass); + mono_loader_unlock (); + } else { + /* Send all pending type load events */ + MonoClass *klass; + while (TRUE) { + klass = NULL; + mono_loader_lock (); + if (pending_type_loads->len > 0) { + klass = g_ptr_array_index (pending_type_loads, 0); + g_ptr_array_remove_index (pending_type_loads, 0); + } + mono_loader_unlock (); + if (klass) + send_type_load (klass); + else + break; + } + + send_type_load (method->klass); } - mono_loader_unlock (); - if (type_load) - process_profiler_event (EVENT_KIND_TYPE_LOAD, method->klass); if (!result) add_pending_breakpoints (method, jinfo); @@ -2915,8 +3110,8 @@ jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result) typedef struct { long il_offset, native_offset; guint8 *ip; - gboolean pending, entry; MonoJitInfo *ji; + MonoDomain *domain; } BreakpointInstance; /* @@ -2931,7 +3126,6 @@ typedef struct { */ MonoMethod *method; long il_offset; - gboolean pending, entry; EventRequest *req; /* * A list of BreakpointInstance structures describing where the breakpoint @@ -2953,22 +3147,6 @@ breakpoints_init (void) bp_locs = g_hash_table_new (NULL, NULL); } -static void -breakpoints_cleanup (void) -{ - int i; - - mono_loader_lock (); - - for (i = 0; i < breakpoints->len; ++i) - g_free (g_ptr_array_index (breakpoints, i)); - - g_ptr_array_free (breakpoints, TRUE); - g_hash_table_destroy (bp_locs); - - mono_loader_unlock (); -} - /* * insert_breakpoint: * @@ -2976,10 +3154,10 @@ breakpoints_cleanup (void) * JI. */ static void -insert_breakpoint (MonoSeqPointInfo *seq_points, MonoJitInfo *ji, MonoBreakpoint *bp) +insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo *ji, MonoBreakpoint *bp) { int i, count; - gint32 il_offset, native_offset; + gint32 il_offset = -1, native_offset; BreakpointInstance *inst; native_offset = 0; @@ -2991,14 +3169,16 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoJitInfo *ji, MonoBreakpoint break; } - if (i == seq_points->len) + if (i == seq_points->len) { /* Have to handle this somehow */ - NOT_IMPLEMENTED; + g_error ("Unable to insert breakpoint at %s:%d, seq_points=%d\n", mono_method_full_name (ji->method, TRUE), bp->il_offset, seq_points->len); + } inst = g_new0 (BreakpointInstance, 1); inst->native_offset = native_offset; inst->ip = (guint8*)ji->code_start + native_offset; inst->ji = ji; + inst->domain = domain; mono_loader_lock (); @@ -3015,6 +3195,8 @@ insert_breakpoint (MonoSeqPointInfo *seq_points, MonoJitInfo *ji, MonoBreakpoint NOT_IMPLEMENTED; #endif } + + DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x.\n", mono_method_full_name (ji->method, TRUE), (int)il_offset)); } static void @@ -3040,6 +3222,12 @@ remove_breakpoint (BreakpointInstance *inst) #endif } +static inline gboolean +bp_matches_method (MonoBreakpoint *bp, MonoMethod *method) +{ + return (!bp->method || method == bp->method || (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method)); +} + /* * add_pending_breakpoints: * @@ -3048,7 +3236,7 @@ remove_breakpoint (BreakpointInstance *inst) static void add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji) { - int i; + int i, j; MonoSeqPointInfo *seq_points; MonoDomain *domain; @@ -3061,8 +3249,19 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji) for (i = 0; i < breakpoints->len; ++i) { MonoBreakpoint *bp = g_ptr_array_index (breakpoints, i); + gboolean found = FALSE; - if (bp->pending && (bp->method == method || !bp->method || (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method))) { + if (!bp_matches_method (bp, method)) + continue; + + for (j = 0; j < bp->children->len; ++j) { + BreakpointInstance *inst = g_ptr_array_index (bp->children, j); + + if (inst->ji == ji) + found = TRUE; + } + + if (!found) { mono_domain_lock (domain); seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, ji->method); mono_domain_unlock (domain); @@ -3071,7 +3270,7 @@ add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji) continue; g_assert (seq_points); - insert_breakpoint (seq_points, ji, bp); + insert_breakpoint (seq_points, domain, ji, bp); } } @@ -3094,26 +3293,41 @@ set_bp_in_method (MonoDomain *domain, MonoMethod *method, MonoSeqPointInfo *seq_ } g_assert (code); - insert_breakpoint (seq_points, ji, bp); + insert_breakpoint (seq_points, domain, ji, bp); } +typedef struct +{ + MonoBreakpoint *bp; + MonoDomain *domain; +} SetBpUserData; + static void set_bp_in_method_cb (gpointer key, gpointer value, gpointer user_data) { MonoMethod *method = key; MonoSeqPointInfo *seq_points = value; - MonoBreakpoint *bp = user_data; - MonoDomain *domain = mono_domain_get (); + SetBpUserData *ud = user_data; + MonoBreakpoint *bp = ud->bp; + MonoDomain *domain = ud->domain; - if (bp->method) { - if (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method) { - /* Generic instance */ - set_bp_in_method (domain, method, seq_points, bp); - } - } else { - /* Method entry/exit */ + if (bp_matches_method (bp, method)) set_bp_in_method (domain, method, seq_points, bp); - } +} + +static void +set_bp_in_domain (gpointer key, gpointer value, gpointer user_data) +{ + MonoDomain *domain = key; + MonoBreakpoint *bp = user_data; + SetBpUserData ud; + + ud.bp = bp; + ud.domain = domain; + + mono_domain_lock (domain); + g_hash_table_foreach (domain_jit_info (domain)->seq_points, set_bp_in_method_cb, &ud); + mono_domain_unlock (domain); } /* @@ -3127,13 +3341,10 @@ set_bp_in_method_cb (gpointer key, gpointer value, gpointer user_data) static MonoBreakpoint* set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req) { - MonoSeqPointInfo *seq_points; - MonoDomain *domain; MonoBreakpoint *bp; - // FIXME: + // FIXME: // - suspend/resume the vm to prevent code patching problems - // - appdomains // - multiple breakpoints on the same location // - dynamic methods // - races @@ -3146,25 +3357,11 @@ set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req) 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); - if (method) { - seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method); - if (seq_points) { - set_bp_in_method (domain, method, seq_points, bp); - } else { - if (method->is_generic) - /* There might be already JITted instances */ - g_hash_table_foreach (domain_jit_info (domain)->seq_points, set_bp_in_method_cb, bp); + mono_loader_lock (); - /* Not yet JITted */ - bp->pending = TRUE; - } - } else { - g_hash_table_foreach (domain_jit_info (domain)->seq_points, set_bp_in_method_cb, bp); - bp->pending = TRUE; - } - mono_domain_unlock (domain); + g_hash_table_foreach (domains, set_bp_in_domain, bp); + + mono_loader_unlock (); mono_loader_lock (); g_ptr_array_add (breakpoints, bp); @@ -3195,6 +3392,79 @@ clear_breakpoint (MonoBreakpoint *bp) g_free (bp); } +static void +breakpoints_cleanup (void) +{ + int i; + + mono_loader_lock (); + i = 0; + while (i < event_requests->len) { + EventRequest *req = g_ptr_array_index (event_requests, i); + + if (req->event_kind == EVENT_KIND_BREAKPOINT) { + clear_breakpoint (req->info); + g_ptr_array_remove_index_fast (event_requests, i); + g_free (req); + } else { + i ++; + } + } + + for (i = 0; i < breakpoints->len; ++i) + g_free (g_ptr_array_index (breakpoints, i)); + + g_ptr_array_free (breakpoints, TRUE); + g_hash_table_destroy (bp_locs); + + breakpoints = NULL; + bp_locs = NULL; + + mono_loader_unlock (); +} + +/* + * clear_breakpoints_for_domain: + * + * Clear breakpoint instances which reference DOMAIN. + */ +static void +clear_breakpoints_for_domain (MonoDomain *domain) +{ + int i, j; + + /* This could be called after shutdown */ + if (!breakpoints) + return; + + mono_loader_lock (); + for (i = 0; i < breakpoints->len; ++i) { + MonoBreakpoint *bp = g_ptr_array_index (breakpoints, i); + + j = 0; + while (j < bp->children->len) { + BreakpointInstance *inst = g_ptr_array_index (bp->children, j); + + if (inst->domain == domain) { + remove_breakpoint (inst); + + g_free (inst); + + g_ptr_array_remove_index_fast (bp->children, j); + } else { + j ++; + } + } + } + mono_loader_unlock (); +} + +static gboolean +breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly) +{ + return bp->method && bp->method->klass->image->assembly == assembly; +} + static void process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx) { @@ -3211,7 +3481,7 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx) // 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); @@ -3323,7 +3593,7 @@ process_breakpoint_inner (DebuggerTlsData *tls, MonoContext *ctx) g_ptr_array_add (ss_reqs, req); /* Start single stepping again from the current sequence point */ - ss_start (ss_req, ji->method, sp, info); + ss_start (ss_req, ji->method, sp, info, ctx, NULL); } if (ss_reqs->len > 0) @@ -3414,7 +3684,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 @@ -3439,7 +3709,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: @@ -3476,7 +3746,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); @@ -3685,10 +3955,10 @@ ss_stop (SingleStepReq *ss_req) * 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) +ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls) { gboolean use_bp = FALSE; - int i; + int i, frame_index; SeqPoint *next_sp; MonoBreakpoint *bp; @@ -3699,7 +3969,25 @@ ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointI * Implement single stepping using breakpoints if possible. */ if (ss_req->depth == STEP_DEPTH_OVER) { - if (sp->next_len > 0) { + 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]]; @@ -3794,7 +4082,7 @@ ss_create (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequ } } - ss_start (ss_req, method, sp, info); + ss_start (ss_req, method, sp, info, NULL, tls); return 0; } @@ -3812,14 +4100,42 @@ ss_destroy (SingleStepReq *req) } 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; + + if (thread_to_tls != NULL) { + MonoInternalThread *thread = mono_thread_internal_current (); + DebuggerTlsData *tls; + + mono_loader_lock (); + tls = mono_g_hash_table_lookup (thread_to_tls, thread); + mono_loader_unlock (); + + if (tls && tls->abort_requested) + return; + } + + 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; @@ -3827,7 +4143,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); @@ -3840,7 +4156,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; } } @@ -3848,30 +4165,16 @@ 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); } /* @@ -4036,8 +4339,14 @@ decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 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; } @@ -4078,6 +4387,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; @@ -4110,6 +4438,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) { @@ -4239,6 +4568,82 @@ clear_event_request (int req_id, int etype) mono_loader_unlock (); } +static gboolean +event_req_matches_assembly (EventRequest *req, MonoAssembly *assembly) +{ + if (req->event_kind == EVENT_KIND_BREAKPOINT) + return breakpoint_matches_assembly (req->info, assembly); + else { + int i, j; + + for (i = 0; i < req->nmodifiers; ++i) { + Modifier *m = &req->modifiers [i]; + + if (m->kind == MOD_KIND_EXCEPTION_ONLY && m->data.exc_class && m->data.exc_class->image->assembly == assembly) + return TRUE; + if (m->kind == MOD_KIND_ASSEMBLY_ONLY && m->data.assemblies) { + for (j = 0; m->data.assemblies [j]; ++j) + if (m->data.assemblies [j] == assembly) + return TRUE; + } + } + } + + return FALSE; +} + +/* + * clear_event_requests_for_assembly: + * + * Clear all events requests which reference ASSEMBLY. + */ +static void +clear_event_requests_for_assembly (MonoAssembly *assembly) +{ + int i; + gboolean found; + + mono_loader_lock (); + found = TRUE; + while (found) { + found = FALSE; + for (i = 0; i < event_requests->len; ++i) { + EventRequest *req = g_ptr_array_index (event_requests, i); + + if (event_req_matches_assembly (req, assembly)) { + clear_event_request (req->id, req->event_kind); + found = TRUE; + break; + } + } + } + mono_loader_unlock (); +} + +/* + * type_comes_from_assembly: + * + * GHRFunc that returns TRUE if klass comes from assembly + */ +static gboolean +type_comes_from_assembly (gpointer klass, gpointer also_klass, gpointer assembly) +{ + return (mono_class_get_image ((MonoClass*)klass) == mono_assembly_get_image ((MonoAssembly*)assembly)); +} + +/* + * clear_types_for_assembly: + * + * Clears types from loaded_classes for a given assembly + */ +static void +clear_types_for_assembly (MonoAssembly *assembly) +{ + mono_loader_lock (); + g_hash_table_foreach_remove (loaded_classes, type_comes_from_assembly, assembly); + mono_loader_unlock (); +} + static void add_thread (gpointer key, gpointer value, gpointer user_data) { @@ -4284,9 +4689,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; @@ -4427,7 +4840,7 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke) /* * invoke_method: * - * Invoke the method given by tls->invoke in the current thread. + * Invoke the method given by tls->pending_invoke in the current thread. */ static void invoke_method (void) @@ -4446,9 +4859,21 @@ invoke_method (void) tls = TlsGetValue (debugger_tls_id); g_assert (tls); - invoke = tls->invoke; + /* + * Store the `InvokeData *' in `tls->invoke' until we're done with + * the invocation, so CMD_VM_ABORT_INVOKE can check it. + */ + + mono_loader_lock (); + + invoke = tls->pending_invoke; g_assert (invoke); - tls->invoke = NULL; + tls->pending_invoke = NULL; + + invoke->last_invoke = tls->invoke; + tls->invoke = invoke; + + mono_loader_unlock (); tls->frames_up_to_date = FALSE; @@ -4478,6 +4903,24 @@ invoke_method (void) DEBUG (1, printf ("[%p] Invoke finished, resume_count = %d.\n", (gpointer)GetCurrentThreadId (), tls->resume_count)); + /* + * Take the loader lock to avoid race conditions with CMD_VM_ABORT_INVOKE: + * + * It is possible that ves_icall_System_Threading_Thread_Abort () was called + * after the mono_runtime_invoke() already returned, but it doesn't matter + * because we reset the abort here. + */ + + mono_loader_lock (); + + if (tls->abort_requested) + mono_thread_internal_reset_abort (tls->thread); + + tls->invoke = tls->invoke->last_invoke; + tls->abort_requested = FALSE; + + mono_loader_unlock (); + g_free (invoke->p); g_free (invoke); @@ -4517,6 +4960,13 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) g_free (version); break; } + case CMD_VM_SET_PROTOCOL_VERSION: { + major_version = decode_int (p, &p, end); + minor_version = decode_int (p, &p, end); + protocol_version_set = TRUE; + DEBUG(1, fprintf (log_file, "[dbg] Protocol version %d.%d, client protocol version %d.%d.\n", MAJOR_VERSION, MINOR_VERSION, major_version, minor_version)); + break; + } case CMD_VM_ALL_THREADS: { // FIXME: Domains mono_loader_lock (); @@ -4600,9 +5050,9 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) args [0] = g_malloc (sizeof (int)); *(int*)(args [0]) = exit_code; - tls->invoke = g_new0 (InvokeData, 1); - tls->invoke->method = exit_method; - tls->invoke->args = args; + tls->pending_invoke = g_new0 (InvokeData, 1); + tls->pending_invoke->method = exit_method; + tls->pending_invoke->args = args; while (suspend_count > 0) resume_vm (); @@ -4665,15 +5115,15 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) * Store the invoke data into tls, the thread will execute it after it is * resumed. */ - if (tls->invoke) + if (tls->pending_invoke) NOT_IMPLEMENTED; - tls->invoke = g_new0 (InvokeData, 1); - tls->invoke->id = id; - tls->invoke->flags = flags; - 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; + tls->pending_invoke = g_new0 (InvokeData, 1); + tls->pending_invoke->id = id; + tls->pending_invoke->flags = flags; + tls->pending_invoke->p = g_malloc (end - p); + 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; if (flags & INVOKE_FLAG_SINGLE_THREADED) resume_thread (THREAD_TO_INTERNAL (thread)); @@ -4681,6 +5131,49 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf) resume_vm (); break; } + case CMD_VM_ABORT_INVOKE: { + int objid = decode_objid (p, &p, end); + MonoThread *thread; + DebuggerTlsData *tls; + int invoke_id, err; + + err = get_object (objid, (MonoObject**)&thread); + if (err) + return err; + + invoke_id = decode_int (p, &p, end); + + mono_loader_lock (); + tls = mono_g_hash_table_lookup (thread_to_tls, THREAD_TO_INTERNAL (thread)); + g_assert (tls); + + if (tls->abort_requested) { + mono_loader_unlock (); + break; + } + + /* + * Check whether we're still inside the mono_runtime_invoke() and that it's + * actually the correct invocation. + * + * Careful, we do not stop the thread that's doing the invocation, so we can't + * inspect its stack. However, invoke_method() also acquires the loader lock + * when it's done, so we're safe here. + * + */ + + if (!tls->invoke || (tls->invoke->id != invoke_id)) { + mono_loader_unlock (); + return ERR_NO_INVOCATION; + } + + tls->abort_requested = TRUE; + + ves_icall_System_Threading_Thread_Abort (THREAD_TO_INTERNAL (thread), NULL); + mono_loader_unlock (); + break; + } + default: return ERR_NOT_IMPLEMENTED; } @@ -4742,6 +5235,9 @@ 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); + 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 (exc_class) { req->modifiers [i].data.exc_class = exc_class; @@ -4910,6 +5406,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; } @@ -5111,10 +5631,8 @@ buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass } static ErrorCode -type_commands (int command, guint8 *p, guint8 *end, Buffer *buf) +type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint8 *p, guint8 *end, Buffer *buf) { - MonoClass *klass; - MonoDomain *domain; MonoClass *nested; MonoType *type; gpointer iter; @@ -5122,10 +5640,6 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf) int err, nnested; char *name; - klass = decode_typeid (p, &p, end, &domain, &err); - if (err) - return err; - switch (command) { case CMD_TYPE_GET_INFO: { buffer_add_string (buf, klass->name_space); @@ -5155,6 +5669,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; @@ -5172,6 +5688,8 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf) gpointer iter = NULL; MonoMethod *m; + mono_class_setup_methods (klass); + nmethods = mono_class_num_methods (klass); buffer_add_int (buf, nmethods); @@ -5344,7 +5862,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; @@ -5354,7 +5875,8 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf) buffer_add_objid (buf, o); break; } - case CMD_TYPE_GET_SOURCE_FILES: { + case CMD_TYPE_GET_SOURCE_FILES: + case CMD_TYPE_GET_SOURCE_FILES_2: { gpointer iter = NULL; MonoMethod *method; char *source_file, *base; @@ -5368,6 +5890,8 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (minfo) { mono_debug_symfile_get_line_numbers (minfo, &source_file, NULL, NULL, NULL); + if (!source_file) + continue; for (i = 0; i < files->len; ++i) if (!strcmp (g_ptr_array_index (files, i), source_file)) @@ -5381,9 +5905,13 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf) buffer_add_int (buf, files->len); for (i = 0; i < files->len; ++i) { source_file = g_ptr_array_index (files, i); - base = g_path_get_basename (source_file); - buffer_add_string (buf, base); - g_free (base); + if (command == CMD_TYPE_GET_SOURCE_FILES_2) { + buffer_add_string (buf, source_file); + } else { + base = g_path_get_basename (source_file); + buffer_add_string (buf, base); + g_free (base); + } g_free (source_file); } g_ptr_array_free (files, TRUE); @@ -5408,17 +5936,33 @@ type_commands (int command, guint8 *p, guint8 *end, Buffer *buf) } static ErrorCode -method_commands (int command, guint8 *p, guint8 *end, Buffer *buf) +type_commands (int command, guint8 *p, guint8 *end, Buffer *buf) { - int err; + MonoClass *klass; + MonoDomain *old_domain; MonoDomain *domain; - MonoMethod *method; - MonoMethodHeader *header; + int err; - method = decode_methodid (p, &p, end, &domain, &err); + klass = decode_typeid (p, &p, end, &domain, &err); if (err) return err; + old_domain = mono_domain_get (); + + mono_domain_set (domain, TRUE); + + err = type_commands_internal (command, klass, domain, p, end, buf); + + mono_domain_set (old_domain, TRUE); + + return err; +} + +static ErrorCode +method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, guint8 *p, guint8 *end, Buffer *buf) +{ + MonoMethodHeader *header; + switch (command) { case CMD_METHOD_GET_NAME: { buffer_add_string (buf, method->name); @@ -5448,6 +5992,7 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf) buffer_add_int (buf, header->code_size); buffer_add_string (buf, ""); buffer_add_int (buf, 0); + mono_metadata_free_mh (header); break; } @@ -5464,6 +6009,7 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf) g_free (source_file); g_free (il_offsets); g_free (line_numbers); + mono_metadata_free_mh (header); break; } case CMD_METHOD_GET_PARAM_INFO: { @@ -5494,8 +6040,7 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf) } case CMD_METHOD_GET_LOCALS_INFO: { int i, j, num_locals; - char **local_names; - int *local_indexes; + MonoDebugLocalsInfo *locals; header = mono_method_get_header (method); g_assert (header); @@ -5507,25 +6052,38 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf) buffer_add_typeid (buf, domain, mono_class_from_mono_type (header->locals [i])); /* Names */ - num_locals = mono_debug_lookup_locals (method, &local_names, &local_indexes); + locals = mono_debug_lookup_locals (method); + if (locals) + num_locals = locals->num_locals; + else + num_locals = 0; for (i = 0; i < header->num_locals; ++i) { for (j = 0; j < num_locals; ++j) - if (local_indexes [j] == i) + if (locals->locals [j].index == i) break; if (j < num_locals) - buffer_add_string (buf, local_names [j]); + buffer_add_string (buf, locals->locals [j].name); else buffer_add_string (buf, ""); } - g_free (local_names); - g_free (local_indexes); - /* Live ranges */ - /* FIXME: This works because we set debug_options.mdb_optimizations */ + /* Scopes */ for (i = 0; i < header->num_locals; ++i) { - buffer_add_int (buf, 0); - buffer_add_int (buf, header->code_size); + for (j = 0; j < num_locals; ++j) + if (locals->locals [j].index == i) + break; + if (j < num_locals && locals->locals [j].block) { + buffer_add_int (buf, locals->locals [j].block->start_offset); + buffer_add_int (buf, locals->locals [j].block->end_offset); + } else { + buffer_add_int (buf, 0); + buffer_add_int (buf, header->code_size); + } } + mono_metadata_free_mh (header); + + if (locals) + mono_debug_symfile_free_locals (locals); break; } @@ -5545,6 +6103,7 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf) for (i = 0; i < header->code_size; ++i) buffer_add_byte (buf, header->code [i]); } + mono_metadata_free_mh (header); break; } case CMD_METHOD_RESOLVE_TOKEN: { @@ -5615,6 +6174,29 @@ method_commands (int command, guint8 *p, guint8 *end, Buffer *buf) return ERR_NONE; } +static ErrorCode +method_commands (int command, guint8 *p, guint8 *end, Buffer *buf) +{ + int err; + MonoDomain *old_domain; + MonoDomain *domain; + MonoMethod *method; + + method = decode_methodid (p, &p, end, &domain, &err); + if (err) + return err; + + old_domain = mono_domain_get (); + + mono_domain_set (domain, TRUE); + + err = method_commands_internal (command, method, domain, p, end, buf); + + mono_domain_set (old_domain, TRUE); + + return err; +} + static ErrorCode thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) { @@ -5693,6 +6275,9 @@ thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf) case CMD_THREAD_GET_INFO: buffer_add_byte (buf, thread->threadpool_thread); break; + case CMD_THREAD_GET_ID: + buffer_add_long (buf, (guint64)(gsize)thread); + break; default: return ERR_NOT_IMPLEMENTED; } @@ -5745,7 +6330,9 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) if (!frame->jit) { frame->jit = mono_debug_find_method (frame->method, frame->domain); - g_assert (frame->jit); + if (!frame->jit) + /* This could happen for aot images with no jit debug info */ + return ERR_ABSENT_INFORMATION; } jit = frame->jit; @@ -5775,6 +6362,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) add_var (buf, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE); } } + mono_metadata_free_mh (header); break; } case CMD_STACK_FRAME_GET_THIS: { @@ -5830,6 +6418,7 @@ frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf) set_var (t, var, &frame->ctx, frame->domain, val_buf); } + mono_metadata_free_mh (header); break; } default: @@ -6110,7 +6699,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) @@ -6132,7 +6721,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; } @@ -6247,14 +6836,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