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