diff --git a/src/coreclr/scripts/genEventPipe.py b/src/coreclr/scripts/genEventPipe.py index c51f231075bea..37acc30361803 100644 --- a/src/coreclr/scripts/genEventPipe.py +++ b/src/coreclr/scripts/genEventPipe.py @@ -662,8 +662,16 @@ def getMonoEventPipeHelperFileImplPrefix(): size_t *size, bool *fixed_buffer) { - if (!value) + if (!value || value_len == 0) { + value_len = sizeof (ep_char16_t); + if ((value_len + *offset) > *size) + ep_raise_error_if_nok (resize_buffer (buffer, size, *offset, *size + value_len, fixed_buffer)); + (*buffer) [*offset] = 0; + (*offset)++; + (*buffer) [*offset] = 0; + (*offset)++; return true; + } GFixedBufferCustomAllocatorData custom_alloc_data; custom_alloc_data.buffer = *buffer + *offset; @@ -695,9 +703,23 @@ def getMonoEventPipeHelperFileImplPrefix(): bool *fixed_buffer) { if (!value) - return true; + value_len = 0; + + if ((value_len + 1 + *offset) > *size) + ep_raise_error_if_nok (resize_buffer (buffer, size, *offset, *size + value_len + 1, fixed_buffer)); + + if (value_len != 0) { + memcpy (*buffer + *offset, value, value_len); + *offset += value_len; + } - return write_buffer ((const uint8_t *)value, (value_len + 1) * sizeof(*value), buffer, offset, size, fixed_buffer); + (*buffer) [*offset] = 0; + (*offset)++; + + return true; + +ep_on_error: + return false; } """ diff --git a/src/coreclr/scripts/genEventing.py b/src/coreclr/scripts/genEventing.py index b9eaacae0727b..cef36174956b8 100644 --- a/src/coreclr/scripts/genEventing.py +++ b/src/coreclr/scripts/genEventing.py @@ -731,7 +731,7 @@ def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern eventpipeProviderCtxName = providerSymbol + "_EVENTPIPE_Context" if is_windows: - Clrallevents.write(('constexpr ' if target_cpp else '') + 'EVENTPIPE_TRACE_CONTEXT ' + eventpipeProviderCtxName + ' = { W("' + providerName + '"), 0, false, 0 };\n') + Clrallevents.write(('constexpr ' if target_cpp else 'static const ') + 'EVENTPIPE_TRACE_CONTEXT ' + eventpipeProviderCtxName + ' = { W("' + providerName + '"), 0, false, 0 };\n') if not is_windows and not write_xplatheader: Clrallevents.write('__attribute__((weak)) EVENTPIPE_TRACE_CONTEXT ' + eventpipeProviderCtxName + ' = { W("' + providerName + '"), 0, false, 0 };\n') @@ -782,14 +782,14 @@ def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern symbolName = eventNode.getAttribute('symbol') keywords = eventNode.getAttribute('keywords') level = convertToLevelId(levelName) - Clrproviders.write(("constexpr " if target_cpp else "const ") + "EVENT_DESCRIPTOR " + symbolName + " = { " + str(level) + ", " + hex(getKeywordsMaskCombined(keywords, keywordsToMask)) + " };\n") + Clrproviders.write(("constexpr " if target_cpp else "static const ") + "EVENT_DESCRIPTOR " + symbolName + " = { " + str(level) + ", " + hex(getKeywordsMaskCombined(keywords, keywordsToMask)) + " };\n") allProviders.append("&" + providerSymbol + "_LTTNG_Context") # define and initialize runtime providers' DOTNET_TRACE_CONTEXT depending on the platform if not is_windows: Clrproviders.write('#define NB_PROVIDERS ' + str(nbProviders) + '\n') - Clrproviders.write(('constexpr ' if target_cpp else 'const ') + 'LTTNG_TRACE_CONTEXT * ALL_LTTNG_PROVIDERS_CONTEXT[NB_PROVIDERS] = { ') + Clrproviders.write(('constexpr ' if target_cpp else 'static const ') + 'LTTNG_TRACE_CONTEXT * ALL_LTTNG_PROVIDERS_CONTEXT[NB_PROVIDERS] = { ') Clrproviders.write(', '.join(allProviders)) Clrproviders.write(' };\n') diff --git a/src/mono/mono/component/event_pipe.c b/src/mono/mono/component/event_pipe.c index 398d463d19eaa..1ea641bd37891 100644 --- a/src/mono/mono/component/event_pipe.c +++ b/src/mono/mono/component/event_pipe.c @@ -17,7 +17,6 @@ #include #endif -extern void ep_rt_mono_component_init (void); static bool _event_pipe_component_inited = false; struct _EventPipeProviderConfigurationNative { @@ -127,7 +126,7 @@ static MonoComponentEventPipe fn_table = { &ep_provider_add_event, &event_pipe_get_session_info, &event_pipe_thread_ctrl_activity_id, - &ep_rt_mono_write_event_ee_startup_start, + &ep_rt_write_event_ee_startup_start, &ep_rt_write_event_threadpool_worker_thread_start, &ep_rt_write_event_threadpool_worker_thread_stop, &ep_rt_write_event_threadpool_worker_thread_wait, diff --git a/src/mono/mono/eglib/glib.h b/src/mono/mono/eglib/glib.h index 77441af31578d..4a8a7e1a543fa 100644 --- a/src/mono/mono/eglib/glib.h +++ b/src/mono/mono/eglib/glib.h @@ -1635,6 +1635,7 @@ __CAST_UTYPE_TO_STYPE(gunichar, gchar, CHAR_MIN, CHAR_MAX) #define GLONG_TO_ULONG(v) G_CAST_TYPE_TO_TYPE(glong, gulong, v) #define GULONG_TO_LONG(v) G_CAST_TYPE_TO_TYPE(gulong, glong, v) +#define GLONG_TO_UINT32(v) G_CAST_TYPE_TO_TYPE(glong, guint32, v) #define GDOUBLE_TO_INT64(v) G_CAST_TYPE_TO_TYPE(gdouble, gint64, v) #define GDOUBLE_TO_UINT64(v) G_CAST_TYPE_TO_TYPE(gdouble, guint64, v) diff --git a/src/mono/mono/eventpipe/ds-rt-mono.h b/src/mono/mono/eventpipe/ds-rt-mono.h index 86898cfccb0dd..541dca1caed1d 100644 --- a/src/mono/mono/eventpipe/ds-rt-mono.h +++ b/src/mono/mono/eventpipe/ds-rt-mono.h @@ -214,8 +214,8 @@ static uint32_t ds_rt_set_environment_variable (const ep_char16_t *name, const ep_char16_t *value) { - gchar *nameNarrow = ep_rt_utf16le_to_utf8_string (name, ep_rt_utf16_string_len (name)); - gchar *valueNarrow = ep_rt_utf16le_to_utf8_string (value, ep_rt_utf16_string_len (value)); + gchar *nameNarrow = ep_rt_utf16le_to_utf8_string (name, -1); + gchar *valueNarrow = ep_rt_utf16le_to_utf8_string (value, -1); gboolean success = g_setenv(nameNarrow, valueNarrow, true); diff --git a/src/mono/mono/eventpipe/ep-rt-mono-profiler-provider.c b/src/mono/mono/eventpipe/ep-rt-mono-profiler-provider.c new file mode 100644 index 0000000000000..b18656b6b8bf2 --- /dev/null +++ b/src/mono/mono/eventpipe/ep-rt-mono-profiler-provider.c @@ -0,0 +1,3194 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +extern EVENTPIPE_TRACE_CONTEXT MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_DOTNET_Context; +#define RUNTIME_MONO_PROFILER_PROVIDER_CONTEXT MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_DOTNET_Context + +// Enable/Disable mono profiler provider. +#define DEFAULT_MONO_PROFILER_PROVIDER_ENABLED false +static bool _mono_profiler_provider_enabled = DEFAULT_MONO_PROFILER_PROVIDER_ENABLED; + +// Mono profilers. +static MonoProfilerHandle _mono_profiler_provider = NULL; +static MonoProfilerHandle _mono_heap_dump_profiler_provider = NULL; + +// Profiler callspec. +static MonoCallSpec _mono_profiler_provider_callspec = {0}; + +// Buffered GC event types. +typedef enum { + BUFFERED_GC_EVENT = 1, + BUFFERED_GC_EVENT_RESIZE = 2, + BUFFERED_GC_EVENT_ROOTS = 3, + BUFFERED_GC_EVENT_MOVES = 4, + BUFFERED_GC_EVENT_OBJECT_REF = 5, + BUFFERED_GC_EVENT_ROOT_REGISTER = 6, + BUFFERED_GC_EVENT_ROOT_UNREGISTER = 7 +} BufferedGCEventType; + +typedef struct _BufferedGCEvent BufferedGCEvent; +struct _BufferedGCEvent { + BufferedGCEventType type; + uint32_t payload_size; +}; + +#define GC_HEAP_DUMP_DEFAULT_MEM_BLOCK_SIZE (mono_pagesize() * 16) +#define GC_HEAP_DUMP_MEM_BLOCK_SIZE_INC (mono_pagesize()) + +typedef struct _GCHeapDumpMemBlock GCHeapDumpMemBlock; +struct _GCHeapDumpMemBlock { + GCHeapDumpMemBlock *next; + GCHeapDumpMemBlock *prev; + uint8_t *start; + uint32_t alloc_size; + uint32_t size; + uint32_t offset; + uint32_t last_used_offset; +}; + +static volatile GCHeapDumpMemBlock *_gc_heap_dump_mem_blocks = NULL; +static volatile GCHeapDumpMemBlock *_gc_heap_dump_current_mem_block = NULL; +static volatile uint32_t _gc_heap_dump_requests = 0; +static volatile uint32_t _gc_heap_dump_in_progress = 0; +static volatile uint64_t _gc_heap_dump_trigger_count = 0; + +static GSList *_mono_profiler_provider_params = NULL; +static GQueue *_gc_heap_dump_request_params = NULL; + +// Lightweight atomic "exclusive/shared" lock, prevents new fire events to happend while GC is in progress and gives GC ability to wait until all pending fire events are done +// before progressing. State uint32_t is split into two uint16_t, upper uint16_t represent gc in progress state, taken when GC starts, preventing new fire events to execute and lower +// uint16_t keeps number of fire events in flight, (gc_in_progress << 16) | (fire_event_count & 0xFFFF). Spin lock is only taken on slow path to queue up pending shared requests +// while GC is in progress and should very rarely be needed. +typedef uint32_t gc_state_t; +typedef uint16_t gc_state_count_t; + +#define GC_STATE_GET_FIRE_EVENT_COUNT(x) ((gc_state_count_t)((x & 0xFFFF))) +#define GC_STATE_INC_FIRE_EVENT_COUNT(x) ((gc_state_t)((gc_state_t)(x & 0xFFFF0000) | (gc_state_t)(GC_STATE_GET_FIRE_EVENT_COUNT(x) + 1))) +#define GC_STATE_DEC_FIRE_EVENT_COUNT(x) ((gc_state_t)((gc_state_t)(x & 0xFFFF0000) | (gc_state_t)(GC_STATE_GET_FIRE_EVENT_COUNT(x) - 1))) +#define GC_STATE_IN_PROGRESS_START(x) ((gc_state_t)((gc_state_t)(0xFFFF << 16) | (gc_state_t)GC_STATE_GET_FIRE_EVENT_COUNT(x))) +#define GC_STATE_IS_IN_PROGRESS(x) (((x >> 16) & 0xFFFF) == 0xFFFF) +#define GC_STATE_IN_PROGRESS_STOP(x) ((gc_state_t)((gc_state_t)GC_STATE_GET_FIRE_EVENT_COUNT(x))) + +static volatile gc_state_t _gc_state = 0; +static ep_rt_spin_lock_handle_t _gc_lock = {0}; + +static +void +fire_buffered_gc_events ( + GCHeapDumpMemBlock *block, + GHashTable *cache); + +static +MonoProfilerCallInstrumentationFlags +method_instrumentation_filter_callback ( + MonoProfiler *prof, + MonoMethod *method); + +static +void +gc_root_register_callback ( + MonoProfiler *prof, + const mono_byte *start, + uintptr_t size, + MonoGCRootSource source, + const void * key, + const char * name); + +static +void +gc_root_unregister_callback ( + MonoProfiler *prof, + const mono_byte *start); + +static +bool +is_keword_enabled (uint64_t enabled_keywords, uint64_t keyword) +{ + return (enabled_keywords & keyword) == keyword; +} + +static +gc_state_t +gc_state_volatile_load (const volatile gc_state_t *ptr) +{ + return ep_rt_volatile_load_uint32_t ((const volatile uint32_t *)ptr); +} + +static +gc_state_t +gc_state_atomic_cas (volatile gc_state_t *target, gc_state_t expected, gc_state_t value) +{ + return (gc_state_t)(mono_atomic_cas_i32 ((volatile gint32 *)(target), (gint32)(value), (gint32)(expected))); +} + +static +void +gc_in_progress_start (void) +{ + gc_state_t old_state = 0; + gc_state_t new_state = 0; + + // Make sure fire event calls will block and wait for GC completion. + ep_rt_spin_lock_acquire (&_gc_lock); + + // Set gc in progress state, preventing new fire event requests. + do { + old_state = gc_state_volatile_load (&_gc_state); + EP_ASSERT (!GC_STATE_IS_IN_PROGRESS (old_state)); + new_state = GC_STATE_IN_PROGRESS_START (old_state); + } while (gc_state_atomic_cas (&_gc_state, old_state, new_state) != old_state); + + gc_state_count_t count = GC_STATE_GET_FIRE_EVENT_COUNT (new_state); + + // Wait for all fire events to complete before progressing with gc. + // NOTE, should never be called recursivly. Default yield count used in SpinLock.cs. + int yield_count = 40; + while (count) { + if (yield_count > 0) { + ep_rt_mono_thread_yield (); + yield_count--; + } else { + ep_rt_thread_sleep (200); + } + count = GC_STATE_GET_FIRE_EVENT_COUNT (gc_state_volatile_load (&_gc_state)); + } +} + +static +void +gc_in_progress_stop (void) +{ + gc_state_t old_state = 0; + gc_state_t new_state = 0; + + // Reset gc in progress state. + do { + old_state = gc_state_volatile_load (&_gc_state); + EP_ASSERT (GC_STATE_IS_IN_PROGRESS (old_state)); + + new_state = GC_STATE_IN_PROGRESS_STOP (old_state); + EP_ASSERT (!GC_STATE_IS_IN_PROGRESS (new_state)); + } while (gc_state_atomic_cas (&_gc_state, old_state, new_state) != old_state); + + // Make sure fire events can continune to execute. + ep_rt_spin_lock_release (&_gc_lock); +} + +static +bool +gc_in_progress (void) +{ + return GC_STATE_IS_IN_PROGRESS (gc_state_volatile_load (&_gc_state)); +} + +static +void +fire_event_enter (void) +{ + gc_state_t old_state = 0; + gc_state_t new_state = 0; + + // NOTE, should never be called recursivly. + do { + old_state = gc_state_volatile_load (&_gc_state); + if (GC_STATE_IS_IN_PROGRESS (old_state)) { + // GC in progress and thread tries to fire event (this should be an unlikely scenario). Wait until GC is done. + ep_rt_spin_lock_acquire (&_gc_lock); + ep_rt_spin_lock_release (&_gc_lock); + old_state = gc_state_volatile_load (&_gc_state); + } + // Increase number of fire event calls. + new_state = GC_STATE_INC_FIRE_EVENT_COUNT (old_state); + } while (gc_state_atomic_cas (&_gc_state, old_state, new_state) != old_state); +} + +static +void +fire_event_exit (void) +{ + gc_state_t old_state = 0; + gc_state_t new_state = 0; + + do { + old_state = gc_state_volatile_load (&_gc_state); + new_state = GC_STATE_DEC_FIRE_EVENT_COUNT (old_state); + } while (gc_state_atomic_cas (&_gc_state, old_state, new_state) != old_state); +} + +static +const EventFilterDescriptor * +provider_params_add (const EventFilterDescriptor *key) +{ + ep_rt_spin_lock_requires_lock_held (&_gc_lock); + + EventFilterDescriptor *param = NULL; + if (key && key->ptr && key->size) { + uint64_t param_ptr = (uint64_t)g_malloc (key->size); + if (param_ptr) { + param = ep_event_filter_desc_alloc (param_ptr, key->size, key->type); + if (param) { + memcpy ((uint8_t*)(uintptr_t)param->ptr,(const uint8_t*)(uintptr_t)key->ptr, key->size); + _mono_profiler_provider_params = g_slist_append (_mono_profiler_provider_params, param); + } else { + g_free ((void *)(uintptr_t)param_ptr); + } + } + } + return param; +} + +static +bool +provider_params_remove (const EventFilterDescriptor *key) +{ + ep_rt_spin_lock_requires_lock_held (&_gc_lock); + + bool removed = false; + if (_mono_profiler_provider_params && key && key->ptr && key->size) { + GSList *list = _mono_profiler_provider_params; + EventFilterDescriptor *param = NULL; + while (list) { + param = (EventFilterDescriptor *)(list->data); + if (param && param->ptr && param->type == key->type && param->size == key->size && + memcmp ((const void *)(uintptr_t)param->ptr, (const void *)(uintptr_t)key->ptr, param->size) == 0) { + g_free ((void *)(uintptr_t)param->ptr); + ep_event_filter_desc_free (param); + _mono_profiler_provider_params = g_slist_delete_link (_mono_profiler_provider_params, list); + removed = true; + break; + } + list = list->next; + } + } + + return removed; +} + +static +void +provider_params_free (void) +{ + for (GSList *list = _mono_profiler_provider_params; list; list = list->next) { + EventFilterDescriptor *param = (EventFilterDescriptor *)(list->data); + if (param) { + g_free ((void *)(uintptr_t)param->ptr); + ep_event_filter_desc_free (param); + } + } + g_slist_free (_mono_profiler_provider_params); + _mono_profiler_provider_params = NULL; +} + +static +bool +provider_params_get_value ( + const EventFilterDescriptor *param, + const ep_char8_t *key, + const ep_char8_t **value) +{ + if (!param || !param->ptr || !param->size || !key) + return false; + + const ep_char8_t *current = (ep_char8_t *)(uintptr_t)param->ptr; + const ep_char8_t *end = current + param->size; + bool found_key = false; + + if (value) + *value = ""; + + if (!current [param->size - 1]) { + while (current < end) { + if (found_key) { + if (value) + *value = current; + break; + } + + if (!ep_rt_utf8_string_compare_ignore_case (current, key)) { + found_key = true; + } + + current = current + strlen (current) + 1; + } + } + + return found_key; +} + +static +bool +provider_params_contains_heap_collect_ondemand (const EventFilterDescriptor *param) +{ + const ep_char8_t *value = NULL; + bool found_heap_collect_ondemand_value = false; + + if (provider_params_get_value (param, "heapcollect", &value)) { + if (strstr (value, "ondemand")) + found_heap_collect_ondemand_value = true; + } + + return found_heap_collect_ondemand_value; +} + +static +const ep_char8_t * +provider_params_get_heap_collect_ondemand_value (void) +{ + ep_rt_spin_lock_requires_lock_held (&_gc_lock); + + const ep_char8_t *value = NULL; + if (_gc_heap_dump_request_params && !g_queue_is_empty (_gc_heap_dump_request_params)) { + EventFilterDescriptor *param = (EventFilterDescriptor *)g_queue_pop_head (_gc_heap_dump_request_params); + if (param) + provider_params_get_value (param, "heapcollect", &value); + g_queue_push_head (_gc_heap_dump_request_params , (gpointer)param); + } + return value ? value : ""; +} + +static +void +gc_heap_dump_request_params_push_value (const EventFilterDescriptor *param) +{ + ep_rt_spin_lock_requires_lock_held (&_gc_lock); + + if (!_gc_heap_dump_request_params) + _gc_heap_dump_request_params = g_queue_new (); + if (_gc_heap_dump_request_params) { + EventFilterDescriptor *desc = NULL; + if (param) { + uint8_t *value = g_malloc (param->size); + memcpy (value, (uint8_t*)(uintptr_t)param->ptr, param->size); + desc = ep_event_filter_desc_alloc ((uint64_t)(uintptr_t)value, param->size, param->type); + } + g_queue_push_tail (_gc_heap_dump_request_params, (gpointer)desc); + } +} + +static +void +gc_heap_dump_request_params_pop_value (void) +{ + ep_rt_spin_lock_requires_lock_held (&_gc_lock); + + if (_gc_heap_dump_request_params && !g_queue_is_empty (_gc_heap_dump_request_params)) { + EventFilterDescriptor *param = (EventFilterDescriptor *)g_queue_pop_head (_gc_heap_dump_request_params); + if (param) { + g_free ((uint8_t*)(uintptr_t)param->ptr); + ep_event_filter_desc_free (param); + } + } +} + +static +void +gc_heap_dump_request_params_free (void) +{ + if (_gc_heap_dump_request_params) { + while (!g_queue_is_empty (_gc_heap_dump_request_params)) + gc_heap_dump_request_params_pop_value (); + g_queue_free (_gc_heap_dump_request_params); + _gc_heap_dump_request_params = NULL; + } +} + +static +void +gc_heap_dump_requests_inc (void) +{ + EP_ASSERT (ep_rt_mono_is_runtime_initialized ()); + ep_rt_atomic_inc_uint32_t (&_gc_heap_dump_requests); +} + +static +void +gc_heap_dump_requests_dec (void) +{ + EP_ASSERT (ep_rt_mono_is_runtime_initialized ()); + ep_rt_atomic_dec_uint32_t (&_gc_heap_dump_requests); +} + +static +bool +gc_heap_dump_requested (void) +{ + if (!ep_rt_mono_is_runtime_initialized ()) + return false; + + return ep_rt_volatile_load_uint32_t(&_gc_heap_dump_requests) != 0 ? true : false; +} + +static +bool +gc_heap_dump_in_progress (void) +{ + ep_rt_spin_lock_requires_lock_held (&_gc_lock); + return ep_rt_volatile_load_uint32_t_without_barrier (&_gc_heap_dump_in_progress) != 0 ? true : false; +} + +static +void +gc_heap_dump_in_progress_start (void) +{ + EP_ASSERT (ep_rt_mono_is_runtime_initialized ()); + + ep_rt_spin_lock_requires_lock_held (&_gc_lock); + ep_rt_volatile_store_uint32_t_without_barrier (&_gc_heap_dump_in_progress, 1); +} + +static +void +gc_heap_dump_in_progress_stop (void) +{ + EP_ASSERT (ep_rt_mono_is_runtime_initialized ()); + + ep_rt_spin_lock_requires_lock_held (&_gc_lock); + ep_rt_volatile_store_uint32_t_without_barrier (&_gc_heap_dump_in_progress, 0); +} + +static +void +gc_heap_dump_trigger_callback (MonoProfiler *prof) +{ + if (gc_heap_dump_requested ()) { + ep_rt_spin_lock_acquire (&_gc_lock); + gc_heap_dump_requests_dec (); + gc_heap_dump_in_progress_start (); + ep_rt_spin_lock_release (&_gc_lock); + + mono_gc_collect (mono_gc_max_generation ()); + + ep_rt_spin_lock_acquire (&_gc_lock); + gc_heap_dump_request_params_pop_value (); + gc_heap_dump_in_progress_stop (); + ep_rt_spin_lock_release (&_gc_lock); + } +} + +static +GCHeapDumpMemBlock * +gc_heap_dump_mem_block_alloc (uint32_t req_size) +{ + EP_ASSERT (gc_in_progress ()); + + GCHeapDumpMemBlock *prev = NULL; + + uint32_t size = GC_HEAP_DUMP_DEFAULT_MEM_BLOCK_SIZE; + while (size - sizeof(GCHeapDumpMemBlock) < req_size) + size += GC_HEAP_DUMP_MEM_BLOCK_SIZE_INC; + + GCHeapDumpMemBlock *block = mono_valloc (NULL, size, MONO_MMAP_READ | MONO_MMAP_WRITE | MONO_MMAP_ANON | MONO_MMAP_PRIVATE, MONO_MEM_ACCOUNT_PROFILER); + if (block) { + block->alloc_size = size; + block->start = (uint8_t *)ALIGN_PTR_TO ((uint8_t *)block + sizeof (GCHeapDumpMemBlock), 16); + block->size = (uint32_t)(((uint8_t*)block + size) - (uint8_t*)block->start); + block->offset = 0; + block->last_used_offset = 0; + + while (true) { + prev = (GCHeapDumpMemBlock *)ep_rt_volatile_load_ptr_without_barrier ((volatile void **)&_gc_heap_dump_mem_blocks); + if (mono_atomic_cas_ptr ((volatile gpointer*)&_gc_heap_dump_mem_blocks, block, prev) == prev) + break; + } + + if (prev) + prev->next = block; + block->prev = prev; + } + + return block; +} + +static +uint8_t * +gc_heap_dump_mem_alloc (uint32_t req_size) +{ + EP_ASSERT (gc_in_progress ()); + + GCHeapDumpMemBlock *current_block = (GCHeapDumpMemBlock *)ep_rt_volatile_load_ptr_without_barrier ((volatile void **)&_gc_heap_dump_current_mem_block); + uint8_t *buffer = NULL; + + if (!current_block) { + current_block = gc_heap_dump_mem_block_alloc (req_size); + if (current_block) { + mono_memory_barrier (); + ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_gc_heap_dump_current_mem_block, current_block); + } + } + + if (current_block) { + uint32_t prev_offset = (uint32_t)mono_atomic_fetch_add_i32 ((volatile int32_t *)¤t_block->offset, (int32_t)req_size); + if (prev_offset + req_size > current_block->size) { + if (prev_offset <= current_block->size) + current_block->last_used_offset = prev_offset; + current_block = gc_heap_dump_mem_block_alloc (req_size); + if (current_block) { + buffer = current_block->start; + current_block->offset += req_size; + mono_memory_barrier (); + ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_gc_heap_dump_current_mem_block, current_block); + } + } else { + buffer = (uint8_t*)current_block->start + prev_offset; + } + } + + return buffer; +} + +static +void +gc_heap_dump_mem_block_free_all (void) +{ + GCHeapDumpMemBlock *current_block = (GCHeapDumpMemBlock *)ep_rt_volatile_load_ptr ((volatile void **)&_gc_heap_dump_current_mem_block); + + ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_gc_heap_dump_current_mem_block, NULL); + ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_gc_heap_dump_mem_blocks, NULL); + + mono_memory_barrier (); + + while (current_block) { + GCHeapDumpMemBlock *prev_block = current_block->prev; + mono_vfree ((uint8_t *)current_block, current_block->alloc_size, MONO_MEM_ACCOUNT_MEM_MANAGER); + current_block = prev_block; + } +} + +static +void +gc_heap_dump_mem_block_free_all_but_current (void) +{ + EP_ASSERT (gc_in_progress ()); + + GCHeapDumpMemBlock *block_to_keep = (GCHeapDumpMemBlock *)ep_rt_volatile_load_ptr ((volatile void **)&_gc_heap_dump_current_mem_block); + GCHeapDumpMemBlock *current_block = block_to_keep; + + ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_gc_heap_dump_current_mem_block, NULL); + ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_gc_heap_dump_mem_blocks, NULL); + + mono_memory_barrier (); + + if (current_block) { + if (current_block->prev) { + current_block = current_block->prev; + while (current_block) { + GCHeapDumpMemBlock *prev_block = current_block->prev; + mono_vfree ((uint8_t *)current_block, current_block->alloc_size, MONO_MEM_ACCOUNT_MEM_MANAGER); + current_block = prev_block; + } + } + } + + if (block_to_keep) { + block_to_keep->prev = NULL; + block_to_keep->next = NULL; + block_to_keep->offset = 0; + block_to_keep->last_used_offset = 0; + } + + mono_memory_barrier (); + + ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_gc_heap_dump_current_mem_block, block_to_keep); + ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_gc_heap_dump_mem_blocks, block_to_keep); +} + +static +uint8_t * +buffered_gc_event_alloc (uint32_t req_size) +{ + EP_ASSERT (gc_in_progress ()); + return gc_heap_dump_mem_alloc (req_size + sizeof (BufferedGCEvent)); +} + +static +void +fire_buffered_gc_events_in_alloc_order (GHashTable *cache) +{ + EP_ASSERT (gc_in_progress ()); + + GCHeapDumpMemBlock *first_block = (GCHeapDumpMemBlock *)ep_rt_volatile_load_ptr ((volatile void **)&_gc_heap_dump_current_mem_block); + while (first_block && first_block->prev) + first_block = first_block->prev; + + GCHeapDumpMemBlock *current_block = first_block; + while (current_block) { + GCHeapDumpMemBlock *next_block = current_block->next; + fire_buffered_gc_events (current_block, cache); + current_block = next_block; + } + + gc_heap_dump_mem_block_free_all_but_current (); +} + +static +void +get_generic_types ( + MonoGenericInst *generic_instance, + uint32_t *generic_type_count, + uint8_t **generic_types) +{ + if (generic_instance) { + uint8_t *buffer = g_malloc (generic_instance->type_argc * (sizeof (uint8_t) + sizeof (uint64_t))); + if (buffer) { + *generic_types = buffer; + *generic_type_count = generic_instance->type_argc; + for (uint32_t i = 0; i < generic_instance->type_argc; ++i) { + uint8_t type = generic_instance->type_argv [i]->type; + ep_write_buffer_uint8_t (&buffer, type); + + uint64_t class_id = (uint64_t)mono_class_from_mono_type_internal (generic_instance->type_argv [i]); + ep_write_buffer_uint64_t (&buffer, class_id); + } + } + } +} + +static +void +get_class_data ( + MonoClass *klass, + uint64_t *class_id, + uint64_t *module_id, + ep_char8_t **class_name, + uint32_t *class_generic_type_count, + uint8_t **class_generic_types) +{ + *class_id = (uint64_t)klass; + *module_id = 0; + + if (klass) + *module_id = (uint64_t)m_class_get_image (klass); + + if (klass && class_name) + *class_name = (ep_char8_t *)mono_type_get_name_full (m_class_get_byval_arg (klass), MONO_TYPE_NAME_FORMAT_IL); + else if (class_name) + *class_name = NULL; + + if (class_generic_type_count && class_generic_types) { + if (mono_class_is_ginst (klass)) { + MonoGenericContext *context = mono_class_get_context (klass); + MonoGenericInst *class_instance = (context && context->class_inst) ? context->class_inst : NULL; + get_generic_types (class_instance, class_generic_type_count, class_generic_types); + } + } +} + +static +void +fire_gc_event_root_register ( + uint8_t *data, + uint32_t payload_size) +{ + EP_ASSERT (gc_in_progress ()); + + uintptr_t root_id; + uintptr_t root_size; + uint8_t root_source; + uintptr_t root_key; + + memcpy (&root_id, data, sizeof (root_id)); + data += sizeof (root_id); + + memcpy (&root_size, data, sizeof (root_size)); + data += sizeof (root_size); + + memcpy (&root_source, data, sizeof (root_source)); + data += sizeof (root_source); + + memcpy (&root_key, data, sizeof (root_key)); + data += sizeof (root_key); + + FireEtwMonoProfilerGCRootRegister ( + (const void *)root_id, + (uint64_t)root_size, + root_source, + (uint64_t)root_key, + (const ep_char8_t *)data, + NULL, + NULL); +} + +static +void +buffer_gc_event_root_register_callback ( + MonoProfiler *prof, + const mono_byte *start, + uintptr_t size, + MonoGCRootSource source, + const void * key, + const char * name) +{ + EP_ASSERT (gc_in_progress ()); + + uintptr_t root_id = (uintptr_t)start; + uintptr_t root_size = size; + uint8_t root_source = (uint8_t)source; + uintptr_t root_key = (uintptr_t)key; + const char *root_name = (name ? name : ""); + size_t root_name_len = strlen (root_name) + 1; + + BufferedGCEvent gc_event_data; + gc_event_data.type = BUFFERED_GC_EVENT_ROOT_REGISTER; + gc_event_data.payload_size = (uint32_t) + (sizeof (root_id) + + sizeof (root_size) + + sizeof (root_source) + + sizeof (root_key) + + root_name_len); + + uint8_t * buffer = buffered_gc_event_alloc (gc_event_data.payload_size); + if (buffer) { + // Internal header + memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); + buffer += sizeof (gc_event_data); + + // GCEvent.RootID + memcpy(buffer, &root_id, sizeof (root_id)); + buffer += sizeof (root_id); + + // GCEvent.RootSize + memcpy(buffer, &root_size, sizeof (root_size)); + buffer += sizeof (root_size); + + // GCEvent.RootType + memcpy(buffer, &root_source, sizeof (root_source)); + buffer += sizeof (root_source); + + // GCEvent.RootKeyID + memcpy(buffer, &root_key, sizeof (root_key)); + buffer += sizeof (root_key); + + // GCEvent.RootKeyName + memcpy(buffer, root_name, root_name_len); + } +} + +static +void +fire_gc_event_root_unregister ( + uint8_t *data, + uint32_t payload_size) +{ + EP_ASSERT (gc_in_progress ()); + + uintptr_t root_id; + + memcpy (&root_id, data, sizeof (root_id)); + + FireEtwMonoProfilerGCRootUnregister ( + (const void *)root_id, + NULL, + NULL); +} + +static +void +buffer_gc_event_root_unregister_callback ( + MonoProfiler *prof, + const mono_byte *start) +{ + EP_ASSERT (gc_in_progress ()); + + uintptr_t root_id = (uintptr_t)start; + + BufferedGCEvent gc_event_data; + gc_event_data.type = BUFFERED_GC_EVENT_ROOT_UNREGISTER; + gc_event_data.payload_size = sizeof (root_id); + + uint8_t * buffer = buffered_gc_event_alloc (gc_event_data.payload_size); + if (buffer) { + // Internal header + memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); + buffer += sizeof (gc_event_data); + + // GCEvent.RootID + memcpy(buffer, &root_id, sizeof (root_id)); + } +} + +static +void +fire_gc_event ( + uint8_t *data, + uint32_t payload_size) +{ + EP_ASSERT (gc_in_progress ()); + + uint8_t gc_event_type; + uint32_t generation; + + memcpy (&gc_event_type, data, sizeof (gc_event_type)); + data += sizeof (gc_event_type); + + memcpy (&generation, data, sizeof (generation)); + + FireEtwMonoProfilerGCEvent ( + gc_event_type, + generation, + NULL, + NULL); +} + +static +void +buffer_gc_event ( + uint8_t gc_event_type, + uint32_t generation) +{ + EP_ASSERT (gc_in_progress ()); + + BufferedGCEvent gc_event_data; + gc_event_data.type = BUFFERED_GC_EVENT; + gc_event_data.payload_size = + sizeof (gc_event_type) + + sizeof (generation); + + uint8_t * buffer = buffered_gc_event_alloc (gc_event_data.payload_size); + if (buffer) { + // Internal header + memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); + buffer += sizeof (gc_event_data); + + // GCEvent.GCEventType + memcpy(buffer, &gc_event_type, sizeof (gc_event_type)); + buffer += sizeof (gc_event_type); + + // GCEvent.GCGeneration + memcpy(buffer, &generation, sizeof (generation)); + } +} + +static +void +fire_gc_event_resize ( + uint8_t *data, + uint32_t payload_size) +{ + EP_ASSERT (gc_in_progress ()); + + uintptr_t size; + + memcpy (&size, data, sizeof (size)); + + FireEtwMonoProfilerGCResize ( + (uint64_t)size, + NULL, + NULL); +} + +static +void +buffer_gc_event_resize_callback ( + MonoProfiler *prof, + uintptr_t size) +{ + EP_ASSERT (gc_in_progress ()); + + BufferedGCEvent gc_event_data; + gc_event_data.type = BUFFERED_GC_EVENT_RESIZE; + gc_event_data.payload_size = sizeof (size); + + uint8_t * buffer = buffered_gc_event_alloc (gc_event_data.payload_size); + if (buffer) { + // Internal header + memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); + buffer += sizeof (gc_event_data); + + // GCResize.NewSize + memcpy(buffer, &size, sizeof (size)); + } +} + +static +void +fire_gc_event_moves ( + uint8_t *data, + uint32_t payload_size) +{ + EP_ASSERT (gc_in_progress ()); + + uint64_t count; + + memcpy (&count, data, sizeof (count)); + data += sizeof (count); + + FireEtwMonoProfilerGCMoves ( + (uint32_t)count, + sizeof (uintptr_t) + sizeof (uintptr_t), + data, + NULL, + NULL); +} + +static +void +buffer_gc_event_moves_callback ( + MonoProfiler *prof, + MonoObject *const* objects, + uint64_t count) +{ + EP_ASSERT (gc_in_progress ()); + + uintptr_t object_id; + uintptr_t address_id; + + // Serialized as object_id/address_id pairs. + count = count / 2; + + BufferedGCEvent gc_event_data; + gc_event_data.type = BUFFERED_GC_EVENT_MOVES; + gc_event_data.payload_size = + (uint32_t)(sizeof (count) + + (count * (sizeof (uintptr_t) + sizeof (uintptr_t)))); + + uint8_t * buffer = buffered_gc_event_alloc (gc_event_data.payload_size); + if (buffer) { + // Internal header + memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); + buffer += sizeof (gc_event_data); + + // GCMoves.Count + memcpy (buffer, &count, sizeof (count)); + buffer += sizeof (count); + + // Serialize directly as memory stream expected by FireEtwMonoProfilerGCMoves. + for (uint64_t i = 0; i < count; i++) { + // GCMoves.Values[].ObjectID. + object_id = (uintptr_t)SGEN_POINTER_UNTAG_ALL (*objects); + ep_write_buffer_uintptr_t (&buffer, object_id); + objects++; + + // GCMoves.Values[].AddressID. + address_id = (uintptr_t)*objects; + ep_write_buffer_uintptr_t (&buffer, address_id); + objects++; + } + } +} + +static +void +fire_gc_event_roots ( + uint8_t *data, + uint32_t payload_size) +{ + EP_ASSERT (gc_in_progress ()); + + uint64_t count; + + memcpy (&count, data, sizeof (count)); + data += sizeof (count); + + FireEtwMonoProfilerGCRoots ( + (uint32_t)count, + sizeof (uintptr_t) + sizeof (uintptr_t), + data, + NULL, + NULL); +} + +static +void +buffer_gc_event_roots_callback ( + MonoProfiler *prof, + uint64_t count, + const mono_byte *const * addresses, + MonoObject *const * objects) +{ + EP_ASSERT (gc_in_progress ()); + + uintptr_t object_id; + uintptr_t address_id; + + BufferedGCEvent gc_event_data; + gc_event_data.type = BUFFERED_GC_EVENT_ROOTS; + gc_event_data.payload_size = + (uint32_t)(sizeof (count) + + (count * (sizeof (uintptr_t) + sizeof (uintptr_t)))); + + uint8_t * buffer = buffered_gc_event_alloc (gc_event_data.payload_size); + if (buffer) { + // Internal header + memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); + buffer += sizeof (gc_event_data); + + // GCRoots.Count + memcpy (buffer, &count, sizeof (count)); + buffer += sizeof (count); + + // Serialize directly as memory stream expected by FireEtwMonoProfilerGCRoots. + for (uint64_t i = 0; i < count; i++) { + // GCRoots.Values[].ObjectID. + object_id = (uintptr_t)SGEN_POINTER_UNTAG_ALL (*objects); + ep_write_buffer_uintptr_t (&buffer, object_id); + objects++; + + // GCRoots.Values[].AddressID. + address_id = (uintptr_t)*addresses; + ep_write_buffer_uintptr_t (&buffer, address_id); + addresses++; + } + } +} + +static +void +fire_gc_event_heap_dump_object_reference ( + uint8_t *data, + uint32_t payload_size, + GHashTable *cache) +{ + EP_ASSERT (gc_in_progress ()); + + uintptr_t object_id; + uintptr_t vtable_id; + uintptr_t object_size; + uint8_t object_gen; + uintptr_t object_ref_count; + + memcpy (&object_id, data, sizeof (object_id)); + data += sizeof (object_id); + + memcpy (&vtable_id, data, sizeof (vtable_id)); + data += sizeof (vtable_id); + + memcpy (&object_size, data, sizeof (object_size)); + data += sizeof (object_size); + + memcpy (&object_gen, data, sizeof (object_gen)); + data += sizeof (object_gen); + + memcpy (&object_ref_count, data, sizeof (object_ref_count)); + data += sizeof (object_ref_count); + + FireEtwMonoProfilerGCHeapDumpObjectReference ( + (const void *)object_id, + (uint64_t)vtable_id, + (uint64_t)object_size, + object_gen, + (uint32_t)object_ref_count, + sizeof (uint32_t) + sizeof (uintptr_t), + data, + NULL, + NULL); + + if (cache) + g_hash_table_insert (cache, (MonoVTable *)SGEN_POINTER_UNTAG_ALL (vtable_id), NULL); +} + +static +int +buffer_gc_event_heap_dump_object_reference_callback ( + MonoObject *obj, + MonoClass *klass, + uintptr_t size, + uintptr_t num, + MonoObject **refs, + uintptr_t *offsets, + void *data) +{ + EP_ASSERT (gc_in_progress ()); + + uintptr_t object_id; + uintptr_t vtable_id; + uint8_t object_gen; + uintptr_t object_size = size; + uintptr_t object_ref_count = num; + uint32_t object_ref_offset; + + /* account for object alignment */ + object_size += 7; + object_size &= ~7; + + size_t payload_size = + sizeof (object_id) + + sizeof (vtable_id) + + sizeof (object_size) + + sizeof (object_gen) + + sizeof (object_ref_count) + + (object_ref_count * (sizeof (uint32_t) + sizeof (uintptr_t))); + + BufferedGCEvent gc_event_data; + gc_event_data.type = BUFFERED_GC_EVENT_OBJECT_REF; + gc_event_data.payload_size = GSIZE_TO_UINT32 (payload_size); + + uint8_t *buffer = buffered_gc_event_alloc (gc_event_data.payload_size); + if (buffer) { + // Internal header + memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); + buffer += sizeof (gc_event_data); + + // GCEvent.ObjectID + object_id = (uintptr_t)SGEN_POINTER_UNTAG_ALL (obj); + memcpy (buffer, &object_id, sizeof (object_id)); + buffer += sizeof (object_id); + + // GCEvent.VTableID + vtable_id = (uintptr_t)SGEN_POINTER_UNTAG_ALL (mono_object_get_vtable_internal (obj)); + memcpy (buffer, &vtable_id, sizeof (vtable_id)); + buffer += sizeof (vtable_id); + + // GCEvent.ObjectSize + memcpy (buffer, &object_size, sizeof (object_size)); + buffer += sizeof (object_size); + + // GCEvent.ObjectGeneration + object_gen = (uint8_t)mono_gc_get_generation (obj); + memcpy (buffer, &object_gen, sizeof (object_gen)); + buffer += sizeof (object_gen); + + // GCEvent.Count + memcpy (buffer, &object_ref_count, sizeof (object_ref_count)); + buffer += sizeof (object_ref_count); + + // Serialize directly as memory stream expected by FireEtwMonoProfilerGCHeapDumpObjectReference. + uintptr_t last_offset = 0; + for (uintptr_t i = 0; i < object_ref_count; i++) { + // GCEvent.Values[].ReferencesOffset + object_ref_offset = GUINTPTR_TO_UINT32 (offsets [i] - last_offset); + ep_write_buffer_uint32_t (&buffer, object_ref_offset); + + // GCEvent.Values[].ObjectID + object_id = (uintptr_t)SGEN_POINTER_UNTAG_ALL (refs[i]); + ep_write_buffer_uintptr_t (&buffer, object_id); + + last_offset = offsets [i]; + } + } + + return 0; +} + +static +void +fire_buffered_gc_events ( + GCHeapDumpMemBlock *block, + GHashTable *cache) +{ + EP_ASSERT (gc_in_progress ()); + + if (block) { + uint32_t current_offset = 0; + uint32_t used_size = (block->offset < block->size) ? block->offset : block->last_used_offset; + BufferedGCEvent gc_event; + while ((current_offset + sizeof (gc_event)) <= used_size) { + uint8_t *data = block->start + current_offset; + memcpy (&gc_event, data, sizeof (gc_event)); + data += sizeof (gc_event); + if ((current_offset + sizeof (gc_event) + gc_event.payload_size) <= used_size) { + switch (gc_event.type) { + case BUFFERED_GC_EVENT: + fire_gc_event (data, gc_event.payload_size); + break; + case BUFFERED_GC_EVENT_RESIZE: + fire_gc_event_resize (data, gc_event.payload_size); + break; + case BUFFERED_GC_EVENT_ROOTS: + fire_gc_event_roots (data, gc_event.payload_size); + break; + case BUFFERED_GC_EVENT_MOVES: + fire_gc_event_moves (data, gc_event.payload_size); + break; + case BUFFERED_GC_EVENT_OBJECT_REF: + fire_gc_event_heap_dump_object_reference (data, gc_event.payload_size, cache); + break; + case BUFFERED_GC_EVENT_ROOT_REGISTER: + fire_gc_event_root_register (data, gc_event.payload_size); + break; + case BUFFERED_GC_EVENT_ROOT_UNREGISTER: + fire_gc_event_root_unregister (data, gc_event.payload_size); + break; + default: + EP_ASSERT (!"Unknown buffered GC event type."); + } + + current_offset += sizeof (gc_event) + gc_event.payload_size; + } else { + break; + } + } + } +} + +static +void +fire_cached_gc_events (GHashTable *cache) +{ + if (cache) { + GHashTableIter iter; + MonoVTable *object_vtable; + g_hash_table_iter_init (&iter, cache); + while (g_hash_table_iter_next (&iter, (void**)&object_vtable, NULL)) { + if (object_vtable) { + uint64_t vtable_id = (uint64_t)object_vtable; + uint64_t class_id; + uint64_t module_id; + ep_char8_t *class_name; + get_class_data (object_vtable->klass, &class_id, &module_id, &class_name, NULL, NULL); + FireEtwMonoProfilerGCHeapDumpVTableClassReference ( + vtable_id, + class_id, + module_id, + class_name, + NULL, + NULL); + g_free (class_name); + } + } + } +} + +static +void +app_domain_loading_callback ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainLoading ()) + return; + + uint64_t domain_id = (uint64_t)domain; + + fire_event_enter (); + + FireEtwMonoProfilerAppDomainLoading ( + domain_id, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +app_domain_loaded_callback ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainLoaded ()) + return; + + uint64_t domain_id = (uint64_t)domain; + + fire_event_enter (); + + FireEtwMonoProfilerAppDomainLoaded ( + domain_id, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +app_domain_unloading_callback ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainUnloading ()) + return; + + uint64_t domain_id = (uint64_t)domain; + + fire_event_enter (); + + FireEtwMonoProfilerAppDomainUnloading ( + domain_id, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +app_domain_unloaded_callback ( + MonoProfiler *prof, + MonoDomain *domain) +{ + if (!EventEnabledMonoProfilerAppDomainUnloaded ()) + return; + + uint64_t domain_id = (uint64_t)domain; + + fire_event_enter (); + + FireEtwMonoProfilerAppDomainUnloaded ( + domain_id, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +app_domain_name_callback ( + MonoProfiler *prof, + MonoDomain *domain, + const char *name) +{ + if (!EventEnabledMonoProfilerAppDomainName ()) + return; + + uint64_t domain_id = (uint64_t)domain; + + fire_event_enter (); + + FireEtwMonoProfilerAppDomainName ( + domain_id, + (const ep_char8_t *)(name ? name : ""), + NULL, + NULL); + + fire_event_exit (); +} + +static +void +get_jit_data ( + MonoMethod *method, + uint64_t *method_id, + uint64_t *module_id, + uint32_t *method_token, + uint32_t *method_generic_type_count, + uint8_t **method_generic_types) +{ + *method_id = (uint64_t)method; + *module_id = 0; + *method_token = 0; + + if (method) { + *method_token = method->token; + if (method->klass) + *module_id = (uint64_t)m_class_get_image (method->klass); + + if (method_generic_type_count && method_generic_types) { + if (method->is_inflated) { + MonoGenericContext *context = mono_method_get_context (method); + MonoGenericInst *method_instance = (context && context->method_inst) ? context->method_inst : NULL; + get_generic_types (method_instance, method_generic_type_count, method_generic_types); + } + } + } +} + +static +void +jit_begin_callback ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerJitBegin ()) + return; + + uint64_t method_id; + uint64_t module_id; + uint32_t method_token; + + get_jit_data (method, &method_id, &module_id, &method_token, NULL, NULL); + + fire_event_enter (); + + FireEtwMonoProfilerJitBegin ( + method_id, + module_id, + method_token, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +jit_failed_callback ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerJitFailed ()) + return; + + uint64_t method_id; + uint64_t module_id; + uint32_t method_token; + + get_jit_data (method, &method_id, &module_id, &method_token, NULL, NULL); + + fire_event_enter (); + + FireEtwMonoProfilerJitFailed ( + method_id, + module_id, + method_token, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +jit_done_callback ( + MonoProfiler *prof, + MonoMethod *method, + MonoJitInfo *ji) +{ + if (!EventEnabledMonoProfilerJitDone () && !EventEnabledMonoProfilerJitDone_V1 () && !EventEnabledMonoProfilerJitDoneVerbose ()) + return; + + bool verbose = (RUNTIME_MONO_PROFILER_PROVIDER_CONTEXT.Level >= (uint8_t)EP_EVENT_LEVEL_VERBOSE); + + uint64_t method_id; + uint64_t module_id; + uint32_t method_token; + + uint32_t method_generic_type_count = 0; + uint8_t *method_generic_types = NULL; + + char *method_namespace = NULL; + const char *method_name = NULL; + char *method_signature = NULL; + + get_jit_data (method, &method_id, &module_id, &method_token, &method_generic_type_count, &method_generic_types); + + if (verbose) { + //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. + method_name = method->name; + method_signature = mono_signature_full_name (mono_method_signature_internal (method)); + if (method->klass) + method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); + } + + fire_event_enter (); + + FireEtwMonoProfilerJitDone_V1 ( + method_id, + module_id, + method_token, + method_generic_type_count, + sizeof (uint8_t) + sizeof (uint64_t), + method_generic_types, + NULL, + NULL); + + if (verbose) { + FireEtwMonoProfilerJitDoneVerbose ( + method_id, + (const ep_char8_t *)method_namespace, + (const ep_char8_t *)method_name, + (const ep_char8_t *)method_signature, + NULL, + NULL); + } + + fire_event_exit (); + + g_free (method_namespace); + g_free (method_signature); + g_free (method_generic_types); +} + +static +void +jit_chunk_created_callback ( + MonoProfiler *prof, + const mono_byte *chunk, + uintptr_t size) +{ + if (!EventEnabledMonoProfilerJitChunkCreated ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerJitChunkCreated ( + chunk, + (uint64_t)size, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +jit_chunk_destroyed_callback ( + MonoProfiler *prof, + const mono_byte *chunk) +{ + if (!EventEnabledMonoProfilerJitChunkDestroyed ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerJitChunkDestroyed ( + chunk, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +jit_code_buffer_callback ( + MonoProfiler *prof, + const mono_byte *buffer, + uint64_t size, + MonoProfilerCodeBufferType type, + const void *data) +{ + if (!EventEnabledMonoProfilerJitCodeBuffer ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerJitCodeBuffer ( + buffer, + size, + (uint8_t)type, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +class_loading_callback ( + MonoProfiler *prof, + MonoClass *klass) +{ + if (!EventEnabledMonoProfilerClassLoading ()) + return; + + uint64_t class_id; + uint64_t module_id; + + get_class_data (klass, &class_id, &module_id, NULL, NULL, NULL); + + fire_event_enter (); + + FireEtwMonoProfilerClassLoading ( + class_id, + module_id, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +class_failed_callback ( + MonoProfiler *prof, + MonoClass *klass) +{ + if (!EventEnabledMonoProfilerClassFailed ()) + return; + + uint64_t class_id; + uint64_t module_id; + + get_class_data (klass, &class_id, &module_id, NULL, NULL, NULL); + + fire_event_enter (); + + FireEtwMonoProfilerClassFailed ( + class_id, + module_id, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +class_loaded_callback ( + MonoProfiler *prof, + MonoClass *klass) +{ + if (!EventEnabledMonoProfilerClassLoaded () && !EventEnabledMonoProfilerClassLoaded_V1 ()) + return; + + uint64_t class_id; + uint64_t module_id; + ep_char8_t *class_name; + + uint32_t class_generic_type_count = 0; + uint8_t *class_generic_types = NULL; + + get_class_data (klass, &class_id, &module_id, &class_name, &class_generic_type_count, &class_generic_types); + + fire_event_enter (); + + FireEtwMonoProfilerClassLoaded_V1 ( + class_id, + module_id, + class_name ? class_name : "", + class_generic_type_count, + sizeof (uint8_t) + sizeof (uint64_t), + class_generic_types, + NULL, + NULL); + + fire_event_exit (); + + g_free (class_name); + g_free (class_generic_types); +} + +static +void +get_vtable_data ( + MonoVTable *vtable, + uint64_t *vtable_id, + uint64_t *class_id, + uint64_t *domain_id) +{ + *vtable_id = (uint64_t)vtable; + *class_id = 0; + *domain_id = 0; + + if (vtable) { + *class_id = (uint64_t)mono_vtable_class_internal (vtable); + *domain_id = (uint64_t)mono_vtable_domain_internal (vtable); + } +} + +static +void +vtable_loading_callback ( + MonoProfiler *prof, + MonoVTable *vtable) +{ + if (!EventEnabledMonoProfilerVTableLoading ()) + return; + + uint64_t vtable_id; + uint64_t class_id; + uint64_t domain_id; + + get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); + + fire_event_enter (); + + FireEtwMonoProfilerVTableLoading ( + vtable_id, + class_id, + domain_id, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +vtable_failed_callback ( + MonoProfiler *prof, + MonoVTable *vtable) +{ + if (!EventEnabledMonoProfilerVTableFailed ()) + return; + + uint64_t vtable_id; + uint64_t class_id; + uint64_t domain_id; + + get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); + + fire_event_enter (); + + FireEtwMonoProfilerVTableFailed ( + vtable_id, + class_id, + domain_id, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +vtable_loaded_callback ( + MonoProfiler *prof, + MonoVTable *vtable) +{ + if (!EventEnabledMonoProfilerVTableLoaded ()) + return; + + uint64_t vtable_id; + uint64_t class_id; + uint64_t domain_id; + + get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); + + fire_event_enter (); + + FireEtwMonoProfilerVTableLoaded ( + vtable_id, + class_id, + domain_id, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +module_loading_callback ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleLoading ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerModuleLoading ( + (uint64_t)image, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +module_failed_callback ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleFailed ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerModuleFailed ( + (uint64_t)image, + NULL, + NULL); + + fire_event_exit (); +} + +static +const char * +get_module_path (MonoImage *image) +{ + if (image && image->filename) { + /* if there's a filename, use it */ + return image->filename; + } else if (image && image->module_name) { + /* otherwise, use the module name */ + return image->module_name; + } + + return ""; +} + +static +void +module_loaded_callback ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleLoaded ()) + return; + + uint64_t module_id = (uint64_t)image; + const ep_char8_t *module_path = NULL; + const ep_char8_t *module_guid = NULL; + + if (image) { + module_path = get_module_path (image); + module_guid = (const ep_char8_t *)mono_image_get_guid (image); + } + + fire_event_enter (); + + FireEtwMonoProfilerModuleLoaded ( + module_id, + module_path ? module_path : "", + module_guid ? module_guid : "", + NULL, + NULL); + + fire_event_exit (); +} + +static +void +module_unloading_callback ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleUnloading ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerModuleUnloading ( + (uint64_t)image, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +module_unloaded_callback ( + MonoProfiler *prof, + MonoImage *image) +{ + if (!EventEnabledMonoProfilerModuleUnloaded ()) + return; + + uint64_t module_id = (uint64_t)image; + const ep_char8_t *module_path = NULL; + const ep_char8_t *module_guid = NULL; + + if (image) { + module_path = get_module_path (image); + module_guid = (const ep_char8_t *)mono_image_get_guid (image); + } + + fire_event_enter (); + + FireEtwMonoProfilerModuleUnloaded ( + module_id, + module_path ? module_path : "", + module_guid ? module_guid : "", + NULL, + NULL); + + fire_event_exit (); +} + +static +void +get_assembly_data ( + MonoAssembly *assembly, + uint64_t *assembly_id, + uint64_t *module_id, + ep_char8_t **assembly_name) +{ + *assembly_id = (uint64_t)assembly; + *module_id = 0; + + if (assembly) + *module_id = (uint64_t)mono_assembly_get_image_internal (assembly); + + if (assembly && assembly_name) + *assembly_name = (ep_char8_t *)mono_stringify_assembly_name (&assembly->aname); + else if (assembly_name) + *assembly_name = NULL; +} + +static +void +assembly_loading_callback ( + MonoProfiler *prof, + MonoAssembly *assembly) +{ + if (!EventEnabledMonoProfilerAssemblyLoading ()) + return; + + uint64_t assembly_id; + uint64_t module_id; + + get_assembly_data (assembly, &assembly_id, &module_id, NULL); + + fire_event_enter (); + + FireEtwMonoProfilerAssemblyLoading ( + assembly_id, + module_id, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +assembly_loaded_callback ( + MonoProfiler *prof, + MonoAssembly *assembly) +{ + if (!EventEnabledMonoProfilerAssemblyLoaded ()) + return; + + uint64_t assembly_id; + uint64_t module_id; + ep_char8_t *assembly_name; + + get_assembly_data (assembly, &assembly_id, &module_id, &assembly_name); + + fire_event_enter (); + + FireEtwMonoProfilerAssemblyLoaded ( + assembly_id, + module_id, + assembly_name ? assembly_name : "", + NULL, + NULL); + + fire_event_exit (); + + g_free (assembly_name); +} + +static +void +assembly_unloading_callback ( + MonoProfiler *prof, + MonoAssembly *assembly) +{ + if (!EventEnabledMonoProfilerAssemblyUnloading ()) + return; + + uint64_t assembly_id; + uint64_t module_id; + + get_assembly_data (assembly, &assembly_id, &module_id, NULL); + + fire_event_enter (); + + FireEtwMonoProfilerAssemblyUnloading ( + assembly_id, + module_id, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +assembly_unloaded_callback ( + MonoProfiler *prof, + MonoAssembly *assembly) +{ + if (!EventEnabledMonoProfilerAssemblyUnloaded ()) + return; + + uint64_t assembly_id; + uint64_t module_id; + ep_char8_t *assembly_name; + + get_assembly_data (assembly, &assembly_id, &module_id, &assembly_name); + + fire_event_enter (); + + FireEtwMonoProfilerAssemblyUnloaded ( + assembly_id, + module_id, + assembly_name ? assembly_name : "", + NULL, + NULL); + + fire_event_exit (); + + g_free (assembly_name); +} + +static +void +method_enter_callback ( + MonoProfiler *prof, + MonoMethod *method, + MonoProfilerCallContext *context) +{ + if (!EventEnabledMonoProfilerMethodEnter ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerMethodEnter ( + (uint64_t)method, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +method_leave_callback ( + MonoProfiler *prof, + MonoMethod *method, + MonoProfilerCallContext *context) +{ + if (!EventEnabledMonoProfilerMethodLeave ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerMethodLeave ( + (uint64_t)method, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +method_tail_call_callback ( + MonoProfiler *prof, + MonoMethod *method, + MonoMethod *target_method) +{ + if (!EventEnabledMonoProfilerMethodTailCall ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerMethodTailCall ( + (uint64_t)method, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +method_exception_leave_callback ( + MonoProfiler *prof, + MonoMethod *method, + MonoObject *exc) +{ + if (!EventEnabledMonoProfilerMethodExceptionLeave ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerMethodExceptionLeave ( + (uint64_t)method, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +method_free_callback ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerMethodFree ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerMethodFree ( + (uint64_t)method, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +method_begin_invoke_callback ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerMethodBeginInvoke ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerMethodBeginInvoke ( + (uint64_t)method, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +method_end_invoke_callback ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (!EventEnabledMonoProfilerMethodEndInvoke ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerMethodEndInvoke ( + (uint64_t)method, + NULL, + NULL); + + fire_event_exit (); +} + +static +MonoProfilerCallInstrumentationFlags +method_instrumentation_filter_callback ( + MonoProfiler *prof, + MonoMethod *method) +{ + if (_mono_profiler_provider_callspec.len > 0 && !mono_callspec_eval (method, &_mono_profiler_provider_callspec)) + return MONO_PROFILER_CALL_INSTRUMENTATION_NONE; + + return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | + MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | + MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL | + MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE; +} + +static +void +exception_throw_callback ( + MonoProfiler *prof, + MonoObject *exc) +{ + if (!EventEnabledMonoProfilerExceptionThrow ()) + return; + + uint64_t type_id = 0; + + if (exc && mono_object_class(exc)) + type_id = (uint64_t)m_class_get_byval_arg (mono_object_class(exc)); + + fire_event_enter (); + + FireEtwMonoProfilerExceptionThrow ( + type_id, + SGEN_POINTER_UNTAG_ALL (exc), + NULL, + NULL); + + fire_event_exit (); +} + +static +void +exception_clause_callback ( + MonoProfiler *prof, + MonoMethod *method, + uint32_t clause_num, + MonoExceptionEnum clause_type, + MonoObject *exc) +{ + if (!EventEnabledMonoProfilerExceptionClause ()) + return; + + uint64_t type_id = 0; + + if (exc && mono_object_class(exc)) + type_id = (uint64_t)m_class_get_byval_arg (mono_object_class(exc)); + + fire_event_enter (); + + FireEtwMonoProfilerExceptionClause ( + (uint8_t)clause_type, + clause_num, + (uint64_t)method, + type_id, + SGEN_POINTER_UNTAG_ALL (exc), + NULL, + NULL); + + fire_event_exit (); +} + +static +void +gc_event_callback ( + MonoProfiler *prof, + MonoProfilerGCEvent gc_event, + uint32_t generation, + mono_bool serial) +{ + switch (gc_event) { + case MONO_GC_EVENT_PRE_STOP_WORLD: + case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED: + { + FireEtwMonoProfilerGCEvent ( + (uint8_t)gc_event, + generation, + NULL, + NULL); + break; + } + case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED: + { + FireEtwMonoProfilerGCEvent ( + (uint8_t)gc_event, + generation, + NULL, + NULL); + + gc_in_progress_start (); + + if (gc_heap_dump_in_progress ()) { + FireEtwMonoProfilerGCHeapDumpStart ( + provider_params_get_heap_collect_ondemand_value (), + NULL, + NULL); + } + + break; + } + case MONO_GC_EVENT_POST_STOP_WORLD: + { + if (gc_in_progress ()) { + uint64_t enabled_keywords = RUNTIME_MONO_PROFILER_PROVIDER_CONTEXT.EnabledKeywordsBitmask; + + if (is_keword_enabled (enabled_keywords, GC_ROOT_KEYWORD)) { + mono_profiler_set_gc_root_register_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_root_unregister_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_root_register_callback (_mono_heap_dump_profiler_provider, buffer_gc_event_root_register_callback); + mono_profiler_set_gc_root_unregister_callback (_mono_heap_dump_profiler_provider, buffer_gc_event_root_unregister_callback); + } + + if (gc_heap_dump_in_progress ()) { + if (is_keword_enabled (enabled_keywords, GC_ROOT_KEYWORD)) { + mono_profiler_set_gc_roots_callback (_mono_heap_dump_profiler_provider, buffer_gc_event_roots_callback); + } + + if (is_keword_enabled (enabled_keywords, GC_MOVES_KEYWORD)) { + mono_profiler_set_gc_moves_callback (_mono_heap_dump_profiler_provider, buffer_gc_event_moves_callback); + } + + if (is_keword_enabled (enabled_keywords, GC_RESIZE_KEYWORD)) { + mono_profiler_set_gc_resize_callback (_mono_heap_dump_profiler_provider, buffer_gc_event_resize_callback); + } + } + + buffer_gc_event ( + (uint8_t)gc_event, + generation); + } + break; + } + case MONO_GC_EVENT_START: + case MONO_GC_EVENT_END: + { + if (gc_in_progress ()) { + buffer_gc_event ( + (uint8_t)gc_event, + generation); + } + break; + } + case MONO_GC_EVENT_PRE_START_WORLD: + { + if (gc_in_progress ()) { + uint64_t enabled_keywords = RUNTIME_MONO_PROFILER_PROVIDER_CONTEXT.EnabledKeywordsBitmask; + + if (gc_heap_dump_in_progress () && is_keword_enabled (enabled_keywords, GC_HEAP_DUMP_KEYWORD)) + mono_gc_walk_heap (0, buffer_gc_event_heap_dump_object_reference_callback, NULL); + + mono_profiler_set_gc_root_register_callback (_mono_heap_dump_profiler_provider, NULL); + mono_profiler_set_gc_root_unregister_callback (_mono_heap_dump_profiler_provider, NULL); + mono_profiler_set_gc_roots_callback (_mono_heap_dump_profiler_provider, NULL); + mono_profiler_set_gc_moves_callback (_mono_heap_dump_profiler_provider, NULL); + mono_profiler_set_gc_resize_callback (_mono_heap_dump_profiler_provider, NULL); + + if (is_keword_enabled (enabled_keywords, GC_ROOT_KEYWORD)) { + mono_profiler_set_gc_root_register_callback (_mono_profiler_provider, gc_root_register_callback); + mono_profiler_set_gc_root_unregister_callback (_mono_profiler_provider, gc_root_unregister_callback); + } + + buffer_gc_event ( + (uint8_t)gc_event, + generation); + } + + break; + } + case MONO_GC_EVENT_POST_START_WORLD: + { + if (gc_in_progress ()) { + GHashTable *cache = NULL; + uint64_t enabled_keywords = RUNTIME_MONO_PROFILER_PROVIDER_CONTEXT.EnabledKeywordsBitmask; + + if (gc_heap_dump_in_progress () && is_keword_enabled (enabled_keywords, GC_HEAP_DUMP_VTABLE_CLASS_REF_KEYWORD)) + cache = g_hash_table_new_full (NULL, NULL, NULL, NULL); + + fire_buffered_gc_events_in_alloc_order (cache); + fire_cached_gc_events (cache); + + if (cache) + g_hash_table_destroy (cache); + + if (gc_heap_dump_in_progress ()) { + FireEtwMonoProfilerGCHeapDumpStop ( + NULL, + NULL); + } + + FireEtwMonoProfilerGCEvent ( + (uint8_t)gc_event, + generation, + NULL, + NULL); + + if (!is_keword_enabled (enabled_keywords, GC_KEYWORD)) + mono_profiler_set_gc_event_callback (_mono_profiler_provider, NULL); + + gc_heap_dump_in_progress_stop (); + gc_in_progress_stop (); + } + break; + } + default: + break; + } +} + +static +void +gc_allocation_callback ( + MonoProfiler *prof, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerGCAllocation ()) + return; + + uint64_t vtable_id = 0; + uint64_t object_size = 0; + + if (object) { + vtable_id = (uint64_t)mono_object_get_vtable_internal (object); + object_size = (uint64_t)mono_object_get_size_internal (object); + + /* account for object alignment */ + object_size += 7; + object_size &= ~7; + } + + fire_event_enter (); + + FireEtwMonoProfilerGCAllocation ( + vtable_id, + SGEN_POINTER_UNTAG_ALL (object), + object_size, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +gc_handle_created_callback ( + MonoProfiler *prof, + uint32_t handle, + MonoGCHandleType type, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerGCHandleCreated ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerGCHandleCreated ( + handle, + (uint8_t)type, + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); + + fire_event_exit (); +} + +static +void +gc_handle_deleted_callback ( + MonoProfiler *prof, + uint32_t handle, + MonoGCHandleType type) +{ + if (!EventEnabledMonoProfilerGCHandleDeleted ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerGCHandleDeleted ( + handle, + (uint8_t)type, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +gc_finalizing_callback (MonoProfiler *prof) +{ + if (!EventEnabledMonoProfilerGCFinalizing ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerGCFinalizing ( + NULL, + NULL); + + fire_event_exit (); +} + +static +void +gc_finalized_callback (MonoProfiler *prof) +{ + if (!EventEnabledMonoProfilerGCFinalized ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerGCFinalized ( + NULL, + NULL); + + fire_event_exit (); +} + +static +void +gc_finalizing_object_callback ( + MonoProfiler *prof, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerGCFinalizingObject ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerGCFinalizingObject ( + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); + + fire_event_exit (); +} + +static +void +gc_finalized_object_callback ( + MonoProfiler *prof, + MonoObject * object) +{ + if (!EventEnabledMonoProfilerGCFinalizedObject ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerGCFinalizedObject ( + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); + + fire_event_exit (); +} + +static +void +gc_root_register_callback ( + MonoProfiler *prof, + const mono_byte *start, + uintptr_t size, + MonoGCRootSource source, + const void * key, + const char * name) +{ + if (!EventEnabledMonoProfilerGCRootRegister ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerGCRootRegister ( + start, + (uint64_t)size, + (uint8_t) source, + (uint64_t)key, + (const ep_char8_t *)(name ? name : ""), + NULL, + NULL); + + fire_event_exit (); +} + +static +void +gc_root_unregister_callback ( + MonoProfiler *prof, + const mono_byte *start) +{ + if (!EventEnabledMonoProfilerGCRootUnregister ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerGCRootUnregister ( + start, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +monitor_contention_callback ( + MonoProfiler *prof, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerMonitorContention ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerMonitorContention ( + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); + + fire_event_exit (); +} + +static +void +monitor_failed_callback ( + MonoProfiler *prof, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerMonitorFailed ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerMonitorFailed ( + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); + + fire_event_exit (); +} + +static +void +monitor_acquired_callback ( + MonoProfiler *prof, + MonoObject *object) +{ + if (!EventEnabledMonoProfilerMonitorAcquired ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerMonitorAcquired ( + SGEN_POINTER_UNTAG_ALL (object), + NULL, + NULL); + + fire_event_exit (); +} + +static +void +thread_started_callback ( + MonoProfiler *prof, + uintptr_t tid) +{ + if (!EventEnabledMonoProfilerThreadStarted ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerThreadStarted ( + (uint64_t)tid, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +thread_stopping_callback ( + MonoProfiler *prof, + uintptr_t tid) +{ + if (!EventEnabledMonoProfilerThreadStopping ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerThreadStopping ( + (uint64_t)tid, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +thread_stopped_callback ( + MonoProfiler *prof, + uintptr_t tid) +{ + if (!EventEnabledMonoProfilerThreadStopped ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerThreadStopped ( + (uint64_t)tid, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +thread_exited_callback ( + MonoProfiler *prof, + uintptr_t tid) +{ + if (!EventEnabledMonoProfilerThreadExited ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerThreadExited ( + (uint64_t)tid, + NULL, + NULL); + + fire_event_exit (); +} + +static +void +thread_name_callback ( + MonoProfiler *prof, + uintptr_t tid, + const char *name) +{ + if (!EventEnabledMonoProfilerThreadName ()) + return; + + fire_event_enter (); + + FireEtwMonoProfilerThreadName ( + (uint64_t)tid, + (ep_char8_t *)(name ? name : ""), + NULL, + NULL); + + fire_event_exit (); +} + +static +void +calculate_live_keywords ( + uint64_t *live_keywords, + bool *trigger_heap_dump) +{ + uint64_t keywords[] = { GC_HEAP_COLLECT_KEYWORD }; + uint64_t count[] = { 0 }; + + ep_requires_lock_held (); + + EP_ASSERT (G_N_ELEMENTS (keywords) == G_N_ELEMENTS (count)); + *live_keywords = ep_rt_mono_session_calculate_and_count_all_keywords ( + "Microsoft-DotNETRuntimeMonoProfiler", + keywords, + count, + G_N_ELEMENTS (count)); + + *trigger_heap_dump = ep_rt_mono_is_runtime_initialized (); + *trigger_heap_dump &= is_keword_enabled (*live_keywords, GC_KEYWORD); + *trigger_heap_dump &= is_keword_enabled (*live_keywords, GC_HEAP_COLLECT_KEYWORD); + *trigger_heap_dump &= count [0] > _gc_heap_dump_trigger_count; + + _gc_heap_dump_trigger_count = count [0]; + + ep_requires_lock_held (); +} + +static +void +eventpipe_provider_callback ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_data) +{ + EP_ASSERT (_mono_profiler_provider_enabled); + + ep_rt_config_requires_lock_not_held (); + ep_rt_spin_lock_requires_lock_held (&_gc_lock); + + EP_ASSERT (_mono_profiler_provider != NULL); + EP_ASSERT (_mono_heap_dump_profiler_provider != NULL); + + EP_LOCK_ENTER (section1) + uint64_t live_keywords = 0; + bool trigger_heap_dump = false; + calculate_live_keywords (&live_keywords, &trigger_heap_dump); + + if (is_keword_enabled(live_keywords, LOADER_KEYWORD)) { + mono_profiler_set_domain_loading_callback (_mono_profiler_provider, app_domain_loading_callback); + mono_profiler_set_domain_loaded_callback (_mono_profiler_provider, app_domain_loaded_callback); + mono_profiler_set_domain_unloading_callback (_mono_profiler_provider, app_domain_unloading_callback); + mono_profiler_set_domain_unloaded_callback (_mono_profiler_provider, app_domain_unloaded_callback); + mono_profiler_set_domain_name_callback (_mono_profiler_provider, app_domain_name_callback); + mono_profiler_set_image_loading_callback (_mono_profiler_provider, module_loading_callback); + mono_profiler_set_image_failed_callback (_mono_profiler_provider, module_failed_callback); + mono_profiler_set_image_loaded_callback (_mono_profiler_provider, module_loaded_callback); + mono_profiler_set_image_unloading_callback (_mono_profiler_provider, module_unloading_callback); + mono_profiler_set_image_unloaded_callback (_mono_profiler_provider, module_unloaded_callback); + mono_profiler_set_assembly_loading_callback (_mono_profiler_provider, assembly_loading_callback); + mono_profiler_set_assembly_loaded_callback (_mono_profiler_provider, assembly_loaded_callback); + mono_profiler_set_assembly_unloading_callback (_mono_profiler_provider, assembly_unloading_callback); + mono_profiler_set_assembly_unloaded_callback (_mono_profiler_provider, assembly_unloaded_callback); + } else { + mono_profiler_set_domain_loading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_domain_loaded_callback (_mono_profiler_provider, NULL); + mono_profiler_set_domain_unloading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_domain_unloaded_callback (_mono_profiler_provider, NULL); + mono_profiler_set_domain_name_callback (_mono_profiler_provider, NULL); + mono_profiler_set_image_loading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_image_failed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_image_loaded_callback (_mono_profiler_provider, NULL); + mono_profiler_set_image_unloading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_image_unloaded_callback (_mono_profiler_provider, NULL); + mono_profiler_set_assembly_loading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_assembly_loaded_callback (_mono_profiler_provider, NULL); + mono_profiler_set_assembly_unloading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_assembly_unloaded_callback (_mono_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, JIT_KEYWORD)) { + mono_profiler_set_jit_begin_callback (_mono_profiler_provider, jit_begin_callback); + mono_profiler_set_jit_failed_callback (_mono_profiler_provider, jit_failed_callback); + mono_profiler_set_jit_done_callback (_mono_profiler_provider, jit_done_callback); + mono_profiler_set_jit_chunk_created_callback (_mono_profiler_provider, jit_chunk_created_callback); + mono_profiler_set_jit_chunk_destroyed_callback (_mono_profiler_provider, jit_chunk_destroyed_callback); + mono_profiler_set_jit_code_buffer_callback (_mono_profiler_provider, jit_code_buffer_callback); + } else { + mono_profiler_set_jit_begin_callback (_mono_profiler_provider, NULL); + mono_profiler_set_jit_failed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_jit_done_callback (_mono_profiler_provider, NULL); + mono_profiler_set_jit_chunk_created_callback (_mono_profiler_provider, NULL); + mono_profiler_set_jit_chunk_destroyed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_jit_code_buffer_callback (_mono_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, TYPE_LOADING_KEYWORD)) { + mono_profiler_set_class_loading_callback (_mono_profiler_provider, class_loading_callback); + mono_profiler_set_class_failed_callback (_mono_profiler_provider, class_failed_callback); + mono_profiler_set_class_loaded_callback (_mono_profiler_provider, class_loaded_callback); + mono_profiler_set_vtable_loading_callback (_mono_profiler_provider, vtable_loading_callback); + mono_profiler_set_vtable_failed_callback (_mono_profiler_provider, vtable_failed_callback); + mono_profiler_set_vtable_loaded_callback (_mono_profiler_provider, vtable_loaded_callback); + } else { + mono_profiler_set_class_loading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_class_failed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_class_loaded_callback (_mono_profiler_provider, NULL); + mono_profiler_set_vtable_loading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_vtable_failed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_vtable_loaded_callback (_mono_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, METHOD_TRACING_KEYWORD)) { + mono_profiler_set_method_enter_callback (_mono_profiler_provider, method_enter_callback); + mono_profiler_set_method_leave_callback (_mono_profiler_provider, method_leave_callback); + mono_profiler_set_method_tail_call_callback (_mono_profiler_provider, method_tail_call_callback); + mono_profiler_set_method_exception_leave_callback (_mono_profiler_provider, method_exception_leave_callback); + mono_profiler_set_method_free_callback (_mono_profiler_provider, method_free_callback); + mono_profiler_set_method_begin_invoke_callback (_mono_profiler_provider, method_begin_invoke_callback); + mono_profiler_set_method_end_invoke_callback (_mono_profiler_provider, method_end_invoke_callback); + } else { + mono_profiler_set_method_enter_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_leave_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_tail_call_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_exception_leave_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_free_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_begin_invoke_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_end_invoke_callback (_mono_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, EXCEPTION_KEYWORD)) { + mono_profiler_set_exception_throw_callback (_mono_profiler_provider, exception_throw_callback); + mono_profiler_set_exception_clause_callback (_mono_profiler_provider, exception_clause_callback); + } else { + mono_profiler_set_exception_throw_callback (_mono_profiler_provider, NULL); + mono_profiler_set_exception_clause_callback (_mono_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, GC_KEYWORD)) { + mono_profiler_set_gc_event_callback (_mono_profiler_provider, gc_event_callback); + } else { + // NOTE, disabled in mono_profiler_gc_event, MONO_GC_EVENT_POST_START_WORLD to make sure all + // callbacks during GC fires. + } + + if (is_keword_enabled(live_keywords, GC_ALLOCATION_KEYWORD)) { + mono_profiler_set_gc_allocation_callback (_mono_profiler_provider, gc_allocation_callback); + } else { + mono_profiler_set_gc_allocation_callback (_mono_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, GC_HANDLE_KEYWORD)) { + mono_profiler_set_gc_handle_created_callback (_mono_profiler_provider, gc_handle_created_callback); + mono_profiler_set_gc_handle_deleted_callback (_mono_profiler_provider, gc_handle_deleted_callback); + } else { + mono_profiler_set_gc_handle_created_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_handle_deleted_callback (_mono_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, GC_FINALIZATION_KEYWORD)) { + mono_profiler_set_gc_finalizing_callback (_mono_profiler_provider, gc_finalizing_callback); + mono_profiler_set_gc_finalized_callback (_mono_profiler_provider, gc_finalized_callback); + mono_profiler_set_gc_finalizing_object_callback (_mono_profiler_provider, gc_finalizing_object_callback); + mono_profiler_set_gc_finalized_object_callback (_mono_profiler_provider, gc_finalized_object_callback); + } else { + mono_profiler_set_gc_finalizing_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalized_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalizing_object_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalized_object_callback (_mono_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, GC_ROOT_KEYWORD)) { + mono_profiler_set_gc_root_register_callback (_mono_profiler_provider, gc_root_register_callback); + mono_profiler_set_gc_root_unregister_callback (_mono_profiler_provider, gc_root_unregister_callback); + } else { + mono_profiler_set_gc_root_register_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_root_unregister_callback (_mono_profiler_provider, NULL); + } + + if (is_keword_enabled (live_keywords, GC_HEAP_COLLECT_KEYWORD)) { + mono_profiler_set_gc_finalized_callback (_mono_heap_dump_profiler_provider, gc_heap_dump_trigger_callback); + } else { + mono_profiler_set_gc_finalized_callback (_mono_heap_dump_profiler_provider, NULL); + } + + if (is_keword_enabled (live_keywords, MONITOR_KEYWORD) || is_keword_enabled (match_any_keywords, CONTENTION_KEYWORD)) { + mono_profiler_set_monitor_contention_callback (_mono_profiler_provider, monitor_contention_callback); + } else { + mono_profiler_set_monitor_contention_callback (_mono_profiler_provider, NULL); + } + + if (is_keword_enabled (live_keywords, MONITOR_KEYWORD)) { + mono_profiler_set_monitor_failed_callback (_mono_profiler_provider, monitor_failed_callback); + mono_profiler_set_monitor_acquired_callback (_mono_profiler_provider, monitor_acquired_callback); + } else { + mono_profiler_set_monitor_failed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_monitor_acquired_callback (_mono_profiler_provider, NULL); + } + + if (is_keword_enabled (live_keywords, THREADING_KEYWORD)) { + mono_profiler_set_thread_started_callback (_mono_profiler_provider, thread_started_callback); + mono_profiler_set_thread_stopping_callback (_mono_profiler_provider, thread_stopping_callback); + mono_profiler_set_thread_stopped_callback (_mono_profiler_provider, thread_stopped_callback); + mono_profiler_set_thread_exited_callback (_mono_profiler_provider, thread_exited_callback); + mono_profiler_set_thread_name_callback (_mono_profiler_provider, thread_name_callback); + } else { + mono_profiler_set_thread_started_callback (_mono_profiler_provider, NULL); + mono_profiler_set_thread_stopping_callback (_mono_profiler_provider, NULL); + mono_profiler_set_thread_stopped_callback (_mono_profiler_provider, NULL); + mono_profiler_set_thread_exited_callback (_mono_profiler_provider, NULL); + mono_profiler_set_thread_name_callback (_mono_profiler_provider, NULL); + } + + if (_mono_profiler_provider_callspec.enabled) { + if (is_keword_enabled(live_keywords, METHOD_INSTRUMENTATION_KEYWORD)) { + mono_profiler_set_call_instrumentation_filter_callback (_mono_profiler_provider, method_instrumentation_filter_callback); + } else { + mono_profiler_set_call_instrumentation_filter_callback (_mono_profiler_provider, NULL); + } + } + + if (trigger_heap_dump) { + if (filter_data) { + if (provider_params_contains_heap_collect_ondemand (filter_data) && !provider_params_remove (filter_data)) { + provider_params_add (filter_data); + } + } + + gc_heap_dump_request_params_push_value (filter_data); + gc_heap_dump_requests_inc (); + mono_gc_finalize_notify (); + } else { + provider_params_free (); + } + + RUNTIME_MONO_PROFILER_PROVIDER_CONTEXT.Level = level; + RUNTIME_MONO_PROFILER_PROVIDER_CONTEXT.EnabledKeywordsBitmask = live_keywords; + RUNTIME_MONO_PROFILER_PROVIDER_CONTEXT.IsEnabled = (live_keywords == 1 ? true : false); + EP_LOCK_EXIT (section1) + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +void +EventPipeEtwCallbackDotNETRuntimeMonoProfiler ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_data) +{ + if (!_mono_profiler_provider_enabled) { + mono_trace ( + G_LOG_LEVEL_WARNING, + MONO_TRACE_DIAGNOSTICS, + "Microsoft-DotNETRuntimeMonoProfiler disabled, " + "set MONO_DIAGNOSTICS=--diagnostic-mono-profiler=enable " + "environment variable to enable provider."); + return; + } + + ep_rt_spin_lock_requires_lock_not_held (&_gc_lock); + + EP_SPIN_LOCK_ENTER (&_gc_lock, section1); + eventpipe_provider_callback ( + source_id, + is_enabled, + level, + match_any_keywords, + match_all_keywords, + filter_data, + callback_data); + EP_SPIN_LOCK_EXIT (&_gc_lock, section1); + +ep_on_exit: + ep_rt_spin_lock_requires_lock_not_held (&_gc_lock); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +void +ep_rt_mono_profiler_provider_component_init (void) +{ + if (_mono_profiler_provider_enabled) { + _mono_profiler_provider = mono_profiler_create (NULL); + _mono_heap_dump_profiler_provider = mono_profiler_create (NULL); + } +} + +void +ep_rt_mono_profiler_provider_init (void) +{ + if (_mono_profiler_provider_enabled) { + EP_ASSERT (_mono_profiler_provider != NULL); + EP_ASSERT (_mono_heap_dump_profiler_provider != NULL); + + ep_rt_spin_lock_alloc (&_gc_lock); + } +} + +void +ep_rt_mono_profiler_provider_fini (void) +{ + if (_mono_profiler_provider_enabled) { + if (_mono_profiler_provider_callspec.enabled) + mono_callspec_cleanup (&_mono_profiler_provider_callspec); + + if (_mono_profiler_provider) { + mono_profiler_set_gc_root_register_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_root_unregister_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_event_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_allocation_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_handle_created_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_handle_deleted_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalizing_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalized_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalizing_object_callback (_mono_profiler_provider, NULL); + mono_profiler_set_gc_finalized_object_callback (_mono_profiler_provider, NULL); + + mono_profiler_set_domain_loading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_domain_loaded_callback (_mono_profiler_provider, NULL); + mono_profiler_set_domain_unloading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_domain_unloaded_callback (_mono_profiler_provider, NULL); + mono_profiler_set_domain_name_callback (_mono_profiler_provider, NULL); + + mono_profiler_set_image_loading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_image_failed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_image_loaded_callback (_mono_profiler_provider, NULL); + mono_profiler_set_image_unloading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_image_unloaded_callback (_mono_profiler_provider, NULL); + + mono_profiler_set_assembly_loading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_assembly_loaded_callback (_mono_profiler_provider, NULL); + mono_profiler_set_assembly_unloading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_assembly_unloaded_callback (_mono_profiler_provider, NULL); + + mono_profiler_set_jit_begin_callback (_mono_profiler_provider, NULL); + mono_profiler_set_jit_failed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_jit_done_callback (_mono_profiler_provider, NULL); + mono_profiler_set_jit_chunk_created_callback (_mono_profiler_provider, NULL); + mono_profiler_set_jit_chunk_destroyed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_jit_code_buffer_callback (_mono_profiler_provider, NULL); + + mono_profiler_set_class_loading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_class_failed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_class_loaded_callback (_mono_profiler_provider, NULL); + + mono_profiler_set_vtable_loading_callback (_mono_profiler_provider, NULL); + mono_profiler_set_vtable_failed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_vtable_loaded_callback (_mono_profiler_provider, NULL); + + mono_profiler_set_method_enter_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_leave_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_tail_call_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_exception_leave_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_free_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_begin_invoke_callback (_mono_profiler_provider, NULL); + mono_profiler_set_method_end_invoke_callback (_mono_profiler_provider, NULL); + + mono_profiler_set_exception_throw_callback (_mono_profiler_provider, NULL); + mono_profiler_set_exception_clause_callback (_mono_profiler_provider, NULL); + + mono_profiler_set_monitor_contention_callback (_mono_profiler_provider, NULL); + mono_profiler_set_monitor_failed_callback (_mono_profiler_provider, NULL); + mono_profiler_set_monitor_acquired_callback (_mono_profiler_provider, NULL); + + mono_profiler_set_thread_started_callback (_mono_profiler_provider, NULL); + mono_profiler_set_thread_stopping_callback (_mono_profiler_provider, NULL); + mono_profiler_set_thread_stopped_callback (_mono_profiler_provider, NULL); + mono_profiler_set_thread_exited_callback (_mono_profiler_provider, NULL); + mono_profiler_set_thread_name_callback (_mono_profiler_provider, NULL); + + mono_profiler_set_call_instrumentation_filter_callback (_mono_profiler_provider, NULL); + } + _mono_profiler_provider = NULL; + + if (_mono_heap_dump_profiler_provider) { + mono_profiler_set_gc_root_register_callback (_mono_heap_dump_profiler_provider, NULL); + mono_profiler_set_gc_root_unregister_callback (_mono_heap_dump_profiler_provider, NULL); + mono_profiler_set_gc_roots_callback (_mono_heap_dump_profiler_provider, NULL); + mono_profiler_set_gc_moves_callback (_mono_heap_dump_profiler_provider, NULL); + mono_profiler_set_gc_resize_callback (_mono_heap_dump_profiler_provider, NULL); + mono_profiler_set_gc_finalized_callback (_mono_heap_dump_profiler_provider, NULL); + } + _mono_heap_dump_profiler_provider = NULL; + + _gc_heap_dump_requests = 0; + _gc_heap_dump_in_progress = 0; + _gc_heap_dump_trigger_count = 0; + _gc_state = 0; + + _mono_profiler_provider_enabled = DEFAULT_MONO_PROFILER_PROVIDER_ENABLED; + + gc_heap_dump_mem_block_free_all (); + + gc_heap_dump_request_params_free (); + provider_params_free (); + + ep_rt_spin_lock_free (&_gc_lock); + } +} + +static +bool +profiler_parse_options (const ep_char8_t *option) +{ + do { + if (!*option) + return false; + + if (!strncmp (option, "enable", 6)) { + _mono_profiler_provider_enabled = true; + option += 6; + } else if (!strncmp (option, "disable", 7)) { + _mono_profiler_provider_enabled = false; + option += 7; + } else if (!strncmp (option, "alloc", 5)) { + _mono_profiler_provider_enabled = true; + mono_profiler_enable_allocations (); + option += 5; + } else if (!strncmp (option, "exception", 9)) { + _mono_profiler_provider_enabled = true; + mono_profiler_enable_clauses (); + option += 9; + /*} else if (!strncmp (option, "sample", 6)) { + * _mono_profiler_provider_enabled = true; + mono_profiler_enable_sampling (_mono_profiler_provider); + option += 6;*/ + } else { + return false; + } + + if (*option == ',') + option++; + } while (*option); + + return true; +} + +bool +ep_rt_mono_profiler_provider_parse_options (const char *options) +{ + if (!options) + return false; + + if (strncmp (options, "--diagnostic-mono-profiler=", 27) == 0) { + if (!profiler_parse_options (options + 27)) + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable option: %s", options); + return true; + } else if (strncmp (options, "--diagnostic-mono-profiler-callspec=", 36) == 0) { + char *errstr = NULL; + if (!mono_callspec_parse (options + 36, &_mono_profiler_provider_callspec, &errstr)) { + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing '%s': %s", options, errstr); + g_free (errstr); + mono_callspec_cleanup (&_mono_profiler_provider_callspec); + } else { + mono_profiler_set_call_instrumentation_filter_callback (_mono_profiler_provider, method_instrumentation_filter_callback); + } + return true; + } else { + return false; + } +} + +#endif /* ENABLE_PERFTRACING */ + +MONO_EMPTY_SOURCE_FILE(eventpipe_rt_mono_profiler_provider); diff --git a/src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c b/src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c new file mode 100644 index 0000000000000..151dafced3e8e --- /dev/null +++ b/src/mono/mono/eventpipe/ep-rt-mono-runtime-provider.c @@ -0,0 +1,4675 @@ +#include + +#ifdef ENABLE_PERFTRACING +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern EVENTPIPE_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context; +extern EVENTPIPE_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context; + +#define RUNTIME_PROVIDER_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context +#define RUNTIME_RUNDOWN_PROVIDER_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context + +#define NUM_NANOSECONDS_IN_1_MS 1000000 + +// Sample profiler. +static GArray * _sampled_thread_callstacks = NULL; +static uint32_t _max_sampled_thread_count = 32; + +// Mono profilers. +extern MonoProfilerHandle _ep_rt_mono_default_profiler_provider; + +// Phantom JIT compile method. +static MonoMethod *_runtime_helper_compile_method = NULL; +static MonoJitInfo *_runtime_helper_compile_method_jitinfo = NULL; + +// Monitor.Enter methods. +static MonoMethod *_monitor_enter_method = NULL; +static MonoJitInfo *_monitor_enter_method_jitinfo = NULL; + +static MonoMethod *_monitor_enter_v4_method = NULL; +static MonoJitInfo *_monitor_enter_v4_method_jitinfo = NULL; + +// GC roots table. +static dn_umap_t _gc_roots_table = {0}; + +// Lock used for GC related activities. +static ep_rt_spin_lock_handle_t _gc_lock = {0}; + +// Rundown types. +typedef +bool +(*fire_method_rundown_events_func)( + const uint64_t method_id, + const uint64_t module_id, + const uint64_t method_start_address, + const uint32_t method_size, + const uint32_t method_token, + const uint32_t method_flags, + const ep_char8_t *method_namespace, + const ep_char8_t *method_name, + const ep_char8_t *method_signature, + const uint16_t count_of_map_entries, + const uint32_t *il_offsets, + const uint32_t *native_offsets, + bool aot_method, + bool verbose, + void *user_data); + +typedef +bool +(*fire_assembly_rundown_events_func)( + const uint64_t domain_id, + const uint64_t assembly_id, + const uint32_t assembly_flags, + const uint32_t binding_id, + const ep_char8_t *assembly_name, + const uint64_t module_id, + const uint32_t module_flags, + const uint32_t reserved_flags, + const ep_char8_t *module_il_path, + const ep_char8_t *module_native_path, + const uint8_t *managed_pdb_signature, + const uint32_t managed_pdb_age, + const ep_char8_t *managed_pdb_build_path, + const uint8_t *native_pdb_signature, + const uint32_t native_pdb_age, + const ep_char8_t *native_pdb_build_path, + void *user_data); + +typedef +bool +(*fire_domain_rundown_events_func)( + const uint64_t domain_id, + const uint32_t domain_flags, + const ep_char8_t *domain_name, + const uint32_t domain_index, + void *user_data); + +typedef struct _FireMethodEventsData { + MonoDomain *domain; + uint8_t *buffer; + size_t buffer_size; + fire_method_rundown_events_func method_events_func; +} FireMethodEventsData; + +typedef struct _StackWalkData { + EventPipeStackContents *stack_contents; + bool top_frame; + bool async_frame; + bool safe_point_frame; + bool runtime_invoke_frame; +} StackWalkData; + +typedef struct _SampleProfileStackWalkData { + StackWalkData stack_walk_data; + EventPipeStackContents stack_contents; + uint64_t thread_id; + uintptr_t thread_ip; + uint32_t payload_data; +} SampleProfileStackWalkData; + +// Rundown flags. +#define RUNTIME_SKU_MONO 0x4 +#define METHOD_FLAGS_DYNAMIC_METHOD 0x1 +#define METHOD_FLAGS_GENERIC_METHOD 0x2 +#define METHOD_FLAGS_SHARED_GENERIC_METHOD 0x4 +#define METHOD_FLAGS_JITTED_METHOD 0x8 +#define METHOD_FLAGS_JITTED_HELPER_METHOD 0x10 +#define METHOD_FLAGS_EXTENT_HOT_SECTION 0x00000000 +#define METHOD_FLAGS_EXTENT_COLD_SECTION 0x10000000 + +#define MODULE_FLAGS_NATIVE_MODULE 0x2 +#define MODULE_FLAGS_DYNAMIC_MODULE 0x4 +#define MODULE_FLAGS_MANIFEST_MODULE 0x8 + +#define ASSEMBLY_FLAGS_DYNAMIC_ASSEMBLY 0x2 +#define ASSEMBLY_FLAGS_NATIVE_ASSEMBLY 0x4 +#define ASSEMBLY_FLAGS_COLLECTIBLE_ASSEMBLY 0x8 + +#define DOMAIN_FLAGS_DEFAULT_DOMAIN 0x1 +#define DOMAIN_FLAGS_EXECUTABLE_DOMAIN 0x2 + +// Event data types. +struct _ModuleEventData { + uint8_t module_il_pdb_signature [EP_GUID_SIZE]; + uint8_t module_native_pdb_signature [EP_GUID_SIZE]; + uint64_t domain_id; + uint64_t module_id; + uint64_t assembly_id; + const char *module_il_path; + const char *module_il_pdb_path; + const char *module_native_path; + const char *module_native_pdb_path; + uint32_t module_il_pdb_age; + uint32_t module_native_pdb_age; + uint32_t reserved_flags; + uint32_t module_flags; +}; + +typedef struct _ModuleEventData ModuleEventData; + +struct _AssemblyEventData { + uint64_t domain_id; + uint64_t assembly_id; + uint64_t binding_id; + char *assembly_name; + uint32_t assembly_flags; +}; + +typedef struct _AssemblyEventData AssemblyEventData; + +// Event flags. +#define THREAD_FLAGS_GC_SPECIAL 0x00000001 +#define THREAD_FLAGS_FINALIZER 0x00000002 +#define THREAD_FLAGS_THREADPOOL_WORKER 0x00000004 + +#define EXCEPTION_THROWN_FLAGS_HAS_INNER 0x1 +#define EXCEPTION_THROWN_FLAGS_IS_NESTED 0x2 +#define EXCEPTION_THROWN_FLAGS_IS_RETHROWN 0x4 +#define EXCEPTION_THROWN_FLAGS_IS_CSE 0x8 +#define EXCEPTION_THROWN_FLAGS_IS_CLS_COMPLIANT 0x10 + +// BulkType types. + +typedef enum { + TYPE_FLAGS_DELEGATE = 0x1, + TYPE_FLAGS_FINALIZABLE = 0x2, + TYPE_FLAGS_EXTERNALLY_IMPLEMENTED_COM_OBJECT = 0x4, + TYPE_FLAGS_ARRAY = 0x8, + + TYPE_FLAGS_ARRAY_RANK_MASK = 0x3F00, + TYPE_FLAGS_ARRAY_RANK_SHIFT = 8, + TYPE_FLAGS_ARRAY_RANK_MAX = TYPE_FLAGS_ARRAY_RANK_MASK >> TYPE_FLAGS_ARRAY_RANK_SHIFT +} TypeFlags; + +// This only contains the fixed-size data at the top of each struct in +// the bulk type event. These fields must still match exactly the initial +// fields of the struct described in the manifest. +typedef struct _EventStructBulkTypeFixedSizedData { + uint64_t type_id; + uint64_t module_id; + uint32_t type_name_id; + uint32_t flags; + uint8_t cor_element_type; +} EventStructBulkTypeFixedSizedData; + +// Represents one instance of the Value struct inside a single BulkType event +typedef struct _BulkTypeValue { + EventStructBulkTypeFixedSizedData fixed_sized_data; + uint32_t type_parameters_count; + MonoType **mono_type_parameters; + const ep_char8_t *name; +} BulkTypeValue; + +// ETW has a limitation of 64K for TOTAL event Size, however there is overhead associated with +// the event headers. It is unclear exactly how much that is, but 1K should be sufficiently +// far away to avoid problems without sacrificing the perf of bulk processing. +#define MAX_EVENT_BYTE_COUNT (63 * 1024) + +// The maximum event size, and the size of the buffer that we allocate to hold the event contents. +#define MAX_SIZE_OF_EVENT_BUFFER 65536 + +// Estimate of how many bytes we can squeeze in the event data for the value struct +// array. (Intentionally overestimate the size of the non-array parts to keep it safe.) +// This follows CoreCLR's kMaxBytesTypeValues. +#define MAX_TYPE_VALUES_BYTES (MAX_EVENT_BYTE_COUNT - 0x30) + +// Estimate of how many type value elements we can put into the struct array, while +// staying under the ETW event size limit. Note that this is impossible to calculate +// perfectly, since each element of the struct array has variable size. +// +// In addition to the byte-size limit per event, Windows always forces on us a +// max-number-of-descriptors per event, which in the case of BulkType, will kick in +// far sooner. There's a max number of 128 descriptors allowed per event. 2 are used +// for Count + ClrInstanceID. Then 4 per batched value. (Might actually be 3 if there +// are no type parameters to log, but let's overestimate at 4 per value). +#define K_MAX_COUNT_TYPE_VALUES ((uint32_t)(128 - 2) / 4) + +typedef enum { + TYPE_LOG_BEHAVIOR_IF_FIRST_TIME, + TYPE_LOG_BEHAVIOR_ALWAYS_LOG, + TYPE_LOG_BEHAVIOR_ALWAYS_LOG_TOP_LEVEL +} TypeLogBehavior; + +typedef struct _BulkTypeEventLogger { + BulkTypeValue bulk_type_values [K_MAX_COUNT_TYPE_VALUES]; + uint8_t *bulk_type_event_buffer; + uint32_t bulk_type_value_count; + uint32_t bulk_type_value_byte_count; + MonoMemPool *mem_pool; + dn_umap_t *type_cache; +} BulkTypeEventLogger; + +// ETW has a limit for maximum event size. Do not log overly large method type argument sets +static const uint32_t MAX_METHOD_TYPE_ARGUMENT_COUNT = 1024; + +// GC roots type. +typedef struct _GCRootData { + uintptr_t start; + uintptr_t end; + const void *key; + char *name; + MonoGCRootSource source; +} GCRootData; + +// GC heap dump types. +typedef enum { + BUFFERED_GC_EVENT_OBJECT_REF = 1, + BUFFERED_GC_EVENT_ROOTS = 2, +} BufferedGCEventType; + +typedef struct _BufferedGCEvent BufferedGCEvent; +struct _BufferedGCEvent { + BufferedGCEventType type; + uint32_t payload_size; +}; + +typedef struct _GCHeapDumpMemFileBuffer GCHeapDumpMemFileBuffer; +struct _GCHeapDumpMemFileBuffer { + ep_char8_t *name; + int fd; + uint8_t *start; + uint8_t *current; + uint8_t *end; +}; + +typedef struct _GCHeapDumpBuffer GCHeapDumpBuffer; +struct _GCHeapDumpBuffer{ + void *context; + bool (*reset_func)(void *context); + uint8_t * (*alloc_func)(void *context ,size_t size); + const uint8_t * (*get_next_buffer_func)(void *context, size_t *size); +}; + +typedef struct _GCHeapDumpBulkData GCHeapDumpBulkData; +struct _GCHeapDumpBulkData { + uint8_t *data_start; + uint8_t *data_current; + uint8_t *data_end; + uint32_t index; + uint32_t count; + uint32_t max_count; +}; + +typedef enum { + GC_HEAP_DUMP_CONTEXT_STATE_INIT = 0, + GC_HEAP_DUMP_CONTEXT_STATE_START = 1, + GC_HEAP_DUMP_CONTEXT_STATE_DUMP = 2, + GC_HEAP_DUMP_CONTEXT_STATE_END = 3 +} GCHeapDumpContextState; + +typedef struct _GCHeapDumpContext GCHeapDumpContext; +struct _GCHeapDumpContext { + EVENTPIPE_TRACE_CONTEXT trace_context; + GCHeapDumpBulkData bulk_nodes; + GCHeapDumpBulkData bulk_edges; + GCHeapDumpBulkData bulk_root_edges; + GCHeapDumpBulkData bulk_root_cwt_elem_edges; + GCHeapDumpBulkData bulk_root_static_vars; + BulkTypeEventLogger *bulk_type_logger; + GCHeapDumpBuffer *buffer; + dn_vector_ptr_t *gc_roots; + uint32_t gc_reason; + uint32_t gc_type; + uint32_t gc_count; + uint32_t gc_depth; + uint32_t retry_count; + GCHeapDumpContextState state; +}; + +// Must match GCBulkNode layout in ClrEtwAll.man. +static const uint32_t BULK_NODE_EVENT_TYPE_SIZE = + // Address + sizeof (uintptr_t) + + // Size + sizeof (uint64_t) + + // TypeID + sizeof (uint64_t) + + // EdgeCount + sizeof (uint64_t); + +// Must match GCBulkEdge layout in ClrEtwAll.man. +static const uint32_t BULK_EDGE_EVENT_TYPE_SIZE = + // Value + sizeof (uintptr_t) + + // ReferencingFiledID + sizeof (uint32_t); + +// Must match GCBulkRootEdge layout in ClrEtwAll.man. +static const uint32_t BULK_ROOT_EDGE_EVENT_TYPE_SIZE = + // RootedNodeAddresses + sizeof (uintptr_t) + + // GCRootKind + sizeof (uint8_t) + + // GCRootFlag + sizeof (uint32_t) + + // GCRootID + sizeof (uintptr_t); + +// Must match GCBulkRootConditionalWeakTableElementEdge layout in ClrEtwAll.man. +static const uint32_t BULK_ROOT_CWT_ELEM_EDGE_EVENT_TYPE_SIZE = + // GCKeyNodeID + sizeof (uintptr_t) + + // GCValueNodeID + sizeof (uintptr_t) + + // GCRootID + sizeof (uintptr_t); + +// Must match GCBulkRootStaticVar layout in ClrEtwAll.man. +static const uint32_t BULK_ROOT_STATIC_VAR_EVENT_TYPE_SIZE = + // GCRootID + sizeof (uint64_t) + + // ObjectID + sizeof (uint64_t) + + // TypeID + sizeof (uint64_t) + + // Flags + sizeof (uint32_t) + + //FieldName + sizeof (ep_char16_t); + +// GC heap dump flags. +#define GC_REASON_INDUCED 1 +#define GC_TYPE_NGC 0 +#define GC_ROOT_FLAGS_NONE 0 +#define GC_ROOT_FLAGS_PINNING 1 +#define GC_ROOT_KIND_STACK 0 +#define GC_ROOT_KIND_FINALIZER 1 +#define GC_ROOT_KIND_HANDLE 2 +#define GC_ROOT_KIND_OTHER 3 + +static volatile uint32_t _gc_heap_dump_requests = 0; +static volatile uint32_t _gc_heap_dump_count = 0; +static volatile uint64_t _gc_heap_dump_trigger_count = 0; + +static dn_vector_t _gc_heap_dump_requests_data = {0}; + +static +uint64_t +get_typeid_for_type (MonoType *t); + +static +void +bulk_type_log_type_and_parameters_if_necessary ( + BulkTypeEventLogger *type_logger, + MonoType *mono_type, + TypeLogBehavior log_behavior); + + +static +uint16_t +clr_instance_get_id (void) +{ + // Mono runtime id. + return 9; +} + +static +bool +is_keyword_and_level_enabled ( + const EVENTPIPE_TRACE_CONTEXT *context, + uint8_t level, + uint64_t keyword) +{ + if (context->IsEnabled && level <= context->Level) + return (keyword == 0) || (keyword & context->EnabledKeywordsBitmask) != 0; + return false; +} + +static +bool +is_keword_enabled (uint64_t enabled_keywords, uint64_t keyword) +{ + return (enabled_keywords & keyword) == keyword; +} + +static +bool +is_gc_heap_dump_enabled (GCHeapDumpContext *context) +{ + if (!context) + return false; + + bool enabled = is_keyword_and_level_enabled (&context->trace_context, EP_EVENT_LEVEL_INFORMATIONAL, GC_HEAP_DUMP_KEYWORD); + enabled &= context->gc_reason == GC_REASON_INDUCED; + enabled &= context->gc_type == GC_TYPE_NGC; + return enabled; +} + +static +uint32_t +write_buffer_string_utf8_to_utf16_t ( + uint8_t **buf, + const ep_char8_t *str, + uint32_t len) +{ + uint32_t num_bytes_utf16_str = 0; + if (str && len != 0) { + glong len_utf16 = 0; + ep_char16_t *str_utf16 = (ep_char16_t *)(g_utf8_to_utf16le ((const gchar *)str, (glong)len, NULL, &len_utf16, NULL)); + if (str_utf16 && len_utf16 != 0) { + num_bytes_utf16_str = MIN (GLONG_TO_UINT32 (len_utf16), len) * sizeof (ep_char16_t); + memcpy (*buf, str_utf16, num_bytes_utf16_str); + } + g_free (str_utf16); + } + + (*buf) [num_bytes_utf16_str] = 0; + num_bytes_utf16_str++; + + (*buf) [num_bytes_utf16_str] = 0; + num_bytes_utf16_str++; + + *buf += num_bytes_utf16_str; + return num_bytes_utf16_str; +} + +static +bool +fire_method_rundown_events ( + const uint64_t method_id, + const uint64_t module_id, + const uint64_t method_start_address, + const uint32_t method_size, + const uint32_t method_token, + const uint32_t method_flags, + const ep_char8_t *method_namespace, + const ep_char8_t *method_name, + const ep_char8_t *method_signature, + const uint16_t count_of_map_entries, + const uint32_t *il_offsets, + const uint32_t *native_offsets, + bool aot_method, + bool verbose, + void *user_data) +{ + FireEtwMethodDCEndILToNativeMap ( + method_id, + 0, + 0, + count_of_map_entries, + il_offsets, + native_offsets, + clr_instance_get_id (), + NULL, + NULL); + + if (verbose) { + FireEtwMethodDCEndVerbose_V1 ( + method_id, + module_id, + method_start_address, + method_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, + method_namespace, + method_name, + method_signature, + clr_instance_get_id (), + NULL, + NULL); + + if (aot_method) + FireEtwMethodDCEndVerbose_V1 ( + method_id, + module_id, + method_start_address, + method_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, + method_namespace, + method_name, + method_signature, + clr_instance_get_id (), + NULL, + NULL); + } else { + FireEtwMethodDCEnd_V1 ( + method_id, + module_id, + method_start_address, + method_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, + clr_instance_get_id (), + NULL, + NULL); + + if (aot_method) + FireEtwMethodDCEnd_V1 ( + method_id, + module_id, + method_start_address, + method_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, + clr_instance_get_id (), + NULL, + NULL); + } + + return true; +} + +static +bool +fire_assembly_rundown_events ( + const uint64_t domain_id, + const uint64_t assembly_id, + const uint32_t assembly_flags, + const uint32_t binding_id, + const ep_char8_t *assembly_name, + const uint64_t module_id, + const uint32_t module_flags, + const uint32_t reserved_flags, + const ep_char8_t *module_il_path, + const ep_char8_t *module_native_path, + const uint8_t *managed_pdb_signature, + const uint32_t managed_pdb_age, + const ep_char8_t *managed_pdb_build_path, + const uint8_t *native_pdb_signature, + const uint32_t native_pdb_age, + const ep_char8_t *native_pdb_build_path, + void *user_data) +{ + FireEtwModuleDCEnd_V2 ( + module_id, + assembly_id, + module_flags, + reserved_flags, + module_il_path, + module_native_path, + clr_instance_get_id (), + managed_pdb_signature, + managed_pdb_age, + managed_pdb_build_path, + native_pdb_signature, + native_pdb_age, + native_pdb_build_path, + NULL, + NULL); + + FireEtwDomainModuleDCEnd_V1 ( + module_id, + assembly_id, + domain_id, + module_flags, + reserved_flags, + module_il_path, + module_native_path, + clr_instance_get_id (), + NULL, + NULL); + + FireEtwAssemblyDCEnd_V1 ( + assembly_id, + domain_id, + binding_id, + assembly_flags, + assembly_name, + clr_instance_get_id (), + NULL, + NULL); + + return true; +} + +static +bool +fire_domain_rundown_events ( + const uint64_t domain_id, + const uint32_t domain_flags, + const ep_char8_t *domain_name, + const uint32_t domain_index, + void *user_data) +{ + return FireEtwAppDomainDCEnd_V1 ( + domain_id, + domain_flags, + domain_name, + domain_index, + clr_instance_get_id (), + NULL, + NULL); +} + +static +void +fire_method_events ( + MonoJitInfo *ji, + MonoMethod *method, + FireMethodEventsData *events_data) +{ + EP_ASSERT (ji != NULL); + EP_ASSERT (events_data->domain != NULL); + EP_ASSERT (events_data->method_events_func != NULL); + + uint64_t method_id = 0; + uint64_t module_id = 0; + uint64_t method_code_start = (uint64_t)ji->code_start; + uint32_t method_code_size = (uint32_t)ji->code_size; + uint32_t method_token = 0; + uint32_t method_flags = 0; + uint8_t kind = MONO_CLASS_DEF; + char *method_namespace = NULL; + const char *method_name = NULL; + char *method_signature = NULL; + bool verbose = (RUNTIME_RUNDOWN_PROVIDER_CONTEXT.Level >= (uint8_t)EP_EVENT_LEVEL_VERBOSE); + + //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. + + if (method) { + method_id = (uint64_t)method; + method_token = method->token; + + if (mono_jit_info_get_generic_sharing_context (ji)) + method_flags |= METHOD_FLAGS_SHARED_GENERIC_METHOD; + + if (method->dynamic) + method_flags |= METHOD_FLAGS_DYNAMIC_METHOD; + + if (!ji->from_aot && !ji->from_llvm) { + method_flags |= METHOD_FLAGS_JITTED_METHOD; + if (method->wrapper_type != MONO_WRAPPER_NONE) + method_flags |= METHOD_FLAGS_JITTED_HELPER_METHOD; + } + + if (method->is_generic || method->is_inflated) + method_flags |= METHOD_FLAGS_GENERIC_METHOD; + + if (method->klass) { + module_id = (uint64_t)m_class_get_image (method->klass); + kind = m_class_get_class_kind (method->klass); + if (kind == MONO_CLASS_GTD || kind == MONO_CLASS_GINST) + method_flags |= METHOD_FLAGS_GENERIC_METHOD; + } + + if (verbose) { + method_name = method->name; + method_signature = mono_signature_full_name (mono_method_signature_internal (method)); + if (method->klass) + method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); + } + + } + + uint32_t offset_entries = 0; + uint32_t *il_offsets = NULL; + uint32_t *native_offsets = NULL; + + MonoDebugMethodJitInfo *debug_info = method ? mono_debug_find_method (method, events_data->domain) : NULL; + if (debug_info) { + offset_entries = debug_info->num_line_numbers; + if (offset_entries != 0) { + size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); + if (!events_data->buffer || needed_size > events_data->buffer_size) { + g_free (events_data->buffer); + events_data->buffer_size = (size_t)(needed_size * 1.5); + events_data->buffer = g_new (uint8_t, events_data->buffer_size); + } + + if (events_data->buffer) { + il_offsets = (uint32_t*)events_data->buffer; + native_offsets = il_offsets + offset_entries; + + uint8_t *il_offsets_ptr = (uint8_t *)il_offsets; + uint8_t *native_offsets_ptr = (uint8_t *)native_offsets; + for (uint32_t offset_count = 0; offset_count < offset_entries; ++offset_count) { + ep_write_buffer_uint32_t (&il_offsets_ptr, debug_info->line_numbers [offset_count].il_offset); + ep_write_buffer_uint32_t (&native_offsets_ptr, debug_info->line_numbers [offset_count].native_offset); + } + } + } + + mono_debug_free_method_jit_info (debug_info); + } + + if (events_data->buffer && !il_offsets && !native_offsets) { + // No IL offset -> Native offset mapping available. Put all code on IL offset 0. + EP_ASSERT (events_data->buffer_size >= sizeof (uint32_t) * 2); + offset_entries = 1; + il_offsets = (uint32_t*)events_data->buffer; + native_offsets = il_offsets + offset_entries; + il_offsets [0] = 0; + native_offsets [0] = ep_rt_val_uint32_t ((uint32_t)ji->code_size); + } + + events_data->method_events_func ( + method_id, + module_id, + method_code_start, + method_code_size, + method_token, + method_flags, + (ep_char8_t *)method_namespace, + (ep_char8_t *)method_name, + (ep_char8_t *)method_signature, + GUINT32_TO_UINT16 (offset_entries), + il_offsets, + native_offsets, + (ji->from_aot || ji->from_llvm), + verbose, + NULL); + + g_free (method_namespace); + g_free (method_signature); +} + +static +bool +include_method (MonoMethod *method) +{ + if (!method) { + return false; + } else if (!m_method_is_wrapper (method)) { + return true; + } else { + WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); + return (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) ? true : false; + } +} + +static +bool +get_module_event_data ( + MonoImage *image, + ModuleEventData *module_data) +{ + if (module_data) { + memset (module_data->module_il_pdb_signature, 0, EP_GUID_SIZE); + memset (module_data->module_native_pdb_signature, 0, EP_GUID_SIZE); + + // Under netcore we only have root domain. + MonoDomain *root_domain = mono_get_root_domain (); + + module_data->domain_id = (uint64_t)root_domain; + module_data->module_id = (uint64_t)image; + module_data->assembly_id = image ? (uint64_t)image->assembly : 0; + + // TODO: Extract all module native paths and pdb metadata when available. + module_data->module_native_path = ""; + module_data->module_native_pdb_path = ""; + module_data->module_native_pdb_age = 0; + + module_data->reserved_flags = 0; + + // Netcore has a 1:1 between assemblies and modules, so its always a manifest module. + module_data->module_flags = MODULE_FLAGS_MANIFEST_MODULE; + if (image && image->dynamic) + module_data->module_flags |= MODULE_FLAGS_DYNAMIC_MODULE; + if (image && image->aot_module) + module_data->module_flags |= MODULE_FLAGS_NATIVE_MODULE; + + module_data->module_il_path = NULL; + if (image && image->filename) { + /* if there's a filename, use it */ + module_data->module_il_path = image->filename; + } else if (image && image->module_name) { + /* otherwise, use the module name */ + module_data->module_il_path = image->module_name; + } + if (!module_data->module_il_path) + module_data->module_il_path = ""; + + module_data->module_il_pdb_path = ""; + module_data->module_il_pdb_age = 0; + + if (image && image->image_info) { + MonoPEDirEntry *debug_dir_entry = (MonoPEDirEntry *)&image->image_info->cli_header.datadir.pe_debug; + if (debug_dir_entry->size) { + ImageDebugDirectory debug_dir; + memset (&debug_dir, 0, sizeof (debug_dir)); + + uint32_t offset = mono_cli_rva_image_map (image, debug_dir_entry->rva); + for (uint32_t idx = 0; idx < debug_dir_entry->size / sizeof (ImageDebugDirectory); ++idx) { + uint8_t *data = (uint8_t *) ((ImageDebugDirectory *) (image->raw_data + offset) + idx); + debug_dir.major_version = read16 (data + 8); + debug_dir.minor_version = read16 (data + 10); + debug_dir.type = read32 (data + 12); + debug_dir.pointer = read32 (data + 24); + + if (debug_dir.type == DEBUG_DIR_ENTRY_CODEVIEW && debug_dir.major_version == 0x100 && debug_dir.minor_version == 0x504d) { + data = (uint8_t *)(image->raw_data + debug_dir.pointer); + int32_t signature = read32 (data); + if (signature == 0x53445352) { + memcpy (module_data->module_il_pdb_signature, data + 4, EP_GUID_SIZE); + module_data->module_il_pdb_age = read32 (data + 20); + module_data->module_il_pdb_path = (const char *)(data + 24); + break; + } + } + } + } + } + } + + return true; +} + +static +void +fire_method_events_callback ( + MonoJitInfo *ji, + void *user_data) +{ + FireMethodEventsData *events_data = (FireMethodEventsData *)user_data; + EP_ASSERT (events_data != NULL); + + if (ji && !ji->is_trampoline && !ji->async) { + MonoMethod *method = jinfo_get_method (ji); + if (include_method (method)) + fire_method_events (ji, method, events_data); + } +} + +static +void +fire_assembly_events ( + MonoDomain *domain, + MonoAssembly *assembly, + fire_assembly_rundown_events_func assembly_events_func) +{ + EP_ASSERT (domain != NULL); + EP_ASSERT (assembly != NULL); + EP_ASSERT (assembly_events_func != NULL); + + // Native methods are part of JIT table and already emitted. + // TODO: FireEtwMethodDCEndVerbose_V1_or_V2 for all native methods in module as well? + + uint32_t binding_id = 0; + + ModuleEventData module_data; + memset (&module_data, 0, sizeof (module_data)); + + get_module_event_data (assembly->image, &module_data); + + uint32_t assembly_flags = 0; + if (assembly->dynamic) + assembly_flags |= ASSEMBLY_FLAGS_DYNAMIC_ASSEMBLY; + + if (assembly->image && assembly->image->aot_module) { + assembly_flags |= ASSEMBLY_FLAGS_NATIVE_ASSEMBLY; + } + + char *assembly_name = mono_stringify_assembly_name (&assembly->aname); + + assembly_events_func ( + module_data.domain_id, + module_data.assembly_id, + assembly_flags, + binding_id, + (const ep_char8_t*)assembly_name, + module_data.module_id, + module_data.module_flags, + module_data.reserved_flags, + (const ep_char8_t *)module_data.module_il_path, + (const ep_char8_t *)module_data.module_native_path, + module_data.module_il_pdb_signature, + module_data.module_il_pdb_age, + (const ep_char8_t *)module_data.module_il_pdb_path, + module_data.module_native_pdb_signature, + module_data.module_native_pdb_age, + (const ep_char8_t *)module_data.module_native_pdb_path, + NULL); + + g_free (assembly_name); +} + +static +gboolean +execute_rundown ( + fire_domain_rundown_events_func domain_events_func, + fire_assembly_rundown_events_func assembly_events_func, + fire_method_rundown_events_func method_events_func) +{ + EP_ASSERT (domain_events_func != NULL); + EP_ASSERT (assembly_events_func != NULL); + EP_ASSERT (method_events_func != NULL); + + // Under netcore we only have root domain. + MonoDomain *root_domain = mono_get_root_domain (); + if (root_domain) { + uint64_t domain_id = (uint64_t)root_domain; + + // Emit all functions in use (JIT, AOT and Interpreter). + FireMethodEventsData events_data; + events_data.domain = root_domain; + events_data.buffer_size = 1024 * sizeof(uint32_t); + events_data.buffer = g_new (uint8_t, events_data.buffer_size); + events_data.method_events_func = method_events_func; + + // All called JIT/AOT methods should be included in jit info table. + mono_jit_info_table_foreach_internal (fire_method_events_callback, &events_data); + + // All called interpreted methods should be included in interpreter jit info table. + if (mono_get_runtime_callbacks ()->is_interpreter_enabled()) + mono_get_runtime_callbacks ()->interp_jit_info_foreach (fire_method_events_callback, &events_data); + + // Phantom methods injected in callstacks representing runtime functions. + if (_runtime_helper_compile_method_jitinfo && _runtime_helper_compile_method) + fire_method_events (_runtime_helper_compile_method_jitinfo, _runtime_helper_compile_method, &events_data); + if (_monitor_enter_method_jitinfo && _monitor_enter_method) + fire_method_events (_monitor_enter_method_jitinfo, _monitor_enter_method, &events_data); + if (_monitor_enter_v4_method_jitinfo && _monitor_enter_v4_method) + fire_method_events (_monitor_enter_v4_method_jitinfo, _monitor_enter_v4_method, &events_data); + + g_free (events_data.buffer); + + // Iterate all assemblies in domain. + GPtrArray *assemblies = mono_alc_get_all_loaded_assemblies (); + if (assemblies) { + for (uint32_t i = 0; i < assemblies->len; ++i) { + MonoAssembly *assembly = (MonoAssembly *)g_ptr_array_index (assemblies, i); + if (assembly) + fire_assembly_events (root_domain, assembly, assembly_events_func); + } + g_ptr_array_free (assemblies, TRUE); + } + + uint32_t domain_flags = DOMAIN_FLAGS_DEFAULT_DOMAIN | DOMAIN_FLAGS_EXECUTABLE_DOMAIN; + const char *domain_name = root_domain->friendly_name ? root_domain->friendly_name : ""; + uint32_t domain_index = 1; + + domain_events_func ( + domain_id, + domain_flags, + (const ep_char8_t *)domain_name, + domain_index, + NULL); + } + + return TRUE; +} + +static +bool +in_safe_point_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) +{ + EP_ASSERT (stack_content != NULL); + + // If top of stack is a managed->native icall wrapper for one of the below subtypes, we are at a safe point frame. + if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_state_poll || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_safe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_safe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_unsafe_region_unbalanced || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_unsafe_region_unbalanced)) + return true; + + return false; +} + +static +bool +in_runtime_invoke_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) +{ + EP_ASSERT (stack_content != NULL); + + // If top of stack is a managed->native runtime invoke wrapper, we are at a managed frame. + if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && + (wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC || + wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL)) + return true; + + return false; +} + +static +bool +in_monitor_enter_frame (WrapperInfo *wrapper) +{ + if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_fast || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_internal)) + return true; + + return false; +} + +static +bool +in_monitor_enter_v4_frame (WrapperInfo *wrapper) +{ + if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && + (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_fast || + wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_internal)) + return true; + + return false; +} + +static +gboolean +walk_managed_stack_for_thread ( + MonoStackFrameInfo *frame, + MonoContext *ctx, + StackWalkData *stack_walk_data) +{ + EP_ASSERT (frame != NULL); + EP_ASSERT (stack_walk_data != NULL); + + switch (frame->type) { + case FRAME_TYPE_DEBUGGER_INVOKE: + case FRAME_TYPE_MANAGED_TO_NATIVE: + case FRAME_TYPE_TRAMPOLINE: + case FRAME_TYPE_INTERP_TO_MANAGED: + case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: + case FRAME_TYPE_INTERP_ENTRY: + stack_walk_data->top_frame = false; + return FALSE; + case FRAME_TYPE_JIT_ENTRY: + // Frame in JIT compiler at top of callstack, add phantom frame representing call into JIT compiler. + // Makes it possible to detect stacks waiting on JIT compiler. + if (_runtime_helper_compile_method && stack_walk_data->top_frame) + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_runtime_helper_compile_method), _runtime_helper_compile_method); + stack_walk_data->top_frame = false; + return FALSE; + case FRAME_TYPE_MANAGED: + case FRAME_TYPE_INTERP: + if (frame->ji) { + stack_walk_data->async_frame |= frame->ji->async; + MonoMethod *method = frame->ji->async ? NULL : frame->actual_method; + if (method && m_method_is_wrapper (method)) { + WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); + if (in_safe_point_frame (stack_walk_data->stack_contents, wrapper)) { + stack_walk_data->safe_point_frame = true; + }else if (in_runtime_invoke_frame (stack_walk_data->stack_contents, wrapper)) { + stack_walk_data->runtime_invoke_frame = true; + } else if (_monitor_enter_method && in_monitor_enter_frame (wrapper)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_monitor_enter_method), _monitor_enter_method); + } else if (_monitor_enter_v4_method && in_monitor_enter_v4_frame (wrapper)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_monitor_enter_v4_method), _monitor_enter_v4_method); + } else if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); + } + } else if (method && !m_method_is_wrapper (method)) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); + } else if (!method && frame->ji->async && !frame->ji->is_trampoline) { + ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start), method); + } + } + stack_walk_data->top_frame = false; + return ep_stack_contents_get_length (stack_walk_data->stack_contents) >= EP_MAX_STACK_DEPTH; + default: + EP_UNREACHABLE ("walk_managed_stack_for_thread"); + return FALSE; + } +} + +static +gboolean +walk_managed_stack_for_thread_callback ( + MonoStackFrameInfo *frame, + MonoContext *ctx, + void *data) +{ + return walk_managed_stack_for_thread (frame, ctx, (StackWalkData *)data); +} + +static +gboolean +sample_profiler_walk_managed_stack_for_thread_callback ( + MonoStackFrameInfo *frame, + MonoContext *ctx, + void *data) +{ + EP_ASSERT (frame != NULL); + EP_ASSERT (data != NULL); + + SampleProfileStackWalkData *sample_data = (SampleProfileStackWalkData *)data; + + if (sample_data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR) { + switch (frame->type) { + case FRAME_TYPE_MANAGED: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + break; + case FRAME_TYPE_MANAGED_TO_NATIVE: + case FRAME_TYPE_TRAMPOLINE: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + break; + case FRAME_TYPE_JIT_ENTRY: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + break; + case FRAME_TYPE_INTERP: + sample_data->payload_data = frame->managed ? EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED : EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + break; + case FRAME_TYPE_INTERP_TO_MANAGED: + case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: + break; + default: + sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + } + } + + return walk_managed_stack_for_thread (frame, ctx, &sample_data->stack_walk_data); +} + +bool +ep_rt_mono_walk_managed_stack_for_thread ( + ep_rt_thread_handle_t thread, + EventPipeStackContents *stack_contents) +{ + EP_ASSERT (thread != NULL && stack_contents != NULL); + + StackWalkData stack_walk_data; + stack_walk_data.stack_contents = stack_contents; + stack_walk_data.top_frame = true; + stack_walk_data.async_frame = false; + stack_walk_data.safe_point_frame = false; + stack_walk_data.runtime_invoke_frame = false; + + bool restore_async_context = FALSE; + bool prevent_profiler_event_recursion = FALSE; + EventPipeMonoThreadData *thread_data = ep_rt_mono_thread_data_get_or_create (); + if (thread_data) { + prevent_profiler_event_recursion = thread_data->prevent_profiler_event_recursion; + if (prevent_profiler_event_recursion && !mono_thread_info_is_async_context ()) { + // Running stackwalk in async context mode is currently the only way to prevent + // unwinder to NOT load additional classes during stackwalk, making it signal unsafe and + // potential triggering uncontrolled recursion in profiler class loading event. + mono_thread_info_set_is_async_context (TRUE); + restore_async_context = TRUE; + } + thread_data->prevent_profiler_event_recursion = TRUE; + } + + if (thread == ep_rt_thread_get_handle () && mono_get_eh_callbacks ()->mono_walk_stack_with_ctx) + mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (walk_managed_stack_for_thread_callback, NULL, MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); + else if (mono_get_eh_callbacks ()->mono_walk_stack_with_state) + mono_get_eh_callbacks ()->mono_walk_stack_with_state (walk_managed_stack_for_thread_callback, mono_thread_info_get_suspend_state (thread), MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); + + if (thread_data) { + if (restore_async_context) + mono_thread_info_set_is_async_context (FALSE); + thread_data->prevent_profiler_event_recursion = prevent_profiler_event_recursion; + } + + return true; +} + +bool +ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( + ep_rt_thread_handle_t sampling_thread, + EventPipeEvent *sampling_event) +{ + // Follows CoreClr implementation of sample profiler. Generic invasive/expensive way to do CPU sample profiling relying on STW and stackwalks. + // TODO: Investigate alternatives on platforms supporting Signals/SuspendThread (see Mono profiler) or CPU PMU's (see ETW/perf_event_open). + + // Sample profiler only runs on one thread, no need to synchorinize. + if (!_sampled_thread_callstacks) + _sampled_thread_callstacks = g_array_sized_new (FALSE, FALSE, sizeof (SampleProfileStackWalkData), _max_sampled_thread_count); + + // Make sure there is room based on previous max number of sampled threads. + // NOTE, there is a chance there are more threads than max, if that's the case we will + // miss those threads in this sample, but will be included in next when max has been adjusted. + g_array_set_size (_sampled_thread_callstacks, _max_sampled_thread_count); + + uint32_t filtered_thread_count = 0; + uint32_t sampled_thread_count = 0; + + mono_stop_world (MONO_THREAD_INFO_FLAGS_NO_GC); + + bool restore_async_context = FALSE; + if (!mono_thread_info_is_async_context ()) { + mono_thread_info_set_is_async_context (TRUE); + restore_async_context = TRUE; + } + + // Record all info needed in sample events while runtime is suspended, must be async safe. + FOREACH_THREAD_SAFE_EXCLUDE (thread_info, MONO_THREAD_INFO_FLAGS_NO_GC | MONO_THREAD_INFO_FLAGS_NO_SAMPLE) { + if (!mono_thread_info_is_running (thread_info) && thread_info->jit_data) { + MonoThreadUnwindState *thread_state = mono_thread_info_get_suspend_state (thread_info); + if (thread_state->valid) { + if (sampled_thread_count < _max_sampled_thread_count) { + SampleProfileStackWalkData *data = &g_array_index (_sampled_thread_callstacks, SampleProfileStackWalkData, sampled_thread_count); + data->thread_id = ep_rt_thread_id_t_to_uint64_t (mono_thread_info_get_tid (thread_info)); + data->thread_ip = (uintptr_t)MONO_CONTEXT_GET_IP (&thread_state->ctx); + data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR; + data->stack_walk_data.stack_contents = &data->stack_contents; + data->stack_walk_data.top_frame = true; + data->stack_walk_data.async_frame = false; + data->stack_walk_data.safe_point_frame = false; + data->stack_walk_data.runtime_invoke_frame = false; + ep_stack_contents_reset (&data->stack_contents); + mono_get_eh_callbacks ()->mono_walk_stack_with_state (sample_profiler_walk_managed_stack_for_thread_callback, thread_state, MONO_UNWIND_SIGNAL_SAFE, data); + if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && (data->stack_walk_data.safe_point_frame || data->stack_walk_data.runtime_invoke_frame)) { + // If classified as external code (managed->native frame on top of stack), but have a safe point or runtime invoke frame + // as second, re-classify current callstack to be executing managed code. + data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; + } + if (data->stack_walk_data.top_frame && ep_stack_contents_get_length (&data->stack_contents) == 0) { + // If no managed frames (including helper frames) are located on stack, mark sample as beginning in external code. + // This can happen on attached embedding threads returning to native code between runtime invokes. + // Make sure sample is still written into EventPipe for all attached threads even if they are currently not having + // any managed frames on stack. Prevents some tools applying thread time heuristics to prolong duration of last sample + // when embedding thread returns to native code. It also opens ability to visualize number of samples in unmanaged code + // on attached threads when executing outside of runtime. If tooling is not interested in these sample events, they are easy + // to identify and filter out. + data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; + } + + sampled_thread_count++; + } + } + } + filtered_thread_count++; + } FOREACH_THREAD_SAFE_END + + if (restore_async_context) + mono_thread_info_set_is_async_context (FALSE); + + mono_restart_world (MONO_THREAD_INFO_FLAGS_NO_GC); + + // Fire sample event for threads. Must be done after runtime is resumed since it's not async safe. + // Since we can't keep thread info around after runtime as been suspended, use an empty + // adapter instance and only set recorded tid as parameter inside adapter. + THREAD_INFO_TYPE adapter = { { 0 } }; + for (uint32_t thread_count = 0; thread_count < sampled_thread_count; ++thread_count) { + SampleProfileStackWalkData *data = &g_array_index (_sampled_thread_callstacks, SampleProfileStackWalkData, thread_count); + if ((data->stack_walk_data.top_frame && data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL) || (data->payload_data != EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR && ep_stack_contents_get_length (&data->stack_contents) > 0)) { + // Check if we have an async frame, if so we will need to make sure all frames are registered in regular jit info table. + // TODO: An async frame can contain wrapper methods (no way to check during stackwalk), we could skip writing profile event + // for this specific stackwalk or we could cleanup stack_frames before writing profile event. + if (data->stack_walk_data.async_frame) { + for (uint32_t frame_count = 0; frame_count < data->stack_contents.next_available_frame; ++frame_count) + mono_jit_info_table_find_internal ((gpointer)data->stack_contents.stack_frames [frame_count], TRUE, FALSE); + } + mono_thread_info_set_tid (&adapter, ep_rt_uint64_t_to_thread_id_t (data->thread_id)); + uint32_t payload_data = ep_rt_val_uint32_t (data->payload_data); + ep_write_sample_profile_event (sampling_thread, sampling_event, &adapter, &data->stack_contents, (uint8_t *)&payload_data, sizeof (payload_data)); + } + } + + // Current thread count will be our next maximum sampled threads. + _max_sampled_thread_count = filtered_thread_count; + + return true; +} + +void +ep_rt_mono_execute_rundown (dn_vector_ptr_t *execution_checkpoints) +{ + ep_char8_t runtime_module_path [256]; + const uint8_t object_guid [EP_GUID_SIZE] = { 0 }; + const uint16_t runtime_product_qfe_version = 0; + const uint8_t startup_flags = 0; + const uint8_t startup_mode = 0; + const ep_char8_t *command_line = ""; + + if (!g_module_address ((void *)mono_init, runtime_module_path, sizeof (runtime_module_path), NULL, NULL, 0, NULL)) + runtime_module_path [0] = '\0'; + + FireEtwRuntimeInformationDCStart ( + clr_instance_get_id (), + RUNTIME_SKU_MONO, + RuntimeProductMajorVersion, + RuntimeProductMinorVersion, + RuntimeProductPatchVersion, + runtime_product_qfe_version, + RuntimeFileMajorVersion, + RuntimeFileMajorVersion, + RuntimeFileBuildVersion, + RuntimeFileRevisionVersion, + startup_mode, + startup_flags, + command_line, + object_guid, + runtime_module_path, + NULL, + NULL); + + if (execution_checkpoints) { + DN_VECTOR_PTR_FOREACH_BEGIN (EventPipeExecutionCheckpoint *, checkpoint, execution_checkpoints) { + FireEtwExecutionCheckpointDCEnd ( + clr_instance_get_id (), + checkpoint->name, + checkpoint->timestamp, + NULL, + NULL); + } DN_VECTOR_PTR_FOREACH_END; + } + + FireEtwDCEndInit_V1 ( + clr_instance_get_id (), + NULL, + NULL); + + execute_rundown ( + fire_domain_rundown_events, + fire_assembly_rundown_events, + fire_method_rundown_events); + + FireEtwDCEndComplete_V1 ( + clr_instance_get_id (), + NULL, + NULL); +} + +// Clear out BulkTypeValue before filling it out (array elements can get reused if there +// are enough types that we need to flush to multiple events). +static +void +bulk_type_value_clear (BulkTypeValue *bulk_type_value) +{ + memset (bulk_type_value, 0, sizeof(BulkTypeValue)); +} + +static +int +bulk_type_get_byte_count_in_event (BulkTypeValue *bulk_type_value) +{ + int name_len = bulk_type_value->name && bulk_type_value->name [0] != '\0' + ? GSIZE_TO_INT (strlen (bulk_type_value->name)) + : 0; + + // NOTE, must match manifest BulkType value type. + return sizeof (bulk_type_value->fixed_sized_data.type_id) + + sizeof (bulk_type_value->fixed_sized_data.module_id) + + sizeof (bulk_type_value->fixed_sized_data.type_name_id) + + sizeof (bulk_type_value->fixed_sized_data.flags) + + sizeof (bulk_type_value->fixed_sized_data.cor_element_type) + + (name_len + 1) * sizeof (ep_char16_t) + + sizeof (bulk_type_value->type_parameters_count) + + bulk_type_value->type_parameters_count * sizeof (uint64_t); +} + +static +BulkTypeEventLogger* +bulk_type_event_logger_alloc (void) +{ + BulkTypeEventLogger *type_logger = g_malloc0 (sizeof (BulkTypeEventLogger)); + type_logger->bulk_type_event_buffer = g_malloc0 (sizeof (uint8_t) * MAX_SIZE_OF_EVENT_BUFFER); + type_logger->mem_pool = mono_mempool_new (); + + dn_umap_custom_alloc_params_t params = {0, }; + params.value_dispose_func = g_free; + type_logger->type_cache = dn_umap_custom_alloc (¶ms); + + return type_logger; +} + +static +void +bulk_type_event_logger_free (BulkTypeEventLogger *type_logger) +{ + mono_mempool_destroy (type_logger->mem_pool); + dn_umap_free (type_logger->type_cache); + g_free (type_logger->bulk_type_event_buffer); + g_free (type_logger); +} + +//--------------------------------------------------------------------------------------- +// +// fire_bulk_type_event fires an ETW event for all the types batched so far, +// it then resets the state to start batching new types at the beginning of the +// bulk_type_values array. +// +// This follows CoreCLR's BulkTypeEventLogger::FireBulkTypeEvent + +static +void +bulk_type_fire_bulk_type_event (BulkTypeEventLogger *type_logger) +{ + if (type_logger->bulk_type_value_count == 0) + return; + + uint16_t clr_instance_id = clr_instance_get_id (); + + uint32_t values_element_size = 0; + + uint8_t *ptr = type_logger->bulk_type_event_buffer; + + // NOTE, must match manifest BulkType value type. + for (uint32_t type_value_index = 0; type_value_index < type_logger->bulk_type_value_count; type_value_index++) { + BulkTypeValue *target = &type_logger->bulk_type_values [type_value_index]; + + values_element_size += ep_write_buffer_uint64_t (&ptr, target->fixed_sized_data.type_id); + values_element_size += ep_write_buffer_uint64_t (&ptr, target->fixed_sized_data.module_id); + values_element_size += ep_write_buffer_uint32_t (&ptr, target->fixed_sized_data.type_name_id); + values_element_size += ep_write_buffer_uint32_t (&ptr, target->fixed_sized_data.flags); + values_element_size += ep_write_buffer_uint8_t (&ptr, target->fixed_sized_data.cor_element_type); + + uint32_t target_name_len = target->name && target->name [0] != '\0' ? GSIZE_TO_UINT32 (strlen (target->name)) : 0; + values_element_size += write_buffer_string_utf8_to_utf16_t (&ptr, target->name, target_name_len); + + values_element_size += ep_write_buffer_uint32_t (&ptr, target->type_parameters_count); + + for (uint32_t i = 0; i < target->type_parameters_count; i++) { + uint64_t type_parameter = get_typeid_for_type (target->mono_type_parameters [i]); + values_element_size += ep_write_buffer_uint64_t (&ptr, type_parameter); + } + } + + FireEtwBulkType ( + type_logger->bulk_type_value_count, + clr_instance_id, + values_element_size, + type_logger->bulk_type_event_buffer, + NULL, + NULL); + + memset (type_logger->bulk_type_event_buffer, 0, sizeof (uint8_t) * MAX_SIZE_OF_EVENT_BUFFER); + type_logger->bulk_type_value_count = 0; + type_logger->bulk_type_value_byte_count = 0; +} + +//--------------------------------------------------------------------------------------- +// +// get_typeid_for_type is responsible for obtaining the unique type identifier for a +// particular MonoType. MonoTypes are structs that are not unique pointers. There +// can be two different MonoTypes that both System.Thread or int32 or bool []. There +// is exactly one MonoClass * for any type, so we leverage the MonoClass a MonoType +// points to in order to obtain a unique type identifier in mono. With that unique +// MonoClass, its fields this_arg and _byval_arg are unique as well. +// +// Arguments: +// * mono_type - MonoType to be logged +// +// Return Value: +// type_id - Unique type identifier of mono_type + +static +uint64_t +get_typeid_for_type (MonoType *t) +{ + if (m_type_is_byref (t)) + return (uint64_t)m_class_get_this_arg (mono_class_from_mono_type_internal (t)); + else + return (uint64_t)m_class_get_byval_arg (mono_class_from_mono_type_internal (t)); +} + +static +uint64_t +get_typeid_for_class (MonoClass *c) +{ + return get_typeid_for_type (m_class_get_byval_arg (c)); +} + +//--------------------------------------------------------------------------------------- +// +// bulk_type_log_single_type batches a single type into the bulk type array and flushes +// the array to ETW if it fills up. Most interaction with the type system (type analysis) +// is done here. This does not recursively batch up any parameter types (arrays or generics), +// but does add their unique identifiers to the mono_type_parameters array. +// ep_rt_mono_log_type_and_parameters is responsible for initiating any recursive calls to +// deal with type parameters. +// +// Arguments: +// * type_logger - BulkTypeEventLogger instance +// * mono_type - MonoType to be logged +// +// Return Value: +// Index into array of where this type got batched. -1 if there was a failure. +// +// This follows CoreCLR's BulkTypeEventLogger::LogSingleType + +static +int +bulk_type_log_single_type ( + BulkTypeEventLogger *type_logger, + MonoType *mono_type) +{ + // If there's no room for another type, flush what we've got + if (type_logger->bulk_type_value_count == K_MAX_COUNT_TYPE_VALUES) + bulk_type_fire_bulk_type_event (type_logger); + + EP_ASSERT (type_logger->bulk_type_value_count < K_MAX_COUNT_TYPE_VALUES); + + BulkTypeValue *val = &type_logger->bulk_type_values [type_logger->bulk_type_value_count]; + bulk_type_value_clear (val); + + MonoClass *klass = mono_class_from_mono_type_internal (mono_type); + MonoType *mono_underlying_type = mono_type_get_underlying_type (mono_type); + + // Initialize val fixed_sized_data + val->fixed_sized_data.type_id = get_typeid_for_type (mono_type); + val->fixed_sized_data.module_id = (uint64_t)m_class_get_image (klass); + val->fixed_sized_data.type_name_id = m_class_get_type_token (klass) ? mono_metadata_make_token (MONO_TABLE_TYPEDEF, mono_metadata_token_index (m_class_get_type_token (klass))) : 0; + if (mono_class_has_finalizer (klass)) + val->fixed_sized_data.flags |= TYPE_FLAGS_FINALIZABLE; + if (m_class_is_delegate (klass)) + val->fixed_sized_data.flags |= TYPE_FLAGS_DELEGATE; + if (mono_class_is_com_object (klass)) + val->fixed_sized_data.flags |= TYPE_FLAGS_EXTERNALLY_IMPLEMENTED_COM_OBJECT; + val->fixed_sized_data.cor_element_type = (uint8_t)mono_underlying_type->type; + + // Sets val variable sized parameter type data, type_parameters_count, and mono_type_parameters associated + // with arrays or generics to be recursively batched in the same ep_rt_mono_log_type_and_parameters call + switch (mono_underlying_type->type) { + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + { + MonoArrayType *mono_array_type = mono_type_get_array_type (mono_type); + val->fixed_sized_data.flags |= TYPE_FLAGS_ARRAY; + if (mono_underlying_type->type == MONO_TYPE_ARRAY) { + // Only ranks less than TypeFlagsArrayRankMax are supported. + // Fortunately TypeFlagsArrayRankMax should be greater than the + // number of ranks the type loader will support + uint32_t rank = mono_array_type->rank; + if (rank < TYPE_FLAGS_ARRAY_RANK_MAX) { + rank <<= 8; + val->fixed_sized_data.flags |= rank; + } + } + + // mono arrays are always arrays of by value types + val->mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, 1 * sizeof (MonoType*)); + *val->mono_type_parameters = m_class_get_byval_arg (mono_array_type->eklass); + val->type_parameters_count++; + break; + } + case MONO_TYPE_GENERICINST: + { + MonoGenericInst *class_inst = mono_type->data.generic_class->context.class_inst; + val->type_parameters_count = class_inst->type_argc; + val->mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, val->type_parameters_count * sizeof (MonoType*)); + memcpy (val->mono_type_parameters, class_inst->type_argv, val->type_parameters_count * sizeof (MonoType*)); + break; + } + case MONO_TYPE_CLASS: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_PTR: + case MONO_TYPE_BYREF: + { + if (mono_underlying_type == mono_type) + break; + val->mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, 1 * sizeof (MonoType*)); + *val->mono_type_parameters = mono_underlying_type; + val->type_parameters_count++; + break; + } + default: + break; + } + + val->name = ""; + bool log_type_name = is_keyword_and_level_enabled (&RUNTIME_PROVIDER_CONTEXT, EP_EVENT_LEVEL_INFORMATIONAL, GC_HEAP_AND_TYPE_NAMES_KEYWORD); + if (type_logger->type_cache && log_type_name) { + dn_umap_it_t result = dn_umap_find (type_logger->type_cache, GUINT64_TO_POINTER (val->fixed_sized_data.type_id)); + if (dn_umap_it_end (result) || !dn_umap_it_value (result)) { + dn_umap_result_t insert = dn_umap_insert_or_assign (type_logger->type_cache, GUINT64_TO_POINTER (val->fixed_sized_data.type_id), mono_type_get_name_full (mono_type, MONO_TYPE_NAME_FORMAT_IL)); + if (insert.result) + result = insert.it; + } + val->name = !dn_umap_it_end (result) ? (const ep_char8_t *)dn_umap_it_value (result) : ""; + } + + // Now that we know the full size of this type's data, see if it fits in our + // batch or whether we need to flush + int val_byte_count = bulk_type_get_byte_count_in_event (val); + if (val_byte_count > MAX_TYPE_VALUES_BYTES) { + // NOTE: If name is actively used, set it to NULL and relevant memory management to reduce byte count + // This type is apparently so huge, it's too big to squeeze into an event, even + // if it were the only type batched in the whole event. Bail + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed to log single mono type %p with typeID %llu. Type is too large for the BulkType Event.\n", (gpointer)mono_type, (unsigned long long)val->fixed_sized_data.type_id); + return -1; + } + + if (type_logger->bulk_type_value_byte_count + val_byte_count > MAX_TYPE_VALUES_BYTES) { + // Although this type fits into the array, its size is so big that the entire + // array can't be logged via ETW. So flush the array, and start over by + // calling ourselves--this refetches the type info and puts it at the + // beginning of the array. Since we know this type is small enough to be + // batched into an event on its own, this recursive call will not try to + // call itself again. + g_assert (type_logger->bulk_type_value_byte_count + val_byte_count > MAX_TYPE_VALUES_BYTES); + bulk_type_fire_bulk_type_event (type_logger); + return bulk_type_log_single_type (type_logger, mono_type); + } + + // The type fits into the batch, so update our state + type_logger->bulk_type_value_count++; + type_logger->bulk_type_value_byte_count += val_byte_count; + return type_logger->bulk_type_value_count - 1; +} + +//--------------------------------------------------------------------------------------- +// +// High-level method to batch a type and (recursively) its type parameters, flushing to +// ETW as needed. This is called by bulk_type_log_type_and_parameters_if_necessary. +// +// Arguments: +// * type_logger - BulkTypeEventLogger instance +// * mono_type - MonoType to be logged +// log_behavior - Describe how type should be logged. +// +// This follows CoreCLR's BulkTypeEventLogger::LogTypeAndParameter + +static +void +bulk_type_log_type_and_parameters ( + BulkTypeEventLogger *type_logger, + MonoType *mono_type, + TypeLogBehavior log_behavior) +{ + // Batch up this type. This grabs useful info about the type, including any + // type parameters it may have, and sticks it in bulk_type_values + int bulk_type_value_index = bulk_type_log_single_type (type_logger, mono_type); + if (bulk_type_value_index == -1) { + // There was a failure trying to log the type, so don't bother with its type + // parameters + return; + } + + // Look at the type info we just batched, so we can get the type parameters + BulkTypeValue *val = &type_logger->bulk_type_values [bulk_type_value_index]; + + // We're about to recursively call ourselves for the type parameters, so make a + // local copy of their type handles first (else, as we log them we could flush + // and clear out bulk_type_values, thus trashing val) + uint32_t param_count = val->type_parameters_count; + if (param_count == 0) + return; + + MonoType **mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, param_count * sizeof (MonoType*)); + memcpy (mono_type_parameters, val->mono_type_parameters, sizeof (MonoType*) * param_count); + + if (log_behavior == TYPE_LOG_BEHAVIOR_ALWAYS_LOG_TOP_LEVEL) + log_behavior = TYPE_LOG_BEHAVIOR_IF_FIRST_TIME; + + for (uint32_t i = 0; i < param_count; i++) + bulk_type_log_type_and_parameters_if_necessary (type_logger, mono_type_parameters [i], log_behavior); +} + +//--------------------------------------------------------------------------------------- +// +// Outermost level of ETW-type-logging. This method is used to log a unique type identifier +// (in this case a MonoType) and (recursively) its type parameters when present. +// +// Arguments: +// * type_logger - BulkTypeEventLogger instance +// * mono_type - MonoType to be logged +// log_behavior - Describe how type should be logged. +// +// This follows CoreCLR's BulkTypeEventLogger::LogTypeAndParameters + +static +void +bulk_type_log_type_and_parameters_if_necessary ( + BulkTypeEventLogger *type_logger, + MonoType *mono_type, + TypeLogBehavior log_behavior) +{ + if (!is_keyword_and_level_enabled (&RUNTIME_PROVIDER_CONTEXT, EP_EVENT_LEVEL_INFORMATIONAL, TYPE_KEYWORD)) + return; + + bool log_type = (log_behavior == TYPE_LOG_BEHAVIOR_ALWAYS_LOG || log_behavior == TYPE_LOG_BEHAVIOR_ALWAYS_LOG_TOP_LEVEL); + + if (!log_type && type_logger) { + uint64_t type_id = get_typeid_for_type (mono_type); + dn_umap_result_t result = dn_umap_insert (type_logger->type_cache, GUINT64_TO_POINTER (type_id), NULL); + log_type = result.result; + } + + if (!log_type) + return; + + if (type_logger) + bulk_type_log_type_and_parameters (type_logger, mono_type, log_behavior); +} + +//--------------------------------------------------------------------------------------- +// +// write_method_details_event is the method responsible for sending details of +// methods involved in events such as JitStart, Load/Unload, Rundown, R2R, and other +// eventpipe events. It calls ep_rt_mono_log_type_and_parameters_if_necessary to log +// unique types from the method type and available method instantiation parameter types +// that are ultimately emitted as a BulkType event in ep_rt_mono_fire_bulk_type_event. +// After appropriately logging type information, it sends method details outlined by +// the generated dotnetruntime.c and ClrEtwAll manifest. +// +// Arguments: +// * method - a MonoMethod hit during an eventpipe event +// +// This follows CoreCLR's ETW::MethodLog::SendMethodDetailsEvent + +static +void +write_event_method_details (MonoMethod *method) +{ + if (method->wrapper_type != MONO_WRAPPER_NONE || method->dynamic) + return; + + MonoGenericContext *method_ctx = mono_method_get_context (method); + + MonoGenericInst *method_inst = NULL; + if (method_ctx) + method_inst = method_ctx->method_inst; + + if (method_inst && method_inst->type_argc > MAX_METHOD_TYPE_ARGUMENT_COUNT) + return; + + BulkTypeEventLogger *type_logger = bulk_type_event_logger_alloc (); + + uint64_t method_type_id = 0; + g_assert (mono_metadata_token_index (method->token) != 0); + uint32_t method_token = mono_metadata_make_token (MONO_TABLE_METHOD, mono_metadata_token_index (method->token)); + uint64_t loader_module_id = 0; + MonoClass *klass = method->klass; + if (klass) { + MonoType *method_mono_type = m_class_get_byval_arg (klass); + method_type_id = get_typeid_for_class (klass); + + bulk_type_log_type_and_parameters_if_necessary (type_logger, method_mono_type, TYPE_LOG_BEHAVIOR_ALWAYS_LOG); + + loader_module_id = (uint64_t)mono_class_get_image (klass); + } + + uint32_t method_inst_parameter_types_count = 0; + if (method_inst) + method_inst_parameter_types_count = method_inst->type_argc; + + uint64_t *method_inst_parameters_type_ids = mono_mempool_alloc0 (type_logger->mem_pool, method_inst_parameter_types_count * sizeof (uint64_t)); + uint8_t *buffer = (uint8_t *)method_inst_parameters_type_ids; + for (uint32_t i = 0; i < method_inst_parameter_types_count; i++) { + ep_write_buffer_uint64_t (&buffer, get_typeid_for_type (method_inst->type_argv [i])); + + bulk_type_log_type_and_parameters_if_necessary (type_logger, method_inst->type_argv [i], TYPE_LOG_BEHAVIOR_ALWAYS_LOG); + } + + bulk_type_fire_bulk_type_event (type_logger); + + FireEtwMethodDetails ( + (uint64_t)method, + method_type_id, + method_token, + method_inst_parameter_types_count, + loader_module_id, + (uint64_t*)method_inst_parameters_type_ids, + NULL, + NULL); + + bulk_type_event_logger_free (type_logger); +} + +static +bool +write_event_jit_start (MonoMethod *method) +{ + if (!EventEnabledMethodJittingStarted_V1 ()) + return true; + + //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. + if (method) { + uint64_t method_id = 0; + uint64_t module_id = 0; + uint32_t code_size = 0; + uint32_t method_token = 0; + char *method_namespace = NULL; + const char *method_name = NULL; + char *method_signature = NULL; + + write_event_method_details (method); + + method_id = (uint64_t)method; + + if (!method->dynamic) + method_token = method->token; + + if (!mono_method_has_no_body (method)) { + ERROR_DECL (error); + MonoMethodHeader *header = mono_method_get_header_internal (method, error); + if (header) + code_size = header->code_size; + } + + method_name = method->name; + method_signature = mono_signature_full_name (mono_method_signature_internal (method)); + + if (method->klass) { + module_id = (uint64_t)m_class_get_image (method->klass); + method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); + } + + FireEtwMethodJittingStarted_V1 ( + method_id, + module_id, + method_token, + code_size, + method_namespace, + method_name, + method_signature, + clr_instance_get_id (), + NULL, + NULL); + + g_free (method_namespace); + g_free (method_signature); + } + + return true; +} + +static +bool +write_event_method_il_to_native_map ( + MonoMethod *method, + MonoJitInfo *ji) +{ + if (!EventEnabledMethodILToNativeMap ()) + return true; + + if (method) { + // Under netcore we only have root domain. + MonoDomain *root_domain = mono_get_root_domain (); + + uint64_t method_id = (uint64_t)method; + uint32_t fixed_buffer [64]; + uint8_t *buffer = NULL; + + uint32_t offset_entries = 0; + uint32_t *il_offsets = NULL; + uint32_t *native_offsets = NULL; + + MonoDebugMethodJitInfo *debug_info = method ? mono_debug_find_method (method, root_domain) : NULL; + if (debug_info) { + offset_entries = debug_info->num_line_numbers; + if (offset_entries != 0) { + size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); + if (needed_size > sizeof (fixed_buffer)) { + buffer = g_new (uint8_t, needed_size); + il_offsets = (uint32_t*)buffer; + } else { + il_offsets = fixed_buffer; + } + if (il_offsets) { + native_offsets = il_offsets + offset_entries; + uint8_t *il_offsets_ptr = (uint8_t *)il_offsets; + uint8_t *native_offsets_ptr = (uint8_t *)native_offsets; + for (uint32_t offset_count = 0; offset_count < offset_entries; ++offset_count) { + ep_write_buffer_uint32_t (&il_offsets_ptr, debug_info->line_numbers [offset_count].il_offset); + ep_write_buffer_uint32_t (&native_offsets_ptr, debug_info->line_numbers [offset_count].native_offset); + } + } + } + + mono_debug_free_method_jit_info (debug_info); + } + + if (!il_offsets && !native_offsets) { + // No IL offset -> Native offset mapping available. Put all code on IL offset 0. + EP_ASSERT (sizeof (fixed_buffer) >= sizeof (uint32_t) * 2); + offset_entries = 1; + il_offsets = fixed_buffer; + native_offsets = il_offsets + offset_entries; + il_offsets [0] = 0; + native_offsets [0] = ji ? ep_rt_val_uint32_t ((uint32_t)ji->code_size) : 0; + } + + FireEtwMethodILToNativeMap ( + method_id, + 0, + 0, + GUINT32_TO_UINT16 (offset_entries), + il_offsets, + native_offsets, + clr_instance_get_id (), + NULL, + NULL); + + g_free (buffer); + } + + return true; +} + +static +bool +write_event_method_load ( + MonoMethod *method, + MonoJitInfo *ji) +{ + if (!EventEnabledMethodLoad_V1 () && !EventEnabledMethodLoadVerbose_V1 ()) + return true; + + //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. + if (method) { + uint64_t method_id = 0; + uint64_t module_id = 0; + uint64_t method_code_start = ji ? (uint64_t)ji->code_start : 0; + uint32_t method_code_size = ji ? (uint32_t)ji->code_size : 0; + uint32_t method_token = 0; + uint32_t method_flags = 0; + uint8_t kind = MONO_CLASS_DEF; + char *method_namespace = NULL; + const char *method_name = NULL; + char *method_signature = NULL; + bool verbose = (RUNTIME_PROVIDER_CONTEXT.Level >= (uint8_t)EP_EVENT_LEVEL_VERBOSE); + + method_id = (uint64_t)method; + + if (!method->dynamic) + method_token = method->token; + + if (ji && mono_jit_info_get_generic_sharing_context (ji)) { + method_flags |= METHOD_FLAGS_SHARED_GENERIC_METHOD; + verbose = true; + } + + if (method->dynamic) { + method_flags |= METHOD_FLAGS_DYNAMIC_METHOD; + verbose = true; + } + + if (ji && !ji->from_aot && !ji->from_llvm) { + method_flags |= METHOD_FLAGS_JITTED_METHOD; + if (method->wrapper_type != MONO_WRAPPER_NONE) + method_flags |= METHOD_FLAGS_JITTED_HELPER_METHOD; + } + + if (method->is_generic || method->is_inflated) { + method_flags |= METHOD_FLAGS_GENERIC_METHOD; + verbose = true; + } + + if (method->klass) { + module_id = (uint64_t)m_class_get_image (method->klass); + kind = m_class_get_class_kind (method->klass); + if (kind == MONO_CLASS_GTD || kind == MONO_CLASS_GINST) + method_flags |= METHOD_FLAGS_GENERIC_METHOD; + } + + write_event_method_details (method); + + if (verbose) { + method_name = method->name; + method_signature = mono_signature_full_name (mono_method_signature_internal (method)); + + if (method->klass) + method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); + + FireEtwMethodLoadVerbose_V1 ( + method_id, + module_id, + method_code_start, + method_code_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, + method_namespace, + method_name, + method_signature, + clr_instance_get_id (), + NULL, + NULL); + + if (ji && (ji->from_aot || ji->from_llvm)) + FireEtwMethodLoadVerbose_V1 ( + method_id, + module_id, + method_code_start, + method_code_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, + method_namespace, + method_name, + method_signature, + clr_instance_get_id (), + NULL, + NULL); + } else { + FireEtwMethodLoad_V1 ( + method_id, + module_id, + method_code_start, + method_code_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, + clr_instance_get_id (), + NULL, + NULL); + + if (ji && (ji->from_aot || ji->from_llvm)) + FireEtwMethodLoad_V1 ( + method_id, + module_id, + method_code_start, + method_code_size, + method_token, + method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, + clr_instance_get_id (), + NULL, + NULL); + } + + g_free (method_namespace); + g_free (method_signature); + } + + return true; +} + +static +bool +write_event_module_load (MonoImage *image) +{ + if (!EventEnabledModuleLoad_V2 () && !EventEnabledDomainModuleLoad_V1 ()) + return true; + + if (image) { + ModuleEventData module_data; + memset (&module_data, 0, sizeof (module_data)); + if (get_module_event_data (image, &module_data)) { + FireEtwModuleLoad_V2 ( + module_data.module_id, + module_data.assembly_id, + module_data.module_flags, + module_data.reserved_flags, + module_data.module_il_path, + module_data.module_native_path, + clr_instance_get_id (), + module_data.module_il_pdb_signature, + module_data.module_il_pdb_age, + module_data.module_il_pdb_path, + module_data.module_native_pdb_signature, + module_data.module_native_pdb_age, + module_data.module_native_pdb_path, + NULL, + NULL); + + FireEtwDomainModuleLoad_V1 ( + module_data.module_id, + module_data.assembly_id, + module_data.domain_id, + module_data.module_flags, + module_data.reserved_flags, + module_data.module_il_path, + module_data.module_native_path, + clr_instance_get_id (), + NULL, + NULL); + } + } + + return true; +} + +static +bool +write_event_module_unload (MonoImage *image) +{ + if (!EventEnabledModuleUnload_V2()) + return true; + + if (image) { + ModuleEventData module_data; + memset (&module_data, 0, sizeof (module_data)); + if (get_module_event_data (image, &module_data)) { + FireEtwModuleUnload_V2 ( + module_data.module_id, + module_data.assembly_id, + module_data.module_flags, + module_data.reserved_flags, + module_data.module_il_path, + module_data.module_native_path, + clr_instance_get_id (), + module_data.module_il_pdb_signature, + module_data.module_il_pdb_age, + module_data.module_il_pdb_path, + module_data.module_native_pdb_signature, + module_data.module_native_pdb_age, + module_data.module_native_pdb_path, + NULL, + NULL); + } + } + + return true; +} + +static +bool +get_assembly_event_data ( + MonoAssembly *assembly, + AssemblyEventData *assembly_data) +{ + if (assembly && assembly_data) { + // Under netcore we only have root domain. + MonoDomain *root_domain = mono_get_root_domain (); + + assembly_data->domain_id = (uint64_t)root_domain; + assembly_data->assembly_id = (uint64_t)assembly; + assembly_data->binding_id = 0; + + assembly_data->assembly_flags = 0; + if (assembly->dynamic) + assembly_data->assembly_flags |= ASSEMBLY_FLAGS_DYNAMIC_ASSEMBLY; + + if (assembly->image && assembly->image->aot_module) + assembly_data->assembly_flags |= ASSEMBLY_FLAGS_NATIVE_ASSEMBLY; + + assembly_data->assembly_name = mono_stringify_assembly_name (&assembly->aname); + } + + return true; +} + +static +bool +write_event_assembly_load (MonoAssembly *assembly) +{ + if (!EventEnabledAssemblyLoad_V1 ()) + return true; + + if (assembly) { + AssemblyEventData assembly_data; + memset (&assembly_data, 0, sizeof (assembly_data)); + if (get_assembly_event_data (assembly, &assembly_data)) { + FireEtwAssemblyLoad_V1 ( + assembly_data.assembly_id, + assembly_data.domain_id, + assembly_data.binding_id, + assembly_data.assembly_flags, + assembly_data.assembly_name, + clr_instance_get_id (), + NULL, + NULL); + + g_free (assembly_data.assembly_name); + } + } + + return true; +} + +static +bool +write_event_assembly_unload (MonoAssembly *assembly) +{ + if (!EventEnabledAssemblyUnload_V1 ()) + return true; + + if (assembly) { + AssemblyEventData assembly_data; + memset (&assembly_data, 0, sizeof (assembly_data)); + if (get_assembly_event_data (assembly, &assembly_data)) { + FireEtwAssemblyUnload_V1 ( + assembly_data.assembly_id, + assembly_data.domain_id, + assembly_data.binding_id, + assembly_data.assembly_flags, + assembly_data.assembly_name, + clr_instance_get_id (), + NULL, + NULL); + + g_free (assembly_data.assembly_name); + } + } + + return true; +} + +static +bool +write_event_thread_created (ep_rt_thread_id_t tid) +{ + if (!EventEnabledThreadCreated ()) + return true; + + uint64_t managed_thread = 0; + uint32_t native_thread_id = MONO_NATIVE_THREAD_ID_TO_UINT (tid); + uint32_t managed_thread_id = 0; + uint32_t flags = 0; + + MonoThread *thread = mono_thread_current (); + if (thread && mono_thread_info_get_tid (thread->thread_info) == tid) { + managed_thread_id = mono_thread_get_managed_id (thread); + managed_thread = (uint64_t)thread; + + switch (mono_thread_info_get_flags (thread->thread_info)) { + case MONO_THREAD_INFO_FLAGS_NO_GC: + case MONO_THREAD_INFO_FLAGS_NO_SAMPLE: + flags |= THREAD_FLAGS_GC_SPECIAL; + } + + if (mono_gc_is_finalizer_thread (thread)) + flags |= THREAD_FLAGS_FINALIZER; + + if (thread->threadpool_thread) + flags |= THREAD_FLAGS_THREADPOOL_WORKER; + } + + FireEtwThreadCreated ( + managed_thread, + (uint64_t)mono_get_root_domain (), + flags, + managed_thread_id, + native_thread_id, + clr_instance_get_id (), + NULL, + NULL); + + return true; +} + +static +bool +write_event_thread_terminated (ep_rt_thread_id_t tid) +{ + if (!EventEnabledThreadTerminated ()) + return true; + + uint64_t managed_thread = 0; + MonoThread *thread = mono_thread_current (); + if (thread && mono_thread_info_get_tid (thread->thread_info) == tid) + managed_thread = (uint64_t)thread; + + FireEtwThreadTerminated ( + managed_thread, + (uint64_t)mono_get_root_domain (), + clr_instance_get_id (), + NULL, + NULL); + + return true; +} + +static +uint32_t +get_type_start_id (MonoType *type) +{ + uint32_t start_id = (uint32_t)(uintptr_t)type; + + start_id = (((start_id * 215497) >> 16) ^ ((start_id * 1823231) + start_id)); + +MONO_DISABLE_WARNING(4127) /* conditional expression is constant */ + // Mix in highest bits on 64-bit systems only + if (sizeof (type) > 4) + start_id = start_id ^ GUINT64_TO_UINT32 ((((uint64_t)type >> 31) >> 1)); +MONO_RESTORE_WARNING + + return start_id; +} + +static +bool +write_event_type_load_start (MonoType *type) +{ + if (!EventEnabledTypeLoadStart ()) + return true; + + FireEtwTypeLoadStart ( + get_type_start_id (type), + clr_instance_get_id (), + NULL, + NULL); + + return true; +} + +static +bool +write_event_type_load_stop (MonoType *type) +{ + if (!EventEnabledTypeLoadStop ()) + return true; + + char *type_name = NULL; + if (type) + type_name = mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_IL); + + FireEtwTypeLoadStop ( + get_type_start_id (type), + clr_instance_get_id (), + 6 /* CLASS_LOADED */, + (uint64_t)type, + type_name, + NULL, + NULL); + + g_free (type_name); + + return true; +} + +static +gboolean +get_exception_ip_func ( + MonoStackFrameInfo *frame, + MonoContext *ctx, + void *data) +{ + *(uintptr_t *)data = (uintptr_t)MONO_CONTEXT_GET_IP (ctx); + return TRUE; +} + +static +bool +write_event_exception_thrown (MonoObject *obj) +{ + if (!EventEnabledExceptionThrown_V1 ()) + return true; + + if (obj) { + ERROR_DECL (error); + char *type_name = NULL; + char *exception_message = NULL; + uint16_t flags = 0; + uint32_t hresult = 0; + uintptr_t ip = 0; + + if (mono_object_isinst_checked ((MonoObject *) obj, mono_get_exception_class (), error)) { + MonoException *exception = (MonoException *)obj; + flags |= EXCEPTION_THROWN_FLAGS_IS_CLS_COMPLIANT; + if (exception->inner_ex) + flags |= EXCEPTION_THROWN_FLAGS_HAS_INNER; + if (exception->message) + exception_message = ep_rt_utf16_to_utf8_string (mono_string_chars_internal (exception->message), mono_string_length_internal (exception->message)); + hresult = exception->hresult; + } + + if (exception_message == NULL) + exception_message = g_strdup (""); + + if (mono_get_eh_callbacks ()->mono_walk_stack_with_ctx) + mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (get_exception_ip_func, NULL, MONO_UNWIND_SIGNAL_SAFE, (void *)&ip); + + type_name = mono_type_get_name_full (m_class_get_byval_arg (mono_object_class (obj)), MONO_TYPE_NAME_FORMAT_IL); + + FireEtwExceptionThrown_V1 ( + type_name, + exception_message, + (void *)&ip, + hresult, + flags, + clr_instance_get_id (), + NULL, + NULL); + + if (!mono_component_profiler_clauses_enabled ()) { + FireEtwExceptionThrownStop ( + NULL, + NULL); + } + + g_free (exception_message); + g_free (type_name); + + mono_error_cleanup (error); + } + + return true; +} + +static +bool +write_event_exception_clause ( + MonoMethod *method, + uint32_t clause_num, + MonoExceptionEnum clause_type, + MonoObject *obj) +{ + if (!mono_component_profiler_clauses_enabled ()) + return true; + + if ((clause_type == MONO_EXCEPTION_CLAUSE_FAULT || clause_type == MONO_EXCEPTION_CLAUSE_NONE) && (!EventEnabledExceptionCatchStart() || !EventEnabledExceptionCatchStop())) + return true; + + if (clause_type == MONO_EXCEPTION_CLAUSE_FILTER && (!EventEnabledExceptionFilterStart() || !EventEnabledExceptionFilterStop())) + return true; + + if (clause_type == MONO_EXCEPTION_CLAUSE_FINALLY && (!EventEnabledExceptionFinallyStart() || !EventEnabledExceptionFinallyStop())) + return true; + + uintptr_t ip = 0; //TODO: Have profiler pass along IP of handler block. + uint64_t method_id = (uint64_t)method; + char *method_name = NULL; + + method_name = mono_method_get_name_full (method, TRUE, TRUE, MONO_TYPE_NAME_FORMAT_IL); + + if ((clause_type == MONO_EXCEPTION_CLAUSE_FAULT || clause_type == MONO_EXCEPTION_CLAUSE_NONE)) { + FireEtwExceptionCatchStart ( + (uint64_t)ip, + method_id, + (const ep_char8_t *)method_name, + clr_instance_get_id (), + NULL, + NULL); + + FireEtwExceptionCatchStop ( + NULL, + NULL); + + FireEtwExceptionThrownStop ( + NULL, + NULL); + } + + if (clause_type == MONO_EXCEPTION_CLAUSE_FILTER) { + FireEtwExceptionFilterStart ( + (uint64_t)ip, + method_id, + (const ep_char8_t *)method_name, + clr_instance_get_id (), + NULL, + NULL); + + FireEtwExceptionFilterStop ( + NULL, + NULL); + } + + if (clause_type == MONO_EXCEPTION_CLAUSE_FINALLY) { + FireEtwExceptionFinallyStart ( + (uint64_t)ip, + method_id, + (const ep_char8_t *)method_name, + clr_instance_get_id (), + NULL, + NULL); + + FireEtwExceptionFinallyStop ( + NULL, + NULL); + } + + g_free (method_name); + return true; +} + +static +bool +write_event_monitor_contention_start (MonoObject *obj) +{ + if (!EventEnabledContentionStart_V1 ()) + return true; + + FireEtwContentionStart_V1 ( + 0 /* ManagedContention */, + clr_instance_get_id (), + NULL, + NULL); + + return true; +} + +static +bool +write_event_monitor_contention_stop (MonoObject *obj) +{ + if (!EventEnabledContentionStop ()) + return true; + + FireEtwContentionStop ( + 0 /* ManagedContention */, + clr_instance_get_id (), + NULL, + NULL); + + return true; +} + +static +bool +write_event_method_jit_memory_allocated_for_code ( + const uint8_t *buffer, + uint64_t size, + MonoProfilerCodeBufferType type, + const void *data) +{ + if (!EventEnabledMethodJitMemoryAllocatedForCode ()) + return true; + + if (type != MONO_PROFILER_CODE_BUFFER_METHOD) + return true; + + uint64_t method_id = 0; + uint64_t module_id = 0; + + if (data) { + MonoMethod *method; + method = (MonoMethod *)data; + method_id = (uint64_t)method; + if (method->klass) + module_id = (uint64_t)(uint64_t)m_class_get_image (method->klass); + } + + FireEtwMethodJitMemoryAllocatedForCode ( + method_id, + module_id, + size, + 0, + size, + 0 /* CORJIT_ALLOCMEM_DEFAULT_CODE_ALIGN */, + clr_instance_get_id (), + NULL, + NULL); + + return true; +} + +bool +ep_rt_write_event_ee_startup_start (void) +{ + return FireEtwEEStartupStart_V1 ( + clr_instance_get_id (), + NULL, + NULL); +} + +bool +ep_rt_write_event_threadpool_worker_thread_start ( + uint32_t active_thread_count, + uint32_t retired_worker_thread_count, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolWorkerThreadStart ( + active_thread_count, + retired_worker_thread_count, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_threadpool_worker_thread_stop ( + uint32_t active_thread_count, + uint32_t retired_worker_thread_count, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolWorkerThreadStop ( + active_thread_count, + retired_worker_thread_count, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_threadpool_worker_thread_wait ( + uint32_t active_thread_count, + uint32_t retired_worker_thread_count, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolWorkerThreadWait ( + active_thread_count, + retired_worker_thread_count, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_threadpool_min_max_threads ( + uint16_t min_worker_threads, + uint16_t max_worker_threads, + uint16_t min_io_completion_threads, + uint16_t max_io_completion_threads, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolMinMaxThreads ( + min_worker_threads, + max_worker_threads, + min_io_completion_threads, + max_io_completion_threads, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_threadpool_worker_thread_adjustment_sample ( + double throughput, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolWorkerThreadAdjustmentSample ( + throughput, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_threadpool_worker_thread_adjustment_adjustment ( + double average_throughput, + uint32_t networker_thread_count, + /*NativeRuntimeEventSource.ThreadAdjustmentReasonMap*/ int32_t reason, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolWorkerThreadAdjustmentAdjustment ( + average_throughput, + networker_thread_count, + reason, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_threadpool_worker_thread_adjustment_stats ( + double duration, + double throughput, + double threadpool_worker_thread_wait, + double throughput_wave, + double throughput_error_estimate, + double average_throughput_error_estimate, + double throughput_ratio, + double confidence, + double new_control_setting, + uint16_t new_thread_wave_magnitude, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolWorkerThreadAdjustmentStats ( + duration, + throughput, + threadpool_worker_thread_wait, + throughput_wave, + throughput_error_estimate, + average_throughput_error_estimate, + throughput_ratio, + confidence, + new_control_setting, + new_thread_wave_magnitude, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_threadpool_io_enqueue ( + intptr_t native_overlapped, + intptr_t overlapped, + bool multi_dequeues, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolIOEnqueue ( + (const void *)native_overlapped, + (const void *)overlapped, + multi_dequeues, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_threadpool_io_dequeue ( + intptr_t native_overlapped, + intptr_t overlapped, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolIODequeue ( + (const void *)native_overlapped, + (const void *)overlapped, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_threadpool_working_thread_count ( + uint16_t count, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolWorkingThreadCount ( + count, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_threadpool_io_pack ( + intptr_t native_overlapped, + intptr_t overlapped, + uint16_t clr_instance_id) +{ + return FireEtwThreadPoolIOPack ( + (const void *)native_overlapped, + (const void *)overlapped, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_contention_lock_created ( + intptr_t lock_id, + intptr_t associated_object_id, + uint16_t clr_instance_id) +{ + return FireEtwContentionLockCreated ( + (const void *)lock_id, + (const void *)associated_object_id, + clr_instance_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_contention_start ( + uint8_t contention_flags, + uint16_t clr_instance_id, + intptr_t lock_id, + intptr_t associated_object_id, + uint64_t lock_owner_thread_id) +{ + return FireEtwContentionStart_V2 ( + contention_flags, + clr_instance_id, + (const void *)lock_id, + (const void *)associated_object_id, + lock_owner_thread_id, + NULL, + NULL) == 0 ? true : false; +} + +bool +ep_rt_write_event_contention_stop ( + uint8_t contention_flags, + uint16_t clr_instance_id, + double duration_ns) +{ + return FireEtwContentionStop_V1 ( + contention_flags, + clr_instance_id, + duration_ns, + NULL, + NULL) == 0 ? true : false; +} + +static +void +jit_begin_callback ( + MonoProfiler *prof, + MonoMethod *method) +{ + write_event_jit_start (method); +} + +static +void +jit_failed_callback ( + MonoProfiler *prof, + MonoMethod *method) +{ + //TODO: CoreCLR doesn't have this case, so no failure event currently exists. +} + +static +void +jit_done_callback ( + MonoProfiler *prof, + MonoMethod *method, + MonoJitInfo *ji) +{ + write_event_method_load (method, ji); + write_event_method_il_to_native_map (method, ji); +} + +static +void +image_loaded_callback ( + MonoProfiler *prof, + MonoImage *image) +{ + if (image && image->heap_pdb.size == 0) + write_event_module_load (image); +} + +static +void +image_unloaded_callback ( + MonoProfiler *prof, + MonoImage *image) +{ + if (image && image->heap_pdb.size == 0) + write_event_module_unload (image); +} + +static +void +assembly_loaded_callback ( + MonoProfiler *prof, + MonoAssembly *assembly) +{ + write_event_assembly_load (assembly); +} + +static +void +assembly_unloaded_callback ( + MonoProfiler *prof, + MonoAssembly *assembly) +{ + write_event_assembly_unload (assembly); +} + +static +void +thread_started_callback ( + MonoProfiler *prof, + uintptr_t tid) +{ + write_event_thread_created (ep_rt_uint64_t_to_thread_id_t (tid)); +} + +static +void +thread_stopped_callback ( + MonoProfiler *prof, + uintptr_t tid) +{ + write_event_thread_terminated (ep_rt_uint64_t_to_thread_id_t (tid)); +} + +static +void +class_loading_callback ( + MonoProfiler *prof, + MonoClass *klass) +{ + bool prevent_profiler_event_recursion = FALSE; + EventPipeMonoThreadData *thread_data = ep_rt_mono_thread_data_get_or_create (); + if (thread_data) { + // Prevent additional class loading to happen recursively as part of fire TypeLoadStart event. + // Additional class loading can happen as part of capturing callstack for TypeLoadStart event. + prevent_profiler_event_recursion = thread_data->prevent_profiler_event_recursion; + thread_data->prevent_profiler_event_recursion = TRUE; + } + + write_event_type_load_start (m_class_get_byval_arg (klass)); + + if (thread_data) + thread_data->prevent_profiler_event_recursion = prevent_profiler_event_recursion; +} + +static +void +class_failed_callback ( + MonoProfiler *prof, + MonoClass *klass) +{ + write_event_type_load_stop (m_class_get_byval_arg (klass)); +} + +static +void +class_loaded_callback ( + MonoProfiler *prof, + MonoClass *klass) +{ + write_event_type_load_stop (m_class_get_byval_arg (klass)); +} + +static +void +exception_throw_callback ( + MonoProfiler *prof, + MonoObject *exc) +{ + write_event_exception_thrown (exc); +} + +static +void +exception_clause_callback ( + MonoProfiler *prof, + MonoMethod *method, + uint32_t clause_num, + MonoExceptionEnum clause_type, + MonoObject *exc) +{ + write_event_exception_clause (method, clause_num, clause_type, exc); +} + +static +void +monitor_contention_callback ( + MonoProfiler *prof, + MonoObject *obj) +{ + write_event_monitor_contention_start (obj); +} + +static +void +monitor_acquired_callback ( + MonoProfiler *prof, + MonoObject *obj) +{ + write_event_monitor_contention_stop (obj); +} + +static +void +monitor_failed_callback ( + MonoProfiler *prof, + MonoObject *obj) +{ + write_event_monitor_contention_stop (obj); +} + +static +void +jit_code_buffer_callback ( + MonoProfiler *prof, + const mono_byte *buffer, + uint64_t size, + MonoProfilerCodeBufferType type, + const void *data) +{ + write_event_method_jit_memory_allocated_for_code ((const uint8_t *)buffer, size, type, data); +} + +static +uint32_t +gc_heap_dump_requests_inc (void) +{ + EP_ASSERT (ep_rt_mono_is_runtime_initialized ()); + return ep_rt_atomic_inc_uint32_t (&_gc_heap_dump_requests); +} + +static +uint32_t +gc_heap_dump_requests_dec (void) +{ + EP_ASSERT (ep_rt_mono_is_runtime_initialized ()); + return ep_rt_atomic_dec_uint32_t (&_gc_heap_dump_requests); +} + +static +bool +gc_heap_dump_requested (void) +{ + if (!ep_rt_mono_is_runtime_initialized ()) + return false; + return ep_rt_volatile_load_uint32_t(&_gc_heap_dump_requests) != 0 ? true : false; +} + +static +uint32_t +gc_heap_dump_count_inc (void) +{ + EP_ASSERT (ep_rt_mono_is_runtime_initialized ()); + return ep_rt_atomic_inc_uint32_t (&_gc_heap_dump_count); +} + +static +bool +gc_heap_dump_mem_file_buffer_init (GCHeapDumpMemFileBuffer *file_buffer) +{ + // Called on GC thread so no need to call MONO_ENTER_GC_SAFE/MONO_EXIT_GC_SAFE + // around IO functions. + + EP_ASSERT (file_buffer); + + file_buffer->fd = g_file_open_tmp ("mono_gc_heap_dump_XXXXXX", &file_buffer->name, NULL); + file_buffer->start = g_malloc (MAX_EVENT_BYTE_COUNT); + file_buffer->current = file_buffer->start; + file_buffer->end = file_buffer->start + MAX_EVENT_BYTE_COUNT; + + return file_buffer->fd != -1 && file_buffer->start; +} + +#ifndef g_close +#ifdef G_OS_WIN32 +#define g_close _close +#else +#define g_close close +#endif +#endif + +static +void +gc_heap_dump_mem_file_buffer_fini (GCHeapDumpMemFileBuffer *file_buffer) +{ + // Called on GC thread so no need to call MONO_ENTER_GC_SAFE/MONO_EXIT_GC_SAFE + // around IO functions. + + if (!file_buffer) + return; + + if (file_buffer->fd != -1) { + g_close (file_buffer->fd); + if (file_buffer->name) { + g_unlink (file_buffer->name); + g_free (file_buffer->name); + } + g_free (file_buffer->start); + } +} + +static +bool +gc_heap_dump_mem_file_buffer_write_all ( + int fd, + const uint8_t *buffer, + size_t len) +{ + // Called on GC thread (during GC) while holding GC locks, + // before/during/after STW so no need to MONO_ENTER_GC_SAFE/MONO_EXIT_GC_SAFE + // around async safe IO functions. + + size_t offset = 0; + int nwritten; + + do { + nwritten = g_write (fd, buffer + offset, GSIZE_TO_UINT32 (len - offset)); + if (nwritten > 0) + offset += nwritten; + } while ((nwritten > 0 && offset < len) || (nwritten == -1 && errno == EINTR)); + + return nwritten == len; +} + +static +bool +gc_heap_dump_mem_file_buffer_flush (GCHeapDumpMemFileBuffer *file_buffer) +{ + EP_ASSERT (file_buffer); + EP_ASSERT (file_buffer->fd != -1); + EP_ASSERT (file_buffer->start); + + bool result = true; + uint64_t size = (uint64_t)(file_buffer->current - file_buffer->start); + + result &= gc_heap_dump_mem_file_buffer_write_all (file_buffer->fd, (const uint8_t *)&size, sizeof (size)); + result &= gc_heap_dump_mem_file_buffer_write_all (file_buffer->fd, file_buffer->start, GUINT64_TO_SIZE (size)); + + file_buffer->current = file_buffer->start; + + return result; +} + +#ifndef g_lseek +#ifdef G_OS_WIN32 +#define g_lseek _lseek +#else +#define g_lseek lseek +#endif +#endif + +static +bool +gc_heap_dump_mem_file_buffer_reset_func (void *context) +{ + // Called on GC thread (during GC) while holding GC locks, + // but after STW so no need to MONO_ENTER_GC_SAFE/MONO_EXIT_GC_SAFE + // around async safe IO functions. + + EP_ASSERT (context); + + bool result = true; + GCHeapDumpMemFileBuffer *file_buffer = (GCHeapDumpMemFileBuffer *)context; + + EP_ASSERT (file_buffer->fd != -1); + + result &= gc_heap_dump_mem_file_buffer_flush (file_buffer); + result &= g_lseek (file_buffer->fd, 0, SEEK_SET) != -1; + + return result; +} + +static +uint8_t * +gc_heap_dump_mem_file_buffer_alloc_func ( + void *context, + size_t size) +{ + EP_ASSERT (context); + + GCHeapDumpMemFileBuffer *file_buffer = (GCHeapDumpMemFileBuffer *)context; + + if (size > MAX_EVENT_BYTE_COUNT) + return NULL; + + if (file_buffer->current + size >= file_buffer->end) + if (!gc_heap_dump_mem_file_buffer_flush (file_buffer)) + return NULL; + + uint8_t *result = file_buffer->current; + file_buffer->current = file_buffer->current + size; + return result; +} + +static +bool +gc_heap_dump_mem_file_buffer_read ( + int fd, + uint8_t *buffer, + size_t len) +{ + // Called on GC thread (during GC) while holding GC locks, + // but after STW so no need to MONO_ENTER_GC_SAFE/MONO_EXIT_GC_SAFE + // around async safe IO functions. + + size_t offset = 0; + int nread; + + do { + nread = g_read (fd, buffer + offset, GSIZE_TO_UINT32 (len - offset)); + if (nread > 0) + offset += nread; + } while ((nread > 0 && offset < len) || (nread == -1 && errno == EINTR)); + + return nread == len; +} + +static +const uint8_t * +gc_heap_dump_mem_file_buffer_get_next_buffer_func ( + void *context, + size_t *next_buffer_size) +{ + EP_ASSERT (context); + + GCHeapDumpMemFileBuffer *file_buffer = (GCHeapDumpMemFileBuffer *)context; + + bool result = false; + + uint64_t max_size = (uint64_t)(file_buffer->end - file_buffer->start); + uint64_t size = 0; + + EP_ASSERT (file_buffer->fd != -1); + + if (gc_heap_dump_mem_file_buffer_read (file_buffer->fd, (uint8_t *)&size, sizeof (size))) + if (size <= max_size) + result = gc_heap_dump_mem_file_buffer_read (file_buffer->fd, file_buffer->start, GUINT64_TO_SIZE (size)); + + file_buffer->current = file_buffer->start; + *next_buffer_size = GUINT64_TO_SIZE (size); + + return result ? file_buffer->start : NULL; +} + +static +GCHeapDumpBuffer * +gc_heap_dump_context_buffer_alloc (void) +{ + GCHeapDumpMemFileBuffer *file_buffer = g_new0 (GCHeapDumpMemFileBuffer, 1); + GCHeapDumpBuffer *buffer = g_new0 (GCHeapDumpBuffer, 1); + + buffer->context = file_buffer; + buffer->reset_func = gc_heap_dump_mem_file_buffer_reset_func; + buffer->alloc_func = gc_heap_dump_mem_file_buffer_alloc_func; + buffer->get_next_buffer_func = gc_heap_dump_mem_file_buffer_get_next_buffer_func; + + if (!gc_heap_dump_mem_file_buffer_init (file_buffer)) { + gc_heap_dump_mem_file_buffer_fini (file_buffer); + buffer = NULL; + } + + return buffer; +} + +static +void +gc_heap_dump_context_buffer_free (GCHeapDumpBuffer *buffer) +{ + if (!buffer) + return; + + gc_heap_dump_mem_file_buffer_fini ((GCHeapDumpMemFileBuffer *)buffer->context); + + g_free (buffer->context); + g_free (buffer); +} + +static +bool +gc_heap_dump_context_alloc_bulk_data ( + GCHeapDumpBulkData *data, + size_t len, + size_t count) +{ + data->data_start = g_malloc (len); + data->data_current = data->data_start; + data->data_end = data->data_start + len; + data->max_count = GSIZE_TO_UINT32 (count); + data->index = 0; + data->count = 0; + + return data->data_start; +} + +static +void +gc_heap_dump_context_free_bulk_data (GCHeapDumpBulkData *data) +{ + g_free (data->data_start); +} + +static +void +gc_heap_dump_context_clear_bulk_data (GCHeapDumpBulkData *data) +{ + if (data->data_start) + memset (data->data_start, 0, data->data_end - data->data_start); + data->data_current = data->data_start; + data->count = 0; +} + +static +bool +gc_heap_dump_context_init ( + GCHeapDumpContext *context, + EVENTPIPE_TRACE_CONTEXT trace_context, + uint32_t gc_reason, + uint32_t gc_type, + uint32_t gc_count, + uint32_t gc_depth) +{ + EP_ASSERT (context); + + bool result = true; + + context->trace_context = trace_context; + context->gc_reason = gc_reason; + context->gc_type = gc_type; + context->gc_count = gc_count; + context->gc_depth = gc_depth; + context->retry_count = 0; + context->state = GC_HEAP_DUMP_CONTEXT_STATE_INIT; + + if (is_gc_heap_dump_enabled (context)) { + context->bulk_type_logger = bulk_type_event_logger_alloc (); + + context->buffer = gc_heap_dump_context_buffer_alloc (); + + const size_t bulk_nodes_max_data_len = (MAX_EVENT_BYTE_COUNT - 0x100); + const size_t bulk_nodes_max_count = bulk_nodes_max_data_len / BULK_NODE_EVENT_TYPE_SIZE; + + const size_t bulk_edges_max_data_len = (MAX_EVENT_BYTE_COUNT - 0x100); + const size_t bulk_edges_max_count = bulk_edges_max_data_len / BULK_EDGE_EVENT_TYPE_SIZE; + + const size_t bulk_root_edges_max_data_len = (MAX_EVENT_BYTE_COUNT - 0x100); + const size_t bulk_root_edges_max_count = bulk_root_edges_max_data_len / BULK_ROOT_EDGE_EVENT_TYPE_SIZE; + + const size_t bulk_root_cwt_elem_edges_max_data_len = (MAX_EVENT_BYTE_COUNT - 0x100); + const size_t bulk_root_cwt_elem_edges_max_count = bulk_root_cwt_elem_edges_max_data_len / BULK_ROOT_CWT_ELEM_EDGE_EVENT_TYPE_SIZE; + + const size_t bulk_root_static_vars_max_data_len = (MAX_EVENT_BYTE_COUNT - 0x30); + const size_t bulk_root_static_vars_max_count = bulk_root_static_vars_max_data_len / BULK_ROOT_STATIC_VAR_EVENT_TYPE_SIZE; + + gc_heap_dump_context_alloc_bulk_data (&context->bulk_nodes, bulk_nodes_max_data_len, bulk_nodes_max_count); + gc_heap_dump_context_alloc_bulk_data (&context->bulk_edges, bulk_edges_max_data_len, bulk_edges_max_count); + gc_heap_dump_context_alloc_bulk_data (&context->bulk_root_edges, bulk_root_edges_max_data_len, bulk_root_edges_max_count); + gc_heap_dump_context_alloc_bulk_data (&context->bulk_root_cwt_elem_edges, bulk_root_cwt_elem_edges_max_data_len, bulk_root_cwt_elem_edges_max_count); + gc_heap_dump_context_alloc_bulk_data (&context->bulk_root_static_vars, bulk_root_static_vars_max_data_len, bulk_root_static_vars_max_count); + + result = context->bulk_type_logger && + context->buffer && + context->bulk_nodes.data_start && + context->bulk_edges.data_start && + context->bulk_root_edges.data_start && + context->bulk_root_cwt_elem_edges.data_start && + context->bulk_root_static_vars.data_start; + } + + return result; +} + +static +void +gc_heap_dump_context_fini (GCHeapDumpContext *context) +{ + if (context) { + if (is_gc_heap_dump_enabled (context)) { + if (context->gc_roots) + dn_vector_ptr_free (context->gc_roots); + + gc_heap_dump_context_free_bulk_data (&context->bulk_root_static_vars); + gc_heap_dump_context_free_bulk_data (&context->bulk_root_cwt_elem_edges); + gc_heap_dump_context_free_bulk_data (&context->bulk_root_edges); + gc_heap_dump_context_free_bulk_data (&context->bulk_edges); + gc_heap_dump_context_free_bulk_data (&context->bulk_nodes); + + gc_heap_dump_context_buffer_free (context->buffer); + + if (context->bulk_type_logger) + bulk_type_event_logger_free (context->bulk_type_logger); + } + + memset (context, 0, sizeof (GCHeapDumpContext)); + } +} + +static +GCHeapDumpContext * +gc_heap_dump_context_alloc ( + EVENTPIPE_TRACE_CONTEXT trace_context, + uint32_t gc_reason, + uint32_t gc_type, + uint32_t gc_count, + uint32_t gc_depth) +{ + GCHeapDumpContext *context = g_new0 (GCHeapDumpContext, 1); + gc_heap_dump_context_init ( + context, + trace_context, + gc_reason, + gc_type, + gc_count, + gc_depth); + + return context; +} + +static +void +gc_heap_dump_context_free (GCHeapDumpContext *context) +{ + gc_heap_dump_context_fini (context); + g_free (context); +} + +static +GCHeapDumpContext * +gc_heap_dump_context_get (void) +{ + EventPipeMonoThreadData *thread_data = ep_rt_mono_thread_data_get_or_create (); + return thread_data ? thread_data->gc_heap_dump_context : NULL; +} + +static +void +gc_heap_dump_context_set (GCHeapDumpContext *context) +{ + EventPipeMonoThreadData *thread_data = ep_rt_mono_thread_data_get_or_create (); + if (thread_data) { + if (thread_data->gc_heap_dump_context) + gc_heap_dump_context_free (thread_data->gc_heap_dump_context); + thread_data->gc_heap_dump_context = context; + } +} + +static +void +gc_heap_dump_context_reset (void) +{ + gc_heap_dump_context_set (NULL); +} + +static +int32_t +gc_roots_sort_compare_func ( + const void *a, + const void *b) +{ + EP_ASSERT (a && b); + + GCRootData *root_a = *(GCRootData **)a; + GCRootData *root_b = *(GCRootData **)b; + + EP_ASSERT (root_a && root_b); + if (root_a->start == root_b->start) + return 0; + return (root_a->start > root_b->start) ? 1 : -1; +} + +static +void +gc_heap_dump_context_build_roots (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + ep_rt_spin_lock_requires_lock_not_held (&_gc_lock); + + EP_SPIN_LOCK_ENTER (&_gc_lock, section1) + if (!context->gc_roots) { + dn_vector_ptr_custom_alloc_params_t params = { 0, }; + params.capacity = dn_umap_size (&_gc_roots_table); + context->gc_roots = dn_vector_ptr_custom_alloc (¶ms); + + } else { + dn_vector_ptr_clear (context->gc_roots); + } + + DN_UMAP_FOREACH_BEGIN (const mono_byte *, key, GCRootData *, value, &_gc_roots_table) { + DN_UNREFERENCED_PARAMETER (key); + dn_vector_ptr_push_back (context->gc_roots, value); + } DN_UMAP_FOREACH_END; + EP_SPIN_LOCK_EXIT (&_gc_lock, section1) + + dn_vector_ptr_sort (context->gc_roots, gc_roots_sort_compare_func); + +ep_on_exit: + ep_rt_spin_lock_requires_lock_not_held (&_gc_lock); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +static +GCRootData * +gc_heap_dump_context_find_root ( + GCHeapDumpContext *context, + uintptr_t root) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + const uint8_t *base = (const uint8_t *)dn_vector_ptr_data (context->gc_roots); + const uint8_t *p; + size_t lim; + int32_t cmp; + for (lim = dn_vector_ptr_size (context->gc_roots); lim; lim >>= 1) { + p = base + ((lim >> 1) * dn_vector_ptr_element_size); + + GCRootData *gc_root_data = *(GCRootData **)p; + EP_ASSERT (gc_root_data); + + if (gc_root_data->start <= root && gc_root_data->end > root) + cmp = 0; + else + cmp = (root > gc_root_data->start) ? 1 : -1; + + if (!cmp) + return *(GCRootData **)p; + else if (cmp > 0) { + base = p + dn_vector_ptr_element_size; + lim--; + } + } + + return NULL; +} + +static +void +gc_heap_dump_context_clear_nodes (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + gc_heap_dump_context_clear_bulk_data (&context->bulk_nodes); +} + +static +void +gc_heap_dump_context_clear_edges (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + gc_heap_dump_context_clear_bulk_data (&context->bulk_edges); +} + +static +void +gc_heap_dump_context_clear_root_edges (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + gc_heap_dump_context_clear_bulk_data (&context->bulk_root_edges); +} + +static +void +gc_heap_dump_context_clear_root_cwt_elem_edges (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + gc_heap_dump_context_clear_bulk_data (&context->bulk_root_cwt_elem_edges); +} + +static +void +gc_heap_dump_context_clear_root_static_vars (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + gc_heap_dump_context_clear_bulk_data (&context->bulk_root_static_vars); +} + +static +void +flush_gc_event_bulk_nodes (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + if (!context->bulk_nodes.count) + return; + + FireEtwGCBulkNode ( + context->bulk_nodes.index, + context->bulk_nodes.count, + clr_instance_get_id (), + BULK_NODE_EVENT_TYPE_SIZE, + context->bulk_nodes.data_start, + NULL, + NULL); + + context->bulk_nodes.index++; + gc_heap_dump_context_clear_nodes (context); +} + +static +void +fire_gc_event_bulk_node ( + GCHeapDumpContext *context, + uintptr_t address, + uint64_t size, + uintptr_t type, + uint64_t edge_count) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + bool update_previous_bulk_node = (size == 0 && context->bulk_nodes.count != 0); + + if (update_previous_bulk_node) + context->bulk_nodes.count--; + + if (context->bulk_nodes.count == context->bulk_nodes.max_count) + flush_gc_event_bulk_nodes (context); + + // Mono profiler object reference callback might split bulk node + // into several calls if it contains more edges than internal buffer size. + // Mono profiler pass an object size of 0 to identify this case. + if (update_previous_bulk_node) { + context->bulk_nodes.data_current -= sizeof (edge_count); + uint64_t previous_edge_count; + memcpy (&previous_edge_count, context->bulk_nodes.data_current, sizeof (edge_count)); + edge_count = GUINT64_FROM_LE (previous_edge_count) + edge_count; + ep_write_buffer_uint64_t (&context->bulk_nodes.data_current, edge_count); + } else { + uint64_t type_id = type; + MonoVTable *vtable = (MonoVTable *)type; + if (vtable && vtable->klass) { + MonoType *klass_type = m_class_get_byval_arg (vtable->klass); + bulk_type_log_type_and_parameters_if_necessary (context->bulk_type_logger, klass_type, TYPE_LOG_BEHAVIOR_IF_FIRST_TIME); + type_id = get_typeid_for_type (klass_type); + } + + // NOTE, must match manifest GCBulkNode values type. + ep_write_buffer_uintptr_t (&context->bulk_nodes.data_current, address); + ep_write_buffer_uint64_t (&context->bulk_nodes.data_current, size); + ep_write_buffer_uint64_t (&context->bulk_nodes.data_current, type_id); + ep_write_buffer_uint64_t (&context->bulk_nodes.data_current, edge_count); + } + + context->bulk_nodes.count++; +} + +static +void +flush_gc_event_bulk_edges (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + if (!context->bulk_edges.count) + return; + + FireEtwGCBulkEdge ( + context->bulk_edges.index, + context->bulk_edges.count, + clr_instance_get_id (), + BULK_EDGE_EVENT_TYPE_SIZE, + context->bulk_edges.data_start, + NULL, + NULL); + + context->bulk_edges.index++; + gc_heap_dump_context_clear_edges (context); +} + +static +void +fire_gc_event_bulk_edge ( + GCHeapDumpContext *context, + uintptr_t address, + uint32_t ref_field_id) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + if (context->bulk_edges.count == context->bulk_edges.max_count) + flush_gc_event_bulk_edges (context); + + // NOTE, must match manifest GCBulkEdge values type. + ep_write_buffer_uintptr_t (&context->bulk_edges.data_current, address); + ep_write_buffer_uint32_t (&context->bulk_edges.data_current, ref_field_id); + + context->bulk_edges.count++; +} + +static +void +fire_gc_event_object_reference ( + GCHeapDumpContext *context, + const uint8_t *data, + uint32_t payload_size) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + uintptr_t object_address; + uintptr_t object_type; + uint64_t object_size; + uint64_t edge_count; + + EP_ASSERT (data + sizeof (object_address) <= data + payload_size); + memcpy (&object_address, data, sizeof (object_address)); + data += sizeof (object_address); + + EP_ASSERT (data + sizeof (object_size) <= data + payload_size); + memcpy (&object_size, data, sizeof (object_size)); + data += sizeof (object_size); + + EP_ASSERT (data + sizeof (object_type) <= data + payload_size); + memcpy (&object_type, data, sizeof (object_type)); + data += sizeof (object_type); + + EP_ASSERT (data + sizeof (edge_count) <= data + payload_size); + memcpy (&edge_count, data, sizeof (edge_count)); + data += sizeof (edge_count); + + fire_gc_event_bulk_node ( + context, + object_address, + object_size, + object_type, + edge_count); + + EP_ASSERT (data + (edge_count * sizeof (object_address)) <= data + payload_size); + for (uint32_t i = 0; i < edge_count; i++) { + memcpy (&object_address, data, sizeof (object_address)); + data += sizeof (object_address); + + fire_gc_event_bulk_edge ( + context, + object_address, + 0); + } +} + +static +int +buffer_gc_event_object_reference_callback ( + MonoObject *obj, + MonoClass *klass, + uintptr_t size, + uintptr_t num, + MonoObject **refs, + uintptr_t *offsets, + void *data) +{ + if (!data) + return 1; + + GCHeapDumpContext *context = (GCHeapDumpContext *)data; + + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + uintptr_t object_address = (uintptr_t)SGEN_POINTER_UNTAG_ALL (obj); + uintptr_t object_type = (uintptr_t)SGEN_POINTER_UNTAG_ALL (mono_object_get_vtable_internal (obj)); + uint64_t object_size = (uint64_t)size; + uint64_t edge_count = (uint64_t)num; + + /* account for object alignment */ + object_size += 7; + object_size &= ~7; + + BufferedGCEvent gc_event_data; + gc_event_data.type = BUFFERED_GC_EVENT_OBJECT_REF; + gc_event_data.payload_size = + sizeof (object_address) + + sizeof (object_size) + + sizeof (object_type) + + sizeof (edge_count) + + (edge_count * sizeof (uintptr_t)); + + EP_ASSERT (context->buffer); + EP_ASSERT (context->buffer->context); + + uint8_t *buffer = context->buffer->alloc_func (context->buffer->context, sizeof (gc_event_data) + gc_event_data.payload_size); + if (buffer) { + memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); + buffer += sizeof (gc_event_data); + + memcpy (buffer, &object_address, sizeof (object_address)); + buffer += sizeof (object_address); + + memcpy (buffer, &object_size, sizeof (object_size)); + buffer += sizeof (object_size); + + memcpy (buffer, &object_type, sizeof (object_type)); + buffer += sizeof (object_type); + + memcpy (buffer, &edge_count, sizeof (edge_count)); + buffer += sizeof (edge_count); + + for (uint64_t i = 0; i < edge_count; i++) { + object_address = (uintptr_t)SGEN_POINTER_UNTAG_ALL (refs [i]); + memcpy (buffer, &object_address, sizeof (object_address)); + buffer += sizeof (object_address); + } + } + + return 0; +} + +static +void +flush_gc_event_bulk_root_static_vars (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + if (!context->bulk_root_static_vars.count) + return; + + FireEtwGCBulkRootStaticVar ( + context->bulk_root_static_vars.count, + (uint64_t)mono_get_root_domain (), + clr_instance_get_id (), + context->bulk_root_static_vars.data_current - context->bulk_root_static_vars.data_start, + context->bulk_root_static_vars.data_start, + NULL, + NULL); + + context->bulk_root_static_vars.index++; + gc_heap_dump_context_clear_root_static_vars (context); +} + +static +void +fire_gc_event_bulk_root_static_var ( + GCHeapDumpContext *context, + GCRootData *root_data, + uintptr_t address, + uintptr_t object) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + EP_ASSERT (root_data); + EP_ASSERT (root_data->source == MONO_ROOT_SOURCE_STATIC); + + MonoVTable *vtable = (MonoVTable *)(root_data->key); + uint32_t static_var_flags = 0; + uint64_t type_id = (uint64_t)vtable; + const ep_char8_t *static_var_name = "?"; + + if (vtable && vtable->klass) { + ERROR_DECL (error); + gpointer iter = NULL; + MonoClassField *field; + uint32_t offset = GPTRDIFF_TO_UINT32 (address - root_data->start); + while ((field = mono_class_get_fields_internal (vtable->klass, &iter))) { + if (mono_field_get_flags (field) & FIELD_ATTRIBUTE_LITERAL) + continue; + if (!(mono_field_get_flags (field) & FIELD_ATTRIBUTE_STATIC)) + continue; + if (mono_field_is_deleted (field) || m_field_is_from_update (field)) + continue; + + if (mono_field_get_offset (field) == offset) { + static_var_name = mono_field_get_name (field); + break; + } + } + if (!is_ok (error)) + mono_error_cleanup (error); + error_init_reuse (error); + } + + size_t name_len = static_var_name && static_var_name [0] != '\0' + ? strlen (static_var_name) + : 0; + + size_t event_size = BULK_ROOT_STATIC_VAR_EVENT_TYPE_SIZE + ((name_len +1) * sizeof (ep_char16_t)); + + if (context->bulk_root_static_vars.data_end <= context->bulk_root_static_vars.data_current + event_size) + flush_gc_event_bulk_root_static_vars (context); + + if (context->bulk_root_static_vars.data_end <= context->bulk_root_static_vars.data_current + event_size) + return; + + if (vtable && vtable->klass) { + MonoType *klass_type = m_class_get_byval_arg (vtable->klass); + bulk_type_log_type_and_parameters_if_necessary (context->bulk_type_logger, klass_type, TYPE_LOG_BEHAVIOR_IF_FIRST_TIME); + type_id = get_typeid_for_type (klass_type); + } + + // NOTE, needs to match manifest GCBulkRootStaticVar values type. + ep_write_buffer_uint64_t (&context->bulk_root_static_vars.data_current, (uint64_t)address); + ep_write_buffer_uint64_t (&context->bulk_root_static_vars.data_current, (uint64_t)object); + ep_write_buffer_uint64_t (&context->bulk_root_static_vars.data_current, type_id); + ep_write_buffer_uint32_t (&context->bulk_root_static_vars.data_current, static_var_flags); + write_buffer_string_utf8_to_utf16_t (&context->bulk_root_static_vars.data_current, static_var_name, GSIZE_TO_UINT32 (name_len)); + + context->bulk_root_static_vars.count++; +} + +static +void +flush_gc_event_bulk_root_edges (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + if (!context->bulk_root_edges.count) + return; + + FireEtwGCBulkRootEdge ( + context->bulk_root_edges.index, + context->bulk_root_edges.count, + clr_instance_get_id (), + BULK_ROOT_EDGE_EVENT_TYPE_SIZE, + context->bulk_root_edges.data_start, + NULL, + NULL); + + context->bulk_root_edges.index++; + gc_heap_dump_context_clear_root_edges (context); +} + +static +void +flush_gc_event_bulk_root_cwt_elem_edges (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + if (!context->bulk_root_cwt_elem_edges.count) + return; + + FireEtwGCBulkRootConditionalWeakTableElementEdge ( + context->bulk_root_cwt_elem_edges.index, + context->bulk_root_cwt_elem_edges.count, + clr_instance_get_id (), + BULK_ROOT_CWT_ELEM_EDGE_EVENT_TYPE_SIZE, + context->bulk_root_cwt_elem_edges.data_start, + NULL, + NULL); + + context->bulk_root_cwt_elem_edges.index++; + gc_heap_dump_context_clear_root_cwt_elem_edges (context); +} + +static +void +fire_gc_event_bulk_root_cwt_elem_edge ( + GCHeapDumpContext *context, + uintptr_t address, + uintptr_t key, + uintptr_t value) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + if (context->bulk_root_cwt_elem_edges.count == context->bulk_root_cwt_elem_edges.max_count) + flush_gc_event_bulk_root_cwt_elem_edges (context); + + // NOTE, must match manifest GCBulkRootConditionalWeakTableElementEdge values type. + ep_write_buffer_uintptr_t (&context->bulk_root_cwt_elem_edges.data_current, key); + ep_write_buffer_uintptr_t (&context->bulk_root_cwt_elem_edges.data_current, value); + ep_write_buffer_uintptr_t (&context->bulk_root_cwt_elem_edges.data_current, address); + + context->bulk_root_cwt_elem_edges.count++; +} + +static +void +fire_gc_event_bulk_root_edge ( + GCHeapDumpContext *context, + uintptr_t address, + uintptr_t object) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + if (context->bulk_root_edges.count == context->bulk_root_edges.max_count) + flush_gc_event_bulk_root_edges (context); + + uint8_t root_kind = GC_ROOT_KIND_OTHER; + uint32_t root_flags = GC_ROOT_FLAGS_NONE; + uintptr_t root_id = 0; + + GCRootData *gc_root = gc_heap_dump_context_find_root (context, address); + if (gc_root) { + if (gc_root->source == MONO_ROOT_SOURCE_STATIC) { + fire_gc_event_bulk_root_static_var ( + context, + gc_root, + address, + object); + return; + } + + if (gc_root->source == MONO_ROOT_SOURCE_EPHEMERON) { + // Should be ephemeron key, but current profiler + // API won't report it and key is only validated + // to be none 0 and a aligned pointer by gcdump. + // Use object as key until we get key reported + // in gc_roots profiler callback. + uintptr_t key = object; + uintptr_t value = object; + fire_gc_event_bulk_root_cwt_elem_edge ( + context, + root_id, + key, + value); + return; + } + + switch (gc_root->source) { + case MONO_ROOT_SOURCE_STACK : + root_kind = GC_ROOT_KIND_STACK; + root_id = address; + break; + case MONO_ROOT_SOURCE_FINALIZER_QUEUE : + root_kind = GC_ROOT_KIND_FINALIZER; + break; + case MONO_ROOT_SOURCE_THREAD_STATIC : + root_kind = GC_ROOT_KIND_HANDLE; + root_id = address; + break; + case MONO_ROOT_SOURCE_GC_HANDLE : + root_kind = GC_ROOT_KIND_HANDLE; + root_flags = GPOINTER_TO_INT (gc_root->key) != 0 ? GC_ROOT_FLAGS_PINNING : GC_ROOT_FLAGS_NONE; + root_id = address; + break; + case MONO_ROOT_SOURCE_HANDLE : + root_kind = GC_ROOT_KIND_HANDLE; + root_id = address; + break; + default : + break; + } + } + + // NOTE, needs to match manifest GCBulkRootEdge values type. + ep_write_buffer_uintptr_t (&context->bulk_root_edges.data_current, object); + ep_write_buffer_uint8_t (&context->bulk_root_edges.data_current, root_kind); + ep_write_buffer_uint32_t (&context->bulk_root_edges.data_current, root_flags); + ep_write_buffer_uintptr_t (&context->bulk_root_edges.data_current, root_id); + + context->bulk_root_edges.count++; +} + +static +void +fire_gc_event_roots ( + GCHeapDumpContext *context, + const uint8_t *data, + uint32_t payload_size) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + uint64_t count; + uintptr_t address; + uintptr_t object; + + EP_ASSERT (data + sizeof (count) <= data + payload_size); + memcpy (&count, data, sizeof (count)); + data += sizeof (count); + + EP_ASSERT (data + (count * (sizeof (address) + sizeof (object))) <= data + payload_size); + for (uint32_t i = 0; i < count; i++) { + memcpy (&address, data, sizeof (address)); + data += sizeof (address); + + memcpy (&object, data, sizeof (object)); + data += sizeof (object); + + fire_gc_event_bulk_root_edge ( + context, + address, + object); + } +} + +static +void +buffer_gc_event_roots_callback ( + MonoProfiler *prof, + uint64_t count, + const mono_byte *const * addresses, + MonoObject *const * objects) +{ + GCHeapDumpContext *context = gc_heap_dump_context_get (); + if (!context) + return; + + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + BufferedGCEvent gc_event_data; + gc_event_data.type = BUFFERED_GC_EVENT_ROOTS; + gc_event_data.payload_size = + (uint32_t)(sizeof (count) + + (count * (sizeof (uintptr_t) + sizeof (uintptr_t) + sizeof (uintptr_t)))); + + EP_ASSERT (context->buffer); + EP_ASSERT (context->buffer->context); + + uint8_t *buffer = context->buffer->alloc_func (context->buffer->context, sizeof (gc_event_data) + gc_event_data.payload_size); + if (buffer) { + memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); + buffer += sizeof (gc_event_data); + + memcpy (buffer, &count, sizeof (count)); + buffer += sizeof (count); + + for (uint64_t i = 0; i < count; i++) { + uintptr_t address = (uintptr_t)addresses [i]; + uintptr_t object = (uintptr_t)SGEN_POINTER_UNTAG_ALL (objects [i]); + + memcpy (buffer, &address, sizeof (address)); + buffer += sizeof (address); + + memcpy (buffer, &object, sizeof (object)); + buffer += sizeof (object); + } + } +} + +static +void +flush_gc_events (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + flush_gc_event_bulk_nodes (context); + flush_gc_event_bulk_edges (context); + flush_gc_event_bulk_root_edges (context); + flush_gc_event_bulk_root_cwt_elem_edges (context); + flush_gc_event_bulk_root_static_vars (context); + + if (context->bulk_type_logger && is_keyword_and_level_enabled (&context->trace_context, EP_EVENT_LEVEL_INFORMATIONAL, TYPE_KEYWORD)) + bulk_type_fire_bulk_type_event (context->bulk_type_logger); +} + +static +void +fire_buffered_gc_events (GCHeapDumpContext *context) +{ + EP_ASSERT (is_gc_heap_dump_enabled (context)); + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + + if (context->buffer) { + const uint8_t *buffer = NULL; + const uint8_t *buffer_end = NULL; + size_t size = 0; + + context->buffer->reset_func (context->buffer->context); + + while ((buffer = context->buffer->get_next_buffer_func (context->buffer->context, &size))) { + buffer_end = buffer + size; + while (buffer < buffer_end) { + if (buffer + sizeof (BufferedGCEvent) > buffer_end) + break; + + BufferedGCEvent *gc_event = (BufferedGCEvent *)buffer; + buffer += sizeof (BufferedGCEvent); + + if (buffer + gc_event->payload_size > buffer_end) + break; + + switch (gc_event->type) { + case BUFFERED_GC_EVENT_OBJECT_REF : + fire_gc_event_object_reference (context, buffer, gc_event->payload_size); + break; + case BUFFERED_GC_EVENT_ROOTS : + fire_gc_event_roots (context, buffer, gc_event->payload_size); + break; + default: + EP_ASSERT (!"Unknown buffered GC event type."); + } + + buffer += gc_event->payload_size; + } + } + + flush_gc_events (context); + } +} + +static +void +calculate_live_keywords ( + uint64_t *live_keywords, + bool *trigger_heap_dump) +{ + uint64_t keywords[] = { GC_HEAP_COLLECT_KEYWORD }; + uint64_t count[] = { 0 }; + + ep_requires_lock_held (); + + EP_ASSERT (G_N_ELEMENTS (keywords) == G_N_ELEMENTS (count)); + *live_keywords = ep_rt_mono_session_calculate_and_count_all_keywords ( + ep_config_get_public_provider_name_utf8 (), + keywords, + count, + G_N_ELEMENTS (count)); + + *trigger_heap_dump = ep_rt_mono_is_runtime_initialized (); + *trigger_heap_dump &= is_keword_enabled (*live_keywords, GC_KEYWORD); + *trigger_heap_dump &= is_keword_enabled (*live_keywords, GC_HEAP_COLLECT_KEYWORD); + *trigger_heap_dump &= count [0] > _gc_heap_dump_trigger_count; + + _gc_heap_dump_trigger_count = count [0]; + + ep_requires_lock_held (); +} + +static +void +gc_event_callback ( + MonoProfiler *prof, + MonoProfilerGCEvent gc_event, + uint32_t generation, + mono_bool serial) +{ + switch (gc_event) { + case MONO_GC_EVENT_POST_STOP_WORLD: + { + GCHeapDumpContext *context = gc_heap_dump_context_get (); + if (is_gc_heap_dump_enabled (context)) { + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + mono_profiler_set_gc_roots_callback (_ep_rt_mono_default_profiler_provider, buffer_gc_event_roots_callback); + } + break; + } + case MONO_GC_EVENT_PRE_START_WORLD: + { + GCHeapDumpContext *context = gc_heap_dump_context_get (); + if (is_gc_heap_dump_enabled (context)) { + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + mono_gc_walk_heap (0, buffer_gc_event_object_reference_callback, context); + mono_profiler_set_gc_roots_callback (_ep_rt_mono_default_profiler_provider, NULL); + } + break; + } + case MONO_GC_EVENT_POST_START_WORLD: + { + GCHeapDumpContext *context = gc_heap_dump_context_get (); + if (is_gc_heap_dump_enabled (context)) { + EP_ASSERT (context->state == GC_HEAP_DUMP_CONTEXT_STATE_DUMP); + gc_heap_dump_context_build_roots (context); + fire_buffered_gc_events (context); + } + break; + } + default: + break; + } +} + +static +void +gc_heap_dump_trigger_callback (MonoProfiler *prof) +{ + bool notify_finalizer = false; + GCHeapDumpContext *heap_dump_context = gc_heap_dump_context_get (); + + if (!heap_dump_context && gc_heap_dump_requested ()) { + EP_LOCK_ENTER (section1) + if (gc_heap_dump_requested ()) { + EVENTPIPE_TRACE_CONTEXT context = RUNTIME_PROVIDER_CONTEXT; + if (!dn_vector_empty (&_gc_heap_dump_requests_data)) { + context = *dn_vector_back_t (&_gc_heap_dump_requests_data, EVENTPIPE_TRACE_CONTEXT); + dn_vector_pop_back (&_gc_heap_dump_requests_data); + } + gc_heap_dump_requests_dec (); + + uint32_t gc_count = gc_heap_dump_count_inc (); + uint32_t gc_depth = mono_gc_max_generation () + 1; + uint32_t gc_reason = GC_REASON_INDUCED; + uint32_t gc_type = GC_TYPE_NGC; + + heap_dump_context = + gc_heap_dump_context_alloc ( + context, + gc_reason, + gc_type, + gc_count, + gc_depth); + + gc_heap_dump_context_set (heap_dump_context); + } + EP_LOCK_EXIT (section1) + } + + if (heap_dump_context) { + switch (heap_dump_context->state) { + case GC_HEAP_DUMP_CONTEXT_STATE_INIT : + { + bool all_started = false; + EP_LOCK_ENTER (section2) + all_started = ep_rt_mono_sesion_has_all_started (); + EP_LOCK_EXIT (section2) + + if (!all_started && heap_dump_context->retry_count < 5) { + heap_dump_context->retry_count++; + notify_finalizer = true; + break; + } + + heap_dump_context->state = GC_HEAP_DUMP_CONTEXT_STATE_START; + // Fallthrough + } + case GC_HEAP_DUMP_CONTEXT_STATE_START : + { + FireEtwGCStart_V2 ( + heap_dump_context->gc_count, + heap_dump_context->gc_depth, + heap_dump_context->gc_reason, + heap_dump_context->gc_type, + clr_instance_get_id (), + 0, + NULL, + NULL); + + heap_dump_context->state = GC_HEAP_DUMP_CONTEXT_STATE_DUMP; + notify_finalizer = true; + break; + } + case GC_HEAP_DUMP_CONTEXT_STATE_DUMP : + { + bool gc_dump = true; + gc_dump &= EventPipeEventEnabledGCBulkNode (); + gc_dump &= EventPipeEventEnabledGCBulkEdge (); + gc_dump &= EventPipeEventEnabledGCBulkRootEdge (); + gc_dump &= EventPipeEventEnabledGCBulkRootStaticVar (); + + if (gc_dump) { + mono_profiler_set_gc_event_callback (_ep_rt_mono_default_profiler_provider, gc_event_callback); + mono_gc_collect (mono_gc_max_generation ()); + mono_profiler_set_gc_event_callback (_ep_rt_mono_default_profiler_provider, NULL); + } + + heap_dump_context->state = GC_HEAP_DUMP_CONTEXT_STATE_END; + notify_finalizer = true; + break; + } + case GC_HEAP_DUMP_CONTEXT_STATE_END : + { + FireEtwGCEnd_V1 ( + heap_dump_context->gc_count, + heap_dump_context->gc_depth, + clr_instance_get_id (), + NULL, + NULL); + + gc_heap_dump_context_reset (); + heap_dump_context = NULL; + break; + } + default : + g_assert_not_reached (); + } + } + + if (!heap_dump_context) { + EP_LOCK_ENTER (section3) + bool gc_enabled = is_keword_enabled(RUNTIME_PROVIDER_CONTEXT.EnabledKeywordsBitmask, GC_KEYWORD); + bool gc_dump_enabled = is_keword_enabled(RUNTIME_PROVIDER_CONTEXT.EnabledKeywordsBitmask, GC_HEAP_COLLECT_KEYWORD); + if (!(gc_enabled && gc_dump_enabled)) + mono_profiler_set_gc_finalized_callback (_ep_rt_mono_default_profiler_provider, NULL); + EP_LOCK_EXIT (section3) + } + + if (notify_finalizer) { + ep_rt_thread_sleep (200 * NUM_NANOSECONDS_IN_1_MS); + mono_gc_finalize_notify (); + } + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +void +EventPipeEtwCallbackDotNETRuntime ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_data) +{ + ep_rt_config_requires_lock_not_held (); + + EP_ASSERT (_ep_rt_mono_default_profiler_provider != NULL); + + EP_LOCK_ENTER (section1) + uint64_t live_keywords = 0; + bool trigger_heap_dump = false; + calculate_live_keywords (&live_keywords, &trigger_heap_dump); + + if (is_keword_enabled(live_keywords, JIT_KEYWORD)) { + mono_profiler_set_jit_begin_callback (_ep_rt_mono_default_profiler_provider, jit_begin_callback); + mono_profiler_set_jit_failed_callback (_ep_rt_mono_default_profiler_provider, jit_failed_callback); + mono_profiler_set_jit_done_callback (_ep_rt_mono_default_profiler_provider, jit_done_callback); + } else { + mono_profiler_set_jit_begin_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_jit_failed_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_jit_done_callback (_ep_rt_mono_default_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, LOADER_KEYWORD)) { + mono_profiler_set_image_loaded_callback (_ep_rt_mono_default_profiler_provider, image_loaded_callback); + mono_profiler_set_image_unloaded_callback (_ep_rt_mono_default_profiler_provider, image_unloaded_callback); + mono_profiler_set_assembly_loaded_callback (_ep_rt_mono_default_profiler_provider, assembly_loaded_callback); + mono_profiler_set_assembly_unloaded_callback (_ep_rt_mono_default_profiler_provider, assembly_unloaded_callback); + } else { + mono_profiler_set_image_loaded_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_image_unloaded_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_assembly_loaded_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_assembly_unloaded_callback (_ep_rt_mono_default_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, TYPE_DIAGNOSTIC_KEYWORD)) { + mono_profiler_set_class_loading_callback (_ep_rt_mono_default_profiler_provider, class_loading_callback); + mono_profiler_set_class_failed_callback (_ep_rt_mono_default_profiler_provider, class_failed_callback); + mono_profiler_set_class_loaded_callback (_ep_rt_mono_default_profiler_provider, class_loaded_callback); + } else { + mono_profiler_set_class_loading_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_class_failed_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_class_loaded_callback (_ep_rt_mono_default_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, EXCEPTION_KEYWORD)) { + mono_profiler_set_exception_throw_callback (_ep_rt_mono_default_profiler_provider, exception_throw_callback); + mono_profiler_set_exception_clause_callback (_ep_rt_mono_default_profiler_provider, exception_clause_callback); + } else { + mono_profiler_set_exception_throw_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_exception_clause_callback (_ep_rt_mono_default_profiler_provider, NULL); + } + + if (is_keword_enabled(live_keywords, CONTENTION_KEYWORD)) { + mono_profiler_set_monitor_contention_callback (_ep_rt_mono_default_profiler_provider, monitor_contention_callback); + mono_profiler_set_monitor_acquired_callback (_ep_rt_mono_default_profiler_provider, monitor_acquired_callback); + mono_profiler_set_monitor_failed_callback (_ep_rt_mono_default_profiler_provider, monitor_failed_callback); + } else { + mono_profiler_set_monitor_contention_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_monitor_acquired_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_monitor_failed_callback (_ep_rt_mono_default_profiler_provider, NULL); + } + + // Disabled in gc_heap_dump_trigger_callback when no longer needed. + if (is_keword_enabled(live_keywords, GC_KEYWORD) && is_keword_enabled(live_keywords, GC_HEAP_COLLECT_KEYWORD)) + mono_profiler_set_gc_finalized_callback (_ep_rt_mono_default_profiler_provider, gc_heap_dump_trigger_callback); + + RUNTIME_PROVIDER_CONTEXT.Level = level; + RUNTIME_PROVIDER_CONTEXT.EnabledKeywordsBitmask = live_keywords; + RUNTIME_PROVIDER_CONTEXT.IsEnabled = (live_keywords != 0 ? true : false); + + if (trigger_heap_dump) { + EP_ASSERT (ep_rt_mono_is_runtime_initialized ()); + dn_vector_push_back (&_gc_heap_dump_requests_data, RUNTIME_PROVIDER_CONTEXT); + gc_heap_dump_requests_inc (); + mono_gc_finalize_notify (); + } + EP_LOCK_EXIT (section1) + +ep_on_exit: + ep_rt_config_requires_lock_not_held (); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +static +GCRootData * +gc_root_data_alloc ( + uintptr_t start, + uintptr_t size, + MonoGCRootSource source, + const void *key, + const char *name) +{ + GCRootData *root_data = g_new0 (GCRootData, 1); + root_data->start = start; + root_data->end = start + size; + root_data->key = key; + //root_data->name = g_strdup (name); + root_data->source = source; + return root_data; +} + +static +void +gc_root_data_free (void *data) +{ + GCRootData *root_data = (GCRootData *)data; + if (root_data) { + g_free (root_data->name); + g_free (root_data); + } +} + +static +void +gc_root_register_callback ( + MonoProfiler *prof, + const mono_byte *start, + uintptr_t size, + MonoGCRootSource source, + const void *key, + const char *name) +{ + ep_rt_spin_lock_requires_lock_not_held (&_gc_lock); + + GCRootData *root_data = gc_root_data_alloc ((uintptr_t)start, size, source, key, name); + EP_SPIN_LOCK_ENTER (&_gc_lock, section1) + dn_umap_insert_or_assign (&_gc_roots_table, (void *)start, root_data); + EP_SPIN_LOCK_EXIT (&_gc_lock, section1) + +ep_on_exit: + ep_rt_spin_lock_requires_lock_not_held (&_gc_lock); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +static +void +gc_root_unregister_callback ( + MonoProfiler *prof, + const mono_byte *start) +{ + ep_rt_spin_lock_requires_lock_not_held (&_gc_lock); + + GCRootData *root_data = NULL; + EP_SPIN_LOCK_ENTER (&_gc_lock, section1) + dn_umap_extract_key (&_gc_roots_table, start, NULL, (void **)&root_data); + EP_SPIN_LOCK_EXIT (&_gc_lock, section1) + + g_free (root_data); + +ep_on_exit: + ep_rt_spin_lock_requires_lock_not_held (&_gc_lock); + return; + +ep_on_error: + ep_exit_error_handler (); +} + +void +ep_rt_mono_runtime_provider_component_init (void) +{ + ep_rt_spin_lock_alloc (&_gc_lock); + + dn_umap_custom_init_params_t params = {0,}; + params.value_dispose_func = gc_root_data_free; + + dn_umap_custom_init (&_gc_roots_table, ¶ms); + + dn_vector_init_t (&_gc_heap_dump_requests_data, EVENTPIPE_TRACE_CONTEXT); + + EP_ASSERT (_ep_rt_mono_default_profiler_provider != NULL); + mono_profiler_set_gc_root_register_callback (_ep_rt_mono_default_profiler_provider, gc_root_register_callback); + mono_profiler_set_gc_root_unregister_callback (_ep_rt_mono_default_profiler_provider, gc_root_unregister_callback); +} + +void +ep_rt_mono_runtime_provider_init (void) +{ + MonoMethodSignature *method_signature = mono_metadata_signature_alloc (mono_get_corlib (), 1); + if (method_signature) { + method_signature->params[0] = m_class_get_byval_arg (mono_get_object_class()); + method_signature->ret = m_class_get_byval_arg (mono_get_void_class()); + + ERROR_DECL (error); + MonoClass *runtime_helpers = mono_class_from_name_checked (mono_get_corlib (), "System.Runtime.CompilerServices", "RuntimeHelpers", error); + if (is_ok (error) && runtime_helpers) { + MonoMethodBuilder *method_builder = mono_mb_new (runtime_helpers, "CompileMethod", MONO_WRAPPER_RUNTIME_INVOKE); + if (method_builder) { + _runtime_helper_compile_method = mono_mb_create_method (method_builder, method_signature, 1); + mono_mb_free (method_builder); + } + } + mono_error_cleanup (error); + mono_metadata_free_method_signature (method_signature); + + if (_runtime_helper_compile_method) { + _runtime_helper_compile_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_runtime_helper_compile_method) { + _runtime_helper_compile_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_runtime_helper_compile_method); + _runtime_helper_compile_method_jitinfo->code_size = 20; + _runtime_helper_compile_method_jitinfo->d.method = _runtime_helper_compile_method; + } + } + } + + { + ERROR_DECL (error); + MonoMethodDesc *desc = NULL; + MonoClass *monitor = mono_class_from_name_checked (mono_get_corlib (), "System.Threading", "Monitor", error); + if (is_ok (error) && monitor) { + desc = mono_method_desc_new ("Monitor:Enter(object,bool&)", FALSE); + if (desc) { + _monitor_enter_v4_method = mono_method_desc_search_in_class (desc, monitor); + mono_method_desc_free (desc); + + if (_monitor_enter_v4_method) { + _monitor_enter_v4_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_monitor_enter_v4_method_jitinfo) { + _monitor_enter_v4_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_monitor_enter_v4_method); + _monitor_enter_v4_method_jitinfo->code_size = 20; + _monitor_enter_v4_method_jitinfo->d.method = _monitor_enter_v4_method; + } + } + } + + desc = mono_method_desc_new ("Monitor:Enter(object)", FALSE); + if (desc) { + _monitor_enter_method = mono_method_desc_search_in_class (desc, monitor); + mono_method_desc_free (desc); + + if (_monitor_enter_method ) { + _monitor_enter_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); + if (_monitor_enter_method_jitinfo) { + _monitor_enter_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_monitor_enter_method); + _monitor_enter_method_jitinfo->code_size = 20; + _monitor_enter_method_jitinfo->d.method = _monitor_enter_method; + } + } + } + } + mono_error_cleanup (error); + } +} + +void +ep_rt_mono_runtime_provider_fini (void) +{ + if (_sampled_thread_callstacks) + g_array_free (_sampled_thread_callstacks, TRUE); + _sampled_thread_callstacks = NULL; + + _max_sampled_thread_count = 32; + + g_free (_runtime_helper_compile_method_jitinfo); + _runtime_helper_compile_method_jitinfo = NULL; + + if (_runtime_helper_compile_method) + mono_free_method (_runtime_helper_compile_method); + _runtime_helper_compile_method = NULL; + + g_free (_monitor_enter_method_jitinfo); + _monitor_enter_method_jitinfo = NULL; + _monitor_enter_method = NULL; + + g_free (_monitor_enter_v4_method_jitinfo); + _monitor_enter_v4_method_jitinfo = NULL; + _monitor_enter_v4_method = NULL; + + if (_ep_rt_mono_default_profiler_provider) { + mono_profiler_set_jit_begin_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_jit_failed_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_jit_done_callback (_ep_rt_mono_default_profiler_provider, NULL); + + mono_profiler_set_image_loaded_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_image_unloaded_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_assembly_loaded_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_assembly_unloaded_callback (_ep_rt_mono_default_profiler_provider, NULL); + + mono_profiler_set_class_loading_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_class_failed_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_class_loaded_callback (_ep_rt_mono_default_profiler_provider, NULL); + + mono_profiler_set_exception_throw_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_exception_clause_callback (_ep_rt_mono_default_profiler_provider, NULL); + + mono_profiler_set_monitor_contention_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_monitor_acquired_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_monitor_failed_callback (_ep_rt_mono_default_profiler_provider, NULL); + + mono_profiler_set_gc_root_register_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_gc_root_unregister_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_gc_finalized_callback (_ep_rt_mono_default_profiler_provider, NULL); + } + + _gc_heap_dump_requests = 0; + _gc_heap_dump_count = 0; + _gc_heap_dump_trigger_count = 0; + + dn_vector_dispose (&_gc_heap_dump_requests_data); + memset (&_gc_heap_dump_requests_data, 0, sizeof (_gc_heap_dump_requests_data)); + + dn_umap_dispose (&_gc_roots_table); + memset (&_gc_roots_table, 0, sizeof (_gc_roots_table)); + + ep_rt_spin_lock_free (&_gc_lock); +} + +void +ep_rt_mono_runtime_provider_thread_started_callback ( + MonoProfiler *prof, + uintptr_t tid) +{ + thread_started_callback (prof, tid); +} + +void +ep_rt_mono_runtime_provider_thread_stopped_callback ( + MonoProfiler *prof, + uintptr_t tid) +{ + thread_stopped_callback (prof, tid); +} + +#endif /* ENABLE_PERFTRACING */ + +MONO_EMPTY_SOURCE_FILE(eventpipe_rt_mono_runtime_provider); diff --git a/src/mono/mono/eventpipe/ep-rt-mono.c b/src/mono/mono/eventpipe/ep-rt-mono.c index 0fcfa9dba8725..5a54cd9b9ac00 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.c +++ b/src/mono/mono/eventpipe/ep-rt-mono.c @@ -5,43 +5,32 @@ #include #include #include -#include +#include -#include #include #include -#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include #include #include extern void InitProvidersAndEvents (void); -// EventPipe rt init state. -gboolean _ep_rt_mono_initialized; +// EventPipe init state. +static gboolean _eventpipe_initialized; + +// Runtime init state. +gboolean _ep_rt_mono_runtime_initialized; // EventPipe TLS key. MonoNativeTlsKey _ep_rt_mono_thread_holder_tls_id; -MonoNativeTlsKey _ep_rt_mono_thread_data_tls_id; +static MonoNativeTlsKey _thread_data_tls_id; // Random byte provider. -gpointer _ep_rt_mono_rand_provider; +static gpointer _rand_provider; // EventPipe global config lock. ep_rt_spin_lock_handle_t _ep_rt_mono_config_lock = {0}; @@ -54,7439 +43,860 @@ char *_ep_rt_mono_os_cmd_line = NULL; mono_lazy_init_t _ep_rt_mono_managed_cmd_line_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; char *_ep_rt_mono_managed_cmd_line = NULL; -// Custom Mono EventPipe thread data. -typedef struct _EventPipeThreadData EventPipeThreadData; -struct _EventPipeThreadData { - bool prevent_profiler_event_recursion; -}; - -// Sample profiler. -static GArray * _ep_rt_mono_sampled_thread_callstacks = NULL; -static uint32_t _ep_rt_mono_max_sampled_thread_count = 32; - -// Mono profilers. -static MonoProfilerHandle _ep_rt_default_profiler = NULL; -static MonoProfilerHandle _ep_rt_dotnet_runtime_profiler_provider = NULL; -static MonoProfilerHandle _ep_rt_dotnet_mono_profiler_provider = NULL; -static MonoProfilerHandle _ep_rt_dotnet_mono_profiler_heap_collect_provider = NULL; -static MonoCallSpec _ep_rt_dotnet_mono_profiler_provider_callspec = {0}; - -// Phantom JIT compile method. -MonoMethod *_ep_rt_mono_runtime_helper_compile_method = NULL; -MonoJitInfo *_ep_rt_mono_runtime_helper_compile_method_jitinfo = NULL; +// Mono profilers (shared with runtime provider). +MonoProfilerHandle _ep_rt_mono_default_profiler_provider = NULL; -// Monitor.Enter methods. -MonoMethod *_ep_rt_mono_monitor_enter_method = NULL; -MonoJitInfo *_ep_rt_mono_monitor_enter_method_jitinfo = NULL; +// Providers +EVENTPIPE_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context = {0}; +EVENTPIPE_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context = {0}; +EVENTPIPE_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context = {0}; +EVENTPIPE_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_DOTNET_Context = {0}; +EVENTPIPE_TRACE_CONTEXT MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_DOTNET_Context = {0}; -MonoMethod *_ep_rt_mono_monitor_enter_v4_method = NULL; -MonoJitInfo *_ep_rt_mono_monitor_enter_v4_method_jitinfo = NULL; +#define RUNTIME_PROVIDER_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context +#define RUNTIME_PRIVATE_PROVIDER_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context +#define RUNTIME_RUNDOWN_PROVIDER_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context +#define RUNTIME_STRESS_PROVIDER_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_DOTNET_Context +#define RUNTIME_MONO_PROFILER_PROVIDER_CONTEXT MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_DOTNET_Context -// Rundown types. -typedef bool -(*ep_rt_mono_fire_method_rundown_events_func)( - const uint64_t method_id, - const uint64_t module_id, - const uint64_t method_start_address, - const uint32_t method_size, - const uint32_t method_token, - const uint32_t method_flags, - const ep_char8_t *method_namespace, - const ep_char8_t *method_name, - const ep_char8_t *method_signature, - const uint16_t count_of_map_entries, - const uint32_t *il_offsets, - const uint32_t *native_offsets, - bool aot_method, - bool verbose, - void *user_data); - -typedef -bool -(*ep_rt_mono_fire_assembly_rundown_events_func)( - const uint64_t domain_id, - const uint64_t assembly_id, - const uint32_t assembly_flags, - const uint32_t binding_id, - const ep_char8_t *assembly_name, - const uint64_t module_id, - const uint32_t module_flags, - const uint32_t reserved_flags, - const ep_char8_t *module_il_path, - const ep_char8_t *module_native_path, - const uint8_t *managed_pdb_signature, - const uint32_t managed_pdb_age, - const ep_char8_t *managed_pdb_build_path, - const uint8_t *native_pdb_signature, - const uint32_t native_pdb_age, - const ep_char8_t *native_pdb_build_path, - void *user_data); - -typedef -bool -(*ep_rt_mono_fire_domain_rundown_events_func)( - const uint64_t domain_id, - const uint32_t domain_flags, - const ep_char8_t *domain_name, - const uint32_t domain_index, - void *user_data); - -typedef struct _EventPipeFireMethodEventsData { - MonoDomain *domain; - uint8_t *buffer; - size_t buffer_size; - ep_rt_mono_fire_method_rundown_events_func method_events_func; -} EventPipeFireMethodEventsData; - -typedef struct _EventPipeStackWalkData { - EventPipeStackContents *stack_contents; - bool top_frame; - bool async_frame; - bool safe_point_frame; - bool runtime_invoke_frame; -} EventPipeStackWalkData; - -typedef struct _EventPipeSampleProfileStackWalkData { - EventPipeStackWalkData stack_walk_data; - EventPipeStackContents stack_contents; - uint64_t thread_id; - uintptr_t thread_ip; - uint32_t payload_data; -} EventPipeSampleProfileStackWalkData; - -// Rundown flags. -#define RUNTIME_SKU_MONO 0x4 -#define METHOD_FLAGS_DYNAMIC_METHOD 0x1 -#define METHOD_FLAGS_GENERIC_METHOD 0x2 -#define METHOD_FLAGS_SHARED_GENERIC_METHOD 0x4 -#define METHOD_FLAGS_JITTED_METHOD 0x8 -#define METHOD_FLAGS_JITTED_HELPER_METHOD 0x10 -#define METHOD_FLAGS_EXTENT_HOT_SECTION 0x00000000 -#define METHOD_FLAGS_EXTENT_COLD_SECTION 0x10000000 - -#define MODULE_FLAGS_NATIVE_MODULE 0x2 -#define MODULE_FLAGS_DYNAMIC_MODULE 0x4 -#define MODULE_FLAGS_MANIFEST_MODULE 0x8 - -#define ASSEMBLY_FLAGS_DYNAMIC_ASSEMBLY 0x2 -#define ASSEMBLY_FLAGS_NATIVE_ASSEMBLY 0x4 -#define ASSEMBLY_FLAGS_COLLECTIBLE_ASSEMBLY 0x8 - -#define DOMAIN_FLAGS_DEFAULT_DOMAIN 0x1 -#define DOMAIN_FLAGS_EXECUTABLE_DOMAIN 0x2 - -// Event data types. -struct _ModuleEventData { - uint8_t module_il_pdb_signature [EP_GUID_SIZE]; - uint8_t module_native_pdb_signature [EP_GUID_SIZE]; - uint64_t domain_id; - uint64_t module_id; - uint64_t assembly_id; - const char *module_il_path; - const char *module_il_pdb_path; - const char *module_native_path; - const char *module_native_pdb_path; - uint32_t module_il_pdb_age; - uint32_t module_native_pdb_age; - uint32_t reserved_flags; - uint32_t module_flags; -}; - -typedef struct _ModuleEventData ModuleEventData; - -struct _AssemblyEventData { - uint64_t domain_id; - uint64_t assembly_id; - uint64_t binding_id; - char *assembly_name; - uint32_t assembly_flags; -}; - -typedef struct _AssemblyEventData AssemblyEventData; - -// Event flags. -#define THREAD_FLAGS_GC_SPECIAL 0x00000001 -#define THREAD_FLAGS_FINALIZER 0x00000002 -#define THREAD_FLAGS_THREADPOOL_WORKER 0x00000004 - -#define EXCEPTION_THROWN_FLAGS_HAS_INNER 0x1 -#define EXCEPTION_THROWN_FLAGS_IS_NESTED 0x2 -#define EXCEPTION_THROWN_FLAGS_IS_RETHROWN 0x4 -#define EXCEPTION_THROWN_FLAGS_IS_CSE 0x8 -#define EXCEPTION_THROWN_FLAGS_IS_CLS_COMPLIANT 0x10 - -// Provider keyword flags. -#define GC_KEYWORD 0x1 -#define GC_HANDLE_KEYWORD 0x2 -#define LOADER_KEYWORD 0x8 -#define JIT_KEYWORD 0x10 -#define APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD 0x800 -#define CONTENTION_KEYWORD 0x4000 -#define EXCEPTION_KEYWORD 0x8000 -#define THREADING_KEYWORD 0x10000 -#define GC_HEAP_DUMP_KEYWORD 0x100000 -#define GC_ALLOCATION_KEYWORD 0x200000 -#define GC_MOVES_KEYWORD 0x400000 -#define GC_HEAP_COLLECT_KEYWORD 0x800000 -#define GC_FINALIZATION_KEYWORD 0x1000000 -#define GC_RESIZE_KEYWORD 0x2000000 -#define GC_ROOT_KEYWORD 0x4000000 -#define GC_HEAP_DUMP_VTABLE_CLASS_REF_KEYWORD 0x8000000 -#define METHOD_TRACING_KEYWORD 0x20000000 -#define TYPE_DIAGNOSTIC_KEYWORD 0x8000000000 -#define TYPE_LOADING_KEYWORD 0x8000000000 -#define MONITOR_KEYWORD 0x10000000000 -#define METHOD_INSTRUMENTATION_KEYWORD 0x40000000000 - -// MonoProfiler types. -typedef enum { - MONO_PROFILER_BUFFERED_GC_EVENT = 1, - MONO_PROFILER_BUFFERED_GC_EVENT_RESIZE = 2, - MONO_PROFILER_BUFFERED_GC_EVENT_ROOTS = 3, - MONO_PROFILER_BUFFERED_GC_EVENT_MOVES = 4, - MONO_PROFILER_BUFFERED_GC_EVENT_OBJECT_REF = 5, - MONO_PROFILER_BUFFERED_GC_EVENT_ROOT_REGISTER = 6, - MONO_PROFILER_BUFFERED_GC_EVENT_ROOT_UNREGISTER = 7 -} MonoProfilerBufferedGCEventType; - -typedef struct _MonoProfilerBufferedGCEvent MonoProfilerBufferedGCEvent; -struct _MonoProfilerBufferedGCEvent { - MonoProfilerBufferedGCEventType type; - uint32_t payload_size; -}; - -#define MONO_PROFILER_MEM_DEFAULT_BLOCK_SIZE (mono_pagesize() * 16) -#define MONO_PROFILER_MEM_BLOCK_SIZE_INC (mono_pagesize()) - -typedef struct _MonoProfilerMemBlock MonoProfilerMemBlock; -struct _MonoProfilerMemBlock { - MonoProfilerMemBlock *next; - MonoProfilerMemBlock *prev; - uint8_t *start; - uint32_t alloc_size; - uint32_t size; - uint32_t offset; - uint32_t last_used_offset; -}; - -// MonoProfiler GC dump. -static volatile MonoProfilerMemBlock *_ep_rt_mono_profiler_mem_blocks = NULL; -static volatile MonoProfilerMemBlock *_ep_rt_mono_profiler_current_mem_block = NULL; -static volatile uint32_t _ep_rt_mono_profiler_gc_heap_collect_requests = 0; -static volatile uint32_t _ep_rt_mono_profiler_gc_heap_collect_in_progress = 0; -static bool _ep_rt_mono_profiler_gc_can_collect_heap = false; - -static GSList *_ep_rt_mono_profiler_provider_params = NULL; -static GQueue *_ep_rt_mono_profiler_gc_heap_collect_request_params = NULL; - -// Lightweight atomic "exclusive/shared" lock, prevents new fire events to happend while GC is in progress and gives GC ability to wait until all pending fire events are done -// before progressing. State uint32_t is split into two uint16_t, upper uint16_t represent gc in progress state, taken when GC starts, preventing new fire events to execute and lower -// uint16_t keeps number of fire events in flight, (gc_in_progress << 16) | (fire_event_count & 0xFFFF). Spin lock is only taken on slow path to queue up pending shared requests -// while GC is in progress and should very rarely be needed. -typedef uint32_t mono_profiler_gc_state_t; -typedef uint16_t mono_profiler_gc_state_count_t; - -#define MONO_PROFILER_GC_STATE_GET_FIRE_EVENT_COUNT(x) ((mono_profiler_gc_state_count_t)((x & 0xFFFF))) -#define MONO_PROFILER_GC_STATE_INC_FIRE_EVENT_COUNT(x) ((mono_profiler_gc_state_t)((mono_profiler_gc_state_t)(x & 0xFFFF0000) | (mono_profiler_gc_state_t)(MONO_PROFILER_GC_STATE_GET_FIRE_EVENT_COUNT(x) + 1))) -#define MONO_PROFILER_GC_STATE_DEC_FIRE_EVENT_COUNT(x) ((mono_profiler_gc_state_t)((mono_profiler_gc_state_t)(x & 0xFFFF0000) | (mono_profiler_gc_state_t)(MONO_PROFILER_GC_STATE_GET_FIRE_EVENT_COUNT(x) - 1))) -#define MONO_PROFILER_GC_STATE_GC_IN_PROGRESS_START(x) ((mono_profiler_gc_state_t)((mono_profiler_gc_state_t)(0xFFFF << 16) | (mono_profiler_gc_state_t)MONO_PROFILER_GC_STATE_GET_FIRE_EVENT_COUNT(x))) -#define MONO_PROFILER_GC_STATE_IS_GC_IN_PROGRESS(x) (((x >> 16) & 0xFFFF) == 0xFFFF) -#define MONO_PROFILER_GC_STATE_GC_IN_PROGRESS_STOP(x) ((mono_profiler_gc_state_t)((mono_profiler_gc_state_t)MONO_PROFILER_GC_STATE_GET_FIRE_EVENT_COUNT(x))) - -static volatile mono_profiler_gc_state_t _ep_rt_mono_profiler_gc_state = 0; -static ep_rt_spin_lock_handle_t _ep_rt_mono_profiler_gc_state_lock = {0}; - -/* - * Forward declares of all static functions. - */ - -static -EventPipeThreadData * -eventpipe_thread_data_get_or_create (void); +ep_rt_mono_rand_try_get_bytes ( + uint8_t *buffer, + size_t buffer_size) +{ + EP_ASSERT (_rand_provider != NULL); -static -void -eventpipe_thread_data_free (EventPipeThreadData *thread_data); + ERROR_DECL (error); + return mono_rand_try_get_bytes (&_rand_provider, (guchar *)buffer, (gssize)buffer_size, error); +} -static -bool -fire_method_rundown_events_func ( - const uint64_t method_id, - const uint64_t module_id, - const uint64_t method_start_address, - const uint32_t method_size, - const uint32_t method_token, - const uint32_t method_flags, - const ep_char8_t *method_namespace, - const ep_char8_t *method_name, - const ep_char8_t *method_signature, - const uint16_t count_of_map_entries, - const uint32_t *il_offsets, - const uint32_t *native_offsets, - bool aot_method, - bool verbose, - void *user_data); +char * +ep_rt_mono_get_managed_cmd_line (void) +{ + return mono_runtime_get_managed_cmd_line (); +} -static -bool -fire_assembly_rundown_events_func ( - const uint64_t domain_id, - const uint64_t assembly_id, - const uint32_t assembly_flags, - const uint32_t binding_id, - const ep_char8_t *assembly_name, - const uint64_t module_id, - const uint32_t module_flags, - const uint32_t reserved_flags, - const ep_char8_t *module_il_path, - const ep_char8_t *module_native_path, - const uint8_t *managed_pdb_signature, - const uint32_t managed_pdb_age, - const ep_char8_t *managed_pdb_build_path, - const uint8_t *native_pdb_signature, - const uint32_t native_pdb_age, - const ep_char8_t *native_pdb_build_path, - void *user_data); +char * +ep_rt_mono_get_os_cmd_line (void) +{ + MONO_REQ_GC_NEUTRAL_MODE; -static -bool -fire_domain_rundown_events_func ( - const uint64_t domain_id, - const uint32_t domain_flags, - const ep_char8_t *domain_name, - const uint32_t domain_index, - void *user_data); + // we only return the native host here since getting the full commandline is complicated and + // it's not super important to have the correct value since it'll only be used during startup + // until we have the managed commandline + char *host_path = minipal_getexepath (); -static -void -eventpipe_fire_method_events ( - MonoJitInfo *ji, - MonoMethod *method, - EventPipeFireMethodEventsData *events_data); + // minipal_getexepath doesn't use Mono APIs to allocate strings so + // we can't use g_free (which the callers of this method expect to do) + // so create another copy and return that one + char *res = g_strdup (host_path); + free (host_path); + return res; +} -static -void -eventpipe_fire_method_events_func ( - MonoJitInfo *ji, - void *user_data); +#ifdef HOST_WIN32 -static -void -eventpipe_fire_assembly_events ( - MonoDomain *domain, - MonoAssembly *assembly, - ep_rt_mono_fire_assembly_rundown_events_func assembly_events_func); +ep_rt_file_handle_t +ep_rt_mono_file_open_write (const ep_char8_t *path) +{ + if (!path) + return INVALID_HANDLE_VALUE; -static -gboolean -eventpipe_execute_rundown ( - ep_rt_mono_fire_domain_rundown_events_func domain_events_func, - ep_rt_mono_fire_assembly_rundown_events_func assembly_events_func, - ep_rt_mono_fire_method_rundown_events_func methods_events_func); + ep_char16_t *path_utf16 = ep_rt_utf8_to_utf16le_string (path, -1); -static -gboolean -eventpipe_walk_managed_stack_for_thread ( - MonoStackFrameInfo *frame, - MonoContext *ctx, - EventPipeStackWalkData *stack_walk_data); + if (!path_utf16) + return INVALID_HANDLE_VALUE; -static -gboolean -eventpipe_walk_managed_stack_for_thread_func ( - MonoStackFrameInfo *frame, - MonoContext *ctx, - void *data); + ep_rt_file_handle_t res; + MONO_ENTER_GC_SAFE; + res = (ep_rt_file_handle_t)CreateFileW (path_utf16, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + MONO_EXIT_GC_SAFE; + ep_rt_utf16_string_free (path_utf16); -static -gboolean -eventpipe_sample_profiler_walk_managed_stack_for_thread_func ( - MonoStackFrameInfo *frame, - MonoContext *ctx, - void *data); + return res; +} -static -void -profiler_eventpipe_runtime_initialized (MonoProfiler *prof); +bool +ep_rt_mono_file_close (ep_rt_file_handle_t handle) +{ + bool res; + MONO_ENTER_GC_SAFE; + res = CloseHandle (handle); + MONO_EXIT_GC_SAFE; + return res; +} static void -profiler_eventpipe_thread_exited ( - MonoProfiler *prof, - uintptr_t tid); - -static -bool -parse_mono_profiler_options (const ep_char8_t *option); +win32_io_interrupt_handler (void *ignored) +{ +} -static bool -get_module_event_data ( - MonoImage *image, - ModuleEventData *module_data); +ep_rt_mono_file_write ( + ep_rt_file_handle_t handle, + const uint8_t *buffer, + uint32_t numbytes, + uint32_t *byteswritten) +{ + MONO_REQ_GC_UNSAFE_MODE; -static -bool -get_assembly_event_data ( - MonoAssembly *assembly, - AssemblyEventData *assembly_data); + bool res; + MonoThreadInfo *info = mono_thread_info_current (); + gboolean alerted = FALSE; -static -uint32_t -get_type_start_id (MonoType *type); + if (info) { + mono_thread_info_install_interrupt (win32_io_interrupt_handler, NULL, &alerted); + if (alerted) { + return false; + } + mono_win32_enter_blocking_io_call (info, handle); + } -static -gboolean -get_exception_ip_func ( - MonoStackFrameInfo *frame, - MonoContext *ctx, - void *data); + MONO_ENTER_GC_SAFE; + if (info && mono_thread_info_is_interrupt_state (info)) { + res = false; + } else { + res = WriteFile (handle, buffer, numbytes, (PDWORD)byteswritten, NULL) ? true : false; + } + MONO_EXIT_GC_SAFE; -static -void -runtime_profiler_jit_begin ( - MonoProfiler *prof, - MonoMethod *method); + if (info) { + mono_win32_leave_blocking_io_call (info, handle); + mono_thread_info_uninstall_interrupt (&alerted); + } -static -void -runtime_profiler_jit_failed ( - MonoProfiler *prof, - MonoMethod *method); + return res; +} -static -void -runtime_profiler_jit_done ( - MonoProfiler *prof, - MonoMethod *method, - MonoJitInfo *ji); +#else -static -void -runtime_profiler_image_loaded ( - MonoProfiler *prof, - MonoImage *image); +#include +#include -static -void -runtime_profiler_image_unloaded ( - MonoProfiler *prof, - MonoImage *image); +ep_rt_file_handle_t +ep_rt_mono_file_open_write (const ep_char8_t *path) +{ + int fd; + mode_t perms = 0666; -static -void -runtime_profiler_assembly_loaded ( - MonoProfiler *prof, - MonoAssembly *assembly); + if (!path) + return INVALID_HANDLE_VALUE; -static -void -runtime_profiler_assembly_unloaded ( - MonoProfiler *prof, - MonoAssembly *assembly); + MONO_ENTER_GC_SAFE; + fd = creat (path, perms); + MONO_EXIT_GC_SAFE; -static -void -runtime_profiler_thread_started ( - MonoProfiler *prof, - uintptr_t tid); + if (fd == -1) + return INVALID_HANDLE_VALUE; -static -void -runtime_profiler_thread_stopped ( - MonoProfiler *prof, - uintptr_t tid); + return (ep_rt_file_handle_t)(ptrdiff_t)fd; +} -static -void -runtime_profiler_class_loading ( - MonoProfiler *prof, - MonoClass *klass); +bool +ep_rt_mono_file_close (ep_rt_file_handle_t handle) +{ + int fd = (int)(ptrdiff_t)handle; -static -void -runtime_profiler_class_failed ( - MonoProfiler *prof, - MonoClass *klass); + MONO_ENTER_GC_SAFE; + close (fd); + MONO_EXIT_GC_SAFE; -static -void -runtime_profiler_class_loaded ( - MonoProfiler *prof, - MonoClass *klass); + return true; +} -static -void -runtime_profiler_exception_throw ( - MonoProfiler *prof, - MonoObject *exception); +bool +ep_rt_mono_file_write ( + ep_rt_file_handle_t handle, + const uint8_t *buffer, + uint32_t numbytes, + uint32_t *byteswritten) +{ + MONO_REQ_GC_UNSAFE_MODE; -static -void -runtime_profiler_exception_clause ( - MonoProfiler *prof, - MonoMethod *method, - uint32_t clause_num, - MonoExceptionEnum clause_type, - MonoObject *exc); + int fd = (int)(ptrdiff_t)handle; + uint32_t ret; + MonoThreadInfo *info = mono_thread_info_current (); -static -void -runtime_profiler_monitor_contention ( - MonoProfiler *prof, - MonoObject *obj); + if (byteswritten != NULL) + *byteswritten = 0; -static -void -runtime_profiler_monitor_acquired ( - MonoProfiler *prof, - MonoObject *obj); + do { + MONO_ENTER_GC_SAFE; + ret = write (fd, buffer, numbytes); + MONO_EXIT_GC_SAFE; + } while (ret == -1 && errno == EINTR && + !mono_thread_info_is_interrupt_state (info)); -static -void -runtime_profiler_monitor_failed ( - MonoProfiler *prof, - MonoObject *obj); + if (ret == -1) { + if (errno == EINTR) + ret = 0; + else + return false; + } -static -void -runtime_profiler_jit_code_buffer ( - MonoProfiler *prof, - const mono_byte *buffer, - uint64_t size, - MonoProfilerCodeBufferType type, - const void *data); + if (byteswritten != NULL) + *byteswritten = ret; -static -void -mono_profiler_get_class_data ( - MonoClass *klass, - uint64_t *class_id, - uint64_t *module_id, - ep_char8_t **class_name, - uint32_t *class_generic_type_count, - uint8_t **class_generic_types); + return true; +} -static -void -mono_profiler_fire_event_enter (void); +#endif // HOST_WIN32 -static -void -mono_profiler_fire_event_exit (void); +EventPipeThread * +ep_rt_mono_thread_get_or_create (void) +{ + EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (_ep_rt_mono_thread_holder_tls_id); + if (!thread_holder) { + thread_holder = thread_holder_alloc_func (); + mono_native_tls_set_value (_ep_rt_mono_thread_holder_tls_id, thread_holder); + } + return ep_thread_holder_get_thread (thread_holder); +} -static -void -mono_profiler_gc_in_progress_start (void); +EventPipeMonoThreadData * +ep_rt_mono_thread_data_get_or_create (void) +{ + EventPipeMonoThreadData *thread_data = (EventPipeMonoThreadData *)mono_native_tls_get_value (_thread_data_tls_id); + if (!thread_data) { + thread_data = ep_rt_object_alloc (EventPipeMonoThreadData); + mono_native_tls_set_value (_thread_data_tls_id, thread_data); + } + return thread_data; +} -static -void -mono_profiler_gc_in_progress_stop (void); - -static -MonoProfilerMemBlock * -mono_profiler_mem_block_alloc (uint32_t req_size); - -static -uint8_t * -mono_profiler_mem_alloc (uint32_t req_size); - -static -void -mono_profiler_mem_block_free_all (void); - -static -void -mono_profiler_mem_block_free_all_but_current (void); - -static -void -mono_profiler_trigger_heap_collect (MonoProfiler *prof); - -static -void -mono_profiler_fire_gc_event_root_register ( - uint8_t *data, - uint32_t payload_size); - -static -void -mono_profiler_fire_buffered_gc_event_root_register ( - MonoProfiler *prof, - const mono_byte *start, - uintptr_t size, - MonoGCRootSource source, - const void * key, - const char * name); - -static -void -mono_profiler_fire_gc_event_root_unregister ( - uint8_t *data, - uint32_t payload_size); - -static -void -mono_profiler_fire_buffered_gc_event_root_unregister ( - MonoProfiler *prof, - const mono_byte *start); - -static -void -mono_profiler_fire_gc_event ( - uint8_t *data, - uint32_t payload_size); - -static -void -mono_profiler_fire_buffered_gc_event ( - uint8_t gc_event_type, - uint32_t generation); - -static -void -mono_profiler_fire_gc_event_resize ( - uint8_t *data, - uint32_t payload_size); - -static -void -mono_profiler_fire_buffered_gc_event_resize ( - MonoProfiler *prof, - uintptr_t size); - -static -void -mono_profiler_fire_gc_event_moves ( - uint8_t *data, - uint32_t payload_size); - -static -void -mono_profiler_fire_buffered_gc_event_moves ( - MonoProfiler *prof, - MonoObject *const* objects, - uint64_t count); - -static -void -mono_profiler_fire_gc_event_roots ( - uint8_t *data, - uint32_t payload_size); - -static -void -mono_profiler_fire_buffered_gc_event_roots ( - MonoProfiler *prof, - uint64_t count, - const mono_byte *const * addresses, - MonoObject *const * objects); - -static -void -mono_profiler_fire_gc_event_heap_dump_object_reference ( - uint8_t *data, - uint32_t payload_size, - GHashTable *cache); - -static -int -mono_profiler_fire_buffered_gc_event_heap_dump_object_reference ( - MonoObject *obj, - MonoClass *klass, - uintptr_t size, - uintptr_t num, - MonoObject **refs, - uintptr_t *offsets, - void *data); - -static -void -mono_profiler_fire_buffered_gc_events ( - MonoProfilerMemBlock *block, - GHashTable *cache); - -static -void -mono_profiler_fire_buffered_gc_events_in_alloc_order (GHashTable *cache); - -static -void -mono_profiler_fire_cached_gc_events (GHashTable *cache); - -static -void -mono_profiler_app_domain_loading ( - MonoProfiler *prof, - MonoDomain *domain); - -static -void -mono_profiler_app_domain_loaded ( - MonoProfiler *prof, - MonoDomain *domain); - -static -void -mono_profiler_app_domain_unloading ( - MonoProfiler *prof, - MonoDomain *domain); - -static -void -mono_profiler_app_domain_unloaded ( - MonoProfiler *prof, - MonoDomain *domain); - -static -void -mono_profiler_app_domain_name ( - MonoProfiler *prof, - MonoDomain *domain, - const char *name); - -static -void -mono_profiler_get_generic_types ( - MonoGenericInst *generic_instance, - uint32_t *generic_type_count, - uint8_t **generic_types); - -static -void -mono_profiler_get_jit_data ( - MonoMethod *method, - uint64_t *method_id, - uint64_t *module_id, - uint32_t *method_token, - uint32_t *method_generic_type_count, - uint8_t **method_generic_types); - -static -void -mono_profiler_jit_begin ( - MonoProfiler *prof, - MonoMethod *method); - -static -void -mono_profiler_jit_failed ( - MonoProfiler *prof, - MonoMethod *method); - -static -void -mono_profiler_jit_done ( - MonoProfiler *prof, - MonoMethod *method, - MonoJitInfo *ji); - -static -void -mono_profiler_jit_chunk_created ( - MonoProfiler *prof, - const mono_byte *chunk, - uintptr_t size); - -static -void -mono_profiler_jit_chunk_destroyed ( - MonoProfiler *prof, - const mono_byte *chunk); - -static -void -mono_profiler_jit_code_buffer ( - MonoProfiler *prof, - const mono_byte *buffer, - uint64_t size, - MonoProfilerCodeBufferType type, - const void *data); - -static -void -mono_profiler_class_loading ( - MonoProfiler *prof, - MonoClass *klass); - -static -void -mono_profiler_class_failed ( - MonoProfiler *prof, - MonoClass *klass); - -static -void -mono_profiler_class_loaded ( - MonoProfiler *prof, - MonoClass *klass); - -static -void -mono_profiler_vtable_loading ( - MonoProfiler *prof, - MonoVTable *vtable); - -static -void -mono_profiler_vtable_failed ( - MonoProfiler *prof, - MonoVTable *vtable); - -static -void -mono_profiler_vtable_loaded ( - MonoProfiler *prof, - MonoVTable *vtable); - -static -void -mono_profiler_module_loading ( - MonoProfiler *prof, - MonoImage *image); - -static -void -mono_profiler_module_failed ( - MonoProfiler *prof, - MonoImage *image); - -static -void -mono_profiler_module_loaded ( - MonoProfiler *prof, - MonoImage *image); - -static -void -mono_profiler_module_unloading ( - MonoProfiler *prof, - MonoImage *image); - -static -void -mono_profiler_module_unloaded ( - MonoProfiler *prof, - MonoImage *image); - -static -void -mono_profiler_assembly_loading ( - MonoProfiler *prof, - MonoAssembly *assembly); - -static -void -mono_profiler_assembly_loaded ( - MonoProfiler *prof, - MonoAssembly *assembly); - -static -void -mono_profiler_assembly_unloading ( - MonoProfiler *prof, - MonoAssembly *assembly); - -static -void -mono_profiler_assembly_unloaded ( - MonoProfiler *prof, - MonoAssembly *assembly); - -static -void -mono_profiler_method_enter ( - MonoProfiler *prof, - MonoMethod *method, - MonoProfilerCallContext *context); - -static -void -mono_profiler_method_leave ( - MonoProfiler *prof, - MonoMethod *method, - MonoProfilerCallContext *context); - -static -void -mono_profiler_method_tail_call ( - MonoProfiler *prof, - MonoMethod *method, - MonoMethod *target_method); - -static -void -mono_profiler_method_exception_leave ( - MonoProfiler *prof, - MonoMethod *method, - MonoObject *exc); - -static -void -mono_profiler_method_free ( - MonoProfiler *prof, - MonoMethod *method); - -static -void -mono_profiler_method_begin_invoke ( - MonoProfiler *prof, - MonoMethod *method); - -static -void -mono_profiler_method_end_invoke ( - MonoProfiler *prof, - MonoMethod *method); - -static -MonoProfilerCallInstrumentationFlags -mono_profiler_method_instrumentation ( - MonoProfiler *prof, - MonoMethod *method); - -static -void -mono_profiler_exception_throw ( - MonoProfiler *prof, - MonoObject *exc); - -static -void -mono_profiler_exception_clause ( - MonoProfiler *prof, - MonoMethod *method, - uint32_t clause_num, - MonoExceptionEnum clause_type, - MonoObject *exc); - -static -void -mono_profiler_gc_event ( - MonoProfiler *prof, - MonoProfilerGCEvent gc_event, - uint32_t generation, - mono_bool serial); - -static -void -mono_profiler_gc_allocation ( - MonoProfiler *prof, - MonoObject *object); - -static -void -mono_profiler_gc_handle_created ( - MonoProfiler *prof, - uint32_t handle, - MonoGCHandleType type, - MonoObject * object); - -static -void -mono_profiler_gc_handle_deleted ( - MonoProfiler *prof, - uint32_t handle, - MonoGCHandleType type); - -static -void -mono_profiler_gc_finalizing (MonoProfiler *prof); - -static -void -mono_profiler_gc_finalized (MonoProfiler *prof); - -static -void -mono_profiler_gc_root_register ( - MonoProfiler *prof, - const mono_byte *start, - uintptr_t size, - MonoGCRootSource source, - const void * key, - const char * name); - -static -void -mono_profiler_gc_root_unregister ( - MonoProfiler *prof, - const mono_byte *start); - -static -void -mono_profiler_monitor_contention ( - MonoProfiler *prof, - MonoObject *object); - -static -void -mono_profiler_monitor_failed ( - MonoProfiler *prof, - MonoObject *object); - -static -void -mono_profiler_monitor_acquired ( - MonoProfiler *prof, - MonoObject *object); - -static -void -mono_profiler_thread_started ( - MonoProfiler *prof, - uintptr_t tid); - -static -void -mono_profiler_thread_stopping ( - MonoProfiler *prof, - uintptr_t tid); - -static -void -mono_profiler_thread_stopped ( - MonoProfiler *prof, - uintptr_t tid); - -static -void -mono_profiler_thread_exited ( - MonoProfiler *prof, - uintptr_t tid); - -static -void -mono_profiler_thread_name ( - MonoProfiler *prof, - uintptr_t tid, - const char *name); - -static -const EventFilterDescriptor * -mono_profiler_add_provider_param (const EventFilterDescriptor *key); - -static -bool -mono_profiler_remove_provider_param (const EventFilterDescriptor *key); - -static -void -mono_profiler_free_provider_params (void); - -static -bool -mono_profiler_provider_params_get_value ( - const EventFilterDescriptor *param, - const ep_char8_t *key, - const ep_char8_t **value); - -static -bool -mono_profiler_provider_param_contains_heap_collect_ondemand (const EventFilterDescriptor *param); - -static -void -mono_profiler_push_gc_heap_collect_param_request_value (const EventFilterDescriptor *param); - -static -void -mono_profiler_pop_gc_heap_collect_param_request_value (void); - -static -void -mono_profiler_pop_gc_heap_collect_param_request_value (void); - -static -const ep_char8_t * -mono_profiler_get_gc_heap_collect_param_request_value (void); - -static -void -mono_profiler_free_gc_heap_collect_param_requests (void); - -static -void -mono_profiler_ep_provider_callback ( - const uint8_t *source_id, - unsigned long is_enabled, - uint8_t level, - uint64_t match_any_keywords, - uint64_t match_all_keywords, - EventFilterDescriptor *filter_data, - void *callback_data); - -/* - * Forward declares of all private functions (accessed using extern in ep-rt-mono.h). - */ - -void -ep_rt_mono_component_init (void); - -void -ep_rt_mono_init (void); - -void -ep_rt_mono_init_finish (void); - -void -ep_rt_mono_fini (void); - -bool -ep_rt_mono_rand_try_get_bytes ( - uint8_t *buffer, - size_t buffer_size); - -ep_rt_file_handle_t -ep_rt_mono_file_open_write(const ep_char8_t *path); - -bool -ep_rt_mono_file_close (ep_rt_file_handle_t handle); - -bool -ep_rt_mono_file_write ( - ep_rt_file_handle_t handle, - const uint8_t *buffer, - uint32_t numbytes, - uint32_t *byteswritten); - -EventPipeThread * -ep_rt_mono_thread_get_or_create (void); - -void * -ep_rt_mono_thread_attach (bool background_thread); - -void * -ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type); - -void -ep_rt_mono_thread_detach (void); - -void -ep_rt_mono_thread_exited (void); - -int64_t -ep_rt_mono_perf_counter_query (void); - -int64_t -ep_rt_mono_perf_frequency_query (void); - -void -ep_rt_mono_system_time_get (EventPipeSystemTime *system_time); - -int64_t -ep_rt_mono_system_timestamp_get (void); - -void -ep_rt_mono_os_environment_get_utf16 (dn_vector_ptr_t *os_env); - -void -ep_rt_mono_init_providers_and_events (void); - -void -ep_rt_mono_provider_config_init (EventPipeProviderConfiguration *provider_config); - -bool -ep_rt_mono_providers_validate_all_disabled (void); - -void -ep_rt_mono_fini_providers_and_events (void); - -bool -ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( - ep_rt_thread_handle_t sampling_thread, - EventPipeEvent *sampling_event); - -bool -ep_rt_mono_walk_managed_stack_for_thread ( - ep_rt_thread_handle_t thread, - EventPipeStackContents *stack_contents); - -bool -ep_rt_mono_method_get_simple_assembly_name ( - ep_rt_method_desc_t *method, - ep_char8_t *name, - size_t name_len); - -bool -ep_rt_mono_method_get_full_name ( - ep_rt_method_desc_t *method, - ep_char8_t *name, - size_t name_len); - -void -ep_rt_mono_execute_rundown (dn_vector_ptr_t *execution_checkpoints); - -static -inline -bool -profiler_callback_is_enabled (uint64_t enabled_keywords, uint64_t keyword) -{ - return (enabled_keywords & keyword) == keyword; -} - -static -inline -uint16_t -clr_instance_get_id (void) -{ - // Mono runtime id. - return 9; -} - -static -EventPipeThreadData * -eventpipe_thread_data_get_or_create (void) -{ - EventPipeThreadData *thread_data = (EventPipeThreadData *)mono_native_tls_get_value (_ep_rt_mono_thread_data_tls_id); - if (!thread_data) { - thread_data = ep_rt_object_alloc (EventPipeThreadData); - mono_native_tls_set_value (_ep_rt_mono_thread_data_tls_id, thread_data); - } - return thread_data; -} - -static -void -eventpipe_thread_data_free (EventPipeThreadData *thread_data) -{ - ep_return_void_if_nok (thread_data != NULL); - ep_rt_object_free (thread_data); -} - -static -bool -fire_method_rundown_events_func ( - const uint64_t method_id, - const uint64_t module_id, - const uint64_t method_start_address, - const uint32_t method_size, - const uint32_t method_token, - const uint32_t method_flags, - const ep_char8_t *method_namespace, - const ep_char8_t *method_name, - const ep_char8_t *method_signature, - const uint16_t count_of_map_entries, - const uint32_t *il_offsets, - const uint32_t *native_offsets, - bool aot_method, - bool verbose, - void *user_data) -{ - FireEtwMethodDCEndILToNativeMap ( - method_id, - 0, - 0, - count_of_map_entries, - il_offsets, - native_offsets, - clr_instance_get_id (), - NULL, - NULL); - - if (verbose) { - FireEtwMethodDCEndVerbose_V1 ( - method_id, - module_id, - method_start_address, - method_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, - method_namespace, - method_name, - method_signature, - clr_instance_get_id (), - NULL, - NULL); - - if (aot_method) - FireEtwMethodDCEndVerbose_V1 ( - method_id, - module_id, - method_start_address, - method_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, - method_namespace, - method_name, - method_signature, - clr_instance_get_id (), - NULL, - NULL); - } else { - FireEtwMethodDCEnd_V1 ( - method_id, - module_id, - method_start_address, - method_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, - clr_instance_get_id (), - NULL, - NULL); - - if (aot_method) - FireEtwMethodDCEnd_V1 ( - method_id, - module_id, - method_start_address, - method_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, - clr_instance_get_id (), - NULL, - NULL); - } - - return true; -} - -static -bool -fire_assembly_rundown_events_func ( - const uint64_t domain_id, - const uint64_t assembly_id, - const uint32_t assembly_flags, - const uint32_t binding_id, - const ep_char8_t *assembly_name, - const uint64_t module_id, - const uint32_t module_flags, - const uint32_t reserved_flags, - const ep_char8_t *module_il_path, - const ep_char8_t *module_native_path, - const uint8_t *managed_pdb_signature, - const uint32_t managed_pdb_age, - const ep_char8_t *managed_pdb_build_path, - const uint8_t *native_pdb_signature, - const uint32_t native_pdb_age, - const ep_char8_t *native_pdb_build_path, - void *user_data) -{ - FireEtwModuleDCEnd_V2 ( - module_id, - assembly_id, - module_flags, - reserved_flags, - module_il_path, - module_native_path, - clr_instance_get_id (), - managed_pdb_signature, - managed_pdb_age, - managed_pdb_build_path, - native_pdb_signature, - native_pdb_age, - native_pdb_build_path, - NULL, - NULL); - - FireEtwDomainModuleDCEnd_V1 ( - module_id, - assembly_id, - domain_id, - module_flags, - reserved_flags, - module_il_path, - module_native_path, - clr_instance_get_id (), - NULL, - NULL); - - FireEtwAssemblyDCEnd_V1 ( - assembly_id, - domain_id, - binding_id, - assembly_flags, - assembly_name, - clr_instance_get_id (), - NULL, - NULL); - - return true; -} - -static -bool -fire_domain_rundown_events_func ( - const uint64_t domain_id, - const uint32_t domain_flags, - const ep_char8_t *domain_name, - const uint32_t domain_index, - void *user_data) -{ - return FireEtwAppDomainDCEnd_V1 ( - domain_id, - domain_flags, - domain_name, - domain_index, - clr_instance_get_id (), - NULL, - NULL); -} - -static -void -eventpipe_fire_method_events ( - MonoJitInfo *ji, - MonoMethod *method, - EventPipeFireMethodEventsData *events_data) -{ - EP_ASSERT (ji != NULL); - EP_ASSERT (events_data->domain != NULL); - EP_ASSERT (events_data->method_events_func != NULL); - - uint64_t method_id = 0; - uint64_t module_id = 0; - uint64_t method_code_start = (uint64_t)ji->code_start; - uint32_t method_code_size = (uint32_t)ji->code_size; - uint32_t method_token = 0; - uint32_t method_flags = 0; - uint8_t kind = MONO_CLASS_DEF; - char *method_namespace = NULL; - const char *method_name = NULL; - char *method_signature = NULL; - bool verbose = (MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.Level >= (uint8_t)EP_EVENT_LEVEL_VERBOSE); - - //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. - - if (method) { - method_id = (uint64_t)method; - method_token = method->token; - - if (mono_jit_info_get_generic_sharing_context (ji)) - method_flags |= METHOD_FLAGS_SHARED_GENERIC_METHOD; - - if (method->dynamic) - method_flags |= METHOD_FLAGS_DYNAMIC_METHOD; - - if (!ji->from_aot && !ji->from_llvm) { - method_flags |= METHOD_FLAGS_JITTED_METHOD; - if (method->wrapper_type != MONO_WRAPPER_NONE) - method_flags |= METHOD_FLAGS_JITTED_HELPER_METHOD; - } - - if (method->is_generic || method->is_inflated) - method_flags |= METHOD_FLAGS_GENERIC_METHOD; - - if (method->klass) { - module_id = (uint64_t)m_class_get_image (method->klass); - kind = m_class_get_class_kind (method->klass); - if (kind == MONO_CLASS_GTD || kind == MONO_CLASS_GINST) - method_flags |= METHOD_FLAGS_GENERIC_METHOD; - } - - if (verbose) { - method_name = method->name; - method_signature = mono_signature_full_name (mono_method_signature_internal (method)); - if (method->klass) - method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); - } - - } - - uint32_t offset_entries = 0; - uint32_t *il_offsets = NULL; - uint32_t *native_offsets = NULL; - - MonoDebugMethodJitInfo *debug_info = method ? mono_debug_find_method (method, events_data->domain) : NULL; - if (debug_info) { - offset_entries = debug_info->num_line_numbers; - if (offset_entries != 0) { - size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); - if (!events_data->buffer || needed_size > events_data->buffer_size) { - g_free (events_data->buffer); - events_data->buffer_size = (size_t)(needed_size * 1.5); - events_data->buffer = g_new (uint8_t, events_data->buffer_size); - } - - if (events_data->buffer) { - il_offsets = (uint32_t*)events_data->buffer; - native_offsets = il_offsets + offset_entries; - - uint8_t *il_offsets_ptr = (uint8_t *)il_offsets; - uint8_t *native_offsets_ptr = (uint8_t *)native_offsets; - for (uint32_t offset_count = 0; offset_count < offset_entries; ++offset_count) { - ep_write_buffer_uint32_t (&il_offsets_ptr, debug_info->line_numbers [offset_count].il_offset); - ep_write_buffer_uint32_t (&native_offsets_ptr, debug_info->line_numbers [offset_count].native_offset); - } - } - } - - mono_debug_free_method_jit_info (debug_info); - } - - if (events_data->buffer && !il_offsets && !native_offsets) { - // No IL offset -> Native offset mapping available. Put all code on IL offset 0. - EP_ASSERT (events_data->buffer_size >= sizeof (uint32_t) * 2); - offset_entries = 1; - il_offsets = (uint32_t*)events_data->buffer; - native_offsets = il_offsets + offset_entries; - il_offsets [0] = 0; - native_offsets [0] = ep_rt_val_uint32_t ((uint32_t)ji->code_size); - } - - events_data->method_events_func ( - method_id, - module_id, - method_code_start, - method_code_size, - method_token, - method_flags, - (ep_char8_t *)method_namespace, - (ep_char8_t *)method_name, - (ep_char8_t *)method_signature, - GUINT32_TO_UINT16 (offset_entries), - il_offsets, - native_offsets, - (ji->from_aot || ji->from_llvm), - verbose, - NULL); - - g_free (method_namespace); - g_free (method_signature); -} - -static -inline -bool -include_method (MonoMethod *method) -{ - if (!method) { - return false; - } else if (!m_method_is_wrapper (method)) { - return true; - } else { - WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); - return (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) ? true : false; - } -} - -static -void -eventpipe_fire_method_events_func ( - MonoJitInfo *ji, - void *user_data) -{ - EventPipeFireMethodEventsData *events_data = (EventPipeFireMethodEventsData *)user_data; - EP_ASSERT (events_data != NULL); - - if (ji && !ji->is_trampoline && !ji->async) { - MonoMethod *method = jinfo_get_method (ji); - if (include_method (method)) - eventpipe_fire_method_events (ji, method, events_data); - } -} - -static -void -eventpipe_fire_assembly_events ( - MonoDomain *domain, - MonoAssembly *assembly, - ep_rt_mono_fire_assembly_rundown_events_func assembly_events_func) -{ - EP_ASSERT (domain != NULL); - EP_ASSERT (assembly != NULL); - EP_ASSERT (assembly_events_func != NULL); - - // Native methods are part of JIT table and already emitted. - // TODO: FireEtwMethodDCEndVerbose_V1_or_V2 for all native methods in module as well? - - uint32_t binding_id = 0; - - ModuleEventData module_data; - memset (&module_data, 0, sizeof (module_data)); - - get_module_event_data (assembly->image, &module_data); - - uint32_t assembly_flags = 0; - if (assembly->dynamic) - assembly_flags |= ASSEMBLY_FLAGS_DYNAMIC_ASSEMBLY; - - if (assembly->image && assembly->image->aot_module) { - assembly_flags |= ASSEMBLY_FLAGS_NATIVE_ASSEMBLY; - } - - char *assembly_name = mono_stringify_assembly_name (&assembly->aname); - - assembly_events_func ( - module_data.domain_id, - module_data.assembly_id, - assembly_flags, - binding_id, - (const ep_char8_t*)assembly_name, - module_data.module_id, - module_data.module_flags, - module_data.reserved_flags, - (const ep_char8_t *)module_data.module_il_path, - (const ep_char8_t *)module_data.module_native_path, - module_data.module_il_pdb_signature, - module_data.module_il_pdb_age, - (const ep_char8_t *)module_data.module_il_pdb_path, - module_data.module_native_pdb_signature, - module_data.module_native_pdb_age, - (const ep_char8_t *)module_data.module_native_pdb_path, - NULL); - - g_free (assembly_name); -} - -static -gboolean -eventpipe_execute_rundown ( - ep_rt_mono_fire_domain_rundown_events_func domain_events_func, - ep_rt_mono_fire_assembly_rundown_events_func assembly_events_func, - ep_rt_mono_fire_method_rundown_events_func method_events_func) -{ - EP_ASSERT (domain_events_func != NULL); - EP_ASSERT (assembly_events_func != NULL); - EP_ASSERT (method_events_func != NULL); - - // Under netcore we only have root domain. - MonoDomain *root_domain = mono_get_root_domain (); - if (root_domain) { - uint64_t domain_id = (uint64_t)root_domain; - - // Emit all functions in use (JIT, AOT and Interpreter). - EventPipeFireMethodEventsData events_data; - events_data.domain = root_domain; - events_data.buffer_size = 1024 * sizeof(uint32_t); - events_data.buffer = g_new (uint8_t, events_data.buffer_size); - events_data.method_events_func = method_events_func; - - // All called JIT/AOT methods should be included in jit info table. - mono_jit_info_table_foreach_internal (eventpipe_fire_method_events_func, &events_data); - - // All called interpreted methods should be included in interpreter jit info table. - if (mono_get_runtime_callbacks ()->is_interpreter_enabled()) - mono_get_runtime_callbacks ()->interp_jit_info_foreach (eventpipe_fire_method_events_func, &events_data); - - // Phantom methods injected in callstacks representing runtime functions. - if (_ep_rt_mono_runtime_helper_compile_method_jitinfo && _ep_rt_mono_runtime_helper_compile_method) - eventpipe_fire_method_events (_ep_rt_mono_runtime_helper_compile_method_jitinfo, _ep_rt_mono_runtime_helper_compile_method, &events_data); - if (_ep_rt_mono_monitor_enter_method_jitinfo && _ep_rt_mono_monitor_enter_method) - eventpipe_fire_method_events (_ep_rt_mono_monitor_enter_method_jitinfo, _ep_rt_mono_monitor_enter_method, &events_data); - if (_ep_rt_mono_monitor_enter_v4_method_jitinfo && _ep_rt_mono_monitor_enter_v4_method) - eventpipe_fire_method_events (_ep_rt_mono_monitor_enter_v4_method_jitinfo, _ep_rt_mono_monitor_enter_v4_method, &events_data); - - g_free (events_data.buffer); - - // Iterate all assemblies in domain. - GPtrArray *assemblies = mono_alc_get_all_loaded_assemblies (); - if (assemblies) { - for (uint32_t i = 0; i < assemblies->len; ++i) { - MonoAssembly *assembly = (MonoAssembly *)g_ptr_array_index (assemblies, i); - if (assembly) - eventpipe_fire_assembly_events (root_domain, assembly, assembly_events_func); - } - g_ptr_array_free (assemblies, TRUE); - } - - uint32_t domain_flags = DOMAIN_FLAGS_DEFAULT_DOMAIN | DOMAIN_FLAGS_EXECUTABLE_DOMAIN; - const char *domain_name = root_domain->friendly_name ? root_domain->friendly_name : ""; - uint32_t domain_index = 1; - - domain_events_func ( - domain_id, - domain_flags, - (const ep_char8_t *)domain_name, - domain_index, - NULL); - } - - return TRUE; -} - -inline -static -bool -in_safe_point_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) -{ - EP_ASSERT (stack_content != NULL); - - // If top of stack is a managed->native icall wrapper for one of the below subtypes, we are at a safe point frame. - if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && - (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_state_poll || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_safe_region_unbalanced || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_safe_region_unbalanced || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_enter_gc_unsafe_region_unbalanced || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_unsafe_region_unbalanced)) - return true; - - return false; -} - -inline -static -bool -in_runtime_invoke_frame (EventPipeStackContents *stack_content, WrapperInfo *wrapper) -{ - EP_ASSERT (stack_content != NULL); - - // If top of stack is a managed->native runtime invoke wrapper, we are at a managed frame. - if (wrapper && ep_stack_contents_get_length (stack_content) == 0 && - (wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL || - wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || - wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC || - wrapper->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL)) - return true; - - return false; -} - -inline -static -bool -in_monitor_enter_frame (WrapperInfo *wrapper) -{ - if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && - (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_fast || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_internal)) - return true; - - return false; -} - -inline -static -bool -in_monitor_enter_v4_frame (WrapperInfo *wrapper) -{ - if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER && - (wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_fast || - wrapper->d.icall.jit_icall_id == MONO_JIT_ICALL_mono_monitor_enter_v4_internal)) - return true; - - return false; -} - -static -gboolean -eventpipe_walk_managed_stack_for_thread ( - MonoStackFrameInfo *frame, - MonoContext *ctx, - EventPipeStackWalkData *stack_walk_data) -{ - EP_ASSERT (frame != NULL); - EP_ASSERT (stack_walk_data != NULL); - - switch (frame->type) { - case FRAME_TYPE_DEBUGGER_INVOKE: - case FRAME_TYPE_MANAGED_TO_NATIVE: - case FRAME_TYPE_TRAMPOLINE: - case FRAME_TYPE_INTERP_TO_MANAGED: - case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: - case FRAME_TYPE_INTERP_ENTRY: - stack_walk_data->top_frame = false; - return FALSE; - case FRAME_TYPE_JIT_ENTRY: - // Frame in JIT compiler at top of callstack, add phantom frame representing call into JIT compiler. - // Makes it possible to detect stacks waiting on JIT compiler. - if (_ep_rt_mono_runtime_helper_compile_method && stack_walk_data->top_frame) - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_runtime_helper_compile_method), _ep_rt_mono_runtime_helper_compile_method); - stack_walk_data->top_frame = false; - return FALSE; - case FRAME_TYPE_MANAGED: - case FRAME_TYPE_INTERP: - if (frame->ji) { - stack_walk_data->async_frame |= frame->ji->async; - MonoMethod *method = frame->ji->async ? NULL : frame->actual_method; - if (method && m_method_is_wrapper (method)) { - WrapperInfo *wrapper = mono_marshal_get_wrapper_info (method); - if (in_safe_point_frame (stack_walk_data->stack_contents, wrapper)) { - stack_walk_data->safe_point_frame = true; - }else if (in_runtime_invoke_frame (stack_walk_data->stack_contents, wrapper)) { - stack_walk_data->runtime_invoke_frame = true; - } else if (_ep_rt_mono_monitor_enter_method && in_monitor_enter_frame (wrapper)) { - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_monitor_enter_method), _ep_rt_mono_monitor_enter_method); - } else if (_ep_rt_mono_monitor_enter_v4_method && in_monitor_enter_v4_frame (wrapper)) { - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)_ep_rt_mono_monitor_enter_v4_method), _ep_rt_mono_monitor_enter_v4_method); - } else if (wrapper && wrapper->subtype == WRAPPER_SUBTYPE_PINVOKE) { - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); - } - } else if (method && !m_method_is_wrapper (method)) { - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start + frame->native_offset), method); - } else if (!method && frame->ji->async && !frame->ji->is_trampoline) { - ep_stack_contents_append (stack_walk_data->stack_contents, (uintptr_t)((uint8_t*)frame->ji->code_start), method); - } - } - stack_walk_data->top_frame = false; - return ep_stack_contents_get_length (stack_walk_data->stack_contents) >= EP_MAX_STACK_DEPTH; - default: - EP_UNREACHABLE ("eventpipe_walk_managed_stack_for_thread"); - return FALSE; - } -} - -static -gboolean -eventpipe_walk_managed_stack_for_thread_func ( - MonoStackFrameInfo *frame, - MonoContext *ctx, - void *data) -{ - return eventpipe_walk_managed_stack_for_thread (frame, ctx, (EventPipeStackWalkData *)data); -} - -static -gboolean -eventpipe_sample_profiler_walk_managed_stack_for_thread_func ( - MonoStackFrameInfo *frame, - MonoContext *ctx, - void *data) -{ - EP_ASSERT (frame != NULL); - EP_ASSERT (data != NULL); - - EventPipeSampleProfileStackWalkData *sample_data = (EventPipeSampleProfileStackWalkData *)data; - - if (sample_data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR) { - switch (frame->type) { - case FRAME_TYPE_MANAGED: - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; - break; - case FRAME_TYPE_MANAGED_TO_NATIVE: - case FRAME_TYPE_TRAMPOLINE: - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; - break; - case FRAME_TYPE_JIT_ENTRY: - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; - break; - case FRAME_TYPE_INTERP: - sample_data->payload_data = frame->managed ? EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED : EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; - break; - case FRAME_TYPE_INTERP_TO_MANAGED: - case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX: - break; - default: - sample_data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; - } - } - - return eventpipe_walk_managed_stack_for_thread (frame, ctx, &sample_data->stack_walk_data); -} - -static -void -profiler_eventpipe_runtime_initialized (MonoProfiler *prof) -{ - _ep_rt_mono_profiler_gc_can_collect_heap = true; -} - -static -void -profiler_eventpipe_thread_exited ( - MonoProfiler *prof, - uintptr_t tid) -{ - ep_rt_mono_thread_exited (); -} - -static -bool -parse_mono_profiler_options (const ep_char8_t *option) -{ - do { - if (!*option) - return false; - - if (!strncmp (option, "alloc", 5)) { - mono_profiler_enable_allocations (); - option += 5; - } else if (!strncmp (option, "exception", 9)) { - mono_profiler_enable_clauses (); - option += 9; - /*} else if (!strncmp (option, "sample", 6)) { - mono_profiler_enable_sampling (_ep_rt_dotnet_mono_profiler_provider); - option += 6;*/ - } else { - return false; - } - - if (*option == ',') - option++; - } while (*option); - - return true; -} - -void -ep_rt_mono_component_init (void) -{ - _ep_rt_default_profiler = mono_profiler_create (NULL); - _ep_rt_dotnet_runtime_profiler_provider = mono_profiler_create (NULL); - _ep_rt_dotnet_mono_profiler_provider = mono_profiler_create (NULL); - _ep_rt_dotnet_mono_profiler_heap_collect_provider = mono_profiler_create (NULL); - - char *diag_env = g_getenv("MONO_DIAGNOSTICS"); - if (diag_env) { - int diag_argc = 1; - char **diag_argv = g_new (char *, 1); - if (diag_argv) { - diag_argv [0] = NULL; - if (!mono_parse_options_from (diag_env, &diag_argc, &diag_argv)) { - for (int i = 0; i < diag_argc; ++i) { - if (diag_argv [i]) { - if (strncmp (diag_argv [i], "--diagnostic-mono-profiler=", 27) == 0) { - if (!parse_mono_profiler_options (diag_argv [i] + 27)) - mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable option: %s", diag_argv [i]); - } else if (strncmp (diag_argv [i], "--diagnostic-mono-profiler-callspec=", 36) == 0) { - char *errstr = NULL; - if (!mono_callspec_parse (diag_argv [i] + 36, &_ep_rt_dotnet_mono_profiler_provider_callspec, &errstr)) { - mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing '%s': %s", diag_argv [i], errstr); - g_free (errstr); - mono_callspec_cleanup (&_ep_rt_dotnet_mono_profiler_provider_callspec); - } else { - mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_instrumentation); - } - } else if (strncmp (diag_argv [i], "--diagnostic-ports=", 19) == 0) { - char *diag_ports_env = g_getenv("DOTNET_DiagnosticPorts"); - if (diag_ports_env) - mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_DIAGNOSTICS, "DOTNET_DiagnosticPorts environment variable already set, ignoring --diagnostic-ports used in MONO_DIAGNOSTICS environment variable"); - else - g_setenv ("DOTNET_DiagnosticPorts", diag_argv [i] + 19, TRUE); - g_free (diag_ports_env); - - } else { - mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable, unknown option: %s", diag_argv [i]); - } - - g_free (diag_argv [i]); - diag_argv [i] = NULL; - } - } - - g_free (diag_argv); - } else { - mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable"); - } - } - } - g_free (diag_env); -} - -void -ep_rt_mono_init (void) -{ - mono_native_tls_alloc (&_ep_rt_mono_thread_holder_tls_id, NULL); - mono_native_tls_alloc (&_ep_rt_mono_thread_data_tls_id, NULL); - - mono_100ns_ticks (); - mono_rand_open (); - _ep_rt_mono_rand_provider = mono_rand_init (NULL, 0); - - _ep_rt_mono_initialized = TRUE; - - EP_ASSERT (_ep_rt_default_profiler != NULL); - EP_ASSERT (_ep_rt_dotnet_runtime_profiler_provider != NULL); - EP_ASSERT (_ep_rt_dotnet_mono_profiler_provider != NULL); - EP_ASSERT (_ep_rt_dotnet_mono_profiler_heap_collect_provider != NULL); - - ep_rt_spin_lock_alloc (&_ep_rt_mono_profiler_gc_state_lock); - - mono_profiler_set_runtime_initialized_callback (_ep_rt_default_profiler, profiler_eventpipe_runtime_initialized); - mono_profiler_set_thread_stopped_callback (_ep_rt_default_profiler, profiler_eventpipe_thread_exited); - - MonoMethodSignature *method_signature = mono_metadata_signature_alloc (mono_get_corlib (), 1); - if (method_signature) { - method_signature->params[0] = m_class_get_byval_arg (mono_get_object_class()); - method_signature->ret = m_class_get_byval_arg (mono_get_void_class()); - - ERROR_DECL (error); - MonoClass *runtime_helpers = mono_class_from_name_checked (mono_get_corlib (), "System.Runtime.CompilerServices", "RuntimeHelpers", error); - if (is_ok (error) && runtime_helpers) { - MonoMethodBuilder *method_builder = mono_mb_new (runtime_helpers, "CompileMethod", MONO_WRAPPER_RUNTIME_INVOKE); - if (method_builder) { - _ep_rt_mono_runtime_helper_compile_method = mono_mb_create_method (method_builder, method_signature, 1); - mono_mb_free (method_builder); - } - } - mono_error_cleanup (error); - mono_metadata_free_method_signature (method_signature); - - if (_ep_rt_mono_runtime_helper_compile_method) { - _ep_rt_mono_runtime_helper_compile_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); - if (_ep_rt_mono_runtime_helper_compile_method) { - _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_runtime_helper_compile_method); - _ep_rt_mono_runtime_helper_compile_method_jitinfo->code_size = 20; - _ep_rt_mono_runtime_helper_compile_method_jitinfo->d.method = _ep_rt_mono_runtime_helper_compile_method; - } - } - } - - { - ERROR_DECL (error); - MonoMethodDesc *desc = NULL; - MonoClass *monitor = mono_class_from_name_checked (mono_get_corlib (), "System.Threading", "Monitor", error); - if (is_ok (error) && monitor) { - desc = mono_method_desc_new ("Monitor:Enter(object,bool&)", FALSE); - if (desc) { - _ep_rt_mono_monitor_enter_v4_method = mono_method_desc_search_in_class (desc, monitor); - mono_method_desc_free (desc); - - if (_ep_rt_mono_monitor_enter_v4_method) { - _ep_rt_mono_monitor_enter_v4_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); - if (_ep_rt_mono_monitor_enter_v4_method_jitinfo) { - _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_v4_method); - _ep_rt_mono_monitor_enter_v4_method_jitinfo->code_size = 20; - _ep_rt_mono_monitor_enter_v4_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_v4_method; - } - } - } - - desc = mono_method_desc_new ("Monitor:Enter(object)", FALSE); - if (desc) { - _ep_rt_mono_monitor_enter_method = mono_method_desc_search_in_class (desc, monitor); - mono_method_desc_free (desc); - - if (_ep_rt_mono_monitor_enter_method ) { - _ep_rt_mono_monitor_enter_method_jitinfo = (MonoJitInfo *)g_new0 (MonoJitInfo, 1); - if (_ep_rt_mono_monitor_enter_method_jitinfo) { - _ep_rt_mono_monitor_enter_method_jitinfo->code_start = MINI_FTNPTR_TO_ADDR (_ep_rt_mono_monitor_enter_method); - _ep_rt_mono_monitor_enter_method_jitinfo->code_size = 20; - _ep_rt_mono_monitor_enter_method_jitinfo->d.method = _ep_rt_mono_monitor_enter_method; - } - } - } - } - mono_error_cleanup (error); - } -} - -void -ep_rt_mono_init_finish (void) -{ - if (mono_runtime_get_no_exec ()) - return; - - // Managed init of diagnostics classes, like registration of RuntimeEventSource (if available). - ERROR_DECL (error); - - MonoClass *runtime_event_source = mono_class_from_name_checked (mono_get_corlib (), "System.Diagnostics.Tracing", "RuntimeEventSource", error); - if (is_ok (error) && runtime_event_source) { - MonoMethod *init = mono_class_get_method_from_name_checked (runtime_event_source, "Initialize", -1, 0, error); - if (is_ok (error) && init) { - mono_runtime_try_invoke_handle (init, NULL_HANDLE, NULL, error); - } - } - - mono_error_cleanup (error); -} - -void -ep_rt_mono_fini (void) -{ - if (_ep_rt_mono_sampled_thread_callstacks) - g_array_free (_ep_rt_mono_sampled_thread_callstacks, TRUE); - - if (_ep_rt_mono_initialized) - mono_rand_close (_ep_rt_mono_rand_provider); - - g_free (_ep_rt_mono_runtime_helper_compile_method_jitinfo); - _ep_rt_mono_runtime_helper_compile_method_jitinfo = NULL; - - mono_free_method (_ep_rt_mono_runtime_helper_compile_method); - _ep_rt_mono_runtime_helper_compile_method = NULL; - - g_free (_ep_rt_mono_monitor_enter_method_jitinfo); - _ep_rt_mono_monitor_enter_method_jitinfo = NULL; - _ep_rt_mono_monitor_enter_method = NULL; - - g_free (_ep_rt_mono_monitor_enter_v4_method_jitinfo); - _ep_rt_mono_monitor_enter_v4_method_jitinfo = NULL; - _ep_rt_mono_monitor_enter_v4_method = NULL; - - if (_ep_rt_dotnet_mono_profiler_provider_callspec.enabled) { - mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_callspec_cleanup (&_ep_rt_dotnet_mono_profiler_provider_callspec); - } - - mono_profiler_free_gc_heap_collect_param_requests (); - mono_profiler_free_provider_params (); - - ep_rt_spin_lock_free (&_ep_rt_mono_profiler_gc_state_lock); - - _ep_rt_mono_sampled_thread_callstacks = NULL; - _ep_rt_mono_rand_provider = NULL; - _ep_rt_mono_initialized = FALSE; -} - -bool -ep_rt_mono_rand_try_get_bytes ( - uint8_t *buffer, - size_t buffer_size) -{ - EP_ASSERT (_ep_rt_mono_rand_provider != NULL); - - ERROR_DECL (error); - return mono_rand_try_get_bytes (&_ep_rt_mono_rand_provider, (guchar *)buffer, (gssize)buffer_size, error); -} - -char * -ep_rt_mono_get_managed_cmd_line (void) -{ - return mono_runtime_get_managed_cmd_line (); -} - -char * -ep_rt_mono_get_os_cmd_line (void) -{ - MONO_REQ_GC_NEUTRAL_MODE; - - // we only return the native host here since getting the full commandline is complicated and - // it's not super important to have the correct value since it'll only be used during startup - // until we have the managed commandline - char *host_path = minipal_getexepath (); - - // minipal_getexepath doesn't use Mono APIs to allocate strings so - // we can't use g_free (which the callers of this method expect to do) - // so create another copy and return that one - char *res = g_strdup (host_path); - free (host_path); - return res; -} - -#ifdef HOST_WIN32 - -ep_rt_file_handle_t -ep_rt_mono_file_open_write (const ep_char8_t *path) -{ - if (!path) - return INVALID_HANDLE_VALUE; - - ep_char16_t *path_utf16 = ep_rt_utf8_to_utf16le_string (path, -1); - - if (!path_utf16) - return INVALID_HANDLE_VALUE; - - ep_rt_file_handle_t res; - MONO_ENTER_GC_SAFE; - res = (ep_rt_file_handle_t)CreateFileW (path_utf16, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - MONO_EXIT_GC_SAFE; - ep_rt_utf16_string_free (path_utf16); - - return res; -} - -bool -ep_rt_mono_file_close (ep_rt_file_handle_t handle) -{ - bool res; - MONO_ENTER_GC_SAFE; - res = CloseHandle (handle); - MONO_EXIT_GC_SAFE; - return res; -} - -static -void -win32_io_interrupt_handler (void *ignored) -{ -} - -bool -ep_rt_mono_file_write ( - ep_rt_file_handle_t handle, - const uint8_t *buffer, - uint32_t numbytes, - uint32_t *byteswritten) -{ - MONO_REQ_GC_UNSAFE_MODE; - - bool res; - MonoThreadInfo *info = mono_thread_info_current (); - gboolean alerted = FALSE; - - if (info) { - mono_thread_info_install_interrupt (win32_io_interrupt_handler, NULL, &alerted); - if (alerted) { - return false; - } - mono_win32_enter_blocking_io_call (info, handle); - } - - MONO_ENTER_GC_SAFE; - if (info && mono_thread_info_is_interrupt_state (info)) { - res = false; - } else { - res = WriteFile (handle, buffer, numbytes, (PDWORD)byteswritten, NULL) ? true : false; - } - MONO_EXIT_GC_SAFE; - - if (info) { - mono_win32_leave_blocking_io_call (info, handle); - mono_thread_info_uninstall_interrupt (&alerted); - } - - return res; -} - -#else - -#include -#include - -ep_rt_file_handle_t -ep_rt_mono_file_open_write (const ep_char8_t *path) -{ - int fd; - mode_t perms = 0666; - - if (!path) - return INVALID_HANDLE_VALUE; - - MONO_ENTER_GC_SAFE; - fd = creat (path, perms); - MONO_EXIT_GC_SAFE; - - if (fd == -1) - return INVALID_HANDLE_VALUE; - - return (ep_rt_file_handle_t)(ptrdiff_t)fd; -} - -bool -ep_rt_mono_file_close (ep_rt_file_handle_t handle) -{ - int fd = (int)(ptrdiff_t)handle; - - MONO_ENTER_GC_SAFE; - close (fd); - MONO_EXIT_GC_SAFE; - - return true; -} - -bool -ep_rt_mono_file_write ( - ep_rt_file_handle_t handle, - const uint8_t *buffer, - uint32_t numbytes, - uint32_t *byteswritten) -{ - MONO_REQ_GC_UNSAFE_MODE; - - int fd = (int)(ptrdiff_t)handle; - uint32_t ret; - MonoThreadInfo *info = mono_thread_info_current (); - - if (byteswritten != NULL) - *byteswritten = 0; - - do { - MONO_ENTER_GC_SAFE; - ret = write (fd, buffer, numbytes); - MONO_EXIT_GC_SAFE; - } while (ret == -1 && errno == EINTR && - !mono_thread_info_is_interrupt_state (info)); - - if (ret == -1) { - if (errno == EINTR) - ret = 0; - else - return false; - } - - if (byteswritten != NULL) - *byteswritten = ret; - - return true; -} - -#endif // HOST_WIN32 - -EventPipeThread * -ep_rt_mono_thread_get_or_create (void) -{ - EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (_ep_rt_mono_thread_holder_tls_id); - if (!thread_holder) { - thread_holder = thread_holder_alloc_func (); - mono_native_tls_set_value (_ep_rt_mono_thread_holder_tls_id, thread_holder); - } - return ep_thread_holder_get_thread (thread_holder); -} - -void * -ep_rt_mono_thread_attach (bool background_thread) -{ - MonoThread *thread = NULL; - - // NOTE, under netcore, only root domain exists. - if (!mono_thread_current ()) { - thread = mono_thread_internal_attach (mono_get_root_domain ()); - if (background_thread && thread) { - mono_thread_set_state (thread, ThreadState_Background); - mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_SAMPLE); - } - } - - return thread; -} - -void * -ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type) -{ - void *result = ep_rt_mono_thread_attach (background_thread); - if (result && thread_type == EP_THREAD_TYPE_SAMPLING) { - // Increase sampling thread priority, accepting failures. -#ifdef HOST_WIN32 - SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST); -#elif _POSIX_PRIORITY_SCHEDULING - int policy; - int priority; - struct sched_param param; - int schedparam_result = pthread_getschedparam (pthread_self (), &policy, ¶m); - if (schedparam_result == 0) { - // Attempt to switch the thread to real time scheduling. This will not - // necessarily work on all OSs; for example, most Linux systems will give - // us EPERM here unless configured to allow this. - priority = param.sched_priority; - param.sched_priority = sched_get_priority_max (SCHED_RR); - if (param.sched_priority != -1) { - schedparam_result = pthread_setschedparam (pthread_self (), SCHED_RR, ¶m); - if (schedparam_result != 0) { - // Fallback, attempt to increase to max priority using current policy. - param.sched_priority = sched_get_priority_max (policy); - if (param.sched_priority != -1 && param.sched_priority != priority) - pthread_setschedparam (pthread_self (), policy, ¶m); - } - } - } -#endif - } - - return result; -} - -void -ep_rt_mono_thread_detach (void) -{ - MonoThread *current_thread = mono_thread_current (); - if (current_thread) - mono_thread_internal_detach (current_thread); -} - -void -ep_rt_mono_thread_exited (void) -{ - if (_ep_rt_mono_initialized) { - EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (_ep_rt_mono_thread_holder_tls_id); - if (thread_holder) - thread_holder_free_func (thread_holder); - mono_native_tls_set_value (_ep_rt_mono_thread_holder_tls_id, NULL); - - EventPipeThreadData *thread_data = (EventPipeThreadData *)mono_native_tls_get_value (_ep_rt_mono_thread_data_tls_id); - if (thread_data) - eventpipe_thread_data_free (thread_data); - mono_native_tls_set_value (_ep_rt_mono_thread_data_tls_id, NULL); - } -} - -#ifdef HOST_WIN32 -int64_t -ep_rt_mono_perf_counter_query (void) -{ - LARGE_INTEGER value; - if (QueryPerformanceCounter (&value)) - return (int64_t)value.QuadPart; - else - return 0; -} - -int64_t -ep_rt_mono_perf_frequency_query (void) -{ - LARGE_INTEGER value; - if (QueryPerformanceFrequency (&value)) - return (int64_t)value.QuadPart; - else - return 0; -} - -void -ep_rt_mono_system_time_get (EventPipeSystemTime *system_time) -{ - SYSTEMTIME value; - GetSystemTime (&value); - - EP_ASSERT (system_time != NULL); - ep_system_time_set ( - system_time, - value.wYear, - value.wMonth, - value.wDayOfWeek, - value.wDay, - value.wHour, - value.wMinute, - value.wSecond, - value.wMilliseconds); -} - -int64_t -ep_rt_mono_system_timestamp_get (void) -{ - FILETIME value; - GetSystemTimeAsFileTime (&value); - return (int64_t)((((uint64_t)value.dwHighDateTime) << 32) | (uint64_t)value.dwLowDateTime); -} -#else -#include -#include -#include -#include - -#if HAVE_SYS_TIME_H -#include -#endif // HAVE_SYS_TIME_H - -#if HAVE_MACH_ABSOLUTE_TIME -#include -static mono_lazy_init_t _ep_rt_mono_time_base_info_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; -static mach_timebase_info_data_t _ep_rt_mono_time_base_info = {0}; -#endif - -#ifdef HAVE_LOCALTIME_R -#define HAVE_GMTIME_R 1 -#endif - -static const int64_t SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL; -static const int64_t SECS_TO_100NS = 10000000; -static const int64_t SECS_TO_NS = 1000000000; -static const int64_t MSECS_TO_MIS = 1000; - -/* clock_gettime () is found by configure on Apple builds, but its only present from ios 10, macos 10.12, tvos 10 and watchos 3 */ -#if defined (HAVE_CLOCK_MONOTONIC) && (defined(HOST_IOS) || defined(HOST_OSX) || defined(HOST_WATCHOS) || defined(HOST_TVOS)) -#undef HAVE_CLOCK_MONOTONIC -#endif - -#ifndef HAVE_CLOCK_MONOTONIC -static const int64_t MISECS_TO_NS = 1000; -#endif - -static -void -time_base_info_lazy_init (void); - -static -int64_t -system_time_to_int64 ( - time_t sec, - long nsec); - -#if HAVE_MACH_ABSOLUTE_TIME -static -void -time_base_info_lazy_init (void) -{ - kern_return_t result = mach_timebase_info (&_ep_rt_mono_time_base_info); - if (result != KERN_SUCCESS) - memset (&_ep_rt_mono_time_base_info, 0, sizeof (_ep_rt_mono_time_base_info)); -} -#endif - -int64_t -ep_rt_mono_perf_counter_query (void) -{ -#if HAVE_MACH_ABSOLUTE_TIME - return (int64_t)mach_absolute_time (); -#elif HAVE_CLOCK_MONOTONIC - struct timespec ts; - int result = clock_gettime (CLOCK_MONOTONIC, &ts); - if (result == 0) - return ((int64_t)(ts.tv_sec) * (int64_t)(SECS_TO_NS)) + (int64_t)(ts.tv_nsec); -#else - #error "ep_rt_mono_perf_counter_get requires either mach_absolute_time () or clock_gettime (CLOCK_MONOTONIC) to be supported." -#endif - return 0; -} - -int64_t -ep_rt_mono_perf_frequency_query (void) -{ -#if HAVE_MACH_ABSOLUTE_TIME - // (numer / denom) gives you the nanoseconds per tick, so the below code - // computes the number of ticks per second. We explicitly do the multiplication - // first in order to help minimize the error that is produced by integer division. - mono_lazy_initialize (&_ep_rt_mono_time_base_info_init, time_base_info_lazy_init); - if (_ep_rt_mono_time_base_info.denom == 0 || _ep_rt_mono_time_base_info.numer == 0) - return 0; - return ((int64_t)(SECS_TO_NS) * (int64_t)(_ep_rt_mono_time_base_info.denom)) / (int64_t)(_ep_rt_mono_time_base_info.numer); -#elif HAVE_CLOCK_MONOTONIC - // clock_gettime () returns a result in terms of nanoseconds rather than a count. This - // means that we need to either always scale the result by the actual resolution (to - // get a count) or we need to say the resolution is in terms of nanoseconds. We prefer - // the latter since it allows the highest throughput and should minimize error propagated - // to the user. - return (int64_t)(SECS_TO_NS); -#else - #error "ep_rt_mono_perf_frequency_query requires either mach_absolute_time () or clock_gettime (CLOCK_MONOTONIC) to be supported." -#endif - return 0; -} - -void -ep_rt_mono_system_time_get (EventPipeSystemTime *system_time) -{ - time_t tt; -#if HAVE_GMTIME_R - struct tm ut; -#endif /* HAVE_GMTIME_R */ - struct tm *ut_ptr; - struct timeval time_val; - int timeofday_retval; - - EP_ASSERT (system_time != NULL); - - tt = time (NULL); - - /* We can't get millisecond resolution from time (), so we get it from gettimeofday () */ - timeofday_retval = gettimeofday (&time_val, NULL); - -#if HAVE_GMTIME_R - ut_ptr = &ut; - if (gmtime_r (&tt, ut_ptr) == NULL) -#else /* HAVE_GMTIME_R */ - if ((ut_ptr = gmtime (&tt)) == NULL) -#endif /* HAVE_GMTIME_R */ - EP_UNREACHABLE (); - - uint16_t milliseconds = 0; - if (timeofday_retval != -1) { - int old_seconds; - int new_seconds; - - milliseconds = (uint16_t)(time_val.tv_usec / MSECS_TO_MIS); - - old_seconds = ut_ptr->tm_sec; - new_seconds = time_val.tv_sec % 60; - - /* just in case we reached the next second in the interval between time () and gettimeofday () */ - if (old_seconds != new_seconds) - milliseconds = 999; - } - - ep_system_time_set ( - system_time, - (uint16_t)(1900 + ut_ptr->tm_year), - (uint16_t)ut_ptr->tm_mon + 1, - (uint16_t)ut_ptr->tm_wday, - (uint16_t)ut_ptr->tm_mday, - (uint16_t)ut_ptr->tm_hour, - (uint16_t)ut_ptr->tm_min, - (uint16_t)ut_ptr->tm_sec, - milliseconds); -} - -static -inline -int64_t -system_time_to_int64 ( - time_t sec, - long nsec) -{ - return ((int64_t)sec + SECS_BETWEEN_1601_AND_1970_EPOCHS) * SECS_TO_100NS + (nsec / 100); -} - -int64_t -ep_rt_mono_system_timestamp_get (void) -{ -#if HAVE_CLOCK_MONOTONIC - struct timespec time; - if (clock_gettime (CLOCK_REALTIME, &time) == 0) - return system_time_to_int64 (time.tv_sec, time.tv_nsec); -#else - struct timeval time; - if (gettimeofday (&time, NULL) == 0) - return system_time_to_int64 (time.tv_sec, time.tv_usec * MISECS_TO_NS); -#endif - else - return system_time_to_int64 (0, 0); -} -#endif - -#ifndef HOST_WIN32 -#if defined(__APPLE__) -#if defined (HOST_OSX) -G_BEGIN_DECLS -gchar ***_NSGetEnviron(void); -G_END_DECLS -#define environ (*_NSGetEnviron()) -#else -static char *_ep_rt_mono_environ[1] = { NULL }; -#define environ _ep_rt_mono_environ -#endif /* defined (HOST_OSX) */ -#else -G_BEGIN_DECLS -extern char **environ; -G_END_DECLS -#endif /* defined (__APPLE__) */ -#endif /* !defined (HOST_WIN32) */ - -void -ep_rt_mono_os_environment_get_utf16 (dn_vector_ptr_t *os_env) -{ - EP_ASSERT (os_env != NULL); -#ifdef HOST_WIN32 - LPWSTR envs = GetEnvironmentStringsW (); - if (envs) { - LPWSTR next = envs; - while (*next) { - dn_vector_ptr_push_back (os_env, ep_rt_utf16_string_dup (next)); - next += ep_rt_utf16_string_len (next) + 1; - } - FreeEnvironmentStringsW (envs); - } -#else - gchar **next = NULL; - for (next = environ; *next != NULL; ++next) - dn_vector_ptr_push_back (os_env, ep_rt_utf8_to_utf16le_string (*next, -1)); -#endif -} - -void -ep_rt_mono_init_providers_and_events (void) -{ - InitProvidersAndEvents (); -} - -void -ep_rt_mono_provider_config_init (EventPipeProviderConfiguration *provider_config) -{ - if (!ep_rt_utf8_string_compare (ep_config_get_rundown_provider_name_utf8 (), ep_provider_config_get_provider_name (provider_config))) { - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.Level = (uint8_t)ep_provider_config_get_logging_level (provider_config); - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = ep_provider_config_get_keywords (provider_config); - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.IsEnabled = true; - } -} - -bool -ep_rt_mono_providers_validate_all_disabled (void) -{ - return (!MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled && - !MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.IsEnabled && - !MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.IsEnabled && - !MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.IsEnabled); -} - -void -ep_rt_mono_fini_providers_and_events (void) -{ - // dotnet/runtime: issue 12775: EventPipe shutdown race conditions - // Deallocating providers/events here might cause AV if a WriteEvent - // was to occur. Thus, we are not doing this cleanup. -} - -bool -ep_rt_mono_walk_managed_stack_for_thread ( - ep_rt_thread_handle_t thread, - EventPipeStackContents *stack_contents) -{ - EP_ASSERT (thread != NULL && stack_contents != NULL); - - EventPipeStackWalkData stack_walk_data; - stack_walk_data.stack_contents = stack_contents; - stack_walk_data.top_frame = true; - stack_walk_data.async_frame = false; - stack_walk_data.safe_point_frame = false; - stack_walk_data.runtime_invoke_frame = false; - - bool restore_async_context = FALSE; - bool prevent_profiler_event_recursion = FALSE; - EventPipeThreadData *thread_data = eventpipe_thread_data_get_or_create (); - if (thread_data) { - prevent_profiler_event_recursion = thread_data->prevent_profiler_event_recursion; - if (prevent_profiler_event_recursion && !mono_thread_info_is_async_context ()) { - // Running stackwalk in async context mode is currently the only way to prevent - // unwinder to NOT load additional classes during stackwalk, making it signal unsafe and - // potential triggering uncontrolled recursion in profiler class loading event. - mono_thread_info_set_is_async_context (TRUE); - restore_async_context = TRUE; - } - thread_data->prevent_profiler_event_recursion = TRUE; - } - - if (thread == ep_rt_thread_get_handle () && mono_get_eh_callbacks ()->mono_walk_stack_with_ctx) - mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (eventpipe_walk_managed_stack_for_thread_func, NULL, MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); - else if (mono_get_eh_callbacks ()->mono_walk_stack_with_state) - mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_walk_managed_stack_for_thread_func, mono_thread_info_get_suspend_state (thread), MONO_UNWIND_SIGNAL_SAFE, &stack_walk_data); - - if (thread_data) { - if (restore_async_context) - mono_thread_info_set_is_async_context (FALSE); - thread_data->prevent_profiler_event_recursion = prevent_profiler_event_recursion; - } - - return true; -} - -bool -ep_rt_mono_method_get_simple_assembly_name ( - ep_rt_method_desc_t *method, - ep_char8_t *name, - size_t name_len) -{ - EP_ASSERT (method != NULL); - EP_ASSERT (name != NULL); - - MonoClass *method_class = mono_method_get_class (method); - MonoImage *method_image = method_class ? mono_class_get_image (method_class) : NULL; - const ep_char8_t *assembly_name = method_image ? mono_image_get_name (method_image) : NULL; - - if (!assembly_name) - return false; - - g_strlcpy (name, assembly_name, name_len); - return true; -} - -bool -ep_rt_mono_method_get_full_name ( - ep_rt_method_desc_t *method, - ep_char8_t *name, - size_t name_len) -{ - EP_ASSERT (method != NULL); - EP_ASSERT (name != NULL); - - char *full_method_name = mono_method_get_name_full (method, TRUE, TRUE, MONO_TYPE_NAME_FORMAT_IL); - if (!full_method_name) - return false; - - g_strlcpy (name, full_method_name, name_len); - - g_free (full_method_name); - return true; -} - -bool -ep_rt_mono_sample_profiler_write_sampling_event_for_threads ( - ep_rt_thread_handle_t sampling_thread, - EventPipeEvent *sampling_event) -{ - // Follows CoreClr implementation of sample profiler. Generic invasive/expensive way to do CPU sample profiling relying on STW and stackwalks. - // TODO: Investigate alternatives on platforms supporting Signals/SuspendThread (see Mono profiler) or CPU PMU's (see ETW/perf_event_open). - - // Sample profiler only runs on one thread, no need to synchorinize. - if (!_ep_rt_mono_sampled_thread_callstacks) - _ep_rt_mono_sampled_thread_callstacks = g_array_sized_new (FALSE, FALSE, sizeof (EventPipeSampleProfileStackWalkData), _ep_rt_mono_max_sampled_thread_count); - - // Make sure there is room based on previous max number of sampled threads. - // NOTE, there is a chance there are more threads than max, if that's the case we will - // miss those threads in this sample, but will be included in next when max has been adjusted. - g_array_set_size (_ep_rt_mono_sampled_thread_callstacks, _ep_rt_mono_max_sampled_thread_count); - - uint32_t filtered_thread_count = 0; - uint32_t sampled_thread_count = 0; - - mono_stop_world (MONO_THREAD_INFO_FLAGS_NO_GC); - - bool restore_async_context = FALSE; - if (!mono_thread_info_is_async_context ()) { - mono_thread_info_set_is_async_context (TRUE); - restore_async_context = TRUE; - } - - // Record all info needed in sample events while runtime is suspended, must be async safe. - FOREACH_THREAD_SAFE_EXCLUDE (thread_info, MONO_THREAD_INFO_FLAGS_NO_GC | MONO_THREAD_INFO_FLAGS_NO_SAMPLE) { - if (!mono_thread_info_is_running (thread_info)) { - MonoThreadUnwindState *thread_state = mono_thread_info_get_suspend_state (thread_info); - if (thread_state->valid) { - if (sampled_thread_count < _ep_rt_mono_max_sampled_thread_count) { - EventPipeSampleProfileStackWalkData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileStackWalkData, sampled_thread_count); - data->thread_id = ep_rt_thread_id_t_to_uint64_t (mono_thread_info_get_tid (thread_info)); - data->thread_ip = (uintptr_t)MONO_CONTEXT_GET_IP (&thread_state->ctx); - data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR; - data->stack_walk_data.stack_contents = &data->stack_contents; - data->stack_walk_data.top_frame = true; - data->stack_walk_data.async_frame = false; - data->stack_walk_data.safe_point_frame = false; - data->stack_walk_data.runtime_invoke_frame = false; - ep_stack_contents_reset (&data->stack_contents); - mono_get_eh_callbacks ()->mono_walk_stack_with_state (eventpipe_sample_profiler_walk_managed_stack_for_thread_func, thread_state, MONO_UNWIND_SIGNAL_SAFE, data); - if (data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL && (data->stack_walk_data.safe_point_frame || data->stack_walk_data.runtime_invoke_frame)) { - // If classified as external code (managed->native frame on top of stack), but have a safe point or runtime invoke frame - // as second, re-classify current callstack to be executing managed code. - data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_MANAGED; - } - if (data->stack_walk_data.top_frame && ep_stack_contents_get_length (&data->stack_contents) == 0) { - // If no managed frames (including helper frames) are located on stack, mark sample as beginning in external code. - // This can happen on attached embedding threads returning to native code between runtime invokes. - // Make sure sample is still written into EventPipe for all attached threads even if they are currently not having - // any managed frames on stack. Prevents some tools applying thread time heuristics to prolong duration of last sample - // when embedding thread returns to native code. It also opens ability to visualize number of samples in unmanaged code - // on attached threads when executing outside of runtime. If tooling is not interested in these sample events, they are easy - // to identify and filter out. - data->payload_data = EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL; - } - - sampled_thread_count++; - } - } - } - filtered_thread_count++; - } FOREACH_THREAD_SAFE_END - - if (restore_async_context) - mono_thread_info_set_is_async_context (FALSE); - - mono_restart_world (MONO_THREAD_INFO_FLAGS_NO_GC); - - // Fire sample event for threads. Must be done after runtime is resumed since it's not async safe. - // Since we can't keep thread info around after runtime as been suspended, use an empty - // adapter instance and only set recorded tid as parameter inside adapter. - THREAD_INFO_TYPE adapter = { { 0 } }; - for (uint32_t thread_count = 0; thread_count < sampled_thread_count; ++thread_count) { - EventPipeSampleProfileStackWalkData *data = &g_array_index (_ep_rt_mono_sampled_thread_callstacks, EventPipeSampleProfileStackWalkData, thread_count); - if ((data->stack_walk_data.top_frame && data->payload_data == EP_SAMPLE_PROFILER_SAMPLE_TYPE_EXTERNAL) || (data->payload_data != EP_SAMPLE_PROFILER_SAMPLE_TYPE_ERROR && ep_stack_contents_get_length (&data->stack_contents) > 0)) { - // Check if we have an async frame, if so we will need to make sure all frames are registered in regular jit info table. - // TODO: An async frame can contain wrapper methods (no way to check during stackwalk), we could skip writing profile event - // for this specific stackwalk or we could cleanup stack_frames before writing profile event. - if (data->stack_walk_data.async_frame) { - for (uint32_t frame_count = 0; frame_count < data->stack_contents.next_available_frame; ++frame_count) - mono_jit_info_table_find_internal ((gpointer)data->stack_contents.stack_frames [frame_count], TRUE, FALSE); - } - mono_thread_info_set_tid (&adapter, ep_rt_uint64_t_to_thread_id_t (data->thread_id)); - uint32_t payload_data = ep_rt_val_uint32_t (data->payload_data); - ep_write_sample_profile_event (sampling_thread, sampling_event, &adapter, &data->stack_contents, (uint8_t *)&payload_data, sizeof (payload_data)); - } - } - - // Current thread count will be our next maximum sampled threads. - _ep_rt_mono_max_sampled_thread_count = filtered_thread_count; - - return true; -} - -void -ep_rt_mono_execute_rundown (dn_vector_ptr_t *execution_checkpoints) -{ - ep_char8_t runtime_module_path [256]; - const uint8_t object_guid [EP_GUID_SIZE] = { 0 }; - const uint16_t runtime_product_qfe_version = 0; - const uint8_t startup_flags = 0; - const uint8_t startup_mode = 0; - const ep_char8_t *command_line = ""; - - if (!g_module_address ((void *)mono_init, runtime_module_path, sizeof (runtime_module_path), NULL, NULL, 0, NULL)) - runtime_module_path [0] = '\0'; - - FireEtwRuntimeInformationDCStart ( - clr_instance_get_id (), - RUNTIME_SKU_MONO, - RuntimeProductMajorVersion, - RuntimeProductMinorVersion, - RuntimeProductPatchVersion, - runtime_product_qfe_version, - RuntimeFileMajorVersion, - RuntimeFileMajorVersion, - RuntimeFileBuildVersion, - RuntimeFileRevisionVersion, - startup_mode, - startup_flags, - command_line, - object_guid, - runtime_module_path, - NULL, - NULL); - - if (execution_checkpoints) { - DN_VECTOR_PTR_FOREACH_BEGIN (EventPipeExecutionCheckpoint *, checkpoint, execution_checkpoints) { - FireEtwExecutionCheckpointDCEnd ( - clr_instance_get_id (), - checkpoint->name, - checkpoint->timestamp, - NULL, - NULL); - } DN_VECTOR_PTR_FOREACH_END; - } - - FireEtwDCEndInit_V1 ( - clr_instance_get_id (), - NULL, - NULL); - - eventpipe_execute_rundown ( - fire_domain_rundown_events_func, - fire_assembly_rundown_events_func, - fire_method_rundown_events_func); - - FireEtwDCEndComplete_V1 ( - clr_instance_get_id (), - NULL, - NULL); -} - -bool -ep_rt_mono_write_event_ee_startup_start (void) -{ - return FireEtwEEStartupStart_V1 ( - clr_instance_get_id (), - NULL, - NULL); -} - -#define STACK_ALLOC 256 - -// The maximum number of type parameters for a BulkTypeValue instance -// Aligned with coreCLR StackSArray rgTypeParameters -#define INIT_SIZE_OF_TYPE_PARAMETER_ARRAY ((uint32_t)(STACK_ALLOC / sizeof (intptr_t))) - -// !!!!!!! NOTE !!!!!!!! -// The flags must match those in the ETW manifest exactly -// !!!!!!! NOTE !!!!!!!! - -typedef enum { - TYPE_FLAGS_DELEGATE = 0x1, - TYPE_FLAGS_FINALIZABLE = 0x2, - TYPE_FLAGS_EXTERNALLY_IMPLEMENTED_COM_OBJECT = 0x4, - TYPE_FLAGS_ARRAY = 0x8, - - TYPE_FLAGS_ARRAY_RANK_MASK = 0x3F00, - TYPE_FLAGS_ARRAY_RANK_SHIFT = 8, - TYPE_FLAGS_ARRAY_RANK_MAX = TYPE_FLAGS_ARRAY_RANK_MASK >> TYPE_FLAGS_ARRAY_RANK_SHIFT -} TypeFlags; - -// This only contains the fixed-size data at the top of each struct in -// the bulk type event. These fields must still match exactly the initial -// fields of the struct described in the manifest. -typedef struct _EventStructBulkTypeFixedSizedData { - uint64_t type_id; - uint64_t module_id; - uint32_t type_name_id; - uint32_t flags; - uint8_t cor_element_type; -} EventStructBulkTypeFixedSizedData; - -// Represents one instance of the Value struct inside a single BulkType event -typedef struct _BulkTypeValue { - EventStructBulkTypeFixedSizedData fixed_sized_data; - uint32_t type_parameters_count; - MonoType **mono_type_parameters; - ep_char8_t *name; // Currently should only be NULL, TODO if we want to provide the name in the BulkTypeEvent data, figure out memory management to use -} BulkTypeValue; - -static -void -ep_rt_bulk_type_value_clear (BulkTypeValue *bulk_type_value); - -static -int -ep_rt_mono_get_byte_count_in_event (BulkTypeValue *bulk_type_value); - -static -BulkTypeEventLogger* -ep_rt_bulk_type_event_logger_alloc (void); - -static -void -ep_rt_bulk_type_event_logger_free (BulkTypeEventLogger *type_logger); - -static -int -write_event_buffer ( - const uint8_t *val, - int size, - char *buf_start, - char **buf_next); - -static -int -write_event_buffer_int8 ( - int8_t val, - char *buf_start, - char **buf_next); - -static -int -write_event_buffer_int16 ( - int16_t val, - char *buf_start, - char **buf_next); - -static -int -write_event_buffer_int32 ( - int32_t val, - char *buf_start, - char **buf_next); - -static -int -write_event_buffer_int64 ( - int64_t val, - char *buf_start, - char **buf_next); - -static -uint64_t -get_typeid_for_type (MonoType *t); - -static -uint64_t -get_typeid_for_class (MonoClass *c); - -// Clear out BulkTypeValue before filling it out (array elements can get reused if there -// are enough types that we need to flush to multiple events). -static -void -ep_rt_bulk_type_value_clear (BulkTypeValue *bulk_type_value) -{ - memset (bulk_type_value, 0, sizeof(BulkTypeValue)); -} - -static -int -ep_rt_mono_get_byte_count_in_event (BulkTypeValue *bulk_type_value) -{ - int name_len = 0; - - return sizeof (bulk_type_value->fixed_sized_data.type_id) + // Fixed Sized Data - sizeof (bulk_type_value->fixed_sized_data.module_id) + - sizeof (bulk_type_value->fixed_sized_data.type_name_id) + - sizeof (bulk_type_value->fixed_sized_data.flags) + - sizeof (bulk_type_value->fixed_sized_data.cor_element_type) + - sizeof (bulk_type_value->type_parameters_count) + // Type parameters - (name_len + 1) * sizeof (ep_char8_t) + // Size of name, including null terminator - bulk_type_value->type_parameters_count * sizeof (uint64_t); // Type parameters -} - -// ETW has a limitation of 64K for TOTAL event Size, however there is overhead associated with -// the event headers. It is unclear exactly how much that is, but 1K should be sufficiently -// far away to avoid problems without sacrificing the perf of bulk processing. -#define MAX_EVENT_BYTE_COUNT (63 * 1024) - -// The maximum event size, and the size of the buffer that we allocate to hold the event contents. -#define MAX_SIZE_OF_EVENT_BUFFER 65536 - -// Estimate of how many bytes we can squeeze in the event data for the value struct -// array. (Intentionally overestimate the size of the non-array parts to keep it safe.) -// This follows CoreCLR's kMaxBytesTypeValues. -#define MAX_TYPE_VALUES_BYTES (MAX_EVENT_BYTE_COUNT - 0x30) - -// Estimate of how many type value elements we can put into the struct array, while -// staying under the ETW event size limit. Note that this is impossible to calculate -// perfectly, since each element of the struct array has variable size. -// -// In addition to the byte-size limit per event, Windows always forces on us a -// max-number-of-descriptors per event, which in the case of BulkType, will kick in -// far sooner. There's a max number of 128 descriptors allowed per event. 2 are used -// for Count + ClrInstanceID. Then 4 per batched value. (Might actually be 3 if there -// are no type parameters to log, but let's overestimate at 4 per value). -#define K_MAX_COUNT_TYPE_VALUES ((uint32_t)(128 - 2) / 4) - -struct _BulkTypeEventLogger { - BulkTypeValue bulk_type_values [K_MAX_COUNT_TYPE_VALUES]; - uint8_t *bulk_type_event_buffer; - uint32_t bulk_type_value_count; - uint32_t bulk_type_value_byte_count; - MonoMemPool *mem_pool; -}; - -static -BulkTypeEventLogger* -ep_rt_bulk_type_event_logger_alloc (void) -{ - BulkTypeEventLogger *type_logger = g_malloc0 (sizeof (BulkTypeEventLogger)); - type_logger->bulk_type_event_buffer = g_malloc0 (sizeof (uint8_t) * MAX_SIZE_OF_EVENT_BUFFER); - type_logger->mem_pool = mono_mempool_new (); - return type_logger; -} - -static -void -ep_rt_bulk_type_event_logger_free (BulkTypeEventLogger *type_logger) -{ - mono_mempool_destroy (type_logger->mem_pool); - g_free (type_logger->bulk_type_event_buffer); - g_free (type_logger); -} - -static -int -write_event_buffer ( - const uint8_t *val, - int size, - char *buf_start, - char **buf_next) -{ - memcpy (buf_start, val, size); - *buf_next = buf_start + size; - return size; -} - -static -int -write_event_buffer_int8 ( - int8_t val, - char *buf_start, - char **buf_next) -{ - return write_event_buffer ((const uint8_t *)&val, sizeof (int8_t), buf_start, buf_next); -} - -static -int -write_event_buffer_int16 ( - int16_t val, - char *buf_start, - char **buf_next) -{ - return write_event_buffer ((const uint8_t *)&val, sizeof (int16_t), buf_start, buf_next); -} - -static -int -write_event_buffer_int32 ( - int32_t val, - char *buf_start, - char **buf_next) -{ - return write_event_buffer ((const uint8_t *)&val, sizeof (int32_t), buf_start, buf_next); -} - -static -int -write_event_buffer_int64 ( - int64_t val, - char *buf_start, - char **buf_next) -{ - return write_event_buffer ((const uint8_t *)&val, sizeof (int64_t), buf_start, buf_next); -} - -//--------------------------------------------------------------------------------------- -// -// ep_rt_mono_fire_bulk_type_event fires an ETW event for all the types batched so far, -// it then resets the state to start batching new types at the beginning of the -// bulk_type_values array. -// -// This follows CoreCLR's BulkTypeEventLogger::FireBulkTypeEvent - -void -ep_rt_mono_fire_bulk_type_event (BulkTypeEventLogger *type_logger) -{ - if (type_logger->bulk_type_value_count == 0) - return; - - uint16_t clr_instance_id = clr_instance_get_id (); - - uint32_t values_element_size = 0; - - uint8_t *ptr = type_logger->bulk_type_event_buffer; - - for (uint32_t type_value_index = 0; type_value_index < type_logger->bulk_type_value_count; type_value_index++) { - BulkTypeValue *target = &type_logger->bulk_type_values [type_value_index]; - - values_element_size += ep_write_buffer_uint64_t (&ptr, target->fixed_sized_data.type_id); - values_element_size += ep_write_buffer_uint64_t (&ptr, target->fixed_sized_data.module_id); - values_element_size += ep_write_buffer_uint32_t (&ptr, target->fixed_sized_data.type_name_id); - values_element_size += ep_write_buffer_uint32_t (&ptr, target->fixed_sized_data.flags); - values_element_size += ep_write_buffer_uint8_t (&ptr, target->fixed_sized_data.cor_element_type); - - g_assert (target->name == NULL); - values_element_size += ep_write_buffer_string_utf8_to_utf16_t (&ptr, "", 0); - - values_element_size += ep_write_buffer_uint32_t (&ptr, target->type_parameters_count); - - for (uint32_t i = 0; i < target->type_parameters_count; i++) { - uint64_t type_parameter = get_typeid_for_type (target->mono_type_parameters [i]); - values_element_size += ep_write_buffer_uint64_t (&ptr, type_parameter); - } - } - - FireEtwBulkType ( - type_logger->bulk_type_value_count, - clr_instance_id, - values_element_size, - type_logger->bulk_type_event_buffer, - NULL, - NULL); - - memset (type_logger->bulk_type_event_buffer, 0, sizeof (uint8_t) * MAX_SIZE_OF_EVENT_BUFFER); - type_logger->bulk_type_value_count = 0; - type_logger->bulk_type_value_byte_count = 0; -} - -//--------------------------------------------------------------------------------------- -// -// get_typeid_for_type is responsible for obtaining the unique type identifier for a -// particular MonoType. MonoTypes are structs that are not unique pointers. There -// can be two different MonoTypes that both System.Thread or int32 or bool []. There -// is exactly one MonoClass * for any type, so we leverage the MonoClass a MonoType -// points to in order to obtain a unique type identifier in mono. With that unique -// MonoClass, its fields this_arg and _byval_arg are unique as well. -// -// Arguments: -// * mono_type - MonoType to be logged -// -// Return Value: -// type_id - Unique type identifier of mono_type - -static -uint64_t -get_typeid_for_type (MonoType *t) -{ - if (m_type_is_byref (t)) - return (uint64_t)m_class_get_this_arg (mono_class_from_mono_type_internal (t)); - else - return (uint64_t)m_class_get_byval_arg (mono_class_from_mono_type_internal (t)); -} - -static -uint64_t -get_typeid_for_class (MonoClass *c) -{ - return get_typeid_for_type (m_class_get_byval_arg (c)); -} - -//--------------------------------------------------------------------------------------- -// -// ep_rt_mono_log_single_type batches a single type into the bulk type array and flushes -// the array to ETW if it fills up. Most interaction with the type system (type analysis) -// is done here. This does not recursively batch up any parameter types (arrays or generics), -// but does add their unique identifiers to the mono_type_parameters array. -// ep_rt_mono_log_type_and_parameters is responsible for initiating any recursive calls to -// deal with type parameters. -// -// Arguments: -// * type_logger - BulkTypeEventLogger instance -// * mono_type - MonoType to be logged -// -// Return Value: -// Index into array of where this type got batched. -1 if there was a failure. -// -// This follows CoreCLR's BulkTypeEventLogger::LogSingleType - -int -ep_rt_mono_log_single_type ( - BulkTypeEventLogger *type_logger, - MonoType *mono_type) -{ - // If there's no room for another type, flush what we've got - if (type_logger->bulk_type_value_count == K_MAX_COUNT_TYPE_VALUES) - ep_rt_mono_fire_bulk_type_event (type_logger); - - EP_ASSERT (type_logger->bulk_type_value_count < K_MAX_COUNT_TYPE_VALUES); - - BulkTypeValue *val = &type_logger->bulk_type_values [type_logger->bulk_type_value_count]; - ep_rt_bulk_type_value_clear (val); - - MonoClass *klass = mono_class_from_mono_type_internal (mono_type); - MonoType *mono_underlying_type = mono_type_get_underlying_type (mono_type); - - // Initialize val fixed_sized_data - val->fixed_sized_data.type_id = get_typeid_for_type (mono_type); - val->fixed_sized_data.module_id = (uint64_t)m_class_get_image (klass); - val->fixed_sized_data.type_name_id = m_class_get_type_token (klass) ? mono_metadata_make_token (MONO_TABLE_TYPEDEF, mono_metadata_token_index (m_class_get_type_token (klass))) : 0; - if (mono_class_has_finalizer (klass)) - val->fixed_sized_data.flags |= TYPE_FLAGS_FINALIZABLE; - if (m_class_is_delegate (klass)) - val->fixed_sized_data.flags |= TYPE_FLAGS_DELEGATE; - if (mono_class_is_com_object (klass)) - val->fixed_sized_data.flags |= TYPE_FLAGS_EXTERNALLY_IMPLEMENTED_COM_OBJECT; - val->fixed_sized_data.cor_element_type = (uint8_t)mono_underlying_type->type; - - // Sets val variable sized parameter type data, type_parameters_count, and mono_type_parameters associated - // with arrays or generics to be recursively batched in the same ep_rt_mono_log_type_and_parameters call - switch (mono_underlying_type->type) { - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: - { - MonoArrayType *mono_array_type = mono_type_get_array_type (mono_type); - val->fixed_sized_data.flags |= TYPE_FLAGS_ARRAY; - if (mono_underlying_type->type == MONO_TYPE_ARRAY) { - // Only ranks less than TypeFlagsArrayRankMax are supported. - // Fortunately TypeFlagsArrayRankMax should be greater than the - // number of ranks the type loader will support - uint32_t rank = mono_array_type->rank; - if (rank < TYPE_FLAGS_ARRAY_RANK_MAX) { - rank <<= 8; - val->fixed_sized_data.flags |= rank; - } - } - - // mono arrays are always arrays of by value types - val->mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, 1 * sizeof (MonoType*)); - *val->mono_type_parameters = m_class_get_byval_arg (mono_array_type->eklass); - val->type_parameters_count++; - break; - } - case MONO_TYPE_GENERICINST: - { - MonoGenericInst *class_inst = mono_type->data.generic_class->context.class_inst; - val->type_parameters_count = class_inst->type_argc; - val->mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, val->type_parameters_count * sizeof (MonoType*)); - memcpy (val->mono_type_parameters, class_inst->type_argv, val->type_parameters_count * sizeof (MonoType*)); - break; - } - case MONO_TYPE_CLASS: - case MONO_TYPE_VALUETYPE: - case MONO_TYPE_PTR: - case MONO_TYPE_BYREF: - { - if (mono_underlying_type == mono_type) - break; - val->mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, 1 * sizeof (MonoType*)); - *val->mono_type_parameters = mono_underlying_type; - val->type_parameters_count++; - break; - } - default: - break; - } - - // Now that we know the full size of this type's data, see if it fits in our - // batch or whether we need to flush - int val_byte_count = ep_rt_mono_get_byte_count_in_event (val); - if (val_byte_count > MAX_TYPE_VALUES_BYTES) { - // NOTE: If name is actively used, set it to NULL and relevant memory management to reduce byte count - // This type is apparently so huge, it's too big to squeeze into an event, even - // if it were the only type batched in the whole event. Bail - mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed to log single mono type %p with typeID %llu. Type is too large for the BulkType Event.\n", (gpointer)mono_type, (unsigned long long)val->fixed_sized_data.type_id); - return -1; - } - - if (type_logger->bulk_type_value_byte_count + val_byte_count > MAX_TYPE_VALUES_BYTES) { - // Although this type fits into the array, its size is so big that the entire - // array can't be logged via ETW. So flush the array, and start over by - // calling ourselves--this refetches the type info and puts it at the - // beginning of the array. Since we know this type is small enough to be - // batched into an event on its own, this recursive call will not try to - // call itself again. - g_assert (type_logger->bulk_type_value_byte_count + val_byte_count > MAX_TYPE_VALUES_BYTES); - ep_rt_mono_fire_bulk_type_event (type_logger); - return ep_rt_mono_log_single_type (type_logger, mono_type); - } - - // The type fits into the batch, so update our state - type_logger->bulk_type_value_count++; - type_logger->bulk_type_value_byte_count += val_byte_count; - return type_logger->bulk_type_value_count - 1; -} - -//--------------------------------------------------------------------------------------- -// -// High-level method to batch a type and (recursively) its type parameters, flushing to -// ETW as needed. This is called by ep_rt_mono_log_type_and_parameters_if_necessary. -// -// Arguments: -// * type_logger - BulkTypeEventLogger instance -// * mono_type - MonoType to be logged -// -// This follows CoreCLR's BulkTypeEventLogger::LogTypeAndParameter - -void -ep_rt_mono_log_type_and_parameters ( - BulkTypeEventLogger *type_logger, - MonoType *mono_type) -{ - // Batch up this type. This grabs useful info about the type, including any - // type parameters it may have, and sticks it in bulk_type_values - int bulk_type_value_index = ep_rt_mono_log_single_type (type_logger, mono_type); - if (bulk_type_value_index == -1) { - // There was a failure trying to log the type, so don't bother with its type - // parameters - return; - } - - // Look at the type info we just batched, so we can get the type parameters - BulkTypeValue *val = &type_logger->bulk_type_values [bulk_type_value_index]; - - // We're about to recursively call ourselves for the type parameters, so make a - // local copy of their type handles first (else, as we log them we could flush - // and clear out bulk_type_values, thus trashing val) - uint32_t param_count = val->type_parameters_count; - if (param_count == 0) - return; - - MonoType **mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, param_count * sizeof (MonoType*)); - memcpy (mono_type_parameters, val->mono_type_parameters, sizeof (MonoType*) * param_count); - - for (uint32_t i = 0; i < param_count; i++) - ep_rt_mono_log_type_and_parameters_if_necessary (type_logger, mono_type_parameters [i]); -} - -//--------------------------------------------------------------------------------------- -// -// Outermost level of ETW-type-logging. This method is used to log a unique type identifier -// (in this case a MonoType) and (recursively) its type parameters when present. -// -// Arguments: -// * type_logger - BulkTypeEventLogger instance -// * mono_type - MonoType to be logged -// -// This follows CoreCLR's BulkTypeEventLogger::LogTypeAndParameters - -void -ep_rt_mono_log_type_and_parameters_if_necessary ( - BulkTypeEventLogger *type_logger, - MonoType *mono_type) -{ - // TODO Log the type if necessary - - ep_rt_mono_log_type_and_parameters (type_logger, mono_type); -} - -// ETW has a limit for maximum event size. Do not log overly large method type argument sets -static const uint32_t MAX_METHOD_TYPE_ARGUMENT_COUNT = 1024; - -//--------------------------------------------------------------------------------------- -// -// ep_rt_mono_send_method_details_event is the method responsible for sending details of -// methods involved in events such as JitStart, Load/Unload, Rundown, R2R, and other -// eventpipe events. It calls ep_rt_mono_log_type_and_parameters_if_necessary to log -// unique types from the method type and available method instantiation parameter types -// that are ultimately emitted as a BulkType event in ep_rt_mono_fire_bulk_type_event. -// After appropriately logging type information, it sends method details outlined by -// the generated dotnetruntime.c and ClrEtwAll manifest. -// -// Arguments: -// * method - a MonoMethod hit during an eventpipe event -// -// This follows CoreCLR's ETW::MethodLog::SendMethodDetailsEvent - -void -ep_rt_mono_send_method_details_event (MonoMethod *method) -{ - if (method->wrapper_type != MONO_WRAPPER_NONE || method->dynamic) - return; - - MonoGenericContext *method_ctx = mono_method_get_context (method); - - MonoGenericInst *method_inst = NULL; - if (method_ctx) - method_inst = method_ctx->method_inst; - - if (method_inst && method_inst->type_argc > MAX_METHOD_TYPE_ARGUMENT_COUNT) - return; - - BulkTypeEventLogger *type_logger = ep_rt_bulk_type_event_logger_alloc (); - - uint64_t method_type_id = 0; - g_assert (mono_metadata_token_index (method->token) != 0); - uint32_t method_token = mono_metadata_make_token (MONO_TABLE_METHOD, mono_metadata_token_index (method->token)); - uint64_t loader_module_id = 0; - MonoClass *klass = method->klass; - if (klass) { - MonoType *method_mono_type = m_class_get_byval_arg (klass); - method_type_id = get_typeid_for_class (klass); - - ep_rt_mono_log_type_and_parameters_if_necessary (type_logger, method_mono_type); - - loader_module_id = (uint64_t)mono_class_get_image (klass); - } - - uint32_t method_inst_parameter_types_count = 0; - if (method_inst) - method_inst_parameter_types_count = method_inst->type_argc; - - uint64_t *method_inst_parameters_type_ids = mono_mempool_alloc0 (type_logger->mem_pool, method_inst_parameter_types_count * sizeof (uint64_t)); - uint8_t *buffer = (uint8_t *)method_inst_parameters_type_ids; - for (uint32_t i = 0; i < method_inst_parameter_types_count; i++) { - ep_write_buffer_uint64_t (&buffer, get_typeid_for_type (method_inst->type_argv [i])); - - ep_rt_mono_log_type_and_parameters_if_necessary (type_logger, method_inst->type_argv [i]); - } - - ep_rt_mono_fire_bulk_type_event (type_logger); - - FireEtwMethodDetails ( - (uint64_t)method, - method_type_id, - method_token, - method_inst_parameter_types_count, - loader_module_id, - (uint64_t*)method_inst_parameters_type_ids, - NULL, - NULL); - - ep_rt_bulk_type_event_logger_free (type_logger); -} - -bool -ep_rt_mono_write_event_jit_start (MonoMethod *method) -{ - if (!EventEnabledMethodJittingStarted_V1 ()) - return true; - - //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. - if (method) { - uint64_t method_id = 0; - uint64_t module_id = 0; - uint32_t code_size = 0; - uint32_t method_token = 0; - char *method_namespace = NULL; - const char *method_name = NULL; - char *method_signature = NULL; - - ep_rt_mono_send_method_details_event(method); - - method_id = (uint64_t)method; - - if (!method->dynamic) - method_token = method->token; - - if (!mono_method_has_no_body (method)) { - ERROR_DECL (error); - MonoMethodHeader *header = mono_method_get_header_internal (method, error); - if (header) - code_size = header->code_size; - } - - method_name = method->name; - method_signature = mono_signature_full_name (mono_method_signature_internal (method)); - - if (method->klass) { - module_id = (uint64_t)m_class_get_image (method->klass); - method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); - } - - FireEtwMethodJittingStarted_V1 ( - method_id, - module_id, - method_token, - code_size, - method_namespace, - method_name, - method_signature, - clr_instance_get_id (), - NULL, - NULL); - - g_free (method_namespace); - g_free (method_signature); - } - - return true; -} - -bool -ep_rt_mono_write_event_method_il_to_native_map ( - MonoMethod *method, - MonoJitInfo *ji) -{ - if (!EventEnabledMethodILToNativeMap ()) - return true; - - if (method) { - // Under netcore we only have root domain. - MonoDomain *root_domain = mono_get_root_domain (); - - uint64_t method_id = (uint64_t)method; - uint32_t fixed_buffer [64]; - uint8_t *buffer = NULL; - - uint32_t offset_entries = 0; - uint32_t *il_offsets = NULL; - uint32_t *native_offsets = NULL; - - MonoDebugMethodJitInfo *debug_info = method ? mono_debug_find_method (method, root_domain) : NULL; - if (debug_info) { - offset_entries = debug_info->num_line_numbers; - if (offset_entries != 0) { - size_t needed_size = (offset_entries * sizeof (uint32_t) * 2); - if (needed_size > sizeof (fixed_buffer)) { - buffer = g_new (uint8_t, needed_size); - il_offsets = (uint32_t*)buffer; - } else { - il_offsets = fixed_buffer; - } - if (il_offsets) { - native_offsets = il_offsets + offset_entries; - uint8_t *il_offsets_ptr = (uint8_t *)il_offsets; - uint8_t *native_offsets_ptr = (uint8_t *)native_offsets; - for (uint32_t offset_count = 0; offset_count < offset_entries; ++offset_count) { - ep_write_buffer_uint32_t (&il_offsets_ptr, debug_info->line_numbers [offset_count].il_offset); - ep_write_buffer_uint32_t (&native_offsets_ptr, debug_info->line_numbers [offset_count].native_offset); - } - } - } - - mono_debug_free_method_jit_info (debug_info); - } - - if (!il_offsets && !native_offsets) { - // No IL offset -> Native offset mapping available. Put all code on IL offset 0. - EP_ASSERT (sizeof (fixed_buffer) >= sizeof (uint32_t) * 2); - offset_entries = 1; - il_offsets = fixed_buffer; - native_offsets = il_offsets + offset_entries; - il_offsets [0] = 0; - native_offsets [0] = ji ? ep_rt_val_uint32_t ((uint32_t)ji->code_size) : 0; - } - - FireEtwMethodILToNativeMap ( - method_id, - 0, - 0, - GUINT32_TO_UINT16 (offset_entries), - il_offsets, - native_offsets, - clr_instance_get_id (), - NULL, - NULL); - - g_free (buffer); - } - - return true; -} - -bool -ep_rt_mono_write_event_method_load ( - MonoMethod *method, - MonoJitInfo *ji) -{ - if (!EventEnabledMethodLoad_V1 () && !EventEnabledMethodLoadVerbose_V1 ()) - return true; - - //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. - if (method) { - uint64_t method_id = 0; - uint64_t module_id = 0; - uint64_t method_code_start = ji ? (uint64_t)ji->code_start : 0; - uint32_t method_code_size = ji ? (uint32_t)ji->code_size : 0; - uint32_t method_token = 0; - uint32_t method_flags = 0; - uint8_t kind = MONO_CLASS_DEF; - char *method_namespace = NULL; - const char *method_name = NULL; - char *method_signature = NULL; - bool verbose = (MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.Level >= (uint8_t)EP_EVENT_LEVEL_VERBOSE); - - method_id = (uint64_t)method; - - if (!method->dynamic) - method_token = method->token; - - if (ji && mono_jit_info_get_generic_sharing_context (ji)) { - method_flags |= METHOD_FLAGS_SHARED_GENERIC_METHOD; - verbose = true; - } - - if (method->dynamic) { - method_flags |= METHOD_FLAGS_DYNAMIC_METHOD; - verbose = true; - } - - if (ji && !ji->from_aot && !ji->from_llvm) { - method_flags |= METHOD_FLAGS_JITTED_METHOD; - if (method->wrapper_type != MONO_WRAPPER_NONE) - method_flags |= METHOD_FLAGS_JITTED_HELPER_METHOD; - } - - if (method->is_generic || method->is_inflated) { - method_flags |= METHOD_FLAGS_GENERIC_METHOD; - verbose = true; - } - - if (method->klass) { - module_id = (uint64_t)m_class_get_image (method->klass); - kind = m_class_get_class_kind (method->klass); - if (kind == MONO_CLASS_GTD || kind == MONO_CLASS_GINST) - method_flags |= METHOD_FLAGS_GENERIC_METHOD; - } - - ep_rt_mono_send_method_details_event(method); - - if (verbose) { - method_name = method->name; - method_signature = mono_signature_full_name (mono_method_signature_internal (method)); - - if (method->klass) - method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); - - FireEtwMethodLoadVerbose_V1 ( - method_id, - module_id, - method_code_start, - method_code_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, - method_namespace, - method_name, - method_signature, - clr_instance_get_id (), - NULL, - NULL); - - if (ji && (ji->from_aot || ji->from_llvm)) - FireEtwMethodLoadVerbose_V1 ( - method_id, - module_id, - method_code_start, - method_code_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, - method_namespace, - method_name, - method_signature, - clr_instance_get_id (), - NULL, - NULL); - } else { - FireEtwMethodLoad_V1 ( - method_id, - module_id, - method_code_start, - method_code_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_HOT_SECTION, - clr_instance_get_id (), - NULL, - NULL); - - if (ji && (ji->from_aot || ji->from_llvm)) - FireEtwMethodLoad_V1 ( - method_id, - module_id, - method_code_start, - method_code_size, - method_token, - method_flags | METHOD_FLAGS_EXTENT_COLD_SECTION, - clr_instance_get_id (), - NULL, - NULL); - } - - g_free (method_namespace); - g_free (method_signature); - } - - return true; -} - -static -bool -get_module_event_data ( - MonoImage *image, - ModuleEventData *module_data) -{ - if (module_data) { - memset (module_data->module_il_pdb_signature, 0, EP_GUID_SIZE); - memset (module_data->module_native_pdb_signature, 0, EP_GUID_SIZE); - - // Under netcore we only have root domain. - MonoDomain *root_domain = mono_get_root_domain (); - - module_data->domain_id = (uint64_t)root_domain; - module_data->module_id = (uint64_t)image; - module_data->assembly_id = image ? (uint64_t)image->assembly : 0; - - // TODO: Extract all module native paths and pdb metadata when available. - module_data->module_native_path = ""; - module_data->module_native_pdb_path = ""; - module_data->module_native_pdb_age = 0; - - module_data->reserved_flags = 0; - - // Netcore has a 1:1 between assemblies and modules, so its always a manifest module. - module_data->module_flags = MODULE_FLAGS_MANIFEST_MODULE; - if (image && image->dynamic) - module_data->module_flags |= MODULE_FLAGS_DYNAMIC_MODULE; - if (image && image->aot_module) - module_data->module_flags |= MODULE_FLAGS_NATIVE_MODULE; - - module_data->module_il_path = NULL; - if (image && image->filename) { - /* if there's a filename, use it */ - module_data->module_il_path = image->filename; - } else if (image && image->module_name) { - /* otherwise, use the module name */ - module_data->module_il_path = image->module_name; - } - if (!module_data->module_il_path) - module_data->module_il_path = ""; - - module_data->module_il_pdb_path = ""; - module_data->module_il_pdb_age = 0; - - if (image && image->image_info) { - MonoPEDirEntry *debug_dir_entry = (MonoPEDirEntry *)&image->image_info->cli_header.datadir.pe_debug; - if (debug_dir_entry->size) { - ImageDebugDirectory debug_dir; - memset (&debug_dir, 0, sizeof (debug_dir)); - - uint32_t offset = mono_cli_rva_image_map (image, debug_dir_entry->rva); - for (uint32_t idx = 0; idx < debug_dir_entry->size / sizeof (ImageDebugDirectory); ++idx) { - uint8_t *data = (uint8_t *) ((ImageDebugDirectory *) (image->raw_data + offset) + idx); - debug_dir.major_version = read16 (data + 8); - debug_dir.minor_version = read16 (data + 10); - debug_dir.type = read32 (data + 12); - debug_dir.pointer = read32 (data + 24); - - if (debug_dir.type == DEBUG_DIR_ENTRY_CODEVIEW && debug_dir.major_version == 0x100 && debug_dir.minor_version == 0x504d) { - data = (uint8_t *)(image->raw_data + debug_dir.pointer); - int32_t signature = read32 (data); - if (signature == 0x53445352) { - memcpy (module_data->module_il_pdb_signature, data + 4, EP_GUID_SIZE); - module_data->module_il_pdb_age = read32 (data + 20); - module_data->module_il_pdb_path = (const char *)(data + 24); - break; - } - } - } - } - } - } - - return true; -} - -bool -ep_rt_mono_write_event_module_load (MonoImage *image) -{ - if (!EventEnabledModuleLoad_V2 () && !EventEnabledDomainModuleLoad_V1 ()) - return true; - - if (image) { - ModuleEventData module_data; - memset (&module_data, 0, sizeof (module_data)); - if (get_module_event_data (image, &module_data)) { - FireEtwModuleLoad_V2 ( - module_data.module_id, - module_data.assembly_id, - module_data.module_flags, - module_data.reserved_flags, - module_data.module_il_path, - module_data.module_native_path, - clr_instance_get_id (), - module_data.module_il_pdb_signature, - module_data.module_il_pdb_age, - module_data.module_il_pdb_path, - module_data.module_native_pdb_signature, - module_data.module_native_pdb_age, - module_data.module_native_pdb_path, - NULL, - NULL); - - FireEtwDomainModuleLoad_V1 ( - module_data.module_id, - module_data.assembly_id, - module_data.domain_id, - module_data.module_flags, - module_data.reserved_flags, - module_data.module_il_path, - module_data.module_native_path, - clr_instance_get_id (), - NULL, - NULL); - } - } - - return true; -} - -bool -ep_rt_mono_write_event_module_unload (MonoImage *image) -{ - if (!EventEnabledModuleUnload_V2()) - return true; - - if (image) { - ModuleEventData module_data; - memset (&module_data, 0, sizeof (module_data)); - if (get_module_event_data (image, &module_data)) { - FireEtwModuleUnload_V2 ( - module_data.module_id, - module_data.assembly_id, - module_data.module_flags, - module_data.reserved_flags, - module_data.module_il_path, - module_data.module_native_path, - clr_instance_get_id (), - module_data.module_il_pdb_signature, - module_data.module_il_pdb_age, - module_data.module_il_pdb_path, - module_data.module_native_pdb_signature, - module_data.module_native_pdb_age, - module_data.module_native_pdb_path, - NULL, - NULL); - } - } - - return true; -} - -static -bool -get_assembly_event_data ( - MonoAssembly *assembly, - AssemblyEventData *assembly_data) -{ - if (assembly && assembly_data) { - // Under netcore we only have root domain. - MonoDomain *root_domain = mono_get_root_domain (); - - assembly_data->domain_id = (uint64_t)root_domain; - assembly_data->assembly_id = (uint64_t)assembly; - assembly_data->binding_id = 0; - - assembly_data->assembly_flags = 0; - if (assembly->dynamic) - assembly_data->assembly_flags |= ASSEMBLY_FLAGS_DYNAMIC_ASSEMBLY; - - if (assembly->image && assembly->image->aot_module) - assembly_data->assembly_flags |= ASSEMBLY_FLAGS_NATIVE_ASSEMBLY; - - assembly_data->assembly_name = mono_stringify_assembly_name (&assembly->aname); - } - - return true; -} - -bool -ep_rt_mono_write_event_assembly_load (MonoAssembly *assembly) -{ - if (!EventEnabledAssemblyLoad_V1 ()) - return true; - - if (assembly) { - AssemblyEventData assembly_data; - memset (&assembly_data, 0, sizeof (assembly_data)); - if (get_assembly_event_data (assembly, &assembly_data)) { - FireEtwAssemblyLoad_V1 ( - assembly_data.assembly_id, - assembly_data.domain_id, - assembly_data.binding_id, - assembly_data.assembly_flags, - assembly_data.assembly_name, - clr_instance_get_id (), - NULL, - NULL); - - g_free (assembly_data.assembly_name); - } - } - - return true; -} - -bool -ep_rt_mono_write_event_assembly_unload (MonoAssembly *assembly) -{ - if (!EventEnabledAssemblyUnload_V1 ()) - return true; - - if (assembly) { - AssemblyEventData assembly_data; - memset (&assembly_data, 0, sizeof (assembly_data)); - if (get_assembly_event_data (assembly, &assembly_data)) { - FireEtwAssemblyUnload_V1 ( - assembly_data.assembly_id, - assembly_data.domain_id, - assembly_data.binding_id, - assembly_data.assembly_flags, - assembly_data.assembly_name, - clr_instance_get_id (), - NULL, - NULL); - - g_free (assembly_data.assembly_name); - } - } - - return true; -} - -bool -ep_rt_mono_write_event_thread_created (ep_rt_thread_id_t tid) -{ - if (!EventEnabledThreadCreated ()) - return true; - - uint64_t managed_thread = 0; - uint32_t native_thread_id = MONO_NATIVE_THREAD_ID_TO_UINT (tid); - uint32_t managed_thread_id = 0; - uint32_t flags = 0; - - MonoThread *thread = mono_thread_current (); - if (thread && mono_thread_info_get_tid (thread->thread_info) == tid) { - managed_thread_id = mono_thread_get_managed_id (thread); - managed_thread = (uint64_t)thread; - - switch (mono_thread_info_get_flags (thread->thread_info)) { - case MONO_THREAD_INFO_FLAGS_NO_GC: - case MONO_THREAD_INFO_FLAGS_NO_SAMPLE: - flags |= THREAD_FLAGS_GC_SPECIAL; - } - - if (mono_gc_is_finalizer_thread (thread)) - flags |= THREAD_FLAGS_FINALIZER; - - if (thread->threadpool_thread) - flags |= THREAD_FLAGS_THREADPOOL_WORKER; - } - - FireEtwThreadCreated ( - managed_thread, - (uint64_t)mono_get_root_domain (), - flags, - managed_thread_id, - native_thread_id, - clr_instance_get_id (), - NULL, - NULL); - - return true; -} - -bool -ep_rt_mono_write_event_thread_terminated (ep_rt_thread_id_t tid) -{ - if (!EventEnabledThreadTerminated ()) - return true; - - uint64_t managed_thread = 0; - MonoThread *thread = mono_thread_current (); - if (thread && mono_thread_info_get_tid (thread->thread_info) == tid) - managed_thread = (uint64_t)thread; - - FireEtwThreadTerminated ( - managed_thread, - (uint64_t)mono_get_root_domain (), - clr_instance_get_id (), - NULL, - NULL); - - return true; -} - -static -uint32_t -get_type_start_id (MonoType *type) -{ - uint32_t start_id = (uint32_t)(uintptr_t)type; - - start_id = (((start_id * 215497) >> 16) ^ ((start_id * 1823231) + start_id)); - -MONO_DISABLE_WARNING(4127) /* conditional expression is constant */ - // Mix in highest bits on 64-bit systems only - if (sizeof (type) > 4) - start_id = start_id ^ GUINT64_TO_UINT32 ((((uint64_t)type >> 31) >> 1)); -MONO_RESTORE_WARNING - - return start_id; -} - -bool -ep_rt_mono_write_event_type_load_start (MonoType *type) -{ - if (!EventEnabledTypeLoadStart ()) - return true; - - FireEtwTypeLoadStart ( - get_type_start_id (type), - clr_instance_get_id (), - NULL, - NULL); - - return true; -} - -bool -ep_rt_mono_write_event_type_load_stop (MonoType *type) -{ - if (!EventEnabledTypeLoadStop ()) - return true; - - char *type_name = NULL; - if (type) - type_name = mono_type_get_name_full (type, MONO_TYPE_NAME_FORMAT_IL); - - FireEtwTypeLoadStop ( - get_type_start_id (type), - clr_instance_get_id (), - 6 /* CLASS_LOADED */, - (uint64_t)type, - type_name, - NULL, - NULL); - - g_free (type_name); - - return true; -} - -static -gboolean -get_exception_ip_func ( - MonoStackFrameInfo *frame, - MonoContext *ctx, - void *data) -{ - *(uintptr_t *)data = (uintptr_t)MONO_CONTEXT_GET_IP (ctx); - return TRUE; -} - -bool -ep_rt_mono_write_event_exception_thrown (MonoObject *obj) -{ - if (!EventEnabledExceptionThrown_V1 ()) - return true; - - if (obj) { - ERROR_DECL (error); - char *type_name = NULL; - char *exception_message = NULL; - uint16_t flags = 0; - uint32_t hresult = 0; - uintptr_t ip = 0; - - if (mono_object_isinst_checked ((MonoObject *) obj, mono_get_exception_class (), error)) { - MonoException *exception = (MonoException *)obj; - flags |= EXCEPTION_THROWN_FLAGS_IS_CLS_COMPLIANT; - if (exception->inner_ex) - flags |= EXCEPTION_THROWN_FLAGS_HAS_INNER; - if (exception->message) - exception_message = ep_rt_utf16_to_utf8_string (mono_string_chars_internal (exception->message), mono_string_length_internal (exception->message)); - hresult = exception->hresult; - } - - if (exception_message == NULL) - exception_message = g_strdup (""); - - if (mono_get_eh_callbacks ()->mono_walk_stack_with_ctx) - mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (get_exception_ip_func, NULL, MONO_UNWIND_SIGNAL_SAFE, (void *)&ip); - - type_name = mono_type_get_name_full (m_class_get_byval_arg (mono_object_class (obj)), MONO_TYPE_NAME_FORMAT_IL); - - FireEtwExceptionThrown_V1 ( - type_name, - exception_message, - (void *)&ip, - hresult, - flags, - clr_instance_get_id (), - NULL, - NULL); - - if (!mono_component_profiler_clauses_enabled ()) { - FireEtwExceptionThrownStop ( - NULL, - NULL); - } - - g_free (exception_message); - g_free (type_name); - - mono_error_cleanup (error); - } - - return true; -} - -bool -ep_rt_mono_write_event_exception_clause ( - MonoMethod *method, - uint32_t clause_num, - MonoExceptionEnum clause_type, - MonoObject *obj) -{ - if (!mono_component_profiler_clauses_enabled ()) - return true; - - if ((clause_type == MONO_EXCEPTION_CLAUSE_FAULT || clause_type == MONO_EXCEPTION_CLAUSE_NONE) && (!EventEnabledExceptionCatchStart() || !EventEnabledExceptionCatchStop())) - return true; - - if (clause_type == MONO_EXCEPTION_CLAUSE_FILTER && (!EventEnabledExceptionFilterStart() || !EventEnabledExceptionFilterStop())) - return true; - - if (clause_type == MONO_EXCEPTION_CLAUSE_FINALLY && (!EventEnabledExceptionFinallyStart() || !EventEnabledExceptionFinallyStop())) - return true; - - uintptr_t ip = 0; //TODO: Have profiler pass along IP of handler block. - uint64_t method_id = (uint64_t)method; - char *method_name = NULL; - - method_name = mono_method_get_name_full (method, TRUE, TRUE, MONO_TYPE_NAME_FORMAT_IL); - - if ((clause_type == MONO_EXCEPTION_CLAUSE_FAULT || clause_type == MONO_EXCEPTION_CLAUSE_NONE)) { - FireEtwExceptionCatchStart ( - (uint64_t)ip, - method_id, - (const ep_char8_t *)method_name, - clr_instance_get_id (), - NULL, - NULL); - - FireEtwExceptionCatchStop ( - NULL, - NULL); - - FireEtwExceptionThrownStop ( - NULL, - NULL); - } - - if (clause_type == MONO_EXCEPTION_CLAUSE_FILTER) { - FireEtwExceptionFilterStart ( - (uint64_t)ip, - method_id, - (const ep_char8_t *)method_name, - clr_instance_get_id (), - NULL, - NULL); - - FireEtwExceptionFilterStop ( - NULL, - NULL); - } - - if (clause_type == MONO_EXCEPTION_CLAUSE_FINALLY) { - FireEtwExceptionFinallyStart ( - (uint64_t)ip, - method_id, - (const ep_char8_t *)method_name, - clr_instance_get_id (), - NULL, - NULL); - - FireEtwExceptionFinallyStop ( - NULL, - NULL); - } - - g_free (method_name); - return true; -} - -bool -ep_rt_mono_write_event_monitor_contention_start (MonoObject *obj) -{ - if (!EventEnabledContentionStart_V1 ()) - return true; - - FireEtwContentionStart_V1 ( - 0 /* ManagedContention */, - clr_instance_get_id (), - NULL, - NULL); - - return true; -} - -bool -ep_rt_mono_write_event_monitor_contention_stop (MonoObject *obj) -{ - if (!EventEnabledContentionStop ()) - return true; - - FireEtwContentionStop ( - 0 /* ManagedContention */, - clr_instance_get_id (), - NULL, - NULL); - - return true; -} - -bool -ep_rt_mono_write_event_method_jit_memory_allocated_for_code ( - const uint8_t *buffer, - uint64_t size, - MonoProfilerCodeBufferType type, - const void *data) -{ - if (!EventEnabledMethodJitMemoryAllocatedForCode ()) - return true; - - if (type != MONO_PROFILER_CODE_BUFFER_METHOD) - return true; - - uint64_t method_id = 0; - uint64_t module_id = 0; - - if (data) { - MonoMethod *method; - method = (MonoMethod *)data; - method_id = (uint64_t)method; - if (method->klass) - module_id = (uint64_t)(uint64_t)m_class_get_image (method->klass); - } - - FireEtwMethodJitMemoryAllocatedForCode ( - method_id, - module_id, - size, - 0, - size, - 0 /* CORJIT_ALLOCMEM_DEFAULT_CODE_ALIGN */, - clr_instance_get_id (), - NULL, - NULL); - - return true; -} - -bool -ep_rt_write_event_threadpool_worker_thread_start ( - uint32_t active_thread_count, - uint32_t retired_worker_thread_count, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolWorkerThreadStart ( - active_thread_count, - retired_worker_thread_count, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_threadpool_worker_thread_stop ( - uint32_t active_thread_count, - uint32_t retired_worker_thread_count, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolWorkerThreadStop ( - active_thread_count, - retired_worker_thread_count, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_threadpool_worker_thread_wait ( - uint32_t active_thread_count, - uint32_t retired_worker_thread_count, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolWorkerThreadWait ( - active_thread_count, - retired_worker_thread_count, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_threadpool_min_max_threads ( - uint16_t min_worker_threads, - uint16_t max_worker_threads, - uint16_t min_io_completion_threads, - uint16_t max_io_completion_threads, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolMinMaxThreads ( - min_worker_threads, - max_worker_threads, - min_io_completion_threads, - max_io_completion_threads, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_threadpool_worker_thread_adjustment_sample ( - double throughput, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolWorkerThreadAdjustmentSample ( - throughput, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_threadpool_worker_thread_adjustment_adjustment ( - double average_throughput, - uint32_t networker_thread_count, - /*NativeRuntimeEventSource.ThreadAdjustmentReasonMap*/ int32_t reason, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolWorkerThreadAdjustmentAdjustment ( - average_throughput, - networker_thread_count, - reason, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_threadpool_worker_thread_adjustment_stats ( - double duration, - double throughput, - double threadpool_worker_thread_wait, - double throughput_wave, - double throughput_error_estimate, - double average_throughput_error_estimate, - double throughput_ratio, - double confidence, - double new_control_setting, - uint16_t new_thread_wave_magnitude, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolWorkerThreadAdjustmentStats ( - duration, - throughput, - threadpool_worker_thread_wait, - throughput_wave, - throughput_error_estimate, - average_throughput_error_estimate, - throughput_ratio, - confidence, - new_control_setting, - new_thread_wave_magnitude, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_threadpool_io_enqueue ( - intptr_t native_overlapped, - intptr_t overlapped, - bool multi_dequeues, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolIOEnqueue ( - (const void *)native_overlapped, - (const void *)overlapped, - multi_dequeues, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_threadpool_io_dequeue ( - intptr_t native_overlapped, - intptr_t overlapped, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolIODequeue ( - (const void *)native_overlapped, - (const void *)overlapped, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_threadpool_working_thread_count ( - uint16_t count, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolWorkingThreadCount ( - count, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_threadpool_io_pack ( - intptr_t native_overlapped, - intptr_t overlapped, - uint16_t clr_instance_id) -{ - return FireEtwThreadPoolIOPack ( - (const void *)native_overlapped, - (const void *)overlapped, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_contention_lock_created ( - intptr_t lock_id, - intptr_t associated_object_id, - uint16_t clr_instance_id) -{ - return FireEtwContentionLockCreated ( - (const void *)lock_id, - (const void *)associated_object_id, - clr_instance_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_contention_start ( - uint8_t contention_flags, - uint16_t clr_instance_id, - intptr_t lock_id, - intptr_t associated_object_id, - uint64_t lock_owner_thread_id) -{ - return FireEtwContentionStart_V2 ( - contention_flags, - clr_instance_id, - (const void *)lock_id, - (const void *)associated_object_id, - lock_owner_thread_id, - NULL, - NULL) == 0 ? true : false; -} - -bool -ep_rt_write_event_contention_stop ( - uint8_t contention_flags, - uint16_t clr_instance_id, - double duration_ns) -{ - return FireEtwContentionStop_V1 ( - contention_flags, - clr_instance_id, - duration_ns, - NULL, - NULL) == 0 ? true : false; -} - -static -void -runtime_profiler_jit_begin ( - MonoProfiler *prof, - MonoMethod *method) -{ - ep_rt_mono_write_event_jit_start (method); -} - -static -void -runtime_profiler_jit_failed ( - MonoProfiler *prof, - MonoMethod *method) -{ - //TODO: CoreCLR doesn't have this case, so no failure event currently exists. -} - -static -void -runtime_profiler_jit_done ( - MonoProfiler *prof, - MonoMethod *method, - MonoJitInfo *ji) -{ - ep_rt_mono_write_event_method_load (method, ji); - ep_rt_mono_write_event_method_il_to_native_map (method, ji); -} - -static -void -runtime_profiler_image_loaded ( - MonoProfiler *prof, - MonoImage *image) -{ - if (image && image->heap_pdb.size == 0) - ep_rt_mono_write_event_module_load (image); -} - -static -void -runtime_profiler_image_unloaded ( - MonoProfiler *prof, - MonoImage *image) -{ - if (image && image->heap_pdb.size == 0) - ep_rt_mono_write_event_module_unload (image); -} - -static -void -runtime_profiler_assembly_loaded ( - MonoProfiler *prof, - MonoAssembly *assembly) -{ - ep_rt_mono_write_event_assembly_load (assembly); -} - -static -void -runtime_profiler_assembly_unloaded ( - MonoProfiler *prof, - MonoAssembly *assembly) -{ - ep_rt_mono_write_event_assembly_unload (assembly); -} - -static -void -runtime_profiler_thread_started ( - MonoProfiler *prof, - uintptr_t tid) -{ - ep_rt_mono_write_event_thread_created (ep_rt_uint64_t_to_thread_id_t (tid)); -} - -static -void -runtime_profiler_thread_stopped ( - MonoProfiler *prof, - uintptr_t tid) -{ - ep_rt_mono_write_event_thread_terminated (ep_rt_uint64_t_to_thread_id_t (tid)); -} - -static -void -runtime_profiler_class_loading ( - MonoProfiler *prof, - MonoClass *klass) -{ - bool prevent_profiler_event_recursion = FALSE; - EventPipeThreadData *thread_data = eventpipe_thread_data_get_or_create (); - if (thread_data) { - // Prevent additional class loading to happen recursively as part of fire TypeLoadStart event. - // Additional class loading can happen as part of capturing callstack for TypeLoadStart event. - prevent_profiler_event_recursion = thread_data->prevent_profiler_event_recursion; - thread_data->prevent_profiler_event_recursion = TRUE; - } - - ep_rt_mono_write_event_type_load_start (m_class_get_byval_arg (klass)); - - if (thread_data) - thread_data->prevent_profiler_event_recursion = prevent_profiler_event_recursion; -} - -static -void -runtime_profiler_class_failed ( - MonoProfiler *prof, - MonoClass *klass) -{ - ep_rt_mono_write_event_type_load_stop (m_class_get_byval_arg (klass)); -} - -static -void -runtime_profiler_class_loaded ( - MonoProfiler *prof, - MonoClass *klass) -{ - ep_rt_mono_write_event_type_load_stop (m_class_get_byval_arg (klass)); -} - -static -void -runtime_profiler_exception_throw ( - MonoProfiler *prof, - MonoObject *exc) -{ - ep_rt_mono_write_event_exception_thrown (exc); -} - -static -void -runtime_profiler_exception_clause ( - MonoProfiler *prof, - MonoMethod *method, - uint32_t clause_num, - MonoExceptionEnum clause_type, - MonoObject *exc) -{ - ep_rt_mono_write_event_exception_clause (method, clause_num, clause_type, exc); -} - -static -void -runtime_profiler_monitor_contention ( - MonoProfiler *prof, - MonoObject *obj) -{ - ep_rt_mono_write_event_monitor_contention_start (obj); -} - -static -void -runtime_profiler_monitor_acquired ( - MonoProfiler *prof, - MonoObject *obj) -{ - ep_rt_mono_write_event_monitor_contention_stop (obj); -} - -static -void -runtime_profiler_monitor_failed ( - MonoProfiler *prof, - MonoObject *obj) -{ - ep_rt_mono_write_event_monitor_contention_stop (obj); -} - -static -void -runtime_profiler_jit_code_buffer ( - MonoProfiler *prof, - const mono_byte *buffer, - uint64_t size, - MonoProfilerCodeBufferType type, - const void *data) -{ - ep_rt_mono_write_event_method_jit_memory_allocated_for_code ((const uint8_t *)buffer, size, type, data); -} - -void -EventPipeEtwCallbackDotNETRuntime ( - const uint8_t *source_id, - unsigned long is_enabled, - uint8_t level, - uint64_t match_any_keywords, - uint64_t match_all_keywords, - EventFilterDescriptor *filter_data, - void *callback_data) -{ - ep_rt_config_requires_lock_not_held (); - - EP_ASSERT(is_enabled == 0 || is_enabled == 1) ; - EP_ASSERT (_ep_rt_dotnet_runtime_profiler_provider != NULL); - - match_any_keywords = (is_enabled == 1) ? match_any_keywords : 0; - - EP_LOCK_ENTER (section1) - uint64_t enabled_keywords = MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask; - - if (profiler_callback_is_enabled(match_any_keywords, JIT_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { - mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_jit_begin); - mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_jit_failed); - mono_profiler_set_jit_done_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_jit_done); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { - mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - mono_profiler_set_jit_done_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, LOADER_KEYWORD)) { - if (!profiler_callback_is_enabled(enabled_keywords, LOADER_KEYWORD)) { - mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_image_loaded); - mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_image_unloaded); - mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_assembly_loaded); - mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_assembly_unloaded); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, LOADER_KEYWORD)) { - mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD) || profiler_callback_is_enabled(match_any_keywords, THREADING_KEYWORD)) { - if (!(profiler_callback_is_enabled(enabled_keywords, APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD) && profiler_callback_is_enabled(enabled_keywords, THREADING_KEYWORD))) { - mono_profiler_set_thread_started_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_thread_started); - mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_thread_stopped); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD) || profiler_callback_is_enabled (enabled_keywords, THREADING_KEYWORD)) { - mono_profiler_set_thread_started_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, TYPE_DIAGNOSTIC_KEYWORD)) { - if (!profiler_callback_is_enabled(enabled_keywords, TYPE_DIAGNOSTIC_KEYWORD)) { - mono_profiler_set_class_loading_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_class_loading); - mono_profiler_set_class_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_class_failed); - mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_class_loaded); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, TYPE_DIAGNOSTIC_KEYWORD)) { - mono_profiler_set_class_loading_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - mono_profiler_set_class_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, EXCEPTION_KEYWORD)) { - if (!profiler_callback_is_enabled(enabled_keywords, EXCEPTION_KEYWORD)) { - mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_exception_throw); - mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_exception_clause); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, EXCEPTION_KEYWORD)) { - mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, CONTENTION_KEYWORD)) { - if (!profiler_callback_is_enabled(enabled_keywords, CONTENTION_KEYWORD)) { - mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_monitor_contention); - mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_monitor_acquired); - mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, runtime_profiler_monitor_failed); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, CONTENTION_KEYWORD)) { - mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_runtime_profiler_provider, NULL); - } - } - - MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.Level = level; - MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; - MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); - EP_LOCK_EXIT (section1) - -ep_on_exit: - ep_rt_config_requires_lock_not_held (); - return; - -ep_on_error: - ep_exit_error_handler (); -} - -void -EventPipeEtwCallbackDotNETRuntimeRundown ( - const uint8_t *source_id, - unsigned long is_enabled, - uint8_t level, - uint64_t match_any_keywords, - uint64_t match_all_keywords, - EventFilterDescriptor *filter_data, - void *callback_data) -{ - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.Level = level; - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; - MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); -} - -void -EventPipeEtwCallbackDotNETRuntimePrivate ( - const uint8_t *source_id, - unsigned long is_enabled, - uint8_t level, - uint64_t match_any_keywords, - uint64_t match_all_keywords, - EventFilterDescriptor *filter_data, - void *callback_data) -{ - MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.Level = level; - MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; - MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); -} - -void -EventPipeEtwCallbackDotNETRuntimeStress ( - const uint8_t *source_id, - unsigned long is_enabled, - uint8_t level, - uint64_t match_any_keywords, - uint64_t match_all_keywords, - EventFilterDescriptor *filter_data, - void *callback_data) -{ - MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_EVENTPIPE_Context.Level = level; - MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; - MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); -} - -static -inline -mono_profiler_gc_state_t -mono_profiler_volatile_load_gc_state_t (const volatile mono_profiler_gc_state_t *ptr) -{ - return ep_rt_volatile_load_uint32_t ((const volatile uint32_t *)ptr); -} - -static -inline -mono_profiler_gc_state_t -mono_profiler_atomic_cas_gc_state_t (volatile mono_profiler_gc_state_t *target, mono_profiler_gc_state_t expected, mono_profiler_gc_state_t value) -{ - return (mono_profiler_gc_state_t)(mono_atomic_cas_i32 ((volatile gint32 *)(target), (gint32)(value), (gint32)(expected))); -} - -static -void -mono_profiler_fire_event_enter (void) -{ - mono_profiler_gc_state_t old_state = 0; - mono_profiler_gc_state_t new_state = 0; - - // NOTE, mono_profiler_fire_event_start should never be called recursivly. - do { - old_state = mono_profiler_volatile_load_gc_state_t (&_ep_rt_mono_profiler_gc_state); - if (MONO_PROFILER_GC_STATE_IS_GC_IN_PROGRESS (old_state)) { - // GC in progress and thread tries to fire event (this should be an unlikely scenario). Wait until GC is done. - ep_rt_spin_lock_acquire (&_ep_rt_mono_profiler_gc_state_lock); - ep_rt_spin_lock_release (&_ep_rt_mono_profiler_gc_state_lock); - old_state = mono_profiler_volatile_load_gc_state_t (&_ep_rt_mono_profiler_gc_state); - } - // Increase number of fire event calls. - new_state = MONO_PROFILER_GC_STATE_INC_FIRE_EVENT_COUNT (old_state); - } while (mono_profiler_atomic_cas_gc_state_t (&_ep_rt_mono_profiler_gc_state, old_state, new_state) != old_state); -} - -static -void -mono_profiler_fire_event_exit (void) -{ - mono_profiler_gc_state_t old_state = 0; - mono_profiler_gc_state_t new_state = 0; - - do { - old_state = mono_profiler_volatile_load_gc_state_t (&_ep_rt_mono_profiler_gc_state); - new_state = MONO_PROFILER_GC_STATE_DEC_FIRE_EVENT_COUNT (old_state); - } while (mono_profiler_atomic_cas_gc_state_t (&_ep_rt_mono_profiler_gc_state, old_state, new_state) != old_state); -} - -static -void -mono_profiler_gc_in_progress_start (void) -{ - mono_profiler_gc_state_t old_state = 0; - mono_profiler_gc_state_t new_state = 0; - - // Make sure fire event calls will block and wait for GC completion. - ep_rt_spin_lock_acquire (&_ep_rt_mono_profiler_gc_state_lock); - - // Set gc in progress state, preventing new fire event requests. - do { - old_state = mono_profiler_volatile_load_gc_state_t (&_ep_rt_mono_profiler_gc_state); - EP_ASSERT (!MONO_PROFILER_GC_STATE_IS_GC_IN_PROGRESS (old_state)); - new_state = MONO_PROFILER_GC_STATE_GC_IN_PROGRESS_START (old_state); - } while (mono_profiler_atomic_cas_gc_state_t (&_ep_rt_mono_profiler_gc_state, old_state, new_state) != old_state); - - mono_profiler_gc_state_count_t count = MONO_PROFILER_GC_STATE_GET_FIRE_EVENT_COUNT (new_state); - - // Wait for all fire events to complete before progressing with gc. - // NOTE, mono_profiler_fire_event_start should never be called recursivly. - // Default yield count used in SpinLock.cs. - int yield_count = 40; - while (count) { - if (yield_count > 0) { - ep_rt_mono_thread_yield (); - yield_count--; - } else { - ep_rt_thread_sleep (200); - } - count = MONO_PROFILER_GC_STATE_GET_FIRE_EVENT_COUNT (mono_profiler_volatile_load_gc_state_t (&_ep_rt_mono_profiler_gc_state)); - } -} - -static -void -mono_profiler_gc_in_progress_stop (void) -{ - mono_profiler_gc_state_t old_state = 0; - mono_profiler_gc_state_t new_state = 0; - - // Reset gc in progress state. - do { - old_state = mono_profiler_volatile_load_gc_state_t (&_ep_rt_mono_profiler_gc_state); - EP_ASSERT (MONO_PROFILER_GC_STATE_IS_GC_IN_PROGRESS (old_state)); - - new_state = MONO_PROFILER_GC_STATE_GC_IN_PROGRESS_STOP (old_state); - EP_ASSERT (!MONO_PROFILER_GC_STATE_IS_GC_IN_PROGRESS (new_state)); - } while (mono_profiler_atomic_cas_gc_state_t (&_ep_rt_mono_profiler_gc_state, old_state, new_state) != old_state); - - // Make sure fire events can continune to execute. - ep_rt_spin_lock_release (&_ep_rt_mono_profiler_gc_state_lock); -} - -static -inline -bool -mono_profiler_gc_in_progress (void) -{ - return MONO_PROFILER_GC_STATE_IS_GC_IN_PROGRESS (mono_profiler_volatile_load_gc_state_t (&_ep_rt_mono_profiler_gc_state)); -} - -static -inline -bool -mono_profiler_gc_can_collect_heap (void) -{ - return _ep_rt_mono_profiler_gc_can_collect_heap; -} - -static -inline -void -mono_profiler_gc_heap_collect_requests_inc (void) -{ - EP_ASSERT (mono_profiler_gc_can_collect_heap ()); - ep_rt_atomic_inc_uint32_t (&_ep_rt_mono_profiler_gc_heap_collect_requests); -} - -static -inline -void -mono_profiler_gc_heap_collect_requests_dec (void) -{ - EP_ASSERT (mono_profiler_gc_can_collect_heap ()); - ep_rt_atomic_dec_uint32_t (&_ep_rt_mono_profiler_gc_heap_collect_requests); -} - -static -inline -bool -mono_profiler_gc_heap_collect_requested (void) -{ - if (!mono_profiler_gc_can_collect_heap ()) - return false; - - return ep_rt_volatile_load_uint32_t(&_ep_rt_mono_profiler_gc_heap_collect_requests) != 0 ? true : false; -} - -static -inline -bool -mono_profiler_gc_heap_collect_in_progress (void) -{ - ep_rt_spin_lock_requires_lock_held (&_ep_rt_mono_profiler_gc_state_lock); - return ep_rt_volatile_load_uint32_t_without_barrier (&_ep_rt_mono_profiler_gc_heap_collect_in_progress) != 0 ? true : false; -} - -static -inline -void -mono_profiler_gc_heap_collect_in_progress_start (void) -{ - EP_ASSERT (mono_profiler_gc_can_collect_heap ()); - - ep_rt_spin_lock_requires_lock_held (&_ep_rt_mono_profiler_gc_state_lock); - ep_rt_volatile_store_uint32_t_without_barrier (&_ep_rt_mono_profiler_gc_heap_collect_in_progress, 1); -} - -static -inline -void -mono_profiler_gc_heap_collect_in_progress_stop (void) -{ - EP_ASSERT (mono_profiler_gc_can_collect_heap ()); - - ep_rt_spin_lock_requires_lock_held (&_ep_rt_mono_profiler_gc_state_lock); - ep_rt_volatile_store_uint32_t_without_barrier (&_ep_rt_mono_profiler_gc_heap_collect_in_progress, 0); -} - -static -MonoProfilerMemBlock * -mono_profiler_mem_block_alloc (uint32_t req_size) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - MonoProfilerMemBlock *prev = NULL; - - uint32_t size = MONO_PROFILER_MEM_DEFAULT_BLOCK_SIZE; - while (size - sizeof(MonoProfilerMemBlock) < req_size) - size += MONO_PROFILER_MEM_BLOCK_SIZE_INC; - - MonoProfilerMemBlock *block = mono_valloc (NULL, size, MONO_MMAP_READ | MONO_MMAP_WRITE | MONO_MMAP_ANON | MONO_MMAP_PRIVATE, MONO_MEM_ACCOUNT_PROFILER); - if (block) { - block->alloc_size = size; - block->start = (uint8_t *)ALIGN_PTR_TO ((uint8_t *)block + sizeof (MonoProfilerMemBlock), 16); - block->size = (uint32_t)(((uint8_t*)block + size) - (uint8_t*)block->start); - block->offset = 0; - block->last_used_offset = 0; - - while (true) { - prev = (MonoProfilerMemBlock *)ep_rt_volatile_load_ptr_without_barrier ((volatile void **)&_ep_rt_mono_profiler_mem_blocks); - if (mono_atomic_cas_ptr ((volatile gpointer*)&_ep_rt_mono_profiler_mem_blocks, block, prev) == prev) - break; - } - - if (prev) - prev->next = block; - block->prev = prev; - } - - return block; -} - -static -uint8_t * -mono_profiler_mem_alloc (uint32_t req_size) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - MonoProfilerMemBlock *current_block = (MonoProfilerMemBlock *)ep_rt_volatile_load_ptr_without_barrier ((volatile void **)&_ep_rt_mono_profiler_current_mem_block); - uint8_t *buffer = NULL; - - if (!current_block) { - current_block = mono_profiler_mem_block_alloc (req_size); - if (current_block) { - mono_memory_barrier (); - ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_ep_rt_mono_profiler_current_mem_block, current_block); - } - } - - if (current_block) { - uint32_t prev_offset = (uint32_t)mono_atomic_fetch_add_i32 ((volatile int32_t *)¤t_block->offset, (int32_t)req_size); - if (prev_offset + req_size > current_block->size) { - if (prev_offset <= current_block->size) - current_block->last_used_offset = prev_offset; - current_block = mono_profiler_mem_block_alloc (req_size); - if (current_block) { - buffer = current_block->start; - current_block->offset += req_size; - mono_memory_barrier (); - ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_ep_rt_mono_profiler_current_mem_block, current_block); - } - } else { - buffer = (uint8_t*)current_block->start + prev_offset; - } - } - - return buffer; -} - -static -void -mono_profiler_mem_block_free_all (void) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - MonoProfilerMemBlock *current_block = (MonoProfilerMemBlock *)ep_rt_volatile_load_ptr ((volatile void **)&_ep_rt_mono_profiler_current_mem_block); - - ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_ep_rt_mono_profiler_current_mem_block, NULL); - ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_ep_rt_mono_profiler_mem_blocks, NULL); - - mono_memory_barrier (); - - while (current_block) { - MonoProfilerMemBlock *prev_block = current_block->prev; - mono_vfree ((uint8_t *)current_block, current_block->alloc_size, MONO_MEM_ACCOUNT_MEM_MANAGER); - current_block = prev_block; - } -} - -static -void -mono_profiler_mem_block_free_all_but_current (void) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - MonoProfilerMemBlock *block_to_keep = (MonoProfilerMemBlock *)ep_rt_volatile_load_ptr ((volatile void **)&_ep_rt_mono_profiler_current_mem_block); - MonoProfilerMemBlock *current_block = block_to_keep; - - ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_ep_rt_mono_profiler_current_mem_block, NULL); - ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_ep_rt_mono_profiler_mem_blocks, NULL); - - mono_memory_barrier (); - - if (current_block) { - if (current_block->prev) { - current_block = current_block->prev; - while (current_block) { - MonoProfilerMemBlock *prev_block = current_block->prev; - mono_vfree ((uint8_t *)current_block, current_block->alloc_size, MONO_MEM_ACCOUNT_MEM_MANAGER); - current_block = prev_block; - } - } - } - - if (block_to_keep) { - block_to_keep->prev = NULL; - block_to_keep->next = NULL; - block_to_keep->offset = 0; - block_to_keep->last_used_offset = 0; - } - - mono_memory_barrier (); - - ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_ep_rt_mono_profiler_current_mem_block, block_to_keep); - ep_rt_volatile_store_ptr_without_barrier ((volatile void **)&_ep_rt_mono_profiler_mem_blocks, block_to_keep); -} - -static -inline -uint8_t * -mono_profiler_buffered_gc_event_alloc (uint32_t req_size) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - return mono_profiler_mem_alloc (req_size + sizeof (MonoProfilerBufferedGCEvent)); -} - -static -void -mono_profiler_trigger_heap_collect (MonoProfiler *prof) -{ - if (mono_profiler_gc_heap_collect_requested ()) { - ep_rt_spin_lock_acquire (&_ep_rt_mono_profiler_gc_state_lock); - mono_profiler_gc_heap_collect_requests_dec (); - mono_profiler_gc_heap_collect_in_progress_start (); - ep_rt_spin_lock_release (&_ep_rt_mono_profiler_gc_state_lock); - - mono_gc_collect (mono_gc_max_generation ()); - - ep_rt_spin_lock_acquire (&_ep_rt_mono_profiler_gc_state_lock); - mono_profiler_pop_gc_heap_collect_param_request_value (); - mono_profiler_gc_heap_collect_in_progress_stop (); - ep_rt_spin_lock_release (&_ep_rt_mono_profiler_gc_state_lock); - } -} - -static -void -mono_profiler_fire_gc_event_root_register ( - uint8_t *data, - uint32_t payload_size) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uintptr_t root_id; - uintptr_t root_size; - uint8_t root_source; - uintptr_t root_key; - - memcpy (&root_id, data, sizeof (root_id)); - data += sizeof (root_id); - - memcpy (&root_size, data, sizeof (root_size)); - data += sizeof (root_size); - - memcpy (&root_source, data, sizeof (root_source)); - data += sizeof (root_source); - - memcpy (&root_key, data, sizeof (root_key)); - data += sizeof (root_key); - - FireEtwMonoProfilerGCRootRegister ( - (const void *)root_id, - (uint64_t)root_size, - root_source, - (uint64_t)root_key, - (const ep_char8_t *)data, - NULL, - NULL); -} - -static -void -mono_profiler_fire_buffered_gc_event_root_register ( - MonoProfiler *prof, - const mono_byte *start, - uintptr_t size, - MonoGCRootSource source, - const void * key, - const char * name) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uintptr_t root_id = (uintptr_t)start; - uintptr_t root_size = size; - uint8_t root_source = (uint8_t)source; - uintptr_t root_key = (uintptr_t)key; - const char *root_name = (name ? name : ""); - size_t root_name_len = strlen (root_name) + 1; - - MonoProfilerBufferedGCEvent gc_event_data; - gc_event_data.type = MONO_PROFILER_BUFFERED_GC_EVENT_ROOT_REGISTER; - gc_event_data.payload_size = (uint32_t) - (sizeof (root_id) + - sizeof (root_size) + - sizeof (root_source) + - sizeof (root_key) + - root_name_len); - - uint8_t * buffer = mono_profiler_buffered_gc_event_alloc (gc_event_data.payload_size); - if (buffer) { - // Internal header - memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); - buffer += sizeof (gc_event_data); - - // GCEvent.RootID - memcpy(buffer, &root_id, sizeof (root_id)); - buffer += sizeof (root_id); - - // GCEvent.RootSize - memcpy(buffer, &root_size, sizeof (root_size)); - buffer += sizeof (root_size); - - // GCEvent.RootType - memcpy(buffer, &root_source, sizeof (root_source)); - buffer += sizeof (root_source); - - // GCEvent.RootKeyID - memcpy(buffer, &root_key, sizeof (root_key)); - buffer += sizeof (root_key); - - // GCEvent.RootKeyName - memcpy(buffer, root_name, root_name_len); - } -} - -static -void -mono_profiler_fire_gc_event_root_unregister ( - uint8_t *data, - uint32_t payload_size) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uintptr_t root_id; - - memcpy (&root_id, data, sizeof (root_id)); - - FireEtwMonoProfilerGCRootUnregister ( - (const void *)root_id, - NULL, - NULL); -} - -static -void -mono_profiler_fire_buffered_gc_event_root_unregister ( - MonoProfiler *prof, - const mono_byte *start) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uintptr_t root_id = (uintptr_t)start; - - MonoProfilerBufferedGCEvent gc_event_data; - gc_event_data.type = MONO_PROFILER_BUFFERED_GC_EVENT_ROOT_UNREGISTER; - gc_event_data.payload_size = sizeof (root_id); - - uint8_t * buffer = mono_profiler_buffered_gc_event_alloc (gc_event_data.payload_size); - if (buffer) { - // Internal header - memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); - buffer += sizeof (gc_event_data); - - // GCEvent.RootID - memcpy(buffer, &root_id, sizeof (root_id)); - } -} - -static -void -mono_profiler_fire_gc_event ( - uint8_t *data, - uint32_t payload_size) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uint8_t gc_event_type; - uint32_t generation; - - memcpy (&gc_event_type, data, sizeof (gc_event_type)); - data += sizeof (gc_event_type); - - memcpy (&generation, data, sizeof (generation)); - - FireEtwMonoProfilerGCEvent ( - gc_event_type, - generation, - NULL, - NULL); -} - -static -void -mono_profiler_fire_buffered_gc_event ( - uint8_t gc_event_type, - uint32_t generation) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - MonoProfilerBufferedGCEvent gc_event_data; - gc_event_data.type = MONO_PROFILER_BUFFERED_GC_EVENT; - gc_event_data.payload_size = - sizeof (gc_event_type) + - sizeof (generation); - - uint8_t * buffer = mono_profiler_buffered_gc_event_alloc (gc_event_data.payload_size); - if (buffer) { - // Internal header - memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); - buffer += sizeof (gc_event_data); - - // GCEvent.GCEventType - memcpy(buffer, &gc_event_type, sizeof (gc_event_type)); - buffer += sizeof (gc_event_type); - - // GCEvent.GCGeneration - memcpy(buffer, &generation, sizeof (generation)); - } -} - -static -void -mono_profiler_fire_gc_event_resize ( - uint8_t *data, - uint32_t payload_size) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uintptr_t size; - - memcpy (&size, data, sizeof (size)); - - FireEtwMonoProfilerGCResize ( - (uint64_t)size, - NULL, - NULL); -} - -static -void -mono_profiler_fire_buffered_gc_event_resize ( - MonoProfiler *prof, - uintptr_t size) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - MonoProfilerBufferedGCEvent gc_event_data; - gc_event_data.type = MONO_PROFILER_BUFFERED_GC_EVENT_RESIZE; - gc_event_data.payload_size = sizeof (size); - - uint8_t * buffer = mono_profiler_buffered_gc_event_alloc (gc_event_data.payload_size); - if (buffer) { - // Internal header - memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); - buffer += sizeof (gc_event_data); - - // GCResize.NewSize - memcpy(buffer, &size, sizeof (size)); - } -} - -static -void -mono_profiler_fire_gc_event_moves ( - uint8_t *data, - uint32_t payload_size) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uint64_t count; - - memcpy (&count, data, sizeof (count)); - data += sizeof (count); - - FireEtwMonoProfilerGCMoves ( - (uint32_t)count, - sizeof (uintptr_t) + sizeof (uintptr_t), - data, - NULL, - NULL); -} - -static -void -mono_profiler_fire_buffered_gc_event_moves ( - MonoProfiler *prof, - MonoObject *const* objects, - uint64_t count) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uintptr_t object_id; - uintptr_t address_id; - - // Serialized as object_id/address_id pairs. - count = count / 2; - - MonoProfilerBufferedGCEvent gc_event_data; - gc_event_data.type = MONO_PROFILER_BUFFERED_GC_EVENT_MOVES; - gc_event_data.payload_size = - (uint32_t)(sizeof (count) + - (count * (sizeof (uintptr_t) + sizeof (uintptr_t)))); - - uint8_t * buffer = mono_profiler_buffered_gc_event_alloc (gc_event_data.payload_size); - if (buffer) { - // Internal header - memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); - buffer += sizeof (gc_event_data); - - // GCMoves.Count - memcpy (buffer, &count, sizeof (count)); - buffer += sizeof (count); - - // Serialize directly as memory stream expected by FireEtwMonoProfilerGCMoves. - for (uint64_t i = 0; i < count; i++) { - // GCMoves.Values[].ObjectID. - object_id = (uintptr_t)SGEN_POINTER_UNTAG_ALL (*objects); - ep_write_buffer_uintptr_t (&buffer, object_id); - objects++; - - // GCMoves.Values[].AddressID. - address_id = (uintptr_t)*objects; - ep_write_buffer_uintptr_t (&buffer, address_id); - objects++; - } - } -} - -static -void -mono_profiler_fire_gc_event_roots ( - uint8_t *data, - uint32_t payload_size) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uint64_t count; - - memcpy (&count, data, sizeof (count)); - data += sizeof (count); - - FireEtwMonoProfilerGCRoots ( - (uint32_t)count, - sizeof (uintptr_t) + sizeof (uintptr_t), - data, - NULL, - NULL); -} - -static -void -mono_profiler_fire_buffered_gc_event_roots ( - MonoProfiler *prof, - uint64_t count, - const mono_byte *const * addresses, - MonoObject *const * objects) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uintptr_t object_id; - uintptr_t address_id; - - MonoProfilerBufferedGCEvent gc_event_data; - gc_event_data.type = MONO_PROFILER_BUFFERED_GC_EVENT_ROOTS; - gc_event_data.payload_size = - (uint32_t)(sizeof (count) + - (count * (sizeof (uintptr_t) + sizeof (uintptr_t)))); - - uint8_t * buffer = mono_profiler_buffered_gc_event_alloc (gc_event_data.payload_size); - if (buffer) { - // Internal header - memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); - buffer += sizeof (gc_event_data); - - // GCRoots.Count - memcpy (buffer, &count, sizeof (count)); - buffer += sizeof (count); - - // Serialize directly as memory stream expected by FireEtwMonoProfilerGCRoots. - for (uint64_t i = 0; i < count; i++) { - // GCRoots.Values[].ObjectID. - object_id = (uintptr_t)SGEN_POINTER_UNTAG_ALL (*objects); - ep_write_buffer_uintptr_t (&buffer, object_id); - objects++; - - // GCRoots.Values[].AddressID. - address_id = (uintptr_t)*addresses; - ep_write_buffer_uintptr_t (&buffer, address_id); - addresses++; - } - } -} - -static -void -mono_profiler_fire_gc_event_heap_dump_object_reference ( - uint8_t *data, - uint32_t payload_size, - GHashTable *cache) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uintptr_t object_id; - uintptr_t vtable_id; - uintptr_t object_size; - uint8_t object_gen; - uintptr_t object_ref_count; - - memcpy (&object_id, data, sizeof (object_id)); - data += sizeof (object_id); - - memcpy (&vtable_id, data, sizeof (vtable_id)); - data += sizeof (vtable_id); - - memcpy (&object_size, data, sizeof (object_size)); - data += sizeof (object_size); - - memcpy (&object_gen, data, sizeof (object_gen)); - data += sizeof (object_gen); - - memcpy (&object_ref_count, data, sizeof (object_ref_count)); - data += sizeof (object_ref_count); - - FireEtwMonoProfilerGCHeapDumpObjectReference ( - (const void *)object_id, - (uint64_t)vtable_id, - (uint64_t)object_size, - object_gen, - (uint32_t)object_ref_count, - sizeof (uint32_t) + sizeof (uintptr_t), - data, - NULL, - NULL); - - if (cache) - g_hash_table_insert (cache, (MonoVTable *)SGEN_POINTER_UNTAG_ALL (vtable_id), NULL); -} - -static -int -mono_profiler_fire_buffered_gc_event_heap_dump_object_reference ( - MonoObject *obj, - MonoClass *klass, - uintptr_t size, - uintptr_t num, - MonoObject **refs, - uintptr_t *offsets, - void *data) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - uintptr_t object_id; - uintptr_t vtable_id; - uint8_t object_gen; - uintptr_t object_size = size; - uintptr_t object_ref_count = num; - uint32_t object_ref_offset; - - /* account for object alignment */ - object_size += 7; - object_size &= ~7; - - size_t payload_size = - sizeof (object_id) + - sizeof (vtable_id) + - sizeof (object_size) + - sizeof (object_gen) + - sizeof (object_ref_count) + - (object_ref_count * (sizeof (uint32_t) + sizeof (uintptr_t))); - - MonoProfilerBufferedGCEvent gc_event_data; - gc_event_data.type = MONO_PROFILER_BUFFERED_GC_EVENT_OBJECT_REF; - gc_event_data.payload_size = GSIZE_TO_UINT32 (payload_size); - - uint8_t *buffer = mono_profiler_buffered_gc_event_alloc (gc_event_data.payload_size); - if (buffer) { - // Internal header - memcpy (buffer, &gc_event_data, sizeof (gc_event_data)); - buffer += sizeof (gc_event_data); - - // GCEvent.ObjectID - object_id = (uintptr_t)SGEN_POINTER_UNTAG_ALL (obj); - memcpy (buffer, &object_id, sizeof (object_id)); - buffer += sizeof (object_id); - - // GCEvent.VTableID - vtable_id = (uintptr_t)SGEN_POINTER_UNTAG_ALL (mono_object_get_vtable_internal (obj)); - memcpy (buffer, &vtable_id, sizeof (vtable_id)); - buffer += sizeof (vtable_id); - - // GCEvent.ObjectSize - memcpy (buffer, &object_size, sizeof (object_size)); - buffer += sizeof (object_size); - - // GCEvent.ObjectGeneration - object_gen = (uint8_t)mono_gc_get_generation (obj); - memcpy (buffer, &object_gen, sizeof (object_gen)); - buffer += sizeof (object_gen); - - // GCEvent.Count - memcpy (buffer, &object_ref_count, sizeof (object_ref_count)); - buffer += sizeof (object_ref_count); - - // Serialize directly as memory stream expected by FireEtwMonoProfilerGCHeapDumpObjectReference. - uintptr_t last_offset = 0; - for (uintptr_t i = 0; i < object_ref_count; i++) { - // GCEvent.Values[].ReferencesOffset - object_ref_offset = GUINTPTR_TO_UINT32 (offsets [i] - last_offset); - ep_write_buffer_uint32_t (&buffer, object_ref_offset); - - // GCEvent.Values[].ObjectID - object_id = (uintptr_t)SGEN_POINTER_UNTAG_ALL (refs[i]); - ep_write_buffer_uintptr_t (&buffer, object_id); - - last_offset = offsets [i]; - } - } - - return 0; -} - -static -void -mono_profiler_fire_buffered_gc_events ( - MonoProfilerMemBlock *block, - GHashTable *cache) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - if (block) { - uint32_t current_offset = 0; - uint32_t used_size = (block->offset < block->size) ? block->offset : block->last_used_offset; - MonoProfilerBufferedGCEvent gc_event; - while ((current_offset + sizeof (gc_event)) <= used_size) { - uint8_t *data = block->start + current_offset; - memcpy (&gc_event, data, sizeof (gc_event)); - data += sizeof (gc_event); - if ((current_offset + sizeof (gc_event) + gc_event.payload_size) <= used_size) { - switch (gc_event.type) { - case MONO_PROFILER_BUFFERED_GC_EVENT: - mono_profiler_fire_gc_event (data, gc_event.payload_size); - break; - case MONO_PROFILER_BUFFERED_GC_EVENT_RESIZE: - mono_profiler_fire_gc_event_resize (data, gc_event.payload_size); - break; - case MONO_PROFILER_BUFFERED_GC_EVENT_ROOTS: - mono_profiler_fire_gc_event_roots (data, gc_event.payload_size); - break; - case MONO_PROFILER_BUFFERED_GC_EVENT_MOVES: - mono_profiler_fire_gc_event_moves (data, gc_event.payload_size); - break; - case MONO_PROFILER_BUFFERED_GC_EVENT_OBJECT_REF: - mono_profiler_fire_gc_event_heap_dump_object_reference (data, gc_event.payload_size, cache); - break; - case MONO_PROFILER_BUFFERED_GC_EVENT_ROOT_REGISTER: - mono_profiler_fire_gc_event_root_register (data, gc_event.payload_size); - break; - case MONO_PROFILER_BUFFERED_GC_EVENT_ROOT_UNREGISTER: - mono_profiler_fire_gc_event_root_unregister (data, gc_event.payload_size); - break; - default: - EP_ASSERT (!"Unknown buffered GC event type."); - } - - current_offset += sizeof (gc_event) + gc_event.payload_size; - } else { - break; - } - } - } -} - -static -void -mono_profiler_fire_buffered_gc_events_in_alloc_order (GHashTable *cache) -{ - EP_ASSERT (mono_profiler_gc_in_progress ()); - - MonoProfilerMemBlock *first_block = (MonoProfilerMemBlock *)ep_rt_volatile_load_ptr ((volatile void **)&_ep_rt_mono_profiler_current_mem_block); - while (first_block && first_block->prev) - first_block = first_block->prev; - - MonoProfilerMemBlock *current_block = first_block; - while (current_block) { - MonoProfilerMemBlock *next_block = current_block->next; - mono_profiler_fire_buffered_gc_events (current_block, cache); - current_block = next_block; - } - - mono_profiler_mem_block_free_all_but_current (); -} - -static -void -mono_profiler_fire_cached_gc_events (GHashTable *cache) -{ - if (cache) { - GHashTableIter iter; - MonoVTable *object_vtable; - g_hash_table_iter_init (&iter, cache); - while (g_hash_table_iter_next (&iter, (void**)&object_vtable, NULL)) { - if (object_vtable) { - uint64_t vtable_id = (uint64_t)object_vtable; - uint64_t class_id; - uint64_t module_id; - ep_char8_t *class_name; - mono_profiler_get_class_data (object_vtable->klass, &class_id, &module_id, &class_name, NULL, NULL); - FireEtwMonoProfilerGCHeapDumpVTableClassReference ( - vtable_id, - class_id, - module_id, - class_name, - NULL, - NULL); - g_free (class_name); - } - } - } -} - -static -void -mono_profiler_app_domain_loading ( - MonoProfiler *prof, - MonoDomain *domain) -{ - if (!EventEnabledMonoProfilerAppDomainLoading ()) - return; - - uint64_t domain_id = (uint64_t)domain; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerAppDomainLoading ( - domain_id, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_app_domain_loaded ( - MonoProfiler *prof, - MonoDomain *domain) -{ - if (!EventEnabledMonoProfilerAppDomainLoaded ()) - return; - - uint64_t domain_id = (uint64_t)domain; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerAppDomainLoaded ( - domain_id, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_app_domain_unloading ( - MonoProfiler *prof, - MonoDomain *domain) -{ - if (!EventEnabledMonoProfilerAppDomainUnloading ()) - return; - - uint64_t domain_id = (uint64_t)domain; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerAppDomainUnloading ( - domain_id, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_app_domain_unloaded ( - MonoProfiler *prof, - MonoDomain *domain) -{ - if (!EventEnabledMonoProfilerAppDomainUnloaded ()) - return; - - uint64_t domain_id = (uint64_t)domain; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerAppDomainUnloaded ( - domain_id, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_app_domain_name ( - MonoProfiler *prof, - MonoDomain *domain, - const char *name) -{ - if (!EventEnabledMonoProfilerAppDomainName ()) - return; - - uint64_t domain_id = (uint64_t)domain; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerAppDomainName ( - domain_id, - (const ep_char8_t *)(name ? name : ""), - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_get_generic_types ( - MonoGenericInst *generic_instance, - uint32_t *generic_type_count, - uint8_t **generic_types) -{ - if (generic_instance) { - uint8_t *buffer = g_malloc (generic_instance->type_argc * (sizeof (uint8_t) + sizeof (uint64_t))); - if (buffer) { - *generic_types = buffer; - *generic_type_count = generic_instance->type_argc; - for (uint32_t i = 0; i < generic_instance->type_argc; ++i) { - uint8_t type = generic_instance->type_argv [i]->type; - ep_write_buffer_uint8_t (&buffer, type); - - uint64_t class_id = (uint64_t)mono_class_from_mono_type_internal (generic_instance->type_argv [i]); - ep_write_buffer_uint64_t (&buffer, class_id); - } - } - } -} - -static -void -mono_profiler_get_jit_data ( - MonoMethod *method, - uint64_t *method_id, - uint64_t *module_id, - uint32_t *method_token, - uint32_t *method_generic_type_count, - uint8_t **method_generic_types) -{ - *method_id = (uint64_t)method; - *module_id = 0; - *method_token = 0; - - if (method) { - *method_token = method->token; - if (method->klass) - *module_id = (uint64_t)m_class_get_image (method->klass); - - if (method_generic_type_count && method_generic_types) { - if (method->is_inflated) { - MonoGenericContext *context = mono_method_get_context (method); - MonoGenericInst *method_instance = (context && context->method_inst) ? context->method_inst : NULL; - mono_profiler_get_generic_types (method_instance, method_generic_type_count, method_generic_types); - } - } - } -} - -static -void -mono_profiler_jit_begin ( - MonoProfiler *prof, - MonoMethod *method) -{ - if (!EventEnabledMonoProfilerJitBegin ()) - return; - - uint64_t method_id; - uint64_t module_id; - uint32_t method_token; - - mono_profiler_get_jit_data (method, &method_id, &module_id, &method_token, NULL, NULL); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerJitBegin ( - method_id, - module_id, - method_token, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_jit_failed ( - MonoProfiler *prof, - MonoMethod *method) -{ - if (!EventEnabledMonoProfilerJitFailed ()) - return; - - uint64_t method_id; - uint64_t module_id; - uint32_t method_token; - - mono_profiler_get_jit_data (method, &method_id, &module_id, &method_token, NULL, NULL); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerJitFailed ( - method_id, - module_id, - method_token, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_jit_done ( - MonoProfiler *prof, - MonoMethod *method, - MonoJitInfo *ji) -{ - if (!EventEnabledMonoProfilerJitDone () && !EventEnabledMonoProfilerJitDone_V1 () && !EventEnabledMonoProfilerJitDoneVerbose ()) - return; - - bool verbose = (MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.Level >= (uint8_t)EP_EVENT_LEVEL_VERBOSE); - - uint64_t method_id; - uint64_t module_id; - uint32_t method_token; - - uint32_t method_generic_type_count = 0; - uint8_t *method_generic_types = NULL; - - char *method_namespace = NULL; - const char *method_name = NULL; - char *method_signature = NULL; - - mono_profiler_get_jit_data (method, &method_id, &module_id, &method_token, &method_generic_type_count, &method_generic_types); - - if (verbose) { - //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc. - method_name = method->name; - method_signature = mono_signature_full_name (mono_method_signature_internal (method)); - if (method->klass) - method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL); - } - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerJitDone_V1 ( - method_id, - module_id, - method_token, - method_generic_type_count, - sizeof (uint8_t) + sizeof (uint64_t), - method_generic_types, - NULL, - NULL); - - if (verbose) { - FireEtwMonoProfilerJitDoneVerbose ( - method_id, - (const ep_char8_t *)method_namespace, - (const ep_char8_t *)method_name, - (const ep_char8_t *)method_signature, - NULL, - NULL); - } - - mono_profiler_fire_event_exit (); - - g_free (method_namespace); - g_free (method_signature); - g_free (method_generic_types); -} - -static -void -mono_profiler_jit_chunk_created ( - MonoProfiler *prof, - const mono_byte *chunk, - uintptr_t size) -{ - if (!EventEnabledMonoProfilerJitChunkCreated ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerJitChunkCreated ( - chunk, - (uint64_t)size, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_jit_chunk_destroyed ( - MonoProfiler *prof, - const mono_byte *chunk) -{ - if (!EventEnabledMonoProfilerJitChunkDestroyed ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerJitChunkDestroyed ( - chunk, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_jit_code_buffer ( - MonoProfiler *prof, - const mono_byte *buffer, - uint64_t size, - MonoProfilerCodeBufferType type, - const void *data) -{ - if (!EventEnabledMonoProfilerJitCodeBuffer ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerJitCodeBuffer ( - buffer, - size, - (uint8_t)type, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_get_class_data ( - MonoClass *klass, - uint64_t *class_id, - uint64_t *module_id, - ep_char8_t **class_name, - uint32_t *class_generic_type_count, - uint8_t **class_generic_types) -{ - *class_id = (uint64_t)klass; - *module_id = 0; - - if (klass) - *module_id = (uint64_t)m_class_get_image (klass); - - if (klass && class_name) - *class_name = (ep_char8_t *)mono_type_get_name_full (m_class_get_byval_arg (klass), MONO_TYPE_NAME_FORMAT_IL); - else if (class_name) - *class_name = NULL; - - if (class_generic_type_count && class_generic_types) { - if (mono_class_is_ginst (klass)) { - MonoGenericContext *context = mono_class_get_context (klass); - MonoGenericInst *class_instance = (context && context->class_inst) ? context->class_inst : NULL; - mono_profiler_get_generic_types (class_instance, class_generic_type_count, class_generic_types); - } - } -} - -static -void -mono_profiler_class_loading ( - MonoProfiler *prof, - MonoClass *klass) -{ - if (!EventEnabledMonoProfilerClassLoading ()) - return; - - uint64_t class_id; - uint64_t module_id; - - mono_profiler_get_class_data (klass, &class_id, &module_id, NULL, NULL, NULL); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerClassLoading ( - class_id, - module_id, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_class_failed ( - MonoProfiler *prof, - MonoClass *klass) -{ - if (!EventEnabledMonoProfilerClassFailed ()) - return; - - uint64_t class_id; - uint64_t module_id; - - mono_profiler_get_class_data (klass, &class_id, &module_id, NULL, NULL, NULL); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerClassFailed ( - class_id, - module_id, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_class_loaded ( - MonoProfiler *prof, - MonoClass *klass) -{ - if (!EventEnabledMonoProfilerClassLoaded () && !EventEnabledMonoProfilerClassLoaded_V1 ()) - return; - - uint64_t class_id; - uint64_t module_id; - ep_char8_t *class_name; - - uint32_t class_generic_type_count = 0; - uint8_t *class_generic_types = NULL; - - mono_profiler_get_class_data (klass, &class_id, &module_id, &class_name, &class_generic_type_count, &class_generic_types); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerClassLoaded_V1 ( - class_id, - module_id, - class_name ? class_name : "", - class_generic_type_count, - sizeof (uint8_t) + sizeof (uint64_t), - class_generic_types, - NULL, - NULL); - - mono_profiler_fire_event_exit (); - - g_free (class_name); - g_free (class_generic_types); -} - -static -inline -void -get_vtable_data ( - MonoVTable *vtable, - uint64_t *vtable_id, - uint64_t *class_id, - uint64_t *domain_id) -{ - *vtable_id = (uint64_t)vtable; - *class_id = 0; - *domain_id = 0; - - if (vtable) { - *class_id = (uint64_t)mono_vtable_class_internal (vtable); - *domain_id = (uint64_t)mono_vtable_domain_internal (vtable); - } -} - -static -void -mono_profiler_vtable_loading ( - MonoProfiler *prof, - MonoVTable *vtable) -{ - if (!EventEnabledMonoProfilerVTableLoading ()) - return; - - uint64_t vtable_id; - uint64_t class_id; - uint64_t domain_id; - - get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerVTableLoading ( - vtable_id, - class_id, - domain_id, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_vtable_failed ( - MonoProfiler *prof, - MonoVTable *vtable) -{ - if (!EventEnabledMonoProfilerVTableFailed ()) - return; - - uint64_t vtable_id; - uint64_t class_id; - uint64_t domain_id; - - get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerVTableFailed ( - vtable_id, - class_id, - domain_id, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_vtable_loaded ( - MonoProfiler *prof, - MonoVTable *vtable) -{ - if (!EventEnabledMonoProfilerVTableLoaded ()) - return; - - uint64_t vtable_id; - uint64_t class_id; - uint64_t domain_id; - - get_vtable_data (vtable, &vtable_id, &class_id, &domain_id); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerVTableLoaded ( - vtable_id, - class_id, - domain_id, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_module_loading ( - MonoProfiler *prof, - MonoImage *image) -{ - if (!EventEnabledMonoProfilerModuleLoading ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerModuleLoading ( - (uint64_t)image, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_module_failed ( - MonoProfiler *prof, - MonoImage *image) -{ - if (!EventEnabledMonoProfilerModuleFailed ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerModuleFailed ( - (uint64_t)image, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_module_loaded ( - MonoProfiler *prof, - MonoImage *image) +void * +ep_rt_mono_thread_attach (bool background_thread) { - if (!EventEnabledMonoProfilerModuleLoaded ()) - return; - - uint64_t module_id = (uint64_t)image; - const ep_char8_t *module_path = NULL; - const ep_char8_t *module_guid = NULL; + MonoThread *thread = NULL; - if (image) { - ModuleEventData module_data; - memset (&module_data, 0, sizeof (module_data)); - if (get_module_event_data (image, &module_data)) - module_path = (const ep_char8_t *)module_data.module_il_path; - module_guid = (const ep_char8_t *)mono_image_get_guid (image); + // NOTE, under netcore, only root domain exists. + if (!mono_thread_current ()) { + thread = mono_thread_internal_attach (mono_get_root_domain ()); + if (background_thread && thread) { + mono_thread_set_state (thread, ThreadState_Background); + mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_SAMPLE); + } } - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerModuleLoaded ( - module_id, - module_path ? module_path : "", - module_guid ? module_guid : "", - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_module_unloading ( - MonoProfiler *prof, - MonoImage *image) -{ - if (!EventEnabledMonoProfilerModuleUnloading ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerModuleUnloading ( - (uint64_t)image, - NULL, - NULL); - - mono_profiler_fire_event_exit (); + return thread; } -static -void -mono_profiler_module_unloaded ( - MonoProfiler *prof, - MonoImage *image) +void * +ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type) { - if (!EventEnabledMonoProfilerModuleUnloaded ()) - return; - - uint64_t module_id = (uint64_t)image; - const ep_char8_t *module_path = NULL; - const ep_char8_t *module_guid = NULL; - - if (image) { - ModuleEventData module_data; - memset (&module_data, 0, sizeof (module_data)); - if (get_module_event_data (image, &module_data)) - module_path = (const ep_char8_t *)module_data.module_il_path; - module_guid = (const ep_char8_t *)mono_image_get_guid (image); + void *result = ep_rt_mono_thread_attach (background_thread); + if (result && thread_type == EP_THREAD_TYPE_SAMPLING) { + // Increase sampling thread priority, accepting failures. +#ifdef HOST_WIN32 + SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST); +#elif _POSIX_PRIORITY_SCHEDULING + int policy; + int priority; + struct sched_param param; + int schedparam_result = pthread_getschedparam (pthread_self (), &policy, ¶m); + if (schedparam_result == 0) { + // Attempt to switch the thread to real time scheduling. This will not + // necessarily work on all OSs; for example, most Linux systems will give + // us EPERM here unless configured to allow this. + priority = param.sched_priority; + param.sched_priority = sched_get_priority_max (SCHED_RR); + if (param.sched_priority != -1) { + schedparam_result = pthread_setschedparam (pthread_self (), SCHED_RR, ¶m); + if (schedparam_result != 0) { + // Fallback, attempt to increase to max priority using current policy. + param.sched_priority = sched_get_priority_max (policy); + if (param.sched_priority != -1 && param.sched_priority != priority) + pthread_setschedparam (pthread_self (), policy, ¶m); + } + } + } +#endif } - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerModuleUnloaded ( - module_id, - module_path ? module_path : "", - module_guid ? module_guid : "", - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -inline -void -get_assembly_data ( - MonoAssembly *assembly, - uint64_t *assembly_id, - uint64_t *module_id, - ep_char8_t **assembly_name) -{ - *assembly_id = (uint64_t)assembly; - *module_id = 0; - - if (assembly) - *module_id = (uint64_t)mono_assembly_get_image_internal (assembly); - - if (assembly && assembly_name) - *assembly_name = (ep_char8_t *)mono_stringify_assembly_name (&assembly->aname); - else if (assembly_name) - *assembly_name = NULL; -} - -static -void -mono_profiler_assembly_loading ( - MonoProfiler *prof, - MonoAssembly *assembly) -{ - if (!EventEnabledMonoProfilerAssemblyLoading ()) - return; - - uint64_t assembly_id; - uint64_t module_id; - - get_assembly_data (assembly, &assembly_id, &module_id, NULL); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerAssemblyLoading ( - assembly_id, - module_id, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_assembly_loaded ( - MonoProfiler *prof, - MonoAssembly *assembly) -{ - if (!EventEnabledMonoProfilerAssemblyLoaded ()) - return; - - uint64_t assembly_id; - uint64_t module_id; - ep_char8_t *assembly_name; - - get_assembly_data (assembly, &assembly_id, &module_id, &assembly_name); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerAssemblyLoaded ( - assembly_id, - module_id, - assembly_name ? assembly_name : "", - NULL, - NULL); - - mono_profiler_fire_event_exit (); - - g_free (assembly_name); -} - -static -void -mono_profiler_assembly_unloading ( - MonoProfiler *prof, - MonoAssembly *assembly) -{ - if (!EventEnabledMonoProfilerAssemblyUnloading ()) - return; - - uint64_t assembly_id; - uint64_t module_id; - - get_assembly_data (assembly, &assembly_id, &module_id, NULL); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerAssemblyUnloading ( - assembly_id, - module_id, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_assembly_unloaded ( - MonoProfiler *prof, - MonoAssembly *assembly) -{ - if (!EventEnabledMonoProfilerAssemblyUnloaded ()) - return; - - uint64_t assembly_id; - uint64_t module_id; - ep_char8_t *assembly_name; - - get_assembly_data (assembly, &assembly_id, &module_id, &assembly_name); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerAssemblyUnloaded ( - assembly_id, - module_id, - assembly_name ? assembly_name : "", - NULL, - NULL); - - mono_profiler_fire_event_exit (); - - g_free (assembly_name); -} - -static -void -mono_profiler_method_enter ( - MonoProfiler *prof, - MonoMethod *method, - MonoProfilerCallContext *context) -{ - if (!EventEnabledMonoProfilerMethodEnter ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerMethodEnter ( - (uint64_t)method, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_method_leave ( - MonoProfiler *prof, - MonoMethod *method, - MonoProfilerCallContext *context) -{ - if (!EventEnabledMonoProfilerMethodLeave ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerMethodLeave ( - (uint64_t)method, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_method_tail_call ( - MonoProfiler *prof, - MonoMethod *method, - MonoMethod *target_method) -{ - if (!EventEnabledMonoProfilerMethodTailCall ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerMethodTailCall ( - (uint64_t)method, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_method_exception_leave ( - MonoProfiler *prof, - MonoMethod *method, - MonoObject *exc) -{ - if (!EventEnabledMonoProfilerMethodExceptionLeave ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerMethodExceptionLeave ( - (uint64_t)method, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_method_free ( - MonoProfiler *prof, - MonoMethod *method) -{ - if (!EventEnabledMonoProfilerMethodFree ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerMethodFree ( - (uint64_t)method, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_method_begin_invoke ( - MonoProfiler *prof, - MonoMethod *method) -{ - if (!EventEnabledMonoProfilerMethodBeginInvoke ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerMethodBeginInvoke ( - (uint64_t)method, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -void -mono_profiler_method_end_invoke ( - MonoProfiler *prof, - MonoMethod *method) -{ - if (!EventEnabledMonoProfilerMethodEndInvoke ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerMethodEndInvoke ( - (uint64_t)method, - NULL, - NULL); - - mono_profiler_fire_event_exit (); -} - -static -MonoProfilerCallInstrumentationFlags -mono_profiler_method_instrumentation ( - MonoProfiler *prof, - MonoMethod *method) -{ - if (_ep_rt_dotnet_mono_profiler_provider_callspec.len > 0 && !mono_callspec_eval (method, &_ep_rt_dotnet_mono_profiler_provider_callspec)) - return MONO_PROFILER_CALL_INSTRUMENTATION_NONE; - - return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | - MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | - MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL | - MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE; -} - -static -void -mono_profiler_exception_throw ( - MonoProfiler *prof, - MonoObject *exc) -{ - if (!EventEnabledMonoProfilerExceptionThrow ()) - return; - - uint64_t type_id = 0; - - if (exc && mono_object_class(exc)) - type_id = (uint64_t)m_class_get_byval_arg (mono_object_class(exc)); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerExceptionThrow ( - type_id, - SGEN_POINTER_UNTAG_ALL (exc), - NULL, - NULL); - - mono_profiler_fire_event_exit (); + return result; } -static void -mono_profiler_exception_clause ( - MonoProfiler *prof, - MonoMethod *method, - uint32_t clause_num, - MonoExceptionEnum clause_type, - MonoObject *exc) +ep_rt_mono_thread_detach (void) { - if (!EventEnabledMonoProfilerExceptionClause ()) - return; - - uint64_t type_id = 0; - - if (exc && mono_object_class(exc)) - type_id = (uint64_t)m_class_get_byval_arg (mono_object_class(exc)); - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerExceptionClause ( - (uint8_t)clause_type, - clause_num, - (uint64_t)method, - type_id, - SGEN_POINTER_UNTAG_ALL (exc), - NULL, - NULL); - - mono_profiler_fire_event_exit (); + MonoThread *current_thread = mono_thread_current (); + if (current_thread) + mono_thread_internal_detach (current_thread); } -static -void -mono_profiler_gc_event ( - MonoProfiler *prof, - MonoProfilerGCEvent gc_event, - uint32_t generation, - mono_bool serial) +#ifdef HOST_WIN32 +int64_t +ep_rt_mono_perf_counter_query (void) { - switch (gc_event) { - case MONO_GC_EVENT_PRE_STOP_WORLD: - case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED: - { - FireEtwMonoProfilerGCEvent ( - (uint8_t)gc_event, - generation, - NULL, - NULL); - break; - } - case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED: - { - FireEtwMonoProfilerGCEvent ( - (uint8_t)gc_event, - generation, - NULL, - NULL); - - mono_profiler_gc_in_progress_start (); - - if (mono_profiler_gc_heap_collect_in_progress ()) { - FireEtwMonoProfilerGCHeapDumpStart ( - mono_profiler_get_gc_heap_collect_param_request_value (), - NULL, - NULL); - } - - break; - } - case MONO_GC_EVENT_POST_STOP_WORLD: - { - if (mono_profiler_gc_in_progress ()) { - uint64_t enabled_keywords = MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask; - - if (profiler_callback_is_enabled (enabled_keywords, GC_ROOT_KEYWORD)) { - mono_profiler_set_gc_root_register_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_gc_root_unregister_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_gc_root_register_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, mono_profiler_fire_buffered_gc_event_root_register); - mono_profiler_set_gc_root_unregister_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, mono_profiler_fire_buffered_gc_event_root_unregister); - } - - if (mono_profiler_gc_heap_collect_in_progress ()) { - if (profiler_callback_is_enabled (enabled_keywords, GC_ROOT_KEYWORD)) { - mono_profiler_set_gc_roots_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, mono_profiler_fire_buffered_gc_event_roots); - } - - if (profiler_callback_is_enabled (enabled_keywords, GC_MOVES_KEYWORD)) { - mono_profiler_set_gc_moves_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, mono_profiler_fire_buffered_gc_event_moves); - } - - if (profiler_callback_is_enabled (enabled_keywords, GC_RESIZE_KEYWORD)) { - mono_profiler_set_gc_resize_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, mono_profiler_fire_buffered_gc_event_resize); - } - } - - mono_profiler_fire_buffered_gc_event ( - (uint8_t)gc_event, - generation); - } - break; - } - case MONO_GC_EVENT_START: - case MONO_GC_EVENT_END: - { - if (mono_profiler_gc_in_progress ()) { - mono_profiler_fire_buffered_gc_event ( - (uint8_t)gc_event, - generation); - } - break; - } - case MONO_GC_EVENT_PRE_START_WORLD: - { - if (mono_profiler_gc_in_progress ()) { - uint64_t enabled_keywords = MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask; - - if (mono_profiler_gc_heap_collect_in_progress () && profiler_callback_is_enabled (enabled_keywords, GC_HEAP_DUMP_KEYWORD)) - mono_gc_walk_heap (0, mono_profiler_fire_buffered_gc_event_heap_dump_object_reference, NULL); - - mono_profiler_set_gc_root_register_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, NULL); - mono_profiler_set_gc_root_unregister_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, NULL); - mono_profiler_set_gc_roots_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, NULL); - mono_profiler_set_gc_moves_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, NULL); - mono_profiler_set_gc_resize_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, NULL); - - if (profiler_callback_is_enabled (enabled_keywords, GC_ROOT_KEYWORD)) { - mono_profiler_set_gc_root_register_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_root_register); - mono_profiler_set_gc_root_unregister_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_root_unregister); - } - - mono_profiler_fire_buffered_gc_event ( - (uint8_t)gc_event, - generation); - } - - break; - } - case MONO_GC_EVENT_POST_START_WORLD: - { - if (mono_profiler_gc_in_progress ()) { - GHashTable *cache = NULL; - uint64_t enabled_keywords = MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask; - - if (mono_profiler_gc_heap_collect_in_progress () && profiler_callback_is_enabled (enabled_keywords, GC_HEAP_DUMP_VTABLE_CLASS_REF_KEYWORD)) - cache = g_hash_table_new_full (NULL, NULL, NULL, NULL); - - mono_profiler_fire_buffered_gc_events_in_alloc_order (cache); - mono_profiler_fire_cached_gc_events (cache); - - if (cache) - g_hash_table_destroy (cache); - - if (mono_profiler_gc_heap_collect_in_progress ()) { - FireEtwMonoProfilerGCHeapDumpStop ( - NULL, - NULL); - } + LARGE_INTEGER value; + if (QueryPerformanceCounter (&value)) + return (int64_t)value.QuadPart; + else + return 0; +} - FireEtwMonoProfilerGCEvent ( - (uint8_t)gc_event, - generation, - NULL, - NULL); +int64_t +ep_rt_mono_perf_frequency_query (void) +{ + LARGE_INTEGER value; + if (QueryPerformanceFrequency (&value)) + return (int64_t)value.QuadPart; + else + return 0; +} - if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD)) - mono_profiler_set_gc_event_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); +void +ep_rt_mono_system_time_get (EventPipeSystemTime *system_time) +{ + SYSTEMTIME value; + GetSystemTime (&value); - mono_profiler_gc_heap_collect_in_progress_stop (); - mono_profiler_gc_in_progress_stop (); - } - break; - } - default: - break; - } + EP_ASSERT (system_time != NULL); + ep_system_time_set ( + system_time, + value.wYear, + value.wMonth, + value.wDayOfWeek, + value.wDay, + value.wHour, + value.wMinute, + value.wSecond, + value.wMilliseconds); } -static -void -mono_profiler_gc_allocation ( - MonoProfiler *prof, - MonoObject *object) +int64_t +ep_rt_mono_system_timestamp_get (void) { - if (!EventEnabledMonoProfilerGCAllocation ()) - return; + FILETIME value; + GetSystemTimeAsFileTime (&value); + return (int64_t)((((uint64_t)value.dwHighDateTime) << 32) | (uint64_t)value.dwLowDateTime); +} +#else +#include +#include +#include +#include - uint64_t vtable_id = 0; - uint64_t object_size = 0; +#if HAVE_SYS_TIME_H +#include +#endif // HAVE_SYS_TIME_H - if (object) { - vtable_id = (uint64_t)mono_object_get_vtable_internal (object); - object_size = (uint64_t)mono_object_get_size_internal (object); +#if HAVE_MACH_ABSOLUTE_TIME +#include +static mono_lazy_init_t _ep_rt_mono_time_base_info_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; +static mach_timebase_info_data_t _ep_rt_mono_time_base_info = {0}; +#endif - /* account for object alignment */ - object_size += 7; - object_size &= ~7; - } +#ifdef HAVE_LOCALTIME_R +#define HAVE_GMTIME_R 1 +#endif - mono_profiler_fire_event_enter (); +static const int64_t SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL; +static const int64_t SECS_TO_100NS = 10000000; +static const int64_t SECS_TO_NS = 1000000000; +static const int64_t MSECS_TO_MIS = 1000; - FireEtwMonoProfilerGCAllocation ( - vtable_id, - SGEN_POINTER_UNTAG_ALL (object), - object_size, - NULL, - NULL); +/* clock_gettime () is found by configure on Apple builds, but its only present from ios 10, macos 10.12, tvos 10 and watchos 3 */ +#if defined (HAVE_CLOCK_MONOTONIC) && (defined(HOST_IOS) || defined(HOST_OSX) || defined(HOST_WATCHOS) || defined(HOST_TVOS)) +#undef HAVE_CLOCK_MONOTONIC +#endif - mono_profiler_fire_event_exit (); -} +#ifndef HAVE_CLOCK_MONOTONIC +static const int64_t MISECS_TO_NS = 1000; +#endif static void -mono_profiler_gc_handle_created ( - MonoProfiler *prof, - uint32_t handle, - MonoGCHandleType type, - MonoObject *object) -{ - if (!EventEnabledMonoProfilerGCHandleCreated ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerGCHandleCreated ( - handle, - (uint8_t)type, - SGEN_POINTER_UNTAG_ALL (object), - NULL, - NULL); +time_base_info_lazy_init (void); - mono_profiler_fire_event_exit (); -} +static +int64_t +system_time_to_int64 ( + time_t sec, + long nsec); +#if HAVE_MACH_ABSOLUTE_TIME static void -mono_profiler_gc_handle_deleted ( - MonoProfiler *prof, - uint32_t handle, - MonoGCHandleType type) +time_base_info_lazy_init (void) { - if (!EventEnabledMonoProfilerGCHandleDeleted ()) - return; - - mono_profiler_fire_event_enter (); + kern_return_t result = mach_timebase_info (&_ep_rt_mono_time_base_info); + if (result != KERN_SUCCESS) + memset (&_ep_rt_mono_time_base_info, 0, sizeof (_ep_rt_mono_time_base_info)); +} +#endif - FireEtwMonoProfilerGCHandleDeleted ( - handle, - (uint8_t)type, - NULL, - NULL); +int64_t +ep_rt_mono_perf_counter_query (void) +{ +#if HAVE_MACH_ABSOLUTE_TIME + return (int64_t)mach_absolute_time (); +#elif HAVE_CLOCK_MONOTONIC + struct timespec ts; + int result = clock_gettime (CLOCK_MONOTONIC, &ts); + if (result == 0) + return ((int64_t)(ts.tv_sec) * (int64_t)(SECS_TO_NS)) + (int64_t)(ts.tv_nsec); +#else + #error "ep_rt_mono_perf_counter_get requires either mach_absolute_time () or clock_gettime (CLOCK_MONOTONIC) to be supported." +#endif + return 0; +} - mono_profiler_fire_event_exit (); +int64_t +ep_rt_mono_perf_frequency_query (void) +{ +#if HAVE_MACH_ABSOLUTE_TIME + // (numer / denom) gives you the nanoseconds per tick, so the below code + // computes the number of ticks per second. We explicitly do the multiplication + // first in order to help minimize the error that is produced by integer division. + mono_lazy_initialize (&_ep_rt_mono_time_base_info_init, time_base_info_lazy_init); + if (_ep_rt_mono_time_base_info.denom == 0 || _ep_rt_mono_time_base_info.numer == 0) + return 0; + return ((int64_t)(SECS_TO_NS) * (int64_t)(_ep_rt_mono_time_base_info.denom)) / (int64_t)(_ep_rt_mono_time_base_info.numer); +#elif HAVE_CLOCK_MONOTONIC + // clock_gettime () returns a result in terms of nanoseconds rather than a count. This + // means that we need to either always scale the result by the actual resolution (to + // get a count) or we need to say the resolution is in terms of nanoseconds. We prefer + // the latter since it allows the highest throughput and should minimize error propagated + // to the user. + return (int64_t)(SECS_TO_NS); +#else + #error "ep_rt_mono_perf_frequency_query requires either mach_absolute_time () or clock_gettime (CLOCK_MONOTONIC) to be supported." +#endif + return 0; } -static void -mono_profiler_gc_finalizing (MonoProfiler *prof) +ep_rt_mono_system_time_get (EventPipeSystemTime *system_time) { - if (!EventEnabledMonoProfilerGCFinalizing ()) - return; + time_t tt; +#if HAVE_GMTIME_R + struct tm ut; +#endif /* HAVE_GMTIME_R */ + struct tm *ut_ptr; + struct timeval time_val; + int timeofday_retval; - mono_profiler_fire_event_enter (); + EP_ASSERT (system_time != NULL); - FireEtwMonoProfilerGCFinalizing ( - NULL, - NULL); + tt = time (NULL); - mono_profiler_fire_event_exit (); -} + /* We can't get millisecond resolution from time (), so we get it from gettimeofday () */ + timeofday_retval = gettimeofday (&time_val, NULL); -static -void -mono_profiler_gc_finalized (MonoProfiler *prof) -{ - if (!EventEnabledMonoProfilerGCFinalized ()) - return; +#if HAVE_GMTIME_R + ut_ptr = &ut; + if (gmtime_r (&tt, ut_ptr) == NULL) +#else /* HAVE_GMTIME_R */ + if ((ut_ptr = gmtime (&tt)) == NULL) +#endif /* HAVE_GMTIME_R */ + EP_UNREACHABLE (); + + uint16_t milliseconds = 0; + if (timeofday_retval != -1) { + int old_seconds; + int new_seconds; - mono_profiler_fire_event_enter (); + milliseconds = (uint16_t)(time_val.tv_usec / MSECS_TO_MIS); + + old_seconds = ut_ptr->tm_sec; + new_seconds = time_val.tv_sec % 60; - FireEtwMonoProfilerGCFinalized ( - NULL, - NULL); + /* just in case we reached the next second in the interval between time () and gettimeofday () */ + if (old_seconds != new_seconds) + milliseconds = 999; + } - mono_profiler_fire_event_exit (); + ep_system_time_set ( + system_time, + (uint16_t)(1900 + ut_ptr->tm_year), + (uint16_t)ut_ptr->tm_mon + 1, + (uint16_t)ut_ptr->tm_wday, + (uint16_t)ut_ptr->tm_mday, + (uint16_t)ut_ptr->tm_hour, + (uint16_t)ut_ptr->tm_min, + (uint16_t)ut_ptr->tm_sec, + milliseconds); } static -void -mono_profiler_gc_finalizing_object ( - MonoProfiler *prof, - MonoObject *object) +int64_t +system_time_to_int64 ( + time_t sec, + long nsec) { - if (!EventEnabledMonoProfilerGCFinalizingObject ()) - return; + return ((int64_t)sec + SECS_BETWEEN_1601_AND_1970_EPOCHS) * SECS_TO_100NS + (nsec / 100); +} - mono_profiler_fire_event_enter (); +int64_t +ep_rt_mono_system_timestamp_get (void) +{ +#if HAVE_CLOCK_MONOTONIC + struct timespec time; + if (clock_gettime (CLOCK_REALTIME, &time) == 0) + return system_time_to_int64 (time.tv_sec, time.tv_nsec); +#else + struct timeval time; + if (gettimeofday (&time, NULL) == 0) + return system_time_to_int64 (time.tv_sec, time.tv_usec * MISECS_TO_NS); +#endif + else + return system_time_to_int64 (0, 0); +} +#endif - FireEtwMonoProfilerGCFinalizingObject ( - SGEN_POINTER_UNTAG_ALL (object), - NULL, - NULL); +#ifndef HOST_WIN32 +#if defined(__APPLE__) +#if defined (HOST_OSX) +G_BEGIN_DECLS +gchar ***_NSGetEnviron(void); +G_END_DECLS +#define environ (*_NSGetEnviron()) +#else +static char *_ep_rt_mono_environ[1] = { NULL }; +#define environ _ep_rt_mono_environ +#endif /* defined (HOST_OSX) */ +#else +G_BEGIN_DECLS +extern char **environ; +G_END_DECLS +#endif /* defined (__APPLE__) */ +#endif /* !defined (HOST_WIN32) */ - mono_profiler_fire_event_exit (); +void +ep_rt_mono_os_environment_get_utf16 (dn_vector_ptr_t *os_env) +{ + EP_ASSERT (os_env != NULL); +#ifdef HOST_WIN32 + LPWSTR envs = GetEnvironmentStringsW (); + if (envs) { + LPWSTR next = envs; + while (*next) { + dn_vector_ptr_push_back (os_env, ep_rt_utf16_string_dup (next)); + next += ep_rt_utf16_string_len (next) + 1; + } + FreeEnvironmentStringsW (envs); + } +#else + gchar **next = NULL; + for (next = environ; *next != NULL; ++next) + dn_vector_ptr_push_back (os_env, ep_rt_utf8_to_utf16le_string (*next, -1)); +#endif } -static void -mono_profiler_gc_finalized_object ( - MonoProfiler *prof, - MonoObject * object) +ep_rt_mono_init_providers_and_events (void) { - if (!EventEnabledMonoProfilerGCFinalizedObject ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerGCFinalizedObject ( - SGEN_POINTER_UNTAG_ALL (object), - NULL, - NULL); - - mono_profiler_fire_event_exit (); + InitProvidersAndEvents (); } -static void -mono_profiler_gc_root_register ( - MonoProfiler *prof, - const mono_byte *start, - uintptr_t size, - MonoGCRootSource source, - const void * key, - const char * name) +ep_rt_mono_provider_config_init (EventPipeProviderConfiguration *provider_config) { - if (!EventEnabledMonoProfilerGCRootRegister ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerGCRootRegister ( - start, - (uint64_t)size, - (uint8_t) source, - (uint64_t)key, - (const ep_char8_t *)(name ? name : ""), - NULL, - NULL); - - mono_profiler_fire_event_exit (); + if (!ep_rt_utf8_string_compare (ep_config_get_rundown_provider_name_utf8 (), ep_provider_config_get_provider_name (provider_config))) { + RUNTIME_RUNDOWN_PROVIDER_CONTEXT.Level = (uint8_t)ep_provider_config_get_logging_level (provider_config); + RUNTIME_RUNDOWN_PROVIDER_CONTEXT.EnabledKeywordsBitmask = ep_provider_config_get_keywords (provider_config); + RUNTIME_RUNDOWN_PROVIDER_CONTEXT.IsEnabled = true; + } } -static -void -mono_profiler_gc_root_unregister ( - MonoProfiler *prof, - const mono_byte *start) +bool +ep_rt_mono_providers_validate_all_disabled (void) { - if (!EventEnabledMonoProfilerGCRootUnregister ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerGCRootUnregister ( - start, - NULL, - NULL); - - mono_profiler_fire_event_exit (); + return (!RUNTIME_PROVIDER_CONTEXT.IsEnabled && + !RUNTIME_PRIVATE_PROVIDER_CONTEXT.IsEnabled && + !RUNTIME_RUNDOWN_PROVIDER_CONTEXT.IsEnabled && + !RUNTIME_STRESS_PROVIDER_CONTEXT.IsEnabled && + !RUNTIME_MONO_PROFILER_PROVIDER_CONTEXT.IsEnabled); } -static -void -mono_profiler_monitor_contention ( - MonoProfiler *prof, - MonoObject *object) +bool +ep_rt_mono_method_get_simple_assembly_name ( + ep_rt_method_desc_t *method, + ep_char8_t *name, + size_t name_len) { - if (!EventEnabledMonoProfilerMonitorContention ()) - return; + EP_ASSERT (method != NULL); + EP_ASSERT (name != NULL); - mono_profiler_fire_event_enter (); + MonoClass *method_class = mono_method_get_class (method); + MonoImage *method_image = method_class ? mono_class_get_image (method_class) : NULL; + const ep_char8_t *assembly_name = method_image ? mono_image_get_name (method_image) : NULL; - FireEtwMonoProfilerMonitorContention ( - SGEN_POINTER_UNTAG_ALL (object), - NULL, - NULL); + if (!assembly_name) + return false; - mono_profiler_fire_event_exit (); + g_strlcpy (name, assembly_name, name_len); + return true; } -static -void -mono_profiler_monitor_failed ( - MonoProfiler *prof, - MonoObject *object) +bool +ep_rt_mono_method_get_full_name ( + ep_rt_method_desc_t *method, + ep_char8_t *name, + size_t name_len) { - if (!EventEnabledMonoProfilerMonitorFailed ()) - return; + EP_ASSERT (method != NULL); + EP_ASSERT (name != NULL); - mono_profiler_fire_event_enter (); + char *full_method_name = mono_method_get_name_full (method, TRUE, TRUE, MONO_TYPE_NAME_FORMAT_IL); + if (!full_method_name) + return false; - FireEtwMonoProfilerMonitorFailed ( - SGEN_POINTER_UNTAG_ALL (object), - NULL, - NULL); + g_strlcpy (name, full_method_name, name_len); - mono_profiler_fire_event_exit (); + g_free (full_method_name); + return true; } static -void -mono_profiler_monitor_acquired ( - MonoProfiler *prof, - MonoObject *object) +bool +is_keword_enabled (uint64_t enabled_keywords, uint64_t keyword) { - if (!EventEnabledMonoProfilerMonitorAcquired ()) - return; + return (enabled_keywords & keyword) == keyword; +} - mono_profiler_fire_event_enter (); +uint64_t +ep_rt_mono_session_calculate_and_count_all_keywords ( + const ep_char8_t *provider, + uint64_t keywords[], + uint64_t count[], + size_t len) +{ + ep_requires_lock_held (); + + uint64_t keywords_for_all_sessions = 0; + + for (int i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; i++) { + EventPipeSession *session = ep_volatile_load_session_without_barrier (i); + if (session) { + EventPipeSessionProviderList *providers = ep_session_get_providers (session); + EP_ASSERT (providers != NULL); + + EventPipeSessionProvider *session_provider = ep_session_provider_list_find_by_name (ep_session_provider_list_get_providers (providers), provider); + if (session_provider) { + uint64_t session_keywords = ep_session_provider_get_keywords (session_provider); + for (uint64_t j = 0; j < len; j++) { + if (is_keword_enabled (session_keywords, keywords [j])) + count [j]++; + } + keywords_for_all_sessions = keywords_for_all_sessions | session_keywords; + } + } + } - FireEtwMonoProfilerMonitorAcquired ( - SGEN_POINTER_UNTAG_ALL (object), - NULL, - NULL); + ep_requires_lock_held (); - mono_profiler_fire_event_exit (); + return keywords_for_all_sessions; } -static -void -mono_profiler_thread_started ( - MonoProfiler *prof, - uintptr_t tid) +bool +ep_rt_mono_sesion_has_all_started (void) { - if (!EventEnabledMonoProfilerThreadStarted ()) - return; + ep_requires_lock_held (); - mono_profiler_fire_event_enter (); + bool all_started = true; + for (uint32_t i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; ++i) { + EventPipeSession *session = ep_volatile_load_session_without_barrier (i); + if (session) { + all_started = ep_session_has_started (session); + if (!all_started) + break; + } + } - FireEtwMonoProfilerThreadStarted ( - (uint64_t)tid, - NULL, - NULL); + ep_requires_lock_held (); - mono_profiler_fire_event_exit (); + return all_started; } static void -mono_profiler_thread_stopping ( - MonoProfiler *prof, - uintptr_t tid) +runtime_initialized_callback (MonoProfiler *prof) { - if (!EventEnabledMonoProfilerThreadStopping ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerThreadStopping ( - (uint64_t)tid, - NULL, - NULL); - - mono_profiler_fire_event_exit (); + _ep_rt_mono_runtime_initialized = TRUE; } static void -mono_profiler_thread_stopped ( +thread_started_callback ( MonoProfiler *prof, uintptr_t tid) { - if (!EventEnabledMonoProfilerThreadStopped ()) - return; - - mono_profiler_fire_event_enter (); - - FireEtwMonoProfilerThreadStopped ( - (uint64_t)tid, - NULL, - NULL); - - mono_profiler_fire_event_exit (); + ep_rt_mono_runtime_provider_thread_started_callback (prof, tid); } static void -mono_profiler_thread_exited ( +thread_stopped_callback ( MonoProfiler *prof, uintptr_t tid) { - if (!EventEnabledMonoProfilerThreadExited ()) - return; - - mono_profiler_fire_event_enter (); + ep_rt_mono_runtime_provider_thread_stopped_callback (prof, tid); - FireEtwMonoProfilerThreadExited ( - (uint64_t)tid, - NULL, - NULL); + if (_eventpipe_initialized) { + EventPipeThreadHolder *thread_holder = (EventPipeThreadHolder *)mono_native_tls_get_value (_ep_rt_mono_thread_holder_tls_id); + if (thread_holder) + thread_holder_free_func (thread_holder); + mono_native_tls_set_value (_ep_rt_mono_thread_holder_tls_id, NULL); - mono_profiler_fire_event_exit (); + EventPipeMonoThreadData *thread_data = (EventPipeMonoThreadData *)mono_native_tls_get_value (_thread_data_tls_id); + if (thread_data) + ep_rt_object_free (thread_data); + mono_native_tls_set_value (_thread_data_tls_id, NULL); + } } -static void -mono_profiler_thread_name ( - MonoProfiler *prof, - uintptr_t tid, - const char *name) +ep_rt_mono_component_init (void) { - if (!EventEnabledMonoProfilerThreadName ()) - return; + ep_rt_spin_lock_alloc (&_ep_rt_mono_config_lock); - mono_profiler_fire_event_enter (); + RUNTIME_PROVIDER_CONTEXT = MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_EVENTPIPE_Context; + RUNTIME_PRIVATE_PROVIDER_CONTEXT = MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_EVENTPIPE_Context; + RUNTIME_RUNDOWN_PROVIDER_CONTEXT = MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_EVENTPIPE_Context; + RUNTIME_STRESS_PROVIDER_CONTEXT = MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_EVENTPIPE_Context; + RUNTIME_MONO_PROFILER_PROVIDER_CONTEXT = MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context; - FireEtwMonoProfilerThreadName ( - (uint64_t)tid, - (ep_char8_t *)(name ? name : ""), - NULL, - NULL); + _ep_rt_mono_default_profiler_provider = mono_profiler_create (NULL); - mono_profiler_fire_event_exit (); -} + char *diag_env = g_getenv("MONO_DIAGNOSTICS"); + if (diag_env) { + int diag_argc = 1; + char **diag_argv = g_new (char *, 1); + if (diag_argv) { + diag_argv [0] = NULL; + if (!mono_parse_options_from (diag_env, &diag_argc, &diag_argv)) { + for (int i = 0; i < diag_argc; ++i) { + if (diag_argv [i]) { + if (strncmp (diag_argv [i], "--diagnostic-ports=", 19) == 0) { + char *diag_ports_env = g_getenv("DOTNET_DiagnosticPorts"); + if (diag_ports_env) + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_DIAGNOSTICS, "DOTNET_DiagnosticPorts environment variable already set, ignoring --diagnostic-ports used in MONO_DIAGNOSTICS environment variable"); + else + g_setenv ("DOTNET_DiagnosticPorts", diag_argv [i] + 19, TRUE); + g_free (diag_ports_env); + } else if (ep_rt_mono_profiler_provider_parse_options (diag_argv [i])) { + ; + } else { + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable, unknown option: %s", diag_argv [i]); + } -static -const EventFilterDescriptor * -mono_profiler_add_provider_param (const EventFilterDescriptor *key) -{ - ep_rt_spin_lock_requires_lock_held (&_ep_rt_mono_profiler_gc_state_lock); - - EventFilterDescriptor *param = NULL; - if (key && key->ptr && key->size) { - uint64_t param_ptr = (uint64_t)g_malloc (key->size); - if (param_ptr) { - param = ep_event_filter_desc_alloc (param_ptr, key->size, key->type); - if (param) { - memcpy ((uint8_t*)(uintptr_t)param->ptr,(const uint8_t*)(uintptr_t)key->ptr, key->size); - _ep_rt_mono_profiler_provider_params = g_slist_append (_ep_rt_mono_profiler_provider_params, param); - } else { - g_free ((void *)(uintptr_t)param_ptr); - } - } - } - return param; -} + g_free (diag_argv [i]); + diag_argv [i] = NULL; + } + } -static -bool -mono_profiler_remove_provider_param (const EventFilterDescriptor *key) -{ - ep_rt_spin_lock_requires_lock_held (&_ep_rt_mono_profiler_gc_state_lock); - - bool removed = false; - if (_ep_rt_mono_profiler_provider_params && key && key->ptr && key->size) { - GSList *list = _ep_rt_mono_profiler_provider_params; - EventFilterDescriptor *param = NULL; - while (list) { - param = (EventFilterDescriptor *)(list->data); - if (param && param->ptr && param->type == key->type && param->size == key->size && - memcmp ((const void *)(uintptr_t)param->ptr, (const void *)(uintptr_t)key->ptr, param->size) == 0) { - g_free ((void *)(uintptr_t)param->ptr); - ep_event_filter_desc_free (param); - _ep_rt_mono_profiler_provider_params = g_slist_delete_link (_ep_rt_mono_profiler_provider_params, list); - removed = true; - break; + g_free (diag_argv); + } else { + mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed parsing MONO_DIAGNOSTICS environment variable"); } - list = list->next; } } + g_free (diag_env); - return removed; + ep_rt_mono_runtime_provider_component_init (); + ep_rt_mono_profiler_provider_component_init (); } -static void -mono_profiler_free_provider_params (void) -{ - // Should only be called from ep_rt_mono_fini. - for (GSList *list = _ep_rt_mono_profiler_provider_params; list; list = list->next) { - EventFilterDescriptor *param = (EventFilterDescriptor *)(list->data); - if (param) { - g_free ((void *)(uintptr_t)param->ptr); - ep_event_filter_desc_free (param); - } - } - g_slist_free (_ep_rt_mono_profiler_provider_params); - _ep_rt_mono_profiler_provider_params = NULL; -} - -static -bool -mono_profiler_provider_params_get_value ( - const EventFilterDescriptor *param, - const ep_char8_t *key, - const ep_char8_t **value) +ep_rt_mono_init (void) { - if (!param || !param->ptr || !param->size || !key) - return false; - - const ep_char8_t *current = (ep_char8_t *)(uintptr_t)param->ptr; - const ep_char8_t *end = current + param->size; - bool found_key = false; + EP_ASSERT (_ep_rt_mono_default_profiler_provider != NULL); - if (value) - *value = ""; + mono_native_tls_alloc (&_ep_rt_mono_thread_holder_tls_id, NULL); + mono_native_tls_alloc (&_thread_data_tls_id, NULL); - if (!current [param->size - 1]) { - while (current < end) { - if (found_key) { - if (value) - *value = current; - break; - } + mono_100ns_ticks (); + mono_rand_open (); + _rand_provider = mono_rand_init (NULL, 0); - if (!ep_rt_utf8_string_compare_ignore_case (current, key)) { - found_key = true; - } + ep_rt_mono_runtime_provider_init (); + ep_rt_mono_profiler_provider_init (); - current = current + strlen (current) + 1; - } - } + mono_profiler_set_runtime_initialized_callback (_ep_rt_mono_default_profiler_provider, runtime_initialized_callback); + mono_profiler_set_thread_started_callback (_ep_rt_mono_default_profiler_provider, thread_started_callback); + mono_profiler_set_thread_stopped_callback (_ep_rt_mono_default_profiler_provider, thread_stopped_callback); - return found_key; + _eventpipe_initialized = TRUE; } -static -bool -mono_profiler_provider_param_contains_heap_collect_ondemand (const EventFilterDescriptor *param) +void +ep_rt_mono_init_finish (void) { - const ep_char8_t *value = NULL; - bool found_heap_collect_ondemand_value = false; + if (mono_runtime_get_no_exec ()) + return; - if (mono_profiler_provider_params_get_value (param, "heapcollect", &value)) { - if (strstr (value, "ondemand")) - found_heap_collect_ondemand_value = true; + // Managed init of diagnostics classes, like registration of RuntimeEventSource (if available). + ERROR_DECL (error); + + MonoClass *runtime_event_source = mono_class_from_name_checked (mono_get_corlib (), "System.Diagnostics.Tracing", "RuntimeEventSource", error); + if (is_ok (error) && runtime_event_source) { + MonoMethod *init = mono_class_get_method_from_name_checked (runtime_event_source, "Initialize", -1, 0, error); + if (is_ok (error) && init) { + mono_runtime_try_invoke_handle (init, NULL_HANDLE, NULL, error); + } } - return found_heap_collect_ondemand_value; + mono_error_cleanup (error); } -static void -mono_profiler_push_gc_heap_collect_param_request_value (const EventFilterDescriptor *param) +ep_rt_mono_fini (void) { - ep_rt_spin_lock_requires_lock_held (&_ep_rt_mono_profiler_gc_state_lock); + ep_rt_mono_runtime_provider_fini (); + ep_rt_mono_profiler_provider_fini (); - const ep_char8_t *value = NULL; - if (param) - mono_profiler_provider_params_get_value (param, "heapcollect", &value); + if (_eventpipe_initialized) + mono_rand_close (_rand_provider); - if (!_ep_rt_mono_profiler_gc_heap_collect_request_params) - _ep_rt_mono_profiler_gc_heap_collect_request_params = g_queue_new (); - if (_ep_rt_mono_profiler_gc_heap_collect_request_params) - g_queue_push_tail (_ep_rt_mono_profiler_gc_heap_collect_request_params, (gpointer)ep_rt_utf8_string_dup (value ? value : "")); -} + _rand_provider = NULL; + _eventpipe_initialized = FALSE; -static -void -mono_profiler_pop_gc_heap_collect_param_request_value (void) -{ - ep_rt_spin_lock_requires_lock_held (&_ep_rt_mono_profiler_gc_state_lock); + _ep_rt_mono_runtime_initialized = FALSE; - ep_char8_t *value = NULL; - if (_ep_rt_mono_profiler_gc_heap_collect_request_params && !g_queue_is_empty (_ep_rt_mono_profiler_gc_heap_collect_request_params)) - value = (ep_char8_t *)g_queue_pop_head (_ep_rt_mono_profiler_gc_heap_collect_request_params); - g_free (value); -} + if (_ep_rt_mono_default_profiler_provider) { + mono_profiler_set_runtime_initialized_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_thread_started_callback (_ep_rt_mono_default_profiler_provider, NULL); + mono_profiler_set_thread_stopped_callback (_ep_rt_mono_default_profiler_provider, NULL); + } + _ep_rt_mono_default_profiler_provider = NULL; -static -const ep_char8_t * -mono_profiler_get_gc_heap_collect_param_request_value (void) -{ - ep_rt_spin_lock_requires_lock_held (&_ep_rt_mono_profiler_gc_state_lock); + if (_ep_rt_mono_thread_holder_tls_id) + mono_native_tls_free (_ep_rt_mono_thread_holder_tls_id); + _ep_rt_mono_thread_holder_tls_id = 0; - ep_char8_t *value = NULL; - if (_ep_rt_mono_profiler_gc_heap_collect_request_params && !g_queue_is_empty (_ep_rt_mono_profiler_gc_heap_collect_request_params)) { - value = (ep_char8_t *)g_queue_pop_head (_ep_rt_mono_profiler_gc_heap_collect_request_params); - g_queue_push_head (_ep_rt_mono_profiler_gc_heap_collect_request_params, (gpointer)value); - } - return value ? value : ""; + if (_thread_data_tls_id) + mono_native_tls_free (_thread_data_tls_id); + _thread_data_tls_id = 0; + + _ep_rt_mono_os_cmd_line_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; + _ep_rt_mono_os_cmd_line = NULL; + + _ep_rt_mono_managed_cmd_line_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED; + _ep_rt_mono_managed_cmd_line = NULL; + + ep_rt_spin_lock_free (&_ep_rt_mono_config_lock); } -static void -mono_profiler_free_gc_heap_collect_param_requests (void) +EventPipeEtwCallbackDotNETRuntimeRundown ( + const uint8_t *source_id, + unsigned long is_enabled, + uint8_t level, + uint64_t match_any_keywords, + uint64_t match_all_keywords, + EventFilterDescriptor *filter_data, + void *callback_data) { - // Should only be called from ep_rt_mono_fini. - if (_ep_rt_mono_profiler_gc_heap_collect_request_params) { - while (!g_queue_is_empty (_ep_rt_mono_profiler_gc_heap_collect_request_params)) - g_free (g_queue_pop_head (_ep_rt_mono_profiler_gc_heap_collect_request_params)); - g_queue_free (_ep_rt_mono_profiler_gc_heap_collect_request_params); - _ep_rt_mono_profiler_gc_heap_collect_request_params = NULL; - } + RUNTIME_RUNDOWN_PROVIDER_CONTEXT.Level = level; + RUNTIME_RUNDOWN_PROVIDER_CONTEXT.EnabledKeywordsBitmask = match_any_keywords; + RUNTIME_RUNDOWN_PROVIDER_CONTEXT.IsEnabled = (is_enabled == 1 ? true : false); } -static void -mono_profiler_ep_provider_callback ( +EventPipeEtwCallbackDotNETRuntimePrivate ( const uint8_t *source_id, unsigned long is_enabled, uint8_t level, @@ -7495,288 +905,13 @@ mono_profiler_ep_provider_callback ( EventFilterDescriptor *filter_data, void *callback_data) { - ep_rt_config_requires_lock_not_held (); - ep_rt_spin_lock_requires_lock_held (&_ep_rt_mono_profiler_gc_state_lock); - - EP_ASSERT(is_enabled == 0 || is_enabled == 1) ; - EP_ASSERT (_ep_rt_dotnet_mono_profiler_provider != NULL); - EP_ASSERT (_ep_rt_dotnet_mono_profiler_heap_collect_provider != NULL); - - match_any_keywords = (is_enabled == 1) ? match_any_keywords : 0; - - EP_LOCK_ENTER (section1) - uint64_t enabled_keywords = MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask; - - if (profiler_callback_is_enabled(match_any_keywords, LOADER_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, LOADER_KEYWORD)) { - mono_profiler_set_domain_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_loading); - mono_profiler_set_domain_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_loaded); - mono_profiler_set_domain_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_unloading); - mono_profiler_set_domain_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_unloaded); - mono_profiler_set_domain_name_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_app_domain_name); - mono_profiler_set_image_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_loading); - mono_profiler_set_image_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_failed); - mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_loaded); - mono_profiler_set_image_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_unloading); - mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_module_unloaded); - mono_profiler_set_assembly_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_loading); - mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_loaded); - mono_profiler_set_assembly_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_unloading); - mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_assembly_unloaded); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, LOADER_KEYWORD)) { - mono_profiler_set_domain_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_domain_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_domain_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_domain_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_domain_name_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_image_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_image_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_image_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_image_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_image_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_assembly_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_assembly_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_assembly_unloading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_assembly_unloaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, JIT_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { - mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_begin); - mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_failed); - mono_profiler_set_jit_done_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_done); - mono_profiler_set_jit_chunk_created_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_chunk_created); - mono_profiler_set_jit_chunk_destroyed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_chunk_destroyed); - mono_profiler_set_jit_code_buffer_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_jit_code_buffer); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, JIT_KEYWORD)) { - mono_profiler_set_jit_begin_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_jit_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_jit_done_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_jit_chunk_created_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_jit_chunk_destroyed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_jit_code_buffer_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, TYPE_LOADING_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, TYPE_LOADING_KEYWORD)) { - mono_profiler_set_class_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_class_loading); - mono_profiler_set_class_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_class_failed); - mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_class_loaded); - mono_profiler_set_vtable_loading_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_vtable_loading); - mono_profiler_set_vtable_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_vtable_failed); - mono_profiler_set_vtable_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_vtable_loaded); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, TYPE_LOADING_KEYWORD)) { - mono_profiler_set_class_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_class_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_class_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_vtable_loading_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_vtable_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_vtable_loaded_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, METHOD_TRACING_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, METHOD_TRACING_KEYWORD)) { - mono_profiler_set_method_enter_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_enter); - mono_profiler_set_method_leave_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_leave); - mono_profiler_set_method_tail_call_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_tail_call); - mono_profiler_set_method_exception_leave_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_exception_leave); - mono_profiler_set_method_free_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_free); - mono_profiler_set_method_begin_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_begin_invoke); - mono_profiler_set_method_end_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_end_invoke); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, METHOD_TRACING_KEYWORD)) { - mono_profiler_set_method_enter_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_method_leave_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_method_tail_call_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_method_exception_leave_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_method_free_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_method_begin_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_method_end_invoke_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, EXCEPTION_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, EXCEPTION_KEYWORD)) { - mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_exception_throw); - mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_exception_clause); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, EXCEPTION_KEYWORD)) { - mono_profiler_set_exception_throw_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_exception_clause_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, GC_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, GC_KEYWORD)) { - mono_profiler_set_gc_event_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_event); - } - } else { - // NOTE, disabled in mono_profiler_gc_event, MONO_GC_EVENT_POST_START_WORLD to make sure all - // callbacks during GC fires. - } - - if (profiler_callback_is_enabled(match_any_keywords, GC_ALLOCATION_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, GC_ALLOCATION_KEYWORD)) { - mono_profiler_set_gc_allocation_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_allocation); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, GC_ALLOCATION_KEYWORD)) { - mono_profiler_set_gc_allocation_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, GC_HANDLE_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, GC_HANDLE_KEYWORD)) { - mono_profiler_set_gc_handle_created_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_handle_created); - mono_profiler_set_gc_handle_deleted_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_handle_deleted); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, GC_HANDLE_KEYWORD)) { - mono_profiler_set_gc_handle_created_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_gc_handle_deleted_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, GC_FINALIZATION_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, GC_FINALIZATION_KEYWORD)) { - mono_profiler_set_gc_finalizing_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalizing); - mono_profiler_set_gc_finalized_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalized); - mono_profiler_set_gc_finalizing_object_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalizing_object); - mono_profiler_set_gc_finalized_object_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_finalized_object); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, GC_FINALIZATION_KEYWORD)) { - mono_profiler_set_gc_finalizing_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_gc_finalized_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_gc_finalizing_object_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_gc_finalized_object_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, GC_ROOT_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, GC_ROOT_KEYWORD)) { - mono_profiler_set_gc_root_register_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_root_register); - mono_profiler_set_gc_root_unregister_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_gc_root_unregister); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, GC_ROOT_KEYWORD)) { - mono_profiler_set_gc_root_register_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_gc_root_unregister_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, GC_HEAP_COLLECT_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, GC_HEAP_COLLECT_KEYWORD)) { - mono_profiler_set_gc_finalized_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, mono_profiler_trigger_heap_collect); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, GC_HEAP_COLLECT_KEYWORD)) { - mono_profiler_set_gc_finalized_callback (_ep_rt_dotnet_mono_profiler_heap_collect_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, MONITOR_KEYWORD) || profiler_callback_is_enabled(match_any_keywords, CONTENTION_KEYWORD)) { - if (!(profiler_callback_is_enabled(enabled_keywords, MONITOR_KEYWORD) && profiler_callback_is_enabled(enabled_keywords, CONTENTION_KEYWORD))) { - mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_monitor_contention); - } - } else { - if (profiler_callback_is_enabled(enabled_keywords, MONITOR_KEYWORD) || profiler_callback_is_enabled(enabled_keywords, CONTENTION_KEYWORD)) { - mono_profiler_set_monitor_contention_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, MONITOR_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, MONITOR_KEYWORD)) { - mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_monitor_failed); - mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_monitor_acquired); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, MONITOR_KEYWORD)) { - mono_profiler_set_monitor_failed_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_monitor_acquired_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (profiler_callback_is_enabled(match_any_keywords, THREADING_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, THREADING_KEYWORD)) { - mono_profiler_set_thread_started_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_started); - mono_profiler_set_thread_stopping_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_stopping); - mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_stopped); - mono_profiler_set_thread_exited_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_exited); - mono_profiler_set_thread_name_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_thread_name); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, THREADING_KEYWORD)) { - mono_profiler_set_thread_started_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_thread_stopping_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_thread_stopped_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_thread_exited_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - mono_profiler_set_thread_name_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - - if (!_ep_rt_dotnet_mono_profiler_provider_callspec.enabled) { - if (profiler_callback_is_enabled(match_any_keywords, METHOD_INSTRUMENTATION_KEYWORD)) { - if (!profiler_callback_is_enabled (enabled_keywords, METHOD_INSTRUMENTATION_KEYWORD)) { - mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, mono_profiler_method_instrumentation); - } - } else { - if (profiler_callback_is_enabled (enabled_keywords, METHOD_INSTRUMENTATION_KEYWORD)) { - mono_profiler_set_call_instrumentation_filter_callback (_ep_rt_dotnet_mono_profiler_provider, NULL); - } - } - } - - if (match_any_keywords) { - bool request_heap_collect = false; - if (profiler_callback_is_enabled (match_any_keywords, GC_HEAP_COLLECT_KEYWORD)) { - if (mono_profiler_gc_can_collect_heap () && !profiler_callback_is_enabled (enabled_keywords, GC_HEAP_COLLECT_KEYWORD)) - request_heap_collect = true; - } - - if (filter_data) { - if (mono_profiler_provider_param_contains_heap_collect_ondemand (filter_data) && !mono_profiler_remove_provider_param (filter_data)) { - mono_profiler_add_provider_param (filter_data); - if (mono_profiler_gc_can_collect_heap () && profiler_callback_is_enabled (match_any_keywords, GC_HEAP_COLLECT_KEYWORD)) - request_heap_collect = true; - } - } - - if (request_heap_collect) { - mono_profiler_push_gc_heap_collect_param_request_value (filter_data); - mono_profiler_gc_heap_collect_requests_inc (); - mono_gc_finalize_notify (); - } - } else { - mono_profiler_free_provider_params (); - } - - MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.Level = level; - MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.EnabledKeywordsBitmask = match_any_keywords; - MICROSOFT_DOTNETRUNTIME_MONO_PROFILER_PROVIDER_EVENTPIPE_Context.IsEnabled = (is_enabled == 1 ? true : false); - EP_LOCK_EXIT (section1) - -ep_on_exit: - ep_rt_config_requires_lock_not_held (); - return; - -ep_on_error: - ep_exit_error_handler (); + RUNTIME_PRIVATE_PROVIDER_CONTEXT.Level = level; + RUNTIME_PRIVATE_PROVIDER_CONTEXT.EnabledKeywordsBitmask = match_any_keywords; + RUNTIME_PRIVATE_PROVIDER_CONTEXT.IsEnabled = (is_enabled == 1 ? true : false); } void -EventPipeEtwCallbackDotNETRuntimeMonoProfiler ( +EventPipeEtwCallbackDotNETRuntimeStress ( const uint8_t *source_id, unsigned long is_enabled, uint8_t level, @@ -7785,25 +920,9 @@ EventPipeEtwCallbackDotNETRuntimeMonoProfiler ( EventFilterDescriptor *filter_data, void *callback_data) { - ep_rt_spin_lock_requires_lock_not_held (&_ep_rt_mono_profiler_gc_state_lock); - - EP_SPIN_LOCK_ENTER (&_ep_rt_mono_profiler_gc_state_lock, section1); - mono_profiler_ep_provider_callback ( - source_id, - is_enabled, - level, - match_any_keywords, - match_all_keywords, - filter_data, - callback_data); - EP_SPIN_LOCK_EXIT (&_ep_rt_mono_profiler_gc_state_lock, section1); - -ep_on_exit: - ep_rt_spin_lock_requires_lock_not_held (&_ep_rt_mono_profiler_gc_state_lock); - return; - -ep_on_error: - ep_exit_error_handler (); + RUNTIME_STRESS_PROVIDER_CONTEXT.Level = level; + RUNTIME_STRESS_PROVIDER_CONTEXT.EnabledKeywordsBitmask = match_any_keywords; + RUNTIME_STRESS_PROVIDER_CONTEXT.IsEnabled = (is_enabled == 1 ? true : false); } #endif /* ENABLE_PERFTRACING */ diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h index 7183393a698a4..c2e1088efaf29 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-mono.h @@ -58,6 +58,7 @@ extern bool ep_rt_mono_file_write (ep_rt_file_handle_t handle, const uint8_t *bu extern void * ep_rt_mono_thread_attach (bool background_thread); extern void * ep_rt_mono_thread_attach_2 (bool background_thread, EventPipeThreadType thread_type); extern void ep_rt_mono_thread_detach (void); +extern void ep_rt_mono_component_init (void); extern void ep_rt_mono_init (void); extern void ep_rt_mono_init_finish (void); extern void ep_rt_mono_fini (void); @@ -382,8 +383,6 @@ void ep_rt_init (void) { ep_rt_mono_init (); - - ep_rt_spin_lock_alloc (ep_rt_mono_config_lock_get ()); } static @@ -402,8 +401,6 @@ ep_rt_shutdown (void) mono_lazy_cleanup (managed_command_line_get_init (), managed_command_line_lazy_clean); mono_lazy_cleanup (os_command_line_get_init (), os_command_line_lazy_clean); - ep_rt_spin_lock_free (ep_rt_mono_config_lock_get ()); - ep_rt_mono_fini (); } @@ -1818,90 +1815,7 @@ ep_rt_volatile_store_ptr_without_barrier ( */ bool -ep_rt_mono_write_event_ee_startup_start (void); - -typedef struct _BulkTypeEventLogger BulkTypeEventLogger; - -void -ep_rt_mono_fire_bulk_type_event (BulkTypeEventLogger *p_type_logger); - -int -ep_rt_mono_log_single_type ( - BulkTypeEventLogger *p_type_logger, - MonoType *mono_type); - -void -ep_rt_mono_log_type_and_parameters ( - BulkTypeEventLogger *p_type_logger, - MonoType *mono_type); - -void -ep_rt_mono_log_type_and_parameters_if_necessary ( - BulkTypeEventLogger *p_type_logger, - MonoType *mono_type); - -void -ep_rt_mono_send_method_details_event (MonoMethod *method); - -bool -ep_rt_mono_write_event_jit_start (MonoMethod *method); - -bool -ep_rt_mono_write_event_method_il_to_native_map ( - MonoMethod *method, - MonoJitInfo *ji); - -bool -ep_rt_mono_write_event_method_load ( - MonoMethod *method, - MonoJitInfo *ji); - -bool -ep_rt_mono_write_event_module_load (MonoImage *image); - -bool -ep_rt_mono_write_event_module_unload (MonoImage *image); - -bool -ep_rt_mono_write_event_assembly_load (MonoAssembly *assembly); - -bool -ep_rt_mono_write_event_assembly_unload (MonoAssembly *assembly); - -bool -ep_rt_mono_write_event_thread_created (ep_rt_thread_id_t tid); - -bool -ep_rt_mono_write_event_thread_terminated (ep_rt_thread_id_t tid); - -bool -ep_rt_mono_write_event_type_load_start (MonoType *type); - -bool -ep_rt_mono_write_event_type_load_stop (MonoType *type); - -bool -ep_rt_mono_write_event_exception_thrown (MonoObject *object); - -bool -ep_rt_mono_write_event_exception_clause ( - MonoMethod *method, - uint32_t clause_num, - MonoExceptionEnum clause_type, - MonoObject *obj); - -bool -ep_rt_mono_write_event_monitor_contention_start (MonoObject *obj); - -bool -ep_rt_mono_write_event_monitor_contention_stop (MonoObject *obj); - -bool -ep_rt_mono_write_event_method_jit_memory_allocated_for_code ( - const uint8_t *buffer, - uint64_t size, - MonoProfilerCodeBufferType type, - const void *data); +ep_rt_write_event_ee_startup_start (void); bool ep_rt_write_event_threadpool_worker_thread_start ( @@ -2053,6 +1967,64 @@ EventPipeEtwCallbackDotNETRuntimeMonoProfiler ( EventFilterDescriptor *filter_data, void *callback_data); +/* +* Shared EventPipe provider defines/types/functions. +*/ + +#define GC_KEYWORD 0x1 +#define GC_HANDLE_KEYWORD 0x2 +#define LOADER_KEYWORD 0x8 +#define JIT_KEYWORD 0x10 +#define APP_DOMAIN_RESOURCE_MANAGEMENT_KEYWORD 0x800 +#define CONTENTION_KEYWORD 0x4000 +#define EXCEPTION_KEYWORD 0x8000 +#define THREADING_KEYWORD 0x10000 +#define TYPE_KEYWORD 0x80000 +#define GC_HEAP_DUMP_KEYWORD 0x100000 +#define GC_ALLOCATION_KEYWORD 0x200000 +#define GC_MOVES_KEYWORD 0x400000 +#define GC_HEAP_COLLECT_KEYWORD 0x800000 +#define GC_HEAP_AND_TYPE_NAMES_KEYWORD 0x1000000 +#define GC_FINALIZATION_KEYWORD 0x1000000 +#define GC_RESIZE_KEYWORD 0x2000000 +#define GC_ROOT_KEYWORD 0x4000000 +#define GC_HEAP_DUMP_VTABLE_CLASS_REF_KEYWORD 0x8000000 +#define METHOD_TRACING_KEYWORD 0x20000000 +#define TYPE_DIAGNOSTIC_KEYWORD 0x8000000000 +#define TYPE_LOADING_KEYWORD 0x8000000000 +#define MONITOR_KEYWORD 0x10000000000 +#define METHOD_INSTRUMENTATION_KEYWORD 0x40000000000 + +// Custom Mono EventPipe thread data. +typedef struct _EventPipeMonoThreadData EventPipeMonoThreadData; +struct _EventPipeMonoThreadData { + void *gc_heap_dump_context; + bool prevent_profiler_event_recursion; +}; + +static +inline +bool +ep_rt_mono_is_runtime_initialized (void) +{ + extern gboolean _ep_rt_mono_runtime_initialized; + return !!_ep_rt_mono_runtime_initialized; +} + +extern EventPipeMonoThreadData * ep_rt_mono_thread_data_get_or_create (void); +extern uint64_t ep_rt_mono_session_calculate_and_count_all_keywords (const ep_char8_t *provider, uint64_t keywords[], uint64_t count[], size_t len); +extern bool ep_rt_mono_sesion_has_all_started (void); + +extern void ep_rt_mono_runtime_provider_component_init (void); +extern void ep_rt_mono_runtime_provider_init (void); +extern void ep_rt_mono_runtime_provider_fini (void); +extern void ep_rt_mono_runtime_provider_thread_started_callback (MonoProfiler *prof, uintptr_t tid); +extern void ep_rt_mono_runtime_provider_thread_stopped_callback (MonoProfiler *prof, uintptr_t tid); + +extern void ep_rt_mono_profiler_provider_component_init (void); +extern void ep_rt_mono_profiler_provider_init (void); +extern void ep_rt_mono_profiler_provider_fini (void); +extern bool ep_rt_mono_profiler_provider_parse_options (const char *options); #endif /* ENABLE_PERFTRACING */ #endif /* __EVENTPIPE_RT_MONO_H__ */ diff --git a/src/mono/mono/eventpipe/eventpipe.cmake b/src/mono/mono/eventpipe/eventpipe.cmake index cd0d4a6f123c8..0fc46a01fd215 100644 --- a/src/mono/mono/eventpipe/eventpipe.cmake +++ b/src/mono/mono/eventpipe/eventpipe.cmake @@ -47,6 +47,8 @@ if(ENABLE_PERFTRACING) list(APPEND MONO_EVENTPIPE_SHIM_SOURCES ep-rt-mono.c + ep-rt-mono-runtime-provider.c + ep-rt-mono-profiler-provider.c ) list(APPEND MONO_DIAGNOSTIC_SERVER_SHIM_SOURCES diff --git a/src/mono/mono/eventpipe/gen-eventing-event-inc.lst b/src/mono/mono/eventpipe/gen-eventing-event-inc.lst index 47979354dc6ff..8a9f572a1d5c4 100644 --- a/src/mono/mono/eventpipe/gen-eventing-event-inc.lst +++ b/src/mono/mono/eventpipe/gen-eventing-event-inc.lst @@ -24,6 +24,13 @@ ExceptionFinallyStart ExceptionFinallyStop ExceptionThrown_V1 ExceptionThrownStop +GCBulkEdge +GCBulkNode +GCBulkRootConditionalWeakTableElementEdge +GCBulkRootEdge +GCBulkRootStaticVar +GCEnd_V1 +GCStart_V2 MethodDCEndILToNativeMap MethodDCEnd_V1 MethodDCEndVerbose_V1 diff --git a/src/mono/mono/sgen/sgen-gc.c b/src/mono/mono/sgen/sgen-gc.c index 5b6e5e3ca4c6c..5024faa35173c 100644 --- a/src/mono/mono/sgen/sgen-gc.c +++ b/src/mono/mono/sgen/sgen-gc.c @@ -3897,7 +3897,7 @@ sgen_gc_init (void) sgen_card_table_init (&remset); - sgen_register_root (NULL, 0, sgen_make_user_root_descriptor (sgen_mark_normal_gc_handles), ROOT_TYPE_NORMAL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handles (SGen, Normal)"); + sgen_register_root (NULL, 0, sgen_make_user_root_descriptor (sgen_mark_normal_gc_handles), ROOT_TYPE_NORMAL, MONO_ROOT_SOURCE_GC_HANDLE, GINT_TO_POINTER (ROOT_TYPE_NORMAL), "GC Handles (SGen, Normal)"); gc_initialized = 1; diff --git a/src/mono/mono/sgen/sgen-gchandles.c b/src/mono/mono/sgen/sgen-gchandles.c index fa9742b93c0cc..f869775cb4dcb 100644 --- a/src/mono/mono/sgen/sgen-gchandles.c +++ b/src/mono/mono/sgen/sgen-gchandles.c @@ -105,7 +105,7 @@ static void bucket_alloc_callback (gpointer *bucket, guint32 new_bucket_size, gboolean alloc) { if (alloc) - sgen_register_root ((char *)bucket, new_bucket_size, SGEN_DESCRIPTOR_NULL, ROOT_TYPE_PINNED, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Bucket (SGen, Pinned)"); + sgen_register_root ((char *)bucket, new_bucket_size, SGEN_DESCRIPTOR_NULL, ROOT_TYPE_PINNED, MONO_ROOT_SOURCE_GC_HANDLE, GINT_TO_POINTER (ROOT_TYPE_PINNED), "GC Handle Bucket (SGen, Pinned)"); else sgen_deregister_root ((char *)bucket); } @@ -114,7 +114,7 @@ static void bucket_alloc_report_root (gpointer *bucket, guint32 new_bucket_size, gboolean alloc) { if (alloc) - sgen_client_root_registered ((char *)bucket, new_bucket_size, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Bucket (SGen, Normal)"); + sgen_client_root_registered ((char *)bucket, new_bucket_size, MONO_ROOT_SOURCE_GC_HANDLE, GINT_TO_POINTER (ROOT_TYPE_NORMAL), "GC Handle Bucket (SGen, Normal)"); else sgen_client_root_deregistered ((char *)bucket); } diff --git a/src/native/eventpipe/ep-session.c b/src/native/eventpipe/ep-session.c index 58ae8df627753..61eecd78355eb 100644 --- a/src/native/eventpipe/ep-session.c +++ b/src/native/eventpipe/ep-session.c @@ -50,6 +50,8 @@ EP_RT_DEFINE_THREAD_FUNC (streaming_thread) bool success = true; ep_rt_wait_event_handle_t *wait_event = ep_session_get_wait_event (session); + ep_rt_volatile_store_uint32_t (&session->started, 1); + EP_GCX_PREEMP_ENTER while (ep_session_get_streaming_enabled (session)) { bool events_written = false; @@ -203,6 +205,7 @@ ep_session_alloc ( instance->session_start_timestamp = ep_perf_timestamp_get (); instance->paused = false; instance->enable_stackwalk = ep_rt_config_value_get_enable_stackwalk (); + instance->started = 0; ep_on_exit: ep_requires_lock_held (); @@ -393,6 +396,9 @@ ep_session_start_streaming (EventPipeSession *session) EP_ASSERT (!ep_session_get_streaming_enabled (session)); } + if (session->session_type != EP_SESSION_TYPE_IPCSTREAM && session->session_type != EP_SESSION_TYPE_FILESTREAM) + ep_rt_volatile_store_uint32_t_without_barrier (&session->started, 1); + ep_requires_lock_held (); return; } @@ -581,6 +587,13 @@ ep_session_resume (EventPipeSession *session) session->paused = false; } +bool +ep_session_has_started (EventPipeSession *session) +{ + EP_ASSERT (session != NULL); + return ep_rt_volatile_load_uint32_t (&session->started) == 1 ? true : false; +} + #endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */ #endif /* ENABLE_PERFTRACING */ diff --git a/src/native/eventpipe/ep-session.h b/src/native/eventpipe/ep-session.h index c7923cb5c97cb..6b26dc055e092 100644 --- a/src/native/eventpipe/ep-session.h +++ b/src/native/eventpipe/ep-session.h @@ -61,6 +61,8 @@ struct _EventPipeSession_Internal { bool paused; // Set via environment variable to enable or disable stack collection globally bool enable_stackwalk; + // Indicate that session is fully running (streaming thread started). + volatile uint32_t started; }; #if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_SESSION_GETTER_SETTER) @@ -199,5 +201,8 @@ ep_session_pause (EventPipeSession *session); void ep_session_resume (EventPipeSession *session); +bool +ep_session_has_started (EventPipeSession *session); + #endif /* ENABLE_PERFTRACING */ #endif /* __EVENTPIPE_SESSION_H__ */ diff --git a/src/native/eventpipe/ep.h b/src/native/eventpipe/ep.h index e711815425e63..5073c52df8765 100644 --- a/src/native/eventpipe/ep.h +++ b/src/native/eventpipe/ep.h @@ -336,32 +336,20 @@ ep_write_buffer_uintptr_t (uint8_t **buffer, uintptr_t value) static inline uint32_t -ep_write_buffer_string_utf8_to_utf16_t (uint8_t **buf, const ep_char8_t *str, uint32_t len) +ep_write_buffer_string_utf16_t (uint8_t **buf, const ep_char16_t *str, uint32_t len) { - if (len == 0) { - (*buf)[0] = 0; - (*buf)[1] = 0; - *buf += sizeof (ep_char16_t); - return sizeof (ep_char16_t); + uint32_t num_bytes = 0; + if (str && len != 0) { + num_bytes = len * sizeof (ep_char16_t); + memcpy (*buf, str, num_bytes); } - ep_char16_t *str_utf16 = ep_rt_utf8_to_utf16le_string (str, len); - uint32_t num_bytes_utf16_str = 0; - while (str_utf16[num_bytes_utf16_str] != 0) - ++num_bytes_utf16_str; - num_bytes_utf16_str = (num_bytes_utf16_str + 1) * sizeof (ep_char16_t); - memcpy (*buf, str_utf16, num_bytes_utf16_str); - *buf += num_bytes_utf16_str; - ep_rt_utf16_string_free (str_utf16); - return num_bytes_utf16_str; -} -static -inline -uint32_t -ep_write_buffer_string_utf16_t (uint8_t **buf, const ep_char16_t *str, uint32_t len) -{ - uint32_t num_bytes = (len + 1) * sizeof (ep_char16_t); - memcpy (*buf, str, num_bytes); + (*buf) [num_bytes] = 0; + num_bytes++; + + (*buf) [num_bytes] = 0; + num_bytes++; + *buf += num_bytes; return num_bytes; }