From: Massimiliano Mantione Date: Mon, 7 Jan 2008 15:10:41 +0000 (-0000) Subject: * mono-profiler-oprofile.c: First code drop of new logging profiler. X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;ds=sidebyside;h=eda30aca511ae187564ae6e14e5c97a4add1bd18;p=mono.git * mono-profiler-oprofile.c: First code drop of new logging profiler. * Makefile.am: Added logging profiler, but commented in out to avoid breaking the build on Windows. svn path=/trunk/mono/; revision=92392 --- diff --git a/mono/profiler/Makefile.am b/mono/profiler/Makefile.am index 6da82ef16b7..bf3b14ee331 100644 --- a/mono/profiler/Makefile.am +++ b/mono/profiler/Makefile.am @@ -7,6 +7,7 @@ INCLUDES = \ if JIT_SUPPORTED lib_LTLIBRARIES = libmono-profiler-cov.la libmono-profiler-aot.la +#lib_LTLIBRARIES = libmono-profiler-cov.la libmono-profiler-aot.la libmono-profiler-logging.la endif if HAVE_OPROFILE @@ -17,4 +18,6 @@ libmono_profiler_cov_la_SOURCES = mono-cov.c libmono_profiler_cov_la_LIBADD = $(top_builddir)/mono/mini/libmono.la libmono_profiler_aot_la_SOURCES = mono-profiler-aot.c libmono_profiler_aot_la_LIBADD = $(top_builddir)/mono/mini/libmono.la +libmono_profiler_logging_la_SOURCES = mono-profiler-logging.c +libmono_profiler_logging_la_LIBADD = $(top_builddir)/mono/mini/libmono.la diff --git a/mono/profiler/mono-profiler-logging.c b/mono/profiler/mono-profiler-logging.c new file mode 100644 index 00000000000..6ec46a9ce8a --- /dev/null +++ b/mono/profiler/mono-profiler-logging.c @@ -0,0 +1,3109 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HAS_OPROFILE 1 + +#if (HAS_OPROFILE) +#include +#endif + +// Needed for heap analysis +extern gboolean mono_object_is_alive (MonoObject* obj); + +typedef enum { + MONO_PROFILER_FILE_BLOCK_KIND_INTRO = 1, + MONO_PROFILER_FILE_BLOCK_KIND_END = 2, + MONO_PROFILER_FILE_BLOCK_KIND_MAPPING = 3, + MONO_PROFILER_FILE_BLOCK_KIND_LOADED = 4, + MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED = 5, + MONO_PROFILER_FILE_BLOCK_KIND_EVENTS = 6, + MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL = 7, + MONO_PROFILER_FILE_BLOCK_KIND_HEAP = 8 +} MonoProfilerFileBlockKind; + +#define MONO_PROFILER_LOADED_EVENT_MODULE 1 +#define MONO_PROFILER_LOADED_EVENT_ASSEMBLY 2 +#define MONO_PROFILER_LOADED_EVENT_APPDOMAIN 4 +#define MONO_PROFILER_LOADED_EVENT_SUCCESS 8 +#define MONO_PROFILER_LOADED_EVENT_FAILURE 16 + +typedef enum { + MONO_PROFILER_EVENT_DATA_TYPE_OTHER = 0, + MONO_PROFILER_EVENT_DATA_TYPE_METHOD = 1, + MONO_PROFILER_EVENT_DATA_TYPE_CLASS = 2 +} MonoProfilerEventDataType; + +typedef struct _ProfilerEventData { + union { + gpointer address; + gsize number; + } data; + unsigned int data_type:2; + unsigned int code:3; + unsigned int kind:1; + unsigned int value:26; +} ProfilerEventData; + +#define EXTENDED_EVENT_VALUE_SHIFT (26) +#define MAX_EVENT_VALUE ((1< +#include +#include +#include +#include +#include +#include + +#define MUTEX_TYPE pthread_mutex_t +#define INITIALIZE_PROFILER_MUTEX() pthread_mutex_init (&(profiler->mutex), NULL) +#define DELETE_PROFILER_MUTEX() pthread_mutex_destroy (&(profiler->mutex)) +#define LOCK_PROFILER() pthread_mutex_lock (&(profiler->mutex)) +#define UNLOCK_PROFILER() pthread_mutex_unlock (&(profiler->mutex)) + +#define THREAD_TYPE pthread_t +#define CREATE_WRITER_THREAD(f) pthread_create (&(profiler->data_writer_thread), NULL, ((void*(*)(void*))f), NULL) +#define EXIT_THREAD() pthread_exit (NULL); +#define WAIT_WRITER_THREAD() pthread_join (profiler->data_writer_thread, NULL) +#define CURRENT_THREAD_ID() (gsize) pthread_self () + +#ifndef HAVE_KW_THREAD +static pthread_key_t pthread_profiler_key; +static pthread_once_t profiler_pthread_once = PTHREAD_ONCE_INIT; +static void +make_pthread_profiler_key (void) { + (void) pthread_key_create (&pthread_profiler_key, NULL); +} +#define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) pthread_getspecific (pthread_profiler_key)) +#define SET_PROFILER_THREAD_DATA(x) (void) pthread_setspecific (pthread_profiler_key, (x)) +#define ALLOCATE_PROFILER_THREAD_DATA() (void) pthread_once (&profiler_pthread_once, make_pthread_profiler_key) +#define FREE_PROFILER_THREAD_DATA() (void) pthread_key_delete (pthread_profiler_key) +#endif + +#define EVENT_TYPE sem_t +#define WRITER_EVENT_INIT() (void) sem_init (&(profiler->statistical_data_writer_event), 0, 0) +#define WRITER_EVENT_DESTROY() (void) sem_destroy (&(profiler->statistical_data_writer_event)) +#define WRITER_EVENT_WAIT() (void) sem_wait (&(profiler->statistical_data_writer_event)) +#define WRITER_EVENT_RAISE() (void) sem_post (&(profiler->statistical_data_writer_event)) + +#if 0 +#define FILE_HANDLE_TYPE FILE* +#define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb"); +#define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file) +#define FLUSH_FILE() fflush (profiler->file) +#define CLOSE_FILE() fclose (profiler->file); +#else +#define FILE_HANDLE_TYPE int +#define OPEN_FILE() profiler->file = open (profiler->file_name, O_WRONLY|O_CREAT|O_TRUNC); +#define WRITE_BUFFER(b,s) write (profiler->file, (b), (s)) +#define FLUSH_FILE() +#define CLOSE_FILE() close (profiler->file); +#endif + +#else + +#include + +#define MUTEX_TYPE CRITICAL_SECTION +#define INITIALIZE_PROFILER_MUTEX() InitializeCriticalSection (&(profiler->mutex)) +#define DELETE_PROFILER_MUTEX() DeleteCriticalSection (&(profiler->mutex)) +#define LOCK_PROFILER() EnterCriticalSection (&(profiler->mutex)) +#define UNLOCK_PROFILER() LeaveCriticalSection (&(profiler->mutex)) + +#define THREAD_TYPE HANDLE +#define CREATE_WRITER_THREAD(f) CreateThread (NULL, (1*1024*1024), (f), NULL, 0, NULL); +#define EXIT_THREAD() ExitThread (0); +#define WAIT_WRITER_THREAD() WaitForSingleObject (profiler->data_writer_thread, INFINITE) +#define CURRENT_THREAD_ID() (gsize) GetCurrentThreadId () + +#ifndef HAVE_KW_THREAD +static guint32 profiler_thread_id = -1; +#define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*)TlsGetValue (profiler_thread_id)) +#define SET_PROFILER_THREAD_DATA(x) TlsSetValue (profiler_thread_id, (x)); +#define ALLOCATE_PROFILER_THREAD_DATA() profiler_thread_id = TlsAlloc () +#define FREE_PROFILER_THREAD_DATA() TlsFree (profiler_thread_id) +#endif + +#define EVENT_TYPE HANDLE +#define WRITER_EVENT_INIT() profiler->statistical_data_writer_event = CreateEvent (NULL, FALSE, FALSE, NULL) +#define WRITER_EVENT_DESTROY() CloseHandle (profiler->statistical_data_writer_event) +#define WRITER_EVENT_WAIT() WaitForSingleObject (profiler->statistical_data_writer_event, INFINITE) +#define WRITER_EVENT_RAISE() SetEvent (profiler->statistical_data_writer_event) + +#define FILE_HANDLE_TYPE FILE* +#define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb"); +#define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file) +#define FLUSH_FILE() fflush (profiler->file) +#define CLOSE_FILE() fclose (profiler->file); + +#endif + +#ifdef HAVE_KW_THREAD +static __thread ProfilerPerThreadData * tls_profiler_per_thread_data; +#define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) tls_profiler_per_thread_data) +#define SET_PROFILER_THREAD_DATA(x) tls_profiler_per_thread_data = (x) +#define ALLOCATE_PROFILER_THREAD_DATA() /* nop */ +#define FREE_PROFILER_THREAD_DATA() /* nop */ +#endif + +#define GET_PROFILER_THREAD_DATA(data) do {\ + ProfilerPerThreadData *_result = LOOKUP_PROFILER_THREAD_DATA ();\ + if (!_result) {\ + _result = profiler_per_thread_data_new (profiler->per_thread_buffer_size);\ + LOCK_PROFILER ();\ + _result->next = profiler->per_thread_data;\ + profiler->per_thread_data = _result;\ + UNLOCK_PROFILER ();\ + SET_PROFILER_THREAD_DATA (_result);\ + }\ + (data) = _result;\ +} while (0) + +#define PROFILER_FILE_WRITE_BUFFER_SIZE (profiler->write_buffer_size) +typedef struct _ProfilerFileWriteBuffer { + struct _ProfilerFileWriteBuffer *next; + guint8 buffer []; +} ProfilerFileWriteBuffer; + +struct _MonoProfiler { + MUTEX_TYPE mutex; + + MonoProfileFlags flags; + char *file_name; + FILE_HANDLE_TYPE file; + + guint64 start_time; + guint64 start_counter; + guint64 end_time; + guint64 end_counter; + + MethodIdMapping *methods; + ClassIdMapping *classes; + + GHashTable *loaded_assemblies; + GHashTable *loaded_modules; + GHashTable *loaded_appdomains; + + guint32 per_thread_buffer_size; + guint32 statistical_buffer_size; + ProfilerPerThreadData* per_thread_data; + ProfilerStatisticalData *statistical_data; + ProfilerStatisticalData *statistical_data_ready; + ProfilerStatisticalData *statistical_data_second_buffer; + THREAD_TYPE data_writer_thread; + EVENT_TYPE statistical_data_writer_event; + gboolean terminate_writer_thread; + + ProfilerFileWriteBuffer *write_buffers; + ProfilerFileWriteBuffer *current_write_buffer; + int write_buffer_size; + int current_write_position; + int full_write_buffers; + + ProfilerHeapShotWriteJob *heap_shot_write_jobs; + ProfilerHeapShotHeapBuffers heap; + + ProfilerExecutableMemoryRegions *executable_regions; + + struct { +#if (HAS_OPROFILE) + gboolean oprofile; +#endif + gboolean jit_time; + gboolean unreachable_objects; + gboolean heap_shot; + } action_flags; +}; +static MonoProfiler *profiler; + + + + +#define DEBUG_LOAD_EVENTS 0 +#define DEBUG_MAPPING_EVENTS 1 +#define DEBUG_LOGGING_PROFILER 0 +#define DEBUG_HEAP_PROFILER 1 +#define DEBUG_CLASS_BITMAPS 1 +#define DEBUG_STATISTICAL_PROFILER 0 +#if (DEBUG_LOGGING_PROFILER || DEBUG_STATISTICAL_PROFILER || DEBUG_HEAP_PROFILER || DEBUG_CLASS_BITMAPS) +#define LOG_WRITER_THREAD(m) printf ("WRITER-THREAD-LOG %s\n", m) +#else +#define LOG_WRITER_THREAD(m) +#endif + +#if DEBUG_LOGGING_PROFILER +static int event_counter = 0; +#define EVENT_MARK() printf ("[EVENT:%d]", ++ event_counter) +#endif + + +static ClassIdMappingElement* +class_id_mapping_element_get (MonoClass *klass) { + return g_hash_table_lookup (profiler->classes->table, (gconstpointer) klass); +} + +static MethodIdMappingElement* +method_id_mapping_element_get (MonoMethod *method) { + return g_hash_table_lookup (profiler->methods->table, (gconstpointer) method); +} + +#define BITS_TO_BYTES(v) do {\ + (v) += 7;\ + (v) &= ~7;\ + (v) >>= 3;\ +} while (0) + +static ClassIdMappingElement* +class_id_mapping_element_new (MonoClass *klass) { + ClassIdMappingElement *result = g_new (ClassIdMappingElement, 1); + + result->name = g_strdup_printf ("%s.%s", mono_class_get_namespace (klass), mono_class_get_name (klass)); + result->klass = klass; + result->next_unwritten = profiler->classes->unwritten; + profiler->classes->unwritten = result; + result->id = profiler->classes->next_id; + profiler->classes->next_id ++; + + result->data.bitmap.compact = 0; + result->data.layout.slots = CLASS_LAYOUT_NOT_INITIALIZED; + result->data.layout.references = CLASS_LAYOUT_NOT_INITIALIZED; + + g_hash_table_insert (profiler->classes->table, klass, result); + +#if (DEBUG_MAPPING_EVENTS) + printf ("Created new CLASS mapping element \"%s\" (%p)[%d]\n", result->name, klass, result->id); +#endif + return result; +} + +static void +class_id_mapping_element_build_layout_bitmap (MonoClass *klass, ClassIdMappingElement *klass_id) { + MonoClass *parent_class = mono_class_get_parent (klass); + int number_of_reference_fields = 0; + int max_offset_of_reference_fields = 0; + ClassIdMappingElement *parent_id; + gpointer iter; + MonoClassField *field; + +#if (DEBUG_CLASS_BITMAPS) + printf ("class_id_mapping_element_build_layout_bitmap: building layout for class %s: ", klass_id->name); +#endif + if (parent_class != NULL) { + parent_id = class_id_mapping_element_get (parent_class); + g_assert (parent_id != NULL); + } else { + parent_id = NULL; + } + + iter = NULL; + while ((field = mono_class_get_fields (klass, &iter)) != NULL) { + MonoType* field_type = mono_field_get_type (field); + // For now, skip static fields + if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/) + continue; + + if (MONO_TYPE_IS_REFERENCE (field_type)) { + int field_offset = mono_field_get_offset (field) - sizeof (MonoObject); + if (field_offset > max_offset_of_reference_fields) { + max_offset_of_reference_fields = field_offset; + } + number_of_reference_fields ++; + } else { + MonoClass *field_class = mono_class_from_mono_type (field_type); + if (field_class && mono_class_is_valuetype (field_class)) { + ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class); + g_assert (field_id != NULL); + + if (field_id->data.layout.references > 0) { + int field_offset = mono_field_get_offset (field) - sizeof (MonoObject); + int max_offset_reference_in_field = (field_id->data.layout.slots - 1) * sizeof (gpointer); + + if ((field_offset + max_offset_reference_in_field) > max_offset_of_reference_fields) { + max_offset_of_reference_fields = field_offset + max_offset_reference_in_field; + } + + number_of_reference_fields += field_id->data.layout.references; + } + } + } + } + +#if (DEBUG_CLASS_BITMAPS) + printf ("[allocating bitmap for class %s (references %d, max offset %d, slots %d)]", klass_id->name, number_of_reference_fields, max_offset_of_reference_fields, (int)(max_offset_of_reference_fields / sizeof (gpointer)) + 1); +#endif + if ((number_of_reference_fields == 0) && ((parent_id == NULL) || (parent_id->data.layout.references == 0))) { + klass_id->data.bitmap.compact = 0; + klass_id->data.layout.slots = 0; + klass_id->data.layout.references = 0; +#if (DEBUG_CLASS_BITMAPS) + printf ("[no references at all]"); +#endif + } else { + if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) { + klass_id->data.layout.slots = parent_id->data.layout.slots; + klass_id->data.layout.references = parent_id->data.layout.references; +#if (DEBUG_CLASS_BITMAPS) + printf ("[parent %s has %d references in %d slots]", parent_id->name, parent_id->data.layout.references, parent_id->data.layout.slots); +#endif + } else { + klass_id->data.layout.slots = 0; + klass_id->data.layout.references = 0; +#if (DEBUG_CLASS_BITMAPS) + printf ("[no references from parent]"); +#endif + } + + if (number_of_reference_fields > 0) { + klass_id->data.layout.slots += ((max_offset_of_reference_fields / sizeof (gpointer)) + 1); + klass_id->data.layout.references += number_of_reference_fields; +#if (DEBUG_CLASS_BITMAPS) + printf ("[adding data, going to %d references in %d slots]", klass_id->data.layout.references, klass_id->data.layout.slots); +#endif + } + + if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) { + klass_id->data.bitmap.compact = 0; +#if (DEBUG_CLASS_BITMAPS) + printf ("[zeroing bitmap]"); +#endif + if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) { + klass_id->data.bitmap.compact = parent_id->data.bitmap.compact; +#if (DEBUG_CLASS_BITMAPS) + printf ("[copying compact father bitmap]"); +#endif + } + } else { + int size_of_bitmap = klass_id->data.layout.slots; + BITS_TO_BYTES (size_of_bitmap); + klass_id->data.bitmap.extended = g_malloc0 (size_of_bitmap); +#if (DEBUG_CLASS_BITMAPS) + printf ("[allocating %d bytes for bitmap]", size_of_bitmap); +#endif + if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) { + int size_of_father_bitmap = parent_id->data.layout.slots; + if (size_of_father_bitmap <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) { + int father_slot; + for (father_slot = 0; father_slot < size_of_father_bitmap; father_slot ++) { + if (parent_id->data.bitmap.compact & (((guint64)1) << father_slot)) { + klass_id->data.bitmap.extended [father_slot >> 3] |= (1 << (father_slot & 7)); + } + } +#if (DEBUG_CLASS_BITMAPS) + printf ("[copying %d bits from father bitmap]", size_of_father_bitmap); +#endif + } else { + BITS_TO_BYTES (size_of_father_bitmap); + memcpy (klass_id->data.bitmap.extended, parent_id->data.bitmap.extended, size_of_father_bitmap); +#if (DEBUG_CLASS_BITMAPS) + printf ("[copying %d bytes from father bitmap]", size_of_father_bitmap); +#endif + } + } + } + } + +#if (DEBUG_CLASS_BITMAPS) + printf ("[starting filling iteration]\n"); +#endif + iter = NULL; + while ((field = mono_class_get_fields (klass, &iter)) != NULL) { + MonoType* field_type = mono_field_get_type (field); + // For now, skip static fields + if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/) + continue; + +#if (DEBUG_CLASS_BITMAPS) + printf ("[Working on field %s]", mono_field_get_name (field)); +#endif + if (MONO_TYPE_IS_REFERENCE (field_type)) { + int field_offset = mono_field_get_offset (field) - sizeof (MonoObject); + int field_slot; + g_assert ((field_offset % sizeof (gpointer)) == 0); + field_slot = field_offset / sizeof (gpointer); + if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) { + klass_id->data.bitmap.compact |= (((guint64)1) << field_slot); + } else { + klass_id->data.bitmap.extended [field_slot >> 3] |= (1 << (field_slot & 7)); + } +#if (DEBUG_CLASS_BITMAPS) + printf ("[reference at offset %d, slot %d]", field_offset, field_slot); +#endif + } else { + MonoClass *field_class = mono_class_from_mono_type (field_type); + if (field_class && mono_class_is_valuetype (field_class)) { + ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class); + int field_offset; + int field_slot; + + g_assert (field_id != NULL); + field_offset = mono_field_get_offset (field) - sizeof (MonoObject); + g_assert ((field_id->data.layout.references == 0) || ((field_offset % sizeof (gpointer)) == 0)); + field_slot = field_offset / sizeof (gpointer); +#if (DEBUG_CLASS_BITMAPS) + printf ("[value type at offset %d, slot %d, with %d references in %d slots]", field_offset, field_slot, field_id->data.layout.references, field_id->data.layout.slots); +#endif + + if (field_id->data.layout.references > 0) { + int sub_field_slot; + if (field_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) { + for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) { + if (field_id->data.bitmap.compact & (((guint64)1) << sub_field_slot)) { + int actual_slot = field_slot + sub_field_slot; + if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) { + klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot); + } else { + klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7)); + } + } + } + } else { + for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) { + if (field_id->data.bitmap.extended [sub_field_slot >> 3] & (1 << (sub_field_slot & 7))) { + int actual_slot = field_slot + sub_field_slot; + if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) { + klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot); + } else { + klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7)); + } + } + } + } + } + } + } + } +#if (DEBUG_CLASS_BITMAPS) + do { + int slot; + printf ("Layot of class \"%s\": references %d, slots %d, bitmap {", klass_id->name, klass_id->data.layout.references, klass_id->data.layout.slots); + for (slot = 0; slot < klass_id->data.layout.slots; slot ++) { + if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) { + if (klass_id->data.bitmap.compact & (((guint64)1) << slot)) { + printf (" 1"); + } else { + printf (" 0"); + } + } else { + if (klass_id->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) { + printf (" 1"); + } else { + printf (" 0"); + } +; } + + } + printf (" }\n"); + + } while (0); +#endif +} + +static MethodIdMappingElement* +method_id_mapping_element_new (MonoMethod *method) { + MethodIdMappingElement *result = g_new (MethodIdMappingElement, 1); + char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE); + + result->name = g_strdup_printf ("%s (%s)", mono_method_get_name (method), signature); + g_free (signature); + result->method = method; + result->next_unwritten = profiler->methods->unwritten; + profiler->methods->unwritten = result; + result->id = profiler->methods->next_id; + profiler->methods->next_id ++; + g_hash_table_insert (profiler->methods->table, method, result); + + result->data.code_start = NULL; + result->data.code_size = 0; + +#if (DEBUG_MAPPING_EVENTS) + printf ("Created new METHOD mapping element \"%s\" (%p)[%d]\n", result->name, method, result->id); +#endif + return result; +} + + +static void +method_id_mapping_element_destroy (gpointer element) { + MethodIdMappingElement *e = (MethodIdMappingElement*) element; + if (e->name) + g_free (e->name); + g_free (element); +} + +static void +class_id_mapping_element_destroy (gpointer element) { + ClassIdMappingElement *e = (ClassIdMappingElement*) element; + if (e->name) + g_free (e->name); + if ((e->data.layout.slots != CLASS_LAYOUT_NOT_INITIALIZED) && (e->data.layout.slots > CLASS_LAYOUT_PACKED_BITMAP_SIZE)) + g_free (e->data.bitmap.extended); + g_free (element); +} + +static MethodIdMapping* +method_id_mapping_new (void) { + MethodIdMapping *result = g_new (MethodIdMapping, 1); + //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, method_id_mapping_element_destroy); + result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, method_id_mapping_element_destroy); + result->unwritten = NULL; + result->next_id = 1; + return result; +} + +static ClassIdMapping* +class_id_mapping_new (void) { + ClassIdMapping *result = g_new (ClassIdMapping, 1); + //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, class_id_mapping_element_destroy); + result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, class_id_mapping_element_destroy); + result->unwritten = NULL; + result->next_id = 1; + return result; +} + +static void +method_id_mapping_destroy (MethodIdMapping *map) { + g_hash_table_destroy (map->table); + g_free (map); +} + +static void +class_id_mapping_destroy (ClassIdMapping *map) { + g_hash_table_destroy (map->table); + g_free (map); +} + +#if (DEBUG_LOAD_EVENTS) +static void +print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element); +#endif + +static LoadedElement* +loaded_element_load_start (GHashTable *table, gpointer item) { + LoadedElement *element = g_new0 (LoadedElement, 1); +#if (DEBUG_LOAD_EVENTS) + print_load_event ("LOAD START", table, item, element); +#endif + MONO_PROFILER_GET_CURRENT_COUNTER (element->load_start_counter); + g_hash_table_insert (table, item, element); + return element; +} + +static LoadedElement* +loaded_element_load_end (GHashTable *table, gpointer item, char *name) { + LoadedElement *element = g_hash_table_lookup (table, item); +#if (DEBUG_LOAD_EVENTS) + print_load_event ("LOAD END", table, item, element); +#endif + g_assert (element != NULL); + MONO_PROFILER_GET_CURRENT_COUNTER (element->load_end_counter); + element->name = name; + element->loaded = TRUE; + return element; +} + +static LoadedElement* +loaded_element_unload_start (GHashTable *table, gpointer item) { + LoadedElement *element = g_hash_table_lookup (table, item); +#if (DEBUG_LOAD_EVENTS) + print_load_event ("UNLOAD START", table, item, element); +#endif + g_assert (element != NULL); + MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_start_counter); + return element; +} + +static LoadedElement* +loaded_element_unload_end (GHashTable *table, gpointer item) { + LoadedElement *element = g_hash_table_lookup (table, item); +#if (DEBUG_LOAD_EVENTS) + print_load_event ("UNLOAD END", table, item, element); +#endif + g_assert (element != NULL); + MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_end_counter); + element->unloaded = TRUE; + return element; +} + + +static void +loaded_element_destroy (gpointer element) { + if (((LoadedElement*)element)->name) + g_free (((LoadedElement*)element)->name); + g_free (element); +} + +#if (DEBUG_LOAD_EVENTS) +static void +print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element) { + const char* item_name; + char* item_info; + + if (table == profiler->loaded_assemblies) { + //item_info = g_strdup_printf("ASSEMBLY %p (dynamic %d)", item, mono_image_is_dynamic (mono_assembly_get_image((MonoAssembly*)item))); + item_info = g_strdup_printf("ASSEMBLY %p", item); + } else if (table == profiler->loaded_modules) { + //item_info = g_strdup_printf("MODULE %p (dynamic %d)", item, mono_image_is_dynamic ((MonoImage*)item)); + item_info = g_strdup_printf("MODULE %p", item); + } else if (table == profiler->loaded_appdomains) { + item_info = g_strdup_printf("APPDOMAIN %p (id %d)", item, mono_domain_get_id ((MonoDomain*)item)); + } else { + item_info = NULL; + g_assert_not_reached (); + } + + if (element != NULL) { + item_name = element->name; + } else { + item_name = ""; + } + + printf ("%s EVENT for %s (%s)\n", event_name, item_info, item_name); + g_free (item_info); +} +#endif + +static void +profiler_heap_shot_object_buffers_destroy (ProfilerHeapShotObjectBuffer *buffer) { + while (buffer != NULL) { + ProfilerHeapShotObjectBuffer *next = buffer->next; +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_shot_object_buffers_destroy: destroyed buffer %p (%p-%p)\n", buffer, & (buffer->buffer [0]), buffer->end); +#endif + g_free (buffer); + buffer = next; + } +} + +static ProfilerHeapShotObjectBuffer* +profiler_heap_shot_object_buffer_new (ProfilerPerThreadData *data) { + ProfilerHeapShotObjectBuffer *buffer; + ProfilerHeapShotObjectBuffer *result = g_new (ProfilerHeapShotObjectBuffer, 1); + result->next_free_slot = & (result->buffer [0]); + result->end = & (result->buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE]); + result->first_unprocessed_slot = & (result->buffer [0]); + result->next = data->heap_shot_object_buffers; + data->heap_shot_object_buffers = result; +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_shot_object_buffer_new: created buffer %p (%p-%p)\n", result, result->next_free_slot, result->end); +#endif + for (buffer = result; buffer != NULL; buffer = buffer->next) { + ProfilerHeapShotObjectBuffer *last = buffer->next; + if ((last != NULL) && (last->first_unprocessed_slot == last->end)) { + buffer->next = NULL; + profiler_heap_shot_object_buffers_destroy (last); + } + } + + return result; +} + +static ProfilerHeapShotWriteJob* +profiler_heap_shot_write_job_new (void) { + ProfilerHeapShotWriteJob *job = g_new (ProfilerHeapShotWriteJob, 1); + job->next = NULL; + job->next_unwritten = NULL; + job->buffers = g_new (ProfilerHeapShotWriteBuffer, 1); + job->buffers->next = NULL; + job->last_next = & (job->buffers->next); + job->start = & (job->buffers->buffer [0]); + job->cursor = job->start; + job->end = & (job->buffers->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]); + job->full_buffers = 0; +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_shot_write_job_new: created job %p with buffer %p(%p-%p)\n", job, job->buffers, job->start, job->end); +#endif + return job; +} + +static void +profiler_heap_shot_write_job_add_buffer (ProfilerHeapShotWriteJob *job, gpointer value) { + ProfilerHeapShotWriteBuffer *buffer = g_new (ProfilerHeapShotWriteBuffer, 1); + buffer->next = NULL; + *(job->last_next) = buffer; + job->last_next = & (buffer->next); + job->full_buffers ++; + buffer->buffer [0] = value; + job->start = & (buffer->buffer [0]); + job->cursor = & (buffer->buffer [1]); + job->end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]); +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_shot_write_job_add_buffer: in job %p, added buffer %p(%p-%p) with value %p at address %p (cursor now %p)\n", job, buffer, job->start, job->end, value, &(buffer->buffer [0]), job->cursor); + do { + ProfilerHeapShotWriteBuffer *current_buffer; + for (current_buffer = job->buffers; current_buffer != NULL; current_buffer = current_buffer->next) { + printf ("profiler_heap_shot_write_job_add_buffer: now job %p has buffer %p\n", job, current_buffer); + } + } while (0); +#endif +} + +static void +profiler_heap_shot_write_job_free_buffers (ProfilerHeapShotWriteJob *job) { + ProfilerHeapShotWriteBuffer *buffer = job->buffers; + + while (buffer != NULL) { + ProfilerHeapShotWriteBuffer *next = buffer->next; +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_shot_write_job_free_buffers: in job %p, freeing buffer %p\n", job, buffer); +#endif + g_free (buffer); + buffer = next; + } + + job->buffers = NULL; +} + +static void +profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job); + +static void +profiler_process_heap_shot_write_jobs (void) { + gboolean done = FALSE; + + while (!done) { + ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs; + ProfilerHeapShotWriteJob *previous_job = NULL; + ProfilerHeapShotWriteJob *next_job; + + done = TRUE; + while (current_job != NULL) { + next_job = current_job->next_unwritten; + + if (next_job != NULL) { + if (current_job->buffers != NULL) { + done = FALSE; + } + if (next_job->buffers == NULL) { + current_job->next_unwritten = NULL; + next_job = NULL; + } + } else { + if (current_job->buffers != NULL) { + LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: writing..."); + profiler_heap_shot_write_block (current_job); + LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: done"); + if (previous_job != NULL) { + previous_job->next_unwritten = NULL; + } + } + } + + previous_job = current_job; + current_job = next_job; + } + } +} + +static void +profiler_free_heap_shot_write_jobs (void) { + ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs; + ProfilerHeapShotWriteJob *next_job; + + if (current_job != NULL) { + while (current_job->next_unwritten != NULL) { +#if DEBUG_HEAP_PROFILER + printf ("profiler_free_heap_shot_write_jobs: job %p must not be freed\n", current_job); +#endif + current_job = current_job->next_unwritten; + } + + next_job = current_job->next; + current_job->next = NULL; + current_job = next_job; + + while (current_job != NULL) { +#if DEBUG_HEAP_PROFILER + printf ("profiler_free_heap_shot_write_jobs: job %p will be freed\n", current_job); +#endif + next_job = current_job->next; + g_free (current_job); + current_job = next_job; + } + } +} + +static void +profiler_destroy_heap_shot_write_jobs (void) { + ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs; + ProfilerHeapShotWriteJob *next_job; + + while (current_job != NULL) { + next_job = current_job->next; + profiler_heap_shot_write_job_free_buffers (current_job); + g_free (current_job); + current_job = next_job; + } +} + +static void +profiler_add_heap_shot_write_job (ProfilerHeapShotWriteJob *job) { + job->next = profiler->heap_shot_write_jobs; + job->next_unwritten = job->next; + profiler->heap_shot_write_jobs = job; +#if DEBUG_HEAP_PROFILER + printf ("profiler_add_heap_shot_write_job: added job %p\n", job); +#endif +} + +#if DEBUG_HEAP_PROFILER +#define STORE_ALLOCATED_OBJECT_MESSAGE1(d,o) printf ("STORE_ALLOCATED_OBJECT[TID %ld]: storing object %p at address %p\n", (d)->thread_id, (o), (d)->heap_shot_object_buffers->next_free_slot) +#define STORE_ALLOCATED_OBJECT_MESSAGE2(d,o) printf ("STORE_ALLOCATED_OBJECT[TID %ld]: storing object %p at address %p in new buffer %p\n", (d)->thread_id, (o), buffer->next_free_slot, buffer) +#else +#define STORE_ALLOCATED_OBJECT_MESSAGE1(d,o) +#define STORE_ALLOCATED_OBJECT_MESSAGE2(d,o) +#endif +#define STORE_ALLOCATED_OBJECT(d,o) do {\ + if ((d)->heap_shot_object_buffers->next_free_slot < (d)->heap_shot_object_buffers->end) {\ + STORE_ALLOCATED_OBJECT_MESSAGE1 ((d), (o));\ + *((d)->heap_shot_object_buffers->next_free_slot) = (o);\ + (d)->heap_shot_object_buffers->next_free_slot ++;\ + } else {\ + ProfilerHeapShotObjectBuffer *buffer = profiler_heap_shot_object_buffer_new (d);\ + STORE_ALLOCATED_OBJECT_MESSAGE2 ((d), (o));\ + *((buffer)->next_free_slot) = (o);\ + (buffer)->next_free_slot ++;\ + }\ +} while (0) + +static ProfilerPerThreadData* +profiler_per_thread_data_new (guint32 buffer_size) +{ + ProfilerPerThreadData *data = g_new (ProfilerPerThreadData, 1); + + data->events = g_new0 (ProfilerEventData, buffer_size); + data->next_free_event = data->events; + data->end_event = data->events + (buffer_size - 1); + data->first_unwritten_event = data->events; + data->first_unmapped_event = data->events; + MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter); + data->last_event_counter = data->start_event_counter; + data->thread_id = CURRENT_THREAD_ID (); + data->heap_shot_object_buffers = NULL; + if ((profiler->action_flags.unreachable_objects == TRUE) || (profiler->action_flags.heap_shot == TRUE)) { + profiler_heap_shot_object_buffer_new (data); + } + return data; +} + +static void +profiler_per_thread_data_destroy (ProfilerPerThreadData *data) { + g_free (data->events); + profiler_heap_shot_object_buffers_destroy (data->heap_shot_object_buffers); + g_free (data); +} + +static ProfilerStatisticalData* +profiler_statistical_data_new (guint32 buffer_size) +{ + ProfilerStatisticalData *data = g_new (ProfilerStatisticalData, 1); + + data->addresses = g_new0 (gpointer, buffer_size); + data->next_free_index = 0; + data->end_index = buffer_size; + data->first_unwritten_index = 0; + + return data; +} + +static void +profiler_statistical_data_destroy (ProfilerStatisticalData *data) { + g_free (data->addresses); + g_free (data); +} + +static void +profiler_add_write_buffer (void) { + if (profiler->current_write_buffer->next == NULL) { + profiler->current_write_buffer->next = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE); + profiler->current_write_buffer->next->next = NULL; + + //printf ("Added next buffer %p, to buffer %p\n", profiler->current_write_buffer->next, profiler->current_write_buffer); + + } + profiler->current_write_buffer = profiler->current_write_buffer->next; + profiler->current_write_position = 0; + profiler->full_write_buffers ++; +} + +static void +profiler_free_write_buffers (void) { + ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers; + while (current_buffer != NULL) { + ProfilerFileWriteBuffer *next_buffer = current_buffer->next; + + //printf ("Freeing write buffer %p, next is %p\n", current_buffer, next_buffer); + + g_free (current_buffer); + current_buffer = next_buffer; + } +} + +#define WRITE_BYTE(b) do {\ + if (profiler->current_write_position >= PROFILER_FILE_WRITE_BUFFER_SIZE) {\ + profiler_add_write_buffer ();\ + }\ + profiler->current_write_buffer->buffer [profiler->current_write_position] = (b);\ + profiler->current_write_position ++;\ +} while (0) + + +static void +write_current_block (guint16 code) { + guint32 size = (profiler->full_write_buffers * PROFILER_FILE_WRITE_BUFFER_SIZE) + profiler->current_write_position; + ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers; + guint8 header [6]; + + header [0] = code & 0xff; + header [1] = (code >> 8) & 0xff; + header [2] = size & 0xff; + header [3] = (size >> 8) & 0xff; + header [4] = (size >> 16) & 0xff; + header [5] = (size >> 24) & 0xff; + + WRITE_BUFFER (& (header [0]), 6); + + while ((current_buffer != NULL) && (profiler->full_write_buffers > 0)) { + WRITE_BUFFER (& (current_buffer->buffer [0]), PROFILER_FILE_WRITE_BUFFER_SIZE); + profiler->full_write_buffers --; + current_buffer = current_buffer->next; + } + if (profiler->current_write_position > 0) { + WRITE_BUFFER (& (current_buffer->buffer [0]), profiler->current_write_position); + } + FLUSH_FILE (); + + profiler->current_write_buffer = profiler->write_buffers; + profiler->current_write_position = 0; + profiler->full_write_buffers = 0; +} + + +#define SEVEN_BITS_MASK (0x7f) +#define EIGHT_BIT_MASK (0x80) + +static void +write_uint32 (guint32 value) { + while (value > SEVEN_BITS_MASK) { + WRITE_BYTE (value & SEVEN_BITS_MASK); + value >>= 7; + } + WRITE_BYTE (value | EIGHT_BIT_MASK); +} +static void +write_uint64 (guint64 value) { + while (value > SEVEN_BITS_MASK) { + WRITE_BYTE (value & SEVEN_BITS_MASK); + value >>= 7; + } + WRITE_BYTE (value | EIGHT_BIT_MASK); +} +static void +write_string (const char *string) { + while (*string != 0) { + WRITE_BYTE (*string); + string ++; + } + WRITE_BYTE (0); +} + +#define WRITE_HEAP_SHOT_JOB_VALUE(j,v) do {\ + if ((j)->cursor < (j)->end) {\ + *((j)->cursor) = (v);\ + (j)->cursor ++;\ + } else {\ + profiler_heap_shot_write_job_add_buffer (j, v);\ + }\ +} while (0) +#define WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE(j,v,c) WRITE_HEAP_SHOT_JOB_VALUE (j, GUINT_TO_POINTER (GPOINTER_TO_UINT (v)|(c))) + +#if DEBUG_HEAP_PROFILER +#define UPDATE_JOB_BUFFER_CURSOR_MESSAGE() printf ("profiler_heap_shot_write_block[UPDATE_JOB_BUFFER_CURSOR]: in job %p, moving to buffer %p and cursor %p\n", job, buffer, cursor) +#else +#define UPDATE_JOB_BUFFER_CURSOR_MESSAGE() +#endif +#define UPDATE_JOB_BUFFER_CURSOR() do {\ + cursor++;\ + if (cursor >= end) {\ + buffer = buffer->next;\ + if (buffer != NULL) {\ + cursor = & (buffer->buffer [0]);\ + if (buffer->next != NULL) {\ + end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);\ + } else {\ + end = job->cursor;\ + }\ + } else {\ + cursor = NULL;\ + }\ + }\ + UPDATE_JOB_BUFFER_CURSOR_MESSAGE ();\ +} while (0) + +static void +profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job) { + ProfilerHeapShotWriteBuffer *buffer; + gpointer* cursor; + gpointer* end; + guint64 end_counter; + guint64 end_time; + + write_uint64 (job->start_counter); + write_uint64 (job->start_time); + write_uint64 (job->end_counter); + write_uint64 (job->end_time); +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_shot_write_block: working on job %p...\n", job); +#endif + buffer = job->buffers; + cursor = & (buffer->buffer [0]); + if (buffer->next != NULL) { + end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]); + } else { + end = job->cursor; + } + if (cursor >= end) { + cursor = NULL; + } +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_shot_write_block: in job %p, starting at buffer %p and cursor %p\n", job, buffer, cursor); +#endif + while (cursor != NULL) { + gpointer value = *cursor; + HeapProfilerJobValueCode code = GPOINTER_TO_UINT (value) & HEAP_CODE_MASK; + + UPDATE_JOB_BUFFER_CURSOR (); + if (code == HEAP_CODE_FREE_OBJECT_CLASS) { + MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK)); + //MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) % 4); + ClassIdMappingElement *class_id; + guint32 size; + + class_id = class_id_mapping_element_get (klass); + if (class_id == NULL) { + printf ("profiler_heap_shot_write_block: unknown class %p", klass); + } + g_assert (class_id != NULL); + write_uint32 ((class_id->id << 2) | HEAP_CODE_FREE_OBJECT_CLASS); + + size = GPOINTER_TO_UINT (*cursor); + UPDATE_JOB_BUFFER_CURSOR (); + write_uint32 (size); +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_shot_write_block: wrote unreachable object of class %p (id %d, size %d)\n", klass, class_id->id, size); +#endif + } else if (code == HEAP_CODE_OBJECT) { + guint32 references = GPOINTER_TO_UINT (*cursor); + UPDATE_JOB_BUFFER_CURSOR (); + + write_uint64 (GPOINTER_TO_UINT (value)); + write_uint32 (references); +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_shot_write_block: writing object %p (references %d)\n", value, references); +#endif + + while (references > 0) { + gpointer reference = *cursor; + write_uint64 (GPOINTER_TO_UINT (reference)); + UPDATE_JOB_BUFFER_CURSOR (); + references --; +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_shot_write_block: inside object %p, wrote reference %p)\n", value, reference); +#endif + } + } else { + g_assert_not_reached (); + } + } + write_uint32 (0); + + MONO_PROFILER_GET_CURRENT_COUNTER (end_counter); + MONO_PROFILER_GET_CURRENT_TIME (end_time); + write_uint64 (end_counter); + write_uint64 (end_time); + + write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_HEAP); + + profiler_heap_shot_write_job_free_buffers (job); +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_shot_write_block: work on job %p done.\n", job); +#endif +} + +static void +write_element_load_block (LoadedElement *element, guint8 kind, gsize thread_id) { + WRITE_BYTE (kind); + write_uint64 (element->load_start_counter); + write_uint64 (element->load_end_counter); + write_uint64 (thread_id); + write_string (element->name); + write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_LOADED); + element->load_written = TRUE; +} + +static void +write_element_unload_block (LoadedElement *element, guint8 kind, gsize thread_id) { + WRITE_BYTE (kind); + write_uint64 (element->unload_start_counter); + write_uint64 (element->unload_end_counter); + write_uint64 (thread_id); + write_string (element->name); + write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED); + element->unload_written = TRUE; +} + +static void +write_clock_data (void) { + guint64 counter; + guint64 time; + + MONO_PROFILER_GET_CURRENT_COUNTER (counter); + MONO_PROFILER_GET_CURRENT_TIME (time); + + write_uint64 (counter); + write_uint64 (time); +} + +static void +write_mapping_block (gsize thread_id, gboolean flushObjects) { + ClassIdMappingElement *current_class; + MethodIdMappingElement *current_method; + + if ((profiler->classes->unwritten == NULL) && (profiler->methods->unwritten == NULL)) + return; + +#if (DEBUG_MAPPING_EVENTS) + printf ("[write_mapping_block][TID %ld] START\n", thread_id); +#endif + + write_clock_data (); + write_uint64 (thread_id); + + for (current_class = profiler->classes->unwritten; current_class != NULL; current_class = current_class->next_unwritten) { + write_uint32 (current_class->id); + write_string (current_class->name); +#if (DEBUG_MAPPING_EVENTS) + printf ("mapping CLASS (%d => %s)\n", current_class->id, current_class->name); +#endif + g_free (current_class->name); + current_class->name = NULL; + } + write_uint32 (0); + profiler->classes->unwritten = NULL; + + for (current_method = profiler->methods->unwritten; current_method != NULL; current_method = current_method->next_unwritten) { + MonoMethod *method = current_method->method; + MonoClass *klass = mono_method_get_class (method); + ClassIdMappingElement *class_element = class_id_mapping_element_get (klass); + g_assert (class_element != NULL); + write_uint32 (current_method->id); + write_uint32 (class_element->id); + write_string (current_method->name); +#if (DEBUG_MAPPING_EVENTS) + printf ("mapping METHOD ([%d]%d => %s)\n", class_element?class_element->id:1, current_method->id, current_method->name); +#endif + g_free (current_method->name); + current_method->name = NULL; + } + write_uint32 (0); + profiler->methods->unwritten = NULL; + + write_clock_data (); + write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_MAPPING); + +#if (DEBUG_MAPPING_EVENTS) + printf ("[write_mapping_block][TID %ld] END\n", thread_id); +#endif +} + +static guint64 +get_extended_event_value (ProfilerEventData *event, ProfilerEventData *next) { + guint64 result = next->data.number; + result |= (((guint64) event->value) << 32); + return result; +} + +typedef enum { + MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER = 1, + MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_IMPLICIT = 2, + MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT = 3, + MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION = 4, + MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT = 5, + MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT = 6, + MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT = 7 +} MonoProfilerPackedEventCode; +#define MONO_PROFILER_PACKED_EVENT_CODE_BITS 3 +#define MONO_PROFILER_PACKED_EVENT_DATA_BITS (8-MONO_PROFILER_PACKED_EVENT_CODE_BITS) +#define MONO_PROFILER_PACKED_EVENT_DATA_MASK ((1<>= MONO_PROFILER_PACKED_EVENT_DATA_BITS;\ +} while (0) +#define MONO_PROFILER_EVENT_MAKE_FULL_CODE(result,code,kind,base) do {\ + result = ((base)|((((kind)<<4) | (code)) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\ +} while (0) + +static ProfilerEventData* +write_event (ProfilerEventData *event) { + ProfilerEventData *next = event + 1; + gboolean write_event_value = TRUE; + guint8 event_code; + guint64 event_data; + guint64 event_value; + + event_value = event->value; + if (event_value > MAX_EVENT_VALUE) { + event_value = get_extended_event_value (event, next); + next ++; + } + + if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) { + MethodIdMappingElement *element = method_id_mapping_element_get (event->data.address); + g_assert (element != NULL); + event_data = element->id; + + if (event->code == MONO_PROFILER_EVENT_METHOD_CALL) { + if (event->kind == MONO_PROFILER_EVENT_KIND_START) { + MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER); + } else { + MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT); + } + } else { + MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT); + } + } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) { + ClassIdMappingElement *element = class_id_mapping_element_get (event->data.address); + g_assert (element != NULL); + event_data = element->id; + + if (event->code == MONO_PROFILER_EVENT_CLASS_ALLOCATION) { + MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION); + } else { + MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT); + } + } else { + event_data = event->data.number; + MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT); + } + +#if (DEBUG_LOGGING_PROFILER) + EVENT_MARK (); + printf ("writing EVENT[%p] data_type:%d, kind:%d, code:%d (%d:%ld:%ld)\n", event, + event->data_type, event->kind, event->code, + event_code, event_data, event_value); +#endif + + WRITE_BYTE (event_code); + write_uint64 (event_data); + if (write_event_value) { + write_uint64 (event_value); + } + + return next; +} + +static void +write_thread_data_block (ProfilerPerThreadData *data) { + ProfilerEventData *start = data->first_unwritten_event; + ProfilerEventData *end = data->first_unmapped_event; + + if (start == end) + return; + + write_clock_data (); + write_uint64 (data->thread_id); + + write_uint64 (data->start_event_counter); + + while (start < end) { + start = write_event (start); + } + WRITE_BYTE (0); + data->first_unwritten_event = end; + + write_clock_data (); + write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_EVENTS); +} + +static ProfilerExecutableMemoryRegionData* +profiler_executable_memory_region_new (gpointer *start, gpointer *end, guint32 file_offset, char *file_name, guint32 id) { + ProfilerExecutableMemoryRegionData *result = g_new (ProfilerExecutableMemoryRegionData, 1); + result->start = start; + result->end = end; + result->file_offset = file_offset; + result->file_name = g_strdup (file_name); + result->id = id; + result->is_new = TRUE; + return result; +} + +static void +profiler_executable_memory_region_destroy (ProfilerExecutableMemoryRegionData *data) { + if (data->file_name != NULL) { + g_free (data->file_name); + } + g_free (data); +} + +static ProfilerExecutableMemoryRegions* +profiler_executable_memory_regions_new (void) { + ProfilerExecutableMemoryRegions *result = g_new (ProfilerExecutableMemoryRegions, 1); + result->regions = g_new0 (ProfilerExecutableMemoryRegionData*, 32); + result->regions_capacity = 32; + result->regions_count = 0; + result->next_id = 1; + return result; +} + +static void +profiler_executable_memory_regions_destroy (ProfilerExecutableMemoryRegions *regions) { + int i; + + for (i = 0; i < regions->regions_count; i++) { + profiler_executable_memory_region_destroy (regions->regions [i]); + } + g_free (regions->regions); + g_free (regions); +} + +static ProfilerExecutableMemoryRegionData* +find_address_region (ProfilerExecutableMemoryRegions *regions, gpointer address) { + int low_index = 0; + int high_index = regions->regions_count; + int middle_index = 0; + ProfilerExecutableMemoryRegionData *middle_region = regions->regions [0]; + + if ((regions->regions_count == 0) || (regions->regions [low_index]->start > address) || (regions->regions [high_index - 1]->end < address)) { + return NULL; + } + + //printf ("find_address_region: Looking for address %p in %d regions (from %p to %p)\n", address, regions->regions_count, regions->regions [low_index]->start, regions->regions [high_index - 1]->end); + + while (low_index != high_index) { + middle_index = low_index + ((high_index - low_index) / 2); + middle_region = regions->regions [middle_index]; + + //printf ("find_address_region: Looking for address %p, considering index %d[%p-%p] (%d-%d)\n", address, middle_index, middle_region->start, middle_region->end, low_index, high_index); + + if (middle_region->start > address) { + if (middle_index > 0) { + high_index = middle_index; + } else { + return NULL; + } + } else if (middle_region->end < address) { + if (middle_index < regions->regions_count - 1) { + low_index = middle_index + 1; + } else { + return NULL; + } + } else { + return middle_region; + } + } + + if ((middle_region == NULL) || (middle_region->start > address) || (middle_region->end < address)) { + return NULL; + } else { + return middle_region; + } +} + +static void +append_region (ProfilerExecutableMemoryRegions *regions, gpointer *start, gpointer *end, guint32 file_offset, char *file_name) { + if (regions->regions_count >= regions->regions_capacity) { + ProfilerExecutableMemoryRegionData **new_regions = g_new0 (ProfilerExecutableMemoryRegionData*, regions->regions_capacity * 2); + memcpy (new_regions, regions->regions, regions->regions_capacity * sizeof (ProfilerExecutableMemoryRegionData*)); + g_free (regions->regions); + regions->regions = new_regions; + regions->regions_capacity = regions->regions_capacity * 2; + } + regions->regions [regions->regions_count] = profiler_executable_memory_region_new (start, end, file_offset, file_name, regions->next_id); + regions->regions_count ++; + regions->next_id ++; +} + +static void +restore_region_ids (ProfilerExecutableMemoryRegions *old_regions, ProfilerExecutableMemoryRegions *new_regions) { + int old_i; + int new_i; + + for (old_i = 0; old_i < old_regions->regions_count; old_i++) { + ProfilerExecutableMemoryRegionData *old_region = old_regions->regions [old_i]; + for (new_i = 0; new_i < new_regions->regions_count; new_i++) { + ProfilerExecutableMemoryRegionData *new_region = new_regions->regions [new_i]; + if ((old_region->start == new_region->start) && + (old_region->end == new_region->end) && + (old_region->file_offset == new_region->file_offset) && + ! strcmp (old_region->file_name, new_region->file_name)) { + new_region->is_new = FALSE; + new_region->id = old_region->id; + if (new_region->id >= new_regions->next_id) { + new_regions->next_id = new_region->id + 1; + } + old_region->is_new = TRUE; + } + } + } +} + +static int +compare_regions (const void *a1, const void *a2) { + ProfilerExecutableMemoryRegionData *r1 = * (ProfilerExecutableMemoryRegionData**) a1; + ProfilerExecutableMemoryRegionData *r2 = * (ProfilerExecutableMemoryRegionData**) a2; + return (r1->start < r2->start)? -1 : ((r1->start > r2->start)? 1 : 0); +} + +static void +sort_regions (ProfilerExecutableMemoryRegions *regions) { + qsort (regions->regions, regions->regions_count, sizeof (ProfilerExecutableMemoryRegionData *), compare_regions); +} + +//FIXME: make also Win32 and BSD variants +#define MAPS_BUFFER_SIZE 4096 + +static gboolean +update_regions_buffer (int fd, char *buffer) { + ssize_t result = read (fd, buffer, MAPS_BUFFER_SIZE); + + if (result == MAPS_BUFFER_SIZE) { + return TRUE; + } else if (result >= 0) { + *(buffer + result) = 0; + return FALSE; + } else { + *buffer = 0; + return FALSE; + } +} + +#define GOTO_NEXT_CHAR(c,b,fd) do {\ + (c)++;\ + if (((c) - (b) >= MAPS_BUFFER_SIZE) || ((*(c) == 0) && ((c) != (b)))) {\ + update_regions_buffer ((fd), (b));\ + (c) = (b);\ + }\ +} while (0); + +static int hex_digit_value (char c) { + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } else if ((c >= 'a') && (c <= 'f')) { + return c - 'a'; + } else if ((c >= 'A') && (c <= 'F')) { + return c - 'A'; + } else { + return 0; + } +} + +/* + * Start address + * - + * End address + * (space) + * Permissions + * Offset + * (space) + * Device + * (space) + * Inode + * (space) + * File + * \n + */ +typedef enum { + MAP_LINE_PARSER_STATE_INVALID, + MAP_LINE_PARSER_STATE_START_ADDRESS, + MAP_LINE_PARSER_STATE_END_ADDRESS, + MAP_LINE_PARSER_STATE_PERMISSIONS, + MAP_LINE_PARSER_STATE_OFFSET, + MAP_LINE_PARSER_STATE_DEVICE, + MAP_LINE_PARSER_STATE_INODE, + MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME, + MAP_LINE_PARSER_STATE_FILENAME, + MAP_LINE_PARSER_STATE_DONE +} MapLineParserState; + +const char *map_line_parser_state [] = { + "INVALID", + "START_ADDRESS", + "END_ADDRESS", + "PERMISSIONS", + "OFFSET", + "DEVICE", + "INODE", + "BLANK_BEFORE_FILENAME", + "FILENAME", + "DONE" +}; + +static char* +parse_map_line (ProfilerExecutableMemoryRegions *regions, int fd, char *buffer, char *current) { + MapLineParserState state = MAP_LINE_PARSER_STATE_START_ADDRESS; + gsize start_address = 0; + gsize end_address = 0; + guint32 offset = 0; + char *start_filename = NULL; + char *end_filename = NULL; + gboolean is_executable = FALSE; + gboolean done = FALSE; + + char c = *current; + + while (1) { + switch (state) { + case MAP_LINE_PARSER_STATE_START_ADDRESS: + if (isxdigit (c)) { + start_address <<= 4; + start_address |= hex_digit_value (c); + } else if (c == '-') { + state = MAP_LINE_PARSER_STATE_END_ADDRESS; + } else { + state = MAP_LINE_PARSER_STATE_INVALID; + } + break; + case MAP_LINE_PARSER_STATE_END_ADDRESS: + if (isxdigit (c)) { + end_address <<= 4; + end_address |= hex_digit_value (c); + } else if (isblank (c)) { + state = MAP_LINE_PARSER_STATE_PERMISSIONS; + } else { + state = MAP_LINE_PARSER_STATE_INVALID; + } + break; + case MAP_LINE_PARSER_STATE_PERMISSIONS: + if (c == 'x') { + is_executable = TRUE; + } else if (isblank (c)) { + state = MAP_LINE_PARSER_STATE_OFFSET; + } else if ((c != '-') && ! isalpha (c)) { + state = MAP_LINE_PARSER_STATE_INVALID; + } + break; + case MAP_LINE_PARSER_STATE_OFFSET: + if (isxdigit (c)) { + offset <<= 4; + offset |= hex_digit_value (c); + } else if (isblank (c)) { + state = MAP_LINE_PARSER_STATE_DEVICE; + } else { + state = MAP_LINE_PARSER_STATE_INVALID; + } + break; + case MAP_LINE_PARSER_STATE_DEVICE: + if (isblank (c)) { + state = MAP_LINE_PARSER_STATE_INODE; + } else if ((c != ':') && ! isxdigit (c)) { + state = MAP_LINE_PARSER_STATE_INVALID; + } + break; + case MAP_LINE_PARSER_STATE_INODE: + if (isblank (c)) { + state = MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME; + } else if (! isdigit (c)) { + state = MAP_LINE_PARSER_STATE_INVALID; + } + break; + case MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME: + if (c == '/') { + state = MAP_LINE_PARSER_STATE_FILENAME; + start_filename = current; + } else if (! isblank (c)) { + state = MAP_LINE_PARSER_STATE_INVALID; + } + break; + case MAP_LINE_PARSER_STATE_FILENAME: + if (c == '\n') { + state = MAP_LINE_PARSER_STATE_DONE; + done = TRUE; + end_filename = current; + } + break; + case MAP_LINE_PARSER_STATE_DONE: + if (done && is_executable) { + *end_filename = 0; + append_region (regions, (gpointer) start_address, (gpointer) end_address, offset, start_filename); + } + return current; + case MAP_LINE_PARSER_STATE_INVALID: + if (c == '\n') { + state = MAP_LINE_PARSER_STATE_DONE; + } + break; + } + + + if (c == 0) { + return NULL; + } + + GOTO_NEXT_CHAR(current, buffer, fd); + c = *current; + } +} + +static gboolean +scan_process_regions (ProfilerExecutableMemoryRegions *regions) { + char *buffer; + char *current; + int fd; + + fd = open ("/proc/self/maps", O_RDONLY); + if (fd == -1) { + return FALSE; + } + + buffer = malloc (MAPS_BUFFER_SIZE); + update_regions_buffer (fd, buffer); + current = buffer; + while (current != NULL) { + current = parse_map_line (regions, fd, buffer, current); + } + + free (buffer); + + close (fd); + return TRUE; +} +//End of Linux code + +typedef enum { + MONO_PROFILER_STATISTICAL_CODE_END = 0, + MONO_PROFILER_STATISTICAL_CODE_METHOD = 1, + MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION = 2, + MONO_PROFILER_STATISTICAL_CODE_REGIONS = 3 +} MonoProfilerStatisticalCode; + +static void +refresh_memory_regions (void) { + ProfilerExecutableMemoryRegions *new_regions = profiler_executable_memory_regions_new (); + ProfilerExecutableMemoryRegions *old_regions = profiler->executable_regions; + int i; + + LOG_WRITER_THREAD ("Refreshing memory regions..."); + scan_process_regions (new_regions); + restore_region_ids (old_regions, new_regions); + sort_regions (new_regions); + LOG_WRITER_THREAD ("Refreshed memory regions."); + + // This marks the region "sub-block" + write_uint32 (MONO_PROFILER_STATISTICAL_CODE_REGIONS); + + // First write the "removed" regions + for (i = 0; i < old_regions->regions_count; i++) { + ProfilerExecutableMemoryRegionData *region = old_regions->regions [i]; + if (! region->is_new) { +#if DEBUG_STATISTICAL_PROFILER + printf ("[refresh_memory_regions] Invalidated region %d\n", region->id); +#endif + write_uint32 (region->id); + } + } + write_uint32 (0); + + // Then write the new ones + for (i = 0; i < new_regions->regions_count; i++) { + ProfilerExecutableMemoryRegionData *region = new_regions->regions [i]; + if (region->is_new) { + region->is_new = FALSE; + +#if DEBUG_STATISTICAL_PROFILER + printf ("[refresh_memory_regions] Wrote region %d (%p-%p[%d] '%s')\n", region->id, region->start, region->end, region->file_offset, region->file_name); +#endif + write_uint32 (region->id); + write_uint64 (GPOINTER_TO_INT (region->start)); + write_uint32 (GPOINTER_TO_INT (region->end) - GPOINTER_TO_INT (region->start)); + write_uint32 (region->file_offset); + write_string (region->file_name); + } + } + write_uint32 (0); + + // Finally, free the old ones, and replace them + profiler_executable_memory_regions_destroy (old_regions); + profiler->executable_regions = new_regions; +} + +static void +flush_all_mappings (gboolean flushObjects); + +static void +write_statistical_data_block (ProfilerStatisticalData *data) { + int start_index = data->first_unwritten_index; + int end_index = data->next_free_index; + gboolean regions_refreshed = FALSE; + int index; + + if (end_index > data->end_index) + end_index = data->end_index; + + if (start_index == end_index) + return; + + write_clock_data (); + + for (index = start_index; index < end_index; index ++) { + gpointer address = data->addresses [index]; + MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (char*) address); + + if (ji != NULL) { + MonoMethod *method = mono_jit_info_get_method (ji); + MethodIdMappingElement *element = method_id_mapping_element_get (method); + + if (element != NULL) { +#if DEBUG_STATISTICAL_PROFILER + printf ("[write_statistical_data_block] Wrote method %d\n", element->id); +#endif + write_uint32 ((element->id << 2) | MONO_PROFILER_STATISTICAL_CODE_METHOD); + } else { +#if DEBUG_STATISTICAL_PROFILER + printf ("[write_statistical_data_block] Wrote unknown method %p\n", method); +#endif + write_uint32 (MONO_PROFILER_STATISTICAL_CODE_METHOD); + } + } else { + ProfilerExecutableMemoryRegionData *region = find_address_region (profiler->executable_regions, address); + + if (region == NULL && ! regions_refreshed) { + refresh_memory_regions (); + regions_refreshed = TRUE; + region = find_address_region (profiler->executable_regions, address); + } + + if (region != NULL) { +#if DEBUG_STATISTICAL_PROFILER + printf ("[write_statistical_data_block] Wrote unmanaged hit %d[%d]\n", region->id, GPOINTER_TO_INT (address) - GPOINTER_TO_INT (region->start)); +#endif + write_uint32 ((region->id << 2) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION); + write_uint32 (GPOINTER_TO_INT (address) - GPOINTER_TO_INT (region->start)); + } else { +#if DEBUG_STATISTICAL_PROFILER + printf ("[write_statistical_data_block] Wrote unknown unmanaged hit %p\n", address); +#endif + write_uint32 (MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION); + write_uint64 (GPOINTER_TO_INT (address)); + } + } + } + write_uint32 (MONO_PROFILER_STATISTICAL_CODE_END); + + write_clock_data (); + + write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL); +} + +static void +write_intro_block (void) { + write_uint32 (1); + write_string ("mono"); + write_uint32 (profiler->flags); + write_uint64 (profiler->start_counter); + write_uint64 (profiler->start_time); + write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_INTRO); +} + +static void +write_end_block (void) { + write_uint32 (1); + write_uint64 (profiler->end_counter); + write_uint64 (profiler->end_time); + write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_END); +} + +static void +update_mapping (ProfilerPerThreadData *data) { + ProfilerEventData *start = data->first_unmapped_event; + ProfilerEventData *end = data->next_free_event; + data->first_unmapped_event = end; + +#if (DEBUG_LOGGING_PROFILER) + printf ("[update_mapping][TID %ld] START\n", data->thread_id); +#endif + while (start < end) { +#if DEBUG_LOGGING_PROFILER + printf ("Examining event %p[TID %ld] looking for a new mapping...\n", start, data->thread_id); +#endif + if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) { + ClassIdMappingElement *element = class_id_mapping_element_get (start->data.address); + if (element == NULL) { + MonoClass *klass = start->data.address; + class_id_mapping_element_new (klass); + } + } else if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) { + MethodIdMappingElement *element = method_id_mapping_element_get (start->data.address); + if (element == NULL) { + MonoMethod *method = start->data.address; + method_id_mapping_element_new (method); + } + } + + start ++; + } +#if (DEBUG_LOGGING_PROFILER) + printf ("[update_mapping][TID %ld] END\n", data->thread_id); +#endif +} + +static void +flush_all_mappings (gboolean flushObjects) { + ProfilerPerThreadData *data; + + for (data = profiler->per_thread_data; data != NULL; data = data->next) { + update_mapping (data); + } + for (data = profiler->per_thread_data; data != NULL; data = data->next) { + write_mapping_block (data->thread_id, flushObjects); + } +} + +static void +flush_full_event_data_buffer (ProfilerPerThreadData *data) { + LOCK_PROFILER (); + + // We flush all mappings because some id definitions could come + // from other threads + flush_all_mappings (FALSE); + g_assert (data->first_unmapped_event == data->end_event); + + write_thread_data_block (data); + + data->next_free_event = data->events; + data->first_unwritten_event = data->events; + data->first_unmapped_event = data->events; + MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter); + data->last_event_counter = data->start_event_counter; + + UNLOCK_PROFILER (); +} + +#define GET_NEXT_FREE_EVENT(d,e) {\ + if ((d)->next_free_event >= (d)->end_event) {\ + flush_full_event_data_buffer (d);\ + }\ + (e) = (d)->next_free_event;\ + (d)->next_free_event ++;\ +} while (0) + +static void +flush_everything (gboolean flushObjects) { + ProfilerPerThreadData *data; + + flush_all_mappings (flushObjects); + for (data = profiler->per_thread_data; data != NULL; data = data->next) { + write_thread_data_block (data); + } + write_statistical_data_block (profiler->statistical_data); +} + +#define RESULT_TO_LOAD_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_LOADED_EVENT_SUCCESS:MONO_PROFILER_LOADED_EVENT_FAILURE) +static void +appdomain_start_load (MonoProfiler *profiler, MonoDomain *domain) { + LOCK_PROFILER (); + loaded_element_load_start (profiler->loaded_appdomains, domain); + UNLOCK_PROFILER (); +} + +static void +appdomain_end_load (MonoProfiler *profiler, MonoDomain *domain, int result) { + char *name; + LoadedElement *element; + + name = g_strdup_printf ("%d", mono_domain_get_id (domain)); + LOCK_PROFILER (); + element = loaded_element_load_end (profiler->loaded_appdomains, domain, name); + write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ()); + UNLOCK_PROFILER (); +} + +static void +appdomain_start_unload (MonoProfiler *profiler, MonoDomain *domain) { + LOCK_PROFILER (); + loaded_element_unload_start (profiler->loaded_appdomains, domain); + flush_everything (FALSE); + UNLOCK_PROFILER (); +} + +static void +appdomain_end_unload (MonoProfiler *profiler, MonoDomain *domain) { + LoadedElement *element; + + LOCK_PROFILER (); + element = loaded_element_unload_end (profiler->loaded_appdomains, domain); + write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN, CURRENT_THREAD_ID ()); + UNLOCK_PROFILER (); +} + +static void +module_start_load (MonoProfiler *profiler, MonoImage *module) { + LOCK_PROFILER (); + loaded_element_load_start (profiler->loaded_modules, module); + UNLOCK_PROFILER (); +} + +static void +module_end_load (MonoProfiler *profiler, MonoImage *module, int result) { + char *name; + MonoAssemblyName aname; + LoadedElement *element; + + mono_assembly_fill_assembly_name (module, &aname); + name = mono_stringify_assembly_name (&aname); + LOCK_PROFILER (); + element = loaded_element_load_end (profiler->loaded_modules, module, name); + write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_MODULE | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ()); + UNLOCK_PROFILER (); +} + +static void +module_start_unload (MonoProfiler *profiler, MonoImage *module) { + LOCK_PROFILER (); + loaded_element_unload_start (profiler->loaded_modules, module); + flush_everything (FALSE); + UNLOCK_PROFILER (); +} + +static void +module_end_unload (MonoProfiler *profiler, MonoImage *module) { + LoadedElement *element; + + LOCK_PROFILER (); + element = loaded_element_unload_end (profiler->loaded_modules, module); + write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_MODULE, CURRENT_THREAD_ID ()); + UNLOCK_PROFILER (); +} + +static void +assembly_start_load (MonoProfiler *profiler, MonoAssembly *assembly) { + LOCK_PROFILER (); + loaded_element_load_start (profiler->loaded_assemblies, assembly); + UNLOCK_PROFILER (); +} + +static void +assembly_end_load (MonoProfiler *profiler, MonoAssembly *assembly, int result) { + char *name; + MonoAssemblyName aname; + LoadedElement *element; + + mono_assembly_fill_assembly_name (mono_assembly_get_image (assembly), &aname); + name = mono_stringify_assembly_name (&aname); + LOCK_PROFILER (); + element = loaded_element_load_end (profiler->loaded_assemblies, assembly, name); + write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ()); + UNLOCK_PROFILER (); +} + +static void +assembly_start_unload (MonoProfiler *profiler, MonoAssembly *assembly) { + LOCK_PROFILER (); + loaded_element_unload_start (profiler->loaded_assemblies, assembly); + flush_everything (FALSE); + UNLOCK_PROFILER (); +} +static void +assembly_end_unload (MonoProfiler *profiler, MonoAssembly *assembly) { + LoadedElement *element; + + LOCK_PROFILER (); + element = loaded_element_unload_end (profiler->loaded_assemblies, assembly); + write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY, CURRENT_THREAD_ID ()); + UNLOCK_PROFILER (); +} + +#if (DEBUG_LOGGING_PROFILER) +static const char* +class_event_code_to_string (MonoProfilerClassEvents code) { + switch (code) { + case MONO_PROFILER_EVENT_CLASS_LOAD: return "LOAD"; + case MONO_PROFILER_EVENT_CLASS_UNLOAD: return "UNLOAD"; + case MONO_PROFILER_EVENT_CLASS_ALLOCATION: return "ALLOCATION"; + case MONO_PROFILER_EVENT_CLASS_EXCEPTION: return "EXCEPTION"; + default: g_assert_not_reached (); return ""; + } +} +static const char* +method_event_code_to_string (MonoProfilerClassEvents code) { + switch (code) { + case MONO_PROFILER_EVENT_METHOD_CALL: return "CALL"; + case MONO_PROFILER_EVENT_METHOD_JIT: return "JIT"; + case MONO_PROFILER_EVENT_METHOD_FREED: return "FREED"; + default: g_assert_not_reached (); return ""; + } +} +static const char* +number_event_code_to_string (MonoProfilerEvents code) { + switch (code) { + case MONO_PROFILER_EVENT_THREAD: return "HREAD"; + case MONO_PROFILER_EVENT_GC_COLLECTION: return "GC_COLLECTION"; + case MONO_PROFILER_EVENT_GC_MARK: return "GC_MARK"; + case MONO_PROFILER_EVENT_GC_SWEEP: return "GC_SWEEP"; + case MONO_PROFILER_EVENT_GC_RESIZE: return "GC_RESIZE"; + default: g_assert_not_reached (); return ""; + } +} +static const char* +event_result_to_string (MonoProfilerEventResult code) { + switch (code) { + case MONO_PROFILER_EVENT_RESULT_SUCCESS: return "SUCCESS"; + case MONO_PROFILER_EVENT_RESULT_FAILURE: return "FAILURE"; + default: g_assert_not_reached (); return ""; + } +} +static const char* +event_kind_to_string (MonoProfilerEventKind code) { + switch (code) { + case MONO_PROFILER_EVENT_KIND_START: return "START"; + case MONO_PROFILER_EVENT_KIND_END: return "END"; + default: g_assert_not_reached (); return ""; + } +} +static void +print_event_data (gsize thread_id, ProfilerEventData *event, guint64 value) { + if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) { + printf ("[TID %ld] CLASS[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s)\n", + thread_id, + event->data.address, + event, + class_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK), + event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK), + event_kind_to_string (event->kind), + event->data_type, + event->kind, + event->code, + value, + mono_class_get_namespace ((MonoClass*) event->data.address), + mono_class_get_name ((MonoClass*) event->data.address)); + } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) { + printf ("[TID %ld] METHOD[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s:%s (?))\n", + thread_id, + event->data.address, + event, + method_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK), + event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK), + event_kind_to_string (event->kind), + event->data_type, + event->kind, + event->code, + value, + mono_class_get_namespace (mono_method_get_class ((MonoMethod*) event->data.address)), + mono_class_get_name (mono_method_get_class ((MonoMethod*) event->data.address)), + mono_method_get_name ((MonoMethod*) event->data.address)); + } else { + printf ("[TID %ld] NUMBER[%ld] event [%p] %s:%s[%d-%d-%d] %ld\n", + thread_id, + (guint64) event->data.number, + event, + number_event_code_to_string (event->code), + event_kind_to_string (event->kind), + event->data_type, + event->kind, + event->code, + value); + } +} +#define LOG_EVENT(tid,ev,val) print_event_data ((tid),(ev),(val)) +#else +#define LOG_EVENT(tid,ev,val) +#endif + +#define RESULT_TO_EVENT_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_EVENT_RESULT_SUCCESS:MONO_PROFILER_EVENT_RESULT_FAILURE) + +#define STORE_EVENT_ITEM_COUNTER(p,i,dt,c,k) do {\ + ProfilerPerThreadData *data;\ + ProfilerEventData *event;\ + guint64 counter;\ + guint64 delta;\ + GET_PROFILER_THREAD_DATA (data);\ + GET_NEXT_FREE_EVENT (data, event);\ + MONO_PROFILER_GET_CURRENT_COUNTER (counter);\ + event->data.address = (i);\ + event->data_type = (dt);\ + event->code = (c);\ + event->kind = (k);\ + delta = counter - data->last_event_counter;\ + if (delta < MAX_EVENT_VALUE) {\ + event->value = delta;\ + } else {\ + ProfilerEventData *extension = data->next_free_event;\ + data->next_free_event ++;\ + event->value = delta >> 32;\ + extension->data.number = delta & 0xffffffff;\ + }\ + data->last_event_counter = counter;\ + LOG_EVENT (data->thread_id, event, delta);\ +} while (0); +#define STORE_EVENT_ITEM_VALUE(p,i,dt,c,k,v) do {\ + ProfilerPerThreadData *data;\ + ProfilerEventData *event;\ + GET_PROFILER_THREAD_DATA (data);\ + GET_NEXT_FREE_EVENT (data, event);\ + event->data.address = (i);\ + event->data_type = (dt);\ + event->code = (c);\ + event->kind = (k);\ + if ((v) < MAX_EVENT_VALUE) {\ + event->value = (v);\ + } else {\ + ProfilerEventData *extension = data->next_free_event;\ + data->next_free_event ++;\ + event->value = (v) >> 32;\ + extension->data.number = (v) & 0xffffffff;\ + }\ + LOG_EVENT (data->thread_id, event, (v));\ +}while (0); +#define STORE_EVENT_NUMBER_COUNTER(p,n,dt,c,k) do {\ + ProfilerPerThreadData *data;\ + ProfilerEventData *event;\ + guint64 counter;\ + guint64 delta;\ + GET_PROFILER_THREAD_DATA (data);\ + GET_NEXT_FREE_EVENT (data, event);\ + MONO_PROFILER_GET_CURRENT_COUNTER (counter);\ + event->data.number = (n);\ + event->data_type = (dt);\ + event->code = (c);\ + event->kind = (k);\ + delta = counter - data->last_event_counter;\ + if (delta < MAX_EVENT_VALUE) {\ + event->value = delta;\ + } else {\ + ProfilerEventData *extension = data->next_free_event;\ + data->next_free_event ++;\ + event->value = delta >> 32;\ + extension->data.number = delta & 0xffffffff;\ + }\ + data->last_event_counter = counter;\ + LOG_EVENT (data->thread_id, event, delta);\ +}while (0); +#define STORE_EVENT_NUMBER_VALUE(p,n,dt,c,k,v) do {\ + ProfilerPerThreadData *data;\ + ProfilerEventData *event;\ + GET_PROFILER_THREAD_DATA (data);\ + GET_NEXT_FREE_EVENT (data, event);\ + event->data.number = (n);\ + event->data_type = (dt);\ + event->code = (c);\ + event->kind = (k);\ + if ((v) < MAX_EVENT_VALUE) {\ + event->value = (v);\ + } else {\ + ProfilerEventData *extension = data->next_free_event;\ + data->next_free_event ++;\ + event->value = (v) >> 32;\ + extension->data.number = (v) & 0xffffffff;\ + }\ + LOG_EVENT (data->thread_id, event, (v));\ +}while (0); + + +static void +class_start_load (MonoProfiler *profiler, MonoClass *klass) { + STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD, MONO_PROFILER_EVENT_KIND_START); +} +static void +class_end_load (MonoProfiler *profiler, MonoClass *klass, int result) { + STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD | RESULT_TO_EVENT_CODE (result), MONO_PROFILER_EVENT_KIND_END); +} +static void +class_start_unload (MonoProfiler *profiler, MonoClass *klass) { + STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_START); +} +static void +class_end_unload (MonoProfiler *profiler, MonoClass *klass) { + STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_END); +} + +static void +method_start_jit (MonoProfiler *profiler, MonoMethod *method) { + if (profiler->action_flags.jit_time) { + STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT, MONO_PROFILER_EVENT_KIND_START); + } +} +static void +method_end_jit (MonoProfiler *profiler, MonoMethod *method, int result) { + if (profiler->action_flags.jit_time) { + STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT | RESULT_TO_EVENT_CODE (result), MONO_PROFILER_EVENT_KIND_END); + } +} + +#if (HAS_OPROFILE) +static void +method_jit_result (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result) { + if (profiler->action_flags.oprofile && (result == MONO_PROFILE_OK)) { + MonoClass *klass = mono_method_get_class (method); + char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE); + char *name = g_strdup_printf ("%s.%s:%s (%s)", mono_class_get_namespace (klass), mono_class_get_name (klass), mono_method_get_name (method), signature); + gpointer code_start = mono_jit_info_get_code_start (jinfo); + int code_size = mono_jit_info_get_code_size (jinfo); + + if (op_write_native_code (name, code_start, code_size)) { + g_warning ("Problem calling op_write_native_code\n"); + } + + g_free (signature); + g_free (name); + } +} +#endif + + +static void +method_enter (MonoProfiler *profiler, MonoMethod *method) { + STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_START); +} +static void +method_leave (MonoProfiler *profiler, MonoMethod *method) { + STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_END); +} + +static void +method_free (MonoProfiler *profiler, MonoMethod *method) { + STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_FREED, 0); +} + +static void +thread_start (MonoProfiler *profiler, gsize tid) { + STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_START); +} +static void +thread_end (MonoProfiler *profiler, gsize tid) { + STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_END); +} + +static void +object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) { + ProfilerPerThreadData *thread_data; + + STORE_EVENT_ITEM_VALUE (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_ALLOCATION, 0, (guint64) mono_object_get_size (obj)); + if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) { + GET_PROFILER_THREAD_DATA (thread_data); + STORE_ALLOCATED_OBJECT (thread_data, obj); + } +} + + +static void +statistical_hit (MonoProfiler *profiler, guchar *ip, void *context) { + ProfilerStatisticalData *data; + int index; + + do { + data = profiler->statistical_data; + index = InterlockedIncrement (&data->next_free_index); + + if (index <= data->end_index) { + data->addresses [index - 1] = (gpointer) ip; + } else { + /* Check if we are the one that must swap the buffers */ + if (index == data->end_index + 1) { + ProfilerStatisticalData *new_data; + + /* In the *impossible* case that the writer thread has not finished yet, */ + /* loop waiting for it and meanwhile lose all statistical events... */ + do { + /* First, wait that it consumed the ready buffer */ + while (profiler->statistical_data_ready != NULL); + /* Then, wait that it produced the free buffer */ + new_data = profiler->statistical_data_second_buffer; + } while (new_data == NULL); + + profiler->statistical_data_ready = data; + profiler->statistical_data = new_data; + profiler->statistical_data_second_buffer = NULL; + WRITER_EVENT_RAISE (); + } + + /* Loop again, hoping to acquire a free slot this time */ + data = NULL; + } + } while (data == NULL); +} + +static MonoProfilerEvents +gc_event_code_from_profiler_event (MonoGCEvent event) { + switch (event) { + case MONO_GC_EVENT_START: + case MONO_GC_EVENT_END: + return MONO_PROFILER_EVENT_GC_COLLECTION; + case MONO_GC_EVENT_MARK_START: + case MONO_GC_EVENT_MARK_END: + return MONO_PROFILER_EVENT_GC_MARK; + case MONO_GC_EVENT_RECLAIM_START: + case MONO_GC_EVENT_RECLAIM_END: + return MONO_PROFILER_EVENT_GC_SWEEP; + default: + g_assert_not_reached (); + return 0; + } +} + +static MonoProfilerEventKind +gc_event_kind_from_profiler_event (MonoGCEvent event) { + switch (event) { + case MONO_GC_EVENT_START: + case MONO_GC_EVENT_MARK_START: + case MONO_GC_EVENT_RECLAIM_START: + return MONO_PROFILER_EVENT_KIND_START; + case MONO_GC_EVENT_END: + case MONO_GC_EVENT_MARK_END: + case MONO_GC_EVENT_RECLAIM_END: + return MONO_PROFILER_EVENT_KIND_END; + default: + g_assert_not_reached (); + return 0; + } +} + +static void +profiler_heap_buffers_setup (ProfilerHeapShotHeapBuffers *heap) { + heap->buffers = g_new (ProfilerHeapShotHeapBuffer, 1); + heap->buffers->previous = NULL; + heap->buffers->next = NULL; + heap->buffers->start_slot = &(heap->buffers->buffer [0]); + heap->buffers->end_slot = &(heap->buffers->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]); + heap->last = heap->buffers; + heap->current = heap->buffers; + heap->first_free_slot = & (heap->buffers->buffer [0]); +} +static void +profiler_heap_buffers_clear (ProfilerHeapShotHeapBuffers *heap) { + heap->buffers = NULL; + heap->last = NULL; + heap->current = NULL; + heap->first_free_slot = NULL; +} +static void +profiler_heap_buffers_free (ProfilerHeapShotHeapBuffers *heap) { + ProfilerHeapShotHeapBuffer *current = heap->buffers; + while (current != NULL) { + ProfilerHeapShotHeapBuffer *next = current->next; + g_free (current); + current = next; + } + profiler_heap_buffers_clear (heap); +} + +static int +report_object_references (gpointer *start, ClassIdMappingElement *layout, ProfilerHeapShotWriteJob *job) { + int reported_references = 0; + int slot; + + for (slot = 0; slot < layout->data.layout.slots; slot ++) { + gboolean slot_has_reference; + if (layout->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) { + if (layout->data.bitmap.compact & (((guint64)1) << slot)) { + slot_has_reference = TRUE; + } else { + slot_has_reference = FALSE; + } + } else { + if (layout->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) { + slot_has_reference = TRUE; + } else { + slot_has_reference = FALSE; + } + } + + if (slot_has_reference) { + gpointer field = start [slot]; + + if ((field != NULL) && mono_object_is_alive (field)) { + reported_references ++; + WRITE_HEAP_SHOT_JOB_VALUE (job, field); + } + } + } + + return reported_references; +} + +static void +profiler_heap_report_object_reachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) { + if (profiler->action_flags.heap_shot) { + MonoClass *klass = mono_object_get_class (obj); + int reference_counter = 0; + gpointer *reference_counter_location; + + WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, obj, HEAP_CODE_OBJECT); + WRITE_HEAP_SHOT_JOB_VALUE (job, NULL); + reference_counter_location = job->cursor - 1; + + if (mono_class_get_rank (klass)) { + MonoArray *array = (MonoArray *) obj; + MonoClass *element_class = mono_class_get_element_class (klass); + ClassIdMappingElement *element_id = class_id_mapping_element_get (element_class); + + g_assert (element_id != NULL); + if (element_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) { + class_id_mapping_element_build_layout_bitmap (element_class, element_id); + } + if (! mono_class_is_valuetype (element_class)) { + int length = mono_array_length (array); + int i; + for (i = 0; i < length; i++) { + MonoObject *array_element = mono_array_get (array, MonoObject*, i); + if ((array_element != NULL) && mono_object_is_alive (array_element)) { + reference_counter ++; + WRITE_HEAP_SHOT_JOB_VALUE (job, array_element); + } + } + } else if (element_id->data.layout.references > 0) { + int length = mono_array_length (array); + int array_element_size = mono_array_element_size (klass); + int counter = 0; + int i; + for (i = 0; i < length; i++) { + gpointer array_element_address = mono_array_addr_with_size (array, array_element_size, i); + counter += report_object_references (array_element_address, element_id, job); + } + } + } else { + ClassIdMappingElement *class_id = class_id_mapping_element_get (klass); + if (class_id == NULL) { + printf ("profiler_heap_report_object_reachable: class %p (%s.%s) has no id\n", klass, mono_class_get_namespace (klass), mono_class_get_name (klass)); + } + g_assert (class_id != NULL); + if (class_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) { + class_id_mapping_element_build_layout_bitmap (klass, class_id); + } + if (class_id->data.layout.references > 0) { + reference_counter += report_object_references ((gpointer) (obj + sizeof (MonoObject)), class_id, job); + } + } + + *reference_counter_location = GINT_TO_POINTER (reference_counter); + } +} +static void +profiler_heap_report_object_unreachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) { + MonoClass *klass = mono_object_get_class (obj); + guint32 size = mono_object_get_size (obj); + +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_report_object_unreachable: at job %p writing klass %p\n", job, klass); +#endif + WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, klass, HEAP_CODE_FREE_OBJECT_CLASS); + +#if DEBUG_HEAP_PROFILER + printf ("profiler_heap_report_object_unreachable: at job %p writing size %p\n", job, GUINT_TO_POINTER (size)); +#endif + WRITE_HEAP_SHOT_JOB_VALUE (job, GUINT_TO_POINTER (size)); +} + +static void +profiler_heap_add_object (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject *obj) { + if (heap->first_free_slot >= heap->current->end_slot) { + if (heap->current->next != NULL) { + heap->current = heap->current->next; + } else { + ProfilerHeapShotHeapBuffer *buffer = g_new (ProfilerHeapShotHeapBuffer, 1); + buffer->previous = heap->last; + buffer->next = NULL; + buffer->start_slot = &(buffer->buffer [0]); + buffer->end_slot = &(buffer->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]); + heap->current = buffer; + heap->last->next = buffer; + heap->last = buffer; + } + heap->first_free_slot = &(heap->current->buffer [0]); + } + + *(heap->first_free_slot) = obj; + heap->first_free_slot ++; + profiler_heap_report_object_reachable (job, obj); +} + +static MonoObject* +profiler_heap_pop_object_from_end (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject** current_slot) { + while (heap->first_free_slot != current_slot) { + MonoObject* obj; + + if (heap->first_free_slot > heap->current->start_slot) { + heap->first_free_slot --; + } else { + heap->current = heap->current->previous; + g_assert (heap->current != NULL); + heap->first_free_slot = heap->current->end_slot - 1; + } + + obj = *(heap->first_free_slot); + + if (mono_object_is_alive (obj)) { + profiler_heap_report_object_reachable (job, obj); + return obj; + } else { + profiler_heap_report_object_unreachable (job, obj); + } + } + return NULL; +} + +static void +profiler_heap_scan (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job) { + ProfilerHeapShotHeapBuffer *current_buffer = heap->buffers; + MonoObject** current_slot = current_buffer->start_slot; + + while (current_slot != heap->first_free_slot) { + MonoObject *obj = *current_slot; + if (mono_object_is_alive (obj)) { + profiler_heap_report_object_reachable (job, obj); + } else { + profiler_heap_report_object_unreachable (job, obj); + *current_slot = profiler_heap_pop_object_from_end (heap, job, current_slot); + } + + if (*current_slot != NULL) { + current_slot ++; + + if (current_slot == current_buffer->end_slot) { + current_buffer = current_buffer->next; + //g_assert (current_buffer != NULL); + if (current_buffer == NULL) { + printf ("KO\n"); + G_BREAKPOINT (); + g_assert_not_reached (); + } + current_slot = current_buffer->start_slot; + } + } + } +} + +static void +gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) { + STORE_EVENT_NUMBER_COUNTER (profiler, generation, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, gc_event_code_from_profiler_event (ev), gc_event_kind_from_profiler_event (ev)); + if ((ev == MONO_GC_EVENT_MARK_END) && (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot)) { + ProfilerHeapShotWriteJob *job = profiler_heap_shot_write_job_new (); + ProfilerPerThreadData *data; + + MONO_PROFILER_GET_CURRENT_COUNTER (job->start_counter); + MONO_PROFILER_GET_CURRENT_TIME (job->start_time); + + profiler_heap_scan (&(profiler->heap), job); + + for (data = profiler->per_thread_data; data != NULL; data = data->next) { + ProfilerHeapShotObjectBuffer *buffer; + for (buffer = data->heap_shot_object_buffers; buffer != NULL; buffer = buffer->next) { + MonoObject **cursor; + for (cursor = buffer->first_unprocessed_slot; cursor < buffer->next_free_slot; cursor ++) { + MonoObject *obj = *cursor; +#if DEBUG_HEAP_PROFILER + printf ("gc_event: in object buffer %p(%p-%p) cursor at %p has object %p\n", buffer, &(buffer->buffer [0]), buffer->end, cursor, obj); +#endif + if (mono_object_is_alive (obj)) { + profiler_heap_add_object (&(profiler->heap), job, obj); + } else { + profiler_heap_report_object_unreachable (job, obj); + } + } + buffer->first_unprocessed_slot = cursor; + } + } + MONO_PROFILER_GET_CURRENT_COUNTER (job->end_counter); + MONO_PROFILER_GET_CURRENT_TIME (job->end_time); + + profiler_add_heap_shot_write_job (job); + profiler_free_heap_shot_write_jobs (); + WRITER_EVENT_RAISE (); + } +} + +static void +gc_resize (MonoProfiler *profiler, gint64 new_size) { + STORE_EVENT_NUMBER_COUNTER (profiler, new_size, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_GC_RESIZE, 0); +} + +/* called at the end of the program */ +static void +profiler_shutdown (MonoProfiler *prof) +{ + ProfilerPerThreadData* current_thread_data; + + LOG_WRITER_THREAD ("profiler_shutdown: zeroing relevant flags"); + mono_profiler_set_events (0); + //profiler->flags = 0; + //profiler->action_flags.unreachable_objects = FALSE; + //profiler->action_flags.heap_shot = FALSE; + + LOG_WRITER_THREAD ("profiler_shutdown: asking stats thread to exit"); + profiler->terminate_writer_thread = TRUE; + WRITER_EVENT_RAISE (); + LOG_WRITER_THREAD ("profiler_shutdown: waiting for stats thread to exit"); + WAIT_WRITER_THREAD (); + LOG_WRITER_THREAD ("profiler_shutdown: stats thread should be dead now"); + WRITER_EVENT_DESTROY (); + + LOCK_PROFILER (); + + MONO_PROFILER_GET_CURRENT_TIME (profiler->end_time); + MONO_PROFILER_GET_CURRENT_COUNTER (profiler->end_counter); + + flush_everything (FALSE); + write_end_block (); + FLUSH_FILE (); + CLOSE_FILE(); + UNLOCK_PROFILER (); + g_free (profiler->file_name); + + method_id_mapping_destroy (profiler->methods); + class_id_mapping_destroy (profiler->classes); + g_hash_table_destroy (profiler->loaded_assemblies); + g_hash_table_destroy (profiler->loaded_modules); + g_hash_table_destroy (profiler->loaded_appdomains); + + FREE_PROFILER_THREAD_DATA (); + + for (current_thread_data = profiler->per_thread_data; current_thread_data != NULL; current_thread_data = current_thread_data->next) { + profiler_per_thread_data_destroy (current_thread_data); + } + if (profiler->statistical_data != NULL) { + profiler_statistical_data_destroy (profiler->statistical_data); + } + if (profiler->statistical_data_ready != NULL) { + profiler_statistical_data_destroy (profiler->statistical_data_ready); + } + if (profiler->statistical_data_second_buffer != NULL) { + profiler_statistical_data_destroy (profiler->statistical_data_second_buffer); + } + if (profiler->executable_regions != NULL) { + profiler_executable_memory_regions_destroy (profiler->executable_regions); + } + + profiler_heap_buffers_free (&(profiler->heap)); + + profiler_free_write_buffers (); + profiler_destroy_heap_shot_write_jobs (); + + DELETE_PROFILER_MUTEX (); + +#if (HAS_OPROFILE) + if (profiler->action_flags.oprofile) { + op_close_agent (); + } +#endif + + g_free (profiler); + profiler = NULL; +} + +#define DEFAULT_ARGUMENTS "s" +static void +setup_user_options (const char *arguments) { + gchar **arguments_array, **current_argument; + + profiler->file_name = NULL; + profiler->per_thread_buffer_size = 10000; + profiler->statistical_buffer_size = 10000; + profiler->write_buffer_size = 1024; + profiler->flags = MONO_PROFILE_APPDOMAIN_EVENTS| + MONO_PROFILE_ASSEMBLY_EVENTS| + MONO_PROFILE_MODULE_EVENTS| + MONO_PROFILE_CLASS_EVENTS| + MONO_PROFILE_METHOD_EVENTS; + + if (arguments == NULL) { + arguments = DEFAULT_ARGUMENTS; + } else if (strstr (arguments, ":")) { + arguments = strstr (arguments, ":") + 1; + if (arguments [0] == 0) { + arguments = DEFAULT_ARGUMENTS; + } + } + + arguments_array = g_strsplit (arguments, ",", -1); + + for (current_argument = arguments_array; ((current_argument != NULL) && (current_argument [0] != 0)); current_argument ++) { + char *argument = *current_argument; + char *equals = strstr (argument, "="); + + if (equals != NULL) { + int equals_position = equals - argument; + + if (! (strncmp (argument, "per-thread-buffer-size", equals_position) && strncmp (argument, "tbs", equals_position))) { + int value = atoi (equals + 1); + if (value > 0) { + profiler->per_thread_buffer_size = value; + } + } else if (! (strncmp (argument, "statistical-thread-buffer-size", equals_position) && strncmp (argument, "sbs", equals_position))) { + int value = atoi (equals + 1); + if (value > 0) { + profiler->statistical_buffer_size = value; + } + } else if (! (strncmp (argument, "write-buffer-size", equals_position) && strncmp (argument, "wbs", equals_position))) { + int value = atoi (equals + 1); + if (value > 0) { + profiler->write_buffer_size = value; + } + } else if (! (strncmp (argument, "output", equals_position) && strncmp (argument, "out", equals_position) && strncmp (argument, "o", equals_position) && strncmp (argument, "O", equals_position))) { + if (strlen (equals + 1) > 0) { + profiler->file_name = g_strdup (equals + 1); + } + } else { + g_warning ("Cannot parse valued argument %s\n", argument); + } + } else { + if (! (strcmp (argument, "jit") && strcmp (argument, "j"))) { + profiler->flags |= MONO_PROFILE_JIT_COMPILATION; + profiler->action_flags.jit_time = TRUE; + } else if (! (strcmp (argument, "allocations") && strcmp (argument, "alloc") && strcmp (argument, "a"))) { + profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC; + } else if (! (strcmp (argument, "gc") && strcmp (argument, "g"))) { + profiler->flags |= MONO_PROFILE_GC; + } else if (! (strcmp (argument, "heap-shot") && strcmp (argument, "heap") && strcmp (argument, "h"))) { + profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC; + profiler->action_flags.unreachable_objects = TRUE; + profiler->action_flags.heap_shot = TRUE; + } else if (! (strcmp (argument, "unreachable") && strcmp (argument, "free") && strcmp (argument, "f"))) { + profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC; + profiler->action_flags.unreachable_objects = TRUE; + } else if (! (strcmp (argument, "threads") && strcmp (argument, "t"))) { + profiler->flags |= MONO_PROFILE_THREADS; + } else if (! (strcmp (argument, "enter-leave") && strcmp (argument, "calls") && strcmp (argument, "c"))) { + profiler->flags |= MONO_PROFILE_ENTER_LEAVE; + } else if (! (strcmp (argument, "statistical") && strcmp (argument, "stat") && strcmp (argument, "s"))) { + profiler->flags |= MONO_PROFILE_STATISTICAL; +#if (HAS_OPROFILE) + } else if (! (strcmp (argument, "oprofile") && strcmp (argument, "oprof"))) { + profiler->flags |= MONO_PROFILE_JIT_COMPILATION; + profiler->action_flags.oprofile = TRUE; + if (op_open_agent ()) { + g_warning ("Problem calling op_open_agent\n"); + } +#endif + } else if (strcmp (argument, "logging")) { + g_warning ("Cannot parse flag argument %s\n", argument); + } + } + } + + g_free (arguments_array); + + if (profiler->file_name == NULL) { + profiler->file_name = g_strdup ("profiler-log.prof"); + } +} + + +static guint32 +data_writer_thread (gpointer nothing) { + for (;;) { + ProfilerStatisticalData *statistical_data; + gboolean done; + + LOG_WRITER_THREAD ("data_writer_thread: going to sleep"); + WRITER_EVENT_WAIT (); + LOG_WRITER_THREAD ("data_writer_thread: just woke up"); + + statistical_data = profiler->statistical_data_ready; + done = (statistical_data == NULL) && (profiler->heap_shot_write_jobs == NULL); + + if (!done) { + LOG_WRITER_THREAD ("data_writer_thread: acquiring lock and writing data"); + LOCK_PROFILER (); + + // This makes sure that all method ids are in place + LOG_WRITER_THREAD ("data_writer_thread: writing mapping..."); + flush_all_mappings (FALSE); + LOG_WRITER_THREAD ("data_writer_thread: wrote mapping"); + + if (statistical_data != NULL) { + LOG_WRITER_THREAD ("data_writer_thread: writing statistical data..."); + LOCK_PROFILER (); + profiler->statistical_data_ready = NULL; + write_statistical_data_block (statistical_data); + statistical_data->next_free_index = 0; + statistical_data->first_unwritten_index = 0; + profiler->statistical_data_second_buffer = statistical_data; + UNLOCK_PROFILER (); + LOG_WRITER_THREAD ("data_writer_thread: wrote statistical data"); + } + + profiler_process_heap_shot_write_jobs (); + + UNLOCK_PROFILER (); + LOG_WRITER_THREAD ("data_writer_thread: wrote data and released lock"); + + } + + if (profiler->terminate_writer_thread) { + LOG_WRITER_THREAD ("data_writer_thread: exiting thread"); + EXIT_THREAD (); + } + } + return 0; +} + +void +mono_profiler_startup (const char *desc); + +/* the entry point (mono_profiler_load?) */ +void +mono_profiler_startup (const char *desc) +{ + profiler = g_new0 (MonoProfiler, 1); + + setup_user_options ((desc != NULL) ? desc : ""); + + INITIALIZE_PROFILER_MUTEX (); + MONO_PROFILER_GET_CURRENT_TIME (profiler->start_time); + MONO_PROFILER_GET_CURRENT_COUNTER (profiler->start_counter); + + profiler->methods = method_id_mapping_new (); + profiler->classes = class_id_mapping_new (); + profiler->loaded_assemblies = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy); + profiler->loaded_modules = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy); + profiler->loaded_appdomains = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy); + + profiler->statistical_data = profiler_statistical_data_new (profiler->statistical_buffer_size); + profiler->statistical_data_second_buffer = profiler_statistical_data_new (profiler->statistical_buffer_size); + + profiler->write_buffers = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE); + profiler->write_buffers->next = NULL; + profiler->current_write_buffer = profiler->write_buffers; + profiler->current_write_position = 0; + profiler->full_write_buffers = 0; + + profiler->executable_regions = profiler_executable_memory_regions_new (); + + profiler->heap_shot_write_jobs = NULL; + if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) { + profiler_heap_buffers_setup (&(profiler->heap)); + } else { + profiler_heap_buffers_clear (&(profiler->heap)); + } + + WRITER_EVENT_INIT (); + LOG_WRITER_THREAD ("mono_profiler_startup: creating writer thread"); + CREATE_WRITER_THREAD (data_writer_thread); + LOG_WRITER_THREAD ("mono_profiler_startup: created writer thread"); + + ALLOCATE_PROFILER_THREAD_DATA (); + + OPEN_FILE (); + + write_intro_block (); + + mono_profiler_install (profiler, profiler_shutdown); + + mono_profiler_install_appdomain (appdomain_start_load, appdomain_end_load, + appdomain_start_unload, appdomain_end_unload); + mono_profiler_install_assembly (assembly_start_load, assembly_end_load, + assembly_start_unload, assembly_end_unload); + mono_profiler_install_module (module_start_load, module_end_load, + module_start_unload, module_end_unload); + mono_profiler_install_class (class_start_load, class_end_load, + class_start_unload, class_end_unload); + mono_profiler_install_jit_compile (method_start_jit, method_end_jit); + mono_profiler_install_enter_leave (method_enter, method_leave); + mono_profiler_install_method_free (method_free); + mono_profiler_install_thread (thread_start, thread_end); + mono_profiler_install_allocation (object_allocated); + mono_profiler_install_statistical (statistical_hit); + mono_profiler_install_gc (gc_event, gc_resize); +#if (HAS_OPROFILE) + mono_profiler_install_jit_end (method_jit_result); +#endif + + mono_profiler_set_events (profiler->flags); +} +