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