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