X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fprofiler%2Fmono-profiler-logging.c;h=7ad574b82f449b5df390133858b53a87b79ff53c;hb=5fc8a5719a1bc1af414a931ff5c48a5d4d9389f3;hp=92a0096c92be9f3487dc90202d5bf4a928e6d488;hpb=3331634f37c395ea87d15a2f3338b2bc66a8470a;p=mono.git diff --git a/mono/profiler/mono-profiler-logging.c b/mono/profiler/mono-profiler-logging.c index 92a0096c92b..7ad574b82f4 100644 --- a/mono/profiler/mono-profiler-logging.c +++ b/mono/profiler/mono-profiler-logging.c @@ -1,6 +1,15 @@ +/* + * mono-profiler-logging.c: Logging profiler for Mono. + * + * Author: + * Massimiliano Mantione (massi@ximian.com) + * + * Copyright 2008-2009 Novell, Inc (http://www.novell.com) + */ #include #include #include +#include #include #include #include @@ -16,6 +25,10 @@ #include +#include +#include +#include + #define HAS_OPROFILE 0 #if (HAS_OPROFILE) @@ -43,6 +56,9 @@ typedef enum { MONO_PROFILER_DIRECTIVE_ALLOCATIONS_CARRY_CALLER = 1, MONO_PROFILER_DIRECTIVE_ALLOCATIONS_HAVE_STACK = 2, MONO_PROFILER_DIRECTIVE_ALLOCATIONS_CARRY_ID = 3, + MONO_PROFILER_DIRECTIVE_LOADED_ELEMENTS_CARRY_ID = 4, + MONO_PROFILER_DIRECTIVE_CLASSES_CARRY_ASSEMBLY_ID = 5, + MONO_PROFILER_DIRECTIVE_METHODS_CARRY_WRAPPER_FLAG = 6, MONO_PROFILER_DIRECTIVE_LAST } MonoProfilerDirectives; @@ -84,7 +100,8 @@ typedef enum { MONO_PROFILER_EVENT_CLASS_LOAD = 0, MONO_PROFILER_EVENT_CLASS_UNLOAD = 1, MONO_PROFILER_EVENT_CLASS_EXCEPTION = 2, - MONO_PROFILER_EVENT_CLASS_ALLOCATION = 3 + MONO_PROFILER_EVENT_CLASS_MONITOR = 3, + MONO_PROFILER_EVENT_CLASS_ALLOCATION = 4 } MonoProfilerClassEvents; typedef enum { MONO_PROFILER_EVENT_RESULT_SUCCESS = 0, @@ -101,7 +118,8 @@ typedef enum { MONO_PROFILER_EVENT_GC_START_WORLD = 7, MONO_PROFILER_EVENT_JIT_TIME_ALLOCATION = 8, MONO_PROFILER_EVENT_STACK_SECTION = 9, - MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID = 10 + MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID = 10, + MONO_PROFILER_EVENT_OBJECT_MONITOR = 11 } MonoProfilerEvents; typedef enum { MONO_PROFILER_EVENT_KIND_START = 0, @@ -116,7 +134,7 @@ typedef enum { static gboolean use_fast_timer = FALSE; -#if (defined(__i386__) || defined(__x86_64__)) && ! defined(PLATFORM_WIN32) +#if (defined(__i386__) || defined(__x86_64__)) && ! defined(HOST_WIN32) #if defined(__i386__) static const guchar cpuid_impl [] = { @@ -298,11 +316,48 @@ typedef struct _LoadedElement { guint64 load_end_counter; guint64 unload_start_counter; guint64 unload_end_counter; + guint32 id; guint8 loaded; guint8 load_written; guint8 unloaded; guint8 unload_written; } LoadedElement; +struct _ProfilerCodeBufferArray; +typedef struct _ProfilerCodeBuffer { + gpointer start; + gpointer end; + struct { + union { + MonoMethod *method; + MonoClass *klass; + void *data; + struct _ProfilerCodeBufferArray *sub_buffers; + } data; + guint16 value; + guint16 type; + } info; +} ProfilerCodeBuffer; + +#define PROFILER_CODE_BUFFER_ARRAY_SIZE 64 +typedef struct _ProfilerCodeBufferArray { + int level; + int number_of_buffers; + ProfilerCodeBuffer buffers [PROFILER_CODE_BUFFER_ARRAY_SIZE]; +} ProfilerCodeBufferArray; + +typedef struct _ProfilerCodeChunk { + gpointer start; + gpointer end; + gboolean destroyed; + ProfilerCodeBufferArray *buffers; +} ProfilerCodeChunk; + +typedef struct _ProfilerCodeChunks { + int capacity; + int number_of_chunks;; + ProfilerCodeChunk *chunks; +} ProfilerCodeChunks; + #define PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE 1024 #define PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE 4096 @@ -362,7 +417,7 @@ typedef struct _ProfilerHeapShotWriteJob { ProfilerHeapShotWriteBuffer *buffers; ProfilerHeapShotWriteBuffer **last_next; guint32 full_buffers; - gboolean heap_shot_was_signalled; + gboolean heap_shot_was_requested; guint64 start_counter; guint64 start_time; guint64 end_counter; @@ -385,6 +440,7 @@ typedef struct _ProfilerThreadStack { typedef struct _ProfilerPerThreadData { ProfilerEventData *events; ProfilerEventData *next_free_event; + ProfilerEventData *next_unreserved_event; ProfilerEventData *end_event; ProfilerEventData *first_unwritten_event; ProfilerEventData *first_unmapped_event; @@ -403,9 +459,9 @@ typedef struct _ProfilerStatisticalHit { typedef struct _ProfilerStatisticalData { ProfilerStatisticalHit *hits; - int next_free_index; - int end_index; - int first_unwritten_index; + unsigned int next_free_index; + unsigned int end_index; + unsigned int first_unwritten_index; } ProfilerStatisticalData; typedef struct _ProfilerUnmanagedSymbol { @@ -416,6 +472,7 @@ typedef struct _ProfilerUnmanagedSymbol { } ProfilerUnmanagedSymbol; struct _ProfilerExecutableFile; +struct _ProfilerExecutableFileSectionRegion; typedef struct _ProfilerExecutableMemoryRegionData { gpointer start; @@ -426,6 +483,7 @@ typedef struct _ProfilerExecutableMemoryRegionData { gboolean is_new; struct _ProfilerExecutableFile *file; + struct _ProfilerExecutableFileSectionRegion *file_region_reference; guint32 symbols_count; guint32 symbols_capacity; ProfilerUnmanagedSymbol *symbols; @@ -617,7 +675,7 @@ typedef struct _ProfilerExecutableFiles { #define CLEANUP_WRITER_THREAD() do {profiler->writer_thread_terminated = TRUE;} while (0) #define CHECK_WRITER_THREAD() (! profiler->writer_thread_terminated) -#ifndef PLATFORM_WIN32 +#ifndef HOST_WIN32 #include #include #include @@ -640,6 +698,7 @@ typedef struct _ProfilerExecutableFiles { #define THREAD_TYPE pthread_t #define CREATE_WRITER_THREAD(f) pthread_create (&(profiler->data_writer_thread), NULL, ((void*(*)(void*))f), NULL) +#define CREATE_USER_THREAD(f) pthread_create (&(profiler->user_thread), NULL, ((void*(*)(void*))f), NULL) #define EXIT_THREAD() pthread_exit (NULL); #define WAIT_WRITER_THREAD() do {\ if (CHECK_WRITER_THREAD ()) {\ @@ -688,13 +747,13 @@ make_pthread_profiler_key (void) { #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); +#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, 0664); #define WRITE_BUFFER(b,s) write (profiler->file, (b), (s)) -#define FLUSH_FILE() -#define CLOSE_FILE() close (profiler->file); +#define FLUSH_FILE() fsync (profiler->file) +#define CLOSE_FILE() close (profiler->file) #endif #else @@ -780,7 +839,7 @@ static __thread ProfilerPerThreadData * tls_profiler_per_thread_data; #define PROFILER_FILE_WRITE_BUFFER_SIZE (profiler->write_buffer_size) typedef struct _ProfilerFileWriteBuffer { struct _ProfilerFileWriteBuffer *next; - guint8 buffer []; + guint8 buffer [MONO_ZERO_LEN_ARRAY]; } ProfilerFileWriteBuffer; #define CHECK_PROFILER_ENABLED() do {\ @@ -806,6 +865,7 @@ struct _MonoProfiler { MethodIdMapping *methods; ClassIdMapping *classes; + guint32 loaded_element_next_free_id; GHashTable *loaded_assemblies; GHashTable *loaded_modules; GHashTable *loaded_appdomains; @@ -817,16 +877,17 @@ struct _MonoProfiler { ProfilerStatisticalData *statistical_data_ready; ProfilerStatisticalData *statistical_data_second_buffer; int statistical_call_chain_depth; + MonoProfilerCallChainStrategy statistical_call_chain_strategy; + + ProfilerCodeChunks code_chunks; THREAD_TYPE data_writer_thread; + THREAD_TYPE user_thread; EVENT_TYPE enable_data_writer_event; EVENT_TYPE wake_data_writer_event; EVENT_TYPE done_data_writer_event; gboolean terminate_writer_thread; gboolean writer_thread_terminated; - gboolean detach_writer_thread; - gboolean writer_thread_enabled; - gboolean writer_thread_flush_everything; ProfilerFileWriteBuffer *write_buffers; ProfilerFileWriteBuffer *current_write_buffer; @@ -837,10 +898,10 @@ struct _MonoProfiler { ProfilerHeapShotWriteJob *heap_shot_write_jobs; ProfilerHeapShotHeapBuffers heap; - char *heap_shot_command_file_name; + int command_port; + int dump_next_heap_snapshots; - guint64 heap_shot_command_file_access_time; - gboolean heap_shot_was_signalled; + gboolean heap_shot_was_requested; guint32 garbage_collection_counter; ProfilerExecutableMemoryRegions *executable_regions; @@ -853,6 +914,7 @@ struct _MonoProfiler { gboolean jit_time; gboolean unreachable_objects; gboolean collection_summary; + gboolean report_gc_events; gboolean heap_shot; gboolean track_stack; gboolean track_calls; @@ -863,88 +925,24 @@ struct _MonoProfiler { }; static MonoProfiler *profiler; -#ifndef PLATFORM_WIN32 -#include - -#ifdef MONO_ARCH_USE_SIGACTION -#define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, siginfo_t *info, void *context) -#elif defined(__sparc__) -#define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, void *sigctx) -#else -#define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy) -#endif - -static void -request_heap_snapshot (void) { - profiler->heap_shot_was_signalled = TRUE; - mono_gc_collect (mono_gc_max_generation ()); -} - -static void -SIG_HANDLER_SIGNATURE (gc_request_handler) { - profiler->heap_shot_was_signalled = TRUE; - WRITER_EVENT_RAISE (); -} - -static void -add_gc_request_handler (int signal_number) -{ - struct sigaction sa; - -#ifdef MONO_ARCH_USE_SIGACTION - sa.sa_sigaction = gc_request_handler; - sigemptyset (&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; -#else - sa.sa_handler = gc_request_handler; - sigemptyset (&sa.sa_mask); - sa.sa_flags = 0; -#endif - - g_assert (sigaction (signal_number, &sa, NULL) != -1); -} - static void enable_profiler (void) { profiler->profiler_enabled = TRUE; } +static void flush_everything (void); + static void disable_profiler (void) { profiler->profiler_enabled = FALSE; -} - - - -static void -SIG_HANDLER_SIGNATURE (toggle_handler) { - if (profiler->profiler_enabled) { - profiler->profiler_enabled = FALSE; - } else { - profiler->profiler_enabled = TRUE; - } + flush_everything (); } static void -add_toggle_handler (int signal_number) -{ - struct sigaction sa; - -#ifdef MONO_ARCH_USE_SIGACTION - sa.sa_sigaction = toggle_handler; - sigemptyset (&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; -#else - sa.sa_handler = toggle_handler; - sigemptyset (&sa.sa_mask); - sa.sa_flags = 0; -#endif - - g_assert (sigaction (signal_number, &sa, NULL) != -1); +request_heap_snapshot (void) { + profiler->heap_shot_was_requested = TRUE; + mono_gc_collect (mono_gc_max_generation ()); } -#endif - - #define DEBUG_LOAD_EVENTS 0 #define DEBUG_MAPPING_EVENTS 0 @@ -953,12 +951,18 @@ add_toggle_handler (int signal_number) #define DEBUG_CLASS_BITMAPS 0 #define DEBUG_STATISTICAL_PROFILER 0 #define DEBUG_WRITER_THREAD 0 +#define DEBUG_USER_THREAD 0 #define DEBUG_FILE_WRITES 0 #if (DEBUG_LOGGING_PROFILER || DEBUG_STATISTICAL_PROFILER || DEBUG_HEAP_PROFILER || DEBUG_WRITER_THREAD || DEBUG_FILE_WRITES) #define LOG_WRITER_THREAD(m) printf ("WRITER-THREAD-LOG %s\n", m) #else #define LOG_WRITER_THREAD(m) #endif +#if (DEBUG_LOGGING_PROFILER || DEBUG_STATISTICAL_PROFILER || DEBUG_HEAP_PROFILER || DEBUG_USER_THREAD || DEBUG_FILE_WRITES) +#define LOG_USER_THREAD(m) printf ("USER-THREAD-LOG %s\n", m) +#else +#define LOG_USER_THREAD(m) +#endif #if DEBUG_LOGGING_PROFILER static int event_counter = 0; @@ -1494,6 +1498,8 @@ print_load_event (const char *event_name, GHashTable *table, gpointer item, Load static LoadedElement* loaded_element_load_start (GHashTable *table, gpointer item) { LoadedElement *element = g_new0 (LoadedElement, 1); + element->id = profiler->loaded_element_next_free_id; + profiler->loaded_element_next_free_id ++; #if (DEBUG_LOAD_EVENTS) print_load_event ("LOAD START", table, item, element); #endif @@ -1538,6 +1544,21 @@ loaded_element_unload_end (GHashTable *table, gpointer item) { return element; } +static LoadedElement* +loaded_element_find (GHashTable *table, gpointer item) { + LoadedElement *element = g_hash_table_lookup (table, item); + return element; +} + +static guint32 +loaded_element_get_id (GHashTable *table, gpointer item) { + LoadedElement *element = loaded_element_find (table, item); + if (element != NULL) { + return element->id; + } else { + return 0; + } +} static void loaded_element_destroy (gpointer element) { @@ -1571,7 +1592,7 @@ print_load_event (const char *event_name, GHashTable *table, gpointer item, Load item_name = ""; } - printf ("%s EVENT for %s (%s)\n", event_name, item_info, item_name); + printf ("%s EVENT for %s (%s [id %d])\n", event_name, item_info, item_name, element->id); g_free (item_info); } #endif @@ -1612,7 +1633,7 @@ profiler_heap_shot_object_buffer_new (ProfilerPerThreadData *data) { } static ProfilerHeapShotWriteJob* -profiler_heap_shot_write_job_new (gboolean heap_shot_was_signalled, gboolean dump_heap_data, guint32 collection) { +profiler_heap_shot_write_job_new (gboolean heap_shot_was_requested, gboolean dump_heap_data, guint32 collection) { ProfilerHeapShotWriteJob *job = g_new (ProfilerHeapShotWriteJob, 1); job->next = NULL; job->next_unwritten = NULL; @@ -1641,7 +1662,7 @@ profiler_heap_shot_write_job_new (gboolean heap_shot_was_signalled, gboolean dum job->summary.per_class_data = NULL; } - job->heap_shot_was_signalled = heap_shot_was_signalled; + job->heap_shot_was_requested = heap_shot_was_requested; job->collection = collection; job->dump_heap_data = dump_heap_data; #if DEBUG_HEAP_PROFILER @@ -1819,6 +1840,7 @@ profiler_per_thread_data_new (guint32 buffer_size) data->events = g_new0 (ProfilerEventData, buffer_size); data->next_free_event = data->events; + data->next_unreserved_event = data->events; data->end_event = data->events + (buffer_size - 1); data->first_unwritten_event = data->events; data->first_unmapped_event = data->events; @@ -1866,6 +1888,286 @@ profiler_statistical_data_destroy (ProfilerStatisticalData *data) { g_free (data); } +static ProfilerCodeBufferArray* +profiler_code_buffer_array_new (ProfilerCodeBufferArray *child) { + ProfilerCodeBufferArray *result = g_new0 (ProfilerCodeBufferArray, 1); + if (child == NULL) { + result->level = 0; + } else { + result->level = child->level + 1; + result->number_of_buffers = 1; + result->buffers [0].info.data.sub_buffers = child; + result->buffers [0].start = child->buffers [0].start; + result->buffers [0].end = child->buffers [child->number_of_buffers - 1].end; + } + return result; +} + +static void +profiler_code_buffer_array_destroy (ProfilerCodeBufferArray *buffers) { + if (buffers->level > 0) { + int i; + for (i = 0; i < buffers->number_of_buffers; i++) { + ProfilerCodeBufferArray *sub_buffers = buffers->buffers [i].info.data.sub_buffers; + profiler_code_buffer_array_destroy (sub_buffers); + } + } + g_free (buffers); +} + +static gboolean +profiler_code_buffer_array_is_full (ProfilerCodeBufferArray *buffers) { + while (buffers->level > 0) { + ProfilerCodeBufferArray *next; + if (buffers->number_of_buffers < PROFILER_CODE_BUFFER_ARRAY_SIZE) { + return FALSE; + } + next = buffers->buffers [PROFILER_CODE_BUFFER_ARRAY_SIZE - 1].info.data.sub_buffers; + if (next->level < (buffers->level - 1)) { + return FALSE; + } + buffers = next; + } + return (buffers->number_of_buffers == PROFILER_CODE_BUFFER_ARRAY_SIZE); +} + +static ProfilerCodeBufferArray* +profiler_code_buffer_add (ProfilerCodeBufferArray *buffers, gpointer *buffer, int size, MonoProfilerCodeBufferType type, void *data) { + if (buffers == NULL) { + buffers = profiler_code_buffer_array_new (NULL); + } + + if (profiler_code_buffer_array_is_full (buffers)) { + ProfilerCodeBufferArray *new_slot = profiler_code_buffer_add (NULL, buffer, size, type, data); + buffers = profiler_code_buffer_array_new (buffers); + buffers->buffers [buffers->number_of_buffers].info.data.sub_buffers = new_slot; + buffers->buffers [buffers->number_of_buffers].start = new_slot->buffers [0].start; + buffers->buffers [buffers->number_of_buffers].end = new_slot->buffers [new_slot->number_of_buffers - 1].end; + buffers->number_of_buffers ++; + } else if (buffers->level > 0) { + ProfilerCodeBufferArray *new_slot = profiler_code_buffer_add (buffers->buffers [buffers->number_of_buffers - 1].info.data.sub_buffers, buffer, size, type, data); + buffers->buffers [buffers->number_of_buffers - 1].info.data.sub_buffers = new_slot; + buffers->buffers [buffers->number_of_buffers - 1].start = new_slot->buffers [0].start; + buffers->buffers [buffers->number_of_buffers - 1].end = new_slot->buffers [new_slot->number_of_buffers - 1].end; + } else { + buffers->buffers [buffers->number_of_buffers].start = buffer; + buffers->buffers [buffers->number_of_buffers].end = (((guint8*) buffer) + size); + buffers->buffers [buffers->number_of_buffers].info.type = type; + switch (type) { + case MONO_PROFILER_CODE_BUFFER_UNKNOWN: + buffers->buffers [buffers->number_of_buffers].info.data.data = NULL; + break; + case MONO_PROFILER_CODE_BUFFER_METHOD: + buffers->buffers [buffers->number_of_buffers].info.data.method = data; + break; + default: + buffers->buffers [buffers->number_of_buffers].info.type = MONO_PROFILER_CODE_BUFFER_UNKNOWN; + buffers->buffers [buffers->number_of_buffers].info.data.data = NULL; + } + buffers->number_of_buffers ++; + } + return buffers; +} + +static ProfilerCodeBuffer* +profiler_code_buffer_find (ProfilerCodeBufferArray *buffers, gpointer *address) { + if (buffers != NULL) { + ProfilerCodeBuffer *result = NULL; + do { + int low = 0; + int high = buffers->number_of_buffers - 1; + + while (high != low) { + int middle = low + ((high - low) >> 1); + + if ((guint8*) address < (guint8*) buffers->buffers [low].start) { + return NULL; + } + if ((guint8*) address >= (guint8*) buffers->buffers [high].end) { + return NULL; + } + + if ((guint8*) address < (guint8*) buffers->buffers [middle].start) { + high = middle - 1; + if (high < low) { + high = low; + } + } else if ((guint8*) address >= (guint8*) buffers->buffers [middle].end) { + low = middle + 1; + if (low > high) { + low = high; + } + } else { + high = middle; + low = middle; + } + } + + if (((guint8*) address >= (guint8*) buffers->buffers [low].start) && ((guint8*) address < (guint8*) buffers->buffers [low].end)) { + if (buffers->level == 0) { + result = & (buffers->buffers [low]); + } else { + buffers = buffers->buffers [low].info.data.sub_buffers; + } + } else { + return NULL; + } + } while (result == NULL); + return result; + } else { + return NULL; + } +} + +static void +profiler_code_chunk_initialize (ProfilerCodeChunk *chunk, gpointer memory, gsize size) { + chunk->buffers = profiler_code_buffer_array_new (NULL); + chunk->destroyed = FALSE; + chunk->start = memory; + chunk->end = ((guint8*)memory) + size; +} + +static void +profiler_code_chunk_cleanup (ProfilerCodeChunk *chunk) { + if (chunk->buffers != NULL) { + profiler_code_buffer_array_destroy (chunk->buffers); + chunk->buffers = NULL; + } + chunk->start = NULL; + chunk->end = NULL; +} + +static void +profiler_code_chunks_initialize (ProfilerCodeChunks *chunks) { + chunks->capacity = 32; + chunks->chunks = g_new0 (ProfilerCodeChunk, 32); + chunks->number_of_chunks = 0; +} + +static void +profiler_code_chunks_cleanup (ProfilerCodeChunks *chunks) { + int i; + for (i = 0; i < chunks->number_of_chunks; i++) { + profiler_code_chunk_cleanup (& (chunks->chunks [i])); + } + chunks->capacity = 0; + chunks->number_of_chunks = 0; + g_free (chunks->chunks); + chunks->chunks = NULL; +} + +static int +compare_code_chunks (const void* c1, const void* c2) { + ProfilerCodeChunk *chunk1 = (ProfilerCodeChunk*) c1; + ProfilerCodeChunk *chunk2 = (ProfilerCodeChunk*) c2; + return ((guint8*) chunk1->end < (guint8*) chunk2->start) ? -1 : (((guint8*) chunk1->start >= (guint8*) chunk2->end) ? 1 : 0); +} + +static int +compare_address_and_code_chunk (const void* a, const void* c) { + gpointer address = (gpointer) a; + ProfilerCodeChunk *chunk = (ProfilerCodeChunk*) c; + return ((guint8*) address < (guint8*) chunk->start) ? -1 : (((guint8*) address >= (guint8*) chunk->end) ? 1 : 0); +} + +static void +profiler_code_chunks_sort (ProfilerCodeChunks *chunks) { + qsort (chunks->chunks, chunks->number_of_chunks, sizeof (ProfilerCodeChunk), compare_code_chunks); +} + +static ProfilerCodeChunk* +profiler_code_chunk_find (ProfilerCodeChunks *chunks, gpointer address) { + return bsearch (address, chunks->chunks, chunks->number_of_chunks, sizeof (ProfilerCodeChunk), compare_address_and_code_chunk); +} + +static ProfilerCodeChunk* +profiler_code_chunk_new (ProfilerCodeChunks *chunks, gpointer memory, gsize size) { + ProfilerCodeChunk *result; + + if (chunks->number_of_chunks == chunks->capacity) { + ProfilerCodeChunk *new_chunks = g_new0 (ProfilerCodeChunk, chunks->capacity * 2); + memcpy (new_chunks, chunks->chunks, chunks->capacity * sizeof (ProfilerCodeChunk)); + chunks->capacity *= 2; + g_free (chunks->chunks); + chunks->chunks = new_chunks; + } + + result = & (chunks->chunks [chunks->number_of_chunks]); + chunks->number_of_chunks ++; + profiler_code_chunk_initialize (result, memory, size); + profiler_code_chunks_sort (chunks); + return result; +} + +static int +profiler_code_chunk_to_index (ProfilerCodeChunks *chunks, ProfilerCodeChunk *chunk) { + return (int) (chunk - chunks->chunks); +} + +static void +profiler_code_chunk_remove (ProfilerCodeChunks *chunks, ProfilerCodeChunk *chunk) { + int index = profiler_code_chunk_to_index (chunks, chunk); + + profiler_code_chunk_cleanup (chunk); + if ((index >= 0) && (index < chunks->number_of_chunks)) { + memmove (chunk, chunk + 1, (chunks->number_of_chunks - index) * sizeof (ProfilerCodeChunk)); + } +} + +/* This assumes the profiler lock is held */ +static ProfilerCodeBuffer* +profiler_code_buffer_from_address (MonoProfiler *prof, gpointer address) { + ProfilerCodeChunks *chunks = & (prof->code_chunks); + + ProfilerCodeChunk *chunk = profiler_code_chunk_find (chunks, address); + if (chunk != NULL) { + return profiler_code_buffer_find (chunk->buffers, address); + } else { + return NULL; + } +} + +static void +profiler_code_chunk_new_callback (MonoProfiler *prof, gpointer address, int size) { + ProfilerCodeChunks *chunks = & (prof->code_chunks); + + if (prof->code_chunks.chunks != NULL) { + LOCK_PROFILER (); + profiler_code_chunk_new (chunks, address, size); + UNLOCK_PROFILER (); + } +} + +static void +profiler_code_chunk_destroy_callback (MonoProfiler *prof, gpointer address) { + ProfilerCodeChunks *chunks = & (prof->code_chunks); + ProfilerCodeChunk *chunk; + + if (prof->code_chunks.chunks != NULL) { + LOCK_PROFILER (); + chunk = profiler_code_chunk_find (chunks, address); + if (chunk != NULL) { + profiler_code_chunk_remove (chunks, chunk); + } + UNLOCK_PROFILER (); + } +} + +static void +profiler_code_buffer_new_callback (MonoProfiler *prof, gpointer address, int size, MonoProfilerCodeBufferType type, void *data) { + ProfilerCodeChunks *chunks = & (prof->code_chunks); + ProfilerCodeChunk *chunk; + + if (prof->code_chunks.chunks != NULL) { + LOCK_PROFILER (); + chunk = profiler_code_chunk_find (chunks, address); + if (chunk != NULL) { + chunk->buffers = profiler_code_buffer_add (chunk->buffers, address, size, type, data); + } + UNLOCK_PROFILER (); + } +} + static void profiler_add_write_buffer (void) { if (profiler->current_write_buffer->next == NULL) { @@ -1901,6 +2203,9 @@ profiler_free_write_buffers (void) { profiler->current_write_position ++;\ } while (0) +#if (DEBUG_FILE_WRITES) +static int bytes_written = 0; +#endif static void write_current_block (guint16 code) { @@ -1930,13 +2235,15 @@ write_current_block (guint16 code) { header [9] = (counter_delta >> 24) & 0xff; #if (DEBUG_FILE_WRITES) - printf ("write_current_block: writing header (code %d)\n", code); + printf ("write_current_block: writing header (code %d) at offset %d\n", code, bytes_written); + bytes_written += 10; #endif WRITE_BUFFER (& (header [0]), 10); while ((current_buffer != NULL) && (profiler->full_write_buffers > 0)) { #if (DEBUG_FILE_WRITES) printf ("write_current_block: writing buffer (size %d)\n", PROFILER_FILE_WRITE_BUFFER_SIZE); + bytes_written += PROFILER_FILE_WRITE_BUFFER_SIZE; #endif WRITE_BUFFER (& (current_buffer->buffer [0]), PROFILER_FILE_WRITE_BUFFER_SIZE); profiler->full_write_buffers --; @@ -1945,12 +2252,13 @@ write_current_block (guint16 code) { if (profiler->current_write_position > 0) { #if (DEBUG_FILE_WRITES) printf ("write_current_block: writing last buffer (size %d)\n", profiler->current_write_position); + bytes_written += profiler->current_write_position; #endif WRITE_BUFFER (& (current_buffer->buffer [0]), profiler->current_write_position); } FLUSH_FILE (); #if (DEBUG_FILE_WRITES) - printf ("write_current_block: buffers flushed\n"); + printf ("write_current_block: buffers flushed (file size %d)\n", bytes_written); #endif profiler->current_write_buffer = profiler->write_buffers; @@ -2002,6 +2310,9 @@ write_directives_block (gboolean start) { if (profiler->action_flags.allocations_carry_id) { write_uint32 (MONO_PROFILER_DIRECTIVE_ALLOCATIONS_CARRY_ID); } + write_uint32 (MONO_PROFILER_DIRECTIVE_LOADED_ELEMENTS_CARRY_ID); + write_uint32 (MONO_PROFILER_DIRECTIVE_CLASSES_CARRY_ASSEMBLY_ID); + write_uint32 (MONO_PROFILER_DIRECTIVE_METHODS_CARRY_WRAPPER_FLAG); } write_uint32 (MONO_PROFILER_DIRECTIVE_END); @@ -2234,12 +2545,37 @@ profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job) { } static void -write_element_load_block (LoadedElement *element, guint8 kind, gsize thread_id) { +write_element_load_block (LoadedElement *element, guint8 kind, gsize thread_id, gpointer item) { WRITE_BYTE (kind); write_uint64 (element->load_start_counter); write_uint64 (element->load_end_counter); write_uint64 (thread_id); + write_uint32 (element->id); write_string (element->name); + if (kind & MONO_PROFILER_LOADED_EVENT_ASSEMBLY) { + MonoImage *image = mono_assembly_get_image ((MonoAssembly*) item); + MonoAssemblyName aname; + if (mono_assembly_fill_assembly_name (image, &aname)) { + write_string (aname.name); + write_uint32 (aname.major); + write_uint32 (aname.minor); + write_uint32 (aname.build); + write_uint32 (aname.revision); + write_string (aname.culture && *aname.culture? aname.culture: "neutral"); + write_string (aname.public_key_token [0] ? (char *)aname.public_key_token : "null"); + /* Retargetable flag */ + write_uint32 ((aname.flags & 0x00000100) ? 1 : 0); + } else { + write_string ("UNKNOWN"); + write_uint32 (0); + write_uint32 (0); + write_uint32 (0); + write_uint32 (0); + write_string ("neutral"); + write_string ("null"); + write_uint32 (0); + } + } write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_LOADED); element->load_written = TRUE; } @@ -2250,6 +2586,7 @@ write_element_unload_block (LoadedElement *element, guint8 kind, gsize thread_id write_uint64 (element->unload_start_counter); write_uint64 (element->unload_end_counter); write_uint64 (thread_id); + write_uint32 (element->id); write_string (element->name); write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED); element->unload_written = TRUE; @@ -2283,7 +2620,11 @@ write_mapping_block (gsize thread_id) { write_uint64 (thread_id); for (current_class = profiler->classes->unwritten; current_class != NULL; current_class = current_class->next_unwritten) { + MonoImage *image = mono_class_get_image (current_class->klass); + MonoAssembly *assembly = mono_image_get_assembly (image); + guint32 assembly_id = loaded_element_get_id (profiler->loaded_assemblies, assembly); write_uint32 (current_class->id); + write_uint32 (assembly_id); write_string (current_class->name); #if (DEBUG_MAPPING_EVENTS) printf ("mapping CLASS (%d => %s)\n", current_class->id, current_class->name); @@ -2301,6 +2642,11 @@ write_mapping_block (gsize thread_id) { g_assert (class_element != NULL); write_uint32 (current_method->id); write_uint32 (class_element->id); + if (method->wrapper_type != 0) { + write_uint32 (1); + } else { + write_uint32 (0); + } 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); @@ -2350,13 +2696,9 @@ rewrite_last_written_stack (ProfilerThreadStack *stack) { write_uint32 (0); write_uint32 (i); - printf ("rewrite_last_written_stack START (%d %p)\n", i, stack); - while (i > 0) { i--; write_uint32 (thread_stack_written_frame_at_index (stack, i)); - - printf ("rewrite_last_written_stack ELEMENT (%d %d)\n", i, thread_stack_written_frame_at_index (stack, i)); } } @@ -2472,6 +2814,15 @@ write_event (ProfilerEventData *event, ProfilerPerThreadData *data) { write_event_value_extension_2 = TRUE; next ++; } + } else if (event->code == MONO_PROFILER_EVENT_CLASS_MONITOR) { + g_assert (next->code == MONO_PROFILER_EVENT_OBJECT_MONITOR); + + MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT); + event_value_extension_1 = next->value; + write_event_value_extension_1 = TRUE; + event_value_extension_2 = GPOINTER_TO_UINT (next->data.address); + write_event_value_extension_2 = TRUE; + next ++; } else { MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT); } @@ -2526,8 +2877,11 @@ write_thread_data_block (ProfilerPerThreadData *data) { write_uint64 (data->start_event_counter); - /* Make sure that stack sections can be fully reconstructed even reading only one block */ - rewrite_last_written_stack (&(data->stack)); + /* If we are tracking the stack, make sure that stack sections */ + /* can be fully reconstructed even reading only one block */ + if (profiler->action_flags.track_stack) { + rewrite_last_written_stack (&(data->stack)); + } while (start < end) { start = write_event (start, data); @@ -2553,6 +2907,7 @@ profiler_executable_memory_region_new (gpointer *start, gpointer *end, guint32 f result->is_new = TRUE; result->file = NULL; + result->file_region_reference = NULL; result->symbols_capacity = id; result->symbols_count = id; result->symbols = NULL; @@ -2565,14 +2920,17 @@ executable_file_close (ProfilerExecutableMemoryRegionData *region); static void profiler_executable_memory_region_destroy (ProfilerExecutableMemoryRegionData *data) { - if (data->file_name != NULL) { - g_free (data->file_name); + if (data->file != NULL) { + executable_file_close (data); + data->file = NULL; } if (data->symbols != NULL) { g_free (data->symbols); + data->symbols = NULL; } - if (data->file != NULL) { - executable_file_close (data); + if (data->file_name != NULL) { + g_free (data->file_name); + data->file_name = NULL; } g_free (data); } @@ -2656,19 +3014,35 @@ append_region (ProfilerExecutableMemoryRegions *regions, gpointer *start, gpoint regions->next_id ++; } +static gboolean +regions_are_equivalent (ProfilerExecutableMemoryRegionData *region1, ProfilerExecutableMemoryRegionData *region2) { + if ((region1->start == region2->start) && + (region1->end == region2->end) && + (region1->file_offset == region2->file_offset) && + ! strcmp (region1->file_name, region2->file_name)) { + return TRUE; + } else { + return FALSE; + } +} + +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 restore_old_regions (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)) { + for (new_i = 0; new_i < new_regions->regions_count; new_i++) { + ProfilerExecutableMemoryRegionData *new_region = new_regions->regions [new_i]; + for (old_i = 0; old_i < old_regions->regions_count; old_i++) { + ProfilerExecutableMemoryRegionData *old_region = old_regions->regions [old_i]; + if ( regions_are_equivalent (old_region, new_region)) { new_regions->regions [new_i] = old_region; old_regions->regions [old_i] = new_region; @@ -2679,16 +3053,49 @@ restore_old_regions (ProfilerExecutableMemoryRegions *old_regions, ProfilerExecu } } -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) { + if (regions->regions_count > 1) { + int i; + + qsort (regions->regions, regions->regions_count, sizeof (ProfilerExecutableMemoryRegionData *), compare_regions); + + i = 1; + while (i < regions->regions_count) { + ProfilerExecutableMemoryRegionData *current_region = regions->regions [i]; + ProfilerExecutableMemoryRegionData *previous_region = regions->regions [i - 1]; + + if (regions_are_equivalent (previous_region, current_region)) { + int j; + + if (! current_region->is_new) { + profiler_executable_memory_region_destroy (previous_region); + regions->regions [i - 1] = current_region; + } else { + profiler_executable_memory_region_destroy (current_region); + } + + for (j = i + 1; j < regions->regions_count; j++) { + regions->regions [j - 1] = regions->regions [j]; + } + + regions->regions_count --; + } else { + i++; + } + } + } } static void -sort_regions (ProfilerExecutableMemoryRegions *regions) { - qsort (regions->regions, regions->regions_count, sizeof (ProfilerExecutableMemoryRegionData *), compare_regions); +fix_region_references (ProfilerExecutableMemoryRegions *regions) { + int i; + for (i = 0; i < regions->regions_count; i++) { + ProfilerExecutableMemoryRegionData *region = regions->regions [i]; + if (region->file_region_reference != NULL) { + region->file_region_reference->region = region; + } + } } static void @@ -2705,150 +3112,176 @@ executable_file_add_region_reference (ProfilerExecutableFile *file, ProfilerExec section_region->region = region; section_region->section_address = (gpointer) section_header->sh_addr; section_region->section_offset = section_header->sh_offset; + region->file_region_reference = section_region; } } } -static ProfilerExecutableFile* -executable_file_open (ProfilerExecutableMemoryRegionData *region) { - ProfilerExecutableFiles *files = & (profiler->executable_files); - ProfilerExecutableFile *file = (ProfilerExecutableFile*) g_hash_table_lookup (files->table, region->file_name); - if (file == NULL) { - guint16 test = 0x0102; - struct stat stat_buffer; - int symtab_index = 0; - int strtab_index = 0; - int dynsym_index = 0; - int dynstr_index = 0; - ElfHeader *header; - guint8 *section_headers; - int section_index; - int strings_index; - - file = g_new0 (ProfilerExecutableFile, 1); - region->file = file; - file->reference_count ++; - - file->fd = open (region->file_name, O_RDONLY); - if (file->fd == -1) { - //g_warning ("Cannot open file '%s': '%s'", region->file_name, strerror (errno)); - return file; - } else { - if (fstat (file->fd, &stat_buffer) != 0) { - //g_warning ("Cannot stat file '%s': '%s'", region->file_name, strerror (errno)); - return file; - } else { - size_t region_length = ((guint8*)region->end) - ((guint8*)region->start); - file->length = stat_buffer.st_size; - - if (file->length == region_length) { - file->data = region->start; - close (file->fd); - file->fd = -1; +static gboolean check_elf_header (ElfHeader* header) { + guint16 test = 0x0102; + + if ((header->e_ident [EI_MAG0] != 0x7f) || (header->e_ident [EI_MAG1] != 'E') || + (header->e_ident [EI_MAG2] != 'L') || (header->e_ident [EI_MAG3] != 'F')) { + return FALSE; + } + + if (sizeof (gsize) == 4) { + if (header->e_ident [EI_CLASS] != ELF_CLASS_32) { + g_warning ("Class is not ELF_CLASS_32 with gsize size %d", (int) sizeof (gsize)); + return FALSE; + } + } else if (sizeof (gsize) == 8) { + if (header->e_ident [EI_CLASS] != ELF_CLASS_64) { + g_warning ("Class is not ELF_CLASS_64 with gsize size %d", (int) sizeof (gsize)); + return FALSE; + } + } else { + g_warning ("Absurd gsize size %d", (int) sizeof (gsize)); + return FALSE; + } + + if ((*(guint8*)(&test)) == 0x01) { + if (header->e_ident [EI_DATA] != ELF_DATA_MSB) { + g_warning ("Data is not ELF_DATA_MSB with first test byte 0x01"); + return FALSE; + } + } else if ((*(guint8*)(&test)) == 0x02) { + if (header->e_ident [EI_DATA] != ELF_DATA_LSB) { + g_warning ("Data is not ELF_DATA_LSB with first test byte 0x02"); + return FALSE; + } + } else { + g_warning ("Absurd test byte value"); + return FALSE; + } + + return TRUE; +} + +static gboolean check_elf_file (int fd) { + void *header = malloc (sizeof (ElfHeader)); + ssize_t read_result = read (fd, header, sizeof (ElfHeader)); + gboolean result; + + if (read_result != sizeof (ElfHeader)) { + result = FALSE; + } else { + result = check_elf_header ((ElfHeader*) header); + } + + free (header); + return result; +} + +static ProfilerExecutableFile* +executable_file_open (ProfilerExecutableMemoryRegionData *region) { + ProfilerExecutableFiles *files = & (profiler->executable_files); + ProfilerExecutableFile *file = region->file; + + if (file == NULL) { + file = (ProfilerExecutableFile*) g_hash_table_lookup (files->table, region->file_name); + + if (file == NULL) { + struct stat stat_buffer; + int symtab_index = 0; + int strtab_index = 0; + int dynsym_index = 0; + int dynstr_index = 0; + ElfHeader *header; + guint8 *section_headers; + int section_index; + int strings_index; + + file = g_new0 (ProfilerExecutableFile, 1); + region->file = file; + g_hash_table_insert (files->table, region->file_name, file); + file->reference_count ++; + file->next_new_file = files->new_files; + files->new_files = file; + + file->fd = open (region->file_name, O_RDONLY); + if (file->fd == -1) { + //g_warning ("Cannot open file '%s': '%s'", region->file_name, strerror (errno)); + return file; + } else { + if (fstat (file->fd, &stat_buffer) != 0) { + //g_warning ("Cannot stat file '%s': '%s'", region->file_name, strerror (errno)); + return file; + } else if (! check_elf_file (file->fd)) { + return file; } else { - file->data = mmap (NULL, file->length, PROT_READ, MAP_PRIVATE, file->fd, 0); + size_t region_length = ((guint8*)region->end) - ((guint8*)region->start); + file->length = stat_buffer.st_size; - if (file->data == MAP_FAILED) { + if (file->length == region_length) { + file->data = region->start; close (file->fd); - //g_warning ("Cannot map file '%s': '%s'", region->file_name, strerror (errno)); - file->data = NULL; - return file; + file->fd = -1; + } else { + file->data = mmap (NULL, file->length, PROT_READ, MAP_PRIVATE, file->fd, 0); + + if (file->data == MAP_FAILED) { + close (file->fd); + //g_warning ("Cannot map file '%s': '%s'", region->file_name, strerror (errno)); + file->data = NULL; + return file; + } } } } - } - - header = (ElfHeader*) file->data; - - if ((header->e_ident [EI_MAG0] != 0x7f) || (header->e_ident [EI_MAG1] != 'E') || - (header->e_ident [EI_MAG2] != 'L') || (header->e_ident [EI_MAG3] != 'F')) { - return file; - } - - if (sizeof (gsize) == 4) { - if (header->e_ident [EI_CLASS] != ELF_CLASS_32) { - g_warning ("Class is not ELF_CLASS_32 with gsize size %d", (int) sizeof (gsize)); - return file; - } - } else if (sizeof (gsize) == 8) { - if (header->e_ident [EI_CLASS] != ELF_CLASS_64) { - g_warning ("Class is not ELF_CLASS_64 with gsize size %d", (int) sizeof (gsize)); - return file; - } - } else { - g_warning ("Absurd gsize size %d", (int) sizeof (gsize)); - return file; - } - - if ((*(guint8*)(&test)) == 0x01) { - if (header->e_ident [EI_DATA] != ELF_DATA_MSB) { - g_warning ("Data is not ELF_DATA_MSB with first test byte 0x01"); - return file; + + /* OK, this is a usable elf file, and we mmapped it... */ + header = (ElfHeader*) file->data; + file->header = header; + section_headers = file->data + file->header->e_shoff; + file->main_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * header->e_shstrndx)))->sh_offset); + + for (section_index = 0; section_index < header->e_shnum; section_index ++) { + ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index)); + + if (section_header->sh_type == ELF_SHT_SYMTAB) { + symtab_index = section_index; + } else if (section_header->sh_type == ELF_SHT_DYNSYM) { + dynsym_index = section_index; + } else if (section_header->sh_type == ELF_SHT_STRTAB) { + if (! strcmp (file->main_string_table + section_header->sh_name, ".strtab")) { + strtab_index = section_index; + } else if (! strcmp (file->main_string_table + section_header->sh_name, ".dynstr")) { + dynstr_index = section_index; + } + } } - } else if ((*(guint8*)(&test)) == 0x02) { - if (header->e_ident [EI_DATA] != ELF_DATA_LSB) { - g_warning ("Data is not ELF_DATA_LSB with first test byte 0x02"); - return file; + + if ((symtab_index != 0) && (strtab_index != 0)) { + section_index = symtab_index; + strings_index = strtab_index; + } else if ((dynsym_index != 0) && (dynstr_index != 0)) { + section_index = dynsym_index; + strings_index = dynstr_index; + } else { + section_index = 0; + strings_index = 0; } - } else { - g_warning ("Absurd test byte value"); - return file; - } - - /* OK, this is a usable elf file... */ - file->header = header; - section_headers = file->data + header->e_shoff; - file->main_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * header->e_shstrndx)))->sh_offset); - - for (section_index = 0; section_index < header->e_shnum; section_index ++) { - ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index)); - if (section_header->sh_type == ELF_SHT_SYMTAB) { - symtab_index = section_index; - } else if (section_header->sh_type == ELF_SHT_DYNSYM) { - dynsym_index = section_index; - } else if (section_header->sh_type == ELF_SHT_STRTAB) { - if (! strcmp (file->main_string_table + section_header->sh_name, ".strtab")) { - strtab_index = section_index; - } else if (! strcmp (file->main_string_table + section_header->sh_name, ".dynstr")) { - dynstr_index = section_index; - } + if (section_index != 0) { + ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index)); + file->symbol_size = section_header->sh_entsize; + file->symbols_count = (guint32) (section_header->sh_size / section_header->sh_entsize); + file->symbols_start = file->data + section_header->sh_offset; + file->symbols_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * strings_index)))->sh_offset); } - } - - if ((symtab_index != 0) && (strtab_index != 0)) { - section_index = symtab_index; - strings_index = strtab_index; - } else if ((dynsym_index != 0) && (dynstr_index != 0)) { - section_index = dynsym_index; - strings_index = dynstr_index; + + file->section_regions = g_new0 (ProfilerExecutableFileSectionRegion, file->header->e_shnum); } else { - section_index = 0; - strings_index = 0; - } - - if (section_index != 0) { - ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index)); - file->symbol_size = section_header->sh_entsize; - file->symbols_count = (guint32) (section_header->sh_size / section_header->sh_entsize); - file->symbols_start = file->data + section_header->sh_offset; - file->symbols_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * strings_index)))->sh_offset); + region->file = file; + file->reference_count ++; } - - file->section_regions = g_new0 (ProfilerExecutableFileSectionRegion, file->header->e_shnum); - } else { - region->file = file; - file->reference_count ++; } if (file->header != NULL) { executable_file_add_region_reference (file, region); } - if (file->next_new_file == NULL) { - file->next_new_file = files->new_files; - files->new_files = file; - } return file; } @@ -2866,6 +3299,7 @@ executable_file_free (ProfilerExecutableFile* file) { } if (file->section_regions != NULL) { g_free (file->section_regions); + file->section_regions = NULL; } g_free (file); } @@ -2874,6 +3308,12 @@ static void executable_file_close (ProfilerExecutableMemoryRegionData *region) { region->file->reference_count --; + if ((region->file_region_reference != NULL) && (region->file_region_reference->region == region)) { + region->file_region_reference->region = NULL; + region->file_region_reference->section_address = 0; + region->file_region_reference->section_offset = 0; + } + if (region->file->reference_count <= 0) { ProfilerExecutableFiles *files = & (profiler->executable_files); g_hash_table_remove (files->table, region->file_name); @@ -3034,6 +3474,7 @@ executable_memory_region_find_symbol (ProfilerExecutableMemoryRegionData *region //FIXME: make also Win32 and BSD variants #define MAPS_BUFFER_SIZE 4096 +#define MAPS_FILENAME_SIZE 2048 static gboolean update_regions_buffer (int fd, char *buffer) { @@ -3112,13 +3553,12 @@ const char *map_line_parser_state [] = { }; static char* -parse_map_line (ProfilerExecutableMemoryRegions *regions, int fd, char *buffer, char *current) { +parse_map_line (ProfilerExecutableMemoryRegions *regions, int fd, char *buffer, char *filename, 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; + int filename_index = 0; gboolean is_executable = FALSE; gboolean done = FALSE; @@ -3182,22 +3622,31 @@ parse_map_line (ProfilerExecutableMemoryRegions *regions, int fd, char *buffer, case MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME: if ((c == '/') || (c == '[')) { state = MAP_LINE_PARSER_STATE_FILENAME; - start_filename = current; + filename [filename_index] = *current; + filename_index ++; } 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; + if (filename_index < MAPS_FILENAME_SIZE) { + if (c == '\n') { + state = MAP_LINE_PARSER_STATE_DONE; + done = TRUE; + filename [filename_index] = 0; + } else { + filename [filename_index] = *current; + filename_index ++; + } + } else { + filename [filename_index] = 0; + g_warning ("ELF filename too long: \"%s\"...\n", filename); } 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); + filename [filename_index] = 0; + append_region (regions, (gpointer) start_address, (gpointer) end_address, offset, filename); } return current; case MAP_LINE_PARSER_STATE_INVALID: @@ -3221,6 +3670,7 @@ parse_map_line (ProfilerExecutableMemoryRegions *regions, int fd, char *buffer, static gboolean scan_process_regions (ProfilerExecutableMemoryRegions *regions) { char *buffer; + char *filename; char *current; int fd; @@ -3230,13 +3680,15 @@ scan_process_regions (ProfilerExecutableMemoryRegions *regions) { } buffer = malloc (MAPS_BUFFER_SIZE); + filename = malloc (MAPS_FILENAME_SIZE); update_regions_buffer (fd, buffer); current = buffer; while (current != NULL) { - current = parse_map_line (regions, fd, buffer, current); + current = parse_map_line (regions, fd, buffer, filename, current); } free (buffer); + free (filename); close (fd); return TRUE; @@ -3261,8 +3713,9 @@ refresh_memory_regions (void) { LOG_WRITER_THREAD ("Refreshing memory regions..."); scan_process_regions (new_regions); - restore_old_regions (old_regions, new_regions); sort_regions (new_regions); + restore_old_regions (old_regions, new_regions); + fix_region_references (new_regions); LOG_WRITER_THREAD ("Refreshed memory regions."); LOG_WRITER_THREAD ("Building symbol tables..."); @@ -3334,11 +3787,11 @@ refresh_memory_regions (void) { } static gboolean -write_statistical_hit (MonoDomain *domain, gpointer address, gboolean regions_refreshed) { - MonoJitInfo *ji = (domain != NULL) ? mono_jit_info_table_find (domain, (char*) address) : NULL; +write_statistical_hit (gpointer address, gboolean regions_refreshed) { + ProfilerCodeBuffer *code_buffer = profiler_code_buffer_from_address (profiler, address); - if (ji != NULL) { - MonoMethod *method = mono_jit_info_get_method (ji); + if ((code_buffer != NULL) && (code_buffer->info.type == MONO_PROFILER_CODE_BUFFER_METHOD)) { + MonoMethod *method = code_buffer->info.data.method; MethodIdMappingElement *element = method_id_mapping_element_get (method); if (element != NULL) { @@ -3410,7 +3863,6 @@ flush_all_mappings (void); static void write_statistical_data_block (ProfilerStatisticalData *data) { - MonoThread *current_thread = mono_thread_current (); int start_index = data->first_unwritten_index; int end_index = data->next_free_index; gboolean regions_refreshed = FALSE; @@ -3436,7 +3888,7 @@ write_statistical_data_block (ProfilerStatisticalData *data) { ProfilerStatisticalHit hit = data->hits [base_index]; int callers_count; - regions_refreshed = write_statistical_hit ((current_thread != NULL) ? hit.domain : NULL, hit.address, regions_refreshed); + regions_refreshed = write_statistical_hit (hit.address, regions_refreshed); base_index ++; for (callers_count = 0; callers_count < call_chain_depth; callers_count ++) { @@ -3452,7 +3904,7 @@ write_statistical_data_block (ProfilerStatisticalData *data) { for (callers_count = 0; callers_count < call_chain_depth; callers_count ++) { hit = data->hits [base_index + callers_count]; if (hit.address != NULL) { - regions_refreshed = write_statistical_hit ((current_thread != NULL) ? hit.domain : NULL, hit.address, regions_refreshed); + regions_refreshed = write_statistical_hit (hit.address, regions_refreshed); } else { break; } @@ -3550,6 +4002,7 @@ flush_full_event_data_buffer (ProfilerPerThreadData *data) { write_thread_data_block (data); data->next_free_event = data->events; + data->next_unreserved_event = data->events; data->first_unwritten_event = data->events; data->first_unmapped_event = data->events; MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter); @@ -3559,14 +4012,17 @@ flush_full_event_data_buffer (ProfilerPerThreadData *data) { } /* The ">=" operator is intentional, to leave one spare slot for "extended values" */ -#define RESERVE_EVENTS(d,e,count) {\ - if ((d)->next_free_event >= ((d)->end_event - (count))) {\ +#define RESERVE_EVENTS(d,e,count) do {\ + if ((d)->next_unreserved_event >= ((d)->end_event - (count))) {\ flush_full_event_data_buffer (d);\ }\ - (e) = (d)->next_free_event;\ - (d)->next_free_event += (count);\ + (e) = (d)->next_unreserved_event;\ + (d)->next_unreserved_event += (count);\ } while (0) #define GET_NEXT_FREE_EVENT(d,e) RESERVE_EVENTS ((d),(e),1) +#define COMMIT_RESERVED_EVENTS(d) do {\ + data->next_free_event = data->next_unreserved_event;\ +} while (0) static void flush_everything (void) { @@ -3579,21 +4035,6 @@ flush_everything (void) { write_statistical_data_block (profiler->statistical_data); } -/* This assumes the lock is held: it just offloads the work to the writer thread. */ -static void -writer_thread_flush_everything (void) { - if (CHECK_WRITER_THREAD ()) { - profiler->writer_thread_flush_everything = TRUE; - LOG_WRITER_THREAD ("writer_thread_flush_everything: raising event..."); - WRITER_EVENT_RAISE (); - LOG_WRITER_THREAD ("writer_thread_flush_everything: waiting event..."); - WRITER_EVENT_DONE_WAIT (); - LOG_WRITER_THREAD ("writer_thread_flush_everything: got event."); - } else { - LOG_WRITER_THREAD ("writer_thread_flush_everything: no thread."); - } -} - #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) { @@ -3610,7 +4051,7 @@ appdomain_end_load (MonoProfiler *profiler, MonoDomain *domain, int result) { 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 ()); + write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID (), domain); UNLOCK_PROFILER (); } @@ -3618,7 +4059,7 @@ static void appdomain_start_unload (MonoProfiler *profiler, MonoDomain *domain) { LOCK_PROFILER (); loaded_element_unload_start (profiler->loaded_appdomains, domain); - writer_thread_flush_everything (); + flush_everything (); UNLOCK_PROFILER (); } @@ -3652,7 +4093,7 @@ module_end_load (MonoProfiler *profiler, MonoImage *module, int result) { } 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 ()); + write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_MODULE | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID (), module); UNLOCK_PROFILER (); } @@ -3660,7 +4101,7 @@ static void module_start_unload (MonoProfiler *profiler, MonoImage *module) { LOCK_PROFILER (); loaded_element_unload_start (profiler->loaded_modules, module); - writer_thread_flush_everything (); + flush_everything (); UNLOCK_PROFILER (); } @@ -3694,7 +4135,7 @@ assembly_end_load (MonoProfiler *profiler, MonoAssembly *assembly, int result) { } 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 ()); + write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID (), assembly); UNLOCK_PROFILER (); } @@ -3702,7 +4143,7 @@ static void assembly_start_unload (MonoProfiler *profiler, MonoAssembly *assembly) { LOCK_PROFILER (); loaded_element_unload_start (profiler->loaded_assemblies, assembly); - writer_thread_flush_everything (); + flush_everything (); UNLOCK_PROFILER (); } static void @@ -3833,8 +4274,8 @@ print_event_data (ProfilerPerThreadData *data, ProfilerEventData *event, guint64 if (delta < MAX_EVENT_VALUE) {\ (event)->value = delta;\ } else {\ - ProfilerEventData *extension = data->next_free_event;\ - data->next_free_event ++;\ + ProfilerEventData *extension = data->next_unreserved_event;\ + data->next_unreserved_event ++;\ (event)->value = MAX_EVENT_VALUE;\ *(guint64*)extension = delta;\ }\ @@ -3849,8 +4290,8 @@ print_event_data (ProfilerPerThreadData *data, ProfilerEventData *event, guint64 if ((v) < MAX_EVENT_VALUE) {\ (event)->value = (v);\ } else {\ - ProfilerEventData *extension = data->next_free_event;\ - data->next_free_event ++;\ + ProfilerEventData *extension = data->next_unreserved_event;\ + data->next_unreserved_event ++;\ (event)->value = MAX_EVENT_VALUE;\ *(guint64*)extension = (v);\ }\ @@ -3868,8 +4309,8 @@ print_event_data (ProfilerPerThreadData *data, ProfilerEventData *event, guint64 if (delta < MAX_EVENT_VALUE) {\ (event)->value = delta;\ } else {\ - ProfilerEventData *extension = data->next_free_event;\ - data->next_free_event ++;\ + ProfilerEventData *extension = data->next_unreserved_event;\ + data->next_unreserved_event ++;\ (event)->value = MAX_EVENT_VALUE;\ *(guint64*)extension = delta;\ }\ @@ -3884,13 +4325,20 @@ print_event_data (ProfilerPerThreadData *data, ProfilerEventData *event, guint64 if ((v) < MAX_EVENT_VALUE) {\ (event)->value = (v);\ } else {\ - ProfilerEventData *extension = data->next_free_event;\ - data->next_free_event ++;\ + ProfilerEventData *extension = data->next_unreserved_event;\ + data->next_unreserved_event ++;\ (event)->value = MAX_EVENT_VALUE;\ *(guint64*)extension = (v);\ }\ LOG_EVENT (data, (event), (v));\ }while (0); +#define INCREMENT_EVENT(event) do {\ + if ((event)->value != MAX_EVENT_VALUE) {\ + (event) ++;\ + } else {\ + (event) += 2;\ + }\ +}while (0); static void class_start_load (MonoProfiler *profiler, MonoClass *klass) { @@ -3899,6 +4347,7 @@ class_start_load (MonoProfiler *profiler, MonoClass *klass) { GET_PROFILER_THREAD_DATA (data); GET_NEXT_FREE_EVENT (data, event); STORE_EVENT_ITEM_COUNTER (event, profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD, MONO_PROFILER_EVENT_KIND_START); + COMMIT_RESERVED_EVENTS (data); } static void class_end_load (MonoProfiler *profiler, MonoClass *klass, int result) { @@ -3907,6 +4356,7 @@ class_end_load (MonoProfiler *profiler, MonoClass *klass, int result) { GET_PROFILER_THREAD_DATA (data); GET_NEXT_FREE_EVENT (data, event); STORE_EVENT_ITEM_COUNTER (event, profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD | RESULT_TO_EVENT_CODE (result), MONO_PROFILER_EVENT_KIND_END); + COMMIT_RESERVED_EVENTS (data); } static void class_start_unload (MonoProfiler *profiler, MonoClass *klass) { @@ -3915,6 +4365,7 @@ class_start_unload (MonoProfiler *profiler, MonoClass *klass) { GET_PROFILER_THREAD_DATA (data); GET_NEXT_FREE_EVENT (data, event); STORE_EVENT_ITEM_COUNTER (event, profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_START); + COMMIT_RESERVED_EVENTS (data); } static void class_end_unload (MonoProfiler *profiler, MonoClass *klass) { @@ -3923,6 +4374,7 @@ class_end_unload (MonoProfiler *profiler, MonoClass *klass) { GET_PROFILER_THREAD_DATA (data); GET_NEXT_FREE_EVENT (data, event); STORE_EVENT_ITEM_COUNTER (event, profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_END); + COMMIT_RESERVED_EVENTS (data); } static void @@ -3933,6 +4385,7 @@ method_start_jit (MonoProfiler *profiler, MonoMethod *method) { GET_NEXT_FREE_EVENT (data, event); thread_stack_push_jitted_safely (&(data->stack), method, TRUE); STORE_EVENT_ITEM_COUNTER (event, profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT, MONO_PROFILER_EVENT_KIND_START); + COMMIT_RESERVED_EVENTS (data); } static void method_end_jit (MonoProfiler *profiler, MonoMethod *method, int result) { @@ -3942,6 +4395,7 @@ method_end_jit (MonoProfiler *profiler, MonoMethod *method, int result) { GET_NEXT_FREE_EVENT (data, event); STORE_EVENT_ITEM_COUNTER (event, profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT | RESULT_TO_EVENT_CODE (result), MONO_PROFILER_EVENT_KIND_END); thread_stack_pop (&(data->stack)); + COMMIT_RESERVED_EVENTS (data); } #if (HAS_OPROFILE) @@ -3975,6 +4429,7 @@ method_enter (MonoProfiler *profiler, MonoMethod *method) { ProfilerEventData *event; GET_NEXT_FREE_EVENT (data, event); STORE_EVENT_ITEM_COUNTER (event, profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_START); + COMMIT_RESERVED_EVENTS (data); } if (profiler->action_flags.track_stack) { thread_stack_push_safely (&(data->stack), method); @@ -3990,6 +4445,7 @@ method_leave (MonoProfiler *profiler, MonoMethod *method) { ProfilerEventData *event; GET_NEXT_FREE_EVENT (data, event); STORE_EVENT_ITEM_COUNTER (event, profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_END); + COMMIT_RESERVED_EVENTS (data); } if (profiler->action_flags.track_stack) { thread_stack_pop (&(data->stack)); @@ -4003,23 +4459,47 @@ method_free (MonoProfiler *profiler, MonoMethod *method) { GET_PROFILER_THREAD_DATA (data); GET_NEXT_FREE_EVENT (data, event); STORE_EVENT_ITEM_COUNTER (event, profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_FREED, 0); + COMMIT_RESERVED_EVENTS (data); } static void -thread_start (MonoProfiler *profiler, gsize tid) { +thread_start (MonoProfiler *profiler, uintptr_t tid) { ProfilerPerThreadData *data; ProfilerEventData *event; GET_PROFILER_THREAD_DATA (data); GET_NEXT_FREE_EVENT (data, event); STORE_EVENT_NUMBER_COUNTER (event, profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_START); + COMMIT_RESERVED_EVENTS (data); } static void -thread_end (MonoProfiler *profiler, gsize tid) { +thread_end (MonoProfiler *profiler, uintptr_t tid) { ProfilerPerThreadData *data; ProfilerEventData *event; GET_PROFILER_THREAD_DATA (data); GET_NEXT_FREE_EVENT (data, event); STORE_EVENT_NUMBER_COUNTER (event, profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_END); + COMMIT_RESERVED_EVENTS (data); +} + +static ProfilerEventData* +save_stack_delta (MonoProfiler *profiler, ProfilerPerThreadData *data, ProfilerEventData *events, int unsaved_frames) { + int i; + + /* In this loop it is safe to simply increment "events" because MAX_EVENT_VALUE cannot be reached. */ + STORE_EVENT_NUMBER_VALUE (events, profiler, data->stack.last_saved_top, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_STACK_SECTION, 0, unsaved_frames); + events++; + for (i = 0; i < unsaved_frames; i++) { + if (! thread_stack_index_from_top_is_jitted (&(data->stack), i)) { + STORE_EVENT_ITEM_VALUE (events, profiler, thread_stack_index_from_top (&(data->stack), i), MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER, 0, 0); + } else { + STORE_EVENT_ITEM_VALUE (events, profiler, thread_stack_index_from_top (&(data->stack), i), MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER, 0, 0); + } + events ++; + } + + data->stack.last_saved_top = data->stack.top; + + return events; } static void @@ -4046,20 +4526,7 @@ object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) { RESERVE_EVENTS (data, events, event_slot_count); if (profiler->action_flags.save_allocation_stack) { - int i; - - STORE_EVENT_NUMBER_VALUE (events, profiler, data->stack.last_saved_top, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_STACK_SECTION, 0, unsaved_frames); - events++; - for (i = 0; i < unsaved_frames; i++) { - if (! thread_stack_index_from_top_is_jitted (&(data->stack), i)) { - STORE_EVENT_ITEM_VALUE (events, profiler, thread_stack_index_from_top (&(data->stack), i), MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER, 0, 0); - } else { - STORE_EVENT_ITEM_VALUE (events, profiler, thread_stack_index_from_top (&(data->stack), i), MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER, 0, 0); - } - events ++; - } - - data->stack.last_saved_top = data->stack.top; + events = save_stack_delta (profiler, data, events, unsaved_frames); } STORE_EVENT_ITEM_VALUE (events, profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_ALLOCATION, 0, (guint64) mono_object_get_size (obj)); @@ -4071,6 +4538,7 @@ object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) { MonoMethod *caller = thread_stack_top (&(data->stack)); gboolean caller_is_jitted = thread_stack_top_is_jitted (&(data->stack)); int index = 1; + /* In this loop it is safe to simply increment "events" because MAX_EVENT_VALUE cannot be reached. */ events ++; while ((caller != NULL) && (caller->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)) { @@ -4088,22 +4556,54 @@ object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) { events ++; STORE_EVENT_ITEM_VALUE (events, profiler, obj, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID, 0, 0); } + + COMMIT_RESERVED_EVENTS (data); +} + +static void +monitor_event (MonoProfiler *profiler, MonoObject *obj, MonoProfilerMonitorEvent event) { + ProfilerPerThreadData *data; + ProfilerEventData *events; + MonoClass *klass; + int unsaved_frames; + int event_slot_count; + + CHECK_PROFILER_ENABLED (); + + GET_PROFILER_THREAD_DATA (data); + klass = mono_object_get_class (obj); + + unsaved_frames = thread_stack_count_unsaved_frames (&(data->stack)); + if (unsaved_frames > 0) { + event_slot_count = unsaved_frames + 3; + } else { + event_slot_count = 2; + } + + RESERVE_EVENTS (data, events, event_slot_count); + if (unsaved_frames > 0) { + events = save_stack_delta (profiler, data, events, unsaved_frames); + } + STORE_EVENT_ITEM_COUNTER (events, profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_MONITOR, MONO_PROFILER_EVENT_KIND_START); + INCREMENT_EVENT (events); + STORE_EVENT_ITEM_VALUE (events, profiler, obj, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_OBJECT_MONITOR, 0, event); + COMMIT_RESERVED_EVENTS (data); } static void statistical_call_chain (MonoProfiler *profiler, int call_chain_depth, guchar **ips, void *context) { MonoDomain *domain = mono_domain_get (); ProfilerStatisticalData *data; - int index; + unsigned int index; CHECK_PROFILER_ENABLED (); do { data = profiler->statistical_data; - index = InterlockedIncrement (&data->next_free_index); + index = InterlockedIncrement ((int*) &data->next_free_index); if (index <= data->end_index) { - int base_index = (index - 1) * (profiler->statistical_call_chain_depth + 1); - int call_chain_index = 0; + unsigned int base_index = (index - 1) * (profiler->statistical_call_chain_depth + 1); + unsigned int call_chain_index = 0; //printf ("[statistical_call_chain] (%d)\n", call_chain_depth); while (call_chain_index < call_chain_depth) { @@ -4138,9 +4638,12 @@ statistical_call_chain (MonoProfiler *profiler, int call_chain_depth, guchar **i profiler->statistical_data = new_data; profiler->statistical_data_second_buffer = NULL; WRITER_EVENT_RAISE (); + /* Otherwise exit from the handler and drop the event... */ + } else { + break; } - /* Loop again, hoping to acquire a free slot this time */ + /* Loop again, hoping to acquire a free slot this time (otherwise the event will be dropped) */ data = NULL; } } while (data == NULL); @@ -4150,12 +4653,12 @@ static void statistical_hit (MonoProfiler *profiler, guchar *ip, void *context) { MonoDomain *domain = mono_domain_get (); ProfilerStatisticalData *data; - int index; + unsigned int index; CHECK_PROFILER_ENABLED (); do { data = profiler->statistical_data; - index = InterlockedIncrement (&data->next_free_index); + index = InterlockedIncrement ((int*) &data->next_free_index); if (index <= data->end_index) { ProfilerStatisticalHit *hit = & (data->hits [index - 1]); @@ -4232,46 +4735,13 @@ gc_event_kind_from_profiler_event (MonoGCEvent event) { } } -#define HEAP_SHOT_COMMAND_FILE_MAX_LENGTH 64 -static void -profiler_heap_shot_process_command_file (void) { - //FIXME: Port to Windows as well - struct stat stat_buf; - int fd; - char buffer [HEAP_SHOT_COMMAND_FILE_MAX_LENGTH + 1]; - - if (profiler->heap_shot_command_file_name == NULL) - return; - if (stat (profiler->heap_shot_command_file_name, &stat_buf) != 0) - return; - if (stat_buf.st_size > HEAP_SHOT_COMMAND_FILE_MAX_LENGTH) - return; - if ((stat_buf.st_mtim.tv_sec * 1000000) < profiler->heap_shot_command_file_access_time) - return; - - fd = open (profiler->heap_shot_command_file_name, O_RDONLY); - if (fd < 0) { - return; - } else { - if (read (fd, &(buffer [0]), stat_buf.st_size) != stat_buf.st_size) { - return; - } else { - buffer [stat_buf.st_size] = 0; - profiler->dump_next_heap_snapshots = atoi (buffer); - MONO_PROFILER_GET_CURRENT_TIME (profiler->heap_shot_command_file_access_time); - } - close (fd); - } -} - static gboolean dump_current_heap_snapshot (void) { gboolean result; - if (profiler->heap_shot_was_signalled) { + if (profiler->heap_shot_was_requested) { result = TRUE; } else { - profiler_heap_shot_process_command_file (); if (profiler->dump_next_heap_snapshots > 0) { profiler->dump_next_heap_snapshots--; result = TRUE; @@ -4534,76 +5004,83 @@ heap_shot_write_job_should_be_created (gboolean dump_heap_data) { } static void -handle_heap_profiling (MonoProfiler *profiler, MonoGCEvent ev) { +process_gc_event (MonoProfiler *profiler, gboolean do_heap_profiling, MonoGCEvent ev) { static gboolean dump_heap_data; switch (ev) { case MONO_GC_EVENT_PRE_STOP_WORLD: // Get the lock, so we are sure nobody is flushing events during the collection, // and we can update all mappings (building the class descriptors). + // This is necessary also during lock profiling (even if do_heap_profiling is FALSE). LOCK_PROFILER (); break; case MONO_GC_EVENT_POST_STOP_WORLD: - dump_heap_data = dump_current_heap_snapshot (); - if (heap_shot_write_job_should_be_created (dump_heap_data)) { - ProfilerPerThreadData *data; - // Update all mappings, so that we have built all the class descriptors. - flush_all_mappings (); - // Also write all event buffers, so that allocations are recorded. - for (data = profiler->per_thread_data; data != NULL; data = data->next) { - write_thread_data_block (data); + if (do_heap_profiling) { + dump_heap_data = dump_current_heap_snapshot (); + if (heap_shot_write_job_should_be_created (dump_heap_data)) { + ProfilerPerThreadData *data; + // Update all mappings, so that we have built all the class descriptors. + flush_all_mappings (); + // Also write all event buffers, so that allocations are recorded. + for (data = profiler->per_thread_data; data != NULL; data = data->next) { + write_thread_data_block (data); + } } + } else { + dump_heap_data = FALSE; } // Release lock... UNLOCK_PROFILER (); break; case MONO_GC_EVENT_MARK_END: { - ProfilerHeapShotWriteJob *job; - ProfilerPerThreadData *data; - - if (heap_shot_write_job_should_be_created (dump_heap_data)) { - job = profiler_heap_shot_write_job_new (profiler->heap_shot_was_signalled, dump_heap_data, profiler->garbage_collection_counter); - profiler->heap_shot_was_signalled = FALSE; - MONO_PROFILER_GET_CURRENT_COUNTER (job->start_counter); - MONO_PROFILER_GET_CURRENT_TIME (job->start_time); - } else { - job = NULL; - } - - 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 (do_heap_profiling) { + ProfilerHeapShotWriteJob *job; + ProfilerPerThreadData *data; + + if (heap_shot_write_job_should_be_created (dump_heap_data)) { + job = profiler_heap_shot_write_job_new (profiler->heap_shot_was_requested, dump_heap_data, profiler->garbage_collection_counter); + profiler->heap_shot_was_requested = FALSE; + MONO_PROFILER_GET_CURRENT_COUNTER (job->start_counter); + MONO_PROFILER_GET_CURRENT_TIME (job->start_time); + } else { + job = NULL; + } + + 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 ", buffer, &(buffer->buffer [0]), buffer->end, cursor, obj); + printf ("gc_event: in object buffer %p(%p-%p) cursor at %p has object %p ", buffer, &(buffer->buffer [0]), buffer->end, cursor, obj); #endif - if (mono_object_is_alive (obj)) { + if (mono_object_is_alive (obj)) { #if DEBUG_HEAP_PROFILER - printf ("(object is alive, adding to heap)\n"); + printf ("(object is alive, adding to heap)\n"); #endif - profiler_heap_add_object (&(profiler->heap), job, obj); - } else { + profiler_heap_add_object (&(profiler->heap), job, obj); + } else { #if DEBUG_HEAP_PROFILER - printf ("(object is unreachable, reporting in job)\n"); + printf ("(object is unreachable, reporting in job)\n"); #endif - profiler_heap_report_object_unreachable (job, obj); + profiler_heap_report_object_unreachable (job, obj); + } } + buffer->first_unprocessed_slot = cursor; } - buffer->first_unprocessed_slot = cursor; } - } - - if (job != NULL) { - 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 (); + if (job != NULL) { + 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 (); + } } break; } @@ -4625,16 +5102,20 @@ gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) { event_value = (profiler->garbage_collection_counter << 8) | generation; - if (do_heap_profiling && (ev == MONO_GC_EVENT_POST_STOP_WORLD)) { - handle_heap_profiling (profiler, ev); + if (ev == MONO_GC_EVENT_POST_STOP_WORLD) { + process_gc_event (profiler, do_heap_profiling, ev); } - GET_PROFILER_THREAD_DATA (data); - GET_NEXT_FREE_EVENT (data, event); - STORE_EVENT_NUMBER_COUNTER (event, profiler, event_value, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, gc_event_code_from_profiler_event (ev), gc_event_kind_from_profiler_event (ev)); + /* Check if the gc event should be recorded. */ + if (profiler->action_flags.report_gc_events || do_heap_profiling) { + GET_PROFILER_THREAD_DATA (data); + GET_NEXT_FREE_EVENT (data, event); + STORE_EVENT_NUMBER_COUNTER (event, profiler, event_value, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, gc_event_code_from_profiler_event (ev), gc_event_kind_from_profiler_event (ev)); + COMMIT_RESERVED_EVENTS (data); + } - if (do_heap_profiling && (ev != MONO_GC_EVENT_POST_STOP_WORLD)) { - handle_heap_profiling (profiler, ev); + if (ev != MONO_GC_EVENT_POST_STOP_WORLD) { + process_gc_event (profiler, do_heap_profiling, ev); } } @@ -4646,21 +5127,185 @@ gc_resize (MonoProfiler *profiler, gint64 new_size) { GET_NEXT_FREE_EVENT (data, event); profiler->garbage_collection_counter ++; STORE_EVENT_NUMBER_VALUE (event, profiler, new_size, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_GC_RESIZE, 0, profiler->garbage_collection_counter); + COMMIT_RESERVED_EVENTS (data); } static void runtime_initialized (MonoProfiler *profiler) { - LOG_WRITER_THREAD ("runtime_initialized: waking writer thread to enable it...\n"); - WRITER_EVENT_ENABLE_RAISE (); - LOG_WRITER_THREAD ("runtime_initialized: waiting writer thread...\n"); - WRITER_EVENT_DONE_WAIT (); - LOG_WRITER_THREAD ("runtime_initialized: writer thread enabled.\n"); + LOG_WRITER_THREAD ("runtime_initialized: initializing internal calls.\n"); mono_add_internal_call ("Mono.Profiler.RuntimeControls::EnableProfiler", enable_profiler); mono_add_internal_call ("Mono.Profiler.RuntimeControls::DisableProfiler", disable_profiler); mono_add_internal_call ("Mono.Profiler.RuntimeControls::TakeHeapSnapshot", request_heap_snapshot); LOG_WRITER_THREAD ("runtime_initialized: initialized internal calls.\n"); } + +#define MAX_COMMAND_LENGTH (1024) +static int server_socket; +static int command_socket; + +static void +write_user_response (const char *response) { + LOG_USER_THREAD ("write_user_response: writing response:"); + LOG_USER_THREAD (response); + send (command_socket, response, strlen (response), 0); +} + +static void +execute_user_command (char *command) { + char *line_feed; + + LOG_USER_THREAD ("execute_user_command: executing command:"); + LOG_USER_THREAD (command); + + /* Ignore leading and trailing '\r' */ + line_feed = strchr (command, '\r'); + if (line_feed == command) { + command ++; + line_feed = strchr (command, '\r'); + } + if ((line_feed != NULL) && (* (line_feed + 1) == 0)) { + *line_feed = 0; + } + + if (strcmp (command, "enable") == 0) { + LOG_USER_THREAD ("execute_user_command: enabling profiler"); + enable_profiler (); + write_user_response ("DONE\n"); + } else if (strcmp (command, "disable") == 0) { + LOG_USER_THREAD ("execute_user_command: disabling profiler"); + disable_profiler (); + write_user_response ("DONE\n"); + } else if (strcmp (command, "heap-snapshot") == 0) { + LOG_USER_THREAD ("execute_user_command: taking heap snapshot"); + profiler->heap_shot_was_requested = TRUE; + WRITER_EVENT_RAISE (); + write_user_response ("DONE\n"); + } else if (strstr (command, "heap-snapshot-counter") == 0) { + char *equals; + LOG_USER_THREAD ("execute_user_command: changing heap counter"); + equals = strstr (command, "="); + if (equals != NULL) { + equals ++; + if (strcmp (equals, "all") == 0) { + LOG_USER_THREAD ("execute_user_command: heap counter is \"all\""); + profiler->garbage_collection_counter = -1; + } else if (strcmp (equals, "none") == 0) { + LOG_USER_THREAD ("execute_user_command: heap counter is \"none\""); + profiler->garbage_collection_counter = 0; + } else { + profiler->garbage_collection_counter = atoi (equals); + } + write_user_response ("DONE\n"); + } else { + write_user_response ("ERROR\n"); + } + profiler->heap_shot_was_requested = TRUE; + } else { + LOG_USER_THREAD ("execute_user_command: command not recognized"); + write_user_response ("ERROR\n"); + } +} + +static gboolean +process_user_commands (void) { + char *command_buffer = malloc (MAX_COMMAND_LENGTH); + int command_buffer_current_index = 0; + gboolean loop = TRUE; + gboolean result = TRUE; + + while (loop) { + int unprocessed_characters; + + LOG_USER_THREAD ("process_user_commands: reading from socket..."); + unprocessed_characters = recv (command_socket, command_buffer + command_buffer_current_index, MAX_COMMAND_LENGTH - command_buffer_current_index, 0); + + if (unprocessed_characters > 0) { + char *command_end = NULL; + + LOG_USER_THREAD ("process_user_commands: received characters."); + + do { + if (command_end != NULL) { + *command_end = 0; + execute_user_command (command_buffer); + unprocessed_characters -= (((command_end - command_buffer) - command_buffer_current_index) + 1); + + if (unprocessed_characters > 0) { + memmove (command_buffer, command_end + 1, unprocessed_characters); + } + command_buffer_current_index = 0; + } + + command_end = memchr (command_buffer, '\n', command_buffer_current_index + unprocessed_characters); + } while (command_end != NULL); + + command_buffer_current_index += unprocessed_characters; + + } else if (unprocessed_characters == 0) { + LOG_USER_THREAD ("process_user_commands: received no character."); + result = TRUE; + loop = FALSE; + } else { + LOG_USER_THREAD ("process_user_commands: received error."); + result = FALSE; + loop = FALSE; + } + } + + free (command_buffer); + return result; +} + +static guint32 +user_thread (gpointer nothing) { + struct sockaddr_in server_address; + + server_socket = -1; + command_socket = -1; + + LOG_USER_THREAD ("user_thread: starting up..."); + + server_socket = socket (AF_INET, SOCK_STREAM, 0); + if (server_socket < 0) { + LOG_USER_THREAD ("user_thread: error creating socket."); + return 0; + } + memset (& server_address, 0, sizeof (server_address)); + + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = INADDR_ANY; + if ((profiler->command_port < 1023) || (profiler->command_port > 65535)) { + LOG_USER_THREAD ("user_thread: invalid port number."); + return 0; + } + server_address.sin_port = htons (profiler->command_port); + + if (bind (server_socket, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) { + LOG_USER_THREAD ("user_thread: error binding socket."); + close (server_socket); + return 0; + } + + LOG_USER_THREAD ("user_thread: listening...\n"); + listen (server_socket, 1); + command_socket = accept (server_socket, NULL, NULL); + if (command_socket < 0) { + LOG_USER_THREAD ("user_thread: error accepting socket."); + close (server_socket); + return 0; + } + + LOG_USER_THREAD ("user_thread: processing user commands..."); + process_user_commands (); + + LOG_USER_THREAD ("user_thread: exiting cleanly."); + close (server_socket); + close (command_socket); + return 0; +} + + /* called at the end of the program */ static void profiler_shutdown (MonoProfiler *prof) @@ -4670,6 +5315,10 @@ profiler_shutdown (MonoProfiler *prof) LOG_WRITER_THREAD ("profiler_shutdown: zeroing relevant flags"); mono_profiler_set_events (0); + /* During shutdown searching for MonoJitInfo is not possible... */ + if (profiler->statistical_call_chain_strategy == MONO_PROFILER_CALL_CHAIN_MANAGED) { + mono_profiler_install_statistical_call_chain (NULL, 0, MONO_PROFILER_CALL_CHAIN_NONE); + } //profiler->flags = 0; //profiler->action_flags.unreachable_objects = FALSE; //profiler->action_flags.heap_shot = FALSE; @@ -4689,6 +5338,10 @@ profiler_shutdown (MonoProfiler *prof) write_end_block (); FLUSH_FILE (); CLOSE_FILE(); + mono_profiler_install_code_chunk_new (NULL); + mono_profiler_install_code_chunk_destroy (NULL); + mono_profiler_install_code_buffer_new (NULL); + profiler_code_chunks_cleanup (& (profiler->code_chunks)); UNLOCK_PROFILER (); g_free (profiler->file_name); @@ -4722,9 +5375,6 @@ profiler_shutdown (MonoProfiler *prof) } profiler_heap_buffers_free (&(profiler->heap)); - if (profiler->heap_shot_command_file_name != NULL) { - g_free (profiler->heap_shot_command_file_name); - } profiler_free_write_buffers (); profiler_destroy_heap_shot_write_jobs (); @@ -4741,39 +5391,25 @@ profiler_shutdown (MonoProfiler *prof) profiler = NULL; } -#ifndef PLATFORM_WIN32 -static int -parse_signal_name (const char *signal_name) { - if (! strcasecmp (signal_name, "SIGUSR1")) { - return SIGUSR1; - } else if (! strcasecmp (signal_name, "SIGUSR2")) { - return SIGUSR2; - } else if (! strcasecmp (signal_name, "SIGPROF")) { - return SIGPROF; - } else { - return atoi (signal_name); - } -} -static gboolean -check_signal_number (int signal_number) { - if (((signal_number == SIGPROF) && ! (profiler->flags & MONO_PROFILE_STATISTICAL)) || - (signal_number == SIGUSR1) || - (signal_number == SIGUSR2)) { - return TRUE; - } else { - return FALSE; - } -} -#endif +#define FAIL_ARGUMENT_CHECK(message) do {\ + failure_message = (message);\ + goto failure_handling;\ +} while (0) +#define FAIL_PARSING_VALUED_ARGUMENT FAIL_ARGUMENT_CHECK("cannot parse valued argument %s") +#define FAIL_PARSING_FLAG_ARGUMENT FAIL_ARGUMENT_CHECK("cannot parse flag argument %s") +#define CHECK_CONDITION(condition,message) do {\ + gboolean result = (condition);\ + if (result) {\ + FAIL_ARGUMENT_CHECK (message);\ + }\ +} while (0) +#define FAIL_IF_HAS_MINUS CHECK_CONDITION(has_minus,"minus ('-') modifier not allowed for argument %s") +#define TRUE_IF_NOT_MINUS ((!has_minus)?TRUE:FALSE) #define DEFAULT_ARGUMENTS "s" static void setup_user_options (const char *arguments) { gchar **arguments_array, **current_argument; -#ifndef PLATFORM_WIN32 - int gc_request_signal_number = 0; - int toggle_signal_number = 0; -#endif detect_fast_timer (); profiler->file_name = NULL; @@ -4781,11 +5417,10 @@ setup_user_options (const char *arguments) { profiler->per_thread_buffer_size = 10000; profiler->statistical_buffer_size = 10000; profiler->statistical_call_chain_depth = 0; + profiler->statistical_call_chain_strategy = MONO_PROFILER_CALL_CHAIN_NATIVE; profiler->write_buffer_size = 1024; - profiler->heap_shot_command_file_name = NULL; profiler->dump_next_heap_snapshots = 0; - profiler->heap_shot_command_file_access_time = 0; - profiler->heap_shot_was_signalled = FALSE; + profiler->heap_shot_was_requested = FALSE; profiler->flags = MONO_PROFILE_APPDOMAIN_EVENTS| MONO_PROFILE_ASSEMBLY_EVENTS| MONO_PROFILE_MODULE_EVENTS| @@ -4808,138 +5443,227 @@ setup_user_options (const char *arguments) { for (current_argument = arguments_array; ((current_argument != NULL) && (current_argument [0] != 0)); current_argument ++) { char *argument = *current_argument; char *equals = strstr (argument, "="); + const char *failure_message = NULL; + gboolean has_plus; + gboolean has_minus; + + if (*argument == '+') { + has_plus = TRUE; + has_minus = FALSE; + argument ++; + } else if (*argument == '-') { + has_plus = FALSE; + has_minus = TRUE; + argument ++; + } else { + has_plus = FALSE; + has_minus = FALSE; + } 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); + FAIL_IF_HAS_MINUS; if (value > 0) { profiler->per_thread_buffer_size = value; } } else if (! (strncmp (argument, "statistical", equals_position) && strncmp (argument, "stat", equals_position) && strncmp (argument, "s", equals_position))) { int value = atoi (equals + 1); + FAIL_IF_HAS_MINUS; if (value > 0) { - if (value > 16) { - value = 16; + if (value > MONO_PROFILER_MAX_STAT_CALL_CHAIN_DEPTH) { + value = MONO_PROFILER_MAX_STAT_CALL_CHAIN_DEPTH; } profiler->statistical_call_chain_depth = value; - profiler->flags |= MONO_PROFILE_STATISTICAL|MONO_PROFILE_JIT_COMPILATION; + profiler->flags |= MONO_PROFILE_STATISTICAL; + } + } else if (! (strncmp (argument, "call-chain-strategy", equals_position) && strncmp (argument, "ccs", equals_position))) { + char *parameter = equals + 1; + FAIL_IF_HAS_MINUS; + if (! strcmp (parameter, "native")) { + profiler->statistical_call_chain_strategy = MONO_PROFILER_CALL_CHAIN_NATIVE; + } else if (! strcmp (parameter, "glibc")) { + profiler->statistical_call_chain_strategy = MONO_PROFILER_CALL_CHAIN_GLIBC; + } else if (! strcmp (parameter, "managed")) { + profiler->statistical_call_chain_strategy = MONO_PROFILER_CALL_CHAIN_MANAGED; + } else { + failure_message = "invalid call chain strategy in argument %s"; + goto failure_handling; } } else if (! (strncmp (argument, "statistical-thread-buffer-size", equals_position) && strncmp (argument, "sbs", equals_position))) { int value = atoi (equals + 1); + FAIL_IF_HAS_MINUS; 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); + FAIL_IF_HAS_MINUS; 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))) { + FAIL_IF_HAS_MINUS; if (strlen (equals + 1) > 0) { profiler->file_name = g_strdup (equals + 1); } } else if (! (strncmp (argument, "output-suffix", equals_position) && strncmp (argument, "suffix", equals_position) && strncmp (argument, "os", equals_position) && strncmp (argument, "OS", equals_position))) { + FAIL_IF_HAS_MINUS; if (strlen (equals + 1) > 0) { profiler->file_name_suffix = g_strdup (equals + 1); } - } else if (! (strncmp (argument, "gc-commands", equals_position) && strncmp (argument, "gc-c", equals_position) && strncmp (argument, "gcc", equals_position))) { - if (strlen (equals + 1) > 0) { - profiler->heap_shot_command_file_name = g_strdup (equals + 1); + } else if (! (strncmp (argument, "heap-shot", equals_position) && strncmp (argument, "heap", equals_position) && strncmp (argument, "h", equals_position))) { + char *parameter = equals + 1; + if (! strcmp (parameter, "all")) { + profiler->dump_next_heap_snapshots = -1; + } else { + profiler->dump_next_heap_snapshots = atoi (parameter); + } + FAIL_IF_HAS_MINUS; + if (! has_plus) { + profiler->action_flags.save_allocation_caller = TRUE; + profiler->action_flags.save_allocation_stack = TRUE; + profiler->action_flags.allocations_carry_id = TRUE_IF_NOT_MINUS; } + profiler->action_flags.heap_shot = TRUE_IF_NOT_MINUS; } else if (! (strncmp (argument, "gc-dumps", equals_position) && strncmp (argument, "gc-d", equals_position) && strncmp (argument, "gcd", equals_position))) { + FAIL_IF_HAS_MINUS; if (strlen (equals + 1) > 0) { profiler->dump_next_heap_snapshots = atoi (equals + 1); } -#ifndef PLATFORM_WIN32 - } else if (! (strncmp (argument, "gc-signal", equals_position) && strncmp (argument, "gc-s", equals_position) && strncmp (argument, "gcs", equals_position))) { + } else if (! (strncmp (argument, "command-port", equals_position) && strncmp (argument, "cp", equals_position))) { + FAIL_IF_HAS_MINUS; if (strlen (equals + 1) > 0) { - char *signal_name = equals + 1; - gc_request_signal_number = parse_signal_name (signal_name); + profiler->command_port = atoi (equals + 1); } - } else if (! (strncmp (argument, "toggle-signal", equals_position) && strncmp (argument, "ts", equals_position))) { - if (strlen (equals + 1) > 0) { - char *signal_name = equals + 1; - toggle_signal_number = parse_signal_name (signal_name); - } -#endif } else { - g_warning ("Cannot parse valued argument %s\n", argument); + FAIL_PARSING_VALUED_ARGUMENT; } } else { if (! (strcmp (argument, "jit") && strcmp (argument, "j"))) { - profiler->flags |= MONO_PROFILE_JIT_COMPILATION; - profiler->action_flags.jit_time = TRUE; + profiler->action_flags.jit_time = TRUE_IF_NOT_MINUS; } else if (! (strcmp (argument, "allocations") && strcmp (argument, "alloc") && strcmp (argument, "a"))) { - profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC; + FAIL_IF_HAS_MINUS; + if (! has_plus) { + profiler->action_flags.save_allocation_caller = TRUE; + profiler->action_flags.save_allocation_stack = TRUE; + } + if (! has_minus) { + profiler->flags |= MONO_PROFILE_ALLOCATIONS; + } else { + profiler->flags &= ~MONO_PROFILE_ALLOCATIONS; + } + } else if (! (strcmp (argument, "monitor") && strcmp (argument, "locks") && strcmp (argument, "lock"))) { + FAIL_IF_HAS_MINUS; + profiler->action_flags.track_stack = TRUE; + profiler->flags |= MONO_PROFILE_MONITOR_EVENTS; + profiler->flags |= MONO_PROFILE_GC; } else if (! (strcmp (argument, "gc") && strcmp (argument, "g"))) { + FAIL_IF_HAS_MINUS; + profiler->action_flags.report_gc_events = TRUE; profiler->flags |= MONO_PROFILE_GC; } else if (! (strcmp (argument, "allocations-summary") && strcmp (argument, "as"))) { - profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC; - profiler->action_flags.collection_summary = TRUE; + profiler->action_flags.collection_summary = TRUE_IF_NOT_MINUS; } else if (! (strcmp (argument, "heap-shot") && strcmp (argument, "heap") && strcmp (argument, "h"))) { - profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC; - profiler->action_flags.heap_shot = TRUE; + FAIL_IF_HAS_MINUS; + if (! has_plus) { + profiler->action_flags.save_allocation_caller = TRUE; + profiler->action_flags.save_allocation_stack = TRUE; + profiler->action_flags.allocations_carry_id = TRUE_IF_NOT_MINUS; + } + profiler->action_flags.heap_shot = TRUE_IF_NOT_MINUS; } 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; + profiler->action_flags.unreachable_objects = TRUE_IF_NOT_MINUS; } else if (! (strcmp (argument, "threads") && strcmp (argument, "t"))) { - profiler->flags |= MONO_PROFILE_THREADS; + if (! has_minus) { + profiler->flags |= MONO_PROFILE_THREADS; + } else { + profiler->flags &= ~MONO_PROFILE_THREADS; + } } else if (! (strcmp (argument, "enter-leave") && strcmp (argument, "calls") && strcmp (argument, "c"))) { - profiler->flags |= MONO_PROFILE_ENTER_LEAVE; - profiler->action_flags.jit_time = TRUE; - profiler->action_flags.track_calls = TRUE; + profiler->action_flags.track_calls = TRUE_IF_NOT_MINUS; } else if (! (strcmp (argument, "statistical") && strcmp (argument, "stat") && strcmp (argument, "s"))) { - profiler->flags |= MONO_PROFILE_STATISTICAL; - } else if (! (strcmp (argument, "track-stack") && strcmp (argument, "ts"))) { - profiler->flags |= MONO_PROFILE_ENTER_LEAVE; - profiler->action_flags.track_stack = TRUE; - profiler->action_flags.save_allocation_caller = TRUE; + if (! has_minus) { + profiler->flags |= MONO_PROFILE_STATISTICAL; + } else { + profiler->flags &= ~MONO_PROFILE_STATISTICAL; + } + } else if (! (strcmp (argument, "save-allocation-caller") && strcmp (argument, "sac"))) { + profiler->action_flags.save_allocation_caller = TRUE_IF_NOT_MINUS; } else if (! (strcmp (argument, "save-allocation-stack") && strcmp (argument, "sas"))) { - profiler->flags |= MONO_PROFILE_ENTER_LEAVE; - profiler->action_flags.track_stack = TRUE; - profiler->action_flags.save_allocation_stack = TRUE; + profiler->action_flags.save_allocation_stack = TRUE_IF_NOT_MINUS; } else if (! (strcmp (argument, "allocations-carry-id") && strcmp (argument, "aci"))) { - profiler->action_flags.allocations_carry_id = TRUE; + profiler->action_flags.allocations_carry_id = TRUE_IF_NOT_MINUS; } else if (! (strcmp (argument, "start-enabled") && strcmp (argument, "se"))) { - profiler->profiler_enabled = TRUE; + profiler->profiler_enabled = TRUE_IF_NOT_MINUS; } else if (! (strcmp (argument, "start-disabled") && strcmp (argument, "sd"))) { - profiler->profiler_enabled = FALSE; + profiler->profiler_enabled = ! TRUE_IF_NOT_MINUS; } else if (! (strcmp (argument, "force-accurate-timer") && strcmp (argument, "fac"))) { - use_fast_timer = FALSE; + use_fast_timer = TRUE_IF_NOT_MINUS; #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"); + FAIL_ARGUMENT_CHECK ("problem calling op_open_agent"); } #endif } else if (strcmp (argument, "logging")) { - g_warning ("Cannot parse flag argument %s\n", argument); + FAIL_PARSING_FLAG_ARGUMENT; } } + +failure_handling: + if (failure_message != NULL) { + g_warning (failure_message, argument); + failure_message = NULL; + } } g_free (arguments_array); -#ifndef PLATFORM_WIN32 - if (gc_request_signal_number != 0) { - if (check_signal_number (gc_request_signal_number) && (gc_request_signal_number != toggle_signal_number)) { - add_gc_request_handler (gc_request_signal_number); - } else { - g_error ("Cannot use signal %d", gc_request_signal_number); - } + /* Ensure that the profiler flags needed to support required action flags are active */ + if (profiler->action_flags.jit_time) { + profiler->flags |= MONO_PROFILE_JIT_COMPILATION; } - if (toggle_signal_number != 0) { - if (check_signal_number (toggle_signal_number) && (toggle_signal_number != gc_request_signal_number)) { - add_toggle_handler (toggle_signal_number); - } else { - g_error ("Cannot use signal %d", gc_request_signal_number); - } + if (profiler->action_flags.save_allocation_caller || profiler->action_flags.save_allocation_stack || profiler->action_flags.allocations_carry_id) { + profiler->flags |= MONO_PROFILE_ALLOCATIONS; + } + if (profiler->action_flags.collection_summary || profiler->action_flags.heap_shot || profiler->action_flags.unreachable_objects) { + profiler->flags |= MONO_PROFILE_ALLOCATIONS; + profiler->action_flags.report_gc_events = TRUE; + } + if (profiler->action_flags.track_calls) { + profiler->flags |= MONO_PROFILE_ENTER_LEAVE; + profiler->action_flags.jit_time = TRUE; + } + if (profiler->action_flags.save_allocation_caller || profiler->action_flags.save_allocation_stack) { + profiler->action_flags.track_stack = TRUE; + profiler->flags |= MONO_PROFILE_ENTER_LEAVE; + } + if (profiler->action_flags.track_stack) { + profiler->flags |= MONO_PROFILE_ENTER_LEAVE; + } + + /* Tracking call stacks is useless if we already emit all enter-exit events... */ + if (profiler->action_flags.track_calls) { + profiler->action_flags.track_stack = FALSE; + profiler->action_flags.save_allocation_caller = FALSE; + profiler->action_flags.save_allocation_stack = FALSE; } -#endif + + /* Without JIT events the stat profiler will not find method IDs... */ + if (profiler->flags | MONO_PROFILE_STATISTICAL) { + profiler->flags |= MONO_PROFILE_JIT_COMPILATION; + } + /* Profiling allocations without knowing which gc we are doing is not nice... */ + if (profiler->flags | MONO_PROFILE_ALLOCATIONS) { + profiler->flags |= MONO_PROFILE_GC; + profiler->action_flags.report_gc_events = TRUE; + } + if (profiler->file_name == NULL) { char *program_name = g_get_prgname (); @@ -4985,42 +5709,8 @@ setup_user_options (const char *arguments) { } } -static gboolean -thread_detach_callback (MonoThread *thread) { - LOG_WRITER_THREAD ("thread_detach_callback: asking writer thread to detach"); - profiler->detach_writer_thread = TRUE; - WRITER_EVENT_RAISE (); - LOG_WRITER_THREAD ("thread_detach_callback: done"); - return FALSE; -} - static guint32 data_writer_thread (gpointer nothing) { - static gboolean thread_attached = FALSE; - static gboolean thread_detached = FALSE; - static MonoThread *this_thread = NULL; - - /* Wait for the OK to attach to the runtime */ - WRITER_EVENT_ENABLE_WAIT (); - if (! profiler->terminate_writer_thread) { - MonoDomain * root_domain = mono_get_root_domain (); - if (root_domain != NULL) { - LOG_WRITER_THREAD ("data_writer_thread: attaching thread"); - this_thread = mono_thread_attach (root_domain); - mono_thread_set_manage_callback (this_thread, thread_detach_callback); - thread_attached = TRUE; - } else { - g_error ("Cannot get root domain\n"); - } - } else { - /* Execution was too short, pretend we attached and detached. */ - thread_attached = TRUE; - thread_detached = TRUE; - } - profiler->writer_thread_enabled = TRUE; - /* Notify that we are attached to the runtime */ - WRITER_EVENT_DONE_RAISE (); - for (;;) { ProfilerStatisticalData *statistical_data; gboolean done; @@ -5029,78 +5719,59 @@ data_writer_thread (gpointer nothing) { WRITER_EVENT_WAIT (); LOG_WRITER_THREAD ("data_writer_thread: just woke up"); - if (profiler->heap_shot_was_signalled) { + if (profiler->heap_shot_was_requested) { + MonoDomain * root_domain = mono_get_root_domain (); + + if (root_domain != NULL) { + MonoThread *this_thread; + LOG_WRITER_THREAD ("data_writer_thread: attaching thread"); + this_thread = mono_thread_attach (root_domain); LOG_WRITER_THREAD ("data_writer_thread: starting requested collection"); mono_gc_collect (mono_gc_max_generation ()); LOG_WRITER_THREAD ("data_writer_thread: requested collection done"); + LOG_WRITER_THREAD ("data_writer_thread: detaching thread"); + mono_thread_detach (this_thread); + this_thread = NULL; + LOG_WRITER_THREAD ("data_writer_thread: collection sequence completed"); + } else { + LOG_WRITER_THREAD ("data_writer_thread: cannot get root domain, collection sequence skipped"); + } + } statistical_data = profiler->statistical_data_ready; - done = (statistical_data == NULL) && (profiler->heap_shot_write_jobs == NULL) && (profiler->writer_thread_flush_everything == FALSE); + done = (statistical_data == NULL) && (profiler->heap_shot_write_jobs == NULL); - if ((!done) && thread_attached) { - if (profiler->writer_thread_flush_everything) { - /* Note that this assumes the lock is held by the thread that woke us up! */ - if (! thread_detached) { - LOG_WRITER_THREAD ("data_writer_thread: flushing everything..."); - flush_everything (); - profiler->writer_thread_flush_everything = FALSE; - WRITER_EVENT_DONE_RAISE (); - LOG_WRITER_THREAD ("data_writer_thread: flushed everything."); - } else { - LOG_WRITER_THREAD ("data_writer_thread: flushing requested, but thread is detached..."); - profiler->writer_thread_flush_everything = FALSE; - WRITER_EVENT_DONE_RAISE (); - LOG_WRITER_THREAD ("data_writer_thread: done event raised."); - } - } else { - 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 (); - LOG_WRITER_THREAD ("data_writer_thread: wrote mapping"); - - if ((statistical_data != NULL) && ! thread_detached) { - LOG_WRITER_THREAD ("data_writer_thread: writing statistical data..."); - 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; - 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 (!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 (); + LOG_WRITER_THREAD ("data_writer_thread: wrote mapping"); + + if (statistical_data != NULL) { + LOG_WRITER_THREAD ("data_writer_thread: writing statistical data..."); + 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; + 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"); } else { - if (profiler->writer_thread_flush_everything) { - LOG_WRITER_THREAD ("data_writer_thread: flushing requested, but thread is not attached..."); - profiler->writer_thread_flush_everything = FALSE; - WRITER_EVENT_DONE_RAISE (); - LOG_WRITER_THREAD ("data_writer_thread: done event raised."); - } - } - - if (profiler->detach_writer_thread) { - if (this_thread != NULL) { - LOG_WRITER_THREAD ("data_writer_thread: detach requested, acquiring lock and flushing data"); - LOCK_PROFILER (); - flush_everything (); - UNLOCK_PROFILER (); - LOG_WRITER_THREAD ("data_writer_thread: flushed data and released lock"); - LOG_WRITER_THREAD ("data_writer_thread: detaching thread"); - mono_thread_detach (this_thread); - this_thread = NULL; - profiler->detach_writer_thread = FALSE; - thread_detached = TRUE; - } else { - LOG_WRITER_THREAD ("data_writer_thread: warning: thread has already been detached"); - } + LOG_WRITER_THREAD ("data_writer_thread: acquiring lock and flushing buffers"); + LOCK_PROFILER (); + LOG_WRITER_THREAD ("data_writer_thread: lock acquired, flushing buffers"); + flush_everything (); + UNLOCK_PROFILER (); + LOG_WRITER_THREAD ("data_writer_thread: flushed buffers and released lock"); } if (profiler->terminate_writer_thread) { @@ -5130,6 +5801,7 @@ mono_profiler_startup (const char *desc) profiler->methods = method_id_mapping_new (); profiler->classes = class_id_mapping_new (); + profiler->loaded_element_next_free_id = 1; 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); @@ -5142,6 +5814,7 @@ mono_profiler_startup (const char *desc) profiler->current_write_buffer = profiler->write_buffers; profiler->current_write_position = 0; profiler->full_write_buffers = 0; + profiler_code_chunks_initialize (& (profiler->code_chunks)); profiler->executable_regions = profiler_executable_memory_regions_new (1, 1); @@ -5160,6 +5833,13 @@ mono_profiler_startup (const char *desc) LOG_WRITER_THREAD ("mono_profiler_startup: creating writer thread"); CREATE_WRITER_THREAD (data_writer_thread); LOG_WRITER_THREAD ("mono_profiler_startup: created writer thread"); + if ((profiler->command_port >= 1024) && (profiler->command_port <= 65535)) { + LOG_USER_THREAD ("mono_profiler_startup: creating user thread"); + CREATE_USER_THREAD (user_thread); + LOG_USER_THREAD ("mono_profiler_startup: created user thread"); + } else { + LOG_USER_THREAD ("mono_profiler_startup: skipping user thread creation"); + } ALLOCATE_PROFILER_THREAD_DATA (); @@ -5183,13 +5863,19 @@ mono_profiler_startup (const char *desc) mono_profiler_install_method_free (method_free); mono_profiler_install_thread (thread_start, thread_end); mono_profiler_install_allocation (object_allocated); + mono_profiler_install_monitor (monitor_event); mono_profiler_install_statistical (statistical_hit); - mono_profiler_install_statistical_call_chain (statistical_call_chain, profiler->statistical_call_chain_depth); + mono_profiler_install_statistical_call_chain (statistical_call_chain, profiler->statistical_call_chain_depth, profiler->statistical_call_chain_strategy); mono_profiler_install_gc (gc_event, gc_resize); mono_profiler_install_runtime_initialized (runtime_initialized); #if (HAS_OPROFILE) mono_profiler_install_jit_end (method_jit_result); #endif + if (profiler->flags | MONO_PROFILE_STATISTICAL) { + mono_profiler_install_code_chunk_new (profiler_code_chunk_new_callback); + mono_profiler_install_code_chunk_destroy (profiler_code_chunk_destroy_callback); + mono_profiler_install_code_buffer_new (profiler_code_buffer_new_callback); + } mono_profiler_set_events (profiler->flags); }