#include <mono/metadata/assembly.h>
#include <mono/metadata/exception.h>
#include <mono/metadata/metadata-internals.h>
-#include <mono/metadata/gc-internals.h>
#include <mono/metadata/appdomain.h>
#include <mono/metadata/mono-debug-debugger.h>
#include <mono/metadata/mono-config.h>
#include <mono/metadata/w32semaphore.h>
#include <mono/metadata/w32event.h>
#include <mono/metadata/w32process.h>
+#include <mono/metadata/w32file.h>
#include <metadata/threads.h>
#include <metadata/profiler-private.h>
#include <mono/metadata/coree.h>
-#include <mono/io-layer/io-layer.h>
//#define DEBUG_DOMAIN_UNLOAD 1
mono_appdomains_unlock ();
#ifdef HAVE_BOEHM_GC
- /*
- * Boehm doesn't like roots inside GC allocated objects, and alloc_fixed returns
- * a GC_MALLOC-ed object, contrary to the api docs. This causes random crashes when
- * running the corlib test suite.
- * To solve this, we pass a NULL descriptor, and don't register roots.
- */
- domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), NULL, MONO_ROOT_SOURCE_DOMAIN, "domain object");
+ domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, "domain object");
#else
domain = (MonoDomain *)mono_gc_alloc_fixed (sizeof (MonoDomain), domain_gc_desc, MONO_ROOT_SOURCE_DOMAIN, "domain object");
mono_gc_register_root ((char*)&(domain->MONO_DOMAIN_FIRST_GC_TRACKED), G_STRUCT_OFFSET (MonoDomain, MONO_DOMAIN_LAST_GC_TRACKED) - G_STRUCT_OFFSET (MonoDomain, MONO_DOMAIN_FIRST_GC_TRACKED), MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_DOMAIN, "misc domain fields");
#ifndef HOST_WIN32
mono_w32handle_init ();
mono_w32handle_namespace_init ();
- wapi_init ();
#endif
mono_w32mutex_init ();
mono_w32semaphore_init ();
mono_w32event_init ();
mono_w32process_init ();
+ mono_w32file_init ();
#ifndef DISABLE_PERFCOUNTERS
mono_perfcounters_init ();
mono_reflection_init ();
mono_runtime_init_tls ();
- /* FIXME: When should we release this memory? */
- MONO_GC_REGISTER_ROOT_FIXED (appdomains_list, MONO_ROOT_SOURCE_DOMAIN, "domains list");
-
domain = mono_domain_create ();
mono_root_domain = domain;
mono_coop_mutex_destroy (&appdomains_mutex);
mono_w32process_cleanup ();
-
-#ifndef HOST_WIN32
- wapi_cleanup ();
-#endif
+ mono_w32file_cleanup ();
}
void
#include <mono/metadata/object-internals.h>
#include <mono/metadata/threads-types.h>
#include <mono/sgen/gc-internal-agnostic.h>
-#include <mono/utils/gc_wrapper.h>
#define mono_domain_finalizers_lock(domain) mono_os_mutex_lock (&(domain)->finalizable_objects_hash_lock);
#define mono_domain_finalizers_unlock(domain) mono_os_mutex_unlock (&(domain)->finalizable_objects_hash_lock);
#define MONO_GC_UNREGISTER_ROOT(x) mono_gc_deregister_root ((char*)&(x))
- /*
- * Register a memory location as a root pointing to memory allocated using
- * mono_gc_alloc_fixed (). This includes MonoGHashTable.
- */
- /* The result of alloc_fixed () is not GC tracked memory */
- #define MONO_GC_REGISTER_ROOT_FIXED(x,src,msg) do { \
- if (!mono_gc_is_moving ()) \
- MONO_GC_REGISTER_ROOT_PINNING ((x),(src),(msg)); \
- } while (0)
-
/*
* Return a GC descriptor for an array containing N pointers to memory allocated
* by mono_gc_alloc_fixed ().
* The memory is non-moving and it will be explicitly deallocated.
* size bytes will be available from the returned address (ie, descr
* must not be stored in the returned memory)
- * NOTE: Under Boehm, this returns memory allocated using GC_malloc, so the result should
- * be stored into a location registered using MONO_GC_REGISTER_ROOT_FIXED ().
*/
void* mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg);
void mono_gc_free_fixed (void* addr);
#include <mono/metadata/assembly.h>
#include <mono/metadata/marshal.h>
#include "mono/metadata/debug-helpers.h"
-#include "mono/metadata/marshal.h"
#include <mono/metadata/threads.h>
#include <mono/metadata/threads-types.h>
#include <mono/metadata/environment.h>
#include "mono/metadata/profiler-private.h"
#include "mono/metadata/security-manager.h"
#include "mono/metadata/mono-debug-debugger.h"
-#include <mono/metadata/gc-internals.h>
#include <mono/metadata/verify-internals.h>
#include <mono/metadata/reflection-internals.h>
#include <mono/metadata/w32event.h>
#include <mono/utils/mono-threads.h>
#include <mono/utils/mono-threads-coop.h>
#include "cominterop.h"
-#include <mono/io-layer/io-layer.h>
+#include <mono/utils/w32api.h>
static void
get_default_field_value (MonoDomain* domain, MonoClassField *field, void *value, MonoError *error);
static char *
mono_string_to_utf8_internal (MonoMemPool *mp, MonoImage *image, MonoString *s, gboolean ignore_error, MonoError *error);
+static void
+array_full_copy_unchecked_size (MonoArray *src, MonoArray *dest, MonoClass *klass, uintptr_t size);
+
static MonoMethod*
class_get_virtual_method (MonoClass *klass, MonoMethod *method, gboolean is_proxy, MonoError *error);
/* Class lazy loading functions */
-static GENERATE_GET_CLASS_WITH_CACHE (pointer, System.Reflection, Pointer)
-static GENERATE_GET_CLASS_WITH_CACHE (remoting_services, System.Runtime.Remoting, RemotingServices)
-static GENERATE_GET_CLASS_WITH_CACHE (unhandled_exception_event_args, System, UnhandledExceptionEventArgs)
-static GENERATE_GET_CLASS_WITH_CACHE (sta_thread_attribute, System, STAThreadAttribute)
-static GENERATE_GET_CLASS_WITH_CACHE (activation_services, System.Runtime.Remoting.Activation, ActivationServices)
+static GENERATE_GET_CLASS_WITH_CACHE (pointer, "System.Reflection", "Pointer")
+static GENERATE_GET_CLASS_WITH_CACHE (remoting_services, "System.Runtime.Remoting", "RemotingServices")
+static GENERATE_GET_CLASS_WITH_CACHE (unhandled_exception_event_args, "System", "UnhandledExceptionEventArgs")
+static GENERATE_GET_CLASS_WITH_CACHE (sta_thread_attribute, "System", "STAThreadAttribute")
+static GENERATE_GET_CLASS_WITH_CACHE (activation_services, "System.Runtime.Remoting.Activation", "ActivationServices")
#define ldstr_lock() mono_os_mutex_lock (&ldstr_section)
MonoNativeThreadId initializing_tid;
guint32 waiting_count;
gboolean done;
- MonoCoopMutex initialization_section;
+ MonoCoopMutex mutex;
+ /* condvar used to wait for 'done' becoming TRUE */
+ MonoCoopCond cond;
} TypeInitializationLock;
/* for locking access to type_initialization_hash and blocked_thread_hash */
{
MONO_REQ_GC_NEUTRAL_MODE;
- mono_coop_mutex_lock (&lock->initialization_section);
+ mono_coop_mutex_lock (&lock->mutex);
}
static void
mono_type_init_unlock (TypeInitializationLock *lock)
{
- mono_coop_mutex_unlock (&lock->initialization_section);
+ mono_coop_mutex_unlock (&lock->mutex);
}
/* from vtable to lock */
mono_error_assert_ok (&error);
}
+/*
+ * Returns TRUE if the lock was freed.
+ * LOCKING: Caller should hold type_initialization_lock.
+ */
+static gboolean
+unref_type_lock (TypeInitializationLock *lock)
+{
+ --lock->waiting_count;
+ if (lock->waiting_count == 0) {
+ mono_coop_mutex_destroy (&lock->mutex);
+ mono_coop_cond_destroy (&lock->cond);
+ g_free (lock);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
/**
* mono_runtime_class_init_full:
* @vtable that neeeds to be initialized
tid = mono_native_thread_id_get ();
+ /*
+ * Due some preprocessing inside a global lock. If we are the first thread
+ * trying to initialize this class, create a separate lock+cond var, and
+ * acquire it before leaving the global lock. The other threads will wait
+ * on this cond var.
+ */
+
mono_type_initialization_lock ();
/* double check... */
if (vtable->initialized) {
return FALSE;
}
}
- lock = (TypeInitializationLock *)g_malloc (sizeof (TypeInitializationLock));
- mono_coop_mutex_init_recursive (&lock->initialization_section);
+ lock = (TypeInitializationLock *)g_malloc0 (sizeof (TypeInitializationLock));
+ mono_coop_mutex_init_recursive (&lock->mutex);
+ mono_coop_cond_init (&lock->cond);
lock->initializing_tid = tid;
lock->waiting_count = 1;
lock->done = FALSE;
gpointer blocked;
TypeInitializationLock *pending_lock;
- if (mono_native_thread_id_equals (lock->initializing_tid, tid) || lock->done) {
+ if (mono_native_thread_id_equals (lock->initializing_tid, tid)) {
mono_type_initialization_unlock ();
return TRUE;
}
if (do_initialization) {
MonoException *exc = NULL;
+ /* We are holding the per-vtable lock, do the actual initialization */
+
mono_threads_begin_abort_protected_block ();
mono_runtime_try_invoke (method, NULL, NULL, (MonoObject**) &exc, error);
- mono_threads_end_abort_protected_block ();
+ gboolean got_pending_interrupt = mono_threads_end_abort_protected_block ();
//exception extracted, error will be set to the right value later
if (exc == NULL && !mono_error_ok (error))//invoking failed but exc was not set
if (last_domain)
mono_domain_set (last_domain, TRUE);
+ /* Signal to the other threads that we are done */
lock->done = TRUE;
+ mono_coop_cond_broadcast (&lock->cond);
+
mono_type_init_unlock (lock);
+
+ //This can happen if the cctor self-aborts
if (exc && mono_object_class (exc) == mono_defaults.threadabortexception_class)
pending_tae = exc;
+
//TAEs are blocked around .cctors, they must escape as soon as no cctor is left to run.
- if (!pending_tae && mono_get_eh_callbacks ()->mono_above_abort_threshold ())
+ if (!pending_tae && got_pending_interrupt)
pending_tae = mono_thread_try_resume_interruption ();
} else {
/* this just blocks until the initializing thread is done */
mono_type_init_lock (lock);
+ while (!lock->done)
+ mono_coop_cond_wait (&lock->cond, &lock->mutex);
mono_type_init_unlock (lock);
}
+ /* Do cleanup and setting vtable->initialized inside the global lock again */
mono_type_initialization_lock ();
- if (!mono_native_thread_id_equals (lock->initializing_tid, tid))
+ if (!do_initialization)
g_hash_table_remove (blocked_thread_hash, GUINT_TO_POINTER (tid));
- --lock->waiting_count;
- if (lock->waiting_count == 0) {
- mono_coop_mutex_destroy (&lock->initialization_section);
+ gboolean deleted = unref_type_lock (lock);
+ if (deleted)
g_hash_table_remove (type_initialization_hash, vtable);
- g_free (lock);
- }
- mono_memory_barrier ();
- if (!vtable->init_failed)
+ /* Have to set this here since we check it inside the global lock */
+ if (do_initialization && !vtable->init_failed)
vtable->initialized = 1;
mono_type_initialization_unlock ();
* and get_type_init_exception_for_class () needs to be aware of this.
*/
vtable->init_failed = 1;
+ mono_coop_cond_broadcast (&lock->cond);
mono_type_init_unlock (lock);
- --lock->waiting_count;
- if (lock->waiting_count == 0) {
- mono_coop_mutex_destroy (&lock->initialization_section);
- g_free (lock);
+ gboolean deleted = unref_type_lock (lock);
+ if (deleted)
return TRUE;
- }
}
return FALSE;
}
type = mono_type_get_underlying_type (field->type);
switch (type->type) {
+ case MONO_TYPE_U:
case MONO_TYPE_I:
case MONO_TYPE_PTR:
case MONO_TYPE_FNPTR:
break;
- /* only UIntPtr is allowed to be GC-tracked and only in mscorlib */
- case MONO_TYPE_U:
- #ifdef HAVE_SGEN_GC
- break;
- #else
- if (klass->image != mono_defaults.corlib)
- break;
- #endif
case MONO_TYPE_STRING:
case MONO_TYPE_SZARRAY:
case MONO_TYPE_CLASS:
size = mono_array_length (src);
g_assert (size == mono_array_length (dest));
size *= mono_array_element_size (klass);
+
+ array_full_copy_unchecked_size (src, dest, klass, size);
+}
+
+static void
+array_full_copy_unchecked_size (MonoArray *src, MonoArray *dest, MonoClass *klass, uintptr_t size)
+{
#ifdef HAVE_SGEN_GC
if (klass->element_class->valuetype) {
if (klass->element_class->has_references)
* This routine returns a copy of the array that is hosted on the
* specified MonoDomain. On failure returns NULL and sets @error.
*/
-MonoArray*
-mono_array_clone_in_domain (MonoDomain *domain, MonoArray *array, MonoError *error)
+MonoArrayHandle
+mono_array_clone_in_domain (MonoDomain *domain, MonoArrayHandle array_handle, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
- MonoArray *o;
- uintptr_t size, i;
- uintptr_t *sizes;
- MonoClass *klass = array->obj.vtable->klass;
+ MonoArrayHandle result = MONO_HANDLE_NEW (MonoArray, NULL);
+ uintptr_t size = 0;
+ MonoClass *klass = mono_handle_class (array_handle);
mono_error_init (error);
- if (array->bounds == NULL) {
- size = mono_array_length (array);
- o = mono_array_new_full_checked (domain, klass, &size, NULL, error);
- return_val_if_nok (error, NULL);
-
- size *= mono_array_element_size (klass);
-#ifdef HAVE_SGEN_GC
- if (klass->element_class->valuetype) {
- if (klass->element_class->has_references)
- mono_value_copy_array (o, 0, mono_array_addr_with_size_fast (array, 0, 0), mono_array_length (array));
- else
- mono_gc_memmove_atomic (&o->vector, &array->vector, size);
- } else {
- mono_array_memcpy_refs (o, 0, array, 0, mono_array_length (array));
- }
-#else
- mono_gc_memmove_atomic (&o->vector, &array->vector, size);
-#endif
- return o;
- }
+ /* Pin source array here - if bounds is non-NULL, it's a pointer into the object data */
+ uint32_t src_handle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, array_handle), TRUE);
- sizes = (uintptr_t *)alloca (klass->rank * sizeof(intptr_t) * 2);
- size = mono_array_element_size (klass);
- for (i = 0; i < klass->rank; ++i) {
- sizes [i] = array->bounds [i].length;
- size *= array->bounds [i].length;
- sizes [i + klass->rank] = array->bounds [i].lower_bound;
- }
- o = mono_array_new_full_checked (domain, klass, sizes, (intptr_t*)sizes + klass->rank, error);
- return_val_if_nok (error, NULL);
-#ifdef HAVE_SGEN_GC
- if (klass->element_class->valuetype) {
- if (klass->element_class->has_references)
- mono_value_copy_array (o, 0, mono_array_addr_with_size_fast (array, 0, 0), mono_array_length (array));
- else
- mono_gc_memmove_atomic (&o->vector, &array->vector, size);
+ MonoArrayBounds *array_bounds = MONO_HANDLE_GETVAL (array_handle, bounds);
+ MonoArrayHandle o;
+ if (array_bounds == NULL) {
+ size = mono_array_handle_length (array_handle);
+ o = mono_array_new_full_handle (domain, klass, &size, NULL, error);
+ if (!is_ok (error))
+ goto leave;
+ size *= mono_array_element_size (klass);
} else {
- mono_array_memcpy_refs (o, 0, array, 0, mono_array_length (array));
+ uintptr_t *sizes = (uintptr_t *)alloca (klass->rank * sizeof (uintptr_t));
+ intptr_t *lower_bounds = (intptr_t *)alloca (klass->rank * sizeof (intptr_t));
+ size = mono_array_element_size (klass);
+ for (int i = 0; i < klass->rank; ++i) {
+ sizes [i] = array_bounds [i].length;
+ size *= array_bounds [i].length;
+ lower_bounds [i] = array_bounds [i].lower_bound;
+ }
+ o = mono_array_new_full_handle (domain, klass, sizes, lower_bounds, error);
+ if (!is_ok (error))
+ goto leave;
}
-#else
- mono_gc_memmove_atomic (&o->vector, &array->vector, size);
-#endif
- return o;
+ uint32_t dst_handle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, o), TRUE);
+ array_full_copy_unchecked_size (MONO_HANDLE_RAW (array_handle), MONO_HANDLE_RAW (o), klass, size);
+ mono_gchandle_free (dst_handle);
+
+ MONO_HANDLE_ASSIGN (result, o);
+
+leave:
+ mono_gchandle_free (src_handle);
+ return result;
}
/**
* failure returns NULL and sets @error.
*/
MonoArray*
-mono_array_clone_checked (MonoArray *array, MonoError *error)
+mono_array_clone_checked (MonoArray *array_raw, MonoError *error)
{
-
MONO_REQ_GC_UNSAFE_MODE;
- return mono_array_clone_in_domain (((MonoObject *)array)->vtable->domain, array, error);
+ HANDLE_FUNCTION_ENTER ();
+ /* FIXME: callers of mono_array_clone_checked should use handles */
+ mono_error_init (error);
+ MONO_HANDLE_DCL (MonoArray, array);
+ MonoArrayHandle result = mono_array_clone_in_domain (MONO_HANDLE_DOMAIN (array), array, error);
+ HANDLE_FUNCTION_RETURN_OBJ (result);
}
/* helper macros to check for overflow when calculating the size of arrays */
return s;
}
+/**
+ * mono_string_new_utf16_handle:
+ * @text: a pointer to an utf16 string
+ * @len: the length of the string
+ * @error: written on error.
+ *
+ * Returns: A newly created string object which contains @text.
+ * On error, returns NULL and sets @error.
+ */
+MonoStringHandle
+mono_string_new_utf16_handle (MonoDomain *domain, const guint16 *text, gint32 len, MonoError *error)
+{
+ return MONO_HANDLE_NEW (MonoString, mono_string_new_utf16_checked (domain, text, len, error));
+}
+
/**
* mono_string_new_utf32:
* @text: a pointer to an utf32 string
* Same as mono_string_to_utf8, but allocate the string from the image mempool.
*/
char *
-mono_string_to_utf8_image (MonoImage *image, MonoString *s, MonoError *error)
+mono_string_to_utf8_image (MonoImage *image, MonoStringHandle s, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
- return mono_string_to_utf8_internal (NULL, image, s, FALSE, error);
+ return mono_string_to_utf8_internal (NULL, image, MONO_HANDLE_RAW (s), FALSE, error); /* FIXME pin the string */
}
/**
return s->length;
}
+/**
+ * mono_string_handle_length:
+ * @s: MonoString
+ *
+ * Returns the lenght in characters of the string
+ */
+int
+mono_string_handle_length (MonoStringHandle s)
+{
+ MONO_REQ_GC_UNSAFE_MODE;
+
+ return MONO_HANDLE_GETVAL (s, length);
+}
+
+
/**
* mono_array_length:
* @array: a MonoArray*
#include <mono/metadata/gc-internals.h>
#include <mono/metadata/marshal.h>
#include <mono/metadata/runtime.h>
-#include <mono/io-layer/io-layer.h>
#include <mono/metadata/object-internals.h>
#include <mono/metadata/mono-debug-debugger.h>
#include <mono/utils/monobitset.h>
#include <mono/utils/mono-tls.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-memory-model.h>
-#include <mono/utils/mono-threads-coop.h>
#include <mono/utils/mono-error-internals.h>
#include <mono/utils/os-event.h>
#include <mono/utils/mono-threads-debug.h>
#include <mono/metadata/w32event.h>
#include <mono/metadata/w32mutex.h>
-#include <mono/metadata/gc-internals.h>
#include <mono/metadata/reflection-internals.h>
#include <mono/metadata/abi-details.h>
+#include <mono/metadata/w32error.h>
+#include <mono/utils/w32api.h>
#ifdef HAVE_SIGNAL_H
#include <signal.h>
static gint32 managed_thread_id_counter = 0;
/* Class lazy loading functions */
-static GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, System, AppDomainUnloadedException)
+static GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")
static void
mono_threads_lock (void)
return InterlockedIncrement (&managed_thread_id_counter);
}
+enum {
+ INTERRUPT_REQUESTED_BIT = 0x1,
+ INTERRUPT_REQUEST_DEFERRED_BIT = 0x2,
+ ABORT_PROT_BLOCK_SHIFT = 2,
+ ABORT_PROT_BLOCK_BITS = 8,
+ ABORT_PROT_BLOCK_MASK = (((1 << ABORT_PROT_BLOCK_BITS) - 1) << ABORT_PROT_BLOCK_SHIFT)
+};
+
+static int
+mono_thread_get_abort_prot_block_count (MonoInternalThread *thread)
+{
+ gsize state = thread->thread_state;
+ return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
+}
+
+static void
+verify_thread_state (gsize state)
+{
+ //can't have both INTERRUPT_REQUESTED_BIT and INTERRUPT_REQUEST_DEFERRED_BIT set at the same time
+ g_assert ((state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)) != (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT));
+
+ //XXX This would be nice to be true, but can happen due to self-aborts (and possibly set-pending-exception)
+ //if prot_count > 0, INTERRUPT_REQUESTED_BIT must never be set
+ // int prot_count = (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
+ // g_assert (!(prot_count > 0 && (state & INTERRUPT_REQUESTED_BIT)));
+}
+
+void
+mono_threads_begin_abort_protected_block (void)
+{
+ MonoInternalThread *thread = mono_thread_internal_current ();
+ gsize old_state, new_state;
+ do {
+ old_state = thread->thread_state;
+ verify_thread_state (old_state);
+
+ int new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
+
+ new_state = 0;
+ if (old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)) {
+ if (old_state & INTERRUPT_REQUESTED_BIT)
+ printf ("begin prot happy as it demoted interrupt to deferred interrupt\n");
+ new_state |= INTERRUPT_REQUEST_DEFERRED_BIT;
+ }
+
+ //bounds check abort_prot_count
+ g_assert (new_val > 0);
+ g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
+ new_state |= new_val << ABORT_PROT_BLOCK_SHIFT;
+
+ } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+}
+
+gboolean
+mono_threads_end_abort_protected_block (void)
+{
+ MonoInternalThread *thread = mono_thread_internal_current ();
+ gsize old_state, new_state;
+ do {
+ old_state = thread->thread_state;
+ verify_thread_state (old_state);
+
+ int new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
+ new_state = 0;
+
+ if ((old_state & INTERRUPT_REQUEST_DEFERRED_BIT) && new_val == 0) {
+ printf ("end abort on alert, promoted deferred to pront interrupt\n");
+ new_state |= INTERRUPT_REQUESTED_BIT;
+ }
+
+ //bounds check abort_prot_count
+ g_assert (new_val >= 0);
+ g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
+ new_state |= new_val << ABORT_PROT_BLOCK_SHIFT;
+
+ } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+ return (new_state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
+}
+
+
+//Don't use this function, use inc/dec below
+static void
+mono_thread_abort_prot_block_count_add (MonoInternalThread *thread, int val)
+{
+ gsize old_state, new_state;
+ do {
+ old_state = thread->thread_state;
+ verify_thread_state (old_state);
+
+ int new_val = val + ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT);
+ //bounds check abort_prot_count
+ g_assert (new_val >= 0);
+ g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
+ new_state = (old_state & ~ABORT_PROT_BLOCK_MASK) | (new_val << ABORT_PROT_BLOCK_SHIFT);
+
+ } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+}
+
+static void
+mono_thread_inc_abort_prot_block_count (MonoInternalThread *thread)
+{
+ mono_thread_abort_prot_block_count_add (thread, 1);
+}
+
+static void
+mono_thread_dec_abort_prot_block_count (MonoInternalThread *thread)
+{
+ mono_thread_abort_prot_block_count_add (thread, -1);
+}
+
+static gboolean
+mono_thread_get_interruption_requested (MonoInternalThread *thread)
+{
+ gsize state = thread->thread_state;
+ return (state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
+}
+
+/* Returns TRUE is there was a state change */
+static gboolean
+mono_thread_clear_interruption_requested (MonoInternalThread *thread)
+{
+ gsize old_state, new_state;
+ do {
+ old_state = thread->thread_state;
+ verify_thread_state (old_state);
+
+ //Already cleared
+ if (!(old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)))
+ return FALSE;
+ new_state = old_state & ~(INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT);
+ } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+ return TRUE;
+}
+
+/* Returns TRUE is there was a state change */
+static gboolean
+mono_thread_set_interruption_requested (MonoInternalThread *thread)
+{
+ //always force when the current thread is doing it to itself.
+ gboolean force_interrupt = thread == mono_thread_internal_current ();
+ gsize old_state, new_state;
+ do {
+ old_state = thread->thread_state;
+ verify_thread_state (old_state);
+
+ int prot_count = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT);
+ //Already set
+ if (old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT))
+ return FALSE;
+
+ //If there's an outstanding prot block, we queue it
+ if (prot_count && !force_interrupt) {
+ printf ("set interrupt unhappy, as it's only putting a deferred req %d\n", force_interrupt);
+ new_state = old_state | INTERRUPT_REQUEST_DEFERRED_BIT;
+ } else
+ new_state = old_state | INTERRUPT_REQUESTED_BIT;
+ } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+
+ return (new_state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
+}
+
static inline MonoNativeThreadId
thread_get_tid (MonoInternalThread *thread)
{
param.sched_priority = 0;
break;
default:
- g_error ("%s: unknown policy %d", __func__, policy);
+ g_warning ("%s: unknown policy %d", __func__, policy);
+ return;
}
}
}
if (!threads) {
- MONO_GC_REGISTER_ROOT_FIXED (threads, MONO_ROOT_SOURCE_THREADING, "threads table");
threads = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "threads table");
}
return FALSE;
}
if (threads_starting_up == NULL) {
- MONO_GC_REGISTER_ROOT_FIXED (threads_starting_up, MONO_ROOT_SOURCE_THREADING, "starting threads table");
threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "starting threads table");
}
mono_g_hash_table_insert (threads_starting_up, thread, thread);
mono_threads_lock ();
mono_g_hash_table_remove (threads_starting_up, thread);
mono_threads_unlock ();
- mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", GetLastError());
+ mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", mono_w32error_get_last());
/* ref is not going to be decremented in start_wrapper_internal */
InterlockedDecrement (&start_info->ref);
ret = FALSE;
Leaving the counter unbalanced will cause a performance degradation since all threads
will now keep checking their local flags all the time.
*/
- if (InterlockedExchange (&thread->interruption_requested, 0) != 0)
+ if (mono_thread_clear_interruption_requested (thread))
InterlockedDecrement (&thread_interruption_requested);
mono_threads_lock ();
}
void
-mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, MonoError *error)
+mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error)
{
LOCK_THREAD (this_obj);
mono_error_init (error);
- if ((this_obj->flags & MONO_THREAD_FLAG_NAME_SET)) {
+ if (reset) {
+ this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET;
+ } else if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) {
UNLOCK_THREAD (this_obj);
mono_error_set_invalid_operation (error, "Thread.Name can only be set once.");
this_obj->name_len = 0;
}
if (name) {
- this_obj->name = g_new (gunichar2, mono_string_length (name));
- memcpy (this_obj->name, mono_string_chars (name), mono_string_length (name) * 2);
+ this_obj->name = g_memdup (mono_string_chars (name), mono_string_length (name) * sizeof (gunichar2));
this_obj->name_len = mono_string_length (name);
if (permanent)
ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
{
MonoError error;
- mono_thread_set_name_internal (this_obj, name, TRUE, &error);
+ mono_thread_set_name_internal (this_obj, name, TRUE, FALSE, &error);
mono_error_set_pending_exception (&error);
}
LOCK_THREAD (thread);
/* MonoThread::interruption_requested can only be changed with atomics */
- if (InterlockedCompareExchange (&thread->interruption_requested, FALSE, TRUE)) {
+ if (mono_thread_clear_interruption_requested (thread)) {
/* this will consume pending APC calls */
#ifdef HOST_WIN32
WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
thread->state & ThreadState_Background)
ExitThread (1);
#endif
- if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
+ if (!mono_thread_set_interruption_requested (thread))
return NULL;
InterlockedIncrement (&thread_interruption_requested);
if (!still_aborting)
return FALSE;
- if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
+ if (!mono_thread_set_interruption_requested (thread))
return NULL;
InterlockedIncrement (&thread_interruption_requested);
MonoInternalThread *thread = mono_thread_internal_current ();
/* The thread may already be stopping */
if (thread != NULL)
- return (thread->interruption_requested);
+ return mono_thread_get_interruption_requested (thread);
}
return FALSE;
}
/* The thread may already be stopping */
if (!thread)
return NULL;
- if (!thread->interruption_requested)
+ if (!mono_thread_get_interruption_requested (thread))
return NULL;
if (!bypass_abort_protection && is_running_protected_wrapper ())
return NULL;
The target thread is running at least one protected block, which must not be interrupted, so we give up.
The protected block code will give them a chance when appropriate.
*/
- if (thread->abort_protected_block_count)
+ if (mono_thread_get_abort_prot_block_count (thread) > 0)
return MonoResumeThread;
/*someone is already interrupting it*/
- if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
+ if (!mono_thread_set_interruption_requested (thread))
return MonoResumeThread;
InterlockedIncrement (&thread_interruption_requested);
return KeepSuspended;
}
} else {
- if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
+ if (mono_thread_set_interruption_requested (thread))
InterlockedIncrement (&thread_interruption_requested);
if (data->interrupt)
data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
if (thread != pthread_self ()) {
MONO_ENTER_GC_SAFE;
/* This shouldn't block */
+ mono_threads_join_lock ();
mono_native_thread_join (thread);
+ mono_threads_join_unlock ();
MONO_EXIT_GC_SAFE;
}
} else {
}
}
-void
-mono_threads_begin_abort_protected_block (void)
-{
- MonoInternalThread *thread;
-
- thread = mono_thread_internal_current ();
- ++thread->abort_protected_block_count;
- mono_memory_barrier ();
-}
-
-void
-mono_threads_end_abort_protected_block (void)
-{
- MonoInternalThread *thread;
-
- thread = mono_thread_internal_current ();
-
- mono_memory_barrier ();
- --thread->abort_protected_block_count;
-}
-
MonoException*
mono_thread_try_resume_interruption (void)
{
MonoInternalThread *thread;
thread = mono_thread_internal_current ();
- if (thread->abort_protected_block_count || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
+ if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
+ return NULL;
+ if (mono_thread_get_abort_prot_block_count (thread) > 0 || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
return NULL;
return mono_thread_resume_interruption ();
return FALSE;
}
- if (thread->abort_protected_block_count || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
+ if (mono_thread_get_abort_prot_block_count (thread) || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
UNLOCK_THREAD (thread);
return FALSE;
}
#include <mono/metadata/environment.h>
#include <mono/metadata/threads-types.h>
#include <mono/metadata/threadpool.h>
-#include <mono/metadata/socket-io.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/runtime.h>
#include <mono/metadata/verify-internals.h>
#include <mono/metadata/reflection-internals.h>
+#include <mono/metadata/w32socket.h>
#include <mono/utils/mono-coop-mutex.h>
#include <mono/utils/mono-coop-semaphore.h>
#include <mono/utils/mono-error-internals.h>
#include "debugger-agent.h"
#include "mini.h"
#include "seq-points.h"
-#include <mono/io-layer/io-layer.h>
+#include <mono/utils/w32api.h>
/*
* On iOS we can't use System.Environment.Exit () as it will do the wrong
#define HEADER_LENGTH 11
#define MAJOR_VERSION 2
-#define MINOR_VERSION 44
+#define MINOR_VERSION 45
typedef enum {
CMD_SET_VM = 1,
CMD_ASSEMBLY_GET_MANIFEST_MODULE = 3,
CMD_ASSEMBLY_GET_OBJECT = 4,
CMD_ASSEMBLY_GET_TYPE = 5,
- CMD_ASSEMBLY_GET_NAME = 6
+ CMD_ASSEMBLY_GET_NAME = 6,
+ CMD_ASSEMBLY_GET_DOMAIN = 7
} CmdAssembly;
typedef enum {
int nframes;
/* If set, don't stop in methods that are not part of user assemblies */
MonoAssembly** user_assemblies;
+ /* Used to distinguish stepping breakpoint hits in parallel tasks executions */
+ int async_id;
+ /* Used to know if we are in process of async step-out and distishing from exception breakpoints */
+ MonoMethod* async_stepout_method;
} SingleStepReq;
/*
if (pos == NULL || pos == address)
return 1;
- *host = (char *)g_malloc (pos - address + 1);
- strncpy (*host, address, pos - address);
- (*host) [pos - address] = '\0';
+ size_t len = pos - address;
+ *host = (char *)g_malloc (len + 1);
+ memcpy (*host, address, len);
+ (*host) [len] = '\0';
*port = atoi (pos + 1);
mono_gc_base_init ();
thread_to_tls = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "thread-to-tls table");
- MONO_GC_REGISTER_ROOT_FIXED (thread_to_tls, MONO_ROOT_SOURCE_DEBUGGER, "thread-to-tls table");
tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread table");
- MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread table");
tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread object table");
- MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread_obj, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread object table");
pending_assembly_loads = g_ptr_array_new ();
domains = g_hash_table_new (mono_aligned_addr_hash, NULL);
objrefs = g_hash_table_new_full (NULL, NULL, NULL, free_objref);
obj_to_objref = g_hash_table_new (NULL, NULL);
suspended_objs = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "suspended objects table");
- MONO_GC_REGISTER_ROOT_FIXED (suspended_objs, MONO_ROOT_SOURCE_DEBUGGER, "suspended objects table");
}
static void
static void
send_types_for_domain (MonoDomain *domain, void *user_data)
{
+ MonoDomain* old_domain;
AgentDomainInfo *info = NULL;
info = get_agent_domain_info (domain);
g_assert (info);
+
+ old_domain = mono_domain_get ();
+
+ mono_domain_set (domain, TRUE);
mono_loader_lock ();
g_hash_table_foreach (info->loaded_classes, emit_type_load, NULL);
mono_loader_unlock ();
+
+ mono_domain_set (old_domain, TRUE);
+}
+
+static void
+send_assemblies_for_domain (MonoDomain *domain, void *user_data)
+{
+ GSList *tmp;
+ MonoDomain* old_domain;
+
+ old_domain = mono_domain_get ();
+
+ mono_domain_set (domain, TRUE);
+
+ mono_domain_assemblies_lock (domain);
+ for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
+ MonoAssembly* ass = (MonoAssembly *)tmp->data;
+ emit_assembly_load (ass, NULL);
+ }
+ mono_domain_assemblies_unlock (domain);
+
+ mono_domain_set (old_domain, TRUE);
}
static void
compute_frame_info (tls->thread, tls);
}
+static gboolean
+ensure_jit (StackFrame* frame)
+{
+ if (!frame->jit) {
+ frame->jit = mono_debug_find_method (frame->api_method, frame->domain);
+ if (!frame->jit && frame->api_method->is_inflated)
+ frame->jit = mono_debug_find_method(mono_method_get_declaring_generic_method (frame->api_method), frame->domain);
+ if (!frame->jit) {
+ char *s;
+
+ /* This could happen for aot images with no jit debug info */
+ s = mono_method_full_name (frame->api_method, TRUE);
+ DEBUG_PRINTF(1, "[dbg] No debug information found for '%s'.\n", s);
+ g_free (s);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
/*
* ss_update:
*
* Return FALSE if single stepping needs to continue.
*/
static gboolean
-ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx)
+ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx, MonoMethod* method)
{
MonoDebugMethodInfo *minfo;
MonoDebugSourceLocation *loc = NULL;
gboolean hit = TRUE;
- MonoMethod *method;
+
+ if (req->async_stepout_method == method) {
+ DEBUG_PRINTF (1, "[%p] Breakpoint hit during async step-out at %s hit, continuing stepping out.\n", (gpointer)(gsize)mono_native_thread_id_get (), method->name);
+ return FALSE;
+ }
if (req->depth == STEP_DEPTH_OVER && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK)) {
/*
return FALSE;
}
- if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit) {
+ if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit && !req->async_stepout_method) {
gboolean is_step_out = req->depth == STEP_DEPTH_OUT;
ss_calculate_framecount (tls, ctx);
}
if (req->depth == STEP_DEPTH_INTO && req->size == STEP_SIZE_MIN && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) && ss_req->start_method){
- method = jinfo_get_method (ji);
ss_calculate_framecount (tls, ctx);
if (ss_req->start_method == method && req->nframes && tls->frame_count == req->nframes) {//Check also frame count(could be recursion)
DEBUG_PRINTF (1, "[%p] Seq point at nonempty stack %x while stepping in, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), sp->il_offset);
}
}
+ MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
+ if (asyncMethod) {
+ for (int i = 0; i < asyncMethod->num_awaits; i++)
+ {
+ if (asyncMethod->yield_offsets[i] == sp->il_offset || asyncMethod->resume_offsets[i] == sp->il_offset) {
+ mono_debug_free_method_async_debug_info (asyncMethod);
+ return FALSE;
+ }
+ }
+ mono_debug_free_method_async_debug_info (asyncMethod);
+ }
+
if (req->size != STEP_SIZE_LINE)
return TRUE;
/* Have to check whenever a different source line was reached */
- method = jinfo_get_method (ji);
minfo = mono_debug_lookup_method (method);
if (minfo)
return bp->method && bp->method->klass->image->assembly == assembly;
}
+static MonoObject*
+get_this (StackFrame *frame)
+{
+ //Logic inspiered by "add_var" method and took out path that happens in async method for getting this
+ MonoDebugVarInfo *var = frame->jit->this_var;
+ if ((var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS) != MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET)
+ return NULL;
+
+ guint8 * addr = (guint8 *)mono_arch_context_get_int_reg (&frame->ctx, var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS);
+ addr += (gint32)var->offset;
+ return *(MonoObject**)addr;
+}
+
+//This ID is used to figure out if breakpoint hit on resumeOffset belongs to us or not
+//since thread probably changed...
+static int
+get_this_async_id (StackFrame *frame)
+{
+ return get_objid (get_this (frame));
+}
+
+static MonoMethod* set_notification_method_cache = NULL;
+
+static MonoMethod*
+get_set_notification_method ()
+{
+ if(set_notification_method_cache != NULL)
+ return set_notification_method_cache;
+ MonoError error;
+ MonoClass* async_builder_class = mono_class_load_from_name (mono_defaults.corlib, "System.Runtime.CompilerServices", "AsyncTaskMethodBuilder");
+ GPtrArray* array = mono_class_get_methods_by_name (async_builder_class, "SetNotificationForWaitCompletion", 0x24, FALSE, FALSE, &error);
+ mono_error_assert_ok (&error);
+ g_assert (array->len == 1);
+ set_notification_method_cache = (MonoMethod *)g_ptr_array_index (array, 0);
+ g_ptr_array_free (array, TRUE);
+ return set_notification_method_cache;
+}
+
+static void
+set_set_notification_for_wait_completion_flag (StackFrame *frame)
+{
+ MonoObject* obj = get_this (frame);
+ g_assert (obj);
+ MonoClassField *builder_field = mono_class_get_field_from_name (obj->vtable->klass, "<>t__builder");
+ g_assert (builder_field);
+ MonoObject* builder;
+ MonoError error;
+ builder = mono_field_get_value_object_checked (frame->domain, builder_field, obj, &error);
+ mono_error_assert_ok (&error);
+ g_assert (builder);
+
+ void* args [1];
+ gboolean arg = TRUE;
+ args [0] = &arg;
+ mono_runtime_invoke_checked (get_set_notification_method(), mono_object_unbox (builder), args, &error);
+ mono_error_assert_ok (&error);
+ mono_field_set_value (obj, builder_field, mono_object_unbox (builder));
+}
+
+static MonoMethod* notify_debugger_of_wait_completion_method_cache = NULL;
+
+static MonoMethod*
+get_notify_debugger_of_wait_completion_method ()
+{
+ if (notify_debugger_of_wait_completion_method_cache != NULL)
+ return notify_debugger_of_wait_completion_method_cache;
+ MonoError error;
+ MonoClass* task_class = mono_class_load_from_name (mono_defaults.corlib, "System.Threading.Tasks", "Task");
+ GPtrArray* array = mono_class_get_methods_by_name (task_class, "NotifyDebuggerOfWaitCompletion", 0x24, FALSE, FALSE, &error);
+ mono_error_assert_ok (&error);
+ g_assert (array->len == 1);
+ notify_debugger_of_wait_completion_method_cache = (MonoMethod *)g_ptr_array_index (array, 0);
+ g_ptr_array_free (array, TRUE);
+ return notify_debugger_of_wait_completion_method_cache;
+}
+
static void
process_breakpoint_inner (DebuggerTlsData *tls, gboolean from_signal)
{
SingleStepReq *ss_req = (SingleStepReq *)req->info;
gboolean hit;
- if (mono_thread_internal_current () != ss_req->thread)
- continue;
+ //if we hit async_stepout_method, it's our no matter which thread
+ if ((ss_req->async_stepout_method != method) && (ss_req->async_id || mono_thread_internal_current () != ss_req->thread)) {
+ //We have different thread and we don't have async stepping in progress
+ //it's breakpoint in parallel thread, ignore it
+ if (ss_req->async_id == 0)
+ continue;
+
+ tls->context.valid = FALSE;
+ tls->async_state.valid = FALSE;
+ invalidate_frames (tls);
+ ss_calculate_framecount(tls, ctx);
+ //make sure we have enough data to get current async method instance id
+ if (tls->frame_count == 0 || !ensure_jit (tls->frames [0]))
+ continue;
- hit = ss_update (ss_req, ji, &sp, tls, ctx);
+ //Check method is async before calling get_this_async_id
+ MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
+ if (!asyncMethod)
+ continue;
+ else
+ mono_debug_free_method_async_debug_info (asyncMethod);
+
+ //breakpoint was hit in parallelly executing async method, ignore it
+ if (ss_req->async_id != get_this_async_id (tls->frames [0]))
+ continue;
+ }
+
+ //Update stepping request to new thread/frame_count that we are continuing on
+ //so continuing with normal stepping works as expected
+ if (ss_req->async_stepout_method || ss_req->async_id) {
+ tls->context.valid = FALSE;
+ tls->async_state.valid = FALSE;
+ invalidate_frames (tls);
+ ss_calculate_framecount (tls, ctx);
+ ss_req->thread = mono_thread_internal_current ();
+ ss_req->nframes = tls->frame_count;
+ }
+
+ hit = ss_update (ss_req, ji, &sp, tls, ctx, method);
if (hit)
g_ptr_array_add (ss_reqs, req);
il_offset = sp.il_offset;
- if (!ss_update (ss_req, ji, &sp, tls, ctx))
+ if (!ss_update (ss_req, ji, &sp, tls, ctx, method))
return;
/* Start single stepping again from the current sequence point */
ss_req->bps = NULL;
}
+ ss_req->async_id = 0;
+ ss_req->async_stepout_method = NULL;
if (ss_req->global) {
stop_single_stepping ();
ss_req->global = FALSE;
}
}
+static gboolean
+is_last_non_empty (SeqPoint* sp, MonoSeqPointInfo *info)
+{
+ if (!sp->next_len)
+ return TRUE;
+ SeqPoint* next = g_new (SeqPoint, sp->next_len);
+ mono_seq_point_init_next (info, *sp, next);
+ for (int i = 0; i < sp->next_len; i++) {
+ if (next [i].flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) {
+ if (!is_last_non_empty (&next [i], info)) {
+ g_free (next);
+ return FALSE;
+ }
+ } else {
+ g_free (next);
+ return FALSE;
+ }
+ }
+ g_free (next);
+ return TRUE;
+}
+
/*
* ss_start:
*
nframes = tls->frame_count;
}
+ MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
+
/* Need to stop in catch clauses as well */
for (i = ss_req->depth == STEP_DEPTH_OUT ? 1 : 0; i < nframes; ++i) {
StackFrame *frame = frames [i];
if (frame->ji) {
MonoJitInfo *jinfo = frame->ji;
for (j = 0; j < jinfo->num_clauses; ++j) {
+ // In case of async method we don't want to place breakpoint on last catch handler(which state machine added for whole method)
+ if (asyncMethod && asyncMethod->num_awaits && i == 0 && j + 1 == jinfo->num_clauses)
+ break;
MonoJitExceptionInfo *ei = &jinfo->clauses [j];
if (mono_find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL, &local_sp))
}
}
+ if (asyncMethod && asyncMethod->num_awaits && nframes && ensure_jit (frames [0])) {
+ //asyncMethod has value and num_awaits > 0, this means we are inside async method with awaits
+
+ // Check if we hit yield_offset during normal stepping, because if we did...
+ // Go into special async stepping mode which places breakpoint on resumeOffset
+ // of this await call and sets async_id so we can distinguish it from parallel executions
+ for (i = 0; i < asyncMethod->num_awaits; i++) {
+ if (sp->il_offset == asyncMethod->yield_offsets [i]) {
+ ss_req->async_id = get_this_async_id (frames [0]);
+ ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, asyncMethod->resume_offsets [i]);
+ if (ss_req_bp_cache)
+ g_hash_table_destroy (ss_req_bp_cache);
+ mono_debug_free_method_async_debug_info (asyncMethod);
+ return;
+ }
+ }
+ //If we are at end of async method and doing step-in or step-over...
+ //Switch to step-out, so whole NotifyDebuggerOfWaitCompletion magic happens...
+ if (is_last_non_empty (sp, info)) {
+ ss_req->depth = STEP_DEPTH_OUT;//setting depth to step-out is important, don't inline IF, because code later depends on this
+ }
+ if (ss_req->depth == STEP_DEPTH_OUT) {
+ set_set_notification_for_wait_completion_flag (frames [0]);
+ ss_req->async_id = get_this_async_id (frames [0]);
+ ss_req->async_stepout_method = get_notify_debugger_of_wait_completion_method ();
+ ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, ss_req->async_stepout_method, 0);
+ if (ss_req_bp_cache)
+ g_hash_table_destroy (ss_req_bp_cache);
+ mono_debug_free_method_async_debug_info (asyncMethod);
+ return;
+ }
+ }
+
+ if (asyncMethod)
+ mono_debug_free_method_async_debug_info (asyncMethod);
+
/*
- * Find the first sequence point in the current or in a previous frame which
- * is not the last in its method.
- */
+ * Find the first sequence point in the current or in a previous frame which
+ * is not the last in its method.
+ */
if (ss_req->depth == STEP_DEPTH_OUT) {
/* Ignore seq points in current method */
while (frame_index < nframes) {
break;
case EVENT_KIND_ASSEMBLY_LOAD:
/* Emit load events for currently loaded assemblies */
- mono_assembly_foreach (emit_assembly_load, NULL);
+ mono_domain_foreach (send_assemblies_for_domain, NULL);
break;
case EVENT_KIND_THREAD_START:
/* Emit start events for currently started threads */
mono_error_cleanup (&error);
return err;
}
+ case CMD_ASSEMBLY_GET_DOMAIN: {
+ buffer_add_domainid (buf, domain);
+ break;
+ }
case CMD_ASSEMBLY_GET_TYPE: {
MonoError error;
char *s = decode_string (p, &p, end);
if (!frame->has_ctx)
return ERR_ABSENT_INFORMATION;
- if (!frame->jit) {
- frame->jit = mono_debug_find_method (frame->api_method, frame->domain);
- if (!frame->jit && frame->api_method->is_inflated)
- frame->jit = mono_debug_find_method (mono_method_get_declaring_generic_method (frame->api_method), frame->domain);
- if (!frame->jit) {
- char *s;
+ if (!ensure_jit (frame))
+ return ERR_ABSENT_INFORMATION;
- /* This could happen for aot images with no jit debug info */
- s = mono_method_full_name (frame->api_method, TRUE);
- DEBUG_PRINTF (1, "[dbg] No debug information found for '%s'.\n", s);
- g_free (s);
- return ERR_ABSENT_INFORMATION;
- }
- }
jit = frame->jit;
sig = mono_method_signature (frame->actual_method);
"GET_MANIFEST_MODULE",
"GET_OBJECT",
"GET_TYPE",
- "GET_NAME"
+ "GET_NAME",
+ "GET_DOMAIN"
};
static const char* module_cmds_str[] = {
debugger_thread_id = mono_native_thread_id_get ();
MonoThread *thread = mono_thread_attach (mono_get_root_domain ());
- mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Debugger agent"), TRUE, &error);
+ mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Debugger agent"), TRUE, FALSE, &error);
mono_error_assert_ok (&error);
thread->internal_thread->state |= ThreadState_Background;