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