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>
15 #define HAS_OPROFILE 1
18 #include <libopagent.h>
21 // Needed for heap analysis
22 extern gboolean mono_object_is_alive (MonoObject* obj);
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;
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
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;
47 typedef struct _ProfilerEventData {
52 unsigned int data_type:2;
55 unsigned int value:26;
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))
63 MONO_PROFILER_EVENT_METHOD_JIT = 0,
64 MONO_PROFILER_EVENT_METHOD_FREED = 1,
65 MONO_PROFILER_EVENT_METHOD_CALL = 2
66 } MonoProfilerMethodEvents;
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;
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
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
86 MONO_PROFILER_EVENT_KIND_START = 0,
87 MONO_PROFILER_EVENT_KIND_END = 1
88 } MonoProfilerEventKind;
90 #define MONO_PROFILER_GET_CURRENT_TIME(t) {\
91 struct timeval current_time;\
92 gettimeofday (¤t_time, NULL);\
93 (t) = (((guint64)current_time.tv_sec) * 1000000) + current_time.tv_usec;\
95 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) MONO_PROFILER_GET_CURRENT_TIME ((c));
98 #define CLASS_LAYOUT_PACKED_BITMAP_SIZE 64
99 #define CLASS_LAYOUT_NOT_INITIALIZED (0xFFFF)
102 HEAP_CODE_OBJECT = 1,
103 HEAP_CODE_FREE_OBJECT_CLASS = 2,
105 } HeapProfilerJobValueCode;
106 typedef struct _MonoProfilerClassData {
115 } MonoProfilerClassData;
117 typedef struct _MonoProfilerMethodData {
120 } MonoProfilerMethodData;
122 typedef struct _ClassIdMappingElement {
126 struct _ClassIdMappingElement *next_unwritten;
127 MonoProfilerClassData data;
128 } ClassIdMappingElement;
130 typedef struct _MethodIdMappingElement {
134 struct _MethodIdMappingElement *next_unwritten;
135 MonoProfilerMethodData data;
136 } MethodIdMappingElement;
138 typedef struct _ClassIdMapping {
140 ClassIdMappingElement *unwritten;
144 typedef struct _MethodIdMapping {
146 MethodIdMappingElement *unwritten;
150 typedef struct _LoadedElement {
152 guint64 load_start_counter;
153 guint64 load_end_counter;
154 guint64 unload_start_counter;
155 guint64 unload_end_counter;
159 guint8 unload_written;
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
166 typedef struct _ProfilerHeapShotObjectBuffer {
167 struct _ProfilerHeapShotObjectBuffer *next;
168 MonoObject **next_free_slot;
170 MonoObject **first_unprocessed_slot;
171 MonoObject *buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE];
172 } ProfilerHeapShotObjectBuffer;
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;
182 typedef struct _ProfilerHeapShotHeapBuffers {
183 ProfilerHeapShotHeapBuffer *buffers;
184 ProfilerHeapShotHeapBuffer *last;
185 ProfilerHeapShotHeapBuffer *current;
186 MonoObject **first_free_slot;
187 } ProfilerHeapShotHeapBuffers;
190 typedef struct _ProfilerHeapShotWriteBuffer {
191 struct _ProfilerHeapShotWriteBuffer *next;
192 gpointer buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE];
193 } ProfilerHeapShotWriteBuffer;
195 typedef struct _ProfilerHeapShotWriteJob {
196 struct _ProfilerHeapShotWriteJob *next;
197 struct _ProfilerHeapShotWriteJob *next_unwritten;
201 ProfilerHeapShotWriteBuffer *buffers;
202 ProfilerHeapShotWriteBuffer **last_next;
203 guint32 full_buffers;
204 guint64 start_counter;
208 } ProfilerHeapShotWriteJob;
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;
219 ProfilerHeapShotObjectBuffer *heap_shot_object_buffers;
220 struct _ProfilerPerThreadData* next;
221 } ProfilerPerThreadData;
223 typedef struct _ProfilerStatisticalData {
227 int first_unwritten_index;
228 } ProfilerStatisticalData;
230 typedef struct _ProfilerExecutableMemoryRegionData {
237 } ProfilerExecutableMemoryRegionData;
239 typedef struct _ProfilerExecutableMemoryRegions {
240 ProfilerExecutableMemoryRegionData **regions;
241 guint32 regions_capacity;
242 guint32 regions_count;
244 } ProfilerExecutableMemoryRegions;
247 #ifndef PLATFORM_WIN32
248 #include <sys/types.h>
249 #include <sys/time.h>
250 #include <sys/stat.h>
254 #include <semaphore.h>
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))
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 ()
268 #ifndef HAVE_KW_THREAD
269 static pthread_key_t pthread_profiler_key;
270 static pthread_once_t profiler_pthread_once = PTHREAD_ONCE_INIT;
272 make_pthread_profiler_key (void) {
273 (void) pthread_key_create (&pthread_profiler_key, NULL);
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)
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))
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);
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))
298 #define CLOSE_FILE() close (profiler->file);
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))
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 ()
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)
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)
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);
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 */
347 #define GET_PROFILER_THREAD_DATA(data) do {\
348 ProfilerPerThreadData *_result = LOOKUP_PROFILER_THREAD_DATA ();\
350 _result = profiler_per_thread_data_new (profiler->per_thread_buffer_size);\
352 _result->next = profiler->per_thread_data;\
353 profiler->per_thread_data = _result;\
355 SET_PROFILER_THREAD_DATA (_result);\
360 #define PROFILER_FILE_WRITE_BUFFER_SIZE (profiler->write_buffer_size)
361 typedef struct _ProfilerFileWriteBuffer {
362 struct _ProfilerFileWriteBuffer *next;
364 } ProfilerFileWriteBuffer;
366 struct _MonoProfiler {
369 MonoProfileFlags flags;
371 FILE_HANDLE_TYPE file;
374 guint64 start_counter;
378 MethodIdMapping *methods;
379 ClassIdMapping *classes;
381 GHashTable *loaded_assemblies;
382 GHashTable *loaded_modules;
383 GHashTable *loaded_appdomains;
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;
395 ProfilerFileWriteBuffer *write_buffers;
396 ProfilerFileWriteBuffer *current_write_buffer;
397 int write_buffer_size;
398 int current_write_position;
399 int full_write_buffers;
401 ProfilerHeapShotWriteJob *heap_shot_write_jobs;
402 ProfilerHeapShotHeapBuffers heap;
404 ProfilerExecutableMemoryRegions *executable_regions;
411 gboolean unreachable_objects;
415 static MonoProfiler *profiler;
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)
429 #define LOG_WRITER_THREAD(m)
432 #if DEBUG_LOGGING_PROFILER
433 static int event_counter = 0;
434 #define EVENT_MARK() printf ("[EVENT:%d]", ++ event_counter)
438 static ClassIdMappingElement*
439 class_id_mapping_element_get (MonoClass *klass) {
440 return g_hash_table_lookup (profiler->classes->table, (gconstpointer) klass);
443 static MethodIdMappingElement*
444 method_id_mapping_element_get (MonoMethod *method) {
445 return g_hash_table_lookup (profiler->methods->table, (gconstpointer) method);
448 #define BITS_TO_BYTES(v) do {\
454 static ClassIdMappingElement*
455 class_id_mapping_element_new (MonoClass *klass) {
456 ClassIdMappingElement *result = g_new (ClassIdMappingElement, 1);
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 ++;
465 result->data.bitmap.compact = 0;
466 result->data.layout.slots = CLASS_LAYOUT_NOT_INITIALIZED;
467 result->data.layout.references = CLASS_LAYOUT_NOT_INITIALIZED;
469 g_hash_table_insert (profiler->classes->table, klass, result);
471 #if (DEBUG_MAPPING_EVENTS)
472 printf ("Created new CLASS mapping element \"%s\" (%p)[%d]\n", result->name, klass, result->id);
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;
484 MonoClassField *field;
486 #if (DEBUG_CLASS_BITMAPS)
487 printf ("class_id_mapping_element_build_layout_bitmap: building layout for class %s: ", klass_id->name);
489 if (parent_class != NULL) {
490 parent_id = class_id_mapping_element_get (parent_class);
491 g_assert (parent_id != 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*/)
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;
508 number_of_reference_fields ++;
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);
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);
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;
523 number_of_reference_fields += field_id->data.layout.references;
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);
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]");
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);
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]");
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);
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]");
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]");
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);
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) {
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));
589 #if (DEBUG_CLASS_BITMAPS)
590 printf ("[copying %d bits from father bitmap]", size_of_father_bitmap);
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);
603 #if (DEBUG_CLASS_BITMAPS)
604 printf ("[starting filling iteration]\n");
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*/)
613 #if (DEBUG_CLASS_BITMAPS)
614 printf ("[Working on field %s]", mono_field_get_name (field));
616 if (MONO_TYPE_IS_REFERENCE (field_type)) {
617 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
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);
624 klass_id->data.bitmap.extended [field_slot >> 3] |= (1 << (field_slot & 7));
626 #if (DEBUG_CLASS_BITMAPS)
627 printf ("[reference at offset %d, slot %d]", field_offset, field_slot);
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);
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);
644 if (field_id->data.layout.references > 0) {
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);
653 klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
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);
664 klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
673 #if (DEBUG_CLASS_BITMAPS)
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)) {
685 if (klass_id->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
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);
704 result->name = g_strdup_printf ("%s (%s)", mono_method_get_name (method), 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);
713 result->data.code_start = NULL;
714 result->data.code_size = 0;
716 #if (DEBUG_MAPPING_EVENTS)
717 printf ("Created new METHOD mapping element \"%s\" (%p)[%d]\n", result->name, method, result->id);
724 method_id_mapping_element_destroy (gpointer element) {
725 MethodIdMappingElement *e = (MethodIdMappingElement*) element;
732 class_id_mapping_element_destroy (gpointer element) {
733 ClassIdMappingElement *e = (ClassIdMappingElement*) element;
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);
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;
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;
762 method_id_mapping_destroy (MethodIdMapping *map) {
763 g_hash_table_destroy (map->table);
768 class_id_mapping_destroy (ClassIdMapping *map) {
769 g_hash_table_destroy (map->table);
773 #if (DEBUG_LOAD_EVENTS)
775 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element);
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);
784 MONO_PROFILER_GET_CURRENT_COUNTER (element->load_start_counter);
785 g_hash_table_insert (table, item, element);
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);
795 g_assert (element != NULL);
796 MONO_PROFILER_GET_CURRENT_COUNTER (element->load_end_counter);
797 element->name = name;
798 element->loaded = TRUE;
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);
808 g_assert (element != NULL);
809 MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_start_counter);
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);
819 g_assert (element != NULL);
820 MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_end_counter);
821 element->unloaded = TRUE;
827 loaded_element_destroy (gpointer element) {
828 if (((LoadedElement*)element)->name)
829 g_free (((LoadedElement*)element)->name);
833 #if (DEBUG_LOAD_EVENTS)
835 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element) {
836 const char* item_name;
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));
849 g_assert_not_reached ();
852 if (element != NULL) {
853 item_name = element->name;
855 item_name = "<NULL>";
858 printf ("%s EVENT for %s (%s)\n", event_name, item_info, item_name);
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);
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);
887 for (buffer = result; buffer != NULL; buffer = buffer->next) {
888 ProfilerHeapShotObjectBuffer *last = buffer->next;
889 if ((last != NULL) && (last->first_unprocessed_slot == last->end)) {
891 profiler_heap_shot_object_buffers_destroy (last);
898 static ProfilerHeapShotWriteJob*
899 profiler_heap_shot_write_job_new (void) {
900 ProfilerHeapShotWriteJob *job = g_new (ProfilerHeapShotWriteJob, 1);
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);
917 profiler_heap_shot_write_job_add_buffer (ProfilerHeapShotWriteJob *job, gpointer value) {
918 ProfilerHeapShotWriteBuffer *buffer = g_new (ProfilerHeapShotWriteBuffer, 1);
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);
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);
939 profiler_heap_shot_write_job_free_buffers (ProfilerHeapShotWriteJob *job) {
940 ProfilerHeapShotWriteBuffer *buffer = job->buffers;
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);
955 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job);
958 profiler_process_heap_shot_write_jobs (void) {
959 gboolean done = FALSE;
962 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
963 ProfilerHeapShotWriteJob *previous_job = NULL;
964 ProfilerHeapShotWriteJob *next_job;
967 while (current_job != NULL) {
968 next_job = current_job->next_unwritten;
970 if (next_job != NULL) {
971 if (current_job->buffers != NULL) {
974 if (next_job->buffers == NULL) {
975 current_job->next_unwritten = NULL;
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;
989 previous_job = current_job;
990 current_job = next_job;
996 profiler_free_heap_shot_write_jobs (void) {
997 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
998 ProfilerHeapShotWriteJob *next_job;
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);
1005 current_job = current_job->next_unwritten;
1008 next_job = current_job->next;
1009 current_job->next = NULL;
1010 current_job = next_job;
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);
1016 next_job = current_job->next;
1017 g_free (current_job);
1018 current_job = next_job;
1024 profiler_destroy_heap_shot_write_jobs (void) {
1025 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1026 ProfilerHeapShotWriteJob *next_job;
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;
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);
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)
1050 #define STORE_ALLOCATED_OBJECT_MESSAGE1(d,o)
1051 #define STORE_ALLOCATED_OBJECT_MESSAGE2(d,o)
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 ++;\
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 ++;\
1066 static ProfilerPerThreadData*
1067 profiler_per_thread_data_new (guint32 buffer_size)
1069 ProfilerPerThreadData *data = g_new (ProfilerPerThreadData, 1);
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);
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);
1093 static ProfilerStatisticalData*
1094 profiler_statistical_data_new (guint32 buffer_size)
1096 ProfilerStatisticalData *data = g_new (ProfilerStatisticalData, 1);
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;
1107 profiler_statistical_data_destroy (ProfilerStatisticalData *data) {
1108 g_free (data->addresses);
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;
1118 //printf ("Added next buffer %p, to buffer %p\n", profiler->current_write_buffer->next, profiler->current_write_buffer);
1121 profiler->current_write_buffer = profiler->current_write_buffer->next;
1122 profiler->current_write_position = 0;
1123 profiler->full_write_buffers ++;
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;
1132 //printf ("Freeing write buffer %p, next is %p\n", current_buffer, next_buffer);
1134 g_free (current_buffer);
1135 current_buffer = next_buffer;
1139 #define WRITE_BYTE(b) do {\
1140 if (profiler->current_write_position >= PROFILER_FILE_WRITE_BUFFER_SIZE) {\
1141 profiler_add_write_buffer ();\
1143 profiler->current_write_buffer->buffer [profiler->current_write_position] = (b);\
1144 profiler->current_write_position ++;\
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;
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;
1161 WRITE_BUFFER (& (header [0]), 6);
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;
1168 if (profiler->current_write_position > 0) {
1169 WRITE_BUFFER (& (current_buffer->buffer [0]), profiler->current_write_position);
1173 profiler->current_write_buffer = profiler->write_buffers;
1174 profiler->current_write_position = 0;
1175 profiler->full_write_buffers = 0;
1179 #define SEVEN_BITS_MASK (0x7f)
1180 #define EIGHT_BIT_MASK (0x80)
1183 write_uint32 (guint32 value) {
1184 while (value > SEVEN_BITS_MASK) {
1185 WRITE_BYTE (value & SEVEN_BITS_MASK);
1188 WRITE_BYTE (value | EIGHT_BIT_MASK);
1191 write_uint64 (guint64 value) {
1192 while (value > SEVEN_BITS_MASK) {
1193 WRITE_BYTE (value & SEVEN_BITS_MASK);
1196 WRITE_BYTE (value | EIGHT_BIT_MASK);
1199 write_string (const char *string) {
1200 while (*string != 0) {
1201 WRITE_BYTE (*string);
1207 #define WRITE_HEAP_SHOT_JOB_VALUE(j,v) do {\
1208 if ((j)->cursor < (j)->end) {\
1209 *((j)->cursor) = (v);\
1212 profiler_heap_shot_write_job_add_buffer (j, v);\
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)))
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)
1220 #define UPDATE_JOB_BUFFER_CURSOR_MESSAGE()
1222 #define UPDATE_JOB_BUFFER_CURSOR() do {\
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]);\
1237 UPDATE_JOB_BUFFER_CURSOR_MESSAGE ();\
1241 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job) {
1242 ProfilerHeapShotWriteBuffer *buffer;
1245 guint64 end_counter;
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);
1255 buffer = job->buffers;
1256 cursor = & (buffer->buffer [0]);
1257 if (buffer->next != NULL) {
1258 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1262 if (cursor >= end) {
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);
1268 while (cursor != NULL) {
1269 gpointer value = *cursor;
1270 HeapProfilerJobValueCode code = GPOINTER_TO_UINT (value) & HEAP_CODE_MASK;
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;
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);
1283 g_assert (class_id != NULL);
1284 write_uint32 ((class_id->id << 2) | HEAP_CODE_FREE_OBJECT_CLASS);
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);
1292 } else if (code == HEAP_CODE_OBJECT) {
1293 guint32 references = GPOINTER_TO_UINT (*cursor);
1294 UPDATE_JOB_BUFFER_CURSOR ();
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);
1302 while (references > 0) {
1303 gpointer reference = *cursor;
1304 write_uint64 (GPOINTER_TO_UINT (reference));
1305 UPDATE_JOB_BUFFER_CURSOR ();
1307 #if DEBUG_HEAP_PROFILER
1308 printf ("profiler_heap_shot_write_block: inside object %p, wrote reference %p)\n", value, reference);
1312 g_assert_not_reached ();
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);
1322 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_HEAP);
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);
1331 write_element_load_block (LoadedElement *element, guint8 kind, gsize thread_id) {
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;
1342 write_element_unload_block (LoadedElement *element, guint8 kind, gsize thread_id) {
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;
1353 write_clock_data (void) {
1357 MONO_PROFILER_GET_CURRENT_COUNTER (counter);
1358 MONO_PROFILER_GET_CURRENT_TIME (time);
1360 write_uint64 (counter);
1361 write_uint64 (time);
1365 write_mapping_block (gsize thread_id, gboolean flushObjects) {
1366 ClassIdMappingElement *current_class;
1367 MethodIdMappingElement *current_method;
1369 if ((profiler->classes->unwritten == NULL) && (profiler->methods->unwritten == NULL))
1372 #if (DEBUG_MAPPING_EVENTS)
1373 printf ("[write_mapping_block][TID %ld] START\n", thread_id);
1376 write_clock_data ();
1377 write_uint64 (thread_id);
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);
1385 g_free (current_class->name);
1386 current_class->name = NULL;
1389 profiler->classes->unwritten = NULL;
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);
1402 g_free (current_method->name);
1403 current_method->name = NULL;
1406 profiler->methods->unwritten = NULL;
1408 write_clock_data ();
1409 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_MAPPING);
1411 #if (DEBUG_MAPPING_EVENTS)
1412 printf ("[write_mapping_block][TID %ld] END\n", thread_id);
1417 get_extended_event_value (ProfilerEventData *event, ProfilerEventData *next) {
1418 guint64 result = next->data.number;
1419 result |= (((guint64) event->value) << 32);
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)
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;\
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));\
1444 static ProfilerEventData*
1445 write_event (ProfilerEventData *event) {
1446 ProfilerEventData *next = event + 1;
1447 gboolean write_event_value = TRUE;
1450 guint64 event_value;
1452 event_value = event->value;
1453 if (event_value > MAX_EVENT_VALUE) {
1454 event_value = get_extended_event_value (event, next);
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;
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);
1467 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT);
1470 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT);
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;
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);
1480 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT);
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);
1487 #if (DEBUG_LOGGING_PROFILER)
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);
1494 WRITE_BYTE (event_code);
1495 write_uint64 (event_data);
1496 if (write_event_value) {
1497 write_uint64 (event_value);
1504 write_thread_data_block (ProfilerPerThreadData *data) {
1505 ProfilerEventData *start = data->first_unwritten_event;
1506 ProfilerEventData *end = data->first_unmapped_event;
1511 write_clock_data ();
1512 write_uint64 (data->thread_id);
1514 write_uint64 (data->start_event_counter);
1516 while (start < end) {
1517 start = write_event (start);
1520 data->first_unwritten_event = end;
1522 write_clock_data ();
1523 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_EVENTS);
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;
1531 result->file_offset = file_offset;
1532 result->file_name = g_strdup (file_name);
1534 result->is_new = TRUE;
1539 profiler_executable_memory_region_destroy (ProfilerExecutableMemoryRegionData *data) {
1540 if (data->file_name != NULL) {
1541 g_free (data->file_name);
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;
1557 profiler_executable_memory_regions_destroy (ProfilerExecutableMemoryRegions *regions) {
1560 for (i = 0; i < regions->regions_count; i++) {
1561 profiler_executable_memory_region_destroy (regions->regions [i]);
1563 g_free (regions->regions);
1567 static ProfilerExecutableMemoryRegionData*
1568 find_address_region (ProfilerExecutableMemoryRegions *regions, gpointer address) {
1570 int high_index = regions->regions_count;
1571 int middle_index = 0;
1572 ProfilerExecutableMemoryRegionData *middle_region = regions->regions [0];
1574 if ((regions->regions_count == 0) || (regions->regions [low_index]->start > address) || (regions->regions [high_index - 1]->end < address)) {
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);
1580 while (low_index != high_index) {
1581 middle_index = low_index + ((high_index - low_index) / 2);
1582 middle_region = regions->regions [middle_index];
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);
1586 if (middle_region->start > address) {
1587 if (middle_index > 0) {
1588 high_index = middle_index;
1592 } else if (middle_region->end < address) {
1593 if (middle_index < regions->regions_count - 1) {
1594 low_index = middle_index + 1;
1599 return middle_region;
1603 if ((middle_region == NULL) || (middle_region->start > address) || (middle_region->end < address)) {
1606 return middle_region;
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;
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 ++;
1625 restore_region_ids (ProfilerExecutableMemoryRegions *old_regions, ProfilerExecutableMemoryRegions *new_regions) {
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;
1642 old_region->is_new = TRUE;
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);
1656 sort_regions (ProfilerExecutableMemoryRegions *regions) {
1657 qsort (regions->regions, regions->regions_count, sizeof (ProfilerExecutableMemoryRegionData *), compare_regions);
1660 //FIXME: make also Win32 and BSD variants
1661 #define MAPS_BUFFER_SIZE 4096
1664 update_regions_buffer (int fd, char *buffer) {
1665 ssize_t result = read (fd, buffer, MAPS_BUFFER_SIZE);
1667 if (result == MAPS_BUFFER_SIZE) {
1669 } else if (result >= 0) {
1670 *(buffer + result) = 0;
1678 #define GOTO_NEXT_CHAR(c,b,fd) do {\
1680 if (((c) - (b) >= MAPS_BUFFER_SIZE) || ((*(c) == 0) && ((c) != (b)))) {\
1681 update_regions_buffer ((fd), (b));\
1686 static int hex_digit_value (char c) {
1687 if ((c >= '0') && (c <= '9')) {
1689 } else if ((c >= 'a') && (c <= 'f')) {
1691 } else if ((c >= 'A') && (c <= 'F')) {
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;
1726 const char *map_line_parser_state [] = {
1734 "BLANK_BEFORE_FILENAME",
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;
1745 char *start_filename = NULL;
1746 char *end_filename = NULL;
1747 gboolean is_executable = FALSE;
1748 gboolean done = FALSE;
1754 case MAP_LINE_PARSER_STATE_START_ADDRESS:
1756 start_address <<= 4;
1757 start_address |= hex_digit_value (c);
1758 } else if (c == '-') {
1759 state = MAP_LINE_PARSER_STATE_END_ADDRESS;
1761 state = MAP_LINE_PARSER_STATE_INVALID;
1764 case MAP_LINE_PARSER_STATE_END_ADDRESS:
1767 end_address |= hex_digit_value (c);
1768 } else if (isblank (c)) {
1769 state = MAP_LINE_PARSER_STATE_PERMISSIONS;
1771 state = MAP_LINE_PARSER_STATE_INVALID;
1774 case MAP_LINE_PARSER_STATE_PERMISSIONS:
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;
1783 case MAP_LINE_PARSER_STATE_OFFSET:
1786 offset |= hex_digit_value (c);
1787 } else if (isblank (c)) {
1788 state = MAP_LINE_PARSER_STATE_DEVICE;
1790 state = MAP_LINE_PARSER_STATE_INVALID;
1793 case MAP_LINE_PARSER_STATE_DEVICE:
1795 state = MAP_LINE_PARSER_STATE_INODE;
1796 } else if ((c != ':') && ! isxdigit (c)) {
1797 state = MAP_LINE_PARSER_STATE_INVALID;
1800 case MAP_LINE_PARSER_STATE_INODE:
1802 state = MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME;
1803 } else if (! isdigit (c)) {
1804 state = MAP_LINE_PARSER_STATE_INVALID;
1807 case MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME:
1809 state = MAP_LINE_PARSER_STATE_FILENAME;
1810 start_filename = current;
1811 } else if (! isblank (c)) {
1812 state = MAP_LINE_PARSER_STATE_INVALID;
1815 case MAP_LINE_PARSER_STATE_FILENAME:
1817 state = MAP_LINE_PARSER_STATE_DONE;
1819 end_filename = current;
1822 case MAP_LINE_PARSER_STATE_DONE:
1823 if (done && is_executable) {
1825 append_region (regions, (gpointer) start_address, (gpointer) end_address, offset, start_filename);
1828 case MAP_LINE_PARSER_STATE_INVALID:
1830 state = MAP_LINE_PARSER_STATE_DONE;
1840 GOTO_NEXT_CHAR(current, buffer, fd);
1846 scan_process_regions (ProfilerExecutableMemoryRegions *regions) {
1851 fd = open ("/proc/self/maps", O_RDONLY);
1856 buffer = malloc (MAPS_BUFFER_SIZE);
1857 update_regions_buffer (fd, buffer);
1859 while (current != NULL) {
1860 current = parse_map_line (regions, fd, buffer, current);
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;
1878 refresh_memory_regions (void) {
1879 ProfilerExecutableMemoryRegions *new_regions = profiler_executable_memory_regions_new ();
1880 ProfilerExecutableMemoryRegions *old_regions = profiler->executable_regions;
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.");
1889 // This marks the region "sub-block"
1890 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_REGIONS);
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);
1899 write_uint32 (region->id);
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;
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);
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);
1922 // Finally, free the old ones, and replace them
1923 profiler_executable_memory_regions_destroy (old_regions);
1924 profiler->executable_regions = new_regions;
1928 flush_all_mappings (gboolean flushObjects);
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;
1937 if (end_index > data->end_index)
1938 end_index = data->end_index;
1940 if (start_index == end_index)
1943 write_clock_data ();
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);
1950 MonoMethod *method = mono_jit_info_get_method (ji);
1951 MethodIdMappingElement *element = method_id_mapping_element_get (method);
1953 if (element != NULL) {
1954 #if DEBUG_STATISTICAL_PROFILER
1955 printf ("[write_statistical_data_block] Wrote method %d\n", element->id);
1957 write_uint32 ((element->id << 2) | MONO_PROFILER_STATISTICAL_CODE_METHOD);
1959 #if DEBUG_STATISTICAL_PROFILER
1960 printf ("[write_statistical_data_block] Wrote unknown method %p\n", method);
1962 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_METHOD);
1965 ProfilerExecutableMemoryRegionData *region = find_address_region (profiler->executable_regions, address);
1967 if (region == NULL && ! regions_refreshed) {
1968 refresh_memory_regions ();
1969 regions_refreshed = TRUE;
1970 region = find_address_region (profiler->executable_regions, address);
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));
1977 write_uint32 ((region->id << 2) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION);
1978 write_uint32 (GPOINTER_TO_INT (address) - GPOINTER_TO_INT (region->start));
1980 #if DEBUG_STATISTICAL_PROFILER
1981 printf ("[write_statistical_data_block] Wrote unknown unmanaged hit %p\n", address);
1983 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION);
1984 write_uint64 (GPOINTER_TO_INT (address));
1988 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_END);
1990 write_clock_data ();
1992 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL);
1996 write_intro_block (void) {
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);
2006 write_end_block (void) {
2008 write_uint64 (profiler->end_counter);
2009 write_uint64 (profiler->end_time);
2010 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_END);
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;
2019 #if (DEBUG_LOGGING_PROFILER)
2020 printf ("[update_mapping][TID %ld] START\n", data->thread_id);
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);
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);
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);
2042 #if (DEBUG_LOGGING_PROFILER)
2043 printf ("[update_mapping][TID %ld] END\n", data->thread_id);
2048 flush_all_mappings (gboolean flushObjects) {
2049 ProfilerPerThreadData *data;
2051 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2052 update_mapping (data);
2054 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2055 write_mapping_block (data->thread_id, flushObjects);
2060 flush_full_event_data_buffer (ProfilerPerThreadData *data) {
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);
2068 write_thread_data_block (data);
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;
2079 #define GET_NEXT_FREE_EVENT(d,e) {\
2080 if ((d)->next_free_event >= (d)->end_event) {\
2081 flush_full_event_data_buffer (d);\
2083 (e) = (d)->next_free_event;\
2084 (d)->next_free_event ++;\
2088 flush_everything (gboolean flushObjects) {
2089 ProfilerPerThreadData *data;
2091 flush_all_mappings (flushObjects);
2092 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2093 write_thread_data_block (data);
2095 write_statistical_data_block (profiler->statistical_data);
2098 #define RESULT_TO_LOAD_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_LOADED_EVENT_SUCCESS:MONO_PROFILER_LOADED_EVENT_FAILURE)
2100 appdomain_start_load (MonoProfiler *profiler, MonoDomain *domain) {
2102 loaded_element_load_start (profiler->loaded_appdomains, domain);
2107 appdomain_end_load (MonoProfiler *profiler, MonoDomain *domain, int result) {
2109 LoadedElement *element;
2111 name = g_strdup_printf ("%d", mono_domain_get_id (domain));
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 ());
2119 appdomain_start_unload (MonoProfiler *profiler, MonoDomain *domain) {
2121 loaded_element_unload_start (profiler->loaded_appdomains, domain);
2122 flush_everything (FALSE);
2127 appdomain_end_unload (MonoProfiler *profiler, MonoDomain *domain) {
2128 LoadedElement *element;
2131 element = loaded_element_unload_end (profiler->loaded_appdomains, domain);
2132 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN, CURRENT_THREAD_ID ());
2137 module_start_load (MonoProfiler *profiler, MonoImage *module) {
2139 loaded_element_load_start (profiler->loaded_modules, module);
2144 module_end_load (MonoProfiler *profiler, MonoImage *module, int result) {
2146 MonoAssemblyName aname;
2147 LoadedElement *element;
2149 mono_assembly_fill_assembly_name (module, &aname);
2150 name = mono_stringify_assembly_name (&aname);
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 ());
2158 module_start_unload (MonoProfiler *profiler, MonoImage *module) {
2160 loaded_element_unload_start (profiler->loaded_modules, module);
2161 flush_everything (FALSE);
2166 module_end_unload (MonoProfiler *profiler, MonoImage *module) {
2167 LoadedElement *element;
2170 element = loaded_element_unload_end (profiler->loaded_modules, module);
2171 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_MODULE, CURRENT_THREAD_ID ());
2176 assembly_start_load (MonoProfiler *profiler, MonoAssembly *assembly) {
2178 loaded_element_load_start (profiler->loaded_assemblies, assembly);
2183 assembly_end_load (MonoProfiler *profiler, MonoAssembly *assembly, int result) {
2185 MonoAssemblyName aname;
2186 LoadedElement *element;
2188 mono_assembly_fill_assembly_name (mono_assembly_get_image (assembly), &aname);
2189 name = mono_stringify_assembly_name (&aname);
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 ());
2197 assembly_start_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
2199 loaded_element_unload_start (profiler->loaded_assemblies, assembly);
2200 flush_everything (FALSE);
2204 assembly_end_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
2205 LoadedElement *element;
2208 element = loaded_element_unload_end (profiler->loaded_assemblies, assembly);
2209 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY, CURRENT_THREAD_ID ());
2213 #if (DEBUG_LOGGING_PROFILER)
2215 class_event_code_to_string (MonoProfilerClassEvents 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 "";
2225 method_event_code_to_string (MonoProfilerClassEvents 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 "";
2234 number_event_code_to_string (MonoProfilerEvents 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 "";
2245 event_result_to_string (MonoProfilerEventResult 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 "";
2253 event_kind_to_string (MonoProfilerEventKind 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 "";
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",
2265 event->data.address,
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),
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",
2279 event->data.address,
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),
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));
2292 printf ("[TID %ld] NUMBER[%ld] event [%p] %s:%s[%d-%d-%d] %ld\n",
2294 (guint64) event->data.number,
2296 number_event_code_to_string (event->code),
2297 event_kind_to_string (event->kind),
2304 #define LOG_EVENT(tid,ev,val) print_event_data ((tid),(ev),(val))
2306 #define LOG_EVENT(tid,ev,val)
2309 #define RESULT_TO_EVENT_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_EVENT_RESULT_SUCCESS:MONO_PROFILER_EVENT_RESULT_FAILURE)
2311 #define STORE_EVENT_ITEM_COUNTER(p,i,dt,c,k) do {\
2312 ProfilerPerThreadData *data;\
2313 ProfilerEventData *event;\
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);\
2323 delta = counter - data->last_event_counter;\
2324 if (delta < MAX_EVENT_VALUE) {\
2325 event->value = delta;\
2327 ProfilerEventData *extension = data->next_free_event;\
2328 data->next_free_event ++;\
2329 event->value = delta >> 32;\
2330 extension->data.number = delta & 0xffffffff;\
2332 data->last_event_counter = counter;\
2333 LOG_EVENT (data->thread_id, event, delta);\
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);\
2344 if ((v) < MAX_EVENT_VALUE) {\
2345 event->value = (v);\
2347 ProfilerEventData *extension = data->next_free_event;\
2348 data->next_free_event ++;\
2349 event->value = (v) >> 32;\
2350 extension->data.number = (v) & 0xffffffff;\
2352 LOG_EVENT (data->thread_id, event, (v));\
2354 #define STORE_EVENT_NUMBER_COUNTER(p,n,dt,c,k) do {\
2355 ProfilerPerThreadData *data;\
2356 ProfilerEventData *event;\
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);\
2366 delta = counter - data->last_event_counter;\
2367 if (delta < MAX_EVENT_VALUE) {\
2368 event->value = delta;\
2370 ProfilerEventData *extension = data->next_free_event;\
2371 data->next_free_event ++;\
2372 event->value = delta >> 32;\
2373 extension->data.number = delta & 0xffffffff;\
2375 data->last_event_counter = counter;\
2376 LOG_EVENT (data->thread_id, event, delta);\
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);\
2387 if ((v) < MAX_EVENT_VALUE) {\
2388 event->value = (v);\
2390 ProfilerEventData *extension = data->next_free_event;\
2391 data->next_free_event ++;\
2392 event->value = (v) >> 32;\
2393 extension->data.number = (v) & 0xffffffff;\
2395 LOG_EVENT (data->thread_id, event, (v));\
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);
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);
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);
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);
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);
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);
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);
2439 if (op_write_native_code (name, code_start, code_size)) {
2440 g_warning ("Problem calling op_write_native_code\n");
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);
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);
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);
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);
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);
2474 object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) {
2475 ProfilerPerThreadData *thread_data;
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);
2486 statistical_hit (MonoProfiler *profiler, guchar *ip, void *context) {
2487 ProfilerStatisticalData *data;
2491 data = profiler->statistical_data;
2492 index = InterlockedIncrement (&data->next_free_index);
2494 if (index <= data->end_index) {
2495 data->addresses [index - 1] = (gpointer) ip;
2497 /* Check if we are the one that must swap the buffers */
2498 if (index == data->end_index + 1) {
2499 ProfilerStatisticalData *new_data;
2501 /* In the *impossible* case that the writer thread has not finished yet, */
2502 /* loop waiting for it and meanwhile lose all statistical events... */
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);
2510 profiler->statistical_data_ready = data;
2511 profiler->statistical_data = new_data;
2512 profiler->statistical_data_second_buffer = NULL;
2513 WRITER_EVENT_RAISE ();
2516 /* Loop again, hoping to acquire a free slot this time */
2519 } while (data == NULL);
2522 static MonoProfilerEvents
2523 gc_event_code_from_profiler_event (MonoGCEvent 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;
2535 g_assert_not_reached ();
2540 static MonoProfilerEventKind
2541 gc_event_kind_from_profiler_event (MonoGCEvent 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;
2552 g_assert_not_reached ();
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]);
2569 profiler_heap_buffers_clear (ProfilerHeapShotHeapBuffers *heap) {
2570 heap->buffers = NULL;
2572 heap->current = NULL;
2573 heap->first_free_slot = NULL;
2576 profiler_heap_buffers_free (ProfilerHeapShotHeapBuffers *heap) {
2577 ProfilerHeapShotHeapBuffer *current = heap->buffers;
2578 while (current != NULL) {
2579 ProfilerHeapShotHeapBuffer *next = current->next;
2583 profiler_heap_buffers_clear (heap);
2587 report_object_references (gpointer *start, ClassIdMappingElement *layout, ProfilerHeapShotWriteJob *job) {
2588 int reported_references = 0;
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;
2597 slot_has_reference = FALSE;
2600 if (layout->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
2601 slot_has_reference = TRUE;
2603 slot_has_reference = FALSE;
2607 if (slot_has_reference) {
2608 gpointer field = start [slot];
2610 if ((field != NULL) && mono_object_is_alive (field)) {
2611 reported_references ++;
2612 WRITE_HEAP_SHOT_JOB_VALUE (job, field);
2617 return reported_references;
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;
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;
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);
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);
2640 if (! mono_class_is_valuetype (element_class)) {
2641 int length = mono_array_length (array);
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);
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);
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);
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));
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);
2669 if (class_id->data.layout.references > 0) {
2670 reference_counter += report_object_references ((gpointer) (obj + sizeof (MonoObject)), class_id, job);
2674 *reference_counter_location = GINT_TO_POINTER (reference_counter);
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);
2682 #if DEBUG_HEAP_PROFILER
2683 printf ("profiler_heap_report_object_unreachable: at job %p writing klass %p\n", job, klass);
2685 WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, klass, HEAP_CODE_FREE_OBJECT_CLASS);
2687 #if DEBUG_HEAP_PROFILER
2688 printf ("profiler_heap_report_object_unreachable: at job %p writing size %p\n", job, GUINT_TO_POINTER (size));
2690 WRITE_HEAP_SHOT_JOB_VALUE (job, GUINT_TO_POINTER (size));
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;
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;
2708 heap->first_free_slot = &(heap->current->buffer [0]);
2711 *(heap->first_free_slot) = obj;
2712 heap->first_free_slot ++;
2713 profiler_heap_report_object_reachable (job, obj);
2717 profiler_heap_pop_object_from_end (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject** current_slot) {
2718 while (heap->first_free_slot != current_slot) {
2721 if (heap->first_free_slot > heap->current->start_slot) {
2722 heap->first_free_slot --;
2724 heap->current = heap->current->previous;
2725 g_assert (heap->current != NULL);
2726 heap->first_free_slot = heap->current->end_slot - 1;
2729 obj = *(heap->first_free_slot);
2731 if (mono_object_is_alive (obj)) {
2732 profiler_heap_report_object_reachable (job, obj);
2735 profiler_heap_report_object_unreachable (job, obj);
2742 profiler_heap_scan (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job) {
2743 ProfilerHeapShotHeapBuffer *current_buffer = heap->buffers;
2744 MonoObject** current_slot = current_buffer->start_slot;
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);
2751 profiler_heap_report_object_unreachable (job, obj);
2752 *current_slot = profiler_heap_pop_object_from_end (heap, job, current_slot);
2755 if (*current_slot != NULL) {
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) {
2764 g_assert_not_reached ();
2766 current_slot = current_buffer->start_slot;
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;
2779 MONO_PROFILER_GET_CURRENT_COUNTER (job->start_counter);
2780 MONO_PROFILER_GET_CURRENT_TIME (job->start_time);
2782 profiler_heap_scan (&(profiler->heap), job);
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);
2793 if (mono_object_is_alive (obj)) {
2794 profiler_heap_add_object (&(profiler->heap), job, obj);
2796 profiler_heap_report_object_unreachable (job, obj);
2799 buffer->first_unprocessed_slot = cursor;
2802 MONO_PROFILER_GET_CURRENT_COUNTER (job->end_counter);
2803 MONO_PROFILER_GET_CURRENT_TIME (job->end_time);
2805 profiler_add_heap_shot_write_job (job);
2806 profiler_free_heap_shot_write_jobs ();
2807 WRITER_EVENT_RAISE ();
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);
2816 /* called at the end of the program */
2818 profiler_shutdown (MonoProfiler *prof)
2820 ProfilerPerThreadData* current_thread_data;
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;
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 ();
2838 MONO_PROFILER_GET_CURRENT_TIME (profiler->end_time);
2839 MONO_PROFILER_GET_CURRENT_COUNTER (profiler->end_counter);
2841 flush_everything (FALSE);
2846 g_free (profiler->file_name);
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);
2854 FREE_PROFILER_THREAD_DATA ();
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);
2859 if (profiler->statistical_data != NULL) {
2860 profiler_statistical_data_destroy (profiler->statistical_data);
2862 if (profiler->statistical_data_ready != NULL) {
2863 profiler_statistical_data_destroy (profiler->statistical_data_ready);
2865 if (profiler->statistical_data_second_buffer != NULL) {
2866 profiler_statistical_data_destroy (profiler->statistical_data_second_buffer);
2868 if (profiler->executable_regions != NULL) {
2869 profiler_executable_memory_regions_destroy (profiler->executable_regions);
2872 profiler_heap_buffers_free (&(profiler->heap));
2874 profiler_free_write_buffers ();
2875 profiler_destroy_heap_shot_write_jobs ();
2877 DELETE_PROFILER_MUTEX ();
2880 if (profiler->action_flags.oprofile) {
2889 #define DEFAULT_ARGUMENTS "s"
2891 setup_user_options (const char *arguments) {
2892 gchar **arguments_array, **current_argument;
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;
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;
2913 arguments_array = g_strsplit (arguments, ",", -1);
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, "=");
2919 if (equals != NULL) {
2920 int equals_position = equals - argument;
2922 if (! (strncmp (argument, "per-thread-buffer-size", equals_position) && strncmp (argument, "tbs", equals_position))) {
2923 int value = atoi (equals + 1);
2925 profiler->per_thread_buffer_size = value;
2927 } else if (! (strncmp (argument, "statistical-thread-buffer-size", equals_position) && strncmp (argument, "sbs", equals_position))) {
2928 int value = atoi (equals + 1);
2930 profiler->statistical_buffer_size = value;
2932 } else if (! (strncmp (argument, "write-buffer-size", equals_position) && strncmp (argument, "wbs", equals_position))) {
2933 int value = atoi (equals + 1);
2935 profiler->write_buffer_size = value;
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);
2942 g_warning ("Cannot parse valued argument %s\n", argument);
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;
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");
2973 } else if (strcmp (argument, "logging")) {
2974 g_warning ("Cannot parse flag argument %s\n", argument);
2979 g_free (arguments_array);
2981 if (profiler->file_name == NULL) {
2982 profiler->file_name = g_strdup ("profiler-log.prof");
2988 data_writer_thread (gpointer nothing) {
2990 ProfilerStatisticalData *statistical_data;
2993 LOG_WRITER_THREAD ("data_writer_thread: going to sleep");
2994 WRITER_EVENT_WAIT ();
2995 LOG_WRITER_THREAD ("data_writer_thread: just woke up");
2997 statistical_data = profiler->statistical_data_ready;
2998 done = (statistical_data == NULL) && (profiler->heap_shot_write_jobs == NULL);
3001 LOG_WRITER_THREAD ("data_writer_thread: acquiring lock and writing data");
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");
3009 if (statistical_data != NULL) {
3010 LOG_WRITER_THREAD ("data_writer_thread: writing statistical data...");
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;
3018 LOG_WRITER_THREAD ("data_writer_thread: wrote statistical data");
3021 profiler_process_heap_shot_write_jobs ();
3024 LOG_WRITER_THREAD ("data_writer_thread: wrote data and released lock");
3028 if (profiler->terminate_writer_thread) {
3029 LOG_WRITER_THREAD ("data_writer_thread: exiting thread");
3037 mono_profiler_startup (const char *desc);
3039 /* the entry point (mono_profiler_load?) */
3041 mono_profiler_startup (const char *desc)
3043 profiler = g_new0 (MonoProfiler, 1);
3045 setup_user_options ((desc != NULL) ? desc : "");
3047 INITIALIZE_PROFILER_MUTEX ();
3048 MONO_PROFILER_GET_CURRENT_TIME (profiler->start_time);
3049 MONO_PROFILER_GET_CURRENT_COUNTER (profiler->start_counter);
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);
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);
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;
3066 profiler->executable_regions = profiler_executable_memory_regions_new ();
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));
3072 profiler_heap_buffers_clear (&(profiler->heap));
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");
3080 ALLOCATE_PROFILER_THREAD_DATA ();
3084 write_intro_block ();
3086 mono_profiler_install (profiler, profiler_shutdown);
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);
3104 mono_profiler_install_jit_end (method_jit_result);
3107 mono_profiler_set_events (profiler->flags);