New test.
[mono.git] / mono / profiler / mono-profiler-logging.c
1 #include <config.h>
2 #include <mono/metadata/profiler.h>
3 #include <mono/metadata/class.h>
4 #include <mono/metadata/class-internals.h>
5 #include <mono/metadata/assembly.h>
6 #include <mono/metadata/loader.h>
7 #include <mono/metadata/threads.h>
8 #include <mono/metadata/debug-helpers.h>
9 #include <mono/metadata/mono-gc.h>
10 #include <mono/io-layer/atomic.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include <glib.h>
16
17 #include <dlfcn.h>
18
19 #define HAS_OPROFILE 0
20
21 #if (HAS_OPROFILE)
22 #include <libopagent.h>
23 #endif
24
25 // Needed for heap analysis
26 extern gboolean mono_object_is_alive (MonoObject* obj);
27
28 typedef enum {
29         MONO_PROFILER_FILE_BLOCK_KIND_INTRO = 1,
30         MONO_PROFILER_FILE_BLOCK_KIND_END = 2,
31         MONO_PROFILER_FILE_BLOCK_KIND_MAPPING = 3,
32         MONO_PROFILER_FILE_BLOCK_KIND_LOADED = 4,
33         MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED = 5,
34         MONO_PROFILER_FILE_BLOCK_KIND_EVENTS = 6,
35         MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL = 7,
36         MONO_PROFILER_FILE_BLOCK_KIND_HEAP_DATA = 8,
37         MONO_PROFILER_FILE_BLOCK_KIND_HEAP_SUMMARY = 9,
38         MONO_PROFILER_FILE_BLOCK_KIND_DIRECTIVES = 10
39 } MonoProfilerFileBlockKind;
40
41 typedef enum {
42         MONO_PROFILER_DIRECTIVE_END = 0,
43         MONO_PROFILER_DIRECTIVE_ALLOCATIONS_CARRY_CALLER = 1,
44         MONO_PROFILER_DIRECTIVE_ALLOCATIONS_HAVE_STACK = 2,
45         MONO_PROFILER_DIRECTIVE_ALLOCATIONS_CARRY_ID = 3,
46         MONO_PROFILER_DIRECTIVE_LAST
47 } MonoProfilerDirectives;
48
49
50 #define MONO_PROFILER_LOADED_EVENT_MODULE     1
51 #define MONO_PROFILER_LOADED_EVENT_ASSEMBLY   2
52 #define MONO_PROFILER_LOADED_EVENT_APPDOMAIN  4
53 #define MONO_PROFILER_LOADED_EVENT_SUCCESS    8
54 #define MONO_PROFILER_LOADED_EVENT_FAILURE   16
55
56 typedef enum {
57         MONO_PROFILER_EVENT_DATA_TYPE_OTHER = 0,
58         MONO_PROFILER_EVENT_DATA_TYPE_METHOD = 1,
59         MONO_PROFILER_EVENT_DATA_TYPE_CLASS = 2
60 } MonoProfilerEventDataType;
61
62 typedef struct _ProfilerEventData {
63         union {
64                 gpointer address;
65                 gsize number;
66         } data;
67         unsigned int data_type:2;
68         unsigned int code:4;
69         unsigned int kind:1;
70         unsigned int value:25;
71 } ProfilerEventData;
72
73 #define EVENT_VALUE_BITS (25)
74 #define MAX_EVENT_VALUE ((1<<EVENT_VALUE_BITS)-1)
75
76 typedef enum {
77         MONO_PROFILER_EVENT_METHOD_JIT = 0,
78         MONO_PROFILER_EVENT_METHOD_FREED = 1,
79         MONO_PROFILER_EVENT_METHOD_CALL = 2,
80         MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER = 3,
81         MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER = 4
82 } MonoProfilerMethodEvents;
83 typedef enum {
84         MONO_PROFILER_EVENT_CLASS_LOAD = 0,
85         MONO_PROFILER_EVENT_CLASS_UNLOAD = 1,
86         MONO_PROFILER_EVENT_CLASS_EXCEPTION = 2,
87         MONO_PROFILER_EVENT_CLASS_ALLOCATION = 3
88 } MonoProfilerClassEvents;
89 typedef enum {
90         MONO_PROFILER_EVENT_RESULT_SUCCESS = 0,
91         MONO_PROFILER_EVENT_RESULT_FAILURE = 4
92 } MonoProfilerEventResult;
93 #define MONO_PROFILER_EVENT_RESULT_MASK MONO_PROFILER_EVENT_RESULT_FAILURE
94 typedef enum {
95         MONO_PROFILER_EVENT_THREAD = 1,
96         MONO_PROFILER_EVENT_GC_COLLECTION = 2,
97         MONO_PROFILER_EVENT_GC_MARK = 3,
98         MONO_PROFILER_EVENT_GC_SWEEP = 4,
99         MONO_PROFILER_EVENT_GC_RESIZE = 5,
100         MONO_PROFILER_EVENT_GC_STOP_WORLD = 6,
101         MONO_PROFILER_EVENT_GC_START_WORLD = 7,
102         MONO_PROFILER_EVENT_JIT_TIME_ALLOCATION = 8,
103         MONO_PROFILER_EVENT_STACK_SECTION = 9,
104         MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID = 10
105 } MonoProfilerEvents;
106 typedef enum {
107         MONO_PROFILER_EVENT_KIND_START = 0,
108         MONO_PROFILER_EVENT_KIND_END = 1
109 } MonoProfilerEventKind;
110
111 #define MONO_PROFILER_GET_CURRENT_TIME(t) {\
112         struct timeval current_time;\
113         gettimeofday (&current_time, NULL);\
114         (t) = (((guint64)current_time.tv_sec) * 1000000) + current_time.tv_usec;\
115 } while (0)
116
117 static gboolean use_fast_timer = FALSE;
118
119 #if (defined(__i386__) || defined(__x86_64__)) && ! defined(PLATFORM_WIN32)
120
121 #if defined(__i386__)
122 static const guchar cpuid_impl [] = {
123         0x55,                           /* push   %ebp */
124         0x89, 0xe5,                     /* mov    %esp,%ebp */
125         0x53,                           /* push   %ebx */
126         0x8b, 0x45, 0x08,               /* mov    0x8(%ebp),%eax */
127         0x0f, 0xa2,                     /* cpuid   */
128         0x50,                           /* push   %eax */
129         0x8b, 0x45, 0x10,               /* mov    0x10(%ebp),%eax */
130         0x89, 0x18,                     /* mov    %ebx,(%eax) */
131         0x8b, 0x45, 0x14,               /* mov    0x14(%ebp),%eax */
132         0x89, 0x08,                     /* mov    %ecx,(%eax) */
133         0x8b, 0x45, 0x18,               /* mov    0x18(%ebp),%eax */
134         0x89, 0x10,                     /* mov    %edx,(%eax) */
135         0x58,                           /* pop    %eax */
136         0x8b, 0x55, 0x0c,               /* mov    0xc(%ebp),%edx */
137         0x89, 0x02,                     /* mov    %eax,(%edx) */
138         0x5b,                           /* pop    %ebx */
139         0xc9,                           /* leave   */
140         0xc3,                           /* ret     */
141 };
142
143 typedef void (*CpuidFunc) (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx);
144
145 static int 
146 cpuid (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx) {
147         int have_cpuid = 0;
148 #ifndef _MSC_VER
149         __asm__  __volatile__ (
150                 "pushfl\n"
151                 "popl %%eax\n"
152                 "movl %%eax, %%edx\n"
153                 "xorl $0x200000, %%eax\n"
154                 "pushl %%eax\n"
155                 "popfl\n"
156                 "pushfl\n"
157                 "popl %%eax\n"
158                 "xorl %%edx, %%eax\n"
159                 "andl $0x200000, %%eax\n"
160                 "movl %%eax, %0"
161                 : "=r" (have_cpuid)
162                 :
163                 : "%eax", "%edx"
164         );
165 #else
166         __asm {
167                 pushfd
168                 pop eax
169                 mov edx, eax
170                 xor eax, 0x200000
171                 push eax
172                 popfd
173                 pushfd
174                 pop eax
175                 xor eax, edx
176                 and eax, 0x200000
177                 mov have_cpuid, eax
178         }
179 #endif
180         if (have_cpuid) {
181                 CpuidFunc func = (CpuidFunc) cpuid_impl;
182                 func (id, p_eax, p_ebx, p_ecx, p_edx);
183                 /*
184                  * We use this approach because of issues with gcc and pic code, see:
185                  * http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gcc&pr=7329
186                 __asm__ __volatile__ ("cpuid"
187                         : "=a" (*p_eax), "=b" (*p_ebx), "=c" (*p_ecx), "=d" (*p_edx)
188                         : "a" (id));
189                 */
190                 return 1;
191         }
192         return 0;
193 }
194
195 static void detect_fast_timer (void) {
196         int p_eax, p_ebx, p_ecx, p_edx;
197         
198         if (cpuid (0x1, &p_eax, &p_ebx, &p_ecx, &p_edx)) {
199                 if (p_edx & 0x10) {
200                         use_fast_timer = TRUE;
201                 } else {
202                         use_fast_timer = FALSE;
203                 }
204         } else {
205                 use_fast_timer = FALSE;
206         }
207 }
208 #endif
209
210 #if defined(__x86_64__)
211 static void detect_fast_timer (void) {
212         guint32 op = 0x1;
213         guint32 eax,ebx,ecx,edx;
214         __asm__ __volatile__ ("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(op));
215         if (edx & 0x10) {
216                 use_fast_timer = TRUE;
217         } else {
218                 use_fast_timer = FALSE;
219         }
220 }
221 #endif
222
223 static __inline__ guint64 rdtsc(void) {
224         guint32 hi, lo;
225         __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
226         return ((guint64) lo) | (((guint64) hi) << 32);
227 }
228 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) {\
229         if (use_fast_timer) {\
230                 (c) = rdtsc ();\
231         } else {\
232                 MONO_PROFILER_GET_CURRENT_TIME ((c));\
233         }\
234 } while (0)
235 #else
236 static void detect_fast_timer (void) {
237         use_fast_timer = FALSE;
238 }
239 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) MONO_PROFILER_GET_CURRENT_TIME ((c))
240 #endif
241
242
243 #define CLASS_LAYOUT_PACKED_BITMAP_SIZE 64
244 #define CLASS_LAYOUT_NOT_INITIALIZED (0xFFFF)
245 typedef enum {
246         HEAP_CODE_NONE = 0,
247         HEAP_CODE_OBJECT = 1,
248         HEAP_CODE_FREE_OBJECT_CLASS = 2,
249         HEAP_CODE_MASK = 3
250 } HeapProfilerJobValueCode;
251 typedef struct _MonoProfilerClassData {
252         union {
253                 guint64 compact;
254                 guint8 *extended;
255         } bitmap;
256         struct {
257                 guint16 slots;
258                 guint16 references;
259         } layout;
260 } MonoProfilerClassData;
261
262 typedef struct _MonoProfilerMethodData {
263         gpointer code_start;
264         guint32 code_size;
265 } MonoProfilerMethodData;
266
267 typedef struct _ClassIdMappingElement {
268         char *name;
269         guint32 id;
270         MonoClass *klass;
271         struct _ClassIdMappingElement *next_unwritten;
272         MonoProfilerClassData data;
273 } ClassIdMappingElement;
274
275 typedef struct _MethodIdMappingElement {
276         char *name;
277         guint32 id;
278         MonoMethod *method;
279         struct _MethodIdMappingElement *next_unwritten;
280         MonoProfilerMethodData data;
281 } MethodIdMappingElement;
282
283 typedef struct _ClassIdMapping {
284         GHashTable *table;
285         ClassIdMappingElement *unwritten;
286         guint32 next_id;
287 } ClassIdMapping;
288
289 typedef struct _MethodIdMapping {
290         GHashTable *table;
291         MethodIdMappingElement *unwritten;
292         guint32 next_id;
293 } MethodIdMapping;
294
295 typedef struct _LoadedElement {
296         char *name;
297         guint64 load_start_counter;
298         guint64 load_end_counter;
299         guint64 unload_start_counter;
300         guint64 unload_end_counter;
301         guint8 loaded;
302         guint8 load_written;
303         guint8 unloaded;
304         guint8 unload_written;
305 } LoadedElement;
306
307 #define PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE 1024
308 #define PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE 4096
309 #define PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE 4096
310
311 typedef struct _ProfilerHeapShotObjectBuffer {
312         struct _ProfilerHeapShotObjectBuffer *next;
313         MonoObject **next_free_slot;
314         MonoObject **end;
315         MonoObject **first_unprocessed_slot;
316         MonoObject *buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE];
317 } ProfilerHeapShotObjectBuffer;
318
319 typedef struct _ProfilerHeapShotHeapBuffer {
320         struct _ProfilerHeapShotHeapBuffer *next;
321         struct _ProfilerHeapShotHeapBuffer *previous;
322         MonoObject **start_slot;
323         MonoObject **end_slot;
324         MonoObject *buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE];
325 } ProfilerHeapShotHeapBuffer;
326
327 typedef struct _ProfilerHeapShotHeapBuffers {
328         ProfilerHeapShotHeapBuffer *buffers;
329         ProfilerHeapShotHeapBuffer *last;
330         ProfilerHeapShotHeapBuffer *current;
331         MonoObject **first_free_slot;
332 } ProfilerHeapShotHeapBuffers;
333
334
335 typedef struct _ProfilerHeapShotWriteBuffer {
336         struct _ProfilerHeapShotWriteBuffer *next;
337         gpointer buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE];
338 } ProfilerHeapShotWriteBuffer;
339
340 typedef struct _ProfilerHeapShotClassSummary {
341         struct {
342                 guint32 instances;
343                 guint32 bytes;
344         } reachable;
345         struct {
346                 guint32 instances;
347                 guint32 bytes;
348         } unreachable;
349 } ProfilerHeapShotClassSummary;
350
351 typedef struct _ProfilerHeapShotCollectionSummary {
352         ProfilerHeapShotClassSummary *per_class_data;
353         guint32 capacity;
354 } ProfilerHeapShotCollectionSummary;
355
356 typedef struct _ProfilerHeapShotWriteJob {
357         struct _ProfilerHeapShotWriteJob *next;
358         struct _ProfilerHeapShotWriteJob *next_unwritten;
359         gpointer *start;
360         gpointer *cursor;
361         gpointer *end;
362         ProfilerHeapShotWriteBuffer *buffers;
363         ProfilerHeapShotWriteBuffer **last_next;
364         guint32 full_buffers;
365         gboolean heap_shot_was_signalled;
366         guint64 start_counter;
367         guint64 start_time;
368         guint64 end_counter;
369         guint64 end_time;
370         guint32 collection;
371         ProfilerHeapShotCollectionSummary summary;
372         gboolean dump_heap_data;
373 } ProfilerHeapShotWriteJob;
374
375 typedef struct _ProfilerThreadStack {
376         guint32 capacity;
377         guint32 top;
378         guint32 last_saved_top;
379         guint32 last_written_frame;
380         MonoMethod **stack;
381         guint8 *method_is_jitted;
382         guint32 *written_frames;
383 } ProfilerThreadStack;
384
385 typedef struct _ProfilerPerThreadData {
386         ProfilerEventData *events;
387         ProfilerEventData *next_free_event;
388         ProfilerEventData *end_event;
389         ProfilerEventData *first_unwritten_event;
390         ProfilerEventData *first_unmapped_event;
391         guint64 start_event_counter;
392         guint64 last_event_counter;
393         gsize thread_id;
394         ProfilerHeapShotObjectBuffer *heap_shot_object_buffers;
395         ProfilerThreadStack stack;
396         struct _ProfilerPerThreadData* next;
397 } ProfilerPerThreadData;
398
399 typedef struct _ProfilerStatisticalHit {
400         gpointer *address;
401         MonoDomain *domain;
402 } ProfilerStatisticalHit;
403
404 typedef struct _ProfilerStatisticalData {
405         ProfilerStatisticalHit *hits;
406         unsigned int next_free_index;
407         unsigned int end_index;
408         unsigned int first_unwritten_index;
409 } ProfilerStatisticalData;
410
411 typedef struct _ProfilerUnmanagedSymbol {
412         guint32 offset;
413         guint32 size;
414         guint32 id;
415         guint32 index;
416 } ProfilerUnmanagedSymbol;
417
418 struct _ProfilerExecutableFile;
419 struct _ProfilerExecutableFileSectionRegion;
420
421 typedef struct _ProfilerExecutableMemoryRegionData {
422         gpointer start;
423         gpointer end;
424         guint32 file_offset;
425         char *file_name;
426         guint32 id;
427         gboolean is_new;
428         
429         struct _ProfilerExecutableFile *file;
430         struct _ProfilerExecutableFileSectionRegion *file_region_reference;
431         guint32 symbols_count;
432         guint32 symbols_capacity;
433         ProfilerUnmanagedSymbol *symbols;
434 } ProfilerExecutableMemoryRegionData;
435
436 typedef struct _ProfilerExecutableMemoryRegions {
437         ProfilerExecutableMemoryRegionData **regions;
438         guint32 regions_capacity;
439         guint32 regions_count;
440         guint32 next_id;
441         guint32 next_unmanaged_function_id;
442 } ProfilerExecutableMemoryRegions;
443
444 /* Start of ELF definitions */
445 #define EI_NIDENT 16
446 typedef guint16 ElfHalf;
447 typedef guint32 ElfWord;
448 typedef gsize ElfAddr;
449 typedef gsize ElfOff;
450
451 typedef struct {
452         unsigned char e_ident[EI_NIDENT];
453         ElfHalf e_type;
454         ElfHalf e_machine;
455         ElfWord e_version;
456         ElfAddr e_entry;
457         ElfOff  e_phoff;
458         ElfOff  e_shoff; // Section header table
459         ElfWord e_flags;
460         ElfHalf e_ehsize; // Header size
461         ElfHalf e_phentsize;
462         ElfHalf e_phnum;
463         ElfHalf e_shentsize; // Section header entry size
464         ElfHalf e_shnum; // Section header entries number
465         ElfHalf e_shstrndx; // String table index
466 } ElfHeader;
467
468 #if (SIZEOF_VOID_P == 4)
469 typedef struct {
470         ElfWord sh_name;
471         ElfWord sh_type;
472         ElfWord sh_flags;
473         ElfAddr sh_addr; // Address in memory
474         ElfOff  sh_offset; // Offset in file
475         ElfWord sh_size;
476         ElfWord sh_link;
477         ElfWord sh_info;
478         ElfWord sh_addralign;
479         ElfWord sh_entsize;
480 } ElfSection;
481 typedef struct {
482         ElfWord       st_name;
483         ElfAddr       st_value;
484         ElfWord       st_size;
485         unsigned char st_info; // Use ELF32_ST_TYPE to get symbol type
486         unsigned char st_other;
487         ElfHalf       st_shndx; // Or one of SHN_ABS, SHN_COMMON or SHN_UNDEF.
488 } ElfSymbol;
489 #elif (SIZEOF_VOID_P == 8)
490 typedef struct {
491         ElfWord sh_name;
492         ElfWord sh_type;
493         ElfOff sh_flags;
494         ElfAddr sh_addr; // Address in memory
495         ElfOff  sh_offset; // Offset in file
496         ElfOff sh_size;
497         ElfWord sh_link;
498         ElfWord sh_info;
499         ElfOff sh_addralign;
500         ElfOff sh_entsize;
501 } ElfSection;
502 typedef struct {
503         ElfWord       st_name;
504         unsigned char st_info; // Use ELF_ST_TYPE to get symbol type
505         unsigned char st_other;
506         ElfHalf       st_shndx; // Or one of SHN_ABS, SHN_COMMON or SHN_UNDEF.
507         ElfAddr       st_value;
508         ElfAddr       st_size;
509 } ElfSymbol;
510 #else
511 #error Bad size of void pointer
512 #endif
513
514
515 #define ELF_ST_BIND(i)   ((i)>>4)
516 #define ELF_ST_TYPE(i)   ((i)&0xf)
517
518
519 typedef enum {
520         EI_MAG0 = 0,
521         EI_MAG1 = 1,
522         EI_MAG2 = 2,
523         EI_MAG3 = 3,
524         EI_CLASS = 4,
525         EI_DATA = 5
526 } ElfIdentFields;
527
528 typedef enum {
529         ELF_FILE_TYPE_NONE = 0,
530         ELF_FILE_TYPE_REL = 1,
531         ELF_FILE_TYPE_EXEC = 2,
532         ELF_FILE_TYPE_DYN = 3,
533         ELF_FILE_TYPE_CORE = 4
534 } ElfFileType;
535
536 typedef enum {
537         ELF_CLASS_NONE = 0,
538         ELF_CLASS_32 = 1,
539         ELF_CLASS_64 = 2
540 } ElfIdentClass;
541
542 typedef enum {
543         ELF_DATA_NONE = 0,
544         ELF_DATA_LSB = 1,
545         ELF_DATA_MSB = 2
546 } ElfIdentData;
547
548 typedef enum {
549         ELF_SHT_NULL = 0,
550         ELF_SHT_PROGBITS = 1,
551         ELF_SHT_SYMTAB = 2,
552         ELF_SHT_STRTAB = 3,
553         ELF_SHT_RELA = 4,
554         ELF_SHT_HASH = 5,
555         ELF_SHT_DYNAMIC = 6,
556         ELF_SHT_NOTE = 7,
557         ELF_SHT_NOBITS = 8,
558         ELF_SHT_REL = 9,
559         ELF_SHT_SHLIB = 10,
560         ELF_SHT_DYNSYM = 11
561 } ElfSectionType;
562
563 typedef enum {
564         ELF_STT_NOTYPE = 0,
565         ELF_STT_OBJECT = 1,
566         ELF_STT_FUNC = 2,
567         ELF_STT_SECTION = 3,
568         ELF_STT_FILE = 4
569 } ElfSymbolType;
570
571 typedef enum {
572         ELF_SHF_WRITE = 1,
573         ELF_SHF_ALLOC = 2,
574         ELF_SHF_EXECINSTR = 4,
575 } ElfSectionFlags;
576
577 #define ELF_SHN_UNDEF       0
578 #define ELF_SHN_LORESERVE   0xff00
579 #define ELF_SHN_LOPROC      0xff00
580 #define ELF_SHN_HIPROC      0xff1f
581 #define ELF_SHN_ABS         0xfff1
582 #define ELF_SHN_COMMON      0xfff2
583 #define ELF_SHN_HIRESERVE   0xffff
584 /* End of ELF definitions */
585
586 typedef struct _ProfilerExecutableFileSectionRegion {
587         ProfilerExecutableMemoryRegionData *region;
588         guint8 *section_address;
589         gsize section_offset;
590 } ProfilerExecutableFileSectionRegion;
591
592 typedef struct _ProfilerExecutableFile {
593         guint32 reference_count;
594         
595         /* Used for mmap and munmap */
596         int fd;
597         guint8 *data;
598         size_t length;
599         
600         /* File data */
601         ElfHeader *header;
602         guint8 *symbols_start;
603         guint32 symbols_count;
604         guint32 symbol_size;
605         const char *symbols_string_table;
606         const char *main_string_table;
607         
608         ProfilerExecutableFileSectionRegion *section_regions;
609         
610         struct _ProfilerExecutableFile *next_new_file;
611 } ProfilerExecutableFile;
612
613 typedef struct _ProfilerExecutableFiles {
614         GHashTable *table;
615         ProfilerExecutableFile *new_files;
616 } ProfilerExecutableFiles;
617
618
619 #define CLEANUP_WRITER_THREAD() do {profiler->writer_thread_terminated = TRUE;} while (0)
620 #define CHECK_WRITER_THREAD() (! profiler->writer_thread_terminated)
621
622 #ifndef PLATFORM_WIN32
623 #include <sys/types.h>
624 #include <sys/time.h>
625 #include <sys/stat.h>
626 #include <unistd.h>
627 #include <fcntl.h>
628 #include <pthread.h>
629 #include <semaphore.h>
630
631 #include <sys/mman.h>
632 #include <sys/types.h>
633 #include <sys/stat.h>
634 #include <unistd.h>
635 #include <errno.h>
636
637 #define MUTEX_TYPE pthread_mutex_t
638 #define INITIALIZE_PROFILER_MUTEX() pthread_mutex_init (&(profiler->mutex), NULL)
639 #define DELETE_PROFILER_MUTEX() pthread_mutex_destroy (&(profiler->mutex))
640 #define LOCK_PROFILER() do {/*LOG_WRITER_THREAD ("LOCK_PROFILER");*/ pthread_mutex_lock (&(profiler->mutex));} while (0)
641 #define UNLOCK_PROFILER() do {/*LOG_WRITER_THREAD ("UNLOCK_PROFILER");*/ pthread_mutex_unlock (&(profiler->mutex));} while (0)
642
643 #define THREAD_TYPE pthread_t
644 #define CREATE_WRITER_THREAD(f) pthread_create (&(profiler->data_writer_thread), NULL, ((void*(*)(void*))f), NULL)
645 #define EXIT_THREAD() pthread_exit (NULL);
646 #define WAIT_WRITER_THREAD() do {\
647         if (CHECK_WRITER_THREAD ()) {\
648                 pthread_join (profiler->data_writer_thread, NULL);\
649         }\
650 } while (0)
651 #define CURRENT_THREAD_ID() (gsize) pthread_self ()
652
653 #ifndef HAVE_KW_THREAD
654 static pthread_key_t pthread_profiler_key;
655 static pthread_once_t profiler_pthread_once = PTHREAD_ONCE_INIT;
656 static void
657 make_pthread_profiler_key (void) {
658     (void) pthread_key_create (&pthread_profiler_key, NULL);
659 }
660 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) pthread_getspecific (pthread_profiler_key))
661 #define SET_PROFILER_THREAD_DATA(x) (void) pthread_setspecific (pthread_profiler_key, (x))
662 #define ALLOCATE_PROFILER_THREAD_DATA() (void) pthread_once (&profiler_pthread_once, make_pthread_profiler_key)
663 #define FREE_PROFILER_THREAD_DATA() (void) pthread_key_delete (pthread_profiler_key)
664 #endif
665
666 #define EVENT_TYPE sem_t
667 #define WRITER_EVENT_INIT() do {\
668         sem_init (&(profiler->enable_data_writer_event), 0, 0);\
669         sem_init (&(profiler->wake_data_writer_event), 0, 0);\
670         sem_init (&(profiler->done_data_writer_event), 0, 0);\
671 } while (0)
672 #define WRITER_EVENT_DESTROY() do {\
673         sem_destroy (&(profiler->enable_data_writer_event));\
674         sem_destroy (&(profiler->wake_data_writer_event));\
675         sem_destroy (&(profiler->done_data_writer_event));\
676 } while (0)
677 #define WRITER_EVENT_WAIT() (void) sem_wait (&(profiler->wake_data_writer_event))
678 #define WRITER_EVENT_RAISE() (void) sem_post (&(profiler->wake_data_writer_event))
679 #define WRITER_EVENT_ENABLE_WAIT() (void) sem_wait (&(profiler->enable_data_writer_event))
680 #define WRITER_EVENT_ENABLE_RAISE() (void) sem_post (&(profiler->enable_data_writer_event))
681 #define WRITER_EVENT_DONE_WAIT() do {\
682         if (CHECK_WRITER_THREAD ()) {\
683                 (void) sem_wait (&(profiler->done_data_writer_event));\
684         }\
685 } while (0)
686 #define WRITER_EVENT_DONE_RAISE() (void) sem_post (&(profiler->done_data_writer_event))
687
688 #if 0
689 #define FILE_HANDLE_TYPE FILE*
690 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
691 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
692 #define FLUSH_FILE() fflush (profiler->file)
693 #define CLOSE_FILE() fclose (profiler->file);
694 #else
695 #define FILE_HANDLE_TYPE int
696 #define OPEN_FILE() profiler->file = open (profiler->file_name, O_WRONLY|O_CREAT|O_TRUNC, 0664);
697 #define WRITE_BUFFER(b,s) write (profiler->file, (b), (s))
698 #define FLUSH_FILE()
699 #define CLOSE_FILE() close (profiler->file);
700 #endif
701
702 #else
703
704 #include <windows.h>
705
706 #define MUTEX_TYPE CRITICAL_SECTION
707 #define INITIALIZE_PROFILER_MUTEX() InitializeCriticalSection (&(profiler->mutex))
708 #define DELETE_PROFILER_MUTEX() DeleteCriticalSection (&(profiler->mutex))
709 #define LOCK_PROFILER() EnterCriticalSection (&(profiler->mutex))
710 #define UNLOCK_PROFILER() LeaveCriticalSection (&(profiler->mutex))
711
712 #define THREAD_TYPE HANDLE
713 #define CREATE_WRITER_THREAD(f) CreateThread (NULL, (1*1024*1024), (f), NULL, 0, NULL);
714 #define EXIT_THREAD() ExitThread (0);
715 #define WAIT_WRITER_THREAD() do {\
716         if (CHECK_WRITER_THREAD ()) {\
717                  WaitForSingleObject (profiler->data_writer_thread, INFINITE);\
718         }\
719 } while (0)
720 #define CURRENT_THREAD_ID() (gsize) GetCurrentThreadId ()
721
722 #ifndef HAVE_KW_THREAD
723 static guint32 profiler_thread_id = -1;
724 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*)TlsGetValue (profiler_thread_id))
725 #define SET_PROFILER_THREAD_DATA(x) TlsSetValue (profiler_thread_id, (x));
726 #define ALLOCATE_PROFILER_THREAD_DATA() profiler_thread_id = TlsAlloc ()
727 #define FREE_PROFILER_THREAD_DATA() TlsFree (profiler_thread_id)
728 #endif
729
730 #define EVENT_TYPE HANDLE
731 #define WRITER_EVENT_INIT() (void) do {\
732         profiler->enable_data_writer_event = CreateEvent (NULL, FALSE, FALSE, NULL);\
733         profiler->wake_data_writer_event = CreateEvent (NULL, FALSE, FALSE, NULL);\
734         profiler->done_data_writer_event = CreateEvent (NULL, FALSE, FALSE, NULL);\
735 } while (0)
736 #define WRITER_EVENT_DESTROY() CloseHandle (profiler->statistical_data_writer_event)
737 #define WRITER_EVENT_INIT() (void) do {\
738         CloseHandle (profiler->enable_data_writer_event);\
739         CloseHandle (profiler->wake_data_writer_event);\
740         CloseHandle (profiler->done_data_writer_event);\
741 } while (0)
742 #define WRITER_EVENT_WAIT() WaitForSingleObject (profiler->wake_data_writer_event, INFINITE)
743 #define WRITER_EVENT_RAISE() SetEvent (profiler->wake_data_writer_event)
744 #define WRITER_EVENT_ENABLE_WAIT() WaitForSingleObject (profiler->enable_data_writer_event, INFINITE)
745 #define WRITER_EVENT_ENABLE_RAISE() SetEvent (profiler->enable_data_writer_event)
746 #define WRITER_EVENT_DONE_WAIT() do {\
747         if (CHECK_WRITER_THREAD ()) {\
748                 WaitForSingleObject (profiler->done_data_writer_event, INFINITE);\
749         }\
750 } while (0)
751 #define WRITER_EVENT_DONE_RAISE() SetEvent (profiler->done_data_writer_event)
752
753 #define FILE_HANDLE_TYPE FILE*
754 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
755 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
756 #define FLUSH_FILE() fflush (profiler->file)
757 #define CLOSE_FILE() fclose (profiler->file);
758
759 #endif
760
761 #ifdef HAVE_KW_THREAD
762 static __thread ProfilerPerThreadData * tls_profiler_per_thread_data;
763 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) tls_profiler_per_thread_data)
764 #define SET_PROFILER_THREAD_DATA(x) tls_profiler_per_thread_data = (x)
765 #define ALLOCATE_PROFILER_THREAD_DATA() /* nop */
766 #define FREE_PROFILER_THREAD_DATA() /* nop */
767 #endif
768
769 #define GET_PROFILER_THREAD_DATA(data) do {\
770         ProfilerPerThreadData *_result = LOOKUP_PROFILER_THREAD_DATA ();\
771         if (!_result) {\
772                 _result = profiler_per_thread_data_new (profiler->per_thread_buffer_size);\
773                 LOCK_PROFILER ();\
774                 _result->next = profiler->per_thread_data;\
775                 profiler->per_thread_data = _result;\
776                 UNLOCK_PROFILER ();\
777                 SET_PROFILER_THREAD_DATA (_result);\
778         }\
779         (data) = _result;\
780 } while (0)
781
782 #define PROFILER_FILE_WRITE_BUFFER_SIZE (profiler->write_buffer_size)
783 typedef struct _ProfilerFileWriteBuffer {
784         struct _ProfilerFileWriteBuffer *next;
785         guint8 buffer [];
786 } ProfilerFileWriteBuffer;
787
788 #define CHECK_PROFILER_ENABLED() do {\
789         if (! profiler->profiler_enabled)\
790                 return;\
791 } while (0)
792 struct _MonoProfiler {
793         MUTEX_TYPE mutex;
794         
795         MonoProfileFlags flags;
796         gboolean profiler_enabled;
797         char *file_name;
798         char *file_name_suffix;
799         FILE_HANDLE_TYPE file;
800         
801         guint64 start_time;
802         guint64 start_counter;
803         guint64 end_time;
804         guint64 end_counter;
805         
806         guint64 last_header_counter;
807         
808         MethodIdMapping *methods;
809         ClassIdMapping *classes;
810         
811         GHashTable *loaded_assemblies;
812         GHashTable *loaded_modules;
813         GHashTable *loaded_appdomains;
814         
815         guint32 per_thread_buffer_size;
816         guint32 statistical_buffer_size;
817         ProfilerPerThreadData* per_thread_data;
818         ProfilerStatisticalData *statistical_data;
819         ProfilerStatisticalData *statistical_data_ready;
820         ProfilerStatisticalData *statistical_data_second_buffer;
821         int statistical_call_chain_depth;
822         
823         THREAD_TYPE data_writer_thread;
824         EVENT_TYPE enable_data_writer_event;
825         EVENT_TYPE wake_data_writer_event;
826         EVENT_TYPE done_data_writer_event;
827         gboolean terminate_writer_thread;
828         gboolean writer_thread_terminated;
829         gboolean detach_writer_thread;
830         gboolean writer_thread_enabled;
831         gboolean writer_thread_flush_everything;
832         
833         ProfilerFileWriteBuffer *write_buffers;
834         ProfilerFileWriteBuffer *current_write_buffer;
835         int write_buffer_size;
836         int current_write_position;
837         int full_write_buffers;
838         
839         ProfilerHeapShotWriteJob *heap_shot_write_jobs;
840         ProfilerHeapShotHeapBuffers heap;
841         
842         char *heap_shot_command_file_name;
843         int dump_next_heap_snapshots;
844         guint64 heap_shot_command_file_access_time;
845         gboolean heap_shot_was_signalled;
846         guint32 garbage_collection_counter;
847         
848         ProfilerExecutableMemoryRegions *executable_regions;
849         ProfilerExecutableFiles executable_files;
850         
851         struct {
852 #if (HAS_OPROFILE)
853                 gboolean oprofile;
854 #endif
855                 gboolean jit_time;
856                 gboolean unreachable_objects;
857                 gboolean collection_summary;
858                 gboolean heap_shot;
859                 gboolean track_stack;
860                 gboolean track_calls;
861                 gboolean save_allocation_caller;
862                 gboolean save_allocation_stack;
863                 gboolean allocations_carry_id;
864         } action_flags;
865 };
866 static MonoProfiler *profiler;
867
868 #ifndef PLATFORM_WIN32
869 #include <signal.h>
870
871 #ifdef MONO_ARCH_USE_SIGACTION
872 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, siginfo_t *info, void *context)
873 #elif defined(__sparc__)
874 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, void *sigctx)
875 #else
876 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy)
877 #endif
878
879 static void
880 request_heap_snapshot (void) {
881         profiler->heap_shot_was_signalled = TRUE;
882         mono_gc_collect (mono_gc_max_generation ());
883 }
884
885 static void
886 SIG_HANDLER_SIGNATURE (gc_request_handler) {
887         profiler->heap_shot_was_signalled = TRUE;
888         WRITER_EVENT_RAISE ();
889 }
890
891 static void
892 add_gc_request_handler (int signal_number)
893 {
894         struct sigaction sa;
895         
896 #ifdef MONO_ARCH_USE_SIGACTION
897         sa.sa_sigaction = gc_request_handler;
898         sigemptyset (&sa.sa_mask);
899         sa.sa_flags = SA_SIGINFO;
900 #else
901         sa.sa_handler = gc_request_handler;
902         sigemptyset (&sa.sa_mask);
903         sa.sa_flags = 0;
904 #endif
905         
906         g_assert (sigaction (signal_number, &sa, NULL) != -1);
907 }
908
909 static void
910 enable_profiler (void) {
911         profiler->profiler_enabled = TRUE;
912 }
913
914 static void
915 disable_profiler (void) {
916         profiler->profiler_enabled = FALSE;
917 }
918
919
920
921 static void
922 SIG_HANDLER_SIGNATURE (toggle_handler) {
923         if (profiler->profiler_enabled) {
924                 profiler->profiler_enabled = FALSE;
925         } else {
926                 profiler->profiler_enabled = TRUE;
927         }
928 }
929
930 static void
931 add_toggle_handler (int signal_number)
932 {
933         struct sigaction sa;
934         
935 #ifdef MONO_ARCH_USE_SIGACTION
936         sa.sa_sigaction = toggle_handler;
937         sigemptyset (&sa.sa_mask);
938         sa.sa_flags = SA_SIGINFO;
939 #else
940         sa.sa_handler = toggle_handler;
941         sigemptyset (&sa.sa_mask);
942         sa.sa_flags = 0;
943 #endif
944         
945         g_assert (sigaction (signal_number, &sa, NULL) != -1);
946 }
947 #endif
948
949
950
951 #define DEBUG_LOAD_EVENTS 0
952 #define DEBUG_MAPPING_EVENTS 0
953 #define DEBUG_LOGGING_PROFILER 0
954 #define DEBUG_HEAP_PROFILER 0
955 #define DEBUG_CLASS_BITMAPS 0
956 #define DEBUG_STATISTICAL_PROFILER 0
957 #define DEBUG_WRITER_THREAD 0
958 #define DEBUG_FILE_WRITES 0
959 #if (DEBUG_LOGGING_PROFILER || DEBUG_STATISTICAL_PROFILER || DEBUG_HEAP_PROFILER || DEBUG_WRITER_THREAD || DEBUG_FILE_WRITES)
960 #define LOG_WRITER_THREAD(m) printf ("WRITER-THREAD-LOG %s\n", m)
961 #else
962 #define LOG_WRITER_THREAD(m)
963 #endif
964
965 #if DEBUG_LOGGING_PROFILER
966 static int event_counter = 0;
967 #define EVENT_MARK() printf ("[EVENT:%d]", ++ event_counter)
968 #endif
969
970 static void
971 thread_stack_initialize_empty (ProfilerThreadStack *stack) {
972         stack->capacity = 0;
973         stack->top = 0;
974         stack->last_saved_top = 0;
975         stack->last_written_frame = 0;
976         stack->stack = NULL;
977         stack->method_is_jitted = NULL;
978         stack->written_frames = NULL;
979 }
980
981 static void
982 thread_stack_free (ProfilerThreadStack *stack) {
983         stack->capacity = 0;
984         stack->top = 0;
985         stack->last_saved_top = 0;
986         stack->last_written_frame = 0;
987         if (stack->stack != NULL) {
988                 g_free (stack->stack);
989                 stack->stack = NULL;
990         }
991         if (stack->method_is_jitted != NULL) {
992                 g_free (stack->method_is_jitted);
993                 stack->method_is_jitted = NULL;
994         }
995         if (stack->written_frames != NULL) {
996                 g_free (stack->written_frames);
997                 stack->written_frames = NULL;
998         }
999 }
1000
1001 static void
1002 thread_stack_initialize (ProfilerThreadStack *stack, guint32 capacity) {
1003         stack->capacity = capacity;
1004         stack->top = 0;
1005         stack->last_saved_top = 0;
1006         stack->last_written_frame = 0;
1007         stack->stack = g_new0 (MonoMethod*, capacity);
1008         stack->method_is_jitted = g_new0 (guint8, capacity);
1009         stack->written_frames = g_new0 (guint32, capacity);
1010 }
1011
1012 static void
1013 thread_stack_push_jitted (ProfilerThreadStack *stack, MonoMethod* method, gboolean method_is_jitted) {
1014         if (stack->top >= stack->capacity) {
1015                 MonoMethod **old_stack = stack->stack;
1016                 guint8 *old_method_is_jitted = stack->method_is_jitted;
1017                 guint32 *old_written_frames = stack->written_frames;
1018                 guint32 top = stack->top;
1019                 guint32 last_saved_top = stack->last_saved_top;
1020                 guint32 last_written_frame = stack->last_written_frame;
1021                 thread_stack_initialize (stack, stack->capacity * 2);
1022                 memcpy (stack->stack, old_stack, top * sizeof (MonoMethod*));
1023                 memcpy (stack->method_is_jitted, old_method_is_jitted, top * sizeof (guint8));
1024                 memcpy (stack->written_frames, old_written_frames, top * sizeof (guint32));
1025                 g_free (old_stack);
1026                 g_free (old_method_is_jitted);
1027                 g_free (old_written_frames);
1028                 stack->top = top;
1029                 stack->last_saved_top = last_saved_top;
1030                 stack->last_written_frame = last_written_frame;
1031         }
1032         stack->stack [stack->top] = method;
1033         stack->method_is_jitted [stack->top] = method_is_jitted;
1034         stack->top ++;
1035 }
1036
1037 static inline void
1038 thread_stack_push (ProfilerThreadStack *stack, MonoMethod* method) {
1039         thread_stack_push_jitted (stack, method, FALSE);
1040 }
1041
1042 static MonoMethod*
1043 thread_stack_pop (ProfilerThreadStack *stack) {
1044         if (stack->top > 0) {
1045                 stack->top --;
1046                 if (stack->last_saved_top > stack->top) {
1047                         stack->last_saved_top = stack->top;
1048                 }
1049                 return stack->stack [stack->top];
1050         } else {
1051                 return NULL;
1052         }
1053 }
1054
1055 static MonoMethod*
1056 thread_stack_top (ProfilerThreadStack *stack) {
1057         if (stack->top > 0) {
1058                 return stack->stack [stack->top - 1];
1059         } else {
1060                 return NULL;
1061         }
1062 }
1063
1064 static gboolean
1065 thread_stack_top_is_jitted (ProfilerThreadStack *stack) {
1066         if (stack->top > 0) {
1067                 return stack->method_is_jitted [stack->top - 1];
1068         } else {
1069                 return FALSE;
1070         }
1071 }
1072
1073 static MonoMethod*
1074 thread_stack_index_from_top (ProfilerThreadStack *stack, int index) {
1075         if (stack->top > index) {
1076                 return stack->stack [stack->top - (index + 1)];
1077         } else {
1078                 return NULL;
1079         }
1080 }
1081
1082 static gboolean
1083 thread_stack_index_from_top_is_jitted (ProfilerThreadStack *stack, int index) {
1084         if (stack->top > index) {
1085                 return stack->method_is_jitted [stack->top - (index + 1)];
1086         } else {
1087                 return FALSE;
1088         }
1089 }
1090
1091 static inline void
1092 thread_stack_push_safely (ProfilerThreadStack *stack, MonoMethod* method) {
1093         if (stack->stack != NULL) {
1094                 thread_stack_push (stack, method);
1095         }
1096 }
1097
1098 static inline void
1099 thread_stack_push_jitted_safely (ProfilerThreadStack *stack, MonoMethod* method, gboolean method_is_jitted) {
1100         if (stack->stack != NULL) {
1101                 thread_stack_push_jitted (stack, method, method_is_jitted);
1102         }
1103 }
1104
1105 static inline int
1106 thread_stack_count_unsaved_frames (ProfilerThreadStack *stack) {
1107         int result = stack->top - stack->last_saved_top;
1108         return (result > 0) ? result : 0;
1109 }
1110
1111 static inline int
1112 thread_stack_get_last_written_frame (ProfilerThreadStack *stack) {
1113         return stack->last_written_frame;
1114 }
1115
1116 static inline void
1117 thread_stack_set_last_written_frame (ProfilerThreadStack *stack, int last_written_frame) {
1118         stack->last_written_frame = last_written_frame;
1119 }
1120
1121 static inline guint32
1122 thread_stack_written_frame_at_index (ProfilerThreadStack *stack, int index) {
1123         return stack->written_frames [index];
1124 }
1125
1126 static inline void
1127 thread_stack_write_frame_at_index (ProfilerThreadStack *stack, int index, guint32 method_id_and_is_jitted) {
1128         stack->written_frames [index] = method_id_and_is_jitted;
1129 }
1130
1131 static ClassIdMappingElement*
1132 class_id_mapping_element_get (MonoClass *klass) {
1133         return g_hash_table_lookup (profiler->classes->table, (gconstpointer) klass);
1134 }
1135
1136 static MethodIdMappingElement*
1137 method_id_mapping_element_get (MonoMethod *method) {
1138         return g_hash_table_lookup (profiler->methods->table, (gconstpointer) method);
1139 }
1140
1141 #define BITS_TO_BYTES(v) do {\
1142         (v) += 7;\
1143         (v) &= ~7;\
1144         (v) >>= 3;\
1145 } while (0)
1146
1147 static ClassIdMappingElement*
1148 class_id_mapping_element_new (MonoClass *klass) {
1149         ClassIdMappingElement *result = g_new (ClassIdMappingElement, 1);
1150         
1151         result->name = mono_type_full_name (mono_class_get_type (klass));
1152         result->klass = klass;
1153         result->next_unwritten = profiler->classes->unwritten;
1154         profiler->classes->unwritten = result;
1155         result->id = profiler->classes->next_id;
1156         profiler->classes->next_id ++;
1157         
1158         result->data.bitmap.compact = 0;
1159         result->data.layout.slots = CLASS_LAYOUT_NOT_INITIALIZED;
1160         result->data.layout.references = CLASS_LAYOUT_NOT_INITIALIZED;
1161         
1162         g_hash_table_insert (profiler->classes->table, klass, result);
1163         
1164 #if (DEBUG_MAPPING_EVENTS)
1165         printf ("Created new CLASS mapping element \"%s\" (%p)[%d]\n", result->name, klass, result->id);
1166 #endif
1167         return result;
1168 }
1169
1170 static void
1171 class_id_mapping_element_build_layout_bitmap (MonoClass *klass, ClassIdMappingElement *klass_id) {
1172         MonoClass *parent_class = mono_class_get_parent (klass);
1173         int number_of_reference_fields = 0;
1174         int max_offset_of_reference_fields = 0;
1175         ClassIdMappingElement *parent_id;
1176         gpointer iter;
1177         MonoClassField *field;
1178         
1179 #if (DEBUG_CLASS_BITMAPS)
1180         printf ("class_id_mapping_element_build_layout_bitmap: building layout for class %s.%s: ", mono_class_get_namespace (klass), mono_class_get_name (klass));
1181 #endif
1182         
1183         if (parent_class != NULL) {
1184                 parent_id = class_id_mapping_element_get (parent_class);
1185                 g_assert (parent_id != NULL);
1186                 
1187                 if (parent_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
1188 #if (DEBUG_CLASS_BITMAPS)
1189                         printf ("[recursively building bitmap for father class]\n");
1190 #endif
1191                         class_id_mapping_element_build_layout_bitmap (parent_class, parent_id);
1192                 }
1193         } else {
1194                 parent_id = NULL;
1195         }
1196         
1197         iter = NULL;
1198         while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
1199                 MonoType* field_type = mono_field_get_type (field);
1200                 // For now, skip static fields
1201                 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
1202                         continue;
1203                 
1204                 if (MONO_TYPE_IS_REFERENCE (field_type)) {
1205                         int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
1206                         if (field_offset > max_offset_of_reference_fields) {
1207                                 max_offset_of_reference_fields = field_offset;
1208                         }
1209                         number_of_reference_fields ++;
1210                 } else {
1211                         MonoClass *field_class = mono_class_from_mono_type (field_type);
1212                         if (field_class && mono_class_is_valuetype (field_class)) {
1213                                 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
1214                                 g_assert (field_id != NULL);
1215                                 
1216                                 if (field_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
1217                                         if (field_id != klass_id) {
1218 #if (DEBUG_CLASS_BITMAPS)
1219                                                 printf ("[recursively building bitmap for field %s]\n", mono_field_get_name (field));
1220 #endif
1221                                                 class_id_mapping_element_build_layout_bitmap (field_class, field_id);
1222                                         } else {
1223 #if (DEBUG_CLASS_BITMAPS)
1224                                                 printf ("[breaking recursive bitmap build for field %s]", mono_field_get_name (field));
1225                                                 
1226 #endif
1227                                                 klass_id->data.bitmap.compact = 0;
1228                                                 klass_id->data.layout.slots = 0;
1229                                                 klass_id->data.layout.references = 0;
1230                                         }
1231                                 }
1232                                 
1233                                 if (field_id->data.layout.references > 0) {
1234                                         int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
1235                                         int max_offset_reference_in_field = (field_id->data.layout.slots - 1) * sizeof (gpointer);
1236                                         
1237                                         if ((field_offset + max_offset_reference_in_field) > max_offset_of_reference_fields) {
1238                                                 max_offset_of_reference_fields = field_offset + max_offset_reference_in_field;
1239                                         }
1240                                         
1241                                         number_of_reference_fields += field_id->data.layout.references;
1242                                 }
1243                         }
1244                 }
1245         }
1246         
1247 #if (DEBUG_CLASS_BITMAPS)
1248         printf ("[allocating bitmap for class %s.%s (references %d, max offset %d, slots %d)]", mono_class_get_namespace (klass), mono_class_get_name (klass), number_of_reference_fields, max_offset_of_reference_fields, (int)(max_offset_of_reference_fields / sizeof (gpointer)) + 1);
1249 #endif
1250         if ((number_of_reference_fields == 0) && ((parent_id == NULL) || (parent_id->data.layout.references == 0))) {
1251 #if (DEBUG_CLASS_BITMAPS)
1252                 printf ("[no references at all]");
1253 #endif
1254                 klass_id->data.bitmap.compact = 0;
1255                 klass_id->data.layout.slots = 0;
1256                 klass_id->data.layout.references = 0;
1257         } else {
1258                 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
1259 #if (DEBUG_CLASS_BITMAPS)
1260                         printf ("[parent %s.%s has %d references in %d slots]", mono_class_get_namespace (parent_class), mono_class_get_name (parent_class), parent_id->data.layout.references, parent_id->data.layout.slots);
1261 #endif
1262                         klass_id->data.layout.slots = parent_id->data.layout.slots;
1263                         klass_id->data.layout.references = parent_id->data.layout.references;
1264                 } else {
1265 #if (DEBUG_CLASS_BITMAPS)
1266                         printf ("[no references from parent]");
1267 #endif
1268                         klass_id->data.layout.slots = 0;
1269                         klass_id->data.layout.references = 0;
1270                 }
1271                 
1272                 if (number_of_reference_fields > 0) {
1273                         klass_id->data.layout.slots += ((max_offset_of_reference_fields / sizeof (gpointer)) + 1);
1274                         klass_id->data.layout.references += number_of_reference_fields;
1275 #if (DEBUG_CLASS_BITMAPS)
1276                         printf ("[adding data, going to %d references in %d slots]", klass_id->data.layout.references, klass_id->data.layout.slots);
1277 #endif
1278                 }
1279                 
1280                 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1281 #if (DEBUG_CLASS_BITMAPS)
1282                                 printf ("[zeroing bitmap]");
1283 #endif
1284                                 klass_id->data.bitmap.compact = 0;
1285                         if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
1286 #if (DEBUG_CLASS_BITMAPS)
1287                                 printf ("[copying compact father bitmap]");
1288 #endif
1289                                 klass_id->data.bitmap.compact = parent_id->data.bitmap.compact;
1290                         }
1291                 } else {
1292                         int size_of_bitmap = klass_id->data.layout.slots;
1293                         BITS_TO_BYTES (size_of_bitmap);
1294 #if (DEBUG_CLASS_BITMAPS)
1295                         printf ("[allocating %d bytes for bitmap]", size_of_bitmap);
1296 #endif
1297                         klass_id->data.bitmap.extended = g_malloc0 (size_of_bitmap);
1298                         if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
1299                                 int size_of_father_bitmap = parent_id->data.layout.slots;
1300                                 if (size_of_father_bitmap <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1301                                         int father_slot;
1302 #if (DEBUG_CLASS_BITMAPS)
1303                                         printf ("[copying %d bits from father bitmap]", size_of_father_bitmap);
1304 #endif
1305                                         for (father_slot = 0; father_slot < size_of_father_bitmap; father_slot ++) {
1306                                                 if (parent_id->data.bitmap.compact & (((guint64)1) << father_slot)) {
1307                                                         klass_id->data.bitmap.extended [father_slot >> 3] |= (1 << (father_slot & 7));
1308                                                 }
1309                                         }
1310                                 } else {
1311                                         BITS_TO_BYTES (size_of_father_bitmap);
1312 #if (DEBUG_CLASS_BITMAPS)
1313                                         printf ("[copying %d bytes from father bitmap]", size_of_father_bitmap);
1314 #endif
1315                                         memcpy (klass_id->data.bitmap.extended, parent_id->data.bitmap.extended, size_of_father_bitmap);
1316                                 }
1317                         }
1318                 }
1319         }
1320         
1321 #if (DEBUG_CLASS_BITMAPS)
1322         printf ("[starting filling iteration]\n");
1323 #endif
1324         iter = NULL;
1325         while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
1326                 MonoType* field_type = mono_field_get_type (field);
1327                 // For now, skip static fields
1328                 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
1329                         continue;
1330                 
1331 #if (DEBUG_CLASS_BITMAPS)
1332                 printf ("[Working on field %s]", mono_field_get_name (field));
1333 #endif
1334                 if (MONO_TYPE_IS_REFERENCE (field_type)) {
1335                         int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
1336                         int field_slot;
1337                         g_assert ((field_offset % sizeof (gpointer)) == 0);
1338                         field_slot = field_offset / sizeof (gpointer);
1339                         if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1340                                 klass_id->data.bitmap.compact |= (((guint64)1) << field_slot);
1341                         } else {
1342                                 klass_id->data.bitmap.extended [field_slot >> 3] |= (1 << (field_slot & 7));
1343                         }
1344 #if (DEBUG_CLASS_BITMAPS)
1345                         printf ("[reference at offset %d, slot %d]", field_offset, field_slot);
1346 #endif
1347                 } else {
1348                         MonoClass *field_class = mono_class_from_mono_type (field_type);
1349                         if (field_class && mono_class_is_valuetype (field_class)) {
1350                                 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
1351                                 int field_offset;
1352                                 int field_slot;
1353                                 
1354                                 g_assert (field_id != NULL);
1355                                 field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
1356                                 g_assert ((field_id->data.layout.references == 0) || ((field_offset % sizeof (gpointer)) == 0));
1357                                 field_slot = field_offset / sizeof (gpointer);
1358 #if (DEBUG_CLASS_BITMAPS)
1359                                 printf ("[value type at offset %d, slot %d, with %d references in %d slots]", field_offset, field_slot, field_id->data.layout.references, field_id->data.layout.slots);
1360 #endif
1361                                 
1362                                 if (field_id->data.layout.references > 0) {
1363                                         int sub_field_slot;
1364                                         if (field_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1365                                                 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
1366                                                         if (field_id->data.bitmap.compact & (((guint64)1) << sub_field_slot)) {
1367                                                                 int actual_slot = field_slot + sub_field_slot;
1368                                                                 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1369                                                                         klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
1370                                                                 } else {
1371                                                                         klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
1372                                                                 }
1373                                                         }
1374                                                 }
1375                                         } else {
1376                                                 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
1377                                                         if (field_id->data.bitmap.extended [sub_field_slot >> 3] & (1 << (sub_field_slot & 7))) {
1378                                                                 int actual_slot = field_slot + sub_field_slot;
1379                                                                 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1380                                                                         klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
1381                                                                 } else {
1382                                                                         klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
1383                                                                 }
1384                                                         }
1385                                                 }
1386                                         }
1387                                 }
1388                         }
1389                 }
1390         }
1391 #if (DEBUG_CLASS_BITMAPS)
1392         do {
1393                 int slot;
1394                 printf ("\nLayot of class \"%s.%s\": references %d, slots %d, bitmap {", mono_class_get_namespace (klass), mono_class_get_name (klass), klass_id->data.layout.references, klass_id->data.layout.slots);
1395                 for (slot = 0; slot < klass_id->data.layout.slots; slot ++) {
1396                         if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1397                                 if (klass_id->data.bitmap.compact & (((guint64)1) << slot)) {
1398                                         printf (" 1");
1399                                 } else {
1400                                         printf (" 0");
1401                                 }
1402                         } else {
1403                                 if (klass_id->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
1404                                         printf (" 1");
1405                                 } else {
1406                                         printf (" 0");
1407                                 }
1408 ;                       }
1409                         
1410                 }
1411                 printf (" }\n");
1412                 
1413         } while (0);
1414 #endif
1415 }
1416
1417 static MethodIdMappingElement*
1418 method_id_mapping_element_new (MonoMethod *method) {
1419         MethodIdMappingElement *result = g_new (MethodIdMappingElement, 1);
1420         char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
1421         
1422         result->name = g_strdup_printf ("%s (%s)", mono_method_get_name (method), signature);
1423         g_free (signature);
1424         result->method = method;
1425         result->next_unwritten = profiler->methods->unwritten;
1426         profiler->methods->unwritten = result;
1427         result->id = profiler->methods->next_id;
1428         profiler->methods->next_id ++;
1429         g_hash_table_insert (profiler->methods->table, method, result);
1430         
1431         result->data.code_start = NULL;
1432         result->data.code_size = 0;
1433         
1434 #if (DEBUG_MAPPING_EVENTS)
1435         printf ("Created new METHOD mapping element \"%s\" (%p)[%d]\n", result->name, method, result->id);
1436 #endif
1437         return result;
1438 }
1439
1440
1441 static void
1442 method_id_mapping_element_destroy (gpointer element) {
1443         MethodIdMappingElement *e = (MethodIdMappingElement*) element;
1444         if (e->name)
1445                 g_free (e->name);
1446         g_free (element);
1447 }
1448
1449 static void
1450 class_id_mapping_element_destroy (gpointer element) {
1451         ClassIdMappingElement *e = (ClassIdMappingElement*) element;
1452         if (e->name)
1453                 g_free (e->name);
1454         if ((e->data.layout.slots != CLASS_LAYOUT_NOT_INITIALIZED) && (e->data.layout.slots > CLASS_LAYOUT_PACKED_BITMAP_SIZE))
1455                 g_free (e->data.bitmap.extended);
1456         g_free (element);
1457 }
1458
1459 static MethodIdMapping*
1460 method_id_mapping_new (void) {
1461         MethodIdMapping *result = g_new (MethodIdMapping, 1);
1462         //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, method_id_mapping_element_destroy);
1463         result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, method_id_mapping_element_destroy);
1464         result->unwritten = NULL;
1465         result->next_id = 1;
1466         return result;
1467 }
1468
1469 static ClassIdMapping*
1470 class_id_mapping_new (void) {
1471         ClassIdMapping *result = g_new (ClassIdMapping, 1);
1472         //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, class_id_mapping_element_destroy);
1473         result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, class_id_mapping_element_destroy);
1474         result->unwritten = NULL;
1475         result->next_id = 1;
1476         return result;
1477 }
1478
1479 static void
1480 method_id_mapping_destroy (MethodIdMapping *map) {
1481         g_hash_table_destroy (map->table);
1482         g_free (map);
1483 }
1484
1485 static void
1486 class_id_mapping_destroy (ClassIdMapping *map) {
1487         g_hash_table_destroy (map->table);
1488         g_free (map);
1489 }
1490
1491 #if (DEBUG_LOAD_EVENTS)
1492 static void
1493 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element);
1494 #endif
1495
1496 static LoadedElement*
1497 loaded_element_load_start (GHashTable *table, gpointer item) {
1498         LoadedElement *element = g_new0 (LoadedElement, 1);
1499 #if (DEBUG_LOAD_EVENTS)
1500         print_load_event ("LOAD START", table, item, element);
1501 #endif
1502         MONO_PROFILER_GET_CURRENT_COUNTER (element->load_start_counter);
1503         g_hash_table_insert (table, item, element);
1504         return element;
1505 }
1506
1507 static LoadedElement*
1508 loaded_element_load_end (GHashTable *table, gpointer item, char *name) {
1509         LoadedElement *element = g_hash_table_lookup (table, item);
1510 #if (DEBUG_LOAD_EVENTS)
1511         print_load_event ("LOAD END", table, item, element);
1512 #endif
1513         g_assert (element != NULL);
1514         MONO_PROFILER_GET_CURRENT_COUNTER (element->load_end_counter);
1515         element->name = name;
1516         element->loaded = TRUE;
1517         return element;
1518 }
1519
1520 static LoadedElement*
1521 loaded_element_unload_start (GHashTable *table, gpointer item) {
1522         LoadedElement *element = g_hash_table_lookup (table, item);
1523 #if (DEBUG_LOAD_EVENTS)
1524         print_load_event ("UNLOAD START", table, item, element);
1525 #endif
1526         g_assert (element != NULL);
1527         MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_start_counter);
1528         return element;
1529 }
1530
1531 static LoadedElement*
1532 loaded_element_unload_end (GHashTable *table, gpointer item) {
1533         LoadedElement *element = g_hash_table_lookup (table, item);
1534 #if (DEBUG_LOAD_EVENTS)
1535         print_load_event ("UNLOAD END", table, item, element);
1536 #endif
1537         g_assert (element != NULL);
1538         MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_end_counter);
1539         element->unloaded = TRUE;
1540         return element;
1541 }
1542
1543
1544 static void
1545 loaded_element_destroy (gpointer element) {
1546         if (((LoadedElement*)element)->name)
1547                 g_free (((LoadedElement*)element)->name);
1548         g_free (element);
1549 }
1550
1551 #if (DEBUG_LOAD_EVENTS)
1552 static void
1553 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element) {
1554         const char* item_name;
1555         char* item_info;
1556         
1557         if (table == profiler->loaded_assemblies) {
1558                 //item_info = g_strdup_printf("ASSEMBLY %p (dynamic %d)", item, mono_image_is_dynamic (mono_assembly_get_image((MonoAssembly*)item)));
1559                 item_info = g_strdup_printf("ASSEMBLY %p", item);
1560         } else if (table == profiler->loaded_modules) {
1561                 //item_info = g_strdup_printf("MODULE %p (dynamic %d)", item, mono_image_is_dynamic ((MonoImage*)item));
1562                 item_info = g_strdup_printf("MODULE %p", item);
1563         } else if (table == profiler->loaded_appdomains) {
1564                 item_info = g_strdup_printf("APPDOMAIN %p (id %d)", item, mono_domain_get_id ((MonoDomain*)item));
1565         } else {
1566                 item_info = NULL;
1567                 g_assert_not_reached ();
1568         }
1569         
1570         if (element != NULL) {
1571                 item_name = element->name;
1572         } else {
1573                 item_name = "<NULL>";
1574         }
1575         
1576         printf ("%s EVENT for %s (%s)\n", event_name, item_info, item_name);
1577         g_free (item_info);
1578 }
1579 #endif
1580
1581 static void
1582 profiler_heap_shot_object_buffers_destroy (ProfilerHeapShotObjectBuffer *buffer) {
1583         while (buffer != NULL) {
1584                 ProfilerHeapShotObjectBuffer *next = buffer->next;
1585 #if DEBUG_HEAP_PROFILER
1586                 printf ("profiler_heap_shot_object_buffers_destroy: destroyed buffer %p (%p-%p)\n", buffer, & (buffer->buffer [0]), buffer->end);
1587 #endif
1588                 g_free (buffer);
1589                 buffer = next;
1590         }
1591 }
1592
1593 static ProfilerHeapShotObjectBuffer*
1594 profiler_heap_shot_object_buffer_new (ProfilerPerThreadData *data) {
1595         ProfilerHeapShotObjectBuffer *buffer;
1596         ProfilerHeapShotObjectBuffer *result = g_new (ProfilerHeapShotObjectBuffer, 1);
1597         result->next_free_slot = & (result->buffer [0]);
1598         result->end = & (result->buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE]);
1599         result->first_unprocessed_slot = & (result->buffer [0]);
1600         result->next = data->heap_shot_object_buffers;
1601         data->heap_shot_object_buffers = result;
1602 #if DEBUG_HEAP_PROFILER
1603         printf ("profiler_heap_shot_object_buffer_new: created buffer %p (%p-%p)\n", result, result->next_free_slot, result->end);
1604 #endif
1605         for (buffer = result; buffer != NULL; buffer = buffer->next) {
1606                 ProfilerHeapShotObjectBuffer *last = buffer->next;
1607                 if ((last != NULL) && (last->first_unprocessed_slot == last->end)) {
1608                         buffer->next = NULL;
1609                         profiler_heap_shot_object_buffers_destroy (last);
1610                 }
1611         }
1612         
1613         return result;
1614 }
1615
1616 static ProfilerHeapShotWriteJob*
1617 profiler_heap_shot_write_job_new (gboolean heap_shot_was_signalled, gboolean dump_heap_data, guint32 collection) {
1618         ProfilerHeapShotWriteJob *job = g_new (ProfilerHeapShotWriteJob, 1);
1619         job->next = NULL;
1620         job->next_unwritten = NULL;
1621         
1622         if (profiler->action_flags.unreachable_objects || dump_heap_data) {
1623                 job->buffers = g_new (ProfilerHeapShotWriteBuffer, 1);
1624                 job->buffers->next = NULL;
1625                 job->last_next = & (job->buffers->next);
1626                 job->start = & (job->buffers->buffer [0]);
1627                 job->cursor = job->start;
1628                 job->end = & (job->buffers->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1629         } else {
1630                 job->buffers = NULL;
1631                 job->last_next = NULL;
1632                 job->start = NULL;
1633                 job->cursor = NULL;
1634                 job->end = NULL;
1635         }
1636         job->full_buffers = 0;
1637         
1638         if (profiler->action_flags.collection_summary) {
1639                 job->summary.capacity = profiler->classes->next_id;
1640                 job->summary.per_class_data = g_new0 (ProfilerHeapShotClassSummary, job->summary.capacity);
1641         } else {
1642                 job->summary.capacity = 0;
1643                 job->summary.per_class_data = NULL;
1644         }
1645
1646         job->heap_shot_was_signalled = heap_shot_was_signalled;
1647         job->collection = collection;
1648         job->dump_heap_data = dump_heap_data;
1649 #if DEBUG_HEAP_PROFILER
1650         printf ("profiler_heap_shot_write_job_new: created job %p with buffer %p(%p-%p) (collection %d, dump %d)\n", job, job->buffers, job->start, job->end, collection, dump_heap_data);
1651 #endif
1652         return job;
1653 }
1654
1655 static gboolean
1656 profiler_heap_shot_write_job_has_data (ProfilerHeapShotWriteJob *job) {
1657         return ((job->buffers != NULL) || (job->summary.capacity > 0));
1658 }
1659
1660 static void
1661 profiler_heap_shot_write_job_add_buffer (ProfilerHeapShotWriteJob *job, gpointer value) {
1662         ProfilerHeapShotWriteBuffer *buffer = g_new (ProfilerHeapShotWriteBuffer, 1);
1663         buffer->next = NULL;
1664         *(job->last_next) = buffer;
1665         job->last_next = & (buffer->next);
1666         job->full_buffers ++;
1667         buffer->buffer [0] = value;
1668         job->start = & (buffer->buffer [0]);
1669         job->cursor = & (buffer->buffer [1]);
1670         job->end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1671 #if DEBUG_HEAP_PROFILER
1672         printf ("profiler_heap_shot_write_job_add_buffer: in job %p, added buffer %p(%p-%p) with value %p at address %p (cursor now %p)\n", job, buffer, job->start, job->end, value, &(buffer->buffer [0]), job->cursor);
1673         do {
1674                 ProfilerHeapShotWriteBuffer *current_buffer;
1675                 for (current_buffer = job->buffers; current_buffer != NULL; current_buffer = current_buffer->next) {
1676                         printf ("profiler_heap_shot_write_job_add_buffer: now job %p has buffer %p\n", job, current_buffer);
1677                 }
1678         } while (0);
1679 #endif
1680 }
1681
1682 static void
1683 profiler_heap_shot_write_job_free_buffers (ProfilerHeapShotWriteJob *job) {
1684         ProfilerHeapShotWriteBuffer *buffer = job->buffers;
1685         
1686         while (buffer != NULL) {
1687                 ProfilerHeapShotWriteBuffer *next = buffer->next;
1688 #if DEBUG_HEAP_PROFILER
1689                 printf ("profiler_heap_shot_write_job_free_buffers: in job %p, freeing buffer %p\n", job, buffer);
1690 #endif
1691                 g_free (buffer);
1692                 buffer = next;
1693         }
1694         
1695         job->buffers = NULL;
1696         
1697         if (job->summary.per_class_data != NULL) {
1698                 g_free (job->summary.per_class_data);
1699                 job->summary.per_class_data = NULL;
1700         }
1701         job->summary.capacity = 0;
1702 }
1703
1704 static void
1705 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job);
1706
1707 static void
1708 profiler_process_heap_shot_write_jobs (void) {
1709         gboolean done = FALSE;
1710         
1711         while (!done) {
1712                 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1713                 ProfilerHeapShotWriteJob *previous_job = NULL;
1714                 ProfilerHeapShotWriteJob *next_job;
1715                 
1716                 done = TRUE;
1717                 while (current_job != NULL) {
1718                         next_job = current_job->next_unwritten;
1719                         
1720                         if (next_job != NULL) {
1721                                 if (profiler_heap_shot_write_job_has_data (current_job)) {
1722                                         done = FALSE;
1723                                 }
1724                                 if (! profiler_heap_shot_write_job_has_data (next_job)) {
1725                                         current_job->next_unwritten = NULL;
1726                                         next_job = NULL;
1727                                 }
1728                         } else {
1729                                 if (profiler_heap_shot_write_job_has_data (current_job)) {
1730                                         LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: writing...");
1731                                         profiler_heap_shot_write_block (current_job);
1732                                         LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: done");
1733                                         if (previous_job != NULL) {
1734                                                 previous_job->next_unwritten = NULL;
1735                                         }
1736                                 }
1737                         }
1738                         
1739                         previous_job = current_job;
1740                         current_job = next_job;
1741                 }
1742         }
1743 }
1744
1745 static void
1746 profiler_free_heap_shot_write_jobs (void) {
1747         ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1748         ProfilerHeapShotWriteJob *next_job;
1749         
1750         if (current_job != NULL) {
1751                 while (current_job->next_unwritten != NULL) {
1752 #if DEBUG_HEAP_PROFILER
1753                         printf ("profiler_free_heap_shot_write_jobs: job %p must not be freed\n", current_job);
1754 #endif
1755                         current_job = current_job->next_unwritten;
1756                 }
1757                 
1758                 next_job = current_job->next;
1759                 current_job->next = NULL;
1760                 current_job = next_job;
1761                 
1762                 while (current_job != NULL) {
1763 #if DEBUG_HEAP_PROFILER
1764                         printf ("profiler_free_heap_shot_write_jobs: job %p will be freed\n", current_job);
1765 #endif
1766                         next_job = current_job->next;
1767                         profiler_heap_shot_write_job_free_buffers (current_job);
1768                         g_free (current_job);
1769                         current_job = next_job;
1770                 }
1771         }
1772 }
1773
1774 static void
1775 profiler_destroy_heap_shot_write_jobs (void) {
1776         ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1777         ProfilerHeapShotWriteJob *next_job;
1778         
1779         while (current_job != NULL) {
1780                 next_job = current_job->next;
1781                 profiler_heap_shot_write_job_free_buffers (current_job);
1782                 g_free (current_job);
1783                 current_job = next_job;
1784         }
1785 }
1786
1787 static void
1788 profiler_add_heap_shot_write_job (ProfilerHeapShotWriteJob *job) {
1789         job->next = profiler->heap_shot_write_jobs;
1790         job->next_unwritten = job->next;
1791         profiler->heap_shot_write_jobs = job;
1792 #if DEBUG_HEAP_PROFILER
1793         printf ("profiler_add_heap_shot_write_job: added job %p\n", job);
1794 #endif
1795 }
1796
1797 #if DEBUG_HEAP_PROFILER
1798 #define STORE_ALLOCATED_OBJECT_MESSAGE1(d,o) printf ("STORE_ALLOCATED_OBJECT[TID %ld]: storing object %p at address %p\n", (d)->thread_id, (o), (d)->heap_shot_object_buffers->next_free_slot)
1799 #define STORE_ALLOCATED_OBJECT_MESSAGE2(d,o) printf ("STORE_ALLOCATED_OBJECT[TID %ld]: storing object %p at address %p in new buffer %p\n", (d)->thread_id, (o), buffer->next_free_slot, buffer)
1800 #else
1801 #define STORE_ALLOCATED_OBJECT_MESSAGE1(d,o)
1802 #define STORE_ALLOCATED_OBJECT_MESSAGE2(d,o)
1803 #endif
1804 #define STORE_ALLOCATED_OBJECT(d,o) do {\
1805         if ((d)->heap_shot_object_buffers->next_free_slot < (d)->heap_shot_object_buffers->end) {\
1806                 STORE_ALLOCATED_OBJECT_MESSAGE1 ((d), (o));\
1807                 *((d)->heap_shot_object_buffers->next_free_slot) = (o);\
1808                 (d)->heap_shot_object_buffers->next_free_slot ++;\
1809         } else {\
1810                 ProfilerHeapShotObjectBuffer *buffer = profiler_heap_shot_object_buffer_new (d);\
1811                 STORE_ALLOCATED_OBJECT_MESSAGE2 ((d), (o));\
1812                 *((buffer)->next_free_slot) = (o);\
1813                 (buffer)->next_free_slot ++;\
1814         }\
1815 } while (0)
1816
1817 static ProfilerPerThreadData*
1818 profiler_per_thread_data_new (guint32 buffer_size)
1819 {
1820         ProfilerPerThreadData *data = g_new (ProfilerPerThreadData, 1);
1821
1822         data->events = g_new0 (ProfilerEventData, buffer_size);
1823         data->next_free_event = data->events;
1824         data->end_event = data->events + (buffer_size - 1);
1825         data->first_unwritten_event = data->events;
1826         data->first_unmapped_event = data->events;
1827         MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
1828         data->last_event_counter = data->start_event_counter;
1829         data->thread_id = CURRENT_THREAD_ID ();
1830         data->heap_shot_object_buffers = NULL;
1831         if ((profiler->action_flags.unreachable_objects == TRUE) ||
1832                         (profiler->action_flags.heap_shot == TRUE) ||
1833                         (profiler->action_flags.collection_summary == TRUE)) {
1834                 profiler_heap_shot_object_buffer_new (data);
1835         }
1836         if (profiler->action_flags.track_stack) {
1837                 thread_stack_initialize (&(data->stack), 64);
1838         } else {
1839                 thread_stack_initialize_empty (&(data->stack));
1840         }
1841         return data;
1842 }
1843
1844 static void
1845 profiler_per_thread_data_destroy (ProfilerPerThreadData *data) {
1846         g_free (data->events);
1847         profiler_heap_shot_object_buffers_destroy (data->heap_shot_object_buffers);
1848         thread_stack_free (&(data->stack));
1849         g_free (data);
1850 }
1851
1852 static ProfilerStatisticalData*
1853 profiler_statistical_data_new (MonoProfiler *profiler) {
1854         int buffer_size = profiler->statistical_buffer_size * (profiler->statistical_call_chain_depth + 1);
1855         ProfilerStatisticalData *data = g_new (ProfilerStatisticalData, 1);
1856
1857         data->hits = g_new0 (ProfilerStatisticalHit, buffer_size);
1858         data->next_free_index = 0;
1859         data->end_index = profiler->statistical_buffer_size;
1860         data->first_unwritten_index = 0;
1861         
1862         return data;
1863 }
1864
1865 static void
1866 profiler_statistical_data_destroy (ProfilerStatisticalData *data) {
1867         g_free (data->hits);
1868         g_free (data);
1869 }
1870
1871 static void
1872 profiler_add_write_buffer (void) {
1873         if (profiler->current_write_buffer->next == NULL) {
1874                 profiler->current_write_buffer->next = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
1875                 profiler->current_write_buffer->next->next = NULL;
1876                 
1877                 //printf ("Added next buffer %p, to buffer %p\n", profiler->current_write_buffer->next, profiler->current_write_buffer);
1878                 
1879         }
1880         profiler->current_write_buffer = profiler->current_write_buffer->next;
1881         profiler->current_write_position = 0;
1882         profiler->full_write_buffers ++;
1883 }
1884
1885 static void
1886 profiler_free_write_buffers (void) {
1887         ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1888         while (current_buffer != NULL) {
1889                 ProfilerFileWriteBuffer *next_buffer = current_buffer->next;
1890                 
1891                 //printf ("Freeing write buffer %p, next is %p\n", current_buffer, next_buffer);
1892                 
1893                 g_free (current_buffer);
1894                 current_buffer = next_buffer;
1895         }
1896 }
1897
1898 #define WRITE_BYTE(b) do {\
1899         if (profiler->current_write_position >= PROFILER_FILE_WRITE_BUFFER_SIZE) {\
1900                 profiler_add_write_buffer ();\
1901         }\
1902         profiler->current_write_buffer->buffer [profiler->current_write_position] = (b);\
1903         profiler->current_write_position ++;\
1904 } while (0)
1905
1906
1907 static void
1908 write_current_block (guint16 code) {
1909         guint32 size = (profiler->full_write_buffers * PROFILER_FILE_WRITE_BUFFER_SIZE) + profiler->current_write_position;
1910         ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1911         guint64 current_counter;
1912         guint32 counter_delta;
1913         guint8 header [10];
1914         
1915         MONO_PROFILER_GET_CURRENT_COUNTER (current_counter);
1916         if (profiler->last_header_counter != 0) {
1917                 counter_delta = current_counter - profiler->last_header_counter;
1918         } else {
1919                 counter_delta = 0;
1920         }
1921         profiler->last_header_counter = current_counter;
1922         
1923         header [0] = code & 0xff;
1924         header [1] = (code >> 8) & 0xff;
1925         header [2] = size & 0xff;
1926         header [3] = (size >> 8) & 0xff;
1927         header [4] = (size >> 16) & 0xff;
1928         header [5] = (size >> 24) & 0xff;
1929         header [6] = counter_delta & 0xff;
1930         header [7] = (counter_delta >> 8) & 0xff;
1931         header [8] = (counter_delta >> 16) & 0xff;
1932         header [9] = (counter_delta >> 24) & 0xff;
1933         
1934 #if (DEBUG_FILE_WRITES)
1935         printf ("write_current_block: writing header (code %d)\n", code);
1936 #endif
1937         WRITE_BUFFER (& (header [0]), 10);
1938         
1939         while ((current_buffer != NULL) && (profiler->full_write_buffers > 0)) {
1940 #if (DEBUG_FILE_WRITES)
1941                 printf ("write_current_block: writing buffer (size %d)\n", PROFILER_FILE_WRITE_BUFFER_SIZE);
1942 #endif
1943                 WRITE_BUFFER (& (current_buffer->buffer [0]), PROFILER_FILE_WRITE_BUFFER_SIZE);
1944                 profiler->full_write_buffers --;
1945                 current_buffer = current_buffer->next;
1946         }
1947         if (profiler->current_write_position > 0) {
1948 #if (DEBUG_FILE_WRITES)
1949                 printf ("write_current_block: writing last buffer (size %d)\n", profiler->current_write_position);
1950 #endif
1951                 WRITE_BUFFER (& (current_buffer->buffer [0]), profiler->current_write_position);
1952         }
1953         FLUSH_FILE ();
1954 #if (DEBUG_FILE_WRITES)
1955         printf ("write_current_block: buffers flushed\n");
1956 #endif
1957         
1958         profiler->current_write_buffer = profiler->write_buffers;
1959         profiler->current_write_position = 0;
1960         profiler->full_write_buffers = 0;
1961 }
1962
1963
1964 #define SEVEN_BITS_MASK (0x7f)
1965 #define EIGHT_BIT_MASK (0x80)
1966
1967 static void
1968 write_uint32 (guint32 value) {
1969         while (value > SEVEN_BITS_MASK) {
1970                 WRITE_BYTE (value & SEVEN_BITS_MASK);
1971                 value >>= 7;
1972         }
1973         WRITE_BYTE (value | EIGHT_BIT_MASK);
1974 }
1975 static void
1976 write_uint64 (guint64 value) {
1977         while (value > SEVEN_BITS_MASK) {
1978                 WRITE_BYTE (value & SEVEN_BITS_MASK);
1979                 value >>= 7;
1980         }
1981         WRITE_BYTE (value | EIGHT_BIT_MASK);
1982 }
1983 static void
1984 write_string (const char *string) {
1985         while (*string != 0) {
1986                 WRITE_BYTE (*string);
1987                 string ++;
1988         }
1989         WRITE_BYTE (0);
1990 }
1991
1992 static void write_clock_data (void);
1993 static void
1994 write_directives_block (gboolean start) {
1995         write_clock_data ();
1996         
1997         if (start) {
1998                 if (profiler->action_flags.save_allocation_caller) {
1999                         write_uint32 (MONO_PROFILER_DIRECTIVE_ALLOCATIONS_CARRY_CALLER);
2000                 }
2001                 if (profiler->action_flags.save_allocation_stack || profiler->action_flags.track_calls) {
2002                         write_uint32 (MONO_PROFILER_DIRECTIVE_ALLOCATIONS_HAVE_STACK);
2003                 }
2004                 if (profiler->action_flags.allocations_carry_id) {
2005                         write_uint32 (MONO_PROFILER_DIRECTIVE_ALLOCATIONS_CARRY_ID);
2006                 }
2007         }
2008         write_uint32 (MONO_PROFILER_DIRECTIVE_END);
2009         
2010         write_clock_data ();
2011         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_DIRECTIVES);
2012 }
2013
2014 #if DEBUG_HEAP_PROFILER
2015 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c) printf ("WRITE_HEAP_SHOT_JOB_VALUE: writing value %p at cursor %p\n", (v), (c))
2016 #else
2017 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c)
2018 #endif
2019 #define WRITE_HEAP_SHOT_JOB_VALUE(j,v) do {\
2020         if ((j)->cursor < (j)->end) {\
2021                 WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE ((v), ((j)->cursor));\
2022                 *((j)->cursor) = (v);\
2023                 (j)->cursor ++;\
2024         } else {\
2025                 profiler_heap_shot_write_job_add_buffer (j, v);\
2026         }\
2027 } while (0)
2028
2029
2030 #undef GUINT_TO_POINTER
2031 #undef GPOINTER_TO_UINT
2032 #if (SIZEOF_VOID_P == 4)
2033 #define GUINT_TO_POINTER(u) ((void*)(guint32)(u))
2034 #define GPOINTER_TO_UINT(p) ((guint32)(void*)(p))
2035 #elif (SIZEOF_VOID_P == 8)
2036 #define GUINT_TO_POINTER(u) ((void*)(guint64)(u))
2037 #define GPOINTER_TO_UINT(p) ((guint64)(void*)(p))
2038 #else
2039 #error Bad size of void pointer
2040 #endif
2041
2042 #define WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE(j,v,c) WRITE_HEAP_SHOT_JOB_VALUE (j, GUINT_TO_POINTER (GPOINTER_TO_UINT (v)|(c)))
2043
2044 #if DEBUG_HEAP_PROFILER
2045 #define UPDATE_JOB_BUFFER_CURSOR_MESSAGE() printf ("profiler_heap_shot_write_block[UPDATE_JOB_BUFFER_CURSOR]: in job %p, moving to buffer %p and cursor %p\n", job, buffer, cursor)
2046 #else
2047 #define UPDATE_JOB_BUFFER_CURSOR_MESSAGE()
2048 #endif
2049 #define UPDATE_JOB_BUFFER_CURSOR() do {\
2050         cursor++;\
2051         if (cursor >= end) {\
2052                 buffer = buffer->next;\
2053                 if (buffer != NULL) {\
2054                         cursor = & (buffer->buffer [0]);\
2055                         if (buffer->next != NULL) {\
2056                                 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);\
2057                         } else {\
2058                                 end = job->cursor;\
2059                         }\
2060                 } else {\
2061                         cursor = NULL;\
2062                 }\
2063         }\
2064         UPDATE_JOB_BUFFER_CURSOR_MESSAGE ();\
2065 } while (0)
2066
2067 static void
2068 profiler_heap_shot_write_data_block (ProfilerHeapShotWriteJob *job) {
2069         ProfilerHeapShotWriteBuffer *buffer;
2070         gpointer* cursor;
2071         gpointer* end;
2072         guint64 start_counter;
2073         guint64 start_time;
2074         guint64 end_counter;
2075         guint64 end_time;
2076         
2077         write_uint64 (job->start_counter);
2078         write_uint64 (job->start_time);
2079         write_uint64 (job->end_counter);
2080         write_uint64 (job->end_time);
2081         write_uint32 (job->collection);
2082         MONO_PROFILER_GET_CURRENT_COUNTER (start_counter);
2083         MONO_PROFILER_GET_CURRENT_TIME (start_time);
2084         write_uint64 (start_counter);
2085         write_uint64 (start_time);
2086 #if DEBUG_HEAP_PROFILER
2087         printf ("profiler_heap_shot_write_data_block: start writing job %p (start %p, end %p)...\n", job, & (job->buffers->buffer [0]), job->cursor);
2088 #endif
2089         buffer = job->buffers;
2090         cursor = & (buffer->buffer [0]);
2091         if (buffer->next != NULL) {
2092                 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
2093         } else {
2094                 end = job->cursor;
2095         }
2096         if (cursor >= end) {
2097                 cursor = NULL;
2098         }
2099 #if DEBUG_HEAP_PROFILER
2100         printf ("profiler_heap_shot_write_data_block: in job %p, starting at buffer %p and cursor %p\n", job, buffer, cursor);
2101 #endif
2102         while (cursor != NULL) {
2103                 gpointer value = *cursor;
2104                 HeapProfilerJobValueCode code = GPOINTER_TO_UINT (value) & HEAP_CODE_MASK;
2105 #if DEBUG_HEAP_PROFILER
2106                 printf ("profiler_heap_shot_write_data_block: got value %p and code %d\n", value, code);
2107 #endif
2108                 
2109                 UPDATE_JOB_BUFFER_CURSOR ();
2110                 if (code == HEAP_CODE_FREE_OBJECT_CLASS) {
2111                         MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
2112                         //MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) % 4);
2113                         ClassIdMappingElement *class_id;
2114                         guint32 size;
2115                         
2116                         class_id = class_id_mapping_element_get (klass);
2117                         if (class_id == NULL) {
2118                                 printf ("profiler_heap_shot_write_data_block: unknown class %p", klass);
2119                         }
2120                         g_assert (class_id != NULL);
2121                         write_uint32 ((class_id->id << 2) | HEAP_CODE_FREE_OBJECT_CLASS);
2122                         
2123                         size = GPOINTER_TO_UINT (*cursor);
2124                         UPDATE_JOB_BUFFER_CURSOR ();
2125                         write_uint32 (size);
2126 #if DEBUG_HEAP_PROFILER
2127                         printf ("profiler_heap_shot_write_data_block: wrote unreachable object of class %p (id %d, size %d)\n", klass, class_id->id, size);
2128 #endif
2129                 } else if (code == HEAP_CODE_OBJECT) {
2130                         MonoObject *object = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
2131                         MonoClass *klass = mono_object_get_class (object);
2132                         ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
2133                         guint32 size = mono_object_get_size (object);
2134                         guint32 references = GPOINTER_TO_UINT (*cursor);
2135                         UPDATE_JOB_BUFFER_CURSOR ();
2136                         
2137                         if (class_id == NULL) {
2138                                 printf ("profiler_heap_shot_write_data_block: unknown class %p", klass);
2139                         }
2140                         g_assert (class_id != NULL);
2141                         
2142                         write_uint64 (GPOINTER_TO_UINT (value));
2143                         write_uint32 (class_id->id);
2144                         write_uint32 (size);
2145                         write_uint32 (references);
2146 #if DEBUG_HEAP_PROFILER
2147                         printf ("profiler_heap_shot_write_data_block: writing object %p (references %d)\n", value, references);
2148 #endif
2149                         
2150                         while (references > 0) {
2151                                 gpointer reference = *cursor;
2152                                 write_uint64 (GPOINTER_TO_UINT (reference));
2153                                 UPDATE_JOB_BUFFER_CURSOR ();
2154                                 references --;
2155 #if DEBUG_HEAP_PROFILER
2156                                 printf ("profiler_heap_shot_write_data_block:   inside object %p, wrote reference %p)\n", value, reference);
2157 #endif
2158                         }
2159                 } else {
2160 #if DEBUG_HEAP_PROFILER
2161                         printf ("profiler_heap_shot_write_data_block: unknown code %d in value %p\n", code, value);
2162 #endif
2163                         g_assert_not_reached ();
2164                 }
2165         }
2166         write_uint32 (0);
2167         
2168         MONO_PROFILER_GET_CURRENT_COUNTER (end_counter);
2169         MONO_PROFILER_GET_CURRENT_TIME (end_time);
2170         write_uint64 (end_counter);
2171         write_uint64 (end_time);
2172         
2173         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_HEAP_DATA);
2174 #if DEBUG_HEAP_PROFILER
2175         printf ("profiler_heap_shot_write_data_block: writing job %p done.\n", job);
2176 #endif
2177 }
2178 static void
2179 profiler_heap_shot_write_summary_block (ProfilerHeapShotWriteJob *job) {
2180         guint64 start_counter;
2181         guint64 start_time;
2182         guint64 end_counter;
2183         guint64 end_time;
2184         int id;
2185         
2186 #if DEBUG_HEAP_PROFILER
2187         printf ("profiler_heap_shot_write_summary_block: start writing job %p...\n", job);
2188 #endif
2189         MONO_PROFILER_GET_CURRENT_COUNTER (start_counter);
2190         MONO_PROFILER_GET_CURRENT_TIME (start_time);
2191         write_uint64 (start_counter);
2192         write_uint64 (start_time);
2193         
2194         write_uint32 (job->collection);
2195         
2196         for (id = 0; id < job->summary.capacity; id ++) {
2197                 if ((job->summary.per_class_data [id].reachable.instances > 0) || (job->summary.per_class_data [id].unreachable.instances > 0)) {
2198                         write_uint32 (id);
2199                         write_uint32 (job->summary.per_class_data [id].reachable.instances);
2200                         write_uint32 (job->summary.per_class_data [id].reachable.bytes);
2201                         write_uint32 (job->summary.per_class_data [id].unreachable.instances);
2202                         write_uint32 (job->summary.per_class_data [id].unreachable.bytes);
2203                 }
2204         }
2205         write_uint32 (0);
2206         
2207         MONO_PROFILER_GET_CURRENT_COUNTER (end_counter);
2208         MONO_PROFILER_GET_CURRENT_TIME (end_time);
2209         write_uint64 (end_counter);
2210         write_uint64 (end_time);
2211         
2212         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_HEAP_SUMMARY);
2213 #if DEBUG_HEAP_PROFILER
2214         printf ("profiler_heap_shot_write_summary_block: writing job %p done.\n", job);
2215 #endif
2216 }
2217
2218 static void
2219 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job) {
2220 #if DEBUG_HEAP_PROFILER
2221         printf ("profiler_heap_shot_write_block: working on job %p...\n", job);
2222 #endif
2223         
2224         if (profiler->action_flags.collection_summary == TRUE) {
2225                 profiler_heap_shot_write_summary_block (job);
2226         }
2227         
2228         if ((profiler->action_flags.unreachable_objects == TRUE) || (profiler->action_flags.heap_shot == TRUE)) {
2229                 profiler_heap_shot_write_data_block (job);
2230         }
2231         
2232         profiler_heap_shot_write_job_free_buffers (job);
2233 #if DEBUG_HEAP_PROFILER
2234         printf ("profiler_heap_shot_write_block: work on job %p done.\n", job);
2235 #endif
2236 }
2237
2238 static void
2239 write_element_load_block (LoadedElement *element, guint8 kind, gsize thread_id) {
2240         WRITE_BYTE (kind);
2241         write_uint64 (element->load_start_counter);
2242         write_uint64 (element->load_end_counter);
2243         write_uint64 (thread_id);
2244         write_string (element->name);
2245         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_LOADED);
2246         element->load_written = TRUE;
2247 }
2248
2249 static void
2250 write_element_unload_block (LoadedElement *element, guint8 kind, gsize thread_id) {
2251         WRITE_BYTE (kind);
2252         write_uint64 (element->unload_start_counter);
2253         write_uint64 (element->unload_end_counter);
2254         write_uint64 (thread_id);
2255         write_string (element->name);
2256         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED);
2257         element->unload_written = TRUE;
2258 }
2259
2260 static void
2261 write_clock_data (void) {
2262         guint64 counter;
2263         guint64 time;
2264         
2265         MONO_PROFILER_GET_CURRENT_COUNTER (counter);
2266         MONO_PROFILER_GET_CURRENT_TIME (time);
2267         
2268         write_uint64 (counter);
2269         write_uint64 (time);
2270 }
2271
2272 static void
2273 write_mapping_block (gsize thread_id) {
2274         ClassIdMappingElement *current_class;
2275         MethodIdMappingElement *current_method;
2276         
2277         if ((profiler->classes->unwritten == NULL) && (profiler->methods->unwritten == NULL))
2278                 return;
2279         
2280 #if (DEBUG_MAPPING_EVENTS || DEBUG_FILE_WRITES)
2281         printf ("[write_mapping_block][TID %ld] START\n", thread_id);
2282 #endif
2283         
2284         write_clock_data ();
2285         write_uint64 (thread_id);
2286         
2287         for (current_class = profiler->classes->unwritten; current_class != NULL; current_class = current_class->next_unwritten) {
2288                 write_uint32 (current_class->id);
2289                 write_string (current_class->name);
2290 #if (DEBUG_MAPPING_EVENTS)
2291                 printf ("mapping CLASS (%d => %s)\n", current_class->id, current_class->name);
2292 #endif
2293                 g_free (current_class->name);
2294                 current_class->name = NULL;
2295         }
2296         write_uint32 (0);
2297         profiler->classes->unwritten = NULL;
2298         
2299         for (current_method = profiler->methods->unwritten; current_method != NULL; current_method = current_method->next_unwritten) {
2300                 MonoMethod *method = current_method->method;
2301                 MonoClass *klass = mono_method_get_class (method);
2302                 ClassIdMappingElement *class_element = class_id_mapping_element_get (klass);
2303                 g_assert (class_element != NULL);
2304                 write_uint32 (current_method->id);
2305                 write_uint32 (class_element->id);
2306                 write_string (current_method->name);
2307 #if (DEBUG_MAPPING_EVENTS)
2308                 printf ("mapping METHOD ([%d]%d => %s)\n", class_element?class_element->id:1, current_method->id, current_method->name);
2309 #endif
2310                 g_free (current_method->name);
2311                 current_method->name = NULL;
2312         }
2313         write_uint32 (0);
2314         profiler->methods->unwritten = NULL;
2315         
2316         write_clock_data ();
2317         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_MAPPING);
2318         
2319 #if (DEBUG_MAPPING_EVENTS || DEBUG_FILE_WRITES)
2320         printf ("[write_mapping_block][TID %ld] END\n", thread_id);
2321 #endif
2322 }
2323
2324 typedef enum {
2325         MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER = 1,
2326         MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_IMPLICIT = 2,
2327         MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT = 3,
2328         MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION = 4,
2329         MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT = 5,
2330         MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT = 6,
2331         MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT = 7
2332 } MonoProfilerPackedEventCode;
2333 #define MONO_PROFILER_PACKED_EVENT_CODE_BITS 3
2334 #define MONO_PROFILER_PACKED_EVENT_DATA_BITS (8-MONO_PROFILER_PACKED_EVENT_CODE_BITS)
2335 #define MONO_PROFILER_PACKED_EVENT_DATA_MASK ((1<<MONO_PROFILER_PACKED_EVENT_DATA_BITS)-1)
2336
2337 #define MONO_PROFILER_EVENT_MAKE_PACKED_CODE(result,data,base) do {\
2338         result = ((base)|((data & MONO_PROFILER_PACKED_EVENT_DATA_MASK) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
2339         data >>= MONO_PROFILER_PACKED_EVENT_DATA_BITS;\
2340 } while (0)
2341 #define MONO_PROFILER_EVENT_MAKE_FULL_CODE(result,code,kind,base) do {\
2342         result = ((base)|((((kind)<<4) | (code)) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
2343 } while (0)
2344
2345 static void
2346 rewrite_last_written_stack (ProfilerThreadStack *stack) {
2347         guint8 event_code;
2348         int i = thread_stack_get_last_written_frame (stack);
2349         
2350         MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, MONO_PROFILER_EVENT_STACK_SECTION, 0, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
2351         WRITE_BYTE (event_code);
2352         write_uint32 (0);
2353         write_uint32 (i);
2354         
2355         while (i > 0) {
2356                 i--;
2357                 write_uint32 (thread_stack_written_frame_at_index (stack, i));
2358         }
2359 }
2360
2361
2362 static ProfilerEventData*
2363 write_stack_section_event (ProfilerEventData *events, ProfilerPerThreadData *data) {
2364         int last_saved_frame = events->data.number;
2365         int saved_frames = events->value;
2366         guint8 event_code;
2367         int i;
2368         
2369         MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, MONO_PROFILER_EVENT_STACK_SECTION, 0, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
2370         WRITE_BYTE (event_code);
2371         write_uint32 (last_saved_frame);
2372         write_uint32 (saved_frames);
2373         thread_stack_set_last_written_frame (&(data->stack), last_saved_frame + saved_frames);
2374         events++;
2375         
2376         for (i = 0; i < saved_frames; i++) {
2377                 guint8 code = events->code;
2378                 guint32 jit_flag;
2379                 MethodIdMappingElement *method;
2380                 guint32 frame_value;
2381                 
2382                 if (code == MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER) {
2383                         jit_flag = 0;
2384                 } else if (code == MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER) {
2385                         jit_flag = 1;
2386                 } else {
2387                         g_assert_not_reached ();
2388                         jit_flag = 0;
2389                 }
2390                 
2391                 method = method_id_mapping_element_get (events->data.address);
2392                 g_assert (method != NULL);
2393                 frame_value = (method->id << 1) | jit_flag;
2394                 write_uint32 (frame_value);
2395                 thread_stack_write_frame_at_index (&(data->stack), last_saved_frame + saved_frames - (1 + i), frame_value);
2396                 events ++;
2397         }
2398         
2399         return events;
2400 }
2401
2402 static ProfilerEventData*
2403 write_event (ProfilerEventData *event, ProfilerPerThreadData *data) {
2404         ProfilerEventData *next = event + 1;
2405         gboolean write_event_value = TRUE;
2406         guint8 event_code;
2407         guint64 event_data;
2408         guint64 event_value;
2409         gboolean write_event_value_extension_1 = FALSE;
2410         guint64 event_value_extension_1 = 0;
2411         gboolean write_event_value_extension_2 = FALSE;
2412         guint64 event_value_extension_2 = 0;
2413
2414         event_value = event->value;
2415         if (event_value == MAX_EVENT_VALUE) {
2416                 event_value = *((guint64*)next);
2417                 next ++;
2418         }
2419         
2420         if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
2421                 MethodIdMappingElement *element = method_id_mapping_element_get (event->data.address);
2422                 g_assert (element != NULL);
2423                 event_data = element->id;
2424                 
2425                 if (event->code == MONO_PROFILER_EVENT_METHOD_CALL) {
2426                         if (event->kind == MONO_PROFILER_EVENT_KIND_START) {
2427                                 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER);
2428                         } else {
2429                                 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT);
2430                         }
2431                 } else {
2432                         MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT); 
2433                 }
2434         } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
2435                 ClassIdMappingElement *element = class_id_mapping_element_get (event->data.address);
2436                 g_assert (element != NULL);
2437                 event_data = element->id;
2438                 
2439                 if (event->code == MONO_PROFILER_EVENT_CLASS_ALLOCATION) {
2440                         if ((! profiler->action_flags.save_allocation_caller) || (! (next->code == MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER))) {
2441                                 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION);
2442                         } else {
2443                                 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, MONO_PROFILER_EVENT_JIT_TIME_ALLOCATION, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
2444                         }
2445                         
2446                         if (profiler->action_flags.save_allocation_caller) {
2447                                 MonoMethod *caller_method = next->data.address;
2448                                 
2449                                 if ((next->code != MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER) && (next->code != MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER)) {
2450                                         g_assert_not_reached ();
2451                                 }
2452                                 
2453                                 if (caller_method != NULL) {
2454                                         MethodIdMappingElement *caller = method_id_mapping_element_get (caller_method);
2455                                         g_assert (caller != NULL);
2456                                         event_value_extension_1 = caller->id;
2457                                 }
2458
2459                                 write_event_value_extension_1 = TRUE;
2460                                 next ++;
2461                         }
2462                         
2463                         if (profiler->action_flags.allocations_carry_id) {
2464                                 event_value_extension_2  = GPOINTER_TO_UINT (next->data.address);
2465                                 
2466                                 if (next->code != MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID) {
2467                                         g_assert_not_reached ();
2468                                 }
2469                                 
2470                                 write_event_value_extension_2 = TRUE;
2471                                 next ++;
2472                         }
2473                 } else {
2474                         MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT);
2475                 }
2476         } else {
2477                 if (event->code == MONO_PROFILER_EVENT_STACK_SECTION) {
2478                         return write_stack_section_event (event, data);
2479                 } else {
2480                         event_data = event->data.number;
2481                         MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
2482                 }
2483         }
2484         
2485         /* Skip writing JIT events if the user did not ask for them */
2486         if ((event->code == MONO_PROFILER_EVENT_METHOD_JIT) && ! profiler->action_flags.jit_time) {
2487                 return next;
2488         }
2489         
2490 #if (DEBUG_LOGGING_PROFILER)
2491         EVENT_MARK ();
2492         printf ("writing EVENT[%p] data_type:%d, kind:%d, code:%d (%d:%ld:%ld)\n", event,
2493                         event->data_type, event->kind, event->code,
2494                         event_code, event_data, event_value);
2495 #endif
2496         
2497         WRITE_BYTE (event_code);
2498         write_uint64 (event_data);
2499         if (write_event_value) {
2500                 write_uint64 (event_value);
2501                 if (write_event_value_extension_1) {
2502                         write_uint64 (event_value_extension_1);
2503                 }
2504                 if (write_event_value_extension_2) {
2505                         write_uint64 (event_value_extension_2);
2506                 }
2507         }
2508         
2509         return next;
2510 }
2511
2512 static void
2513 write_thread_data_block (ProfilerPerThreadData *data) {
2514         ProfilerEventData *start = data->first_unwritten_event;
2515         ProfilerEventData *end = data->first_unmapped_event;
2516         
2517         if (start == end)
2518                 return;
2519 #if (DEBUG_FILE_WRITES)
2520         printf ("write_thread_data_block: preparing buffer for thread %ld\n", (guint64) data->thread_id);
2521 #endif
2522         write_clock_data ();
2523         write_uint64 (data->thread_id);
2524         
2525         write_uint64 (data->start_event_counter);
2526         
2527         /* Make sure that stack sections can be fully reconstructed even reading only one block */
2528         rewrite_last_written_stack (&(data->stack));
2529         
2530         while (start < end) {
2531                 start = write_event (start, data);
2532         }
2533         WRITE_BYTE (0);
2534         data->first_unwritten_event = end;
2535         
2536         write_clock_data ();
2537         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_EVENTS);
2538 #if (DEBUG_FILE_WRITES)
2539         printf ("write_thread_data_block: buffer for thread %ld written\n", (guint64) data->thread_id);
2540 #endif
2541 }
2542
2543 static ProfilerExecutableMemoryRegionData*
2544 profiler_executable_memory_region_new (gpointer *start, gpointer *end, guint32 file_offset, char *file_name, guint32 id) {
2545         ProfilerExecutableMemoryRegionData *result = g_new (ProfilerExecutableMemoryRegionData, 1);
2546         result->start = start;
2547         result->end = end;
2548         result->file_offset = file_offset;
2549         result->file_name = g_strdup (file_name);
2550         result->id = id;
2551         result->is_new = TRUE;
2552         
2553         result->file = NULL;
2554         result->file_region_reference = NULL;
2555         result->symbols_capacity = id;
2556         result->symbols_count = id;
2557         result->symbols = NULL;
2558         
2559         return result;
2560 }
2561
2562 static void
2563 executable_file_close (ProfilerExecutableMemoryRegionData *region);
2564
2565 static void
2566 profiler_executable_memory_region_destroy (ProfilerExecutableMemoryRegionData *data) {
2567         if (data->file != NULL) {
2568                 executable_file_close (data);
2569                 data->file = NULL;
2570         }
2571         if (data->symbols != NULL) {
2572                 g_free (data->symbols);
2573                 data->symbols = NULL;
2574         }
2575         if (data->file_name != NULL) {
2576                 g_free (data->file_name);
2577                 data->file_name = NULL;
2578         }
2579         g_free (data);
2580 }
2581
2582 static ProfilerExecutableMemoryRegions*
2583 profiler_executable_memory_regions_new (int next_id, int next_unmanaged_function_id) {
2584         ProfilerExecutableMemoryRegions *result = g_new (ProfilerExecutableMemoryRegions, 1);
2585         result->regions = g_new0 (ProfilerExecutableMemoryRegionData*, 32);
2586         result->regions_capacity = 32;
2587         result->regions_count = 0;
2588         result->next_id = next_id;
2589         result->next_unmanaged_function_id = next_unmanaged_function_id;
2590         return result;
2591 }
2592
2593 static void
2594 profiler_executable_memory_regions_destroy (ProfilerExecutableMemoryRegions *regions) {
2595         int i;
2596         
2597         for (i = 0; i < regions->regions_count; i++) {
2598                 profiler_executable_memory_region_destroy (regions->regions [i]);
2599         }
2600         g_free (regions->regions);
2601         g_free (regions);
2602 }
2603
2604 static ProfilerExecutableMemoryRegionData*
2605 find_address_region (ProfilerExecutableMemoryRegions *regions, gpointer address) {
2606         int low_index = 0;
2607         int high_index = regions->regions_count;
2608         int middle_index = 0;
2609         ProfilerExecutableMemoryRegionData *middle_region = regions->regions [0];
2610         
2611         if ((regions->regions_count == 0) || (regions->regions [low_index]->start > address) || (regions->regions [high_index - 1]->end < address)) {
2612                 return NULL;
2613         }
2614         
2615         //printf ("find_address_region: Looking for address %p in %d regions (from %p to %p)\n", address, regions->regions_count, regions->regions [low_index]->start, regions->regions [high_index - 1]->end);
2616         
2617         while (low_index != high_index) {
2618                 middle_index = low_index + ((high_index - low_index) / 2);
2619                 middle_region = regions->regions [middle_index];
2620                 
2621                 //printf ("find_address_region: Looking for address %p, considering index %d[%p-%p] (%d-%d)\n", address, middle_index, middle_region->start, middle_region->end, low_index, high_index);
2622                 
2623                 if (middle_region->start > address) {
2624                         if (middle_index > 0) {
2625                                 high_index = middle_index;
2626                         } else {
2627                                 return NULL;
2628                         }
2629                 } else if (middle_region->end < address) {
2630                         if (middle_index < regions->regions_count - 1) {
2631                                 low_index = middle_index + 1;
2632                         } else {
2633                                 return NULL;
2634                         }
2635                 } else {
2636                         return middle_region;
2637                 }
2638         }
2639         
2640         if ((middle_region == NULL) || (middle_region->start > address) || (middle_region->end < address)) {
2641                 return NULL;
2642         } else {
2643                 return middle_region;
2644         }
2645 }
2646
2647 static void
2648 append_region (ProfilerExecutableMemoryRegions *regions, gpointer *start, gpointer *end, guint32 file_offset, char *file_name) {
2649         if (regions->regions_count >= regions->regions_capacity) {
2650                 ProfilerExecutableMemoryRegionData **new_regions = g_new0 (ProfilerExecutableMemoryRegionData*, regions->regions_capacity * 2);
2651                 memcpy (new_regions, regions->regions, regions->regions_capacity * sizeof (ProfilerExecutableMemoryRegionData*));
2652                 g_free (regions->regions);
2653                 regions->regions = new_regions;
2654                 regions->regions_capacity = regions->regions_capacity * 2;
2655         }
2656         regions->regions [regions->regions_count] = profiler_executable_memory_region_new (start, end, file_offset, file_name, regions->next_id);
2657         regions->regions_count ++;
2658         regions->next_id ++;
2659 }
2660
2661 static void
2662 restore_old_regions (ProfilerExecutableMemoryRegions *old_regions, ProfilerExecutableMemoryRegions *new_regions) {
2663         int old_i;
2664         int new_i;
2665         
2666         for (old_i = 0; old_i < old_regions->regions_count; old_i++) {
2667                 ProfilerExecutableMemoryRegionData *old_region = old_regions->regions [old_i];
2668                 for (new_i = 0; new_i < new_regions->regions_count; new_i++) {
2669                         ProfilerExecutableMemoryRegionData *new_region = new_regions->regions [new_i];
2670                         if ((old_region->start == new_region->start) &&
2671                                         (old_region->end == new_region->end) &&
2672                                         (old_region->file_offset == new_region->file_offset) &&
2673                                         ! strcmp (old_region->file_name, new_region->file_name)) {
2674                                 new_regions->regions [new_i] = old_region;
2675                                 old_regions->regions [old_i] = new_region;
2676                                 
2677                                 // FIXME (sanity check)
2678                                 g_assert (new_region->is_new && ! old_region->is_new);
2679                         }
2680                 }
2681         }
2682 }
2683
2684 static int
2685 compare_regions (const void *a1, const void *a2) {
2686         ProfilerExecutableMemoryRegionData *r1 = * (ProfilerExecutableMemoryRegionData**) a1;
2687         ProfilerExecutableMemoryRegionData *r2 = * (ProfilerExecutableMemoryRegionData**) a2;
2688         return (r1->start < r2->start)? -1 : ((r1->start > r2->start)? 1 : 0);
2689 }
2690
2691 static void
2692 sort_regions (ProfilerExecutableMemoryRegions *regions) {
2693         qsort (regions->regions, regions->regions_count, sizeof (ProfilerExecutableMemoryRegionData *), compare_regions);
2694 }
2695
2696 static void
2697 executable_file_add_region_reference (ProfilerExecutableFile *file, ProfilerExecutableMemoryRegionData *region) {
2698         guint8 *section_headers = file->data + file->header->e_shoff;
2699         int section_index;
2700         
2701         for (section_index = 1; section_index < file->header->e_shnum; section_index ++) {
2702                 ElfSection *section_header = (ElfSection*) (section_headers + (file->header->e_shentsize * section_index));
2703                 
2704                 if ((section_header->sh_addr != 0) && (section_header->sh_flags & ELF_SHF_EXECINSTR) &&
2705                                 (region->file_offset <= section_header->sh_offset) && (region->file_offset + (((guint8*)region->end)-((guint8*)region->start)) >= (section_header->sh_offset + section_header->sh_size))) {
2706                         ProfilerExecutableFileSectionRegion *section_region = & (file->section_regions [section_index]);
2707                         section_region->region = region;
2708                         section_region->section_address = (gpointer) section_header->sh_addr;
2709                         section_region->section_offset = section_header->sh_offset;
2710                         region->file_region_reference = section_region;
2711                 }
2712         }
2713 }
2714
2715 static ProfilerExecutableFile*
2716 executable_file_open (ProfilerExecutableMemoryRegionData *region) {
2717         ProfilerExecutableFiles *files = & (profiler->executable_files);
2718         ProfilerExecutableFile *file = region->file;
2719         
2720         if (file == NULL) {
2721                 file = (ProfilerExecutableFile*) g_hash_table_lookup (files->table, region->file_name);
2722                 
2723                 if (file == NULL) {
2724                         guint16 test = 0x0102;
2725                         struct stat stat_buffer;
2726                         int symtab_index = 0;
2727                         int strtab_index = 0;
2728                         int dynsym_index = 0;
2729                         int dynstr_index = 0;
2730                         ElfHeader *header;
2731                         guint8 *section_headers;
2732                         int section_index;
2733                         int strings_index;
2734                         
2735                         file = g_new0 (ProfilerExecutableFile, 1);
2736                         region->file = file;
2737                         g_hash_table_insert (files->table, region->file_name, file);
2738                         file->reference_count ++;
2739                         file->next_new_file = files->new_files;
2740                         files->new_files = file;
2741                         
2742                         file->fd = open (region->file_name, O_RDONLY);
2743                         if (file->fd == -1) {
2744                                 //g_warning ("Cannot open file '%s': '%s'", region->file_name, strerror (errno));
2745                                 return file;
2746                         } else {
2747                                 if (fstat (file->fd, &stat_buffer) != 0) {
2748                                         //g_warning ("Cannot stat file '%s': '%s'", region->file_name, strerror (errno));
2749                                         return file;
2750                                 } else {
2751                                         size_t region_length = ((guint8*)region->end) - ((guint8*)region->start);
2752                                         file->length = stat_buffer.st_size;
2753                                         
2754                                         if (file->length == region_length) {
2755                                                 file->data = region->start;
2756                                                 close (file->fd);
2757                                                 file->fd = -1;
2758                                         } else {
2759                                                 file->data = mmap (NULL, file->length, PROT_READ, MAP_PRIVATE, file->fd, 0);
2760                                                 
2761                                                 if (file->data == MAP_FAILED) {
2762                                                         close (file->fd);
2763                                                         //g_warning ("Cannot map file '%s': '%s'", region->file_name, strerror (errno));
2764                                                         file->data = NULL;
2765                                                         return file;
2766                                                 }
2767                                         }
2768                                 }
2769                         }
2770                         
2771                         header = (ElfHeader*) file->data;
2772                         
2773                         if ((header->e_ident [EI_MAG0] != 0x7f) || (header->e_ident [EI_MAG1] != 'E') ||
2774                                         (header->e_ident [EI_MAG2] != 'L') || (header->e_ident [EI_MAG3] != 'F')) {
2775                                 return file;
2776                         }
2777                         
2778                         if (sizeof (gsize) == 4) {
2779                                 if (header->e_ident [EI_CLASS] != ELF_CLASS_32) {
2780                                         g_warning ("Class is not ELF_CLASS_32 with gsize size %d", (int) sizeof (gsize));
2781                                         return file;
2782                                 }
2783                         } else if (sizeof (gsize) == 8) {
2784                                 if (header->e_ident [EI_CLASS] != ELF_CLASS_64) {
2785                                         g_warning ("Class is not ELF_CLASS_64 with gsize size %d", (int) sizeof (gsize));
2786                                         return file;
2787                                 }
2788                         } else {
2789                                 g_warning ("Absurd gsize size %d", (int) sizeof (gsize));
2790                                 return file;
2791                         }
2792                         
2793                         if ((*(guint8*)(&test)) == 0x01) {
2794                                 if (header->e_ident [EI_DATA] != ELF_DATA_MSB) {
2795                                         g_warning ("Data is not ELF_DATA_MSB with first test byte 0x01");
2796                                         return file;
2797                                 }
2798                         } else if ((*(guint8*)(&test)) == 0x02) {
2799                                 if (header->e_ident [EI_DATA] != ELF_DATA_LSB) {
2800                                         g_warning ("Data is not ELF_DATA_LSB with first test byte 0x02");
2801                                         return file;
2802                                 }
2803                         } else {
2804                                 g_warning ("Absurd test byte value");
2805                                 return file;
2806                         }
2807                         
2808                         /* OK, this is a usable elf file... */
2809                         file->header = header;
2810                         section_headers = file->data + header->e_shoff;
2811                         file->main_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * header->e_shstrndx)))->sh_offset);
2812                         
2813                         for (section_index = 0; section_index < header->e_shnum; section_index ++) {
2814                                 ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index));
2815                                 
2816                                 if (section_header->sh_type == ELF_SHT_SYMTAB) {
2817                                         symtab_index = section_index;
2818                                 } else if (section_header->sh_type == ELF_SHT_DYNSYM) {
2819                                         dynsym_index = section_index;
2820                                 } else if (section_header->sh_type == ELF_SHT_STRTAB) {
2821                                         if (! strcmp (file->main_string_table + section_header->sh_name, ".strtab")) {
2822                                                 strtab_index = section_index;
2823                                         } else if (! strcmp (file->main_string_table + section_header->sh_name, ".dynstr")) {
2824                                                 dynstr_index = section_index;
2825                                         }
2826                                 }
2827                         }
2828                         
2829                         if ((symtab_index != 0) && (strtab_index != 0)) {
2830                                 section_index = symtab_index;
2831                                 strings_index = strtab_index;
2832                         } else if ((dynsym_index != 0) && (dynstr_index != 0)) {
2833                                 section_index = dynsym_index;
2834                                 strings_index = dynstr_index;
2835                         } else {
2836                                 section_index = 0;
2837                                 strings_index = 0;
2838                         }
2839                         
2840                         if (section_index != 0) {
2841                                 ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index));
2842                                 file->symbol_size = section_header->sh_entsize;
2843                                 file->symbols_count = (guint32) (section_header->sh_size / section_header->sh_entsize);
2844                                 file->symbols_start = file->data + section_header->sh_offset;
2845                                 file->symbols_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * strings_index)))->sh_offset);
2846                         }
2847                         
2848                         file->section_regions = g_new0 (ProfilerExecutableFileSectionRegion, file->header->e_shnum);
2849                 } else {
2850                         region->file = file;
2851                         file->reference_count ++;
2852                 }
2853         }
2854         
2855         if (file->header != NULL) {
2856                 executable_file_add_region_reference (file, region);
2857         }
2858         
2859         return file;
2860 }
2861
2862 static void
2863 executable_file_free (ProfilerExecutableFile* file) {
2864         if (file->fd != -1) {
2865                 if (close (file->fd) != 0) {
2866                         g_warning ("Cannot close file: '%s'", strerror (errno));
2867                 }
2868                 if (file->data != NULL) {
2869                         if (munmap (file->data, file->length) != 0) {
2870                                 g_warning ("Cannot unmap file: '%s'", strerror (errno));
2871                         }
2872                 }
2873         }
2874         if (file->section_regions != NULL) {
2875                 g_free (file->section_regions);
2876                 file->section_regions = NULL;
2877         }
2878         g_free (file);
2879 }
2880
2881 static void
2882 executable_file_close (ProfilerExecutableMemoryRegionData *region) {
2883         region->file->reference_count --;
2884         
2885         if ((region->file_region_reference != NULL) && (region->file_region_reference->region == region)) {
2886                 region->file_region_reference->region = NULL;
2887                 region->file_region_reference->section_address = 0;
2888                 region->file_region_reference->section_offset = 0;
2889         }
2890         
2891         if (region->file->reference_count <= 0) {
2892                 ProfilerExecutableFiles *files = & (profiler->executable_files);
2893                 g_hash_table_remove (files->table, region->file_name);
2894                 executable_file_free (region->file);
2895                 region->file = NULL;
2896         }
2897 }
2898
2899 static void
2900 executable_file_count_symbols (ProfilerExecutableFile *file) {
2901         int symbol_index;
2902         
2903         for (symbol_index = 0; symbol_index < file->symbols_count; symbol_index ++) {
2904                 ElfSymbol *symbol = (ElfSymbol*) (file->symbols_start + (symbol_index * file->symbol_size));
2905                 
2906                 if ((ELF_ST_TYPE (symbol->st_info) == ELF_STT_FUNC) &&
2907                                 (symbol->st_shndx > 0) &&
2908                                 (symbol->st_shndx < file->header->e_shnum)) {
2909                         int symbol_section_index = symbol->st_shndx;
2910                         ProfilerExecutableMemoryRegionData *region = file->section_regions [symbol_section_index].region;
2911                         if ((region != NULL) && (region->symbols == NULL)) {
2912                                 region->symbols_count ++;
2913                         }
2914                 }
2915         }
2916 }
2917
2918 static void
2919 executable_memory_regions_prepare_symbol_tables (ProfilerExecutableMemoryRegions *regions) {
2920         int i;
2921         for (i = 0; i < regions->regions_count; i++) {
2922                 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2923                 if ((region->symbols_count > 0) && (region->symbols == NULL)) {
2924                         region->symbols = g_new (ProfilerUnmanagedSymbol, region->symbols_count);
2925                         region->symbols_capacity = region->symbols_count;
2926                         region->symbols_count = 0;
2927                 }
2928         }
2929 }
2930
2931 static const char*
2932 executable_region_symbol_get_name (ProfilerExecutableMemoryRegionData *region, ProfilerUnmanagedSymbol *symbol) {
2933         ElfSymbol *elf_symbol = (ElfSymbol*) (region->file->symbols_start + (symbol->index * region->file->symbol_size));
2934         return region->file->symbols_string_table + elf_symbol->st_name;
2935 }
2936
2937 static void
2938 executable_file_build_symbol_tables (ProfilerExecutableFile *file) {
2939         int symbol_index;
2940         
2941         for (symbol_index = 0; symbol_index < file->symbols_count; symbol_index ++) {
2942                 ElfSymbol *symbol = (ElfSymbol*) (file->symbols_start + (symbol_index * file->symbol_size));
2943                 
2944                 if ((ELF_ST_TYPE (symbol->st_info) == ELF_STT_FUNC) &&
2945                                 (symbol->st_shndx > 0) &&
2946                                 (symbol->st_shndx < file->header->e_shnum)) {
2947                         int symbol_section_index = symbol->st_shndx;
2948                         ProfilerExecutableFileSectionRegion *section_region = & (file->section_regions [symbol_section_index]);
2949                         ProfilerExecutableMemoryRegionData *region = section_region->region;
2950                         
2951                         if (region != NULL) {
2952                                 ProfilerUnmanagedSymbol *new_symbol = & (region->symbols [region->symbols_count]);
2953                                 region->symbols_count ++;
2954                                 
2955                                 new_symbol->id = 0;
2956                                 new_symbol->index = symbol_index;
2957                                 new_symbol->size = symbol->st_size;
2958                                 new_symbol->offset = (((guint8*) symbol->st_value) - section_region->section_address) - (region->file_offset - section_region->section_offset);
2959                         }
2960                 }
2961         }
2962 }
2963
2964 static int
2965 compare_region_symbols (const void *p1, const void *p2) {
2966         const ProfilerUnmanagedSymbol *s1 = p1;
2967         const ProfilerUnmanagedSymbol *s2 = p2;
2968         return (s1->offset < s2->offset)? -1 : ((s1->offset > s2->offset)? 1 : 0);
2969 }
2970
2971 static void
2972 executable_memory_regions_sort_symbol_tables (ProfilerExecutableMemoryRegions *regions) {
2973         int i;
2974         for (i = 0; i < regions->regions_count; i++) {
2975                 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2976                 if ((region->is_new) && (region->symbols != NULL)) {
2977                         qsort (region->symbols, region->symbols_count, sizeof (ProfilerUnmanagedSymbol), compare_region_symbols);
2978                 }
2979         }
2980 }
2981
2982 static void
2983 build_symbol_tables (ProfilerExecutableMemoryRegions *regions, ProfilerExecutableFiles *files) {
2984         int i;
2985         ProfilerExecutableFile *file;
2986         
2987         for (i = 0; i < regions->regions_count; i++) {
2988                 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2989                 if ((region->is_new) && (region->file == NULL)) {
2990                         executable_file_open (region);
2991                 }
2992         }
2993         
2994         for (file = files->new_files; file != NULL; file = file->next_new_file) {
2995                 executable_file_count_symbols (file);
2996         }
2997         
2998         executable_memory_regions_prepare_symbol_tables (regions);
2999         
3000         for (file = files->new_files; file != NULL; file = file->next_new_file) {
3001                 executable_file_build_symbol_tables (file);
3002         }
3003         
3004         executable_memory_regions_sort_symbol_tables (regions);
3005         
3006         file = files->new_files;
3007         while (file != NULL) {
3008                 ProfilerExecutableFile *next_file = file->next_new_file;
3009                 file->next_new_file = NULL;
3010                 file = next_file;
3011         }
3012         files->new_files = NULL;
3013 }
3014
3015 static ProfilerUnmanagedSymbol*
3016 executable_memory_region_find_symbol (ProfilerExecutableMemoryRegionData *region, guint32 offset) {
3017         if (region->symbols_count > 0) {
3018                 ProfilerUnmanagedSymbol *low = region->symbols;
3019                 ProfilerUnmanagedSymbol *high = region->symbols + (region->symbols_count - 1);
3020                 int step = region->symbols_count >> 1;
3021                 ProfilerUnmanagedSymbol *current = region->symbols + step;
3022                 
3023                 do {
3024                         step = (high - low) >> 1;
3025                         
3026                         if (offset < current->offset) {
3027                                 high = current;
3028                                 current = high - step;
3029                         } else if (offset >= current->offset) {
3030                                 if (offset >= (current->offset + current->size)) {
3031                                         low = current;
3032                                         current = low + step;
3033                                 } else {
3034                                         return current;
3035                                 }
3036                         }
3037                 } while (step > 0);
3038                 
3039                 if ((offset >= current->offset) && (offset < (current->offset + current->size))) {
3040                         return current;
3041                 } else {
3042                         return NULL;
3043                 }
3044         } else {
3045                 return NULL;
3046         }
3047 }
3048
3049 //FIXME: make also Win32 and BSD variants
3050 #define MAPS_BUFFER_SIZE 4096
3051 #define MAPS_FILENAME_SIZE 2048
3052
3053 static gboolean
3054 update_regions_buffer (int fd, char *buffer) {
3055         ssize_t result = read (fd, buffer, MAPS_BUFFER_SIZE);
3056         
3057         if (result == MAPS_BUFFER_SIZE) {
3058                 return TRUE;
3059         } else if (result >= 0) {
3060                 *(buffer + result) = 0;
3061                 return FALSE;
3062         } else {
3063                 *buffer = 0;
3064                 return FALSE;
3065         }
3066 }
3067
3068 #define GOTO_NEXT_CHAR(c,b,fd) do {\
3069         (c)++;\
3070         if (((c) - (b) >= MAPS_BUFFER_SIZE) || ((*(c) == 0) && ((c) != (b)))) {\
3071                 update_regions_buffer ((fd), (b));\
3072                 (c) = (b);\
3073         }\
3074 } while (0);
3075
3076 static int hex_digit_value (char c) {
3077         if ((c >= '0') && (c <= '9')) {
3078                 return c - '0';
3079         } else if ((c >= 'a') && (c <= 'f')) {
3080                 return c - 'a' + 10;
3081         } else if ((c >= 'A') && (c <= 'F')) {
3082                 return c - 'A' + 10;
3083         } else {
3084                 return 0;
3085         }
3086 }
3087
3088 /*
3089  * Start address
3090  * -
3091  * End address
3092  * (space)
3093  * Permissions
3094  * Offset
3095  * (space)
3096  * Device
3097  * (space)
3098  * Inode
3099  * (space)
3100  * File
3101  * \n
3102  */
3103 typedef enum {
3104         MAP_LINE_PARSER_STATE_INVALID,
3105         MAP_LINE_PARSER_STATE_START_ADDRESS,
3106         MAP_LINE_PARSER_STATE_END_ADDRESS,
3107         MAP_LINE_PARSER_STATE_PERMISSIONS,
3108         MAP_LINE_PARSER_STATE_OFFSET,
3109         MAP_LINE_PARSER_STATE_DEVICE,
3110         MAP_LINE_PARSER_STATE_INODE,
3111         MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME,
3112         MAP_LINE_PARSER_STATE_FILENAME,
3113         MAP_LINE_PARSER_STATE_DONE
3114 } MapLineParserState;
3115
3116 const char *map_line_parser_state [] = {
3117         "INVALID",
3118         "START_ADDRESS",
3119         "END_ADDRESS",
3120         "PERMISSIONS",
3121         "OFFSET",
3122         "DEVICE",
3123         "INODE",
3124         "BLANK_BEFORE_FILENAME",
3125         "FILENAME",
3126         "DONE"
3127 };
3128
3129 static char*
3130 parse_map_line (ProfilerExecutableMemoryRegions *regions, int fd, char *buffer, char *filename, char *current) {
3131         MapLineParserState state = MAP_LINE_PARSER_STATE_START_ADDRESS;
3132         gsize start_address = 0;
3133         gsize end_address = 0;
3134         guint32 offset = 0;
3135         int filename_index = 0;
3136         gboolean is_executable = FALSE;
3137         gboolean done = FALSE;
3138         
3139         char c = *current;
3140         
3141         while (1) {
3142                 switch (state) {
3143                 case MAP_LINE_PARSER_STATE_START_ADDRESS:
3144                         if (isxdigit (c)) {
3145                                 start_address <<= 4;
3146                                 start_address |= hex_digit_value (c);
3147                         } else if (c == '-') {
3148                                 state = MAP_LINE_PARSER_STATE_END_ADDRESS;
3149                         } else {
3150                                 state = MAP_LINE_PARSER_STATE_INVALID;
3151                         }
3152                         break;
3153                 case MAP_LINE_PARSER_STATE_END_ADDRESS:
3154                         if (isxdigit (c)) {
3155                                 end_address <<= 4;
3156                                 end_address |= hex_digit_value (c);
3157                         } else if (isblank (c)) {
3158                                 state = MAP_LINE_PARSER_STATE_PERMISSIONS;
3159                         } else {
3160                                 state = MAP_LINE_PARSER_STATE_INVALID;
3161                         }
3162                         break;
3163                 case MAP_LINE_PARSER_STATE_PERMISSIONS:
3164                         if (c == 'x') {
3165                                 is_executable = TRUE;
3166                         } else if (isblank (c)) {
3167                                 state = MAP_LINE_PARSER_STATE_OFFSET;
3168                         } else if ((c != '-') && ! isalpha (c)) {
3169                                 state = MAP_LINE_PARSER_STATE_INVALID;
3170                         }
3171                         break;
3172                 case MAP_LINE_PARSER_STATE_OFFSET:
3173                         if (isxdigit (c)) {
3174                                 offset <<= 4;
3175                                 offset |= hex_digit_value (c);
3176                         } else if (isblank (c)) {
3177                                 state = MAP_LINE_PARSER_STATE_DEVICE;
3178                         } else {
3179                                 state = MAP_LINE_PARSER_STATE_INVALID;
3180                         }
3181                         break;
3182                 case MAP_LINE_PARSER_STATE_DEVICE:
3183                         if (isblank (c)) {
3184                                 state = MAP_LINE_PARSER_STATE_INODE;
3185                         } else if ((c != ':') && ! isxdigit (c)) {
3186                                 state = MAP_LINE_PARSER_STATE_INVALID;
3187                         }
3188                         break;
3189                 case MAP_LINE_PARSER_STATE_INODE:
3190                         if (isblank (c)) {
3191                                 state = MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME;
3192                         } else if (! isdigit (c)) {
3193                                 state = MAP_LINE_PARSER_STATE_INVALID;
3194                         }
3195                         break;
3196                 case MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME:
3197                         if ((c == '/') || (c == '[')) {
3198                                 state = MAP_LINE_PARSER_STATE_FILENAME;
3199                                 filename [filename_index] = *current;
3200                                 filename_index ++;
3201                         } else if (! isblank (c)) {
3202                                 state = MAP_LINE_PARSER_STATE_INVALID;
3203                         }
3204                         break;
3205                 case MAP_LINE_PARSER_STATE_FILENAME:
3206                         if (filename_index < MAPS_FILENAME_SIZE) {
3207                                 if (c == '\n') {
3208                                         state = MAP_LINE_PARSER_STATE_DONE;
3209                                         done = TRUE;
3210                                         filename [filename_index] = 0;
3211                                 } else {
3212                                         filename [filename_index] = *current;
3213                                         filename_index ++;
3214                                 }
3215                         } else {
3216                                 filename [filename_index] = 0;
3217                                 g_warning ("ELF filename too long: \"%s\"...\n", filename);
3218                         }
3219                         break;
3220                 case MAP_LINE_PARSER_STATE_DONE:
3221                         if (done && is_executable) {
3222                                 filename [filename_index] = 0;
3223                                 append_region (regions, (gpointer) start_address, (gpointer) end_address, offset, filename);
3224                         }
3225                         return current;
3226                 case MAP_LINE_PARSER_STATE_INVALID:
3227                         if (c == '\n') {
3228                                 state = MAP_LINE_PARSER_STATE_DONE;
3229                         }
3230                         break;
3231                 }
3232                 
3233                 if (c == 0) {
3234                         return NULL;
3235                 } else if (c == '\n') {
3236                         state = MAP_LINE_PARSER_STATE_DONE;
3237                 }
3238                 
3239                 GOTO_NEXT_CHAR(current, buffer, fd);
3240                 c = *current;
3241         }
3242 }
3243
3244 static gboolean
3245 scan_process_regions (ProfilerExecutableMemoryRegions *regions) {
3246         char *buffer;
3247         char *filename;
3248         char *current;
3249         int fd;
3250         
3251         fd = open ("/proc/self/maps", O_RDONLY);
3252         if (fd == -1) {
3253                 return FALSE;
3254         }
3255         
3256         buffer = malloc (MAPS_BUFFER_SIZE);
3257         filename = malloc (MAPS_FILENAME_SIZE);
3258         update_regions_buffer (fd, buffer);
3259         current = buffer;
3260         while (current != NULL) {
3261                 current = parse_map_line (regions, fd, buffer, filename, current);
3262         }
3263         
3264         free (buffer);
3265         free (filename);
3266         
3267         close (fd);
3268         return TRUE;
3269 }
3270 //End of Linux code
3271
3272 typedef enum {
3273         MONO_PROFILER_STATISTICAL_CODE_END = 0,
3274         MONO_PROFILER_STATISTICAL_CODE_METHOD = 1,
3275         MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID = 2,
3276         MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_NEW_ID = 3,
3277         MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION = 4,
3278         MONO_PROFILER_STATISTICAL_CODE_CALL_CHAIN = 5,
3279         MONO_PROFILER_STATISTICAL_CODE_REGIONS = 7
3280 } MonoProfilerStatisticalCode;
3281
3282 static void
3283 refresh_memory_regions (void) {
3284         ProfilerExecutableMemoryRegions *old_regions = profiler->executable_regions;
3285         ProfilerExecutableMemoryRegions *new_regions = profiler_executable_memory_regions_new (old_regions->next_id, old_regions->next_unmanaged_function_id);
3286         int i;
3287         
3288         LOG_WRITER_THREAD ("Refreshing memory regions...");
3289         scan_process_regions (new_regions);
3290         restore_old_regions (old_regions, new_regions);
3291         sort_regions (new_regions);
3292         LOG_WRITER_THREAD ("Refreshed memory regions.");
3293         
3294         LOG_WRITER_THREAD ("Building symbol tables...");
3295         build_symbol_tables (new_regions, & (profiler->executable_files));
3296 #if 0
3297         printf ("Symbol tables done!\n");
3298         printf ("Region summary...\n");
3299         for (i = 0; i < new_regions->regions_count; i++) {
3300                 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
3301                 printf ("Region %d[%d][NEW:%d] (%p-%p) at %d in file %s\n", i, region->id, region->is_new,
3302                                 region->start, region->end, region->file_offset, region->file_name);
3303         }
3304         printf ("New symbol tables dump...\n");
3305         for (i = 0; i < new_regions->regions_count; i++) {
3306                 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
3307                 
3308                 if (region->is_new) {
3309                         int symbol_index;
3310                         
3311                         printf ("Region %d[%d][NEW:%d] (%p-%p) at %d in file %s\n", i, region->id, region->is_new,
3312                                         region->start, region->end, region->file_offset, region->file_name);
3313                         for (symbol_index = 0; symbol_index < region->symbols_count; symbol_index ++) {
3314                                 ProfilerUnmanagedSymbol *symbol = & (region->symbols [symbol_index]);
3315                                 printf ("  [%d] Symbol %s (offset %d, size %d)\n", symbol_index,
3316                                                 executable_region_symbol_get_name (region, symbol),
3317                                                 symbol->offset, symbol->size);
3318                         }
3319                 }
3320         }
3321 #endif
3322         LOG_WRITER_THREAD ("Built symbol tables.");
3323         
3324         // This marks the region "sub-block"
3325         write_uint32 (MONO_PROFILER_STATISTICAL_CODE_REGIONS);
3326         
3327         // First write the "removed" regions 
3328         for (i = 0; i < old_regions->regions_count; i++) {
3329                 ProfilerExecutableMemoryRegionData *region = old_regions->regions [i];
3330                 if (! region->is_new) {
3331 #if DEBUG_STATISTICAL_PROFILER
3332                         printf ("[refresh_memory_regions] Invalidated region %d\n", region->id);
3333 #endif
3334                         write_uint32 (region->id);
3335                 }
3336         }
3337         write_uint32 (0);
3338         
3339         // Then write the new ones
3340         for (i = 0; i < new_regions->regions_count; i++) {
3341                 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
3342                 if (region->is_new) {
3343                         region->is_new = FALSE;
3344                         
3345 #if DEBUG_STATISTICAL_PROFILER
3346                         printf ("[refresh_memory_regions] Wrote region %d (%p-%p[%d] '%s')\n", region->id, region->start, region->end, region->file_offset, region->file_name);
3347 #endif
3348                         write_uint32 (region->id);
3349                         write_uint64 (GPOINTER_TO_UINT (region->start));
3350                         write_uint32 (GPOINTER_TO_UINT (region->end) - GPOINTER_TO_UINT (region->start));
3351                         write_uint32 (region->file_offset);
3352                         write_string (region->file_name);
3353                 }
3354         }
3355         write_uint32 (0);
3356         
3357         // Finally, free the old ones, and replace them
3358         profiler_executable_memory_regions_destroy (old_regions);
3359         profiler->executable_regions = new_regions;
3360 }
3361
3362 static gboolean
3363 write_statistical_hit (MonoDomain *domain, gpointer address, gboolean regions_refreshed) {
3364         MonoJitInfo *ji = (domain != NULL) ? mono_jit_info_table_find (domain, (char*) address) : NULL;
3365         
3366         if (ji != NULL) {
3367                 MonoMethod *method = mono_jit_info_get_method (ji);
3368                 MethodIdMappingElement *element = method_id_mapping_element_get (method);
3369                 
3370                 if (element != NULL) {
3371 #if DEBUG_STATISTICAL_PROFILER
3372                         printf ("[write_statistical_hit] Wrote method %d\n", element->id);
3373 #endif
3374                         write_uint32 ((element->id << 3) | MONO_PROFILER_STATISTICAL_CODE_METHOD);
3375                 } else {
3376 #if DEBUG_STATISTICAL_PROFILER
3377                         printf ("[write_statistical_hit] Wrote unknown method %p\n", method);
3378 #endif
3379                         write_uint32 (MONO_PROFILER_STATISTICAL_CODE_METHOD);
3380                 }
3381         } else {
3382                 ProfilerExecutableMemoryRegionData *region = find_address_region (profiler->executable_regions, address);
3383                 
3384                 if (region == NULL && ! regions_refreshed) {
3385 #if DEBUG_STATISTICAL_PROFILER
3386                         printf ("[write_statistical_hit] Cannot find region for address %p, refreshing...\n", address);
3387 #endif
3388                         refresh_memory_regions ();
3389                         regions_refreshed = TRUE;
3390                         region = find_address_region (profiler->executable_regions, address);
3391                 }
3392                 
3393                 if (region != NULL) {
3394                         guint32 offset = ((guint8*)address) - ((guint8*)region->start);
3395                         ProfilerUnmanagedSymbol *symbol = executable_memory_region_find_symbol (region, offset);
3396                         
3397                         if (symbol != NULL) {
3398                                 if (symbol->id > 0) {
3399 #if DEBUG_STATISTICAL_PROFILER
3400                                         printf ("[write_statistical_hit] Wrote unmanaged symbol %d\n", symbol->id);
3401 #endif
3402                                         write_uint32 ((symbol->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID);
3403                                 } else {
3404                                         ProfilerExecutableMemoryRegions *regions = profiler->executable_regions;
3405                                         const char *symbol_name = executable_region_symbol_get_name (region, symbol);
3406                                         symbol->id = regions->next_unmanaged_function_id;
3407                                         regions->next_unmanaged_function_id ++;
3408 #if DEBUG_STATISTICAL_PROFILER
3409                                         printf ("[write_statistical_hit] Wrote new unmanaged symbol in region %d[%d]\n", region->id, offset);
3410 #endif
3411                                         write_uint32 ((region->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_NEW_ID);
3412                                         write_uint32 (symbol->id);
3413                                         write_string (symbol_name);
3414                                 }
3415                         } else {
3416 #if DEBUG_STATISTICAL_PROFILER
3417                                 printf ("[write_statistical_hit] Wrote unknown unmanaged hit in region %d[%d] (address %p)\n", region->id, offset, address);
3418 #endif
3419                                 write_uint32 ((region->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION);
3420                                 write_uint32 (offset);
3421                         }
3422                 } else {
3423 #if DEBUG_STATISTICAL_PROFILER
3424                         printf ("[write_statistical_hit] Wrote unknown unmanaged hit %p\n", address);
3425 #endif
3426                         write_uint32 (MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION);
3427                         write_uint64 (GPOINTER_TO_UINT (address));
3428                 }
3429         }
3430         
3431         return regions_refreshed;
3432 }
3433
3434 static void
3435 flush_all_mappings (void);
3436
3437 static void
3438 write_statistical_data_block (ProfilerStatisticalData *data) {
3439         MonoThread *current_thread = mono_thread_current ();
3440         int start_index = data->first_unwritten_index;
3441         int end_index = data->next_free_index;
3442         gboolean regions_refreshed = FALSE;
3443         int call_chain_depth = profiler->statistical_call_chain_depth;
3444         int index;
3445         
3446         if (end_index > data->end_index)
3447                 end_index = data->end_index;
3448         
3449         if (start_index == end_index)
3450                 return;
3451         
3452         data->first_unwritten_index = end_index;
3453         
3454         write_clock_data ();
3455         
3456 #if DEBUG_STATISTICAL_PROFILER
3457         printf ("[write_statistical_data_block] Starting loop at index %d\n", start_index);
3458 #endif
3459         
3460         for (index = start_index; index < end_index; index ++) {
3461                 int base_index = index * (call_chain_depth + 1);
3462                 ProfilerStatisticalHit hit = data->hits [base_index];
3463                 int callers_count;
3464                 
3465                 regions_refreshed = write_statistical_hit ((current_thread != NULL) ? hit.domain : NULL, hit.address, regions_refreshed);
3466                 base_index ++;
3467                 
3468                 for (callers_count = 0; callers_count < call_chain_depth; callers_count ++) {
3469                         hit = data->hits [base_index + callers_count];
3470                         if (hit.address == NULL) {
3471                                 break;
3472                         }
3473                 }
3474                 
3475                 if (callers_count > 0) {
3476                         write_uint32 ((callers_count << 3) | MONO_PROFILER_STATISTICAL_CODE_CALL_CHAIN);
3477                         
3478                         for (callers_count = 0; callers_count < call_chain_depth; callers_count ++) {
3479                                 hit = data->hits [base_index + callers_count];
3480                                 if (hit.address != NULL) {
3481                                         regions_refreshed = write_statistical_hit ((current_thread != NULL) ? hit.domain : NULL, hit.address, regions_refreshed);
3482                                 } else {
3483                                         break;
3484                                 }
3485                         }
3486                 }
3487         }
3488         write_uint32 (MONO_PROFILER_STATISTICAL_CODE_END);
3489         
3490 #if DEBUG_STATISTICAL_PROFILER
3491         printf ("[write_statistical_data_block] Ending loop at index %d\n", end_index);
3492 #endif
3493         write_clock_data ();
3494         
3495         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL);
3496 }
3497
3498 static void
3499 write_intro_block (void) {
3500         write_uint32 (1);
3501         write_string ("mono");
3502         write_uint32 (profiler->flags);
3503         write_uint64 (profiler->start_counter);
3504         write_uint64 (profiler->start_time);
3505         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_INTRO);
3506 }
3507
3508 static void
3509 write_end_block (void) {
3510         write_uint32 (1);
3511         write_uint64 (profiler->end_counter);
3512         write_uint64 (profiler->end_time);
3513         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_END);
3514 }
3515
3516 static void
3517 update_mapping (ProfilerPerThreadData *data) {
3518         ProfilerEventData *start = data->first_unmapped_event;
3519         ProfilerEventData *end = data->next_free_event;
3520         data->first_unmapped_event = end;
3521         
3522 #if (DEBUG_LOGGING_PROFILER)
3523         printf ("[update_mapping][TID %ld] START\n", data->thread_id);
3524 #endif
3525         while (start < end) {
3526 #if DEBUG_LOGGING_PROFILER
3527                 printf ("Examining event %p[TID %ld] looking for a new mapping...\n", start, data->thread_id);
3528 #endif
3529                 if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
3530                         ClassIdMappingElement *element = class_id_mapping_element_get (start->data.address);
3531                         if (element == NULL) {
3532                                 MonoClass *klass = start->data.address;
3533                                 class_id_mapping_element_new (klass);
3534                         }
3535                 } else if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
3536                         MethodIdMappingElement *element = method_id_mapping_element_get (start->data.address);
3537                         if (element == NULL) {
3538                                 MonoMethod *method = start->data.address;
3539                                 if (method != NULL) {
3540                                         method_id_mapping_element_new (method);
3541                                 }
3542                         }
3543                 }
3544                 
3545                 if (start->value == MAX_EVENT_VALUE) {
3546                         start ++;
3547                 }
3548                 start ++;
3549         }
3550 #if (DEBUG_LOGGING_PROFILER)
3551         printf ("[update_mapping][TID %ld] END\n", data->thread_id);
3552 #endif
3553 }
3554
3555 static void
3556 flush_all_mappings (void) {
3557         ProfilerPerThreadData *data;
3558         
3559         for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3560                 update_mapping (data);
3561         }
3562         for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3563                 write_mapping_block (data->thread_id);
3564         }
3565 }
3566
3567 static void
3568 flush_full_event_data_buffer (ProfilerPerThreadData *data) {
3569         LOCK_PROFILER ();
3570         
3571         // We flush all mappings because some id definitions could come
3572         // from other threads
3573         flush_all_mappings ();
3574         g_assert (data->first_unmapped_event >= data->next_free_event);
3575         
3576         write_thread_data_block (data);
3577         
3578         data->next_free_event = data->events;
3579         data->first_unwritten_event = data->events;
3580         data->first_unmapped_event = data->events;
3581         MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
3582         data->last_event_counter = data->start_event_counter;
3583         
3584         UNLOCK_PROFILER ();
3585 }
3586
3587 /* The ">=" operator is intentional, to leave one spare slot for "extended values" */
3588 #define RESERVE_EVENTS(d,e,count) {\
3589         if ((d)->next_free_event >= ((d)->end_event - (count))) {\
3590                 flush_full_event_data_buffer (d);\
3591         }\
3592         (e) = (d)->next_free_event;\
3593         (d)->next_free_event += (count);\
3594 } while (0)
3595 #define GET_NEXT_FREE_EVENT(d,e) RESERVE_EVENTS ((d),(e),1)
3596
3597 static void
3598 flush_everything (void) {
3599         ProfilerPerThreadData *data;
3600         
3601         flush_all_mappings ();
3602         for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3603                 write_thread_data_block (data);
3604         }
3605         write_statistical_data_block (profiler->statistical_data);
3606 }
3607
3608 /* This assumes the lock is held: it just offloads the work to the writer thread. */
3609 static void
3610 writer_thread_flush_everything (void) {
3611         if (CHECK_WRITER_THREAD ()) {
3612                 profiler->writer_thread_flush_everything = TRUE;
3613                 LOG_WRITER_THREAD ("writer_thread_flush_everything: raising event...");
3614                 WRITER_EVENT_RAISE ();
3615                 LOG_WRITER_THREAD ("writer_thread_flush_everything: waiting event...");
3616                 WRITER_EVENT_DONE_WAIT ();
3617                 LOG_WRITER_THREAD ("writer_thread_flush_everything: got event.");
3618         } else {
3619                 LOG_WRITER_THREAD ("writer_thread_flush_everything: no thread.");
3620         }
3621 }
3622
3623 #define RESULT_TO_LOAD_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_LOADED_EVENT_SUCCESS:MONO_PROFILER_LOADED_EVENT_FAILURE)
3624 static void
3625 appdomain_start_load (MonoProfiler *profiler, MonoDomain *domain) {
3626         LOCK_PROFILER ();
3627         loaded_element_load_start (profiler->loaded_appdomains, domain);
3628         UNLOCK_PROFILER ();
3629 }
3630
3631 static void
3632 appdomain_end_load (MonoProfiler *profiler, MonoDomain *domain, int result) {
3633         char *name;
3634         LoadedElement *element;
3635         
3636         name = g_strdup_printf ("%d", mono_domain_get_id (domain));
3637         LOCK_PROFILER ();
3638         element = loaded_element_load_end (profiler->loaded_appdomains, domain, name);
3639         write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
3640         UNLOCK_PROFILER ();
3641 }
3642
3643 static void
3644 appdomain_start_unload (MonoProfiler *profiler, MonoDomain *domain) {
3645         LOCK_PROFILER ();
3646         loaded_element_unload_start (profiler->loaded_appdomains, domain);
3647         writer_thread_flush_everything ();
3648         UNLOCK_PROFILER ();
3649 }
3650
3651 static void
3652 appdomain_end_unload (MonoProfiler *profiler, MonoDomain *domain) {
3653         LoadedElement *element;
3654         
3655         LOCK_PROFILER ();
3656         element = loaded_element_unload_end (profiler->loaded_appdomains, domain);
3657         write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN, CURRENT_THREAD_ID ());
3658         UNLOCK_PROFILER ();
3659 }
3660
3661 static void
3662 module_start_load (MonoProfiler *profiler, MonoImage *module) {
3663         LOCK_PROFILER ();
3664         loaded_element_load_start (profiler->loaded_modules, module);
3665         UNLOCK_PROFILER ();
3666 }
3667
3668 static void
3669 module_end_load (MonoProfiler *profiler, MonoImage *module, int result) {
3670         char *name;
3671         MonoAssemblyName aname;
3672         LoadedElement *element;
3673         
3674         if (mono_assembly_fill_assembly_name (module, &aname)) {
3675                 name = mono_stringify_assembly_name (&aname);
3676         } else {
3677                 name = g_strdup_printf ("Dynamic module \"%p\"", module);
3678         }
3679         LOCK_PROFILER ();
3680         element = loaded_element_load_end (profiler->loaded_modules, module, name);
3681         write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_MODULE | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
3682         UNLOCK_PROFILER ();
3683 }
3684
3685 static void
3686 module_start_unload (MonoProfiler *profiler, MonoImage *module) {
3687         LOCK_PROFILER ();
3688         loaded_element_unload_start (profiler->loaded_modules, module);
3689         writer_thread_flush_everything ();
3690         UNLOCK_PROFILER ();
3691 }
3692
3693 static void
3694 module_end_unload (MonoProfiler *profiler, MonoImage *module) {
3695         LoadedElement *element;
3696         
3697         LOCK_PROFILER ();
3698         element = loaded_element_unload_end (profiler->loaded_modules, module);
3699         write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_MODULE, CURRENT_THREAD_ID ());
3700         UNLOCK_PROFILER ();
3701 }
3702
3703 static void
3704 assembly_start_load (MonoProfiler *profiler, MonoAssembly *assembly) {
3705         LOCK_PROFILER ();
3706         loaded_element_load_start (profiler->loaded_assemblies, assembly);
3707         UNLOCK_PROFILER ();
3708 }
3709
3710 static void
3711 assembly_end_load (MonoProfiler *profiler, MonoAssembly *assembly, int result) {
3712         char *name;
3713         MonoAssemblyName aname;
3714         LoadedElement *element;
3715         
3716         if (mono_assembly_fill_assembly_name (mono_assembly_get_image (assembly), &aname)) {
3717                 name = mono_stringify_assembly_name (&aname);
3718         } else {
3719                 name = g_strdup_printf ("Dynamic assembly \"%p\"", assembly);
3720         }
3721         LOCK_PROFILER ();
3722         element = loaded_element_load_end (profiler->loaded_assemblies, assembly, name);
3723         write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
3724         UNLOCK_PROFILER ();
3725 }
3726
3727 static void
3728 assembly_start_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
3729         LOCK_PROFILER ();
3730         loaded_element_unload_start (profiler->loaded_assemblies, assembly);
3731         writer_thread_flush_everything ();
3732         UNLOCK_PROFILER ();
3733 }
3734 static void
3735 assembly_end_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
3736         LoadedElement *element;
3737         
3738         LOCK_PROFILER ();
3739         element = loaded_element_unload_end (profiler->loaded_assemblies, assembly);
3740         write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY, CURRENT_THREAD_ID ());
3741         UNLOCK_PROFILER ();
3742 }
3743
3744 #if (DEBUG_LOGGING_PROFILER)            
3745 static const char*
3746 class_event_code_to_string (MonoProfilerClassEvents code) {
3747         switch (code) {
3748         case MONO_PROFILER_EVENT_CLASS_LOAD: return "LOAD";
3749         case MONO_PROFILER_EVENT_CLASS_UNLOAD: return "UNLOAD";
3750         case MONO_PROFILER_EVENT_CLASS_ALLOCATION: return "ALLOCATION";
3751         case MONO_PROFILER_EVENT_CLASS_EXCEPTION: return "EXCEPTION";
3752         default: g_assert_not_reached (); return "";
3753         }
3754 }
3755 static const char*
3756 method_event_code_to_string (MonoProfilerMethodEvents code) {
3757         switch (code) {
3758         case MONO_PROFILER_EVENT_METHOD_CALL: return "CALL";
3759         case MONO_PROFILER_EVENT_METHOD_JIT: return "JIT";
3760         case MONO_PROFILER_EVENT_METHOD_FREED: return "FREED";
3761         case MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER: return "ALLOCATION_CALLER";
3762         case MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER: return "ALLOCATION_JIT_TIME_CALLER";
3763         case MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID: return "ALLOCATION_OBJECT_ID";
3764         default: g_assert_not_reached (); return "";
3765         }
3766 }
3767 static const char*
3768 number_event_code_to_string (MonoProfilerEvents code) {
3769         switch (code) {
3770         case MONO_PROFILER_EVENT_THREAD: return "THREAD";
3771         case MONO_PROFILER_EVENT_GC_COLLECTION: return "GC_COLLECTION";
3772         case MONO_PROFILER_EVENT_GC_MARK: return "GC_MARK";
3773         case MONO_PROFILER_EVENT_GC_SWEEP: return "GC_SWEEP";
3774         case MONO_PROFILER_EVENT_GC_RESIZE: return "GC_RESIZE";
3775         case MONO_PROFILER_EVENT_GC_STOP_WORLD: return "GC_STOP_WORLD";
3776         case MONO_PROFILER_EVENT_GC_START_WORLD: return "GC_START_WORLD";
3777         case MONO_PROFILER_EVENT_JIT_TIME_ALLOCATION: return "JIT_TIME_ALLOCATION";
3778         case MONO_PROFILER_EVENT_STACK_SECTION: return "STACK_SECTION";
3779         case MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID: return "ALLOCATION_OBJECT_ID";
3780         default: g_assert_not_reached (); return "";
3781         }
3782 }
3783 static const char*
3784 event_result_to_string (MonoProfilerEventResult code) {
3785         switch (code) {
3786         case MONO_PROFILER_EVENT_RESULT_SUCCESS: return "SUCCESS";
3787         case MONO_PROFILER_EVENT_RESULT_FAILURE: return "FAILURE";
3788         default: g_assert_not_reached (); return "";
3789         }
3790 }
3791 static const char*
3792 event_kind_to_string (MonoProfilerEventKind code) {
3793         switch (code) {
3794         case MONO_PROFILER_EVENT_KIND_START: return "START";
3795         case MONO_PROFILER_EVENT_KIND_END: return "END";
3796         default: g_assert_not_reached (); return "";
3797         }
3798 }
3799 static void
3800 print_event_data (ProfilerPerThreadData *data, ProfilerEventData *event, guint64 value) {
3801         if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
3802                 printf ("STORE EVENT [TID %ld][EVENT %ld] CLASS[%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s)\n",
3803                                 data->thread_id,
3804                                 event - data->events,
3805                                 event->data.address,
3806                                 class_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
3807                                 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
3808                                 event_kind_to_string (event->kind),
3809                                 event->data_type,
3810                                 event->kind,
3811                                 event->code,
3812                                 value,
3813                                 mono_class_get_namespace ((MonoClass*) event->data.address),
3814                                 mono_class_get_name ((MonoClass*) event->data.address));
3815         } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
3816                 printf ("STORE EVENT [TID %ld][EVENT %ld]  METHOD[%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s:%s (?))\n",
3817                                 data->thread_id,
3818                                 event - data->events,
3819                                 event->data.address,
3820                                 method_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
3821                                 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
3822                                 event_kind_to_string (event->kind),
3823                                 event->data_type,
3824                                 event->kind,
3825                                 event->code,
3826                                 value,
3827                                 (event->data.address != NULL) ? mono_class_get_namespace (mono_method_get_class ((MonoMethod*) event->data.address)) : "<NULL>",
3828                                 (event->data.address != NULL) ? mono_class_get_name (mono_method_get_class ((MonoMethod*) event->data.address)) : "<NULL>",
3829                                 (event->data.address != NULL) ? mono_method_get_name ((MonoMethod*) event->data.address) : "<NULL>");
3830         } else {
3831                 printf ("STORE EVENT [TID %ld][EVENT %ld]  NUMBER[%ld] %s:%s[%d-%d-%d] %ld\n",
3832                                 data->thread_id,
3833                                 event - data->events,
3834                                 (guint64) event->data.number,
3835                                 number_event_code_to_string (event->code),
3836                                 event_kind_to_string (event->kind),
3837                                 event->data_type,
3838                                 event->kind,
3839                                 event->code,
3840                                 value);
3841         }
3842 }
3843 #define LOG_EVENT(data,ev,val) print_event_data ((data),(ev),(val))
3844 #else
3845 #define LOG_EVENT(data,ev,val)
3846 #endif
3847
3848 #define RESULT_TO_EVENT_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_EVENT_RESULT_SUCCESS:MONO_PROFILER_EVENT_RESULT_FAILURE)
3849
3850 #define STORE_EVENT_ITEM_COUNTER(event,p,i,dt,c,k) do {\
3851         guint64 counter;\
3852         guint64 delta;\
3853         MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
3854         (event)->data.address = (i);\
3855         (event)->data_type = (dt);\
3856         (event)->code = (c);\
3857         (event)->kind = (k);\
3858         delta = counter - data->last_event_counter;\
3859         if (delta < MAX_EVENT_VALUE) {\
3860                 (event)->value = delta;\
3861         } else {\
3862                 ProfilerEventData *extension = data->next_free_event;\
3863                 data->next_free_event ++;\
3864                 (event)->value = MAX_EVENT_VALUE;\
3865                 *(guint64*)extension = delta;\
3866         }\
3867         data->last_event_counter = counter;\
3868         LOG_EVENT (data, (event), delta);\
3869 } while (0);
3870 #define STORE_EVENT_ITEM_VALUE(event,p,i,dt,c,k,v) do {\
3871         (event)->data.address = (i);\
3872         (event)->data_type = (dt);\
3873         (event)->code = (c);\
3874         (event)->kind = (k);\
3875         if ((v) < MAX_EVENT_VALUE) {\
3876                 (event)->value = (v);\
3877         } else {\
3878                 ProfilerEventData *extension = data->next_free_event;\
3879                 data->next_free_event ++;\
3880                 (event)->value = MAX_EVENT_VALUE;\
3881                 *(guint64*)extension = (v);\
3882         }\
3883         LOG_EVENT (data, (event), (v));\
3884 }while (0);
3885 #define STORE_EVENT_NUMBER_COUNTER(event,p,n,dt,c,k) do {\
3886         guint64 counter;\
3887         guint64 delta;\
3888         MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
3889         (event)->data.number = (n);\
3890         (event)->data_type = (dt);\
3891         (event)->code = (c);\
3892         (event)->kind = (k);\
3893         delta = counter - data->last_event_counter;\
3894         if (delta < MAX_EVENT_VALUE) {\
3895                 (event)->value = delta;\
3896         } else {\
3897                 ProfilerEventData *extension = data->next_free_event;\
3898                 data->next_free_event ++;\
3899                 (event)->value = MAX_EVENT_VALUE;\
3900                 *(guint64*)extension = delta;\
3901         }\
3902         data->last_event_counter = counter;\
3903         LOG_EVENT (data, (event), delta);\
3904 }while (0);
3905 #define STORE_EVENT_NUMBER_VALUE(event,p,n,dt,c,k,v) do {\
3906         (event)->data.number = (n);\
3907         (event)->data_type = (dt);\
3908         (event)->code = (c);\
3909         (event)->kind = (k);\
3910         if ((v) < MAX_EVENT_VALUE) {\
3911                 (event)->value = (v);\
3912         } else {\
3913                 ProfilerEventData *extension = data->next_free_event;\
3914                 data->next_free_event ++;\
3915                 (event)->value = MAX_EVENT_VALUE;\
3916                 *(guint64*)extension = (v);\
3917         }\
3918         LOG_EVENT (data, (event), (v));\
3919 }while (0);
3920
3921 static void
3922 class_start_load (MonoProfiler *profiler, MonoClass *klass) {
3923         ProfilerPerThreadData *data;
3924         ProfilerEventData *event;
3925         GET_PROFILER_THREAD_DATA (data);
3926         GET_NEXT_FREE_EVENT (data, event);
3927         STORE_EVENT_ITEM_COUNTER (event, profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD, MONO_PROFILER_EVENT_KIND_START);
3928 }
3929 static void
3930 class_end_load (MonoProfiler *profiler, MonoClass *klass, int result) {
3931         ProfilerPerThreadData *data;
3932         ProfilerEventData *event;
3933         GET_PROFILER_THREAD_DATA (data);
3934         GET_NEXT_FREE_EVENT (data, event);
3935         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);
3936 }
3937 static void
3938 class_start_unload (MonoProfiler *profiler, MonoClass *klass) {
3939         ProfilerPerThreadData *data;
3940         ProfilerEventData *event;
3941         GET_PROFILER_THREAD_DATA (data);
3942         GET_NEXT_FREE_EVENT (data, event);
3943         STORE_EVENT_ITEM_COUNTER (event, profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_START);
3944 }
3945 static void
3946 class_end_unload (MonoProfiler *profiler, MonoClass *klass) {
3947         ProfilerPerThreadData *data;
3948         ProfilerEventData *event;
3949         GET_PROFILER_THREAD_DATA (data);
3950         GET_NEXT_FREE_EVENT (data, event);
3951         STORE_EVENT_ITEM_COUNTER (event, profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_END);
3952 }
3953
3954 static void
3955 method_start_jit (MonoProfiler *profiler, MonoMethod *method) {
3956         ProfilerPerThreadData *data;
3957         ProfilerEventData *event;
3958         GET_PROFILER_THREAD_DATA (data);
3959         GET_NEXT_FREE_EVENT (data, event);
3960         thread_stack_push_jitted_safely (&(data->stack), method, TRUE);
3961         STORE_EVENT_ITEM_COUNTER (event, profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT, MONO_PROFILER_EVENT_KIND_START);
3962 }
3963 static void
3964 method_end_jit (MonoProfiler *profiler, MonoMethod *method, int result) {
3965         ProfilerPerThreadData *data;
3966         ProfilerEventData *event;
3967         GET_PROFILER_THREAD_DATA (data);
3968         GET_NEXT_FREE_EVENT (data, event);
3969         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);
3970         thread_stack_pop (&(data->stack));
3971 }
3972
3973 #if (HAS_OPROFILE)
3974 static void
3975 method_jit_result (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result) {
3976         if (profiler->action_flags.oprofile && (result == MONO_PROFILE_OK)) {
3977                 MonoClass *klass = mono_method_get_class (method);
3978                 char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
3979                 char *name = g_strdup_printf ("%s.%s:%s (%s)", mono_class_get_namespace (klass), mono_class_get_name (klass), mono_method_get_name (method), signature);
3980                 gpointer code_start = mono_jit_info_get_code_start (jinfo);
3981                 int code_size = mono_jit_info_get_code_size (jinfo);
3982                 
3983                 if (op_write_native_code (name, code_start, code_size)) {
3984                         g_warning ("Problem calling op_write_native_code\n");
3985                 }
3986                 
3987                 g_free (signature);
3988                 g_free (name);
3989         }
3990 }
3991 #endif
3992
3993
3994 static void
3995 method_enter (MonoProfiler *profiler, MonoMethod *method) {
3996         ProfilerPerThreadData *data;
3997         
3998         CHECK_PROFILER_ENABLED ();
3999         GET_PROFILER_THREAD_DATA (data);
4000         if (profiler->action_flags.track_calls) {
4001                 ProfilerEventData *event;
4002                 GET_NEXT_FREE_EVENT (data, event);
4003                 STORE_EVENT_ITEM_COUNTER (event, profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_START);
4004         }
4005         if (profiler->action_flags.track_stack) {
4006                 thread_stack_push_safely (&(data->stack), method);
4007         }
4008 }
4009 static void
4010 method_leave (MonoProfiler *profiler, MonoMethod *method) {
4011         ProfilerPerThreadData *data;
4012         
4013         CHECK_PROFILER_ENABLED ();
4014         GET_PROFILER_THREAD_DATA (data);
4015         if (profiler->action_flags.track_calls) {
4016                 ProfilerEventData *event;
4017                 GET_NEXT_FREE_EVENT (data, event);
4018                 STORE_EVENT_ITEM_COUNTER (event, profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_END);
4019         }
4020         if (profiler->action_flags.track_stack) {
4021                 thread_stack_pop (&(data->stack));
4022         }
4023 }
4024
4025 static void
4026 method_free (MonoProfiler *profiler, MonoMethod *method) {
4027         ProfilerPerThreadData *data;
4028         ProfilerEventData *event;
4029         GET_PROFILER_THREAD_DATA (data);
4030         GET_NEXT_FREE_EVENT (data, event);
4031         STORE_EVENT_ITEM_COUNTER (event, profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_FREED, 0);
4032 }
4033
4034 static void
4035 thread_start (MonoProfiler *profiler, gsize tid) {
4036         ProfilerPerThreadData *data;
4037         ProfilerEventData *event;
4038         GET_PROFILER_THREAD_DATA (data);
4039         GET_NEXT_FREE_EVENT (data, event);
4040         STORE_EVENT_NUMBER_COUNTER (event, profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_START);
4041 }
4042 static void
4043 thread_end (MonoProfiler *profiler, gsize tid) {
4044         ProfilerPerThreadData *data;
4045         ProfilerEventData *event;
4046         GET_PROFILER_THREAD_DATA (data);
4047         GET_NEXT_FREE_EVENT (data, event);
4048         STORE_EVENT_NUMBER_COUNTER (event, profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_END);
4049 }
4050
4051 static void
4052 object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) {
4053         ProfilerPerThreadData *data;
4054         ProfilerEventData *events;
4055         int unsaved_frames;
4056         int event_slot_count;
4057         
4058         GET_PROFILER_THREAD_DATA (data);
4059         event_slot_count = 1;
4060         if (profiler->action_flags.save_allocation_caller) {
4061                 event_slot_count ++;
4062         }
4063         if (profiler->action_flags.allocations_carry_id) {
4064                 event_slot_count ++;
4065         }
4066         if (profiler->action_flags.save_allocation_stack) {
4067                 unsaved_frames = thread_stack_count_unsaved_frames (&(data->stack));
4068                 event_slot_count += (unsaved_frames + 1);
4069         } else {
4070                 unsaved_frames = 0;
4071         }
4072         RESERVE_EVENTS (data, events, event_slot_count);
4073         
4074         if (profiler->action_flags.save_allocation_stack) {
4075                 int i;
4076                 
4077                 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);
4078                 events++;
4079                 for (i = 0; i < unsaved_frames; i++) {
4080                         if (! thread_stack_index_from_top_is_jitted (&(data->stack), i)) {
4081                                 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);
4082                         } else {
4083                                 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);
4084                         }
4085                         events ++;
4086                 }
4087                 
4088                 data->stack.last_saved_top = data->stack.top;
4089         }
4090         
4091         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));
4092         if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot || profiler->action_flags.collection_summary) {
4093                 STORE_ALLOCATED_OBJECT (data, obj);
4094         }
4095         
4096         if (profiler->action_flags.save_allocation_caller) {
4097                 MonoMethod *caller = thread_stack_top (&(data->stack));
4098                 gboolean caller_is_jitted = thread_stack_top_is_jitted (&(data->stack));
4099                 int index = 1;
4100                 events ++;
4101                 
4102                 while ((caller != NULL) && (caller->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)) {
4103                         caller = thread_stack_index_from_top (&(data->stack), index);
4104                         caller_is_jitted = thread_stack_index_from_top_is_jitted (&(data->stack), index);
4105                         index ++;
4106                 }
4107                 if (! caller_is_jitted) {
4108                         STORE_EVENT_ITEM_VALUE (events, profiler, caller, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_ALLOCATION_CALLER, 0, 0);
4109                 } else {
4110                         STORE_EVENT_ITEM_VALUE (events, profiler, caller, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_ALLOCATION_JIT_TIME_CALLER, 0, 0);
4111                 }
4112         }
4113         if (profiler->action_flags.allocations_carry_id) {
4114                 events ++;
4115                 STORE_EVENT_ITEM_VALUE (events, profiler, obj, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_ALLOCATION_OBJECT_ID, 0, 0);
4116         }
4117 }
4118
4119 static void
4120 statistical_call_chain (MonoProfiler *profiler, int call_chain_depth, guchar **ips, void *context) {
4121         MonoDomain *domain = mono_domain_get ();
4122         ProfilerStatisticalData *data;
4123         unsigned int index;
4124         
4125         CHECK_PROFILER_ENABLED ();
4126         do {
4127                 data = profiler->statistical_data;
4128                 index = InterlockedIncrement ((int*) &data->next_free_index);
4129                 
4130                 if (index <= data->end_index) {
4131                         unsigned int base_index = (index - 1) * (profiler->statistical_call_chain_depth + 1);
4132                         unsigned int call_chain_index = 0;
4133                         
4134                         //printf ("[statistical_call_chain] (%d)\n", call_chain_depth);
4135                         while (call_chain_index < call_chain_depth) {
4136                                 ProfilerStatisticalHit *hit = & (data->hits [base_index + call_chain_index]);
4137                                 //printf ("[statistical_call_chain] [%d] = %p\n", base_index + call_chain_index, ips [call_chain_index]);
4138                                 hit->address = (gpointer) ips [call_chain_index];
4139                                 hit->domain = domain;
4140                                 call_chain_index ++;
4141                         }
4142                         while (call_chain_index <= profiler->statistical_call_chain_depth) {
4143                                 ProfilerStatisticalHit *hit = & (data->hits [base_index + call_chain_index]);
4144                                 //printf ("[statistical_call_chain] [%d] = NULL\n", base_index + call_chain_index);
4145                                 hit->address = NULL;
4146                                 hit->domain = NULL;
4147                                 call_chain_index ++;
4148                         }
4149                 } else {
4150                         /* Check if we are the one that must swap the buffers */
4151                         if (index == data->end_index + 1) {
4152                                 ProfilerStatisticalData *new_data;
4153
4154                                 /* In the *impossible* case that the writer thread has not finished yet, */
4155                                 /* loop waiting for it and meanwhile lose all statistical events... */
4156                                 do {
4157                                         /* First, wait that it consumed the ready buffer */
4158                                         while (profiler->statistical_data_ready != NULL);
4159                                         /* Then, wait that it produced the free buffer */
4160                                         new_data = profiler->statistical_data_second_buffer;
4161                                 } while (new_data == NULL);
4162
4163                                 profiler->statistical_data_ready = data;
4164                                 profiler->statistical_data = new_data;
4165                                 profiler->statistical_data_second_buffer = NULL;
4166                                 WRITER_EVENT_RAISE ();
4167                                 /* Otherwise exit from the handler and drop the event... */
4168                         } else {
4169                                 break;
4170                         }
4171                         
4172                         /* Loop again, hoping to acquire a free slot this time (otherwise the event will be dropped) */
4173                         data = NULL;
4174                 }
4175         } while (data == NULL);
4176 }
4177
4178 static void
4179 statistical_hit (MonoProfiler *profiler, guchar *ip, void *context) {
4180         MonoDomain *domain = mono_domain_get ();
4181         ProfilerStatisticalData *data;
4182         unsigned int index;
4183         
4184         CHECK_PROFILER_ENABLED ();
4185         do {
4186                 data = profiler->statistical_data;
4187                 index = InterlockedIncrement ((int*) &data->next_free_index);
4188                 
4189                 if (index <= data->end_index) {
4190                         ProfilerStatisticalHit *hit = & (data->hits [index - 1]);
4191                         hit->address = (gpointer) ip;
4192                         hit->domain = domain;
4193                 } else {
4194                         /* Check if we are the one that must swap the buffers */
4195                         if (index == data->end_index + 1) {
4196                                 ProfilerStatisticalData *new_data;
4197
4198                                 /* In the *impossible* case that the writer thread has not finished yet, */
4199                                 /* loop waiting for it and meanwhile lose all statistical events... */
4200                                 do {
4201                                         /* First, wait that it consumed the ready buffer */
4202                                         while (profiler->statistical_data_ready != NULL);
4203                                         /* Then, wait that it produced the free buffer */
4204                                         new_data = profiler->statistical_data_second_buffer;
4205                                 } while (new_data == NULL);
4206                                 
4207                                 profiler->statistical_data_ready = data;
4208                                 profiler->statistical_data = new_data;
4209                                 profiler->statistical_data_second_buffer = NULL;
4210                                 WRITER_EVENT_RAISE ();
4211                         }
4212                         
4213                         /* Loop again, hoping to acquire a free slot this time */
4214                         data = NULL;
4215                 }
4216         } while (data == NULL);
4217 }
4218
4219 static MonoProfilerEvents
4220 gc_event_code_from_profiler_event (MonoGCEvent event) {
4221         switch (event) {
4222         case MONO_GC_EVENT_START:
4223         case MONO_GC_EVENT_END:
4224                 return MONO_PROFILER_EVENT_GC_COLLECTION;
4225         case MONO_GC_EVENT_MARK_START:
4226         case MONO_GC_EVENT_MARK_END:
4227                 return MONO_PROFILER_EVENT_GC_MARK;
4228         case MONO_GC_EVENT_RECLAIM_START:
4229         case MONO_GC_EVENT_RECLAIM_END:
4230                 return MONO_PROFILER_EVENT_GC_SWEEP;
4231         case MONO_GC_EVENT_PRE_STOP_WORLD:
4232         case MONO_GC_EVENT_POST_STOP_WORLD:
4233                 return MONO_PROFILER_EVENT_GC_STOP_WORLD;
4234         case MONO_GC_EVENT_PRE_START_WORLD:
4235         case MONO_GC_EVENT_POST_START_WORLD:
4236                 return MONO_PROFILER_EVENT_GC_START_WORLD;
4237         default:
4238                 g_assert_not_reached ();
4239                 return 0;
4240         }
4241 }
4242
4243 static MonoProfilerEventKind
4244 gc_event_kind_from_profiler_event (MonoGCEvent event) {
4245         switch (event) {
4246         case MONO_GC_EVENT_START:
4247         case MONO_GC_EVENT_MARK_START:
4248         case MONO_GC_EVENT_RECLAIM_START:
4249         case MONO_GC_EVENT_PRE_STOP_WORLD:
4250         case MONO_GC_EVENT_PRE_START_WORLD:
4251                 return MONO_PROFILER_EVENT_KIND_START;
4252         case MONO_GC_EVENT_END:
4253         case MONO_GC_EVENT_MARK_END:
4254         case MONO_GC_EVENT_RECLAIM_END:
4255         case MONO_GC_EVENT_POST_START_WORLD:
4256         case MONO_GC_EVENT_POST_STOP_WORLD:
4257                 return MONO_PROFILER_EVENT_KIND_END;
4258         default:
4259                 g_assert_not_reached ();
4260                 return 0;
4261         }
4262 }
4263
4264 #define HEAP_SHOT_COMMAND_FILE_MAX_LENGTH 64
4265 static void
4266 profiler_heap_shot_process_command_file (void) {
4267         //FIXME: Port to Windows as well
4268         struct stat stat_buf;
4269         int fd;
4270         char buffer [HEAP_SHOT_COMMAND_FILE_MAX_LENGTH + 1];
4271         
4272         if (profiler->heap_shot_command_file_name == NULL)
4273                 return;
4274         if (stat (profiler->heap_shot_command_file_name, &stat_buf) != 0)
4275                 return;
4276         if (stat_buf.st_size > HEAP_SHOT_COMMAND_FILE_MAX_LENGTH)
4277                 return;
4278         if ((stat_buf.st_mtim.tv_sec * 1000000) < profiler->heap_shot_command_file_access_time)
4279                 return;
4280         
4281         fd = open (profiler->heap_shot_command_file_name, O_RDONLY);
4282         if (fd < 0) {
4283                 return;
4284         } else {
4285                 if (read (fd, &(buffer [0]), stat_buf.st_size) != stat_buf.st_size) {
4286                         return;
4287                 } else {
4288                         buffer [stat_buf.st_size] = 0;
4289                         profiler->dump_next_heap_snapshots = atoi (buffer);
4290                         MONO_PROFILER_GET_CURRENT_TIME (profiler->heap_shot_command_file_access_time);
4291                 }
4292                 close (fd);
4293         }
4294 }
4295
4296 static gboolean
4297 dump_current_heap_snapshot (void) {
4298         gboolean result;
4299         
4300         if (profiler->heap_shot_was_signalled) {
4301                 result = TRUE;
4302         } else {
4303                 profiler_heap_shot_process_command_file ();
4304                 if (profiler->dump_next_heap_snapshots > 0) {
4305                         profiler->dump_next_heap_snapshots--;
4306                         result = TRUE;
4307                 } else if (profiler->dump_next_heap_snapshots < 0) {
4308                         result = TRUE;
4309                 } else {
4310                         result = FALSE;
4311                 }
4312         }
4313         
4314         return result;
4315 }
4316
4317 static void
4318 profiler_heap_buffers_setup (ProfilerHeapShotHeapBuffers *heap) {
4319         heap->buffers = g_new (ProfilerHeapShotHeapBuffer, 1);
4320         heap->buffers->previous = NULL;
4321         heap->buffers->next = NULL;
4322         heap->buffers->start_slot = &(heap->buffers->buffer [0]);
4323         heap->buffers->end_slot = &(heap->buffers->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
4324         heap->last = heap->buffers;
4325         heap->current = heap->buffers;
4326         heap->first_free_slot = & (heap->buffers->buffer [0]);
4327 }
4328 static void
4329 profiler_heap_buffers_clear (ProfilerHeapShotHeapBuffers *heap) {
4330         heap->buffers = NULL;
4331         heap->last = NULL;
4332         heap->current = NULL;
4333         heap->first_free_slot = NULL;
4334 }
4335 static void
4336 profiler_heap_buffers_free (ProfilerHeapShotHeapBuffers *heap) {
4337         ProfilerHeapShotHeapBuffer *current = heap->buffers;
4338         while (current != NULL) {
4339                 ProfilerHeapShotHeapBuffer *next = current->next;
4340                 g_free (current);
4341                 current = next;
4342         }
4343         profiler_heap_buffers_clear (heap);
4344 }
4345
4346 static int
4347 report_object_references (gpointer *start, ClassIdMappingElement *layout, ProfilerHeapShotWriteJob *job) {
4348         int reported_references = 0;
4349         int slot;
4350         
4351         for (slot = 0; slot < layout->data.layout.slots; slot ++) {
4352                 gboolean slot_has_reference;
4353                 if (layout->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
4354                         if (layout->data.bitmap.compact & (((guint64)1) << slot)) {
4355                                 slot_has_reference = TRUE;
4356                         } else {
4357                                 slot_has_reference = FALSE;
4358                         }
4359                 } else {
4360                         if (layout->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
4361                                 slot_has_reference = TRUE;
4362                         } else {
4363                                 slot_has_reference = FALSE;
4364                         }
4365                 }
4366                 
4367                 if (slot_has_reference) {
4368                         gpointer field = start [slot];
4369                         
4370                         if ((field != NULL) && mono_object_is_alive (field)) {
4371                                 reported_references ++;
4372                                 WRITE_HEAP_SHOT_JOB_VALUE (job, field);
4373                         }
4374                 }
4375         }
4376         
4377         return reported_references;
4378 }
4379
4380 static void
4381 profiler_heap_report_object_reachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
4382         if (job != NULL) {
4383                 MonoClass *klass = mono_object_get_class (obj);
4384                 ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
4385                 if (class_id == NULL) {
4386                         printf ("profiler_heap_report_object_reachable: class %p (%s.%s) has no id\n", klass, mono_class_get_namespace (klass), mono_class_get_name (klass));
4387                 }
4388                 g_assert (class_id != NULL);
4389                 
4390                 if (job->summary.capacity > 0) {
4391                         guint32 id = class_id->id;
4392                         g_assert (id < job->summary.capacity);
4393                         
4394                         job->summary.per_class_data [id].reachable.instances ++;
4395                         job->summary.per_class_data [id].reachable.bytes += mono_object_get_size (obj);
4396                 }
4397                 if (profiler->action_flags.heap_shot && job->dump_heap_data) {
4398                         int reference_counter = 0;
4399                         gpointer *reference_counter_location;
4400                         
4401                         WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, obj, HEAP_CODE_OBJECT);
4402 #if DEBUG_HEAP_PROFILER
4403                         printf ("profiler_heap_report_object_reachable: reported object %p at cursor %p\n", obj, (job->cursor - 1));
4404 #endif
4405                         WRITE_HEAP_SHOT_JOB_VALUE (job, NULL);
4406                         reference_counter_location = job->cursor - 1;
4407                         
4408                         if (mono_class_get_rank (klass)) {
4409                                 MonoArray *array = (MonoArray *) obj;
4410                                 MonoClass *element_class = mono_class_get_element_class (klass);
4411                                 ClassIdMappingElement *element_id = class_id_mapping_element_get (element_class);
4412                                 
4413                                 g_assert (element_id != NULL);
4414                                 if (element_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
4415                                         class_id_mapping_element_build_layout_bitmap (element_class, element_id);
4416                                 }
4417                                 if (! mono_class_is_valuetype (element_class)) {
4418                                         int length = mono_array_length (array);
4419                                         int i;
4420                                         for (i = 0; i < length; i++) {
4421                                                 MonoObject *array_element = mono_array_get (array, MonoObject*, i);
4422                                                 if ((array_element != NULL) && mono_object_is_alive (array_element)) {
4423                                                         reference_counter ++;
4424                                                         WRITE_HEAP_SHOT_JOB_VALUE (job, array_element);
4425                                                 }
4426                                         }
4427                                 } else if (element_id->data.layout.references > 0) {
4428                                         int length = mono_array_length (array);
4429                                         int array_element_size = mono_array_element_size (klass);
4430                                         int i;
4431                                         for (i = 0; i < length; i++) {
4432                                                 gpointer array_element_address = mono_array_addr_with_size (array, array_element_size, i);
4433                                                 reference_counter += report_object_references (array_element_address, element_id, job);
4434                                         }
4435                                 }
4436                         } else {
4437                                 if (class_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
4438                                         class_id_mapping_element_build_layout_bitmap (klass, class_id);
4439                                 }
4440                                 if (class_id->data.layout.references > 0) {
4441                                         reference_counter += report_object_references ((gpointer)(((char*)obj) + sizeof (MonoObject)), class_id, job);
4442                                 }
4443                         }
4444                         
4445                         *reference_counter_location = GINT_TO_POINTER (reference_counter);
4446 #if DEBUG_HEAP_PROFILER
4447                         printf ("profiler_heap_report_object_reachable: updated reference_counter_location %p with value %d\n", reference_counter_location, reference_counter);
4448 #endif
4449                 }
4450         }
4451 }
4452 static void
4453 profiler_heap_report_object_unreachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
4454         if (job != NULL) {
4455                 MonoClass *klass = mono_object_get_class (obj);
4456                 guint32 size = mono_object_get_size (obj);
4457                 
4458                 if (job->summary.capacity > 0) {
4459                         ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
4460                         guint32 id;
4461                         
4462                         if (class_id == NULL) {
4463                                 printf ("profiler_heap_report_object_reachable: class %p (%s.%s) has no id\n", klass, mono_class_get_namespace (klass), mono_class_get_name (klass));
4464                         }
4465                         g_assert (class_id != NULL);
4466                         id = class_id->id;
4467                         g_assert (id < job->summary.capacity);
4468                         
4469                         job->summary.per_class_data [id].unreachable.instances ++;
4470                         job->summary.per_class_data [id].unreachable.bytes += size;
4471                 }
4472                 if (profiler->action_flags.unreachable_objects && job->dump_heap_data) {
4473 #if DEBUG_HEAP_PROFILER
4474                         printf ("profiler_heap_report_object_unreachable: at job %p writing klass %p\n", job, klass);
4475 #endif
4476                         WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, klass, HEAP_CODE_FREE_OBJECT_CLASS);
4477         
4478 #if DEBUG_HEAP_PROFILER
4479                         printf ("profiler_heap_report_object_unreachable: at job %p writing size %p\n", job, GUINT_TO_POINTER (size));
4480 #endif
4481                         WRITE_HEAP_SHOT_JOB_VALUE (job, GUINT_TO_POINTER (size));
4482                 }
4483         }
4484 }
4485
4486 static void
4487 profiler_heap_add_object (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject *obj) {
4488         if (heap->first_free_slot >= heap->current->end_slot) {
4489                 if (heap->current->next != NULL) {
4490                         heap->current = heap->current->next;
4491                 } else {
4492                         ProfilerHeapShotHeapBuffer *buffer = g_new (ProfilerHeapShotHeapBuffer, 1);
4493                         buffer->previous = heap->last;
4494                         buffer->next = NULL;
4495                         buffer->start_slot = &(buffer->buffer [0]);
4496                         buffer->end_slot = &(buffer->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
4497                         heap->current = buffer;
4498                         heap->last->next = buffer;
4499                         heap->last = buffer;
4500                 }
4501                 heap->first_free_slot = &(heap->current->buffer [0]);
4502         }
4503         
4504         *(heap->first_free_slot) = obj;
4505         heap->first_free_slot ++;
4506         profiler_heap_report_object_reachable (job, obj);
4507 }
4508
4509 static MonoObject*
4510 profiler_heap_pop_object_from_end (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject** current_slot) {
4511         while (heap->first_free_slot != current_slot) {
4512                 MonoObject* obj;
4513                 
4514                 if (heap->first_free_slot > heap->current->start_slot) {
4515                         heap->first_free_slot --;
4516                 } else {
4517                         heap->current = heap->current->previous;
4518                         g_assert (heap->current != NULL);
4519                         heap->first_free_slot = heap->current->end_slot - 1;
4520                 }
4521                 
4522                 obj = *(heap->first_free_slot);
4523                 
4524                 if (mono_object_is_alive (obj)) {
4525                         profiler_heap_report_object_reachable (job, obj);
4526                         return obj;
4527                 } else {
4528                         profiler_heap_report_object_unreachable (job, obj);
4529                 }
4530         }
4531         return NULL;
4532 }
4533
4534 static void
4535 profiler_heap_scan (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job) {
4536         ProfilerHeapShotHeapBuffer *current_buffer = heap->buffers;
4537         MonoObject** current_slot = current_buffer->start_slot;
4538         
4539         while (current_slot != heap->first_free_slot) {
4540                 MonoObject *obj = *current_slot;
4541                 if (mono_object_is_alive (obj)) {
4542                         profiler_heap_report_object_reachable (job, obj);
4543                 } else {
4544                         profiler_heap_report_object_unreachable (job, obj);
4545                         *current_slot = profiler_heap_pop_object_from_end (heap, job, current_slot);
4546                 }
4547                 
4548                 if (*current_slot != NULL) {
4549                         current_slot ++;
4550                         
4551                         if (current_slot == current_buffer->end_slot) {
4552                                 current_buffer = current_buffer->next;
4553                                 g_assert (current_buffer != NULL);
4554                                 current_slot = current_buffer->start_slot;
4555                         }
4556                 }
4557         }
4558 }
4559
4560 static inline gboolean
4561 heap_shot_write_job_should_be_created (gboolean dump_heap_data) {
4562         return dump_heap_data || profiler->action_flags.unreachable_objects || profiler->action_flags.collection_summary;
4563 }
4564
4565 static void
4566 handle_heap_profiling (MonoProfiler *profiler, MonoGCEvent ev) {
4567         static gboolean dump_heap_data;
4568         
4569         switch (ev) {
4570         case MONO_GC_EVENT_PRE_STOP_WORLD:
4571                 // Get the lock, so we are sure nobody is flushing events during the collection,
4572                 // and we can update all mappings (building the class descriptors).
4573                 LOCK_PROFILER ();
4574                 break;
4575         case MONO_GC_EVENT_POST_STOP_WORLD:
4576                 dump_heap_data = dump_current_heap_snapshot ();
4577                 if (heap_shot_write_job_should_be_created (dump_heap_data)) {
4578                         ProfilerPerThreadData *data;
4579                         // Update all mappings, so that we have built all the class descriptors.
4580                         flush_all_mappings ();
4581                         // Also write all event buffers, so that allocations are recorded.
4582                         for (data = profiler->per_thread_data; data != NULL; data = data->next) {
4583                                 write_thread_data_block (data);
4584                         }
4585                 }
4586                 // Release lock...
4587                 UNLOCK_PROFILER ();
4588                 break;
4589         case MONO_GC_EVENT_MARK_END: {
4590                 ProfilerHeapShotWriteJob *job;
4591                 ProfilerPerThreadData *data;
4592                 
4593                 if (heap_shot_write_job_should_be_created (dump_heap_data)) {
4594                         job = profiler_heap_shot_write_job_new (profiler->heap_shot_was_signalled, dump_heap_data, profiler->garbage_collection_counter);
4595                         profiler->heap_shot_was_signalled = FALSE;
4596                         MONO_PROFILER_GET_CURRENT_COUNTER (job->start_counter);
4597                         MONO_PROFILER_GET_CURRENT_TIME (job->start_time);
4598                 } else {
4599                         job = NULL;
4600                 }
4601                 
4602                 profiler_heap_scan (&(profiler->heap), job);
4603                 
4604                 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
4605                         ProfilerHeapShotObjectBuffer *buffer;
4606                         for (buffer = data->heap_shot_object_buffers; buffer != NULL; buffer = buffer->next) {
4607                                 MonoObject **cursor;
4608                                 for (cursor = buffer->first_unprocessed_slot; cursor < buffer->next_free_slot; cursor ++) {
4609                                         MonoObject *obj = *cursor;
4610 #if DEBUG_HEAP_PROFILER
4611                                         printf ("gc_event: in object buffer %p(%p-%p) cursor at %p has object %p ", buffer, &(buffer->buffer [0]), buffer->end, cursor, obj);
4612 #endif
4613                                         if (mono_object_is_alive (obj)) {
4614 #if DEBUG_HEAP_PROFILER
4615                                                 printf ("(object is alive, adding to heap)\n");
4616 #endif
4617                                                 profiler_heap_add_object (&(profiler->heap), job, obj);
4618                                         } else {
4619 #if DEBUG_HEAP_PROFILER
4620                                                 printf ("(object is unreachable, reporting in job)\n");
4621 #endif
4622                                                 profiler_heap_report_object_unreachable (job, obj);
4623                                         }
4624                                 }
4625                                 buffer->first_unprocessed_slot = cursor;
4626                         }
4627                 }
4628                 
4629                 if (job != NULL) {
4630                         MONO_PROFILER_GET_CURRENT_COUNTER (job->end_counter);
4631                         MONO_PROFILER_GET_CURRENT_TIME (job->end_time);
4632                         
4633                         profiler_add_heap_shot_write_job (job);
4634                         profiler_free_heap_shot_write_jobs ();
4635                         WRITER_EVENT_RAISE ();
4636                 }
4637                 break;
4638         }
4639         default:
4640                 break;
4641         }
4642 }
4643
4644 static void
4645 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
4646         ProfilerPerThreadData *data;
4647         ProfilerEventData *event;
4648         gboolean do_heap_profiling = profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot || profiler->action_flags.collection_summary;
4649         guint32 event_value;
4650         
4651         if (ev == MONO_GC_EVENT_START) {
4652                 profiler->garbage_collection_counter ++;
4653         }
4654         
4655         event_value = (profiler->garbage_collection_counter << 8) | generation;
4656         
4657         if (do_heap_profiling && (ev == MONO_GC_EVENT_POST_STOP_WORLD)) {
4658                 handle_heap_profiling (profiler, ev);
4659         }
4660         
4661         GET_PROFILER_THREAD_DATA (data);
4662         GET_NEXT_FREE_EVENT (data, event);
4663         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));
4664         
4665         if (do_heap_profiling && (ev != MONO_GC_EVENT_POST_STOP_WORLD)) {
4666                 handle_heap_profiling (profiler, ev);
4667         }
4668 }
4669
4670 static void
4671 gc_resize (MonoProfiler *profiler, gint64 new_size) {
4672         ProfilerPerThreadData *data;
4673         ProfilerEventData *event;
4674         GET_PROFILER_THREAD_DATA (data);
4675         GET_NEXT_FREE_EVENT (data, event);
4676         profiler->garbage_collection_counter ++;
4677         STORE_EVENT_NUMBER_VALUE (event, profiler, new_size, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_GC_RESIZE, 0, profiler->garbage_collection_counter);
4678 }
4679
4680 static void
4681 runtime_initialized (MonoProfiler *profiler) {
4682         LOG_WRITER_THREAD ("runtime_initialized: waking writer thread to enable it...\n");
4683         WRITER_EVENT_ENABLE_RAISE ();
4684         LOG_WRITER_THREAD ("runtime_initialized: waiting writer thread...\n");
4685         WRITER_EVENT_DONE_WAIT ();
4686         LOG_WRITER_THREAD ("runtime_initialized: writer thread enabled.\n");
4687         mono_add_internal_call ("Mono.Profiler.RuntimeControls::EnableProfiler", enable_profiler);
4688         mono_add_internal_call ("Mono.Profiler.RuntimeControls::DisableProfiler", disable_profiler);
4689         mono_add_internal_call ("Mono.Profiler.RuntimeControls::TakeHeapSnapshot", request_heap_snapshot);
4690         LOG_WRITER_THREAD ("runtime_initialized: initialized internal calls.\n");
4691 }
4692
4693 /* called at the end of the program */
4694 static void
4695 profiler_shutdown (MonoProfiler *prof)
4696 {
4697         ProfilerPerThreadData* current_thread_data;
4698         ProfilerPerThreadData* next_thread_data;
4699         
4700         LOG_WRITER_THREAD ("profiler_shutdown: zeroing relevant flags");
4701         mono_profiler_set_events (0);
4702         //profiler->flags = 0;
4703         //profiler->action_flags.unreachable_objects = FALSE;
4704         //profiler->action_flags.heap_shot = FALSE;
4705         
4706         LOG_WRITER_THREAD ("profiler_shutdown: asking stats thread to exit");
4707         profiler->terminate_writer_thread = TRUE;
4708         WRITER_EVENT_RAISE ();
4709         LOG_WRITER_THREAD ("profiler_shutdown: waiting for stats thread to exit");
4710         WAIT_WRITER_THREAD ();
4711         LOG_WRITER_THREAD ("profiler_shutdown: stats thread should be dead now");
4712         WRITER_EVENT_DESTROY ();
4713         
4714         LOCK_PROFILER ();
4715         flush_everything ();
4716         MONO_PROFILER_GET_CURRENT_TIME (profiler->end_time);
4717         MONO_PROFILER_GET_CURRENT_COUNTER (profiler->end_counter);
4718         write_end_block ();
4719         FLUSH_FILE ();
4720         CLOSE_FILE();
4721         UNLOCK_PROFILER ();
4722         
4723         g_free (profiler->file_name);
4724         if (profiler->file_name_suffix != NULL) {
4725                 g_free (profiler->file_name_suffix);
4726         }
4727         
4728         method_id_mapping_destroy (profiler->methods);
4729         class_id_mapping_destroy (profiler->classes);
4730         g_hash_table_destroy (profiler->loaded_assemblies);
4731         g_hash_table_destroy (profiler->loaded_modules);
4732         g_hash_table_destroy (profiler->loaded_appdomains);
4733         
4734         FREE_PROFILER_THREAD_DATA ();
4735         
4736         for (current_thread_data = profiler->per_thread_data; current_thread_data != NULL; current_thread_data = next_thread_data) {
4737                 next_thread_data = current_thread_data->next;
4738                 profiler_per_thread_data_destroy (current_thread_data);
4739         }
4740         if (profiler->statistical_data != NULL) {
4741                 profiler_statistical_data_destroy (profiler->statistical_data);
4742         }
4743         if (profiler->statistical_data_ready != NULL) {
4744                 profiler_statistical_data_destroy (profiler->statistical_data_ready);
4745         }
4746         if (profiler->statistical_data_second_buffer != NULL) {
4747                 profiler_statistical_data_destroy (profiler->statistical_data_second_buffer);
4748         }
4749         if (profiler->executable_regions != NULL) {
4750                 profiler_executable_memory_regions_destroy (profiler->executable_regions);
4751         }
4752         
4753         profiler_heap_buffers_free (&(profiler->heap));
4754         if (profiler->heap_shot_command_file_name != NULL) {
4755                 g_free (profiler->heap_shot_command_file_name);
4756         }
4757         
4758         profiler_free_write_buffers ();
4759         profiler_destroy_heap_shot_write_jobs ();
4760         
4761         DELETE_PROFILER_MUTEX ();
4762         
4763 #if (HAS_OPROFILE)
4764         if (profiler->action_flags.oprofile) {
4765                 op_close_agent ();
4766         }
4767 #endif
4768         
4769         g_free (profiler);
4770         profiler = NULL;
4771 }
4772
4773 #ifndef PLATFORM_WIN32
4774 static int
4775 parse_signal_name (const char *signal_name) {
4776         if (! strcasecmp (signal_name, "SIGUSR1")) {
4777                 return SIGUSR1;
4778         } else if (! strcasecmp (signal_name, "SIGUSR2")) {
4779                 return SIGUSR2;
4780         } else if (! strcasecmp (signal_name, "SIGPROF")) {
4781                 return SIGPROF;
4782         } else {
4783                 return atoi (signal_name);
4784         }
4785 }
4786 static gboolean
4787 check_signal_number (int signal_number) {
4788         if (((signal_number == SIGPROF) && ! (profiler->flags & MONO_PROFILE_STATISTICAL)) ||
4789                         (signal_number == SIGUSR1) ||
4790                         (signal_number == SIGUSR2)) {
4791                 return TRUE;
4792         } else {
4793                 return FALSE;
4794         }
4795 }
4796 #endif
4797
4798 #define FAIL_ARGUMENT_CHECK(message) do {\
4799         failure_message = (message);\
4800         goto failure_handling;\
4801 } while (0)
4802 #define FAIL_PARSING_VALUED_ARGUMENT FAIL_ARGUMENT_CHECK("cannot parse valued argument %s")
4803 #define FAIL_PARSING_FLAG_ARGUMENT FAIL_ARGUMENT_CHECK("cannot parse flag argument %s")
4804 #define CHECK_CONDITION(condition,message) do {\
4805         gboolean result = (condition);\
4806         if (result) {\
4807                 FAIL_ARGUMENT_CHECK (message);\
4808         }\
4809 } while (0)
4810 #define FAIL_IF_HAS_MINUS CHECK_CONDITION(has_minus,"minus ('-') modifier not allowed for argument %s")
4811 #define TRUE_IF_NOT_MINUS ((!has_minus)?TRUE:FALSE)
4812
4813 #define DEFAULT_ARGUMENTS "s"
4814 static void
4815 setup_user_options (const char *arguments) {
4816         gchar **arguments_array, **current_argument;
4817 #ifndef PLATFORM_WIN32
4818         int gc_request_signal_number = 0;
4819         int toggle_signal_number = 0;
4820 #endif
4821         detect_fast_timer ();
4822         
4823         profiler->file_name = NULL;
4824         profiler->file_name_suffix = NULL;
4825         profiler->per_thread_buffer_size = 10000;
4826         profiler->statistical_buffer_size = 10000;
4827         profiler->statistical_call_chain_depth = 0;
4828         profiler->write_buffer_size = 1024;
4829         profiler->heap_shot_command_file_name = NULL;
4830         profiler->dump_next_heap_snapshots = 0;
4831         profiler->heap_shot_command_file_access_time = 0;
4832         profiler->heap_shot_was_signalled = FALSE;
4833         profiler->flags = MONO_PROFILE_APPDOMAIN_EVENTS|
4834                         MONO_PROFILE_ASSEMBLY_EVENTS|
4835                         MONO_PROFILE_MODULE_EVENTS|
4836                         MONO_PROFILE_CLASS_EVENTS|
4837                         MONO_PROFILE_METHOD_EVENTS|
4838                         MONO_PROFILE_JIT_COMPILATION;
4839         profiler->profiler_enabled = TRUE;
4840         
4841         if (arguments == NULL) {
4842                 arguments = DEFAULT_ARGUMENTS;
4843         } else if (strstr (arguments, ":")) {
4844                 arguments = strstr (arguments, ":") + 1;
4845                 if (arguments [0] == 0) {
4846                         arguments = DEFAULT_ARGUMENTS;
4847                 }
4848         }
4849         
4850         arguments_array = g_strsplit (arguments, ",", -1);
4851         
4852         for (current_argument = arguments_array; ((current_argument != NULL) && (current_argument [0] != 0)); current_argument ++) {
4853                 char *argument = *current_argument;
4854                 char *equals = strstr (argument, "=");
4855                 const char *failure_message = NULL;
4856                 gboolean has_plus;
4857                 gboolean has_minus;
4858                 
4859                 if (*argument == '+') {
4860                         has_plus = TRUE;
4861                         has_minus = FALSE;
4862                         argument ++;
4863                 } else if (*argument == '-') {
4864                         has_plus = FALSE;
4865                         has_minus = TRUE;
4866                         argument ++;
4867                 } else {
4868                         has_plus = FALSE;
4869                         has_minus = FALSE;
4870                 }
4871                 
4872                 if (equals != NULL) {
4873                         int equals_position = equals - argument;
4874                         
4875                         if (! (strncmp (argument, "per-thread-buffer-size", equals_position) && strncmp (argument, "tbs", equals_position))) {
4876                                 int value = atoi (equals + 1);
4877                                 FAIL_IF_HAS_MINUS;
4878                                 if (value > 0) {
4879                                         profiler->per_thread_buffer_size = value;
4880                                 }
4881                         } else if (! (strncmp (argument, "statistical", equals_position) && strncmp (argument, "stat", equals_position) && strncmp (argument, "s", equals_position))) {
4882                                 int value = atoi (equals + 1);
4883                                 FAIL_IF_HAS_MINUS;
4884                                 if (value > 0) {
4885                                         if (value > 16) {
4886                                                 value = 16;
4887                                         }
4888                                         profiler->statistical_call_chain_depth = value;
4889                                         profiler->flags |= MONO_PROFILE_STATISTICAL;
4890                                 }
4891                         } else if (! (strncmp (argument, "statistical-thread-buffer-size", equals_position) && strncmp (argument, "sbs", equals_position))) {
4892                                 int value = atoi (equals + 1);
4893                                 FAIL_IF_HAS_MINUS;
4894                                 if (value > 0) {
4895                                         profiler->statistical_buffer_size = value;
4896                                 }
4897                         } else if (! (strncmp (argument, "write-buffer-size", equals_position) && strncmp (argument, "wbs", equals_position))) {
4898                                 int value = atoi (equals + 1);
4899                                 FAIL_IF_HAS_MINUS;
4900                                 if (value > 0) {
4901                                         profiler->write_buffer_size = value;
4902                                 }
4903                         } else if (! (strncmp (argument, "output", equals_position) && strncmp (argument, "out", equals_position) && strncmp (argument, "o", equals_position) && strncmp (argument, "O", equals_position))) {
4904                                 FAIL_IF_HAS_MINUS;
4905                                 if (strlen (equals + 1) > 0) {
4906                                         profiler->file_name = g_strdup (equals + 1);
4907                                 }
4908                         } else if (! (strncmp (argument, "output-suffix", equals_position) && strncmp (argument, "suffix", equals_position) && strncmp (argument, "os", equals_position) && strncmp (argument, "OS", equals_position))) {
4909                                 FAIL_IF_HAS_MINUS;
4910                                 if (strlen (equals + 1) > 0) {
4911                                         profiler->file_name_suffix = g_strdup (equals + 1);
4912                                 }
4913                         } else if (! (strncmp (argument, "heap-shot", equals_position) && strncmp (argument, "heap", equals_position) && strncmp (argument, "h", equals_position))) {
4914                                 char *parameter = equals + 1;
4915                                 if (! strcmp (parameter, "all")) {
4916                                         profiler->dump_next_heap_snapshots = -1;
4917                                 } else {
4918                                         gc_request_signal_number = parse_signal_name (parameter);
4919                                 }
4920                                 FAIL_IF_HAS_MINUS;
4921                                 if (! has_plus) {
4922                                         profiler->action_flags.save_allocation_caller = TRUE;
4923                                         profiler->action_flags.save_allocation_stack = TRUE;
4924                                         profiler->action_flags.allocations_carry_id = TRUE_IF_NOT_MINUS;
4925                                 }
4926                                 profiler->action_flags.heap_shot = TRUE_IF_NOT_MINUS;
4927                         } else if (! (strncmp (argument, "gc-commands", equals_position) && strncmp (argument, "gc-c", equals_position) && strncmp (argument, "gcc", equals_position))) {
4928                                 FAIL_IF_HAS_MINUS;
4929                                 if (strlen (equals + 1) > 0) {
4930                                         profiler->heap_shot_command_file_name = g_strdup (equals + 1);
4931                                 }
4932                         } else if (! (strncmp (argument, "gc-dumps", equals_position) && strncmp (argument, "gc-d", equals_position) && strncmp (argument, "gcd", equals_position))) {
4933                                 FAIL_IF_HAS_MINUS;
4934                                 if (strlen (equals + 1) > 0) {
4935                                         profiler->dump_next_heap_snapshots = atoi (equals + 1);
4936                                 }
4937 #ifndef PLATFORM_WIN32
4938                         } else if (! (strncmp (argument, "gc-signal", equals_position) && strncmp (argument, "gc-s", equals_position) && strncmp (argument, "gcs", equals_position))) {
4939                                 FAIL_IF_HAS_MINUS;
4940                                 if (strlen (equals + 1) > 0) {
4941                                         char *signal_name = equals + 1;
4942                                         gc_request_signal_number = parse_signal_name (signal_name);
4943                                 }
4944                         } else if (! (strncmp (argument, "toggle-signal", equals_position) && strncmp (argument, "ts", equals_position))) {
4945                                 FAIL_IF_HAS_MINUS;
4946                                 if (strlen (equals + 1) > 0) {
4947                                         char *signal_name = equals + 1;
4948                                         toggle_signal_number = parse_signal_name (signal_name);
4949                                 }
4950 #endif
4951                         } else {
4952                                 FAIL_PARSING_VALUED_ARGUMENT;
4953                         }
4954                 } else {
4955                         if (! (strcmp (argument, "jit") && strcmp (argument, "j"))) {
4956                                 profiler->action_flags.jit_time = TRUE_IF_NOT_MINUS;
4957                         } else if (! (strcmp (argument, "allocations") && strcmp (argument, "alloc") && strcmp (argument, "a"))) {
4958                                 FAIL_IF_HAS_MINUS;
4959                                 if (! has_plus) {
4960                                         profiler->action_flags.save_allocation_caller = TRUE;
4961                                         profiler->action_flags.save_allocation_stack = TRUE;
4962                                 }
4963                                 if (! has_minus) {
4964                                         profiler->flags |= MONO_PROFILE_ALLOCATIONS;
4965                                 } else {
4966                                         profiler->flags &= ~MONO_PROFILE_ALLOCATIONS;
4967                                 }
4968                         } else if (! (strcmp (argument, "gc") && strcmp (argument, "g"))) {
4969                                 FAIL_IF_HAS_MINUS;
4970                                 profiler->flags |= MONO_PROFILE_GC;
4971                         } else if (! (strcmp (argument, "allocations-summary") && strcmp (argument, "as"))) {
4972                                 profiler->action_flags.collection_summary = TRUE_IF_NOT_MINUS;
4973                         } else if (! (strcmp (argument, "heap-shot") && strcmp (argument, "heap") && strcmp (argument, "h"))) {
4974                                 FAIL_IF_HAS_MINUS;
4975                                 if (! has_plus) {
4976                                         profiler->action_flags.save_allocation_caller = TRUE;
4977                                         profiler->action_flags.save_allocation_stack = TRUE;
4978                                         profiler->action_flags.allocations_carry_id = TRUE_IF_NOT_MINUS;
4979                                 }
4980                                 profiler->action_flags.heap_shot = TRUE_IF_NOT_MINUS;
4981                         } else if (! (strcmp (argument, "unreachable") && strcmp (argument, "free") && strcmp (argument, "f"))) {
4982                                 profiler->action_flags.unreachable_objects = TRUE_IF_NOT_MINUS;
4983                         } else if (! (strcmp (argument, "threads") && strcmp (argument, "t"))) {
4984                                 if (! has_minus) {
4985                                         profiler->flags |= MONO_PROFILE_THREADS;
4986                                 } else {
4987                                         profiler->flags &= ~MONO_PROFILE_THREADS;
4988                                 }
4989                         } else if (! (strcmp (argument, "enter-leave") && strcmp (argument, "calls") && strcmp (argument, "c"))) {
4990                                 profiler->action_flags.track_calls = TRUE_IF_NOT_MINUS;
4991                         } else if (! (strcmp (argument, "statistical") && strcmp (argument, "stat") && strcmp (argument, "s"))) {
4992                                 if (! has_minus) {
4993                                         profiler->flags |= MONO_PROFILE_STATISTICAL;
4994                                 } else {
4995                                         profiler->flags &= ~MONO_PROFILE_STATISTICAL;
4996                                 }
4997                         } else if (! (strcmp (argument, "save-allocation-caller") && strcmp (argument, "sac"))) {
4998                                 profiler->action_flags.save_allocation_caller = TRUE_IF_NOT_MINUS;
4999                         } else if (! (strcmp (argument, "save-allocation-stack") && strcmp (argument, "sas"))) {
5000                                 profiler->action_flags.save_allocation_stack = TRUE_IF_NOT_MINUS;
5001                         } else if (! (strcmp (argument, "allocations-carry-id") && strcmp (argument, "aci"))) {
5002                                 profiler->action_flags.allocations_carry_id = TRUE_IF_NOT_MINUS;
5003                         } else if (! (strcmp (argument, "start-enabled") && strcmp (argument, "se"))) {
5004                                 profiler->profiler_enabled = TRUE_IF_NOT_MINUS;
5005                         } else if (! (strcmp (argument, "start-disabled") && strcmp (argument, "sd"))) {
5006                                 profiler->profiler_enabled = TRUE_IF_NOT_MINUS;
5007                         } else if (! (strcmp (argument, "force-accurate-timer") && strcmp (argument, "fac"))) {
5008                                 use_fast_timer = TRUE_IF_NOT_MINUS;
5009 #if (HAS_OPROFILE)
5010                         } else if (! (strcmp (argument, "oprofile") && strcmp (argument, "oprof"))) {
5011                                 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
5012                                 profiler->action_flags.oprofile = TRUE;
5013                                 if (op_open_agent ()) {
5014                                         FAIL_ARGUMENT_CHECK ("problem calling op_open_agent");
5015                                 }
5016 #endif
5017                         } else if (strcmp (argument, "logging")) {
5018                                 FAIL_PARSING_FLAG_ARGUMENT;
5019                         }
5020                 }
5021                 
5022 failure_handling:
5023                 if (failure_message != NULL) {
5024                         g_warning (failure_message, argument);
5025                         failure_message = NULL;
5026                 }
5027         }
5028         
5029         g_free (arguments_array);
5030         
5031 #ifndef PLATFORM_WIN32
5032         if (gc_request_signal_number != 0) {
5033                 if (check_signal_number (gc_request_signal_number) && (gc_request_signal_number != toggle_signal_number)) {
5034                         add_gc_request_handler (gc_request_signal_number);
5035                 } else {
5036                         g_error ("Cannot use signal %d", gc_request_signal_number);
5037                 }
5038         }
5039         if (toggle_signal_number != 0) {
5040                 if (check_signal_number (toggle_signal_number) && (toggle_signal_number != gc_request_signal_number)) {
5041                         add_toggle_handler (toggle_signal_number);
5042                 } else {
5043                         g_error ("Cannot use signal %d", gc_request_signal_number);
5044                 }
5045         }
5046 #endif
5047         
5048         /* Ensure that the profiler flags needed to support required action flags are active */
5049         if (profiler->action_flags.jit_time) {
5050                 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
5051         }
5052         if (profiler->action_flags.save_allocation_caller || profiler->action_flags.save_allocation_stack || profiler->action_flags.allocations_carry_id) {
5053                 profiler->flags |= MONO_PROFILE_ALLOCATIONS;
5054         }
5055         if (profiler->action_flags.collection_summary || profiler->action_flags.heap_shot || profiler->action_flags.unreachable_objects) {
5056                 profiler->flags |= MONO_PROFILE_ALLOCATIONS;
5057         }
5058         if (profiler->action_flags.track_calls) {
5059                 profiler->flags |= MONO_PROFILE_ENTER_LEAVE;
5060                 profiler->action_flags.jit_time = TRUE;
5061         }
5062         if (profiler->action_flags.save_allocation_caller || profiler->action_flags.save_allocation_stack) {
5063                 profiler->action_flags.track_stack = TRUE;
5064                 profiler->flags |= MONO_PROFILE_ENTER_LEAVE;
5065         }
5066         
5067         /* Without JIT events the stat profiler will not find method IDs... */
5068         if (profiler->flags | MONO_PROFILE_STATISTICAL) {
5069                 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
5070         }
5071         /* Profiling allocations without knowing which gc we are doing is not nice... */
5072         if (profiler->flags | MONO_PROFILE_ALLOCATIONS) {
5073                 profiler->flags |= MONO_PROFILE_GC;
5074         }
5075
5076         
5077         if (profiler->file_name == NULL) {
5078                 char *program_name = g_get_prgname ();
5079                 
5080                 if (program_name != NULL) {
5081                         char *name_buffer = g_strdup (program_name);
5082                         char *name_start = name_buffer;
5083                         char *cursor;
5084                         
5085                         /* Jump over the last '/' */
5086                         cursor = strrchr (name_buffer, '/');
5087                         if (cursor == NULL) {
5088                                 cursor = name_buffer;
5089                         } else {
5090                                 cursor ++;
5091                         }
5092                         name_start = cursor;
5093                         
5094                         /* Then jump over the last '\\' */
5095                         cursor = strrchr (name_start, '\\');
5096                         if (cursor == NULL) {
5097                                 cursor = name_start;
5098                         } else {
5099                                 cursor ++;
5100                         }
5101                         name_start = cursor;
5102                         
5103                         /* Finally, find the last '.' */
5104                         cursor = strrchr (name_start, '.');
5105                         if (cursor != NULL) {
5106                                 *cursor = 0;
5107                         }
5108                         
5109                         if (profiler->file_name_suffix == NULL) {
5110                                 profiler->file_name = g_strdup_printf ("%s.mprof", name_start);
5111                         } else {
5112                                 profiler->file_name = g_strdup_printf ("%s-%s.mprof", name_start, profiler->file_name_suffix);
5113                         }
5114                         g_free (name_buffer);
5115                 } else {
5116                         profiler->file_name = g_strdup_printf ("%s.mprof", "profiler-log");
5117                 }
5118         }
5119 }
5120
5121 static gboolean
5122 thread_detach_callback (MonoThread *thread) {
5123         LOG_WRITER_THREAD ("thread_detach_callback: asking writer thread to detach");
5124         profiler->detach_writer_thread = TRUE;
5125         WRITER_EVENT_RAISE ();
5126         LOG_WRITER_THREAD ("thread_detach_callback: done");
5127         return FALSE;
5128 }
5129
5130 static guint32
5131 data_writer_thread (gpointer nothing) {
5132         static gboolean thread_attached = FALSE;
5133         static gboolean thread_detached = FALSE;
5134         static MonoThread *this_thread = NULL;
5135         
5136         /* Wait for the OK to attach to the runtime */
5137         WRITER_EVENT_ENABLE_WAIT ();
5138         if (! profiler->terminate_writer_thread) {
5139                 MonoDomain * root_domain = mono_get_root_domain ();
5140                 if (root_domain != NULL) {
5141                         LOG_WRITER_THREAD ("data_writer_thread: attaching thread");
5142                         this_thread = mono_thread_attach (root_domain);
5143                         mono_thread_set_manage_callback (this_thread, thread_detach_callback);
5144                         thread_attached = TRUE;
5145                 } else {
5146                         g_error ("Cannot get root domain\n");
5147                 }
5148         } else {
5149                 /* Execution was too short, pretend we attached and detached. */
5150                 thread_attached = TRUE;
5151                 thread_detached = TRUE;
5152         }
5153         profiler->writer_thread_enabled = TRUE;
5154         /* Notify that we are attached to the runtime */
5155         WRITER_EVENT_DONE_RAISE ();
5156         
5157         for (;;) {
5158                 ProfilerStatisticalData *statistical_data;
5159                 gboolean done;
5160                 
5161                 LOG_WRITER_THREAD ("data_writer_thread: going to sleep");
5162                 WRITER_EVENT_WAIT ();
5163                 LOG_WRITER_THREAD ("data_writer_thread: just woke up");
5164                 
5165                 if (profiler->heap_shot_was_signalled) {
5166                         LOG_WRITER_THREAD ("data_writer_thread: starting requested collection");
5167                         mono_gc_collect (mono_gc_max_generation ());
5168                         LOG_WRITER_THREAD ("data_writer_thread: requested collection done");
5169                 }
5170                 
5171                 statistical_data = profiler->statistical_data_ready;
5172                 done = (statistical_data == NULL) && (profiler->heap_shot_write_jobs == NULL) && (profiler->writer_thread_flush_everything == FALSE);
5173                 
5174                 if ((!done) && thread_attached) {
5175                         if (profiler->writer_thread_flush_everything) {
5176                                 /* Note that this assumes the lock is held by the thread that woke us up! */
5177                                 if (! thread_detached) {
5178                                         LOG_WRITER_THREAD ("data_writer_thread: flushing everything...");
5179                                         flush_everything ();
5180                                         profiler->writer_thread_flush_everything = FALSE;
5181                                         WRITER_EVENT_DONE_RAISE ();
5182                                         LOG_WRITER_THREAD ("data_writer_thread: flushed everything.");
5183                                 } else {
5184                                         LOG_WRITER_THREAD ("data_writer_thread: flushing requested, but thread is detached...");
5185                                         profiler->writer_thread_flush_everything = FALSE;
5186                                         WRITER_EVENT_DONE_RAISE ();
5187                                         LOG_WRITER_THREAD ("data_writer_thread: done event raised.");
5188                                 }
5189                         } else {
5190                                 LOG_WRITER_THREAD ("data_writer_thread: acquiring lock and writing data");
5191                                 LOCK_PROFILER ();
5192                                 
5193                                 // This makes sure that all method ids are in place
5194                                 LOG_WRITER_THREAD ("data_writer_thread: writing mapping...");
5195                                 flush_all_mappings ();
5196                                 LOG_WRITER_THREAD ("data_writer_thread: wrote mapping");
5197                                 
5198                                 if ((statistical_data != NULL) && ! thread_detached) {
5199                                         LOG_WRITER_THREAD ("data_writer_thread: writing statistical data...");
5200                                         profiler->statistical_data_ready = NULL;
5201                                         write_statistical_data_block (statistical_data);
5202                                         statistical_data->next_free_index = 0;
5203                                         statistical_data->first_unwritten_index = 0;
5204                                         profiler->statistical_data_second_buffer = statistical_data;
5205                                         LOG_WRITER_THREAD ("data_writer_thread: wrote statistical data");
5206                                 }
5207                                 
5208                                 profiler_process_heap_shot_write_jobs ();
5209                                 
5210                                 UNLOCK_PROFILER ();
5211                                 LOG_WRITER_THREAD ("data_writer_thread: wrote data and released lock");
5212                         }
5213                 } else {
5214                         if (profiler->writer_thread_flush_everything) {
5215                                 LOG_WRITER_THREAD ("data_writer_thread: flushing requested, but thread is not attached...");
5216                                 profiler->writer_thread_flush_everything = FALSE;
5217                                 WRITER_EVENT_DONE_RAISE ();
5218                                 LOG_WRITER_THREAD ("data_writer_thread: done event raised.");
5219                         }
5220                 }
5221                 
5222                 if (profiler->detach_writer_thread) {
5223                         if (this_thread != NULL) {
5224                                 LOG_WRITER_THREAD ("data_writer_thread: detach requested, acquiring lock and flushing data");
5225                                 LOCK_PROFILER ();
5226                                 flush_everything ();
5227                                 UNLOCK_PROFILER ();
5228                                 LOG_WRITER_THREAD ("data_writer_thread: flushed data and released lock");
5229                                 LOG_WRITER_THREAD ("data_writer_thread: detaching thread");
5230                                 mono_thread_detach (this_thread);
5231                                 this_thread = NULL;
5232                                 profiler->detach_writer_thread = FALSE;
5233                                 thread_detached = TRUE;
5234                         } else {
5235                                 LOG_WRITER_THREAD ("data_writer_thread: warning: thread has already been detached");
5236                         }
5237                 }
5238                 
5239                 if (profiler->terminate_writer_thread) {
5240                 LOG_WRITER_THREAD ("data_writer_thread: exiting thread");
5241                         CLEANUP_WRITER_THREAD ();
5242                         EXIT_THREAD ();
5243                 }
5244         }
5245         return 0;
5246 }
5247
5248 void
5249 mono_profiler_startup (const char *desc);
5250
5251 /* the entry point (mono_profiler_load?) */
5252 void
5253 mono_profiler_startup (const char *desc)
5254 {
5255         profiler = g_new0 (MonoProfiler, 1);
5256         
5257         setup_user_options ((desc != NULL) ? desc : DEFAULT_ARGUMENTS);
5258         
5259         INITIALIZE_PROFILER_MUTEX ();
5260         MONO_PROFILER_GET_CURRENT_TIME (profiler->start_time);
5261         MONO_PROFILER_GET_CURRENT_COUNTER (profiler->start_counter);
5262         profiler->last_header_counter = 0;
5263         
5264         profiler->methods = method_id_mapping_new ();
5265         profiler->classes = class_id_mapping_new ();
5266         profiler->loaded_assemblies = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
5267         profiler->loaded_modules = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
5268         profiler->loaded_appdomains = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
5269         
5270         profiler->statistical_data = profiler_statistical_data_new (profiler);
5271         profiler->statistical_data_second_buffer = profiler_statistical_data_new (profiler);
5272         
5273         profiler->write_buffers = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
5274         profiler->write_buffers->next = NULL;
5275         profiler->current_write_buffer = profiler->write_buffers;
5276         profiler->current_write_position = 0;
5277         profiler->full_write_buffers = 0;
5278         
5279         profiler->executable_regions = profiler_executable_memory_regions_new (1, 1);
5280         
5281         profiler->executable_files.table = g_hash_table_new (g_str_hash, g_str_equal); 
5282         profiler->executable_files.new_files = NULL; 
5283         
5284         profiler->heap_shot_write_jobs = NULL;
5285         if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot || profiler->action_flags.collection_summary) {
5286                 profiler_heap_buffers_setup (&(profiler->heap));
5287         } else {
5288                 profiler_heap_buffers_clear (&(profiler->heap));
5289         }
5290         profiler->garbage_collection_counter = 0;
5291         
5292         WRITER_EVENT_INIT ();
5293         LOG_WRITER_THREAD ("mono_profiler_startup: creating writer thread");
5294         CREATE_WRITER_THREAD (data_writer_thread);
5295         LOG_WRITER_THREAD ("mono_profiler_startup: created writer thread");
5296
5297         ALLOCATE_PROFILER_THREAD_DATA ();
5298         
5299         OPEN_FILE ();
5300         
5301         write_intro_block ();
5302         write_directives_block (TRUE);
5303         
5304         mono_profiler_install (profiler, profiler_shutdown);
5305         
5306         mono_profiler_install_appdomain (appdomain_start_load, appdomain_end_load,
5307                         appdomain_start_unload, appdomain_end_unload);
5308         mono_profiler_install_assembly (assembly_start_load, assembly_end_load,
5309                         assembly_start_unload, assembly_end_unload);
5310         mono_profiler_install_module (module_start_load, module_end_load,
5311                         module_start_unload, module_end_unload);
5312         mono_profiler_install_class (class_start_load, class_end_load,
5313                         class_start_unload, class_end_unload);
5314         mono_profiler_install_jit_compile (method_start_jit, method_end_jit);
5315         mono_profiler_install_enter_leave (method_enter, method_leave);
5316         mono_profiler_install_method_free (method_free);
5317         mono_profiler_install_thread (thread_start, thread_end);
5318         mono_profiler_install_allocation (object_allocated);
5319         mono_profiler_install_statistical (statistical_hit);
5320         mono_profiler_install_statistical_call_chain (statistical_call_chain, profiler->statistical_call_chain_depth);
5321         mono_profiler_install_gc (gc_event, gc_resize);
5322         mono_profiler_install_runtime_initialized (runtime_initialized);
5323 #if (HAS_OPROFILE)
5324         mono_profiler_install_jit_end (method_jit_result);
5325 #endif
5326         
5327         mono_profiler_set_events (profiler->flags);
5328 }
5329