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__)) && ! defined(PLATFORM_WIN32)
104 #if defined(__i386__)
105 static const guchar cpuid_impl [] = {
106 0x55, /* push %ebp */
107 0x89, 0xe5, /* mov %esp,%ebp */
108 0x53, /* push %ebx */
109 0x8b, 0x45, 0x08, /* mov 0x8(%ebp),%eax */
110 0x0f, 0xa2, /* cpuid */
111 0x50, /* push %eax */
112 0x8b, 0x45, 0x10, /* mov 0x10(%ebp),%eax */
113 0x89, 0x18, /* mov %ebx,(%eax) */
114 0x8b, 0x45, 0x14, /* mov 0x14(%ebp),%eax */
115 0x89, 0x08, /* mov %ecx,(%eax) */
116 0x8b, 0x45, 0x18, /* mov 0x18(%ebp),%eax */
117 0x89, 0x10, /* mov %edx,(%eax) */
119 0x8b, 0x55, 0x0c, /* mov 0xc(%ebp),%edx */
120 0x89, 0x02, /* mov %eax,(%edx) */
126 typedef void (*CpuidFunc) (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx);
129 cpuid (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx) {
132 __asm__ __volatile__ (
135 "movl %%eax, %%edx\n"
136 "xorl $0x200000, %%eax\n"
141 "xorl %%edx, %%eax\n"
142 "andl $0x200000, %%eax\n"
164 CpuidFunc func = (CpuidFunc) cpuid_impl;
165 func (id, p_eax, p_ebx, p_ecx, p_edx);
167 * We use this approach because of issues with gcc and pic code, see:
168 * http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gcc&pr=7329
169 __asm__ __volatile__ ("cpuid"
170 : "=a" (*p_eax), "=b" (*p_ebx), "=c" (*p_ecx), "=d" (*p_edx)
178 static void detect_fast_timer (void) {
179 int p_eax, p_ebx, p_ecx, p_edx;
181 if (cpuid (0x1, &p_eax, &p_ebx, &p_ecx, &p_edx)) {
183 use_fast_timer = TRUE;
185 use_fast_timer = FALSE;
188 use_fast_timer = FALSE;
193 #if defined(__x86_64__)
194 static void detect_fast_timer (void) {
196 guint32 eax,ebx,ecx,edx;
197 __asm__ __volatile__ ("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(op));
199 use_fast_timer = TRUE;
201 use_fast_timer = FALSE;
206 static __inline__ guint64 rdtsc(void) {
208 __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
209 return ((guint64) lo) | (((guint64) hi) << 32);
211 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) {\
212 if (use_fast_timer) {\
215 MONO_PROFILER_GET_CURRENT_TIME ((c));\
219 static detect_fast_timer (void) {
220 use_fast_timer = FALSE;
222 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) MONO_PROFILER_GET_CURRENT_TIME ((c))
226 #define CLASS_LAYOUT_PACKED_BITMAP_SIZE 64
227 #define CLASS_LAYOUT_NOT_INITIALIZED (0xFFFF)
230 HEAP_CODE_OBJECT = 1,
231 HEAP_CODE_FREE_OBJECT_CLASS = 2,
233 } HeapProfilerJobValueCode;
234 typedef struct _MonoProfilerClassData {
243 } MonoProfilerClassData;
245 typedef struct _MonoProfilerMethodData {
248 } MonoProfilerMethodData;
250 typedef struct _ClassIdMappingElement {
254 struct _ClassIdMappingElement *next_unwritten;
255 MonoProfilerClassData data;
256 } ClassIdMappingElement;
258 typedef struct _MethodIdMappingElement {
262 struct _MethodIdMappingElement *next_unwritten;
263 MonoProfilerMethodData data;
264 } MethodIdMappingElement;
266 typedef struct _ClassIdMapping {
268 ClassIdMappingElement *unwritten;
272 typedef struct _MethodIdMapping {
274 MethodIdMappingElement *unwritten;
278 typedef struct _LoadedElement {
280 guint64 load_start_counter;
281 guint64 load_end_counter;
282 guint64 unload_start_counter;
283 guint64 unload_end_counter;
287 guint8 unload_written;
290 #define PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE 1024
291 #define PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE 4096
292 #define PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE 4096
294 typedef struct _ProfilerHeapShotObjectBuffer {
295 struct _ProfilerHeapShotObjectBuffer *next;
296 MonoObject **next_free_slot;
298 MonoObject **first_unprocessed_slot;
299 MonoObject *buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE];
300 } ProfilerHeapShotObjectBuffer;
302 typedef struct _ProfilerHeapShotHeapBuffer {
303 struct _ProfilerHeapShotHeapBuffer *next;
304 struct _ProfilerHeapShotHeapBuffer *previous;
305 MonoObject **start_slot;
306 MonoObject **end_slot;
307 MonoObject *buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE];
308 } ProfilerHeapShotHeapBuffer;
310 typedef struct _ProfilerHeapShotHeapBuffers {
311 ProfilerHeapShotHeapBuffer *buffers;
312 ProfilerHeapShotHeapBuffer *last;
313 ProfilerHeapShotHeapBuffer *current;
314 MonoObject **first_free_slot;
315 } ProfilerHeapShotHeapBuffers;
318 typedef struct _ProfilerHeapShotWriteBuffer {
319 struct _ProfilerHeapShotWriteBuffer *next;
320 gpointer buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE];
321 } ProfilerHeapShotWriteBuffer;
323 typedef struct _ProfilerHeapShotWriteJob {
324 struct _ProfilerHeapShotWriteJob *next;
325 struct _ProfilerHeapShotWriteJob *next_unwritten;
329 ProfilerHeapShotWriteBuffer *buffers;
330 ProfilerHeapShotWriteBuffer **last_next;
331 guint32 full_buffers;
332 gboolean heap_shot_was_signalled;
333 guint64 start_counter;
338 } ProfilerHeapShotWriteJob;
340 typedef struct _ProfilerPerThreadData {
341 ProfilerEventData *events;
342 ProfilerEventData *next_free_event;
343 ProfilerEventData *end_event;
344 ProfilerEventData *first_unwritten_event;
345 ProfilerEventData *first_unmapped_event;
346 guint64 start_event_counter;
347 guint64 last_event_counter;
349 ProfilerHeapShotObjectBuffer *heap_shot_object_buffers;
350 struct _ProfilerPerThreadData* next;
351 } ProfilerPerThreadData;
353 typedef struct _ProfilerStatisticalData {
357 int first_unwritten_index;
358 } ProfilerStatisticalData;
360 typedef struct _ProfilerUnmanagedSymbol {
365 } ProfilerUnmanagedSymbol;
367 struct _ProfilerExecutableFile;
369 typedef struct _ProfilerExecutableMemoryRegionData {
377 struct _ProfilerExecutableFile *file;
378 guint32 symbols_count;
379 guint32 symbols_capacity;
380 ProfilerUnmanagedSymbol *symbols;
381 } ProfilerExecutableMemoryRegionData;
383 typedef struct _ProfilerExecutableMemoryRegions {
384 ProfilerExecutableMemoryRegionData **regions;
385 guint32 regions_capacity;
386 guint32 regions_count;
388 guint32 next_unmanaged_function_id;
389 } ProfilerExecutableMemoryRegions;
391 /* Start of ELF definitions */
393 typedef guint16 ElfHalf;
394 typedef guint32 ElfWord;
395 typedef gsize ElfAddr;
396 typedef gsize ElfOff;
399 unsigned char e_ident[EI_NIDENT];
405 ElfOff e_shoff; // Section header table
407 ElfHalf e_ehsize; // Header size
410 ElfHalf e_shentsize; // Section header entry size
411 ElfHalf e_shnum; // Section header entries number
412 ElfHalf e_shstrndx; // String table index
415 #if (SIZEOF_VOID_P == 4)
420 ElfAddr sh_addr; // Address in memory
421 ElfOff sh_offset; // Offset in file
425 ElfWord sh_addralign;
432 unsigned char st_info; // Use ELF32_ST_TYPE to get symbol type
433 unsigned char st_other;
434 ElfHalf st_shndx; // Or one of SHN_ABS, SHN_COMMON or SHN_UNDEF.
436 #elif (SIZEOF_VOID_P == 8)
441 ElfAddr sh_addr; // Address in memory
442 ElfOff sh_offset; // Offset in file
451 unsigned char st_info; // Use ELF_ST_TYPE to get symbol type
452 unsigned char st_other;
453 ElfHalf st_shndx; // Or one of SHN_ABS, SHN_COMMON or SHN_UNDEF.
458 #error Bad size of void pointer
462 #define ELF_ST_BIND(i) ((i)>>4)
463 #define ELF_ST_TYPE(i) ((i)&0xf)
476 ELF_FILE_TYPE_NONE = 0,
477 ELF_FILE_TYPE_REL = 1,
478 ELF_FILE_TYPE_EXEC = 2,
479 ELF_FILE_TYPE_DYN = 3,
480 ELF_FILE_TYPE_CORE = 4
497 ELF_SHT_PROGBITS = 1,
521 ELF_SHF_EXECINSTR = 4,
524 #define ELF_SHN_UNDEF 0
525 #define ELF_SHN_LORESERVE 0xff00
526 #define ELF_SHN_LOPROC 0xff00
527 #define ELF_SHN_HIPROC 0xff1f
528 #define ELF_SHN_ABS 0xfff1
529 #define ELF_SHN_COMMON 0xfff2
530 #define ELF_SHN_HIRESERVE 0xffff
531 /* End of ELF definitions */
533 typedef struct _ProfilerExecutableFileSectionRegion {
534 ProfilerExecutableMemoryRegionData *region;
535 guint8 *section_address;
536 gsize section_offset;
537 } ProfilerExecutableFileSectionRegion;
539 typedef struct _ProfilerExecutableFile {
540 guint32 reference_count;
542 /* Used for mmap and munmap */
549 guint8 *symbols_start;
550 guint32 symbols_count;
552 const char *symbols_string_table;
553 const char *main_string_table;
555 ProfilerExecutableFileSectionRegion *section_regions;
557 struct _ProfilerExecutableFile *next_new_file;
558 } ProfilerExecutableFile;
560 typedef struct _ProfilerExecutableFiles {
562 ProfilerExecutableFile *new_files;
563 } ProfilerExecutableFiles;
566 #ifndef PLATFORM_WIN32
567 #include <sys/types.h>
568 #include <sys/time.h>
569 #include <sys/stat.h>
573 #include <semaphore.h>
575 #include <sys/mman.h>
576 #include <sys/types.h>
577 #include <sys/stat.h>
581 #define MUTEX_TYPE pthread_mutex_t
582 #define INITIALIZE_PROFILER_MUTEX() pthread_mutex_init (&(profiler->mutex), NULL)
583 #define DELETE_PROFILER_MUTEX() pthread_mutex_destroy (&(profiler->mutex))
584 #define LOCK_PROFILER() do {/*LOG_WRITER_THREAD ("LOCK_PROFILER");*/ pthread_mutex_lock (&(profiler->mutex));} while (0)
585 #define UNLOCK_PROFILER() do {/*LOG_WRITER_THREAD ("UNLOCK_PROFILER");*/ pthread_mutex_unlock (&(profiler->mutex));} while (0)
587 #define THREAD_TYPE pthread_t
588 #define CREATE_WRITER_THREAD(f) pthread_create (&(profiler->data_writer_thread), NULL, ((void*(*)(void*))f), NULL)
589 #define EXIT_THREAD() pthread_exit (NULL);
590 #define WAIT_WRITER_THREAD() pthread_join (profiler->data_writer_thread, NULL)
591 #define CURRENT_THREAD_ID() (gsize) pthread_self ()
593 #ifndef HAVE_KW_THREAD
594 static pthread_key_t pthread_profiler_key;
595 static pthread_once_t profiler_pthread_once = PTHREAD_ONCE_INIT;
597 make_pthread_profiler_key (void) {
598 (void) pthread_key_create (&pthread_profiler_key, NULL);
600 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) pthread_getspecific (pthread_profiler_key))
601 #define SET_PROFILER_THREAD_DATA(x) (void) pthread_setspecific (pthread_profiler_key, (x))
602 #define ALLOCATE_PROFILER_THREAD_DATA() (void) pthread_once (&profiler_pthread_once, make_pthread_profiler_key)
603 #define FREE_PROFILER_THREAD_DATA() (void) pthread_key_delete (pthread_profiler_key)
606 #define EVENT_TYPE sem_t
607 #define WRITER_EVENT_INIT() (void) sem_init (&(profiler->statistical_data_writer_event), 0, 0)
608 #define WRITER_EVENT_DESTROY() (void) sem_destroy (&(profiler->statistical_data_writer_event))
609 #define WRITER_EVENT_WAIT() (void) sem_wait (&(profiler->statistical_data_writer_event))
610 #define WRITER_EVENT_RAISE() (void) sem_post (&(profiler->statistical_data_writer_event))
613 #define FILE_HANDLE_TYPE FILE*
614 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
615 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
616 #define FLUSH_FILE() fflush (profiler->file)
617 #define CLOSE_FILE() fclose (profiler->file);
619 #define FILE_HANDLE_TYPE int
620 #define OPEN_FILE() profiler->file = open (profiler->file_name, O_WRONLY|O_CREAT|O_TRUNC, 0664);
621 #define WRITE_BUFFER(b,s) write (profiler->file, (b), (s))
623 #define CLOSE_FILE() close (profiler->file);
630 #define MUTEX_TYPE CRITICAL_SECTION
631 #define INITIALIZE_PROFILER_MUTEX() InitializeCriticalSection (&(profiler->mutex))
632 #define DELETE_PROFILER_MUTEX() DeleteCriticalSection (&(profiler->mutex))
633 #define LOCK_PROFILER() EnterCriticalSection (&(profiler->mutex))
634 #define UNLOCK_PROFILER() LeaveCriticalSection (&(profiler->mutex))
636 #define THREAD_TYPE HANDLE
637 #define CREATE_WRITER_THREAD(f) CreateThread (NULL, (1*1024*1024), (f), NULL, 0, NULL);
638 #define EXIT_THREAD() ExitThread (0);
639 #define WAIT_WRITER_THREAD() WaitForSingleObject (profiler->data_writer_thread, INFINITE)
640 #define CURRENT_THREAD_ID() (gsize) GetCurrentThreadId ()
642 #ifndef HAVE_KW_THREAD
643 static guint32 profiler_thread_id = -1;
644 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*)TlsGetValue (profiler_thread_id))
645 #define SET_PROFILER_THREAD_DATA(x) TlsSetValue (profiler_thread_id, (x));
646 #define ALLOCATE_PROFILER_THREAD_DATA() profiler_thread_id = TlsAlloc ()
647 #define FREE_PROFILER_THREAD_DATA() TlsFree (profiler_thread_id)
650 #define EVENT_TYPE HANDLE
651 #define WRITER_EVENT_INIT() profiler->statistical_data_writer_event = CreateEvent (NULL, FALSE, FALSE, NULL)
652 #define WRITER_EVENT_DESTROY() CloseHandle (profiler->statistical_data_writer_event)
653 #define WRITER_EVENT_WAIT() WaitForSingleObject (profiler->statistical_data_writer_event, INFINITE)
654 #define WRITER_EVENT_RAISE() SetEvent (profiler->statistical_data_writer_event)
656 #define FILE_HANDLE_TYPE FILE*
657 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
658 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
659 #define FLUSH_FILE() fflush (profiler->file)
660 #define CLOSE_FILE() fclose (profiler->file);
664 #ifdef HAVE_KW_THREAD
665 static __thread ProfilerPerThreadData * tls_profiler_per_thread_data;
666 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) tls_profiler_per_thread_data)
667 #define SET_PROFILER_THREAD_DATA(x) tls_profiler_per_thread_data = (x)
668 #define ALLOCATE_PROFILER_THREAD_DATA() /* nop */
669 #define FREE_PROFILER_THREAD_DATA() /* nop */
672 #define GET_PROFILER_THREAD_DATA(data) do {\
673 ProfilerPerThreadData *_result = LOOKUP_PROFILER_THREAD_DATA ();\
675 _result = profiler_per_thread_data_new (profiler->per_thread_buffer_size);\
677 _result->next = profiler->per_thread_data;\
678 profiler->per_thread_data = _result;\
680 SET_PROFILER_THREAD_DATA (_result);\
685 #define PROFILER_FILE_WRITE_BUFFER_SIZE (profiler->write_buffer_size)
686 typedef struct _ProfilerFileWriteBuffer {
687 struct _ProfilerFileWriteBuffer *next;
689 } ProfilerFileWriteBuffer;
691 struct _MonoProfiler {
694 MonoProfileFlags flags;
696 char *file_name_suffix;
697 FILE_HANDLE_TYPE file;
700 guint64 start_counter;
704 guint64 last_header_counter;
706 MethodIdMapping *methods;
707 ClassIdMapping *classes;
709 GHashTable *loaded_assemblies;
710 GHashTable *loaded_modules;
711 GHashTable *loaded_appdomains;
713 guint32 per_thread_buffer_size;
714 guint32 statistical_buffer_size;
715 ProfilerPerThreadData* per_thread_data;
716 ProfilerStatisticalData *statistical_data;
717 ProfilerStatisticalData *statistical_data_ready;
718 ProfilerStatisticalData *statistical_data_second_buffer;
719 int statistical_call_chain_depth;
721 THREAD_TYPE data_writer_thread;
722 EVENT_TYPE statistical_data_writer_event;
723 gboolean terminate_writer_thread;
724 gboolean detach_writer_thread;
726 ProfilerFileWriteBuffer *write_buffers;
727 ProfilerFileWriteBuffer *current_write_buffer;
728 int write_buffer_size;
729 int current_write_position;
730 int full_write_buffers;
732 ProfilerHeapShotWriteJob *heap_shot_write_jobs;
733 ProfilerHeapShotHeapBuffers heap;
735 char *heap_shot_command_file_name;
736 int dump_next_heap_snapshots;
737 guint64 heap_shot_command_file_access_time;
738 gboolean heap_shot_was_signalled;
739 guint32 garbage_collection_counter;
741 ProfilerExecutableMemoryRegions *executable_regions;
742 ProfilerExecutableFiles executable_files;
749 gboolean unreachable_objects;
753 static MonoProfiler *profiler;
755 #ifndef PLATFORM_WIN32
758 #ifdef MONO_ARCH_USE_SIGACTION
759 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, siginfo_t *info, void *context)
760 #elif defined(__sparc__)
761 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, void *sigctx)
763 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy)
767 SIG_HANDLER_SIGNATURE (gc_request_handler) {
768 profiler->heap_shot_was_signalled = TRUE;
769 WRITER_EVENT_RAISE ();
773 add_gc_request_handler (int signal_number)
777 #ifdef MONO_ARCH_USE_SIGACTION
778 sa.sa_sigaction = gc_request_handler;
779 sigemptyset (&sa.sa_mask);
780 sa.sa_flags = SA_SIGINFO;
782 sa.sa_handler = gc_request_handler;
783 sigemptyset (&sa.sa_mask);
787 g_assert (sigaction (signal_number, &sa, NULL) != -1);
793 #define DEBUG_LOAD_EVENTS 0
794 #define DEBUG_MAPPING_EVENTS 0
795 #define DEBUG_LOGGING_PROFILER 0
796 #define DEBUG_HEAP_PROFILER 0
797 #define DEBUG_CLASS_BITMAPS 0
798 #define DEBUG_STATISTICAL_PROFILER 0
799 #define DEBUG_WRITER_THREAD 0
800 #if (DEBUG_LOGGING_PROFILER || DEBUG_STATISTICAL_PROFILER || DEBUG_HEAP_PROFILER || DEBUG_WRITER_THREAD)
801 #define LOG_WRITER_THREAD(m) printf ("WRITER-THREAD-LOG %s\n", m)
803 #define LOG_WRITER_THREAD(m)
806 #if DEBUG_LOGGING_PROFILER
807 static int event_counter = 0;
808 #define EVENT_MARK() printf ("[EVENT:%d]", ++ event_counter)
812 static ClassIdMappingElement*
813 class_id_mapping_element_get (MonoClass *klass) {
814 return g_hash_table_lookup (profiler->classes->table, (gconstpointer) klass);
817 static MethodIdMappingElement*
818 method_id_mapping_element_get (MonoMethod *method) {
819 return g_hash_table_lookup (profiler->methods->table, (gconstpointer) method);
822 #define BITS_TO_BYTES(v) do {\
828 static ClassIdMappingElement*
829 class_id_mapping_element_new (MonoClass *klass) {
830 ClassIdMappingElement *result = g_new (ClassIdMappingElement, 1);
832 result->name = g_strdup_printf ("%s.%s", mono_class_get_namespace (klass), mono_class_get_name (klass));
833 result->klass = klass;
834 result->next_unwritten = profiler->classes->unwritten;
835 profiler->classes->unwritten = result;
836 result->id = profiler->classes->next_id;
837 profiler->classes->next_id ++;
839 result->data.bitmap.compact = 0;
840 result->data.layout.slots = CLASS_LAYOUT_NOT_INITIALIZED;
841 result->data.layout.references = CLASS_LAYOUT_NOT_INITIALIZED;
843 g_hash_table_insert (profiler->classes->table, klass, result);
845 #if (DEBUG_MAPPING_EVENTS)
846 printf ("Created new CLASS mapping element \"%s\" (%p)[%d]\n", result->name, klass, result->id);
852 class_id_mapping_element_build_layout_bitmap (MonoClass *klass, ClassIdMappingElement *klass_id) {
853 MonoClass *parent_class = mono_class_get_parent (klass);
854 int number_of_reference_fields = 0;
855 int max_offset_of_reference_fields = 0;
856 ClassIdMappingElement *parent_id;
858 MonoClassField *field;
860 #if (DEBUG_CLASS_BITMAPS)
861 printf ("class_id_mapping_element_build_layout_bitmap: building layout for class %s.%s: ", mono_class_get_namespace (klass), mono_class_get_name (klass));
864 if (parent_class != NULL) {
865 parent_id = class_id_mapping_element_get (parent_class);
866 g_assert (parent_id != NULL);
868 if (parent_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
869 #if (DEBUG_CLASS_BITMAPS)
870 printf ("[recursively building bitmap for father class]\n");
872 class_id_mapping_element_build_layout_bitmap (parent_class, parent_id);
879 while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
880 MonoType* field_type = mono_field_get_type (field);
881 // For now, skip static fields
882 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
885 if (MONO_TYPE_IS_REFERENCE (field_type)) {
886 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
887 if (field_offset > max_offset_of_reference_fields) {
888 max_offset_of_reference_fields = field_offset;
890 number_of_reference_fields ++;
892 MonoClass *field_class = mono_class_from_mono_type (field_type);
893 if (field_class && mono_class_is_valuetype (field_class)) {
894 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
895 g_assert (field_id != NULL);
897 if (field_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
898 if (field_id != klass_id) {
899 #if (DEBUG_CLASS_BITMAPS)
900 printf ("[recursively building bitmap for field %s]\n", mono_field_get_name (field));
902 class_id_mapping_element_build_layout_bitmap (field_class, field_id);
904 #if (DEBUG_CLASS_BITMAPS)
905 printf ("[breaking recursive bitmap build for field %s]", mono_field_get_name (field));
908 klass_id->data.bitmap.compact = 0;
909 klass_id->data.layout.slots = 0;
910 klass_id->data.layout.references = 0;
914 if (field_id->data.layout.references > 0) {
915 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
916 int max_offset_reference_in_field = (field_id->data.layout.slots - 1) * sizeof (gpointer);
918 if ((field_offset + max_offset_reference_in_field) > max_offset_of_reference_fields) {
919 max_offset_of_reference_fields = field_offset + max_offset_reference_in_field;
922 number_of_reference_fields += field_id->data.layout.references;
928 #if (DEBUG_CLASS_BITMAPS)
929 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);
931 if ((number_of_reference_fields == 0) && ((parent_id == NULL) || (parent_id->data.layout.references == 0))) {
932 #if (DEBUG_CLASS_BITMAPS)
933 printf ("[no references at all]");
935 klass_id->data.bitmap.compact = 0;
936 klass_id->data.layout.slots = 0;
937 klass_id->data.layout.references = 0;
939 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
940 #if (DEBUG_CLASS_BITMAPS)
941 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);
943 klass_id->data.layout.slots = parent_id->data.layout.slots;
944 klass_id->data.layout.references = parent_id->data.layout.references;
946 #if (DEBUG_CLASS_BITMAPS)
947 printf ("[no references from parent]");
949 klass_id->data.layout.slots = 0;
950 klass_id->data.layout.references = 0;
953 if (number_of_reference_fields > 0) {
954 klass_id->data.layout.slots += ((max_offset_of_reference_fields / sizeof (gpointer)) + 1);
955 klass_id->data.layout.references += number_of_reference_fields;
956 #if (DEBUG_CLASS_BITMAPS)
957 printf ("[adding data, going to %d references in %d slots]", klass_id->data.layout.references, klass_id->data.layout.slots);
961 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
962 #if (DEBUG_CLASS_BITMAPS)
963 printf ("[zeroing bitmap]");
965 klass_id->data.bitmap.compact = 0;
966 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
967 #if (DEBUG_CLASS_BITMAPS)
968 printf ("[copying compact father bitmap]");
970 klass_id->data.bitmap.compact = parent_id->data.bitmap.compact;
973 int size_of_bitmap = klass_id->data.layout.slots;
974 BITS_TO_BYTES (size_of_bitmap);
975 #if (DEBUG_CLASS_BITMAPS)
976 printf ("[allocating %d bytes for bitmap]", size_of_bitmap);
978 klass_id->data.bitmap.extended = g_malloc0 (size_of_bitmap);
979 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
980 int size_of_father_bitmap = parent_id->data.layout.slots;
981 if (size_of_father_bitmap <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
983 #if (DEBUG_CLASS_BITMAPS)
984 printf ("[copying %d bits from father bitmap]", size_of_father_bitmap);
986 for (father_slot = 0; father_slot < size_of_father_bitmap; father_slot ++) {
987 if (parent_id->data.bitmap.compact & (((guint64)1) << father_slot)) {
988 klass_id->data.bitmap.extended [father_slot >> 3] |= (1 << (father_slot & 7));
992 BITS_TO_BYTES (size_of_father_bitmap);
993 #if (DEBUG_CLASS_BITMAPS)
994 printf ("[copying %d bytes from father bitmap]", size_of_father_bitmap);
996 memcpy (klass_id->data.bitmap.extended, parent_id->data.bitmap.extended, size_of_father_bitmap);
1002 #if (DEBUG_CLASS_BITMAPS)
1003 printf ("[starting filling iteration]\n");
1006 while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
1007 MonoType* field_type = mono_field_get_type (field);
1008 // For now, skip static fields
1009 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
1012 #if (DEBUG_CLASS_BITMAPS)
1013 printf ("[Working on field %s]", mono_field_get_name (field));
1015 if (MONO_TYPE_IS_REFERENCE (field_type)) {
1016 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
1018 g_assert ((field_offset % sizeof (gpointer)) == 0);
1019 field_slot = field_offset / sizeof (gpointer);
1020 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1021 klass_id->data.bitmap.compact |= (((guint64)1) << field_slot);
1023 klass_id->data.bitmap.extended [field_slot >> 3] |= (1 << (field_slot & 7));
1025 #if (DEBUG_CLASS_BITMAPS)
1026 printf ("[reference at offset %d, slot %d]", field_offset, field_slot);
1029 MonoClass *field_class = mono_class_from_mono_type (field_type);
1030 if (field_class && mono_class_is_valuetype (field_class)) {
1031 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
1035 g_assert (field_id != NULL);
1036 field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
1037 g_assert ((field_id->data.layout.references == 0) || ((field_offset % sizeof (gpointer)) == 0));
1038 field_slot = field_offset / sizeof (gpointer);
1039 #if (DEBUG_CLASS_BITMAPS)
1040 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);
1043 if (field_id->data.layout.references > 0) {
1045 if (field_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1046 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
1047 if (field_id->data.bitmap.compact & (((guint64)1) << sub_field_slot)) {
1048 int actual_slot = field_slot + sub_field_slot;
1049 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1050 klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
1052 klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
1057 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
1058 if (field_id->data.bitmap.extended [sub_field_slot >> 3] & (1 << (sub_field_slot & 7))) {
1059 int actual_slot = field_slot + sub_field_slot;
1060 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1061 klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
1063 klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
1072 #if (DEBUG_CLASS_BITMAPS)
1075 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);
1076 for (slot = 0; slot < klass_id->data.layout.slots; slot ++) {
1077 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1078 if (klass_id->data.bitmap.compact & (((guint64)1) << slot)) {
1084 if (klass_id->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
1098 static MethodIdMappingElement*
1099 method_id_mapping_element_new (MonoMethod *method) {
1100 MethodIdMappingElement *result = g_new (MethodIdMappingElement, 1);
1101 char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
1103 result->name = g_strdup_printf ("%s (%s)", mono_method_get_name (method), signature);
1105 result->method = method;
1106 result->next_unwritten = profiler->methods->unwritten;
1107 profiler->methods->unwritten = result;
1108 result->id = profiler->methods->next_id;
1109 profiler->methods->next_id ++;
1110 g_hash_table_insert (profiler->methods->table, method, result);
1112 result->data.code_start = NULL;
1113 result->data.code_size = 0;
1115 #if (DEBUG_MAPPING_EVENTS)
1116 printf ("Created new METHOD mapping element \"%s\" (%p)[%d]\n", result->name, method, result->id);
1123 method_id_mapping_element_destroy (gpointer element) {
1124 MethodIdMappingElement *e = (MethodIdMappingElement*) element;
1131 class_id_mapping_element_destroy (gpointer element) {
1132 ClassIdMappingElement *e = (ClassIdMappingElement*) element;
1135 if ((e->data.layout.slots != CLASS_LAYOUT_NOT_INITIALIZED) && (e->data.layout.slots > CLASS_LAYOUT_PACKED_BITMAP_SIZE))
1136 g_free (e->data.bitmap.extended);
1140 static MethodIdMapping*
1141 method_id_mapping_new (void) {
1142 MethodIdMapping *result = g_new (MethodIdMapping, 1);
1143 //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, method_id_mapping_element_destroy);
1144 result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, method_id_mapping_element_destroy);
1145 result->unwritten = NULL;
1146 result->next_id = 1;
1150 static ClassIdMapping*
1151 class_id_mapping_new (void) {
1152 ClassIdMapping *result = g_new (ClassIdMapping, 1);
1153 //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, class_id_mapping_element_destroy);
1154 result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, class_id_mapping_element_destroy);
1155 result->unwritten = NULL;
1156 result->next_id = 1;
1161 method_id_mapping_destroy (MethodIdMapping *map) {
1162 g_hash_table_destroy (map->table);
1167 class_id_mapping_destroy (ClassIdMapping *map) {
1168 g_hash_table_destroy (map->table);
1172 #if (DEBUG_LOAD_EVENTS)
1174 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element);
1177 static LoadedElement*
1178 loaded_element_load_start (GHashTable *table, gpointer item) {
1179 LoadedElement *element = g_new0 (LoadedElement, 1);
1180 #if (DEBUG_LOAD_EVENTS)
1181 print_load_event ("LOAD START", table, item, element);
1183 MONO_PROFILER_GET_CURRENT_COUNTER (element->load_start_counter);
1184 g_hash_table_insert (table, item, element);
1188 static LoadedElement*
1189 loaded_element_load_end (GHashTable *table, gpointer item, char *name) {
1190 LoadedElement *element = g_hash_table_lookup (table, item);
1191 #if (DEBUG_LOAD_EVENTS)
1192 print_load_event ("LOAD END", table, item, element);
1194 g_assert (element != NULL);
1195 MONO_PROFILER_GET_CURRENT_COUNTER (element->load_end_counter);
1196 element->name = name;
1197 element->loaded = TRUE;
1201 static LoadedElement*
1202 loaded_element_unload_start (GHashTable *table, gpointer item) {
1203 LoadedElement *element = g_hash_table_lookup (table, item);
1204 #if (DEBUG_LOAD_EVENTS)
1205 print_load_event ("UNLOAD START", table, item, element);
1207 g_assert (element != NULL);
1208 MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_start_counter);
1212 static LoadedElement*
1213 loaded_element_unload_end (GHashTable *table, gpointer item) {
1214 LoadedElement *element = g_hash_table_lookup (table, item);
1215 #if (DEBUG_LOAD_EVENTS)
1216 print_load_event ("UNLOAD END", table, item, element);
1218 g_assert (element != NULL);
1219 MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_end_counter);
1220 element->unloaded = TRUE;
1226 loaded_element_destroy (gpointer element) {
1227 if (((LoadedElement*)element)->name)
1228 g_free (((LoadedElement*)element)->name);
1232 #if (DEBUG_LOAD_EVENTS)
1234 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element) {
1235 const char* item_name;
1238 if (table == profiler->loaded_assemblies) {
1239 //item_info = g_strdup_printf("ASSEMBLY %p (dynamic %d)", item, mono_image_is_dynamic (mono_assembly_get_image((MonoAssembly*)item)));
1240 item_info = g_strdup_printf("ASSEMBLY %p", item);
1241 } else if (table == profiler->loaded_modules) {
1242 //item_info = g_strdup_printf("MODULE %p (dynamic %d)", item, mono_image_is_dynamic ((MonoImage*)item));
1243 item_info = g_strdup_printf("MODULE %p", item);
1244 } else if (table == profiler->loaded_appdomains) {
1245 item_info = g_strdup_printf("APPDOMAIN %p (id %d)", item, mono_domain_get_id ((MonoDomain*)item));
1248 g_assert_not_reached ();
1251 if (element != NULL) {
1252 item_name = element->name;
1254 item_name = "<NULL>";
1257 printf ("%s EVENT for %s (%s)\n", event_name, item_info, item_name);
1263 profiler_heap_shot_object_buffers_destroy (ProfilerHeapShotObjectBuffer *buffer) {
1264 while (buffer != NULL) {
1265 ProfilerHeapShotObjectBuffer *next = buffer->next;
1266 #if DEBUG_HEAP_PROFILER
1267 printf ("profiler_heap_shot_object_buffers_destroy: destroyed buffer %p (%p-%p)\n", buffer, & (buffer->buffer [0]), buffer->end);
1274 static ProfilerHeapShotObjectBuffer*
1275 profiler_heap_shot_object_buffer_new (ProfilerPerThreadData *data) {
1276 ProfilerHeapShotObjectBuffer *buffer;
1277 ProfilerHeapShotObjectBuffer *result = g_new (ProfilerHeapShotObjectBuffer, 1);
1278 result->next_free_slot = & (result->buffer [0]);
1279 result->end = & (result->buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE]);
1280 result->first_unprocessed_slot = & (result->buffer [0]);
1281 result->next = data->heap_shot_object_buffers;
1282 data->heap_shot_object_buffers = result;
1283 #if DEBUG_HEAP_PROFILER
1284 printf ("profiler_heap_shot_object_buffer_new: created buffer %p (%p-%p)\n", result, result->next_free_slot, result->end);
1286 for (buffer = result; buffer != NULL; buffer = buffer->next) {
1287 ProfilerHeapShotObjectBuffer *last = buffer->next;
1288 if ((last != NULL) && (last->first_unprocessed_slot == last->end)) {
1289 buffer->next = NULL;
1290 profiler_heap_shot_object_buffers_destroy (last);
1297 static ProfilerHeapShotWriteJob*
1298 profiler_heap_shot_write_job_new (gboolean heap_shot_was_signalled, guint32 collection) {
1299 ProfilerHeapShotWriteJob *job = g_new (ProfilerHeapShotWriteJob, 1);
1301 job->next_unwritten = NULL;
1302 job->buffers = g_new (ProfilerHeapShotWriteBuffer, 1);
1303 job->buffers->next = NULL;
1304 job->last_next = & (job->buffers->next);
1305 job->start = & (job->buffers->buffer [0]);
1306 job->cursor = job->start;
1307 job->end = & (job->buffers->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1308 job->full_buffers = 0;
1309 job->heap_shot_was_signalled = heap_shot_was_signalled;
1310 job->collection = collection;
1311 #if DEBUG_HEAP_PROFILER
1312 printf ("profiler_heap_shot_write_job_new: created job %p with buffer %p(%p-%p)\n", job, job->buffers, job->start, job->end);
1318 profiler_heap_shot_write_job_add_buffer (ProfilerHeapShotWriteJob *job, gpointer value) {
1319 ProfilerHeapShotWriteBuffer *buffer = g_new (ProfilerHeapShotWriteBuffer, 1);
1320 buffer->next = NULL;
1321 *(job->last_next) = buffer;
1322 job->last_next = & (buffer->next);
1323 job->full_buffers ++;
1324 buffer->buffer [0] = value;
1325 job->start = & (buffer->buffer [0]);
1326 job->cursor = & (buffer->buffer [1]);
1327 job->end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1328 #if DEBUG_HEAP_PROFILER
1329 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);
1331 ProfilerHeapShotWriteBuffer *current_buffer;
1332 for (current_buffer = job->buffers; current_buffer != NULL; current_buffer = current_buffer->next) {
1333 printf ("profiler_heap_shot_write_job_add_buffer: now job %p has buffer %p\n", job, current_buffer);
1340 profiler_heap_shot_write_job_free_buffers (ProfilerHeapShotWriteJob *job) {
1341 ProfilerHeapShotWriteBuffer *buffer = job->buffers;
1343 while (buffer != NULL) {
1344 ProfilerHeapShotWriteBuffer *next = buffer->next;
1345 #if DEBUG_HEAP_PROFILER
1346 printf ("profiler_heap_shot_write_job_free_buffers: in job %p, freeing buffer %p\n", job, buffer);
1352 job->buffers = NULL;
1356 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job);
1359 profiler_process_heap_shot_write_jobs (void) {
1360 gboolean done = FALSE;
1363 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1364 ProfilerHeapShotWriteJob *previous_job = NULL;
1365 ProfilerHeapShotWriteJob *next_job;
1368 while (current_job != NULL) {
1369 next_job = current_job->next_unwritten;
1371 if (next_job != NULL) {
1372 if (current_job->buffers != NULL) {
1375 if (next_job->buffers == NULL) {
1376 current_job->next_unwritten = NULL;
1380 if (current_job->buffers != NULL) {
1381 LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: writing...");
1382 profiler_heap_shot_write_block (current_job);
1383 LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: done");
1384 if (previous_job != NULL) {
1385 previous_job->next_unwritten = NULL;
1390 previous_job = current_job;
1391 current_job = next_job;
1397 profiler_free_heap_shot_write_jobs (void) {
1398 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1399 ProfilerHeapShotWriteJob *next_job;
1401 if (current_job != NULL) {
1402 while (current_job->next_unwritten != NULL) {
1403 #if DEBUG_HEAP_PROFILER
1404 printf ("profiler_free_heap_shot_write_jobs: job %p must not be freed\n", current_job);
1406 current_job = current_job->next_unwritten;
1409 next_job = current_job->next;
1410 current_job->next = NULL;
1411 current_job = next_job;
1413 while (current_job != NULL) {
1414 #if DEBUG_HEAP_PROFILER
1415 printf ("profiler_free_heap_shot_write_jobs: job %p will be freed\n", current_job);
1417 next_job = current_job->next;
1418 g_free (current_job);
1419 current_job = next_job;
1425 profiler_destroy_heap_shot_write_jobs (void) {
1426 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1427 ProfilerHeapShotWriteJob *next_job;
1429 while (current_job != NULL) {
1430 next_job = current_job->next;
1431 profiler_heap_shot_write_job_free_buffers (current_job);
1432 g_free (current_job);
1433 current_job = next_job;
1438 profiler_add_heap_shot_write_job (ProfilerHeapShotWriteJob *job) {
1439 job->next = profiler->heap_shot_write_jobs;
1440 job->next_unwritten = job->next;
1441 profiler->heap_shot_write_jobs = job;
1442 #if DEBUG_HEAP_PROFILER
1443 printf ("profiler_add_heap_shot_write_job: added job %p\n", job);
1447 #if DEBUG_HEAP_PROFILER
1448 #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)
1449 #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)
1451 #define STORE_ALLOCATED_OBJECT_MESSAGE1(d,o)
1452 #define STORE_ALLOCATED_OBJECT_MESSAGE2(d,o)
1454 #define STORE_ALLOCATED_OBJECT(d,o) do {\
1455 if ((d)->heap_shot_object_buffers->next_free_slot < (d)->heap_shot_object_buffers->end) {\
1456 STORE_ALLOCATED_OBJECT_MESSAGE1 ((d), (o));\
1457 *((d)->heap_shot_object_buffers->next_free_slot) = (o);\
1458 (d)->heap_shot_object_buffers->next_free_slot ++;\
1460 ProfilerHeapShotObjectBuffer *buffer = profiler_heap_shot_object_buffer_new (d);\
1461 STORE_ALLOCATED_OBJECT_MESSAGE2 ((d), (o));\
1462 *((buffer)->next_free_slot) = (o);\
1463 (buffer)->next_free_slot ++;\
1467 static ProfilerPerThreadData*
1468 profiler_per_thread_data_new (guint32 buffer_size)
1470 ProfilerPerThreadData *data = g_new (ProfilerPerThreadData, 1);
1472 data->events = g_new0 (ProfilerEventData, buffer_size);
1473 data->next_free_event = data->events;
1474 data->end_event = data->events + (buffer_size - 1);
1475 data->first_unwritten_event = data->events;
1476 data->first_unmapped_event = data->events;
1477 MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
1478 data->last_event_counter = data->start_event_counter;
1479 data->thread_id = CURRENT_THREAD_ID ();
1480 data->heap_shot_object_buffers = NULL;
1481 if ((profiler->action_flags.unreachable_objects == TRUE) || (profiler->action_flags.heap_shot == TRUE)) {
1482 profiler_heap_shot_object_buffer_new (data);
1488 profiler_per_thread_data_destroy (ProfilerPerThreadData *data) {
1489 g_free (data->events);
1490 profiler_heap_shot_object_buffers_destroy (data->heap_shot_object_buffers);
1494 static ProfilerStatisticalData*
1495 profiler_statistical_data_new (MonoProfiler *profiler) {
1496 int buffer_size = profiler->statistical_buffer_size * (profiler->statistical_call_chain_depth + 1);
1497 ProfilerStatisticalData *data = g_new (ProfilerStatisticalData, 1);
1499 data->addresses = g_new0 (gpointer, buffer_size);
1500 data->next_free_index = 0;
1501 data->end_index = buffer_size;
1502 data->first_unwritten_index = 0;
1508 profiler_statistical_data_destroy (ProfilerStatisticalData *data) {
1509 g_free (data->addresses);
1514 profiler_add_write_buffer (void) {
1515 if (profiler->current_write_buffer->next == NULL) {
1516 profiler->current_write_buffer->next = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
1517 profiler->current_write_buffer->next->next = NULL;
1519 //printf ("Added next buffer %p, to buffer %p\n", profiler->current_write_buffer->next, profiler->current_write_buffer);
1522 profiler->current_write_buffer = profiler->current_write_buffer->next;
1523 profiler->current_write_position = 0;
1524 profiler->full_write_buffers ++;
1528 profiler_free_write_buffers (void) {
1529 ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1530 while (current_buffer != NULL) {
1531 ProfilerFileWriteBuffer *next_buffer = current_buffer->next;
1533 //printf ("Freeing write buffer %p, next is %p\n", current_buffer, next_buffer);
1535 g_free (current_buffer);
1536 current_buffer = next_buffer;
1540 #define WRITE_BYTE(b) do {\
1541 if (profiler->current_write_position >= PROFILER_FILE_WRITE_BUFFER_SIZE) {\
1542 profiler_add_write_buffer ();\
1544 profiler->current_write_buffer->buffer [profiler->current_write_position] = (b);\
1545 profiler->current_write_position ++;\
1550 write_current_block (guint16 code) {
1551 guint32 size = (profiler->full_write_buffers * PROFILER_FILE_WRITE_BUFFER_SIZE) + profiler->current_write_position;
1552 ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1553 guint64 current_counter;
1554 guint32 counter_delta;
1557 MONO_PROFILER_GET_CURRENT_COUNTER (current_counter);
1558 if (profiler->last_header_counter != 0) {
1559 counter_delta = current_counter - profiler->last_header_counter;
1563 profiler->last_header_counter = current_counter;
1565 header [0] = code & 0xff;
1566 header [1] = (code >> 8) & 0xff;
1567 header [2] = size & 0xff;
1568 header [3] = (size >> 8) & 0xff;
1569 header [4] = (size >> 16) & 0xff;
1570 header [5] = (size >> 24) & 0xff;
1571 header [6] = counter_delta & 0xff;
1572 header [7] = (counter_delta >> 8) & 0xff;
1573 header [8] = (counter_delta >> 16) & 0xff;
1574 header [9] = (counter_delta >> 24) & 0xff;
1576 WRITE_BUFFER (& (header [0]), 10);
1578 while ((current_buffer != NULL) && (profiler->full_write_buffers > 0)) {
1579 WRITE_BUFFER (& (current_buffer->buffer [0]), PROFILER_FILE_WRITE_BUFFER_SIZE);
1580 profiler->full_write_buffers --;
1581 current_buffer = current_buffer->next;
1583 if (profiler->current_write_position > 0) {
1584 WRITE_BUFFER (& (current_buffer->buffer [0]), profiler->current_write_position);
1588 profiler->current_write_buffer = profiler->write_buffers;
1589 profiler->current_write_position = 0;
1590 profiler->full_write_buffers = 0;
1594 #define SEVEN_BITS_MASK (0x7f)
1595 #define EIGHT_BIT_MASK (0x80)
1598 write_uint32 (guint32 value) {
1599 while (value > SEVEN_BITS_MASK) {
1600 WRITE_BYTE (value & SEVEN_BITS_MASK);
1603 WRITE_BYTE (value | EIGHT_BIT_MASK);
1606 write_uint64 (guint64 value) {
1607 while (value > SEVEN_BITS_MASK) {
1608 WRITE_BYTE (value & SEVEN_BITS_MASK);
1611 WRITE_BYTE (value | EIGHT_BIT_MASK);
1614 write_string (const char *string) {
1615 while (*string != 0) {
1616 WRITE_BYTE (*string);
1622 #if DEBUG_HEAP_PROFILER
1623 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c) printf ("WRITE_HEAP_SHOT_JOB_VALUE: writing value %p at cursor %p\n", (v), (c))
1625 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c)
1627 #define WRITE_HEAP_SHOT_JOB_VALUE(j,v) do {\
1628 if ((j)->cursor < (j)->end) {\
1629 WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE ((v), ((j)->cursor));\
1630 *((j)->cursor) = (v);\
1633 profiler_heap_shot_write_job_add_buffer (j, v);\
1638 #undef GUINT_TO_POINTER
1639 #undef GPOINTER_TO_UINT
1640 #if (SIZEOF_VOID_P == 4)
1641 #define GUINT_TO_POINTER(u) ((void*)(guint32)(u))
1642 #define GPOINTER_TO_UINT(p) ((guint32)(void*)(p))
1643 #elif (SIZEOF_VOID_P == 8)
1644 #define GUINT_TO_POINTER(u) ((void*)(guint64)(u))
1645 #define GPOINTER_TO_UINT(p) ((guint64)(void*)(p))
1647 #error Bad size of void pointer
1650 #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)))
1652 #if DEBUG_HEAP_PROFILER
1653 #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)
1655 #define UPDATE_JOB_BUFFER_CURSOR_MESSAGE()
1657 #define UPDATE_JOB_BUFFER_CURSOR() do {\
1659 if (cursor >= end) {\
1660 buffer = buffer->next;\
1661 if (buffer != NULL) {\
1662 cursor = & (buffer->buffer [0]);\
1663 if (buffer->next != NULL) {\
1664 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);\
1672 UPDATE_JOB_BUFFER_CURSOR_MESSAGE ();\
1676 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job) {
1677 ProfilerHeapShotWriteBuffer *buffer;
1680 guint64 start_counter;
1682 guint64 end_counter;
1685 write_uint64 (job->start_counter);
1686 write_uint64 (job->start_time);
1687 write_uint64 (job->end_counter);
1688 write_uint64 (job->end_time);
1689 write_uint32 (job->collection);
1690 MONO_PROFILER_GET_CURRENT_COUNTER (start_counter);
1691 MONO_PROFILER_GET_CURRENT_TIME (start_time);
1692 write_uint64 (start_counter);
1693 write_uint64 (start_time);
1694 #if DEBUG_HEAP_PROFILER
1695 printf ("profiler_heap_shot_write_block: working on job %p...\n", job);
1697 buffer = job->buffers;
1698 cursor = & (buffer->buffer [0]);
1699 if (buffer->next != NULL) {
1700 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1704 if (cursor >= end) {
1707 #if DEBUG_HEAP_PROFILER
1708 printf ("profiler_heap_shot_write_block: in job %p, starting at buffer %p and cursor %p\n", job, buffer, cursor);
1710 while (cursor != NULL) {
1711 gpointer value = *cursor;
1712 HeapProfilerJobValueCode code = GPOINTER_TO_UINT (value) & HEAP_CODE_MASK;
1713 #if DEBUG_HEAP_PROFILER
1714 printf ("profiler_heap_shot_write_block: got value %p and code %d\n", value, code);
1717 UPDATE_JOB_BUFFER_CURSOR ();
1718 if (code == HEAP_CODE_FREE_OBJECT_CLASS) {
1719 MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
1720 //MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) % 4);
1721 ClassIdMappingElement *class_id;
1724 class_id = class_id_mapping_element_get (klass);
1725 if (class_id == NULL) {
1726 printf ("profiler_heap_shot_write_block: unknown class %p", klass);
1728 g_assert (class_id != NULL);
1729 write_uint32 ((class_id->id << 2) | HEAP_CODE_FREE_OBJECT_CLASS);
1731 size = GPOINTER_TO_UINT (*cursor);
1732 UPDATE_JOB_BUFFER_CURSOR ();
1733 write_uint32 (size);
1734 #if DEBUG_HEAP_PROFILER
1735 printf ("profiler_heap_shot_write_block: wrote unreachable object of class %p (id %d, size %d)\n", klass, class_id->id, size);
1737 } else if (code == HEAP_CODE_OBJECT) {
1738 MonoObject *object = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
1739 MonoClass *klass = mono_object_get_class (object);
1740 ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
1741 guint32 size = mono_object_get_size (object);
1742 guint32 references = GPOINTER_TO_UINT (*cursor);
1743 UPDATE_JOB_BUFFER_CURSOR ();
1745 if (class_id == NULL) {
1746 printf ("profiler_heap_shot_write_block: unknown class %p", klass);
1748 g_assert (class_id != NULL);
1750 write_uint64 (GPOINTER_TO_UINT (value));
1751 write_uint32 (class_id->id);
1752 write_uint32 (size);
1753 write_uint32 (references);
1754 #if DEBUG_HEAP_PROFILER
1755 printf ("profiler_heap_shot_write_block: writing object %p (references %d)\n", value, references);
1758 while (references > 0) {
1759 gpointer reference = *cursor;
1760 write_uint64 (GPOINTER_TO_UINT (reference));
1761 UPDATE_JOB_BUFFER_CURSOR ();
1763 #if DEBUG_HEAP_PROFILER
1764 printf ("profiler_heap_shot_write_block: inside object %p, wrote reference %p)\n", value, reference);
1768 #if DEBUG_HEAP_PROFILER
1769 printf ("profiler_heap_shot_write_block: unknown code %d in value %p\n", code, value);
1771 g_assert_not_reached ();
1776 MONO_PROFILER_GET_CURRENT_COUNTER (end_counter);
1777 MONO_PROFILER_GET_CURRENT_TIME (end_time);
1778 write_uint64 (end_counter);
1779 write_uint64 (end_time);
1781 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_HEAP);
1783 profiler_heap_shot_write_job_free_buffers (job);
1784 #if DEBUG_HEAP_PROFILER
1785 printf ("profiler_heap_shot_write_block: work on job %p done.\n", job);
1790 write_element_load_block (LoadedElement *element, guint8 kind, gsize thread_id) {
1792 write_uint64 (element->load_start_counter);
1793 write_uint64 (element->load_end_counter);
1794 write_uint64 (thread_id);
1795 write_string (element->name);
1796 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_LOADED);
1797 element->load_written = TRUE;
1801 write_element_unload_block (LoadedElement *element, guint8 kind, gsize thread_id) {
1803 write_uint64 (element->unload_start_counter);
1804 write_uint64 (element->unload_end_counter);
1805 write_uint64 (thread_id);
1806 write_string (element->name);
1807 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED);
1808 element->unload_written = TRUE;
1812 write_clock_data (void) {
1816 MONO_PROFILER_GET_CURRENT_COUNTER (counter);
1817 MONO_PROFILER_GET_CURRENT_TIME (time);
1819 write_uint64 (counter);
1820 write_uint64 (time);
1824 write_mapping_block (gsize thread_id) {
1825 ClassIdMappingElement *current_class;
1826 MethodIdMappingElement *current_method;
1828 if ((profiler->classes->unwritten == NULL) && (profiler->methods->unwritten == NULL))
1831 #if (DEBUG_MAPPING_EVENTS)
1832 printf ("[write_mapping_block][TID %ld] START\n", thread_id);
1835 write_clock_data ();
1836 write_uint64 (thread_id);
1838 for (current_class = profiler->classes->unwritten; current_class != NULL; current_class = current_class->next_unwritten) {
1839 write_uint32 (current_class->id);
1840 write_string (current_class->name);
1841 #if (DEBUG_MAPPING_EVENTS)
1842 printf ("mapping CLASS (%d => %s)\n", current_class->id, current_class->name);
1844 g_free (current_class->name);
1845 current_class->name = NULL;
1848 profiler->classes->unwritten = NULL;
1850 for (current_method = profiler->methods->unwritten; current_method != NULL; current_method = current_method->next_unwritten) {
1851 MonoMethod *method = current_method->method;
1852 MonoClass *klass = mono_method_get_class (method);
1853 ClassIdMappingElement *class_element = class_id_mapping_element_get (klass);
1854 g_assert (class_element != NULL);
1855 write_uint32 (current_method->id);
1856 write_uint32 (class_element->id);
1857 write_string (current_method->name);
1858 #if (DEBUG_MAPPING_EVENTS)
1859 printf ("mapping METHOD ([%d]%d => %s)\n", class_element?class_element->id:1, current_method->id, current_method->name);
1861 g_free (current_method->name);
1862 current_method->name = NULL;
1865 profiler->methods->unwritten = NULL;
1867 write_clock_data ();
1868 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_MAPPING);
1870 #if (DEBUG_MAPPING_EVENTS)
1871 printf ("[write_mapping_block][TID %ld] END\n", thread_id);
1876 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER = 1,
1877 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_IMPLICIT = 2,
1878 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT = 3,
1879 MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION = 4,
1880 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT = 5,
1881 MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT = 6,
1882 MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT = 7
1883 } MonoProfilerPackedEventCode;
1884 #define MONO_PROFILER_PACKED_EVENT_CODE_BITS 3
1885 #define MONO_PROFILER_PACKED_EVENT_DATA_BITS (8-MONO_PROFILER_PACKED_EVENT_CODE_BITS)
1886 #define MONO_PROFILER_PACKED_EVENT_DATA_MASK ((1<<MONO_PROFILER_PACKED_EVENT_DATA_BITS)-1)
1888 #define MONO_PROFILER_EVENT_MAKE_PACKED_CODE(result,data,base) do {\
1889 result = ((base)|((data & MONO_PROFILER_PACKED_EVENT_DATA_MASK) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
1890 data >>= MONO_PROFILER_PACKED_EVENT_DATA_BITS;\
1892 #define MONO_PROFILER_EVENT_MAKE_FULL_CODE(result,code,kind,base) do {\
1893 result = ((base)|((((kind)<<4) | (code)) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
1896 static ProfilerEventData*
1897 write_event (ProfilerEventData *event) {
1898 ProfilerEventData *next = event + 1;
1899 gboolean write_event_value = TRUE;
1902 guint64 event_value;
1904 event_value = event->value;
1905 if (event_value == MAX_EVENT_VALUE) {
1906 event_value = *((guint64*)next);
1910 if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
1911 MethodIdMappingElement *element = method_id_mapping_element_get (event->data.address);
1912 g_assert (element != NULL);
1913 event_data = element->id;
1915 if (event->code == MONO_PROFILER_EVENT_METHOD_CALL) {
1916 if (event->kind == MONO_PROFILER_EVENT_KIND_START) {
1917 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER);
1919 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT);
1922 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT);
1924 } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
1925 ClassIdMappingElement *element = class_id_mapping_element_get (event->data.address);
1926 g_assert (element != NULL);
1927 event_data = element->id;
1929 if (event->code == MONO_PROFILER_EVENT_CLASS_ALLOCATION) {
1930 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION);
1932 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT);
1935 event_data = event->data.number;
1936 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
1939 #if (DEBUG_LOGGING_PROFILER)
1941 printf ("writing EVENT[%p] data_type:%d, kind:%d, code:%d (%d:%ld:%ld)\n", event,
1942 event->data_type, event->kind, event->code,
1943 event_code, event_data, event_value);
1946 WRITE_BYTE (event_code);
1947 write_uint64 (event_data);
1948 if (write_event_value) {
1949 write_uint64 (event_value);
1956 write_thread_data_block (ProfilerPerThreadData *data) {
1957 ProfilerEventData *start = data->first_unwritten_event;
1958 ProfilerEventData *end = data->first_unmapped_event;
1963 write_clock_data ();
1964 write_uint64 (data->thread_id);
1966 write_uint64 (data->start_event_counter);
1968 while (start < end) {
1969 start = write_event (start);
1972 data->first_unwritten_event = end;
1974 write_clock_data ();
1975 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_EVENTS);
1978 static ProfilerExecutableMemoryRegionData*
1979 profiler_executable_memory_region_new (gpointer *start, gpointer *end, guint32 file_offset, char *file_name, guint32 id) {
1980 ProfilerExecutableMemoryRegionData *result = g_new (ProfilerExecutableMemoryRegionData, 1);
1981 result->start = start;
1983 result->file_offset = file_offset;
1984 result->file_name = g_strdup (file_name);
1986 result->is_new = TRUE;
1988 result->file = NULL;
1989 result->symbols_capacity = id;
1990 result->symbols_count = id;
1991 result->symbols = NULL;
1997 executable_file_close (ProfilerExecutableMemoryRegionData *region);
2000 profiler_executable_memory_region_destroy (ProfilerExecutableMemoryRegionData *data) {
2001 if (data->file_name != NULL) {
2002 g_free (data->file_name);
2004 if (data->symbols != NULL) {
2005 g_free (data->symbols);
2007 if (data->file != NULL) {
2008 executable_file_close (data);
2013 static ProfilerExecutableMemoryRegions*
2014 profiler_executable_memory_regions_new (int next_id, int next_unmanaged_function_id) {
2015 ProfilerExecutableMemoryRegions *result = g_new (ProfilerExecutableMemoryRegions, 1);
2016 result->regions = g_new0 (ProfilerExecutableMemoryRegionData*, 32);
2017 result->regions_capacity = 32;
2018 result->regions_count = 0;
2019 result->next_id = next_id;
2020 result->next_unmanaged_function_id = next_unmanaged_function_id;
2025 profiler_executable_memory_regions_destroy (ProfilerExecutableMemoryRegions *regions) {
2028 for (i = 0; i < regions->regions_count; i++) {
2029 profiler_executable_memory_region_destroy (regions->regions [i]);
2031 g_free (regions->regions);
2035 static ProfilerExecutableMemoryRegionData*
2036 find_address_region (ProfilerExecutableMemoryRegions *regions, gpointer address) {
2038 int high_index = regions->regions_count;
2039 int middle_index = 0;
2040 ProfilerExecutableMemoryRegionData *middle_region = regions->regions [0];
2042 if ((regions->regions_count == 0) || (regions->regions [low_index]->start > address) || (regions->regions [high_index - 1]->end < address)) {
2046 //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);
2048 while (low_index != high_index) {
2049 middle_index = low_index + ((high_index - low_index) / 2);
2050 middle_region = regions->regions [middle_index];
2052 //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);
2054 if (middle_region->start > address) {
2055 if (middle_index > 0) {
2056 high_index = middle_index;
2060 } else if (middle_region->end < address) {
2061 if (middle_index < regions->regions_count - 1) {
2062 low_index = middle_index + 1;
2067 return middle_region;
2071 if ((middle_region == NULL) || (middle_region->start > address) || (middle_region->end < address)) {
2074 return middle_region;
2079 append_region (ProfilerExecutableMemoryRegions *regions, gpointer *start, gpointer *end, guint32 file_offset, char *file_name) {
2080 if (regions->regions_count >= regions->regions_capacity) {
2081 ProfilerExecutableMemoryRegionData **new_regions = g_new0 (ProfilerExecutableMemoryRegionData*, regions->regions_capacity * 2);
2082 memcpy (new_regions, regions->regions, regions->regions_capacity * sizeof (ProfilerExecutableMemoryRegionData*));
2083 g_free (regions->regions);
2084 regions->regions = new_regions;
2085 regions->regions_capacity = regions->regions_capacity * 2;
2087 regions->regions [regions->regions_count] = profiler_executable_memory_region_new (start, end, file_offset, file_name, regions->next_id);
2088 regions->regions_count ++;
2089 regions->next_id ++;
2093 restore_old_regions (ProfilerExecutableMemoryRegions *old_regions, ProfilerExecutableMemoryRegions *new_regions) {
2097 for (old_i = 0; old_i < old_regions->regions_count; old_i++) {
2098 ProfilerExecutableMemoryRegionData *old_region = old_regions->regions [old_i];
2099 for (new_i = 0; new_i < new_regions->regions_count; new_i++) {
2100 ProfilerExecutableMemoryRegionData *new_region = new_regions->regions [new_i];
2101 if ((old_region->start == new_region->start) &&
2102 (old_region->end == new_region->end) &&
2103 (old_region->file_offset == new_region->file_offset) &&
2104 ! strcmp (old_region->file_name, new_region->file_name)) {
2105 new_regions->regions [new_i] = old_region;
2106 old_regions->regions [old_i] = new_region;
2108 // FIXME (sanity check)
2109 g_assert (new_region->is_new && ! old_region->is_new);
2116 compare_regions (const void *a1, const void *a2) {
2117 ProfilerExecutableMemoryRegionData *r1 = * (ProfilerExecutableMemoryRegionData**) a1;
2118 ProfilerExecutableMemoryRegionData *r2 = * (ProfilerExecutableMemoryRegionData**) a2;
2119 return (r1->start < r2->start)? -1 : ((r1->start > r2->start)? 1 : 0);
2123 sort_regions (ProfilerExecutableMemoryRegions *regions) {
2124 qsort (regions->regions, regions->regions_count, sizeof (ProfilerExecutableMemoryRegionData *), compare_regions);
2128 executable_file_add_region_reference (ProfilerExecutableFile *file, ProfilerExecutableMemoryRegionData *region) {
2129 guint8 *section_headers = file->data + file->header->e_shoff;
2132 for (section_index = 1; section_index < file->header->e_shnum; section_index ++) {
2133 ElfSection *section_header = (ElfSection*) (section_headers + (file->header->e_shentsize * section_index));
2135 if ((section_header->sh_addr != 0) && (section_header->sh_flags & ELF_SHF_EXECINSTR) &&
2136 (region->file_offset <= section_header->sh_offset) && (region->file_offset + (((guint8*)region->end)-((guint8*)region->start)) >= (section_header->sh_offset + section_header->sh_size))) {
2137 ProfilerExecutableFileSectionRegion *section_region = & (file->section_regions [section_index]);
2138 section_region->region = region;
2139 section_region->section_address = (gpointer) section_header->sh_addr;
2140 section_region->section_offset = section_header->sh_offset;
2145 static ProfilerExecutableFile*
2146 executable_file_open (ProfilerExecutableMemoryRegionData *region) {
2147 ProfilerExecutableFiles *files = & (profiler->executable_files);
2148 ProfilerExecutableFile *file = (ProfilerExecutableFile*) g_hash_table_lookup (files->table, region->file_name);
2150 guint16 test = 0x0102;
2151 struct stat stat_buffer;
2152 int symtab_index = 0;
2153 int strtab_index = 0;
2154 int dynsym_index = 0;
2155 int dynstr_index = 0;
2157 guint8 *section_headers;
2161 file = g_new0 (ProfilerExecutableFile, 1);
2162 region->file = file;
2163 file->reference_count ++;
2165 file->fd = open (region->file_name, O_RDONLY);
2166 if (file->fd == -1) {
2167 //g_warning ("Cannot open file '%s': '%s'", region->file_name, strerror (errno));
2170 if (fstat (file->fd, &stat_buffer) != 0) {
2171 //g_warning ("Cannot stat file '%s': '%s'", region->file_name, strerror (errno));
2174 size_t region_length = ((guint8*)region->end) - ((guint8*)region->start);
2175 file->length = stat_buffer.st_size;
2177 if (file->length == region_length) {
2178 file->data = region->start;
2182 file->data = mmap (NULL, file->length, PROT_READ, MAP_PRIVATE, file->fd, 0);
2184 if (file->data == MAP_FAILED) {
2186 //g_warning ("Cannot map file '%s': '%s'", region->file_name, strerror (errno));
2194 header = (ElfHeader*) file->data;
2196 if ((header->e_ident [EI_MAG0] != 0x7f) || (header->e_ident [EI_MAG1] != 'E') ||
2197 (header->e_ident [EI_MAG2] != 'L') || (header->e_ident [EI_MAG3] != 'F')) {
2201 if (sizeof (gsize) == 4) {
2202 if (header->e_ident [EI_CLASS] != ELF_CLASS_32) {
2203 g_warning ("Class is not ELF_CLASS_32 with gsize size %d", (int) sizeof (gsize));
2206 } else if (sizeof (gsize) == 8) {
2207 if (header->e_ident [EI_CLASS] != ELF_CLASS_64) {
2208 g_warning ("Class is not ELF_CLASS_64 with gsize size %d", (int) sizeof (gsize));
2212 g_warning ("Absurd gsize size %d", (int) sizeof (gsize));
2216 if ((*(guint8*)(&test)) == 0x01) {
2217 if (header->e_ident [EI_DATA] != ELF_DATA_MSB) {
2218 g_warning ("Data is not ELF_DATA_MSB with first test byte 0x01");
2221 } else if ((*(guint8*)(&test)) == 0x02) {
2222 if (header->e_ident [EI_DATA] != ELF_DATA_LSB) {
2223 g_warning ("Data is not ELF_DATA_LSB with first test byte 0x02");
2227 g_warning ("Absurd test byte value");
2231 /* OK, this is a usable elf file... */
2232 file->header = header;
2233 section_headers = file->data + header->e_shoff;
2234 file->main_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * header->e_shstrndx)))->sh_offset);
2236 for (section_index = 0; section_index < header->e_shnum; section_index ++) {
2237 ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index));
2239 if (section_header->sh_type == ELF_SHT_SYMTAB) {
2240 symtab_index = section_index;
2241 } else if (section_header->sh_type == ELF_SHT_DYNSYM) {
2242 dynsym_index = section_index;
2243 } else if (section_header->sh_type == ELF_SHT_STRTAB) {
2244 if (! strcmp (file->main_string_table + section_header->sh_name, ".strtab")) {
2245 strtab_index = section_index;
2246 } else if (! strcmp (file->main_string_table + section_header->sh_name, ".dynstr")) {
2247 dynstr_index = section_index;
2252 if ((symtab_index != 0) && (strtab_index != 0)) {
2253 section_index = symtab_index;
2254 strings_index = strtab_index;
2255 } else if ((dynsym_index != 0) && (dynstr_index != 0)) {
2256 section_index = dynsym_index;
2257 strings_index = dynstr_index;
2263 if (section_index != 0) {
2264 ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index));
2265 file->symbol_size = section_header->sh_entsize;
2266 file->symbols_count = (guint32) (section_header->sh_size / section_header->sh_entsize);
2267 file->symbols_start = file->data + section_header->sh_offset;
2268 file->symbols_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * strings_index)))->sh_offset);
2271 file->section_regions = g_new0 (ProfilerExecutableFileSectionRegion, file->header->e_shnum);
2273 region->file = file;
2274 file->reference_count ++;
2277 if (file->header != NULL) {
2278 executable_file_add_region_reference (file, region);
2281 if (file->next_new_file == NULL) {
2282 file->next_new_file = files->new_files;
2283 files->new_files = file;
2289 executable_file_free (ProfilerExecutableFile* file) {
2290 if (file->fd != -1) {
2291 if (close (file->fd) != 0) {
2292 g_warning ("Cannot close file: '%s'", strerror (errno));
2294 if (file->data != NULL) {
2295 if (munmap (file->data, file->length) != 0) {
2296 g_warning ("Cannot unmap file: '%s'", strerror (errno));
2300 if (file->section_regions != NULL) {
2301 g_free (file->section_regions);
2307 executable_file_close (ProfilerExecutableMemoryRegionData *region) {
2308 region->file->reference_count --;
2310 if (region->file->reference_count <= 0) {
2311 ProfilerExecutableFiles *files = & (profiler->executable_files);
2312 g_hash_table_remove (files->table, region->file_name);
2313 executable_file_free (region->file);
2314 region->file = NULL;
2319 executable_file_count_symbols (ProfilerExecutableFile *file) {
2322 for (symbol_index = 0; symbol_index < file->symbols_count; symbol_index ++) {
2323 ElfSymbol *symbol = (ElfSymbol*) (file->symbols_start + (symbol_index * file->symbol_size));
2325 if ((ELF_ST_TYPE (symbol->st_info) == ELF_STT_FUNC) &&
2326 (symbol->st_shndx > 0) &&
2327 (symbol->st_shndx < file->header->e_shnum)) {
2328 int symbol_section_index = symbol->st_shndx;
2329 ProfilerExecutableMemoryRegionData *region = file->section_regions [symbol_section_index].region;
2330 if ((region != NULL) && (region->symbols == NULL)) {
2331 region->symbols_count ++;
2338 executable_memory_regions_prepare_symbol_tables (ProfilerExecutableMemoryRegions *regions) {
2340 for (i = 0; i < regions->regions_count; i++) {
2341 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2342 if ((region->symbols_count > 0) && (region->symbols == NULL)) {
2343 region->symbols = g_new (ProfilerUnmanagedSymbol, region->symbols_count);
2344 region->symbols_capacity = region->symbols_count;
2345 region->symbols_count = 0;
2351 executable_region_symbol_get_name (ProfilerExecutableMemoryRegionData *region, ProfilerUnmanagedSymbol *symbol) {
2352 ElfSymbol *elf_symbol = (ElfSymbol*) (region->file->symbols_start + (symbol->index * region->file->symbol_size));
2353 return region->file->symbols_string_table + elf_symbol->st_name;
2357 executable_file_build_symbol_tables (ProfilerExecutableFile *file) {
2360 for (symbol_index = 0; symbol_index < file->symbols_count; symbol_index ++) {
2361 ElfSymbol *symbol = (ElfSymbol*) (file->symbols_start + (symbol_index * file->symbol_size));
2363 if ((ELF_ST_TYPE (symbol->st_info) == ELF_STT_FUNC) &&
2364 (symbol->st_shndx > 0) &&
2365 (symbol->st_shndx < file->header->e_shnum)) {
2366 int symbol_section_index = symbol->st_shndx;
2367 ProfilerExecutableFileSectionRegion *section_region = & (file->section_regions [symbol_section_index]);
2368 ProfilerExecutableMemoryRegionData *region = section_region->region;
2370 if (region != NULL) {
2371 ProfilerUnmanagedSymbol *new_symbol = & (region->symbols [region->symbols_count]);
2372 region->symbols_count ++;
2375 new_symbol->index = symbol_index;
2376 new_symbol->size = symbol->st_size;
2377 new_symbol->offset = (((guint8*) symbol->st_value) - section_region->section_address) - (region->file_offset - section_region->section_offset);
2384 compare_region_symbols (const void *p1, const void *p2) {
2385 const ProfilerUnmanagedSymbol *s1 = p1;
2386 const ProfilerUnmanagedSymbol *s2 = p2;
2387 return (s1->offset < s2->offset)? -1 : ((s1->offset > s2->offset)? 1 : 0);
2391 executable_memory_regions_sort_symbol_tables (ProfilerExecutableMemoryRegions *regions) {
2393 for (i = 0; i < regions->regions_count; i++) {
2394 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2395 if ((region->is_new) && (region->symbols != NULL)) {
2396 qsort (region->symbols, region->symbols_count, sizeof (ProfilerUnmanagedSymbol), compare_region_symbols);
2402 build_symbol_tables (ProfilerExecutableMemoryRegions *regions, ProfilerExecutableFiles *files) {
2404 ProfilerExecutableFile *file;
2406 for (i = 0; i < regions->regions_count; i++) {
2407 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2408 if ((region->is_new) && (region->file == NULL)) {
2409 executable_file_open (region);
2413 for (file = files->new_files; file != NULL; file = file->next_new_file) {
2414 executable_file_count_symbols (file);
2417 executable_memory_regions_prepare_symbol_tables (regions);
2419 for (file = files->new_files; file != NULL; file = file->next_new_file) {
2420 executable_file_build_symbol_tables (file);
2423 executable_memory_regions_sort_symbol_tables (regions);
2425 file = files->new_files;
2426 while (file != NULL) {
2427 ProfilerExecutableFile *next_file = file->next_new_file;
2428 file->next_new_file = NULL;
2431 files->new_files = NULL;
2434 static ProfilerUnmanagedSymbol*
2435 executable_memory_region_find_symbol (ProfilerExecutableMemoryRegionData *region, guint32 offset) {
2436 if (region->symbols_count > 0) {
2437 ProfilerUnmanagedSymbol *low = region->symbols;
2438 ProfilerUnmanagedSymbol *high = region->symbols + (region->symbols_count - 1);
2439 int step = region->symbols_count >> 1;
2440 ProfilerUnmanagedSymbol *current = region->symbols + step;
2443 step = (high - low) >> 1;
2445 if (offset < current->offset) {
2447 current = high - step;
2448 } else if (offset >= current->offset) {
2449 if (offset >= (current->offset + current->size)) {
2451 current = low + step;
2458 if ((offset >= current->offset) && (offset < (current->offset + current->size))) {
2468 //FIXME: make also Win32 and BSD variants
2469 #define MAPS_BUFFER_SIZE 4096
2472 update_regions_buffer (int fd, char *buffer) {
2473 ssize_t result = read (fd, buffer, MAPS_BUFFER_SIZE);
2475 if (result == MAPS_BUFFER_SIZE) {
2477 } else if (result >= 0) {
2478 *(buffer + result) = 0;
2486 #define GOTO_NEXT_CHAR(c,b,fd) do {\
2488 if (((c) - (b) >= MAPS_BUFFER_SIZE) || ((*(c) == 0) && ((c) != (b)))) {\
2489 update_regions_buffer ((fd), (b));\
2494 static int hex_digit_value (char c) {
2495 if ((c >= '0') && (c <= '9')) {
2497 } else if ((c >= 'a') && (c <= 'f')) {
2498 return c - 'a' + 10;
2499 } else if ((c >= 'A') && (c <= 'F')) {
2500 return c - 'A' + 10;
2522 MAP_LINE_PARSER_STATE_INVALID,
2523 MAP_LINE_PARSER_STATE_START_ADDRESS,
2524 MAP_LINE_PARSER_STATE_END_ADDRESS,
2525 MAP_LINE_PARSER_STATE_PERMISSIONS,
2526 MAP_LINE_PARSER_STATE_OFFSET,
2527 MAP_LINE_PARSER_STATE_DEVICE,
2528 MAP_LINE_PARSER_STATE_INODE,
2529 MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME,
2530 MAP_LINE_PARSER_STATE_FILENAME,
2531 MAP_LINE_PARSER_STATE_DONE
2532 } MapLineParserState;
2534 const char *map_line_parser_state [] = {
2542 "BLANK_BEFORE_FILENAME",
2548 parse_map_line (ProfilerExecutableMemoryRegions *regions, int fd, char *buffer, char *current) {
2549 MapLineParserState state = MAP_LINE_PARSER_STATE_START_ADDRESS;
2550 gsize start_address = 0;
2551 gsize end_address = 0;
2553 char *start_filename = NULL;
2554 char *end_filename = NULL;
2555 gboolean is_executable = FALSE;
2556 gboolean done = FALSE;
2562 case MAP_LINE_PARSER_STATE_START_ADDRESS:
2564 start_address <<= 4;
2565 start_address |= hex_digit_value (c);
2566 } else if (c == '-') {
2567 state = MAP_LINE_PARSER_STATE_END_ADDRESS;
2569 state = MAP_LINE_PARSER_STATE_INVALID;
2572 case MAP_LINE_PARSER_STATE_END_ADDRESS:
2575 end_address |= hex_digit_value (c);
2576 } else if (isblank (c)) {
2577 state = MAP_LINE_PARSER_STATE_PERMISSIONS;
2579 state = MAP_LINE_PARSER_STATE_INVALID;
2582 case MAP_LINE_PARSER_STATE_PERMISSIONS:
2584 is_executable = TRUE;
2585 } else if (isblank (c)) {
2586 state = MAP_LINE_PARSER_STATE_OFFSET;
2587 } else if ((c != '-') && ! isalpha (c)) {
2588 state = MAP_LINE_PARSER_STATE_INVALID;
2591 case MAP_LINE_PARSER_STATE_OFFSET:
2594 offset |= hex_digit_value (c);
2595 } else if (isblank (c)) {
2596 state = MAP_LINE_PARSER_STATE_DEVICE;
2598 state = MAP_LINE_PARSER_STATE_INVALID;
2601 case MAP_LINE_PARSER_STATE_DEVICE:
2603 state = MAP_LINE_PARSER_STATE_INODE;
2604 } else if ((c != ':') && ! isxdigit (c)) {
2605 state = MAP_LINE_PARSER_STATE_INVALID;
2608 case MAP_LINE_PARSER_STATE_INODE:
2610 state = MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME;
2611 } else if (! isdigit (c)) {
2612 state = MAP_LINE_PARSER_STATE_INVALID;
2615 case MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME:
2616 if ((c == '/') || (c == '[')) {
2617 state = MAP_LINE_PARSER_STATE_FILENAME;
2618 start_filename = current;
2619 } else if (! isblank (c)) {
2620 state = MAP_LINE_PARSER_STATE_INVALID;
2623 case MAP_LINE_PARSER_STATE_FILENAME:
2625 state = MAP_LINE_PARSER_STATE_DONE;
2627 end_filename = current;
2630 case MAP_LINE_PARSER_STATE_DONE:
2631 if (done && is_executable) {
2633 append_region (regions, (gpointer) start_address, (gpointer) end_address, offset, start_filename);
2636 case MAP_LINE_PARSER_STATE_INVALID:
2638 state = MAP_LINE_PARSER_STATE_DONE;
2645 } else if (c == '\n') {
2646 state = MAP_LINE_PARSER_STATE_DONE;
2649 GOTO_NEXT_CHAR(current, buffer, fd);
2655 scan_process_regions (ProfilerExecutableMemoryRegions *regions) {
2660 fd = open ("/proc/self/maps", O_RDONLY);
2665 buffer = malloc (MAPS_BUFFER_SIZE);
2666 update_regions_buffer (fd, buffer);
2668 while (current != NULL) {
2669 current = parse_map_line (regions, fd, buffer, current);
2680 MONO_PROFILER_STATISTICAL_CODE_END = 0,
2681 MONO_PROFILER_STATISTICAL_CODE_METHOD = 1,
2682 MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID = 2,
2683 MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_NEW_ID = 3,
2684 MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION = 4,
2685 MONO_PROFILER_STATISTICAL_CODE_CALL_CHAIN = 5,
2686 MONO_PROFILER_STATISTICAL_CODE_REGIONS = 7
2687 } MonoProfilerStatisticalCode;
2690 refresh_memory_regions (void) {
2691 ProfilerExecutableMemoryRegions *old_regions = profiler->executable_regions;
2692 ProfilerExecutableMemoryRegions *new_regions = profiler_executable_memory_regions_new (old_regions->next_id, old_regions->next_unmanaged_function_id);
2695 LOG_WRITER_THREAD ("Refreshing memory regions...");
2696 scan_process_regions (new_regions);
2697 restore_old_regions (old_regions, new_regions);
2698 sort_regions (new_regions);
2699 LOG_WRITER_THREAD ("Refreshed memory regions.");
2701 LOG_WRITER_THREAD ("Building symbol tables...");
2702 build_symbol_tables (new_regions, & (profiler->executable_files));
2704 printf ("Symbol tables done!\n");
2705 printf ("Region summary...\n");
2706 for (i = 0; i < new_regions->regions_count; i++) {
2707 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
2708 printf ("Region %d[%d][NEW:%d] (%p-%p) at %d in file %s\n", i, region->id, region->is_new,
2709 region->start, region->end, region->file_offset, region->file_name);
2711 printf ("New symbol tables dump...\n");
2712 for (i = 0; i < new_regions->regions_count; i++) {
2713 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
2715 if (region->is_new) {
2718 printf ("Region %d[%d][NEW:%d] (%p-%p) at %d in file %s\n", i, region->id, region->is_new,
2719 region->start, region->end, region->file_offset, region->file_name);
2720 for (symbol_index = 0; symbol_index < region->symbols_count; symbol_index ++) {
2721 ProfilerUnmanagedSymbol *symbol = & (region->symbols [symbol_index]);
2722 printf (" [%d] Symbol %s (offset %d, size %d)\n", symbol_index,
2723 executable_region_symbol_get_name (region, symbol),
2724 symbol->offset, symbol->size);
2729 LOG_WRITER_THREAD ("Built symbol tables.");
2731 // This marks the region "sub-block"
2732 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_REGIONS);
2734 // First write the "removed" regions
2735 for (i = 0; i < old_regions->regions_count; i++) {
2736 ProfilerExecutableMemoryRegionData *region = old_regions->regions [i];
2737 if (! region->is_new) {
2738 #if DEBUG_STATISTICAL_PROFILER
2739 printf ("[refresh_memory_regions] Invalidated region %d\n", region->id);
2741 write_uint32 (region->id);
2746 // Then write the new ones
2747 for (i = 0; i < new_regions->regions_count; i++) {
2748 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
2749 if (region->is_new) {
2750 region->is_new = FALSE;
2752 #if DEBUG_STATISTICAL_PROFILER
2753 printf ("[refresh_memory_regions] Wrote region %d (%p-%p[%d] '%s')\n", region->id, region->start, region->end, region->file_offset, region->file_name);
2755 write_uint32 (region->id);
2756 write_uint64 (GPOINTER_TO_UINT (region->start));
2757 write_uint32 (GPOINTER_TO_UINT (region->end) - GPOINTER_TO_UINT (region->start));
2758 write_uint32 (region->file_offset);
2759 write_string (region->file_name);
2764 // Finally, free the old ones, and replace them
2765 profiler_executable_memory_regions_destroy (old_regions);
2766 profiler->executable_regions = new_regions;
2770 write_statistical_hit (MonoDomain *domain, gpointer address, gboolean regions_refreshed) {
2771 MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (char*) address);
2774 MonoMethod *method = mono_jit_info_get_method (ji);
2775 MethodIdMappingElement *element = method_id_mapping_element_get (method);
2777 if (element != NULL) {
2778 #if DEBUG_STATISTICAL_PROFILER
2779 printf ("[write_statistical_hit] Wrote method %d\n", element->id);
2781 write_uint32 ((element->id << 3) | MONO_PROFILER_STATISTICAL_CODE_METHOD);
2783 #if DEBUG_STATISTICAL_PROFILER
2784 printf ("[write_statistical_hit] Wrote unknown method %p\n", method);
2786 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_METHOD);
2789 ProfilerExecutableMemoryRegionData *region = find_address_region (profiler->executable_regions, address);
2791 if (region == NULL && ! regions_refreshed) {
2792 #if DEBUG_STATISTICAL_PROFILER
2793 printf ("[write_statistical_hit] Cannot find region for address %p, refreshing...\n", address);
2795 refresh_memory_regions ();
2796 regions_refreshed = TRUE;
2797 region = find_address_region (profiler->executable_regions, address);
2800 if (region != NULL) {
2801 guint32 offset = ((guint8*)address) - ((guint8*)region->start);
2802 ProfilerUnmanagedSymbol *symbol = executable_memory_region_find_symbol (region, offset);
2804 if (symbol != NULL) {
2805 if (symbol->id > 0) {
2806 #if DEBUG_STATISTICAL_PROFILER
2807 printf ("[write_statistical_hit] Wrote unmanaged symbol %d\n", symbol->id);
2809 write_uint32 ((symbol->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID);
2811 ProfilerExecutableMemoryRegions *regions = profiler->executable_regions;
2812 const char *symbol_name = executable_region_symbol_get_name (region, symbol);
2813 symbol->id = regions->next_unmanaged_function_id;
2814 regions->next_unmanaged_function_id ++;
2815 #if DEBUG_STATISTICAL_PROFILER
2816 printf ("[write_statistical_hit] Wrote new unmanaged symbol in region %d[%d]\n", region->id, offset);
2818 write_uint32 ((region->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_NEW_ID);
2819 write_uint32 (symbol->id);
2820 write_string (symbol_name);
2823 #if DEBUG_STATISTICAL_PROFILER
2824 printf ("[write_statistical_hit] Wrote unknown unmanaged hit in region %d[%d] (address %p)\n", region->id, offset, address);
2826 write_uint32 ((region->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION);
2827 write_uint32 (offset);
2830 #if DEBUG_STATISTICAL_PROFILER
2831 printf ("[write_statistical_hit] Wrote unknown unmanaged hit %p\n", address);
2833 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION);
2834 write_uint64 (GPOINTER_TO_UINT (address));
2838 return regions_refreshed;
2842 flush_all_mappings (void);
2845 write_statistical_data_block (ProfilerStatisticalData *data) {
2846 int start_index = data->first_unwritten_index;
2847 int end_index = data->next_free_index;
2848 gboolean regions_refreshed = FALSE;
2849 int call_chain_depth = profiler->statistical_call_chain_depth;
2850 MonoDomain *domain = mono_domain_get ();
2853 if (end_index > data->end_index)
2854 end_index = data->end_index;
2856 if (start_index == end_index)
2859 data->first_unwritten_index = end_index;
2861 write_clock_data ();
2863 #if DEBUG_STATISTICAL_PROFILER
2864 printf ("[write_statistical_data_block] Starting loop at index %d\n", start_index);
2867 for (index = start_index; index < end_index; index ++) {
2868 int base_index = index * (call_chain_depth + 1);
2869 gpointer address = data->addresses [base_index];
2872 regions_refreshed = write_statistical_hit (domain, address, regions_refreshed);
2875 for (callers_count = 0; callers_count < call_chain_depth; callers_count ++) {
2876 address = data->addresses [base_index + callers_count];
2877 if (address == NULL) {
2882 if (callers_count > 0) {
2883 write_uint32 ((callers_count << 3) | MONO_PROFILER_STATISTICAL_CODE_CALL_CHAIN);
2885 for (callers_count = 0; callers_count < call_chain_depth; callers_count ++) {
2886 address = data->addresses [base_index + callers_count];
2887 if (address != NULL) {
2888 regions_refreshed = write_statistical_hit (domain, address, regions_refreshed);
2895 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_END);
2897 #if DEBUG_STATISTICAL_PROFILER
2898 printf ("[write_statistical_data_block] Ending loop at index %d\n", end_index);
2900 write_clock_data ();
2902 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL);
2906 write_intro_block (void) {
2908 write_string ("mono");
2909 write_uint32 (profiler->flags);
2910 write_uint64 (profiler->start_counter);
2911 write_uint64 (profiler->start_time);
2912 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_INTRO);
2916 write_end_block (void) {
2918 write_uint64 (profiler->end_counter);
2919 write_uint64 (profiler->end_time);
2920 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_END);
2924 update_mapping (ProfilerPerThreadData *data) {
2925 ProfilerEventData *start = data->first_unmapped_event;
2926 ProfilerEventData *end = data->next_free_event;
2927 data->first_unmapped_event = end;
2929 #if (DEBUG_LOGGING_PROFILER)
2930 printf ("[update_mapping][TID %ld] START\n", data->thread_id);
2932 while (start < end) {
2933 #if DEBUG_LOGGING_PROFILER
2934 printf ("Examining event %p[TID %ld] looking for a new mapping...\n", start, data->thread_id);
2936 if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
2937 ClassIdMappingElement *element = class_id_mapping_element_get (start->data.address);
2938 if (element == NULL) {
2939 MonoClass *klass = start->data.address;
2940 class_id_mapping_element_new (klass);
2942 } else if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
2943 MethodIdMappingElement *element = method_id_mapping_element_get (start->data.address);
2944 if (element == NULL) {
2945 MonoMethod *method = start->data.address;
2946 method_id_mapping_element_new (method);
2950 if (start->value == MAX_EVENT_VALUE) {
2955 #if (DEBUG_LOGGING_PROFILER)
2956 printf ("[update_mapping][TID %ld] END\n", data->thread_id);
2961 flush_all_mappings (void) {
2962 ProfilerPerThreadData *data;
2964 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2965 update_mapping (data);
2967 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
2968 write_mapping_block (data->thread_id);
2973 flush_full_event_data_buffer (ProfilerPerThreadData *data) {
2976 // We flush all mappings because some id definitions could come
2977 // from other threads
2978 flush_all_mappings ();
2979 g_assert (data->first_unmapped_event >= data->end_event);
2981 write_thread_data_block (data);
2983 data->next_free_event = data->events;
2984 data->first_unwritten_event = data->events;
2985 data->first_unmapped_event = data->events;
2986 MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
2987 data->last_event_counter = data->start_event_counter;
2992 #define GET_NEXT_FREE_EVENT(d,e) {\
2993 if ((d)->next_free_event >= (d)->end_event) {\
2994 flush_full_event_data_buffer (d);\
2996 (e) = (d)->next_free_event;\
2997 (d)->next_free_event ++;\
3001 flush_everything (void) {
3002 ProfilerPerThreadData *data;
3004 flush_all_mappings ();
3005 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3006 write_thread_data_block (data);
3008 write_statistical_data_block (profiler->statistical_data);
3011 #define RESULT_TO_LOAD_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_LOADED_EVENT_SUCCESS:MONO_PROFILER_LOADED_EVENT_FAILURE)
3013 appdomain_start_load (MonoProfiler *profiler, MonoDomain *domain) {
3015 loaded_element_load_start (profiler->loaded_appdomains, domain);
3020 appdomain_end_load (MonoProfiler *profiler, MonoDomain *domain, int result) {
3022 LoadedElement *element;
3024 name = g_strdup_printf ("%d", mono_domain_get_id (domain));
3026 element = loaded_element_load_end (profiler->loaded_appdomains, domain, name);
3027 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
3032 appdomain_start_unload (MonoProfiler *profiler, MonoDomain *domain) {
3034 loaded_element_unload_start (profiler->loaded_appdomains, domain);
3035 flush_everything ();
3040 appdomain_end_unload (MonoProfiler *profiler, MonoDomain *domain) {
3041 LoadedElement *element;
3044 element = loaded_element_unload_end (profiler->loaded_appdomains, domain);
3045 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN, CURRENT_THREAD_ID ());
3050 module_start_load (MonoProfiler *profiler, MonoImage *module) {
3052 loaded_element_load_start (profiler->loaded_modules, module);
3057 module_end_load (MonoProfiler *profiler, MonoImage *module, int result) {
3059 MonoAssemblyName aname;
3060 LoadedElement *element;
3062 mono_assembly_fill_assembly_name (module, &aname);
3063 name = mono_stringify_assembly_name (&aname);
3065 element = loaded_element_load_end (profiler->loaded_modules, module, name);
3066 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_MODULE | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
3071 module_start_unload (MonoProfiler *profiler, MonoImage *module) {
3073 loaded_element_unload_start (profiler->loaded_modules, module);
3074 flush_everything ();
3079 module_end_unload (MonoProfiler *profiler, MonoImage *module) {
3080 LoadedElement *element;
3083 element = loaded_element_unload_end (profiler->loaded_modules, module);
3084 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_MODULE, CURRENT_THREAD_ID ());
3089 assembly_start_load (MonoProfiler *profiler, MonoAssembly *assembly) {
3091 loaded_element_load_start (profiler->loaded_assemblies, assembly);
3096 assembly_end_load (MonoProfiler *profiler, MonoAssembly *assembly, int result) {
3098 MonoAssemblyName aname;
3099 LoadedElement *element;
3101 mono_assembly_fill_assembly_name (mono_assembly_get_image (assembly), &aname);
3102 name = mono_stringify_assembly_name (&aname);
3104 element = loaded_element_load_end (profiler->loaded_assemblies, assembly, name);
3105 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
3110 assembly_start_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
3112 loaded_element_unload_start (profiler->loaded_assemblies, assembly);
3113 flush_everything ();
3117 assembly_end_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
3118 LoadedElement *element;
3121 element = loaded_element_unload_end (profiler->loaded_assemblies, assembly);
3122 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY, CURRENT_THREAD_ID ());
3126 #if (DEBUG_LOGGING_PROFILER)
3128 class_event_code_to_string (MonoProfilerClassEvents code) {
3130 case MONO_PROFILER_EVENT_CLASS_LOAD: return "LOAD";
3131 case MONO_PROFILER_EVENT_CLASS_UNLOAD: return "UNLOAD";
3132 case MONO_PROFILER_EVENT_CLASS_ALLOCATION: return "ALLOCATION";
3133 case MONO_PROFILER_EVENT_CLASS_EXCEPTION: return "EXCEPTION";
3134 default: g_assert_not_reached (); return "";
3138 method_event_code_to_string (MonoProfilerClassEvents code) {
3140 case MONO_PROFILER_EVENT_METHOD_CALL: return "CALL";
3141 case MONO_PROFILER_EVENT_METHOD_JIT: return "JIT";
3142 case MONO_PROFILER_EVENT_METHOD_FREED: return "FREED";
3143 default: g_assert_not_reached (); return "";
3147 number_event_code_to_string (MonoProfilerEvents code) {
3149 case MONO_PROFILER_EVENT_THREAD: return "THREAD";
3150 case MONO_PROFILER_EVENT_GC_COLLECTION: return "GC_COLLECTION";
3151 case MONO_PROFILER_EVENT_GC_MARK: return "GC_MARK";
3152 case MONO_PROFILER_EVENT_GC_SWEEP: return "GC_SWEEP";
3153 case MONO_PROFILER_EVENT_GC_RESIZE: return "GC_RESIZE";
3154 case MONO_PROFILER_EVENT_GC_STOP_WORLD: return "GC_STOP_WORLD";
3155 case MONO_PROFILER_EVENT_GC_START_WORLD: return "GC_START_WORLD";
3156 default: g_assert_not_reached (); return "";
3160 event_result_to_string (MonoProfilerEventResult code) {
3162 case MONO_PROFILER_EVENT_RESULT_SUCCESS: return "SUCCESS";
3163 case MONO_PROFILER_EVENT_RESULT_FAILURE: return "FAILURE";
3164 default: g_assert_not_reached (); return "";
3168 event_kind_to_string (MonoProfilerEventKind code) {
3170 case MONO_PROFILER_EVENT_KIND_START: return "START";
3171 case MONO_PROFILER_EVENT_KIND_END: return "END";
3172 default: g_assert_not_reached (); return "";
3176 print_event_data (gsize thread_id, ProfilerEventData *event, guint64 value) {
3177 if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
3178 printf ("[TID %ld] CLASS[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s)\n",
3180 event->data.address,
3182 class_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
3183 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
3184 event_kind_to_string (event->kind),
3189 mono_class_get_namespace ((MonoClass*) event->data.address),
3190 mono_class_get_name ((MonoClass*) event->data.address));
3191 } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
3192 printf ("[TID %ld] METHOD[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s:%s (?))\n",
3194 event->data.address,
3196 method_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
3197 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
3198 event_kind_to_string (event->kind),
3203 mono_class_get_namespace (mono_method_get_class ((MonoMethod*) event->data.address)),
3204 mono_class_get_name (mono_method_get_class ((MonoMethod*) event->data.address)),
3205 mono_method_get_name ((MonoMethod*) event->data.address));
3207 printf ("[TID %ld] NUMBER[%ld] event [%p] %s:%s[%d-%d-%d] %ld\n",
3209 (guint64) event->data.number,
3211 number_event_code_to_string (event->code),
3212 event_kind_to_string (event->kind),
3219 #define LOG_EVENT(tid,ev,val) print_event_data ((tid),(ev),(val))
3221 #define LOG_EVENT(tid,ev,val)
3224 #define RESULT_TO_EVENT_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_EVENT_RESULT_SUCCESS:MONO_PROFILER_EVENT_RESULT_FAILURE)
3226 #define STORE_EVENT_ITEM_COUNTER(p,i,dt,c,k) do {\
3227 ProfilerPerThreadData *data;\
3228 ProfilerEventData *event;\
3231 GET_PROFILER_THREAD_DATA (data);\
3232 GET_NEXT_FREE_EVENT (data, event);\
3233 MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
3234 event->data.address = (i);\
3235 event->data_type = (dt);\
3238 delta = counter - data->last_event_counter;\
3239 if (delta < MAX_EVENT_VALUE) {\
3240 event->value = delta;\
3242 ProfilerEventData *extension = data->next_free_event;\
3243 data->next_free_event ++;\
3244 event->value = MAX_EVENT_VALUE;\
3245 *(guint64*)extension = delta;\
3247 data->last_event_counter = counter;\
3248 LOG_EVENT (data->thread_id, event, delta);\
3250 #define STORE_EVENT_ITEM_VALUE(p,i,dt,c,k,v) do {\
3251 ProfilerPerThreadData *data;\
3252 ProfilerEventData *event;\
3253 GET_PROFILER_THREAD_DATA (data);\
3254 GET_NEXT_FREE_EVENT (data, event);\
3255 event->data.address = (i);\
3256 event->data_type = (dt);\
3259 if ((v) < MAX_EVENT_VALUE) {\
3260 event->value = (v);\
3262 ProfilerEventData *extension = data->next_free_event;\
3263 data->next_free_event ++;\
3264 event->value = MAX_EVENT_VALUE;\
3265 *(guint64*)extension = (v);\
3267 LOG_EVENT (data->thread_id, event, (v));\
3269 #define STORE_EVENT_NUMBER_COUNTER(p,n,dt,c,k) do {\
3270 ProfilerPerThreadData *data;\
3271 ProfilerEventData *event;\
3274 GET_PROFILER_THREAD_DATA (data);\
3275 GET_NEXT_FREE_EVENT (data, event);\
3276 MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
3277 event->data.number = (n);\
3278 event->data_type = (dt);\
3281 delta = counter - data->last_event_counter;\
3282 if (delta < MAX_EVENT_VALUE) {\
3283 event->value = delta;\
3285 ProfilerEventData *extension = data->next_free_event;\
3286 data->next_free_event ++;\
3287 event->value = MAX_EVENT_VALUE;\
3288 *(guint64*)extension = delta;\
3290 data->last_event_counter = counter;\
3291 LOG_EVENT (data->thread_id, event, delta);\
3293 #define STORE_EVENT_NUMBER_VALUE(p,n,dt,c,k,v) do {\
3294 ProfilerPerThreadData *data;\
3295 ProfilerEventData *event;\
3296 GET_PROFILER_THREAD_DATA (data);\
3297 GET_NEXT_FREE_EVENT (data, event);\
3298 event->data.number = (n);\
3299 event->data_type = (dt);\
3302 if ((v) < MAX_EVENT_VALUE) {\
3303 event->value = (v);\
3305 ProfilerEventData *extension = data->next_free_event;\
3306 data->next_free_event ++;\
3307 event->value = MAX_EVENT_VALUE;\
3308 *(guint64*)extension = (v);\
3310 LOG_EVENT (data->thread_id, event, (v));\
3315 class_start_load (MonoProfiler *profiler, MonoClass *klass) {
3316 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD, MONO_PROFILER_EVENT_KIND_START);
3319 class_end_load (MonoProfiler *profiler, MonoClass *klass, int result) {
3320 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);
3323 class_start_unload (MonoProfiler *profiler, MonoClass *klass) {
3324 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_START);
3327 class_end_unload (MonoProfiler *profiler, MonoClass *klass) {
3328 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_END);
3332 method_start_jit (MonoProfiler *profiler, MonoMethod *method) {
3333 if (profiler->action_flags.jit_time) {
3334 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT, MONO_PROFILER_EVENT_KIND_START);
3338 method_end_jit (MonoProfiler *profiler, MonoMethod *method, int result) {
3339 if (profiler->action_flags.jit_time) {
3340 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);
3346 method_jit_result (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result) {
3347 if (profiler->action_flags.oprofile && (result == MONO_PROFILE_OK)) {
3348 MonoClass *klass = mono_method_get_class (method);
3349 char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
3350 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);
3351 gpointer code_start = mono_jit_info_get_code_start (jinfo);
3352 int code_size = mono_jit_info_get_code_size (jinfo);
3354 if (op_write_native_code (name, code_start, code_size)) {
3355 g_warning ("Problem calling op_write_native_code\n");
3366 method_enter (MonoProfiler *profiler, MonoMethod *method) {
3367 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_START);
3370 method_leave (MonoProfiler *profiler, MonoMethod *method) {
3371 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_END);
3375 method_free (MonoProfiler *profiler, MonoMethod *method) {
3376 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_FREED, 0);
3380 thread_start (MonoProfiler *profiler, gsize tid) {
3381 STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_START);
3384 thread_end (MonoProfiler *profiler, gsize tid) {
3385 STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_END);
3389 object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) {
3390 ProfilerPerThreadData *thread_data;
3392 STORE_EVENT_ITEM_VALUE (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_ALLOCATION, 0, (guint64) mono_object_get_size (obj));
3393 if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
3394 GET_PROFILER_THREAD_DATA (thread_data);
3395 STORE_ALLOCATED_OBJECT (thread_data, obj);
3400 statistical_call_chain (MonoProfiler *profiler, int call_chain_depth, guchar **ips, void *context) {
3401 ProfilerStatisticalData *data;
3405 data = profiler->statistical_data;
3406 index = InterlockedIncrement (&data->next_free_index);
3408 if (index <= data->end_index) {
3409 int base_index = (index - 1) * (profiler->statistical_call_chain_depth + 1);
3410 int call_chain_index = 0;
3412 //printf ("[statistical_call_chain] (%d)\n", call_chain_depth);
3413 while (call_chain_index < call_chain_depth) {
3414 //printf ("[statistical_call_chain] [%d] = %p\n", base_index + call_chain_index, ips [call_chain_index]);
3415 data->addresses [base_index + call_chain_index] = (gpointer) ips [call_chain_index];
3416 call_chain_index ++;
3418 while (call_chain_index <= profiler->statistical_call_chain_depth) {
3419 //printf ("[statistical_call_chain] [%d] = NULL\n", base_index + call_chain_index);
3420 data->addresses [base_index + call_chain_index] = NULL;
3421 call_chain_index ++;
3424 /* Check if we are the one that must swap the buffers */
3425 if (index == data->end_index + 1) {
3426 ProfilerStatisticalData *new_data;
3428 /* In the *impossible* case that the writer thread has not finished yet, */
3429 /* loop waiting for it and meanwhile lose all statistical events... */
3431 /* First, wait that it consumed the ready buffer */
3432 while (profiler->statistical_data_ready != NULL);
3433 /* Then, wait that it produced the free buffer */
3434 new_data = profiler->statistical_data_second_buffer;
3435 } while (new_data == NULL);
3437 profiler->statistical_data_ready = data;
3438 profiler->statistical_data = new_data;
3439 profiler->statistical_data_second_buffer = NULL;
3440 WRITER_EVENT_RAISE ();
3443 /* Loop again, hoping to acquire a free slot this time */
3446 } while (data == NULL);
3450 statistical_hit (MonoProfiler *profiler, guchar *ip, void *context) {
3451 ProfilerStatisticalData *data;
3455 data = profiler->statistical_data;
3456 index = InterlockedIncrement (&data->next_free_index);
3458 if (index <= data->end_index) {
3459 data->addresses [index - 1] = (gpointer) ip;
3461 /* Check if we are the one that must swap the buffers */
3462 if (index == data->end_index + 1) {
3463 ProfilerStatisticalData *new_data;
3465 /* In the *impossible* case that the writer thread has not finished yet, */
3466 /* loop waiting for it and meanwhile lose all statistical events... */
3468 /* First, wait that it consumed the ready buffer */
3469 while (profiler->statistical_data_ready != NULL);
3470 /* Then, wait that it produced the free buffer */
3471 new_data = profiler->statistical_data_second_buffer;
3472 } while (new_data == NULL);
3474 profiler->statistical_data_ready = data;
3475 profiler->statistical_data = new_data;
3476 profiler->statistical_data_second_buffer = NULL;
3477 WRITER_EVENT_RAISE ();
3480 /* Loop again, hoping to acquire a free slot this time */
3483 } while (data == NULL);
3486 static MonoProfilerEvents
3487 gc_event_code_from_profiler_event (MonoGCEvent event) {
3489 case MONO_GC_EVENT_START:
3490 case MONO_GC_EVENT_END:
3491 return MONO_PROFILER_EVENT_GC_COLLECTION;
3492 case MONO_GC_EVENT_MARK_START:
3493 case MONO_GC_EVENT_MARK_END:
3494 return MONO_PROFILER_EVENT_GC_MARK;
3495 case MONO_GC_EVENT_RECLAIM_START:
3496 case MONO_GC_EVENT_RECLAIM_END:
3497 return MONO_PROFILER_EVENT_GC_SWEEP;
3498 case MONO_GC_EVENT_PRE_STOP_WORLD:
3499 case MONO_GC_EVENT_POST_STOP_WORLD:
3500 return MONO_PROFILER_EVENT_GC_STOP_WORLD;
3501 case MONO_GC_EVENT_PRE_START_WORLD:
3502 case MONO_GC_EVENT_POST_START_WORLD:
3503 return MONO_PROFILER_EVENT_GC_START_WORLD;
3505 g_assert_not_reached ();
3510 static MonoProfilerEventKind
3511 gc_event_kind_from_profiler_event (MonoGCEvent event) {
3513 case MONO_GC_EVENT_START:
3514 case MONO_GC_EVENT_MARK_START:
3515 case MONO_GC_EVENT_RECLAIM_START:
3516 case MONO_GC_EVENT_PRE_STOP_WORLD:
3517 case MONO_GC_EVENT_PRE_START_WORLD:
3518 return MONO_PROFILER_EVENT_KIND_START;
3519 case MONO_GC_EVENT_END:
3520 case MONO_GC_EVENT_MARK_END:
3521 case MONO_GC_EVENT_RECLAIM_END:
3522 case MONO_GC_EVENT_POST_START_WORLD:
3523 case MONO_GC_EVENT_POST_STOP_WORLD:
3524 return MONO_PROFILER_EVENT_KIND_END;
3526 g_assert_not_reached ();
3531 #define HEAP_SHOT_COMMAND_FILE_MAX_LENGTH 64
3533 profiler_heap_shot_process_command_file (void) {
3534 //FIXME: Port to Windows as well
3535 struct stat stat_buf;
3537 char buffer [HEAP_SHOT_COMMAND_FILE_MAX_LENGTH + 1];
3539 if (profiler->heap_shot_command_file_name == NULL)
3541 if (stat (profiler->heap_shot_command_file_name, &stat_buf) != 0)
3543 if (stat_buf.st_size > HEAP_SHOT_COMMAND_FILE_MAX_LENGTH)
3545 if ((stat_buf.st_mtim.tv_sec * 1000000) < profiler->heap_shot_command_file_access_time)
3548 fd = open (profiler->heap_shot_command_file_name, O_RDONLY);
3552 if (read (fd, &(buffer [0]), stat_buf.st_size) != stat_buf.st_size) {
3555 buffer [stat_buf.st_size] = 0;
3556 profiler->dump_next_heap_snapshots = atoi (buffer);
3557 MONO_PROFILER_GET_CURRENT_TIME (profiler->heap_shot_command_file_access_time);
3564 dump_current_heap_snapshot (void) {
3567 if (profiler->heap_shot_was_signalled) {
3570 profiler_heap_shot_process_command_file ();
3571 if (profiler->dump_next_heap_snapshots > 0) {
3572 profiler->dump_next_heap_snapshots--;
3574 } else if (profiler->dump_next_heap_snapshots < 0) {
3585 profiler_heap_buffers_setup (ProfilerHeapShotHeapBuffers *heap) {
3586 heap->buffers = g_new (ProfilerHeapShotHeapBuffer, 1);
3587 heap->buffers->previous = NULL;
3588 heap->buffers->next = NULL;
3589 heap->buffers->start_slot = &(heap->buffers->buffer [0]);
3590 heap->buffers->end_slot = &(heap->buffers->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
3591 heap->last = heap->buffers;
3592 heap->current = heap->buffers;
3593 heap->first_free_slot = & (heap->buffers->buffer [0]);
3596 profiler_heap_buffers_clear (ProfilerHeapShotHeapBuffers *heap) {
3597 heap->buffers = NULL;
3599 heap->current = NULL;
3600 heap->first_free_slot = NULL;
3603 profiler_heap_buffers_free (ProfilerHeapShotHeapBuffers *heap) {
3604 ProfilerHeapShotHeapBuffer *current = heap->buffers;
3605 while (current != NULL) {
3606 ProfilerHeapShotHeapBuffer *next = current->next;
3610 profiler_heap_buffers_clear (heap);
3614 report_object_references (gpointer *start, ClassIdMappingElement *layout, ProfilerHeapShotWriteJob *job) {
3615 int reported_references = 0;
3618 for (slot = 0; slot < layout->data.layout.slots; slot ++) {
3619 gboolean slot_has_reference;
3620 if (layout->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
3621 if (layout->data.bitmap.compact & (((guint64)1) << slot)) {
3622 slot_has_reference = TRUE;
3624 slot_has_reference = FALSE;
3627 if (layout->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
3628 slot_has_reference = TRUE;
3630 slot_has_reference = FALSE;
3634 if (slot_has_reference) {
3635 gpointer field = start [slot];
3637 if ((field != NULL) && mono_object_is_alive (field)) {
3638 reported_references ++;
3639 WRITE_HEAP_SHOT_JOB_VALUE (job, field);
3644 return reported_references;
3648 profiler_heap_report_object_reachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
3649 if (profiler->action_flags.heap_shot && (job != NULL)) {
3650 MonoClass *klass = mono_object_get_class (obj);
3651 int reference_counter = 0;
3652 gpointer *reference_counter_location;
3654 WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, obj, HEAP_CODE_OBJECT);
3655 #if DEBUG_HEAP_PROFILER
3656 printf ("profiler_heap_report_object_reachable: reported object %p at cursor %p\n", obj, (job->cursor - 1));
3658 WRITE_HEAP_SHOT_JOB_VALUE (job, NULL);
3659 reference_counter_location = job->cursor - 1;
3661 if (mono_class_get_rank (klass)) {
3662 MonoArray *array = (MonoArray *) obj;
3663 MonoClass *element_class = mono_class_get_element_class (klass);
3664 ClassIdMappingElement *element_id = class_id_mapping_element_get (element_class);
3666 g_assert (element_id != NULL);
3667 if (element_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
3668 class_id_mapping_element_build_layout_bitmap (element_class, element_id);
3670 if (! mono_class_is_valuetype (element_class)) {
3671 int length = mono_array_length (array);
3673 for (i = 0; i < length; i++) {
3674 MonoObject *array_element = mono_array_get (array, MonoObject*, i);
3675 if ((array_element != NULL) && mono_object_is_alive (array_element)) {
3676 reference_counter ++;
3677 WRITE_HEAP_SHOT_JOB_VALUE (job, array_element);
3680 } else if (element_id->data.layout.references > 0) {
3681 int length = mono_array_length (array);
3682 int array_element_size = mono_array_element_size (klass);
3684 for (i = 0; i < length; i++) {
3685 gpointer array_element_address = mono_array_addr_with_size (array, array_element_size, i);
3686 reference_counter += report_object_references (array_element_address, element_id, job);
3690 ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
3691 if (class_id == NULL) {
3692 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));
3694 g_assert (class_id != NULL);
3695 if (class_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
3696 class_id_mapping_element_build_layout_bitmap (klass, class_id);
3698 if (class_id->data.layout.references > 0) {
3699 reference_counter += report_object_references ((gpointer)(((char*)obj) + sizeof (MonoObject)), class_id, job);
3703 *reference_counter_location = GINT_TO_POINTER (reference_counter);
3704 #if DEBUG_HEAP_PROFILER
3705 printf ("profiler_heap_report_object_reachable: updated reference_counter_location %p with value %d\n", reference_counter_location, reference_counter);
3710 profiler_heap_report_object_unreachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
3712 MonoClass *klass = mono_object_get_class (obj);
3713 guint32 size = mono_object_get_size (obj);
3715 #if DEBUG_HEAP_PROFILER
3716 printf ("profiler_heap_report_object_unreachable: at job %p writing klass %p\n", job, klass);
3718 WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, klass, HEAP_CODE_FREE_OBJECT_CLASS);
3720 #if DEBUG_HEAP_PROFILER
3721 printf ("profiler_heap_report_object_unreachable: at job %p writing size %p\n", job, GUINT_TO_POINTER (size));
3723 WRITE_HEAP_SHOT_JOB_VALUE (job, GUINT_TO_POINTER (size));
3728 profiler_heap_add_object (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject *obj) {
3729 if (heap->first_free_slot >= heap->current->end_slot) {
3730 if (heap->current->next != NULL) {
3731 heap->current = heap->current->next;
3733 ProfilerHeapShotHeapBuffer *buffer = g_new (ProfilerHeapShotHeapBuffer, 1);
3734 buffer->previous = heap->last;
3735 buffer->next = NULL;
3736 buffer->start_slot = &(buffer->buffer [0]);
3737 buffer->end_slot = &(buffer->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
3738 heap->current = buffer;
3739 heap->last->next = buffer;
3740 heap->last = buffer;
3742 heap->first_free_slot = &(heap->current->buffer [0]);
3745 *(heap->first_free_slot) = obj;
3746 heap->first_free_slot ++;
3747 profiler_heap_report_object_reachable (job, obj);
3751 profiler_heap_pop_object_from_end (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject** current_slot) {
3752 while (heap->first_free_slot != current_slot) {
3755 if (heap->first_free_slot > heap->current->start_slot) {
3756 heap->first_free_slot --;
3758 heap->current = heap->current->previous;
3759 g_assert (heap->current != NULL);
3760 heap->first_free_slot = heap->current->end_slot - 1;
3763 obj = *(heap->first_free_slot);
3765 if (mono_object_is_alive (obj)) {
3766 profiler_heap_report_object_reachable (job, obj);
3769 profiler_heap_report_object_unreachable (job, obj);
3776 profiler_heap_scan (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job) {
3777 ProfilerHeapShotHeapBuffer *current_buffer = heap->buffers;
3778 MonoObject** current_slot = current_buffer->start_slot;
3780 while (current_slot != heap->first_free_slot) {
3781 MonoObject *obj = *current_slot;
3782 if (mono_object_is_alive (obj)) {
3783 profiler_heap_report_object_reachable (job, obj);
3785 profiler_heap_report_object_unreachable (job, obj);
3786 *current_slot = profiler_heap_pop_object_from_end (heap, job, current_slot);
3789 if (*current_slot != NULL) {
3792 if (current_slot == current_buffer->end_slot) {
3793 current_buffer = current_buffer->next;
3794 g_assert (current_buffer != NULL);
3795 current_slot = current_buffer->start_slot;
3802 handle_heap_profiling (MonoProfiler *profiler, MonoGCEvent ev) {
3803 static gboolean create_heap_shot_write_job;
3806 case MONO_GC_EVENT_PRE_STOP_WORLD:
3807 // Get the lock, so we are sure nobody is flushing events during the collection,
3808 // and we can update all mappings (building the class descriptors).
3811 case MONO_GC_EVENT_POST_STOP_WORLD:
3812 create_heap_shot_write_job = dump_current_heap_snapshot ();
3813 if (create_heap_shot_write_job) {
3814 ProfilerPerThreadData *data;
3815 // Update all mappings, so that we have built all the class descriptors.
3816 flush_all_mappings ();
3817 // Also write all event buffers, so that allocations are recorded.
3818 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3819 write_thread_data_block (data);
3825 case MONO_GC_EVENT_MARK_END: {
3826 ProfilerHeapShotWriteJob *job;
3827 ProfilerPerThreadData *data;
3829 if (create_heap_shot_write_job) {
3830 job = profiler_heap_shot_write_job_new (profiler->heap_shot_was_signalled, profiler->garbage_collection_counter);
3831 profiler->heap_shot_was_signalled = FALSE;
3832 MONO_PROFILER_GET_CURRENT_COUNTER (job->start_counter);
3833 MONO_PROFILER_GET_CURRENT_TIME (job->start_time);
3838 profiler_heap_scan (&(profiler->heap), job);
3840 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3841 ProfilerHeapShotObjectBuffer *buffer;
3842 for (buffer = data->heap_shot_object_buffers; buffer != NULL; buffer = buffer->next) {
3843 MonoObject **cursor;
3844 for (cursor = buffer->first_unprocessed_slot; cursor < buffer->next_free_slot; cursor ++) {
3845 MonoObject *obj = *cursor;
3846 #if DEBUG_HEAP_PROFILER
3847 printf ("gc_event: in object buffer %p(%p-%p) cursor at %p has object %p ", buffer, &(buffer->buffer [0]), buffer->end, cursor, obj);
3849 if (mono_object_is_alive (obj)) {
3850 #if DEBUG_HEAP_PROFILER
3851 printf ("(object is alive, adding to heap)\n");
3853 profiler_heap_add_object (&(profiler->heap), job, obj);
3855 #if DEBUG_HEAP_PROFILER
3856 printf ("(object is unreachable, reporting in job)\n");
3858 profiler_heap_report_object_unreachable (job, obj);
3861 buffer->first_unprocessed_slot = cursor;
3866 MONO_PROFILER_GET_CURRENT_COUNTER (job->end_counter);
3867 MONO_PROFILER_GET_CURRENT_TIME (job->end_time);
3869 profiler_add_heap_shot_write_job (job);
3870 profiler_free_heap_shot_write_jobs ();
3871 WRITER_EVENT_RAISE ();
3881 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
3882 gboolean do_heap_profiling = profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot;
3883 guint32 event_value;
3885 if (ev == MONO_GC_EVENT_START) {
3886 profiler->garbage_collection_counter ++;
3889 event_value = (profiler->garbage_collection_counter << 8) | generation;
3891 if (do_heap_profiling && (ev == MONO_GC_EVENT_POST_STOP_WORLD)) {
3892 handle_heap_profiling (profiler, ev);
3894 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));
3895 if (do_heap_profiling && (ev != MONO_GC_EVENT_POST_STOP_WORLD)) {
3896 handle_heap_profiling (profiler, ev);
3901 gc_resize (MonoProfiler *profiler, gint64 new_size) {
3902 profiler->garbage_collection_counter ++;
3903 STORE_EVENT_NUMBER_VALUE (profiler, new_size, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_GC_RESIZE, 0, profiler->garbage_collection_counter);
3906 /* called at the end of the program */
3908 profiler_shutdown (MonoProfiler *prof)
3910 ProfilerPerThreadData* current_thread_data;
3912 LOG_WRITER_THREAD ("profiler_shutdown: zeroing relevant flags");
3913 mono_profiler_set_events (0);
3914 //profiler->flags = 0;
3915 //profiler->action_flags.unreachable_objects = FALSE;
3916 //profiler->action_flags.heap_shot = FALSE;
3918 LOG_WRITER_THREAD ("profiler_shutdown: asking stats thread to exit");
3919 profiler->terminate_writer_thread = TRUE;
3920 WRITER_EVENT_RAISE ();
3921 LOG_WRITER_THREAD ("profiler_shutdown: waiting for stats thread to exit");
3922 WAIT_WRITER_THREAD ();
3923 LOG_WRITER_THREAD ("profiler_shutdown: stats thread should be dead now");
3924 WRITER_EVENT_DESTROY ();
3928 MONO_PROFILER_GET_CURRENT_TIME (profiler->end_time);
3929 MONO_PROFILER_GET_CURRENT_COUNTER (profiler->end_counter);
3931 flush_everything ();
3936 g_free (profiler->file_name);
3937 if (profiler->file_name_suffix != NULL) {
3938 g_free (profiler->file_name_suffix);
3941 method_id_mapping_destroy (profiler->methods);
3942 class_id_mapping_destroy (profiler->classes);
3943 g_hash_table_destroy (profiler->loaded_assemblies);
3944 g_hash_table_destroy (profiler->loaded_modules);
3945 g_hash_table_destroy (profiler->loaded_appdomains);
3947 FREE_PROFILER_THREAD_DATA ();
3949 for (current_thread_data = profiler->per_thread_data; current_thread_data != NULL; current_thread_data = current_thread_data->next) {
3950 profiler_per_thread_data_destroy (current_thread_data);
3952 if (profiler->statistical_data != NULL) {
3953 profiler_statistical_data_destroy (profiler->statistical_data);
3955 if (profiler->statistical_data_ready != NULL) {
3956 profiler_statistical_data_destroy (profiler->statistical_data_ready);
3958 if (profiler->statistical_data_second_buffer != NULL) {
3959 profiler_statistical_data_destroy (profiler->statistical_data_second_buffer);
3961 if (profiler->executable_regions != NULL) {
3962 profiler_executable_memory_regions_destroy (profiler->executable_regions);
3965 profiler_heap_buffers_free (&(profiler->heap));
3966 if (profiler->heap_shot_command_file_name != NULL) {
3967 g_free (profiler->heap_shot_command_file_name);
3970 profiler_free_write_buffers ();
3971 profiler_destroy_heap_shot_write_jobs ();
3973 DELETE_PROFILER_MUTEX ();
3976 if (profiler->action_flags.oprofile) {
3985 #define DEFAULT_ARGUMENTS "s"
3987 setup_user_options (const char *arguments) {
3988 gchar **arguments_array, **current_argument;
3989 #ifndef PLATFORM_WIN32
3990 int gc_request_signal_number = 0;
3992 detect_fast_timer ();
3994 profiler->file_name = NULL;
3995 profiler->file_name_suffix = NULL;
3996 profiler->per_thread_buffer_size = 10000;
3997 profiler->statistical_buffer_size = 10000;
3998 profiler->statistical_call_chain_depth = 0;
3999 profiler->write_buffer_size = 1024;
4000 profiler->heap_shot_command_file_name = NULL;
4001 profiler->dump_next_heap_snapshots = 0;
4002 profiler->heap_shot_command_file_access_time = 0;
4003 profiler->heap_shot_was_signalled = FALSE;
4004 profiler->flags = MONO_PROFILE_APPDOMAIN_EVENTS|
4005 MONO_PROFILE_ASSEMBLY_EVENTS|
4006 MONO_PROFILE_MODULE_EVENTS|
4007 MONO_PROFILE_CLASS_EVENTS|
4008 MONO_PROFILE_METHOD_EVENTS;
4010 if (arguments == NULL) {
4011 arguments = DEFAULT_ARGUMENTS;
4012 } else if (strstr (arguments, ":")) {
4013 arguments = strstr (arguments, ":") + 1;
4014 if (arguments [0] == 0) {
4015 arguments = DEFAULT_ARGUMENTS;
4019 arguments_array = g_strsplit (arguments, ",", -1);
4021 for (current_argument = arguments_array; ((current_argument != NULL) && (current_argument [0] != 0)); current_argument ++) {
4022 char *argument = *current_argument;
4023 char *equals = strstr (argument, "=");
4025 if (equals != NULL) {
4026 int equals_position = equals - argument;
4028 if (! (strncmp (argument, "per-thread-buffer-size", equals_position) && strncmp (argument, "tbs", equals_position))) {
4029 int value = atoi (equals + 1);
4031 profiler->per_thread_buffer_size = value;
4033 } else if (! (strncmp (argument, "statistical", equals_position) && strncmp (argument, "stat", equals_position) && strncmp (argument, "s", equals_position))) {
4034 int value = atoi (equals + 1);
4039 profiler->statistical_call_chain_depth = value;
4040 profiler->flags |= MONO_PROFILE_STATISTICAL|MONO_PROFILE_JIT_COMPILATION;
4041 profiler->action_flags.jit_time = TRUE;
4043 } else if (! (strncmp (argument, "statistical-thread-buffer-size", equals_position) && strncmp (argument, "sbs", equals_position))) {
4044 int value = atoi (equals + 1);
4046 profiler->statistical_buffer_size = value;
4048 } else if (! (strncmp (argument, "write-buffer-size", equals_position) && strncmp (argument, "wbs", equals_position))) {
4049 int value = atoi (equals + 1);
4051 profiler->write_buffer_size = value;
4053 } else if (! (strncmp (argument, "output", equals_position) && strncmp (argument, "out", equals_position) && strncmp (argument, "o", equals_position) && strncmp (argument, "O", equals_position))) {
4054 if (strlen (equals + 1) > 0) {
4055 profiler->file_name = g_strdup (equals + 1);
4057 } else if (! (strncmp (argument, "output-suffix", equals_position) && strncmp (argument, "suffix", equals_position) && strncmp (argument, "os", equals_position) && strncmp (argument, "OS", equals_position))) {
4058 if (strlen (equals + 1) > 0) {
4059 profiler->file_name_suffix = g_strdup (equals + 1);
4061 } else if (! (strncmp (argument, "gc-commands", equals_position) && strncmp (argument, "gc-c", equals_position) && strncmp (argument, "gcc", equals_position))) {
4062 if (strlen (equals + 1) > 0) {
4063 profiler->heap_shot_command_file_name = g_strdup (equals + 1);
4065 } else if (! (strncmp (argument, "gc-dumps", equals_position) && strncmp (argument, "gc-d", equals_position) && strncmp (argument, "gcd", equals_position))) {
4066 if (strlen (equals + 1) > 0) {
4067 profiler->dump_next_heap_snapshots = atoi (equals + 1);
4069 #ifndef PLATFORM_WIN32
4070 } else if (! (strncmp (argument, "gc-signal", equals_position) && strncmp (argument, "gc-s", equals_position) && strncmp (argument, "gcs", equals_position))) {
4071 if (strlen (equals + 1) > 0) {
4072 char *signal_name = equals + 1;
4073 if (! strcasecmp (signal_name, "SIGUSR1")) {
4074 gc_request_signal_number = SIGUSR1;
4075 } else if (! strcasecmp (signal_name, "SIGUSR2")) {
4076 gc_request_signal_number = SIGUSR2;
4077 } else if (! strcasecmp (signal_name, "SIGPROF")) {
4078 gc_request_signal_number = SIGPROF;
4080 gc_request_signal_number = atoi (signal_name);
4085 g_warning ("Cannot parse valued argument %s\n", argument);
4088 if (! (strcmp (argument, "jit") && strcmp (argument, "j"))) {
4089 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
4090 profiler->action_flags.jit_time = TRUE;
4091 } else if (! (strcmp (argument, "allocations") && strcmp (argument, "alloc") && strcmp (argument, "a"))) {
4092 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
4093 } else if (! (strcmp (argument, "gc") && strcmp (argument, "g"))) {
4094 profiler->flags |= MONO_PROFILE_GC;
4095 } else if (! (strcmp (argument, "heap-shot") && strcmp (argument, "heap") && strcmp (argument, "h"))) {
4096 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
4097 profiler->action_flags.unreachable_objects = TRUE;
4098 profiler->action_flags.heap_shot = TRUE;
4099 } else if (! (strcmp (argument, "unreachable") && strcmp (argument, "free") && strcmp (argument, "f"))) {
4100 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
4101 profiler->action_flags.unreachable_objects = TRUE;
4102 } else if (! (strcmp (argument, "threads") && strcmp (argument, "t"))) {
4103 profiler->flags |= MONO_PROFILE_THREADS;
4104 } else if (! (strcmp (argument, "enter-leave") && strcmp (argument, "calls") && strcmp (argument, "c"))) {
4105 profiler->flags |= MONO_PROFILE_ENTER_LEAVE;
4106 } else if (! (strcmp (argument, "statistical") && strcmp (argument, "stat") && strcmp (argument, "s"))) {
4107 profiler->flags |= MONO_PROFILE_STATISTICAL|MONO_PROFILE_JIT_COMPILATION;
4108 profiler->action_flags.jit_time = TRUE;
4109 } else if (! (strcmp (argument, "force-accurate-timer") && strcmp (argument, "fac"))) {
4110 use_fast_timer = FALSE;
4112 } else if (! (strcmp (argument, "oprofile") && strcmp (argument, "oprof"))) {
4113 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
4114 profiler->action_flags.oprofile = TRUE;
4115 if (op_open_agent ()) {
4116 g_warning ("Problem calling op_open_agent\n");
4119 } else if (strcmp (argument, "logging")) {
4120 g_warning ("Cannot parse flag argument %s\n", argument);
4125 g_free (arguments_array);
4127 #ifndef PLATFORM_WIN32
4128 if (gc_request_signal_number != 0) {
4129 if (((gc_request_signal_number == SIGPROF) && ! (profiler->flags & MONO_PROFILE_STATISTICAL)) ||
4130 (gc_request_signal_number == SIGUSR1) ||
4131 (gc_request_signal_number == SIGUSR2)) {
4132 add_gc_request_handler (gc_request_signal_number);
4134 g_error ("Cannot use signal %d", gc_request_signal_number);
4139 if (profiler->file_name == NULL) {
4140 char *program_name = g_get_prgname ();
4142 if (program_name != NULL) {
4143 char *name_buffer = g_strdup (program_name);
4144 char *name_start = name_buffer;
4147 /* Jump over the last '/' */
4148 cursor = strrchr (name_buffer, '/');
4149 if (cursor == NULL) {
4150 cursor = name_buffer;
4154 name_start = cursor;
4156 /* Then jump over the last '\\' */
4157 cursor = strrchr (name_start, '\\');
4158 if (cursor == NULL) {
4159 cursor = name_start;
4163 name_start = cursor;
4165 /* Finally, find the last '.' */
4166 cursor = strrchr (name_start, '.');
4167 if (cursor != NULL) {
4171 if (profiler->file_name_suffix == NULL) {
4172 profiler->file_name = g_strdup_printf ("%s.mprof", name_start);
4174 profiler->file_name = g_strdup_printf ("%s-%s.mprof", name_start, profiler->file_name_suffix);
4176 g_free (name_buffer);
4178 profiler->file_name = g_strdup_printf ("%s.mprof", "profiler-log");
4184 thread_detach_callback (MonoThread *thread) {
4185 LOG_WRITER_THREAD ("thread_detach_callback: asking writer thread to detach");
4186 profiler->detach_writer_thread = TRUE;
4187 WRITER_EVENT_RAISE ();
4188 LOG_WRITER_THREAD ("thread_detach_callback: done");
4193 data_writer_thread (gpointer nothing) {
4194 static gboolean thread_attached = FALSE;
4195 static gboolean thread_detached = FALSE;
4196 static MonoThread *this_thread = NULL;
4199 ProfilerStatisticalData *statistical_data;
4202 LOG_WRITER_THREAD ("data_writer_thread: going to sleep");
4203 WRITER_EVENT_WAIT ();
4204 LOG_WRITER_THREAD ("data_writer_thread: just woke up");
4206 if (! thread_attached) {
4207 if (! profiler->terminate_writer_thread) {
4208 MonoDomain * root_domain = mono_get_root_domain ();
4209 if (root_domain != NULL) {
4210 LOG_WRITER_THREAD ("data_writer_thread: attaching thread");
4211 this_thread = mono_thread_attach (root_domain);
4212 mono_thread_set_manage_callback (this_thread, thread_detach_callback);
4213 thread_attached = TRUE;
4215 g_error ("Cannot get root domain\n");
4218 /* Execution was too short, pretend we attached and detached. */
4219 thread_attached = TRUE;
4220 thread_detached = TRUE;
4224 if (profiler->heap_shot_was_signalled) {
4225 LOG_WRITER_THREAD ("data_writer_thread: starting requested collection");
4226 mono_gc_collect (mono_gc_max_generation ());
4227 LOG_WRITER_THREAD ("data_writer_thread: requested collection done");
4230 statistical_data = profiler->statistical_data_ready;
4231 done = (statistical_data == NULL) && (profiler->heap_shot_write_jobs == NULL);
4234 LOG_WRITER_THREAD ("data_writer_thread: acquiring lock and writing data");
4237 // This makes sure that all method ids are in place
4238 LOG_WRITER_THREAD ("data_writer_thread: writing mapping...");
4239 flush_all_mappings ();
4240 LOG_WRITER_THREAD ("data_writer_thread: wrote mapping");
4242 if ((statistical_data != NULL) && ! thread_detached) {
4243 LOG_WRITER_THREAD ("data_writer_thread: writing statistical data...");
4244 profiler->statistical_data_ready = NULL;
4245 write_statistical_data_block (statistical_data);
4246 statistical_data->next_free_index = 0;
4247 statistical_data->first_unwritten_index = 0;
4248 profiler->statistical_data_second_buffer = statistical_data;
4249 LOG_WRITER_THREAD ("data_writer_thread: wrote statistical data");
4252 profiler_process_heap_shot_write_jobs ();
4255 LOG_WRITER_THREAD ("data_writer_thread: wrote data and released lock");
4258 if (profiler->detach_writer_thread) {
4259 if (this_thread != NULL) {
4260 LOG_WRITER_THREAD ("data_writer_thread: detaching thread");
4261 mono_thread_detach (this_thread);
4263 profiler->detach_writer_thread = FALSE;
4264 thread_detached = TRUE;
4266 LOG_WRITER_THREAD ("data_writer_thread: warning: thread has already been detached");
4270 if (profiler->terminate_writer_thread) {
4271 LOG_WRITER_THREAD ("data_writer_thread: exiting thread");
4279 mono_profiler_startup (const char *desc);
4281 /* the entry point (mono_profiler_load?) */
4283 mono_profiler_startup (const char *desc)
4285 profiler = g_new0 (MonoProfiler, 1);
4287 setup_user_options ((desc != NULL) ? desc : "");
4289 INITIALIZE_PROFILER_MUTEX ();
4290 MONO_PROFILER_GET_CURRENT_TIME (profiler->start_time);
4291 MONO_PROFILER_GET_CURRENT_COUNTER (profiler->start_counter);
4292 profiler->last_header_counter = 0;
4294 profiler->methods = method_id_mapping_new ();
4295 profiler->classes = class_id_mapping_new ();
4296 profiler->loaded_assemblies = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
4297 profiler->loaded_modules = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
4298 profiler->loaded_appdomains = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
4300 profiler->statistical_data = profiler_statistical_data_new (profiler);
4301 profiler->statistical_data_second_buffer = profiler_statistical_data_new (profiler);
4303 profiler->write_buffers = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
4304 profiler->write_buffers->next = NULL;
4305 profiler->current_write_buffer = profiler->write_buffers;
4306 profiler->current_write_position = 0;
4307 profiler->full_write_buffers = 0;
4309 profiler->executable_regions = profiler_executable_memory_regions_new (1, 1);
4311 profiler->executable_files.table = g_hash_table_new (g_str_hash, g_str_equal);
4312 profiler->executable_files.new_files = NULL;
4314 profiler->heap_shot_write_jobs = NULL;
4315 if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
4316 profiler_heap_buffers_setup (&(profiler->heap));
4318 profiler_heap_buffers_clear (&(profiler->heap));
4320 profiler->garbage_collection_counter = 0;
4322 WRITER_EVENT_INIT ();
4323 LOG_WRITER_THREAD ("mono_profiler_startup: creating writer thread");
4324 CREATE_WRITER_THREAD (data_writer_thread);
4325 LOG_WRITER_THREAD ("mono_profiler_startup: created writer thread");
4327 ALLOCATE_PROFILER_THREAD_DATA ();
4331 write_intro_block ();
4333 mono_profiler_install (profiler, profiler_shutdown);
4335 mono_profiler_install_appdomain (appdomain_start_load, appdomain_end_load,
4336 appdomain_start_unload, appdomain_end_unload);
4337 mono_profiler_install_assembly (assembly_start_load, assembly_end_load,
4338 assembly_start_unload, assembly_end_unload);
4339 mono_profiler_install_module (module_start_load, module_end_load,
4340 module_start_unload, module_end_unload);
4341 mono_profiler_install_class (class_start_load, class_end_load,
4342 class_start_unload, class_end_unload);
4343 mono_profiler_install_jit_compile (method_start_jit, method_end_jit);
4344 mono_profiler_install_enter_leave (method_enter, method_leave);
4345 mono_profiler_install_method_free (method_free);
4346 mono_profiler_install_thread (thread_start, thread_end);
4347 mono_profiler_install_allocation (object_allocated);
4348 mono_profiler_install_statistical (statistical_hit);
4349 mono_profiler_install_statistical_call_chain (statistical_call_chain, profiler->statistical_call_chain_depth);
4350 mono_profiler_install_gc (gc_event, gc_resize);
4352 mono_profiler_install_jit_end (method_jit_result);
4355 mono_profiler_set_events (profiler->flags);