2 #include <mono/metadata/profiler.h>
3 #include <mono/metadata/class.h>
4 #include <mono/metadata/assembly.h>
5 #include <mono/metadata/loader.h>
6 #include <mono/metadata/threads.h>
7 #include <mono/metadata/debug-helpers.h>
8 #include <mono/metadata/mono-gc.h>
9 #include <mono/io-layer/atomic.h>
18 #define HAS_OPROFILE 0
21 #include <libopagent.h>
24 // Needed for heap analysis
25 extern gboolean mono_object_is_alive (MonoObject* obj);
28 MONO_PROFILER_FILE_BLOCK_KIND_INTRO = 1,
29 MONO_PROFILER_FILE_BLOCK_KIND_END = 2,
30 MONO_PROFILER_FILE_BLOCK_KIND_MAPPING = 3,
31 MONO_PROFILER_FILE_BLOCK_KIND_LOADED = 4,
32 MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED = 5,
33 MONO_PROFILER_FILE_BLOCK_KIND_EVENTS = 6,
34 MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL = 7,
35 MONO_PROFILER_FILE_BLOCK_KIND_HEAP = 8
36 } MonoProfilerFileBlockKind;
38 #define MONO_PROFILER_LOADED_EVENT_MODULE 1
39 #define MONO_PROFILER_LOADED_EVENT_ASSEMBLY 2
40 #define MONO_PROFILER_LOADED_EVENT_APPDOMAIN 4
41 #define MONO_PROFILER_LOADED_EVENT_SUCCESS 8
42 #define MONO_PROFILER_LOADED_EVENT_FAILURE 16
45 MONO_PROFILER_EVENT_DATA_TYPE_OTHER = 0,
46 MONO_PROFILER_EVENT_DATA_TYPE_METHOD = 1,
47 MONO_PROFILER_EVENT_DATA_TYPE_CLASS = 2
48 } MonoProfilerEventDataType;
50 typedef struct _ProfilerEventData {
55 unsigned int data_type:2;
58 unsigned int value:26;
61 #define EXTENDED_EVENT_VALUE_SHIFT (26)
62 #define MAX_EVENT_VALUE ((1<<EXTENDED_EVENT_VALUE_SHIFT)-1)
63 #define MAX_EXTENDED_EVENT_VALUE ((((guint64))MAX_EVENT_VALUE<<32)|((guint64)0xffffffff))
66 MONO_PROFILER_EVENT_METHOD_JIT = 0,
67 MONO_PROFILER_EVENT_METHOD_FREED = 1,
68 MONO_PROFILER_EVENT_METHOD_CALL = 2
69 } MonoProfilerMethodEvents;
71 MONO_PROFILER_EVENT_CLASS_LOAD = 0,
72 MONO_PROFILER_EVENT_CLASS_UNLOAD = 1,
73 MONO_PROFILER_EVENT_CLASS_EXCEPTION = 2,
74 MONO_PROFILER_EVENT_CLASS_ALLOCATION = 3
75 } MonoProfilerClassEvents;
77 MONO_PROFILER_EVENT_RESULT_SUCCESS = 0,
78 MONO_PROFILER_EVENT_RESULT_FAILURE = 4
79 } MonoProfilerEventResult;
80 #define MONO_PROFILER_EVENT_RESULT_MASK MONO_PROFILER_EVENT_RESULT_FAILURE
82 MONO_PROFILER_EVENT_THREAD = 1,
83 MONO_PROFILER_EVENT_GC_COLLECTION = 2,
84 MONO_PROFILER_EVENT_GC_MARK = 3,
85 MONO_PROFILER_EVENT_GC_SWEEP = 4,
86 MONO_PROFILER_EVENT_GC_RESIZE = 5,
87 MONO_PROFILER_EVENT_GC_STOP_WORLD = 6,
88 MONO_PROFILER_EVENT_GC_START_WORLD = 7
91 MONO_PROFILER_EVENT_KIND_START = 0,
92 MONO_PROFILER_EVENT_KIND_END = 1
93 } MonoProfilerEventKind;
95 #define MONO_PROFILER_GET_CURRENT_TIME(t) {\
96 struct timeval current_time;\
97 gettimeofday (¤t_time, NULL);\
98 (t) = (((guint64)current_time.tv_sec) * 1000000) + current_time.tv_usec;\
101 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) MONO_PROFILER_GET_CURRENT_TIME ((c));
103 static __inline__ guint64 rdtsc(void) {
105 __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
106 return ((guint64) lo) | (((guint64) hi) << 32);
108 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) {\
114 #define CLASS_LAYOUT_PACKED_BITMAP_SIZE 64
115 #define CLASS_LAYOUT_NOT_INITIALIZED (0xFFFF)
118 HEAP_CODE_OBJECT = 1,
119 HEAP_CODE_FREE_OBJECT_CLASS = 2,
121 } HeapProfilerJobValueCode;
122 typedef struct _MonoProfilerClassData {
131 } MonoProfilerClassData;
133 typedef struct _MonoProfilerMethodData {
136 } MonoProfilerMethodData;
138 typedef struct _ClassIdMappingElement {
142 struct _ClassIdMappingElement *next_unwritten;
143 MonoProfilerClassData data;
144 } ClassIdMappingElement;
146 typedef struct _MethodIdMappingElement {
150 struct _MethodIdMappingElement *next_unwritten;
151 MonoProfilerMethodData data;
152 } MethodIdMappingElement;
154 typedef struct _ClassIdMapping {
156 ClassIdMappingElement *unwritten;
160 typedef struct _MethodIdMapping {
162 MethodIdMappingElement *unwritten;
166 typedef struct _LoadedElement {
168 guint64 load_start_counter;
169 guint64 load_end_counter;
170 guint64 unload_start_counter;
171 guint64 unload_end_counter;
175 guint8 unload_written;
178 #define PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE 1024
179 #define PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE 4096
180 #define PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE 4096
182 typedef struct _ProfilerHeapShotObjectBuffer {
183 struct _ProfilerHeapShotObjectBuffer *next;
184 MonoObject **next_free_slot;
186 MonoObject **first_unprocessed_slot;
187 MonoObject *buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE];
188 } ProfilerHeapShotObjectBuffer;
190 typedef struct _ProfilerHeapShotHeapBuffer {
191 struct _ProfilerHeapShotHeapBuffer *next;
192 struct _ProfilerHeapShotHeapBuffer *previous;
193 MonoObject **start_slot;
194 MonoObject **end_slot;
195 MonoObject *buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE];
196 } ProfilerHeapShotHeapBuffer;
198 typedef struct _ProfilerHeapShotHeapBuffers {
199 ProfilerHeapShotHeapBuffer *buffers;
200 ProfilerHeapShotHeapBuffer *last;
201 ProfilerHeapShotHeapBuffer *current;
202 MonoObject **first_free_slot;
203 } ProfilerHeapShotHeapBuffers;
206 typedef struct _ProfilerHeapShotWriteBuffer {
207 struct _ProfilerHeapShotWriteBuffer *next;
208 gpointer buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE];
209 } ProfilerHeapShotWriteBuffer;
211 typedef struct _ProfilerHeapShotWriteJob {
212 struct _ProfilerHeapShotWriteJob *next;
213 struct _ProfilerHeapShotWriteJob *next_unwritten;
217 ProfilerHeapShotWriteBuffer *buffers;
218 ProfilerHeapShotWriteBuffer **last_next;
219 guint32 full_buffers;
220 gboolean heap_shot_was_signalled;
221 guint64 start_counter;
225 } ProfilerHeapShotWriteJob;
227 typedef struct _ProfilerPerThreadData {
228 ProfilerEventData *events;
229 ProfilerEventData *next_free_event;
230 ProfilerEventData *end_event;
231 ProfilerEventData *first_unwritten_event;
232 ProfilerEventData *first_unmapped_event;
233 guint64 start_event_counter;
234 guint64 last_event_counter;
236 ProfilerHeapShotObjectBuffer *heap_shot_object_buffers;
237 struct _ProfilerPerThreadData* next;
238 } ProfilerPerThreadData;
240 typedef struct _ProfilerStatisticalData {
244 int first_unwritten_index;
245 } ProfilerStatisticalData;
247 typedef struct _ProfilerExecutableMemoryRegionData {
254 } ProfilerExecutableMemoryRegionData;
256 typedef struct _ProfilerExecutableMemoryRegions {
257 ProfilerExecutableMemoryRegionData **regions;
258 guint32 regions_capacity;
259 guint32 regions_count;
261 } ProfilerExecutableMemoryRegions;
263 typedef struct _ProfilerUnmanagedFunction {
267 struct _ProfilerUnmanagedFunction *next_unwritten;
268 } ProfilerUnmanagedFunction;
270 typedef struct _ProfilerUnmanagedFunctions {
272 ProfilerUnmanagedFunction *unwritten_queue;
273 ProfilerUnmanagedFunction *unwritten_queue_end;
275 ProfilerUnmanagedFunction actual_unwritten_queue_end;
276 } ProfilerUnmanagedFunctions;
278 #ifndef PLATFORM_WIN32
279 #include <sys/types.h>
280 #include <sys/time.h>
281 #include <sys/stat.h>
285 #include <semaphore.h>
287 #define MUTEX_TYPE pthread_mutex_t
288 #define INITIALIZE_PROFILER_MUTEX() pthread_mutex_init (&(profiler->mutex), NULL)
289 #define DELETE_PROFILER_MUTEX() pthread_mutex_destroy (&(profiler->mutex))
290 #define LOCK_PROFILER() do {/*LOG_WRITER_THREAD ("LOCK_PROFILER");*/ pthread_mutex_lock (&(profiler->mutex));} while (0)
291 #define UNLOCK_PROFILER() do {/*LOG_WRITER_THREAD ("UNLOCK_PROFILER");*/ pthread_mutex_unlock (&(profiler->mutex));} while (0)
293 #define THREAD_TYPE pthread_t
294 #define CREATE_WRITER_THREAD(f) pthread_create (&(profiler->data_writer_thread), NULL, ((void*(*)(void*))f), NULL)
295 #define EXIT_THREAD() pthread_exit (NULL);
296 #define WAIT_WRITER_THREAD() pthread_join (profiler->data_writer_thread, NULL)
297 #define CURRENT_THREAD_ID() (gsize) pthread_self ()
299 #ifndef HAVE_KW_THREAD
300 static pthread_key_t pthread_profiler_key;
301 static pthread_once_t profiler_pthread_once = PTHREAD_ONCE_INIT;
303 make_pthread_profiler_key (void) {
304 (void) pthread_key_create (&pthread_profiler_key, NULL);
306 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) pthread_getspecific (pthread_profiler_key))
307 #define SET_PROFILER_THREAD_DATA(x) (void) pthread_setspecific (pthread_profiler_key, (x))
308 #define ALLOCATE_PROFILER_THREAD_DATA() (void) pthread_once (&profiler_pthread_once, make_pthread_profiler_key)
309 #define FREE_PROFILER_THREAD_DATA() (void) pthread_key_delete (pthread_profiler_key)
312 #define EVENT_TYPE sem_t
313 #define WRITER_EVENT_INIT() (void) sem_init (&(profiler->statistical_data_writer_event), 0, 0)
314 #define WRITER_EVENT_DESTROY() (void) sem_destroy (&(profiler->statistical_data_writer_event))
315 #define WRITER_EVENT_WAIT() (void) sem_wait (&(profiler->statistical_data_writer_event))
316 #define WRITER_EVENT_RAISE() (void) sem_post (&(profiler->statistical_data_writer_event))
319 #define FILE_HANDLE_TYPE FILE*
320 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
321 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
322 #define FLUSH_FILE() fflush (profiler->file)
323 #define CLOSE_FILE() fclose (profiler->file);
325 #define FILE_HANDLE_TYPE int
326 #define OPEN_FILE() profiler->file = open (profiler->file_name, O_WRONLY|O_CREAT|O_TRUNC, 0664);
327 #define WRITE_BUFFER(b,s) write (profiler->file, (b), (s))
329 #define CLOSE_FILE() close (profiler->file);
336 #define MUTEX_TYPE CRITICAL_SECTION
337 #define INITIALIZE_PROFILER_MUTEX() InitializeCriticalSection (&(profiler->mutex))
338 #define DELETE_PROFILER_MUTEX() DeleteCriticalSection (&(profiler->mutex))
339 #define LOCK_PROFILER() EnterCriticalSection (&(profiler->mutex))
340 #define UNLOCK_PROFILER() LeaveCriticalSection (&(profiler->mutex))
342 #define THREAD_TYPE HANDLE
343 #define CREATE_WRITER_THREAD(f) CreateThread (NULL, (1*1024*1024), (f), NULL, 0, NULL);
344 #define EXIT_THREAD() ExitThread (0);
345 #define WAIT_WRITER_THREAD() WaitForSingleObject (profiler->data_writer_thread, INFINITE)
346 #define CURRENT_THREAD_ID() (gsize) GetCurrentThreadId ()
348 #ifndef HAVE_KW_THREAD
349 static guint32 profiler_thread_id = -1;
350 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*)TlsGetValue (profiler_thread_id))
351 #define SET_PROFILER_THREAD_DATA(x) TlsSetValue (profiler_thread_id, (x));
352 #define ALLOCATE_PROFILER_THREAD_DATA() profiler_thread_id = TlsAlloc ()
353 #define FREE_PROFILER_THREAD_DATA() TlsFree (profiler_thread_id)
356 #define EVENT_TYPE HANDLE
357 #define WRITER_EVENT_INIT() profiler->statistical_data_writer_event = CreateEvent (NULL, FALSE, FALSE, NULL)
358 #define WRITER_EVENT_DESTROY() CloseHandle (profiler->statistical_data_writer_event)
359 #define WRITER_EVENT_WAIT() WaitForSingleObject (profiler->statistical_data_writer_event, INFINITE)
360 #define WRITER_EVENT_RAISE() SetEvent (profiler->statistical_data_writer_event)
362 #define FILE_HANDLE_TYPE FILE*
363 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
364 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
365 #define FLUSH_FILE() fflush (profiler->file)
366 #define CLOSE_FILE() fclose (profiler->file);
370 #ifdef HAVE_KW_THREAD
371 static __thread ProfilerPerThreadData * tls_profiler_per_thread_data;
372 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) tls_profiler_per_thread_data)
373 #define SET_PROFILER_THREAD_DATA(x) tls_profiler_per_thread_data = (x)
374 #define ALLOCATE_PROFILER_THREAD_DATA() /* nop */
375 #define FREE_PROFILER_THREAD_DATA() /* nop */
378 #define GET_PROFILER_THREAD_DATA(data) do {\
379 ProfilerPerThreadData *_result = LOOKUP_PROFILER_THREAD_DATA ();\
381 _result = profiler_per_thread_data_new (profiler->per_thread_buffer_size);\
383 _result->next = profiler->per_thread_data;\
384 profiler->per_thread_data = _result;\
386 SET_PROFILER_THREAD_DATA (_result);\
391 #define PROFILER_FILE_WRITE_BUFFER_SIZE (profiler->write_buffer_size)
392 typedef struct _ProfilerFileWriteBuffer {
393 struct _ProfilerFileWriteBuffer *next;
395 } ProfilerFileWriteBuffer;
397 struct _MonoProfiler {
400 MonoProfileFlags flags;
402 char *file_name_suffix;
403 FILE_HANDLE_TYPE file;
406 guint64 start_counter;
410 guint64 last_header_counter;
412 MethodIdMapping *methods;
413 ClassIdMapping *classes;
415 GHashTable *loaded_assemblies;
416 GHashTable *loaded_modules;
417 GHashTable *loaded_appdomains;
419 guint32 per_thread_buffer_size;
420 guint32 statistical_buffer_size;
421 ProfilerPerThreadData* per_thread_data;
422 ProfilerStatisticalData *statistical_data;
423 ProfilerStatisticalData *statistical_data_ready;
424 ProfilerStatisticalData *statistical_data_second_buffer;
425 ProfilerUnmanagedFunctions unmanaged_functions;
426 THREAD_TYPE data_writer_thread;
427 EVENT_TYPE statistical_data_writer_event;
428 gboolean terminate_writer_thread;
429 gboolean detach_writer_thread;
431 ProfilerFileWriteBuffer *write_buffers;
432 ProfilerFileWriteBuffer *current_write_buffer;
433 int write_buffer_size;
434 int current_write_position;
435 int full_write_buffers;
437 ProfilerHeapShotWriteJob *heap_shot_write_jobs;
438 ProfilerHeapShotHeapBuffers heap;
440 char *heap_shot_command_file_name;
441 int dump_next_heap_snapshots;
442 guint64 heap_shot_command_file_access_time;
443 gboolean heap_shot_was_signalled;
445 ProfilerExecutableMemoryRegions *executable_regions;
452 gboolean unreachable_objects;
456 static MonoProfiler *profiler;
458 #ifndef PLATFORM_WIN32
461 #ifdef MONO_ARCH_USE_SIGACTION
462 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, siginfo_t *info, void *context)
463 #elif defined(__sparc__)
464 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, void *sigctx)
466 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy)
470 SIG_HANDLER_SIGNATURE (gc_request_handler) {
471 profiler->heap_shot_was_signalled = TRUE;
472 WRITER_EVENT_RAISE ();
476 add_gc_request_handler (int signal_number)
480 #ifdef MONO_ARCH_USE_SIGACTION
481 sa.sa_sigaction = gc_request_handler;
482 sigemptyset (&sa.sa_mask);
483 sa.sa_flags = SA_SIGINFO;
485 sa.sa_handler = gc_request_handler;
486 sigemptyset (&sa.sa_mask);
490 g_assert (sigaction (signal_number, &sa, NULL) != -1);
496 #define DEBUG_LOAD_EVENTS 0
497 #define DEBUG_MAPPING_EVENTS 0
498 #define DEBUG_LOGGING_PROFILER 0
499 #define DEBUG_HEAP_PROFILER 0
500 #define DEBUG_CLASS_BITMAPS 0
501 #define DEBUG_STATISTICAL_PROFILER 0
502 #define DEBUG_WRITER_THREAD 0
503 #if (DEBUG_LOGGING_PROFILER || DEBUG_STATISTICAL_PROFILER || DEBUG_HEAP_PROFILER || DEBUG_WRITER_THREAD)
504 #define LOG_WRITER_THREAD(m) printf ("WRITER-THREAD-LOG %s\n", m)
506 #define LOG_WRITER_THREAD(m)
509 #if DEBUG_LOGGING_PROFILER
510 static int event_counter = 0;
511 #define EVENT_MARK() printf ("[EVENT:%d]", ++ event_counter)
515 static ClassIdMappingElement*
516 class_id_mapping_element_get (MonoClass *klass) {
517 return g_hash_table_lookup (profiler->classes->table, (gconstpointer) klass);
520 static MethodIdMappingElement*
521 method_id_mapping_element_get (MonoMethod *method) {
522 return g_hash_table_lookup (profiler->methods->table, (gconstpointer) method);
525 #define BITS_TO_BYTES(v) do {\
531 static ClassIdMappingElement*
532 class_id_mapping_element_new (MonoClass *klass) {
533 ClassIdMappingElement *result = g_new (ClassIdMappingElement, 1);
535 result->name = g_strdup_printf ("%s.%s", mono_class_get_namespace (klass), mono_class_get_name (klass));
536 result->klass = klass;
537 result->next_unwritten = profiler->classes->unwritten;
538 profiler->classes->unwritten = result;
539 result->id = profiler->classes->next_id;
540 profiler->classes->next_id ++;
542 result->data.bitmap.compact = 0;
543 result->data.layout.slots = CLASS_LAYOUT_NOT_INITIALIZED;
544 result->data.layout.references = CLASS_LAYOUT_NOT_INITIALIZED;
546 g_hash_table_insert (profiler->classes->table, klass, result);
548 #if (DEBUG_MAPPING_EVENTS)
549 printf ("Created new CLASS mapping element \"%s\" (%p)[%d]\n", result->name, klass, result->id);
555 class_id_mapping_element_build_layout_bitmap (MonoClass *klass, ClassIdMappingElement *klass_id) {
556 MonoClass *parent_class = mono_class_get_parent (klass);
557 int number_of_reference_fields = 0;
558 int max_offset_of_reference_fields = 0;
559 ClassIdMappingElement *parent_id;
561 MonoClassField *field;
563 #if (DEBUG_CLASS_BITMAPS)
564 printf ("class_id_mapping_element_build_layout_bitmap: building layout for class %s.%s: ", mono_class_get_namespace (klass), mono_class_get_name (klass));
567 if (parent_class != NULL) {
568 parent_id = class_id_mapping_element_get (parent_class);
569 g_assert (parent_id != NULL);
571 if (parent_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
572 #if (DEBUG_CLASS_BITMAPS)
573 printf ("[recursively building bitmap for father class]\n");
575 class_id_mapping_element_build_layout_bitmap (parent_class, parent_id);
582 while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
583 MonoType* field_type = mono_field_get_type (field);
584 // For now, skip static fields
585 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
588 if (MONO_TYPE_IS_REFERENCE (field_type)) {
589 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
590 if (field_offset > max_offset_of_reference_fields) {
591 max_offset_of_reference_fields = field_offset;
593 number_of_reference_fields ++;
595 MonoClass *field_class = mono_class_from_mono_type (field_type);
596 if (field_class && mono_class_is_valuetype (field_class)) {
597 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
598 g_assert (field_id != NULL);
600 if (field_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
601 if (field_id != klass_id) {
602 #if (DEBUG_CLASS_BITMAPS)
603 printf ("[recursively building bitmap for field %s]\n", mono_field_get_name (field));
605 class_id_mapping_element_build_layout_bitmap (field_class, field_id);
607 #if (DEBUG_CLASS_BITMAPS)
608 printf ("[breaking recursive bitmap build for field %s]", mono_field_get_name (field));
611 klass_id->data.bitmap.compact = 0;
612 klass_id->data.layout.slots = 0;
613 klass_id->data.layout.references = 0;
617 if (field_id->data.layout.references > 0) {
618 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
619 int max_offset_reference_in_field = (field_id->data.layout.slots - 1) * sizeof (gpointer);
621 if ((field_offset + max_offset_reference_in_field) > max_offset_of_reference_fields) {
622 max_offset_of_reference_fields = field_offset + max_offset_reference_in_field;
625 number_of_reference_fields += field_id->data.layout.references;
631 #if (DEBUG_CLASS_BITMAPS)
632 printf ("[allocating bitmap for class %s.%s (references %d, max offset %d, slots %d)]", mono_class_get_namespace (klass), mono_class_get_name (klass), number_of_reference_fields, max_offset_of_reference_fields, (int)(max_offset_of_reference_fields / sizeof (gpointer)) + 1);
634 if ((number_of_reference_fields == 0) && ((parent_id == NULL) || (parent_id->data.layout.references == 0))) {
635 #if (DEBUG_CLASS_BITMAPS)
636 printf ("[no references at all]");
638 klass_id->data.bitmap.compact = 0;
639 klass_id->data.layout.slots = 0;
640 klass_id->data.layout.references = 0;
642 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
643 #if (DEBUG_CLASS_BITMAPS)
644 printf ("[parent %s.%s has %d references in %d slots]", mono_class_get_namespace (parent_class), mono_class_get_name (parent_class), parent_id->data.layout.references, parent_id->data.layout.slots);
646 klass_id->data.layout.slots = parent_id->data.layout.slots;
647 klass_id->data.layout.references = parent_id->data.layout.references;
649 #if (DEBUG_CLASS_BITMAPS)
650 printf ("[no references from parent]");
652 klass_id->data.layout.slots = 0;
653 klass_id->data.layout.references = 0;
656 if (number_of_reference_fields > 0) {
657 klass_id->data.layout.slots += ((max_offset_of_reference_fields / sizeof (gpointer)) + 1);
658 klass_id->data.layout.references += number_of_reference_fields;
659 #if (DEBUG_CLASS_BITMAPS)
660 printf ("[adding data, going to %d references in %d slots]", klass_id->data.layout.references, klass_id->data.layout.slots);
664 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
665 #if (DEBUG_CLASS_BITMAPS)
666 printf ("[zeroing bitmap]");
668 klass_id->data.bitmap.compact = 0;
669 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
670 #if (DEBUG_CLASS_BITMAPS)
671 printf ("[copying compact father bitmap]");
673 klass_id->data.bitmap.compact = parent_id->data.bitmap.compact;
676 int size_of_bitmap = klass_id->data.layout.slots;
677 BITS_TO_BYTES (size_of_bitmap);
678 #if (DEBUG_CLASS_BITMAPS)
679 printf ("[allocating %d bytes for bitmap]", size_of_bitmap);
681 klass_id->data.bitmap.extended = g_malloc0 (size_of_bitmap);
682 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
683 int size_of_father_bitmap = parent_id->data.layout.slots;
684 if (size_of_father_bitmap <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
686 #if (DEBUG_CLASS_BITMAPS)
687 printf ("[copying %d bits from father bitmap]", size_of_father_bitmap);
689 for (father_slot = 0; father_slot < size_of_father_bitmap; father_slot ++) {
690 if (parent_id->data.bitmap.compact & (((guint64)1) << father_slot)) {
691 klass_id->data.bitmap.extended [father_slot >> 3] |= (1 << (father_slot & 7));
695 BITS_TO_BYTES (size_of_father_bitmap);
696 #if (DEBUG_CLASS_BITMAPS)
697 printf ("[copying %d bytes from father bitmap]", size_of_father_bitmap);
699 memcpy (klass_id->data.bitmap.extended, parent_id->data.bitmap.extended, size_of_father_bitmap);
705 #if (DEBUG_CLASS_BITMAPS)
706 printf ("[starting filling iteration]\n");
709 while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
710 MonoType* field_type = mono_field_get_type (field);
711 // For now, skip static fields
712 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
715 #if (DEBUG_CLASS_BITMAPS)
716 printf ("[Working on field %s]", mono_field_get_name (field));
718 if (MONO_TYPE_IS_REFERENCE (field_type)) {
719 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
721 g_assert ((field_offset % sizeof (gpointer)) == 0);
722 field_slot = field_offset / sizeof (gpointer);
723 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
724 klass_id->data.bitmap.compact |= (((guint64)1) << field_slot);
726 klass_id->data.bitmap.extended [field_slot >> 3] |= (1 << (field_slot & 7));
728 #if (DEBUG_CLASS_BITMAPS)
729 printf ("[reference at offset %d, slot %d]", field_offset, field_slot);
732 MonoClass *field_class = mono_class_from_mono_type (field_type);
733 if (field_class && mono_class_is_valuetype (field_class)) {
734 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
738 g_assert (field_id != NULL);
739 field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
740 g_assert ((field_id->data.layout.references == 0) || ((field_offset % sizeof (gpointer)) == 0));
741 field_slot = field_offset / sizeof (gpointer);
742 #if (DEBUG_CLASS_BITMAPS)
743 printf ("[value type at offset %d, slot %d, with %d references in %d slots]", field_offset, field_slot, field_id->data.layout.references, field_id->data.layout.slots);
746 if (field_id->data.layout.references > 0) {
748 if (field_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
749 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
750 if (field_id->data.bitmap.compact & (((guint64)1) << sub_field_slot)) {
751 int actual_slot = field_slot + sub_field_slot;
752 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
753 klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
755 klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
760 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
761 if (field_id->data.bitmap.extended [sub_field_slot >> 3] & (1 << (sub_field_slot & 7))) {
762 int actual_slot = field_slot + sub_field_slot;
763 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
764 klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
766 klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
775 #if (DEBUG_CLASS_BITMAPS)
778 printf ("\nLayot of class \"%s.%s\": references %d, slots %d, bitmap {", mono_class_get_namespace (klass), mono_class_get_name (klass), klass_id->data.layout.references, klass_id->data.layout.slots);
779 for (slot = 0; slot < klass_id->data.layout.slots; slot ++) {
780 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
781 if (klass_id->data.bitmap.compact & (((guint64)1) << slot)) {
787 if (klass_id->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
801 static MethodIdMappingElement*
802 method_id_mapping_element_new (MonoMethod *method) {
803 MethodIdMappingElement *result = g_new (MethodIdMappingElement, 1);
804 char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
806 result->name = g_strdup_printf ("%s (%s)", mono_method_get_name (method), signature);
808 result->method = method;
809 result->next_unwritten = profiler->methods->unwritten;
810 profiler->methods->unwritten = result;
811 result->id = profiler->methods->next_id;
812 profiler->methods->next_id ++;
813 g_hash_table_insert (profiler->methods->table, method, result);
815 result->data.code_start = NULL;
816 result->data.code_size = 0;
818 #if (DEBUG_MAPPING_EVENTS)
819 printf ("Created new METHOD mapping element \"%s\" (%p)[%d]\n", result->name, method, result->id);
826 method_id_mapping_element_destroy (gpointer element) {
827 MethodIdMappingElement *e = (MethodIdMappingElement*) element;
834 class_id_mapping_element_destroy (gpointer element) {
835 ClassIdMappingElement *e = (ClassIdMappingElement*) element;
838 if ((e->data.layout.slots != CLASS_LAYOUT_NOT_INITIALIZED) && (e->data.layout.slots > CLASS_LAYOUT_PACKED_BITMAP_SIZE))
839 g_free (e->data.bitmap.extended);
843 static MethodIdMapping*
844 method_id_mapping_new (void) {
845 MethodIdMapping *result = g_new (MethodIdMapping, 1);
846 //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, method_id_mapping_element_destroy);
847 result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, method_id_mapping_element_destroy);
848 result->unwritten = NULL;
853 static ClassIdMapping*
854 class_id_mapping_new (void) {
855 ClassIdMapping *result = g_new (ClassIdMapping, 1);
856 //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, class_id_mapping_element_destroy);
857 result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, class_id_mapping_element_destroy);
858 result->unwritten = NULL;
864 method_id_mapping_destroy (MethodIdMapping *map) {
865 g_hash_table_destroy (map->table);
870 class_id_mapping_destroy (ClassIdMapping *map) {
871 g_hash_table_destroy (map->table);
876 unmanaged_function_new (ProfilerUnmanagedFunctions *functions, Dl_info *dl_info) {
877 ProfilerUnmanagedFunction *function = g_new (ProfilerUnmanagedFunction, 1);
878 function->id = functions->next_id;
879 functions->next_id ++;
881 function->next_unwritten = functions->unwritten_queue;
882 functions->unwritten_queue = function;
883 function->name = g_strdup_printf ("[%s]:%s", dl_info->dli_fname, dl_info->dli_sname);
884 g_hash_table_insert (functions->table, dl_info->dli_saddr, function);
888 unmanaged_function_destroy (gpointer element) {
889 ProfilerUnmanagedFunction *function = (ProfilerUnmanagedFunction*) element;
890 if (function->name) {
891 g_free (function->name);
892 function->name = NULL;
898 unmanaged_function_hit (ProfilerUnmanagedFunctions *functions, gpointer address) {
900 if (dladdr (address, &dl_info) && (dl_info.dli_saddr != NULL) && (dl_info.dli_fname != NULL)) {
901 ProfilerUnmanagedFunction *function = g_hash_table_lookup (functions->table, dl_info.dli_saddr);
903 if (function != NULL) {
904 if (function->next_unwritten != NULL) {
908 function->next_unwritten = functions->unwritten_queue;
909 functions->unwritten_queue = function;
912 unmanaged_function_new (functions, &dl_info);
922 unmanaged_functions_init (ProfilerUnmanagedFunctions *functions) {
923 functions->next_id = 1;
924 functions->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, unmanaged_function_destroy);
925 functions->unwritten_queue_end = &(functions->actual_unwritten_queue_end);
926 functions->unwritten_queue = functions->unwritten_queue_end;
927 functions->actual_unwritten_queue_end.hits = 0;
928 functions->actual_unwritten_queue_end.id = 0;
929 functions->actual_unwritten_queue_end.name = NULL;
930 functions->actual_unwritten_queue_end.next_unwritten = NULL;
934 unmanaged_functions_dispose (ProfilerUnmanagedFunctions *functions) {
935 functions->next_id = 0;
936 g_hash_table_destroy (functions->table);
937 functions->table = NULL;
938 functions->unwritten_queue = NULL;
941 #if (DEBUG_LOAD_EVENTS)
943 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element);
946 static LoadedElement*
947 loaded_element_load_start (GHashTable *table, gpointer item) {
948 LoadedElement *element = g_new0 (LoadedElement, 1);
949 #if (DEBUG_LOAD_EVENTS)
950 print_load_event ("LOAD START", table, item, element);
952 MONO_PROFILER_GET_CURRENT_COUNTER (element->load_start_counter);
953 g_hash_table_insert (table, item, element);
957 static LoadedElement*
958 loaded_element_load_end (GHashTable *table, gpointer item, char *name) {
959 LoadedElement *element = g_hash_table_lookup (table, item);
960 #if (DEBUG_LOAD_EVENTS)
961 print_load_event ("LOAD END", table, item, element);
963 g_assert (element != NULL);
964 MONO_PROFILER_GET_CURRENT_COUNTER (element->load_end_counter);
965 element->name = name;
966 element->loaded = TRUE;
970 static LoadedElement*
971 loaded_element_unload_start (GHashTable *table, gpointer item) {
972 LoadedElement *element = g_hash_table_lookup (table, item);
973 #if (DEBUG_LOAD_EVENTS)
974 print_load_event ("UNLOAD START", table, item, element);
976 g_assert (element != NULL);
977 MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_start_counter);
981 static LoadedElement*
982 loaded_element_unload_end (GHashTable *table, gpointer item) {
983 LoadedElement *element = g_hash_table_lookup (table, item);
984 #if (DEBUG_LOAD_EVENTS)
985 print_load_event ("UNLOAD END", table, item, element);
987 g_assert (element != NULL);
988 MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_end_counter);
989 element->unloaded = TRUE;
995 loaded_element_destroy (gpointer element) {
996 if (((LoadedElement*)element)->name)
997 g_free (((LoadedElement*)element)->name);
1001 #if (DEBUG_LOAD_EVENTS)
1003 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element) {
1004 const char* item_name;
1007 if (table == profiler->loaded_assemblies) {
1008 //item_info = g_strdup_printf("ASSEMBLY %p (dynamic %d)", item, mono_image_is_dynamic (mono_assembly_get_image((MonoAssembly*)item)));
1009 item_info = g_strdup_printf("ASSEMBLY %p", item);
1010 } else if (table == profiler->loaded_modules) {
1011 //item_info = g_strdup_printf("MODULE %p (dynamic %d)", item, mono_image_is_dynamic ((MonoImage*)item));
1012 item_info = g_strdup_printf("MODULE %p", item);
1013 } else if (table == profiler->loaded_appdomains) {
1014 item_info = g_strdup_printf("APPDOMAIN %p (id %d)", item, mono_domain_get_id ((MonoDomain*)item));
1017 g_assert_not_reached ();
1020 if (element != NULL) {
1021 item_name = element->name;
1023 item_name = "<NULL>";
1026 printf ("%s EVENT for %s (%s)\n", event_name, item_info, item_name);
1032 profiler_heap_shot_object_buffers_destroy (ProfilerHeapShotObjectBuffer *buffer) {
1033 while (buffer != NULL) {
1034 ProfilerHeapShotObjectBuffer *next = buffer->next;
1035 #if DEBUG_HEAP_PROFILER
1036 printf ("profiler_heap_shot_object_buffers_destroy: destroyed buffer %p (%p-%p)\n", buffer, & (buffer->buffer [0]), buffer->end);
1043 static ProfilerHeapShotObjectBuffer*
1044 profiler_heap_shot_object_buffer_new (ProfilerPerThreadData *data) {
1045 ProfilerHeapShotObjectBuffer *buffer;
1046 ProfilerHeapShotObjectBuffer *result = g_new (ProfilerHeapShotObjectBuffer, 1);
1047 result->next_free_slot = & (result->buffer [0]);
1048 result->end = & (result->buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE]);
1049 result->first_unprocessed_slot = & (result->buffer [0]);
1050 result->next = data->heap_shot_object_buffers;
1051 data->heap_shot_object_buffers = result;
1052 #if DEBUG_HEAP_PROFILER
1053 printf ("profiler_heap_shot_object_buffer_new: created buffer %p (%p-%p)\n", result, result->next_free_slot, result->end);
1055 for (buffer = result; buffer != NULL; buffer = buffer->next) {
1056 ProfilerHeapShotObjectBuffer *last = buffer->next;
1057 if ((last != NULL) && (last->first_unprocessed_slot == last->end)) {
1058 buffer->next = NULL;
1059 profiler_heap_shot_object_buffers_destroy (last);
1066 static ProfilerHeapShotWriteJob*
1067 profiler_heap_shot_write_job_new (gboolean heap_shot_was_signalled) {
1068 ProfilerHeapShotWriteJob *job = g_new (ProfilerHeapShotWriteJob, 1);
1070 job->next_unwritten = NULL;
1071 job->buffers = g_new (ProfilerHeapShotWriteBuffer, 1);
1072 job->buffers->next = NULL;
1073 job->last_next = & (job->buffers->next);
1074 job->start = & (job->buffers->buffer [0]);
1075 job->cursor = job->start;
1076 job->end = & (job->buffers->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1077 job->full_buffers = 0;
1078 job->heap_shot_was_signalled = heap_shot_was_signalled;
1079 #if DEBUG_HEAP_PROFILER
1080 printf ("profiler_heap_shot_write_job_new: created job %p with buffer %p(%p-%p)\n", job, job->buffers, job->start, job->end);
1086 profiler_heap_shot_write_job_add_buffer (ProfilerHeapShotWriteJob *job, gpointer value) {
1087 ProfilerHeapShotWriteBuffer *buffer = g_new (ProfilerHeapShotWriteBuffer, 1);
1088 buffer->next = NULL;
1089 *(job->last_next) = buffer;
1090 job->last_next = & (buffer->next);
1091 job->full_buffers ++;
1092 buffer->buffer [0] = value;
1093 job->start = & (buffer->buffer [0]);
1094 job->cursor = & (buffer->buffer [1]);
1095 job->end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1096 #if DEBUG_HEAP_PROFILER
1097 printf ("profiler_heap_shot_write_job_add_buffer: in job %p, added buffer %p(%p-%p) with value %p at address %p (cursor now %p)\n", job, buffer, job->start, job->end, value, &(buffer->buffer [0]), job->cursor);
1099 ProfilerHeapShotWriteBuffer *current_buffer;
1100 for (current_buffer = job->buffers; current_buffer != NULL; current_buffer = current_buffer->next) {
1101 printf ("profiler_heap_shot_write_job_add_buffer: now job %p has buffer %p\n", job, current_buffer);
1108 profiler_heap_shot_write_job_free_buffers (ProfilerHeapShotWriteJob *job) {
1109 ProfilerHeapShotWriteBuffer *buffer = job->buffers;
1111 while (buffer != NULL) {
1112 ProfilerHeapShotWriteBuffer *next = buffer->next;
1113 #if DEBUG_HEAP_PROFILER
1114 printf ("profiler_heap_shot_write_job_free_buffers: in job %p, freeing buffer %p\n", job, buffer);
1120 job->buffers = NULL;
1124 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job);
1127 profiler_process_heap_shot_write_jobs (void) {
1128 gboolean done = FALSE;
1131 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1132 ProfilerHeapShotWriteJob *previous_job = NULL;
1133 ProfilerHeapShotWriteJob *next_job;
1136 while (current_job != NULL) {
1137 next_job = current_job->next_unwritten;
1139 if (next_job != NULL) {
1140 if (current_job->buffers != NULL) {
1143 if (next_job->buffers == NULL) {
1144 current_job->next_unwritten = NULL;
1148 if (current_job->buffers != NULL) {
1149 LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: writing...");
1150 profiler_heap_shot_write_block (current_job);
1151 LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: done");
1152 if (previous_job != NULL) {
1153 previous_job->next_unwritten = NULL;
1158 previous_job = current_job;
1159 current_job = next_job;
1165 profiler_free_heap_shot_write_jobs (void) {
1166 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1167 ProfilerHeapShotWriteJob *next_job;
1169 if (current_job != NULL) {
1170 while (current_job->next_unwritten != NULL) {
1171 #if DEBUG_HEAP_PROFILER
1172 printf ("profiler_free_heap_shot_write_jobs: job %p must not be freed\n", current_job);
1174 current_job = current_job->next_unwritten;
1177 next_job = current_job->next;
1178 current_job->next = NULL;
1179 current_job = next_job;
1181 while (current_job != NULL) {
1182 #if DEBUG_HEAP_PROFILER
1183 printf ("profiler_free_heap_shot_write_jobs: job %p will be freed\n", current_job);
1185 next_job = current_job->next;
1186 g_free (current_job);
1187 current_job = next_job;
1193 profiler_destroy_heap_shot_write_jobs (void) {
1194 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1195 ProfilerHeapShotWriteJob *next_job;
1197 while (current_job != NULL) {
1198 next_job = current_job->next;
1199 profiler_heap_shot_write_job_free_buffers (current_job);
1200 g_free (current_job);
1201 current_job = next_job;
1206 profiler_add_heap_shot_write_job (ProfilerHeapShotWriteJob *job) {
1207 job->next = profiler->heap_shot_write_jobs;
1208 job->next_unwritten = job->next;
1209 profiler->heap_shot_write_jobs = job;
1210 #if DEBUG_HEAP_PROFILER
1211 printf ("profiler_add_heap_shot_write_job: added job %p\n", job);
1215 #if DEBUG_HEAP_PROFILER
1216 #define STORE_ALLOCATED_OBJECT_MESSAGE1(d,o) printf ("STORE_ALLOCATED_OBJECT[TID %ld]: storing object %p at address %p\n", (d)->thread_id, (o), (d)->heap_shot_object_buffers->next_free_slot)
1217 #define STORE_ALLOCATED_OBJECT_MESSAGE2(d,o) printf ("STORE_ALLOCATED_OBJECT[TID %ld]: storing object %p at address %p in new buffer %p\n", (d)->thread_id, (o), buffer->next_free_slot, buffer)
1219 #define STORE_ALLOCATED_OBJECT_MESSAGE1(d,o)
1220 #define STORE_ALLOCATED_OBJECT_MESSAGE2(d,o)
1222 #define STORE_ALLOCATED_OBJECT(d,o) do {\
1223 if ((d)->heap_shot_object_buffers->next_free_slot < (d)->heap_shot_object_buffers->end) {\
1224 STORE_ALLOCATED_OBJECT_MESSAGE1 ((d), (o));\
1225 *((d)->heap_shot_object_buffers->next_free_slot) = (o);\
1226 (d)->heap_shot_object_buffers->next_free_slot ++;\
1228 ProfilerHeapShotObjectBuffer *buffer = profiler_heap_shot_object_buffer_new (d);\
1229 STORE_ALLOCATED_OBJECT_MESSAGE2 ((d), (o));\
1230 *((buffer)->next_free_slot) = (o);\
1231 (buffer)->next_free_slot ++;\
1235 static ProfilerPerThreadData*
1236 profiler_per_thread_data_new (guint32 buffer_size)
1238 ProfilerPerThreadData *data = g_new (ProfilerPerThreadData, 1);
1240 data->events = g_new0 (ProfilerEventData, buffer_size);
1241 data->next_free_event = data->events;
1242 data->end_event = data->events + (buffer_size - 1);
1243 data->first_unwritten_event = data->events;
1244 data->first_unmapped_event = data->events;
1245 MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
1246 data->last_event_counter = data->start_event_counter;
1247 data->thread_id = CURRENT_THREAD_ID ();
1248 data->heap_shot_object_buffers = NULL;
1249 if ((profiler->action_flags.unreachable_objects == TRUE) || (profiler->action_flags.heap_shot == TRUE)) {
1250 profiler_heap_shot_object_buffer_new (data);
1256 profiler_per_thread_data_destroy (ProfilerPerThreadData *data) {
1257 g_free (data->events);
1258 profiler_heap_shot_object_buffers_destroy (data->heap_shot_object_buffers);
1262 static ProfilerStatisticalData*
1263 profiler_statistical_data_new (guint32 buffer_size)
1265 ProfilerStatisticalData *data = g_new (ProfilerStatisticalData, 1);
1267 data->addresses = g_new0 (gpointer, buffer_size);
1268 data->next_free_index = 0;
1269 data->end_index = buffer_size;
1270 data->first_unwritten_index = 0;
1276 profiler_statistical_data_destroy (ProfilerStatisticalData *data) {
1277 g_free (data->addresses);
1282 profiler_add_write_buffer (void) {
1283 if (profiler->current_write_buffer->next == NULL) {
1284 profiler->current_write_buffer->next = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
1285 profiler->current_write_buffer->next->next = NULL;
1287 //printf ("Added next buffer %p, to buffer %p\n", profiler->current_write_buffer->next, profiler->current_write_buffer);
1290 profiler->current_write_buffer = profiler->current_write_buffer->next;
1291 profiler->current_write_position = 0;
1292 profiler->full_write_buffers ++;
1296 profiler_free_write_buffers (void) {
1297 ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1298 while (current_buffer != NULL) {
1299 ProfilerFileWriteBuffer *next_buffer = current_buffer->next;
1301 //printf ("Freeing write buffer %p, next is %p\n", current_buffer, next_buffer);
1303 g_free (current_buffer);
1304 current_buffer = next_buffer;
1308 #define WRITE_BYTE(b) do {\
1309 if (profiler->current_write_position >= PROFILER_FILE_WRITE_BUFFER_SIZE) {\
1310 profiler_add_write_buffer ();\
1312 profiler->current_write_buffer->buffer [profiler->current_write_position] = (b);\
1313 profiler->current_write_position ++;\
1318 write_current_block (guint16 code) {
1319 guint32 size = (profiler->full_write_buffers * PROFILER_FILE_WRITE_BUFFER_SIZE) + profiler->current_write_position;
1320 ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1321 guint64 current_counter;
1322 guint32 counter_delta;
1325 MONO_PROFILER_GET_CURRENT_COUNTER (current_counter);
1326 if (profiler->last_header_counter != 0) {
1327 counter_delta = current_counter - profiler->last_header_counter;
1331 profiler->last_header_counter = current_counter;
1333 header [0] = code & 0xff;
1334 header [1] = (code >> 8) & 0xff;
1335 header [2] = size & 0xff;
1336 header [3] = (size >> 8) & 0xff;
1337 header [4] = (size >> 16) & 0xff;
1338 header [5] = (size >> 24) & 0xff;
1339 header [6] = counter_delta & 0xff;
1340 header [7] = (counter_delta >> 8) & 0xff;
1341 header [8] = (counter_delta >> 16) & 0xff;
1342 header [9] = (counter_delta >> 24) & 0xff;
1344 WRITE_BUFFER (& (header [0]), 10);
1346 while ((current_buffer != NULL) && (profiler->full_write_buffers > 0)) {
1347 WRITE_BUFFER (& (current_buffer->buffer [0]), PROFILER_FILE_WRITE_BUFFER_SIZE);
1348 profiler->full_write_buffers --;
1349 current_buffer = current_buffer->next;
1351 if (profiler->current_write_position > 0) {
1352 WRITE_BUFFER (& (current_buffer->buffer [0]), profiler->current_write_position);
1356 profiler->current_write_buffer = profiler->write_buffers;
1357 profiler->current_write_position = 0;
1358 profiler->full_write_buffers = 0;
1362 #define SEVEN_BITS_MASK (0x7f)
1363 #define EIGHT_BIT_MASK (0x80)
1366 write_uint32 (guint32 value) {
1367 while (value > SEVEN_BITS_MASK) {
1368 WRITE_BYTE (value & SEVEN_BITS_MASK);
1371 WRITE_BYTE (value | EIGHT_BIT_MASK);
1374 write_uint64 (guint64 value) {
1375 while (value > SEVEN_BITS_MASK) {
1376 WRITE_BYTE (value & SEVEN_BITS_MASK);
1379 WRITE_BYTE (value | EIGHT_BIT_MASK);
1382 write_string (const char *string) {
1383 while (*string != 0) {
1384 WRITE_BYTE (*string);
1390 #if DEBUG_HEAP_PROFILER
1391 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c) printf ("WRITE_HEAP_SHOT_JOB_VALUE: writing value %p at cursor %p\n", (v), (c))
1393 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c)
1395 #define WRITE_HEAP_SHOT_JOB_VALUE(j,v) do {\
1396 if ((j)->cursor < (j)->end) {\
1397 WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE ((v), ((j)->cursor));\
1398 *((j)->cursor) = (v);\
1401 profiler_heap_shot_write_job_add_buffer (j, v);\
1405 #undef GUINT_TO_POINTER
1406 #define GUINT_TO_POINTER(u) ((void*)(guint64)(u))
1407 #undef GPOINTER_TO_UINT
1408 #define GPOINTER_TO_UINT(p) ((guint64)(void*)(p))
1410 #define WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE(j,v,c) WRITE_HEAP_SHOT_JOB_VALUE (j, GUINT_TO_POINTER (GPOINTER_TO_UINT (v)|(c)))
1412 #if DEBUG_HEAP_PROFILER
1413 #define UPDATE_JOB_BUFFER_CURSOR_MESSAGE() printf ("profiler_heap_shot_write_block[UPDATE_JOB_BUFFER_CURSOR]: in job %p, moving to buffer %p and cursor %p\n", job, buffer, cursor)
1415 #define UPDATE_JOB_BUFFER_CURSOR_MESSAGE()
1417 #define UPDATE_JOB_BUFFER_CURSOR() do {\
1419 if (cursor >= end) {\
1420 buffer = buffer->next;\
1421 if (buffer != NULL) {\
1422 cursor = & (buffer->buffer [0]);\
1423 if (buffer->next != NULL) {\
1424 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);\
1432 UPDATE_JOB_BUFFER_CURSOR_MESSAGE ();\
1436 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job) {
1437 ProfilerHeapShotWriteBuffer *buffer;
1440 guint64 start_counter;
1442 guint64 end_counter;
1445 write_uint64 (job->start_counter);
1446 write_uint64 (job->start_time);
1447 write_uint64 (job->end_counter);
1448 write_uint64 (job->end_time);
1450 MONO_PROFILER_GET_CURRENT_COUNTER (start_counter);
1451 MONO_PROFILER_GET_CURRENT_TIME (start_time);
1452 write_uint64 (start_counter);
1453 write_uint64 (start_time);
1454 #if DEBUG_HEAP_PROFILER
1455 printf ("profiler_heap_shot_write_block: working on job %p...\n", job);
1457 buffer = job->buffers;
1458 cursor = & (buffer->buffer [0]);
1459 if (buffer->next != NULL) {
1460 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1464 if (cursor >= end) {
1467 #if DEBUG_HEAP_PROFILER
1468 printf ("profiler_heap_shot_write_block: in job %p, starting at buffer %p and cursor %p\n", job, buffer, cursor);
1470 while (cursor != NULL) {
1471 gpointer value = *cursor;
1472 HeapProfilerJobValueCode code = GPOINTER_TO_UINT (value) & HEAP_CODE_MASK;
1473 #if DEBUG_HEAP_PROFILER
1474 printf ("profiler_heap_shot_write_block: got value %p and code %d\n", value, code);
1477 UPDATE_JOB_BUFFER_CURSOR ();
1478 if (code == HEAP_CODE_FREE_OBJECT_CLASS) {
1479 MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
1480 //MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) % 4);
1481 ClassIdMappingElement *class_id;
1484 class_id = class_id_mapping_element_get (klass);
1485 if (class_id == NULL) {
1486 printf ("profiler_heap_shot_write_block: unknown class %p", klass);
1488 g_assert (class_id != NULL);
1489 write_uint32 ((class_id->id << 2) | HEAP_CODE_FREE_OBJECT_CLASS);
1491 size = GPOINTER_TO_UINT (*cursor);
1492 UPDATE_JOB_BUFFER_CURSOR ();
1493 write_uint32 (size);
1494 #if DEBUG_HEAP_PROFILER
1495 printf ("profiler_heap_shot_write_block: wrote unreachable object of class %p (id %d, size %d)\n", klass, class_id->id, size);
1497 } else if (code == HEAP_CODE_OBJECT) {
1498 MonoObject *object = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
1499 MonoClass *klass = mono_object_get_class (object);
1500 ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
1501 guint32 size = mono_object_get_size (object);
1502 guint32 references = GPOINTER_TO_UINT (*cursor);
1503 UPDATE_JOB_BUFFER_CURSOR ();
1505 if (class_id == NULL) {
1506 printf ("profiler_heap_shot_write_block: unknown class %p", klass);
1508 g_assert (class_id != NULL);
1510 write_uint64 (GPOINTER_TO_UINT (value));
1511 write_uint32 (class_id->id);
1512 write_uint32 (size);
1513 write_uint32 (references);
1514 #if DEBUG_HEAP_PROFILER
1515 printf ("profiler_heap_shot_write_block: writing object %p (references %d)\n", value, references);
1518 while (references > 0) {
1519 gpointer reference = *cursor;
1520 write_uint64 (GPOINTER_TO_UINT (reference));
1521 UPDATE_JOB_BUFFER_CURSOR ();
1523 #if DEBUG_HEAP_PROFILER
1524 printf ("profiler_heap_shot_write_block: inside object %p, wrote reference %p)\n", value, reference);
1528 #if DEBUG_HEAP_PROFILER
1529 printf ("profiler_heap_shot_write_block: unknown code %d in value %p\n", code, value);
1531 g_assert_not_reached ();
1536 MONO_PROFILER_GET_CURRENT_COUNTER (end_counter);
1537 MONO_PROFILER_GET_CURRENT_TIME (end_time);
1538 write_uint64 (end_counter);
1539 write_uint64 (end_time);
1541 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_HEAP);
1543 profiler_heap_shot_write_job_free_buffers (job);
1544 #if DEBUG_HEAP_PROFILER
1545 printf ("profiler_heap_shot_write_block: work on job %p done.\n", job);
1550 write_element_load_block (LoadedElement *element, guint8 kind, gsize thread_id) {
1552 write_uint64 (element->load_start_counter);
1553 write_uint64 (element->load_end_counter);
1554 write_uint64 (thread_id);
1555 write_string (element->name);
1556 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_LOADED);
1557 element->load_written = TRUE;
1561 write_element_unload_block (LoadedElement *element, guint8 kind, gsize thread_id) {
1563 write_uint64 (element->unload_start_counter);
1564 write_uint64 (element->unload_end_counter);
1565 write_uint64 (thread_id);
1566 write_string (element->name);
1567 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED);
1568 element->unload_written = TRUE;
1572 write_clock_data (void) {
1576 MONO_PROFILER_GET_CURRENT_COUNTER (counter);
1577 MONO_PROFILER_GET_CURRENT_TIME (time);
1579 write_uint64 (counter);
1580 write_uint64 (time);
1584 write_mapping_block (gsize thread_id) {
1585 ClassIdMappingElement *current_class;
1586 MethodIdMappingElement *current_method;
1588 if ((profiler->classes->unwritten == NULL) && (profiler->methods->unwritten == NULL))
1591 #if (DEBUG_MAPPING_EVENTS)
1592 printf ("[write_mapping_block][TID %ld] START\n", thread_id);
1595 write_clock_data ();
1596 write_uint64 (thread_id);
1598 for (current_class = profiler->classes->unwritten; current_class != NULL; current_class = current_class->next_unwritten) {
1599 write_uint32 (current_class->id);
1600 write_string (current_class->name);
1601 #if (DEBUG_MAPPING_EVENTS)
1602 printf ("mapping CLASS (%d => %s)\n", current_class->id, current_class->name);
1604 g_free (current_class->name);
1605 current_class->name = NULL;
1608 profiler->classes->unwritten = NULL;
1610 for (current_method = profiler->methods->unwritten; current_method != NULL; current_method = current_method->next_unwritten) {
1611 MonoMethod *method = current_method->method;
1612 MonoClass *klass = mono_method_get_class (method);
1613 ClassIdMappingElement *class_element = class_id_mapping_element_get (klass);
1614 g_assert (class_element != NULL);
1615 write_uint32 (current_method->id);
1616 write_uint32 (class_element->id);
1617 write_string (current_method->name);
1618 #if (DEBUG_MAPPING_EVENTS)
1619 printf ("mapping METHOD ([%d]%d => %s)\n", class_element?class_element->id:1, current_method->id, current_method->name);
1621 g_free (current_method->name);
1622 current_method->name = NULL;
1625 profiler->methods->unwritten = NULL;
1627 write_clock_data ();
1628 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_MAPPING);
1630 #if (DEBUG_MAPPING_EVENTS)
1631 printf ("[write_mapping_block][TID %ld] END\n", thread_id);
1636 get_extended_event_value (ProfilerEventData *event, ProfilerEventData *next) {
1637 guint64 result = next->data.number;
1638 result |= (((guint64) event->value) << 32);
1643 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER = 1,
1644 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_IMPLICIT = 2,
1645 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT = 3,
1646 MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION = 4,
1647 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT = 5,
1648 MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT = 6,
1649 MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT = 7
1650 } MonoProfilerPackedEventCode;
1651 #define MONO_PROFILER_PACKED_EVENT_CODE_BITS 3
1652 #define MONO_PROFILER_PACKED_EVENT_DATA_BITS (8-MONO_PROFILER_PACKED_EVENT_CODE_BITS)
1653 #define MONO_PROFILER_PACKED_EVENT_DATA_MASK ((1<<MONO_PROFILER_PACKED_EVENT_DATA_BITS)-1)
1655 #define MONO_PROFILER_EVENT_MAKE_PACKED_CODE(result,data,base) do {\
1656 result = ((base)|((data & MONO_PROFILER_PACKED_EVENT_DATA_MASK) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
1657 data >>= MONO_PROFILER_PACKED_EVENT_DATA_BITS;\
1659 #define MONO_PROFILER_EVENT_MAKE_FULL_CODE(result,code,kind,base) do {\
1660 result = ((base)|((((kind)<<4) | (code)) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
1663 static ProfilerEventData*
1664 write_event (ProfilerEventData *event) {
1665 ProfilerEventData *next = event + 1;
1666 gboolean write_event_value = TRUE;
1669 guint64 event_value;
1671 event_value = event->value;
1672 if (event_value > MAX_EVENT_VALUE) {
1673 event_value = get_extended_event_value (event, next);
1677 if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
1678 MethodIdMappingElement *element = method_id_mapping_element_get (event->data.address);
1679 g_assert (element != NULL);
1680 event_data = element->id;
1682 if (event->code == MONO_PROFILER_EVENT_METHOD_CALL) {
1683 if (event->kind == MONO_PROFILER_EVENT_KIND_START) {
1684 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER);
1686 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT);
1689 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT);
1691 } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
1692 ClassIdMappingElement *element = class_id_mapping_element_get (event->data.address);
1693 g_assert (element != NULL);
1694 event_data = element->id;
1696 if (event->code == MONO_PROFILER_EVENT_CLASS_ALLOCATION) {
1697 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION);
1699 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT);
1702 event_data = event->data.number;
1703 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
1706 #if (DEBUG_LOGGING_PROFILER)
1708 printf ("writing EVENT[%p] data_type:%d, kind:%d, code:%d (%d:%ld:%ld)\n", event,
1709 event->data_type, event->kind, event->code,
1710 event_code, event_data, event_value);
1713 WRITE_BYTE (event_code);
1714 write_uint64 (event_data);
1715 if (write_event_value) {
1716 write_uint64 (event_value);
1723 write_thread_data_block (ProfilerPerThreadData *data) {
1724 ProfilerEventData *start = data->first_unwritten_event;
1725 ProfilerEventData *end = data->first_unmapped_event;
1730 write_clock_data ();
1731 write_uint64 (data->thread_id);
1733 write_uint64 (data->start_event_counter);
1735 while (start < end) {
1736 start = write_event (start);
1739 data->first_unwritten_event = end;
1741 write_clock_data ();
1742 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_EVENTS);
1745 static ProfilerExecutableMemoryRegionData*
1746 profiler_executable_memory_region_new (gpointer *start, gpointer *end, guint32 file_offset, char *file_name, guint32 id) {
1747 ProfilerExecutableMemoryRegionData *result = g_new (ProfilerExecutableMemoryRegionData, 1);
1748 result->start = start;
1750 result->file_offset = file_offset;
1751 result->file_name = g_strdup (file_name);
1753 result->is_new = TRUE;
1758 profiler_executable_memory_region_destroy (ProfilerExecutableMemoryRegionData *data) {
1759 if (data->file_name != NULL) {
1760 g_free (data->file_name);
1765 static ProfilerExecutableMemoryRegions*
1766 profiler_executable_memory_regions_new (int next_id) {
1767 ProfilerExecutableMemoryRegions *result = g_new (ProfilerExecutableMemoryRegions, 1);
1768 result->regions = g_new0 (ProfilerExecutableMemoryRegionData*, 32);
1769 result->regions_capacity = 32;
1770 result->regions_count = 0;
1771 result->next_id = next_id;
1776 profiler_executable_memory_regions_destroy (ProfilerExecutableMemoryRegions *regions) {
1779 for (i = 0; i < regions->regions_count; i++) {
1780 profiler_executable_memory_region_destroy (regions->regions [i]);
1782 g_free (regions->regions);
1786 static ProfilerExecutableMemoryRegionData*
1787 find_address_region (ProfilerExecutableMemoryRegions *regions, gpointer address) {
1789 int high_index = regions->regions_count;
1790 int middle_index = 0;
1791 ProfilerExecutableMemoryRegionData *middle_region = regions->regions [0];
1793 if ((regions->regions_count == 0) || (regions->regions [low_index]->start > address) || (regions->regions [high_index - 1]->end < address)) {
1797 //printf ("find_address_region: Looking for address %p in %d regions (from %p to %p)\n", address, regions->regions_count, regions->regions [low_index]->start, regions->regions [high_index - 1]->end);
1799 while (low_index != high_index) {
1800 middle_index = low_index + ((high_index - low_index) / 2);
1801 middle_region = regions->regions [middle_index];
1803 //printf ("find_address_region: Looking for address %p, considering index %d[%p-%p] (%d-%d)\n", address, middle_index, middle_region->start, middle_region->end, low_index, high_index);
1805 if (middle_region->start > address) {
1806 if (middle_index > 0) {
1807 high_index = middle_index;
1811 } else if (middle_region->end < address) {
1812 if (middle_index < regions->regions_count - 1) {
1813 low_index = middle_index + 1;
1818 return middle_region;
1822 if ((middle_region == NULL) || (middle_region->start > address) || (middle_region->end < address)) {
1825 return middle_region;
1830 append_region (ProfilerExecutableMemoryRegions *regions, gpointer *start, gpointer *end, guint32 file_offset, char *file_name) {
1831 if (regions->regions_count >= regions->regions_capacity) {
1832 ProfilerExecutableMemoryRegionData **new_regions = g_new0 (ProfilerExecutableMemoryRegionData*, regions->regions_capacity * 2);
1833 memcpy (new_regions, regions->regions, regions->regions_capacity * sizeof (ProfilerExecutableMemoryRegionData*));
1834 g_free (regions->regions);
1835 regions->regions = new_regions;
1836 regions->regions_capacity = regions->regions_capacity * 2;
1838 regions->regions [regions->regions_count] = profiler_executable_memory_region_new (start, end, file_offset, file_name, regions->next_id);
1839 regions->regions_count ++;
1840 regions->next_id ++;
1844 restore_region_ids (ProfilerExecutableMemoryRegions *old_regions, ProfilerExecutableMemoryRegions *new_regions) {
1848 for (old_i = 0; old_i < old_regions->regions_count; old_i++) {
1849 ProfilerExecutableMemoryRegionData *old_region = old_regions->regions [old_i];
1850 for (new_i = 0; new_i < new_regions->regions_count; new_i++) {
1851 ProfilerExecutableMemoryRegionData *new_region = new_regions->regions [new_i];
1852 if ((old_region->start == new_region->start) &&
1853 (old_region->end == new_region->end) &&
1854 (old_region->file_offset == new_region->file_offset) &&
1855 ! strcmp (old_region->file_name, new_region->file_name)) {
1856 new_region->is_new = FALSE;
1857 new_region->id = old_region->id;
1858 old_region->is_new = TRUE;
1865 compare_regions (const void *a1, const void *a2) {
1866 ProfilerExecutableMemoryRegionData *r1 = * (ProfilerExecutableMemoryRegionData**) a1;
1867 ProfilerExecutableMemoryRegionData *r2 = * (ProfilerExecutableMemoryRegionData**) a2;
1868 return (r1->start < r2->start)? -1 : ((r1->start > r2->start)? 1 : 0);
1872 sort_regions (ProfilerExecutableMemoryRegions *regions) {
1873 qsort (regions->regions, regions->regions_count, sizeof (ProfilerExecutableMemoryRegionData *), compare_regions);
1876 //FIXME: make also Win32 and BSD variants
1877 #define MAPS_BUFFER_SIZE 4096
1880 update_regions_buffer (int fd, char *buffer) {
1881 ssize_t result = read (fd, buffer, MAPS_BUFFER_SIZE);
1883 if (result == MAPS_BUFFER_SIZE) {
1885 } else if (result >= 0) {
1886 *(buffer + result) = 0;
1894 #define GOTO_NEXT_CHAR(c,b,fd) do {\
1896 if (((c) - (b) >= MAPS_BUFFER_SIZE) || ((*(c) == 0) && ((c) != (b)))) {\
1897 update_regions_buffer ((fd), (b));\
1902 static int hex_digit_value (char c) {
1903 if ((c >= '0') && (c <= '9')) {
1905 } else if ((c >= 'a') && (c <= 'f')) {
1907 } else if ((c >= 'A') && (c <= 'F')) {
1930 MAP_LINE_PARSER_STATE_INVALID,
1931 MAP_LINE_PARSER_STATE_START_ADDRESS,
1932 MAP_LINE_PARSER_STATE_END_ADDRESS,
1933 MAP_LINE_PARSER_STATE_PERMISSIONS,
1934 MAP_LINE_PARSER_STATE_OFFSET,
1935 MAP_LINE_PARSER_STATE_DEVICE,
1936 MAP_LINE_PARSER_STATE_INODE,
1937 MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME,
1938 MAP_LINE_PARSER_STATE_FILENAME,
1939 MAP_LINE_PARSER_STATE_DONE
1940 } MapLineParserState;
1942 const char *map_line_parser_state [] = {
1950 "BLANK_BEFORE_FILENAME",
1956 parse_map_line (ProfilerExecutableMemoryRegions *regions, int fd, char *buffer, char *current) {
1957 MapLineParserState state = MAP_LINE_PARSER_STATE_START_ADDRESS;
1958 gsize start_address = 0;
1959 gsize end_address = 0;
1961 char *start_filename = NULL;
1962 char *end_filename = NULL;
1963 gboolean is_executable = FALSE;
1964 gboolean done = FALSE;
1970 case MAP_LINE_PARSER_STATE_START_ADDRESS:
1972 start_address <<= 4;
1973 start_address |= hex_digit_value (c);
1974 } else if (c == '-') {
1975 state = MAP_LINE_PARSER_STATE_END_ADDRESS;
1977 state = MAP_LINE_PARSER_STATE_INVALID;
1980 case MAP_LINE_PARSER_STATE_END_ADDRESS:
1983 end_address |= hex_digit_value (c);
1984 } else if (isblank (c)) {
1985 state = MAP_LINE_PARSER_STATE_PERMISSIONS;
1987 state = MAP_LINE_PARSER_STATE_INVALID;
1990 case MAP_LINE_PARSER_STATE_PERMISSIONS:
1992 is_executable = TRUE;
1993 } else if (isblank (c)) {
1994 state = MAP_LINE_PARSER_STATE_OFFSET;
1995 } else if ((c != '-') && ! isalpha (c)) {
1996 state = MAP_LINE_PARSER_STATE_INVALID;
1999 case MAP_LINE_PARSER_STATE_OFFSET:
2002 offset |= hex_digit_value (c);
2003 } else if (isblank (c)) {
2004 state = MAP_LINE_PARSER_STATE_DEVICE;
2006 state = MAP_LINE_PARSER_STATE_INVALID;
2009 case MAP_LINE_PARSER_STATE_DEVICE:
2011 state = MAP_LINE_PARSER_STATE_INODE;
2012 } else if ((c != ':') && ! isxdigit (c)) {
2013 state = MAP_LINE_PARSER_STATE_INVALID;
2016 case MAP_LINE_PARSER_STATE_INODE:
2018 state = MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME;
2019 } else if (! isdigit (c)) {
2020 state = MAP_LINE_PARSER_STATE_INVALID;
2023 case MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME:
2025 state = MAP_LINE_PARSER_STATE_FILENAME;
2026 start_filename = current;
2027 } else if (! isblank (c)) {
2028 state = MAP_LINE_PARSER_STATE_INVALID;
2031 case MAP_LINE_PARSER_STATE_FILENAME:
2033 state = MAP_LINE_PARSER_STATE_DONE;
2035 end_filename = current;
2038 case MAP_LINE_PARSER_STATE_DONE:
2039 if (done && is_executable) {
2041 append_region (regions, (gpointer) start_address, (gpointer) end_address, offset, start_filename);
2044 case MAP_LINE_PARSER_STATE_INVALID:
2046 state = MAP_LINE_PARSER_STATE_DONE;
2056 GOTO_NEXT_CHAR(current, buffer, fd);
2062 scan_process_regions (ProfilerExecutableMemoryRegions *regions) {
2067 fd = open ("/proc/self/maps", O_RDONLY);
2072 buffer = malloc (MAPS_BUFFER_SIZE);
2073 update_regions_buffer (fd, buffer);
2075 while (current != NULL) {
2076 current = parse_map_line (regions, fd, buffer, current);
2087 MONO_PROFILER_STATISTICAL_CODE_END = 0,
2088 MONO_PROFILER_STATISTICAL_CODE_METHOD = 1,
2089 MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID = 2,
2090 MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_IN_REGION = 3,
2091 MONO_PROFILER_STATISTICAL_CODE_REGIONS = 7
2092 } MonoProfilerStatisticalCode;
2095 refresh_memory_regions (void) {
2096 ProfilerExecutableMemoryRegions *old_regions = profiler->executable_regions;
2097 ProfilerExecutableMemoryRegions *new_regions = profiler_executable_memory_regions_new (old_regions->next_id);
2100 LOG_WRITER_THREAD ("Refreshing memory regions...");
2101 scan_process_regions (new_regions);
2102 restore_region_ids (old_regions, new_regions);
2103 sort_regions (new_regions);
2104 LOG_WRITER_THREAD ("Refreshed memory regions.");
2106 // This marks the region "sub-block"
2107 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_REGIONS);
2109 // First write the "removed" regions
2110 for (i = 0; i < old_regions->regions_count; i++) {
2111 ProfilerExecutableMemoryRegionData *region = old_regions->regions [i];
2112 if (! region->is_new) {
2113 #if DEBUG_STATISTICAL_PROFILER
2114 printf ("[refresh_memory_regions] Invalidated region %d\n", region->id);
2116 write_uint32 (region->id);
2121 // Then write the new ones
2122 for (i = 0; i < new_regions->regions_count; i++) {
2123 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
2124 if (region->is_new) {
2125 region->is_new = FALSE;
2127 #if DEBUG_STATISTICAL_PROFILER
2128 printf ("[refresh_memory_regions] Wrote region %d (%p-%p[%d] '%s')\n", region->id, region->start, region->end, region->file_offset, region->file_name);
2130 write_uint32 (region->id);
2131 write_uint64 (GPOINTER_TO_INT (region->start));
2132 write_uint32 (GPOINTER_TO_INT (region->end) - GPOINTER_TO_INT (region->start));
2133 write_uint32 (region->file_offset);
2134 write_string (region->file_name);
2139 // Finally, free the old ones, and replace them
2140 profiler_executable_memory_regions_destroy (old_regions);
2141 profiler->executable_regions = new_regions;
2145 flush_all_mappings (void);
2148 write_statistical_data_block (ProfilerStatisticalData *data) {
2149 int start_index = data->first_unwritten_index;
2150 int end_index = data->next_free_index;
2151 gboolean regions_refreshed = FALSE;
2153 ProfilerUnmanagedFunctions *functions = &(profiler->unmanaged_functions);
2155 if (end_index > data->end_index)
2156 end_index = data->end_index;
2158 if (start_index == end_index)
2161 data->first_unwritten_index = end_index;
2163 write_clock_data ();
2165 for (index = start_index; index < end_index; index ++) {
2166 gpointer address = data->addresses [index];
2167 MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (char*) address);
2170 MonoMethod *method = mono_jit_info_get_method (ji);
2171 MethodIdMappingElement *element = method_id_mapping_element_get (method);
2173 if (element != NULL) {
2174 #if DEBUG_STATISTICAL_PROFILER
2175 printf ("[write_statistical_data_block] Wrote method %d\n", element->id);
2177 write_uint32 ((element->id << 3) | MONO_PROFILER_STATISTICAL_CODE_METHOD);
2179 #if DEBUG_STATISTICAL_PROFILER
2180 printf ("[write_statistical_data_block] Wrote unknown method %p\n", method);
2182 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_METHOD);
2185 if (! unmanaged_function_hit (functions, address)) {
2186 ProfilerExecutableMemoryRegionData *region = find_address_region (profiler->executable_regions, address);
2188 if (region == NULL && ! regions_refreshed) {
2189 refresh_memory_regions ();
2190 regions_refreshed = TRUE;
2191 region = find_address_region (profiler->executable_regions, address);
2194 if (region != NULL) {
2195 #if DEBUG_STATISTICAL_PROFILER
2196 printf ("[write_statistical_data_block] Wrote unmanaged hit %d[%d]\n", region->id, GPOINTER_TO_INT (address) - GPOINTER_TO_INT (region->start));
2198 write_uint32 ((region->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_IN_REGION);
2199 write_uint32 (GPOINTER_TO_INT (address) - GPOINTER_TO_INT (region->start));
2201 #if DEBUG_STATISTICAL_PROFILER
2202 printf ("[write_statistical_data_block] Wrote unknown unmanaged hit %p\n", address);
2204 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_IN_REGION);
2205 write_uint64 (GPOINTER_TO_INT (address));
2210 if (functions->unwritten_queue != functions->unwritten_queue_end) {
2211 ProfilerUnmanagedFunction *end = functions->unwritten_queue_end;
2212 ProfilerUnmanagedFunction *function = functions->unwritten_queue;
2213 functions->unwritten_queue = functions->unwritten_queue_end;
2215 while (function != end) {
2216 ProfilerUnmanagedFunction *next = function->next_unwritten;
2218 write_uint32 ((function->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID);
2219 if (function->name != NULL) {
2221 write_string (function->name);
2222 g_free (function->name);
2223 function->name = NULL;
2225 write_uint32 (function->hits);
2228 function->next_unwritten = NULL;
2232 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_END);
2234 write_clock_data ();
2236 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL);
2240 write_intro_block (void) {
2242 write_string ("mono");
2243 write_uint32 (profiler->flags);
2244 write_uint64 (profiler->start_counter);
2245 write_uint64 (profiler->start_time);
2246 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_INTRO);
2250 write_end_block (void) {
2252 write_uint64 (profiler->end_counter);
2253 write_uint64 (profiler->end_time);
2254 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_END);
2258 update_mapping (ProfilerPerThreadData *data) {
2259 ProfilerEventData *start = data->first_unmapped_event;
2260 ProfilerEventData *end = data->next_free_event;
2261 data->first_unmapped_event = end;
2263 #if (DEBUG_LOGGING_PROFILER)
2264 printf ("[update_mapping][TID %ld] START\n", data->thread_id);
2266 while (start < end) {
2267 #if DEBUG_LOGGING_PROFILER
2268 printf ("Examining event %p[TID %ld] looking for a new mapping...\n", start, data->thread_id);
2270 if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
2271 ClassIdMappingElement *element = class_id_mapping_element_get (start->data.address);
2272 if (element == NULL) {
2273 MonoClass *klass = start->data.address;
2274 class_id_mapping_element_new (klass);
2276 } else if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
2277 MethodIdMappingElement *element = method_id_mapping_element_get (start->data.address);
2278 if (element == NULL) {
2279 MonoMethod *method = start->data.address;
2280 method_id_mapping_element_new (method);
2286 #if (DEBUG_LOGGING_PROFILER)
2287 printf ("[update_mapping][TID %ld] END\n", data->thread_id);
2292 flush_all_mappings (void) {
2293 ProfilerPerThreadData *data;
2295 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2296 update_mapping (data);
2298 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2299 write_mapping_block (data->thread_id);
2304 flush_full_event_data_buffer (ProfilerPerThreadData *data) {
2307 // We flush all mappings because some id definitions could come
2308 // from other threads
2309 flush_all_mappings ();
2310 g_assert (data->first_unmapped_event == data->end_event);
2312 write_thread_data_block (data);
2314 data->next_free_event = data->events;
2315 data->first_unwritten_event = data->events;
2316 data->first_unmapped_event = data->events;
2317 MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
2318 data->last_event_counter = data->start_event_counter;
2323 #define GET_NEXT_FREE_EVENT(d,e) {\
2324 if ((d)->next_free_event >= (d)->end_event) {\
2325 flush_full_event_data_buffer (d);\
2327 (e) = (d)->next_free_event;\
2328 (d)->next_free_event ++;\
2332 flush_everything (void) {
2333 ProfilerPerThreadData *data;
2335 flush_all_mappings ();
2336 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2337 write_thread_data_block (data);
2339 write_statistical_data_block (profiler->statistical_data);
2342 #define RESULT_TO_LOAD_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_LOADED_EVENT_SUCCESS:MONO_PROFILER_LOADED_EVENT_FAILURE)
2344 appdomain_start_load (MonoProfiler *profiler, MonoDomain *domain) {
2346 loaded_element_load_start (profiler->loaded_appdomains, domain);
2351 appdomain_end_load (MonoProfiler *profiler, MonoDomain *domain, int result) {
2353 LoadedElement *element;
2355 name = g_strdup_printf ("%d", mono_domain_get_id (domain));
2357 element = loaded_element_load_end (profiler->loaded_appdomains, domain, name);
2358 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
2363 appdomain_start_unload (MonoProfiler *profiler, MonoDomain *domain) {
2365 loaded_element_unload_start (profiler->loaded_appdomains, domain);
2366 flush_everything ();
2371 appdomain_end_unload (MonoProfiler *profiler, MonoDomain *domain) {
2372 LoadedElement *element;
2375 element = loaded_element_unload_end (profiler->loaded_appdomains, domain);
2376 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN, CURRENT_THREAD_ID ());
2381 module_start_load (MonoProfiler *profiler, MonoImage *module) {
2383 loaded_element_load_start (profiler->loaded_modules, module);
2388 module_end_load (MonoProfiler *profiler, MonoImage *module, int result) {
2390 MonoAssemblyName aname;
2391 LoadedElement *element;
2393 mono_assembly_fill_assembly_name (module, &aname);
2394 name = mono_stringify_assembly_name (&aname);
2396 element = loaded_element_load_end (profiler->loaded_modules, module, name);
2397 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_MODULE | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
2402 module_start_unload (MonoProfiler *profiler, MonoImage *module) {
2404 loaded_element_unload_start (profiler->loaded_modules, module);
2405 flush_everything ();
2410 module_end_unload (MonoProfiler *profiler, MonoImage *module) {
2411 LoadedElement *element;
2414 element = loaded_element_unload_end (profiler->loaded_modules, module);
2415 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_MODULE, CURRENT_THREAD_ID ());
2420 assembly_start_load (MonoProfiler *profiler, MonoAssembly *assembly) {
2422 loaded_element_load_start (profiler->loaded_assemblies, assembly);
2427 assembly_end_load (MonoProfiler *profiler, MonoAssembly *assembly, int result) {
2429 MonoAssemblyName aname;
2430 LoadedElement *element;
2432 mono_assembly_fill_assembly_name (mono_assembly_get_image (assembly), &aname);
2433 name = mono_stringify_assembly_name (&aname);
2435 element = loaded_element_load_end (profiler->loaded_assemblies, assembly, name);
2436 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
2441 assembly_start_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
2443 loaded_element_unload_start (profiler->loaded_assemblies, assembly);
2444 flush_everything ();
2448 assembly_end_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
2449 LoadedElement *element;
2452 element = loaded_element_unload_end (profiler->loaded_assemblies, assembly);
2453 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY, CURRENT_THREAD_ID ());
2457 #if (DEBUG_LOGGING_PROFILER)
2459 class_event_code_to_string (MonoProfilerClassEvents code) {
2461 case MONO_PROFILER_EVENT_CLASS_LOAD: return "LOAD";
2462 case MONO_PROFILER_EVENT_CLASS_UNLOAD: return "UNLOAD";
2463 case MONO_PROFILER_EVENT_CLASS_ALLOCATION: return "ALLOCATION";
2464 case MONO_PROFILER_EVENT_CLASS_EXCEPTION: return "EXCEPTION";
2465 default: g_assert_not_reached (); return "";
2469 method_event_code_to_string (MonoProfilerClassEvents code) {
2471 case MONO_PROFILER_EVENT_METHOD_CALL: return "CALL";
2472 case MONO_PROFILER_EVENT_METHOD_JIT: return "JIT";
2473 case MONO_PROFILER_EVENT_METHOD_FREED: return "FREED";
2474 default: g_assert_not_reached (); return "";
2478 number_event_code_to_string (MonoProfilerEvents code) {
2480 case MONO_PROFILER_EVENT_THREAD: return "THREAD";
2481 case MONO_PROFILER_EVENT_GC_COLLECTION: return "GC_COLLECTION";
2482 case MONO_PROFILER_EVENT_GC_MARK: return "GC_MARK";
2483 case MONO_PROFILER_EVENT_GC_SWEEP: return "GC_SWEEP";
2484 case MONO_PROFILER_EVENT_GC_RESIZE: return "GC_RESIZE";
2485 case MONO_PROFILER_EVENT_GC_STOP_WORLD: return "GC_STOP_WORLD";
2486 case MONO_PROFILER_EVENT_GC_START_WORLD: return "GC_START_WORLD";
2487 default: g_assert_not_reached (); return "";
2491 event_result_to_string (MonoProfilerEventResult code) {
2493 case MONO_PROFILER_EVENT_RESULT_SUCCESS: return "SUCCESS";
2494 case MONO_PROFILER_EVENT_RESULT_FAILURE: return "FAILURE";
2495 default: g_assert_not_reached (); return "";
2499 event_kind_to_string (MonoProfilerEventKind code) {
2501 case MONO_PROFILER_EVENT_KIND_START: return "START";
2502 case MONO_PROFILER_EVENT_KIND_END: return "END";
2503 default: g_assert_not_reached (); return "";
2507 print_event_data (gsize thread_id, ProfilerEventData *event, guint64 value) {
2508 if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
2509 printf ("[TID %ld] CLASS[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s)\n",
2511 event->data.address,
2513 class_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
2514 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
2515 event_kind_to_string (event->kind),
2520 mono_class_get_namespace ((MonoClass*) event->data.address),
2521 mono_class_get_name ((MonoClass*) event->data.address));
2522 } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
2523 printf ("[TID %ld] METHOD[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s:%s (?))\n",
2525 event->data.address,
2527 method_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
2528 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
2529 event_kind_to_string (event->kind),
2534 mono_class_get_namespace (mono_method_get_class ((MonoMethod*) event->data.address)),
2535 mono_class_get_name (mono_method_get_class ((MonoMethod*) event->data.address)),
2536 mono_method_get_name ((MonoMethod*) event->data.address));
2538 printf ("[TID %ld] NUMBER[%ld] event [%p] %s:%s[%d-%d-%d] %ld\n",
2540 (guint64) event->data.number,
2542 number_event_code_to_string (event->code),
2543 event_kind_to_string (event->kind),
2550 #define LOG_EVENT(tid,ev,val) print_event_data ((tid),(ev),(val))
2552 #define LOG_EVENT(tid,ev,val)
2555 #define RESULT_TO_EVENT_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_EVENT_RESULT_SUCCESS:MONO_PROFILER_EVENT_RESULT_FAILURE)
2557 #define STORE_EVENT_ITEM_COUNTER(p,i,dt,c,k) do {\
2558 ProfilerPerThreadData *data;\
2559 ProfilerEventData *event;\
2562 GET_PROFILER_THREAD_DATA (data);\
2563 GET_NEXT_FREE_EVENT (data, event);\
2564 MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
2565 event->data.address = (i);\
2566 event->data_type = (dt);\
2569 delta = counter - data->last_event_counter;\
2570 if (delta < MAX_EVENT_VALUE) {\
2571 event->value = delta;\
2573 ProfilerEventData *extension = data->next_free_event;\
2574 data->next_free_event ++;\
2575 event->value = delta >> 32;\
2576 extension->data.number = delta & 0xffffffff;\
2578 data->last_event_counter = counter;\
2579 LOG_EVENT (data->thread_id, event, delta);\
2581 #define STORE_EVENT_ITEM_VALUE(p,i,dt,c,k,v) do {\
2582 ProfilerPerThreadData *data;\
2583 ProfilerEventData *event;\
2584 GET_PROFILER_THREAD_DATA (data);\
2585 GET_NEXT_FREE_EVENT (data, event);\
2586 event->data.address = (i);\
2587 event->data_type = (dt);\
2590 if ((v) < MAX_EVENT_VALUE) {\
2591 event->value = (v);\
2593 ProfilerEventData *extension = data->next_free_event;\
2594 data->next_free_event ++;\
2595 event->value = (v) >> 32;\
2596 extension->data.number = (v) & 0xffffffff;\
2598 LOG_EVENT (data->thread_id, event, (v));\
2600 #define STORE_EVENT_NUMBER_COUNTER(p,n,dt,c,k) do {\
2601 ProfilerPerThreadData *data;\
2602 ProfilerEventData *event;\
2605 GET_PROFILER_THREAD_DATA (data);\
2606 GET_NEXT_FREE_EVENT (data, event);\
2607 MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
2608 event->data.number = (n);\
2609 event->data_type = (dt);\
2612 delta = counter - data->last_event_counter;\
2613 if (delta < MAX_EVENT_VALUE) {\
2614 event->value = delta;\
2616 ProfilerEventData *extension = data->next_free_event;\
2617 data->next_free_event ++;\
2618 event->value = delta >> 32;\
2619 extension->data.number = delta & 0xffffffff;\
2621 data->last_event_counter = counter;\
2622 LOG_EVENT (data->thread_id, event, delta);\
2624 #define STORE_EVENT_NUMBER_VALUE(p,n,dt,c,k,v) do {\
2625 ProfilerPerThreadData *data;\
2626 ProfilerEventData *event;\
2627 GET_PROFILER_THREAD_DATA (data);\
2628 GET_NEXT_FREE_EVENT (data, event);\
2629 event->data.number = (n);\
2630 event->data_type = (dt);\
2633 if ((v) < MAX_EVENT_VALUE) {\
2634 event->value = (v);\
2636 ProfilerEventData *extension = data->next_free_event;\
2637 data->next_free_event ++;\
2638 event->value = (v) >> 32;\
2639 extension->data.number = (v) & 0xffffffff;\
2641 LOG_EVENT (data->thread_id, event, (v));\
2646 class_start_load (MonoProfiler *profiler, MonoClass *klass) {
2647 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD, MONO_PROFILER_EVENT_KIND_START);
2650 class_end_load (MonoProfiler *profiler, MonoClass *klass, int result) {
2651 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD | RESULT_TO_EVENT_CODE (result), MONO_PROFILER_EVENT_KIND_END);
2654 class_start_unload (MonoProfiler *profiler, MonoClass *klass) {
2655 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_START);
2658 class_end_unload (MonoProfiler *profiler, MonoClass *klass) {
2659 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_END);
2663 method_start_jit (MonoProfiler *profiler, MonoMethod *method) {
2664 if (profiler->action_flags.jit_time) {
2665 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT, MONO_PROFILER_EVENT_KIND_START);
2669 method_end_jit (MonoProfiler *profiler, MonoMethod *method, int result) {
2670 if (profiler->action_flags.jit_time) {
2671 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT | RESULT_TO_EVENT_CODE (result), MONO_PROFILER_EVENT_KIND_END);
2677 method_jit_result (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result) {
2678 if (profiler->action_flags.oprofile && (result == MONO_PROFILE_OK)) {
2679 MonoClass *klass = mono_method_get_class (method);
2680 char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
2681 char *name = g_strdup_printf ("%s.%s:%s (%s)", mono_class_get_namespace (klass), mono_class_get_name (klass), mono_method_get_name (method), signature);
2682 gpointer code_start = mono_jit_info_get_code_start (jinfo);
2683 int code_size = mono_jit_info_get_code_size (jinfo);
2685 if (op_write_native_code (name, code_start, code_size)) {
2686 g_warning ("Problem calling op_write_native_code\n");
2697 method_enter (MonoProfiler *profiler, MonoMethod *method) {
2698 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_START);
2701 method_leave (MonoProfiler *profiler, MonoMethod *method) {
2702 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_END);
2706 method_free (MonoProfiler *profiler, MonoMethod *method) {
2707 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_FREED, 0);
2711 thread_start (MonoProfiler *profiler, gsize tid) {
2712 STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_START);
2715 thread_end (MonoProfiler *profiler, gsize tid) {
2716 STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_END);
2720 object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) {
2721 ProfilerPerThreadData *thread_data;
2723 STORE_EVENT_ITEM_VALUE (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_ALLOCATION, 0, (guint64) mono_object_get_size (obj));
2724 if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
2725 GET_PROFILER_THREAD_DATA (thread_data);
2726 STORE_ALLOCATED_OBJECT (thread_data, obj);
2732 statistical_hit (MonoProfiler *profiler, guchar *ip, void *context) {
2733 ProfilerStatisticalData *data;
2737 data = profiler->statistical_data;
2738 index = InterlockedIncrement (&data->next_free_index);
2740 if (index <= data->end_index) {
2741 data->addresses [index - 1] = (gpointer) ip;
2743 /* Check if we are the one that must swap the buffers */
2744 if (index == data->end_index + 1) {
2745 ProfilerStatisticalData *new_data;
2747 /* In the *impossible* case that the writer thread has not finished yet, */
2748 /* loop waiting for it and meanwhile lose all statistical events... */
2750 /* First, wait that it consumed the ready buffer */
2751 while (profiler->statistical_data_ready != NULL);
2752 /* Then, wait that it produced the free buffer */
2753 new_data = profiler->statistical_data_second_buffer;
2754 } while (new_data == NULL);
2756 profiler->statistical_data_ready = data;
2757 profiler->statistical_data = new_data;
2758 profiler->statistical_data_second_buffer = NULL;
2759 WRITER_EVENT_RAISE ();
2762 /* Loop again, hoping to acquire a free slot this time */
2765 } while (data == NULL);
2768 static MonoProfilerEvents
2769 gc_event_code_from_profiler_event (MonoGCEvent event) {
2771 case MONO_GC_EVENT_START:
2772 case MONO_GC_EVENT_END:
2773 return MONO_PROFILER_EVENT_GC_COLLECTION;
2774 case MONO_GC_EVENT_MARK_START:
2775 case MONO_GC_EVENT_MARK_END:
2776 return MONO_PROFILER_EVENT_GC_MARK;
2777 case MONO_GC_EVENT_RECLAIM_START:
2778 case MONO_GC_EVENT_RECLAIM_END:
2779 return MONO_PROFILER_EVENT_GC_SWEEP;
2780 case MONO_GC_EVENT_PRE_STOP_WORLD:
2781 case MONO_GC_EVENT_POST_STOP_WORLD:
2782 return MONO_PROFILER_EVENT_GC_STOP_WORLD;
2783 case MONO_GC_EVENT_PRE_START_WORLD:
2784 case MONO_GC_EVENT_POST_START_WORLD:
2785 return MONO_PROFILER_EVENT_GC_START_WORLD;
2787 g_assert_not_reached ();
2792 static MonoProfilerEventKind
2793 gc_event_kind_from_profiler_event (MonoGCEvent event) {
2795 case MONO_GC_EVENT_START:
2796 case MONO_GC_EVENT_MARK_START:
2797 case MONO_GC_EVENT_RECLAIM_START:
2798 case MONO_GC_EVENT_PRE_STOP_WORLD:
2799 case MONO_GC_EVENT_PRE_START_WORLD:
2800 return MONO_PROFILER_EVENT_KIND_START;
2801 case MONO_GC_EVENT_END:
2802 case MONO_GC_EVENT_MARK_END:
2803 case MONO_GC_EVENT_RECLAIM_END:
2804 case MONO_GC_EVENT_POST_START_WORLD:
2805 case MONO_GC_EVENT_POST_STOP_WORLD:
2806 return MONO_PROFILER_EVENT_KIND_END;
2808 g_assert_not_reached ();
2813 #define HEAP_SHOT_COMMAND_FILE_MAX_LENGTH 64
2815 profiler_heap_shot_process_command_file (void) {
2816 //FIXME: Port to Windows as well
2817 struct stat stat_buf;
2819 char buffer [HEAP_SHOT_COMMAND_FILE_MAX_LENGTH + 1];
2821 if (profiler->heap_shot_command_file_name == NULL)
2823 if (stat (profiler->heap_shot_command_file_name, &stat_buf) != 0)
2825 if (stat_buf.st_size > HEAP_SHOT_COMMAND_FILE_MAX_LENGTH)
2827 if ((stat_buf.st_mtim.tv_sec * 1000000) < profiler->heap_shot_command_file_access_time)
2830 fd = open (profiler->heap_shot_command_file_name, O_RDONLY);
2834 if (read (fd, &(buffer [0]), stat_buf.st_size) != stat_buf.st_size) {
2837 buffer [stat_buf.st_size] = 0;
2838 profiler->dump_next_heap_snapshots = atoi (buffer);
2839 MONO_PROFILER_GET_CURRENT_TIME (profiler->heap_shot_command_file_access_time);
2846 dump_current_heap_snapshot (void) {
2849 if (profiler->heap_shot_was_signalled) {
2852 profiler_heap_shot_process_command_file ();
2853 if (profiler->dump_next_heap_snapshots > 0) {
2854 profiler->dump_next_heap_snapshots--;
2856 } else if (profiler->dump_next_heap_snapshots < 0) {
2867 profiler_heap_buffers_setup (ProfilerHeapShotHeapBuffers *heap) {
2868 heap->buffers = g_new (ProfilerHeapShotHeapBuffer, 1);
2869 heap->buffers->previous = NULL;
2870 heap->buffers->next = NULL;
2871 heap->buffers->start_slot = &(heap->buffers->buffer [0]);
2872 heap->buffers->end_slot = &(heap->buffers->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
2873 heap->last = heap->buffers;
2874 heap->current = heap->buffers;
2875 heap->first_free_slot = & (heap->buffers->buffer [0]);
2878 profiler_heap_buffers_clear (ProfilerHeapShotHeapBuffers *heap) {
2879 heap->buffers = NULL;
2881 heap->current = NULL;
2882 heap->first_free_slot = NULL;
2885 profiler_heap_buffers_free (ProfilerHeapShotHeapBuffers *heap) {
2886 ProfilerHeapShotHeapBuffer *current = heap->buffers;
2887 while (current != NULL) {
2888 ProfilerHeapShotHeapBuffer *next = current->next;
2892 profiler_heap_buffers_clear (heap);
2896 report_object_references (gpointer *start, ClassIdMappingElement *layout, ProfilerHeapShotWriteJob *job) {
2897 int reported_references = 0;
2900 for (slot = 0; slot < layout->data.layout.slots; slot ++) {
2901 gboolean slot_has_reference;
2902 if (layout->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
2903 if (layout->data.bitmap.compact & (((guint64)1) << slot)) {
2904 slot_has_reference = TRUE;
2906 slot_has_reference = FALSE;
2909 if (layout->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
2910 slot_has_reference = TRUE;
2912 slot_has_reference = FALSE;
2916 if (slot_has_reference) {
2917 gpointer field = start [slot];
2919 if ((field != NULL) && mono_object_is_alive (field)) {
2920 reported_references ++;
2921 WRITE_HEAP_SHOT_JOB_VALUE (job, field);
2926 return reported_references;
2930 profiler_heap_report_object_reachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
2931 if (profiler->action_flags.heap_shot && (job != NULL)) {
2932 MonoClass *klass = mono_object_get_class (obj);
2933 int reference_counter = 0;
2934 gpointer *reference_counter_location;
2936 WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, obj, HEAP_CODE_OBJECT);
2937 #if DEBUG_HEAP_PROFILER
2938 printf ("profiler_heap_report_object_reachable: reported object %p at cursor %p\n", obj, (job->cursor - 1));
2940 WRITE_HEAP_SHOT_JOB_VALUE (job, NULL);
2941 reference_counter_location = job->cursor - 1;
2943 if (mono_class_get_rank (klass)) {
2944 MonoArray *array = (MonoArray *) obj;
2945 MonoClass *element_class = mono_class_get_element_class (klass);
2946 ClassIdMappingElement *element_id = class_id_mapping_element_get (element_class);
2948 g_assert (element_id != NULL);
2949 if (element_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
2950 class_id_mapping_element_build_layout_bitmap (element_class, element_id);
2952 if (! mono_class_is_valuetype (element_class)) {
2953 int length = mono_array_length (array);
2955 for (i = 0; i < length; i++) {
2956 MonoObject *array_element = mono_array_get (array, MonoObject*, i);
2957 if ((array_element != NULL) && mono_object_is_alive (array_element)) {
2958 reference_counter ++;
2959 WRITE_HEAP_SHOT_JOB_VALUE (job, array_element);
2962 } else if (element_id->data.layout.references > 0) {
2963 int length = mono_array_length (array);
2964 int array_element_size = mono_array_element_size (klass);
2966 for (i = 0; i < length; i++) {
2967 gpointer array_element_address = mono_array_addr_with_size (array, array_element_size, i);
2968 reference_counter += report_object_references (array_element_address, element_id, job);
2972 ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
2973 if (class_id == NULL) {
2974 printf ("profiler_heap_report_object_reachable: class %p (%s.%s) has no id\n", klass, mono_class_get_namespace (klass), mono_class_get_name (klass));
2976 g_assert (class_id != NULL);
2977 if (class_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
2978 class_id_mapping_element_build_layout_bitmap (klass, class_id);
2980 if (class_id->data.layout.references > 0) {
2981 reference_counter += report_object_references ((gpointer)(((char*)obj) + sizeof (MonoObject)), class_id, job);
2985 *reference_counter_location = GINT_TO_POINTER (reference_counter);
2986 #if DEBUG_HEAP_PROFILER
2987 printf ("profiler_heap_report_object_reachable: updated reference_counter_location %p with value %d\n", reference_counter_location, reference_counter);
2992 profiler_heap_report_object_unreachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
2994 MonoClass *klass = mono_object_get_class (obj);
2995 guint32 size = mono_object_get_size (obj);
2997 #if DEBUG_HEAP_PROFILER
2998 printf ("profiler_heap_report_object_unreachable: at job %p writing klass %p\n", job, klass);
3000 WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, klass, HEAP_CODE_FREE_OBJECT_CLASS);
3002 #if DEBUG_HEAP_PROFILER
3003 printf ("profiler_heap_report_object_unreachable: at job %p writing size %p\n", job, GUINT_TO_POINTER (size));
3005 WRITE_HEAP_SHOT_JOB_VALUE (job, GUINT_TO_POINTER (size));
3010 profiler_heap_add_object (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject *obj) {
3011 if (heap->first_free_slot >= heap->current->end_slot) {
3012 if (heap->current->next != NULL) {
3013 heap->current = heap->current->next;
3015 ProfilerHeapShotHeapBuffer *buffer = g_new (ProfilerHeapShotHeapBuffer, 1);
3016 buffer->previous = heap->last;
3017 buffer->next = NULL;
3018 buffer->start_slot = &(buffer->buffer [0]);
3019 buffer->end_slot = &(buffer->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
3020 heap->current = buffer;
3021 heap->last->next = buffer;
3022 heap->last = buffer;
3024 heap->first_free_slot = &(heap->current->buffer [0]);
3027 *(heap->first_free_slot) = obj;
3028 heap->first_free_slot ++;
3029 profiler_heap_report_object_reachable (job, obj);
3033 profiler_heap_pop_object_from_end (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject** current_slot) {
3034 while (heap->first_free_slot != current_slot) {
3037 if (heap->first_free_slot > heap->current->start_slot) {
3038 heap->first_free_slot --;
3040 heap->current = heap->current->previous;
3041 g_assert (heap->current != NULL);
3042 heap->first_free_slot = heap->current->end_slot - 1;
3045 obj = *(heap->first_free_slot);
3047 if (mono_object_is_alive (obj)) {
3048 profiler_heap_report_object_reachable (job, obj);
3051 profiler_heap_report_object_unreachable (job, obj);
3058 profiler_heap_scan (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job) {
3059 ProfilerHeapShotHeapBuffer *current_buffer = heap->buffers;
3060 MonoObject** current_slot = current_buffer->start_slot;
3062 while (current_slot != heap->first_free_slot) {
3063 MonoObject *obj = *current_slot;
3064 if (mono_object_is_alive (obj)) {
3065 profiler_heap_report_object_reachable (job, obj);
3067 profiler_heap_report_object_unreachable (job, obj);
3068 *current_slot = profiler_heap_pop_object_from_end (heap, job, current_slot);
3071 if (*current_slot != NULL) {
3074 if (current_slot == current_buffer->end_slot) {
3075 current_buffer = current_buffer->next;
3076 g_assert (current_buffer != NULL);
3077 current_slot = current_buffer->start_slot;
3084 handle_heap_profiling (MonoProfiler *profiler, MonoGCEvent ev) {
3086 case MONO_GC_EVENT_PRE_STOP_WORLD:
3087 // Get the lock, so we are sure nobody is flushing events during the collection,
3088 // and we can update all mappings (building the class descriptors).
3091 case MONO_GC_EVENT_POST_STOP_WORLD:
3092 // Update all mappings, so that we have built all the class descriptors.
3093 flush_all_mappings ();
3097 case MONO_GC_EVENT_MARK_END: {
3098 ProfilerHeapShotWriteJob *job;
3099 ProfilerPerThreadData *data;
3101 if (dump_current_heap_snapshot ()) {
3102 job = profiler_heap_shot_write_job_new (profiler->heap_shot_was_signalled);
3103 profiler->heap_shot_was_signalled = FALSE;
3104 MONO_PROFILER_GET_CURRENT_COUNTER (job->start_counter);
3105 MONO_PROFILER_GET_CURRENT_TIME (job->start_time);
3110 profiler_heap_scan (&(profiler->heap), job);
3112 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3113 ProfilerHeapShotObjectBuffer *buffer;
3114 for (buffer = data->heap_shot_object_buffers; buffer != NULL; buffer = buffer->next) {
3115 MonoObject **cursor;
3116 for (cursor = buffer->first_unprocessed_slot; cursor < buffer->next_free_slot; cursor ++) {
3117 MonoObject *obj = *cursor;
3118 #if DEBUG_HEAP_PROFILER
3119 printf ("gc_event: in object buffer %p(%p-%p) cursor at %p has object %p ", buffer, &(buffer->buffer [0]), buffer->end, cursor, obj);
3121 if (mono_object_is_alive (obj)) {
3122 #if DEBUG_HEAP_PROFILER
3123 printf ("(object is alive, adding to heap)\n");
3125 profiler_heap_add_object (&(profiler->heap), job, obj);
3127 #if DEBUG_HEAP_PROFILER
3128 printf ("(object is unreachable, reporting in job)\n");
3130 profiler_heap_report_object_unreachable (job, obj);
3133 buffer->first_unprocessed_slot = cursor;
3138 MONO_PROFILER_GET_CURRENT_COUNTER (job->end_counter);
3139 MONO_PROFILER_GET_CURRENT_TIME (job->end_time);
3141 profiler_add_heap_shot_write_job (job);
3142 profiler_free_heap_shot_write_jobs ();
3143 WRITER_EVENT_RAISE ();
3153 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
3154 STORE_EVENT_NUMBER_COUNTER (profiler, generation, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, gc_event_code_from_profiler_event (ev), gc_event_kind_from_profiler_event (ev));
3155 if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
3156 handle_heap_profiling (profiler, ev);
3161 gc_resize (MonoProfiler *profiler, gint64 new_size) {
3162 STORE_EVENT_NUMBER_COUNTER (profiler, new_size, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_GC_RESIZE, 0);
3165 /* called at the end of the program */
3167 profiler_shutdown (MonoProfiler *prof)
3169 ProfilerPerThreadData* current_thread_data;
3171 LOG_WRITER_THREAD ("profiler_shutdown: zeroing relevant flags");
3172 mono_profiler_set_events (0);
3173 //profiler->flags = 0;
3174 //profiler->action_flags.unreachable_objects = FALSE;
3175 //profiler->action_flags.heap_shot = FALSE;
3177 LOG_WRITER_THREAD ("profiler_shutdown: asking stats thread to exit");
3178 profiler->terminate_writer_thread = TRUE;
3179 WRITER_EVENT_RAISE ();
3180 LOG_WRITER_THREAD ("profiler_shutdown: waiting for stats thread to exit");
3181 WAIT_WRITER_THREAD ();
3182 LOG_WRITER_THREAD ("profiler_shutdown: stats thread should be dead now");
3183 WRITER_EVENT_DESTROY ();
3187 MONO_PROFILER_GET_CURRENT_TIME (profiler->end_time);
3188 MONO_PROFILER_GET_CURRENT_COUNTER (profiler->end_counter);
3190 flush_everything ();
3195 g_free (profiler->file_name);
3196 if (profiler->file_name_suffix != NULL) {
3197 g_free (profiler->file_name_suffix);
3200 method_id_mapping_destroy (profiler->methods);
3201 class_id_mapping_destroy (profiler->classes);
3202 g_hash_table_destroy (profiler->loaded_assemblies);
3203 g_hash_table_destroy (profiler->loaded_modules);
3204 g_hash_table_destroy (profiler->loaded_appdomains);
3206 FREE_PROFILER_THREAD_DATA ();
3208 for (current_thread_data = profiler->per_thread_data; current_thread_data != NULL; current_thread_data = current_thread_data->next) {
3209 profiler_per_thread_data_destroy (current_thread_data);
3211 if (profiler->statistical_data != NULL) {
3212 profiler_statistical_data_destroy (profiler->statistical_data);
3214 if (profiler->statistical_data_ready != NULL) {
3215 profiler_statistical_data_destroy (profiler->statistical_data_ready);
3217 if (profiler->statistical_data_second_buffer != NULL) {
3218 profiler_statistical_data_destroy (profiler->statistical_data_second_buffer);
3220 if (profiler->executable_regions != NULL) {
3221 profiler_executable_memory_regions_destroy (profiler->executable_regions);
3223 unmanaged_functions_dispose (&(profiler->unmanaged_functions));
3225 profiler_heap_buffers_free (&(profiler->heap));
3226 if (profiler->heap_shot_command_file_name != NULL) {
3227 g_free (profiler->heap_shot_command_file_name);
3230 profiler_free_write_buffers ();
3231 profiler_destroy_heap_shot_write_jobs ();
3233 DELETE_PROFILER_MUTEX ();
3236 if (profiler->action_flags.oprofile) {
3245 #define DEFAULT_ARGUMENTS "s"
3247 setup_user_options (const char *arguments) {
3248 gchar **arguments_array, **current_argument;
3249 #ifndef PLATFORM_WIN32
3250 int gc_request_signal_number = 0;
3253 profiler->file_name = NULL;
3254 profiler->file_name_suffix = NULL;
3255 profiler->per_thread_buffer_size = 10000;
3256 profiler->statistical_buffer_size = 10000;
3257 profiler->write_buffer_size = 1024;
3258 profiler->heap_shot_command_file_name = NULL;
3259 profiler->dump_next_heap_snapshots = 0;
3260 profiler->heap_shot_command_file_access_time = 0;
3261 profiler->heap_shot_was_signalled = FALSE;
3262 profiler->flags = MONO_PROFILE_APPDOMAIN_EVENTS|
3263 MONO_PROFILE_ASSEMBLY_EVENTS|
3264 MONO_PROFILE_MODULE_EVENTS|
3265 MONO_PROFILE_CLASS_EVENTS|
3266 MONO_PROFILE_METHOD_EVENTS;
3268 if (arguments == NULL) {
3269 arguments = DEFAULT_ARGUMENTS;
3270 } else if (strstr (arguments, ":")) {
3271 arguments = strstr (arguments, ":") + 1;
3272 if (arguments [0] == 0) {
3273 arguments = DEFAULT_ARGUMENTS;
3277 arguments_array = g_strsplit (arguments, ",", -1);
3279 for (current_argument = arguments_array; ((current_argument != NULL) && (current_argument [0] != 0)); current_argument ++) {
3280 char *argument = *current_argument;
3281 char *equals = strstr (argument, "=");
3283 if (equals != NULL) {
3284 int equals_position = equals - argument;
3286 if (! (strncmp (argument, "per-thread-buffer-size", equals_position) && strncmp (argument, "tbs", equals_position))) {
3287 int value = atoi (equals + 1);
3289 profiler->per_thread_buffer_size = value;
3291 } else if (! (strncmp (argument, "statistical-thread-buffer-size", equals_position) && strncmp (argument, "sbs", equals_position))) {
3292 int value = atoi (equals + 1);
3294 profiler->statistical_buffer_size = value;
3296 } else if (! (strncmp (argument, "write-buffer-size", equals_position) && strncmp (argument, "wbs", equals_position))) {
3297 int value = atoi (equals + 1);
3299 profiler->write_buffer_size = value;
3301 } else if (! (strncmp (argument, "output", equals_position) && strncmp (argument, "out", equals_position) && strncmp (argument, "o", equals_position) && strncmp (argument, "O", equals_position))) {
3302 if (strlen (equals + 1) > 0) {
3303 profiler->file_name = g_strdup (equals + 1);
3305 } else if (! (strncmp (argument, "output-suffix", equals_position) && strncmp (argument, "suffix", equals_position) && strncmp (argument, "os", equals_position) && strncmp (argument, "OS", equals_position))) {
3306 if (strlen (equals + 1) > 0) {
3307 profiler->file_name_suffix = g_strdup (equals + 1);
3309 } else if (! (strncmp (argument, "gc-commands", equals_position) && strncmp (argument, "gc-c", equals_position) && strncmp (argument, "gcc", equals_position))) {
3310 if (strlen (equals + 1) > 0) {
3311 profiler->heap_shot_command_file_name = g_strdup (equals + 1);
3313 } else if (! (strncmp (argument, "gc-dumps", equals_position) && strncmp (argument, "gc-d", equals_position) && strncmp (argument, "gcd", equals_position))) {
3314 if (strlen (equals + 1) > 0) {
3315 profiler->dump_next_heap_snapshots = atoi (equals + 1);
3317 #ifndef PLATFORM_WIN32
3318 } else if (! (strncmp (argument, "gc-signal", equals_position) && strncmp (argument, "gc-s", equals_position) && strncmp (argument, "gcs", equals_position))) {
3319 if (strlen (equals + 1) > 0) {
3320 char *signal_name = equals + 1;
3321 if (! strcasecmp (signal_name, "SIGUSR1")) {
3322 gc_request_signal_number = SIGUSR1;
3323 } else if (! strcasecmp (signal_name, "SIGUSR2")) {
3324 gc_request_signal_number = SIGUSR2;
3325 } else if (! strcasecmp (signal_name, "SIGPROF")) {
3326 gc_request_signal_number = SIGPROF;
3328 gc_request_signal_number = atoi (signal_name);
3333 g_warning ("Cannot parse valued argument %s\n", argument);
3336 if (! (strcmp (argument, "jit") && strcmp (argument, "j"))) {
3337 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
3338 profiler->action_flags.jit_time = TRUE;
3339 } else if (! (strcmp (argument, "allocations") && strcmp (argument, "alloc") && strcmp (argument, "a"))) {
3340 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
3341 } else if (! (strcmp (argument, "gc") && strcmp (argument, "g"))) {
3342 profiler->flags |= MONO_PROFILE_GC;
3343 } else if (! (strcmp (argument, "heap-shot") && strcmp (argument, "heap") && strcmp (argument, "h"))) {
3344 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
3345 profiler->action_flags.unreachable_objects = TRUE;
3346 profiler->action_flags.heap_shot = TRUE;
3347 } else if (! (strcmp (argument, "unreachable") && strcmp (argument, "free") && strcmp (argument, "f"))) {
3348 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
3349 profiler->action_flags.unreachable_objects = TRUE;
3350 } else if (! (strcmp (argument, "threads") && strcmp (argument, "t"))) {
3351 profiler->flags |= MONO_PROFILE_THREADS;
3352 } else if (! (strcmp (argument, "enter-leave") && strcmp (argument, "calls") && strcmp (argument, "c"))) {
3353 profiler->flags |= MONO_PROFILE_ENTER_LEAVE;
3354 } else if (! (strcmp (argument, "statistical") && strcmp (argument, "stat") && strcmp (argument, "s"))) {
3355 profiler->flags |= MONO_PROFILE_STATISTICAL|MONO_PROFILE_JIT_COMPILATION;
3356 profiler->action_flags.jit_time = TRUE;
3358 } else if (! (strcmp (argument, "oprofile") && strcmp (argument, "oprof"))) {
3359 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
3360 profiler->action_flags.oprofile = TRUE;
3361 if (op_open_agent ()) {
3362 g_warning ("Problem calling op_open_agent\n");
3365 } else if (strcmp (argument, "logging")) {
3366 g_warning ("Cannot parse flag argument %s\n", argument);
3371 g_free (arguments_array);
3373 #ifndef PLATFORM_WIN32
3374 if (gc_request_signal_number != 0) {
3375 if (((gc_request_signal_number == SIGPROF) && ! (profiler->flags & MONO_PROFILE_STATISTICAL)) ||
3376 (gc_request_signal_number == SIGUSR1) ||
3377 (gc_request_signal_number == SIGUSR2)) {
3378 add_gc_request_handler (gc_request_signal_number);
3380 g_error ("Cannot use signal %d", gc_request_signal_number);
3385 if (profiler->file_name == NULL) {
3386 char *program_name = g_get_prgname ();
3388 if (program_name != NULL) {
3389 char *name_buffer = g_strdup (program_name);
3390 char *name_start = name_buffer;
3393 /* Jump over the last '/' */
3394 cursor = strrchr (name_buffer, '/');
3395 if (cursor == NULL) {
3396 cursor = name_buffer;
3400 name_start = cursor;
3402 /* Then jump over the last '\\' */
3403 cursor = strrchr (name_start, '\\');
3404 if (cursor == NULL) {
3405 cursor = name_start;
3409 name_start = cursor;
3411 /* Finally, find the last '.' */
3412 cursor = strrchr (name_start, '.');
3413 if (cursor != NULL) {
3417 if (profiler->file_name_suffix == NULL) {
3418 profiler->file_name = g_strdup_printf ("%s.mprof", name_start);
3420 profiler->file_name = g_strdup_printf ("%s-%s.mprof", name_start, profiler->file_name_suffix);
3422 g_free (name_buffer);
3424 profiler->file_name = g_strdup_printf ("%s.mprof", "profiler-log");
3430 thread_detach_callback (MonoThread *thread) {
3431 LOG_WRITER_THREAD ("thread_detach_callback: asking writer thread to detach");
3432 profiler->detach_writer_thread = TRUE;
3433 WRITER_EVENT_RAISE ();
3434 LOG_WRITER_THREAD ("thread_detach_callback: done");
3439 data_writer_thread (gpointer nothing) {
3440 static gboolean thread_attached = FALSE;
3441 static gboolean thread_detached = FALSE;
3442 static MonoThread *this_thread = NULL;
3445 ProfilerStatisticalData *statistical_data;
3448 LOG_WRITER_THREAD ("data_writer_thread: going to sleep");
3449 WRITER_EVENT_WAIT ();
3450 LOG_WRITER_THREAD ("data_writer_thread: just woke up");
3452 if (! thread_attached) {
3453 if (! profiler->terminate_writer_thread) {
3454 MonoDomain * root_domain = mono_get_root_domain ();
3455 if (root_domain != NULL) {
3456 LOG_WRITER_THREAD ("data_writer_thread: attaching thread");
3457 this_thread = mono_thread_attach (root_domain);
3458 mono_thread_set_manage_callback (this_thread, thread_detach_callback);
3459 thread_attached = TRUE;
3461 g_error ("Cannot get root domain\n");
3464 /* Execution was too short, pretend we attached and detached. */
3465 thread_attached = TRUE;
3466 thread_detached = TRUE;
3470 if (profiler->heap_shot_was_signalled) {
3471 LOG_WRITER_THREAD ("data_writer_thread: starting requested collection");
3472 mono_gc_collect (mono_gc_max_generation ());
3473 LOG_WRITER_THREAD ("data_writer_thread: requested collection done");
3476 statistical_data = profiler->statistical_data_ready;
3477 done = (statistical_data == NULL) && (profiler->heap_shot_write_jobs == NULL);
3480 LOG_WRITER_THREAD ("data_writer_thread: acquiring lock and writing data");
3483 // This makes sure that all method ids are in place
3484 LOG_WRITER_THREAD ("data_writer_thread: writing mapping...");
3485 flush_all_mappings ();
3486 LOG_WRITER_THREAD ("data_writer_thread: wrote mapping");
3488 if ((statistical_data != NULL) && ! thread_detached) {
3489 LOG_WRITER_THREAD ("data_writer_thread: writing statistical data...");
3490 profiler->statistical_data_ready = NULL;
3491 write_statistical_data_block (statistical_data);
3492 statistical_data->next_free_index = 0;
3493 statistical_data->first_unwritten_index = 0;
3494 profiler->statistical_data_second_buffer = statistical_data;
3495 LOG_WRITER_THREAD ("data_writer_thread: wrote statistical data");
3498 profiler_process_heap_shot_write_jobs ();
3501 LOG_WRITER_THREAD ("data_writer_thread: wrote data and released lock");
3504 if (profiler->detach_writer_thread) {
3505 if (this_thread != NULL) {
3506 LOG_WRITER_THREAD ("data_writer_thread: detaching thread");
3507 mono_thread_detach (this_thread);
3509 profiler->detach_writer_thread = FALSE;
3510 thread_detached = TRUE;
3512 LOG_WRITER_THREAD ("data_writer_thread: warning: thread has already been detached");
3516 if (profiler->terminate_writer_thread) {
3517 LOG_WRITER_THREAD ("data_writer_thread: exiting thread");
3525 mono_profiler_startup (const char *desc);
3527 /* the entry point (mono_profiler_load?) */
3529 mono_profiler_startup (const char *desc)
3531 profiler = g_new0 (MonoProfiler, 1);
3533 setup_user_options ((desc != NULL) ? desc : "");
3535 INITIALIZE_PROFILER_MUTEX ();
3536 MONO_PROFILER_GET_CURRENT_TIME (profiler->start_time);
3537 MONO_PROFILER_GET_CURRENT_COUNTER (profiler->start_counter);
3538 profiler->last_header_counter = 0;
3540 profiler->methods = method_id_mapping_new ();
3541 profiler->classes = class_id_mapping_new ();
3542 profiler->loaded_assemblies = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
3543 profiler->loaded_modules = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
3544 profiler->loaded_appdomains = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
3546 profiler->statistical_data = profiler_statistical_data_new (profiler->statistical_buffer_size);
3547 profiler->statistical_data_second_buffer = profiler_statistical_data_new (profiler->statistical_buffer_size);
3548 unmanaged_functions_init (&(profiler->unmanaged_functions));
3550 profiler->write_buffers = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
3551 profiler->write_buffers->next = NULL;
3552 profiler->current_write_buffer = profiler->write_buffers;
3553 profiler->current_write_position = 0;
3554 profiler->full_write_buffers = 0;
3556 profiler->executable_regions = profiler_executable_memory_regions_new (1);
3558 profiler->heap_shot_write_jobs = NULL;
3559 if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
3560 profiler_heap_buffers_setup (&(profiler->heap));
3562 profiler_heap_buffers_clear (&(profiler->heap));
3565 WRITER_EVENT_INIT ();
3566 LOG_WRITER_THREAD ("mono_profiler_startup: creating writer thread");
3567 CREATE_WRITER_THREAD (data_writer_thread);
3568 LOG_WRITER_THREAD ("mono_profiler_startup: created writer thread");
3570 ALLOCATE_PROFILER_THREAD_DATA ();
3574 write_intro_block ();
3576 mono_profiler_install (profiler, profiler_shutdown);
3578 mono_profiler_install_appdomain (appdomain_start_load, appdomain_end_load,
3579 appdomain_start_unload, appdomain_end_unload);
3580 mono_profiler_install_assembly (assembly_start_load, assembly_end_load,
3581 assembly_start_unload, assembly_end_unload);
3582 mono_profiler_install_module (module_start_load, module_end_load,
3583 module_start_unload, module_end_unload);
3584 mono_profiler_install_class (class_start_load, class_end_load,
3585 class_start_unload, class_end_unload);
3586 mono_profiler_install_jit_compile (method_start_jit, method_end_jit);
3587 mono_profiler_install_enter_leave (method_enter, method_leave);
3588 mono_profiler_install_method_free (method_free);
3589 mono_profiler_install_thread (thread_start, thread_end);
3590 mono_profiler_install_allocation (object_allocated);
3591 mono_profiler_install_statistical (statistical_hit);
3592 mono_profiler_install_gc (gc_event, gc_resize);
3594 mono_profiler_install_jit_end (method_jit_result);
3597 mono_profiler_set_events (profiler->flags);