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