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 EVENT_VALUE_BITS (26)
62 #define MAX_EVENT_VALUE ((1<<EVENT_VALUE_BITS)-1)
65 MONO_PROFILER_EVENT_METHOD_JIT = 0,
66 MONO_PROFILER_EVENT_METHOD_FREED = 1,
67 MONO_PROFILER_EVENT_METHOD_CALL = 2
68 } MonoProfilerMethodEvents;
70 MONO_PROFILER_EVENT_CLASS_LOAD = 0,
71 MONO_PROFILER_EVENT_CLASS_UNLOAD = 1,
72 MONO_PROFILER_EVENT_CLASS_EXCEPTION = 2,
73 MONO_PROFILER_EVENT_CLASS_ALLOCATION = 3
74 } MonoProfilerClassEvents;
76 MONO_PROFILER_EVENT_RESULT_SUCCESS = 0,
77 MONO_PROFILER_EVENT_RESULT_FAILURE = 4
78 } MonoProfilerEventResult;
79 #define MONO_PROFILER_EVENT_RESULT_MASK MONO_PROFILER_EVENT_RESULT_FAILURE
81 MONO_PROFILER_EVENT_THREAD = 1,
82 MONO_PROFILER_EVENT_GC_COLLECTION = 2,
83 MONO_PROFILER_EVENT_GC_MARK = 3,
84 MONO_PROFILER_EVENT_GC_SWEEP = 4,
85 MONO_PROFILER_EVENT_GC_RESIZE = 5,
86 MONO_PROFILER_EVENT_GC_STOP_WORLD = 6,
87 MONO_PROFILER_EVENT_GC_START_WORLD = 7
90 MONO_PROFILER_EVENT_KIND_START = 0,
91 MONO_PROFILER_EVENT_KIND_END = 1
92 } MonoProfilerEventKind;
94 #define MONO_PROFILER_GET_CURRENT_TIME(t) {\
95 struct timeval current_time;\
96 gettimeofday (¤t_time, NULL);\
97 (t) = (((guint64)current_time.tv_sec) * 1000000) + current_time.tv_usec;\
100 static gboolean use_fast_timer = FALSE;
102 #if defined(__i386__) || defined(__x86_64__)
103 static void detect_fast_timer (void) {
105 guint32 eax,ebx,ecx,edx;
106 __asm__ __volatile__ ("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(op));
108 use_fast_timer = TRUE;
110 use_fast_timer = FALSE;
114 static __inline__ guint64 rdtsc(void) {
116 __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
117 return ((guint64) lo) | (((guint64) hi) << 32);
119 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) {\
120 if (use_fast_timer) {\
123 MONO_PROFILER_GET_CURRENT_TIME ((c));\
127 static detect_fast_timer (void) {
128 use_fast_timer = FALSE;
130 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) MONO_PROFILER_GET_CURRENT_TIME ((c))
134 #define CLASS_LAYOUT_PACKED_BITMAP_SIZE 64
135 #define CLASS_LAYOUT_NOT_INITIALIZED (0xFFFF)
138 HEAP_CODE_OBJECT = 1,
139 HEAP_CODE_FREE_OBJECT_CLASS = 2,
141 } HeapProfilerJobValueCode;
142 typedef struct _MonoProfilerClassData {
151 } MonoProfilerClassData;
153 typedef struct _MonoProfilerMethodData {
156 } MonoProfilerMethodData;
158 typedef struct _ClassIdMappingElement {
162 struct _ClassIdMappingElement *next_unwritten;
163 MonoProfilerClassData data;
164 } ClassIdMappingElement;
166 typedef struct _MethodIdMappingElement {
170 struct _MethodIdMappingElement *next_unwritten;
171 MonoProfilerMethodData data;
172 } MethodIdMappingElement;
174 typedef struct _ClassIdMapping {
176 ClassIdMappingElement *unwritten;
180 typedef struct _MethodIdMapping {
182 MethodIdMappingElement *unwritten;
186 typedef struct _LoadedElement {
188 guint64 load_start_counter;
189 guint64 load_end_counter;
190 guint64 unload_start_counter;
191 guint64 unload_end_counter;
195 guint8 unload_written;
198 #define PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE 1024
199 #define PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE 4096
200 #define PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE 4096
202 typedef struct _ProfilerHeapShotObjectBuffer {
203 struct _ProfilerHeapShotObjectBuffer *next;
204 MonoObject **next_free_slot;
206 MonoObject **first_unprocessed_slot;
207 MonoObject *buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE];
208 } ProfilerHeapShotObjectBuffer;
210 typedef struct _ProfilerHeapShotHeapBuffer {
211 struct _ProfilerHeapShotHeapBuffer *next;
212 struct _ProfilerHeapShotHeapBuffer *previous;
213 MonoObject **start_slot;
214 MonoObject **end_slot;
215 MonoObject *buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE];
216 } ProfilerHeapShotHeapBuffer;
218 typedef struct _ProfilerHeapShotHeapBuffers {
219 ProfilerHeapShotHeapBuffer *buffers;
220 ProfilerHeapShotHeapBuffer *last;
221 ProfilerHeapShotHeapBuffer *current;
222 MonoObject **first_free_slot;
223 } ProfilerHeapShotHeapBuffers;
226 typedef struct _ProfilerHeapShotWriteBuffer {
227 struct _ProfilerHeapShotWriteBuffer *next;
228 gpointer buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE];
229 } ProfilerHeapShotWriteBuffer;
231 typedef struct _ProfilerHeapShotWriteJob {
232 struct _ProfilerHeapShotWriteJob *next;
233 struct _ProfilerHeapShotWriteJob *next_unwritten;
237 ProfilerHeapShotWriteBuffer *buffers;
238 ProfilerHeapShotWriteBuffer **last_next;
239 guint32 full_buffers;
240 gboolean heap_shot_was_signalled;
241 guint64 start_counter;
246 } ProfilerHeapShotWriteJob;
248 typedef struct _ProfilerPerThreadData {
249 ProfilerEventData *events;
250 ProfilerEventData *next_free_event;
251 ProfilerEventData *end_event;
252 ProfilerEventData *first_unwritten_event;
253 ProfilerEventData *first_unmapped_event;
254 guint64 start_event_counter;
255 guint64 last_event_counter;
257 ProfilerHeapShotObjectBuffer *heap_shot_object_buffers;
258 struct _ProfilerPerThreadData* next;
259 } ProfilerPerThreadData;
261 typedef struct _ProfilerStatisticalData {
265 int first_unwritten_index;
266 } ProfilerStatisticalData;
268 typedef struct _ProfilerUnmanagedSymbol {
273 } ProfilerUnmanagedSymbol;
275 struct _ProfilerExecutableFile;
277 typedef struct _ProfilerExecutableMemoryRegionData {
285 struct _ProfilerExecutableFile *file;
286 guint32 symbols_count;
287 guint32 symbols_capacity;
288 ProfilerUnmanagedSymbol *symbols;
289 } ProfilerExecutableMemoryRegionData;
291 typedef struct _ProfilerExecutableMemoryRegions {
292 ProfilerExecutableMemoryRegionData **regions;
293 guint32 regions_capacity;
294 guint32 regions_count;
296 guint32 next_unmanaged_function_id;
297 } ProfilerExecutableMemoryRegions;
299 /* Start of ELF definitions */
301 typedef guint16 ElfHalf;
302 typedef guint32 ElfWord;
303 typedef gsize ElfAddr;
304 typedef gsize ElfOff;
307 unsigned char e_ident[EI_NIDENT];
313 ElfOff e_shoff; // Section header table
315 ElfHalf e_ehsize; // Header size
318 ElfHalf e_shentsize; // Section header entry size
319 ElfHalf e_shnum; // Section header entries number
320 ElfHalf e_shstrndx; // String table index
323 #if (SIZEOF_VOID_P == 4)
328 ElfAddr sh_addr; // Address in memory
329 ElfOff sh_offset; // Offset in file
333 ElfWord sh_addralign;
340 unsigned char st_info; // Use ELF32_ST_TYPE to get symbol type
341 unsigned char st_other;
342 ElfHalf st_shndx; // Or one of SHN_ABS, SHN_COMMON or SHN_UNDEF.
344 #elif (SIZEOF_VOID_P == 8)
349 ElfAddr sh_addr; // Address in memory
350 ElfOff sh_offset; // Offset in file
359 unsigned char st_info; // Use ELF_ST_TYPE to get symbol type
360 unsigned char st_other;
361 ElfHalf st_shndx; // Or one of SHN_ABS, SHN_COMMON or SHN_UNDEF.
366 #error Bad size of void pointer
370 #define ELF_ST_BIND(i) ((i)>>4)
371 #define ELF_ST_TYPE(i) ((i)&0xf)
384 ELF_FILE_TYPE_NONE = 0,
385 ELF_FILE_TYPE_REL = 1,
386 ELF_FILE_TYPE_EXEC = 2,
387 ELF_FILE_TYPE_DYN = 3,
388 ELF_FILE_TYPE_CORE = 4
405 ELF_SHT_PROGBITS = 1,
429 ELF_SHF_EXECINSTR = 4,
432 #define ELF_SHN_UNDEF 0
433 #define ELF_SHN_LORESERVE 0xff00
434 #define ELF_SHN_LOPROC 0xff00
435 #define ELF_SHN_HIPROC 0xff1f
436 #define ELF_SHN_ABS 0xfff1
437 #define ELF_SHN_COMMON 0xfff2
438 #define ELF_SHN_HIRESERVE 0xffff
439 /* End of ELF definitions */
441 typedef struct _ProfilerExecutableFileSectionRegion {
442 ProfilerExecutableMemoryRegionData *region;
443 guint8 *section_address;
444 gsize section_offset;
445 } ProfilerExecutableFileSectionRegion;
447 typedef struct _ProfilerExecutableFile {
448 guint32 reference_count;
450 /* Used for mmap and munmap */
457 guint8 *symbols_start;
458 guint32 symbols_count;
460 const char *symbols_string_table;
461 const char *main_string_table;
463 ProfilerExecutableFileSectionRegion *section_regions;
465 struct _ProfilerExecutableFile *next_new_file;
466 } ProfilerExecutableFile;
468 typedef struct _ProfilerExecutableFiles {
470 ProfilerExecutableFile *new_files;
471 } ProfilerExecutableFiles;
474 #ifndef PLATFORM_WIN32
475 #include <sys/types.h>
476 #include <sys/time.h>
477 #include <sys/stat.h>
481 #include <semaphore.h>
483 #include <sys/mman.h>
484 #include <sys/types.h>
485 #include <sys/stat.h>
489 #define MUTEX_TYPE pthread_mutex_t
490 #define INITIALIZE_PROFILER_MUTEX() pthread_mutex_init (&(profiler->mutex), NULL)
491 #define DELETE_PROFILER_MUTEX() pthread_mutex_destroy (&(profiler->mutex))
492 #define LOCK_PROFILER() do {/*LOG_WRITER_THREAD ("LOCK_PROFILER");*/ pthread_mutex_lock (&(profiler->mutex));} while (0)
493 #define UNLOCK_PROFILER() do {/*LOG_WRITER_THREAD ("UNLOCK_PROFILER");*/ pthread_mutex_unlock (&(profiler->mutex));} while (0)
495 #define THREAD_TYPE pthread_t
496 #define CREATE_WRITER_THREAD(f) pthread_create (&(profiler->data_writer_thread), NULL, ((void*(*)(void*))f), NULL)
497 #define EXIT_THREAD() pthread_exit (NULL);
498 #define WAIT_WRITER_THREAD() pthread_join (profiler->data_writer_thread, NULL)
499 #define CURRENT_THREAD_ID() (gsize) pthread_self ()
501 #ifndef HAVE_KW_THREAD
502 static pthread_key_t pthread_profiler_key;
503 static pthread_once_t profiler_pthread_once = PTHREAD_ONCE_INIT;
505 make_pthread_profiler_key (void) {
506 (void) pthread_key_create (&pthread_profiler_key, NULL);
508 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) pthread_getspecific (pthread_profiler_key))
509 #define SET_PROFILER_THREAD_DATA(x) (void) pthread_setspecific (pthread_profiler_key, (x))
510 #define ALLOCATE_PROFILER_THREAD_DATA() (void) pthread_once (&profiler_pthread_once, make_pthread_profiler_key)
511 #define FREE_PROFILER_THREAD_DATA() (void) pthread_key_delete (pthread_profiler_key)
514 #define EVENT_TYPE sem_t
515 #define WRITER_EVENT_INIT() (void) sem_init (&(profiler->statistical_data_writer_event), 0, 0)
516 #define WRITER_EVENT_DESTROY() (void) sem_destroy (&(profiler->statistical_data_writer_event))
517 #define WRITER_EVENT_WAIT() (void) sem_wait (&(profiler->statistical_data_writer_event))
518 #define WRITER_EVENT_RAISE() (void) sem_post (&(profiler->statistical_data_writer_event))
521 #define FILE_HANDLE_TYPE FILE*
522 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
523 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
524 #define FLUSH_FILE() fflush (profiler->file)
525 #define CLOSE_FILE() fclose (profiler->file);
527 #define FILE_HANDLE_TYPE int
528 #define OPEN_FILE() profiler->file = open (profiler->file_name, O_WRONLY|O_CREAT|O_TRUNC, 0664);
529 #define WRITE_BUFFER(b,s) write (profiler->file, (b), (s))
531 #define CLOSE_FILE() close (profiler->file);
538 #define MUTEX_TYPE CRITICAL_SECTION
539 #define INITIALIZE_PROFILER_MUTEX() InitializeCriticalSection (&(profiler->mutex))
540 #define DELETE_PROFILER_MUTEX() DeleteCriticalSection (&(profiler->mutex))
541 #define LOCK_PROFILER() EnterCriticalSection (&(profiler->mutex))
542 #define UNLOCK_PROFILER() LeaveCriticalSection (&(profiler->mutex))
544 #define THREAD_TYPE HANDLE
545 #define CREATE_WRITER_THREAD(f) CreateThread (NULL, (1*1024*1024), (f), NULL, 0, NULL);
546 #define EXIT_THREAD() ExitThread (0);
547 #define WAIT_WRITER_THREAD() WaitForSingleObject (profiler->data_writer_thread, INFINITE)
548 #define CURRENT_THREAD_ID() (gsize) GetCurrentThreadId ()
550 #ifndef HAVE_KW_THREAD
551 static guint32 profiler_thread_id = -1;
552 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*)TlsGetValue (profiler_thread_id))
553 #define SET_PROFILER_THREAD_DATA(x) TlsSetValue (profiler_thread_id, (x));
554 #define ALLOCATE_PROFILER_THREAD_DATA() profiler_thread_id = TlsAlloc ()
555 #define FREE_PROFILER_THREAD_DATA() TlsFree (profiler_thread_id)
558 #define EVENT_TYPE HANDLE
559 #define WRITER_EVENT_INIT() profiler->statistical_data_writer_event = CreateEvent (NULL, FALSE, FALSE, NULL)
560 #define WRITER_EVENT_DESTROY() CloseHandle (profiler->statistical_data_writer_event)
561 #define WRITER_EVENT_WAIT() WaitForSingleObject (profiler->statistical_data_writer_event, INFINITE)
562 #define WRITER_EVENT_RAISE() SetEvent (profiler->statistical_data_writer_event)
564 #define FILE_HANDLE_TYPE FILE*
565 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
566 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
567 #define FLUSH_FILE() fflush (profiler->file)
568 #define CLOSE_FILE() fclose (profiler->file);
572 #ifdef HAVE_KW_THREAD
573 static __thread ProfilerPerThreadData * tls_profiler_per_thread_data;
574 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) tls_profiler_per_thread_data)
575 #define SET_PROFILER_THREAD_DATA(x) tls_profiler_per_thread_data = (x)
576 #define ALLOCATE_PROFILER_THREAD_DATA() /* nop */
577 #define FREE_PROFILER_THREAD_DATA() /* nop */
580 #define GET_PROFILER_THREAD_DATA(data) do {\
581 ProfilerPerThreadData *_result = LOOKUP_PROFILER_THREAD_DATA ();\
583 _result = profiler_per_thread_data_new (profiler->per_thread_buffer_size);\
585 _result->next = profiler->per_thread_data;\
586 profiler->per_thread_data = _result;\
588 SET_PROFILER_THREAD_DATA (_result);\
593 #define PROFILER_FILE_WRITE_BUFFER_SIZE (profiler->write_buffer_size)
594 typedef struct _ProfilerFileWriteBuffer {
595 struct _ProfilerFileWriteBuffer *next;
597 } ProfilerFileWriteBuffer;
599 struct _MonoProfiler {
602 MonoProfileFlags flags;
604 char *file_name_suffix;
605 FILE_HANDLE_TYPE file;
608 guint64 start_counter;
612 guint64 last_header_counter;
614 MethodIdMapping *methods;
615 ClassIdMapping *classes;
617 GHashTable *loaded_assemblies;
618 GHashTable *loaded_modules;
619 GHashTable *loaded_appdomains;
621 guint32 per_thread_buffer_size;
622 guint32 statistical_buffer_size;
623 ProfilerPerThreadData* per_thread_data;
624 ProfilerStatisticalData *statistical_data;
625 ProfilerStatisticalData *statistical_data_ready;
626 ProfilerStatisticalData *statistical_data_second_buffer;
628 THREAD_TYPE data_writer_thread;
629 EVENT_TYPE statistical_data_writer_event;
630 gboolean terminate_writer_thread;
631 gboolean detach_writer_thread;
633 ProfilerFileWriteBuffer *write_buffers;
634 ProfilerFileWriteBuffer *current_write_buffer;
635 int write_buffer_size;
636 int current_write_position;
637 int full_write_buffers;
639 ProfilerHeapShotWriteJob *heap_shot_write_jobs;
640 ProfilerHeapShotHeapBuffers heap;
642 char *heap_shot_command_file_name;
643 int dump_next_heap_snapshots;
644 guint64 heap_shot_command_file_access_time;
645 gboolean heap_shot_was_signalled;
646 guint32 garbage_collection_counter;
648 ProfilerExecutableMemoryRegions *executable_regions;
649 ProfilerExecutableFiles executable_files;
656 gboolean unreachable_objects;
660 static MonoProfiler *profiler;
662 #ifndef PLATFORM_WIN32
665 #ifdef MONO_ARCH_USE_SIGACTION
666 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, siginfo_t *info, void *context)
667 #elif defined(__sparc__)
668 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, void *sigctx)
670 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy)
674 SIG_HANDLER_SIGNATURE (gc_request_handler) {
675 profiler->heap_shot_was_signalled = TRUE;
676 WRITER_EVENT_RAISE ();
680 add_gc_request_handler (int signal_number)
684 #ifdef MONO_ARCH_USE_SIGACTION
685 sa.sa_sigaction = gc_request_handler;
686 sigemptyset (&sa.sa_mask);
687 sa.sa_flags = SA_SIGINFO;
689 sa.sa_handler = gc_request_handler;
690 sigemptyset (&sa.sa_mask);
694 g_assert (sigaction (signal_number, &sa, NULL) != -1);
700 #define DEBUG_LOAD_EVENTS 0
701 #define DEBUG_MAPPING_EVENTS 0
702 #define DEBUG_LOGGING_PROFILER 0
703 #define DEBUG_HEAP_PROFILER 0
704 #define DEBUG_CLASS_BITMAPS 0
705 #define DEBUG_STATISTICAL_PROFILER 0
706 #define DEBUG_WRITER_THREAD 0
707 #if (DEBUG_LOGGING_PROFILER || DEBUG_STATISTICAL_PROFILER || DEBUG_HEAP_PROFILER || DEBUG_WRITER_THREAD)
708 #define LOG_WRITER_THREAD(m) printf ("WRITER-THREAD-LOG %s\n", m)
710 #define LOG_WRITER_THREAD(m)
713 #if DEBUG_LOGGING_PROFILER
714 static int event_counter = 0;
715 #define EVENT_MARK() printf ("[EVENT:%d]", ++ event_counter)
719 static ClassIdMappingElement*
720 class_id_mapping_element_get (MonoClass *klass) {
721 return g_hash_table_lookup (profiler->classes->table, (gconstpointer) klass);
724 static MethodIdMappingElement*
725 method_id_mapping_element_get (MonoMethod *method) {
726 return g_hash_table_lookup (profiler->methods->table, (gconstpointer) method);
729 #define BITS_TO_BYTES(v) do {\
735 static ClassIdMappingElement*
736 class_id_mapping_element_new (MonoClass *klass) {
737 ClassIdMappingElement *result = g_new (ClassIdMappingElement, 1);
739 result->name = g_strdup_printf ("%s.%s", mono_class_get_namespace (klass), mono_class_get_name (klass));
740 result->klass = klass;
741 result->next_unwritten = profiler->classes->unwritten;
742 profiler->classes->unwritten = result;
743 result->id = profiler->classes->next_id;
744 profiler->classes->next_id ++;
746 result->data.bitmap.compact = 0;
747 result->data.layout.slots = CLASS_LAYOUT_NOT_INITIALIZED;
748 result->data.layout.references = CLASS_LAYOUT_NOT_INITIALIZED;
750 g_hash_table_insert (profiler->classes->table, klass, result);
752 #if (DEBUG_MAPPING_EVENTS)
753 printf ("Created new CLASS mapping element \"%s\" (%p)[%d]\n", result->name, klass, result->id);
759 class_id_mapping_element_build_layout_bitmap (MonoClass *klass, ClassIdMappingElement *klass_id) {
760 MonoClass *parent_class = mono_class_get_parent (klass);
761 int number_of_reference_fields = 0;
762 int max_offset_of_reference_fields = 0;
763 ClassIdMappingElement *parent_id;
765 MonoClassField *field;
767 #if (DEBUG_CLASS_BITMAPS)
768 printf ("class_id_mapping_element_build_layout_bitmap: building layout for class %s.%s: ", mono_class_get_namespace (klass), mono_class_get_name (klass));
771 if (parent_class != NULL) {
772 parent_id = class_id_mapping_element_get (parent_class);
773 g_assert (parent_id != NULL);
775 if (parent_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
776 #if (DEBUG_CLASS_BITMAPS)
777 printf ("[recursively building bitmap for father class]\n");
779 class_id_mapping_element_build_layout_bitmap (parent_class, parent_id);
786 while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
787 MonoType* field_type = mono_field_get_type (field);
788 // For now, skip static fields
789 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
792 if (MONO_TYPE_IS_REFERENCE (field_type)) {
793 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
794 if (field_offset > max_offset_of_reference_fields) {
795 max_offset_of_reference_fields = field_offset;
797 number_of_reference_fields ++;
799 MonoClass *field_class = mono_class_from_mono_type (field_type);
800 if (field_class && mono_class_is_valuetype (field_class)) {
801 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
802 g_assert (field_id != NULL);
804 if (field_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
805 if (field_id != klass_id) {
806 #if (DEBUG_CLASS_BITMAPS)
807 printf ("[recursively building bitmap for field %s]\n", mono_field_get_name (field));
809 class_id_mapping_element_build_layout_bitmap (field_class, field_id);
811 #if (DEBUG_CLASS_BITMAPS)
812 printf ("[breaking recursive bitmap build for field %s]", mono_field_get_name (field));
815 klass_id->data.bitmap.compact = 0;
816 klass_id->data.layout.slots = 0;
817 klass_id->data.layout.references = 0;
821 if (field_id->data.layout.references > 0) {
822 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
823 int max_offset_reference_in_field = (field_id->data.layout.slots - 1) * sizeof (gpointer);
825 if ((field_offset + max_offset_reference_in_field) > max_offset_of_reference_fields) {
826 max_offset_of_reference_fields = field_offset + max_offset_reference_in_field;
829 number_of_reference_fields += field_id->data.layout.references;
835 #if (DEBUG_CLASS_BITMAPS)
836 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);
838 if ((number_of_reference_fields == 0) && ((parent_id == NULL) || (parent_id->data.layout.references == 0))) {
839 #if (DEBUG_CLASS_BITMAPS)
840 printf ("[no references at all]");
842 klass_id->data.bitmap.compact = 0;
843 klass_id->data.layout.slots = 0;
844 klass_id->data.layout.references = 0;
846 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
847 #if (DEBUG_CLASS_BITMAPS)
848 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);
850 klass_id->data.layout.slots = parent_id->data.layout.slots;
851 klass_id->data.layout.references = parent_id->data.layout.references;
853 #if (DEBUG_CLASS_BITMAPS)
854 printf ("[no references from parent]");
856 klass_id->data.layout.slots = 0;
857 klass_id->data.layout.references = 0;
860 if (number_of_reference_fields > 0) {
861 klass_id->data.layout.slots += ((max_offset_of_reference_fields / sizeof (gpointer)) + 1);
862 klass_id->data.layout.references += number_of_reference_fields;
863 #if (DEBUG_CLASS_BITMAPS)
864 printf ("[adding data, going to %d references in %d slots]", klass_id->data.layout.references, klass_id->data.layout.slots);
868 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
869 #if (DEBUG_CLASS_BITMAPS)
870 printf ("[zeroing bitmap]");
872 klass_id->data.bitmap.compact = 0;
873 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
874 #if (DEBUG_CLASS_BITMAPS)
875 printf ("[copying compact father bitmap]");
877 klass_id->data.bitmap.compact = parent_id->data.bitmap.compact;
880 int size_of_bitmap = klass_id->data.layout.slots;
881 BITS_TO_BYTES (size_of_bitmap);
882 #if (DEBUG_CLASS_BITMAPS)
883 printf ("[allocating %d bytes for bitmap]", size_of_bitmap);
885 klass_id->data.bitmap.extended = g_malloc0 (size_of_bitmap);
886 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
887 int size_of_father_bitmap = parent_id->data.layout.slots;
888 if (size_of_father_bitmap <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
890 #if (DEBUG_CLASS_BITMAPS)
891 printf ("[copying %d bits from father bitmap]", size_of_father_bitmap);
893 for (father_slot = 0; father_slot < size_of_father_bitmap; father_slot ++) {
894 if (parent_id->data.bitmap.compact & (((guint64)1) << father_slot)) {
895 klass_id->data.bitmap.extended [father_slot >> 3] |= (1 << (father_slot & 7));
899 BITS_TO_BYTES (size_of_father_bitmap);
900 #if (DEBUG_CLASS_BITMAPS)
901 printf ("[copying %d bytes from father bitmap]", size_of_father_bitmap);
903 memcpy (klass_id->data.bitmap.extended, parent_id->data.bitmap.extended, size_of_father_bitmap);
909 #if (DEBUG_CLASS_BITMAPS)
910 printf ("[starting filling iteration]\n");
913 while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
914 MonoType* field_type = mono_field_get_type (field);
915 // For now, skip static fields
916 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
919 #if (DEBUG_CLASS_BITMAPS)
920 printf ("[Working on field %s]", mono_field_get_name (field));
922 if (MONO_TYPE_IS_REFERENCE (field_type)) {
923 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
925 g_assert ((field_offset % sizeof (gpointer)) == 0);
926 field_slot = field_offset / sizeof (gpointer);
927 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
928 klass_id->data.bitmap.compact |= (((guint64)1) << field_slot);
930 klass_id->data.bitmap.extended [field_slot >> 3] |= (1 << (field_slot & 7));
932 #if (DEBUG_CLASS_BITMAPS)
933 printf ("[reference at offset %d, slot %d]", field_offset, field_slot);
936 MonoClass *field_class = mono_class_from_mono_type (field_type);
937 if (field_class && mono_class_is_valuetype (field_class)) {
938 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
942 g_assert (field_id != NULL);
943 field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
944 g_assert ((field_id->data.layout.references == 0) || ((field_offset % sizeof (gpointer)) == 0));
945 field_slot = field_offset / sizeof (gpointer);
946 #if (DEBUG_CLASS_BITMAPS)
947 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);
950 if (field_id->data.layout.references > 0) {
952 if (field_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
953 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
954 if (field_id->data.bitmap.compact & (((guint64)1) << sub_field_slot)) {
955 int actual_slot = field_slot + sub_field_slot;
956 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
957 klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
959 klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
964 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
965 if (field_id->data.bitmap.extended [sub_field_slot >> 3] & (1 << (sub_field_slot & 7))) {
966 int actual_slot = field_slot + sub_field_slot;
967 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
968 klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
970 klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
979 #if (DEBUG_CLASS_BITMAPS)
982 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);
983 for (slot = 0; slot < klass_id->data.layout.slots; slot ++) {
984 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
985 if (klass_id->data.bitmap.compact & (((guint64)1) << slot)) {
991 if (klass_id->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
1005 static MethodIdMappingElement*
1006 method_id_mapping_element_new (MonoMethod *method) {
1007 MethodIdMappingElement *result = g_new (MethodIdMappingElement, 1);
1008 char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
1010 result->name = g_strdup_printf ("%s (%s)", mono_method_get_name (method), signature);
1012 result->method = method;
1013 result->next_unwritten = profiler->methods->unwritten;
1014 profiler->methods->unwritten = result;
1015 result->id = profiler->methods->next_id;
1016 profiler->methods->next_id ++;
1017 g_hash_table_insert (profiler->methods->table, method, result);
1019 result->data.code_start = NULL;
1020 result->data.code_size = 0;
1022 #if (DEBUG_MAPPING_EVENTS)
1023 printf ("Created new METHOD mapping element \"%s\" (%p)[%d]\n", result->name, method, result->id);
1030 method_id_mapping_element_destroy (gpointer element) {
1031 MethodIdMappingElement *e = (MethodIdMappingElement*) element;
1038 class_id_mapping_element_destroy (gpointer element) {
1039 ClassIdMappingElement *e = (ClassIdMappingElement*) element;
1042 if ((e->data.layout.slots != CLASS_LAYOUT_NOT_INITIALIZED) && (e->data.layout.slots > CLASS_LAYOUT_PACKED_BITMAP_SIZE))
1043 g_free (e->data.bitmap.extended);
1047 static MethodIdMapping*
1048 method_id_mapping_new (void) {
1049 MethodIdMapping *result = g_new (MethodIdMapping, 1);
1050 //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, method_id_mapping_element_destroy);
1051 result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, method_id_mapping_element_destroy);
1052 result->unwritten = NULL;
1053 result->next_id = 1;
1057 static ClassIdMapping*
1058 class_id_mapping_new (void) {
1059 ClassIdMapping *result = g_new (ClassIdMapping, 1);
1060 //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, class_id_mapping_element_destroy);
1061 result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, class_id_mapping_element_destroy);
1062 result->unwritten = NULL;
1063 result->next_id = 1;
1068 method_id_mapping_destroy (MethodIdMapping *map) {
1069 g_hash_table_destroy (map->table);
1074 class_id_mapping_destroy (ClassIdMapping *map) {
1075 g_hash_table_destroy (map->table);
1079 #if (DEBUG_LOAD_EVENTS)
1081 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element);
1084 static LoadedElement*
1085 loaded_element_load_start (GHashTable *table, gpointer item) {
1086 LoadedElement *element = g_new0 (LoadedElement, 1);
1087 #if (DEBUG_LOAD_EVENTS)
1088 print_load_event ("LOAD START", table, item, element);
1090 MONO_PROFILER_GET_CURRENT_COUNTER (element->load_start_counter);
1091 g_hash_table_insert (table, item, element);
1095 static LoadedElement*
1096 loaded_element_load_end (GHashTable *table, gpointer item, char *name) {
1097 LoadedElement *element = g_hash_table_lookup (table, item);
1098 #if (DEBUG_LOAD_EVENTS)
1099 print_load_event ("LOAD END", table, item, element);
1101 g_assert (element != NULL);
1102 MONO_PROFILER_GET_CURRENT_COUNTER (element->load_end_counter);
1103 element->name = name;
1104 element->loaded = TRUE;
1108 static LoadedElement*
1109 loaded_element_unload_start (GHashTable *table, gpointer item) {
1110 LoadedElement *element = g_hash_table_lookup (table, item);
1111 #if (DEBUG_LOAD_EVENTS)
1112 print_load_event ("UNLOAD START", table, item, element);
1114 g_assert (element != NULL);
1115 MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_start_counter);
1119 static LoadedElement*
1120 loaded_element_unload_end (GHashTable *table, gpointer item) {
1121 LoadedElement *element = g_hash_table_lookup (table, item);
1122 #if (DEBUG_LOAD_EVENTS)
1123 print_load_event ("UNLOAD END", table, item, element);
1125 g_assert (element != NULL);
1126 MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_end_counter);
1127 element->unloaded = TRUE;
1133 loaded_element_destroy (gpointer element) {
1134 if (((LoadedElement*)element)->name)
1135 g_free (((LoadedElement*)element)->name);
1139 #if (DEBUG_LOAD_EVENTS)
1141 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element) {
1142 const char* item_name;
1145 if (table == profiler->loaded_assemblies) {
1146 //item_info = g_strdup_printf("ASSEMBLY %p (dynamic %d)", item, mono_image_is_dynamic (mono_assembly_get_image((MonoAssembly*)item)));
1147 item_info = g_strdup_printf("ASSEMBLY %p", item);
1148 } else if (table == profiler->loaded_modules) {
1149 //item_info = g_strdup_printf("MODULE %p (dynamic %d)", item, mono_image_is_dynamic ((MonoImage*)item));
1150 item_info = g_strdup_printf("MODULE %p", item);
1151 } else if (table == profiler->loaded_appdomains) {
1152 item_info = g_strdup_printf("APPDOMAIN %p (id %d)", item, mono_domain_get_id ((MonoDomain*)item));
1155 g_assert_not_reached ();
1158 if (element != NULL) {
1159 item_name = element->name;
1161 item_name = "<NULL>";
1164 printf ("%s EVENT for %s (%s)\n", event_name, item_info, item_name);
1170 profiler_heap_shot_object_buffers_destroy (ProfilerHeapShotObjectBuffer *buffer) {
1171 while (buffer != NULL) {
1172 ProfilerHeapShotObjectBuffer *next = buffer->next;
1173 #if DEBUG_HEAP_PROFILER
1174 printf ("profiler_heap_shot_object_buffers_destroy: destroyed buffer %p (%p-%p)\n", buffer, & (buffer->buffer [0]), buffer->end);
1181 static ProfilerHeapShotObjectBuffer*
1182 profiler_heap_shot_object_buffer_new (ProfilerPerThreadData *data) {
1183 ProfilerHeapShotObjectBuffer *buffer;
1184 ProfilerHeapShotObjectBuffer *result = g_new (ProfilerHeapShotObjectBuffer, 1);
1185 result->next_free_slot = & (result->buffer [0]);
1186 result->end = & (result->buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE]);
1187 result->first_unprocessed_slot = & (result->buffer [0]);
1188 result->next = data->heap_shot_object_buffers;
1189 data->heap_shot_object_buffers = result;
1190 #if DEBUG_HEAP_PROFILER
1191 printf ("profiler_heap_shot_object_buffer_new: created buffer %p (%p-%p)\n", result, result->next_free_slot, result->end);
1193 for (buffer = result; buffer != NULL; buffer = buffer->next) {
1194 ProfilerHeapShotObjectBuffer *last = buffer->next;
1195 if ((last != NULL) && (last->first_unprocessed_slot == last->end)) {
1196 buffer->next = NULL;
1197 profiler_heap_shot_object_buffers_destroy (last);
1204 static ProfilerHeapShotWriteJob*
1205 profiler_heap_shot_write_job_new (gboolean heap_shot_was_signalled, guint32 collection) {
1206 ProfilerHeapShotWriteJob *job = g_new (ProfilerHeapShotWriteJob, 1);
1208 job->next_unwritten = NULL;
1209 job->buffers = g_new (ProfilerHeapShotWriteBuffer, 1);
1210 job->buffers->next = NULL;
1211 job->last_next = & (job->buffers->next);
1212 job->start = & (job->buffers->buffer [0]);
1213 job->cursor = job->start;
1214 job->end = & (job->buffers->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1215 job->full_buffers = 0;
1216 job->heap_shot_was_signalled = heap_shot_was_signalled;
1217 job->collection = collection;
1218 #if DEBUG_HEAP_PROFILER
1219 printf ("profiler_heap_shot_write_job_new: created job %p with buffer %p(%p-%p)\n", job, job->buffers, job->start, job->end);
1225 profiler_heap_shot_write_job_add_buffer (ProfilerHeapShotWriteJob *job, gpointer value) {
1226 ProfilerHeapShotWriteBuffer *buffer = g_new (ProfilerHeapShotWriteBuffer, 1);
1227 buffer->next = NULL;
1228 *(job->last_next) = buffer;
1229 job->last_next = & (buffer->next);
1230 job->full_buffers ++;
1231 buffer->buffer [0] = value;
1232 job->start = & (buffer->buffer [0]);
1233 job->cursor = & (buffer->buffer [1]);
1234 job->end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1235 #if DEBUG_HEAP_PROFILER
1236 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);
1238 ProfilerHeapShotWriteBuffer *current_buffer;
1239 for (current_buffer = job->buffers; current_buffer != NULL; current_buffer = current_buffer->next) {
1240 printf ("profiler_heap_shot_write_job_add_buffer: now job %p has buffer %p\n", job, current_buffer);
1247 profiler_heap_shot_write_job_free_buffers (ProfilerHeapShotWriteJob *job) {
1248 ProfilerHeapShotWriteBuffer *buffer = job->buffers;
1250 while (buffer != NULL) {
1251 ProfilerHeapShotWriteBuffer *next = buffer->next;
1252 #if DEBUG_HEAP_PROFILER
1253 printf ("profiler_heap_shot_write_job_free_buffers: in job %p, freeing buffer %p\n", job, buffer);
1259 job->buffers = NULL;
1263 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job);
1266 profiler_process_heap_shot_write_jobs (void) {
1267 gboolean done = FALSE;
1270 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1271 ProfilerHeapShotWriteJob *previous_job = NULL;
1272 ProfilerHeapShotWriteJob *next_job;
1275 while (current_job != NULL) {
1276 next_job = current_job->next_unwritten;
1278 if (next_job != NULL) {
1279 if (current_job->buffers != NULL) {
1282 if (next_job->buffers == NULL) {
1283 current_job->next_unwritten = NULL;
1287 if (current_job->buffers != NULL) {
1288 LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: writing...");
1289 profiler_heap_shot_write_block (current_job);
1290 LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: done");
1291 if (previous_job != NULL) {
1292 previous_job->next_unwritten = NULL;
1297 previous_job = current_job;
1298 current_job = next_job;
1304 profiler_free_heap_shot_write_jobs (void) {
1305 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1306 ProfilerHeapShotWriteJob *next_job;
1308 if (current_job != NULL) {
1309 while (current_job->next_unwritten != NULL) {
1310 #if DEBUG_HEAP_PROFILER
1311 printf ("profiler_free_heap_shot_write_jobs: job %p must not be freed\n", current_job);
1313 current_job = current_job->next_unwritten;
1316 next_job = current_job->next;
1317 current_job->next = NULL;
1318 current_job = next_job;
1320 while (current_job != NULL) {
1321 #if DEBUG_HEAP_PROFILER
1322 printf ("profiler_free_heap_shot_write_jobs: job %p will be freed\n", current_job);
1324 next_job = current_job->next;
1325 g_free (current_job);
1326 current_job = next_job;
1332 profiler_destroy_heap_shot_write_jobs (void) {
1333 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1334 ProfilerHeapShotWriteJob *next_job;
1336 while (current_job != NULL) {
1337 next_job = current_job->next;
1338 profiler_heap_shot_write_job_free_buffers (current_job);
1339 g_free (current_job);
1340 current_job = next_job;
1345 profiler_add_heap_shot_write_job (ProfilerHeapShotWriteJob *job) {
1346 job->next = profiler->heap_shot_write_jobs;
1347 job->next_unwritten = job->next;
1348 profiler->heap_shot_write_jobs = job;
1349 #if DEBUG_HEAP_PROFILER
1350 printf ("profiler_add_heap_shot_write_job: added job %p\n", job);
1354 #if DEBUG_HEAP_PROFILER
1355 #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)
1356 #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)
1358 #define STORE_ALLOCATED_OBJECT_MESSAGE1(d,o)
1359 #define STORE_ALLOCATED_OBJECT_MESSAGE2(d,o)
1361 #define STORE_ALLOCATED_OBJECT(d,o) do {\
1362 if ((d)->heap_shot_object_buffers->next_free_slot < (d)->heap_shot_object_buffers->end) {\
1363 STORE_ALLOCATED_OBJECT_MESSAGE1 ((d), (o));\
1364 *((d)->heap_shot_object_buffers->next_free_slot) = (o);\
1365 (d)->heap_shot_object_buffers->next_free_slot ++;\
1367 ProfilerHeapShotObjectBuffer *buffer = profiler_heap_shot_object_buffer_new (d);\
1368 STORE_ALLOCATED_OBJECT_MESSAGE2 ((d), (o));\
1369 *((buffer)->next_free_slot) = (o);\
1370 (buffer)->next_free_slot ++;\
1374 static ProfilerPerThreadData*
1375 profiler_per_thread_data_new (guint32 buffer_size)
1377 ProfilerPerThreadData *data = g_new (ProfilerPerThreadData, 1);
1379 data->events = g_new0 (ProfilerEventData, buffer_size);
1380 data->next_free_event = data->events;
1381 data->end_event = data->events + (buffer_size - 1);
1382 data->first_unwritten_event = data->events;
1383 data->first_unmapped_event = data->events;
1384 MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
1385 data->last_event_counter = data->start_event_counter;
1386 data->thread_id = CURRENT_THREAD_ID ();
1387 data->heap_shot_object_buffers = NULL;
1388 if ((profiler->action_flags.unreachable_objects == TRUE) || (profiler->action_flags.heap_shot == TRUE)) {
1389 profiler_heap_shot_object_buffer_new (data);
1395 profiler_per_thread_data_destroy (ProfilerPerThreadData *data) {
1396 g_free (data->events);
1397 profiler_heap_shot_object_buffers_destroy (data->heap_shot_object_buffers);
1401 static ProfilerStatisticalData*
1402 profiler_statistical_data_new (guint32 buffer_size)
1404 ProfilerStatisticalData *data = g_new (ProfilerStatisticalData, 1);
1406 data->addresses = g_new0 (gpointer, buffer_size);
1407 data->next_free_index = 0;
1408 data->end_index = buffer_size;
1409 data->first_unwritten_index = 0;
1415 profiler_statistical_data_destroy (ProfilerStatisticalData *data) {
1416 g_free (data->addresses);
1421 profiler_add_write_buffer (void) {
1422 if (profiler->current_write_buffer->next == NULL) {
1423 profiler->current_write_buffer->next = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
1424 profiler->current_write_buffer->next->next = NULL;
1426 //printf ("Added next buffer %p, to buffer %p\n", profiler->current_write_buffer->next, profiler->current_write_buffer);
1429 profiler->current_write_buffer = profiler->current_write_buffer->next;
1430 profiler->current_write_position = 0;
1431 profiler->full_write_buffers ++;
1435 profiler_free_write_buffers (void) {
1436 ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1437 while (current_buffer != NULL) {
1438 ProfilerFileWriteBuffer *next_buffer = current_buffer->next;
1440 //printf ("Freeing write buffer %p, next is %p\n", current_buffer, next_buffer);
1442 g_free (current_buffer);
1443 current_buffer = next_buffer;
1447 #define WRITE_BYTE(b) do {\
1448 if (profiler->current_write_position >= PROFILER_FILE_WRITE_BUFFER_SIZE) {\
1449 profiler_add_write_buffer ();\
1451 profiler->current_write_buffer->buffer [profiler->current_write_position] = (b);\
1452 profiler->current_write_position ++;\
1457 write_current_block (guint16 code) {
1458 guint32 size = (profiler->full_write_buffers * PROFILER_FILE_WRITE_BUFFER_SIZE) + profiler->current_write_position;
1459 ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1460 guint64 current_counter;
1461 guint32 counter_delta;
1464 MONO_PROFILER_GET_CURRENT_COUNTER (current_counter);
1465 if (profiler->last_header_counter != 0) {
1466 counter_delta = current_counter - profiler->last_header_counter;
1470 profiler->last_header_counter = current_counter;
1472 header [0] = code & 0xff;
1473 header [1] = (code >> 8) & 0xff;
1474 header [2] = size & 0xff;
1475 header [3] = (size >> 8) & 0xff;
1476 header [4] = (size >> 16) & 0xff;
1477 header [5] = (size >> 24) & 0xff;
1478 header [6] = counter_delta & 0xff;
1479 header [7] = (counter_delta >> 8) & 0xff;
1480 header [8] = (counter_delta >> 16) & 0xff;
1481 header [9] = (counter_delta >> 24) & 0xff;
1483 WRITE_BUFFER (& (header [0]), 10);
1485 while ((current_buffer != NULL) && (profiler->full_write_buffers > 0)) {
1486 WRITE_BUFFER (& (current_buffer->buffer [0]), PROFILER_FILE_WRITE_BUFFER_SIZE);
1487 profiler->full_write_buffers --;
1488 current_buffer = current_buffer->next;
1490 if (profiler->current_write_position > 0) {
1491 WRITE_BUFFER (& (current_buffer->buffer [0]), profiler->current_write_position);
1495 profiler->current_write_buffer = profiler->write_buffers;
1496 profiler->current_write_position = 0;
1497 profiler->full_write_buffers = 0;
1501 #define SEVEN_BITS_MASK (0x7f)
1502 #define EIGHT_BIT_MASK (0x80)
1505 write_uint32 (guint32 value) {
1506 while (value > SEVEN_BITS_MASK) {
1507 WRITE_BYTE (value & SEVEN_BITS_MASK);
1510 WRITE_BYTE (value | EIGHT_BIT_MASK);
1513 write_uint64 (guint64 value) {
1514 while (value > SEVEN_BITS_MASK) {
1515 WRITE_BYTE (value & SEVEN_BITS_MASK);
1518 WRITE_BYTE (value | EIGHT_BIT_MASK);
1521 write_string (const char *string) {
1522 while (*string != 0) {
1523 WRITE_BYTE (*string);
1529 #if DEBUG_HEAP_PROFILER
1530 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c) printf ("WRITE_HEAP_SHOT_JOB_VALUE: writing value %p at cursor %p\n", (v), (c))
1532 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c)
1534 #define WRITE_HEAP_SHOT_JOB_VALUE(j,v) do {\
1535 if ((j)->cursor < (j)->end) {\
1536 WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE ((v), ((j)->cursor));\
1537 *((j)->cursor) = (v);\
1540 profiler_heap_shot_write_job_add_buffer (j, v);\
1544 #undef GUINT_TO_POINTER
1545 #define GUINT_TO_POINTER(u) ((void*)(guint64)(u))
1546 #undef GPOINTER_TO_UINT
1547 #define GPOINTER_TO_UINT(p) ((guint64)(void*)(p))
1549 #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)))
1551 #if DEBUG_HEAP_PROFILER
1552 #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)
1554 #define UPDATE_JOB_BUFFER_CURSOR_MESSAGE()
1556 #define UPDATE_JOB_BUFFER_CURSOR() do {\
1558 if (cursor >= end) {\
1559 buffer = buffer->next;\
1560 if (buffer != NULL) {\
1561 cursor = & (buffer->buffer [0]);\
1562 if (buffer->next != NULL) {\
1563 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);\
1571 UPDATE_JOB_BUFFER_CURSOR_MESSAGE ();\
1575 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job) {
1576 ProfilerHeapShotWriteBuffer *buffer;
1579 guint64 start_counter;
1581 guint64 end_counter;
1584 write_uint64 (job->start_counter);
1585 write_uint64 (job->start_time);
1586 write_uint64 (job->end_counter);
1587 write_uint64 (job->end_time);
1588 write_uint32 (job->collection);
1589 MONO_PROFILER_GET_CURRENT_COUNTER (start_counter);
1590 MONO_PROFILER_GET_CURRENT_TIME (start_time);
1591 write_uint64 (start_counter);
1592 write_uint64 (start_time);
1593 #if DEBUG_HEAP_PROFILER
1594 printf ("profiler_heap_shot_write_block: working on job %p...\n", job);
1596 buffer = job->buffers;
1597 cursor = & (buffer->buffer [0]);
1598 if (buffer->next != NULL) {
1599 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1603 if (cursor >= end) {
1606 #if DEBUG_HEAP_PROFILER
1607 printf ("profiler_heap_shot_write_block: in job %p, starting at buffer %p and cursor %p\n", job, buffer, cursor);
1609 while (cursor != NULL) {
1610 gpointer value = *cursor;
1611 HeapProfilerJobValueCode code = GPOINTER_TO_UINT (value) & HEAP_CODE_MASK;
1612 #if DEBUG_HEAP_PROFILER
1613 printf ("profiler_heap_shot_write_block: got value %p and code %d\n", value, code);
1616 UPDATE_JOB_BUFFER_CURSOR ();
1617 if (code == HEAP_CODE_FREE_OBJECT_CLASS) {
1618 MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
1619 //MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) % 4);
1620 ClassIdMappingElement *class_id;
1623 class_id = class_id_mapping_element_get (klass);
1624 if (class_id == NULL) {
1625 printf ("profiler_heap_shot_write_block: unknown class %p", klass);
1627 g_assert (class_id != NULL);
1628 write_uint32 ((class_id->id << 2) | HEAP_CODE_FREE_OBJECT_CLASS);
1630 size = GPOINTER_TO_UINT (*cursor);
1631 UPDATE_JOB_BUFFER_CURSOR ();
1632 write_uint32 (size);
1633 #if DEBUG_HEAP_PROFILER
1634 printf ("profiler_heap_shot_write_block: wrote unreachable object of class %p (id %d, size %d)\n", klass, class_id->id, size);
1636 } else if (code == HEAP_CODE_OBJECT) {
1637 MonoObject *object = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
1638 MonoClass *klass = mono_object_get_class (object);
1639 ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
1640 guint32 size = mono_object_get_size (object);
1641 guint32 references = GPOINTER_TO_UINT (*cursor);
1642 UPDATE_JOB_BUFFER_CURSOR ();
1644 if (class_id == NULL) {
1645 printf ("profiler_heap_shot_write_block: unknown class %p", klass);
1647 g_assert (class_id != NULL);
1649 write_uint64 (GPOINTER_TO_UINT (value));
1650 write_uint32 (class_id->id);
1651 write_uint32 (size);
1652 write_uint32 (references);
1653 #if DEBUG_HEAP_PROFILER
1654 printf ("profiler_heap_shot_write_block: writing object %p (references %d)\n", value, references);
1657 while (references > 0) {
1658 gpointer reference = *cursor;
1659 write_uint64 (GPOINTER_TO_UINT (reference));
1660 UPDATE_JOB_BUFFER_CURSOR ();
1662 #if DEBUG_HEAP_PROFILER
1663 printf ("profiler_heap_shot_write_block: inside object %p, wrote reference %p)\n", value, reference);
1667 #if DEBUG_HEAP_PROFILER
1668 printf ("profiler_heap_shot_write_block: unknown code %d in value %p\n", code, value);
1670 g_assert_not_reached ();
1675 MONO_PROFILER_GET_CURRENT_COUNTER (end_counter);
1676 MONO_PROFILER_GET_CURRENT_TIME (end_time);
1677 write_uint64 (end_counter);
1678 write_uint64 (end_time);
1680 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_HEAP);
1682 profiler_heap_shot_write_job_free_buffers (job);
1683 #if DEBUG_HEAP_PROFILER
1684 printf ("profiler_heap_shot_write_block: work on job %p done.\n", job);
1689 write_element_load_block (LoadedElement *element, guint8 kind, gsize thread_id) {
1691 write_uint64 (element->load_start_counter);
1692 write_uint64 (element->load_end_counter);
1693 write_uint64 (thread_id);
1694 write_string (element->name);
1695 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_LOADED);
1696 element->load_written = TRUE;
1700 write_element_unload_block (LoadedElement *element, guint8 kind, gsize thread_id) {
1702 write_uint64 (element->unload_start_counter);
1703 write_uint64 (element->unload_end_counter);
1704 write_uint64 (thread_id);
1705 write_string (element->name);
1706 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED);
1707 element->unload_written = TRUE;
1711 write_clock_data (void) {
1715 MONO_PROFILER_GET_CURRENT_COUNTER (counter);
1716 MONO_PROFILER_GET_CURRENT_TIME (time);
1718 write_uint64 (counter);
1719 write_uint64 (time);
1723 write_mapping_block (gsize thread_id) {
1724 ClassIdMappingElement *current_class;
1725 MethodIdMappingElement *current_method;
1727 if ((profiler->classes->unwritten == NULL) && (profiler->methods->unwritten == NULL))
1730 #if (DEBUG_MAPPING_EVENTS)
1731 printf ("[write_mapping_block][TID %ld] START\n", thread_id);
1734 write_clock_data ();
1735 write_uint64 (thread_id);
1737 for (current_class = profiler->classes->unwritten; current_class != NULL; current_class = current_class->next_unwritten) {
1738 write_uint32 (current_class->id);
1739 write_string (current_class->name);
1740 #if (DEBUG_MAPPING_EVENTS)
1741 printf ("mapping CLASS (%d => %s)\n", current_class->id, current_class->name);
1743 g_free (current_class->name);
1744 current_class->name = NULL;
1747 profiler->classes->unwritten = NULL;
1749 for (current_method = profiler->methods->unwritten; current_method != NULL; current_method = current_method->next_unwritten) {
1750 MonoMethod *method = current_method->method;
1751 MonoClass *klass = mono_method_get_class (method);
1752 ClassIdMappingElement *class_element = class_id_mapping_element_get (klass);
1753 g_assert (class_element != NULL);
1754 write_uint32 (current_method->id);
1755 write_uint32 (class_element->id);
1756 write_string (current_method->name);
1757 #if (DEBUG_MAPPING_EVENTS)
1758 printf ("mapping METHOD ([%d]%d => %s)\n", class_element?class_element->id:1, current_method->id, current_method->name);
1760 g_free (current_method->name);
1761 current_method->name = NULL;
1764 profiler->methods->unwritten = NULL;
1766 write_clock_data ();
1767 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_MAPPING);
1769 #if (DEBUG_MAPPING_EVENTS)
1770 printf ("[write_mapping_block][TID %ld] END\n", thread_id);
1775 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER = 1,
1776 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_IMPLICIT = 2,
1777 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT = 3,
1778 MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION = 4,
1779 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT = 5,
1780 MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT = 6,
1781 MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT = 7
1782 } MonoProfilerPackedEventCode;
1783 #define MONO_PROFILER_PACKED_EVENT_CODE_BITS 3
1784 #define MONO_PROFILER_PACKED_EVENT_DATA_BITS (8-MONO_PROFILER_PACKED_EVENT_CODE_BITS)
1785 #define MONO_PROFILER_PACKED_EVENT_DATA_MASK ((1<<MONO_PROFILER_PACKED_EVENT_DATA_BITS)-1)
1787 #define MONO_PROFILER_EVENT_MAKE_PACKED_CODE(result,data,base) do {\
1788 result = ((base)|((data & MONO_PROFILER_PACKED_EVENT_DATA_MASK) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
1789 data >>= MONO_PROFILER_PACKED_EVENT_DATA_BITS;\
1791 #define MONO_PROFILER_EVENT_MAKE_FULL_CODE(result,code,kind,base) do {\
1792 result = ((base)|((((kind)<<4) | (code)) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
1795 static ProfilerEventData*
1796 write_event (ProfilerEventData *event) {
1797 ProfilerEventData *next = event + 1;
1798 gboolean write_event_value = TRUE;
1801 guint64 event_value;
1803 event_value = event->value;
1804 if (event_value == MAX_EVENT_VALUE) {
1805 event_value = *((guint64*)next);
1809 if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
1810 MethodIdMappingElement *element = method_id_mapping_element_get (event->data.address);
1811 g_assert (element != NULL);
1812 event_data = element->id;
1814 if (event->code == MONO_PROFILER_EVENT_METHOD_CALL) {
1815 if (event->kind == MONO_PROFILER_EVENT_KIND_START) {
1816 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER);
1818 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT);
1821 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT);
1823 } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
1824 ClassIdMappingElement *element = class_id_mapping_element_get (event->data.address);
1825 g_assert (element != NULL);
1826 event_data = element->id;
1828 if (event->code == MONO_PROFILER_EVENT_CLASS_ALLOCATION) {
1829 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION);
1831 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT);
1834 event_data = event->data.number;
1835 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
1838 #if (DEBUG_LOGGING_PROFILER)
1840 printf ("writing EVENT[%p] data_type:%d, kind:%d, code:%d (%d:%ld:%ld)\n", event,
1841 event->data_type, event->kind, event->code,
1842 event_code, event_data, event_value);
1845 WRITE_BYTE (event_code);
1846 write_uint64 (event_data);
1847 if (write_event_value) {
1848 write_uint64 (event_value);
1855 write_thread_data_block (ProfilerPerThreadData *data) {
1856 ProfilerEventData *start = data->first_unwritten_event;
1857 ProfilerEventData *end = data->first_unmapped_event;
1862 write_clock_data ();
1863 write_uint64 (data->thread_id);
1865 write_uint64 (data->start_event_counter);
1867 while (start < end) {
1868 start = write_event (start);
1871 data->first_unwritten_event = end;
1873 write_clock_data ();
1874 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_EVENTS);
1877 static ProfilerExecutableMemoryRegionData*
1878 profiler_executable_memory_region_new (gpointer *start, gpointer *end, guint32 file_offset, char *file_name, guint32 id) {
1879 ProfilerExecutableMemoryRegionData *result = g_new (ProfilerExecutableMemoryRegionData, 1);
1880 result->start = start;
1882 result->file_offset = file_offset;
1883 result->file_name = g_strdup (file_name);
1885 result->is_new = TRUE;
1887 result->file = NULL;
1888 result->symbols_capacity = id;
1889 result->symbols_count = id;
1890 result->symbols = NULL;
1896 executable_file_close (ProfilerExecutableMemoryRegionData *region);
1899 profiler_executable_memory_region_destroy (ProfilerExecutableMemoryRegionData *data) {
1900 if (data->file_name != NULL) {
1901 g_free (data->file_name);
1903 if (data->symbols != NULL) {
1904 g_free (data->symbols);
1906 if (data->file != NULL) {
1907 executable_file_close (data);
1912 static ProfilerExecutableMemoryRegions*
1913 profiler_executable_memory_regions_new (int next_id, int next_unmanaged_function_id) {
1914 ProfilerExecutableMemoryRegions *result = g_new (ProfilerExecutableMemoryRegions, 1);
1915 result->regions = g_new0 (ProfilerExecutableMemoryRegionData*, 32);
1916 result->regions_capacity = 32;
1917 result->regions_count = 0;
1918 result->next_id = next_id;
1919 result->next_unmanaged_function_id = next_unmanaged_function_id;
1924 profiler_executable_memory_regions_destroy (ProfilerExecutableMemoryRegions *regions) {
1927 for (i = 0; i < regions->regions_count; i++) {
1928 profiler_executable_memory_region_destroy (regions->regions [i]);
1930 g_free (regions->regions);
1934 static ProfilerExecutableMemoryRegionData*
1935 find_address_region (ProfilerExecutableMemoryRegions *regions, gpointer address) {
1937 int high_index = regions->regions_count;
1938 int middle_index = 0;
1939 ProfilerExecutableMemoryRegionData *middle_region = regions->regions [0];
1941 if ((regions->regions_count == 0) || (regions->regions [low_index]->start > address) || (regions->regions [high_index - 1]->end < address)) {
1945 //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);
1947 while (low_index != high_index) {
1948 middle_index = low_index + ((high_index - low_index) / 2);
1949 middle_region = regions->regions [middle_index];
1951 //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);
1953 if (middle_region->start > address) {
1954 if (middle_index > 0) {
1955 high_index = middle_index;
1959 } else if (middle_region->end < address) {
1960 if (middle_index < regions->regions_count - 1) {
1961 low_index = middle_index + 1;
1966 return middle_region;
1970 if ((middle_region == NULL) || (middle_region->start > address) || (middle_region->end < address)) {
1973 return middle_region;
1978 append_region (ProfilerExecutableMemoryRegions *regions, gpointer *start, gpointer *end, guint32 file_offset, char *file_name) {
1979 if (regions->regions_count >= regions->regions_capacity) {
1980 ProfilerExecutableMemoryRegionData **new_regions = g_new0 (ProfilerExecutableMemoryRegionData*, regions->regions_capacity * 2);
1981 memcpy (new_regions, regions->regions, regions->regions_capacity * sizeof (ProfilerExecutableMemoryRegionData*));
1982 g_free (regions->regions);
1983 regions->regions = new_regions;
1984 regions->regions_capacity = regions->regions_capacity * 2;
1986 regions->regions [regions->regions_count] = profiler_executable_memory_region_new (start, end, file_offset, file_name, regions->next_id);
1987 regions->regions_count ++;
1988 regions->next_id ++;
1992 restore_old_regions (ProfilerExecutableMemoryRegions *old_regions, ProfilerExecutableMemoryRegions *new_regions) {
1996 for (old_i = 0; old_i < old_regions->regions_count; old_i++) {
1997 ProfilerExecutableMemoryRegionData *old_region = old_regions->regions [old_i];
1998 for (new_i = 0; new_i < new_regions->regions_count; new_i++) {
1999 ProfilerExecutableMemoryRegionData *new_region = new_regions->regions [new_i];
2000 if ((old_region->start == new_region->start) &&
2001 (old_region->end == new_region->end) &&
2002 (old_region->file_offset == new_region->file_offset) &&
2003 ! strcmp (old_region->file_name, new_region->file_name)) {
2004 new_regions->regions [new_i] = old_region;
2005 old_regions->regions [old_i] = new_region;
2007 // FIXME (sanity check)
2008 g_assert (new_region->is_new && ! old_region->is_new);
2015 compare_regions (const void *a1, const void *a2) {
2016 ProfilerExecutableMemoryRegionData *r1 = * (ProfilerExecutableMemoryRegionData**) a1;
2017 ProfilerExecutableMemoryRegionData *r2 = * (ProfilerExecutableMemoryRegionData**) a2;
2018 return (r1->start < r2->start)? -1 : ((r1->start > r2->start)? 1 : 0);
2022 sort_regions (ProfilerExecutableMemoryRegions *regions) {
2023 qsort (regions->regions, regions->regions_count, sizeof (ProfilerExecutableMemoryRegionData *), compare_regions);
2027 executable_file_add_region_reference (ProfilerExecutableFile *file, ProfilerExecutableMemoryRegionData *region) {
2028 guint8 *section_headers = file->data + file->header->e_shoff;
2031 for (section_index = 1; section_index < file->header->e_shnum; section_index ++) {
2032 ElfSection *section_header = (ElfSection*) (section_headers + (file->header->e_shentsize * section_index));
2034 if ((section_header->sh_addr != 0) && (section_header->sh_flags & ELF_SHF_EXECINSTR) &&
2035 (region->file_offset <= section_header->sh_offset) && (region->file_offset + (((guint8*)region->end)-((guint8*)region->start)) >= (section_header->sh_offset + section_header->sh_size))) {
2036 ProfilerExecutableFileSectionRegion *section_region = & (file->section_regions [section_index]);
2037 section_region->region = region;
2038 section_region->section_address = (gpointer) section_header->sh_addr;
2039 section_region->section_offset = section_header->sh_offset;
2044 static ProfilerExecutableFile*
2045 executable_file_open (ProfilerExecutableMemoryRegionData *region) {
2046 ProfilerExecutableFiles *files = & (profiler->executable_files);
2047 ProfilerExecutableFile *file = (ProfilerExecutableFile*) g_hash_table_lookup (files->table, region->file_name);
2049 guint16 test = 0x0102;
2050 struct stat stat_buffer;
2051 int symtab_index = 0;
2052 int strtab_index = 0;
2053 int dynsym_index = 0;
2054 int dynstr_index = 0;
2056 guint8 *section_headers;
2060 file = g_new0 (ProfilerExecutableFile, 1);
2061 region->file = file;
2062 file->reference_count ++;
2064 file->fd = open (region->file_name, O_RDONLY);
2065 if (file->fd == -1) {
2066 //g_warning ("Cannot open file '%s': '%s'", region->file_name, strerror (errno));
2069 if (fstat (file->fd, &stat_buffer) != 0) {
2070 //g_warning ("Cannot stat file '%s': '%s'", region->file_name, strerror (errno));
2073 size_t region_length = ((guint8*)region->end) - ((guint8*)region->start);
2074 file->length = stat_buffer.st_size;
2076 if (file->length == region_length) {
2077 file->data = region->start;
2081 file->data = mmap (NULL, file->length, PROT_READ, MAP_PRIVATE, file->fd, 0);
2083 if (file->data == MAP_FAILED) {
2085 //g_warning ("Cannot map file '%s': '%s'", region->file_name, strerror (errno));
2093 header = (ElfHeader*) file->data;
2095 if ((header->e_ident [EI_MAG0] != 0x7f) || (header->e_ident [EI_MAG1] != 'E') ||
2096 (header->e_ident [EI_MAG2] != 'L') || (header->e_ident [EI_MAG3] != 'F')) {
2100 if (sizeof (gsize) == 4) {
2101 if (header->e_ident [EI_CLASS] != ELF_CLASS_32) {
2102 g_warning ("Class is not ELF_CLASS_32 with gsize size %d", (int) sizeof (gsize));
2105 } else if (sizeof (gsize) == 8) {
2106 if (header->e_ident [EI_CLASS] != ELF_CLASS_64) {
2107 g_warning ("Class is not ELF_CLASS_64 with gsize size %d", (int) sizeof (gsize));
2111 g_warning ("Absurd gsize size %d", (int) sizeof (gsize));
2115 if ((*(guint8*)(&test)) == 0x01) {
2116 if (header->e_ident [EI_DATA] != ELF_DATA_MSB) {
2117 g_warning ("Data is not ELF_DATA_MSB with first test byte 0x01");
2120 } else if ((*(guint8*)(&test)) == 0x02) {
2121 if (header->e_ident [EI_DATA] != ELF_DATA_LSB) {
2122 g_warning ("Data is not ELF_DATA_LSB with first test byte 0x02");
2126 g_warning ("Absurd test byte value");
2130 /* OK, this is a usable elf file... */
2131 file->header = header;
2132 section_headers = file->data + header->e_shoff;
2133 file->main_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * header->e_shstrndx)))->sh_offset);
2135 for (section_index = 0; section_index < header->e_shnum; section_index ++) {
2136 ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index));
2138 if (section_header->sh_type == ELF_SHT_SYMTAB) {
2139 symtab_index = section_index;
2140 } else if (section_header->sh_type == ELF_SHT_DYNSYM) {
2141 dynsym_index = section_index;
2142 } else if (section_header->sh_type == ELF_SHT_STRTAB) {
2143 if (! strcmp (file->main_string_table + section_header->sh_name, ".strtab")) {
2144 strtab_index = section_index;
2145 } else if (! strcmp (file->main_string_table + section_header->sh_name, ".dynstr")) {
2146 dynstr_index = section_index;
2151 if ((symtab_index != 0) && (strtab_index != 0)) {
2152 section_index = symtab_index;
2153 strings_index = strtab_index;
2154 } else if ((dynsym_index != 0) && (dynstr_index != 0)) {
2155 section_index = dynsym_index;
2156 strings_index = dynstr_index;
2161 if (section_index != 0) {
2162 ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index));
2163 file->symbol_size = section_header->sh_entsize;
2164 file->symbols_count = (guint32) (section_header->sh_size / section_header->sh_entsize);
2165 file->symbols_start = file->data + section_header->sh_offset;
2166 file->symbols_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * strings_index)))->sh_offset);
2169 file->section_regions = g_new0 (ProfilerExecutableFileSectionRegion, file->header->e_shnum);
2171 region->file = file;
2172 file->reference_count ++;
2175 if (file->header != NULL) {
2176 executable_file_add_region_reference (file, region);
2179 if (file->next_new_file == NULL) {
2180 file->next_new_file = files->new_files;
2181 files->new_files = file;
2187 executable_file_free (ProfilerExecutableFile* file) {
2188 if (file->fd != -1) {
2189 if (close (file->fd) != 0) {
2190 g_warning ("Cannot close file: '%s'", strerror (errno));
2192 if (file->data != NULL) {
2193 if (munmap (file->data, file->length) != 0) {
2194 g_warning ("Cannot unmap file: '%s'", strerror (errno));
2198 if (file->section_regions != NULL) {
2199 g_free (file->section_regions);
2205 executable_file_close (ProfilerExecutableMemoryRegionData *region) {
2206 region->file->reference_count --;
2208 if (region->file->reference_count <= 0) {
2209 ProfilerExecutableFiles *files = & (profiler->executable_files);
2210 g_hash_table_remove (files->table, region->file_name);
2211 executable_file_free (region->file);
2212 region->file = NULL;
2217 executable_file_count_symbols (ProfilerExecutableFile *file) {
2220 for (symbol_index = 0; symbol_index < file->symbols_count; symbol_index ++) {
2221 ElfSymbol *symbol = (ElfSymbol*) (file->symbols_start + (symbol_index * file->symbol_size));
2223 if ((ELF_ST_TYPE (symbol->st_info) == ELF_STT_FUNC) &&
2224 (symbol->st_shndx > 0) &&
2225 (symbol->st_shndx < file->header->e_shnum)) {
2226 int symbol_section_index = symbol->st_shndx;
2227 ProfilerExecutableMemoryRegionData *region = file->section_regions [symbol_section_index].region;
2228 if ((region != NULL) && (region->symbols == NULL)) {
2229 region->symbols_count ++;
2236 executable_memory_regions_prepare_symbol_tables (ProfilerExecutableMemoryRegions *regions) {
2238 for (i = 0; i < regions->regions_count; i++) {
2239 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2240 if ((region->symbols_count > 0) && (region->symbols == NULL)) {
2241 region->symbols = g_new (ProfilerUnmanagedSymbol, region->symbols_count);
2242 region->symbols_capacity = region->symbols_count;
2243 region->symbols_count = 0;
2249 executable_region_symbol_get_name (ProfilerExecutableMemoryRegionData *region, ProfilerUnmanagedSymbol *symbol) {
2250 ElfSymbol *elf_symbol = (ElfSymbol*) (region->file->symbols_start + (symbol->index * region->file->symbol_size));
2251 return region->file->symbols_string_table + elf_symbol->st_name;
2255 executable_file_build_symbol_tables (ProfilerExecutableFile *file) {
2258 for (symbol_index = 0; symbol_index < file->symbols_count; symbol_index ++) {
2259 ElfSymbol *symbol = (ElfSymbol*) (file->symbols_start + (symbol_index * file->symbol_size));
2261 if ((ELF_ST_TYPE (symbol->st_info) == ELF_STT_FUNC) &&
2262 (symbol->st_shndx > 0) &&
2263 (symbol->st_shndx < file->header->e_shnum)) {
2264 int symbol_section_index = symbol->st_shndx;
2265 ProfilerExecutableFileSectionRegion *section_region = & (file->section_regions [symbol_section_index]);
2266 ProfilerExecutableMemoryRegionData *region = section_region->region;
2268 if (region != NULL) {
2269 ProfilerUnmanagedSymbol *new_symbol = & (region->symbols [region->symbols_count]);
2270 region->symbols_count ++;
2273 new_symbol->index = symbol_index;
2274 new_symbol->size = symbol->st_size;
2275 new_symbol->offset = (((guint8*) symbol->st_value) - section_region->section_address) - (region->file_offset - section_region->section_offset);
2282 compare_region_symbols (const void *p1, const void *p2) {
2283 const ProfilerUnmanagedSymbol *s1 = p1;
2284 const ProfilerUnmanagedSymbol *s2 = p2;
2285 return (s1->offset < s2->offset)? -1 : ((s1->offset > s2->offset)? 1 : 0);
2289 executable_memory_regions_sort_symbol_tables (ProfilerExecutableMemoryRegions *regions) {
2291 for (i = 0; i < regions->regions_count; i++) {
2292 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2293 if ((region->is_new) && (region->symbols != NULL)) {
2294 qsort (region->symbols, region->symbols_count, sizeof (ProfilerUnmanagedSymbol), compare_region_symbols);
2300 build_symbol_tables (ProfilerExecutableMemoryRegions *regions, ProfilerExecutableFiles *files) {
2302 ProfilerExecutableFile *file;
2304 for (i = 0; i < regions->regions_count; i++) {
2305 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2306 if ((region->is_new) && (region->file == NULL)) {
2307 executable_file_open (region);
2311 for (file = files->new_files; file != NULL; file = file->next_new_file) {
2312 executable_file_count_symbols (file);
2315 executable_memory_regions_prepare_symbol_tables (regions);
2317 for (file = files->new_files; file != NULL; file = file->next_new_file) {
2318 executable_file_build_symbol_tables (file);
2321 executable_memory_regions_sort_symbol_tables (regions);
2323 file = files->new_files;
2324 while (file != NULL) {
2325 ProfilerExecutableFile *next_file = file->next_new_file;
2326 file->next_new_file = NULL;
2329 files->new_files = NULL;
2332 static ProfilerUnmanagedSymbol*
2333 executable_memory_region_find_symbol (ProfilerExecutableMemoryRegionData *region, guint32 offset) {
2334 if (region->symbols_count > 0) {
2335 ProfilerUnmanagedSymbol *low = region->symbols;
2336 ProfilerUnmanagedSymbol *high = region->symbols + (region->symbols_count - 1);
2337 int step = region->symbols_count >> 1;
2338 ProfilerUnmanagedSymbol *current = region->symbols + step;
2341 step = (high - low) >> 1;
2343 if (offset < current->offset) {
2345 current = high - step;
2346 } else if (offset >= current->offset) {
2347 if (offset >= (current->offset + current->size)) {
2349 current = low + step;
2356 if ((offset >= current->offset) && (offset < (current->offset + current->size))) {
2366 //FIXME: make also Win32 and BSD variants
2367 #define MAPS_BUFFER_SIZE 4096
2370 update_regions_buffer (int fd, char *buffer) {
2371 ssize_t result = read (fd, buffer, MAPS_BUFFER_SIZE);
2373 if (result == MAPS_BUFFER_SIZE) {
2375 } else if (result >= 0) {
2376 *(buffer + result) = 0;
2384 #define GOTO_NEXT_CHAR(c,b,fd) do {\
2386 if (((c) - (b) >= MAPS_BUFFER_SIZE) || ((*(c) == 0) && ((c) != (b)))) {\
2387 update_regions_buffer ((fd), (b));\
2392 static int hex_digit_value (char c) {
2393 if ((c >= '0') && (c <= '9')) {
2395 } else if ((c >= 'a') && (c <= 'f')) {
2396 return c - 'a' + 10;
2397 } else if ((c >= 'A') && (c <= 'F')) {
2398 return c - 'A' + 10;
2420 MAP_LINE_PARSER_STATE_INVALID,
2421 MAP_LINE_PARSER_STATE_START_ADDRESS,
2422 MAP_LINE_PARSER_STATE_END_ADDRESS,
2423 MAP_LINE_PARSER_STATE_PERMISSIONS,
2424 MAP_LINE_PARSER_STATE_OFFSET,
2425 MAP_LINE_PARSER_STATE_DEVICE,
2426 MAP_LINE_PARSER_STATE_INODE,
2427 MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME,
2428 MAP_LINE_PARSER_STATE_FILENAME,
2429 MAP_LINE_PARSER_STATE_DONE
2430 } MapLineParserState;
2432 const char *map_line_parser_state [] = {
2440 "BLANK_BEFORE_FILENAME",
2446 parse_map_line (ProfilerExecutableMemoryRegions *regions, int fd, char *buffer, char *current) {
2447 MapLineParserState state = MAP_LINE_PARSER_STATE_START_ADDRESS;
2448 gsize start_address = 0;
2449 gsize end_address = 0;
2451 char *start_filename = NULL;
2452 char *end_filename = NULL;
2453 gboolean is_executable = FALSE;
2454 gboolean done = FALSE;
2460 case MAP_LINE_PARSER_STATE_START_ADDRESS:
2462 start_address <<= 4;
2463 start_address |= hex_digit_value (c);
2464 } else if (c == '-') {
2465 state = MAP_LINE_PARSER_STATE_END_ADDRESS;
2467 state = MAP_LINE_PARSER_STATE_INVALID;
2470 case MAP_LINE_PARSER_STATE_END_ADDRESS:
2473 end_address |= hex_digit_value (c);
2474 } else if (isblank (c)) {
2475 state = MAP_LINE_PARSER_STATE_PERMISSIONS;
2477 state = MAP_LINE_PARSER_STATE_INVALID;
2480 case MAP_LINE_PARSER_STATE_PERMISSIONS:
2482 is_executable = TRUE;
2483 } else if (isblank (c)) {
2484 state = MAP_LINE_PARSER_STATE_OFFSET;
2485 } else if ((c != '-') && ! isalpha (c)) {
2486 state = MAP_LINE_PARSER_STATE_INVALID;
2489 case MAP_LINE_PARSER_STATE_OFFSET:
2492 offset |= hex_digit_value (c);
2493 } else if (isblank (c)) {
2494 state = MAP_LINE_PARSER_STATE_DEVICE;
2496 state = MAP_LINE_PARSER_STATE_INVALID;
2499 case MAP_LINE_PARSER_STATE_DEVICE:
2501 state = MAP_LINE_PARSER_STATE_INODE;
2502 } else if ((c != ':') && ! isxdigit (c)) {
2503 state = MAP_LINE_PARSER_STATE_INVALID;
2506 case MAP_LINE_PARSER_STATE_INODE:
2508 state = MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME;
2509 } else if (! isdigit (c)) {
2510 state = MAP_LINE_PARSER_STATE_INVALID;
2513 case MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME:
2514 if ((c == '/') || (c == '[')) {
2515 state = MAP_LINE_PARSER_STATE_FILENAME;
2516 start_filename = current;
2517 } else if (! isblank (c)) {
2518 state = MAP_LINE_PARSER_STATE_INVALID;
2521 case MAP_LINE_PARSER_STATE_FILENAME:
2523 state = MAP_LINE_PARSER_STATE_DONE;
2525 end_filename = current;
2528 case MAP_LINE_PARSER_STATE_DONE:
2529 if (done && is_executable) {
2531 append_region (regions, (gpointer) start_address, (gpointer) end_address, offset, start_filename);
2534 case MAP_LINE_PARSER_STATE_INVALID:
2536 state = MAP_LINE_PARSER_STATE_DONE;
2543 } else if (c == '\n') {
2544 state = MAP_LINE_PARSER_STATE_DONE;
2547 GOTO_NEXT_CHAR(current, buffer, fd);
2553 scan_process_regions (ProfilerExecutableMemoryRegions *regions) {
2558 fd = open ("/proc/self/maps", O_RDONLY);
2563 buffer = malloc (MAPS_BUFFER_SIZE);
2564 update_regions_buffer (fd, buffer);
2566 while (current != NULL) {
2567 current = parse_map_line (regions, fd, buffer, current);
2578 MONO_PROFILER_STATISTICAL_CODE_END = 0,
2579 MONO_PROFILER_STATISTICAL_CODE_METHOD = 1,
2580 MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID = 2,
2581 MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_NEW_ID = 3,
2582 MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION = 4,
2583 MONO_PROFILER_STATISTICAL_CODE_REGIONS = 7
2584 } MonoProfilerStatisticalCode;
2587 refresh_memory_regions (void) {
2588 ProfilerExecutableMemoryRegions *old_regions = profiler->executable_regions;
2589 ProfilerExecutableMemoryRegions *new_regions = profiler_executable_memory_regions_new (old_regions->next_id, old_regions->next_unmanaged_function_id);
2592 LOG_WRITER_THREAD ("Refreshing memory regions...");
2593 scan_process_regions (new_regions);
2594 restore_old_regions (old_regions, new_regions);
2595 sort_regions (new_regions);
2596 LOG_WRITER_THREAD ("Refreshed memory regions.");
2598 LOG_WRITER_THREAD ("Building symbol tables...");
2599 build_symbol_tables (new_regions, & (profiler->executable_files));
2601 printf ("Symbol tables done!\n");
2602 printf ("Region summary...\n");
2603 for (i = 0; i < new_regions->regions_count; i++) {
2604 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
2605 printf ("Region %d[%d][NEW:%d] (%p-%p) at %d in file %s\n", i, region->id, region->is_new,
2606 region->start, region->end, region->file_offset, region->file_name);
2608 printf ("New symbol tables dump...\n");
2609 for (i = 0; i < new_regions->regions_count; i++) {
2610 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
2612 if (region->is_new) {
2615 printf ("Region %d[%d][NEW:%d] (%p-%p) at %d in file %s\n", i, region->id, region->is_new,
2616 region->start, region->end, region->file_offset, region->file_name);
2617 for (symbol_index = 0; symbol_index < region->symbols_count; symbol_index ++) {
2618 ProfilerUnmanagedSymbol *symbol = & (region->symbols [symbol_index]);
2619 printf (" [%d] Symbol %s (offset %d, size %d)\n", symbol_index,
2620 executable_region_symbol_get_name (region, symbol),
2621 symbol->offset, symbol->size);
2626 LOG_WRITER_THREAD ("Built symbol tables.");
2628 // This marks the region "sub-block"
2629 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_REGIONS);
2631 // First write the "removed" regions
2632 for (i = 0; i < old_regions->regions_count; i++) {
2633 ProfilerExecutableMemoryRegionData *region = old_regions->regions [i];
2634 if (! region->is_new) {
2635 #if DEBUG_STATISTICAL_PROFILER
2636 printf ("[refresh_memory_regions] Invalidated region %d\n", region->id);
2638 write_uint32 (region->id);
2643 // Then write the new ones
2644 for (i = 0; i < new_regions->regions_count; i++) {
2645 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
2646 if (region->is_new) {
2647 region->is_new = FALSE;
2649 #if DEBUG_STATISTICAL_PROFILER
2650 printf ("[refresh_memory_regions] Wrote region %d (%p-%p[%d] '%s')\n", region->id, region->start, region->end, region->file_offset, region->file_name);
2652 write_uint32 (region->id);
2653 write_uint64 (GPOINTER_TO_INT (region->start));
2654 write_uint32 (GPOINTER_TO_INT (region->end) - GPOINTER_TO_INT (region->start));
2655 write_uint32 (region->file_offset);
2656 write_string (region->file_name);
2661 // Finally, free the old ones, and replace them
2662 profiler_executable_memory_regions_destroy (old_regions);
2663 profiler->executable_regions = new_regions;
2667 flush_all_mappings (void);
2670 write_statistical_data_block (ProfilerStatisticalData *data) {
2671 int start_index = data->first_unwritten_index;
2672 int end_index = data->next_free_index;
2673 gboolean regions_refreshed = FALSE;
2676 if (end_index > data->end_index)
2677 end_index = data->end_index;
2679 if (start_index == end_index)
2682 data->first_unwritten_index = end_index;
2684 write_clock_data ();
2686 for (index = start_index; index < end_index; index ++) {
2687 gpointer address = data->addresses [index];
2688 MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (char*) address);
2691 MonoMethod *method = mono_jit_info_get_method (ji);
2692 MethodIdMappingElement *element = method_id_mapping_element_get (method);
2694 if (element != NULL) {
2695 #if DEBUG_STATISTICAL_PROFILER
2696 printf ("[write_statistical_data_block] Wrote method %d\n", element->id);
2698 write_uint32 ((element->id << 3) | MONO_PROFILER_STATISTICAL_CODE_METHOD);
2700 #if DEBUG_STATISTICAL_PROFILER
2701 printf ("[write_statistical_data_block] Wrote unknown method %p\n", method);
2703 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_METHOD);
2706 ProfilerExecutableMemoryRegionData *region = find_address_region (profiler->executable_regions, address);
2708 if (region == NULL && ! regions_refreshed) {
2709 #if DEBUG_STATISTICAL_PROFILER
2710 printf ("[write_statistical_data_block] Cannot find region for address %p, refreshing...\n", address);
2712 refresh_memory_regions ();
2713 regions_refreshed = TRUE;
2714 region = find_address_region (profiler->executable_regions, address);
2717 if (region != NULL) {
2718 guint32 offset = ((guint8*)address) - ((guint8*)region->start);
2719 ProfilerUnmanagedSymbol *symbol = executable_memory_region_find_symbol (region, offset);
2721 if (symbol != NULL) {
2722 if (symbol->id > 0) {
2723 #if DEBUG_STATISTICAL_PROFILER
2724 printf ("[write_statistical_data_block] Wrote unmanaged symbol %d\n", symbol->id);
2726 write_uint32 ((symbol->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID);
2728 ProfilerExecutableMemoryRegions *regions = profiler->executable_regions;
2729 const char *symbol_name = executable_region_symbol_get_name (region, symbol);
2730 symbol->id = regions->next_unmanaged_function_id;
2731 regions->next_unmanaged_function_id ++;
2732 #if DEBUG_STATISTICAL_PROFILER
2733 printf ("[write_statistical_data_block] Wrote new unmanaged symbol in region %d[%d]\n", region->id, offset);
2735 write_uint32 ((region->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_NEW_ID);
2736 write_uint32 (symbol->id);
2737 write_string (symbol_name);
2740 #if DEBUG_STATISTICAL_PROFILER
2741 printf ("[write_statistical_data_block] Wrote unknown unmanaged hit in region %d[%d] (address %p)\n", region->id, offset, address);
2743 write_uint32 ((region->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION);
2744 write_uint32 (offset);
2747 #if DEBUG_STATISTICAL_PROFILER
2748 printf ("[write_statistical_data_block] Wrote unknown unmanaged hit %p\n", address);
2750 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION);
2751 write_uint64 (GPOINTER_TO_INT (address));
2755 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_END);
2757 write_clock_data ();
2759 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL);
2763 write_intro_block (void) {
2765 write_string ("mono");
2766 write_uint32 (profiler->flags);
2767 write_uint64 (profiler->start_counter);
2768 write_uint64 (profiler->start_time);
2769 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_INTRO);
2773 write_end_block (void) {
2775 write_uint64 (profiler->end_counter);
2776 write_uint64 (profiler->end_time);
2777 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_END);
2781 update_mapping (ProfilerPerThreadData *data) {
2782 ProfilerEventData *start = data->first_unmapped_event;
2783 ProfilerEventData *end = data->next_free_event;
2784 data->first_unmapped_event = end;
2786 #if (DEBUG_LOGGING_PROFILER)
2787 printf ("[update_mapping][TID %ld] START\n", data->thread_id);
2789 while (start < end) {
2790 #if DEBUG_LOGGING_PROFILER
2791 printf ("Examining event %p[TID %ld] looking for a new mapping...\n", start, data->thread_id);
2793 if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
2794 ClassIdMappingElement *element = class_id_mapping_element_get (start->data.address);
2795 if (element == NULL) {
2796 MonoClass *klass = start->data.address;
2797 class_id_mapping_element_new (klass);
2799 } else if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
2800 MethodIdMappingElement *element = method_id_mapping_element_get (start->data.address);
2801 if (element == NULL) {
2802 MonoMethod *method = start->data.address;
2803 method_id_mapping_element_new (method);
2807 if (start->value == MAX_EVENT_VALUE) {
2812 #if (DEBUG_LOGGING_PROFILER)
2813 printf ("[update_mapping][TID %ld] END\n", data->thread_id);
2818 flush_all_mappings (void) {
2819 ProfilerPerThreadData *data;
2821 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2822 update_mapping (data);
2824 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2825 write_mapping_block (data->thread_id);
2830 flush_full_event_data_buffer (ProfilerPerThreadData *data) {
2833 // We flush all mappings because some id definitions could come
2834 // from other threads
2835 flush_all_mappings ();
2836 g_assert (data->first_unmapped_event >= data->end_event);
2838 write_thread_data_block (data);
2840 data->next_free_event = data->events;
2841 data->first_unwritten_event = data->events;
2842 data->first_unmapped_event = data->events;
2843 MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
2844 data->last_event_counter = data->start_event_counter;
2849 #define GET_NEXT_FREE_EVENT(d,e) {\
2850 if ((d)->next_free_event >= (d)->end_event) {\
2851 flush_full_event_data_buffer (d);\
2853 (e) = (d)->next_free_event;\
2854 (d)->next_free_event ++;\
2858 flush_everything (void) {
2859 ProfilerPerThreadData *data;
2861 flush_all_mappings ();
2862 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2863 write_thread_data_block (data);
2865 write_statistical_data_block (profiler->statistical_data);
2868 #define RESULT_TO_LOAD_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_LOADED_EVENT_SUCCESS:MONO_PROFILER_LOADED_EVENT_FAILURE)
2870 appdomain_start_load (MonoProfiler *profiler, MonoDomain *domain) {
2872 loaded_element_load_start (profiler->loaded_appdomains, domain);
2877 appdomain_end_load (MonoProfiler *profiler, MonoDomain *domain, int result) {
2879 LoadedElement *element;
2881 name = g_strdup_printf ("%d", mono_domain_get_id (domain));
2883 element = loaded_element_load_end (profiler->loaded_appdomains, domain, name);
2884 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
2889 appdomain_start_unload (MonoProfiler *profiler, MonoDomain *domain) {
2891 loaded_element_unload_start (profiler->loaded_appdomains, domain);
2892 flush_everything ();
2897 appdomain_end_unload (MonoProfiler *profiler, MonoDomain *domain) {
2898 LoadedElement *element;
2901 element = loaded_element_unload_end (profiler->loaded_appdomains, domain);
2902 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN, CURRENT_THREAD_ID ());
2907 module_start_load (MonoProfiler *profiler, MonoImage *module) {
2909 loaded_element_load_start (profiler->loaded_modules, module);
2914 module_end_load (MonoProfiler *profiler, MonoImage *module, int result) {
2916 MonoAssemblyName aname;
2917 LoadedElement *element;
2919 mono_assembly_fill_assembly_name (module, &aname);
2920 name = mono_stringify_assembly_name (&aname);
2922 element = loaded_element_load_end (profiler->loaded_modules, module, name);
2923 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_MODULE | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
2928 module_start_unload (MonoProfiler *profiler, MonoImage *module) {
2930 loaded_element_unload_start (profiler->loaded_modules, module);
2931 flush_everything ();
2936 module_end_unload (MonoProfiler *profiler, MonoImage *module) {
2937 LoadedElement *element;
2940 element = loaded_element_unload_end (profiler->loaded_modules, module);
2941 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_MODULE, CURRENT_THREAD_ID ());
2946 assembly_start_load (MonoProfiler *profiler, MonoAssembly *assembly) {
2948 loaded_element_load_start (profiler->loaded_assemblies, assembly);
2953 assembly_end_load (MonoProfiler *profiler, MonoAssembly *assembly, int result) {
2955 MonoAssemblyName aname;
2956 LoadedElement *element;
2958 mono_assembly_fill_assembly_name (mono_assembly_get_image (assembly), &aname);
2959 name = mono_stringify_assembly_name (&aname);
2961 element = loaded_element_load_end (profiler->loaded_assemblies, assembly, name);
2962 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
2967 assembly_start_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
2969 loaded_element_unload_start (profiler->loaded_assemblies, assembly);
2970 flush_everything ();
2974 assembly_end_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
2975 LoadedElement *element;
2978 element = loaded_element_unload_end (profiler->loaded_assemblies, assembly);
2979 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY, CURRENT_THREAD_ID ());
2983 #if (DEBUG_LOGGING_PROFILER)
2985 class_event_code_to_string (MonoProfilerClassEvents code) {
2987 case MONO_PROFILER_EVENT_CLASS_LOAD: return "LOAD";
2988 case MONO_PROFILER_EVENT_CLASS_UNLOAD: return "UNLOAD";
2989 case MONO_PROFILER_EVENT_CLASS_ALLOCATION: return "ALLOCATION";
2990 case MONO_PROFILER_EVENT_CLASS_EXCEPTION: return "EXCEPTION";
2991 default: g_assert_not_reached (); return "";
2995 method_event_code_to_string (MonoProfilerClassEvents code) {
2997 case MONO_PROFILER_EVENT_METHOD_CALL: return "CALL";
2998 case MONO_PROFILER_EVENT_METHOD_JIT: return "JIT";
2999 case MONO_PROFILER_EVENT_METHOD_FREED: return "FREED";
3000 default: g_assert_not_reached (); return "";
3004 number_event_code_to_string (MonoProfilerEvents code) {
3006 case MONO_PROFILER_EVENT_THREAD: return "THREAD";
3007 case MONO_PROFILER_EVENT_GC_COLLECTION: return "GC_COLLECTION";
3008 case MONO_PROFILER_EVENT_GC_MARK: return "GC_MARK";
3009 case MONO_PROFILER_EVENT_GC_SWEEP: return "GC_SWEEP";
3010 case MONO_PROFILER_EVENT_GC_RESIZE: return "GC_RESIZE";
3011 case MONO_PROFILER_EVENT_GC_STOP_WORLD: return "GC_STOP_WORLD";
3012 case MONO_PROFILER_EVENT_GC_START_WORLD: return "GC_START_WORLD";
3013 default: g_assert_not_reached (); return "";
3017 event_result_to_string (MonoProfilerEventResult code) {
3019 case MONO_PROFILER_EVENT_RESULT_SUCCESS: return "SUCCESS";
3020 case MONO_PROFILER_EVENT_RESULT_FAILURE: return "FAILURE";
3021 default: g_assert_not_reached (); return "";
3025 event_kind_to_string (MonoProfilerEventKind code) {
3027 case MONO_PROFILER_EVENT_KIND_START: return "START";
3028 case MONO_PROFILER_EVENT_KIND_END: return "END";
3029 default: g_assert_not_reached (); return "";
3033 print_event_data (gsize thread_id, ProfilerEventData *event, guint64 value) {
3034 if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
3035 printf ("[TID %ld] CLASS[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s)\n",
3037 event->data.address,
3039 class_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
3040 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
3041 event_kind_to_string (event->kind),
3046 mono_class_get_namespace ((MonoClass*) event->data.address),
3047 mono_class_get_name ((MonoClass*) event->data.address));
3048 } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
3049 printf ("[TID %ld] METHOD[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s:%s (?))\n",
3051 event->data.address,
3053 method_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
3054 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
3055 event_kind_to_string (event->kind),
3060 mono_class_get_namespace (mono_method_get_class ((MonoMethod*) event->data.address)),
3061 mono_class_get_name (mono_method_get_class ((MonoMethod*) event->data.address)),
3062 mono_method_get_name ((MonoMethod*) event->data.address));
3064 printf ("[TID %ld] NUMBER[%ld] event [%p] %s:%s[%d-%d-%d] %ld\n",
3066 (guint64) event->data.number,
3068 number_event_code_to_string (event->code),
3069 event_kind_to_string (event->kind),
3076 #define LOG_EVENT(tid,ev,val) print_event_data ((tid),(ev),(val))
3078 #define LOG_EVENT(tid,ev,val)
3081 #define RESULT_TO_EVENT_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_EVENT_RESULT_SUCCESS:MONO_PROFILER_EVENT_RESULT_FAILURE)
3083 #define STORE_EVENT_ITEM_COUNTER(p,i,dt,c,k) do {\
3084 ProfilerPerThreadData *data;\
3085 ProfilerEventData *event;\
3088 GET_PROFILER_THREAD_DATA (data);\
3089 GET_NEXT_FREE_EVENT (data, event);\
3090 MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
3091 event->data.address = (i);\
3092 event->data_type = (dt);\
3095 delta = counter - data->last_event_counter;\
3096 if (counter < data->last_event_counter) {\
3097 printf ("STORE_EVENT_ITEM_COUNTER: counter %ld < data->last_event_counter %ld\n", counter, data->last_event_counter);\
3099 if (delta < MAX_EVENT_VALUE) {\
3100 event->value = delta;\
3102 ProfilerEventData *extension = data->next_free_event;\
3103 data->next_free_event ++;\
3104 event->value = MAX_EVENT_VALUE;\
3105 *(guint64*)extension = delta;\
3107 data->last_event_counter = counter;\
3108 LOG_EVENT (data->thread_id, event, delta);\
3110 #define STORE_EVENT_ITEM_VALUE(p,i,dt,c,k,v) do {\
3111 ProfilerPerThreadData *data;\
3112 ProfilerEventData *event;\
3113 GET_PROFILER_THREAD_DATA (data);\
3114 GET_NEXT_FREE_EVENT (data, event);\
3115 event->data.address = (i);\
3116 event->data_type = (dt);\
3119 if ((v) < MAX_EVENT_VALUE) {\
3120 event->value = (v);\
3122 ProfilerEventData *extension = data->next_free_event;\
3123 data->next_free_event ++;\
3124 event->value = MAX_EVENT_VALUE;\
3125 *(guint64*)extension = (v);\
3127 LOG_EVENT (data->thread_id, event, (v));\
3129 #define STORE_EVENT_NUMBER_COUNTER(p,n,dt,c,k) do {\
3130 ProfilerPerThreadData *data;\
3131 ProfilerEventData *event;\
3134 GET_PROFILER_THREAD_DATA (data);\
3135 GET_NEXT_FREE_EVENT (data, event);\
3136 MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
3137 event->data.number = (n);\
3138 event->data_type = (dt);\
3141 delta = counter - data->last_event_counter;\
3142 if (delta < MAX_EVENT_VALUE) {\
3143 event->value = delta;\
3145 ProfilerEventData *extension = data->next_free_event;\
3146 data->next_free_event ++;\
3147 event->value = MAX_EVENT_VALUE;\
3148 *(guint64*)extension = delta;\
3150 data->last_event_counter = counter;\
3151 LOG_EVENT (data->thread_id, event, delta);\
3153 #define STORE_EVENT_NUMBER_VALUE(p,n,dt,c,k,v) do {\
3154 ProfilerPerThreadData *data;\
3155 ProfilerEventData *event;\
3156 GET_PROFILER_THREAD_DATA (data);\
3157 GET_NEXT_FREE_EVENT (data, event);\
3158 event->data.number = (n);\
3159 event->data_type = (dt);\
3162 if ((v) < MAX_EVENT_VALUE) {\
3163 event->value = (v);\
3165 ProfilerEventData *extension = data->next_free_event;\
3166 data->next_free_event ++;\
3167 event->value = MAX_EVENT_VALUE;\
3168 *(guint64*)extension = (v);\
3170 LOG_EVENT (data->thread_id, event, (v));\
3175 class_start_load (MonoProfiler *profiler, MonoClass *klass) {
3176 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD, MONO_PROFILER_EVENT_KIND_START);
3179 class_end_load (MonoProfiler *profiler, MonoClass *klass, int result) {
3180 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);
3183 class_start_unload (MonoProfiler *profiler, MonoClass *klass) {
3184 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_START);
3187 class_end_unload (MonoProfiler *profiler, MonoClass *klass) {
3188 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_END);
3192 method_start_jit (MonoProfiler *profiler, MonoMethod *method) {
3193 if (profiler->action_flags.jit_time) {
3194 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT, MONO_PROFILER_EVENT_KIND_START);
3198 method_end_jit (MonoProfiler *profiler, MonoMethod *method, int result) {
3199 if (profiler->action_flags.jit_time) {
3200 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);
3206 method_jit_result (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result) {
3207 if (profiler->action_flags.oprofile && (result == MONO_PROFILE_OK)) {
3208 MonoClass *klass = mono_method_get_class (method);
3209 char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
3210 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);
3211 gpointer code_start = mono_jit_info_get_code_start (jinfo);
3212 int code_size = mono_jit_info_get_code_size (jinfo);
3214 if (op_write_native_code (name, code_start, code_size)) {
3215 g_warning ("Problem calling op_write_native_code\n");
3226 method_enter (MonoProfiler *profiler, MonoMethod *method) {
3227 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_START);
3230 method_leave (MonoProfiler *profiler, MonoMethod *method) {
3231 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_END);
3235 method_free (MonoProfiler *profiler, MonoMethod *method) {
3236 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_FREED, 0);
3240 thread_start (MonoProfiler *profiler, gsize tid) {
3241 STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_START);
3244 thread_end (MonoProfiler *profiler, gsize tid) {
3245 STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_END);
3249 object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) {
3250 ProfilerPerThreadData *thread_data;
3252 STORE_EVENT_ITEM_VALUE (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_ALLOCATION, 0, (guint64) mono_object_get_size (obj));
3253 if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
3254 GET_PROFILER_THREAD_DATA (thread_data);
3255 STORE_ALLOCATED_OBJECT (thread_data, obj);
3261 statistical_hit (MonoProfiler *profiler, guchar *ip, void *context) {
3262 ProfilerStatisticalData *data;
3266 data = profiler->statistical_data;
3267 index = InterlockedIncrement (&data->next_free_index);
3269 if (index <= data->end_index) {
3270 data->addresses [index - 1] = (gpointer) ip;
3272 /* Check if we are the one that must swap the buffers */
3273 if (index == data->end_index + 1) {
3274 ProfilerStatisticalData *new_data;
3276 /* In the *impossible* case that the writer thread has not finished yet, */
3277 /* loop waiting for it and meanwhile lose all statistical events... */
3279 /* First, wait that it consumed the ready buffer */
3280 while (profiler->statistical_data_ready != NULL);
3281 /* Then, wait that it produced the free buffer */
3282 new_data = profiler->statistical_data_second_buffer;
3283 } while (new_data == NULL);
3285 profiler->statistical_data_ready = data;
3286 profiler->statistical_data = new_data;
3287 profiler->statistical_data_second_buffer = NULL;
3288 WRITER_EVENT_RAISE ();
3291 /* Loop again, hoping to acquire a free slot this time */
3294 } while (data == NULL);
3297 static MonoProfilerEvents
3298 gc_event_code_from_profiler_event (MonoGCEvent event) {
3300 case MONO_GC_EVENT_START:
3301 case MONO_GC_EVENT_END:
3302 return MONO_PROFILER_EVENT_GC_COLLECTION;
3303 case MONO_GC_EVENT_MARK_START:
3304 case MONO_GC_EVENT_MARK_END:
3305 return MONO_PROFILER_EVENT_GC_MARK;
3306 case MONO_GC_EVENT_RECLAIM_START:
3307 case MONO_GC_EVENT_RECLAIM_END:
3308 return MONO_PROFILER_EVENT_GC_SWEEP;
3309 case MONO_GC_EVENT_PRE_STOP_WORLD:
3310 case MONO_GC_EVENT_POST_STOP_WORLD:
3311 return MONO_PROFILER_EVENT_GC_STOP_WORLD;
3312 case MONO_GC_EVENT_PRE_START_WORLD:
3313 case MONO_GC_EVENT_POST_START_WORLD:
3314 return MONO_PROFILER_EVENT_GC_START_WORLD;
3316 g_assert_not_reached ();
3321 static MonoProfilerEventKind
3322 gc_event_kind_from_profiler_event (MonoGCEvent event) {
3324 case MONO_GC_EVENT_START:
3325 case MONO_GC_EVENT_MARK_START:
3326 case MONO_GC_EVENT_RECLAIM_START:
3327 case MONO_GC_EVENT_PRE_STOP_WORLD:
3328 case MONO_GC_EVENT_PRE_START_WORLD:
3329 return MONO_PROFILER_EVENT_KIND_START;
3330 case MONO_GC_EVENT_END:
3331 case MONO_GC_EVENT_MARK_END:
3332 case MONO_GC_EVENT_RECLAIM_END:
3333 case MONO_GC_EVENT_POST_START_WORLD:
3334 case MONO_GC_EVENT_POST_STOP_WORLD:
3335 return MONO_PROFILER_EVENT_KIND_END;
3337 g_assert_not_reached ();
3342 #define HEAP_SHOT_COMMAND_FILE_MAX_LENGTH 64
3344 profiler_heap_shot_process_command_file (void) {
3345 //FIXME: Port to Windows as well
3346 struct stat stat_buf;
3348 char buffer [HEAP_SHOT_COMMAND_FILE_MAX_LENGTH + 1];
3350 if (profiler->heap_shot_command_file_name == NULL)
3352 if (stat (profiler->heap_shot_command_file_name, &stat_buf) != 0)
3354 if (stat_buf.st_size > HEAP_SHOT_COMMAND_FILE_MAX_LENGTH)
3356 if ((stat_buf.st_mtim.tv_sec * 1000000) < profiler->heap_shot_command_file_access_time)
3359 fd = open (profiler->heap_shot_command_file_name, O_RDONLY);
3363 if (read (fd, &(buffer [0]), stat_buf.st_size) != stat_buf.st_size) {
3366 buffer [stat_buf.st_size] = 0;
3367 profiler->dump_next_heap_snapshots = atoi (buffer);
3368 MONO_PROFILER_GET_CURRENT_TIME (profiler->heap_shot_command_file_access_time);
3375 dump_current_heap_snapshot (void) {
3378 if (profiler->heap_shot_was_signalled) {
3381 profiler_heap_shot_process_command_file ();
3382 if (profiler->dump_next_heap_snapshots > 0) {
3383 profiler->dump_next_heap_snapshots--;
3385 } else if (profiler->dump_next_heap_snapshots < 0) {
3396 profiler_heap_buffers_setup (ProfilerHeapShotHeapBuffers *heap) {
3397 heap->buffers = g_new (ProfilerHeapShotHeapBuffer, 1);
3398 heap->buffers->previous = NULL;
3399 heap->buffers->next = NULL;
3400 heap->buffers->start_slot = &(heap->buffers->buffer [0]);
3401 heap->buffers->end_slot = &(heap->buffers->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
3402 heap->last = heap->buffers;
3403 heap->current = heap->buffers;
3404 heap->first_free_slot = & (heap->buffers->buffer [0]);
3407 profiler_heap_buffers_clear (ProfilerHeapShotHeapBuffers *heap) {
3408 heap->buffers = NULL;
3410 heap->current = NULL;
3411 heap->first_free_slot = NULL;
3414 profiler_heap_buffers_free (ProfilerHeapShotHeapBuffers *heap) {
3415 ProfilerHeapShotHeapBuffer *current = heap->buffers;
3416 while (current != NULL) {
3417 ProfilerHeapShotHeapBuffer *next = current->next;
3421 profiler_heap_buffers_clear (heap);
3425 report_object_references (gpointer *start, ClassIdMappingElement *layout, ProfilerHeapShotWriteJob *job) {
3426 int reported_references = 0;
3429 for (slot = 0; slot < layout->data.layout.slots; slot ++) {
3430 gboolean slot_has_reference;
3431 if (layout->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
3432 if (layout->data.bitmap.compact & (((guint64)1) << slot)) {
3433 slot_has_reference = TRUE;
3435 slot_has_reference = FALSE;
3438 if (layout->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
3439 slot_has_reference = TRUE;
3441 slot_has_reference = FALSE;
3445 if (slot_has_reference) {
3446 gpointer field = start [slot];
3448 if ((field != NULL) && mono_object_is_alive (field)) {
3449 reported_references ++;
3450 WRITE_HEAP_SHOT_JOB_VALUE (job, field);
3455 return reported_references;
3459 profiler_heap_report_object_reachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
3460 if (profiler->action_flags.heap_shot && (job != NULL)) {
3461 MonoClass *klass = mono_object_get_class (obj);
3462 int reference_counter = 0;
3463 gpointer *reference_counter_location;
3465 WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, obj, HEAP_CODE_OBJECT);
3466 #if DEBUG_HEAP_PROFILER
3467 printf ("profiler_heap_report_object_reachable: reported object %p at cursor %p\n", obj, (job->cursor - 1));
3469 WRITE_HEAP_SHOT_JOB_VALUE (job, NULL);
3470 reference_counter_location = job->cursor - 1;
3472 if (mono_class_get_rank (klass)) {
3473 MonoArray *array = (MonoArray *) obj;
3474 MonoClass *element_class = mono_class_get_element_class (klass);
3475 ClassIdMappingElement *element_id = class_id_mapping_element_get (element_class);
3477 g_assert (element_id != NULL);
3478 if (element_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
3479 class_id_mapping_element_build_layout_bitmap (element_class, element_id);
3481 if (! mono_class_is_valuetype (element_class)) {
3482 int length = mono_array_length (array);
3484 for (i = 0; i < length; i++) {
3485 MonoObject *array_element = mono_array_get (array, MonoObject*, i);
3486 if ((array_element != NULL) && mono_object_is_alive (array_element)) {
3487 reference_counter ++;
3488 WRITE_HEAP_SHOT_JOB_VALUE (job, array_element);
3491 } else if (element_id->data.layout.references > 0) {
3492 int length = mono_array_length (array);
3493 int array_element_size = mono_array_element_size (klass);
3495 for (i = 0; i < length; i++) {
3496 gpointer array_element_address = mono_array_addr_with_size (array, array_element_size, i);
3497 reference_counter += report_object_references (array_element_address, element_id, job);
3501 ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
3502 if (class_id == NULL) {
3503 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));
3505 g_assert (class_id != NULL);
3506 if (class_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
3507 class_id_mapping_element_build_layout_bitmap (klass, class_id);
3509 if (class_id->data.layout.references > 0) {
3510 reference_counter += report_object_references ((gpointer)(((char*)obj) + sizeof (MonoObject)), class_id, job);
3514 *reference_counter_location = GINT_TO_POINTER (reference_counter);
3515 #if DEBUG_HEAP_PROFILER
3516 printf ("profiler_heap_report_object_reachable: updated reference_counter_location %p with value %d\n", reference_counter_location, reference_counter);
3521 profiler_heap_report_object_unreachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
3523 MonoClass *klass = mono_object_get_class (obj);
3524 guint32 size = mono_object_get_size (obj);
3526 #if DEBUG_HEAP_PROFILER
3527 printf ("profiler_heap_report_object_unreachable: at job %p writing klass %p\n", job, klass);
3529 WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, klass, HEAP_CODE_FREE_OBJECT_CLASS);
3531 #if DEBUG_HEAP_PROFILER
3532 printf ("profiler_heap_report_object_unreachable: at job %p writing size %p\n", job, GUINT_TO_POINTER (size));
3534 WRITE_HEAP_SHOT_JOB_VALUE (job, GUINT_TO_POINTER (size));
3539 profiler_heap_add_object (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject *obj) {
3540 if (heap->first_free_slot >= heap->current->end_slot) {
3541 if (heap->current->next != NULL) {
3542 heap->current = heap->current->next;
3544 ProfilerHeapShotHeapBuffer *buffer = g_new (ProfilerHeapShotHeapBuffer, 1);
3545 buffer->previous = heap->last;
3546 buffer->next = NULL;
3547 buffer->start_slot = &(buffer->buffer [0]);
3548 buffer->end_slot = &(buffer->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
3549 heap->current = buffer;
3550 heap->last->next = buffer;
3551 heap->last = buffer;
3553 heap->first_free_slot = &(heap->current->buffer [0]);
3556 *(heap->first_free_slot) = obj;
3557 heap->first_free_slot ++;
3558 profiler_heap_report_object_reachable (job, obj);
3562 profiler_heap_pop_object_from_end (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject** current_slot) {
3563 while (heap->first_free_slot != current_slot) {
3566 if (heap->first_free_slot > heap->current->start_slot) {
3567 heap->first_free_slot --;
3569 heap->current = heap->current->previous;
3570 g_assert (heap->current != NULL);
3571 heap->first_free_slot = heap->current->end_slot - 1;
3574 obj = *(heap->first_free_slot);
3576 if (mono_object_is_alive (obj)) {
3577 profiler_heap_report_object_reachable (job, obj);
3580 profiler_heap_report_object_unreachable (job, obj);
3587 profiler_heap_scan (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job) {
3588 ProfilerHeapShotHeapBuffer *current_buffer = heap->buffers;
3589 MonoObject** current_slot = current_buffer->start_slot;
3591 while (current_slot != heap->first_free_slot) {
3592 MonoObject *obj = *current_slot;
3593 if (mono_object_is_alive (obj)) {
3594 profiler_heap_report_object_reachable (job, obj);
3596 profiler_heap_report_object_unreachable (job, obj);
3597 *current_slot = profiler_heap_pop_object_from_end (heap, job, current_slot);
3600 if (*current_slot != NULL) {
3603 if (current_slot == current_buffer->end_slot) {
3604 current_buffer = current_buffer->next;
3605 g_assert (current_buffer != NULL);
3606 current_slot = current_buffer->start_slot;
3613 handle_heap_profiling (MonoProfiler *profiler, MonoGCEvent ev) {
3614 static gboolean create_heap_shot_write_job;
3617 case MONO_GC_EVENT_PRE_STOP_WORLD:
3618 // Get the lock, so we are sure nobody is flushing events during the collection,
3619 // and we can update all mappings (building the class descriptors).
3622 case MONO_GC_EVENT_POST_STOP_WORLD:
3623 create_heap_shot_write_job = dump_current_heap_snapshot ();
3624 if (create_heap_shot_write_job) {
3625 ProfilerPerThreadData *data;
3626 // Update all mappings, so that we have built all the class descriptors.
3627 flush_all_mappings ();
3628 // Also write all event buffers, so that allocations are recorded.
3629 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3630 write_thread_data_block (data);
3636 case MONO_GC_EVENT_MARK_END: {
3637 ProfilerHeapShotWriteJob *job;
3638 ProfilerPerThreadData *data;
3640 if (create_heap_shot_write_job) {
3641 job = profiler_heap_shot_write_job_new (profiler->heap_shot_was_signalled, profiler->garbage_collection_counter);
3642 profiler->heap_shot_was_signalled = FALSE;
3643 MONO_PROFILER_GET_CURRENT_COUNTER (job->start_counter);
3644 MONO_PROFILER_GET_CURRENT_TIME (job->start_time);
3649 profiler_heap_scan (&(profiler->heap), job);
3651 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3652 ProfilerHeapShotObjectBuffer *buffer;
3653 for (buffer = data->heap_shot_object_buffers; buffer != NULL; buffer = buffer->next) {
3654 MonoObject **cursor;
3655 for (cursor = buffer->first_unprocessed_slot; cursor < buffer->next_free_slot; cursor ++) {
3656 MonoObject *obj = *cursor;
3657 #if DEBUG_HEAP_PROFILER
3658 printf ("gc_event: in object buffer %p(%p-%p) cursor at %p has object %p ", buffer, &(buffer->buffer [0]), buffer->end, cursor, obj);
3660 if (mono_object_is_alive (obj)) {
3661 #if DEBUG_HEAP_PROFILER
3662 printf ("(object is alive, adding to heap)\n");
3664 profiler_heap_add_object (&(profiler->heap), job, obj);
3666 #if DEBUG_HEAP_PROFILER
3667 printf ("(object is unreachable, reporting in job)\n");
3669 profiler_heap_report_object_unreachable (job, obj);
3672 buffer->first_unprocessed_slot = cursor;
3677 MONO_PROFILER_GET_CURRENT_COUNTER (job->end_counter);
3678 MONO_PROFILER_GET_CURRENT_TIME (job->end_time);
3680 profiler_add_heap_shot_write_job (job);
3681 profiler_free_heap_shot_write_jobs ();
3682 WRITER_EVENT_RAISE ();
3692 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
3693 gboolean do_heap_profiling = profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot;
3694 guint32 event_value;
3696 if (ev == MONO_GC_EVENT_START) {
3697 profiler->garbage_collection_counter ++;
3700 event_value = (profiler->garbage_collection_counter << 8) | generation;
3702 if (do_heap_profiling && (ev == MONO_GC_EVENT_POST_STOP_WORLD)) {
3703 handle_heap_profiling (profiler, ev);
3705 STORE_EVENT_NUMBER_COUNTER (profiler, event_value, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, gc_event_code_from_profiler_event (ev), gc_event_kind_from_profiler_event (ev));
3706 if (do_heap_profiling && (ev != MONO_GC_EVENT_POST_STOP_WORLD)) {
3707 handle_heap_profiling (profiler, ev);
3712 gc_resize (MonoProfiler *profiler, gint64 new_size) {
3713 profiler->garbage_collection_counter ++;
3714 STORE_EVENT_NUMBER_VALUE (profiler, new_size, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_GC_RESIZE, 0, profiler->garbage_collection_counter);
3717 /* called at the end of the program */
3719 profiler_shutdown (MonoProfiler *prof)
3721 ProfilerPerThreadData* current_thread_data;
3723 LOG_WRITER_THREAD ("profiler_shutdown: zeroing relevant flags");
3724 mono_profiler_set_events (0);
3725 //profiler->flags = 0;
3726 //profiler->action_flags.unreachable_objects = FALSE;
3727 //profiler->action_flags.heap_shot = FALSE;
3729 LOG_WRITER_THREAD ("profiler_shutdown: asking stats thread to exit");
3730 profiler->terminate_writer_thread = TRUE;
3731 WRITER_EVENT_RAISE ();
3732 LOG_WRITER_THREAD ("profiler_shutdown: waiting for stats thread to exit");
3733 WAIT_WRITER_THREAD ();
3734 LOG_WRITER_THREAD ("profiler_shutdown: stats thread should be dead now");
3735 WRITER_EVENT_DESTROY ();
3739 MONO_PROFILER_GET_CURRENT_TIME (profiler->end_time);
3740 MONO_PROFILER_GET_CURRENT_COUNTER (profiler->end_counter);
3742 flush_everything ();
3747 g_free (profiler->file_name);
3748 if (profiler->file_name_suffix != NULL) {
3749 g_free (profiler->file_name_suffix);
3752 method_id_mapping_destroy (profiler->methods);
3753 class_id_mapping_destroy (profiler->classes);
3754 g_hash_table_destroy (profiler->loaded_assemblies);
3755 g_hash_table_destroy (profiler->loaded_modules);
3756 g_hash_table_destroy (profiler->loaded_appdomains);
3758 FREE_PROFILER_THREAD_DATA ();
3760 for (current_thread_data = profiler->per_thread_data; current_thread_data != NULL; current_thread_data = current_thread_data->next) {
3761 profiler_per_thread_data_destroy (current_thread_data);
3763 if (profiler->statistical_data != NULL) {
3764 profiler_statistical_data_destroy (profiler->statistical_data);
3766 if (profiler->statistical_data_ready != NULL) {
3767 profiler_statistical_data_destroy (profiler->statistical_data_ready);
3769 if (profiler->statistical_data_second_buffer != NULL) {
3770 profiler_statistical_data_destroy (profiler->statistical_data_second_buffer);
3772 if (profiler->executable_regions != NULL) {
3773 profiler_executable_memory_regions_destroy (profiler->executable_regions);
3776 profiler_heap_buffers_free (&(profiler->heap));
3777 if (profiler->heap_shot_command_file_name != NULL) {
3778 g_free (profiler->heap_shot_command_file_name);
3781 profiler_free_write_buffers ();
3782 profiler_destroy_heap_shot_write_jobs ();
3784 DELETE_PROFILER_MUTEX ();
3787 if (profiler->action_flags.oprofile) {
3796 #define DEFAULT_ARGUMENTS "s"
3798 setup_user_options (const char *arguments) {
3799 gchar **arguments_array, **current_argument;
3800 #ifndef PLATFORM_WIN32
3801 int gc_request_signal_number = 0;
3803 detect_fast_timer ();
3805 profiler->file_name = NULL;
3806 profiler->file_name_suffix = NULL;
3807 profiler->per_thread_buffer_size = 10000;
3808 profiler->statistical_buffer_size = 10000;
3809 profiler->write_buffer_size = 1024;
3810 profiler->heap_shot_command_file_name = NULL;
3811 profiler->dump_next_heap_snapshots = 0;
3812 profiler->heap_shot_command_file_access_time = 0;
3813 profiler->heap_shot_was_signalled = FALSE;
3814 profiler->flags = MONO_PROFILE_APPDOMAIN_EVENTS|
3815 MONO_PROFILE_ASSEMBLY_EVENTS|
3816 MONO_PROFILE_MODULE_EVENTS|
3817 MONO_PROFILE_CLASS_EVENTS|
3818 MONO_PROFILE_METHOD_EVENTS;
3820 if (arguments == NULL) {
3821 arguments = DEFAULT_ARGUMENTS;
3822 } else if (strstr (arguments, ":")) {
3823 arguments = strstr (arguments, ":") + 1;
3824 if (arguments [0] == 0) {
3825 arguments = DEFAULT_ARGUMENTS;
3829 arguments_array = g_strsplit (arguments, ",", -1);
3831 for (current_argument = arguments_array; ((current_argument != NULL) && (current_argument [0] != 0)); current_argument ++) {
3832 char *argument = *current_argument;
3833 char *equals = strstr (argument, "=");
3835 if (equals != NULL) {
3836 int equals_position = equals - argument;
3838 if (! (strncmp (argument, "per-thread-buffer-size", equals_position) && strncmp (argument, "tbs", equals_position))) {
3839 int value = atoi (equals + 1);
3841 profiler->per_thread_buffer_size = value;
3843 } else if (! (strncmp (argument, "statistical-thread-buffer-size", equals_position) && strncmp (argument, "sbs", equals_position))) {
3844 int value = atoi (equals + 1);
3846 profiler->statistical_buffer_size = value;
3848 } else if (! (strncmp (argument, "write-buffer-size", equals_position) && strncmp (argument, "wbs", equals_position))) {
3849 int value = atoi (equals + 1);
3851 profiler->write_buffer_size = value;
3853 } else if (! (strncmp (argument, "output", equals_position) && strncmp (argument, "out", equals_position) && strncmp (argument, "o", equals_position) && strncmp (argument, "O", equals_position))) {
3854 if (strlen (equals + 1) > 0) {
3855 profiler->file_name = g_strdup (equals + 1);
3857 } else if (! (strncmp (argument, "output-suffix", equals_position) && strncmp (argument, "suffix", equals_position) && strncmp (argument, "os", equals_position) && strncmp (argument, "OS", equals_position))) {
3858 if (strlen (equals + 1) > 0) {
3859 profiler->file_name_suffix = g_strdup (equals + 1);
3861 } else if (! (strncmp (argument, "gc-commands", equals_position) && strncmp (argument, "gc-c", equals_position) && strncmp (argument, "gcc", equals_position))) {
3862 if (strlen (equals + 1) > 0) {
3863 profiler->heap_shot_command_file_name = g_strdup (equals + 1);
3865 } else if (! (strncmp (argument, "gc-dumps", equals_position) && strncmp (argument, "gc-d", equals_position) && strncmp (argument, "gcd", equals_position))) {
3866 if (strlen (equals + 1) > 0) {
3867 profiler->dump_next_heap_snapshots = atoi (equals + 1);
3869 #ifndef PLATFORM_WIN32
3870 } else if (! (strncmp (argument, "gc-signal", equals_position) && strncmp (argument, "gc-s", equals_position) && strncmp (argument, "gcs", equals_position))) {
3871 if (strlen (equals + 1) > 0) {
3872 char *signal_name = equals + 1;
3873 if (! strcasecmp (signal_name, "SIGUSR1")) {
3874 gc_request_signal_number = SIGUSR1;
3875 } else if (! strcasecmp (signal_name, "SIGUSR2")) {
3876 gc_request_signal_number = SIGUSR2;
3877 } else if (! strcasecmp (signal_name, "SIGPROF")) {
3878 gc_request_signal_number = SIGPROF;
3880 gc_request_signal_number = atoi (signal_name);
3885 g_warning ("Cannot parse valued argument %s\n", argument);
3888 if (! (strcmp (argument, "jit") && strcmp (argument, "j"))) {
3889 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
3890 profiler->action_flags.jit_time = TRUE;
3891 } else if (! (strcmp (argument, "allocations") && strcmp (argument, "alloc") && strcmp (argument, "a"))) {
3892 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
3893 } else if (! (strcmp (argument, "gc") && strcmp (argument, "g"))) {
3894 profiler->flags |= MONO_PROFILE_GC;
3895 } else if (! (strcmp (argument, "heap-shot") && strcmp (argument, "heap") && strcmp (argument, "h"))) {
3896 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
3897 profiler->action_flags.unreachable_objects = TRUE;
3898 profiler->action_flags.heap_shot = TRUE;
3899 } else if (! (strcmp (argument, "unreachable") && strcmp (argument, "free") && strcmp (argument, "f"))) {
3900 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
3901 profiler->action_flags.unreachable_objects = TRUE;
3902 } else if (! (strcmp (argument, "threads") && strcmp (argument, "t"))) {
3903 profiler->flags |= MONO_PROFILE_THREADS;
3904 } else if (! (strcmp (argument, "enter-leave") && strcmp (argument, "calls") && strcmp (argument, "c"))) {
3905 profiler->flags |= MONO_PROFILE_ENTER_LEAVE;
3906 } else if (! (strcmp (argument, "statistical") && strcmp (argument, "stat") && strcmp (argument, "s"))) {
3907 profiler->flags |= MONO_PROFILE_STATISTICAL|MONO_PROFILE_JIT_COMPILATION;
3908 profiler->action_flags.jit_time = TRUE;
3909 } else if (! (strcmp (argument, "force-accurate-timer") && strcmp (argument, "fac"))) {
3910 use_fast_timer = FALSE;
3912 } else if (! (strcmp (argument, "oprofile") && strcmp (argument, "oprof"))) {
3913 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
3914 profiler->action_flags.oprofile = TRUE;
3915 if (op_open_agent ()) {
3916 g_warning ("Problem calling op_open_agent\n");
3919 } else if (strcmp (argument, "logging")) {
3920 g_warning ("Cannot parse flag argument %s\n", argument);
3925 g_free (arguments_array);
3927 #ifndef PLATFORM_WIN32
3928 if (gc_request_signal_number != 0) {
3929 if (((gc_request_signal_number == SIGPROF) && ! (profiler->flags & MONO_PROFILE_STATISTICAL)) ||
3930 (gc_request_signal_number == SIGUSR1) ||
3931 (gc_request_signal_number == SIGUSR2)) {
3932 add_gc_request_handler (gc_request_signal_number);
3934 g_error ("Cannot use signal %d", gc_request_signal_number);
3939 if (profiler->file_name == NULL) {
3940 char *program_name = g_get_prgname ();
3942 if (program_name != NULL) {
3943 char *name_buffer = g_strdup (program_name);
3944 char *name_start = name_buffer;
3947 /* Jump over the last '/' */
3948 cursor = strrchr (name_buffer, '/');
3949 if (cursor == NULL) {
3950 cursor = name_buffer;
3954 name_start = cursor;
3956 /* Then jump over the last '\\' */
3957 cursor = strrchr (name_start, '\\');
3958 if (cursor == NULL) {
3959 cursor = name_start;
3963 name_start = cursor;
3965 /* Finally, find the last '.' */
3966 cursor = strrchr (name_start, '.');
3967 if (cursor != NULL) {
3971 if (profiler->file_name_suffix == NULL) {
3972 profiler->file_name = g_strdup_printf ("%s.mprof", name_start);
3974 profiler->file_name = g_strdup_printf ("%s-%s.mprof", name_start, profiler->file_name_suffix);
3976 g_free (name_buffer);
3978 profiler->file_name = g_strdup_printf ("%s.mprof", "profiler-log");
3984 thread_detach_callback (MonoThread *thread) {
3985 LOG_WRITER_THREAD ("thread_detach_callback: asking writer thread to detach");
3986 profiler->detach_writer_thread = TRUE;
3987 WRITER_EVENT_RAISE ();
3988 LOG_WRITER_THREAD ("thread_detach_callback: done");
3993 data_writer_thread (gpointer nothing) {
3994 static gboolean thread_attached = FALSE;
3995 static gboolean thread_detached = FALSE;
3996 static MonoThread *this_thread = NULL;
3999 ProfilerStatisticalData *statistical_data;
4002 LOG_WRITER_THREAD ("data_writer_thread: going to sleep");
4003 WRITER_EVENT_WAIT ();
4004 LOG_WRITER_THREAD ("data_writer_thread: just woke up");
4006 if (! thread_attached) {
4007 if (! profiler->terminate_writer_thread) {
4008 MonoDomain * root_domain = mono_get_root_domain ();
4009 if (root_domain != NULL) {
4010 LOG_WRITER_THREAD ("data_writer_thread: attaching thread");
4011 this_thread = mono_thread_attach (root_domain);
4012 mono_thread_set_manage_callback (this_thread, thread_detach_callback);
4013 thread_attached = TRUE;
4015 g_error ("Cannot get root domain\n");
4018 /* Execution was too short, pretend we attached and detached. */
4019 thread_attached = TRUE;
4020 thread_detached = TRUE;
4024 if (profiler->heap_shot_was_signalled) {
4025 LOG_WRITER_THREAD ("data_writer_thread: starting requested collection");
4026 mono_gc_collect (mono_gc_max_generation ());
4027 LOG_WRITER_THREAD ("data_writer_thread: requested collection done");
4030 statistical_data = profiler->statistical_data_ready;
4031 done = (statistical_data == NULL) && (profiler->heap_shot_write_jobs == NULL);
4034 LOG_WRITER_THREAD ("data_writer_thread: acquiring lock and writing data");
4037 // This makes sure that all method ids are in place
4038 LOG_WRITER_THREAD ("data_writer_thread: writing mapping...");
4039 flush_all_mappings ();
4040 LOG_WRITER_THREAD ("data_writer_thread: wrote mapping");
4042 if ((statistical_data != NULL) && ! thread_detached) {
4043 LOG_WRITER_THREAD ("data_writer_thread: writing statistical data...");
4044 profiler->statistical_data_ready = NULL;
4045 write_statistical_data_block (statistical_data);
4046 statistical_data->next_free_index = 0;
4047 statistical_data->first_unwritten_index = 0;
4048 profiler->statistical_data_second_buffer = statistical_data;
4049 LOG_WRITER_THREAD ("data_writer_thread: wrote statistical data");
4052 profiler_process_heap_shot_write_jobs ();
4055 LOG_WRITER_THREAD ("data_writer_thread: wrote data and released lock");
4058 if (profiler->detach_writer_thread) {
4059 if (this_thread != NULL) {
4060 LOG_WRITER_THREAD ("data_writer_thread: detaching thread");
4061 mono_thread_detach (this_thread);
4063 profiler->detach_writer_thread = FALSE;
4064 thread_detached = TRUE;
4066 LOG_WRITER_THREAD ("data_writer_thread: warning: thread has already been detached");
4070 if (profiler->terminate_writer_thread) {
4071 LOG_WRITER_THREAD ("data_writer_thread: exiting thread");
4079 mono_profiler_startup (const char *desc);
4081 /* the entry point (mono_profiler_load?) */
4083 mono_profiler_startup (const char *desc)
4085 profiler = g_new0 (MonoProfiler, 1);
4087 setup_user_options ((desc != NULL) ? desc : "");
4089 INITIALIZE_PROFILER_MUTEX ();
4090 MONO_PROFILER_GET_CURRENT_TIME (profiler->start_time);
4091 MONO_PROFILER_GET_CURRENT_COUNTER (profiler->start_counter);
4092 profiler->last_header_counter = 0;
4094 profiler->methods = method_id_mapping_new ();
4095 profiler->classes = class_id_mapping_new ();
4096 profiler->loaded_assemblies = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
4097 profiler->loaded_modules = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
4098 profiler->loaded_appdomains = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
4100 profiler->statistical_data = profiler_statistical_data_new (profiler->statistical_buffer_size);
4101 profiler->statistical_data_second_buffer = profiler_statistical_data_new (profiler->statistical_buffer_size);
4103 profiler->write_buffers = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
4104 profiler->write_buffers->next = NULL;
4105 profiler->current_write_buffer = profiler->write_buffers;
4106 profiler->current_write_position = 0;
4107 profiler->full_write_buffers = 0;
4109 profiler->executable_regions = profiler_executable_memory_regions_new (1, 1);
4111 profiler->executable_files.table = g_hash_table_new (g_str_hash, g_str_equal);
4112 profiler->executable_files.new_files = NULL;
4114 profiler->heap_shot_write_jobs = NULL;
4115 if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
4116 profiler_heap_buffers_setup (&(profiler->heap));
4118 profiler_heap_buffers_clear (&(profiler->heap));
4120 profiler->garbage_collection_counter = 0;
4122 WRITER_EVENT_INIT ();
4123 LOG_WRITER_THREAD ("mono_profiler_startup: creating writer thread");
4124 CREATE_WRITER_THREAD (data_writer_thread);
4125 LOG_WRITER_THREAD ("mono_profiler_startup: created writer thread");
4127 ALLOCATE_PROFILER_THREAD_DATA ();
4131 write_intro_block ();
4133 mono_profiler_install (profiler, profiler_shutdown);
4135 mono_profiler_install_appdomain (appdomain_start_load, appdomain_end_load,
4136 appdomain_start_unload, appdomain_end_unload);
4137 mono_profiler_install_assembly (assembly_start_load, assembly_end_load,
4138 assembly_start_unload, assembly_end_unload);
4139 mono_profiler_install_module (module_start_load, module_end_load,
4140 module_start_unload, module_end_unload);
4141 mono_profiler_install_class (class_start_load, class_end_load,
4142 class_start_unload, class_end_unload);
4143 mono_profiler_install_jit_compile (method_start_jit, method_end_jit);
4144 mono_profiler_install_enter_leave (method_enter, method_leave);
4145 mono_profiler_install_method_free (method_free);
4146 mono_profiler_install_thread (thread_start, thread_end);
4147 mono_profiler_install_allocation (object_allocated);
4148 mono_profiler_install_statistical (statistical_hit);
4149 mono_profiler_install_gc (gc_event, gc_resize);
4151 mono_profiler_install_jit_end (method_jit_result);
4154 mono_profiler_set_events (profiler->flags);