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