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