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