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