a9a76acedb4daa3fe7fef1f1980168f16c8a839d
[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/io-layer/atomic.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13 #include <glib.h>
14
15 #include <dlfcn.h>
16
17 #define HAS_OPROFILE 0
18
19 #if (HAS_OPROFILE)
20 #include <libopagent.h>
21 #endif
22
23 // Needed for heap analysis
24 extern gboolean mono_object_is_alive (MonoObject* obj);
25
26 typedef enum {
27         MONO_PROFILER_FILE_BLOCK_KIND_INTRO = 1,
28         MONO_PROFILER_FILE_BLOCK_KIND_END = 2,
29         MONO_PROFILER_FILE_BLOCK_KIND_MAPPING = 3,
30         MONO_PROFILER_FILE_BLOCK_KIND_LOADED = 4,
31         MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED = 5,
32         MONO_PROFILER_FILE_BLOCK_KIND_EVENTS = 6,
33         MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL = 7,
34         MONO_PROFILER_FILE_BLOCK_KIND_HEAP = 8
35 } MonoProfilerFileBlockKind;
36
37 #define MONO_PROFILER_LOADED_EVENT_MODULE     1
38 #define MONO_PROFILER_LOADED_EVENT_ASSEMBLY   2
39 #define MONO_PROFILER_LOADED_EVENT_APPDOMAIN  4
40 #define MONO_PROFILER_LOADED_EVENT_SUCCESS    8
41 #define MONO_PROFILER_LOADED_EVENT_FAILURE   16
42
43 typedef enum {
44         MONO_PROFILER_EVENT_DATA_TYPE_OTHER = 0,
45         MONO_PROFILER_EVENT_DATA_TYPE_METHOD = 1,
46         MONO_PROFILER_EVENT_DATA_TYPE_CLASS = 2
47 } MonoProfilerEventDataType;
48
49 typedef struct _ProfilerEventData {
50         union {
51                 gpointer address;
52                 gsize number;
53         } data;
54         unsigned int data_type:2;
55         unsigned int code:3;
56         unsigned int kind:1;
57         unsigned int value:26;
58 } ProfilerEventData;
59
60 #define EXTENDED_EVENT_VALUE_SHIFT (26)
61 #define MAX_EVENT_VALUE ((1<<EXTENDED_EVENT_VALUE_SHIFT)-1)
62 #define MAX_EXTENDED_EVENT_VALUE ((((guint64))MAX_EVENT_VALUE<<32)|((guint64)0xffffffff))
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 #if 1
100 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) MONO_PROFILER_GET_CURRENT_TIME ((c));
101 #else
102 static __inline__ guint64 rdtsc(void) {
103         guint32 hi, lo;
104         __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
105         return ((guint64) lo) | (((guint64) hi) << 32);
106 }
107 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) {\
108         (c) = rdtsc ();\
109 } while (0)
110 #endif
111
112
113 #define CLASS_LAYOUT_PACKED_BITMAP_SIZE 64
114 #define CLASS_LAYOUT_NOT_INITIALIZED (0xFFFF)
115 typedef enum {
116         HEAP_CODE_NONE = 0,
117         HEAP_CODE_OBJECT = 1,
118         HEAP_CODE_FREE_OBJECT_CLASS = 2,
119         HEAP_CODE_MASK = 3
120 } HeapProfilerJobValueCode;
121 typedef struct _MonoProfilerClassData {
122         union {
123                 guint64 compact;
124                 guint8 *extended;
125         } bitmap;
126         struct {
127                 guint16 slots;
128                 guint16 references;
129         } layout;
130 } MonoProfilerClassData;
131
132 typedef struct _MonoProfilerMethodData {
133         gpointer code_start;
134         guint32 code_size;
135 } MonoProfilerMethodData;
136
137 typedef struct _ClassIdMappingElement {
138         char *name;
139         guint32 id;
140         MonoClass *klass;
141         struct _ClassIdMappingElement *next_unwritten;
142         MonoProfilerClassData data;
143 } ClassIdMappingElement;
144
145 typedef struct _MethodIdMappingElement {
146         char *name;
147         guint32 id;
148         MonoMethod *method;
149         struct _MethodIdMappingElement *next_unwritten;
150         MonoProfilerMethodData data;
151 } MethodIdMappingElement;
152
153 typedef struct _ClassIdMapping {
154         GHashTable *table;
155         ClassIdMappingElement *unwritten;
156         guint32 next_id;
157 } ClassIdMapping;
158
159 typedef struct _MethodIdMapping {
160         GHashTable *table;
161         MethodIdMappingElement *unwritten;
162         guint32 next_id;
163 } MethodIdMapping;
164
165 typedef struct _LoadedElement {
166         char *name;
167         guint64 load_start_counter;
168         guint64 load_end_counter;
169         guint64 unload_start_counter;
170         guint64 unload_end_counter;
171         guint8 loaded;
172         guint8 load_written;
173         guint8 unloaded;
174         guint8 unload_written;
175 } LoadedElement;
176
177 #define PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE 1024
178 #define PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE 4096
179 #define PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE 4096
180
181 typedef struct _ProfilerHeapShotObjectBuffer {
182         struct _ProfilerHeapShotObjectBuffer *next;
183         MonoObject **next_free_slot;
184         MonoObject **end;
185         MonoObject **first_unprocessed_slot;
186         MonoObject *buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE];
187 } ProfilerHeapShotObjectBuffer;
188
189 typedef struct _ProfilerHeapShotHeapBuffer {
190         struct _ProfilerHeapShotHeapBuffer *next;
191         struct _ProfilerHeapShotHeapBuffer *previous;
192         MonoObject **start_slot;
193         MonoObject **end_slot;
194         MonoObject *buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE];
195 } ProfilerHeapShotHeapBuffer;
196
197 typedef struct _ProfilerHeapShotHeapBuffers {
198         ProfilerHeapShotHeapBuffer *buffers;
199         ProfilerHeapShotHeapBuffer *last;
200         ProfilerHeapShotHeapBuffer *current;
201         MonoObject **first_free_slot;
202 } ProfilerHeapShotHeapBuffers;
203
204
205 typedef struct _ProfilerHeapShotWriteBuffer {
206         struct _ProfilerHeapShotWriteBuffer *next;
207         gpointer buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE];
208 } ProfilerHeapShotWriteBuffer;
209
210 typedef struct _ProfilerHeapShotWriteJob {
211         struct _ProfilerHeapShotWriteJob *next;
212         struct _ProfilerHeapShotWriteJob *next_unwritten;
213         gpointer *start;
214         gpointer *cursor;
215         gpointer *end;
216         ProfilerHeapShotWriteBuffer *buffers;
217         ProfilerHeapShotWriteBuffer **last_next;
218         guint32 full_buffers;
219         gboolean heap_shot_was_signalled;
220         guint64 start_counter;
221         guint64 start_time;
222         guint64 end_counter;
223         guint64 end_time;
224 } ProfilerHeapShotWriteJob;
225
226 typedef struct _ProfilerPerThreadData {
227         ProfilerEventData *events;
228         ProfilerEventData *next_free_event;
229         ProfilerEventData *end_event;
230         ProfilerEventData *first_unwritten_event;
231         ProfilerEventData *first_unmapped_event;
232         guint64 start_event_counter;
233         guint64 last_event_counter;
234         gsize thread_id;
235         ProfilerHeapShotObjectBuffer *heap_shot_object_buffers;
236         struct _ProfilerPerThreadData* next;
237 } ProfilerPerThreadData;
238
239 typedef struct _ProfilerStatisticalData {
240         gpointer *addresses;
241         int next_free_index;
242         int end_index;
243         int first_unwritten_index;
244 } ProfilerStatisticalData;
245
246 typedef struct _ProfilerExecutableMemoryRegionData {
247         gpointer start;
248         gpointer end;
249         guint32 file_offset;
250         char *file_name;
251         guint32 id;
252         gboolean is_new;
253 } ProfilerExecutableMemoryRegionData;
254
255 typedef struct _ProfilerExecutableMemoryRegions {
256         ProfilerExecutableMemoryRegionData **regions;
257         guint32 regions_capacity;
258         guint32 regions_count;
259         guint32 next_id;
260 } ProfilerExecutableMemoryRegions;
261
262 typedef struct _ProfilerUnmanagedFunction {
263         guint32 id;
264         guint32 hits;
265         char *name;
266         struct _ProfilerUnmanagedFunction *next_unwritten;
267 } ProfilerUnmanagedFunction;
268
269 typedef struct _ProfilerUnmanagedFunctions {
270         GHashTable *table;
271         ProfilerUnmanagedFunction *unwritten_queue;
272         ProfilerUnmanagedFunction *unwritten_queue_end;
273         guint32 next_id;
274         ProfilerUnmanagedFunction actual_unwritten_queue_end;
275 } ProfilerUnmanagedFunctions;
276
277 #ifndef PLATFORM_WIN32
278 #include <sys/types.h>
279 #include <sys/time.h>
280 #include <sys/stat.h>
281 #include <unistd.h>
282 #include <fcntl.h>
283 #include <pthread.h>
284 #include <semaphore.h>
285
286 #define MUTEX_TYPE pthread_mutex_t
287 #define INITIALIZE_PROFILER_MUTEX() pthread_mutex_init (&(profiler->mutex), NULL)
288 #define DELETE_PROFILER_MUTEX() pthread_mutex_destroy (&(profiler->mutex))
289 #define LOCK_PROFILER() do {/*LOG_WRITER_THREAD ("LOCK_PROFILER");*/ pthread_mutex_lock (&(profiler->mutex));} while (0)
290 #define UNLOCK_PROFILER() do {/*LOG_WRITER_THREAD ("UNLOCK_PROFILER");*/ pthread_mutex_unlock (&(profiler->mutex));} while (0)
291
292 #define THREAD_TYPE pthread_t
293 #define CREATE_WRITER_THREAD(f) pthread_create (&(profiler->data_writer_thread), NULL, ((void*(*)(void*))f), NULL)
294 #define EXIT_THREAD() pthread_exit (NULL);
295 #define WAIT_WRITER_THREAD() pthread_join (profiler->data_writer_thread, NULL)
296 #define CURRENT_THREAD_ID() (gsize) pthread_self ()
297
298 #ifndef HAVE_KW_THREAD
299 static pthread_key_t pthread_profiler_key;
300 static pthread_once_t profiler_pthread_once = PTHREAD_ONCE_INIT;
301 static void
302 make_pthread_profiler_key (void) {
303     (void) pthread_key_create (&pthread_profiler_key, NULL);
304 }
305 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) pthread_getspecific (pthread_profiler_key))
306 #define SET_PROFILER_THREAD_DATA(x) (void) pthread_setspecific (pthread_profiler_key, (x))
307 #define ALLOCATE_PROFILER_THREAD_DATA() (void) pthread_once (&profiler_pthread_once, make_pthread_profiler_key)
308 #define FREE_PROFILER_THREAD_DATA() (void) pthread_key_delete (pthread_profiler_key)
309 #endif
310
311 #define EVENT_TYPE sem_t
312 #define WRITER_EVENT_INIT() (void) sem_init (&(profiler->statistical_data_writer_event), 0, 0)
313 #define WRITER_EVENT_DESTROY() (void) sem_destroy (&(profiler->statistical_data_writer_event))
314 #define WRITER_EVENT_WAIT() (void) sem_wait (&(profiler->statistical_data_writer_event))
315 #define WRITER_EVENT_RAISE() (void) sem_post (&(profiler->statistical_data_writer_event))
316
317 #if 0
318 #define FILE_HANDLE_TYPE FILE*
319 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
320 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
321 #define FLUSH_FILE() fflush (profiler->file)
322 #define CLOSE_FILE() fclose (profiler->file);
323 #else
324 #define FILE_HANDLE_TYPE int
325 #define OPEN_FILE() profiler->file = open (profiler->file_name, O_WRONLY|O_CREAT|O_TRUNC, 0664);
326 #define WRITE_BUFFER(b,s) write (profiler->file, (b), (s))
327 #define FLUSH_FILE()
328 #define CLOSE_FILE() close (profiler->file);
329 #endif
330
331 #else
332
333 #include <windows.h>
334
335 #define MUTEX_TYPE CRITICAL_SECTION
336 #define INITIALIZE_PROFILER_MUTEX() InitializeCriticalSection (&(profiler->mutex))
337 #define DELETE_PROFILER_MUTEX() DeleteCriticalSection (&(profiler->mutex))
338 #define LOCK_PROFILER() EnterCriticalSection (&(profiler->mutex))
339 #define UNLOCK_PROFILER() LeaveCriticalSection (&(profiler->mutex))
340
341 #define THREAD_TYPE HANDLE
342 #define CREATE_WRITER_THREAD(f) CreateThread (NULL, (1*1024*1024), (f), NULL, 0, NULL);
343 #define EXIT_THREAD() ExitThread (0);
344 #define WAIT_WRITER_THREAD() WaitForSingleObject (profiler->data_writer_thread, INFINITE)
345 #define CURRENT_THREAD_ID() (gsize) GetCurrentThreadId ()
346
347 #ifndef HAVE_KW_THREAD
348 static guint32 profiler_thread_id = -1;
349 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*)TlsGetValue (profiler_thread_id))
350 #define SET_PROFILER_THREAD_DATA(x) TlsSetValue (profiler_thread_id, (x));
351 #define ALLOCATE_PROFILER_THREAD_DATA() profiler_thread_id = TlsAlloc ()
352 #define FREE_PROFILER_THREAD_DATA() TlsFree (profiler_thread_id)
353 #endif
354
355 #define EVENT_TYPE HANDLE
356 #define WRITER_EVENT_INIT() profiler->statistical_data_writer_event = CreateEvent (NULL, FALSE, FALSE, NULL)
357 #define WRITER_EVENT_DESTROY() CloseHandle (profiler->statistical_data_writer_event)
358 #define WRITER_EVENT_WAIT() WaitForSingleObject (profiler->statistical_data_writer_event, INFINITE)
359 #define WRITER_EVENT_RAISE() SetEvent (profiler->statistical_data_writer_event)
360
361 #define FILE_HANDLE_TYPE FILE*
362 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
363 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
364 #define FLUSH_FILE() fflush (profiler->file)
365 #define CLOSE_FILE() fclose (profiler->file);
366
367 #endif
368
369 #ifdef HAVE_KW_THREAD
370 static __thread ProfilerPerThreadData * tls_profiler_per_thread_data;
371 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) tls_profiler_per_thread_data)
372 #define SET_PROFILER_THREAD_DATA(x) tls_profiler_per_thread_data = (x)
373 #define ALLOCATE_PROFILER_THREAD_DATA() /* nop */
374 #define FREE_PROFILER_THREAD_DATA() /* nop */
375 #endif
376
377 #define GET_PROFILER_THREAD_DATA(data) do {\
378         ProfilerPerThreadData *_result = LOOKUP_PROFILER_THREAD_DATA ();\
379         if (!_result) {\
380                 _result = profiler_per_thread_data_new (profiler->per_thread_buffer_size);\
381                 LOCK_PROFILER ();\
382                 _result->next = profiler->per_thread_data;\
383                 profiler->per_thread_data = _result;\
384                 UNLOCK_PROFILER ();\
385                 SET_PROFILER_THREAD_DATA (_result);\
386         }\
387         (data) = _result;\
388 } while (0)
389
390 #define PROFILER_FILE_WRITE_BUFFER_SIZE (profiler->write_buffer_size)
391 typedef struct _ProfilerFileWriteBuffer {
392         struct _ProfilerFileWriteBuffer *next;
393         guint8 buffer [];
394 } ProfilerFileWriteBuffer;
395
396 struct _MonoProfiler {
397         MUTEX_TYPE mutex;
398         
399         MonoProfileFlags flags;
400         char *file_name;
401         FILE_HANDLE_TYPE file;
402         
403         guint64 start_time;
404         guint64 start_counter;
405         guint64 end_time;
406         guint64 end_counter;
407         
408         MethodIdMapping *methods;
409         ClassIdMapping *classes;
410         
411         GHashTable *loaded_assemblies;
412         GHashTable *loaded_modules;
413         GHashTable *loaded_appdomains;
414         
415         guint32 per_thread_buffer_size;
416         guint32 statistical_buffer_size;
417         ProfilerPerThreadData* per_thread_data;
418         ProfilerStatisticalData *statistical_data;
419         ProfilerStatisticalData *statistical_data_ready;
420         ProfilerStatisticalData *statistical_data_second_buffer;
421         ProfilerUnmanagedFunctions unmanaged_functions;
422         THREAD_TYPE data_writer_thread;
423         EVENT_TYPE statistical_data_writer_event;
424         gboolean terminate_writer_thread;
425         
426         ProfilerFileWriteBuffer *write_buffers;
427         ProfilerFileWriteBuffer *current_write_buffer;
428         int write_buffer_size;
429         int current_write_position;
430         int full_write_buffers;
431         
432         ProfilerHeapShotWriteJob *heap_shot_write_jobs;
433         ProfilerHeapShotHeapBuffers heap;
434         
435         char *heap_shot_command_file_name;
436         int dump_next_heap_snapshots;
437         guint64 heap_shot_command_file_access_time;
438         gboolean heap_shot_was_signalled;
439         
440         ProfilerExecutableMemoryRegions *executable_regions;
441         
442         struct {
443 #if (HAS_OPROFILE)
444                 gboolean oprofile;
445 #endif
446                 gboolean jit_time;
447                 gboolean unreachable_objects;
448                 gboolean heap_shot;
449         } action_flags;
450 };
451 static MonoProfiler *profiler;
452
453
454 #define DEBUG_LOAD_EVENTS 0
455 #define DEBUG_MAPPING_EVENTS 0
456 #define DEBUG_LOGGING_PROFILER 0
457 #define DEBUG_HEAP_PROFILER 0
458 #define DEBUG_CLASS_BITMAPS 0
459 #define DEBUG_STATISTICAL_PROFILER 0
460 #define DEBUG_WRITER_THREAD 0
461 #if (DEBUG_LOGGING_PROFILER || DEBUG_STATISTICAL_PROFILER || DEBUG_HEAP_PROFILER || DEBUG_WRITER_THREAD)
462 #define LOG_WRITER_THREAD(m) printf ("WRITER-THREAD-LOG %s\n", m)
463 #else
464 #define LOG_WRITER_THREAD(m)
465 #endif
466
467 #if DEBUG_LOGGING_PROFILER
468 static int event_counter = 0;
469 #define EVENT_MARK() printf ("[EVENT:%d]", ++ event_counter)
470 #endif
471
472
473 static ClassIdMappingElement*
474 class_id_mapping_element_get (MonoClass *klass) {
475         return g_hash_table_lookup (profiler->classes->table, (gconstpointer) klass);
476 }
477
478 static MethodIdMappingElement*
479 method_id_mapping_element_get (MonoMethod *method) {
480         return g_hash_table_lookup (profiler->methods->table, (gconstpointer) method);
481 }
482
483 #define BITS_TO_BYTES(v) do {\
484         (v) += 7;\
485         (v) &= ~7;\
486         (v) >>= 3;\
487 } while (0)
488
489 static ClassIdMappingElement*
490 class_id_mapping_element_new (MonoClass *klass) {
491         ClassIdMappingElement *result = g_new (ClassIdMappingElement, 1);
492         
493         result->name = g_strdup_printf ("%s.%s", mono_class_get_namespace (klass), mono_class_get_name (klass));
494         result->klass = klass;
495         result->next_unwritten = profiler->classes->unwritten;
496         profiler->classes->unwritten = result;
497         result->id = profiler->classes->next_id;
498         profiler->classes->next_id ++;
499         
500         result->data.bitmap.compact = 0;
501         result->data.layout.slots = CLASS_LAYOUT_NOT_INITIALIZED;
502         result->data.layout.references = CLASS_LAYOUT_NOT_INITIALIZED;
503         
504         g_hash_table_insert (profiler->classes->table, klass, result);
505         
506 #if (DEBUG_MAPPING_EVENTS)
507         printf ("Created new CLASS mapping element \"%s\" (%p)[%d]\n", result->name, klass, result->id);
508 #endif
509         return result;
510 }
511
512 static void
513 class_id_mapping_element_build_layout_bitmap (MonoClass *klass, ClassIdMappingElement *klass_id) {
514         MonoClass *parent_class = mono_class_get_parent (klass);
515         int number_of_reference_fields = 0;
516         int max_offset_of_reference_fields = 0;
517         ClassIdMappingElement *parent_id;
518         gpointer iter;
519         MonoClassField *field;
520         
521 #if (DEBUG_CLASS_BITMAPS)
522         printf ("class_id_mapping_element_build_layout_bitmap: building layout for class %s.%s: ", mono_class_get_namespace (klass), mono_class_get_name (klass));
523 #endif
524         
525         if (parent_class != NULL) {
526                 parent_id = class_id_mapping_element_get (parent_class);
527                 g_assert (parent_id != NULL);
528                 
529                 if (parent_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
530 #if (DEBUG_CLASS_BITMAPS)
531                         printf ("[recursively building bitmap for father class]\n");
532 #endif
533                         class_id_mapping_element_build_layout_bitmap (parent_class, parent_id);
534                 }
535         } else {
536                 parent_id = NULL;
537         }
538         
539         iter = NULL;
540         while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
541                 MonoType* field_type = mono_field_get_type (field);
542                 // For now, skip static fields
543                 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
544                         continue;
545                 
546                 if (MONO_TYPE_IS_REFERENCE (field_type)) {
547                         int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
548                         if (field_offset > max_offset_of_reference_fields) {
549                                 max_offset_of_reference_fields = field_offset;
550                         }
551                         number_of_reference_fields ++;
552                 } else {
553                         MonoClass *field_class = mono_class_from_mono_type (field_type);
554                         if (field_class && mono_class_is_valuetype (field_class)) {
555                                 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
556                                 g_assert (field_id != NULL);
557                                 
558                                 if (field_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
559                                         if (field_id != klass_id) {
560 #if (DEBUG_CLASS_BITMAPS)
561                                                 printf ("[recursively building bitmap for field %s]\n", mono_field_get_name (field));
562 #endif
563                                                 class_id_mapping_element_build_layout_bitmap (field_class, field_id);
564                                         } else {
565 #if (DEBUG_CLASS_BITMAPS)
566                                                 printf ("[breaking recursive bitmap build for field %s]", mono_field_get_name (field));
567                                                 
568 #endif
569                                                 klass_id->data.bitmap.compact = 0;
570                                                 klass_id->data.layout.slots = 0;
571                                                 klass_id->data.layout.references = 0;
572                                         }
573                                 }
574                                 
575                                 if (field_id->data.layout.references > 0) {
576                                         int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
577                                         int max_offset_reference_in_field = (field_id->data.layout.slots - 1) * sizeof (gpointer);
578                                         
579                                         if ((field_offset + max_offset_reference_in_field) > max_offset_of_reference_fields) {
580                                                 max_offset_of_reference_fields = field_offset + max_offset_reference_in_field;
581                                         }
582                                         
583                                         number_of_reference_fields += field_id->data.layout.references;
584                                 }
585                         }
586                 }
587         }
588         
589 #if (DEBUG_CLASS_BITMAPS)
590         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);
591 #endif
592         if ((number_of_reference_fields == 0) && ((parent_id == NULL) || (parent_id->data.layout.references == 0))) {
593 #if (DEBUG_CLASS_BITMAPS)
594                 printf ("[no references at all]");
595 #endif
596                 klass_id->data.bitmap.compact = 0;
597                 klass_id->data.layout.slots = 0;
598                 klass_id->data.layout.references = 0;
599         } else {
600                 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
601 #if (DEBUG_CLASS_BITMAPS)
602                         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);
603 #endif
604                         klass_id->data.layout.slots = parent_id->data.layout.slots;
605                         klass_id->data.layout.references = parent_id->data.layout.references;
606                 } else {
607 #if (DEBUG_CLASS_BITMAPS)
608                         printf ("[no references from parent]");
609 #endif
610                         klass_id->data.layout.slots = 0;
611                         klass_id->data.layout.references = 0;
612                 }
613                 
614                 if (number_of_reference_fields > 0) {
615                         klass_id->data.layout.slots += ((max_offset_of_reference_fields / sizeof (gpointer)) + 1);
616                         klass_id->data.layout.references += number_of_reference_fields;
617 #if (DEBUG_CLASS_BITMAPS)
618                         printf ("[adding data, going to %d references in %d slots]", klass_id->data.layout.references, klass_id->data.layout.slots);
619 #endif
620                 }
621                 
622                 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
623 #if (DEBUG_CLASS_BITMAPS)
624                                 printf ("[zeroing bitmap]");
625 #endif
626                                 klass_id->data.bitmap.compact = 0;
627                         if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
628 #if (DEBUG_CLASS_BITMAPS)
629                                 printf ("[copying compact father bitmap]");
630 #endif
631                                 klass_id->data.bitmap.compact = parent_id->data.bitmap.compact;
632                         }
633                 } else {
634                         int size_of_bitmap = klass_id->data.layout.slots;
635                         BITS_TO_BYTES (size_of_bitmap);
636 #if (DEBUG_CLASS_BITMAPS)
637                         printf ("[allocating %d bytes for bitmap]", size_of_bitmap);
638 #endif
639                         klass_id->data.bitmap.extended = g_malloc0 (size_of_bitmap);
640                         if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
641                                 int size_of_father_bitmap = parent_id->data.layout.slots;
642                                 if (size_of_father_bitmap <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
643                                         int father_slot;
644 #if (DEBUG_CLASS_BITMAPS)
645                                         printf ("[copying %d bits from father bitmap]", size_of_father_bitmap);
646 #endif
647                                         for (father_slot = 0; father_slot < size_of_father_bitmap; father_slot ++) {
648                                                 if (parent_id->data.bitmap.compact & (((guint64)1) << father_slot)) {
649                                                         klass_id->data.bitmap.extended [father_slot >> 3] |= (1 << (father_slot & 7));
650                                                 }
651                                         }
652                                 } else {
653                                         BITS_TO_BYTES (size_of_father_bitmap);
654 #if (DEBUG_CLASS_BITMAPS)
655                                         printf ("[copying %d bytes from father bitmap]", size_of_father_bitmap);
656 #endif
657                                         memcpy (klass_id->data.bitmap.extended, parent_id->data.bitmap.extended, size_of_father_bitmap);
658                                 }
659                         }
660                 }
661         }
662         
663 #if (DEBUG_CLASS_BITMAPS)
664         printf ("[starting filling iteration]\n");
665 #endif
666         iter = NULL;
667         while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
668                 MonoType* field_type = mono_field_get_type (field);
669                 // For now, skip static fields
670                 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
671                         continue;
672                 
673 #if (DEBUG_CLASS_BITMAPS)
674                 printf ("[Working on field %s]", mono_field_get_name (field));
675 #endif
676                 if (MONO_TYPE_IS_REFERENCE (field_type)) {
677                         int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
678                         int field_slot;
679                         g_assert ((field_offset % sizeof (gpointer)) == 0);
680                         field_slot = field_offset / sizeof (gpointer);
681                         if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
682                                 klass_id->data.bitmap.compact |= (((guint64)1) << field_slot);
683                         } else {
684                                 klass_id->data.bitmap.extended [field_slot >> 3] |= (1 << (field_slot & 7));
685                         }
686 #if (DEBUG_CLASS_BITMAPS)
687                         printf ("[reference at offset %d, slot %d]", field_offset, field_slot);
688 #endif
689                 } else {
690                         MonoClass *field_class = mono_class_from_mono_type (field_type);
691                         if (field_class && mono_class_is_valuetype (field_class)) {
692                                 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
693                                 int field_offset;
694                                 int field_slot;
695                                 
696                                 g_assert (field_id != NULL);
697                                 field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
698                                 g_assert ((field_id->data.layout.references == 0) || ((field_offset % sizeof (gpointer)) == 0));
699                                 field_slot = field_offset / sizeof (gpointer);
700 #if (DEBUG_CLASS_BITMAPS)
701                                 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);
702 #endif
703                                 
704                                 if (field_id->data.layout.references > 0) {
705                                         int sub_field_slot;
706                                         if (field_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
707                                                 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
708                                                         if (field_id->data.bitmap.compact & (((guint64)1) << sub_field_slot)) {
709                                                                 int actual_slot = field_slot + sub_field_slot;
710                                                                 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
711                                                                         klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
712                                                                 } else {
713                                                                         klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
714                                                                 }
715                                                         }
716                                                 }
717                                         } else {
718                                                 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
719                                                         if (field_id->data.bitmap.extended [sub_field_slot >> 3] & (1 << (sub_field_slot & 7))) {
720                                                                 int actual_slot = field_slot + sub_field_slot;
721                                                                 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
722                                                                         klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
723                                                                 } else {
724                                                                         klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
725                                                                 }
726                                                         }
727                                                 }
728                                         }
729                                 }
730                         }
731                 }
732         }
733 #if (DEBUG_CLASS_BITMAPS)
734         do {
735                 int slot;
736                 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);
737                 for (slot = 0; slot < klass_id->data.layout.slots; slot ++) {
738                         if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
739                                 if (klass_id->data.bitmap.compact & (((guint64)1) << slot)) {
740                                         printf (" 1");
741                                 } else {
742                                         printf (" 0");
743                                 }
744                         } else {
745                                 if (klass_id->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
746                                         printf (" 1");
747                                 } else {
748                                         printf (" 0");
749                                 }
750 ;                       }
751                         
752                 }
753                 printf (" }\n");
754                 
755         } while (0);
756 #endif
757 }
758
759 static MethodIdMappingElement*
760 method_id_mapping_element_new (MonoMethod *method) {
761         MethodIdMappingElement *result = g_new (MethodIdMappingElement, 1);
762         char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
763         
764         result->name = g_strdup_printf ("%s (%s)", mono_method_get_name (method), signature);
765         g_free (signature);
766         result->method = method;
767         result->next_unwritten = profiler->methods->unwritten;
768         profiler->methods->unwritten = result;
769         result->id = profiler->methods->next_id;
770         profiler->methods->next_id ++;
771         g_hash_table_insert (profiler->methods->table, method, result);
772         
773         result->data.code_start = NULL;
774         result->data.code_size = 0;
775         
776 #if (DEBUG_MAPPING_EVENTS)
777         printf ("Created new METHOD mapping element \"%s\" (%p)[%d]\n", result->name, method, result->id);
778 #endif
779         return result;
780 }
781
782
783 static void
784 method_id_mapping_element_destroy (gpointer element) {
785         MethodIdMappingElement *e = (MethodIdMappingElement*) element;
786         if (e->name)
787                 g_free (e->name);
788         g_free (element);
789 }
790
791 static void
792 class_id_mapping_element_destroy (gpointer element) {
793         ClassIdMappingElement *e = (ClassIdMappingElement*) element;
794         if (e->name)
795                 g_free (e->name);
796         if ((e->data.layout.slots != CLASS_LAYOUT_NOT_INITIALIZED) && (e->data.layout.slots > CLASS_LAYOUT_PACKED_BITMAP_SIZE))
797                 g_free (e->data.bitmap.extended);
798         g_free (element);
799 }
800
801 static MethodIdMapping*
802 method_id_mapping_new (void) {
803         MethodIdMapping *result = g_new (MethodIdMapping, 1);
804         //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, method_id_mapping_element_destroy);
805         result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, method_id_mapping_element_destroy);
806         result->unwritten = NULL;
807         result->next_id = 1;
808         return result;
809 }
810
811 static ClassIdMapping*
812 class_id_mapping_new (void) {
813         ClassIdMapping *result = g_new (ClassIdMapping, 1);
814         //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, class_id_mapping_element_destroy);
815         result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, class_id_mapping_element_destroy);
816         result->unwritten = NULL;
817         result->next_id = 1;
818         return result;
819 }
820
821 static void
822 method_id_mapping_destroy (MethodIdMapping *map) {
823         g_hash_table_destroy (map->table);
824         g_free (map);
825 }
826
827 static void
828 class_id_mapping_destroy (ClassIdMapping *map) {
829         g_hash_table_destroy (map->table);
830         g_free (map);
831 }
832
833 static void
834 unmanaged_function_new (ProfilerUnmanagedFunctions *functions, Dl_info *dl_info) {
835         ProfilerUnmanagedFunction *function = g_new (ProfilerUnmanagedFunction, 1);
836         function->id = functions->next_id;
837         functions->next_id ++;
838         function->hits = 1;
839         function->next_unwritten = functions->unwritten_queue;
840         functions->unwritten_queue = function;
841         function->name = g_strdup_printf ("[%s]:%s", dl_info->dli_fname, dl_info->dli_sname);
842         g_hash_table_insert (functions->table, dl_info->dli_saddr, function);
843 }
844
845 static void
846 unmanaged_function_destroy (gpointer element) {
847         ProfilerUnmanagedFunction *function = (ProfilerUnmanagedFunction*) element;
848         if (function->name) {
849                 g_free (function->name);
850                 function->name = NULL;
851         }
852         g_free (function);
853 }
854
855 static gboolean
856 unmanaged_function_hit (ProfilerUnmanagedFunctions *functions, gpointer address) {
857         Dl_info dl_info;
858         if (dladdr (address, &dl_info) && (dl_info.dli_saddr != NULL) && (dl_info.dli_fname != NULL)) {
859                 ProfilerUnmanagedFunction *function = g_hash_table_lookup (functions->table, dl_info.dli_saddr);
860                 
861                 if (function != NULL) {
862                         if (function->next_unwritten != NULL) {
863                                 function->hits ++;
864                         } else {
865                                 function->hits = 1;
866                                 function->next_unwritten = functions->unwritten_queue;
867                                 functions->unwritten_queue = function;
868                         }
869                 } else {
870                         unmanaged_function_new (functions, &dl_info);
871                 }
872                 
873                 return TRUE;
874         } else {
875                 return FALSE;
876         }
877 }
878
879 static void
880 unmanaged_functions_init (ProfilerUnmanagedFunctions *functions) {
881         functions->next_id = 1;
882         functions->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, unmanaged_function_destroy);
883         functions->unwritten_queue_end = &(functions->actual_unwritten_queue_end);
884         functions->unwritten_queue = functions->unwritten_queue_end;
885         functions->actual_unwritten_queue_end.hits = 0;
886         functions->actual_unwritten_queue_end.id = 0;
887         functions->actual_unwritten_queue_end.name = NULL;
888         functions->actual_unwritten_queue_end.next_unwritten = NULL;
889 }
890
891 static void
892 unmanaged_functions_dispose (ProfilerUnmanagedFunctions *functions) {
893         functions->next_id = 0;
894         g_hash_table_destroy (functions->table);
895         functions->table = NULL;
896         functions->unwritten_queue = NULL;
897 }
898
899 #if (DEBUG_LOAD_EVENTS)
900 static void
901 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element);
902 #endif
903
904 static LoadedElement*
905 loaded_element_load_start (GHashTable *table, gpointer item) {
906         LoadedElement *element = g_new0 (LoadedElement, 1);
907 #if (DEBUG_LOAD_EVENTS)
908         print_load_event ("LOAD START", table, item, element);
909 #endif
910         MONO_PROFILER_GET_CURRENT_COUNTER (element->load_start_counter);
911         g_hash_table_insert (table, item, element);
912         return element;
913 }
914
915 static LoadedElement*
916 loaded_element_load_end (GHashTable *table, gpointer item, char *name) {
917         LoadedElement *element = g_hash_table_lookup (table, item);
918 #if (DEBUG_LOAD_EVENTS)
919         print_load_event ("LOAD END", table, item, element);
920 #endif
921         g_assert (element != NULL);
922         MONO_PROFILER_GET_CURRENT_COUNTER (element->load_end_counter);
923         element->name = name;
924         element->loaded = TRUE;
925         return element;
926 }
927
928 static LoadedElement*
929 loaded_element_unload_start (GHashTable *table, gpointer item) {
930         LoadedElement *element = g_hash_table_lookup (table, item);
931 #if (DEBUG_LOAD_EVENTS)
932         print_load_event ("UNLOAD START", table, item, element);
933 #endif
934         g_assert (element != NULL);
935         MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_start_counter);
936         return element;
937 }
938
939 static LoadedElement*
940 loaded_element_unload_end (GHashTable *table, gpointer item) {
941         LoadedElement *element = g_hash_table_lookup (table, item);
942 #if (DEBUG_LOAD_EVENTS)
943         print_load_event ("UNLOAD END", table, item, element);
944 #endif
945         g_assert (element != NULL);
946         MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_end_counter);
947         element->unloaded = TRUE;
948         return element;
949 }
950
951
952 static void
953 loaded_element_destroy (gpointer element) {
954         if (((LoadedElement*)element)->name)
955                 g_free (((LoadedElement*)element)->name);
956         g_free (element);
957 }
958
959 #if (DEBUG_LOAD_EVENTS)
960 static void
961 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element) {
962         const char* item_name;
963         char* item_info;
964         
965         if (table == profiler->loaded_assemblies) {
966                 //item_info = g_strdup_printf("ASSEMBLY %p (dynamic %d)", item, mono_image_is_dynamic (mono_assembly_get_image((MonoAssembly*)item)));
967                 item_info = g_strdup_printf("ASSEMBLY %p", item);
968         } else if (table == profiler->loaded_modules) {
969                 //item_info = g_strdup_printf("MODULE %p (dynamic %d)", item, mono_image_is_dynamic ((MonoImage*)item));
970                 item_info = g_strdup_printf("MODULE %p", item);
971         } else if (table == profiler->loaded_appdomains) {
972                 item_info = g_strdup_printf("APPDOMAIN %p (id %d)", item, mono_domain_get_id ((MonoDomain*)item));
973         } else {
974                 item_info = NULL;
975                 g_assert_not_reached ();
976         }
977         
978         if (element != NULL) {
979                 item_name = element->name;
980         } else {
981                 item_name = "<NULL>";
982         }
983         
984         printf ("%s EVENT for %s (%s)\n", event_name, item_info, item_name);
985         g_free (item_info);
986 }
987 #endif
988
989 static void
990 profiler_heap_shot_object_buffers_destroy (ProfilerHeapShotObjectBuffer *buffer) {
991         while (buffer != NULL) {
992                 ProfilerHeapShotObjectBuffer *next = buffer->next;
993 #if DEBUG_HEAP_PROFILER
994                 printf ("profiler_heap_shot_object_buffers_destroy: destroyed buffer %p (%p-%p)\n", buffer, & (buffer->buffer [0]), buffer->end);
995 #endif
996                 g_free (buffer);
997                 buffer = next;
998         }
999 }
1000
1001 static ProfilerHeapShotObjectBuffer*
1002 profiler_heap_shot_object_buffer_new (ProfilerPerThreadData *data) {
1003         ProfilerHeapShotObjectBuffer *buffer;
1004         ProfilerHeapShotObjectBuffer *result = g_new (ProfilerHeapShotObjectBuffer, 1);
1005         result->next_free_slot = & (result->buffer [0]);
1006         result->end = & (result->buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE]);
1007         result->first_unprocessed_slot = & (result->buffer [0]);
1008         result->next = data->heap_shot_object_buffers;
1009         data->heap_shot_object_buffers = result;
1010 #if DEBUG_HEAP_PROFILER
1011         printf ("profiler_heap_shot_object_buffer_new: created buffer %p (%p-%p)\n", result, result->next_free_slot, result->end);
1012 #endif
1013         for (buffer = result; buffer != NULL; buffer = buffer->next) {
1014                 ProfilerHeapShotObjectBuffer *last = buffer->next;
1015                 if ((last != NULL) && (last->first_unprocessed_slot == last->end)) {
1016                         buffer->next = NULL;
1017                         profiler_heap_shot_object_buffers_destroy (last);
1018                 }
1019         }
1020         
1021         return result;
1022 }
1023
1024 static ProfilerHeapShotWriteJob*
1025 profiler_heap_shot_write_job_new (gboolean heap_shot_was_signalled) {
1026         ProfilerHeapShotWriteJob *job = g_new (ProfilerHeapShotWriteJob, 1);
1027         job->next = NULL;
1028         job->next_unwritten = NULL;
1029         job->buffers = g_new (ProfilerHeapShotWriteBuffer, 1);
1030         job->buffers->next = NULL;
1031         job->last_next = & (job->buffers->next);
1032         job->start = & (job->buffers->buffer [0]);
1033         job->cursor = job->start;
1034         job->end = & (job->buffers->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1035         job->full_buffers = 0;
1036         job->heap_shot_was_signalled = heap_shot_was_signalled;
1037 #if DEBUG_HEAP_PROFILER
1038         printf ("profiler_heap_shot_write_job_new: created job %p with buffer %p(%p-%p)\n", job, job->buffers, job->start, job->end);
1039 #endif
1040         return job;
1041 }
1042
1043 static void
1044 profiler_heap_shot_write_job_add_buffer (ProfilerHeapShotWriteJob *job, gpointer value) {
1045         ProfilerHeapShotWriteBuffer *buffer = g_new (ProfilerHeapShotWriteBuffer, 1);
1046         buffer->next = NULL;
1047         *(job->last_next) = buffer;
1048         job->last_next = & (buffer->next);
1049         job->full_buffers ++;
1050         buffer->buffer [0] = value;
1051         job->start = & (buffer->buffer [0]);
1052         job->cursor = & (buffer->buffer [1]);
1053         job->end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1054 #if DEBUG_HEAP_PROFILER
1055         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);
1056         do {
1057                 ProfilerHeapShotWriteBuffer *current_buffer;
1058                 for (current_buffer = job->buffers; current_buffer != NULL; current_buffer = current_buffer->next) {
1059                         printf ("profiler_heap_shot_write_job_add_buffer: now job %p has buffer %p\n", job, current_buffer);
1060                 }
1061         } while (0);
1062 #endif
1063 }
1064
1065 static void
1066 profiler_heap_shot_write_job_free_buffers (ProfilerHeapShotWriteJob *job) {
1067         ProfilerHeapShotWriteBuffer *buffer = job->buffers;
1068         
1069         while (buffer != NULL) {
1070                 ProfilerHeapShotWriteBuffer *next = buffer->next;
1071 #if DEBUG_HEAP_PROFILER
1072                 printf ("profiler_heap_shot_write_job_free_buffers: in job %p, freeing buffer %p\n", job, buffer);
1073 #endif
1074                 g_free (buffer);
1075                 buffer = next;
1076         }
1077         
1078         job->buffers = NULL;
1079 }
1080
1081 static void
1082 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job);
1083
1084 static void
1085 profiler_process_heap_shot_write_jobs (void) {
1086         gboolean done = FALSE;
1087         
1088         while (!done) {
1089                 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1090                 ProfilerHeapShotWriteJob *previous_job = NULL;
1091                 ProfilerHeapShotWriteJob *next_job;
1092                 
1093                 done = TRUE;
1094                 while (current_job != NULL) {
1095                         next_job = current_job->next_unwritten;
1096                         
1097                         if (next_job != NULL) {
1098                                 if (current_job->buffers != NULL) {
1099                                         done = FALSE;
1100                                 }
1101                                 if (next_job->buffers == NULL) {
1102                                         current_job->next_unwritten = NULL;
1103                                         next_job = NULL;
1104                                 }
1105                         } else {
1106                                 if (current_job->buffers != NULL) {
1107                                         LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: writing...");
1108                                         profiler_heap_shot_write_block (current_job);
1109                                         LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: done");
1110                                         if (previous_job != NULL) {
1111                                                 previous_job->next_unwritten = NULL;
1112                                         }
1113                                 }
1114                         }
1115                         
1116                         previous_job = current_job;
1117                         current_job = next_job;
1118                 }
1119         }
1120 }
1121
1122 static void
1123 profiler_free_heap_shot_write_jobs (void) {
1124         ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1125         ProfilerHeapShotWriteJob *next_job;
1126         
1127         if (current_job != NULL) {
1128                 while (current_job->next_unwritten != NULL) {
1129 #if DEBUG_HEAP_PROFILER
1130                         printf ("profiler_free_heap_shot_write_jobs: job %p must not be freed\n", current_job);
1131 #endif
1132                         current_job = current_job->next_unwritten;
1133                 }
1134                 
1135                 next_job = current_job->next;
1136                 current_job->next = NULL;
1137                 current_job = next_job;
1138                 
1139                 while (current_job != NULL) {
1140 #if DEBUG_HEAP_PROFILER
1141                         printf ("profiler_free_heap_shot_write_jobs: job %p will be freed\n", current_job);
1142 #endif
1143                         next_job = current_job->next;
1144                         g_free (current_job);
1145                         current_job = next_job;
1146                 }
1147         }
1148 }
1149
1150 static void
1151 profiler_destroy_heap_shot_write_jobs (void) {
1152         ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1153         ProfilerHeapShotWriteJob *next_job;
1154         
1155         while (current_job != NULL) {
1156                 next_job = current_job->next;
1157                 profiler_heap_shot_write_job_free_buffers (current_job);
1158                 g_free (current_job);
1159                 current_job = next_job;
1160         }
1161 }
1162
1163 static void
1164 profiler_add_heap_shot_write_job (ProfilerHeapShotWriteJob *job) {
1165         job->next = profiler->heap_shot_write_jobs;
1166         job->next_unwritten = job->next;
1167         profiler->heap_shot_write_jobs = job;
1168 #if DEBUG_HEAP_PROFILER
1169         printf ("profiler_add_heap_shot_write_job: added job %p\n", job);
1170 #endif
1171 }
1172
1173 #if DEBUG_HEAP_PROFILER
1174 #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)
1175 #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)
1176 #else
1177 #define STORE_ALLOCATED_OBJECT_MESSAGE1(d,o)
1178 #define STORE_ALLOCATED_OBJECT_MESSAGE2(d,o)
1179 #endif
1180 #define STORE_ALLOCATED_OBJECT(d,o) do {\
1181         if ((d)->heap_shot_object_buffers->next_free_slot < (d)->heap_shot_object_buffers->end) {\
1182                 STORE_ALLOCATED_OBJECT_MESSAGE1 ((d), (o));\
1183                 *((d)->heap_shot_object_buffers->next_free_slot) = (o);\
1184                 (d)->heap_shot_object_buffers->next_free_slot ++;\
1185         } else {\
1186                 ProfilerHeapShotObjectBuffer *buffer = profiler_heap_shot_object_buffer_new (d);\
1187                 STORE_ALLOCATED_OBJECT_MESSAGE2 ((d), (o));\
1188                 *((buffer)->next_free_slot) = (o);\
1189                 (buffer)->next_free_slot ++;\
1190         }\
1191 } while (0)
1192
1193 static ProfilerPerThreadData*
1194 profiler_per_thread_data_new (guint32 buffer_size)
1195 {
1196         ProfilerPerThreadData *data = g_new (ProfilerPerThreadData, 1);
1197
1198         data->events = g_new0 (ProfilerEventData, buffer_size);
1199         data->next_free_event = data->events;
1200         data->end_event = data->events + (buffer_size - 1);
1201         data->first_unwritten_event = data->events;
1202         data->first_unmapped_event = data->events;
1203         MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
1204         data->last_event_counter = data->start_event_counter;
1205         data->thread_id = CURRENT_THREAD_ID ();
1206         data->heap_shot_object_buffers = NULL;
1207         if ((profiler->action_flags.unreachable_objects == TRUE) || (profiler->action_flags.heap_shot == TRUE)) {
1208                 profiler_heap_shot_object_buffer_new (data);
1209         }
1210         return data;
1211 }
1212
1213 static void
1214 profiler_per_thread_data_destroy (ProfilerPerThreadData *data) {
1215         g_free (data->events);
1216         profiler_heap_shot_object_buffers_destroy (data->heap_shot_object_buffers);
1217         g_free (data);
1218 }
1219
1220 static ProfilerStatisticalData*
1221 profiler_statistical_data_new (guint32 buffer_size)
1222 {
1223         ProfilerStatisticalData *data = g_new (ProfilerStatisticalData, 1);
1224
1225         data->addresses = g_new0 (gpointer, buffer_size);
1226         data->next_free_index = 0;
1227         data->end_index = buffer_size;
1228         data->first_unwritten_index = 0;
1229         
1230         return data;
1231 }
1232
1233 static void
1234 profiler_statistical_data_destroy (ProfilerStatisticalData *data) {
1235         g_free (data->addresses);
1236         g_free (data);
1237 }
1238
1239 static void
1240 profiler_add_write_buffer (void) {
1241         if (profiler->current_write_buffer->next == NULL) {
1242                 profiler->current_write_buffer->next = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
1243                 profiler->current_write_buffer->next->next = NULL;
1244                 
1245                 //printf ("Added next buffer %p, to buffer %p\n", profiler->current_write_buffer->next, profiler->current_write_buffer);
1246                 
1247         }
1248         profiler->current_write_buffer = profiler->current_write_buffer->next;
1249         profiler->current_write_position = 0;
1250         profiler->full_write_buffers ++;
1251 }
1252
1253 static void
1254 profiler_free_write_buffers (void) {
1255         ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1256         while (current_buffer != NULL) {
1257                 ProfilerFileWriteBuffer *next_buffer = current_buffer->next;
1258                 
1259                 //printf ("Freeing write buffer %p, next is %p\n", current_buffer, next_buffer);
1260                 
1261                 g_free (current_buffer);
1262                 current_buffer = next_buffer;
1263         }
1264 }
1265
1266 #define WRITE_BYTE(b) do {\
1267         if (profiler->current_write_position >= PROFILER_FILE_WRITE_BUFFER_SIZE) {\
1268                 profiler_add_write_buffer ();\
1269         }\
1270         profiler->current_write_buffer->buffer [profiler->current_write_position] = (b);\
1271         profiler->current_write_position ++;\
1272 } while (0)
1273
1274
1275 static void
1276 write_current_block (guint16 code) {
1277         guint32 size = (profiler->full_write_buffers * PROFILER_FILE_WRITE_BUFFER_SIZE) + profiler->current_write_position;
1278         ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1279         guint8 header [6];
1280         
1281         header [0] = code & 0xff;
1282         header [1] = (code >> 8) & 0xff;
1283         header [2] = size & 0xff;
1284         header [3] = (size >> 8) & 0xff;
1285         header [4] = (size >> 16) & 0xff;
1286         header [5] = (size >> 24) & 0xff;
1287         
1288         WRITE_BUFFER (& (header [0]), 6);
1289         
1290         while ((current_buffer != NULL) && (profiler->full_write_buffers > 0)) {
1291                 WRITE_BUFFER (& (current_buffer->buffer [0]), PROFILER_FILE_WRITE_BUFFER_SIZE);
1292                 profiler->full_write_buffers --;
1293                 current_buffer = current_buffer->next;
1294         }
1295         if (profiler->current_write_position > 0) {
1296                 WRITE_BUFFER (& (current_buffer->buffer [0]), profiler->current_write_position);
1297         }
1298         FLUSH_FILE ();
1299         
1300         profiler->current_write_buffer = profiler->write_buffers;
1301         profiler->current_write_position = 0;
1302         profiler->full_write_buffers = 0;
1303 }
1304
1305
1306 #define SEVEN_BITS_MASK (0x7f)
1307 #define EIGHT_BIT_MASK (0x80)
1308
1309 static void
1310 write_uint32 (guint32 value) {
1311         while (value > SEVEN_BITS_MASK) {
1312                 WRITE_BYTE (value & SEVEN_BITS_MASK);
1313                 value >>= 7;
1314         }
1315         WRITE_BYTE (value | EIGHT_BIT_MASK);
1316 }
1317 static void
1318 write_uint64 (guint64 value) {
1319         while (value > SEVEN_BITS_MASK) {
1320                 WRITE_BYTE (value & SEVEN_BITS_MASK);
1321                 value >>= 7;
1322         }
1323         WRITE_BYTE (value | EIGHT_BIT_MASK);
1324 }
1325 static void
1326 write_string (const char *string) {
1327         while (*string != 0) {
1328                 WRITE_BYTE (*string);
1329                 string ++;
1330         }
1331         WRITE_BYTE (0);
1332 }
1333
1334 #if DEBUG_HEAP_PROFILER
1335 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c) printf ("WRITE_HEAP_SHOT_JOB_VALUE: writing value %p at cursor %p\n", (v), (c))
1336 #else
1337 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c)
1338 #endif
1339 #define WRITE_HEAP_SHOT_JOB_VALUE(j,v) do {\
1340         if ((j)->cursor < (j)->end) {\
1341                 WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE ((v), ((j)->cursor));\
1342                 *((j)->cursor) = (v);\
1343                 (j)->cursor ++;\
1344         } else {\
1345                 profiler_heap_shot_write_job_add_buffer (j, v);\
1346         }\
1347 } while (0)
1348
1349 #undef GUINT_TO_POINTER
1350 #define GUINT_TO_POINTER(u) ((void*)(guint64)(u))
1351 #undef GPOINTER_TO_UINT
1352 #define GPOINTER_TO_UINT(p) ((guint64)(void*)(p))
1353
1354 #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)))
1355
1356 #if DEBUG_HEAP_PROFILER
1357 #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)
1358 #else
1359 #define UPDATE_JOB_BUFFER_CURSOR_MESSAGE()
1360 #endif
1361 #define UPDATE_JOB_BUFFER_CURSOR() do {\
1362         cursor++;\
1363         if (cursor >= end) {\
1364                 buffer = buffer->next;\
1365                 if (buffer != NULL) {\
1366                         cursor = & (buffer->buffer [0]);\
1367                         if (buffer->next != NULL) {\
1368                                 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);\
1369                         } else {\
1370                                 end = job->cursor;\
1371                         }\
1372                 } else {\
1373                         cursor = NULL;\
1374                 }\
1375         }\
1376         UPDATE_JOB_BUFFER_CURSOR_MESSAGE ();\
1377 } while (0)
1378
1379 static void
1380 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job) {
1381         ProfilerHeapShotWriteBuffer *buffer;
1382         gpointer* cursor;
1383         gpointer* end;
1384         guint64 start_counter;
1385         guint64 start_time;
1386         guint64 end_counter;
1387         guint64 end_time;
1388         
1389         write_uint64 (job->start_counter);
1390         write_uint64 (job->start_time);
1391         write_uint64 (job->end_counter);
1392         write_uint64 (job->end_time);
1393         
1394         MONO_PROFILER_GET_CURRENT_COUNTER (start_counter);
1395         MONO_PROFILER_GET_CURRENT_TIME (start_time);
1396         write_uint64 (start_counter);
1397         write_uint64 (start_time);
1398 #if DEBUG_HEAP_PROFILER
1399         printf ("profiler_heap_shot_write_block: working on job %p...\n", job);
1400 #endif
1401         buffer = job->buffers;
1402         cursor = & (buffer->buffer [0]);
1403         if (buffer->next != NULL) {
1404                 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1405         } else {
1406                 end = job->cursor;
1407         }
1408         if (cursor >= end) {
1409                 cursor = NULL;
1410         }
1411 #if DEBUG_HEAP_PROFILER
1412         printf ("profiler_heap_shot_write_block: in job %p, starting at buffer %p and cursor %p\n", job, buffer, cursor);
1413 #endif
1414         while (cursor != NULL) {
1415                 gpointer value = *cursor;
1416                 HeapProfilerJobValueCode code = GPOINTER_TO_UINT (value) & HEAP_CODE_MASK;
1417 #if DEBUG_HEAP_PROFILER
1418                 printf ("profiler_heap_shot_write_block: got value %p and code %d\n", value, code);
1419 #endif
1420                 
1421                 UPDATE_JOB_BUFFER_CURSOR ();
1422                 if (code == HEAP_CODE_FREE_OBJECT_CLASS) {
1423                         MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
1424                         //MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) % 4);
1425                         ClassIdMappingElement *class_id;
1426                         guint32 size;
1427                         
1428                         class_id = class_id_mapping_element_get (klass);
1429                         if (class_id == NULL) {
1430                                 printf ("profiler_heap_shot_write_block: unknown class %p", klass);
1431                         }
1432                         g_assert (class_id != NULL);
1433                         write_uint32 ((class_id->id << 2) | HEAP_CODE_FREE_OBJECT_CLASS);
1434                         
1435                         size = GPOINTER_TO_UINT (*cursor);
1436                         UPDATE_JOB_BUFFER_CURSOR ();
1437                         write_uint32 (size);
1438 #if DEBUG_HEAP_PROFILER
1439                         printf ("profiler_heap_shot_write_block: wrote unreachable object of class %p (id %d, size %d)\n", klass, class_id->id, size);
1440 #endif
1441                 } else if (code == HEAP_CODE_OBJECT) {
1442                         MonoObject *object = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
1443                         MonoClass *klass = mono_object_get_class (object);
1444                         ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
1445                         guint32 size = mono_object_get_size (object);
1446                         guint32 references = GPOINTER_TO_UINT (*cursor);
1447                         UPDATE_JOB_BUFFER_CURSOR ();
1448                         
1449                         if (class_id == NULL) {
1450                                 printf ("profiler_heap_shot_write_block: unknown class %p", klass);
1451                         }
1452                         g_assert (class_id != NULL);
1453                         
1454                         write_uint64 (GPOINTER_TO_UINT (object));
1455                         write_uint32 (class_id->id);
1456                         write_uint32 (size);
1457                         write_uint32 (references);
1458 #if DEBUG_HEAP_PROFILER
1459                         printf ("profiler_heap_shot_write_block: writing object %p (references %d)\n", value, references);
1460 #endif
1461                         
1462                         while (references > 0) {
1463                                 gpointer reference = *cursor;
1464                                 write_uint64 (GPOINTER_TO_UINT (reference));
1465                                 UPDATE_JOB_BUFFER_CURSOR ();
1466                                 references --;
1467 #if DEBUG_HEAP_PROFILER
1468                                 printf ("profiler_heap_shot_write_block:   inside object %p, wrote reference %p)\n", value, reference);
1469 #endif
1470                         }
1471                 } else {
1472 #if DEBUG_HEAP_PROFILER
1473                         printf ("profiler_heap_shot_write_block: unknown code %d in value %p\n", code, value);
1474 #endif
1475                         g_assert_not_reached ();
1476                 }
1477         }
1478         write_uint32 (0);
1479         
1480         MONO_PROFILER_GET_CURRENT_COUNTER (end_counter);
1481         MONO_PROFILER_GET_CURRENT_TIME (end_time);
1482         write_uint64 (end_counter);
1483         write_uint64 (end_time);
1484         
1485         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_HEAP);
1486         
1487         profiler_heap_shot_write_job_free_buffers (job);
1488 #if DEBUG_HEAP_PROFILER
1489         printf ("profiler_heap_shot_write_block: work on job %p done.\n", job);
1490 #endif
1491 }
1492
1493 static void
1494 write_element_load_block (LoadedElement *element, guint8 kind, gsize thread_id) {
1495         WRITE_BYTE (kind);
1496         write_uint64 (element->load_start_counter);
1497         write_uint64 (element->load_end_counter);
1498         write_uint64 (thread_id);
1499         write_string (element->name);
1500         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_LOADED);
1501         element->load_written = TRUE;
1502 }
1503
1504 static void
1505 write_element_unload_block (LoadedElement *element, guint8 kind, gsize thread_id) {
1506         WRITE_BYTE (kind);
1507         write_uint64 (element->unload_start_counter);
1508         write_uint64 (element->unload_end_counter);
1509         write_uint64 (thread_id);
1510         write_string (element->name);
1511         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED);
1512         element->unload_written = TRUE;
1513 }
1514
1515 static void
1516 write_clock_data (void) {
1517         guint64 counter;
1518         guint64 time;
1519         
1520         MONO_PROFILER_GET_CURRENT_COUNTER (counter);
1521         MONO_PROFILER_GET_CURRENT_TIME (time);
1522         
1523         write_uint64 (counter);
1524         write_uint64 (time);
1525 }
1526
1527 static void
1528 write_mapping_block (gsize thread_id) {
1529         ClassIdMappingElement *current_class;
1530         MethodIdMappingElement *current_method;
1531         
1532         if ((profiler->classes->unwritten == NULL) && (profiler->methods->unwritten == NULL))
1533                 return;
1534         
1535 #if (DEBUG_MAPPING_EVENTS)
1536         printf ("[write_mapping_block][TID %ld] START\n", thread_id);
1537 #endif
1538         
1539         write_clock_data ();
1540         write_uint64 (thread_id);
1541         
1542         for (current_class = profiler->classes->unwritten; current_class != NULL; current_class = current_class->next_unwritten) {
1543                 write_uint32 (current_class->id);
1544                 write_string (current_class->name);
1545 #if (DEBUG_MAPPING_EVENTS)
1546                 printf ("mapping CLASS (%d => %s)\n", current_class->id, current_class->name);
1547 #endif
1548                 g_free (current_class->name);
1549                 current_class->name = NULL;
1550         }
1551         write_uint32 (0);
1552         profiler->classes->unwritten = NULL;
1553         
1554         for (current_method = profiler->methods->unwritten; current_method != NULL; current_method = current_method->next_unwritten) {
1555                 MonoMethod *method = current_method->method;
1556                 MonoClass *klass = mono_method_get_class (method);
1557                 ClassIdMappingElement *class_element = class_id_mapping_element_get (klass);
1558                 g_assert (class_element != NULL);
1559                 write_uint32 (current_method->id);
1560                 write_uint32 (class_element->id);
1561                 write_string (current_method->name);
1562 #if (DEBUG_MAPPING_EVENTS)
1563                 printf ("mapping METHOD ([%d]%d => %s)\n", class_element?class_element->id:1, current_method->id, current_method->name);
1564 #endif
1565                 g_free (current_method->name);
1566                 current_method->name = NULL;
1567         }
1568         write_uint32 (0);
1569         profiler->methods->unwritten = NULL;
1570         
1571         write_clock_data ();
1572         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_MAPPING);
1573         
1574 #if (DEBUG_MAPPING_EVENTS)
1575         printf ("[write_mapping_block][TID %ld] END\n", thread_id);
1576 #endif
1577 }
1578
1579 static guint64
1580 get_extended_event_value (ProfilerEventData *event, ProfilerEventData *next) {
1581         guint64 result = next->data.number;
1582         result |= (((guint64) event->value) << 32);
1583         return result;
1584 }
1585
1586 typedef enum {
1587         MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER = 1,
1588         MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_IMPLICIT = 2,
1589         MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT = 3,
1590         MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION = 4,
1591         MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT = 5,
1592         MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT = 6,
1593         MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT = 7
1594 } MonoProfilerPackedEventCode;
1595 #define MONO_PROFILER_PACKED_EVENT_CODE_BITS 3
1596 #define MONO_PROFILER_PACKED_EVENT_DATA_BITS (8-MONO_PROFILER_PACKED_EVENT_CODE_BITS)
1597 #define MONO_PROFILER_PACKED_EVENT_DATA_MASK ((1<<MONO_PROFILER_PACKED_EVENT_DATA_BITS)-1)
1598
1599 #define MONO_PROFILER_EVENT_MAKE_PACKED_CODE(result,data,base) do {\
1600         result = ((base)|((data & MONO_PROFILER_PACKED_EVENT_DATA_MASK) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
1601         data >>= MONO_PROFILER_PACKED_EVENT_DATA_BITS;\
1602 } while (0)
1603 #define MONO_PROFILER_EVENT_MAKE_FULL_CODE(result,code,kind,base) do {\
1604         result = ((base)|((((kind)<<4) | (code)) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
1605 } while (0)
1606
1607 static ProfilerEventData*
1608 write_event (ProfilerEventData *event) {
1609         ProfilerEventData *next = event + 1;
1610         gboolean write_event_value = TRUE;
1611         guint8 event_code;
1612         guint64 event_data;
1613         guint64 event_value;
1614
1615         event_value = event->value;
1616         if (event_value > MAX_EVENT_VALUE) {
1617                 event_value = get_extended_event_value (event, next);
1618                 next ++;
1619         }
1620         
1621         if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
1622                 MethodIdMappingElement *element = method_id_mapping_element_get (event->data.address);
1623                 g_assert (element != NULL);
1624                 event_data = element->id;
1625                 
1626                 if (event->code == MONO_PROFILER_EVENT_METHOD_CALL) {
1627                         if (event->kind == MONO_PROFILER_EVENT_KIND_START) {
1628                                 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER);
1629                         } else {
1630                                 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT);
1631                         }
1632                 } else {
1633                         MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT); 
1634                 }
1635         } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
1636                 ClassIdMappingElement *element = class_id_mapping_element_get (event->data.address);
1637                 g_assert (element != NULL);
1638                 event_data = element->id;
1639                 
1640                 if (event->code == MONO_PROFILER_EVENT_CLASS_ALLOCATION) {
1641                         MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION);
1642                 } else {
1643                         MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT);
1644                 }
1645         } else {
1646                 event_data = event->data.number;
1647                 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
1648         }
1649         
1650 #if (DEBUG_LOGGING_PROFILER)
1651         EVENT_MARK ();
1652         printf ("writing EVENT[%p] data_type:%d, kind:%d, code:%d (%d:%ld:%ld)\n", event,
1653                         event->data_type, event->kind, event->code,
1654                         event_code, event_data, event_value);
1655 #endif
1656         
1657         WRITE_BYTE (event_code);
1658         write_uint64 (event_data);
1659         if (write_event_value) {
1660                 write_uint64 (event_value);
1661         }
1662         
1663         return next;
1664 }
1665
1666 static void
1667 write_thread_data_block (ProfilerPerThreadData *data) {
1668         ProfilerEventData *start = data->first_unwritten_event;
1669         ProfilerEventData *end = data->first_unmapped_event;
1670         
1671         if (start == end)
1672                 return;
1673         
1674         write_clock_data ();
1675         write_uint64 (data->thread_id);
1676         
1677         write_uint64 (data->start_event_counter);
1678         
1679         while (start < end) {
1680                 start = write_event (start);
1681         }
1682         WRITE_BYTE (0);
1683         data->first_unwritten_event = end;
1684         
1685         write_clock_data ();
1686         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_EVENTS);
1687 }
1688
1689 static ProfilerExecutableMemoryRegionData*
1690 profiler_executable_memory_region_new (gpointer *start, gpointer *end, guint32 file_offset, char *file_name, guint32 id) {
1691         ProfilerExecutableMemoryRegionData *result = g_new (ProfilerExecutableMemoryRegionData, 1);
1692         result->start = start;
1693         result->end = end;
1694         result->file_offset = file_offset;
1695         result->file_name = g_strdup (file_name);
1696         result->id = id;
1697         result->is_new = TRUE;
1698         return result;
1699 }
1700
1701 static void
1702 profiler_executable_memory_region_destroy (ProfilerExecutableMemoryRegionData *data) {
1703         if (data->file_name != NULL) {
1704                 g_free (data->file_name);
1705         }
1706         g_free (data);
1707 }
1708
1709 static ProfilerExecutableMemoryRegions*
1710 profiler_executable_memory_regions_new (int next_id) {
1711         ProfilerExecutableMemoryRegions *result = g_new (ProfilerExecutableMemoryRegions, 1);
1712         result->regions = g_new0 (ProfilerExecutableMemoryRegionData*, 32);
1713         result->regions_capacity = 32;
1714         result->regions_count = 0;
1715         result->next_id = next_id;
1716         return result;
1717 }
1718
1719 static void
1720 profiler_executable_memory_regions_destroy (ProfilerExecutableMemoryRegions *regions) {
1721         int i;
1722         
1723         for (i = 0; i < regions->regions_count; i++) {
1724                 profiler_executable_memory_region_destroy (regions->regions [i]);
1725         }
1726         g_free (regions->regions);
1727         g_free (regions);
1728 }
1729
1730 static ProfilerExecutableMemoryRegionData*
1731 find_address_region (ProfilerExecutableMemoryRegions *regions, gpointer address) {
1732         int low_index = 0;
1733         int high_index = regions->regions_count;
1734         int middle_index = 0;
1735         ProfilerExecutableMemoryRegionData *middle_region = regions->regions [0];
1736         
1737         if ((regions->regions_count == 0) || (regions->regions [low_index]->start > address) || (regions->regions [high_index - 1]->end < address)) {
1738                 return NULL;
1739         }
1740         
1741         //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);
1742         
1743         while (low_index != high_index) {
1744                 middle_index = low_index + ((high_index - low_index) / 2);
1745                 middle_region = regions->regions [middle_index];
1746                 
1747                 //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);
1748                 
1749                 if (middle_region->start > address) {
1750                         if (middle_index > 0) {
1751                                 high_index = middle_index;
1752                         } else {
1753                                 return NULL;
1754                         }
1755                 } else if (middle_region->end < address) {
1756                         if (middle_index < regions->regions_count - 1) {
1757                                 low_index = middle_index + 1;
1758                         } else {
1759                                 return NULL;
1760                         }
1761                 } else {
1762                         return middle_region;
1763                 }
1764         }
1765         
1766         if ((middle_region == NULL) || (middle_region->start > address) || (middle_region->end < address)) {
1767                 return NULL;
1768         } else {
1769                 return middle_region;
1770         }
1771 }
1772
1773 static void
1774 append_region (ProfilerExecutableMemoryRegions *regions, gpointer *start, gpointer *end, guint32 file_offset, char *file_name) {
1775         if (regions->regions_count >= regions->regions_capacity) {
1776                 ProfilerExecutableMemoryRegionData **new_regions = g_new0 (ProfilerExecutableMemoryRegionData*, regions->regions_capacity * 2);
1777                 memcpy (new_regions, regions->regions, regions->regions_capacity * sizeof (ProfilerExecutableMemoryRegionData*));
1778                 g_free (regions->regions);
1779                 regions->regions = new_regions;
1780                 regions->regions_capacity = regions->regions_capacity * 2;
1781         }
1782         regions->regions [regions->regions_count] = profiler_executable_memory_region_new (start, end, file_offset, file_name, regions->next_id);
1783         regions->regions_count ++;
1784         regions->next_id ++;
1785 }
1786
1787 static void
1788 restore_region_ids (ProfilerExecutableMemoryRegions *old_regions, ProfilerExecutableMemoryRegions *new_regions) {
1789         int old_i;
1790         int new_i;
1791         
1792         for (old_i = 0; old_i < old_regions->regions_count; old_i++) {
1793                 ProfilerExecutableMemoryRegionData *old_region = old_regions->regions [old_i];
1794                 for (new_i = 0; new_i < new_regions->regions_count; new_i++) {
1795                         ProfilerExecutableMemoryRegionData *new_region = new_regions->regions [new_i];
1796                         if ((old_region->start == new_region->start) &&
1797                                         (old_region->end == new_region->end) &&
1798                                         (old_region->file_offset == new_region->file_offset) &&
1799                                         ! strcmp (old_region->file_name, new_region->file_name)) {
1800                                 new_region->is_new = FALSE;
1801                                 new_region->id = old_region->id;
1802                                 old_region->is_new = TRUE;
1803                         }
1804                 }
1805         }
1806 }
1807
1808 static int
1809 compare_regions (const void *a1, const void *a2) {
1810         ProfilerExecutableMemoryRegionData *r1 = * (ProfilerExecutableMemoryRegionData**) a1;
1811         ProfilerExecutableMemoryRegionData *r2 = * (ProfilerExecutableMemoryRegionData**) a2;
1812         return (r1->start < r2->start)? -1 : ((r1->start > r2->start)? 1 : 0);
1813 }
1814
1815 static void
1816 sort_regions (ProfilerExecutableMemoryRegions *regions) {
1817         qsort (regions->regions, regions->regions_count, sizeof (ProfilerExecutableMemoryRegionData *), compare_regions);
1818 }
1819
1820 //FIXME: make also Win32 and BSD variants
1821 #define MAPS_BUFFER_SIZE 4096
1822
1823 static gboolean
1824 update_regions_buffer (int fd, char *buffer) {
1825         ssize_t result = read (fd, buffer, MAPS_BUFFER_SIZE);
1826         
1827         if (result == MAPS_BUFFER_SIZE) {
1828                 return TRUE;
1829         } else if (result >= 0) {
1830                 *(buffer + result) = 0;
1831                 return FALSE;
1832         } else {
1833                 *buffer = 0;
1834                 return FALSE;
1835         }
1836 }
1837
1838 #define GOTO_NEXT_CHAR(c,b,fd) do {\
1839         (c)++;\
1840         if (((c) - (b) >= MAPS_BUFFER_SIZE) || ((*(c) == 0) && ((c) != (b)))) {\
1841                 update_regions_buffer ((fd), (b));\
1842                 (c) = (b);\
1843         }\
1844 } while (0);
1845
1846 static int hex_digit_value (char c) {
1847         if ((c >= '0') && (c <= '9')) {
1848                 return c - '0';
1849         } else if ((c >= 'a') && (c <= 'f')) {
1850                 return c - 'a';
1851         } else if ((c >= 'A') && (c <= 'F')) {
1852                 return c - 'A';
1853         } else {
1854                 return 0;
1855         }
1856 }
1857
1858 /*
1859  * Start address
1860  * -
1861  * End address
1862  * (space)
1863  * Permissions
1864  * Offset
1865  * (space)
1866  * Device
1867  * (space)
1868  * Inode
1869  * (space)
1870  * File
1871  * \n
1872  */
1873 typedef enum {
1874         MAP_LINE_PARSER_STATE_INVALID,
1875         MAP_LINE_PARSER_STATE_START_ADDRESS,
1876         MAP_LINE_PARSER_STATE_END_ADDRESS,
1877         MAP_LINE_PARSER_STATE_PERMISSIONS,
1878         MAP_LINE_PARSER_STATE_OFFSET,
1879         MAP_LINE_PARSER_STATE_DEVICE,
1880         MAP_LINE_PARSER_STATE_INODE,
1881         MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME,
1882         MAP_LINE_PARSER_STATE_FILENAME,
1883         MAP_LINE_PARSER_STATE_DONE
1884 } MapLineParserState;
1885
1886 const char *map_line_parser_state [] = {
1887         "INVALID",
1888         "START_ADDRESS",
1889         "END_ADDRESS",
1890         "PERMISSIONS",
1891         "OFFSET",
1892         "DEVICE",
1893         "INODE",
1894         "BLANK_BEFORE_FILENAME",
1895         "FILENAME",
1896         "DONE"
1897 };
1898
1899 static char*
1900 parse_map_line (ProfilerExecutableMemoryRegions *regions, int fd, char *buffer, char *current) {
1901         MapLineParserState state = MAP_LINE_PARSER_STATE_START_ADDRESS;
1902         gsize start_address = 0;
1903         gsize end_address = 0;
1904         guint32 offset = 0;
1905         char *start_filename = NULL;
1906         char *end_filename = NULL;
1907         gboolean is_executable = FALSE;
1908         gboolean done = FALSE;
1909         
1910         char c = *current;
1911         
1912         while (1) {
1913                 switch (state) {
1914                 case MAP_LINE_PARSER_STATE_START_ADDRESS:
1915                         if (isxdigit (c)) {
1916                                 start_address <<= 4;
1917                                 start_address |= hex_digit_value (c);
1918                         } else if (c == '-') {
1919                                 state = MAP_LINE_PARSER_STATE_END_ADDRESS;
1920                         } else {
1921                                 state = MAP_LINE_PARSER_STATE_INVALID;
1922                         }
1923                         break;
1924                 case MAP_LINE_PARSER_STATE_END_ADDRESS:
1925                         if (isxdigit (c)) {
1926                                 end_address <<= 4;
1927                                 end_address |= hex_digit_value (c);
1928                         } else if (isblank (c)) {
1929                                 state = MAP_LINE_PARSER_STATE_PERMISSIONS;
1930                         } else {
1931                                 state = MAP_LINE_PARSER_STATE_INVALID;
1932                         }
1933                         break;
1934                 case MAP_LINE_PARSER_STATE_PERMISSIONS:
1935                         if (c == 'x') {
1936                                 is_executable = TRUE;
1937                         } else if (isblank (c)) {
1938                                 state = MAP_LINE_PARSER_STATE_OFFSET;
1939                         } else if ((c != '-') && ! isalpha (c)) {
1940                                 state = MAP_LINE_PARSER_STATE_INVALID;
1941                         }
1942                         break;
1943                 case MAP_LINE_PARSER_STATE_OFFSET:
1944                         if (isxdigit (c)) {
1945                                 offset <<= 4;
1946                                 offset |= hex_digit_value (c);
1947                         } else if (isblank (c)) {
1948                                 state = MAP_LINE_PARSER_STATE_DEVICE;
1949                         } else {
1950                                 state = MAP_LINE_PARSER_STATE_INVALID;
1951                         }
1952                         break;
1953                 case MAP_LINE_PARSER_STATE_DEVICE:
1954                         if (isblank (c)) {
1955                                 state = MAP_LINE_PARSER_STATE_INODE;
1956                         } else if ((c != ':') && ! isxdigit (c)) {
1957                                 state = MAP_LINE_PARSER_STATE_INVALID;
1958                         }
1959                         break;
1960                 case MAP_LINE_PARSER_STATE_INODE:
1961                         if (isblank (c)) {
1962                                 state = MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME;
1963                         } else if (! isdigit (c)) {
1964                                 state = MAP_LINE_PARSER_STATE_INVALID;
1965                         }
1966                         break;
1967                 case MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME:
1968                         if (c == '/') {
1969                                 state = MAP_LINE_PARSER_STATE_FILENAME;
1970                                 start_filename = current;
1971                         } else if (! isblank (c)) {
1972                                 state = MAP_LINE_PARSER_STATE_INVALID;
1973                         }
1974                         break;
1975                 case MAP_LINE_PARSER_STATE_FILENAME:
1976                         if (c == '\n') {
1977                                 state = MAP_LINE_PARSER_STATE_DONE;
1978                                 done = TRUE;
1979                                 end_filename = current;
1980                         }
1981                         break;
1982                 case MAP_LINE_PARSER_STATE_DONE:
1983                         if (done && is_executable) {
1984                                 *end_filename = 0;
1985                                 append_region (regions, (gpointer) start_address, (gpointer) end_address, offset, start_filename);
1986                         }
1987                         return current;
1988                 case MAP_LINE_PARSER_STATE_INVALID:
1989                         if (c == '\n') {
1990                                 state = MAP_LINE_PARSER_STATE_DONE;
1991                         }
1992                         break;
1993                 }
1994                 
1995                 
1996                 if (c == 0) {
1997                         return NULL;
1998                 }
1999                 
2000                 GOTO_NEXT_CHAR(current, buffer, fd);
2001                 c = *current;
2002         }
2003 }
2004
2005 static gboolean
2006 scan_process_regions (ProfilerExecutableMemoryRegions *regions) {
2007         char *buffer;
2008         char *current;
2009         int fd;
2010         
2011         fd = open ("/proc/self/maps", O_RDONLY);
2012         if (fd == -1) {
2013                 return FALSE;
2014         }
2015         
2016         buffer = malloc (MAPS_BUFFER_SIZE);
2017         update_regions_buffer (fd, buffer);
2018         current = buffer;
2019         while (current != NULL) {
2020                 current = parse_map_line (regions, fd, buffer, current);
2021         }
2022         
2023         free (buffer);
2024         
2025         close (fd);
2026         return TRUE;
2027 }
2028 //End of Linux code
2029
2030 typedef enum {
2031         MONO_PROFILER_STATISTICAL_CODE_END = 0,
2032         MONO_PROFILER_STATISTICAL_CODE_METHOD = 1,
2033         MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID = 2,
2034         MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_IN_REGION = 3,
2035         MONO_PROFILER_STATISTICAL_CODE_REGIONS = 7
2036 } MonoProfilerStatisticalCode;
2037
2038 static void
2039 refresh_memory_regions (void) {
2040         ProfilerExecutableMemoryRegions *old_regions = profiler->executable_regions;
2041         ProfilerExecutableMemoryRegions *new_regions = profiler_executable_memory_regions_new (old_regions->next_id);
2042         int i;
2043         
2044         LOG_WRITER_THREAD ("Refreshing memory regions...");
2045         scan_process_regions (new_regions);
2046         restore_region_ids (old_regions, new_regions);
2047         sort_regions (new_regions);
2048         LOG_WRITER_THREAD ("Refreshed memory regions.");
2049         
2050         // This marks the region "sub-block"
2051         write_uint32 (MONO_PROFILER_STATISTICAL_CODE_REGIONS);
2052         
2053         // First write the "removed" regions 
2054         for (i = 0; i < old_regions->regions_count; i++) {
2055                 ProfilerExecutableMemoryRegionData *region = old_regions->regions [i];
2056                 if (! region->is_new) {
2057 #if DEBUG_STATISTICAL_PROFILER
2058                         printf ("[refresh_memory_regions] Invalidated region %d\n", region->id);
2059 #endif
2060                         write_uint32 (region->id);
2061                 }
2062         }
2063         write_uint32 (0);
2064         
2065         // Then write the new ones
2066         for (i = 0; i < new_regions->regions_count; i++) {
2067                 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
2068                 if (region->is_new) {
2069                         region->is_new = FALSE;
2070                         
2071 #if DEBUG_STATISTICAL_PROFILER
2072                         printf ("[refresh_memory_regions] Wrote region %d (%p-%p[%d] '%s')\n", region->id, region->start, region->end, region->file_offset, region->file_name);
2073 #endif
2074                         write_uint32 (region->id);
2075                         write_uint64 (GPOINTER_TO_INT (region->start));
2076                         write_uint32 (GPOINTER_TO_INT (region->end) - GPOINTER_TO_INT (region->start));
2077                         write_uint32 (region->file_offset);
2078                         write_string (region->file_name);
2079                 }
2080         }
2081         write_uint32 (0);
2082         
2083         // Finally, free the old ones, and replace them
2084         profiler_executable_memory_regions_destroy (old_regions);
2085         profiler->executable_regions = new_regions;
2086 }
2087
2088 static void
2089 flush_all_mappings (void);
2090
2091 static void
2092 write_statistical_data_block (ProfilerStatisticalData *data) {
2093         int start_index = data->first_unwritten_index;
2094         int end_index = data->next_free_index;
2095         gboolean regions_refreshed = FALSE;
2096         int index;
2097         ProfilerUnmanagedFunctions *functions = &(profiler->unmanaged_functions);
2098         
2099         if (end_index > data->end_index)
2100                 end_index = data->end_index;
2101         
2102         if (start_index == end_index)
2103                 return;
2104         
2105         data->first_unwritten_index = end_index;
2106         
2107         write_clock_data ();
2108         
2109         for (index = start_index; index < end_index; index ++) {
2110                 gpointer address = data->addresses [index];
2111                 MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (char*) address);
2112                 
2113                 if (ji != NULL) {
2114                         MonoMethod *method = mono_jit_info_get_method (ji);
2115                         MethodIdMappingElement *element = method_id_mapping_element_get (method);
2116                         
2117                         if (element != NULL) {
2118 #if DEBUG_STATISTICAL_PROFILER
2119                                 printf ("[write_statistical_data_block] Wrote method %d\n", element->id);
2120 #endif
2121                                 write_uint32 ((element->id << 3) | MONO_PROFILER_STATISTICAL_CODE_METHOD);
2122                         } else {
2123 #if DEBUG_STATISTICAL_PROFILER
2124                                 printf ("[write_statistical_data_block] Wrote unknown method %p\n", method);
2125 #endif
2126                                 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_METHOD);
2127                         }
2128                 } else {
2129                         if (! unmanaged_function_hit (functions, address)) {
2130                                 ProfilerExecutableMemoryRegionData *region = find_address_region (profiler->executable_regions, address);
2131                                 
2132                                 if (region == NULL && ! regions_refreshed) {
2133                                         refresh_memory_regions ();
2134                                         regions_refreshed = TRUE;
2135                                         region = find_address_region (profiler->executable_regions, address);
2136                                 }
2137                                 
2138                                 if (region != NULL) {
2139 #if DEBUG_STATISTICAL_PROFILER
2140                                         printf ("[write_statistical_data_block] Wrote unmanaged hit %d[%d]\n", region->id, GPOINTER_TO_INT (address) - GPOINTER_TO_INT (region->start));
2141 #endif
2142                                         write_uint32 ((region->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_IN_REGION);
2143                                         write_uint32 (GPOINTER_TO_INT (address) - GPOINTER_TO_INT (region->start));
2144                                 } else {
2145 #if DEBUG_STATISTICAL_PROFILER
2146                                         printf ("[write_statistical_data_block] Wrote unknown unmanaged hit %p\n", address);
2147 #endif
2148                                         write_uint32 (MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_IN_REGION);
2149                                         write_uint64 (GPOINTER_TO_INT (address));
2150                                 }
2151                         }
2152                 }
2153         }
2154         if (functions->unwritten_queue != functions->unwritten_queue_end) {
2155                 ProfilerUnmanagedFunction *end = functions->unwritten_queue_end;
2156                 ProfilerUnmanagedFunction *function = functions->unwritten_queue;
2157                 functions->unwritten_queue = functions->unwritten_queue_end;
2158                 
2159                 while (function != end) {
2160                         ProfilerUnmanagedFunction *next = function->next_unwritten;
2161                         
2162                         write_uint32 ((function->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID);
2163                         if (function->name != NULL) {
2164                                 write_uint32 (0);
2165                                 write_string (function->name);
2166                                 g_free (function->name);
2167                                 function->name = NULL;
2168                         }
2169                         write_uint32 (function->hits);
2170                         function->hits = 0;
2171                         
2172                         function->next_unwritten = NULL;
2173                         function = next;
2174                 }
2175         }
2176         write_uint32 (MONO_PROFILER_STATISTICAL_CODE_END);
2177         
2178         write_clock_data ();
2179         
2180         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL);
2181 }
2182
2183 static void
2184 write_intro_block (void) {
2185         write_uint32 (1);
2186         write_string ("mono");
2187         write_uint32 (profiler->flags);
2188         write_uint64 (profiler->start_counter);
2189         write_uint64 (profiler->start_time);
2190         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_INTRO);
2191 }
2192
2193 static void
2194 write_end_block (void) {
2195         write_uint32 (1);
2196         write_uint64 (profiler->end_counter);
2197         write_uint64 (profiler->end_time);
2198         write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_END);
2199 }
2200
2201 static void
2202 update_mapping (ProfilerPerThreadData *data) {
2203         ProfilerEventData *start = data->first_unmapped_event;
2204         ProfilerEventData *end = data->next_free_event;
2205         data->first_unmapped_event = end;
2206         
2207 #if (DEBUG_LOGGING_PROFILER)
2208         printf ("[update_mapping][TID %ld] START\n", data->thread_id);
2209 #endif
2210         while (start < end) {
2211 #if DEBUG_LOGGING_PROFILER
2212                 printf ("Examining event %p[TID %ld] looking for a new mapping...\n", start, data->thread_id);
2213 #endif
2214                 if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
2215                         ClassIdMappingElement *element = class_id_mapping_element_get (start->data.address);
2216                         if (element == NULL) {
2217                                 MonoClass *klass = start->data.address;
2218                                 class_id_mapping_element_new (klass);
2219                         }
2220                 } else if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
2221                         MethodIdMappingElement *element = method_id_mapping_element_get (start->data.address);
2222                         if (element == NULL) {
2223                                 MonoMethod *method = start->data.address;
2224                                 method_id_mapping_element_new (method);
2225                         }
2226                 }
2227                 
2228                 start ++;
2229         }
2230 #if (DEBUG_LOGGING_PROFILER)
2231         printf ("[update_mapping][TID %ld] END\n", data->thread_id);
2232 #endif
2233 }
2234
2235 static void
2236 flush_all_mappings (void) {
2237         ProfilerPerThreadData *data;
2238         
2239         for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2240                 update_mapping (data);
2241         }
2242         for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2243                 write_mapping_block (data->thread_id);
2244         }
2245 }
2246
2247 static void
2248 flush_full_event_data_buffer (ProfilerPerThreadData *data) {
2249         LOCK_PROFILER ();
2250         
2251         // We flush all mappings because some id definitions could come
2252         // from other threads
2253         flush_all_mappings ();
2254         g_assert (data->first_unmapped_event == data->end_event);
2255         
2256         write_thread_data_block (data);
2257         
2258         data->next_free_event = data->events;
2259         data->first_unwritten_event = data->events;
2260         data->first_unmapped_event = data->events;
2261         MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
2262         data->last_event_counter = data->start_event_counter;
2263         
2264         UNLOCK_PROFILER ();
2265 }
2266
2267 #define GET_NEXT_FREE_EVENT(d,e) {\
2268         if ((d)->next_free_event >= (d)->end_event) {\
2269                 flush_full_event_data_buffer (d);\
2270         }\
2271         (e) = (d)->next_free_event;\
2272         (d)->next_free_event ++;\
2273 } while (0)
2274
2275 static void
2276 flush_everything (void) {
2277         ProfilerPerThreadData *data;
2278         
2279         flush_all_mappings ();
2280         for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2281                 write_thread_data_block (data);
2282         }
2283         write_statistical_data_block (profiler->statistical_data);
2284 }
2285
2286 #define RESULT_TO_LOAD_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_LOADED_EVENT_SUCCESS:MONO_PROFILER_LOADED_EVENT_FAILURE)
2287 static void
2288 appdomain_start_load (MonoProfiler *profiler, MonoDomain *domain) {
2289         LOCK_PROFILER ();
2290         loaded_element_load_start (profiler->loaded_appdomains, domain);
2291         UNLOCK_PROFILER ();
2292 }
2293
2294 static void
2295 appdomain_end_load (MonoProfiler *profiler, MonoDomain *domain, int result) {
2296         char *name;
2297         LoadedElement *element;
2298         
2299         name = g_strdup_printf ("%d", mono_domain_get_id (domain));
2300         LOCK_PROFILER ();
2301         element = loaded_element_load_end (profiler->loaded_appdomains, domain, name);
2302         write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
2303         UNLOCK_PROFILER ();
2304 }
2305
2306 static void
2307 appdomain_start_unload (MonoProfiler *profiler, MonoDomain *domain) {
2308         LOCK_PROFILER ();
2309         loaded_element_unload_start (profiler->loaded_appdomains, domain);
2310         flush_everything ();
2311         UNLOCK_PROFILER ();
2312 }
2313
2314 static void
2315 appdomain_end_unload (MonoProfiler *profiler, MonoDomain *domain) {
2316         LoadedElement *element;
2317         
2318         LOCK_PROFILER ();
2319         element = loaded_element_unload_end (profiler->loaded_appdomains, domain);
2320         write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN, CURRENT_THREAD_ID ());
2321         UNLOCK_PROFILER ();
2322 }
2323
2324 static void
2325 module_start_load (MonoProfiler *profiler, MonoImage *module) {
2326         LOCK_PROFILER ();
2327         loaded_element_load_start (profiler->loaded_modules, module);
2328         UNLOCK_PROFILER ();
2329 }
2330
2331 static void
2332 module_end_load (MonoProfiler *profiler, MonoImage *module, int result) {
2333         char *name;
2334         MonoAssemblyName aname;
2335         LoadedElement *element;
2336         
2337         mono_assembly_fill_assembly_name (module, &aname);
2338         name = mono_stringify_assembly_name (&aname);
2339         LOCK_PROFILER ();
2340         element = loaded_element_load_end (profiler->loaded_modules, module, name);
2341         write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_MODULE | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
2342         UNLOCK_PROFILER ();
2343 }
2344
2345 static void
2346 module_start_unload (MonoProfiler *profiler, MonoImage *module) {
2347         LOCK_PROFILER ();
2348         loaded_element_unload_start (profiler->loaded_modules, module);
2349         flush_everything ();
2350         UNLOCK_PROFILER ();
2351 }
2352
2353 static void
2354 module_end_unload (MonoProfiler *profiler, MonoImage *module) {
2355         LoadedElement *element;
2356         
2357         LOCK_PROFILER ();
2358         element = loaded_element_unload_end (profiler->loaded_modules, module);
2359         write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_MODULE, CURRENT_THREAD_ID ());
2360         UNLOCK_PROFILER ();
2361 }
2362
2363 static void
2364 assembly_start_load (MonoProfiler *profiler, MonoAssembly *assembly) {
2365         LOCK_PROFILER ();
2366         loaded_element_load_start (profiler->loaded_assemblies, assembly);
2367         UNLOCK_PROFILER ();
2368 }
2369
2370 static void
2371 assembly_end_load (MonoProfiler *profiler, MonoAssembly *assembly, int result) {
2372         char *name;
2373         MonoAssemblyName aname;
2374         LoadedElement *element;
2375         
2376         mono_assembly_fill_assembly_name (mono_assembly_get_image (assembly), &aname);
2377         name = mono_stringify_assembly_name (&aname);
2378         LOCK_PROFILER ();
2379         element = loaded_element_load_end (profiler->loaded_assemblies, assembly, name);
2380         write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
2381         UNLOCK_PROFILER ();
2382 }
2383
2384 static void
2385 assembly_start_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
2386         LOCK_PROFILER ();
2387         loaded_element_unload_start (profiler->loaded_assemblies, assembly);
2388         flush_everything ();
2389         UNLOCK_PROFILER ();
2390 }
2391 static void
2392 assembly_end_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
2393         LoadedElement *element;
2394         
2395         LOCK_PROFILER ();
2396         element = loaded_element_unload_end (profiler->loaded_assemblies, assembly);
2397         write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY, CURRENT_THREAD_ID ());
2398         UNLOCK_PROFILER ();
2399 }
2400
2401 #if (DEBUG_LOGGING_PROFILER)            
2402 static const char*
2403 class_event_code_to_string (MonoProfilerClassEvents code) {
2404         switch (code) {
2405         case MONO_PROFILER_EVENT_CLASS_LOAD: return "LOAD";
2406         case MONO_PROFILER_EVENT_CLASS_UNLOAD: return "UNLOAD";
2407         case MONO_PROFILER_EVENT_CLASS_ALLOCATION: return "ALLOCATION";
2408         case MONO_PROFILER_EVENT_CLASS_EXCEPTION: return "EXCEPTION";
2409         default: g_assert_not_reached (); return "";
2410         }
2411 }
2412 static const char*
2413 method_event_code_to_string (MonoProfilerClassEvents code) {
2414         switch (code) {
2415         case MONO_PROFILER_EVENT_METHOD_CALL: return "CALL";
2416         case MONO_PROFILER_EVENT_METHOD_JIT: return "JIT";
2417         case MONO_PROFILER_EVENT_METHOD_FREED: return "FREED";
2418         default: g_assert_not_reached (); return "";
2419         }
2420 }
2421 static const char*
2422 number_event_code_to_string (MonoProfilerEvents code) {
2423         switch (code) {
2424         case MONO_PROFILER_EVENT_THREAD: return "THREAD";
2425         case MONO_PROFILER_EVENT_GC_COLLECTION: return "GC_COLLECTION";
2426         case MONO_PROFILER_EVENT_GC_MARK: return "GC_MARK";
2427         case MONO_PROFILER_EVENT_GC_SWEEP: return "GC_SWEEP";
2428         case MONO_PROFILER_EVENT_GC_RESIZE: return "GC_RESIZE";
2429         case MONO_PROFILER_EVENT_GC_STOP_WORLD: return "GC_STOP_WORLD";
2430         case MONO_PROFILER_EVENT_GC_START_WORLD: return "GC_START_WORLD";
2431         default: g_assert_not_reached (); return "";
2432         }
2433 }
2434 static const char*
2435 event_result_to_string (MonoProfilerEventResult code) {
2436         switch (code) {
2437         case MONO_PROFILER_EVENT_RESULT_SUCCESS: return "SUCCESS";
2438         case MONO_PROFILER_EVENT_RESULT_FAILURE: return "FAILURE";
2439         default: g_assert_not_reached (); return "";
2440         }
2441 }
2442 static const char*
2443 event_kind_to_string (MonoProfilerEventKind code) {
2444         switch (code) {
2445         case MONO_PROFILER_EVENT_KIND_START: return "START";
2446         case MONO_PROFILER_EVENT_KIND_END: return "END";
2447         default: g_assert_not_reached (); return "";
2448         }
2449 }
2450 static void
2451 print_event_data (gsize thread_id, ProfilerEventData *event, guint64 value) {
2452         if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
2453                 printf ("[TID %ld] CLASS[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s)\n",
2454                                 thread_id,
2455                                 event->data.address,
2456                                 event,
2457                                 class_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
2458                                 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
2459                                 event_kind_to_string (event->kind),
2460                                 event->data_type,
2461                                 event->kind,
2462                                 event->code,
2463                                 value,
2464                                 mono_class_get_namespace ((MonoClass*) event->data.address),
2465                                 mono_class_get_name ((MonoClass*) event->data.address));
2466         } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
2467                 printf ("[TID %ld] METHOD[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s:%s (?))\n",
2468                                 thread_id,
2469                                 event->data.address,
2470                                 event,
2471                                 method_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
2472                                 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
2473                                 event_kind_to_string (event->kind),
2474                                 event->data_type,
2475                                 event->kind,
2476                                 event->code,
2477                                 value,
2478                                 mono_class_get_namespace (mono_method_get_class ((MonoMethod*) event->data.address)),
2479                                 mono_class_get_name (mono_method_get_class ((MonoMethod*) event->data.address)),
2480                                 mono_method_get_name ((MonoMethod*) event->data.address));
2481         } else {
2482                 printf ("[TID %ld] NUMBER[%ld] event [%p] %s:%s[%d-%d-%d] %ld\n",
2483                                 thread_id,
2484                                 (guint64) event->data.number,
2485                                 event,
2486                                 number_event_code_to_string (event->code),
2487                                 event_kind_to_string (event->kind),
2488                                 event->data_type,
2489                                 event->kind,
2490                                 event->code,
2491                                 value);
2492         }
2493 }
2494 #define LOG_EVENT(tid,ev,val) print_event_data ((tid),(ev),(val))
2495 #else
2496 #define LOG_EVENT(tid,ev,val)
2497 #endif
2498
2499 #define RESULT_TO_EVENT_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_EVENT_RESULT_SUCCESS:MONO_PROFILER_EVENT_RESULT_FAILURE)
2500
2501 #define STORE_EVENT_ITEM_COUNTER(p,i,dt,c,k) do {\
2502         ProfilerPerThreadData *data;\
2503         ProfilerEventData *event;\
2504         guint64 counter;\
2505         guint64 delta;\
2506         GET_PROFILER_THREAD_DATA (data);\
2507         GET_NEXT_FREE_EVENT (data, event);\
2508         MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
2509         event->data.address = (i);\
2510         event->data_type = (dt);\
2511         event->code = (c);\
2512         event->kind = (k);\
2513         delta = counter - data->last_event_counter;\
2514         if (delta < MAX_EVENT_VALUE) {\
2515                 event->value = delta;\
2516         } else {\
2517                 ProfilerEventData *extension = data->next_free_event;\
2518                 data->next_free_event ++;\
2519                 event->value = delta >> 32;\
2520                 extension->data.number = delta & 0xffffffff;\
2521         }\
2522         data->last_event_counter = counter;\
2523         LOG_EVENT (data->thread_id, event, delta);\
2524 } while (0);
2525 #define STORE_EVENT_ITEM_VALUE(p,i,dt,c,k,v) do {\
2526         ProfilerPerThreadData *data;\
2527         ProfilerEventData *event;\
2528         GET_PROFILER_THREAD_DATA (data);\
2529         GET_NEXT_FREE_EVENT (data, event);\
2530         event->data.address = (i);\
2531         event->data_type = (dt);\
2532         event->code = (c);\
2533         event->kind = (k);\
2534         if ((v) < MAX_EVENT_VALUE) {\
2535                 event->value = (v);\
2536         } else {\
2537                 ProfilerEventData *extension = data->next_free_event;\
2538                 data->next_free_event ++;\
2539                 event->value = (v) >> 32;\
2540                 extension->data.number = (v) & 0xffffffff;\
2541         }\
2542         LOG_EVENT (data->thread_id, event, (v));\
2543 }while (0);
2544 #define STORE_EVENT_NUMBER_COUNTER(p,n,dt,c,k) do {\
2545         ProfilerPerThreadData *data;\
2546         ProfilerEventData *event;\
2547         guint64 counter;\
2548         guint64 delta;\
2549         GET_PROFILER_THREAD_DATA (data);\
2550         GET_NEXT_FREE_EVENT (data, event);\
2551         MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
2552         event->data.number = (n);\
2553         event->data_type = (dt);\
2554         event->code = (c);\
2555         event->kind = (k);\
2556         delta = counter - data->last_event_counter;\
2557         if (delta < MAX_EVENT_VALUE) {\
2558                 event->value = delta;\
2559         } else {\
2560                 ProfilerEventData *extension = data->next_free_event;\
2561                 data->next_free_event ++;\
2562                 event->value = delta >> 32;\
2563                 extension->data.number = delta & 0xffffffff;\
2564         }\
2565         data->last_event_counter = counter;\
2566         LOG_EVENT (data->thread_id, event, delta);\
2567 }while (0);
2568 #define STORE_EVENT_NUMBER_VALUE(p,n,dt,c,k,v) do {\
2569         ProfilerPerThreadData *data;\
2570         ProfilerEventData *event;\
2571         GET_PROFILER_THREAD_DATA (data);\
2572         GET_NEXT_FREE_EVENT (data, event);\
2573         event->data.number = (n);\
2574         event->data_type = (dt);\
2575         event->code = (c);\
2576         event->kind = (k);\
2577         if ((v) < MAX_EVENT_VALUE) {\
2578                 event->value = (v);\
2579         } else {\
2580                 ProfilerEventData *extension = data->next_free_event;\
2581                 data->next_free_event ++;\
2582                 event->value = (v) >> 32;\
2583                 extension->data.number = (v) & 0xffffffff;\
2584         }\
2585         LOG_EVENT (data->thread_id, event, (v));\
2586 }while (0);
2587
2588
2589 static void
2590 class_start_load (MonoProfiler *profiler, MonoClass *klass) {
2591         STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD, MONO_PROFILER_EVENT_KIND_START);
2592 }
2593 static void
2594 class_end_load (MonoProfiler *profiler, MonoClass *klass, int result) {
2595         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);
2596 }
2597 static void
2598 class_start_unload (MonoProfiler *profiler, MonoClass *klass) {
2599         STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_START);
2600 }
2601 static void
2602 class_end_unload (MonoProfiler *profiler, MonoClass *klass) {
2603         STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_END);
2604 }
2605
2606 static void
2607 method_start_jit (MonoProfiler *profiler, MonoMethod *method) {
2608         if (profiler->action_flags.jit_time) {
2609                 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT, MONO_PROFILER_EVENT_KIND_START);
2610         }
2611 }
2612 static void
2613 method_end_jit (MonoProfiler *profiler, MonoMethod *method, int result) {
2614         if (profiler->action_flags.jit_time) {
2615                 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);
2616         }
2617 }
2618
2619 #if (HAS_OPROFILE)
2620 static void
2621 method_jit_result (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result) {
2622         if (profiler->action_flags.oprofile && (result == MONO_PROFILE_OK)) {
2623                 MonoClass *klass = mono_method_get_class (method);
2624                 char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
2625                 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);
2626                 gpointer code_start = mono_jit_info_get_code_start (jinfo);
2627                 int code_size = mono_jit_info_get_code_size (jinfo);
2628                 
2629                 if (op_write_native_code (name, code_start, code_size)) {
2630                         g_warning ("Problem calling op_write_native_code\n");
2631                 }
2632                 
2633                 g_free (signature);
2634                 g_free (name);
2635         }
2636 }
2637 #endif
2638
2639
2640 static void
2641 method_enter (MonoProfiler *profiler, MonoMethod *method) {
2642         STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_START);
2643 }
2644 static void
2645 method_leave (MonoProfiler *profiler, MonoMethod *method) {
2646         STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_END);
2647 }
2648
2649 static void
2650 method_free (MonoProfiler *profiler, MonoMethod *method) {
2651         STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_FREED, 0);
2652 }
2653
2654 static void
2655 thread_start (MonoProfiler *profiler, gsize tid) {
2656         STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_START);
2657 }
2658 static void
2659 thread_end (MonoProfiler *profiler, gsize tid) {
2660         STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_END);
2661 }
2662
2663 static void
2664 object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) {
2665         ProfilerPerThreadData *thread_data;
2666         
2667         STORE_EVENT_ITEM_VALUE (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_ALLOCATION, 0, (guint64) mono_object_get_size (obj));
2668         if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
2669                 GET_PROFILER_THREAD_DATA (thread_data);
2670                 STORE_ALLOCATED_OBJECT (thread_data, obj);
2671         }
2672 }
2673
2674
2675 static void
2676 statistical_hit (MonoProfiler *profiler, guchar *ip, void *context) {
2677         ProfilerStatisticalData *data;
2678         int index;
2679         
2680         do {
2681                 data = profiler->statistical_data;
2682                 index = InterlockedIncrement (&data->next_free_index);
2683                 
2684                 if (index <= data->end_index) {
2685                         data->addresses [index - 1] = (gpointer) ip;
2686                 } else {
2687                         /* Check if we are the one that must swap the buffers */
2688                         if (index == data->end_index + 1) {
2689                                 ProfilerStatisticalData *new_data;
2690
2691                                 /* In the *impossible* case that the writer thread has not finished yet, */
2692                                 /* loop waiting for it and meanwhile lose all statistical events... */
2693                                 do {
2694                                         /* First, wait that it consumed the ready buffer */
2695                                         while (profiler->statistical_data_ready != NULL);
2696                                         /* Then, wait that it produced the free buffer */
2697                                         new_data = profiler->statistical_data_second_buffer;
2698                                 } while (new_data == NULL);
2699
2700                                 profiler->statistical_data_ready = data;
2701                                 profiler->statistical_data = new_data;
2702                                 profiler->statistical_data_second_buffer = NULL;
2703                                 WRITER_EVENT_RAISE ();
2704                         }
2705                         
2706                         /* Loop again, hoping to acquire a free slot this time */
2707                         data = NULL;
2708                 }
2709         } while (data == NULL);
2710 }
2711
2712 static MonoProfilerEvents
2713 gc_event_code_from_profiler_event (MonoGCEvent event) {
2714         switch (event) {
2715         case MONO_GC_EVENT_START:
2716         case MONO_GC_EVENT_END:
2717                 return MONO_PROFILER_EVENT_GC_COLLECTION;
2718         case MONO_GC_EVENT_MARK_START:
2719         case MONO_GC_EVENT_MARK_END:
2720                 return MONO_PROFILER_EVENT_GC_MARK;
2721         case MONO_GC_EVENT_RECLAIM_START:
2722         case MONO_GC_EVENT_RECLAIM_END:
2723                 return MONO_PROFILER_EVENT_GC_SWEEP;
2724         case MONO_GC_EVENT_PRE_STOP_WORLD:
2725         case MONO_GC_EVENT_POST_STOP_WORLD:
2726                 return MONO_PROFILER_EVENT_GC_STOP_WORLD;
2727         case MONO_GC_EVENT_PRE_START_WORLD:
2728         case MONO_GC_EVENT_POST_START_WORLD:
2729                 return MONO_PROFILER_EVENT_GC_START_WORLD;
2730         default:
2731                 g_assert_not_reached ();
2732                 return 0;
2733         }
2734 }
2735
2736 static MonoProfilerEventKind
2737 gc_event_kind_from_profiler_event (MonoGCEvent event) {
2738         switch (event) {
2739         case MONO_GC_EVENT_START:
2740         case MONO_GC_EVENT_MARK_START:
2741         case MONO_GC_EVENT_RECLAIM_START:
2742         case MONO_GC_EVENT_PRE_STOP_WORLD:
2743         case MONO_GC_EVENT_PRE_START_WORLD:
2744                 return MONO_PROFILER_EVENT_KIND_START;
2745         case MONO_GC_EVENT_END:
2746         case MONO_GC_EVENT_MARK_END:
2747         case MONO_GC_EVENT_RECLAIM_END:
2748         case MONO_GC_EVENT_POST_START_WORLD:
2749         case MONO_GC_EVENT_POST_STOP_WORLD:
2750                 return MONO_PROFILER_EVENT_KIND_END;
2751         default:
2752                 g_assert_not_reached ();
2753                 return 0;
2754         }
2755 }
2756
2757 #define HEAP_SHOT_COMMAND_FILE_MAX_LENGTH 64
2758 static void
2759 profiler_heap_shot_process_command_file (void) {
2760         //FIXME: Port to Windows as well
2761         struct stat stat_buf;
2762         int fd;
2763         char buffer [HEAP_SHOT_COMMAND_FILE_MAX_LENGTH + 1];
2764         
2765         if (profiler->heap_shot_command_file_name == NULL)
2766                 return;
2767         if (stat (profiler->heap_shot_command_file_name, &stat_buf) != 0)
2768                 return;
2769         if (stat_buf.st_size > HEAP_SHOT_COMMAND_FILE_MAX_LENGTH)
2770                 return;
2771         if ((stat_buf.st_mtim.tv_sec * 1000000) < profiler->heap_shot_command_file_access_time)
2772                 return;
2773         
2774         fd = open (profiler->heap_shot_command_file_name, O_RDONLY);
2775         if (fd < 0) {
2776                 return;
2777         } else {
2778                 if (read (fd, &(buffer [0]), stat_buf.st_size) != stat_buf.st_size) {
2779                         return;
2780                 } else {
2781                         buffer [stat_buf.st_size] = 0;
2782                         profiler->dump_next_heap_snapshots = atoi (buffer);
2783                         MONO_PROFILER_GET_CURRENT_TIME (profiler->heap_shot_command_file_access_time);
2784                 }
2785                 close (fd);
2786         }
2787 }
2788
2789 static gboolean
2790 dump_current_heap_snapshot (void) {
2791         gboolean result;
2792         
2793         profiler_heap_shot_process_command_file ();
2794         if (profiler->dump_next_heap_snapshots > 0) {
2795                 profiler->dump_next_heap_snapshots--;
2796                 result = TRUE;
2797         } else if (profiler->dump_next_heap_snapshots < 0) {
2798                 result = TRUE;
2799         } else {
2800                 result = FALSE;
2801         }
2802         
2803         return (result || (profiler->heap_shot_was_signalled));
2804 }
2805
2806 static void
2807 profiler_heap_buffers_setup (ProfilerHeapShotHeapBuffers *heap) {
2808         heap->buffers = g_new (ProfilerHeapShotHeapBuffer, 1);
2809         heap->buffers->previous = NULL;
2810         heap->buffers->next = NULL;
2811         heap->buffers->start_slot = &(heap->buffers->buffer [0]);
2812         heap->buffers->end_slot = &(heap->buffers->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
2813         heap->last = heap->buffers;
2814         heap->current = heap->buffers;
2815         heap->first_free_slot = & (heap->buffers->buffer [0]);
2816 }
2817 static void
2818 profiler_heap_buffers_clear (ProfilerHeapShotHeapBuffers *heap) {
2819         heap->buffers = NULL;
2820         heap->last = NULL;
2821         heap->current = NULL;
2822         heap->first_free_slot = NULL;
2823 }
2824 static void
2825 profiler_heap_buffers_free (ProfilerHeapShotHeapBuffers *heap) {
2826         ProfilerHeapShotHeapBuffer *current = heap->buffers;
2827         while (current != NULL) {
2828                 ProfilerHeapShotHeapBuffer *next = current->next;
2829                 g_free (current);
2830                 current = next;
2831         }
2832         profiler_heap_buffers_clear (heap);
2833 }
2834
2835 static int
2836 report_object_references (gpointer *start, ClassIdMappingElement *layout, ProfilerHeapShotWriteJob *job) {
2837         int reported_references = 0;
2838         int slot;
2839         
2840         for (slot = 0; slot < layout->data.layout.slots; slot ++) {
2841                 gboolean slot_has_reference;
2842                 if (layout->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
2843                         if (layout->data.bitmap.compact & (((guint64)1) << slot)) {
2844                                 slot_has_reference = TRUE;
2845                         } else {
2846                                 slot_has_reference = FALSE;
2847                         }
2848                 } else {
2849                         if (layout->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
2850                                 slot_has_reference = TRUE;
2851                         } else {
2852                                 slot_has_reference = FALSE;
2853                         }
2854                 }
2855                 
2856                 if (slot_has_reference) {
2857                         gpointer field = start [slot];
2858                         
2859                         if ((field != NULL) && mono_object_is_alive (field)) {
2860                                 reported_references ++;
2861                                 WRITE_HEAP_SHOT_JOB_VALUE (job, field);
2862                         }
2863                 }
2864         }
2865         
2866         return reported_references;
2867 }
2868
2869 static void
2870 profiler_heap_report_object_reachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
2871         if (profiler->action_flags.heap_shot && (job != NULL)) {
2872                 MonoClass *klass = mono_object_get_class (obj);
2873                 int reference_counter = 0;
2874                 gpointer *reference_counter_location;
2875                 
2876                 WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, obj, HEAP_CODE_OBJECT);
2877 #if DEBUG_HEAP_PROFILER
2878                 printf ("profiler_heap_report_object_reachable: reported object %p at cursor %p\n", obj, (job->cursor - 1));
2879 #endif
2880                 WRITE_HEAP_SHOT_JOB_VALUE (job, NULL);
2881                 reference_counter_location = job->cursor - 1;
2882                 
2883                 if (mono_class_get_rank (klass)) {
2884                         MonoArray *array = (MonoArray *) obj;
2885                         MonoClass *element_class = mono_class_get_element_class (klass);
2886                         ClassIdMappingElement *element_id = class_id_mapping_element_get (element_class);
2887                         
2888                         g_assert (element_id != NULL);
2889                         if (element_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
2890                                 class_id_mapping_element_build_layout_bitmap (element_class, element_id);
2891                         }
2892                         if (! mono_class_is_valuetype (element_class)) {
2893                                 int length = mono_array_length (array);
2894                                 int i;
2895                                 for (i = 0; i < length; i++) {
2896                                         MonoObject *array_element = mono_array_get (array, MonoObject*, i);
2897                                         if ((array_element != NULL) && mono_object_is_alive (array_element)) {
2898                                                 reference_counter ++;
2899                                                 WRITE_HEAP_SHOT_JOB_VALUE (job, array_element);
2900                                         }
2901                                 }
2902                         } else if (element_id->data.layout.references > 0) {
2903                                 int length = mono_array_length (array);
2904                                 int array_element_size = mono_array_element_size (klass);
2905                                 int i;
2906                                 for (i = 0; i < length; i++) {
2907                                         gpointer array_element_address = mono_array_addr_with_size (array, array_element_size, i);
2908                                         reference_counter += report_object_references (array_element_address, element_id, job);
2909                                 }
2910                         }
2911                 } else {
2912                         ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
2913                         if (class_id == NULL) {
2914                                 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));
2915                         }
2916                         g_assert (class_id != NULL);
2917                         if (class_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
2918                                 class_id_mapping_element_build_layout_bitmap (klass, class_id);
2919                         }
2920                         if (class_id->data.layout.references > 0) {
2921                                 reference_counter += report_object_references ((gpointer)(((char*)obj) + sizeof (MonoObject)), class_id, job);
2922                         }
2923                 }
2924                 
2925                 *reference_counter_location = GINT_TO_POINTER (reference_counter);
2926 #if DEBUG_HEAP_PROFILER
2927                 printf ("profiler_heap_report_object_reachable: updated reference_counter_location %p with value %d\n", reference_counter_location, reference_counter);
2928 #endif
2929         }
2930 }
2931 static void
2932 profiler_heap_report_object_unreachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
2933         if (job != NULL) {
2934                 MonoClass *klass = mono_object_get_class (obj);
2935                 guint32 size = mono_object_get_size (obj);
2936                 
2937 #if DEBUG_HEAP_PROFILER
2938                 printf ("profiler_heap_report_object_unreachable: at job %p writing klass %p\n", job, klass);
2939 #endif
2940                 WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, klass, HEAP_CODE_FREE_OBJECT_CLASS);
2941         
2942 #if DEBUG_HEAP_PROFILER
2943                 printf ("profiler_heap_report_object_unreachable: at job %p writing size %p\n", job, GUINT_TO_POINTER (size));
2944 #endif
2945                 WRITE_HEAP_SHOT_JOB_VALUE (job, GUINT_TO_POINTER (size));
2946         }
2947 }
2948
2949 static void
2950 profiler_heap_add_object (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject *obj) {
2951         if (heap->first_free_slot >= heap->current->end_slot) {
2952                 if (heap->current->next != NULL) {
2953                         heap->current = heap->current->next;
2954                 } else {
2955                         ProfilerHeapShotHeapBuffer *buffer = g_new (ProfilerHeapShotHeapBuffer, 1);
2956                         buffer->previous = heap->last;
2957                         buffer->next = NULL;
2958                         buffer->start_slot = &(buffer->buffer [0]);
2959                         buffer->end_slot = &(buffer->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
2960                         heap->current = buffer;
2961                         heap->last->next = buffer;
2962                         heap->last = buffer;
2963                 }
2964                 heap->first_free_slot = &(heap->current->buffer [0]);
2965         }
2966         
2967         *(heap->first_free_slot) = obj;
2968         heap->first_free_slot ++;
2969         profiler_heap_report_object_reachable (job, obj);
2970 }
2971
2972 static MonoObject*
2973 profiler_heap_pop_object_from_end (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject** current_slot) {
2974         while (heap->first_free_slot != current_slot) {
2975                 MonoObject* obj;
2976                 
2977                 if (heap->first_free_slot > heap->current->start_slot) {
2978                         heap->first_free_slot --;
2979                 } else {
2980                         heap->current = heap->current->previous;
2981                         g_assert (heap->current != NULL);
2982                         heap->first_free_slot = heap->current->end_slot - 1;
2983                 }
2984                 
2985                 obj = *(heap->first_free_slot);
2986                 
2987                 if (mono_object_is_alive (obj)) {
2988                         profiler_heap_report_object_reachable (job, obj);
2989                         return obj;
2990                 } else {
2991                         profiler_heap_report_object_unreachable (job, obj);
2992                 }
2993         }
2994         return NULL;
2995 }
2996
2997 static void
2998 profiler_heap_scan (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job) {
2999         ProfilerHeapShotHeapBuffer *current_buffer = heap->buffers;
3000         MonoObject** current_slot = current_buffer->start_slot;
3001         
3002         while (current_slot != heap->first_free_slot) {
3003                 MonoObject *obj = *current_slot;
3004                 if (mono_object_is_alive (obj)) {
3005                         profiler_heap_report_object_reachable (job, obj);
3006                 } else {
3007                         profiler_heap_report_object_unreachable (job, obj);
3008                         *current_slot = profiler_heap_pop_object_from_end (heap, job, current_slot);
3009                 }
3010                 
3011                 if (*current_slot != NULL) {
3012                         current_slot ++;
3013                         
3014                         if (current_slot == current_buffer->end_slot) {
3015                                 current_buffer = current_buffer->next;
3016                                 //g_assert (current_buffer != NULL);
3017                                 if (current_buffer == NULL) {
3018                                         printf ("KO\n");
3019                                         G_BREAKPOINT ();
3020                                         g_assert_not_reached ();
3021                                 }
3022                                 current_slot = current_buffer->start_slot;
3023                         }
3024                 }
3025         }
3026 }
3027
3028 static void
3029 handle_heap_profiling (MonoProfiler *profiler, MonoGCEvent ev) {
3030         switch (ev) {
3031         case MONO_GC_EVENT_PRE_STOP_WORLD:
3032                 // Get the lock, so we are sure nobody is flushing events during the collection,
3033                 // and we can update all mappings (building the class descriptors).
3034                 LOCK_PROFILER ();
3035                 break;
3036         case MONO_GC_EVENT_POST_STOP_WORLD:
3037                 // Update all mappings, so that we have built all the class descriptors.
3038                 flush_all_mappings ();
3039                 // Release lock...
3040                 UNLOCK_PROFILER ();
3041                 break;
3042         case MONO_GC_EVENT_MARK_END: {
3043                 ProfilerHeapShotWriteJob *job;
3044                 ProfilerPerThreadData *data;
3045                 
3046                 if (dump_current_heap_snapshot ()) {
3047                         job = profiler_heap_shot_write_job_new (profiler->heap_shot_was_signalled);
3048                         profiler->heap_shot_was_signalled = FALSE;
3049                         MONO_PROFILER_GET_CURRENT_COUNTER (job->start_counter);
3050                         MONO_PROFILER_GET_CURRENT_TIME (job->start_time);
3051                 } else {
3052                         job = NULL;
3053                 }
3054                 
3055                 profiler_heap_scan (&(profiler->heap), job);
3056                 
3057                 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3058                         ProfilerHeapShotObjectBuffer *buffer;
3059                         for (buffer = data->heap_shot_object_buffers; buffer != NULL; buffer = buffer->next) {
3060                                 MonoObject **cursor;
3061                                 for (cursor = buffer->first_unprocessed_slot; cursor < buffer->next_free_slot; cursor ++) {
3062                                         MonoObject *obj = *cursor;
3063 #if DEBUG_HEAP_PROFILER
3064                                         printf ("gc_event: in object buffer %p(%p-%p) cursor at %p has object %p ", buffer, &(buffer->buffer [0]), buffer->end, cursor, obj);
3065 #endif
3066                                         if (mono_object_is_alive (obj)) {
3067 #if DEBUG_HEAP_PROFILER
3068                                                 printf ("(object is alive, adding to heap)\n");
3069 #endif
3070                                                 profiler_heap_add_object (&(profiler->heap), job, obj);
3071                                         } else {
3072 #if DEBUG_HEAP_PROFILER
3073                                                 printf ("(object is unreachable, reporting in job)\n");
3074 #endif
3075                                                 profiler_heap_report_object_unreachable (job, obj);
3076                                         }
3077                                 }
3078                                 buffer->first_unprocessed_slot = cursor;
3079                         }
3080                 }
3081                 
3082                 if (job != NULL) {
3083                         MONO_PROFILER_GET_CURRENT_COUNTER (job->end_counter);
3084                         MONO_PROFILER_GET_CURRENT_TIME (job->end_time);
3085                         
3086                         profiler_add_heap_shot_write_job (job);
3087                         profiler_free_heap_shot_write_jobs ();
3088                         WRITER_EVENT_RAISE ();
3089                 }
3090                 break;
3091         }
3092         default:
3093                 break;
3094         }
3095 }
3096
3097 static void
3098 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
3099         STORE_EVENT_NUMBER_COUNTER (profiler, generation, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, gc_event_code_from_profiler_event (ev), gc_event_kind_from_profiler_event (ev));
3100         if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
3101                 handle_heap_profiling (profiler, ev);
3102         }
3103 }
3104
3105 static void
3106 gc_resize (MonoProfiler *profiler, gint64 new_size) {
3107         STORE_EVENT_NUMBER_COUNTER (profiler, new_size, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_GC_RESIZE, 0);
3108 }
3109
3110 /* called at the end of the program */
3111 static void
3112 profiler_shutdown (MonoProfiler *prof)
3113 {
3114         ProfilerPerThreadData* current_thread_data;
3115         
3116         LOG_WRITER_THREAD ("profiler_shutdown: zeroing relevant flags");
3117         mono_profiler_set_events (0);
3118         //profiler->flags = 0;
3119         //profiler->action_flags.unreachable_objects = FALSE;
3120         //profiler->action_flags.heap_shot = FALSE;
3121         
3122         LOG_WRITER_THREAD ("profiler_shutdown: asking stats thread to exit");
3123         profiler->terminate_writer_thread = TRUE;
3124         WRITER_EVENT_RAISE ();
3125         LOG_WRITER_THREAD ("profiler_shutdown: waiting for stats thread to exit");
3126         WAIT_WRITER_THREAD ();
3127         LOG_WRITER_THREAD ("profiler_shutdown: stats thread should be dead now");
3128         WRITER_EVENT_DESTROY ();
3129         
3130         LOCK_PROFILER ();
3131         
3132         MONO_PROFILER_GET_CURRENT_TIME (profiler->end_time);
3133         MONO_PROFILER_GET_CURRENT_COUNTER (profiler->end_counter);
3134         
3135         flush_everything ();
3136         write_end_block ();
3137         FLUSH_FILE ();
3138         CLOSE_FILE();
3139         UNLOCK_PROFILER ();
3140         g_free (profiler->file_name);
3141         
3142         method_id_mapping_destroy (profiler->methods);
3143         class_id_mapping_destroy (profiler->classes);
3144         g_hash_table_destroy (profiler->loaded_assemblies);
3145         g_hash_table_destroy (profiler->loaded_modules);
3146         g_hash_table_destroy (profiler->loaded_appdomains);
3147         
3148         FREE_PROFILER_THREAD_DATA ();
3149         
3150         for (current_thread_data = profiler->per_thread_data; current_thread_data != NULL; current_thread_data = current_thread_data->next) {
3151                 profiler_per_thread_data_destroy (current_thread_data);
3152         }
3153         if (profiler->statistical_data != NULL) {
3154                 profiler_statistical_data_destroy (profiler->statistical_data);
3155         }
3156         if (profiler->statistical_data_ready != NULL) {
3157                 profiler_statistical_data_destroy (profiler->statistical_data_ready);
3158         }
3159         if (profiler->statistical_data_second_buffer != NULL) {
3160                 profiler_statistical_data_destroy (profiler->statistical_data_second_buffer);
3161         }
3162         if (profiler->executable_regions != NULL) {
3163                 profiler_executable_memory_regions_destroy (profiler->executable_regions);
3164         }
3165         unmanaged_functions_dispose (&(profiler->unmanaged_functions));
3166         
3167         profiler_heap_buffers_free (&(profiler->heap));
3168         if (profiler->heap_shot_command_file_name != NULL) {
3169                 g_free (profiler->heap_shot_command_file_name);
3170         }
3171         
3172         profiler_free_write_buffers ();
3173         profiler_destroy_heap_shot_write_jobs ();
3174         
3175         DELETE_PROFILER_MUTEX ();
3176         
3177 #if (HAS_OPROFILE)
3178         if (profiler->action_flags.oprofile) {
3179                 op_close_agent ();
3180         }
3181 #endif
3182         
3183         g_free (profiler);
3184         profiler = NULL;
3185 }
3186
3187 #define DEFAULT_ARGUMENTS "s"
3188 static void
3189 setup_user_options (const char *arguments) {
3190         gchar **arguments_array, **current_argument;
3191         
3192         profiler->file_name = NULL;
3193         profiler->per_thread_buffer_size = 10000;
3194         profiler->statistical_buffer_size = 10000;
3195         profiler->write_buffer_size = 1024;
3196         profiler->heap_shot_command_file_name = NULL;
3197         profiler->dump_next_heap_snapshots = 0;
3198         profiler->heap_shot_command_file_access_time = 0;
3199         profiler->heap_shot_was_signalled = FALSE;
3200         profiler->flags = MONO_PROFILE_APPDOMAIN_EVENTS|
3201                         MONO_PROFILE_ASSEMBLY_EVENTS|
3202                         MONO_PROFILE_MODULE_EVENTS|
3203                         MONO_PROFILE_CLASS_EVENTS|
3204                         MONO_PROFILE_METHOD_EVENTS;
3205         
3206         if (arguments == NULL) {
3207                 arguments = DEFAULT_ARGUMENTS;
3208         } else if (strstr (arguments, ":")) {
3209                 arguments = strstr (arguments, ":") + 1;
3210                 if (arguments [0] == 0) {
3211                         arguments = DEFAULT_ARGUMENTS;
3212                 }
3213         }
3214         
3215         arguments_array = g_strsplit (arguments, ",", -1);
3216         
3217         for (current_argument = arguments_array; ((current_argument != NULL) && (current_argument [0] != 0)); current_argument ++) {
3218                 char *argument = *current_argument;
3219                 char *equals = strstr (argument, "=");
3220                 
3221                 if (equals != NULL) {
3222                         int equals_position = equals - argument;
3223                         
3224                         if (! (strncmp (argument, "per-thread-buffer-size", equals_position) && strncmp (argument, "tbs", equals_position))) {
3225                                 int value = atoi (equals + 1);
3226                                 if (value > 0) {
3227                                         profiler->per_thread_buffer_size = value;
3228                                 }
3229                         } else if (! (strncmp (argument, "statistical-thread-buffer-size", equals_position) && strncmp (argument, "sbs", equals_position))) {
3230                                 int value = atoi (equals + 1);
3231                                 if (value > 0) {
3232                                         profiler->statistical_buffer_size = value;
3233                                 }
3234                         } else if (! (strncmp (argument, "write-buffer-size", equals_position) && strncmp (argument, "wbs", equals_position))) {
3235                                 int value = atoi (equals + 1);
3236                                 if (value > 0) {
3237                                         profiler->write_buffer_size = value;
3238                                 }
3239                         } else if (! (strncmp (argument, "output", equals_position) && strncmp (argument, "out", equals_position) && strncmp (argument, "o", equals_position) && strncmp (argument, "O", equals_position))) {
3240                                 if (strlen (equals + 1) > 0) {
3241                                         profiler->file_name = g_strdup (equals + 1);
3242                                 }
3243                         } else if (! (strncmp (argument, "gc-commands", equals_position) && strncmp (argument, "gc-c", equals_position) && strncmp (argument, "gcc", equals_position))) {
3244                                 if (strlen (equals + 1) > 0) {
3245                                         profiler->heap_shot_command_file_name = g_strdup (equals + 1);
3246                                 }
3247                         } else if (! (strncmp (argument, "gc-dumps", equals_position) && strncmp (argument, "gc-d", equals_position) && strncmp (argument, "gcd", equals_position))) {
3248                                 if (strlen (equals + 1) > 0) {
3249                                         profiler->dump_next_heap_snapshots = atoi (equals + 1);
3250                                 }
3251                         } else {
3252                                 g_warning ("Cannot parse valued argument %s\n", argument);
3253                         }
3254                 } else {
3255                         if (! (strcmp (argument, "jit") && strcmp (argument, "j"))) {
3256                                 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
3257                                 profiler->action_flags.jit_time = TRUE;
3258                         } else if (! (strcmp (argument, "allocations") && strcmp (argument, "alloc") && strcmp (argument, "a"))) {
3259                                 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
3260                         } else if (! (strcmp (argument, "gc") && strcmp (argument, "g"))) {
3261                                 profiler->flags |= MONO_PROFILE_GC;
3262                         } else if (! (strcmp (argument, "heap-shot") && strcmp (argument, "heap") && strcmp (argument, "h"))) {
3263                                 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
3264                                 profiler->action_flags.unreachable_objects = TRUE;
3265                                 profiler->action_flags.heap_shot = TRUE;
3266                         } else if (! (strcmp (argument, "unreachable") && strcmp (argument, "free") && strcmp (argument, "f"))) {
3267                                 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
3268                                 profiler->action_flags.unreachable_objects = TRUE;
3269                         } else if (! (strcmp (argument, "threads") && strcmp (argument, "t"))) {
3270                                 profiler->flags |= MONO_PROFILE_THREADS;
3271                         } else if (! (strcmp (argument, "enter-leave") && strcmp (argument, "calls") && strcmp (argument, "c"))) {
3272                                 profiler->flags |= MONO_PROFILE_ENTER_LEAVE;
3273                         } else if (! (strcmp (argument, "statistical") && strcmp (argument, "stat") && strcmp (argument, "s"))) {
3274                                 profiler->flags |= MONO_PROFILE_STATISTICAL|MONO_PROFILE_JIT_COMPILATION;
3275                                 profiler->action_flags.jit_time = TRUE;
3276 #if (HAS_OPROFILE)
3277                         } else if (! (strcmp (argument, "oprofile") && strcmp (argument, "oprof"))) {
3278                                 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
3279                                 profiler->action_flags.oprofile = TRUE;
3280                                 if (op_open_agent ()) {
3281                                         g_warning ("Problem calling op_open_agent\n");
3282                                 }
3283 #endif
3284                         } else if (strcmp (argument, "logging")) {
3285                                 g_warning ("Cannot parse flag argument %s\n", argument);
3286                         }
3287                 }
3288         }
3289         
3290         g_free (arguments_array);
3291         
3292         if (profiler->file_name == NULL) {
3293                 profiler->file_name = g_strdup ("profiler-log.prof");
3294         }
3295 }
3296
3297
3298 static guint32
3299 data_writer_thread (gpointer nothing) {
3300         for (;;) {
3301                 ProfilerStatisticalData *statistical_data;
3302                 gboolean done;
3303                 
3304                 LOG_WRITER_THREAD ("data_writer_thread: going to sleep");
3305                 WRITER_EVENT_WAIT ();
3306                 LOG_WRITER_THREAD ("data_writer_thread: just woke up");
3307                 
3308                 statistical_data = profiler->statistical_data_ready;
3309                 done = (statistical_data == NULL) && (profiler->heap_shot_write_jobs == NULL);
3310                 
3311                 if (!done) {
3312                         LOG_WRITER_THREAD ("data_writer_thread: acquiring lock and writing data");
3313                         LOCK_PROFILER ();
3314                         
3315                         // This makes sure that all method ids are in place
3316                         LOG_WRITER_THREAD ("data_writer_thread: writing mapping...");
3317                         flush_all_mappings ();
3318                         LOG_WRITER_THREAD ("data_writer_thread: wrote mapping");
3319                         
3320                         if (statistical_data != NULL) {
3321                                 LOG_WRITER_THREAD ("data_writer_thread: writing statistical data...");
3322                                 profiler->statistical_data_ready = NULL;
3323                                 write_statistical_data_block (statistical_data);
3324                                 statistical_data->next_free_index = 0;
3325                                 statistical_data->first_unwritten_index = 0;
3326                                 profiler->statistical_data_second_buffer = statistical_data;
3327                                 LOG_WRITER_THREAD ("data_writer_thread: wrote statistical data");
3328                         }
3329                         
3330                         profiler_process_heap_shot_write_jobs ();
3331                         
3332                         UNLOCK_PROFILER ();
3333                         LOG_WRITER_THREAD ("data_writer_thread: wrote data and released lock");
3334                         
3335                 }
3336                 
3337                 if (profiler->terminate_writer_thread) {
3338                 LOG_WRITER_THREAD ("data_writer_thread: exiting thread");
3339                         EXIT_THREAD ();
3340                 }
3341         }
3342         return 0;
3343 }
3344
3345 void
3346 mono_profiler_startup (const char *desc);
3347
3348 /* the entry point (mono_profiler_load?) */
3349 void
3350 mono_profiler_startup (const char *desc)
3351 {
3352         profiler = g_new0 (MonoProfiler, 1);
3353         
3354         setup_user_options ((desc != NULL) ? desc : "");
3355         
3356         INITIALIZE_PROFILER_MUTEX ();
3357         MONO_PROFILER_GET_CURRENT_TIME (profiler->start_time);
3358         MONO_PROFILER_GET_CURRENT_COUNTER (profiler->start_counter);
3359         
3360         profiler->methods = method_id_mapping_new ();
3361         profiler->classes = class_id_mapping_new ();
3362         profiler->loaded_assemblies = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
3363         profiler->loaded_modules = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
3364         profiler->loaded_appdomains = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
3365         
3366         profiler->statistical_data = profiler_statistical_data_new (profiler->statistical_buffer_size);
3367         profiler->statistical_data_second_buffer = profiler_statistical_data_new (profiler->statistical_buffer_size);
3368         unmanaged_functions_init (&(profiler->unmanaged_functions));
3369         
3370         profiler->write_buffers = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
3371         profiler->write_buffers->next = NULL;
3372         profiler->current_write_buffer = profiler->write_buffers;
3373         profiler->current_write_position = 0;
3374         profiler->full_write_buffers = 0;
3375         
3376         profiler->executable_regions = profiler_executable_memory_regions_new (1);
3377         
3378         profiler->heap_shot_write_jobs = NULL;
3379         if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
3380                 profiler_heap_buffers_setup (&(profiler->heap));
3381         } else {
3382                 profiler_heap_buffers_clear (&(profiler->heap));
3383         }
3384         
3385         WRITER_EVENT_INIT ();
3386         LOG_WRITER_THREAD ("mono_profiler_startup: creating writer thread");
3387         CREATE_WRITER_THREAD (data_writer_thread);
3388         LOG_WRITER_THREAD ("mono_profiler_startup: created writer thread");
3389
3390         ALLOCATE_PROFILER_THREAD_DATA ();
3391         
3392         OPEN_FILE ();
3393         
3394         write_intro_block ();
3395         
3396         mono_profiler_install (profiler, profiler_shutdown);
3397         
3398         mono_profiler_install_appdomain (appdomain_start_load, appdomain_end_load,
3399                         appdomain_start_unload, appdomain_end_unload);
3400         mono_profiler_install_assembly (assembly_start_load, assembly_end_load,
3401                         assembly_start_unload, assembly_end_unload);
3402         mono_profiler_install_module (module_start_load, module_end_load,
3403                         module_start_unload, module_end_unload);
3404         mono_profiler_install_class (class_start_load, class_end_load,
3405                         class_start_unload, class_end_unload);
3406         mono_profiler_install_jit_compile (method_start_jit, method_end_jit);
3407         mono_profiler_install_enter_leave (method_enter, method_leave);
3408         mono_profiler_install_method_free (method_free);
3409         mono_profiler_install_thread (thread_start, thread_end);
3410         mono_profiler_install_allocation (object_allocated);
3411         mono_profiler_install_statistical (statistical_hit);
3412         mono_profiler_install_gc (gc_event, gc_resize);
3413 #if (HAS_OPROFILE)
3414         mono_profiler_install_jit_end (method_jit_result);
3415 #endif
3416         
3417         mono_profiler_set_events (profiler->flags);
3418 }
3419