2008-12-12 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / profiler / mono-profiler-logging.c
index c0cec4ec217f776b27b078e189f4c1da4bb30616..09f3b62428a59505174ef5726730524bb841cc8a 100644 (file)
@@ -41,6 +41,8 @@ typedef enum {
 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;
 
@@ -63,12 +65,12 @@ typedef struct _ProfilerEventData {
                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 {
@@ -97,7 +99,9 @@ typedef enum {
        MONO_PROFILER_EVENT_GC_RESIZE = 5,
        MONO_PROFILER_EVENT_GC_STOP_WORLD = 6,
        MONO_PROFILER_EVENT_GC_START_WORLD = 7,
-       MONO_PROFILER_EVENT_JIT_TIME_ALLOCATION = 8
+       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,
@@ -371,8 +375,11 @@ typedef struct _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 {
@@ -396,9 +403,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 {
@@ -849,6 +856,9 @@ struct _MonoProfiler {
                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;
@@ -959,14 +969,19 @@ 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;
@@ -975,14 +990,21 @@ thread_stack_free (ProfilerThreadStack *stack) {
                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
@@ -990,11 +1012,20 @@ thread_stack_push_jitted (ProfilerThreadStack *stack, MonoMethod* method, gboole
        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;
@@ -1010,6 +1041,9 @@ 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;
@@ -1066,6 +1100,32 @@ thread_stack_push_jitted_safely (ProfilerThreadStack *stack, MonoMethod* method,
        }
 }
 
+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) {
        return g_hash_table_lookup (profiler->classes->table, (gconstpointer) klass);
@@ -1933,9 +1993,15 @@ write_directives_block (gboolean start) {
        write_clock_data ();
        
        if (start) {
-               if (profiler->action_flags.track_stack) {
+               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);
        
@@ -2274,8 +2340,65 @@ typedef enum {
        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;
@@ -2283,6 +2406,8 @@ write_event (ProfilerEventData *event) {
        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) {
@@ -2310,19 +2435,19 @@ write_event (ProfilerEventData *event) {
                event_data = element->id;
                
                if (event->code == MONO_PROFILER_EVENT_CLASS_ALLOCATION) {
-                       if (! profiler->action_flags.track_stack) {
+                       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) {
-                                       MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION);
-                               } else if (next->code == MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER) {
-                                       MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, MONO_PROFILER_EVENT_JIT_TIME_ALLOCATION, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
-                               } else {
-                                       /* If we are tracking the stack, the next event must be the caller method */
+                               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);
@@ -2332,12 +2457,27 @@ write_event (ProfilerEventData *event) {
                                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 */
@@ -2359,6 +2499,9 @@ write_event (ProfilerEventData *event) {
                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;
@@ -2379,8 +2522,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));
+       
        while (start < end) {
-               start = write_event (start);
+               start = write_event (start, data);
        }
        WRITE_BYTE (0);
        data->first_unwritten_event = end;
@@ -2562,133 +2708,139 @@ executable_file_add_region_reference (ProfilerExecutableFile *file, ProfilerExec
 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 = (ProfilerExecutableFile*) g_hash_table_lookup (files->table, region->file_name);
                
-               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));
+               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->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;
+                       region->file = file;
+                       file->reference_count ++;
                }
-               
-               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 {
-               region->file = file;
-               file->reference_count ++;
        }
        
        if (file->header != NULL) {
@@ -3408,20 +3560,15 @@ flush_full_event_data_buffer (ProfilerPerThreadData *data) {
        UNLOCK_PROFILER ();
 }
 
-#define GET_NEXT_FREE_EVENT(d,e) {\
-       if ((d)->next_free_event >= (d)->end_event) {\
-               flush_full_event_data_buffer (d);\
-       }\
-       (e) = (d)->next_free_event;\
-       (d)->next_free_event ++;\
-} while (0)
-#define GET_NEXT_FREE_EVENT_LEAVING_ONE_FREE(d,e) {\
-       if ((d)->next_free_event >= ((d)->end_event - 2)) {\
+/* 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) {
@@ -3589,6 +3736,7 @@ method_event_code_to_string (MonoProfilerMethodEvents code) {
        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 "";
        }
 }
@@ -3602,6 +3750,9 @@ number_event_code_to_string (MonoProfilerEvents code) {
        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 "";
        }
 }
@@ -3622,12 +3773,12 @@ event_kind_to_string (MonoProfilerEventKind code) {
        }
 }
 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),
@@ -3638,10 +3789,10 @@ print_event_data (gsize thread_id, ProfilerEventData *event, guint64 value) {
                                mono_class_get_namespace ((MonoClass*) event->data.address),
                                mono_class_get_name ((MonoClass*) event->data.address));
        } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
-               printf ("[TID %ld] METHOD[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s:%s (?))\n",
-                               thread_id,
+               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),
@@ -3653,10 +3804,10 @@ print_event_data (gsize thread_id, ProfilerEventData *event, guint64 value) {
                                (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,
@@ -3665,131 +3816,133 @@ print_event_data (gsize thread_id, ProfilerEventData *event, guint64 value) {
                                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 {\
-       ProfilerEventData *event;\
+#define STORE_EVENT_ITEM_COUNTER(event,p,i,dt,c,k) do {\
        guint64 counter;\
        guint64 delta;\
-       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_NEXT(p,i,dt,c,k,v,NEXT_FREE_EVENT) do {\
-       ProfilerEventData *event;\
-       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_ITEM_VALUE(p,i,dt,c,k,v) STORE_EVENT_ITEM_VALUE_NEXT(p,i,dt,c,k,v,GET_NEXT_FREE_EVENT)
-#define STORE_EVENT_ITEM_VALUE_LEAVING_ONE_FREE(p,i,dt,c,k,v) STORE_EVENT_ITEM_VALUE_NEXT(p,i,dt,c,k,v,GET_NEXT_FREE_EVENT_LEAVING_ONE_FREE)
-#define STORE_EVENT_NUMBER_COUNTER(p,n,dt,c,k) do {\
-       ProfilerEventData *event;\
+#define STORE_EVENT_NUMBER_COUNTER(event,p,n,dt,c,k) do {\
        guint64 counter;\
        guint64 delta;\
-       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 {\
-       ProfilerEventData *event;\
-       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) {
        ProfilerPerThreadData *data;
+       ProfilerEventData *event;
        GET_PROFILER_THREAD_DATA (data);
-       STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD, MONO_PROFILER_EVENT_KIND_START);
+       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) {
        ProfilerPerThreadData *data;
+       ProfilerEventData *event;
        GET_PROFILER_THREAD_DATA (data);
-       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);
+       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) {
        ProfilerPerThreadData *data;
+       ProfilerEventData *event;
        GET_PROFILER_THREAD_DATA (data);
-       STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_START);
+       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) {
        ProfilerPerThreadData *data;
+       ProfilerEventData *event;
        GET_PROFILER_THREAD_DATA (data);
-       STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_END);
+       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) {
        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 (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT, MONO_PROFILER_EVENT_KIND_START);
+       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) {
        ProfilerPerThreadData *data;
+       ProfilerEventData *event;
        GET_PROFILER_THREAD_DATA (data);
-       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);
+       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));
 }
 
@@ -3821,7 +3974,9 @@ method_enter (MonoProfiler *profiler, MonoMethod *method) {
        CHECK_PROFILER_ENABLED ();
        GET_PROFILER_THREAD_DATA (data);
        if (profiler->action_flags.track_calls) {
-               STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_START);
+               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);
@@ -3834,7 +3989,9 @@ method_leave (MonoProfiler *profiler, MonoMethod *method) {
        CHECK_PROFILER_ENABLED ();
        GET_PROFILER_THREAD_DATA (data);
        if (profiler->action_flags.track_calls) {
-               STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_END);
+               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));
@@ -3844,64 +4001,111 @@ method_leave (MonoProfiler *profiler, MonoMethod *method) {
 static void
 method_free (MonoProfiler *profiler, MonoMethod *method) {
        ProfilerPerThreadData *data;
+       ProfilerEventData *event;
        GET_PROFILER_THREAD_DATA (data);
-       STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_FREED, 0);
+       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) {
        ProfilerPerThreadData *data;
+       ProfilerEventData *event;
        GET_PROFILER_THREAD_DATA (data);
-       STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_START);
+       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) {
        ProfilerPerThreadData *data;
+       ProfilerEventData *event;
        GET_PROFILER_THREAD_DATA (data);
-       STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_END);
+       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 *data;
+       ProfilerEventData *events;
+       int unsaved_frames;
+       int event_slot_count;
+       
        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_LEAVING_ONE_FREE (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_ALLOCATION, 0, (guint64) mono_object_get_size (obj));
+       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.track_stack) {
+       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 (profiler, caller, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER, 0, 0);
+                       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 (profiler, caller, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER, 0, 0);
+                       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);
+       }
 }
 
 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) {
@@ -3948,12 +4152,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]);
@@ -4413,11 +4617,10 @@ handle_heap_profiling (MonoProfiler *profiler, MonoGCEvent ev) {
 static void
 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
        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;
        
-       GET_PROFILER_THREAD_DATA (data);
-       
        if (ev == MONO_GC_EVENT_START) {
                profiler->garbage_collection_counter ++;
        }
@@ -4427,7 +4630,11 @@ gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
        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);
        }
@@ -4436,9 +4643,11 @@ gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
 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
@@ -4559,6 +4768,21 @@ check_signal_number (int signal_number) {
 }
 #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) {
@@ -4601,111 +4825,176 @@ 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->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.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;
+                               if (! has_minus) {
+                                       profiler->flags |= MONO_PROFILE_STATISTICAL;
+                               } else {
+                                       profiler->flags &= ~MONO_PROFILE_STATISTICAL;
+                               }
+                       } 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);
@@ -4727,6 +5016,35 @@ setup_user_options (const char *arguments) {
        }
 #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 ();