#include <config.h>
#include <mono/metadata/profiler.h>
#include <mono/metadata/class.h>
+#include <mono/metadata/class-internals.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/loader.h>
#include <mono/metadata/threads.h>
MONO_PROFILER_FILE_BLOCK_KIND_EVENTS = 6,
MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL = 7,
MONO_PROFILER_FILE_BLOCK_KIND_HEAP_DATA = 8,
- MONO_PROFILER_FILE_BLOCK_KIND_HEAP_SUMMARY = 9
+ MONO_PROFILER_FILE_BLOCK_KIND_HEAP_SUMMARY = 9,
+ MONO_PROFILER_FILE_BLOCK_KIND_DIRECTIVES = 10
} MonoProfilerFileBlockKind;
+typedef enum {
+ MONO_PROFILER_DIRECTIVE_END = 0,
+ MONO_PROFILER_DIRECTIVE_ALLOCATIONS_CARRY_CALLER = 1,
+ MONO_PROFILER_DIRECTIVE_ALLOCATIONS_HAVE_STACK = 2,
+ MONO_PROFILER_DIRECTIVE_ALLOCATIONS_CARRY_ID = 3,
+ MONO_PROFILER_DIRECTIVE_LAST
+} MonoProfilerDirectives;
+
+
#define MONO_PROFILER_LOADED_EVENT_MODULE 1
#define MONO_PROFILER_LOADED_EVENT_ASSEMBLY 2
#define MONO_PROFILER_LOADED_EVENT_APPDOMAIN 4
gsize number;
} data;
unsigned int data_type:2;
- unsigned int code:3;
+ unsigned int code:4;
unsigned int kind:1;
- unsigned int value:26;
+ unsigned int value:25;
} ProfilerEventData;
-#define EVENT_VALUE_BITS (26)
+#define EVENT_VALUE_BITS (25)
#define MAX_EVENT_VALUE ((1<<EVENT_VALUE_BITS)-1)
typedef enum {
MONO_PROFILER_EVENT_METHOD_JIT = 0,
MONO_PROFILER_EVENT_METHOD_FREED = 1,
- MONO_PROFILER_EVENT_METHOD_CALL = 2
+ MONO_PROFILER_EVENT_METHOD_CALL = 2,
+ MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER = 3,
+ MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER = 4
} MonoProfilerMethodEvents;
typedef enum {
MONO_PROFILER_EVENT_CLASS_LOAD = 0,
MONO_PROFILER_EVENT_GC_SWEEP = 4,
MONO_PROFILER_EVENT_GC_RESIZE = 5,
MONO_PROFILER_EVENT_GC_STOP_WORLD = 6,
- MONO_PROFILER_EVENT_GC_START_WORLD = 7
+ 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
} MonoProfilerEvents;
typedef enum {
MONO_PROFILER_EVENT_KIND_START = 0,
}\
} while (0)
#else
-static detect_fast_timer (void) {
+static void detect_fast_timer (void) {
use_fast_timer = FALSE;
}
#define MONO_PROFILER_GET_CURRENT_COUNTER(c) MONO_PROFILER_GET_CURRENT_TIME ((c))
gboolean dump_heap_data;
} ProfilerHeapShotWriteJob;
+typedef struct _ProfilerThreadStack {
+ guint32 capacity;
+ guint32 top;
+ guint32 last_saved_top;
+ guint32 last_written_frame;
+ MonoMethod **stack;
+ guint8 *method_is_jitted;
+ guint32 *written_frames;
+} ProfilerThreadStack;
+
typedef struct _ProfilerPerThreadData {
ProfilerEventData *events;
ProfilerEventData *next_free_event;
guint64 last_event_counter;
gsize thread_id;
ProfilerHeapShotObjectBuffer *heap_shot_object_buffers;
+ ProfilerThreadStack stack;
struct _ProfilerPerThreadData* next;
} ProfilerPerThreadData;
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 {
} ProfilerUnmanagedSymbol;
struct _ProfilerExecutableFile;
+struct _ProfilerExecutableFileSectionRegion;
typedef struct _ProfilerExecutableMemoryRegionData {
gpointer start;
gboolean is_new;
struct _ProfilerExecutableFile *file;
+ struct _ProfilerExecutableFileSectionRegion *file_region_reference;
guint32 symbols_count;
guint32 symbols_capacity;
ProfilerUnmanagedSymbol *symbols;
} ProfilerExecutableFiles;
+#define CLEANUP_WRITER_THREAD() do {profiler->writer_thread_terminated = TRUE;} while (0)
+#define CHECK_WRITER_THREAD() (! profiler->writer_thread_terminated)
+
#ifndef PLATFORM_WIN32
#include <sys/types.h>
#include <sys/time.h>
#define THREAD_TYPE pthread_t
#define CREATE_WRITER_THREAD(f) pthread_create (&(profiler->data_writer_thread), NULL, ((void*(*)(void*))f), NULL)
#define EXIT_THREAD() pthread_exit (NULL);
-#define WAIT_WRITER_THREAD() pthread_join (profiler->data_writer_thread, NULL)
+#define WAIT_WRITER_THREAD() do {\
+ if (CHECK_WRITER_THREAD ()) {\
+ pthread_join (profiler->data_writer_thread, NULL);\
+ }\
+} while (0)
#define CURRENT_THREAD_ID() (gsize) pthread_self ()
#ifndef HAVE_KW_THREAD
#define WRITER_EVENT_RAISE() (void) sem_post (&(profiler->wake_data_writer_event))
#define WRITER_EVENT_ENABLE_WAIT() (void) sem_wait (&(profiler->enable_data_writer_event))
#define WRITER_EVENT_ENABLE_RAISE() (void) sem_post (&(profiler->enable_data_writer_event))
-#define WRITER_EVENT_DONE_WAIT() (void) sem_wait (&(profiler->done_data_writer_event))
+#define WRITER_EVENT_DONE_WAIT() do {\
+ if (CHECK_WRITER_THREAD ()) {\
+ (void) sem_wait (&(profiler->done_data_writer_event));\
+ }\
+} while (0)
#define WRITER_EVENT_DONE_RAISE() (void) sem_post (&(profiler->done_data_writer_event))
#if 0
#define THREAD_TYPE HANDLE
#define CREATE_WRITER_THREAD(f) CreateThread (NULL, (1*1024*1024), (f), NULL, 0, NULL);
#define EXIT_THREAD() ExitThread (0);
-#define WAIT_WRITER_THREAD() WaitForSingleObject (profiler->data_writer_thread, INFINITE)
+#define WAIT_WRITER_THREAD() do {\
+ if (CHECK_WRITER_THREAD ()) {\
+ WaitForSingleObject (profiler->data_writer_thread, INFINITE);\
+ }\
+} while (0)
#define CURRENT_THREAD_ID() (gsize) GetCurrentThreadId ()
#ifndef HAVE_KW_THREAD
#define WRITER_EVENT_RAISE() SetEvent (profiler->wake_data_writer_event)
#define WRITER_EVENT_ENABLE_WAIT() WaitForSingleObject (profiler->enable_data_writer_event, INFINITE)
#define WRITER_EVENT_ENABLE_RAISE() SetEvent (profiler->enable_data_writer_event)
-#define WRITER_EVENT_DONE_WAIT() WaitForSingleObject (profiler->done_data_writer_event, INFINITE)
+#define WRITER_EVENT_DONE_WAIT() do {\
+ if (CHECK_WRITER_THREAD ()) {\
+ WaitForSingleObject (profiler->done_data_writer_event, INFINITE);\
+ }\
+} while (0)
#define WRITER_EVENT_DONE_RAISE() SetEvent (profiler->done_data_writer_event)
#define FILE_HANDLE_TYPE FILE*
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;
gboolean unreachable_objects;
gboolean collection_summary;
gboolean heap_shot;
+ gboolean track_stack;
+ gboolean track_calls;
+ gboolean save_allocation_caller;
+ gboolean save_allocation_stack;
+ gboolean allocations_carry_id;
} action_flags;
};
static MonoProfiler *profiler;
#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;
g_assert (sigaction (signal_number, &sa, NULL) != -1);
}
+static void
+enable_profiler (void) {
+ profiler->profiler_enabled = TRUE;
+}
+
+static void
+disable_profiler (void) {
+ profiler->profiler_enabled = FALSE;
+}
+
+
+
static void
SIG_HANDLER_SIGNATURE (toggle_handler) {
if (profiler->profiler_enabled) {
#define EVENT_MARK() printf ("[EVENT:%d]", ++ event_counter)
#endif
+static void
+thread_stack_initialize_empty (ProfilerThreadStack *stack) {
+ stack->capacity = 0;
+ stack->top = 0;
+ stack->last_saved_top = 0;
+ stack->last_written_frame = 0;
+ stack->stack = NULL;
+ stack->method_is_jitted = NULL;
+ stack->written_frames = NULL;
+}
+
+static void
+thread_stack_free (ProfilerThreadStack *stack) {
+ stack->capacity = 0;
+ stack->top = 0;
+ stack->last_saved_top = 0;
+ stack->last_written_frame = 0;
+ if (stack->stack != NULL) {
+ g_free (stack->stack);
+ stack->stack = NULL;
+ }
+ if (stack->method_is_jitted != NULL) {
+ g_free (stack->method_is_jitted);
+ stack->method_is_jitted = NULL;
+ }
+ if (stack->written_frames != NULL) {
+ g_free (stack->written_frames);
+ stack->written_frames = NULL;
+ }
+}
+
+static void
+thread_stack_initialize (ProfilerThreadStack *stack, guint32 capacity) {
+ stack->capacity = capacity;
+ stack->top = 0;
+ stack->last_saved_top = 0;
+ stack->last_written_frame = 0;
+ stack->stack = g_new0 (MonoMethod*, capacity);
+ stack->method_is_jitted = g_new0 (guint8, capacity);
+ stack->written_frames = g_new0 (guint32, capacity);
+}
+
+static void
+thread_stack_push_jitted (ProfilerThreadStack *stack, MonoMethod* method, gboolean method_is_jitted) {
+ if (stack->top >= stack->capacity) {
+ MonoMethod **old_stack = stack->stack;
+ guint8 *old_method_is_jitted = stack->method_is_jitted;
+ guint32 *old_written_frames = stack->written_frames;
+ guint32 top = stack->top;
+ guint32 last_saved_top = stack->last_saved_top;
+ guint32 last_written_frame = stack->last_written_frame;
+ thread_stack_initialize (stack, stack->capacity * 2);
+ memcpy (stack->stack, old_stack, top * sizeof (MonoMethod*));
+ memcpy (stack->method_is_jitted, old_method_is_jitted, top * sizeof (guint8));
+ memcpy (stack->written_frames, old_written_frames, top * sizeof (guint32));
+ g_free (old_stack);
+ g_free (old_method_is_jitted);
+ g_free (old_written_frames);
+ stack->top = top;
+ stack->last_saved_top = last_saved_top;
+ stack->last_written_frame = last_written_frame;
+ }
+ stack->stack [stack->top] = method;
+ stack->method_is_jitted [stack->top] = method_is_jitted;
+ stack->top ++;
+}
+
+static inline void
+thread_stack_push (ProfilerThreadStack *stack, MonoMethod* method) {
+ thread_stack_push_jitted (stack, method, FALSE);
+}
+
+static MonoMethod*
+thread_stack_pop (ProfilerThreadStack *stack) {
+ if (stack->top > 0) {
+ stack->top --;
+ if (stack->last_saved_top > stack->top) {
+ stack->last_saved_top = stack->top;
+ }
+ return stack->stack [stack->top];
+ } else {
+ return NULL;
+ }
+}
+
+static MonoMethod*
+thread_stack_top (ProfilerThreadStack *stack) {
+ if (stack->top > 0) {
+ return stack->stack [stack->top - 1];
+ } else {
+ return NULL;
+ }
+}
+
+static gboolean
+thread_stack_top_is_jitted (ProfilerThreadStack *stack) {
+ if (stack->top > 0) {
+ return stack->method_is_jitted [stack->top - 1];
+ } else {
+ return FALSE;
+ }
+}
+
+static MonoMethod*
+thread_stack_index_from_top (ProfilerThreadStack *stack, int index) {
+ if (stack->top > index) {
+ return stack->stack [stack->top - (index + 1)];
+ } else {
+ return NULL;
+ }
+}
+
+static gboolean
+thread_stack_index_from_top_is_jitted (ProfilerThreadStack *stack, int index) {
+ if (stack->top > index) {
+ return stack->method_is_jitted [stack->top - (index + 1)];
+ } else {
+ return FALSE;
+ }
+}
+
+static inline void
+thread_stack_push_safely (ProfilerThreadStack *stack, MonoMethod* method) {
+ if (stack->stack != NULL) {
+ thread_stack_push (stack, method);
+ }
+}
+
+static inline void
+thread_stack_push_jitted_safely (ProfilerThreadStack *stack, MonoMethod* method, gboolean method_is_jitted) {
+ if (stack->stack != NULL) {
+ thread_stack_push_jitted (stack, method, method_is_jitted);
+ }
+}
+
+static inline int
+thread_stack_count_unsaved_frames (ProfilerThreadStack *stack) {
+ int result = stack->top - stack->last_saved_top;
+ return (result > 0) ? result : 0;
+}
+
+static inline int
+thread_stack_get_last_written_frame (ProfilerThreadStack *stack) {
+ return stack->last_written_frame;
+}
+
+static inline void
+thread_stack_set_last_written_frame (ProfilerThreadStack *stack, int last_written_frame) {
+ stack->last_written_frame = last_written_frame;
+}
+
+static inline guint32
+thread_stack_written_frame_at_index (ProfilerThreadStack *stack, int index) {
+ return stack->written_frames [index];
+}
+
+static inline void
+thread_stack_write_frame_at_index (ProfilerThreadStack *stack, int index, guint32 method_id_and_is_jitted) {
+ stack->written_frames [index] = method_id_and_is_jitted;
+}
static ClassIdMappingElement*
class_id_mapping_element_get (MonoClass *klass) {
class_id_mapping_element_new (MonoClass *klass) {
ClassIdMappingElement *result = g_new (ClassIdMappingElement, 1);
- result->name = g_strdup_printf ("%s.%s", mono_class_get_namespace (klass), mono_class_get_name (klass));
+ result->name = mono_type_full_name (mono_class_get_type (klass));
result->klass = klass;
result->next_unwritten = profiler->classes->unwritten;
profiler->classes->unwritten = result;
job->end = & (job->buffers->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
} else {
job->buffers = NULL;
- job->buffers->next = NULL;
job->last_next = NULL;
job->start = NULL;
job->cursor = NULL;
return job;
}
+static gboolean
+profiler_heap_shot_write_job_has_data (ProfilerHeapShotWriteJob *job) {
+ return ((job->buffers != NULL) || (job->summary.capacity > 0));
+}
+
static void
profiler_heap_shot_write_job_add_buffer (ProfilerHeapShotWriteJob *job, gpointer value) {
ProfilerHeapShotWriteBuffer *buffer = g_new (ProfilerHeapShotWriteBuffer, 1);
next_job = current_job->next_unwritten;
if (next_job != NULL) {
- if (current_job->buffers != NULL) {
+ if (profiler_heap_shot_write_job_has_data (current_job)) {
done = FALSE;
}
- if (next_job->buffers == NULL) {
+ if (! profiler_heap_shot_write_job_has_data (next_job)) {
current_job->next_unwritten = NULL;
next_job = NULL;
}
} else {
- if (current_job->buffers != NULL) {
+ if (profiler_heap_shot_write_job_has_data (current_job)) {
LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: writing...");
profiler_heap_shot_write_block (current_job);
LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: done");
(profiler->action_flags.collection_summary == TRUE)) {
profiler_heap_shot_object_buffer_new (data);
}
+ if (profiler->action_flags.track_stack) {
+ thread_stack_initialize (&(data->stack), 64);
+ } else {
+ thread_stack_initialize_empty (&(data->stack));
+ }
return data;
}
profiler_per_thread_data_destroy (ProfilerPerThreadData *data) {
g_free (data->events);
profiler_heap_shot_object_buffers_destroy (data->heap_shot_object_buffers);
+ thread_stack_free (&(data->stack));
g_free (data);
}
data->hits = g_new0 (ProfilerStatisticalHit, buffer_size);
data->next_free_index = 0;
- data->end_index = buffer_size;
+ data->end_index = profiler->statistical_buffer_size;
data->first_unwritten_index = 0;
return data;
WRITE_BYTE (0);
}
+static void write_clock_data (void);
+static void
+write_directives_block (gboolean start) {
+ write_clock_data ();
+
+ if (start) {
+ if (profiler->action_flags.save_allocation_caller) {
+ write_uint32 (MONO_PROFILER_DIRECTIVE_ALLOCATIONS_CARRY_CALLER);
+ }
+ if (profiler->action_flags.save_allocation_stack || profiler->action_flags.track_calls) {
+ write_uint32 (MONO_PROFILER_DIRECTIVE_ALLOCATIONS_HAVE_STACK);
+ }
+ if (profiler->action_flags.allocations_carry_id) {
+ write_uint32 (MONO_PROFILER_DIRECTIVE_ALLOCATIONS_CARRY_ID);
+ }
+ }
+ write_uint32 (MONO_PROFILER_DIRECTIVE_END);
+
+ write_clock_data ();
+ write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_DIRECTIVES);
+}
+
#if DEBUG_HEAP_PROFILER
#define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c) printf ("WRITE_HEAP_SHOT_JOB_VALUE: writing value %p at cursor %p\n", (v), (c))
#else
result = ((base)|((((kind)<<4) | (code)) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
} while (0)
+static void
+rewrite_last_written_stack (ProfilerThreadStack *stack) {
+ guint8 event_code;
+ int i = thread_stack_get_last_written_frame (stack);
+
+ MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, MONO_PROFILER_EVENT_STACK_SECTION, 0, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
+ WRITE_BYTE (event_code);
+ write_uint32 (0);
+ write_uint32 (i);
+
+ while (i > 0) {
+ i--;
+ write_uint32 (thread_stack_written_frame_at_index (stack, i));
+ }
+}
+
+
+static ProfilerEventData*
+write_stack_section_event (ProfilerEventData *events, ProfilerPerThreadData *data) {
+ int last_saved_frame = events->data.number;
+ int saved_frames = events->value;
+ guint8 event_code;
+ int i;
+
+ MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, MONO_PROFILER_EVENT_STACK_SECTION, 0, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
+ WRITE_BYTE (event_code);
+ write_uint32 (last_saved_frame);
+ write_uint32 (saved_frames);
+ thread_stack_set_last_written_frame (&(data->stack), last_saved_frame + saved_frames);
+ events++;
+
+ for (i = 0; i < saved_frames; i++) {
+ guint8 code = events->code;
+ guint32 jit_flag;
+ MethodIdMappingElement *method;
+ guint32 frame_value;
+
+ if (code == MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER) {
+ jit_flag = 0;
+ } else if (code == MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER) {
+ jit_flag = 1;
+ } else {
+ g_assert_not_reached ();
+ jit_flag = 0;
+ }
+
+ method = method_id_mapping_element_get (events->data.address);
+ g_assert (method != NULL);
+ frame_value = (method->id << 1) | jit_flag;
+ write_uint32 (frame_value);
+ thread_stack_write_frame_at_index (&(data->stack), last_saved_frame + saved_frames - (1 + i), frame_value);
+ events ++;
+ }
+
+ return events;
+}
+
static ProfilerEventData*
-write_event (ProfilerEventData *event) {
+write_event (ProfilerEventData *event, ProfilerPerThreadData *data) {
ProfilerEventData *next = event + 1;
gboolean write_event_value = TRUE;
guint8 event_code;
guint64 event_data;
guint64 event_value;
+ gboolean write_event_value_extension_1 = FALSE;
+ guint64 event_value_extension_1 = 0;
+ gboolean write_event_value_extension_2 = FALSE;
+ guint64 event_value_extension_2 = 0;
event_value = event->value;
if (event_value == MAX_EVENT_VALUE) {
event_data = element->id;
if (event->code == MONO_PROFILER_EVENT_CLASS_ALLOCATION) {
- MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION);
+ if ((! profiler->action_flags.save_allocation_caller) || (! (next->code == MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER))) {
+ MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION);
+ } else {
+ MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, MONO_PROFILER_EVENT_JIT_TIME_ALLOCATION, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
+ }
+
+ if (profiler->action_flags.save_allocation_caller) {
+ MonoMethod *caller_method = next->data.address;
+
+ if ((next->code != MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER) && (next->code != MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER)) {
+ g_assert_not_reached ();
+ }
+
+ if (caller_method != NULL) {
+ MethodIdMappingElement *caller = method_id_mapping_element_get (caller_method);
+ g_assert (caller != NULL);
+ event_value_extension_1 = caller->id;
+ }
+
+ write_event_value_extension_1 = TRUE;
+ next ++;
+ }
+
+ if (profiler->action_flags.allocations_carry_id) {
+ event_value_extension_2 = GPOINTER_TO_UINT (next->data.address);
+
+ if (next->code != MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID) {
+ g_assert_not_reached ();
+ }
+
+ 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);
}
} else {
- event_data = event->data.number;
- MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
+ if (event->code == MONO_PROFILER_EVENT_STACK_SECTION) {
+ return write_stack_section_event (event, data);
+ } else {
+ event_data = event->data.number;
+ MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
+ }
+ }
+
+ /* Skip writing JIT events if the user did not ask for them */
+ if ((event->code == MONO_PROFILER_EVENT_METHOD_JIT) && ! profiler->action_flags.jit_time) {
+ return next;
}
#if (DEBUG_LOGGING_PROFILER)
write_uint64 (event_data);
if (write_event_value) {
write_uint64 (event_value);
+ if (write_event_value_extension_1) {
+ write_uint64 (event_value_extension_1);
+ }
+ if (write_event_value_extension_2) {
+ write_uint64 (event_value_extension_2);
+ }
}
return next;
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));
+
while (start < end) {
- start = write_event (start);
+ start = write_event (start, data);
}
WRITE_BYTE (0);
data->first_unwritten_event = end;
result->is_new = TRUE;
result->file = NULL;
+ result->file_region_reference = NULL;
result->symbols_capacity = id;
result->symbols_count = id;
result->symbols = NULL;
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);
}
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;
}
}
-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
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);
+ ProfilerExecutableFile *file = region->file;
+
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 = (ProfilerExecutableFile*) g_hash_table_lookup (files->table, region->file_name);
- 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));
+ 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;
+ 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 {
- 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;
+ if (fstat (file->fd, &stat_buffer) != 0) {
+ //g_warning ("Cannot stat file '%s': '%s'", region->file_name, strerror (errno));
+ 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));
+
+ 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;
}
- } 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");
+
+ 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;
}
- } 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");
+
+ 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;
+ }
+ } 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;
+ }
+ } else {
+ g_warning ("Absurd test byte value");
return file;
}
- } 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;
+ /* 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 ((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;
+
+ 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;
+ }
+
+ 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);
+ }
+
+ 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;
}
}
if (file->section_regions != NULL) {
g_free (file->section_regions);
+ file->section_regions = NULL;
}
g_free (file);
}
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);
//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) {
};
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;
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:
static gboolean
scan_process_regions (ProfilerExecutableMemoryRegions *regions) {
char *buffer;
+ char *filename;
char *current;
int fd;
}
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;
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...");
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;
ProfilerStatisticalHit hit = data->hits [base_index];
int callers_count;
- regions_refreshed = write_statistical_hit (hit.domain, hit.address, regions_refreshed);
+ regions_refreshed = write_statistical_hit ((current_thread != NULL) ? hit.domain : NULL, hit.address, regions_refreshed);
base_index ++;
for (callers_count = 0; callers_count < call_chain_depth; callers_count ++) {
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 (hit.domain, hit.address, regions_refreshed);
+ regions_refreshed = write_statistical_hit ((current_thread != NULL) ? hit.domain : NULL, hit.address, regions_refreshed);
} else {
break;
}
MethodIdMappingElement *element = method_id_mapping_element_get (start->data.address);
if (element == NULL) {
MonoMethod *method = start->data.address;
- method_id_mapping_element_new (method);
+ if (method != NULL) {
+ method_id_mapping_element_new (method);
+ }
}
}
// We flush all mappings because some id definitions could come
// from other threads
flush_all_mappings ();
- g_assert (data->first_unmapped_event >= data->end_event);
+ g_assert (data->first_unmapped_event >= data->next_free_event);
write_thread_data_block (data);
UNLOCK_PROFILER ();
}
-#define GET_NEXT_FREE_EVENT(d,e) {\
- if ((d)->next_free_event >= (d)->end_event) {\
+/* 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))) {\
flush_full_event_data_buffer (d);\
}\
(e) = (d)->next_free_event;\
- (d)->next_free_event ++;\
+ (d)->next_free_event += (count);\
} while (0)
+#define GET_NEXT_FREE_EVENT(d,e) RESERVE_EVENTS ((d),(e),1)
static void
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) {
- 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.");
+ 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 const char*
-method_event_code_to_string (MonoProfilerClassEvents code) {
+method_event_code_to_string (MonoProfilerMethodEvents code) {
switch (code) {
case MONO_PROFILER_EVENT_METHOD_CALL: return "CALL";
case MONO_PROFILER_EVENT_METHOD_JIT: return "JIT";
case MONO_PROFILER_EVENT_METHOD_FREED: return "FREED";
+ case MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER: return "ALLOCATION_CALLER";
+ case MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER: return "ALLOCATION_JIT_TIME_CALLER";
+ case MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID: return "ALLOCATION_OBJECT_ID";
default: g_assert_not_reached (); return "";
}
}
case MONO_PROFILER_EVENT_GC_RESIZE: return "GC_RESIZE";
case MONO_PROFILER_EVENT_GC_STOP_WORLD: return "GC_STOP_WORLD";
case MONO_PROFILER_EVENT_GC_START_WORLD: return "GC_START_WORLD";
+ case MONO_PROFILER_EVENT_JIT_TIME_ALLOCATION: return "JIT_TIME_ALLOCATION";
+ case MONO_PROFILER_EVENT_STACK_SECTION: return "STACK_SECTION";
+ case MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID: return "ALLOCATION_OBJECT_ID";
default: g_assert_not_reached (); return "";
}
}
}
}
static void
-print_event_data (gsize thread_id, ProfilerEventData *event, guint64 value) {
+print_event_data (ProfilerPerThreadData *data, ProfilerEventData *event, guint64 value) {
if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
- printf ("[TID %ld] CLASS[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s)\n",
- thread_id,
+ printf ("STORE EVENT [TID %ld][EVENT %ld] CLASS[%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s)\n",
+ data->thread_id,
+ event - data->events,
event->data.address,
- event,
class_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
event_kind_to_string (event->kind),
mono_class_get_namespace ((MonoClass*) event->data.address),
mono_class_get_name ((MonoClass*) event->data.address));
} else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
- printf ("[TID %ld] METHOD[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s:%s (?))\n",
- thread_id,
+ printf ("STORE EVENT [TID %ld][EVENT %ld] METHOD[%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s:%s (?))\n",
+ data->thread_id,
+ event - data->events,
event->data.address,
- event,
method_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
event_kind_to_string (event->kind),
event->kind,
event->code,
value,
- mono_class_get_namespace (mono_method_get_class ((MonoMethod*) event->data.address)),
- mono_class_get_name (mono_method_get_class ((MonoMethod*) event->data.address)),
- mono_method_get_name ((MonoMethod*) event->data.address));
+ (event->data.address != NULL) ? mono_class_get_namespace (mono_method_get_class ((MonoMethod*) event->data.address)) : "<NULL>",
+ (event->data.address != NULL) ? mono_class_get_name (mono_method_get_class ((MonoMethod*) event->data.address)) : "<NULL>",
+ (event->data.address != NULL) ? mono_method_get_name ((MonoMethod*) event->data.address) : "<NULL>");
} else {
- printf ("[TID %ld] NUMBER[%ld] event [%p] %s:%s[%d-%d-%d] %ld\n",
- thread_id,
+ printf ("STORE EVENT [TID %ld][EVENT %ld] NUMBER[%ld] %s:%s[%d-%d-%d] %ld\n",
+ data->thread_id,
+ event - data->events,
(guint64) event->data.number,
- event,
number_event_code_to_string (event->code),
event_kind_to_string (event->kind),
event->data_type,
value);
}
}
-#define LOG_EVENT(tid,ev,val) print_event_data ((tid),(ev),(val))
+#define LOG_EVENT(data,ev,val) print_event_data ((data),(ev),(val))
#else
-#define LOG_EVENT(tid,ev,val)
+#define LOG_EVENT(data,ev,val)
#endif
#define RESULT_TO_EVENT_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_EVENT_RESULT_SUCCESS:MONO_PROFILER_EVENT_RESULT_FAILURE)
-#define STORE_EVENT_ITEM_COUNTER(p,i,dt,c,k) do {\
- ProfilerPerThreadData *data;\
- ProfilerEventData *event;\
+#define STORE_EVENT_ITEM_COUNTER(event,p,i,dt,c,k) do {\
guint64 counter;\
guint64 delta;\
- GET_PROFILER_THREAD_DATA (data);\
- GET_NEXT_FREE_EVENT (data, event);\
MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
- event->data.address = (i);\
- event->data_type = (dt);\
- event->code = (c);\
- event->kind = (k);\
+ (event)->data.address = (i);\
+ (event)->data_type = (dt);\
+ (event)->code = (c);\
+ (event)->kind = (k);\
delta = counter - data->last_event_counter;\
if (delta < MAX_EVENT_VALUE) {\
- event->value = delta;\
+ (event)->value = delta;\
} else {\
ProfilerEventData *extension = data->next_free_event;\
data->next_free_event ++;\
- event->value = MAX_EVENT_VALUE;\
+ (event)->value = MAX_EVENT_VALUE;\
*(guint64*)extension = delta;\
}\
data->last_event_counter = counter;\
- LOG_EVENT (data->thread_id, event, delta);\
+ LOG_EVENT (data, (event), delta);\
} while (0);
-#define STORE_EVENT_ITEM_VALUE(p,i,dt,c,k,v) do {\
- ProfilerPerThreadData *data;\
- ProfilerEventData *event;\
- GET_PROFILER_THREAD_DATA (data);\
- GET_NEXT_FREE_EVENT (data, event);\
- event->data.address = (i);\
- event->data_type = (dt);\
- event->code = (c);\
- event->kind = (k);\
+#define STORE_EVENT_ITEM_VALUE(event,p,i,dt,c,k,v) do {\
+ (event)->data.address = (i);\
+ (event)->data_type = (dt);\
+ (event)->code = (c);\
+ (event)->kind = (k);\
if ((v) < MAX_EVENT_VALUE) {\
- event->value = (v);\
+ (event)->value = (v);\
} else {\
ProfilerEventData *extension = data->next_free_event;\
data->next_free_event ++;\
- event->value = MAX_EVENT_VALUE;\
+ (event)->value = MAX_EVENT_VALUE;\
*(guint64*)extension = (v);\
}\
- LOG_EVENT (data->thread_id, event, (v));\
+ LOG_EVENT (data, (event), (v));\
}while (0);
-#define STORE_EVENT_NUMBER_COUNTER(p,n,dt,c,k) do {\
- ProfilerPerThreadData *data;\
- ProfilerEventData *event;\
+#define STORE_EVENT_NUMBER_COUNTER(event,p,n,dt,c,k) do {\
guint64 counter;\
guint64 delta;\
- GET_PROFILER_THREAD_DATA (data);\
- GET_NEXT_FREE_EVENT (data, event);\
MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
- event->data.number = (n);\
- event->data_type = (dt);\
- event->code = (c);\
- event->kind = (k);\
+ (event)->data.number = (n);\
+ (event)->data_type = (dt);\
+ (event)->code = (c);\
+ (event)->kind = (k);\
delta = counter - data->last_event_counter;\
if (delta < MAX_EVENT_VALUE) {\
- event->value = delta;\
+ (event)->value = delta;\
} else {\
ProfilerEventData *extension = data->next_free_event;\
data->next_free_event ++;\
- event->value = MAX_EVENT_VALUE;\
+ (event)->value = MAX_EVENT_VALUE;\
*(guint64*)extension = delta;\
}\
data->last_event_counter = counter;\
- LOG_EVENT (data->thread_id, event, delta);\
+ LOG_EVENT (data, (event), delta);\
}while (0);
-#define STORE_EVENT_NUMBER_VALUE(p,n,dt,c,k,v) do {\
- ProfilerPerThreadData *data;\
- ProfilerEventData *event;\
- GET_PROFILER_THREAD_DATA (data);\
- GET_NEXT_FREE_EVENT (data, event);\
- event->data.number = (n);\
- event->data_type = (dt);\
- event->code = (c);\
- event->kind = (k);\
+#define STORE_EVENT_NUMBER_VALUE(event,p,n,dt,c,k,v) do {\
+ (event)->data.number = (n);\
+ (event)->data_type = (dt);\
+ (event)->code = (c);\
+ (event)->kind = (k);\
if ((v) < MAX_EVENT_VALUE) {\
- event->value = (v);\
+ (event)->value = (v);\
} else {\
ProfilerEventData *extension = data->next_free_event;\
data->next_free_event ++;\
- event->value = MAX_EVENT_VALUE;\
+ (event)->value = MAX_EVENT_VALUE;\
*(guint64*)extension = (v);\
}\
- LOG_EVENT (data->thread_id, event, (v));\
+ LOG_EVENT (data, (event), (v));\
}while (0);
-
static void
class_start_load (MonoProfiler *profiler, MonoClass *klass) {
- STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD, MONO_PROFILER_EVENT_KIND_START);
+ ProfilerPerThreadData *data;
+ ProfilerEventData *event;
+ 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);
}
static void
class_end_load (MonoProfiler *profiler, MonoClass *klass, int result) {
- STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD | RESULT_TO_EVENT_CODE (result), MONO_PROFILER_EVENT_KIND_END);
+ ProfilerPerThreadData *data;
+ ProfilerEventData *event;
+ 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);
}
static void
class_start_unload (MonoProfiler *profiler, MonoClass *klass) {
- STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_START);
+ ProfilerPerThreadData *data;
+ ProfilerEventData *event;
+ 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);
}
static void
class_end_unload (MonoProfiler *profiler, MonoClass *klass) {
- STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_END);
+ ProfilerPerThreadData *data;
+ ProfilerEventData *event;
+ 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);
}
static void
method_start_jit (MonoProfiler *profiler, MonoMethod *method) {
- if (profiler->action_flags.jit_time) {
- STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT, MONO_PROFILER_EVENT_KIND_START);
- }
+ ProfilerPerThreadData *data;
+ ProfilerEventData *event;
+ GET_PROFILER_THREAD_DATA (data);
+ 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);
}
static void
method_end_jit (MonoProfiler *profiler, MonoMethod *method, int result) {
- if (profiler->action_flags.jit_time) {
- STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT | RESULT_TO_EVENT_CODE (result), MONO_PROFILER_EVENT_KIND_END);
- }
-
- if (! profiler->writer_thread_enabled) {
- WRITER_EVENT_ENABLE_RAISE ();
- }
+ ProfilerPerThreadData *data;
+ ProfilerEventData *event;
+ 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_JIT | RESULT_TO_EVENT_CODE (result), MONO_PROFILER_EVENT_KIND_END);
+ thread_stack_pop (&(data->stack));
}
#if (HAS_OPROFILE)
static void
method_enter (MonoProfiler *profiler, MonoMethod *method) {
+ ProfilerPerThreadData *data;
+
CHECK_PROFILER_ENABLED ();
- STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_START);
+ GET_PROFILER_THREAD_DATA (data);
+ if (profiler->action_flags.track_calls) {
+ 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);
+ }
+ if (profiler->action_flags.track_stack) {
+ thread_stack_push_safely (&(data->stack), method);
+ }
}
static void
method_leave (MonoProfiler *profiler, MonoMethod *method) {
+ ProfilerPerThreadData *data;
+
CHECK_PROFILER_ENABLED ();
- STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_END);
+ GET_PROFILER_THREAD_DATA (data);
+ if (profiler->action_flags.track_calls) {
+ 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);
+ }
+ if (profiler->action_flags.track_stack) {
+ thread_stack_pop (&(data->stack));
+ }
}
static void
method_free (MonoProfiler *profiler, MonoMethod *method) {
- STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_FREED, 0);
+ ProfilerPerThreadData *data;
+ ProfilerEventData *event;
+ 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);
}
static void
thread_start (MonoProfiler *profiler, gsize tid) {
- STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_START);
+ 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);
}
static void
thread_end (MonoProfiler *profiler, gsize tid) {
- STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_END);
+ 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);
}
static void
object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) {
- ProfilerPerThreadData *thread_data;
+ ProfilerPerThreadData *data;
+ ProfilerEventData *events;
+ int unsaved_frames;
+ int event_slot_count;
- STORE_EVENT_ITEM_VALUE (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_ALLOCATION, 0, (guint64) mono_object_get_size (obj));
- if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
- GET_PROFILER_THREAD_DATA (thread_data);
- STORE_ALLOCATED_OBJECT (thread_data, obj);
+ GET_PROFILER_THREAD_DATA (data);
+ event_slot_count = 1;
+ if (profiler->action_flags.save_allocation_caller) {
+ event_slot_count ++;
+ }
+ if (profiler->action_flags.allocations_carry_id) {
+ event_slot_count ++;
+ }
+ if (profiler->action_flags.save_allocation_stack) {
+ unsaved_frames = thread_stack_count_unsaved_frames (&(data->stack));
+ event_slot_count += (unsaved_frames + 1);
+ } else {
+ unsaved_frames = 0;
+ }
+ 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;
+ }
+
+ 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));
+ if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot || profiler->action_flags.collection_summary) {
+ STORE_ALLOCATED_OBJECT (data, obj);
+ }
+
+ if (profiler->action_flags.save_allocation_caller) {
+ MonoMethod *caller = thread_stack_top (&(data->stack));
+ gboolean caller_is_jitted = thread_stack_top_is_jitted (&(data->stack));
+ int index = 1;
+ events ++;
+
+ while ((caller != NULL) && (caller->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)) {
+ caller = thread_stack_index_from_top (&(data->stack), index);
+ caller_is_jitted = thread_stack_index_from_top_is_jitted (&(data->stack), index);
+ index ++;
+ }
+ if (! caller_is_jitted) {
+ STORE_EVENT_ITEM_VALUE (events, profiler, caller, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER, 0, 0);
+ } else {
+ STORE_EVENT_ITEM_VALUE (events, profiler, caller, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER, 0, 0);
+ }
+ }
+ if (profiler->action_flags.allocations_carry_id) {
+ events ++;
+ STORE_EVENT_ITEM_VALUE (events, profiler, obj, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID, 0, 0);
}
}
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) {
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);
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]);
static void
gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
- gboolean do_heap_profiling = profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot;
+ ProfilerPerThreadData *data;
+ ProfilerEventData *event;
+ gboolean do_heap_profiling = profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot || profiler->action_flags.collection_summary;
guint32 event_value;
if (ev == MONO_GC_EVENT_START) {
if (do_heap_profiling && (ev == MONO_GC_EVENT_POST_STOP_WORLD)) {
handle_heap_profiling (profiler, ev);
}
- STORE_EVENT_NUMBER_COUNTER (profiler, event_value, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, gc_event_code_from_profiler_event (ev), gc_event_kind_from_profiler_event (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));
+
if (do_heap_profiling && (ev != MONO_GC_EVENT_POST_STOP_WORLD)) {
handle_heap_profiling (profiler, ev);
}
static void
gc_resize (MonoProfiler *profiler, gint64 new_size) {
+ ProfilerPerThreadData *data;
+ ProfilerEventData *event;
+ GET_PROFILER_THREAD_DATA (data);
+ GET_NEXT_FREE_EVENT (data, event);
profiler->garbage_collection_counter ++;
- STORE_EVENT_NUMBER_VALUE (profiler, new_size, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_GC_RESIZE, 0, 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);
+}
+
+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");
+ 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");
}
/* called at the end of the program */
profiler_shutdown (MonoProfiler *prof)
{
ProfilerPerThreadData* current_thread_data;
+ ProfilerPerThreadData* next_thread_data;
LOG_WRITER_THREAD ("profiler_shutdown: zeroing relevant flags");
mono_profiler_set_events (0);
WRITER_EVENT_DESTROY ();
LOCK_PROFILER ();
- writer_thread_flush_everything ();
+ flush_everything ();
MONO_PROFILER_GET_CURRENT_TIME (profiler->end_time);
MONO_PROFILER_GET_CURRENT_COUNTER (profiler->end_counter);
write_end_block ();
FREE_PROFILER_THREAD_DATA ();
- for (current_thread_data = profiler->per_thread_data; current_thread_data != NULL; current_thread_data = current_thread_data->next) {
+ for (current_thread_data = profiler->per_thread_data; current_thread_data != NULL; current_thread_data = next_thread_data) {
+ next_thread_data = current_thread_data->next;
profiler_per_thread_data_destroy (current_thread_data);
}
if (profiler->statistical_data != NULL) {
}
#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) {
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;
}
profiler->statistical_call_chain_depth = value;
- profiler->flags |= MONO_PROFILE_STATISTICAL|MONO_PROFILE_JIT_COMPILATION;
- profiler->action_flags.jit_time = TRUE;
+ profiler->flags |= MONO_PROFILE_STATISTICAL;
}
} 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, "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 {
+ gc_request_signal_number = parse_signal_name (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-commands", equals_position) && strncmp (argument, "gc-c", equals_position) && strncmp (argument, "gcc", equals_position))) {
+ FAIL_IF_HAS_MINUS;
if (strlen (equals + 1) > 0) {
profiler->heap_shot_command_file_name = g_strdup (equals + 1);
}
} 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))) {
+ FAIL_IF_HAS_MINUS;
if (strlen (equals + 1) > 0) {
char *signal_name = equals + 1;
gc_request_signal_number = parse_signal_name (signal_name);
}
} else if (! (strncmp (argument, "toggle-signal", equals_position) && strncmp (argument, "ts", equals_position))) {
+ FAIL_IF_HAS_MINUS;
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, "gc") && strcmp (argument, "g"))) {
+ FAIL_IF_HAS_MINUS;
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.unreachable_objects = TRUE;
- 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.track_calls = TRUE_IF_NOT_MINUS;
} else if (! (strcmp (argument, "statistical") && strcmp (argument, "stat") && strcmp (argument, "s"))) {
- profiler->flags |= MONO_PROFILE_STATISTICAL;
- profiler->action_flags.jit_time = 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->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_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);
}
#endif
+ /* 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 (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;
+ }
+ 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;
+ }
+
+ /* 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;
+ }
+
+
if (profiler->file_name == NULL) {
char *program_name = g_get_prgname ();
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 ();
thread_detached = TRUE;
}
profiler->writer_thread_enabled = TRUE;
+ /* Notify that we are attached to the runtime */
+ WRITER_EVENT_DONE_RAISE ();
for (;;) {
ProfilerStatisticalData *statistical_data;
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 ();
if (profiler->terminate_writer_thread) {
LOG_WRITER_THREAD ("data_writer_thread: exiting thread");
+ CLEANUP_WRITER_THREAD ();
EXIT_THREAD ();
}
}
profiler->executable_files.new_files = NULL;
profiler->heap_shot_write_jobs = NULL;
- if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
+ if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot || profiler->action_flags.collection_summary) {
profiler_heap_buffers_setup (&(profiler->heap));
} else {
profiler_heap_buffers_clear (&(profiler->heap));
OPEN_FILE ();
write_intro_block ();
+ write_directives_block (TRUE);
mono_profiler_install (profiler, profiler_shutdown);
mono_profiler_install_statistical (statistical_hit);
mono_profiler_install_statistical_call_chain (statistical_call_chain, profiler->statistical_call_chain_depth);
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